Skip to content

Commit

Permalink
Maximize button for resizable instruments (#7514)
Browse files Browse the repository at this point in the history
* Maximize button for resizable instruments

Show the maximize button for resizable instruments.

Most other changes have the character of refactorings and code
reorganizations.

Remove the negation in the if condition for resizable instruments to
make the code better readable.

Only manipulate the system menu if the instrument is not resizable.

Add a TODO to the special code that sets a size.

* Fix rendering of maximized sub windows

In `SubWindow::paintEvent` don't paint anything if the sub window is
maximized . Otherwise some gradients are visible behind the maximized
child content.

In `SubWindow::adjustTitleBar` hide the title label and the buttons if the
sub window is maximized. Always show the title and close button if not
maximized. This is needed to reset the state correctly after
maximization.

* Add SubWindow::addTitleButton

Add the helper method `SubWindow::addTitleButton` to reduce code
repetition in the constructor.

* Only disable the minimize button

Disable the minimize button by taking the current flags and removing
the minimize button hint from them instead of giving a list which might
become incomplete in the future. So only do what we want to do.

* Remove dependency on MdiArea

Remove a dependency on the `MdiArea` when checking if the sub window is
the active one. Query its own window state to find out if it is active.

* Clear Qt::MSWindowsFixedSizeDialogHint

Clear the `Qt::MSWindowsFixedSizeDialogHint` flag for resizable
instruments (symmetric to the `else` case).

* Update the sub window title bar of exchanged instruments

Update the title bar of an instrument's sub window if the model changes, e.g. if an instrument is exchanged via drag & drop.

The main fix is to call the new method `updateSubWindowState` in `InstrumentTrackWindow::modelChanged`. It contains mostly the code that was previously executed in the constructor of `InstrumentTrackWindow`. The constructor now simply calls this method after it has put the constructed instance into a sub window.

With the current implementation the sub window needs to be explicitly triggered to update its title bar once the flags have been adjusted in `updateSubWindowState`. This is done with the new public method `SubWindow::updateTitleBar`. Please note that such an explicit update is not needed if the instrument windows are managed by a `QMdiSubWindow` instead of a `SubWindow`. This means that the implementation of `SubWindow` is still missing something that `QMdiSubWindow` does. However, debugging also showed that setting the window flags of the sub window does not seem to lead to an event that could be caught in `SubWindow::changeEvent`. This was found out by simply dumping the event types of all events that arrive in that method and exchanging an instrument.

The method `updateSubWindowState` uses the added method `findSubWindowInParents` to find the sub window it is contained in. The latter method should be considered to be moved into a templated helper class because it might be useful in other contexts as well.

## Technical details

If you want to experiment with using QMdiSubWindows then simply add the following method to `MainWindow` (right next to `addWindowedWidget`):
```
QMdiSubWindow* MainWindow::addQMdiSubWindow(QWidget *w, Qt::WindowFlags windowFlags)
{
	// wrap the widget in our own *custom* window that patches some errors in QMdiSubWindow
	auto win = new QMdiSubWindow(m_workspace->viewport(), windowFlags);
	win->setAttribute(Qt::WA_DeleteOnClose);
	win->setWidget(w);

	m_workspace->addSubWindow(win);
	return win;
}
```

Then call that method instead of `addWindowedWidget` in the constructor of `InstrumentTrackWindow`:
```
QMdiSubWindow* subWin = getGUI()->mainWindow()->addQMdiSubWindow( this );
```

You can then comment out the cast and the call of `updateTitleBar` in `updateSubWindowState` and everything will still work.

* Update the system menu

Show or hide the "Size" and "Maximize" entries in the system menu
depending on whether the instrument view is resizable or not.

* Show non-resizable instruments as normal

Show the sub windows of non-resizable instruments as normal if the sub
window is maximized because it was previously used with a resizable
instrument.

* Fix typo

* Rename updateSubWindowState

Rename `updateSubWindowState` to `updateSubWindow`.
  • Loading branch information
michaelgregorius authored Jan 1, 2025
1 parent 3fcbb4c commit 303215f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 18 deletions.
4 changes: 4 additions & 0 deletions include/InstrumentTrackWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
class QLabel;
class QLineEdit;
class QWidget;
class QMdiSubWindow;

namespace lmms
{
Expand Down Expand Up @@ -134,6 +135,9 @@ protected slots:
//! required to keep the old look when using a variable sized tab widget
void adjustTabSize(QWidget *w);

QMdiSubWindow* findSubWindowInParents();
void updateSubWindow();

InstrumentTrack * m_track;
InstrumentTrackView * m_itv;

Expand Down
4 changes: 4 additions & 0 deletions include/SubWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class LMMS_EXPORT SubWindow : public QMdiSubWindow
int titleBarHeight() const;
int frameWidth() const;

// TODO Needed to update the title bar when replacing instruments.
// Update works automatically if QMdiSubWindows are used.
void updateTitleBar();

protected:
// hook the QWidget move/resize events to update the tracked geometry
void moveEvent( QMoveEvent * event ) override;
Expand Down
4 changes: 4 additions & 0 deletions src/gui/SubWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ int SubWindow::frameWidth() const
}


void SubWindow::updateTitleBar()
{
adjustTitleBar();
}


/**
Expand Down
94 changes: 76 additions & 18 deletions src/gui/instrument/InstrumentTrackWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
#include "MainWindow.h"
#include "PianoView.h"
#include "PluginFactory.h"
#include "PluginView.h"
#include "Song.h"
#include "StringPairDrag.h"
#include "SubWindow.h"
Expand Down Expand Up @@ -284,25 +283,12 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
updateInstrumentView();

QMdiSubWindow* subWin = getGUI()->mainWindow()->addWindowedWidget( this );
Qt::WindowFlags flags = subWin->windowFlags();
if (!m_instrumentView->isResizable()) {
flags |= Qt::MSWindowsFixedSizeDialogHint;
// any better way than this?
} else {
subWin->setMaximumSize(m_instrumentView->maximumHeight() + 12, m_instrumentView->maximumWidth() + 208);
subWin->setMinimumSize( m_instrumentView->minimumWidth() + 12, m_instrumentView->minimumHeight() + 208);
}
flags &= ~Qt::WindowMaximizeButtonHint;
subWin->setWindowFlags( flags );

// The previous call should have given us a sub window parent. Therefore
// we can reuse this method.
updateSubWindow();

// 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->hide();
}

Expand Down Expand Up @@ -406,6 +392,8 @@ void InstrumentTrackWindow::modelChanged()
m_tuningView->keymapCombo()->setModel(m_track->m_microtuner.keymapModel());
m_tuningView->rangeImportCheckbox()->setModel(m_track->m_microtuner.keyRangeImportModel());
updateName();

updateSubWindow();
}


Expand Down Expand Up @@ -710,5 +698,75 @@ void InstrumentTrackWindow::adjustTabSize(QWidget *w)
w->update();
}

QMdiSubWindow* InstrumentTrackWindow::findSubWindowInParents()
{
// TODO Move to helper? Does not seem to be provided by Qt.
auto p = parentWidget();

while (p != nullptr)
{
auto mdiSubWindow = dynamic_cast<QMdiSubWindow*>(p);
if (mdiSubWindow)
{
return mdiSubWindow;
}
else
{
p = p->parentWidget();
}
}

return nullptr;
}

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?
const auto extraSpace = QSize(12, 208);
subWindow->setMaximumSize(m_instrumentView->maximumSize() + extraSpace);
subWindow->setMinimumSize(m_instrumentView->minimumSize() + extraSpace);

flags &= ~Qt::MSWindowsFixedSizeDialogHint;
flags |= Qt::WindowMaximizeButtonHint;
}
else
{
flags |= Qt::MSWindowsFixedSizeDialogHint;
flags &= ~Qt::WindowMaximizeButtonHint;

// The sub window might be reused from an instrument that was maximized. Show the sub window
// as normal, i.e. not maximized, if the instrument view is not resizable.
if (subWindow->isMaximized())
{
subWindow->showNormal();
}
}

subWindow->setWindowFlags(flags);

// 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

// 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
// missing some implementation details that QMdiSubWindow has.
auto subWin = dynamic_cast<SubWindow*>(subWindow);
if (subWin)
{
subWin->updateTitleBar();
}
}
}

} // namespace lmms::gui

0 comments on commit 303215f

Please sign in to comment.