From 17fc995ee403fa0fb4cf3c8e9cfa0367aedec8a2 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sun, 28 Apr 2024 15:07:23 +0100 Subject: [PATCH] Ladybird: Add a context menu to the tab bar This shows the following actions: * Reload Tab * Duplicate Tab * Move Tab * Move to Start * Move to End * Close Tab * Close Other Tabs * Close Tabs to Left * Close Tabs to Right * Close Other Tabs --- Ladybird/Qt/BrowserWindow.cpp | 5 +++ Ladybird/Qt/BrowserWindow.h | 2 ++ Ladybird/Qt/Tab.cpp | 64 +++++++++++++++++++++++++++++++++++ Ladybird/Qt/Tab.h | 4 +++ Ladybird/Qt/TabBar.cpp | 19 ++++++++++- Ladybird/Qt/TabBar.h | 5 +++ 6 files changed, 98 insertions(+), 1 deletion(-) diff --git a/Ladybird/Qt/BrowserWindow.cpp b/Ladybird/Qt/BrowserWindow.cpp index 0206641bc50..b7ba4a31237 100644 --- a/Ladybird/Qt/BrowserWindow.cpp +++ b/Ladybird/Qt/BrowserWindow.cpp @@ -625,6 +625,11 @@ void BrowserWindow::close_tab(int index) close(); } +void BrowserWindow::move_tab(int old_index, int new_index) +{ + m_tabs_container->tabBar()->moveTab(old_index, new_index); +} + void BrowserWindow::open_file() { m_current_tab->open_file(); diff --git a/Ladybird/Qt/BrowserWindow.h b/Ladybird/Qt/BrowserWindow.h index 22f7e9d8af6..5f4662d5f8c 100644 --- a/Ladybird/Qt/BrowserWindow.h +++ b/Ladybird/Qt/BrowserWindow.h @@ -34,6 +34,7 @@ public: WebContentView& view() const { return m_current_tab->view(); } + int tab_count() { return m_tabs_container->count(); } int tab_index(Tab*); Tab& create_new_tab(Web::HTML::ActivateTab activate_tab); @@ -98,6 +99,7 @@ public slots: Tab& new_child_tab(Web::HTML::ActivateTab, Tab& parent, Web::HTML::WebViewHints, Optional page_index); void activate_tab(int index); void close_tab(int index); + void move_tab(int old_index, int new_index); void close_current_tab(); void open_next_tab(); void open_previous_tab(); diff --git a/Ladybird/Qt/Tab.cpp b/Ladybird/Qt/Tab.cpp index 9d4bc00d231..c7a69ab00d4 100644 --- a/Ladybird/Qt/Tab.cpp +++ b/Ladybird/Qt/Tab.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, Andreas Kling * Copyright (c) 2022, Matthew Costa + * Copyright (c) 2024, Jamie Mansfield * * SPDX-License-Identifier: BSD-2-Clause */ @@ -405,6 +406,69 @@ Tab::Tab(BrowserWindow* window, WebContentOptions const& web_content_options, St emit navigation_buttons_state_changed(tab_index()); }; + auto* reload_tab_action = new QAction("&Reload Tab", this); + QObject::connect(reload_tab_action, &QAction::triggered, this, [this]() { + reload(); + }); + + auto* duplicate_tab_action = new QAction("&Duplicate Tab", this); + QObject::connect(duplicate_tab_action, &QAction::triggered, this, [this]() { + m_window->new_tab_from_url(view().url(), Web::HTML::ActivateTab::Yes); + }); + + auto* move_to_start_action = new QAction("Move to &Start", this); + QObject::connect(move_to_start_action, &QAction::triggered, this, [this]() { + m_window->move_tab(tab_index(), 0); + }); + + auto* move_to_end_action = new QAction("Move to &End", this); + QObject::connect(move_to_end_action, &QAction::triggered, this, [this]() { + m_window->move_tab(tab_index(), m_window->tab_count() - 1); + }); + + auto* close_tab_action = new QAction("&Close Tab", this); + QObject::connect(close_tab_action, &QAction::triggered, this, [this]() { + view().on_close(); + }); + + auto* close_tabs_to_left_action = new QAction("C&lose Tabs to Left", this); + QObject::connect(close_tabs_to_left_action, &QAction::triggered, this, [this]() { + for (auto i = tab_index() - 1; i >= 0; i--) { + m_window->close_tab(i); + } + }); + + auto* close_tabs_to_right_action = new QAction("Close Tabs to R&ight", this); + QObject::connect(close_tabs_to_right_action, &QAction::triggered, this, [this]() { + for (auto i = m_window->tab_count() - 1; i > tab_index(); i--) { + m_window->close_tab(i); + } + }); + + auto* close_other_tabs_action = new QAction("Cl&ose Other Tabs", this); + QObject::connect(close_other_tabs_action, &QAction::triggered, this, [this]() { + for (auto i = m_window->tab_count() - 1; i >= 0; i--) { + if (i == tab_index()) + continue; + + m_window->close_tab(i); + } + }); + + m_context_menu = new QMenu("Context menu", this); + m_context_menu->addAction(reload_tab_action); + m_context_menu->addAction(duplicate_tab_action); + m_context_menu->addSeparator(); + auto* move_tab_menu = m_context_menu->addMenu("Mo&ve Tab"); + move_tab_menu->addAction(move_to_start_action); + move_tab_menu->addAction(move_to_end_action); + m_context_menu->addSeparator(); + m_context_menu->addAction(close_tab_action); + auto* close_multiple_tabs_menu = m_context_menu->addMenu("Close &Multiple Tabs"); + close_multiple_tabs_menu->addAction(close_tabs_to_left_action); + close_multiple_tabs_menu->addAction(close_tabs_to_right_action); + close_multiple_tabs_menu->addAction(close_other_tabs_action); + auto* search_selected_text_action = new QAction("&Search for ", this); search_selected_text_action->setIcon(load_icon_from_uri("resource://icons/16x16/find.png"sv)); QObject::connect(search_selected_text_action, &QAction::triggered, this, [this]() { diff --git a/Ladybird/Qt/Tab.h b/Ladybird/Qt/Tab.h index b7af225e2b7..79bd1da6826 100644 --- a/Ladybird/Qt/Tab.h +++ b/Ladybird/Qt/Tab.h @@ -54,6 +54,8 @@ public: QIcon const& favicon() const { return m_favicon; } QString const& title() const { return m_title; } + QMenu* context_menu() const { return m_context_menu; } + void update_navigation_buttons_state(); public slots: @@ -91,6 +93,8 @@ private: QLabel* m_hover_label { nullptr }; QIcon m_favicon; + QMenu* m_context_menu { nullptr }; + QMenu* m_page_context_menu { nullptr }; Optional m_page_context_menu_search_text; diff --git a/Ladybird/Qt/TabBar.cpp b/Ladybird/Qt/TabBar.cpp index 0acf2d8f7a0..bbcf1eb9b93 100644 --- a/Ladybird/Qt/TabBar.cpp +++ b/Ladybird/Qt/TabBar.cpp @@ -1,16 +1,25 @@ /* * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024, Jamie Mansfield * * SPDX-License-Identifier: BSD-2-Clause */ +#include "Tab.h" #include +#include #include +#include #include #include namespace Ladybird { +TabBar::TabBar(QWidget* parent) + : QTabBar(parent) +{ +} + QSize TabBar::tabSizeHint(int index) const { auto width = this->width() / count(); @@ -22,11 +31,19 @@ QSize TabBar::tabSizeHint(int index) const return hint; } +void TabBar::contextMenuEvent(QContextMenuEvent* event) +{ + auto* tab_widget = verify_cast(this->parent()); + auto* tab = verify_cast(tab_widget->widget(tabAt(event->pos()))); + if (tab) + tab->context_menu()->exec(event->globalPos()); +} + TabWidget::TabWidget(QWidget* parent) : QTabWidget(parent) { // This must be called first, otherwise several of the options below have no effect. - setTabBar(new TabBar()); + setTabBar(new TabBar(this)); setDocumentMode(true); setElideMode(Qt::TextElideMode::ElideRight); diff --git a/Ladybird/Qt/TabBar.h b/Ladybird/Qt/TabBar.h index 07c9a15ee8c..75cde7fa79e 100644 --- a/Ladybird/Qt/TabBar.h +++ b/Ladybird/Qt/TabBar.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024, Jamie Mansfield * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,6 +11,7 @@ #include #include +class QContextMenuEvent; class QEvent; class QIcon; class QWidget; @@ -20,7 +22,10 @@ class TabBar : public QTabBar { Q_OBJECT public: + explicit TabBar(QWidget* parent = nullptr); + virtual QSize tabSizeHint(int index) const override; + virtual void contextMenuEvent(QContextMenuEvent* event) override; }; class TabWidget : public QTabWidget {