Restore the native split host button on non-macOS styles

The merged quick-host control composed a QPushButton and a separate autoRaise arrow QToolButton, which Fusion draws as two adjacent controls. Version one of the quick-host branch used a single QToolButton with MenuButtonPopup, which every style draws as one native split button.

Use that split button on every style that can draw one, and keep the plain primary + settings button pair as the native macOS fallback.
This commit is contained in:
Lukas Brübach 2026-11-08 22:54:56 +01:00
parent 59db732abd
commit 83e451959f
2 changed files with 41 additions and 55 deletions

View file

@ -182,42 +182,35 @@ GameSelector::GameSelector(AbstractClient *_client,
hostGameAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); hostGameAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(hostGameAction, &QAction::triggered, this, &GameSelector::actQuickHost); connect(hostGameAction, &QAction::triggered, this, &GameSelector::actQuickHost);
// A plain QPushButton, so the quick host sits in the same control family as the
// join buttons. A QPushButton does not take its text, tooltip or enabled state from
// an action, so those are mirrored by hand below and in updateHostControlsEnabled().
hostGameButton = new QPushButton(this);
hostGameButton->setObjectName("hostGameButton");
connect(hostGameButton, &QPushButton::clicked, hostGameAction, &QAction::trigger);
hostGameMenu = new QMenu(this); hostGameMenu = new QMenu(this);
hostGameMenu->setObjectName("hostGameMenu"); hostGameMenu->setObjectName("hostGameMenu");
hostGameMenu->addAction(hostGameAction);
gameSettingsAction = hostGameMenu->addAction(QString()); gameSettingsAction = hostGameMenu->addAction(QString());
gameSettingsAction->setObjectName("gameSettingsAction"); gameSettingsAction->setObjectName("gameSettingsAction");
gameSettingsAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); gameSettingsAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(gameSettingsAction, &QAction::triggered, this, &GameSelector::actCreate); connect(gameSettingsAction, &QAction::triggered, this, &GameSelector::actCreate);
// The secondary control: a flat arrow next to the primary button on styles that // A split QToolButton in its native style: the primary half quick-hosts, and the
// support a split control. The native macOS style does not, so there it is a plain // arrow opens the menu with the full form. This is the control on every style that
// sibling button instead (see applyHostGameStyle()). // can draw one; QMacStyle draws the menu half as a second square bezel instead.
hostGameMenuButton = new QToolButton(this); hostGameSplitButton = new QToolButton(this);
hostGameMenuButton->setObjectName("hostGameMenuButton"); hostGameSplitButton->setObjectName("hostGameSplitButton");
hostGameMenuButton->setArrowType(Qt::DownArrow); hostGameSplitButton->setDefaultAction(hostGameAction);
hostGameMenuButton->setAutoRaise(true); hostGameSplitButton->setPopupMode(QToolButton::MenuButtonPopup);
hostGameMenuButton->setPopupMode(QToolButton::InstantPopup); hostGameSplitButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
hostGameMenuButton->setToolButtonStyle(Qt::ToolButtonIconOnly); hostGameSplitButton->setMenu(hostGameMenu);
// The native macOS style has no split rendering, so there the primary stays a plain
// QPushButton next to a plain settings button. A QPushButton does not take its text,
// tooltip or enabled state from an action, so those are mirrored by hand below and
// in updateHostControlsEnabled().
hostGameButton = new QPushButton(this);
hostGameButton->setObjectName("hostGameButton");
connect(hostGameButton, &QPushButton::clicked, hostGameAction, &QAction::trigger);
hostGameSettingsButton = new QPushButton(this); hostGameSettingsButton = new QPushButton(this);
hostGameSettingsButton->setObjectName("hostGameSettingsButton"); hostGameSettingsButton->setObjectName("hostGameSettingsButton");
connect(hostGameSettingsButton, &QPushButton::clicked, gameSettingsAction, &QAction::trigger); connect(hostGameSettingsButton, &QPushButton::clicked, gameSettingsAction, &QAction::trigger);
hostGameButtonWidget = new QWidget(this);
auto *hostGameButtonLayout = new QHBoxLayout(hostGameButtonWidget);
hostGameButtonLayout->setContentsMargins(0, 0, 0, 0);
hostGameButtonLayout->setSpacing(0);
hostGameButtonLayout->addWidget(hostGameButton);
hostGameButtonLayout->addWidget(hostGameMenuButton);
applyHostGameStyle(); applyHostGameStyle();
// The shortcuts belong to the whole room tab, not just the game list, so they also // The shortcuts belong to the whole room tab, not just the game list, so they also
@ -244,7 +237,9 @@ GameSelector::GameSelector(AbstractClient *_client,
} }
buttonLayout->addStretch(); buttonLayout->addStretch();
if (room) { if (room) {
buttonLayout->addWidget(hostGameButtonWidget); // Only the form that matches the active style is visible; hidden widgets take no room.
buttonLayout->addWidget(hostGameSplitButton);
buttonLayout->addWidget(hostGameButton);
buttonLayout->addWidget(hostGameSettingsButton); buttonLayout->addWidget(hostGameSettingsButton);
} }
buttonLayout->addWidget(joinButton); buttonLayout->addWidget(joinButton);
@ -435,23 +430,17 @@ void GameSelector::clearHostError()
void GameSelector::applyHostGameStyle() void GameSelector::applyHostGameStyle()
{ {
if (hostGameButton == nullptr || hostGameMenu == nullptr || hostGameMenuButton == nullptr || if (hostGameSplitButton == nullptr || hostGameButton == nullptr || hostGameSettingsButton == nullptr) {
hostGameSettingsButton == nullptr) {
return; return;
} }
// The primary button always quick-hosts. The native macOS style has no split-button // Every style that can draw a split button gets one. The native macOS style cannot
// rendering (a QToolButton menu becomes a second square bezel), so the secondary // (QMacStyle draws the menu half as a second square bezel), so it gets the plain
// action is a plain sibling button there; every other style gets the split arrow. // primary button and a sibling settings button instead.
if (usesMacStyle()) { const bool macStyle = usesMacStyle();
hostGameMenuButton->setMenu(nullptr); hostGameSplitButton->setVisible(!macStyle);
hostGameMenuButton->setVisible(false); hostGameButton->setVisible(macStyle);
hostGameSettingsButton->setVisible(true); hostGameSettingsButton->setVisible(macStyle);
} else {
hostGameMenuButton->setMenu(hostGameMenu);
hostGameMenuButton->setVisible(true);
hostGameSettingsButton->setVisible(false);
}
// The appearance settings can switch the style while the room is open. // The appearance settings can switch the style while the room is open.
updateHostGameToolTip(); updateHostGameToolTip();
@ -482,8 +471,8 @@ void GameSelector::updateHostControlsEnabled()
if (hostGameButton) { if (hostGameButton) {
hostGameButton->setEnabled(hostControlsEnabled); hostGameButton->setEnabled(hostControlsEnabled);
} }
if (hostGameMenuButton) { if (hostGameSplitButton) {
hostGameMenuButton->setEnabled(hostControlsEnabled); hostGameSplitButton->setEnabled(hostControlsEnabled);
} }
if (hostGameSettingsButton) { if (hostGameSettingsButton) {
hostGameSettingsButton->setEnabled(hostControlsEnabled); hostGameSettingsButton->setEnabled(hostControlsEnabled);
@ -740,10 +729,10 @@ void GameSelector::retranslateUi()
filterButton->setText(tr("&Filter games")); filterButton->setText(tr("&Filter games"));
clearFilterButton->setText(tr("C&lear filter")); clearFilterButton->setText(tr("C&lear filter"));
if (hostGameAction) { if (hostGameAction) {
hostGameAction->setText(tr("Host with last settings")); hostGameAction->setText(tr("Host game"));
if (hostGameButton) { if (hostGameButton) {
// A QPushButton does not draw text from an action; the button keeps the short // A QPushButton does not draw text from an action; on macOS this button is the
// label while the menu item spells out what the remembered settings do. // primary action, so it carries the same label.
hostGameButton->setText(tr("Host game")); hostGameButton->setText(tr("Host game"));
} }
updateHostGameToolTip(); updateHostGameToolTip();
@ -755,11 +744,6 @@ void GameSelector::retranslateUi()
// secondary action, so it carries the same label. // secondary action, so it carries the same label.
hostGameSettingsButton->setText(tr("Game settings…")); hostGameSettingsButton->setText(tr("Game settings…"));
} }
if (hostGameMenuButton) {
// The split arrow has no text of its own; name it for the menu it opens.
hostGameMenuButton->setToolTip(tr("More host options"));
hostGameMenuButton->setAccessibleName(tr("More host options"));
}
} }
joinButton->setText(tr("&Join")); joinButton->setText(tr("&Join"));
joinAsJudgeButton->setText(tr("Join as judge")); joinAsJudgeButton->setText(tr("Join as judge"));
@ -794,6 +778,9 @@ void GameSelector::updateHostGameToolTip()
if (hostGameButton) { if (hostGameButton) {
hostGameButton->setToolTip(quickHostTooltip); hostGameButton->setToolTip(quickHostTooltip);
} }
if (hostGameSplitButton) {
hostGameSplitButton->setToolTip(quickHostTooltip);
}
if (hostGameSettingsButton) { if (hostGameSettingsButton) {
hostGameSettingsButton->setToolTip(tr("Open the game settings before hosting")); hostGameSettingsButton->setToolTip(tr("Open the game settings before hosting"));
} }

View file

@ -164,10 +164,9 @@ private:
QPushButton *filterButton; /**< Button to open the filter dialog. */ QPushButton *filterButton; /**< Button to open the filter dialog. */
QPushButton *clearFilterButton; /**< Button to clear active filters. */ QPushButton *clearFilterButton; /**< Button to clear active filters. */
QPushButton *hostGameButton = nullptr; /**< Host button, in the same family as the join buttons. */ QToolButton *hostGameSplitButton = nullptr; /**< Split host button, on every style that can draw one. */
QPushButton *hostGameButton = nullptr; /**< Primary quick-host button, on the native macOS style only. */
QPushButton *hostGameSettingsButton = nullptr; /**< Opens the host form; a sibling button on macOS. */ QPushButton *hostGameSettingsButton = nullptr; /**< Opens the host form; a sibling button on macOS. */
QToolButton *hostGameMenuButton = nullptr; /**< Split arrow that opens the host menu (non-macOS styles only). */
QWidget *hostGameButtonWidget = nullptr; /**< Container that keeps the split button and its arrow together. */
QMenu *hostGameMenu = nullptr; /**< Menu behind the split arrow (non-macOS styles only). */ QMenu *hostGameMenu = nullptr; /**< Menu behind the split arrow (non-macOS styles only). */
QAction *hostGameAction = nullptr; /**< Quick-host action, carrying the keyboard shortcut. */ QAction *hostGameAction = nullptr; /**< Quick-host action, carrying the keyboard shortcut. */
QAction *gameSettingsAction = nullptr; /**< Opens the host form with the current settings. */ QAction *gameSettingsAction = nullptr; /**< Opens the host form with the current settings. */
@ -193,9 +192,9 @@ private:
/** /**
* @brief Rebuilds the host control for the active style. * @brief Rebuilds the host control for the active style.
* *
* The primary button always quick-hosts. Every other style gets the split button, * The primary button always quick-hosts. Every style that can draw a split control
* whose arrow opens the menu; the native macOS style has no split control, so it * gets one, whose arrow opens the menu; the native macOS style has no split control,
* gets a plain sibling button for the secondary action instead. * so it gets a plain sibling button for the secondary action instead.
*/ */
void applyHostGameStyle(); void applyHostGameStyle();
/** @brief Marks a quick-host command as in flight or finished, then updates the host controls. */ /** @brief Marks a quick-host command as in flight or finished, then updates the host controls. */