LibWebView+UI/AppKit: Display icons in the macOS UI on Tahoe

On macOS Tahoe, it is now recommended to show menu item icons. We use
system symbols for this now. Symbols do not have constant variable names
and must be found via the SF Symbols app.

The symbols chosen here were to match Safari as close as possible.
This commit is contained in:
Timothy Flynn 2025-09-16 12:55:37 -04:00 committed by Luke Wilde
commit ede6314cb6
Notes: github-actions[bot] 2025-09-17 10:30:06 +00:00
6 changed files with 95 additions and 14 deletions

View file

@ -50,7 +50,8 @@ enum class ActionID {
PauseMedia, PauseMedia,
MuteMedia, MuteMedia,
UnmuteMedia, UnmuteMedia,
ToggleMediaControlsState, ShowControls,
HideControls,
ToggleMediaLoopState, ToggleMediaLoopState,
ZoomIn, ZoomIn,

View file

@ -908,7 +908,10 @@ void ViewImplementation::initialize_context_menus()
m_media_unmute_action = Action::create("Unmute"sv, ActionID::UnmuteMedia, [this]() { m_media_unmute_action = Action::create("Unmute"sv, ActionID::UnmuteMedia, [this]() {
client().async_toggle_media_mute_state(page_id()); client().async_toggle_media_mute_state(page_id());
}); });
m_media_controls_action = Action::create_checkable("Show Controls"sv, ActionID::ToggleMediaControlsState, [this]() { m_media_show_controls_action = Action::create("Show Controls"sv, ActionID::ShowControls, [this]() {
client().async_toggle_media_controls_state(page_id());
});
m_media_hide_controls_action = Action::create("Hide Controls"sv, ActionID::HideControls, [this]() {
client().async_toggle_media_controls_state(page_id()); client().async_toggle_media_controls_state(page_id());
}); });
m_media_loop_action = Action::create_checkable("Loop"sv, ActionID::ToggleMediaLoopState, [this]() { m_media_loop_action = Action::create_checkable("Loop"sv, ActionID::ToggleMediaLoopState, [this]() {
@ -947,7 +950,8 @@ void ViewImplementation::initialize_context_menus()
m_media_context_menu->add_action(*m_media_pause_action); m_media_context_menu->add_action(*m_media_pause_action);
m_media_context_menu->add_action(*m_media_mute_action); m_media_context_menu->add_action(*m_media_mute_action);
m_media_context_menu->add_action(*m_media_unmute_action); m_media_context_menu->add_action(*m_media_unmute_action);
m_media_context_menu->add_action(*m_media_controls_action); m_media_context_menu->add_action(*m_media_show_controls_action);
m_media_context_menu->add_action(*m_media_hide_controls_action);
m_media_context_menu->add_action(*m_media_loop_action); m_media_context_menu->add_action(*m_media_loop_action);
m_media_context_menu->add_separator(); m_media_context_menu->add_separator();
m_media_context_menu->add_action(*m_open_audio_action); m_media_context_menu->add_action(*m_open_audio_action);
@ -1027,7 +1031,9 @@ void ViewImplementation::did_request_media_context_menu(Badge<WebContentClient>,
m_media_mute_action->set_visible(!menu.is_muted); m_media_mute_action->set_visible(!menu.is_muted);
m_media_unmute_action->set_visible(menu.is_muted); m_media_unmute_action->set_visible(menu.is_muted);
m_media_controls_action->set_checked(menu.has_user_agent_controls); m_media_show_controls_action->set_visible(!menu.has_user_agent_controls);
m_media_hide_controls_action->set_visible(menu.has_user_agent_controls);
m_media_loop_action->set_checked(menu.is_looping); m_media_loop_action->set_checked(menu.is_looping);
if (m_media_context_menu->on_activation) if (m_media_context_menu->on_activation)

View file

@ -331,7 +331,8 @@ protected:
RefPtr<Action> m_media_pause_action; RefPtr<Action> m_media_pause_action;
RefPtr<Action> m_media_mute_action; RefPtr<Action> m_media_mute_action;
RefPtr<Action> m_media_unmute_action; RefPtr<Action> m_media_unmute_action;
RefPtr<Action> m_media_controls_action; RefPtr<Action> m_media_show_controls_action;
RefPtr<Action> m_media_hide_controls_action;
RefPtr<Action> m_media_loop_action; RefPtr<Action> m_media_loop_action;
Queue<Web::InputEvent> m_pending_input_events; Queue<Web::InputEvent> m_pending_input_events;

View file

@ -19,6 +19,8 @@ NSMenu* create_application_menu(WebView::Menu&);
NSMenu* create_context_menu(LadybirdWebView*, WebView::Menu&); NSMenu* create_context_menu(LadybirdWebView*, WebView::Menu&);
NSMenuItem* create_application_menu_item(WebView::Action&); NSMenuItem* create_application_menu_item(WebView::Action&);
NSButton* create_application_button(WebView::Action&, NSImageName); NSButton* create_application_button(WebView::Action&);
void set_control_image(id control, NSString*);
} }

View file

@ -115,29 +115,87 @@ static void initialize_native_control(WebView::Action& action, id control)
{ {
switch (action.id()) { switch (action.id()) {
case WebView::ActionID::NavigateBack: case WebView::ActionID::NavigateBack:
set_control_image(control, @"chevron.left");
[control setKeyEquivalent:@"["]; [control setKeyEquivalent:@"["];
break; break;
case WebView::ActionID::NavigateForward: case WebView::ActionID::NavigateForward:
set_control_image(control, @"chevron.right");
[control setKeyEquivalent:@"]"]; [control setKeyEquivalent:@"]"];
break; break;
case WebView::ActionID::Reload: case WebView::ActionID::Reload:
set_control_image(control, @"arrow.clockwise");
[control setKeyEquivalent:@"r"]; [control setKeyEquivalent:@"r"];
break; break;
case WebView::ActionID::CopySelection: case WebView::ActionID::CopySelection:
set_control_image(control, @"document.on.document");
[control setKeyEquivalent:@"c"]; [control setKeyEquivalent:@"c"];
break; break;
case WebView::ActionID::Paste: case WebView::ActionID::Paste:
set_control_image(control, @"document.on.clipboard");
[control setKeyEquivalent:@"v"]; [control setKeyEquivalent:@"v"];
break; break;
case WebView::ActionID::SelectAll: case WebView::ActionID::SelectAll:
set_control_image(control, @"character.textbox");
[control setKeyEquivalent:@"a"]; [control setKeyEquivalent:@"a"];
break; break;
case WebView::ActionID::SearchSelectedText:
set_control_image(control, @"magnifyingglass");
break;
case WebView::ActionID::ViewSource: case WebView::ActionID::ViewSource:
set_control_image(control, @"text.document");
[control setKeyEquivalent:@"u"]; [control setKeyEquivalent:@"u"];
break; break;
case WebView::ActionID::TakeVisibleScreenshot:
case WebView::ActionID::TakeFullScreenshot:
set_control_image(control, @"photo");
break;
case WebView::ActionID::OpenInNewTab:
set_control_image(control, @"plus.square.on.square");
break;
case WebView::ActionID::CopyURL:
set_control_image(control, @"document.on.document");
break;
case WebView::ActionID::OpenImage:
set_control_image(control, @"photo");
break;
case WebView::ActionID::CopyImage:
set_control_image(control, @"document.on.document");
break;
case WebView::ActionID::OpenAudio:
set_control_image(control, @"speaker.wave.1");
break;
case WebView::ActionID::OpenVideo:
set_control_image(control, @"video");
break;
case WebView::ActionID::PlayMedia:
set_control_image(control, @"play");
break;
case WebView::ActionID::PauseMedia:
set_control_image(control, @"pause");
break;
case WebView::ActionID::MuteMedia:
set_control_image(control, @"speaker.slash");
break;
case WebView::ActionID::UnmuteMedia:
set_control_image(control, @"speaker.wave.2");
break;
case WebView::ActionID::ShowControls:
set_control_image(control, @"eye");
break;
case WebView::ActionID::HideControls:
set_control_image(control, @"eye.slash");
break;
case WebView::ActionID::ToggleMediaLoopState:
set_control_image(control, @"arrow.clockwise");
break;
case WebView::ActionID::ZoomIn: case WebView::ActionID::ZoomIn:
[control setKeyEquivalent:@"+"]; [control setKeyEquivalent:@"+"];
break; break;
@ -213,14 +271,27 @@ NSMenuItem* create_application_menu_item(WebView::Action& action)
return item; return item;
} }
NSButton* create_application_button(WebView::Action& action, NSImageName image) NSButton* create_application_button(WebView::Action& action)
{ {
auto* button = [[NSButton alloc] init]; auto* button = [[NSButton alloc] init];
if (image)
[button setImage:[NSImage imageNamed:image]];
initialize_native_control(action, button); initialize_native_control(action, button);
return button; return button;
} }
void set_control_image(id control, NSString* image)
{
// System symbols are distributed with the San Fransisco (SF) Symbols font. To see all SF Symbols and their names,
// you will have to install the SF Symbols app: https://developer.apple.com/sf-symbols/
auto set_image = [&]() {
[control setImage:[NSImage imageWithSystemSymbolName:image accessibilityDescription:@""]];
};
if (@available(macOS 26, *)) {
set_image();
} else {
if ([control isKindOfClass:[NSButton class]])
set_image();
}
}
} }

View file

@ -260,7 +260,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
- (NSToolbarItem*)navigate_back_toolbar_item - (NSToolbarItem*)navigate_back_toolbar_item
{ {
if (!_navigate_back_toolbar_item) { if (!_navigate_back_toolbar_item) {
auto* button = Ladybird::create_application_button([[[self tab] web_view] view].navigate_back_action(), NSImageNameGoBackTemplate); auto* button = Ladybird::create_application_button([[[self tab] web_view] view].navigate_back_action());
_navigate_back_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_NAVIGATE_BACK_IDENTIFIER]; _navigate_back_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_NAVIGATE_BACK_IDENTIFIER];
[_navigate_back_toolbar_item setView:button]; [_navigate_back_toolbar_item setView:button];
@ -272,7 +272,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
- (NSToolbarItem*)navigate_forward_toolbar_item - (NSToolbarItem*)navigate_forward_toolbar_item
{ {
if (!_navigate_forward_toolbar_item) { if (!_navigate_forward_toolbar_item) {
auto* button = Ladybird::create_application_button([[[self tab] web_view] view].navigate_forward_action(), NSImageNameGoForwardTemplate); auto* button = Ladybird::create_application_button([[[self tab] web_view] view].navigate_forward_action());
_navigate_forward_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_NAVIGATE_FORWARD_IDENTIFIER]; _navigate_forward_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_NAVIGATE_FORWARD_IDENTIFIER];
[_navigate_forward_toolbar_item setView:button]; [_navigate_forward_toolbar_item setView:button];
@ -284,7 +284,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
- (NSToolbarItem*)reload_toolbar_item - (NSToolbarItem*)reload_toolbar_item
{ {
if (!_reload_toolbar_item) { if (!_reload_toolbar_item) {
auto* button = Ladybird::create_application_button(WebView::Application::the().reload_action(), NSImageNameRefreshTemplate); auto* button = Ladybird::create_application_button(WebView::Application::the().reload_action());
_reload_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_RELOAD_IDENTIFIER]; _reload_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_RELOAD_IDENTIFIER];
[_reload_toolbar_item setView:button]; [_reload_toolbar_item setView:button];
@ -315,7 +315,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde
- (NSToolbarItem*)zoom_toolbar_item - (NSToolbarItem*)zoom_toolbar_item
{ {
if (!_zoom_toolbar_item) { if (!_zoom_toolbar_item) {
auto* button = Ladybird::create_application_button([[[self tab] web_view] view].reset_zoom_action(), nil); auto* button = Ladybird::create_application_button([[[self tab] web_view] view].reset_zoom_action());
_zoom_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_ZOOM_IDENTIFIER]; _zoom_toolbar_item = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_ZOOM_IDENTIFIER];
[_zoom_toolbar_item setView:button]; [_zoom_toolbar_item setView:button];