diff --git a/src/controllers/keyboard/keyboardeventfilter.cpp b/src/controllers/keyboard/keyboardeventfilter.cpp index c11cdf4e3d61..56702ce4273e 100644 --- a/src/controllers/keyboard/keyboardeventfilter.cpp +++ b/src/controllers/keyboard/keyboardeventfilter.cpp @@ -11,6 +11,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); @@ -42,6 +45,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; @@ -68,10 +74,33 @@ 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) && !m_altPressedWithoutKey) { + // on Linux pressing Alt sends Alt+Qt::Key_Alt, so checking for + // Alt modifier is sufficient. + // 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 that created them, e.g. + // WMainMenuBar, so we will not receive menu hotkey keypress events here. + // However, it may happen that we receive a RELEASE event for an Alt+key + // combo for which no KEYPRESS was registered. + // So react only to Alt-only releases. + 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(); @@ -120,28 +149,38 @@ bool KeyboardEventFilter::eventFilter(QObject*, QEvent* e) { // static QKeySequence KeyboardEventFilter::getKeySeq(QKeyEvent* e) { - if (e->key() >= 0x01000020 && e->key() <= 0x01000023) { - // Do not act on Modifier only, avoid returning "khmer vowel sign ie (U+17C0)" - return {}; + QKeySequence k; + + if ((e->key() >= Qt::Key_Shift && e->key() <= Qt::Key_Alt) || + e->key() == Qt::Key_AltGr) { + // Do not act on Modifier only, Shift, Ctrl, Meta, Alt and AltGr + // 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) { + modseq += "Shift+"; } + if (e->modifiers() & Qt::ControlModifier) { + modseq += "Ctrl+"; + } + + if (e->modifiers() & Qt::AltModifier) { + modseq += "Alt+"; + } + + if (e->modifiers() & Qt::MetaModifier) { + modseq += "Meta+"; + } + + QString keyseq = QKeySequence(e->key()).toString(); + k = QKeySequence(modseq + keyseq); + if (CmdlineArgs::Instance().getDeveloper()) { - QString modseq; - QKeySequence k; - if (e->modifiers() & Qt::ShiftModifier) { - modseq += "Shift+"; - } - if (e->modifiers() & Qt::ControlModifier) { - modseq += "Ctrl+"; - } - if (e->modifiers() & Qt::AltModifier) { - modseq += "Alt+"; - } - if (e->modifiers() & Qt::MetaModifier) { - modseq += "Meta+"; - } - QString keyseq = QKeySequence(e->key()).toString(); - k = QKeySequence(modseq + keyseq); if (e->type() == QEvent::KeyPress) { qDebug() << "keyboard press: " << k.toString(); } else if (e->type() == QEvent::KeyRelease) { diff --git a/src/controllers/keyboard/keyboardeventfilter.h b/src/controllers/keyboard/keyboardeventfilter.h index ccb3fdcff8fd..6071d56e7ec6 100644 --- a/src/controllers/keyboard/keyboardeventfilter.h +++ b/src/controllers/keyboard/keyboardeventfilter.h @@ -28,6 +28,11 @@ class KeyboardEventFilter : public QObject { // Returns a valid QString with modifier keys from a QKeyEvent static QKeySequence getKeySeq(QKeyEvent* e); +#ifndef __APPLE__ + signals: + void altPressedWithoutKeys(); +#endif + private: struct KeyDownInformation { KeyDownInformation(int keyId, int modifiers, ControlObject* pControl) @@ -41,6 +46,10 @@ class KeyboardEventFilter : public QObject { ControlObject* pControl; }; +#ifndef __APPLE__ + bool m_altPressedWithoutKey; +#endif + // Run through list of active keys to see if the pressed key is already active // and is not a control that repeats when held. bool shouldSkipHeldKey(int keyId) { diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 5c16b18f251a..a46ed35ed714 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -1,5 +1,6 @@ #include "mixxxmainwindow.h" +#include #include #include #include @@ -81,6 +82,9 @@ inline bool supportsGlobalMenu() { return false; } #endif + +const ConfigKey kHideMenuBarConfigKey = ConfigKey("[Config]", "hide_menubar"); +const ConfigKey kMenuBarHintConfigKey = ConfigKey("[Config]", "show_menubar_hint"); } // namespace MixxxMainWindow::MixxxMainWindow(std::shared_ptr pCoreServices) @@ -302,8 +306,15 @@ void MixxxMainWindow::initialize() { this, &MixxxMainWindow::rebootMixxxView, Qt::DirectConnection); +#ifndef __APPLE__ + connect(m_pPrefDlg, + &DlgPreferences::menuBarAutoHideChanged, + this, + &MixxxMainWindow::slotUpdateMenuBarAltKeyConnection, + Qt::DirectConnection); +#endif - // Connect signals to the menubar. Should be done before emit newSkinLoaded. + // Connect signals to the menubar. Should be done before emit skinLoaded. connectMenuBar(); QWidget* oldWidget = m_pCentralWidget; @@ -369,6 +380,21 @@ void MixxxMainWindow::initialize() { // corrupted. See bug 521509 -- bkgood ?? -- vrince setCentralWidget(m_pCentralWidget); +#ifndef __APPLE__ + // Ask for permission to auto-hide the menu bar if applicable. +#ifdef __LINUX__ + // This makes no sense when starting in windowed mode with a global menu, + // we'll ask when going fullscreen. + if (!m_supportsGlobalMenuBar || isFullScreen()) { + alwaysHideMenuBarDlg(); + slotUpdateMenuBarAltKeyConnection(); + } +#else + alwaysHideMenuBarDlg(); + slotUpdateMenuBarAltKeyConnection(); +#endif +#endif + // Show the menubar after the launch image is replaced by the skin widget, // otherwise it would shift the launch image shortly before the skin is visible. m_pMenuBar->show(); @@ -527,6 +553,51 @@ void MixxxMainWindow::initializeWindow() { slotUpdateWindowTitle(TrackPointer()); } +#ifndef __APPLE__ +void MixxxMainWindow::alwaysHideMenuBarDlg() { + if (!m_pCoreServices->getSettings()->getValue( + kMenuBarHintConfigKey, true)) { + return; + } + QString title = tr("Allow Mixxx to hide the menu bar?"); + //: Always show the menu bar? + QString hideBtnLabel = tr("Hide"); + QString showBtnLabel = tr("Always show"); + //: Keep formatting tags (bold text) and
(linebreak). + //: %1 is the placeholder for the 'Always show' button label + QString desc = tr( + "The Mixxx menu bar is hidden and can be toggled with a single press " + "of the Alt key.

" + "Click %1 to agree.

" + "Click %2 to disable that, for example if you don't use Mixxx " + "with a keyboard.

" + "You can change this setting any time in Preferences -> Interface." + "
") // line break for some extra margin to the checkbox + .arg(hideBtnLabel, showBtnLabel); + + QMessageBox msg; + msg.setIcon(QMessageBox::Question); + msg.setWindowTitle(title); + msg.setText(desc); + QCheckBox askAgainCheckBox; + askAgainCheckBox.setText(tr("Ask me again")); + askAgainCheckBox.setCheckState(Qt::Checked); + msg.setCheckBox(&askAgainCheckBox); + QPushButton* pHideBtn = msg.addButton(hideBtnLabel, QMessageBox::AcceptRole); + QPushButton* pShowBtn = msg.addButton(showBtnLabel, QMessageBox::RejectRole); + msg.setDefaultButton(pShowBtn); + msg.exec(); + + m_pCoreServices->getSettings()->setValue( + kMenuBarHintConfigKey, + askAgainCheckBox.checkState() == Qt::Checked ? 1 : 0); + + m_pCoreServices->getSettings()->setValue( + kHideMenuBarConfigKey, + msg.clickedButton() == pHideBtn ? 1 : 0); +} +#endif + QDialog::DialogCode MixxxMainWindow::soundDeviceErrorDlg( const QString &title, const QString &text, bool* retryClicked) { QMessageBox msgBox; @@ -885,6 +956,29 @@ void MixxxMainWindow::connectMenuBar() { #endif } +#ifndef __APPLE__ +void MixxxMainWindow::slotUpdateMenuBarAltKeyConnection() { + if (!m_pCoreServices->getKeyboardEventFilter() || !m_pMenuBar) { + return; + } + + if (m_pCoreServices->getSettings()->getValue(kHideMenuBarConfigKey, false)) { + connect(m_pCoreServices->getKeyboardEventFilter().get(), + &KeyboardEventFilter::altPressedWithoutKeys, + m_pMenuBar, + &WMainMenuBar::slotToggleMenuBar, + Qt::UniqueConnection); + m_pMenuBar->hideMenuBar(); + } else { + disconnect(m_pCoreServices->getKeyboardEventFilter().get(), + &KeyboardEventFilter::altPressedWithoutKeys, + m_pMenuBar, + &WMainMenuBar::slotToggleMenuBar); + m_pMenuBar->showMenuBar(); + } +} +#endif + void MixxxMainWindow::slotFileLoadSongPlayer(int deck) { QString group = PlayerManager::groupForDeck(deck - 1); @@ -1212,6 +1306,10 @@ bool MixxxMainWindow::eventFilter(QObject* obj, QEvent* event) { QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, isFullScreenNow); createMenuBar(); connectMenuBar(); + // With a global menu we didn't show the menubar auto-hide dialog + // during (windowed) startup, so ask now. + alwaysHideMenuBarDlg(); + slotUpdateMenuBarAltKeyConnection(); } #endif // This will toggle the Fullscreen checkbox and hide the menubar if diff --git a/src/mixxxmainwindow.h b/src/mixxxmainwindow.h index 48c9b2ed5816..7dea6a8f7dde 100644 --- a/src/mixxxmainwindow.h +++ b/src/mixxxmainwindow.h @@ -78,6 +78,11 @@ class MixxxMainWindow : public QMainWindow { void slotNoAuxiliaryInputConfigured(); void slotNoDeckPassthroughInputConfigured(); void slotNoVinylControlInputConfigured(); +#ifndef __APPLE__ + /// Update whether the menubar is toggled pressing the Alt key and show/hide + /// it accordingly + void slotUpdateMenuBarAltKeyConnection(); +#endif void initializationProgressUpdate(int progress, const QString& serviceName); @@ -105,6 +110,10 @@ class MixxxMainWindow : public QMainWindow { void tryParseAndSetDefaultStyleSheet(); bool confirmExit(); +#ifndef __APPLE__ + void alwaysHideMenuBarDlg(); +#endif + QDialog::DialogCode soundDeviceErrorDlg( const QString &title, const QString &text, bool* retryClicked); QDialog::DialogCode soundDeviceBusyDlg(bool* retryClicked); diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index c91abb8f8385..448a509d87ea 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -147,6 +147,11 @@ DlgPreferences::DlgPreferences( this, &DlgPreferences::reloadUserInterface, Qt::DirectConnection); + connect(pInterfacePage, + &DlgPrefInterface::menuBarAutoHideChanged, + this, + &DlgPreferences::menuBarAutoHideChanged, + Qt::DirectConnection); addPageWidget(PreferencesPage(pInterfacePage, new QTreeWidgetItem( contentsTreeWidget, QTreeWidgetItem::Type)), diff --git a/src/preferences/dialog/dlgpreferences.h b/src/preferences/dialog/dlgpreferences.h index c5412839d98e..e2934170c0d4 100644 --- a/src/preferences/dialog/dlgpreferences.h +++ b/src/preferences/dialog/dlgpreferences.h @@ -75,9 +75,9 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { // Emitted if the user clicks Reset to Defaults. void resetToDefaults(); - signals: void reloadUserInterface(); void tooltipModeChanged(mixxx::TooltipsPreference tooltipMode); + void menuBarAutoHideChanged(); protected: bool eventFilter(QObject*, QEvent*); diff --git a/src/preferences/dialog/dlgprefinterface.cpp b/src/preferences/dialog/dlgprefinterface.cpp index 93519c1baaae..1792a953d91e 100644 --- a/src/preferences/dialog/dlgprefinterface.cpp +++ b/src/preferences/dialog/dlgprefinterface.cpp @@ -34,6 +34,11 @@ const QString kResizableSkinKey = QStringLiteral("ResizableSkin"); const QString kLocaleKey = QStringLiteral("Locale"); const QString kTooltipsKey = QStringLiteral("Tooltips"); const QString kMultiSamplingKey = QStringLiteral("multi_sampling"); +const QString kHideMenuBarKey = QStringLiteral("hide_menubar"); + +// TODO move these to a common *_defs.h file, some are also used by e.g. MixxxMainWindow +const bool kStartInFullscreenDefault = false; +const bool kHideMenuBarDefault = true; } // namespace @@ -301,7 +306,10 @@ void DlgPrefInterface::slotUpdate() { spinBoxScaleFactor->setMinimum(m_minScaleFactor * 100); checkBoxStartFullScreen->setChecked(m_pConfig->getValue( - ConfigKey(kConfigGroup, kStartInFullscreenKey), false)); + ConfigKey(kConfigGroup, kStartInFullscreenKey), kStartInFullscreenDefault)); + + checkBoxHideMenuBar->setChecked(m_pConfig->getValue( + ConfigKey(kConfigGroup, kHideMenuBarKey), kHideMenuBarDefault)); loadTooltipPreferenceFromConfig(); @@ -324,7 +332,12 @@ void DlgPrefInterface::slotResetToDefaults() { spinBoxScaleFactor->setValue(100); // Don't start in full screen. - checkBoxStartFullScreen->setChecked(false); + checkBoxStartFullScreen->setChecked(kStartInFullscreenDefault); + + // Always show the menu bar + checkBoxHideMenuBar->setChecked(kHideMenuBarDefault); + // Also show the menu bar hint again on next start? + // Use bool member to set [Config],show_menubar_hint to 1 in slotApply() // Inhibit the screensaver comboBoxScreensaver->setCurrentIndex(comboBoxScreensaver->findData( @@ -442,6 +455,10 @@ void DlgPrefInterface::slotApply() { m_pConfig->set(ConfigKey(kConfigGroup, kStartInFullscreenKey), ConfigValue(checkBoxStartFullScreen->isChecked())); + m_pConfig->set(ConfigKey(kConfigGroup, kHideMenuBarKey), + ConfigValue(checkBoxHideMenuBar->isChecked())); + emit menuBarAutoHideChanged(); + m_pConfig->set(ConfigKey(kControlsGroup, kTooltipsKey), ConfigValue(static_cast(m_tooltipMode))); emit tooltipModeChanged(m_tooltipMode); diff --git a/src/preferences/dialog/dlgprefinterface.h b/src/preferences/dialog/dlgprefinterface.h index bd735d949c94..6256fb0f18dd 100644 --- a/src/preferences/dialog/dlgprefinterface.h +++ b/src/preferences/dialog/dlgprefinterface.h @@ -44,6 +44,7 @@ class DlgPrefInterface : public DlgPreferencePage, public Ui::DlgPrefControlsDlg signals: void reloadUserInterface(); + void menuBarAutoHideChanged(); void tooltipModeChanged(mixxx::TooltipsPreference tooltipMode); private: diff --git a/src/preferences/dialog/dlgprefinterfacedlg.ui b/src/preferences/dialog/dlgprefinterfacedlg.ui index 5f3013df1c8f..2b7def9894f4 100644 --- a/src/preferences/dialog/dlgprefinterfacedlg.ui +++ b/src/preferences/dialog/dlgprefinterfacedlg.ui @@ -206,6 +206,24 @@ + + + Menu bar + + + checkBoxHideMenuBar + + + + + + + Auto-hide the menu bar, toggle it with a single Alt key press + + + + + true @@ -224,7 +242,7 @@ - + @@ -259,14 +277,14 @@ - + Screen saver - + @@ -304,6 +322,7 @@ ComboBoxLocale spinBoxScaleFactor checkBoxStartFullScreen + checkBoxHideMenuBar radioButtonTooltipsOff radioButtonTooltipsLibrary radioButtonTooltipsLibraryAndSkin diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 2148f78ad2ef..9fa9e2d91199 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -1,5 +1,9 @@ #include "widget/wmainmenubar.h" +#ifndef __APPLE__ +#include +#include +#endif #include #include "config.h" @@ -82,6 +86,9 @@ WMainMenuBar::WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, void WMainMenuBar::initialize() { // FILE MENU QMenu* pFileMenu = new QMenu(tr("&File"), this); +#ifndef __APPLE__ + connectMenuToSlotShowMenuBar(pFileMenu); +#endif QString loadTrackText = tr("Load Track to Deck &%1"); QString loadTrackStatusText = tr("Loads a track in deck %1"); @@ -130,6 +137,9 @@ void WMainMenuBar::initialize() { // LIBRARY MENU QMenu* pLibraryMenu = new QMenu(tr("&Library"), this); +#ifndef __APPLE__ + connectMenuToSlotShowMenuBar(pLibraryMenu); +#endif QString rescanTitle = tr("&Rescan Library"); QString rescanText = tr("Rescans library folders for changes to tracks."); @@ -195,6 +205,33 @@ void WMainMenuBar::initialize() { QMenu* pViewMenu = new QMenu(tr("&View") + QStringLiteral("\u200C"), this); #else QMenu* pViewMenu = new QMenu(tr("&View"), this); + connectMenuToSlotShowMenuBar(pViewMenu); +#endif + +#ifndef __APPLE__ + // Show menu bar + QString showMenuBarTitle = tr("Auto-hide menu bar"); + QString showMenuBarText = tr("Auto-hide the main menu bar when it's not used."); + auto* pViewAutoHideMenuBar = new QAction(showMenuBarTitle, this); + pViewAutoHideMenuBar->setCheckable(true); + pViewAutoHideMenuBar->setStatusTip(showMenuBarText); + pViewAutoHideMenuBar->setWhatsThis(buildWhatsThis(showMenuBarTitle, showMenuBarText)); + connect(pViewAutoHideMenuBar, + &QAction::triggered, + this, + &WMainMenuBar::slotAutoHideMenuBarToggled); + // update checked state from config each time the menu is shown + connect(pViewMenu, + &QMenu::aboutToShow, + this, + [this, pViewAutoHideMenuBar]() { + bool autoHide = m_pConfig->getValue( + ConfigKey("[Config]", "hide_menubar"), false); + pViewAutoHideMenuBar->setChecked(autoHide); + }); + pViewMenu->addAction(pViewAutoHideMenuBar); + + pViewMenu->addSeparator(); #endif // Skin Settings Menu @@ -278,7 +315,6 @@ void WMainMenuBar::initialize() { ConfigKey(kSkinGroup, QStringLiteral("show_library_coverart"))); pViewMenu->addAction(pViewShowCoverArt); - QString maximizeLibraryTitle = tr("Maximize Library"); QString maximizeLibraryText = tr("Maximize the track library to take up all the available screen space.") + " " + mayNotBeSupported; @@ -334,6 +370,9 @@ void WMainMenuBar::initialize() { // OPTIONS MENU QMenu* pOptionsMenu = new QMenu(tr("&Options"), this); +#ifndef __APPLE__ + connectMenuToSlotShowMenuBar(pOptionsMenu); +#endif #ifdef __VINYLCONTROL__ QMenu* pVinylControlMenu = new QMenu(tr("&Vinyl Control"), this); @@ -453,6 +492,9 @@ void WMainMenuBar::initialize() { // DEVELOPER MENU if (CmdlineArgs::Instance().getDeveloper()) { QMenu* pDeveloperMenu = new QMenu(tr("&Developer"), this); +#ifndef __APPLE__ + connectMenuToSlotShowMenuBar(pDeveloperMenu); +#endif QString reloadSkinTitle = tr("&Reload Skin"); QString reloadSkinText = tr("Reload the skin"); @@ -554,6 +596,9 @@ void WMainMenuBar::initialize() { // HELP MENU QMenu* pHelpMenu = new QMenu(tr("&Help"), this); +#ifndef __APPLE__ + connectMenuToSlotShowMenuBar(pHelpMenu); +#endif QString externalLinkSuffix; #ifndef __APPLE__ @@ -662,6 +707,32 @@ void WMainMenuBar::initialize() { pHelpMenu->addAction(pHelpAboutApp); addMenu(pHelpMenu); + +#ifndef __APPLE__ + // Watch focus changes to hide the menubar as soon as all menus are closed, + // e.g. when an action was triggered or when all menus are closed by pressing + // Escape or clicking anywhere else + connect(qApp, + &QApplication::focusWindowChanged, + this, + [this]() { + if (!isNativeMenuBar() && height() > 0 && !activeAction()) { + hideMenuBar(); + } + }); + // ... and when the focus widget changes (main window or dialogs) + connect(qApp, + // This would work, too, but unfortunately this is also emitted on + // leaveEvent of WStarRating in the library. + // &QApplication::focusObjectChanged, + &QApplication::focusChanged, + this, + [this]() { + if (!isNativeMenuBar() && height() > 0 && !activeAction()) { + hideMenuBar(); + } + }); +#endif } void WMainMenuBar::onKeywheelChange(int state) { @@ -702,9 +773,91 @@ void WMainMenuBar::onDeveloperToolsHidden() { } void WMainMenuBar::onFullScreenStateChange(bool fullscreen) { +#ifndef __APPLE__ + // always try to hide the menubar when we switched + hideMenuBar(); +#endif emit internalFullScreenStateChange(fullscreen); } +#ifndef __APPLE__ +void WMainMenuBar::connectMenuToSlotShowMenuBar(const QMenu* pMenu) { + // If a menu hotkey like Alt+F(ile) is pressed while the menubar is hidden, + // show the menubar and open the requested menu. See showMenuBar() for details. + + // NOTE(ronso0) Test with xfwm4 and other window managers if you change this + // code or think you found alternative ways to toggle the menubar! + // In Gnome for example, when highlighting (pressed Alt only) or activating + // menus (Alt combos), the menubar receives focusIn events (even while it is + // hidden), and focusOut events respectively when closing menus. Hence we + // could simply show/hide the menubar in QMenuBar::focusInEvent() and focusoutEvent(). + // However, with xfwm4 for example, menus are activated directly, i.e. the + // menubar doesn't receive focus change events. + connect(pMenu, + &QMenu::aboutToShow, + this, + [this]() { + if (!isNativeMenuBar() && height() <= 0) { + showMenuBar(); + } + }); +} + +void WMainMenuBar::slotToggleMenuBar() { + if (isNativeMenuBar()) { + return; + } + + if (height() > 0) { + hideMenuBar(); + } else { + showMenuBar(); + } +} + +void WMainMenuBar::showMenuBar() { + if (isNativeMenuBar()) { + return; + } + // Note: the resulting resizeEvent() calls QMenuBarPrivate::updateGeometries + // which resets currentAction() to nullptr. So in case the menubar is shown + // in response to a specific menu hotkey (Alt-F), or pressing Alt opens the + // first menu (File) (depends on OS / window manager), the current action + // will be reset to null and menus can't be switched with Left/Right keys + // anymore, i.e. we'd be stuck in the triggered menu. + // Workaround: reselect the active action after unhiding. It can be none, + // user-requested (hotkey) or auto-selected (first menu's first action). + QAction* pAct = activeAction(); + setMinimumHeight(sizeHint().height()); + // If there was a menu selected before, reselect that. + // Note: with nullptr this would be a no-op. Though, even if no menu is + // selected, hovering a menu would instantly open that (no click required). + if (pAct) { + setActiveAction(pAct); + } + // TODO Alternatively, activate the first menu? + // setActiveAction(pAct ? pAct : actions().first()); +} + +void WMainMenuBar::hideMenuBar() { + if (isNativeMenuBar()) { + return; + } + if (m_pConfig->getValue(ConfigKey("[Config]", "hide_menubar"), false)) { + // don't use setHidden(true) because Alt hotkeys wouldn't work anymore + setFixedHeight(0); + } +} + +void WMainMenuBar::slotAutoHideMenuBarToggled(bool autoHide) { + m_pConfig->setValue(ConfigKey("[Config]", "hide_menubar"), autoHide ? 1 : 0); + // Just in case it was hidden after toggling the menu action + if (!autoHide) { + showMenuBar(); + } +} +#endif + 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 aabe694cd59d..98ec66c698f7 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -36,6 +36,10 @@ class WMainMenuBar : public QMenuBar { public: WMainMenuBar(QWidget* pParent, UserSettingsPointer pConfig, ConfigObject* pKbdConfig); +#ifndef __APPLE__ + void hideMenuBar(); + void showMenuBar(); +#endif public slots: void onLibraryScanStarted(); @@ -50,6 +54,9 @@ class WMainMenuBar : public QMenuBar { void onVinylControlDeckEnabledStateChange(int deck, bool enabled); void onNumberOfDecksChanged(int decks); void onKeywheelChange(int state); +#ifndef __APPLE__ + void slotToggleMenuBar(); +#endif signals: void createCrate(); @@ -82,6 +89,9 @@ class WMainMenuBar : public QMenuBar { void internalOnNewSkinAboutToLoad(); private slots: +#ifndef __APPLE__ + void slotAutoHideMenuBarToggled(bool autoHide); +#endif void slotDeveloperStatsExperiment(bool enable); void slotDeveloperStatsBase(bool enable); void slotDeveloperDebugger(bool toggle); @@ -89,6 +99,11 @@ class WMainMenuBar : public QMenuBar { private: void initialize(); +#ifndef __APPLE__ + /// this ensures the menubar is shown when a menu hotkey is pressed + /// while the menubar is hidden + void connectMenuToSlotShowMenuBar(const QMenu* pMenu); +#endif void createVisibilityControl(QAction* pAction, const ConfigKey& key); UserSettingsPointer m_pConfig;