Ladybird+LibWeb+WebContent: Add context menu controls for muting audio

This commit is contained in:
Timothy Flynn 2023-06-16 11:29:54 -04:00 committed by Andreas Kling
parent b3bbdb1e2c
commit 9e95c9892c
Notes: sideshowbarker 2024-07-17 00:23:42 +09:00
16 changed files with 78 additions and 0 deletions

View file

@ -376,6 +376,8 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
m_media_context_menu_play_icon = make<QIcon>(QString("%1/res/icons/16x16/play.png").arg(s_serenity_resource_root.characters()));
m_media_context_menu_pause_icon = make<QIcon>(QString("%1/res/icons/16x16/pause.png").arg(s_serenity_resource_root.characters()));
m_media_context_menu_mute_icon = make<QIcon>(QString("%1/res/icons/16x16/audio-volume-muted.png").arg(s_serenity_resource_root.characters()));
m_media_context_menu_unmute_icon = make<QIcon>(QString("%1/res/icons/16x16/audio-volume-high.png").arg(s_serenity_resource_root.characters()));
m_media_context_menu_play_pause_action = make<QAction>("&Play", this);
m_media_context_menu_play_pause_action->setIcon(*m_media_context_menu_play_icon);
@ -383,6 +385,12 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
view().toggle_media_play_state();
});
m_media_context_menu_mute_unmute_action = make<QAction>("&Mute", this);
m_media_context_menu_mute_unmute_action->setIcon(*m_media_context_menu_mute_icon);
QObject::connect(m_media_context_menu_mute_unmute_action, &QAction::triggered, this, [this]() {
view().toggle_media_mute_state();
});
m_media_context_menu_controls_action = make<QAction>("Show &Controls", this);
m_media_context_menu_controls_action->setCheckable(true);
QObject::connect(m_media_context_menu_controls_action, &QAction::triggered, this, [this]() {
@ -403,6 +411,7 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
m_audio_context_menu = make<QMenu>("Audio context menu", this);
m_audio_context_menu->addAction(m_media_context_menu_play_pause_action);
m_audio_context_menu->addAction(m_media_context_menu_mute_unmute_action);
m_audio_context_menu->addAction(m_media_context_menu_controls_action);
m_audio_context_menu->addAction(m_media_context_menu_loop_action);
m_audio_context_menu->addSeparator();
@ -430,6 +439,7 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
m_video_context_menu = make<QMenu>("Video context menu", this);
m_video_context_menu->addAction(m_media_context_menu_play_pause_action);
m_video_context_menu->addAction(m_media_context_menu_mute_unmute_action);
m_video_context_menu->addAction(m_media_context_menu_controls_action);
m_video_context_menu->addAction(m_media_context_menu_loop_action);
m_video_context_menu->addSeparator();
@ -451,6 +461,14 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::
m_media_context_menu_play_pause_action->setText("&Play");
}
if (menu.is_muted) {
m_media_context_menu_mute_unmute_action->setIcon(*m_media_context_menu_unmute_icon);
m_media_context_menu_mute_unmute_action->setText("Un&mute");
} else {
m_media_context_menu_mute_unmute_action->setIcon(*m_media_context_menu_mute_icon);
m_media_context_menu_mute_unmute_action->setText("&Mute");
}
m_media_context_menu_controls_action->setChecked(menu.has_user_agent_controls);
m_media_context_menu_loop_action->setChecked(menu.is_looping);

View file

@ -100,7 +100,10 @@ private:
OwnPtr<QMenu> m_video_context_menu;
OwnPtr<QIcon> m_media_context_menu_play_icon;
OwnPtr<QIcon> m_media_context_menu_pause_icon;
OwnPtr<QIcon> m_media_context_menu_mute_icon;
OwnPtr<QIcon> m_media_context_menu_unmute_icon;
OwnPtr<QAction> m_media_context_menu_play_pause_action;
OwnPtr<QAction> m_media_context_menu_mute_unmute_action;
OwnPtr<QAction> m_media_context_menu_controls_action;
OwnPtr<QAction> m_media_context_menu_loop_action;
URL m_media_context_menu_url;

View file

@ -45,6 +45,8 @@ ErrorOr<IconBag> IconBag::try_create()
icon_bag.rename = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/rename.png"sv));
icon_bag.play = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv));
icon_bag.pause = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv));
icon_bag.mute = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-muted.png"sv));
icon_bag.unmute = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/audio-volume-high.png"sv));
return icon_bag;
}

