/* * Copyright (c) 2025-2026, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace WebView { enum class ActionID { NavigateBack, NavigateForward, Reload, CopySelection, Paste, SelectAll, SearchSelectedText, TakeVisibleScreenshot, TakeFullScreenshot, ManageBookmarks, ToggleBookmark, ToggleBookmarkViaToolbar, ToggleBookmarksBar, BookmarkItem, AddBookmark, AddBookmarkFolder, DeleteBookmark, DeleteBookmarkFolder, EditBookmark, EditBookmarkFolder, OpenAboutPage, OpenProcessesPage, OpenSettingsPage, ToggleDevTools, ViewSource, OpenInNewTab, CopyURL, OpenImage, SaveImage, CopyImage, OpenAudio, OpenVideo, PlayMedia, PauseMedia, MuteMedia, UnmuteMedia, ShowControls, HideControls, ToggleMediaLoopState, EnterFullscreen, ExitFullscreen, ZoomIn, ZoomOut, ResetZoom, ResetZoomViaToolbar, PreferredColorScheme, PreferredContrast, PreferredMotion, DumpSessionHistoryTree, DumpDOMTree, DumpLayoutTree, DumpPaintTree, DumpStackingContextTree, DumpDisplayList, DumpStyleSheets, DumpStyles, DumpCSSErrors, DumpCookies, DumpLocalStorage, DumpGCGraph, ShowLineBoxBorders, CollectGarbage, SpoofUserAgent, NavigatorCompatibilityMode, EnableScripting, EnableContentFiltering, BlockPopUps, }; using ActionText = Variant; inline StringView action_text_to_string_view(ActionText const& text) { return text.visit([](auto const& text) -> StringView { return text; }); } class WEBVIEW_API Action : public RefCounted , public Weakable { public: static NonnullRefPtr create(ActionText text, ActionID id, Function action); static NonnullRefPtr create_checkable(ActionText text, ActionID id, Function action); void activate() { m_action(); } StringView text() const { return action_text_to_string_view(m_text); } void set_text(ActionText); StringView tooltip() const { return action_text_to_string_view(*m_tooltip); } void set_tooltip(ActionText); void set_base64_png_icon(Optional base64_png_icon) { m_base64_png_icon = move(base64_png_icon); } Optional base64_png_icon() const { return m_base64_png_icon; } ActionID id() const { return m_id; } HashMap const& properties() const { return m_properties; } void add_property(StringView name, String value) { m_properties.set(name, move(value)); } bool enabled() const { return m_enabled; } void set_enabled(bool); bool visible() const { return m_visible; } void set_visible(bool); bool engaged() const { return m_engaged; } void set_engaged(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_engaged_state_changed(Action&) { } virtual void on_checked_state_changed(Action&) { } }; void add_observer(NonnullOwnPtr); void remove_observer(Observer const& observer); void set_group(Badge, Menu& group) { m_group = group; } private: Action(ActionText text, ActionID id, Function action) : m_text(move(text)) , m_id(id) , m_action(move(action)) { } void set_checked_internal(bool checked); ActionText m_text; Optional m_tooltip; Optional m_base64_png_icon; ActionID m_id; HashMap m_properties; bool m_enabled { true }; bool m_visible { true }; bool m_engaged { false }; Optional m_checked; Function m_action; Vector, 1> m_observers; WeakPtr m_group; }; struct WEBVIEW_API Separator { }; class WEBVIEW_API Menu : public RefCounted , public Weakable { public: using MenuItem = Variant, NonnullRefPtr, Separator>; static NonnullRefPtr create(ActionText title); static NonnullRefPtr create_group(ActionText title); void add_action(NonnullRefPtr action); void add_submenu(NonnullRefPtr submenu) { m_items.append(move(submenu)); } void add_separator() { m_items.append(Separator {}); } size_t size() const { return m_items.size(); } void shrink(size_t size) { m_items.shrink(size); } StringView title() const { return action_text_to_string_view(m_title); } Span items() { return m_items; } ReadonlySpan items() const { return m_items; } HashMap const& properties() const { return m_properties; } void add_property(StringView name, String value) { m_properties.set(name, move(value)); } void set_render_group_icon(bool render_group_icon) { m_render_group_icon = render_group_icon; } bool render_group_icon() const { return m_render_group_icon; } template void for_each_action(Callback const& callback) { for (auto& item : m_items) { item.visit( [&](NonnullRefPtr& action) { callback(*action); }, [&](NonnullRefPtr& submenu) { submenu->for_each_action(callback); }, [&](Separator) {}); } } Function on_activation; private: explicit Menu(ActionText title) : m_title(move(title)) { } ActionText m_title; Vector m_items; HashMap m_properties; bool m_is_group { false }; bool m_render_group_icon { false }; }; }