mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
This page renders the bookmarks as a tree and hook context menu events up to the UI's bookmarks bar context menus to allow editing bookmarks. Users can also drag-and-drop bookmark items around.
234 lines
8.5 KiB
C++
234 lines
8.5 KiB
C++
/*
|
|
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibWebView/Application.h>
|
|
#include <LibWebView/BookmarkStore.h>
|
|
#include <UI/Qt/BookmarksBar.h>
|
|
#include <UI/Qt/Icon.h>
|
|
#include <UI/Qt/Menu.h>
|
|
#include <UI/Qt/StringUtils.h>
|
|
|
|
#include <QAction>
|
|
#include <QMenu>
|
|
#include <QMouseEvent>
|
|
#include <QToolButton>
|
|
#include <qapplication.h>
|
|
|
|
namespace Ladybird {
|
|
|
|
static constexpr int BOOKMARK_BUTTON_MAX_WIDTH = 150;
|
|
static constexpr int BOOKMARK_BUTTON_ICON_SIZE = 16;
|
|
|
|
static void install_menu_event_filter(QObject* filter, QMenu* menu)
|
|
{
|
|
menu->installEventFilter(filter);
|
|
|
|
for (auto* action : menu->actions()) {
|
|
if (auto* submenu = action->menu())
|
|
install_menu_event_filter(filter, submenu);
|
|
}
|
|
}
|
|
|
|
BookmarksBar::BookmarksBar(QWidget* parent)
|
|
: QToolBar(parent)
|
|
{
|
|
setIconSize({ BOOKMARK_BUTTON_ICON_SIZE, BOOKMARK_BUTTON_ICON_SIZE });
|
|
setVisible(WebView::Application::settings().show_bookmarks_bar());
|
|
setMovable(false);
|
|
|
|
installEventFilter(this);
|
|
|
|
rebuild();
|
|
}
|
|
|
|
void BookmarksBar::rebuild()
|
|
{
|
|
for (auto* action : actions()) {
|
|
if (auto* menu = action->menu())
|
|
menu->close();
|
|
}
|
|
|
|
clear();
|
|
|
|
auto set_button_properties = [&](QToolButton* button, QString const& title) {
|
|
button->setText(button->fontMetrics().elidedText(title, Qt::ElideRight, BOOKMARK_BUTTON_MAX_WIDTH - 28));
|
|
button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
button->setMaximumWidth(BOOKMARK_BUTTON_MAX_WIDTH);
|
|
button->installEventFilter(this);
|
|
};
|
|
|
|
for (auto const& item : WebView::Application::the().bookmarks_menu().items()) {
|
|
item.visit(
|
|
[&](NonnullRefPtr<WebView::Action> const& bookmark) {
|
|
if (bookmark->id() != WebView::ActionID::BookmarkItem)
|
|
return;
|
|
|
|
auto* action = create_application_action(*this, *bookmark);
|
|
addAction(action);
|
|
|
|
if (auto* button = as_if<QToolButton>(widgetForAction(action)))
|
|
set_button_properties(button, qstring_from_ak_string(bookmark->text()));
|
|
},
|
|
[&](NonnullRefPtr<WebView::Menu> const& folder) {
|
|
auto title = qstring_from_ak_string(folder->title());
|
|
|
|
auto* submenu = create_application_menu(*this, *folder);
|
|
install_menu_event_filter(this, submenu);
|
|
|
|
auto* action = new QAction(title, this);
|
|
action->setIcon(create_tvg_icon_with_theme_colors("folder", palette()));
|
|
action->setProperty("id", submenu->property("id"));
|
|
action->setProperty("type", submenu->property("type"));
|
|
action->setProperty("target_folder_id", submenu->property("target_folder_id"));
|
|
action->setMenu(submenu);
|
|
addAction(action);
|
|
|
|
if (auto* button = as_if<QToolButton>(widgetForAction(action))) {
|
|
button->setPopupMode(QToolButton::InstantPopup);
|
|
set_button_properties(button, title);
|
|
}
|
|
},
|
|
[](WebView::Separator) {
|
|
});
|
|
}
|
|
}
|
|
|
|
void BookmarksBar::show_context_menu(QPoint position, Optional<WebView::BookmarkItem const&> item, Optional<String const&> target_folder_id)
|
|
{
|
|
if (item.has_value()) {
|
|
m_selected_bookmark_menu_item_id = item->id;
|
|
m_selected_bookmark_menu_target_folder_id = target_folder_id.copy();
|
|
|
|
if (item->is_bookmark())
|
|
bookmark_context_menu().exec(position);
|
|
else if (item->is_folder())
|
|
bookmark_folder_context_menu().exec(position);
|
|
} else {
|
|
m_selected_bookmark_menu_item_id = {};
|
|
m_selected_bookmark_menu_target_folder_id = {};
|
|
|
|
bookmarks_bar_context_menu().exec(position);
|
|
}
|
|
}
|
|
|
|
bool BookmarksBar::eventFilter(QObject* object, QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
auto& mouse_event = as<QMouseEvent>(*event);
|
|
|
|
if (mouse_event.button() == Qt::LeftButton)
|
|
return handle_left_mouse_click(&mouse_event, object);
|
|
if (mouse_event.button() == Qt::MiddleButton)
|
|
return handle_middle_mouse_click(&mouse_event, object);
|
|
if (mouse_event.button() == Qt::RightButton)
|
|
return handle_right_mouse_click(&mouse_event, object);
|
|
}
|
|
|
|
return QToolBar::eventFilter(object, event);
|
|
}
|
|
|
|
bool BookmarksBar::handle_left_mouse_click(QMouseEvent* event, QObject* item)
|
|
{
|
|
if (event->modifiers().testFlag(Qt::ControlModifier))
|
|
return handle_middle_mouse_click(event, item);
|
|
return false;
|
|
}
|
|
|
|
bool BookmarksBar::handle_middle_mouse_click(QMouseEvent* event, QObject* item)
|
|
{
|
|
auto activate_tab = event->modifiers().testFlag(Qt::ShiftModifier) ? Web::HTML::ActivateTab::No : Web::HTML::ActivateTab::Yes;
|
|
|
|
if (auto* button = as_if<QToolButton>(item)) {
|
|
auto* action = button->defaultAction();
|
|
extract_item_properties(action);
|
|
|
|
if (m_selected_bookmark_menu_item_type == "bookmark")
|
|
WebView::Application::the().open_bookmark_in_new_tab(m_selected_bookmark_menu_item_id, activate_tab);
|
|
} else if (auto* menu = as_if<QMenu>(item)) {
|
|
if (auto* action = menu->actionAt(event->pos())) {
|
|
extract_item_properties(action);
|
|
|
|
if (m_selected_bookmark_menu_item_type == "bookmark")
|
|
WebView::Application::the().open_bookmark_in_new_tab(m_selected_bookmark_menu_item_id, activate_tab);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BookmarksBar::handle_right_mouse_click(QMouseEvent* event, QObject* item)
|
|
{
|
|
if (is<BookmarksBar>(item)) {
|
|
m_selected_bookmark_menu_item_id = {};
|
|
m_selected_bookmark_menu_target_folder_id = {};
|
|
|
|
bookmarks_bar_context_menu().exec(event->globalPosition().toPoint());
|
|
} else if (auto* button = as_if<QToolButton>(item)) {
|
|
auto* action = button->defaultAction();
|
|
extract_item_properties(action);
|
|
|
|
if (m_selected_bookmark_menu_item_type == "bookmark")
|
|
bookmark_context_menu().exec(event->globalPosition().toPoint());
|
|
else if (m_selected_bookmark_menu_item_type == "folder")
|
|
bookmark_folder_context_menu().exec(event->globalPosition().toPoint());
|
|
} else if (auto* menu = as_if<QMenu>(item)) {
|
|
if (auto* action = menu->actionAt(event->pos())) {
|
|
QObject* submenu = action->menu();
|
|
extract_item_properties(submenu ?: action);
|
|
}
|
|
|
|
if (m_selected_bookmark_menu_item_type.isEmpty())
|
|
extract_item_properties(menu);
|
|
|
|
// FIXME: We create a temporary context menu parented to the dropdown. Otherwise, Qt complains that the context
|
|
// menu's parent does not match the current topmost popup. It would be nice if we could figure out a way
|
|
// to avoid this duplicated menu.
|
|
QMenu context_menu(menu);
|
|
|
|
if (m_selected_bookmark_menu_item_type == "bookmark")
|
|
repopulate_application_menu(context_menu, context_menu, WebView::Application::the().bookmark_context_menu());
|
|
else if (m_selected_bookmark_menu_item_type == "folder")
|
|
repopulate_application_menu(context_menu, context_menu, WebView::Application::the().bookmark_folder_context_menu());
|
|
|
|
if (!context_menu.isEmpty() && context_menu.exec(event->globalPosition().toPoint()))
|
|
menu->close();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BookmarksBar::extract_item_properties(QObject* item)
|
|
{
|
|
m_selected_bookmark_menu_item_id = ak_string_from_qstring(item->property("id").toString());
|
|
m_selected_bookmark_menu_item_type = item->property("type").toString();
|
|
|
|
if (auto value = ak_string_from_qstring(item->property("target_folder_id").toString()); !value.is_empty())
|
|
m_selected_bookmark_menu_target_folder_id = AK::move(value);
|
|
}
|
|
|
|
QMenu& BookmarksBar::bookmarks_bar_context_menu()
|
|
{
|
|
if (!m_bookmarks_bar_context_menu)
|
|
m_bookmarks_bar_context_menu = create_application_menu(*this, WebView::Application::the().bookmarks_bar_context_menu());
|
|
return *m_bookmarks_bar_context_menu;
|
|
}
|
|
|
|
QMenu& BookmarksBar::bookmark_context_menu()
|
|
{
|
|
if (!m_bookmark_context_menu)
|
|
m_bookmark_context_menu = create_application_menu(*this, WebView::Application::the().bookmark_context_menu());
|
|
return *m_bookmark_context_menu;
|
|
}
|
|
|
|
QMenu& BookmarksBar::bookmark_folder_context_menu()
|
|
{
|
|
if (!m_bookmark_folder_context_menu)
|
|
m_bookmark_folder_context_menu = create_application_menu(*this, WebView::Application::the().bookmark_folder_context_menu());
|
|
return *m_bookmark_folder_context_menu;
|
|
}
|
|
|
|
}
|