UI/Qt: Correctly adjust hamburger menu positioning on first open

The HamburgerMenu subclass overrode showEvent() to reposition the menu
using move() after QMenu::popup() had already shown it. On the first
open, rect().width() could return a stale value, causing the menu to
appear detached from the button and truncated to a single item. The
move() call also bypassed Qt's screen boundary adjustments.

Instead, we can replace the HamburgerMenu with a normal QMenu, and use a
custom HamburgerButton class for the menu's tool button. In this class,
we override mousePressEvent() to call popup() with the correct right-
aligned position before the menu is shown. This lets Qt manage sizing
and screen boundaries correctly.
This commit is contained in:
Timothy Flynn 2026-03-16 13:16:32 -04:00 committed by Tim Flynn
parent f213ef455d
commit ebaad09b1b
2 changed files with 43 additions and 24 deletions

View file

@ -160,28 +160,6 @@ static QIcon const& app_icon()
return icon;
}
class HamburgerMenu : public QMenu {
public:
using QMenu::QMenu;
virtual ~HamburgerMenu() override = default;
virtual void showEvent(QShowEvent*) override
{
if (!isVisible())
return;
auto* browser_window = as<BrowserWindow>(parentWidget());
if (!browser_window)
return;
auto* current_tab = browser_window->current_tab();
if (!current_tab)
return;
// Ensure the hamburger menu placed within the browser window.
auto* hamburger_button = current_tab->hamburger_button();
auto button_top_right = hamburger_button->mapToGlobal(hamburger_button->rect().bottomRight());
move(button_top_right - QPoint(rect().width(), 0));
}
};
BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
: m_tabs_container(new TabWidget(this))
, m_new_tab_button_toolbar(new QToolBar("New Tab", m_tabs_container))
@ -217,7 +195,7 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, IsPopupWindow
});
}
m_hamburger_menu = new HamburgerMenu(this);
m_hamburger_menu = new QMenu(this);
if (!Settings::the()->show_menubar())
menuBar()->hide();

View file

@ -29,6 +29,40 @@
namespace Ladybird {
class HamburgerButton final : public QToolButton {
public:
using QToolButton::QToolButton;
protected:
virtual void mousePressEvent(QMouseEvent* event) override
{
if (event->button() == Qt::LeftButton)
show_menu();
else
QToolButton::mousePressEvent(event);
}
virtual void keyPressEvent(QKeyEvent* event) override
{
if (first_is_one_of(event->key(), Qt::Key_Select, Qt::Key_Space))
show_menu();
else
QToolButton::keyPressEvent(event);
}
private:
void show_menu()
{
auto* menu = this->menu();
VERIFY(menu);
auto bottom_right = mapToGlobal(rect().bottomRight());
auto menu_width = menu->sizeHint().width();
menu->popup(QPoint { bottom_right.x() - menu_width, bottom_right.y() });
}
};
static QIcon default_favicon()
{
static QIcon icon = load_icon_from_uri("resource://icons/48x48/app-browser.png"sv);
@ -70,7 +104,7 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
m_layout->addWidget(m_view);
m_layout->addWidget(m_find_in_page);
m_hamburger_button = new QToolButton(m_toolbar);
m_hamburger_button = new HamburgerButton(m_toolbar);
m_hamburger_button->setText("Show &Menu");
m_hamburger_button->setToolTip("Show Menu");
m_hamburger_button->setIcon(create_tvg_icon_with_theme_colors("hamburger", palette()));
@ -78,6 +112,13 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
m_hamburger_button->setMenu(&m_window->hamburger_menu());
m_hamburger_button->setStyleSheet(":menu-indicator {image: none}");
QObject::connect(&m_window->hamburger_menu(), &QMenu::aboutToShow, m_hamburger_button, [this]() {
m_hamburger_button->setDown(true);
});
QObject::connect(&m_window->hamburger_menu(), &QMenu::aboutToHide, m_hamburger_button, [this]() {
m_hamburger_button->setDown(false);
});
m_navigate_back_action = create_application_action(*this, view().navigate_back_action());
m_navigate_forward_action = create_application_action(*this, view().navigate_forward_action());
m_reload_action = create_application_action(*this, WebView::Application::the().reload_action());