mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
fix: Linux menu bar renders as horizontal tool buttons instead of collapsed extension popup
On Linux, QMenuBar inside a custom title bar widget (setMenuWidget) collapses all items into the extension overflow popup. Replace with QToolButton widgets on Linux that share the same QMenu objects. Includes hover-to-switch behavior via event filter on open menus. Windows and macOS paths are unchanged — guarded by #ifdef __linux__ and runtime m_useToolButtons flag.
This commit is contained in:
@@ -615,6 +615,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
||||
createSymbolsDock();
|
||||
|
||||
createMenus();
|
||||
if (m_titleBar) m_titleBar->finalizeMenuBar();
|
||||
createStatusBar();
|
||||
|
||||
// Eliminate gap between central widget and status bar
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
#include <QWindow>
|
||||
|
||||
namespace rcx {
|
||||
@@ -25,11 +26,23 @@ TitleBarWidget::TitleBarWidget(QWidget* parent)
|
||||
m_appLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
layout->addWidget(m_appLabel);
|
||||
|
||||
// Menu bar
|
||||
// Menu bar — hidden on Linux; visible on Windows.
|
||||
// On Linux, QMenuBar inside a custom widget collapses all items into an
|
||||
// extension popup. We keep it hidden and mirror its menus as QToolButtons
|
||||
// via finalizeMenuBar() after createMenus() populates it.
|
||||
m_menuBar = new QMenuBar(this);
|
||||
m_menuBar->setNativeMenuBar(false);
|
||||
#ifdef __linux__
|
||||
m_useToolButtons = true;
|
||||
m_menuBar->hide();
|
||||
m_menuBtnLayout = new QHBoxLayout;
|
||||
m_menuBtnLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_menuBtnLayout->setSpacing(0);
|
||||
layout->addLayout(m_menuBtnLayout);
|
||||
#else
|
||||
m_menuBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
layout->addWidget(m_menuBar);
|
||||
#endif
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
@@ -116,6 +129,17 @@ void TitleBarWidget::applyTheme(const Theme& theme) {
|
||||
m_btnMin->setStyleSheet(btnStyle);
|
||||
m_btnMax->setStyleSheet(btnStyle);
|
||||
|
||||
// Linux menu tool buttons
|
||||
if (m_useToolButtons) {
|
||||
QString menuBtnStyle = QStringLiteral(
|
||||
"QToolButton { background: transparent; border: none; padding: 0 8px; color: %1; }"
|
||||
"QToolButton:hover { background: %2; }"
|
||||
"QToolButton::menu-indicator { image: none; }")
|
||||
.arg(theme.text.name(), theme.hover.name());
|
||||
for (auto* btn : m_menuButtons)
|
||||
btn->setStyleSheet(menuBtnStyle);
|
||||
}
|
||||
|
||||
// Close button: themed red hover
|
||||
m_btnClose->setStyleSheet(QStringLiteral(
|
||||
"QToolButton { background: transparent; border: none; }"
|
||||
@@ -164,6 +188,58 @@ void TitleBarWidget::setMenuBarTitleCase(bool titleCase) {
|
||||
action->setText("&" + result);
|
||||
}
|
||||
}
|
||||
// Sync tool button labels on Linux
|
||||
if (m_useToolButtons) {
|
||||
auto actions = m_menuBar->actions();
|
||||
for (int i = 0; i < m_menuButtons.size() && i < actions.size(); ++i)
|
||||
m_menuButtons[i]->setText(actions[i]->text());
|
||||
}
|
||||
}
|
||||
|
||||
void TitleBarWidget::finalizeMenuBar() {
|
||||
if (!m_useToolButtons) return;
|
||||
// Create a QToolButton for each top-level menu in the hidden QMenuBar
|
||||
for (auto* action : m_menuBar->actions()) {
|
||||
if (!action->menu()) continue;
|
||||
auto* btn = new QToolButton(this);
|
||||
btn->setText(action->text());
|
||||
btn->setMenu(action->menu());
|
||||
btn->setPopupMode(QToolButton::InstantPopup);
|
||||
btn->setAutoRaise(true);
|
||||
btn->setFocusPolicy(Qt::NoFocus);
|
||||
btn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
btn->setStyleSheet(QStringLiteral(
|
||||
"QToolButton { background: transparent; border: none; padding: 0 8px; }"
|
||||
"QToolButton:hover { background: %1; }"
|
||||
"QToolButton::menu-indicator { image: none; }")
|
||||
.arg(m_theme.hover.name()));
|
||||
btn->installEventFilter(this);
|
||||
btn->menu()->installEventFilter(this);
|
||||
m_menuBtnLayout->addWidget(btn);
|
||||
m_menuButtons.append(btn);
|
||||
}
|
||||
}
|
||||
|
||||
bool TitleBarWidget::eventFilter(QObject* obj, QEvent* event) {
|
||||
if (!m_useToolButtons) return QWidget::eventFilter(obj, event);
|
||||
|
||||
// Watch for mouse movement inside an open QMenu — if the cursor moves
|
||||
// over a sibling menu button, close this menu and open the other.
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
auto* menu = qobject_cast<QMenu*>(obj);
|
||||
if (!menu || !menu->isVisible()) return false;
|
||||
QPoint globalPos = QCursor::pos();
|
||||
for (auto* btn : m_menuButtons) {
|
||||
if (btn->menu() == menu) continue;
|
||||
QRect btnRect(btn->mapToGlobal(QPoint(0, 0)), btn->size());
|
||||
if (btnRect.contains(globalPos)) {
|
||||
menu->close();
|
||||
QTimer::singleShot(0, btn, [btn]() { btn->showMenu(); });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void TitleBarWidget::updateMaximizeIcon() {
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
void setShowIcon(bool show);
|
||||
void setMenuBarTitleCase(bool titleCase);
|
||||
bool menuBarTitleCase() const { return m_titleCase; }
|
||||
void finalizeMenuBar();
|
||||
|
||||
void updateMaximizeIcon();
|
||||
|
||||
@@ -25,16 +26,20 @@ protected:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
|
||||
private:
|
||||
QLabel* m_appLabel = nullptr;
|
||||
QMenuBar* m_menuBar = nullptr;
|
||||
QHBoxLayout* m_menuBtnLayout = nullptr;
|
||||
QVector<QToolButton*> m_menuButtons;
|
||||
QToolButton* m_btnMin = nullptr;
|
||||
QToolButton* m_btnMax = nullptr;
|
||||
QToolButton* m_btnClose = nullptr;
|
||||
|
||||
Theme m_theme;
|
||||
bool m_titleCase = false;
|
||||
bool m_useToolButtons = false;
|
||||
|
||||
QToolButton* makeChromeButton(const QString& iconPath);
|
||||
void toggleMaximize();
|
||||
|
||||
Reference in New Issue
Block a user