View file

@ -47,5 +47,7 @@ struct IconBag final {
RefPtr<Gfx::Bitmap> rename { nullptr };
RefPtr<Gfx::Bitmap> play { nullptr };
RefPtr<Gfx::Bitmap> pause { nullptr };
RefPtr<Gfx::Bitmap> mute { nullptr };
RefPtr<Gfx::Bitmap> unmute { nullptr };
};
}

View file

@ -377,6 +377,9 @@ Tab::Tab(BrowserWindow& window)
m_media_context_menu_play_pause_action = GUI::Action::create("&Play", g_icon_bag.play, [this](auto&) {
view().toggle_media_play_state();
});
m_media_context_menu_mute_unmute_action = GUI::Action::create("&Mute", g_icon_bag.mute, [this](auto&) {
view().toggle_media_mute_state();
});
m_media_context_menu_controls_action = GUI::Action::create_checkable("Show &Controls", [this](auto&) {
view().toggle_media_controls_state();
});
@ -386,6 +389,7 @@ Tab::Tab(BrowserWindow& window)
m_audio_context_menu = GUI::Menu::construct();
m_audio_context_menu->add_action(*m_media_context_menu_play_pause_action);
m_audio_context_menu->add_action(*m_media_context_menu_mute_unmute_action);
m_audio_context_menu->add_action(*m_media_context_menu_controls_action);
m_audio_context_menu->add_action(*m_media_context_menu_loop_action);
m_audio_context_menu->add_separator();
@ -401,6 +405,7 @@ Tab::Tab(BrowserWindow& window)
m_video_context_menu = GUI::Menu::construct();
m_video_context_menu->add_action(*m_media_context_menu_play_pause_action);
m_video_context_menu->add_action(*m_media_context_menu_mute_unmute_action);
m_video_context_menu->add_action(*m_media_context_menu_controls_action);
m_video_context_menu->add_action(*m_media_context_menu_loop_action);
m_video_context_menu->add_separator();
@ -432,6 +437,14 @@ Tab::Tab(BrowserWindow& window)
m_media_context_menu_play_pause_action->set_text("&Play"sv);
}
if (menu.is_muted) {
m_media_context_menu_mute_unmute_action->set_icon(g_icon_bag.unmute);
m_media_context_menu_mute_unmute_action->set_text("Un&mute"sv);
} else {
m_media_context_menu_mute_unmute_action->set_icon(g_icon_bag.mute);
m_media_context_menu_mute_unmute_action->set_text("&Mute"sv);
}
m_media_context_menu_controls_action->set_checked(menu.has_user_agent_controls);
m_media_context_menu_loop_action->set_checked(menu.is_looping);

View file

@ -146,6 +146,7 @@ private:
RefPtr<GUI::Menu> m_audio_context_menu;
RefPtr<GUI::Menu> m_video_context_menu;
RefPtr<GUI::Action> m_media_context_menu_play_pause_action;
RefPtr<GUI::Action> m_media_context_menu_mute_unmute_action;
RefPtr<GUI::Action> m_media_context_menu_controls_action;
RefPtr<GUI::Action> m_media_context_menu_loop_action;
URL m_media_context_menu_url;

View file

@ -315,6 +315,7 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, unsigned button, unsig
.media_url = media_element.document().parse_url(media_element.current_src()),
.is_video = is<HTML::HTMLVideoElement>(*node),
.is_playing = media_element.potentially_playing(),
.is_muted = media_element.muted(),
.has_user_agent_controls = media_element.has_attribute(HTML::AttributeNames::controls),
.is_looping = media_element.has_attribute(HTML::AttributeNames::loop),
};

View file

