diff --git a/data/themes/default/detach.svg b/data/themes/default/detach.svg
new file mode 100644
index 00000000000..337e3660801
--- /dev/null
+++ b/data/themes/default/detach.svg
@@ -0,0 +1,6 @@
+
diff --git a/data/themes/default/window.svg b/data/themes/default/window.svg
deleted file mode 100644
index fa32ed168c6..00000000000
--- a/data/themes/default/window.svg
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
diff --git a/include/ControllerDialog.h b/include/ControllerDialog.h
index f9145d3b06f..fc6e78f84da 100644
--- a/include/ControllerDialog.h
+++ b/include/ControllerDialog.h
@@ -26,9 +26,11 @@
#ifndef LMMS_GUI_CONTROLLER_DIALOG_H
#define LMMS_GUI_CONTROLLER_DIALOG_H
-#include "DetachableWidget.h"
+#include
+
#include "ModelView.h"
+
namespace lmms
{
@@ -37,7 +39,7 @@ class Controller;
namespace gui
{
-class ControllerDialog : public DetachableWidget, public ModelView
+class ControllerDialog : public QWidget, public ModelView
{
public:
ControllerDialog(Controller* controller, QWidget* parent);
diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h
index 93f125c1f9d..f54b3e1a1c5 100644
--- a/include/ControllerRackView.h
+++ b/include/ControllerRackView.h
@@ -25,13 +25,15 @@
#ifndef LMMS_GUI_CONTROLLER_RACK_VIEW_H
#define LMMS_GUI_CONTROLLER_RACK_VIEW_H
-#include "DetachableWidget.h"
+#include
+
#include "SerializingObject.h"
class QPushButton;
class QScrollArea;
class QVBoxLayout;
+
namespace lmms
{
@@ -42,7 +44,7 @@ namespace gui
class ControllerView;
-class ControllerRackView : public DetachableWidget, public SerializingObject
+class ControllerRackView : public QWidget, public SerializingObject
{
Q_OBJECT
public:
diff --git a/include/ControllerView.h b/include/ControllerView.h
index 9b442672d94..acb52247058 100644
--- a/include/ControllerView.h
+++ b/include/ControllerView.h
@@ -64,7 +64,6 @@ class ControllerView : public QFrame, public ModelView
public slots:
void editControls();
void removeController();
- void closeControls();
void renameController();
void moveUp();
void moveDown();
@@ -85,7 +84,6 @@ public slots:
QMdiSubWindow * m_subWindow;
ControllerDialog * m_controllerDlg;
QLabel * m_nameLabel;
- bool m_show;
} ;
diff --git a/include/DetachableWidget.h b/include/DetachableWidget.h
deleted file mode 100644
index 532dec3303f..00000000000
--- a/include/DetachableWidget.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * DetachableWidget.h - Allows a widget to be detached from
- * LMMS's main window
- *
- * Copyright (c) 2023 Dalton Messmer
- *
- * This file is part of LMMS - https://lmms.io
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program (see COPYING); if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef LMMS_GUI_DETACHABLE_WIDGET
-#define LMMS_GUI_DETACHABLE_WIDGET
-
-#include
-
-#include "lmms_export.h"
-
-namespace lmms::gui {
-
-class LMMS_EXPORT DetachableWidget : public QWidget
-{
- Q_OBJECT
-public:
- using QWidget::QWidget;
-
- void closeEvent(QCloseEvent* ce) override;
-
-signals:
- void closed();
-};
-
-} // namespace lmms::gui
-
-#endif // LMMS_GUI_DETACHABLE_WIDGET
diff --git a/include/DetachableWindow.h b/include/DetachableWindow.h
deleted file mode 100644
index 1636b4fad25..00000000000
--- a/include/DetachableWindow.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * DetachableWindow.h - Allows a window to be detached from
- * LMMS's main window
- *
- * Copyright (c) 2023 Dalton Messmer
- *
- * This file is part of LMMS - https://lmms.io
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program (see COPYING); if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef LMMS_GUI_DETACHABLE_WINDOW
-#define LMMS_GUI_DETACHABLE_WINDOW
-
-#include
-
-#include "lmms_export.h"
-
-namespace lmms::gui {
-
-class LMMS_EXPORT DetachableWindow : public QMainWindow
-{
- Q_OBJECT
-public:
- using QMainWindow::QMainWindow;
-
- void closeEvent(QCloseEvent* ce) override;
-
-signals:
- void closed();
-};
-
-} // namespace lmms::gui
-
-#endif // LMMS_GUI_DETACHABLE_WINDOW
diff --git a/include/Editor.h b/include/Editor.h
index 34e3b48092d..2141717af58 100644
--- a/include/Editor.h
+++ b/include/Editor.h
@@ -25,10 +25,9 @@
#ifndef LMMS_GUI_EDITOR_H
#define LMMS_GUI_EDITOR_H
+#include
#include
-#include "DetachableWindow.h"
-
class QAction;
namespace lmms::gui
@@ -46,7 +45,7 @@ class DropToolBar;
///
/// Those editors include the Song Editor, the Automation Editor, B&B Editor,
/// and the Piano Roll.
-class Editor : public DetachableWindow
+class Editor : public QMainWindow
{
Q_OBJECT
public:
diff --git a/include/EffectControlDialog.h b/include/EffectControlDialog.h
index 1cc748e9a5f..bce85000f6f 100644
--- a/include/EffectControlDialog.h
+++ b/include/EffectControlDialog.h
@@ -26,9 +26,11 @@
#ifndef LMMS_GUI_EFFECT_CONTROL_DIALOG_H
#define LMMS_GUI_EFFECT_CONTROL_DIALOG_H
-#include "DetachableWidget.h"
+#include
+
#include "ModelView.h"
+
namespace lmms
{
@@ -37,14 +39,12 @@ class EffectControls;
namespace gui
{
-class LMMS_EXPORT EffectControlDialog : public DetachableWidget, public ModelView
+class LMMS_EXPORT EffectControlDialog : public QWidget, public ModelView
{
public:
EffectControlDialog(EffectControls* controls);
~EffectControlDialog() override = default;
- virtual bool isResizable() const { return false; }
-
protected:
EffectControls* m_effectControls;
};
diff --git a/include/EffectView.h b/include/EffectView.h
index cd45b735e77..382f7025a2f 100644
--- a/include/EffectView.h
+++ b/include/EffectView.h
@@ -73,7 +73,6 @@ public slots:
void moveUp();
void moveDown();
void deletePlugin();
- void closeEffects();
signals:
diff --git a/include/MainWindow.h b/include/MainWindow.h
index 5f0f23f716e..40d376fe352 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -141,9 +141,13 @@ class MainWindow : public QMainWindow
static void saveWidgetState( QWidget * _w, QDomElement & _de );
static void restoreWidgetState( QWidget * _w, const QDomElement & _de );
+ void setAllSubWindowsDetached(bool detached);
bool eventFilter(QObject* watched, QEvent* event) override;
+signals:
+ void detachAllSubWindows(bool detached);
+
public slots:
void resetWindowTitle();
diff --git a/include/MicrotunerConfig.h b/include/MicrotunerConfig.h
index dc3632b87dd..13d4bbc9789 100644
--- a/include/MicrotunerConfig.h
+++ b/include/MicrotunerConfig.h
@@ -25,9 +25,10 @@
#ifndef LMMS_GUI_MICROTUNER_CONFIG_H
#define LMMS_GUI_MICROTUNER_CONFIG_H
+#include
+
#include "AutomatableModel.h"
#include "ComboBoxModel.h"
-#include "DetachableWidget.h"
#include "SerializingObject.h"
class QLineEdit;
@@ -37,7 +38,7 @@ namespace lmms::gui
{
-class LMMS_EXPORT MicrotunerConfig : public DetachableWidget, public SerializingObject
+class LMMS_EXPORT MicrotunerConfig : public QWidget, public SerializingObject
{
Q_OBJECT
public:
diff --git a/include/MixerView.h b/include/MixerView.h
index a82b84a9835..8eed38bf2d7 100644
--- a/include/MixerView.h
+++ b/include/MixerView.h
@@ -25,8 +25,9 @@
#ifndef LMMS_GUI_MIXER_VIEW_H
#define LMMS_GUI_MIXER_VIEW_H
+#include
+
#include "MixerChannelView.h"
-#include "DetachableWidget.h"
#include "ModelView.h"
#include "SerializingObject.h"
@@ -45,7 +46,7 @@ namespace lmms::gui
{
class LMMS_EXPORT MixerView
- : public DetachableWidget
+ : public QWidget
, public ModelView
, public SerializingObjectHook
{
diff --git a/include/ProjectNotes.h b/include/ProjectNotes.h
index 9154f13ea04..daa7436b9b1 100644
--- a/include/ProjectNotes.h
+++ b/include/ProjectNotes.h
@@ -25,7 +25,8 @@
#ifndef LMMS_GUI_PROJECT_NOTES_H
#define LMMS_GUI_PROJECT_NOTES_H
-#include "DetachableWindow.h"
+#include
+
#include "SerializingObject.h"
class QAction;
@@ -37,7 +38,7 @@ namespace lmms::gui
{
-class LMMS_EXPORT ProjectNotes : public DetachableWindow, public SerializingObject
+class LMMS_EXPORT ProjectNotes : public QMainWindow, public SerializingObject
{
Q_OBJECT
public:
diff --git a/include/SampleTrackWindow.h b/include/SampleTrackWindow.h
index 8b1a136c625..47ce765f1b8 100644
--- a/include/SampleTrackWindow.h
+++ b/include/SampleTrackWindow.h
@@ -25,7 +25,8 @@
#ifndef LMMS_GUI_SAMPLE_TRACK_WINDOW_H
#define LMMS_GUI_SAMPLE_TRACK_WINDOW_H
-#include "DetachableWidget.h"
+#include
+
#include "ModelView.h"
#include "SampleTrack.h"
#include "SerializingObject.h"
@@ -42,7 +43,7 @@ class MixerChannelLcdSpinBox;
class SampleTrackView;
-class SampleTrackWindow : public DetachableWidget, public ModelView, public SerializingObjectHook
+class SampleTrackWindow : public QWidget, public ModelView, public SerializingObjectHook
{
Q_OBJECT
public:
diff --git a/include/SetupDialog.h b/include/SetupDialog.h
index c314ff42d81..f97ba306bed 100644
--- a/include/SetupDialog.h
+++ b/include/SetupDialog.h
@@ -85,6 +85,7 @@ private slots:
void toggleMMPZ(bool enabled);
void toggleDisableBackup(bool enabled);
void toggleOpenLastProject(bool enabled);
+ void detachBehaviorChanged();
void loopMarkerModeChanged();
void setLanguage(int lang);
@@ -147,6 +148,8 @@ private slots:
bool m_MMPZ;
bool m_disableBackup;
bool m_openLastProject;
+ QString m_detachBehavior;
+ QComboBox* m_detachBehaviorComboBox;
QString m_loopMarkerMode;
QComboBox* m_loopMarkerComboBox;
QString m_lang;
diff --git a/include/SimpleTextFloat.h b/include/SimpleTextFloat.h
index b8858418d34..2821ca41183 100644
--- a/include/SimpleTextFloat.h
+++ b/include/SimpleTextFloat.h
@@ -30,6 +30,7 @@
#include "lmms_export.h"
+class QLabel;
class QTimer;
namespace lmms::gui
@@ -57,10 +58,9 @@ class LMMS_EXPORT SimpleTextFloat : public QWidget
}
void hide();
- void show();
private:
- QString m_text;
+ QLabel * m_textLabel;
QTimer * m_showTimer;
QTimer * m_hideTimer;
};
diff --git a/include/SubWindow.h b/include/SubWindow.h
index f300335dbe6..656dc0980a3 100644
--- a/include/SubWindow.h
+++ b/include/SubWindow.h
@@ -63,11 +63,16 @@ class LMMS_EXPORT SubWindow : public QMdiSubWindow
QBrush activeColor() const;
QColor textShadowColor() const;
QColor borderColor() const;
+ QMargins decorationMargins() const;
void setActiveColor( const QBrush & b );
void setTextShadowColor( const QColor &c );
void setBorderColor( const QColor &c );
int titleBarHeight() const;
int frameWidth() const;
+ bool isDetachable() const;
+ void setDetachable(bool on);
+ bool isDetached() const;
+ void setDetached(bool on);
// TODO Needed to update the title bar when replacing instruments.
// Update works automatically if QMdiSubWindows are used.
@@ -80,14 +85,13 @@ public slots:
protected:
// hook the QWidget move/resize events to update the tracked geometry
- void moveEvent( QMoveEvent * event ) override;
- void resizeEvent( QResizeEvent * event ) override;
- void paintEvent( QPaintEvent * pe ) override;
- void changeEvent( QEvent * event ) override;
+ void moveEvent(QMoveEvent* event) override;
+ void resizeEvent(QResizeEvent* event) override;
+ void paintEvent(QPaintEvent* pe) override;
+ void changeEvent(QEvent* event) override;
+ void showEvent(QShowEvent* e) override;
bool eventFilter(QObject* obj, QEvent* event) override;
- bool isDetached() const;
-
signals:
void focusLost();
@@ -106,6 +110,7 @@ public slots:
QLabel * m_windowTitle;
QGraphicsDropShadowEffect * m_shadow;
bool m_hasFocus;
+ bool m_isDetachable;
static void elideText( QLabel *label, QString text );
void adjustTitleBar();
diff --git a/plugins/Amplifier/AmplifierControlDialog.cpp b/plugins/Amplifier/AmplifierControlDialog.cpp
index 9bf0bb649b1..44c0e3be265 100644
--- a/plugins/Amplifier/AmplifierControlDialog.cpp
+++ b/plugins/Amplifier/AmplifierControlDialog.cpp
@@ -57,6 +57,7 @@ AmplifierControlDialog::AmplifierControlDialog(AmplifierControls* controls) :
gridLayout->addWidget(makeKnob(tr("PAN"), tr("Panning:"), "%", &controls->m_panModel, false), 0, 1, Qt::AlignHCenter);
gridLayout->addWidget(makeKnob(tr("LEFT"), tr("Left gain:"), "%", &controls->m_leftModel, true), 1, 0, Qt::AlignHCenter);
gridLayout->addWidget(makeKnob(tr("RIGHT"), tr("Right gain:"), "%", &controls->m_rightModel, true), 1, 1, Qt::AlignHCenter);
+ gridLayout->setSizeConstraint(QLayout::SetFixedSize);
}
} // namespace lmms::gui
diff --git a/plugins/BassBooster/BassBoosterControlDialog.cpp b/plugins/BassBooster/BassBoosterControlDialog.cpp
index fcdf10cc260..93708dbd76c 100644
--- a/plugins/BassBooster/BassBoosterControlDialog.cpp
+++ b/plugins/BassBooster/BassBoosterControlDialog.cpp
@@ -67,6 +67,7 @@ BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* control
tl->addLayout( l );
setLayout( tl );
+ tl->setSizeConstraint(QLayout::SetFixedSize);
}
diff --git a/plugins/Compressor/CompressorControlDialog.h b/plugins/Compressor/CompressorControlDialog.h
index b7e2a87e141..7fe79dcc52b 100755
--- a/plugins/Compressor/CompressorControlDialog.h
+++ b/plugins/Compressor/CompressorControlDialog.h
@@ -71,7 +71,6 @@ class CompressorControlDialog : public EffectControlDialog
public:
CompressorControlDialog(CompressorControls* controls);
- bool isResizable() const override {return true;}
QSize sizeHint() const override {return QSize(COMP_SCREEN_X, COMP_SCREEN_Y);}
// For theming purposes
diff --git a/plugins/Dispersion/DispersionControlDialog.cpp b/plugins/Dispersion/DispersionControlDialog.cpp
index fbf37923cfd..2784da35c37 100644
--- a/plugins/Dispersion/DispersionControlDialog.cpp
+++ b/plugins/Dispersion/DispersionControlDialog.cpp
@@ -43,6 +43,7 @@ DispersionControlDialog::DispersionControlDialog(DispersionControls* controls) :
setAutoFillBackground(true);
auto layout = new QHBoxLayout(this);
layout->setSpacing(5);
+ layout->setSizeConstraint(QLayout::SetFixedSize);
auto amountBox = new LcdSpinBox(3, this, "Amount");
amountBox->setModel(&controls->m_amountModel);
diff --git a/plugins/Flanger/FlangerControlsDialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp
index 7433a6b2c1c..e8d382e52db 100644
--- a/plugins/Flanger/FlangerControlsDialog.cpp
+++ b/plugins/Flanger/FlangerControlsDialog.cpp
@@ -44,6 +44,7 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) :
setPalette( pal );
auto mainLayout = new QVBoxLayout(this);
+ mainLayout->setSizeConstraint(QLayout::SetFixedSize);
auto knobLayout = new QHBoxLayout();
mainLayout->addLayout(knobLayout);
diff --git a/plugins/LadspaEffect/LadspaControlDialog.cpp b/plugins/LadspaEffect/LadspaControlDialog.cpp
index 62eb30ba312..5189b0cde80 100644
--- a/plugins/LadspaEffect/LadspaControlDialog.cpp
+++ b/plugins/LadspaEffect/LadspaControlDialog.cpp
@@ -47,7 +47,6 @@ LadspaControlDialog::LadspaControlDialog( LadspaControls * _ctl ) :
m_stereoLink( nullptr )
{
auto mainLay = new QVBoxLayout(this);
- mainLay->setSizeConstraint(QLayout::SetFixedSize);
m_effectLayout = new QHBoxLayout();
mainLay->addLayout( m_effectLayout );
diff --git a/plugins/LadspaEffect/LadspaMatrixControlDialog.cpp b/plugins/LadspaEffect/LadspaMatrixControlDialog.cpp
index c05481c6d81..f778fb07e54 100644
--- a/plugins/LadspaEffect/LadspaMatrixControlDialog.cpp
+++ b/plugins/LadspaEffect/LadspaMatrixControlDialog.cpp
@@ -47,6 +47,7 @@ LadspaMatrixControlDialog::LadspaMatrixControlDialog(LadspaControls * ladspaCont
m_stereoLink(nullptr)
{
QVBoxLayout * mainLayout = new QVBoxLayout(this);
+ mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
m_scrollArea = new QScrollArea(this);
m_scrollArea->setWidgetResizable(true);
@@ -72,11 +73,6 @@ LadspaMatrixControlDialog::LadspaMatrixControlDialog(LadspaControls * ladspaCont
}
}
-bool LadspaMatrixControlDialog::isResizable() const
-{
- return true;
-}
-
bool LadspaMatrixControlDialog::needsLinkColumn() const
{
LadspaControls * ladspaControls = getLadspaControls();
diff --git a/plugins/LadspaEffect/LadspaMatrixControlDialog.h b/plugins/LadspaEffect/LadspaMatrixControlDialog.h
index fa9a6e1b3db..0189c815947 100644
--- a/plugins/LadspaEffect/LadspaMatrixControlDialog.h
+++ b/plugins/LadspaEffect/LadspaMatrixControlDialog.h
@@ -50,7 +50,6 @@ class LadspaMatrixControlDialog : public EffectControlDialog
Q_OBJECT
public:
LadspaMatrixControlDialog(LadspaControls* ctl);
- bool isResizable() const override;
private slots:
void updateEffectView(LadspaControls* ctl);
diff --git a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp
index 7ce05bc4548..d89ef02bf1d 100644
--- a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp
+++ b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp
@@ -95,6 +95,8 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog(
mainLayout->addLayout( knobLayout );
mainLayout->addLayout( ledLayout );
+ mainLayout->setSizeConstraint(QLayout::SetFixedSize);
+
setLayout( mainLayout );
}
diff --git a/plugins/ReverbSC/ReverbSCControlDialog.cpp b/plugins/ReverbSC/ReverbSCControlDialog.cpp
index 3be156396b2..d507a879275 100644
--- a/plugins/ReverbSC/ReverbSCControlDialog.cpp
+++ b/plugins/ReverbSC/ReverbSCControlDialog.cpp
@@ -44,6 +44,7 @@ ReverbSCControlDialog::ReverbSCControlDialog( ReverbSCControls* controls ) :
setPalette( pal );
auto knobLayout = new QHBoxLayout(this);
+ knobLayout->setSizeConstraint(QLayout::SetFixedSize);
auto inputGainKnob = new Knob(KnobType::Bright26, tr("Input"), this);
inputGainKnob->setModel( &controls->m_inputGainModel );
@@ -68,4 +69,4 @@ ReverbSCControlDialog::ReverbSCControlDialog( ReverbSCControls* controls ) :
}
-} // namespace lmms::gui
\ No newline at end of file
+} // namespace lmms::gui
diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.h b/plugins/SpectrumAnalyzer/SaControlsDialog.h
index f8d67f1e8f5..8567561f35d 100644
--- a/plugins/SpectrumAnalyzer/SaControlsDialog.h
+++ b/plugins/SpectrumAnalyzer/SaControlsDialog.h
@@ -48,7 +48,6 @@ class SaControlsDialog : public EffectControlDialog
explicit SaControlsDialog(SaControls *controls, SaProcessor *processor);
~SaControlsDialog() override = default;
- bool isResizable() const override {return true;}
QSize sizeHint() const override;
private:
diff --git a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp
index 1440a4be51f..e19d7363a51 100644
--- a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp
+++ b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp
@@ -39,6 +39,7 @@ StereoEnhancerControlDialog::StereoEnhancerControlDialog(
EffectControlDialog( _controls )
{
auto l = new QHBoxLayout(this);
+ l->setSizeConstraint(QLayout::SetFixedSize);
auto widthKnob = new Knob(KnobType::Bright26, tr("WIDTH"), this);
widthKnob->setModel( &_controls->m_widthModel );
diff --git a/plugins/Vectorscope/VecControlsDialog.h b/plugins/Vectorscope/VecControlsDialog.h
index 8fee75b4b30..400e04ff5d5 100644
--- a/plugins/Vectorscope/VecControlsDialog.h
+++ b/plugins/Vectorscope/VecControlsDialog.h
@@ -45,7 +45,6 @@ class VecControlsDialog : public EffectControlDialog
explicit VecControlsDialog(VecControls *controls);
~VecControlsDialog() override = default;
- bool isResizable() const override {return true;}
QSize sizeHint() const override;
private:
diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp
index 426856f1e30..abad6df233e 100644
--- a/plugins/Vestige/Vestige.cpp
+++ b/plugins/Vestige/Vestige.cpp
@@ -103,8 +103,9 @@ class vstSubWin : public SubWindow
vstSubWin( QWidget * _parent ) :
SubWindow( _parent )
{
- setAttribute( Qt::WA_DeleteOnClose, false );
- setWindowFlags( Qt::WindowCloseButtonHint );
+ setAttribute(Qt::WA_DeleteOnClose, false);
+ setWindowFlag(Qt::WindowMaximizeButtonHint, false);
+ setDetachable(false);
}
~vstSubWin() override = default;
@@ -113,15 +114,8 @@ class vstSubWin : public SubWindow
{
// ignore close-events - for some reason otherwise the VST GUI
// remains hidden when re-opening
- if (windowFlags().testFlag(Qt::Window))
- {
- e->accept();
- }
- else
- {
- hide();
- e->ignore();
- }
+ hide();
+ e->ignore();
}
};
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 41fecd2aec1..f1712053f67 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -9,8 +9,6 @@ SET(LMMS_SRCS
gui/ControllerRackView.cpp
gui/ControllerView.cpp
gui/Controls.cpp
- gui/DetachableWidget.cpp
- gui/DetachableWindow.cpp
gui/EffectControlDialog.cpp
gui/EffectRackView.cpp
gui/EffectView.cpp
diff --git a/src/gui/ControllerDialog.cpp b/src/gui/ControllerDialog.cpp
index d47da1a98de..ef4cec2c32b 100644
--- a/src/gui/ControllerDialog.cpp
+++ b/src/gui/ControllerDialog.cpp
@@ -31,7 +31,7 @@ namespace lmms::gui
{
ControllerDialog::ControllerDialog(Controller* controller, QWidget* parent)
- : DetachableWidget{parent}
+ : QWidget{parent}
, ModelView{controller, this}
{
}
diff --git a/src/gui/ControllerRackView.cpp b/src/gui/ControllerRackView.cpp
index 299e48772ff..001c8c0bb2d 100644
--- a/src/gui/ControllerRackView.cpp
+++ b/src/gui/ControllerRackView.cpp
@@ -47,7 +47,7 @@ namespace lmms::gui
ControllerRackView::ControllerRackView()
- : DetachableWidget{}
+ : QWidget{}
, m_nextIndex{0}
{
setWindowIcon( embed::getIconPixmap( "controller" ) );
diff --git a/src/gui/ControllerView.cpp b/src/gui/ControllerView.cpp
index 7f7c4729c67..21424385f5f 100644
--- a/src/gui/ControllerView.cpp
+++ b/src/gui/ControllerView.cpp
@@ -44,15 +44,14 @@ namespace lmms::gui
{
-ControllerView::ControllerView( Controller * _model, QWidget * _parent ) :
- QFrame( _parent ),
- ModelView( _model, this ),
- m_subWindow( nullptr ),
- m_controllerDlg( nullptr ),
- m_show( true )
+ControllerView::ControllerView(Controller* model, QWidget* parent)
+ : QFrame{parent}
+ , ModelView{model, this}
+ , m_subWindow{nullptr}
+ , m_controllerDlg{nullptr}
{
- this->setFrameStyle( QFrame::StyledPanel );
- this->setFrameShadow( QFrame::Raised );
+ this->setFrameStyle(QFrame::StyledPanel);
+ this->setFrameShadow(QFrame::Raised);
setFocusPolicy(Qt::StrongFocus);
auto vBoxLayout = new QVBoxLayout(this);
@@ -60,7 +59,7 @@ ControllerView::ControllerView( Controller * _model, QWidget * _parent ) :
auto hBox = new QHBoxLayout();
vBoxLayout->addLayout(hBox);
- auto label = new QLabel("" + _model->displayName() + "", this);
+ auto label = new QLabel("" + model->displayName() + "", this);
QSizePolicy sizePolicy = label->sizePolicy();
sizePolicy.setHorizontalStretch(1);
label->setSizePolicy(sizePolicy);
@@ -72,27 +71,17 @@ ControllerView::ControllerView( Controller * _model, QWidget * _parent ) :
hBox->addWidget(controlsButton);
- m_nameLabel = new QLabel(_model->name(), this);
+ m_nameLabel = new QLabel(model->name(), this);
vBoxLayout->addWidget(m_nameLabel);
- m_controllerDlg = getController()->createDialog( getGUI()->mainWindow()->workspace() );
-
- m_subWindow = getGUI()->mainWindow()->addWindowedWidget( m_controllerDlg );
-
- Qt::WindowFlags flags = m_subWindow->windowFlags();
- flags &= ~Qt::WindowMaximizeButtonHint;
- m_subWindow->setWindowFlags( flags );
- m_subWindow->setFixedSize( m_subWindow->size() );
-
- m_subWindow->setWindowIcon( m_controllerDlg->windowIcon() );
-
- connect( m_controllerDlg, SIGNAL(closed()),
- this, SLOT(closeControls()));
+ m_controllerDlg = getController()->createDialog(getGUI()->mainWindow()->workspace());
+ m_subWindow = getGUI()->mainWindow()->addWindowedWidget(m_controllerDlg);
+ m_subWindow->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
m_subWindow->hide();
- setModel( _model );
+ setModel(model);
}
@@ -111,28 +100,20 @@ ControllerView::~ControllerView()
void ControllerView::editControls()
{
- if( m_show )
+ if (!m_controllerDlg->isVisible())
{
m_subWindow->show();
m_subWindow->raise();
- m_show = false;
}
else
{
m_subWindow->hide();
- m_show = true;
}
}
-void ControllerView::closeControls()
-{
- m_subWindow->hide();
- m_show = true;
-}
-
void ControllerView::moveUp() { emit movedUp(this); }
void ControllerView::moveDown() { emit movedDown(this); }
@@ -188,4 +169,4 @@ void ControllerView::contextMenuEvent( QContextMenuEvent * )
}
-} // namespace lmms::gui
\ No newline at end of file
+} // namespace lmms::gui
diff --git a/src/gui/DetachableWidget.cpp b/src/gui/DetachableWidget.cpp
deleted file mode 100644
index 240ddec16b1..00000000000
--- a/src/gui/DetachableWidget.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * DetachableWidget.cpp - Allows a widget to be detached from
- * LMMS's main window
- *
- * Copyright (c) 2023 Dalton Messmer
- *
- * This file is part of LMMS - https://lmms.io
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program (see COPYING); if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- */
-
-#include "DetachableWidget.h"
-
-#include
-
-#include "GuiApplication.h"
-#include "MainWindow.h"
-#include "SubWindow.h"
-
-namespace lmms::gui {
-
-void DetachableWidget::closeEvent(QCloseEvent* ce)
-{
- if (windowFlags().testFlag(Qt::Window))
- {
- dynamic_cast(*parentWidget()).attach();
- ce->ignore();
- }
- else if (getGUI()->mainWindow()->workspace())
- {
- parentWidget()->hide();
- ce->ignore();
- }
- else
- {
- hide();
- ce->ignore();
- }
- emit closed();
-}
-
-} // namespace lmms::gui
diff --git a/src/gui/DetachableWindow.cpp b/src/gui/DetachableWindow.cpp
deleted file mode 100644
index d800ebf64f1..00000000000
--- a/src/gui/DetachableWindow.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * DetachableWindow.cpp - Allows a window to be detached from
- * LMMS's main window
- *
- * Copyright (c) 2023 Dalton Messmer
- *
- * This file is part of LMMS - https://lmms.io
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program (see COPYING); if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- */
-
-#include "DetachableWindow.h"
-
-#include
-
-#include "GuiApplication.h"
-#include "MainWindow.h"
-#include "SubWindow.h"
-
-namespace lmms::gui {
-
-void DetachableWindow::closeEvent(QCloseEvent* ce)
-{
- if (windowFlags().testFlag(Qt::Window))
- {
- dynamic_cast(*parentWidget()).attach();
- ce->ignore();
- }
- else if (getGUI()->mainWindow()->workspace())
- {
- parentWidget()->hide();
- ce->ignore();
- }
- else
- {
- hide();
- ce->ignore();
- }
- emit closed();
-}
-
-} // namespace lmms::gui
diff --git a/src/gui/EffectControlDialog.cpp b/src/gui/EffectControlDialog.cpp
index 9c689bf5ba9..6a624e4dc49 100644
--- a/src/gui/EffectControlDialog.cpp
+++ b/src/gui/EffectControlDialog.cpp
@@ -31,7 +31,7 @@ namespace lmms::gui
{
EffectControlDialog::EffectControlDialog(EffectControls* controls)
- : DetachableWidget{nullptr}
+ : QWidget{nullptr}
, ModelView{controls, this}
, m_effectControls{controls}
{
diff --git a/src/gui/EffectView.cpp b/src/gui/EffectView.cpp
index 428035b9564..3874f0de647 100644
--- a/src/gui/EffectView.cpp
+++ b/src/gui/EffectView.cpp
@@ -87,26 +87,10 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
this, SLOT(editControls()));
m_controlView = effect()->controls()->createView();
- if( m_controlView )
+ if (m_controlView)
{
- m_subWindow = getGUI()->mainWindow()->addWindowedWidget( m_controlView );
-
- if ( !m_controlView->isResizable() )
- {
- m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
- if (m_subWindow->layout())
- {
- m_subWindow->layout()->setSizeConstraint(QLayout::SetFixedSize);
- }
- }
-
- Qt::WindowFlags flags = m_subWindow->windowFlags();
- flags &= ~Qt::WindowMaximizeButtonHint;
- m_subWindow->setWindowFlags( flags );
-
- connect( m_controlView, SIGNAL(closed()),
- this, SLOT(closeEffects()));
-
+ m_subWindow = getGUI()->mainWindow()->addWindowedWidget(m_controlView);
+ m_subWindow->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
m_subWindow->hide();
}
}
@@ -134,11 +118,11 @@ void EffectView::editControls()
{
if( m_subWindow )
{
- if( !m_subWindow->isVisible() )
+ if( !m_controlView->isVisible() )
{
m_subWindow->show();
m_subWindow->raise();
- effect()->controls()->setViewVisible( true );
+ effect()->controls()->setViewVisible( true ); // TODO is this even needed?
}
else
{
@@ -174,17 +158,6 @@ void EffectView::deletePlugin()
-void EffectView::closeEffects()
-{
- if( m_subWindow )
- {
- m_subWindow->hide();
- }
- effect()->controls()->setViewVisible( false );
-}
-
-
-
void EffectView::contextMenuEvent( QContextMenuEvent * )
{
QPointer contextMenu = new CaptionMenu( model()->displayName(), this );
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index ff5b05bed25..3f68e16575e 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -329,23 +329,16 @@ void MainWindow::finalize()
auto edit_menu = new QMenu(this);
menuBar()->addMenu( edit_menu )->setText( tr( "&Edit" ) );
- m_undoAction = edit_menu->addAction( embed::getIconPixmap( "edit_undo" ),
- tr( "Undo" ),
+ m_undoAction = edit_menu->addAction(embed::getIconPixmap("edit_undo"),
+ tr("Undo"),
this, SLOT(undo()),
- QKeySequence::Undo );
- m_redoAction = edit_menu->addAction( embed::getIconPixmap( "edit_redo" ),
- tr( "Redo" ),
+ QKeySequence::Undo);
+ m_redoAction = edit_menu->addAction(embed::getIconPixmap("edit_redo"),
+ tr("Redo"),
this, SLOT(redo()),
- QKeySequence::Redo );
- // Ensure that both (Ctrl+Y) and (Ctrl+Shift+Z) activate redo shortcut regardless of OS defaults
- if (QKeySequence(QKeySequence::Redo) != QKeySequence(combine(Qt::CTRL, Qt::Key_Y)))
- {
- new QShortcut(QKeySequence(combine(Qt::CTRL, Qt::Key_Y)), this, SLOT(redo()));
- }
- if (QKeySequence(QKeySequence::Redo) != QKeySequence(combine(Qt::CTRL, Qt::SHIFT, Qt::Key_Z)))
- {
- new QShortcut(QKeySequence(combine(Qt::CTRL, Qt::SHIFT, Qt::Key_Z)), this, SLOT(redo()));
- }
+ QKeySequence::Redo);
+ m_undoAction->setShortcutContext(Qt::ApplicationShortcut);
+ m_redoAction->setShortcutContext(Qt::ApplicationShortcut);
edit_menu->addSeparator();
edit_menu->addAction(embed::getIconPixmap("microtuner"), tr("Scales and keymaps"),
@@ -361,6 +354,7 @@ void MainWindow::finalize()
this, SLOT(updateViewMenu()));
connect( m_viewMenu, SIGNAL(triggered(QAction*)), this,
SLOT(updateConfig(QAction*)));
+ updateViewMenu();
m_toolsMenu = new QMenu( this );
@@ -505,10 +499,7 @@ void MainWindow::finalize()
getGUI()->songEditor()
})
{
- QMdiSubWindow* window = addWindowedWidget(widget);
- window->setWindowIcon(widget->windowIcon());
- window->setAttribute(Qt::WA_DeleteOnClose, false);
- window->resize(widget->sizeHint());
+ addWindowedWidget(widget);
}
getGUI()->automationEditor()->parentWidget()->hide();
@@ -557,8 +548,9 @@ SubWindow* MainWindow::addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags
{
// wrap the widget in our own *custom* window that patches some errors in QMdiSubWindow
auto win = new SubWindow(m_workspace->viewport(), windowFlags);
- win->setAttribute(Qt::WA_DeleteOnClose);
+ connect(this, &MainWindow::detachAllSubWindows, win, &SubWindow::setDetached);
win->setWidget(w);
+ if (w) { connect(w, &QWidget::destroyed, win, &SubWindow::deleteLater); } // TODO somehow make this work on any setWidget
if (w && w->sizeHint().isValid()) {
auto titleBarHeight = win->titleBarHeight();
auto frameWidth = win->frameWidth();
@@ -570,6 +562,13 @@ SubWindow* MainWindow::addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags
}
+
+void MainWindow::setAllSubWindowsDetached(bool detachState)
+{
+ emit detachAllSubWindows(detachState);
+}
+
+
void MainWindow::resetWindowTitle()
{
QString title(tr( "Untitled" ));
@@ -660,77 +659,59 @@ void MainWindow::clearKeyModifiers()
-void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de )
+void MainWindow::saveWidgetState(QWidget* w, QDomElement& de)
{
- // If our widget is the main content of a window (e.g. piano roll, Mixer, etc),
- // we really care about the position of the *window* - not the position of the widget within its window
- if( _w->parentWidget() != nullptr &&
- _w->parentWidget()->inherits( "QMdiSubWindow" ) )
- {
- _w = _w->parentWidget();
- }
-
- // If the widget is a SubWindow, then we can make use of the getTrueNormalGeometry() method that
- // performs the same as normalGeometry, but isn't broken on X11 ( see https://bugreports.qt.io/browse/QTBUG-256 )
- auto asSubWindow = qobject_cast(_w);
- QRect normalGeom = asSubWindow != nullptr ? asSubWindow->getTrueNormalGeometry() : _w->normalGeometry();
-
- bool visible = _w->isVisible();
- _de.setAttribute( "visible", visible );
- _de.setAttribute( "minimized", _w->isMinimized() );
- _de.setAttribute( "maximized", _w->isMaximized() );
-
- _de.setAttribute( "x", normalGeom.x() );
- _de.setAttribute( "y", normalGeom.y() );
-
- QSize sizeToStore = normalGeom.size();
- _de.setAttribute( "width", sizeToStore.width() );
- _de.setAttribute( "height", sizeToStore.height() );
+ // TODO only use one of these
+ SubWindow* win = qobject_cast(w); // nullptr if not
+ if (!win && w->parentWidget()) { win = qobject_cast(w->parentWidget()); } // try parent instead
+ if (!win) { return; } // finally, soft-fail if neither can find the window
+
+ de.setAttribute("visible", bool{win->widget() && win->widget()->isVisible()});
+ de.setAttribute("maximized", win->isMaximized());
+
+ QRect normalGeometry = win->getTrueNormalGeometry();
+ de.setAttribute("x", normalGeometry.x());
+ de.setAttribute("y", normalGeometry.y() );
+ de.setAttribute("width", normalGeometry.width());
+ de.setAttribute("height", normalGeometry.height());
}
-void MainWindow::restoreWidgetState( QWidget * _w, const QDomElement & _de )
+void MainWindow::restoreWidgetState(QWidget* w, const QDomElement& de)
{
- QRect r( qMax( 1, _de.attribute( "x" ).toInt() ),
- qMax( 1, _de.attribute( "y" ).toInt() ),
- qMax( _w->sizeHint().width(), _de.attribute( "width" ).toInt() ),
- qMax( _w->minimumHeight(), _de.attribute( "height" ).toInt() ) );
- if( _de.hasAttribute( "visible" ) && !r.isNull() )
+ // TODO only use one of these
+ SubWindow* win = qobject_cast(w); // nullptr if not
+ if (!win && w->parentWidget()) { win = qobject_cast(w->parentWidget()); } // try parent instead
+ if (!win) { return; } // finally, soft-fail if neither can find the window
+
+ QRect normalGeometry(de.attribute("x").toInt(),
+ de.attribute("y").toInt(),
+ de.attribute("width").toInt(),
+ de.attribute("height").toInt());
+
+ if (normalGeometry.isValid())
{
- // If our widget is the main content of a window (e.g. piano roll, Mixer, etc),
- // we really care about the position of the *window* - not the position of the widget within its window
- if ( _w->parentWidget() != nullptr &&
- _w->parentWidget()->inherits( "QMdiSubWindow" ) )
- {
- _w = _w->parentWidget();
- }
// first restore the window, as attempting to resize a maximized window causes graphics glitching
- _w->setWindowState( _w->windowState() & ~(Qt::WindowMaximized | Qt::WindowMinimized) );
+ win->setWindowState(win->windowState() & ~(Qt::WindowMaximized | Qt::WindowMinimized));
- // Check isEmpty() to work around corrupt project files with empty size
- if ( ! r.size().isEmpty() ) {
- _w->resize( r.size() );
- }
- _w->move( r.topLeft() );
+ win->setGeometry(normalGeometry);
// set the window to its correct minimized/maximized/restored state
- Qt::WindowStates flags = _w->windowState();
- flags = _de.attribute( "minimized" ).toInt() ?
- ( flags | Qt::WindowMinimized ) :
- ( flags & ~Qt::WindowMinimized );
- flags = _de.attribute( "maximized" ).toInt() ?
- ( flags | Qt::WindowMaximized ) :
- ( flags & ~Qt::WindowMaximized );
- _w->setWindowState( flags );
-
- _w->setVisible( _de.attribute( "visible" ).toInt() );
+ Qt::WindowStates winState = win->windowState();
+ winState = de.attribute("maximized").toInt()
+ ? (winState | Qt::WindowMaximized)
+ : (winState & ~Qt::WindowMaximized);
+ win->setWindowState(winState);
}
+
+ if (de.hasAttribute("visible")) { win->setVisible(de.attribute("visible").toInt()); }
}
+
void MainWindow::emptySlot()
{
}
@@ -1084,6 +1065,22 @@ void MainWindow::updateViewMenu()
m_viewMenu->addSeparator();
+ auto detachAllAction = m_viewMenu->addAction(embed::getIconPixmap("detach"),
+ tr("Detach all subwindows"),
+ this, [this](){setAllSubWindowsDetached(true);},
+ QKeySequence {Qt::CTRL | Qt::SHIFT | Qt::Key_D}
+ );
+ auto attachAllAction = m_viewMenu->addAction(embed::getIconPixmap("detach"),
+ tr("Attach all subwindows"),
+ this, [this](){setAllSubWindowsDetached(false);},
+ QKeySequence {Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_D}
+ );
+
+ detachAllAction->setShortcutContext(Qt::ApplicationShortcut);
+ attachAllAction->setShortcutContext(Qt::ApplicationShortcut);
+
+ m_viewMenu->addSeparator();
+
// Here we should put all look&feel -stuff from configmanager
// that is safe to change on the fly. There is probably some
// more elegant way to do this.
diff --git a/src/gui/MicrotunerConfig.cpp b/src/gui/MicrotunerConfig.cpp
index 74231c5a5cd..c686b2c01a8 100644
--- a/src/gui/MicrotunerConfig.cpp
+++ b/src/gui/MicrotunerConfig.cpp
@@ -55,7 +55,7 @@ namespace lmms::gui
MicrotunerConfig::MicrotunerConfig() :
- DetachableWidget(),
+ QWidget(),
m_scaleComboModel(nullptr, tr("Selected scale slot")),
m_keymapComboModel(nullptr, tr("Selected keymap slot")),
m_firstKeyModel(0, 0, NumKeys - 1, nullptr, tr("First key")),
diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp
index 72621a173ef..6dc8cde4d92 100644
--- a/src/gui/MixerView.cpp
+++ b/src/gui/MixerView.cpp
@@ -56,7 +56,7 @@ namespace lmms::gui
MixerView::MixerView(Mixer* mixer) :
- DetachableWidget{},
+ QWidget{},
ModelView{nullptr, this},
SerializingObjectHook{},
m_mixer(mixer)
@@ -172,10 +172,6 @@ MixerView::MixerView(Mixer* mixer) :
// timer for updating faders
connect(mainWindow, &MainWindow::periodicUpdate, this, &MixerView::updateFaders);
- // adjust window size
- layout()->invalidate();
- resize(sizeHint());
- setFixedHeight(height());
layout()->setSizeConstraint(QLayout::SetMinimumSize);
// add ourself to workspace
diff --git a/src/gui/ProjectNotes.cpp b/src/gui/ProjectNotes.cpp
index b1d8b58b35d..cff5bc81c76 100644
--- a/src/gui/ProjectNotes.cpp
+++ b/src/gui/ProjectNotes.cpp
@@ -49,7 +49,7 @@ namespace lmms::gui
ProjectNotes::ProjectNotes()
- : DetachableWindow{getGUI()->mainWindow()->workspace()}
+ : QMainWindow{getGUI()->mainWindow()->workspace()}
{
m_edit = new QTextEdit( this );
m_edit->setAutoFillBackground( true );
diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp
index e21fbd5eeb5..6651739512c 100644
--- a/src/gui/SampleTrackWindow.cpp
+++ b/src/gui/SampleTrackWindow.cpp
@@ -48,7 +48,7 @@ namespace lmms::gui
SampleTrackWindow::SampleTrackWindow(SampleTrackView* stv)
- : DetachableWidget{}
+ : QWidget{}
, ModelView{nullptr, this}
, m_track{stv->model()}
, m_stv{stv}
@@ -169,27 +169,14 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView* stv)
QMdiSubWindow * subWin = getGUI()->mainWindow()->addWindowedWidget(this);
Qt::WindowFlags flags = subWin->windowFlags();
- flags |= Qt::MSWindowsFixedSizeDialogHint;
+ flags |= Qt::MSWindowsFixedSizeDialogHint; // resizing is disabled regardless, this makes SubWindow hide related actions
flags &= ~Qt::WindowMaximizeButtonHint;
subWin->setWindowFlags(flags);
- // adjust window size
- layout()->invalidate();
- resize(sizeHint());
- if (parentWidget())
- {
- parentWidget()->resize(parentWidget()->sizeHint());
- }
- setFixedSize(size());
+ // better than `setFixedSize` because it still responds to layout changes
+ layout()->setSizeConstraint(QLayout::SetFixedSize);
- // Hide the Size and Maximize options from the system menu
- // since the dialog size is fixed.
- QMenu * systemMenu = subWin->systemMenu();
- systemMenu->actions().at(2)->setVisible(false); // Size
- systemMenu->actions().at(4)->setVisible(false); // Maximize
-
- subWin->setWindowIcon(embed::getIconPixmap("sample_track"));
- subWin->setFixedSize(subWin->size());
+ setWindowIcon(embed::getIconPixmap("sample_track"));
subWin->hide();
}
@@ -266,8 +253,6 @@ void SampleTrackWindow::toggleVisibility(bool on)
void SampleTrackWindow::closeEvent(QCloseEvent* ce)
{
- DetachableWidget::closeEvent(ce);
-
m_stv->setFocus();
m_stv->m_tlb->setChecked(false);
}
diff --git a/src/gui/SubWindow.cpp b/src/gui/SubWindow.cpp
index a3b3ae78041..9a24be84960 100644
--- a/src/gui/SubWindow.cpp
+++ b/src/gui/SubWindow.cpp
@@ -28,6 +28,8 @@
#include "SubWindow.h"
+#include
+
#include
#include
#include
@@ -41,17 +43,19 @@
#include
#include
+#include "ConfigManager.h"
#include "embed.h"
namespace lmms::gui
{
-SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags windowFlags) :
- QMdiSubWindow(parent, windowFlags),
- m_buttonSize(17, 17),
- m_titleBarHeight(titleBarHeight()),
- m_hasFocus(false)
+SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags windowFlags)
+ : QMdiSubWindow{parent, windowFlags}
+ , m_buttonSize{17, 17}
+ , m_titleBarHeight{titleBarHeight()}
+ , m_hasFocus{false}
+ , m_isDetachable{true}
{
// initialize the tracked geometry to whatever Qt thinks the normal geometry currently is.
// this should always work, since QMdiSubWindows will not start as maximized
@@ -81,7 +85,7 @@ SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags windowFlags) :
m_restoreBtn = createButton("restore", tr("Restore"));
connect(m_restoreBtn, &QPushButton::clicked, this, &QWidget::showNormal);
- m_detachBtn = createButton("window", tr("Detach"));
+ m_detachBtn = createButton("detach", tr("Detach"));
connect(m_detachBtn, &QPushButton::clicked, this, &SubWindow::detach);
// QLabel for the window title and the shadow effect
@@ -158,21 +162,49 @@ void SubWindow::changeEvent( QEvent *event )
{
adjustTitleBar();
}
-
}
+
+
+
void SubWindow::setVisible(bool visible)
{
- if (isDetached() && visible) // avoid showing titlebar here
+ if (isDetached() || visible) { widget()->setVisible(visible); }
+ if (!isDetached()) { QMdiSubWindow::setVisible(visible); }
+}
+
+
+
+
+void SubWindow::showEvent(QShowEvent* e)
+{
+ if (ConfigManager::inst()->value("ui", "detachbehavior", "show") == "detached") { detach(); }
+ if (isDetached())
{
- widget()->show();
- // raise the detached window in case it was minimized
widget()->setWindowState((widget()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
- return;
}
- QMdiSubWindow::setVisible(visible);
}
+
+
+
+bool SubWindow::isDetachable() const
+{
+ return m_isDetachable;
+}
+
+
+
+
+void SubWindow::setDetachable(bool on)
+{
+ m_isDetachable = on;
+
+}
+
+
+
+
bool SubWindow::isDetached() const
{
return widget()->windowFlags().testFlag(Qt::Window);
@@ -181,6 +213,15 @@ bool SubWindow::isDetached() const
+void SubWindow::setDetached(bool on)
+{
+ if (on) { detach(); }
+ else { attach(); }
+}
+
+
+
+
/**
* @brief SubWindow::elideText
*
@@ -261,65 +302,70 @@ void SubWindow::setBorderColor( const QColor &c )
m_borderColor = c;
}
-void SubWindow::detach()
-{
-#if QT_VERSION < 0x50C00
- // Workaround for a bug in Qt versions below 5.12,
- // where argument-dependent-lookup fails for QFlags operators
- // declared inside a namepsace.
- // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h
- // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348
- using ::operator|;
-#endif
- if (isDetached()) { return; }
+
+void SubWindow::detach()
+{
+ if (!isDetachable() || isDetached()) { return; }
const auto pos = mapToGlobal(widget()->pos());
+ const bool shown = isVisible();
auto flags = windowFlags();
flags |= Qt::Window;
- flags &= ~Qt::Widget;
- widget()->setWindowFlags(flags);
- widget()->show();
+ flags &= ~Qt::SubWindow;
+ flags |= Qt::WindowMinimizeButtonHint;
+
hide();
+ widget()->setWindowFlags(flags);
- widget()->windowHandle()->setPosition(pos);
+ if (shown) { widget()->show(); }
+
+ widget()->move(pos);
}
void SubWindow::attach()
{
-#if QT_VERSION < 0x50C00
- // Workaround for a bug in Qt versions below 5.12,
- // where argument-dependent-lookup fails for QFlags operators
- // declared inside a namepsace.
- // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h
- // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348
+ if (!isDetached()) { return; }
- using ::operator|;
-#endif
+ const bool shown = widget()->isVisible();
- if (!isDetached()) { return; }
+ auto frame = widget()->geometry();
+ frame.moveTo(mdiArea()->mapFromGlobal(frame.topLeft()));
+ frame += decorationMargins();
- auto frame = widget()->windowHandle()->frameGeometry();
+ // Make sure the window fully fits on screen
+ frame.setSize({std::min(frame.width(), mdiArea()->width()),
+ std::min(frame.height(), mdiArea()->height())});
+
+ frame.moveTo(std::clamp(frame.left(),
+ 0,
+ mdiArea()->rect().width() - frame.width()),
+ std::clamp(frame.top(),
+ 0,
+ mdiArea()->rect().height() - frame.height()));
auto flags = windowFlags();
flags &= ~Qt::Window;
- flags |= Qt::Widget;
+ flags |= Qt::SubWindow;
+ flags &= ~Qt::WindowMinimizeButtonHint;
widget()->setWindowFlags(flags);
- widget()->show();
- show();
-
- // Delay moving & resizing using event queue. Ensures that this widget is
- // visible first, so that resizing works.
- QObject obj;
- connect(&obj, &QObject::destroyed, this, [this, frame]() {
- if (QGuiApplication::platformName() != "wayland")
- { // Workaround for wayland reporting on-screen pos as 0-0. If ever solved on wayland side, this check is safe to remove.
- move(mdiArea()->mapFromGlobal(frame.topLeft()));
- }
- resize(frame.size());
- }, Qt::QueuedConnection);
+
+ if (shown)
+ {
+ widget()->show();
+ show();
+ }
+
+ if (QGuiApplication::platformName() == "wayland")
+ {
+ resize(frame.size()); // Workaround for wayland reporting position as 0-0, see https://doc.qt.io/qt-6.9/application-windows.html#wayland-peculiarities
+ }
+ else
+ {
+ setGeometry(frame);
+ }
}
@@ -342,6 +388,19 @@ int SubWindow::frameWidth() const
}
+
+
+QMargins SubWindow::decorationMargins() const
+{
+ return QMargins(frameWidth(), // left
+ titleBarHeight(), // top
+ frameWidth(), // right
+ frameWidth()); // bottom
+}
+
+
+
+
void SubWindow::updateTitleBar()
{
adjustTitleBar();
@@ -402,6 +461,7 @@ void SubWindow::adjustTitleBar()
// button adjustments
m_maximizeBtn->hide();
m_restoreBtn->hide();
+ m_detachBtn->hide();
m_closeBtn->show();
const int rightSpace = 3;
@@ -442,8 +502,12 @@ void SubWindow::adjustTitleBar()
buttonPos -= buttonStep;
}
- m_detachBtn->move(buttonPos);
- m_detachBtn->show();
+ if (isDetachable())
+ {
+ m_detachBtn->move(buttonPos);
+ m_detachBtn->show();
+ buttonBarWidth = buttonBarWidth + m_buttonSize.width() + buttonGap;
+ }
if( widget() )
{
@@ -515,6 +579,18 @@ void SubWindow::resizeEvent( QResizeEvent * event )
}
}
+
+
+
+/**
+ * @brief SubWindow::eventFilter
+ *
+ * Override of QMdiSubWindow's event filter.
+ * This is not how regular eventFilters work, it is never installed explicitly.
+ * Instead, it is installed by Qt and conveniently installs itself
+ * onto the child widget. Despite relying on internal implementation details,
+ * as of writing this it seems to be the best way to do so as soon as the widget is set.
+ */
bool SubWindow::eventFilter(QObject* obj, QEvent* event)
{
if (obj != static_cast(widget()))
@@ -524,11 +600,41 @@ bool SubWindow::eventFilter(QObject* obj, QEvent* event)
switch (event->type())
{
- case QEvent::WindowStateChange:
- event->accept();
- return true;
- default:
- return QMdiSubWindow::eventFilter(obj, event);
+ case QEvent::WindowStateChange:
+ event->accept();
+ return true;
+
+ case QEvent::Close:
+ if (isDetached())
+ {
+ QString detachBehavior = ConfigManager::inst()->value("ui", "detachbehavior", "show");
+ if (detachBehavior == "show")
+ {
+ attach();
+ event->ignore();
+ return true;
+ }
+ else if (detachBehavior == "hide")
+ {
+ attach();
+ hide();
+ event->ignore();
+ return QMdiSubWindow::eventFilter(obj, event);
+ }
+ else if (detachBehavior == "detached")
+ {
+ event->accept();
+ return QMdiSubWindow::eventFilter(obj, event);
+ }
+ }
+ else
+ {
+ hide();
+ }
+ return QMdiSubWindow::eventFilter(obj, event);
+
+ default:
+ return QMdiSubWindow::eventFilter(obj, event);
}
}
diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp
index 84f57aaf0c2..1b7f102947a 100644
--- a/src/gui/editors/Editor.cpp
+++ b/src/gui/editors/Editor.cpp
@@ -89,7 +89,7 @@ void Editor::toggleMaximize()
}
Editor::Editor(bool record, bool stepRecord) :
- DetachableWindow(),
+ QMainWindow(),
m_toolBar(new DropToolBar(this)),
m_playAction(nullptr),
m_recordAction(nullptr),
diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp
index ecb926ee747..47e2b41caff 100644
--- a/src/gui/instrument/InstrumentTrackWindow.cpp
+++ b/src/gui/instrument/InstrumentTrackWindow.cpp
@@ -304,7 +304,6 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
// we can reuse this method.
updateSubWindow();
- subWin->setWindowIcon(embed::getIconPixmap("instrument_track"));
subWin->hide();
}
@@ -531,22 +530,6 @@ void InstrumentTrackWindow::toggleVisibility( bool on )
void InstrumentTrackWindow::closeEvent( QCloseEvent* event )
{
- // TODO: When is this event used?
- if (windowFlags().testFlag(Qt::Window))
- {
- event->accept();
- }
- else if (getGUI()->mainWindow()->workspace())
- {
- parentWidget()->hide();
- event->ignore();
- }
- else
- {
- hide();
- event->ignore();
- }
-
m_itv->setFocus();
m_itv->m_tlb->setChecked(false);
}
@@ -618,6 +601,7 @@ void InstrumentTrackWindow::dropEvent( QDropEvent* event )
event->accept();
setFocus();
}
+ updateSubWindow();
}
@@ -677,13 +661,34 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d)
// avoid reloading the window if there is only one instrument, as that will just change the active tab
if (idxOfNext != idxOfMe)
{
+ SubWindow* source_subwin = static_cast(parentWidget());
+ SubWindow* target_subwin = static_cast(newView->getInstrumentTrackWindow()->parentWidget());
+ QWidget* source_widget;
+ QWidget* target_widget;
+
+ // set widgets we move and get our position from
+ if (source_subwin->isDetached())
+ {
+ source_widget = this;
+ target_widget = newView->getInstrumentTrackWindow();
+ }
+ else
+ {
+ source_widget = parentWidget();
+ target_widget = newView->getInstrumentTrackWindow()->parentWidget();
+ }
+
// save current window pos and then hide the window by unchecking its button in the track list
- QPoint curPos = parentWidget()->pos();
+ QPoint curPos = source_widget->pos();
m_itv->m_tlb->setChecked(false);
// enable the new window by checking its track list button & moving it to where our window just was
newView->m_tlb->setChecked(true);
- newView->getInstrumentTrackWindow()->parentWidget()->move(curPos); // TODO
+
+ // sync detached state with current widget like we do with position
+ target_subwin->setDetached(source_subwin->isDetached());
+
+ target_widget->move(curPos);
// scroll the SongEditor/PatternEditor to make sure the new trackview label is visible
bringToFront->trackContainerView()->scrollToTrackView(bringToFront);
@@ -741,25 +746,18 @@ void InstrumentTrackWindow::updateSubWindow()
auto subWindow = findSubWindowInParents();
if (subWindow && m_instrumentView)
{
- Qt::WindowFlags flags = subWindow->windowFlags();
-
const auto instrumentViewResizable = m_instrumentView->isResizable();
if (instrumentViewResizable)
{
// TODO As of writing SlicerT is the only resizable instrument. Is this code specific to SlicerT?
+ // TODO Expand extraSpace in terms of specific widget sizes or replace with QLayout::setSizeConstraint.
const auto extraSpace = QSize(12, 208);
setMaximumSize(m_instrumentView->maximumSize() + extraSpace);
setMinimumSize(m_instrumentView->minimumSize() + extraSpace);
-
- flags &= ~Qt::MSWindowsFixedSizeDialogHint;
- flags |= Qt::WindowMaximizeButtonHint;
}
else
{
- flags |= Qt::MSWindowsFixedSizeDialogHint;
- flags &= ~Qt::WindowMaximizeButtonHint;
-
setFixedSize(sizeHint());
// The sub window might be reused from an instrument that was maximized. Show the sub window
@@ -770,7 +768,7 @@ void InstrumentTrackWindow::updateSubWindow()
}
}
- subWindow->setWindowFlags(flags);
+ subWindow->setWindowFlag(Qt::WindowMaximizeButtonHint, instrumentViewResizable);
// TODO This is only needed if the sub window is implemented with LMMS' own SubWindow class.
// If an QMdiSubWindow is used everything works automatically. It seems that SubWindow is
@@ -780,14 +778,6 @@ void InstrumentTrackWindow::updateSubWindow()
{
subWin->updateTitleBar();
}
- else
- {
- // TODO: Can this be removed?
- // Show or hide the Size and Maximize options from the system menu depending on whether the view is resizable or not
- QMenu* systemMenu = subWindow->systemMenu();
- systemMenu->actions().at(2)->setVisible(instrumentViewResizable); // Size
- systemMenu->actions().at(4)->setVisible(instrumentViewResizable); // Maximize
- }
}
}
diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp
index 4ea0d4dddf9..d3b92ede4e2 100644
--- a/src/gui/modals/SetupDialog.cpp
+++ b/src/gui/modals/SetupDialog.cpp
@@ -118,6 +118,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
"app", "disablebackup").toInt()),
m_openLastProject(ConfigManager::inst()->value(
"app", "openlastproject").toInt()),
+ m_detachBehavior{ConfigManager::inst()->value("ui", "detachbehavior", "show")},
m_loopMarkerMode{ConfigManager::inst()->value("app", "loopmarkermode", "dual")},
m_lang(ConfigManager::inst()->value(
"app", "language")),
@@ -255,6 +256,19 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
addCheckBox(tr("Show warning when deleting a mixer channel that is in use"), guiGroupBox, guiGroupLayout,
m_mixerChannelDeletionWarning, SLOT(toggleMixerChannelDeletionWarning(bool)), false);
+ m_detachBehaviorComboBox = new QComboBox{guiGroupBox};
+
+ m_detachBehaviorComboBox->addItem(tr("Attach and show when closed"), "show");
+ m_detachBehaviorComboBox->addItem(tr("Attach and hide when closed"), "hide");
+ m_detachBehaviorComboBox->addItem(tr("Always detached"), "detached");
+
+ m_detachBehaviorComboBox->setCurrentIndex(m_detachBehaviorComboBox->findData(m_detachBehavior));
+ connect(m_detachBehaviorComboBox, qOverload(&QComboBox::currentIndexChanged),
+ this, &SetupDialog::detachBehaviorChanged);
+
+ guiGroupLayout->addWidget(new QLabel{tr("Detached window behavior"), guiGroupBox});
+ guiGroupLayout->addWidget(m_detachBehaviorComboBox);
+
m_loopMarkerComboBox = new QComboBox{guiGroupBox};
m_loopMarkerComboBox->addItem(tr("Dual-button"), "dual");
@@ -981,6 +995,7 @@ void SetupDialog::accept()
QString::number(!m_disableBackup));
ConfigManager::inst()->setValue("app", "openlastproject",
QString::number(m_openLastProject));
+ ConfigManager::inst()->setValue("ui", "detachbehavior", m_detachBehavior);
ConfigManager::inst()->setValue("app", "loopmarkermode", m_loopMarkerMode);
ConfigManager::inst()->setValue("app", "language", m_lang);
ConfigManager::inst()->setValue("ui", "saveinterval",
@@ -1120,6 +1135,12 @@ void SetupDialog::toggleOpenLastProject(bool enabled)
}
+void SetupDialog::detachBehaviorChanged()
+{
+ m_detachBehavior = m_detachBehaviorComboBox->currentData().toString();
+}
+
+
void SetupDialog::loopMarkerModeChanged()
{
m_loopMarkerMode = m_loopMarkerComboBox->currentData().toString();
diff --git a/src/gui/tracks/InstrumentTrackView.cpp b/src/gui/tracks/InstrumentTrackView.cpp
index c0cba7eefe2..40270db4029 100644
--- a/src/gui/tracks/InstrumentTrackView.cpp
+++ b/src/gui/tracks/InstrumentTrackView.cpp
@@ -205,20 +205,16 @@ void InstrumentTrackView::toggleMidiCCRack()
-InstrumentTrackWindow * InstrumentTrackView::topLevelInstrumentTrackWindow()
+InstrumentTrackWindow* InstrumentTrackView::topLevelInstrumentTrackWindow()
{
- InstrumentTrackWindow * w = nullptr;
- for( const QMdiSubWindow * sw :
- getGUI()->mainWindow()->workspace()->subWindowList(
- QMdiArea::ActivationHistoryOrder ) )
- {
- if( sw->isVisible() && sw->widget()->inherits( "lmms::gui::InstrumentTrackWindow" ) )
- {
- w = qobject_cast( sw->widget() );
- }
- }
-
- return w;
+ auto subWindowList = getGUI()->mainWindow()->workspace()->subWindowList(QMdiArea::ActivationHistoryOrder);
+ auto winIt = std::find_if(subWindowList.rbegin(), subWindowList.rend(),
+ [](QMdiSubWindow* sw){return sw->widget()
+ && sw->widget()->isVisible()
+ && sw->widget()->inherits("lmms::gui::InstrumentTrackWindow");});
+ return winIt != subWindowList.rend() && (**winIt).widget()
+ ? qobject_cast((**winIt).widget())
+ : nullptr;
}
diff --git a/src/gui/widgets/SimpleTextFloat.cpp b/src/gui/widgets/SimpleTextFloat.cpp
index 282fe6500d3..89a45c7f78b 100644
--- a/src/gui/widgets/SimpleTextFloat.cpp
+++ b/src/gui/widgets/SimpleTextFloat.cpp
@@ -25,16 +25,25 @@
#include "SimpleTextFloat.h"
#include
-#include
+#include
+#include
+
+#include "GuiApplication.h"
+#include "MainWindow.h"
namespace lmms::gui
{
SimpleTextFloat::SimpleTextFloat() :
- QWidget()
+ QWidget(getGUI()->mainWindow(), Qt::ToolTip)
{
- m_text = QString();
+ QHBoxLayout * layout = new QHBoxLayout(this);
+ layout->setMargin(3);
+ setLayout(layout);
+
+ m_textLabel = new QLabel(this);
+ layout->addWidget(m_textLabel);
m_showTimer = new QTimer(this);
m_showTimer->setSingleShot(true);
@@ -47,7 +56,7 @@ SimpleTextFloat::SimpleTextFloat() :
void SimpleTextFloat::setText(const QString & text)
{
- m_text = text;
+ m_textLabel->setText(text);
}
void SimpleTextFloat::showWithDelay(int msecBeforeDisplay, int msecDisplayTime)
@@ -67,16 +76,11 @@ void SimpleTextFloat::showWithDelay(int msecBeforeDisplay, int msecDisplayTime)
}
}
-void SimpleTextFloat::show()
-{
- QToolTip::showText(mapToGlobal(QPoint(0, 0)), m_text);
-}
-
void SimpleTextFloat::hide()
{
m_showTimer->stop();
m_hideTimer->stop();
- QToolTip::hideText();
+ QWidget::hide();
}
} // namespace lmms::gui