Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

toggle the menubar with single Alt key press (auto hide) #11526

Merged
merged 12 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 58 additions & 19 deletions src/controllers/keyboard/keyboardeventfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ KeyboardEventFilter::KeyboardEventFilter(ConfigObject<ConfigValueKbd>* pKbdConfi
QObject* parent,
const char* name)
: QObject(parent),
#ifndef __APPLE__
m_altPressedWithoutKey(false),
#endif
m_pKbdConfigObject(nullptr) {
setObjectName(name);
setKeyboardConfig(pKbdConfigObject);
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions src/controllers/keyboard/keyboardeventfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand Down
100 changes: 99 additions & 1 deletion src/mixxxmainwindow.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "mixxxmainwindow.h"

#include <QCheckBox>
#include <QCloseEvent>
#include <QDebug>
#include <QFileDialog>
Expand Down Expand Up @@ -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<mixxx::CoreServices> pCoreServices)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -527,6 +553,51 @@ void MixxxMainWindow::initializeWindow() {
slotUpdateWindowTitle(TrackPointer());
}

#ifndef __APPLE__
void MixxxMainWindow::alwaysHideMenuBarDlg() {
if (!m_pCoreServices->getSettings()->getValue<bool>(
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 <b> (bold text) and <br> (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 <b>Alt</b> key.<br><br>"
"Click <b>%1</b> to agree.<br><br>"
"Click <b>%2</b> to disable that, for example if you don't use Mixxx "
"with a keyboard.<br><br>"
"You can change this setting any time in Preferences -> Interface."
"<br>") // 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;
Expand Down Expand Up @@ -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<bool>(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);

Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/mixxxmainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/preferences/dialog/dlgpreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion src/preferences/dialog/dlgpreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*);
Expand Down
21 changes: 19 additions & 2 deletions src/preferences/dialog/dlgprefinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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();

Expand All @@ -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(
Expand Down Expand Up @@ -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<int>(m_tooltipMode)));
emit tooltipModeChanged(m_tooltipMode);
Expand Down
1 change: 1 addition & 0 deletions src/preferences/dialog/dlgprefinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class DlgPrefInterface : public DlgPreferencePage, public Ui::DlgPrefControlsDlg

signals:
void reloadUserInterface();
void menuBarAutoHideChanged();
void tooltipModeChanged(mixxx::TooltipsPreference tooltipMode);

private:
Expand Down
Loading