From 7cc917cc06e44b09378d4e4e166f2dbf148a1e27 Mon Sep 17 00:00:00 2001 From: Colin Wallace Date: Sun, 14 Jun 2015 22:47:16 +0000 Subject: [PATCH 1/3] Added SubWindow class to compensate for incorrect behavior of normalGeometry() method on X11 --- include/SubWindow.h | 56 +++++++++++++++++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + src/gui/SubWindow.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 include/SubWindow.h create mode 100644 src/gui/SubWindow.cpp diff --git a/include/SubWindow.h b/include/SubWindow.h new file mode 100644 index 000000000..75e058584 --- /dev/null +++ b/include/SubWindow.h @@ -0,0 +1,56 @@ +/* + * SubWindow.h - Implementation of QMdiSubWindow that correctly tracks + * the geometry that windows should be restored to. + * Workaround for https://bugreports.qt.io/browse/QTBUG-256 + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://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 SUBWINDOW_H +#define SUBWINDOW_H + + +#include +#include + + +class QMoveEvent; +class QResizeEvent; +class QWidget; + + +class SubWindow : public QMdiSubWindow +{ + Q_OBJECT +public: + SubWindow(QWidget *parent=NULL, Qt::WindowFlags windowFlags=0); + // same as QWidet::normalGeometry, but works properly under X11 (see https://bugreports.qt.io/browse/QTBUG-256) + QRect getTrueNormalGeometry() const; +protected: + // hook the QWidget move/resize events to update the tracked geometry + virtual void moveEvent(QMoveEvent * event); + virtual void resizeEvent(QResizeEvent * event); +private: + QRect m_trackedNormalGeom; +}; + +#endif \ No newline at end of file diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a23c37f81..4cbf4013b 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -24,6 +24,7 @@ SET(LMMS_SRCS gui/PluginBrowser.cpp gui/SetupDialog.cpp gui/StringPairDrag.cpp + gui/SubWindow.cpp gui/TimeLineWidget.cpp gui/ToolPluginView.cpp gui/TrackContainerView.cpp diff --git a/src/gui/SubWindow.cpp b/src/gui/SubWindow.cpp new file mode 100644 index 000000000..cc2996253 --- /dev/null +++ b/src/gui/SubWindow.cpp @@ -0,0 +1,67 @@ +/* + * SubWindow.cpp - Implementation of QMdiSubWindow that correctly tracks + * the geometry that windows should be restored to. + * Workaround for https://bugreports.qt.io/browse/QTBUG-256 + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://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 "SubWindow.h" + +#include +#include +#include + + +SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags windowFlags) + : QMdiSubWindow(parent, windowFlags) +{ + // initialize the tracked geometry to whatever Qt thinks the normal geometry currently is. + // this should always work, since QMdiSubWindows will not start as maximized + m_trackedNormalGeom = normalGeometry(); +} + +QRect SubWindow::getTrueNormalGeometry() const +{ + return m_trackedNormalGeom; +} + +void SubWindow::moveEvent(QMoveEvent * event) +{ + QMdiSubWindow::moveEvent(event); + // if the window was moved and ISN'T minimized/maximized/fullscreen, + // then save the current position + if (!isMaximized() && !isMinimized() && !isFullScreen()) + { + m_trackedNormalGeom.moveTopLeft(event->pos()); + } +} + +void SubWindow::resizeEvent(QResizeEvent * event) +{ + QMdiSubWindow::resizeEvent(event); + // if the window was resized and ISN'T minimized/maximized/fullscreen, + // then save the current size + if (!isMaximized() && !isMinimized() && !isFullScreen()) + { + m_trackedNormalGeom.setSize(event->size()); + } +} \ No newline at end of file From 908591ba7dc186e942305e76463029643aa21593 Mon Sep 17 00:00:00 2001 From: Colin Wallace Date: Mon, 15 Jun 2015 03:26:07 +0000 Subject: [PATCH 2/3] Have MainWindow and others make use of the new SubWindow class --- include/MainWindow.h | 5 ++ plugins/VstEffect/VstEffectControls.cpp | 2 +- plugins/vestige/vestige.cpp | 2 +- src/gui/FxMixerView.cpp | 3 +- src/gui/MainWindow.cpp | 99 +++++++++++++++---------- src/gui/ToolPluginView.cpp | 2 +- src/gui/widgets/ControllerRackView.cpp | 3 +- src/gui/widgets/ControllerView.cpp | 3 +- src/gui/widgets/EffectView.cpp | 2 +- src/gui/widgets/ProjectNotes.cpp | 2 +- src/gui/widgets/TempoSyncKnob.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 14 ++-- src/tracks/SampleTrack.cpp | 2 +- 13 files changed, 82 insertions(+), 59 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 8ab93d34d..6b75a86a5 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -30,6 +30,8 @@ #include #include +#include "SubWindow.h" + class QAction; class QDomElement; class QGridLayout; @@ -57,6 +59,9 @@ public: int addWidgetToToolBar( QWidget * _w, int _row = -1, int _col = -1 ); void addSpacingToToolBar( int _size ); + // wrap the widget with a window decoration and add it to the workspace + SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags=0); + /// /// \brief Asks whether changes made to the project are to be saved. diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index 8f2594c6a..da223d3a3 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -310,7 +310,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi->m_scrollArea = new QScrollArea( widget ); l = new QGridLayout( widget ); - m_vi->m_subWindow = gui->mainWindow()->workspace()->addSubWindow(new QMdiSubWindow, Qt::SubWindow | + m_vi->m_subWindow = gui->mainWindow()->addWindowedWidget(NULL, Qt::SubWindow | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 4c6870c69..5ce5f468b 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -874,7 +874,7 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume widget = new QWidget(this); l = new QGridLayout( this ); - m_vi->m_subWindow = gui->mainWindow()->workspace()->addSubWindow(new QMdiSubWindow, Qt::SubWindow | + m_vi->m_subWindow = gui->mainWindow()->addWindowedWidget(NULL, Qt::SubWindow | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ); m_vi->m_subWindow->setFixedWidth( 960 ); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 2a8271048..72a749e7f 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -147,8 +147,7 @@ FxMixerView::FxMixerView() : // add ourself to workspace - QMdiSubWindow * subWin = - gui->mainWindow()->workspace()->addSubWindow( this ); + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0cccf7bdb..ab434970b 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -24,48 +24,49 @@ #include "MainWindow.h" -#include -#include #include #include #include +#include #include #include #include #include #include #include +#include #include -#include "lmmsversion.h" -#include "GuiApplication.h" +#include "AboutDialog.h" +#include "AudioDummy.h" +#include "AutomationEditor.h" #include "BBEditor.h" -#include "SongEditor.h" -#include "Song.h" -#include "PianoRoll.h" +#include "ConfigManager.h" +#include "ControllerRackView.h" #include "embed.h" #include "Engine.h" -#include "FxMixerView.h" -#include "InstrumentTrack.h" -#include "PianoView.h" -#include "AboutDialog.h" -#include "ControllerRackView.h" #include "FileBrowser.h" -#include "PluginBrowser.h" -#include "SideBar.h" -#include "ConfigManager.h" +#include "FileDialog.h" +#include "FxMixerView.h" +#include "GuiApplication.h" +#include "InstrumentTrack.h" +#include "lmmsversion.h" #include "Mixer.h" +#include "PianoRoll.h" +#include "PianoView.h" +#include "PluginBrowser.h" #include "PluginFactory.h" #include "PluginView.h" +#include "ProjectJournal.h" #include "ProjectNotes.h" #include "SetupDialog.h" -#include "AudioDummy.h" -#include "ToolPlugin.h" -#include "ToolButton.h" -#include "ProjectJournal.h" -#include "AutomationEditor.h" +#include "SideBar.h" +#include "Song.h" +#include "SongEditor.h" +#include "SubWindow.h" #include "templates.h" -#include "FileDialog.h" +#include "ToolButton.h" +#include "ToolPlugin.h" #include "VersionedSaveDialog.h" @@ -560,7 +561,7 @@ void MainWindow::finalize() gui->songEditor() }) { - QMdiSubWindow* window = workspace()->addSubWindow(widget); + QMdiSubWindow* window = addWindowedWidget(widget); window->setWindowIcon(widget->windowIcon()); window->setAttribute(Qt::WA_DeleteOnClose, false); window->resize(widget->sizeHint()); @@ -607,7 +608,15 @@ void MainWindow::addSpacingToToolBar( int _size ) 7, _size ); } - +SubWindow* MainWindow::addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags) +{ + // wrap the widget in our own *custom* window that patches some errors in QMdiSubWindow + SubWindow *win = new SubWindow(m_workspace->viewport(), windowFlags); + win->setAttribute(Qt::WA_DeleteOnClose); + win->setWidget(w); + m_workspace->addSubWindow(win); + return win; +} void MainWindow::resetWindowTitle() @@ -680,26 +689,29 @@ void MainWindow::clearKeyModifiers() void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de ) { + + qDebug() << "this " << _w->metaObject()->className(); + // If our widget is the main content of a window (e.g. piano roll, FxMixer, etc), + // we really care about the position of the *window* - not the position of the widget within its window if( _w->parentWidget() != NULL && _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 ) + SubWindow *asSubWindow = qobject_cast(_w); + QRect normalGeom = asSubWindow != nullptr ? asSubWindow->getTrueNormalGeometry() : _w->normalGeometry(); + _de.setAttribute( "visible", _w->isVisible() ); _de.setAttribute( "minimized", _w->isMinimized() ); _de.setAttribute( "maximized", _w->isMaximized() ); - bool maxed = _w->isMaximized(); - bool mined = _w->isMinimized(); - if( mined || maxed ) { _w->showNormal(); } - - _de.setAttribute( "x", _w->x() ); - _de.setAttribute( "y", _w->y() ); - _de.setAttribute( "width", _w->width() ); - _de.setAttribute( "height", _w->height() ); - if( maxed ) { _w->showMaximized(); } - if( mined ) { _w->showMinimized(); } + _de.setAttribute( "x", normalGeom.x() ); + _de.setAttribute( "y", normalGeom.y() ); + _de.setAttribute( "width", normalGeom.width() ); + _de.setAttribute( "height", normalGeom.height() ); } @@ -713,21 +725,30 @@ void MainWindow::restoreWidgetState( QWidget * _w, const QDomElement & _de ) qMax( 100, _de.attribute( "height" ).toInt() ) ); if( _de.hasAttribute( "visible" ) && !r.isNull() ) { + // If our widget is the main content of a window (e.g. piano roll, FxMixer, etc), + // we really care about the position of the *window* - not the position of the widget within its window if ( _w->parentWidget() != NULL && _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) ); _w->resize( r.size() ); _w->move( r.topLeft() ); + + // 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() ); - _w->setWindowState( _de.attribute( "minimized" ).toInt() ? - ( _w->windowState() | Qt::WindowMinimized ) : - ( _w->windowState() & ~Qt::WindowMinimized ) ); - _w->setWindowState( _de.attribute( "maximized" ).toInt() ? - ( _w->windowState() | Qt::WindowMaximized ) : - ( _w->windowState() & ~Qt::WindowMaximized ) ); } } diff --git a/src/gui/ToolPluginView.cpp b/src/gui/ToolPluginView.cpp index 8426c146e..ca01410c8 100644 --- a/src/gui/ToolPluginView.cpp +++ b/src/gui/ToolPluginView.cpp @@ -38,7 +38,7 @@ ToolPluginView::ToolPluginView( ToolPlugin * _toolPlugin ) : PluginView( _toolPlugin, NULL ) { - gui->mainWindow()->workspace()->addSubWindow( this ); + gui->mainWindow()->addWindowedWidget( this ); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); setWindowTitle( _toolPlugin->displayName() ); diff --git a/src/gui/widgets/ControllerRackView.cpp b/src/gui/widgets/ControllerRackView.cpp index 72f0b8eed..182571093 100644 --- a/src/gui/widgets/ControllerRackView.cpp +++ b/src/gui/widgets/ControllerRackView.cpp @@ -75,8 +75,7 @@ ControllerRackView::ControllerRackView( ) : layout->addWidget( m_addButton ); this->setLayout( layout ); - QMdiSubWindow * subWin = - gui->mainWindow()->workspace()->addSubWindow( this ); + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); // No maximize button Qt::WindowFlags flags = subWin->windowFlags(); diff --git a/src/gui/widgets/ControllerView.cpp b/src/gui/widgets/ControllerView.cpp index 8b618a3e1..b6ed84333 100644 --- a/src/gui/widgets/ControllerView.cpp +++ b/src/gui/widgets/ControllerView.cpp @@ -65,8 +65,7 @@ ControllerView::ControllerView( Controller * _model, QWidget * _parent ) : m_controllerDlg = getController()->createDialog( gui->mainWindow()->workspace() ); - m_subWindow = gui->mainWindow()->workspace()->addSubWindow( - m_controllerDlg ); + m_subWindow = gui->mainWindow()->addWindowedWidget( m_controllerDlg ); Qt::WindowFlags flags = m_subWindow->windowFlags(); flags &= ~Qt::WindowMaximizeButtonHint; diff --git a/src/gui/widgets/EffectView.cpp b/src/gui/widgets/EffectView.cpp index 64463caed..e8effc1d7 100644 --- a/src/gui/widgets/EffectView.cpp +++ b/src/gui/widgets/EffectView.cpp @@ -110,7 +110,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : m_controlView = effect()->controls()->createView(); if( m_controlView ) { - m_subWindow = gui->mainWindow()->workspace()->addSubWindow( + m_subWindow = gui->mainWindow()->addWindowedWidget( m_controlView, Qt::SubWindow | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint ); diff --git a/src/gui/widgets/ProjectNotes.cpp b/src/gui/widgets/ProjectNotes.cpp index 75f214338..6b4afc409 100644 --- a/src/gui/widgets/ProjectNotes.cpp +++ b/src/gui/widgets/ProjectNotes.cpp @@ -71,7 +71,7 @@ ProjectNotes::ProjectNotes() : setWindowTitle( tr( "Project notes" ) ); setWindowIcon( embed::getIconPixmap( "project_notes" ) ); - gui->mainWindow()->workspace()->addSubWindow( this ); + gui->mainWindow()->addWindowedWidget( this ); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 700, 10 ); parentWidget()->resize( 400, 300 ); diff --git a/src/gui/widgets/TempoSyncKnob.cpp b/src/gui/widgets/TempoSyncKnob.cpp index 598cfc062..b9def491c 100644 --- a/src/gui/widgets/TempoSyncKnob.cpp +++ b/src/gui/widgets/TempoSyncKnob.cpp @@ -293,7 +293,7 @@ void TempoSyncKnob::showCustom() if( m_custom == NULL ) { m_custom = new MeterDialog( gui->mainWindow()->workspace() ); - gui->mainWindow()->workspace()->addSubWindow( m_custom ); + gui->mainWindow()->addWindowedWidget( m_custom ); m_custom->setWindowTitle( "Meter" ); m_custom->setModel( &model()->m_custom ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index eb6a4a2b7..734ef0584 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1423,19 +1423,19 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : setFixedWidth( INSTRUMENT_WIDTH ); resize( sizeHint() ); - QMdiSubWindow * subWin = gui->mainWindow()->workspace()->addSubWindow( this ); + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags |= Qt::MSWindowsFixedSizeDialogHint; flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); - // 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 + // 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( "instrument_track" ) ); + subWin->setWindowIcon( embed::getIconPixmap( "instrument_track" ) ); subWin->setFixedSize( subWin->size() ); subWin->hide(); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 3ee683605..7cec568ad 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -589,7 +589,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_effectRack = new EffectRackView( _t->audioPort()->effects() ); m_effectRack->setFixedSize( 240, 242 ); - m_effWindow = gui->mainWindow()->workspace()->addSubWindow( m_effectRack ); + m_effWindow = gui->mainWindow()->addWindowedWidget( m_effectRack ); m_effWindow->setAttribute( Qt::WA_DeleteOnClose, false ); m_effWindow->layout()->setSizeConstraint( QLayout::SetFixedSize ); m_effWindow->setWindowTitle( _t->name() ); From be564efb1e37c1444c86388ba807b6bcdba24a09 Mon Sep 17 00:00:00 2001 From: Colin Wallace Date: Mon, 15 Jun 2015 23:41:08 +0000 Subject: [PATCH 3/3] Add missing EXPORT directives for new SubWindow class --- include/MainWindow.h | 2 +- include/SubWindow.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 6b75a86a5..68c6dc24e 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -60,7 +60,7 @@ public: void addSpacingToToolBar( int _size ); // wrap the widget with a window decoration and add it to the workspace - SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags=0); + EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags=0); /// diff --git a/include/SubWindow.h b/include/SubWindow.h index 75e058584..821e0a762 100644 --- a/include/SubWindow.h +++ b/include/SubWindow.h @@ -32,13 +32,15 @@ #include #include +#include "export.h" + class QMoveEvent; class QResizeEvent; class QWidget; -class SubWindow : public QMdiSubWindow +class EXPORT SubWindow : public QMdiSubWindow { Q_OBJECT public: