mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-23 09:48:56 +00:00
LibWebView+UI: Add structures to hold context menu and action data
We currently duplicate a lot of code to handle application/context menus and actions. The goal here is to hold the data for the menus and actions in LibWebView. Each UI will then be able to generate menus from the data on-the-fly. The structures added here are meant to support generic and checkable actions, action groups, submenus, etc.
This commit is contained in:
parent
2632b1375b
commit
a5be0f0a18
Notes:
github-actions[bot]
2025-09-11 18:25:23 +00:00
Author: https://github.com/trflynn89
Commit: a5be0f0a18
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6062
10 changed files with 619 additions and 1 deletions
|
@ -11,6 +11,7 @@ set(SOURCES
|
|||
DOMNodeProperties.cpp
|
||||
HeadlessWebView.cpp
|
||||
HelperProcess.cpp
|
||||
Menu.cpp
|
||||
Mutation.cpp
|
||||
Plugins/FontPlugin.cpp
|
||||
Plugins/ImageCodecPlugin.cpp
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
|
||||
namespace WebView {
|
||||
|
||||
class Action;
|
||||
class Application;
|
||||
class Autocomplete;
|
||||
class CookieJar;
|
||||
class Database;
|
||||
class Menu;
|
||||
class OutOfProcessWebView;
|
||||
class ProcessManager;
|
||||
class Settings;
|
||||
|
|
126
Libraries/LibWebView/Menu.cpp
Normal file
126
Libraries/LibWebView/Menu.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWebView/Menu.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
NonnullRefPtr<Action> Action::create(Variant<StringView, String> text, ActionID id, Function<void()> action)
|
||||
{
|
||||
return adopt_ref(*new Action { move(text), id, move(action) });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Action> Action::create_checkable(Variant<StringView, String> text, ActionID id, Function<void()> action)
|
||||
{
|
||||
auto checkable = create(move(text), id, move(action));
|
||||
checkable->m_checked = false;
|
||||
return checkable;
|
||||
}
|
||||
|
||||
void Action::set_text(Variant<StringView, String> text)
|
||||
{
|
||||
if (text.visit([&](auto const& text) { return text == this->text(); }))
|
||||
return;
|
||||
m_text = move(text);
|
||||
|
||||
for (auto& observer : m_observers)
|
||||
observer->on_text_changed(*this);
|
||||
}
|
||||
|
||||
void Action::set_tooltip(StringView tooltip)
|
||||
{
|
||||
if (m_tooltip == tooltip)
|
||||
return;
|
||||
m_tooltip = tooltip;
|
||||
|
||||
for (auto& observer : m_observers)
|
||||
observer->on_tooltip_changed(*this);
|
||||
}
|
||||
|
||||
void Action::set_enabled(bool enabled)
|
||||
{
|
||||
if (m_enabled == enabled)
|
||||
return;
|
||||
m_enabled = enabled;
|
||||
|
||||
for (auto& observer : m_observers)
|
||||
observer->on_enabled_state_changed(*this);
|
||||
}
|
||||
|
||||
void Action::set_visible(bool visible)
|
||||
{
|
||||
if (m_visible == visible)
|
||||
return;
|
||||
m_visible = visible;
|
||||
|
||||
for (auto& observer : m_observers)
|
||||
observer->on_visible_state_changed(*this);
|
||||
}
|
||||
|
||||
void Action::set_checked(bool checked)
|
||||
{
|
||||
set_checked_internal(checked);
|
||||
|
||||
if (auto group = m_group.strong_ref()) {
|
||||
group->for_each_action([&](Action& action) {
|
||||
if (action.is_checkable() && &action != this)
|
||||
action.set_checked_internal(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Action::set_checked_internal(bool checked)
|
||||
{
|
||||
VERIFY(is_checkable());
|
||||
|
||||
if (m_checked == checked)
|
||||
return;
|
||||
m_checked = checked;
|
||||
|
||||
for (auto& observer : m_observers)
|
||||
observer->on_checked_state_changed(*this);
|
||||
}
|
||||
|
||||
void Action::add_observer(NonnullOwnPtr<Observer> observer)
|
||||
{
|
||||
observer->on_text_changed(*this);
|
||||
if (m_tooltip.has_value())
|
||||
observer->on_tooltip_changed(*this);
|
||||
observer->on_enabled_state_changed(*this);
|
||||
observer->on_visible_state_changed(*this);
|
||||
if (is_checkable())
|
||||
observer->on_checked_state_changed(*this);
|
||||
|
||||
m_observers.append(move(observer));
|
||||
}
|
||||
|
||||
void Action::remove_observer(Observer const& observer)
|
||||
{
|
||||
m_observers.remove_first_matching([&](auto const& candidate) {
|
||||
return candidate.ptr() == &observer;
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Menu> Menu::create(StringView name)
|
||||
{
|
||||
return adopt_ref(*new Menu { name });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Menu> Menu::create_group(StringView name)
|
||||
{
|
||||
auto menu = create(name);
|
||||
menu->m_is_group = true;
|
||||
return menu;
|
||||
}
|
||||
|
||||
void Menu::add_action(NonnullRefPtr<Action> action)
|
||||
{
|
||||
if (m_is_group)
|
||||
action->set_group({}, *this);
|
||||
m_items.append(move(action));
|
||||
}
|
||||
|
||||
}
|
142
Libraries/LibWebView/Menu.h
Normal file
142
Libraries/LibWebView/Menu.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibWebView/Forward.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
enum class ActionID {
|
||||
};
|
||||
|
||||
class WEBVIEW_API Action
|
||||
: public RefCounted<Action>
|
||||
, public Weakable<Action> {
|
||||
public:
|
||||
static NonnullRefPtr<Action> create(Variant<StringView, String> text, ActionID id, Function<void()> action);
|
||||
static NonnullRefPtr<Action> create_checkable(Variant<StringView, String> text, ActionID id, Function<void()> action);
|
||||
|
||||
void activate() { m_action(); }
|
||||
|
||||
StringView text() const
|
||||
{
|
||||
return m_text.visit([](auto const& text) -> StringView { return text; });
|
||||
}
|
||||
void set_text(Variant<StringView, String>);
|
||||
|
||||
StringView tooltip() const { return *m_tooltip; }
|
||||
void set_tooltip(StringView);
|
||||
|
||||
ActionID id() const { return m_id; }
|
||||
|
||||
bool enabled() const { return m_enabled; }
|
||||
void set_enabled(bool);
|
||||
|
||||
bool visible() const { return m_visible; }
|
||||
void set_visible(bool);
|
||||
|
||||
bool is_checkable() const { return m_checked.has_value(); }
|
||||
bool checked() const { return *m_checked; }
|
||||
void set_checked(bool);
|
||||
|
||||
struct Observer {
|
||||
virtual ~Observer() = default;
|
||||
|
||||
virtual void on_text_changed(Action&) { }
|
||||
virtual void on_tooltip_changed(Action&) { }
|
||||
virtual void on_enabled_state_changed(Action&) { }
|
||||
virtual void on_visible_state_changed(Action&) { }
|
||||
virtual void on_checked_state_changed(Action&) { }
|
||||
};
|
||||
|
||||
void add_observer(NonnullOwnPtr<Observer>);
|
||||
void remove_observer(Observer const& observer);
|
||||
|
||||
void set_group(Badge<Menu>, Menu& group) { m_group = group; }
|
||||
|
||||
private:
|
||||
Action(Variant<StringView, String> text, ActionID id, Function<void()> action)
|
||||
: m_text(move(text))
|
||||
, m_id(id)
|
||||
, m_action(move(action))
|
||||
{
|
||||
}
|
||||
|
||||
void set_checked_internal(bool checked);
|
||||
|
||||
Variant<StringView, String> m_text;
|
||||
Optional<StringView> m_tooltip;
|
||||
ActionID m_id;
|
||||
|
||||
bool m_enabled { true };
|
||||
bool m_visible { true };
|
||||
Optional<bool> m_checked;
|
||||
|
||||
Function<void()> m_action;
|
||||
Vector<NonnullOwnPtr<Observer>, 1> m_observers;
|
||||
|
||||
WeakPtr<Menu> m_group;
|
||||
};
|
||||
|
||||
struct WEBVIEW_API Separator { };
|
||||
|
||||
class WEBVIEW_API Menu
|
||||
: public RefCounted<Menu>
|
||||
, public Weakable<Menu> {
|
||||
public:
|
||||
using MenuItem = Variant<NonnullRefPtr<Action>, NonnullRefPtr<Menu>, Separator>;
|
||||
|
||||
static NonnullRefPtr<Menu> create(StringView name);
|
||||
static NonnullRefPtr<Menu> create_group(StringView name);
|
||||
|
||||
void add_action(NonnullRefPtr<Action> action);
|
||||
void add_submenu(NonnullRefPtr<Menu> submenu) { m_items.append(move(submenu)); }
|
||||
void add_separator() { m_items.append(Separator {}); }
|
||||
|
||||
StringView title() const { return m_title; }
|
||||
|
||||
Span<MenuItem> items() { return m_items; }
|
||||
ReadonlySpan<MenuItem> items() const { return m_items; }
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_action(Callback const& callback)
|
||||
{
|
||||
for (auto& item : m_items) {
|
||||
item.visit(
|
||||
[&](NonnullRefPtr<Action>& action) { callback(*action); },
|
||||
[&](NonnullRefPtr<Menu>& submenu) { submenu->for_each_action(callback); },
|
||||
[&](Separator) {});
|
||||
}
|
||||
}
|
||||
|
||||
Function<void(Gfx::IntPoint)> on_activation;
|
||||
|
||||
private:
|
||||
explicit Menu(StringView title)
|
||||
: m_title(title)
|
||||
{
|
||||
}
|
||||
|
||||
StringView m_title;
|
||||
Vector<MenuItem> m_items;
|
||||
|
||||
bool m_is_group { false };
|
||||
};
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ add_library(ladybird_impl STATIC
|
|||
Interface/LadybirdWebView.mm
|
||||
Interface/LadybirdWebViewBridge.cpp
|
||||
Interface/LadybirdWebViewWindow.mm
|
||||
Interface/Menu.mm
|
||||
Interface/Palette.mm
|
||||
Interface/SearchPanel.mm
|
||||
Interface/Tab.mm
|
||||
|
|
24
UI/AppKit/Interface/Menu.h
Normal file
24
UI/AppKit/Interface/Menu.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Interface/Menu.h>
|
||||
#include <LibWebView/Menu.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class LadybirdWebView;
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
NSMenu* create_application_menu(WebView::Menu&);
|
||||
NSMenu* create_context_menu(LadybirdWebView*, WebView::Menu&);
|
||||
|
||||
NSMenuItem* create_application_menu_item(WebView::Action&);
|
||||
NSButton* create_application_button(WebView::Action&, NSImageName);
|
||||
|
||||
}
|
168
UI/AppKit/Interface/Menu.mm
Normal file
168
UI/AppKit/Interface/Menu.mm
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#import <Interface/Event.h>
|
||||
#import <Interface/LadybirdWebView.h>
|
||||
#import <Interface/Menu.h>
|
||||
#import <Utilities/Conversions.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface ActionExecutor : NSObject
|
||||
{
|
||||
WeakPtr<WebView::Action> m_action;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ActionExecutor
|
||||
|
||||
+ (instancetype)attachToNativeControl:(WebView::Action const&)action
|
||||
control:(id)control
|
||||
{
|
||||
auto* executor = [[ActionExecutor alloc] init];
|
||||
[control setAction:@selector(execute:)];
|
||||
[control setTarget:executor];
|
||||
|
||||
static char executor_key = 0;
|
||||
objc_setAssociatedObject(control, &executor_key, executor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
|
||||
executor->m_action = action.make_weak_ptr();
|
||||
return executor;
|
||||
}
|
||||
|
||||
- (void)execute:(id)sender
|
||||
{
|
||||
auto action = m_action.strong_ref();
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
if (action->is_checkable())
|
||||
action->set_checked(!action->checked());
|
||||
action->activate();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class ActionObserver final : public WebView::Action::Observer {
|
||||
public:
|
||||
static NonnullOwnPtr<ActionObserver> create(WebView::Action& action, id control)
|
||||
{
|
||||
return adopt_own(*new ActionObserver(action, control));
|
||||
}
|
||||
|
||||
virtual void on_text_changed(WebView::Action& action) override
|
||||
{
|
||||
if ([m_control isKindOfClass:[NSButton class]] && [m_control image] != nil)
|
||||
[m_control setToolTip:string_to_ns_string(action.text())];
|
||||
else
|
||||
[m_control setTitle:string_to_ns_string(action.text())];
|
||||
}
|
||||
|
||||
virtual void on_tooltip_changed(WebView::Action& action) override
|
||||
{
|
||||
[m_control setToolTip:string_to_ns_string(action.tooltip())];
|
||||
}
|
||||
|
||||
virtual void on_enabled_state_changed(WebView::Action& action) override
|
||||
{
|
||||
[m_control setEnabled:action.enabled()];
|
||||
}
|
||||
|
||||
virtual void on_visible_state_changed(WebView::Action& action) override
|
||||
{
|
||||
[m_control setHidden:!action.visible()];
|
||||
}
|
||||
|
||||
virtual void on_checked_state_changed(WebView::Action& action) override
|
||||
{
|
||||
[m_control setState:action.checked() ? NSControlStateValueOn : NSControlStateValueOff];
|
||||
}
|
||||
|
||||
private:
|
||||
ActionObserver(WebView::Action& action, id control)
|
||||
: m_control(control)
|
||||
{
|
||||
[ActionExecutor attachToNativeControl:action control:control];
|
||||
}
|
||||
|
||||
__weak id m_control { nil };
|
||||
};
|
||||
|
||||
static void initialize_native_control(WebView::Action& action, id control)
|
||||
{
|
||||
action.add_observer(ActionObserver::create(action, control));
|
||||
}
|
||||
|
||||
static void add_items_to_menu(NSMenu* menu, Span<WebView::Menu::MenuItem> menu_items)
|
||||
{
|
||||
for (auto& menu_item : menu_items) {
|
||||
menu_item.visit(
|
||||
[&](NonnullRefPtr<WebView::Action>& action) {
|
||||
[menu addItem:create_application_menu_item(action)];
|
||||
},
|
||||
[&](NonnullRefPtr<WebView::Menu> const& submenu) {
|
||||
auto* application_submenu = [[NSMenu alloc] init];
|
||||
add_items_to_menu(application_submenu, submenu->items());
|
||||
|
||||
auto* item = [[NSMenuItem alloc] initWithTitle:string_to_ns_string(submenu->title())
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[item setSubmenu:application_submenu];
|
||||
|
||||
[menu addItem:item];
|
||||
},
|
||||
[&](WebView::Separator) {
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
NSMenu* create_application_menu(WebView::Menu& menu)
|
||||
{
|
||||
auto* application_menu = [[NSMenu alloc] initWithTitle:string_to_ns_string(menu.title())];
|
||||
add_items_to_menu(application_menu, menu.items());
|
||||
return application_menu;
|
||||
}
|
||||
|
||||
NSMenu* create_context_menu(LadybirdWebView* view, WebView::Menu& menu)
|
||||
{
|
||||
auto* application_menu = create_application_menu(menu);
|
||||
|
||||
__weak LadybirdWebView* weak_view = view;
|
||||
__weak NSMenu* weak_application_menu = application_menu;
|
||||
|
||||
menu.on_activation = [weak_view, weak_application_menu](Gfx::IntPoint position) {
|
||||
LadybirdWebView* view = weak_view;
|
||||
NSMenu* application_menu = weak_application_menu;
|
||||
|
||||
if (view && application_menu) {
|
||||
auto* event = create_context_menu_mouse_event(view, position);
|
||||
[NSMenu popUpContextMenu:application_menu withEvent:event forView:view];
|
||||
}
|
||||
};
|
||||
|
||||
return application_menu;
|
||||
}
|
||||
|
||||
NSMenuItem* create_application_menu_item(WebView::Action& action)
|
||||
{
|
||||
auto* item = [[NSMenuItem alloc] init];
|
||||
initialize_native_control(action, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
NSButton* create_application_button(WebView::Action& action, NSImageName image)
|
||||
{
|
||||
auto* button = [[NSButton alloc] init];
|
||||
if (image)
|
||||
[button setImage:[NSImage imageNamed:image]];
|
||||
|
||||
initialize_native_control(action, button);
|
||||
return button;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,11 +6,12 @@ target_sources(ladybird PRIVATE
|
|||
FindInPageWidget.cpp
|
||||
Icon.cpp
|
||||
LocationEdit.cpp
|
||||
Menu.cpp
|
||||
Settings.cpp
|
||||
StringUtils.cpp
|
||||
Tab.cpp
|
||||
TabBar.cpp
|
||||
TVGIconEngine.cpp
|
||||
StringUtils.cpp
|
||||
WebContentView.cpp
|
||||
ladybird.qrc
|
||||
)
|
||||
|
|
130
UI/Qt/Menu.cpp
Normal file
130
UI/Qt/Menu.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <UI/Qt/Icon.h>
|
||||
#include <UI/Qt/Menu.h>
|
||||
#include <UI/Qt/StringUtils.h>
|
||||
#include <UI/Qt/WebContentView.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class ActionObserver final : public WebView::Action::Observer {
|
||||
public:
|
||||
static NonnullOwnPtr<ActionObserver> create(WebView::Action& action, QAction& qaction)
|
||||
{
|
||||
return adopt_own(*new ActionObserver(action, qaction));
|
||||
}
|
||||
|
||||
virtual void on_text_changed(WebView::Action& action) override
|
||||
{
|
||||
if (m_action)
|
||||
m_action->setText(qstring_from_ak_string(action.text()));
|
||||
}
|
||||
|
||||
virtual void on_tooltip_changed(WebView::Action& action) override
|
||||
{
|
||||
if (m_action)
|
||||
m_action->setToolTip(qstring_from_ak_string(action.tooltip()));
|
||||
}
|
||||
|
||||
virtual void on_enabled_state_changed(WebView::Action& action) override
|
||||
{
|
||||
if (m_action)
|
||||
m_action->setEnabled(action.enabled());
|
||||
}
|
||||
|
||||
virtual void on_visible_state_changed(WebView::Action& action) override
|
||||
{
|
||||
if (m_action)
|
||||
m_action->setVisible(action.visible());
|
||||
}
|
||||
|
||||
virtual void on_checked_state_changed(WebView::Action& action) override
|
||||
{
|
||||
if (m_action)
|
||||
m_action->setChecked(action.checked());
|
||||
}
|
||||
|
||||
private:
|
||||
ActionObserver(WebView::Action& action, QAction& qaction)
|
||||
: m_action(&qaction)
|
||||
{
|
||||
QObject::connect(m_action, &QAction::triggered, [weak_action = action.make_weak_ptr()](bool checked) {
|
||||
if (auto action = weak_action.strong_ref()) {
|
||||
if (action->is_checkable())
|
||||
action->set_checked(checked);
|
||||
action->activate();
|
||||
}
|
||||
});
|
||||
QObject::connect(m_action->parent(), &QObject::destroyed, [this, weak_action = action.make_weak_ptr()]() {
|
||||
if (auto action = weak_action.strong_ref())
|
||||
action->remove_observer(*this);
|
||||
});
|
||||
}
|
||||
|
||||
QPointer<QAction> m_action;
|
||||
};
|
||||
|
||||
static void initialize_native_control(WebView::Action& action, QAction& qaction)
|
||||
{
|
||||
if (action.is_checkable())
|
||||
qaction.setCheckable(true);
|
||||
|
||||
action.add_observer(ActionObserver::create(action, qaction));
|
||||
}
|
||||
|
||||
static void add_items_to_menu(QMenu& menu, QWidget& parent, Span<WebView::Menu::MenuItem> menu_items)
|
||||
{
|
||||
for (auto& menu_item : menu_items) {
|
||||
menu_item.visit(
|
||||
[&](NonnullRefPtr<WebView::Action>& action) {
|
||||
auto* qaction = create_application_action(parent, action);
|
||||
menu.addAction(qaction);
|
||||
},
|
||||
[&](NonnullRefPtr<WebView::Menu> const& submenu) {
|
||||
auto* qsubmenu = new QMenu(qstring_from_ak_string(submenu->title()), &menu);
|
||||
add_items_to_menu(*qsubmenu, parent, submenu->items());
|
||||
|
||||
menu.addMenu(qsubmenu);
|
||||
},
|
||||
[&](WebView::Separator) {
|
||||
menu.addSeparator();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QMenu* create_application_menu(QWidget& parent, WebView::Menu& menu)
|
||||
{
|
||||
auto* application_menu = new QMenu(qstring_from_ak_string(menu.title()), &parent);
|
||||
add_items_to_menu(*application_menu, parent, menu.items());
|
||||
return application_menu;
|
||||
}
|
||||
|
||||
QMenu* create_context_menu(QWidget& parent, WebContentView& view, WebView::Menu& menu)
|
||||
{
|
||||
auto* application_menu = create_application_menu(parent, menu);
|
||||
|
||||
menu.on_activation = [view = QPointer { &view }, application_menu = QPointer { application_menu }](Gfx::IntPoint position) {
|
||||
if (view && application_menu)
|
||||
application_menu->exec(view->map_point_to_global_position(position));
|
||||
};
|
||||
|
||||
return application_menu;
|
||||
}
|
||||
|
||||
QAction* create_application_action(QWidget& parent, WebView::Action& action)
|
||||
{
|
||||
auto* qaction = new QAction(&parent);
|
||||
initialize_native_control(action, *qaction);
|
||||
return qaction;
|
||||
}
|
||||
|
||||
}
|
23
UI/Qt/Menu.h
Normal file
23
UI/Qt/Menu.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWebView/Menu.h>
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QWidget;
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class WebContentView;
|
||||
|
||||
QMenu* create_application_menu(QWidget& parent, WebView::Menu&);
|
||||
QMenu* create_context_menu(QWidget& parent, WebContentView&, WebView::Menu&);
|
||||
QAction* create_application_action(QWidget& parent, WebView::Action&);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue