From 3b548ac51b4a969588694ad45af8d184af0171d5 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 9 Mar 2023 22:19:12 +0100 Subject: [PATCH 01/10] KeyboardEventFilter: return early on modifier-only press --- src/controllers/keyboard/keyboardeventfilter.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controllers/keyboard/keyboardeventfilter.cpp b/src/controllers/keyboard/keyboardeventfilter.cpp index 19dc4e947f7c..4c5a84620642 100644 --- a/src/controllers/keyboard/keyboardeventfilter.cpp +++ b/src/controllers/keyboard/keyboardeventfilter.cpp @@ -127,9 +127,15 @@ bool KeyboardEventFilter::eventFilter(QObject*, QEvent* e) { } QKeySequence KeyboardEventFilter::getKeySeq(QKeyEvent* e) { - QString modseq; QKeySequence k; + if (e->key() >= 0x01000020 && e->key() <= 0x01000023) { + // Do not act on Modifier only + // avoid returning "khmer vowel sign ie (U+17C0)" + return k; + } + + QString modseq; // TODO(XXX) check if we may simply return QKeySequence(e->modifiers()+e->key()) if (e->modifiers() & Qt::ShiftModifier) { @@ -148,12 +154,6 @@ QKeySequence KeyboardEventFilter::getKeySeq(QKeyEvent* e) { modseq += "Meta+"; } - if (e->key() >= 0x01000020 && e->key() <= 0x01000023) { - // Do not act on Modifier only - // avoid returning "khmer vowel sign ie (U+17C0)" - return k; - } - QString keyseq = QKeySequence(e->key()).toString(); k = QKeySequence(modseq + keyseq); From b499c20504de709c3f5e0b8b4e6f509fcda9ec25 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Feb 2023 23:00:48 +0100 Subject: [PATCH 02/10] KeyboardEventFilter: separate debug messages for press and release --- src/controllers/keyboard/keyboardeventfilter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/keyboard/keyboardeventfilter.cpp b/src/controllers/keyboard/keyboardeventfilter.cpp index 4c5a84620642..4929c24a431c 100644 --- a/src/controllers/keyboard/keyboardeventfilter.cpp +++ b/src/controllers/keyboard/keyboardeventfilter.cpp @@ -158,7 +158,11 @@ QKeySequence KeyboardEventFilter::getKeySeq(QKeyEvent* e) { k = QKeySequence(modseq + keyseq); if (CmdlineArgs::Instance().getDeveloper()) { - qDebug() << "keyboard press: " << k.toString(); + if (e->type() == QEvent::KeyPress) { + qDebug() << "keyboard press: " << k.toString(); + } else if (e->type() == QEvent::KeyRelease) { + qDebug() << "keyboard release: " << k.toString(); + } } return k; } From 652e00b660447e8e126a8f07b162ca8e3e772655 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Feb 2023 22:59:46 +0100 Subject: [PATCH 03/10] menubar: show/hide with Alt key (Linux & Windows only) --- .../keyboard/keyboardeventfilter.cpp | 29 +++++++++++++++- .../keyboard/keyboardeventfilter.h | 9 +++++ src/mixxxmainwindow.cpp | 10 ++++++ src/widget/wmainmenubar.cpp | 33 +++++++++++++++++++ src/widget/wmainmenubar.h | 3 ++ 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/controllers/keyboard/keyboardeventfilter.cpp b/src/controllers/keyboard/keyboardeventfilter.cpp index 4929c24a431c..39d0a964a247 100644 --- a/src/controllers/keyboard/keyboardeventfilter.cpp +++ b/src/controllers/keyboard/keyboardeventfilter.cpp @@ -13,6 +13,9 @@ KeyboardEventFilter::KeyboardEventFilter(ConfigObject* pKbdConfi QObject* parent, const char* name) : QObject(parent), +#ifndef __APPLE__ + m_altPressedWithoutKey(false), +#endif m_pKbdConfigObject(nullptr) { setObjectName(name); setKeyboardConfig(pKbdConfigObject); @@ -41,7 +44,6 @@ bool KeyboardEventFilter::eventFilter(QObject*, QEvent* e) { // Run through list of active keys to see if the pressed key is already active // Just for returning true if we are consuming this key event - foreach (const KeyDownInformation& keyDownInfo, m_qActiveKeyList) { if (keyDownInfo.keyId == keyId) { return true; @@ -50,6 +52,9 @@ bool KeyboardEventFilter::eventFilter(QObject*, QEvent* e) { QKeySequence ks = getKeySeq(ke); if (!ks.isEmpty()) { +#ifndef __APPLE__ + m_altPressedWithoutKey = false; +#endif ConfigValueKbd ksv(ks); // Check if a shortcut is defined bool result = false; @@ -76,10 +81,32 @@ bool KeyboardEventFilter::eventFilter(QObject*, QEvent* e) { } } return result; +#ifndef __APPLE__ + } else { + // getKeySeq() returns empty string if the press was a modifier only + if (ke->modifiers() & Qt::AltModifier) { + // on Linux pressing Alt sends Alt+Qt::Key_Alt, so checking for + // Alt modifier is suffcient. + // Activate this in case there are issues on Windows + // || ke->key() == Qt::Key_Alt) { + m_altPressedWithoutKey = true; + } +#endif } } else if (e->type()==QEvent::KeyRelease) { QKeyEvent* ke = (QKeyEvent*)e; +#ifndef __APPLE__ + // QAction hotkeys are consumed by the object the created them, so we will + // not receive those here, for example WMainMenuBar hotkeys. + // However, it may happen that we receive an Alt+[key] combo RELEASE for + // which no [key] PRESS was registered. + if (m_altPressedWithoutKey && ke->key() == Qt::Key_Alt) { + emit altPressedWithoutKeys(); + } + m_altPressedWithoutKey = false; +#endif + #ifdef __APPLE__ // On Mac OSX the nativeScanCode is empty int keyId = ke->key(); diff --git a/src/controllers/keyboard/keyboardeventfilter.h b/src/controllers/keyboard/keyboardeventfilter.h index b557a4a8c540..319003c8bf6e 100644 --- a/src/controllers/keyboard/keyboardeventfilter.h +++ b/src/controllers/keyboard/keyboardeventfilter.h @@ -24,6 +24,11 @@ class KeyboardEventFilter : public QObject { void setKeyboardConfig(ConfigObject *pKbdConfigObject); ConfigObject* getKeyboardConfig(); +#ifndef __APPLE__ + signals: + void altPressedWithoutKeys(); +#endif + private: struct KeyDownInformation { KeyDownInformation(int keyId, int modifiers, ControlObject* pControl) @@ -37,6 +42,10 @@ class KeyboardEventFilter : public QObject { ControlObject* pControl; }; +#ifndef __APPLE__ + bool m_altPressedWithoutKey; +#endif + // Returns a valid QString with modifier keys from a QKeyEvent QKeySequence getKeySeq(QKeyEvent *e); // List containing keys which is currently pressed diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index bfaf258bcf66..4433a31abe1c 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -858,6 +858,16 @@ void MixxxMainWindow::connectMenuBar() { &mixxx::LibraryExporter::slotRequestExport, Qt::UniqueConnection); #endif + +#ifndef __APPLE__ + if (m_pCoreServices->getKeyboardEventFilter()) { + connect(m_pCoreServices->getKeyboardEventFilter().get(), + &KeyboardEventFilter::altPressedWithoutKeys, + m_pMenuBar, + &WMainMenuBar::slotToggleMenuBar, + Qt::UniqueConnection); + } +#endif } void MixxxMainWindow::slotFileLoadSongPlayer(int deck) { diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 7c4579c829b6..1c655a7f3a22 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -680,9 +680,42 @@ void WMainMenuBar::onDeveloperToolsHidden() { } void WMainMenuBar::onFullScreenStateChange(bool fullscreen) { +#ifndef __APPLE__ + if (fullscreen) { + hideMenuBar(); + } +#endif emit internalFullScreenStateChange(fullscreen); } +void WMainMenuBar::slotToggleMenuBar() { + if (isNativeMenuBar()) { + return; + } + + if (height() > 0) { + hideMenuBar(); + } else { + showMenuBar(); + } +} + +void WMainMenuBar::showMenuBar() { + if (isNativeMenuBar()) { + return; + } + + setMinimumHeight(sizeHint().height()); +} + +void WMainMenuBar::hideMenuBar() { + if (isNativeMenuBar()) { + return; + } + // don't use setHidden(bool) because hotkeys wouldn't work anymore + setFixedHeight(0); +} + void WMainMenuBar::onVinylControlDeckEnabledStateChange(int deck, bool enabled) { VERIFY_OR_DEBUG_ASSERT(deck >= 0 && deck < m_vinylControlEnabledActions.size()) { return; diff --git a/src/widget/wmainmenubar.h b/src/widget/wmainmenubar.h index 10136d66854f..1626f6db4df7 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -49,6 +49,7 @@ class WMainMenuBar : public QMenuBar { void onVinylControlDeckEnabledStateChange(int deck, bool enabled); void onNumberOfDecksChanged(int decks); void onKeywheelChange(int state); + void slotToggleMenuBar(); signals: void createCrate(); @@ -88,6 +89,8 @@ class WMainMenuBar : public QMenuBar { private: void initialize(); + void showMenuBar(); + void hideMenuBar(); void createVisibilityControl(QAction* pAction, const ConfigKey& key); UserSettingsPointer m_pConfig; From ea7b748a739a717aadad21403d9430330ccd197e Mon Sep 17 00:00:00 2001 From: ronso0 Date: Fri, 24 Feb 2023 23:03:13 +0100 Subject: [PATCH 04/10] menubar: unhide when a menu hotkey is pressed (QMenu::AboutToShow() signal) --- src/widget/wmainmenubar.cpp | 50 +++++++++++++++++++++++++++++++++++++ src/widget/wmainmenubar.h | 5 ++++ 2 files changed, 55 insertions(+) diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 1c655a7f3a22..8792568d3eff 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -81,6 +81,7 @@ WMainMenuBar::WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, void WMainMenuBar::initialize() { // FILE MENU QMenu* pFileMenu = new QMenu(tr("&File"), this); + connectMenuToSlotShowMenuBar(pFileMenu); QString loadTrackText = tr("Load Track to Deck &%1"); QString loadTrackStatusText = tr("Loads a track in deck %1"); @@ -129,6 +130,7 @@ void WMainMenuBar::initialize() { // LIBRARY MENU QMenu* pLibraryMenu = new QMenu(tr("&Library"), this); + connectMenuToSlotShowMenuBar(pLibraryMenu); QString rescanTitle = tr("&Rescan Library"); QString rescanText = tr("Rescans library folders for changes to tracks."); @@ -194,6 +196,7 @@ void WMainMenuBar::initialize() { QMenu* pViewMenu = new QMenu(tr("&View") + QStringLiteral("\u200C"), this); #else QMenu* pViewMenu = new QMenu(tr("&View"), this); + connectMenuToSlotShowMenuBar(pViewMenu); #endif // Skin Settings Menu @@ -327,6 +330,7 @@ void WMainMenuBar::initialize() { // OPTIONS MENU QMenu* pOptionsMenu = new QMenu(tr("&Options"), this); + connectMenuToSlotShowMenuBar(pOptionsMenu); #ifdef __VINYLCONTROL__ QMenu* pVinylControlMenu = new QMenu(tr("&Vinyl Control"), this); @@ -446,6 +450,7 @@ void WMainMenuBar::initialize() { // DEVELOPER MENU if (CmdlineArgs::Instance().getDeveloper()) { QMenu* pDeveloperMenu = new QMenu(tr("&Developer"), this); + connectMenuToSlotShowMenuBar(pDeveloperMenu); QString reloadSkinTitle = tr("&Reload Skin"); QString reloadSkinText = tr("Reload the skin"); @@ -547,6 +552,7 @@ void WMainMenuBar::initialize() { // HELP MENU QMenu* pHelpMenu = new QMenu(tr("&Help"), this); + connectMenuToSlotShowMenuBar(pHelpMenu); QString externalLinkSuffix; #ifndef __APPLE__ @@ -688,6 +694,50 @@ void WMainMenuBar::onFullScreenStateChange(bool fullscreen) { emit internalFullScreenStateChange(fullscreen); } +bool WMainMenuBar::event(QEvent* pEvent) { + // doesn't work: menubar is shown and hidden again instantly without the + // respective menu being selected/opened/highlighted + // if (!isNativeMenuBar() && pEvent->type() == QEvent::Shortcut) { + // qWarning() << " #"; + // qWarning() << " #"; + // qWarning() << " # Shortcut"; + // if (height() <= 0) { + // showMenuBar(); + // qWarning() << " # hidden > show"; + // } + // qWarning() << " #"; + // qWarning() << " #"; + // // doesn't help either + // QMenuBar::event(pEvent); + //} + return QMenuBar::event(pEvent); +} + +void WMainMenuBar::connectMenuToSlotShowMenuBar(const QMenu* pMenu) { + // If a menu hotkey like Alt+F(ile) is pressed while the menubar is hidden, + // show the menubar. + // Note: showMenuBar() results in a resizeEvent() which calls + // QMenuBarPrivate::updateGeometries() which resets currentAction() to + // nullptr, thus menus can't be switched with Left/Right key afterwards + // (= stuck with triggered menu) + // Workaround: set the current action manually after unhiding + connect(pMenu, + &QMenu::aboutToShow, + this, + [this]() { + if (!isNativeMenuBar() && height() <= 0) { + QAction* pAct = nullptr; + if (activeAction()) { + pAct = activeAction(); + } + showMenuBar(); + if (pAct) { + setActiveAction(pAct); + } + } + }); +} + void WMainMenuBar::slotToggleMenuBar() { if (isNativeMenuBar()) { return; diff --git a/src/widget/wmainmenubar.h b/src/widget/wmainmenubar.h index 1626f6db4df7..1885faeb3139 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -89,10 +89,15 @@ class WMainMenuBar : public QMenuBar { private: void initialize(); + /// this ensures the menubar is shown when a menu hotkey is pressed + /// while the menubar is hidden + void connectMenuToSlotShowMenuBar(const QMenu* pMenu); void showMenuBar(); void hideMenuBar(); void createVisibilityControl(QAction* pAction, const ConfigKey& key); + bool event(QEvent* pEvent) override; + UserSettingsPointer m_pConfig; QAction* m_pViewKeywheel; ConfigObject* m_pKbdConfig; From aa6b900613c7e028dd69126c5e405b3c15e56912 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Mon, 27 Feb 2023 14:17:02 +0100 Subject: [PATCH 05/10] View menu: add hint (dummy action) to make Alt toggle more discoverable --- src/widget/wmainmenubar.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 8792568d3eff..e769b4fd95d1 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -1,6 +1,7 @@ #include "widget/wmainmenubar.h" #include +#include #include "config.h" #include "control/controlproxy.h" @@ -326,6 +327,13 @@ void WMainMenuBar::initialize() { &QAction::setChecked); pViewMenu->addAction(pViewFullScreen); + if (!isNativeMenuBar()) { + auto* pAltDummyAction = new QWidgetAction(this); + pAltDummyAction->setText(tr("Toggle menu bar with Alt key")); + pAltDummyAction->setDisabled(true); + pViewMenu->addAction(pAltDummyAction); + } + addMenu(pViewMenu); // OPTIONS MENU From 578d355bda96af1c4a1c29e0cebc96f15ac26935 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 28 Feb 2023 22:32:18 +0100 Subject: [PATCH 06/10] Fullscreen: show popup with with fullscreen and menu hotkeys This modal popup is displayed every time when going fullscreen as long as the "Remind me again" checkbox is ticked when Okay is clicked. It can be styled to match the skin. --- src/mixxxmainwindow.cpp | 143 ++++++++++++++++++++++++++++++++++++ src/mixxxmainwindow.h | 22 ++++++ src/widget/wmainmenubar.cpp | 4 +- src/widget/wmainmenubar.h | 2 + 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 4433a31abe1c..4a871d658a2e 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -1,5 +1,6 @@ #include "mixxxmainwindow.h" +#include #include #include @@ -106,6 +107,9 @@ MixxxMainWindow::MixxxMainWindow(std::shared_ptr pCoreServi : m_pCoreServices(pCoreServices), m_pCentralWidget(nullptr), m_pLaunchImage(nullptr), +#ifndef __APPLE__ + m_pFullScreenHint(nullptr), +#endif m_pGuiTick(nullptr), #ifdef __LINUX__ m_supportsGlobalMenuBar(supportsGlobalMenu()), @@ -373,6 +377,13 @@ void MixxxMainWindow::initialize() { // otherwise it would shift the launch image shortly before the skin is visible. m_pMenuBar->show(); + // Show the fullscreen hotkey hint if we went fullscreen earlier. + // This should make users aware of the new feature (press Alt to show/hide menu bar) + // if they upgraded Mixxx with 'Start in fullscreen mode'. + // Show the hint now so it doesn't interfere with other popups, e.g. the + // sound device warnings. + showFullScreenHotkeyHint(); + // The launch image widget is automatically disposed, but we still have a // pointer to it. m_pLaunchImage = nullptr; @@ -398,6 +409,14 @@ void MixxxMainWindow::initialize() { &PlayerInfo::currentPlayingTrackChanged, this, &MixxxMainWindow::slotUpdateWindowTitle); + + if (m_pCoreServices->getSettings()->getValueString( + ConfigKey("[Config]", "hide_fullscreen_hint")) != "1") { + connect(this, + &MixxxMainWindow::fullScreenChanged, + this, + &MixxxMainWindow::showFullScreenHotkeyHint); + } } MixxxMainWindow::~MixxxMainWindow() { @@ -1271,3 +1290,127 @@ void MixxxMainWindow::initializationProgressUpdate(int progress, const QString& } qApp->processEvents(); } + +void MixxxMainWindow::showFullScreenHotkeyHint() { + if (!isFullScreen()) { + return; + } + if (m_pCoreServices->getSettings()->getValueString( + ConfigKey("[Config]", "hide_fullscreen_hint")) == "1") { + return; + } + // don't show fullscreen hotkey hint during startup + if (centralWidget() == m_pLaunchImage) { + return; + } + + if (!m_pFullScreenHint) { + m_pFullScreenHint = new WFullScreenHint(this, m_pCoreServices->getSettings()); + } + if (m_pFullScreenHint->isVisible()) { + return; + } + // apply skin stylesheet + m_pFullScreenHint->setStyleSheet(m_pCentralWidget->styleSheet()); + m_pFullScreenHint->popup(); +} + +WFullScreenHint::WFullScreenHint(QWidget* parent, UserSettingsPointer pConfig) + : QWidget(parent), + m_pConfig(pConfig) { + QWidget::hide(); + // stay on top, remove OS window frame + setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + // block the GUI as long as it is open + setWindowModality(Qt::ApplicationModal); + // allow custom styles? at least the custom border is not applied without this + setAttribute(Qt::WA_StyledBackground); + // name it like the class so /tools/qsscheck.py can pick it up + setObjectName("WFullScreenHint"); + + QVBoxLayout* pLabelLayout = new QVBoxLayout(); + pLabelLayout->addStretch(); + + auto* fullScreenTitle = new QLabel(tr("Mixxx is now fullscreen"), this); + fullScreenTitle->setObjectName("FullScreenTitle"); + pLabelLayout->addWidget(fullScreenTitle); + + // list all fullscreen keyboard shortcuts + const QList fsshortcuts = WMainMenuBar::s_fullScreenShortcuts; + if (!fsshortcuts.isEmpty()) { + QString fssString; + int i = 1; + for (const auto& shortcut : fsshortcuts) { + if (i > 1) { // separate multiple shortcuts + fssString.append(" / "); + } + fssString.append(QStringLiteral("%1").arg(shortcut.toString())); + i++; + } + QLabel* fullScreenHotkeys = new QLabel(this); + fullScreenHotkeys->setObjectName("FullScreenHotkeyLabel"); + fullScreenHotkeys->setText(tr("Press %1 to toggle fullscreen").arg(fssString)); + pLabelLayout->addWidget(fullScreenHotkeys); + } + +#ifndef __APPLE__ + auto* menuHotkey = new QLabel(tr("Press Alt to toggle the menu bar"), this); + menuHotkey->setObjectName("MenuHotkeyLabel"); + pLabelLayout->addWidget(menuHotkey); +#endif + + pLabelLayout->addStretch(); + + m_pRemindCheckBox = new QCheckBox(tr("&Remind me again"), this); + m_pRemindCheckBox->setObjectName("RemindAgainCheckBox"); + m_pRemindCheckBox->setChecked(true); + pLabelLayout->addWidget(m_pRemindCheckBox); + + pLabelLayout->addStretch(); + + m_pOkayBtn = new QPushButton(tr("&Okay"), this); + connect(m_pOkayBtn, + &QPushButton::clicked, + this, + &WFullScreenHint::close); + pLabelLayout->addWidget(m_pOkayBtn); + + QHBoxLayout* pMainLayout = new QHBoxLayout(); + // center the labels horizontally + pMainLayout->addStretch(); + pMainLayout->addLayout(pLabelLayout); + pMainLayout->addStretch(); + setLayout(pMainLayout); + + // we need to update the the layout here since the size is used to + // calculate the positioning later + layout()->update(); + layout()->activate(); +} + +void WFullScreenHint::popup() { + const QWidget* pParentWidget = parentWidget(); + VERIFY_OR_DEBUG_ASSERT(pParentWidget) { + qWarning() << "WFullScreenHint: no parent widget!"; + } + const QScreen* const pScreen = mixxx::widgethelper::getScreen(*pParentWidget); + int dlgWidth = 400; + VERIFY_OR_DEBUG_ASSERT(pScreen) { + qWarning() << "No screen detectable, assuming screen size of 800x600px."; + } + else { + dlgWidth = pScreen->geometry().width() / 2; + } + setFixedWidth(dlgWidth); + setFixedHeight(static_cast(sizeHint().height() * 1.2)); + // Show the hint centered in the upper part of the window. + move(dlgWidth / 2, 100); + QWidget::show(); + m_pOkayBtn->setFocus(); +} + +void WFullScreenHint::closeEvent(QCloseEvent* event) { + int remind = m_pRemindCheckBox->isChecked() ? 0 : 1; + m_pConfig->set(ConfigKey("[Config]", "hide_fullscreen_hint"), ConfigValue(remind)); + QWidget::closeEvent(event); +} diff --git a/src/mixxxmainwindow.h b/src/mixxxmainwindow.h index eeeb755162cf..d23f80ff61cd 100644 --- a/src/mixxxmainwindow.h +++ b/src/mixxxmainwindow.h @@ -17,7 +17,9 @@ class DlgKeywheel; class GuiTick; class LaunchImage; class VisualsManager; +class WFullScreenHint; class WMainMenuBar; +class QCheckBox; namespace mixxx { @@ -103,6 +105,9 @@ class MixxxMainWindow : public QMainWindow { bool loadConfiguredSkin(); bool confirmExit(); + + void showFullScreenHotkeyHint(); + QDialog::DialogCode soundDeviceErrorDlg( const QString &title, const QString &text, bool* retryClicked); QDialog::DialogCode soundDeviceBusyDlg(bool* retryClicked); @@ -114,6 +119,7 @@ class MixxxMainWindow : public QMainWindow { QWidget* m_pCentralWidget; LaunchImage* m_pLaunchImage; + WFullScreenHint* m_pFullScreenHint; std::shared_ptr m_pSkinLoader; GuiTick* m_pGuiTick; @@ -140,3 +146,19 @@ class MixxxMainWindow : public QMainWindow { QSet m_skinCreatedControls; }; + +class WFullScreenHint : public QWidget { + Q_OBJECT + public: + WFullScreenHint(QWidget* parent, UserSettingsPointer pConfig); + + void popup(); + + protected: + void closeEvent(QCloseEvent* event) override; + + private: + QCheckBox* m_pRemindCheckBox; + QPushButton* m_pOkayBtn; + UserSettingsPointer m_pConfig; +}; diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index e769b4fd95d1..88d734f30907 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -70,6 +70,8 @@ QUrl documentationUrl( } } // namespace +QList WMainMenuBar::s_fullScreenShortcuts = {}; + WMainMenuBar::WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, ConfigObject* pKbdConfig) : QMenuBar(pParent), @@ -313,7 +315,7 @@ void WMainMenuBar::initialize() { if (!osShortcut.isEmpty() && !shortcuts.contains(osShortcut)) { shortcuts << osShortcut; } - + s_fullScreenShortcuts = shortcuts; pViewFullScreen->setShortcuts(shortcuts); pViewFullScreen->setShortcutContext(Qt::ApplicationShortcut); pViewFullScreen->setCheckable(true); diff --git a/src/widget/wmainmenubar.h b/src/widget/wmainmenubar.h index 1885faeb3139..a21fbec102d7 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -36,6 +36,8 @@ class WMainMenuBar : public QMenuBar { WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, ConfigObject* pKbdConfig); + static QList s_fullScreenShortcuts; + public slots: void onLibraryScanStarted(); void onLibraryScanFinished(); From 5f71a6c94be9a6ae01d72b50e0817700eb8209c2 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 16 Mar 2023 22:34:01 +0100 Subject: [PATCH 07/10] fullscreen hotkey hint: don't show on macOS --- src/mixxxmainwindow.cpp | 8 ++++++-- src/mixxxmainwindow.h | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 4a871d658a2e..547466b49c1e 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -377,12 +377,14 @@ void MixxxMainWindow::initialize() { // otherwise it would shift the launch image shortly before the skin is visible. m_pMenuBar->show(); +#ifndef __APPLE__ // Show the fullscreen hotkey hint if we went fullscreen earlier. // This should make users aware of the new feature (press Alt to show/hide menu bar) // if they upgraded Mixxx with 'Start in fullscreen mode'. // Show the hint now so it doesn't interfere with other popups, e.g. the // sound device warnings. showFullScreenHotkeyHint(); +#endif // The launch image widget is automatically disposed, but we still have a // pointer to it. @@ -410,6 +412,7 @@ void MixxxMainWindow::initialize() { this, &MixxxMainWindow::slotUpdateWindowTitle); +#ifndef __APPLE__ if (m_pCoreServices->getSettings()->getValueString( ConfigKey("[Config]", "hide_fullscreen_hint")) != "1") { connect(this, @@ -417,6 +420,7 @@ void MixxxMainWindow::initialize() { this, &MixxxMainWindow::showFullScreenHotkeyHint); } +#endif } MixxxMainWindow::~MixxxMainWindow() { @@ -1291,6 +1295,7 @@ void MixxxMainWindow::initializationProgressUpdate(int progress, const QString& qApp->processEvents(); } +#ifndef __APPLE__ void MixxxMainWindow::showFullScreenHotkeyHint() { if (!isFullScreen()) { return; @@ -1353,11 +1358,9 @@ WFullScreenHint::WFullScreenHint(QWidget* parent, UserSettingsPointer pConfig) pLabelLayout->addWidget(fullScreenHotkeys); } -#ifndef __APPLE__ auto* menuHotkey = new QLabel(tr("Press Alt to toggle the menu bar"), this); menuHotkey->setObjectName("MenuHotkeyLabel"); pLabelLayout->addWidget(menuHotkey); -#endif pLabelLayout->addStretch(); @@ -1414,3 +1417,4 @@ void WFullScreenHint::closeEvent(QCloseEvent* event) { m_pConfig->set(ConfigKey("[Config]", "hide_fullscreen_hint"), ConfigValue(remind)); QWidget::closeEvent(event); } +#endif diff --git a/src/mixxxmainwindow.h b/src/mixxxmainwindow.h index d23f80ff61cd..ebed89e426c4 100644 --- a/src/mixxxmainwindow.h +++ b/src/mixxxmainwindow.h @@ -17,7 +17,9 @@ class DlgKeywheel; class GuiTick; class LaunchImage; class VisualsManager; +#ifndef __APPLE__ class WFullScreenHint; +#endif class WMainMenuBar; class QCheckBox; @@ -106,7 +108,9 @@ class MixxxMainWindow : public QMainWindow { bool confirmExit(); +#ifndef __APPLE__ void showFullScreenHotkeyHint(); +#endif QDialog::DialogCode soundDeviceErrorDlg( const QString &title, const QString &text, bool* retryClicked); @@ -119,7 +123,9 @@ class MixxxMainWindow : public QMainWindow { QWidget* m_pCentralWidget; LaunchImage* m_pLaunchImage; +#ifndef __APPLE__ WFullScreenHint* m_pFullScreenHint; +#endif std::shared_ptr m_pSkinLoader; GuiTick* m_pGuiTick; @@ -147,6 +153,7 @@ class MixxxMainWindow : public QMainWindow { QSet m_skinCreatedControls; }; +#ifndef __APPLE__ class WFullScreenHint : public QWidget { Q_OBJECT public: @@ -162,3 +169,4 @@ class WFullScreenHint : public QWidget { QPushButton* m_pOkayBtn; UserSettingsPointer m_pConfig; }; +#endif From df1557056a3e99cb66c4c7f572fa041209e65eb7 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 28 Feb 2023 22:48:55 +0100 Subject: [PATCH 08/10] WIP WFullScreenHint: add some styles (LateNight, Shade) --- res/skins/LateNight/style.qss | 6 ++++-- res/skins/LateNight/style_classic.qss | 3 ++- res/skins/LateNight/style_palemoon.qss | 7 +++++++ res/skins/Shade/style.qss | 9 +++++++++ res/skins/default.qss | 17 +++++++++++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss index 9b3f8e959c76..c67e2bd9618b 100644 --- a/res/skins/LateNight/style.qss +++ b/res/skins/LateNight/style.qss @@ -341,8 +341,10 @@ WOverview QLabel { /************** font sizes / alignment ****************************************/ /************** font settings *************************************************/ - - +WFullScreenHint QPushButton { + border-width: 2px 2px 2px 2px; + border-image: url(skin:/classic/buttons/spinbox_elevated_border.svg) 2 2 2 2; +} diff --git a/res/skins/LateNight/style_classic.qss b/res/skins/LateNight/style_classic.qss index 0d7354b0bac0..9a43980b1676 100644 --- a/res/skins/LateNight/style_classic.qss +++ b/res/skins/LateNight/style_classic.qss @@ -1480,7 +1480,8 @@ WPushButton#FxParameterButton[displayValue="0"] { #VinylCueButton[displayValue="2"], #FxParameterButton[displayValue="1"], #SplitCue[value="1"], -QPushButton#pushButtonRepeatPlaylist:checked { +QPushButton#pushButtonRepeatPlaylist:checked, +WFullScreenHint QPushButton { background-color: #888; } diff --git a/res/skins/LateNight/style_palemoon.qss b/res/skins/LateNight/style_palemoon.qss index ef6449ddf8a6..62f9d3790966 100644 --- a/res/skins/LateNight/style_palemoon.qss +++ b/res/skins/LateNight/style_palemoon.qss @@ -1200,6 +1200,9 @@ WLibrarySidebar, #LibraryFeatureControls QRadioButton, /* Tooltip and menus */ QToolTip, +WFullScreenHint QLabel, +WFullScreenHint QCheckBox, +WFullScreenHint QPushButton, WLibrarySidebar QMenu, WTrackTableViewHeader QMenu, WLibraryTextBrowser QMenu, @@ -2920,6 +2923,10 @@ WSearchLineEdit QAbstractScrollArea, border-radius: 1px; } + +WFullScreenHint, +WFullScreenHint QLabel, +WFullScreenHint QCheckBox, QToolTip, #MainMenu, #MainMenu::item, diff --git a/res/skins/Shade/style.qss b/res/skins/Shade/style.qss index 0f6f4c3c6360..5d803e2844f9 100644 --- a/res/skins/Shade/style.qss +++ b/res/skins/Shade/style.qss @@ -14,6 +14,8 @@ padding: 5px 0px; } #Mixxx, WWidget, +WFullScreenHint QLabel, +WFullScreenHint QCheckBox, WEffectName, WKey, WLabel, @@ -92,10 +94,17 @@ WBeatSpinBox::down-button { +WFullScreenHint { + border: 1px solid white; +} + /* common colors for WEffectSelector, QMenu, QToolTip */ #MainMenu QMenu, #MainMenu QMenu::item, #MainMenu QMenu QCheckBox, +WFullScreenHint, +WFullScreenHint QLabel, +WFullScreenHint QCheckBox, QToolTip, WLibrarySidebar QMenu, WLibrarySidebar QMenu::item, diff --git a/res/skins/default.qss b/res/skins/default.qss index 668ca94e393c..c25fe93b875c 100644 --- a/res/skins/default.qss +++ b/res/skins/default.qss @@ -167,3 +167,20 @@ WEffectChainPresetButton QMenu QCheckBox::indicator { width: 0.7em; height: 0.7em; } + + +/* The popup that is shown when going fullscreen */ +WFullScreenHint { + font-family: "Open Sans"; + font-weight: normal; + font-style: normal; + padding: 200px; + border-radius: 5px; + border: 1px solid white; +} +WFullScreenHint QLabel, +WFullScreenHint QCheckBox, +WFullScreenHint QPushButton { + /*font-size: 3em;*/ + font-size: 20px; +} From 1a416cea10841606f8d0a3e1c258c80edd669f10 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sat, 8 Apr 2023 01:00:45 +0200 Subject: [PATCH 09/10] Fullscreen hint: Return/Enter always close the popup --- src/mixxxmainwindow.cpp | 9 +++++++++ src/mixxxmainwindow.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 547466b49c1e..c572dcabdacf 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -1412,6 +1412,15 @@ void WFullScreenHint::popup() { m_pOkayBtn->setFocus(); } +void WFullScreenHint::keyPressEvent(QKeyEvent* event) { + // Return/Enter should always close the popup + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + close(); + return; + } + return QWidget::keyPressEvent(event); +} + void WFullScreenHint::closeEvent(QCloseEvent* event) { int remind = m_pRemindCheckBox->isChecked() ? 0 : 1; m_pConfig->set(ConfigKey("[Config]", "hide_fullscreen_hint"), ConfigValue(remind)); diff --git a/src/mixxxmainwindow.h b/src/mixxxmainwindow.h index ebed89e426c4..b732d4c21672 100644 --- a/src/mixxxmainwindow.h +++ b/src/mixxxmainwindow.h @@ -163,6 +163,7 @@ class WFullScreenHint : public QWidget { protected: void closeEvent(QCloseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; private: QCheckBox* m_pRemindCheckBox; From af2cfcb633ea736378b01d5a53d601ba9ef37454 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sun, 9 Apr 2023 23:35:49 +0200 Subject: [PATCH 10/10] WIP Menubar: store hidden state per window state --- src/mixxxmainwindow.cpp | 9 ++++++--- src/widget/wmainmenubar.cpp | 33 +++++++++++++++++++-------------- src/widget/wmainmenubar.h | 12 ++++++++---- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index c572dcabdacf..f70180042afe 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -884,10 +884,13 @@ void MixxxMainWindow::connectMenuBar() { #ifndef __APPLE__ if (m_pCoreServices->getKeyboardEventFilter()) { - connect(m_pCoreServices->getKeyboardEventFilter().get(), + connect( + m_pCoreServices->getKeyboardEventFilter().get(), &KeyboardEventFilter::altPressedWithoutKeys, - m_pMenuBar, - &WMainMenuBar::slotToggleMenuBar, + this, + [this]() { + m_pMenuBar->slotToggleMenuBar(isFullScreen()); + }, Qt::UniqueConnection); } #endif diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 88d734f30907..eebd9ebc83f8 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -84,7 +84,7 @@ WMainMenuBar::WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, void WMainMenuBar::initialize() { // FILE MENU QMenu* pFileMenu = new QMenu(tr("&File"), this); - connectMenuToSlotShowMenuBar(pFileMenu); + maybeConnectMenuToSlotShowMenuBar(pFileMenu); QString loadTrackText = tr("Load Track to Deck &%1"); QString loadTrackStatusText = tr("Loads a track in deck %1"); @@ -133,7 +133,7 @@ void WMainMenuBar::initialize() { // LIBRARY MENU QMenu* pLibraryMenu = new QMenu(tr("&Library"), this); - connectMenuToSlotShowMenuBar(pLibraryMenu); + maybeConnectMenuToSlotShowMenuBar(pLibraryMenu); QString rescanTitle = tr("&Rescan Library"); QString rescanText = tr("Rescans library folders for changes to tracks."); @@ -199,7 +199,7 @@ void WMainMenuBar::initialize() { QMenu* pViewMenu = new QMenu(tr("&View") + QStringLiteral("\u200C"), this); #else QMenu* pViewMenu = new QMenu(tr("&View"), this); - connectMenuToSlotShowMenuBar(pViewMenu); + maybeConnectMenuToSlotShowMenuBar(pViewMenu); #endif // Skin Settings Menu @@ -340,7 +340,7 @@ void WMainMenuBar::initialize() { // OPTIONS MENU QMenu* pOptionsMenu = new QMenu(tr("&Options"), this); - connectMenuToSlotShowMenuBar(pOptionsMenu); + maybeConnectMenuToSlotShowMenuBar(pOptionsMenu); #ifdef __VINYLCONTROL__ QMenu* pVinylControlMenu = new QMenu(tr("&Vinyl Control"), this); @@ -460,7 +460,7 @@ void WMainMenuBar::initialize() { // DEVELOPER MENU if (CmdlineArgs::Instance().getDeveloper()) { QMenu* pDeveloperMenu = new QMenu(tr("&Developer"), this); - connectMenuToSlotShowMenuBar(pDeveloperMenu); + maybeConnectMenuToSlotShowMenuBar(pDeveloperMenu); QString reloadSkinTitle = tr("&Reload Skin"); QString reloadSkinText = tr("Reload the skin"); @@ -562,7 +562,7 @@ void WMainMenuBar::initialize() { // HELP MENU QMenu* pHelpMenu = new QMenu(tr("&Help"), this); - connectMenuToSlotShowMenuBar(pHelpMenu); + maybeConnectMenuToSlotShowMenuBar(pHelpMenu); QString externalLinkSuffix; #ifndef __APPLE__ @@ -698,7 +698,7 @@ void WMainMenuBar::onDeveloperToolsHidden() { void WMainMenuBar::onFullScreenStateChange(bool fullscreen) { #ifndef __APPLE__ if (fullscreen) { - hideMenuBar(); + hideMenuBar(true); } #endif emit internalFullScreenStateChange(fullscreen); @@ -723,7 +723,10 @@ bool WMainMenuBar::event(QEvent* pEvent) { return QMenuBar::event(pEvent); } -void WMainMenuBar::connectMenuToSlotShowMenuBar(const QMenu* pMenu) { +void WMainMenuBar::maybeConnectMenuToSlotShowMenuBar(const QMenu* pMenu) { +#ifdef __APPLE__ + return; +#endif // If a menu hotkey like Alt+F(ile) is pressed while the menubar is hidden, // show the menubar. // Note: showMenuBar() results in a resizeEvent() which calls @@ -740,7 +743,7 @@ void WMainMenuBar::connectMenuToSlotShowMenuBar(const QMenu* pMenu) { if (activeAction()) { pAct = activeAction(); } - showMenuBar(); + showMenuBar(false); // TODO get isFullScreen() if (pAct) { setActiveAction(pAct); } @@ -748,19 +751,20 @@ void WMainMenuBar::connectMenuToSlotShowMenuBar(const QMenu* pMenu) { }); } -void WMainMenuBar::slotToggleMenuBar() { +#ifndef __APPLE__ +void WMainMenuBar::slotToggleMenuBar(bool fullscreen) { if (isNativeMenuBar()) { return; } if (height() > 0) { - hideMenuBar(); + hideMenuBar(fullscreen); } else { - showMenuBar(); + showMenuBar(fullscreen); } } -void WMainMenuBar::showMenuBar() { +void WMainMenuBar::showMenuBar(bool fullscreen) { if (isNativeMenuBar()) { return; } @@ -768,13 +772,14 @@ void WMainMenuBar::showMenuBar() { setMinimumHeight(sizeHint().height()); } -void WMainMenuBar::hideMenuBar() { +void WMainMenuBar::hideMenuBar(bool fullscreen) { if (isNativeMenuBar()) { return; } // don't use setHidden(bool) because hotkeys wouldn't work anymore setFixedHeight(0); } +#endif void WMainMenuBar::onVinylControlDeckEnabledStateChange(int deck, bool enabled) { VERIFY_OR_DEBUG_ASSERT(deck >= 0 && deck < m_vinylControlEnabledActions.size()) { diff --git a/src/widget/wmainmenubar.h b/src/widget/wmainmenubar.h index a21fbec102d7..e6a375ee5fdb 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -51,7 +51,9 @@ class WMainMenuBar : public QMenuBar { void onVinylControlDeckEnabledStateChange(int deck, bool enabled); void onNumberOfDecksChanged(int decks); void onKeywheelChange(int state); - void slotToggleMenuBar(); +#ifndef __APPLE__ + void slotToggleMenuBar(bool fullscreen); +#endif signals: void createCrate(); @@ -93,9 +95,11 @@ class WMainMenuBar : public QMenuBar { void initialize(); /// this ensures the menubar is shown when a menu hotkey is pressed /// while the menubar is hidden - void connectMenuToSlotShowMenuBar(const QMenu* pMenu); - void showMenuBar(); - void hideMenuBar(); + void maybeConnectMenuToSlotShowMenuBar(const QMenu* pMenu); +#ifndef __APPLE__ + void showMenuBar(bool fullscreen); + void hideMenuBar(bool fullscreen); +#endif void createVisibilityControl(QAction* pAction, const ConfigKey& key); bool event(QEvent* pEvent) override;