@ -315,6 +315,21 @@ WebIDL::ExceptionOr<void> Page::toggle_media_play_state()
return {};
}
void Page::toggle_media_mute_state()
{
auto media_element = media_context_menu_element();
if (!media_element)
return;
// FIXME: This runs from outside the context of any user script, so we do not have a running execution
// context. This pushes one to allow the promise creation hook to run.
auto& environment_settings = media_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
media_element->set_muted(!media_element->muted());
}
WebIDL::ExceptionOr<void> Page::toggle_media_loop_state()
{
auto media_element = media_context_menu_element();
@ -380,6 +395,7 @@ ErrorOr<void> IPC::encode(Encoder& encoder, Web::Page::MediaContextMenu const& m
TRY(encoder.encode(menu.media_url));
TRY(encoder.encode(menu.is_video));
TRY(encoder.encode(menu.is_playing));
TRY(encoder.encode(menu.is_muted));
TRY(encoder.encode(menu.has_user_agent_controls));
TRY(encoder.encode(menu.is_looping));
return {};
@ -392,6 +408,7 @@ ErrorOr<Web::Page::MediaContextMenu> IPC::decode(Decoder& decoder)
.media_url = TRY(decoder.decode<AK::URL>()),
.is_video = TRY(decoder.decode<bool>()),
.is_playing = TRY(decoder.decode<bool>()),
.is_muted = TRY(decoder.decode<bool>()),
.has_user_agent_controls = TRY(decoder.decode<bool>()),
.is_looping = TRY(decoder.decode<bool>()),
};

View file

@ -125,11 +125,13 @@ public:
AK::URL media_url;
bool is_video { false };
bool is_playing { false };
bool is_muted { false };
bool has_user_agent_controls { false };
bool is_looping { false };
};
void did_request_media_context_menu(i32 media_id, CSSPixelPoint, DeprecatedString const& target, unsigned modifiers, MediaContextMenu);
WebIDL::ExceptionOr<void> toggle_media_play_state();
void toggle_media_mute_state();
WebIDL::ExceptionOr<void> toggle_media_loop_state();
WebIDL::ExceptionOr<void> toggle_media_controls_state();

View file

@ -156,6 +156,11 @@ void ViewImplementation::toggle_media_play_state()
client().async_toggle_media_play_state();
}
void ViewImplementation::toggle_media_mute_state()
{
client().async_toggle_media_mute_state();
}
void ViewImplementation::toggle_media_loop_state()
{
client().async_toggle_media_loop_state();

View file

@ -75,6 +75,7 @@ public:
void js_console_request_messages(i32 start_index);
void toggle_media_play_state();
void toggle_media_mute_state();
void toggle_media_loop_state();
void toggle_media_controls_state();

View file

@ -790,6 +790,11 @@ void ConnectionFromClient::toggle_media_play_state()
m_page_host->toggle_media_play_state().release_value_but_fixme_should_propagate_errors();
}
void ConnectionFromClient::toggle_media_mute_state()
{
m_page_host->toggle_media_mute_state();
}
void ConnectionFromClient::toggle_media_loop_state()
{
m_page_host->toggle_media_loop_state().release_value_but_fixme_should_propagate_errors();

View file

@ -97,6 +97,7 @@ private:
virtual void prompt_closed(Optional<String> const& response) override;
virtual void toggle_media_play_state() override;
virtual void toggle_media_mute_state() override;
virtual void toggle_media_loop_state() override;
virtual void toggle_media_controls_state() override;

View file

@ -339,6 +339,11 @@ Web::WebIDL::ExceptionOr<void> PageHost::toggle_media_play_state()
return page().toggle_media_play_state();
}
void PageHost::toggle_media_mute_state()
{
page().toggle_media_mute_state();
}
Web::WebIDL::ExceptionOr<void> PageHost::toggle_media_loop_state()
{
return page().toggle_media_loop_state();

View file

@ -50,6 +50,7 @@ public:
void prompt_closed(Optional<String> response);
Web::WebIDL::ExceptionOr<void> toggle_media_play_state();
void toggle_media_mute_state();
Web::WebIDL::ExceptionOr<void> toggle_media_loop_state();
Web::WebIDL::ExceptionOr<void> toggle_media_controls_state();

View file

@ -79,6 +79,7 @@ endpoint WebContentServer
prompt_closed(Optional<String> response) =|
toggle_media_play_state() =|
toggle_media_mute_state() =|
toggle_media_loop_state() =|
toggle_media_controls_state() =|
}