From b221d7fe8b8f1bc1cde1796fea3d366753f918b3 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 6 May 2026 12:00:59 -0400 Subject: [PATCH] LibWeb+LibWebView+WebContent+UI: Add an action to cut text --- Base/res/icons/16x16/edit-cut.png | Bin 0 -> 1459 bytes Libraries/LibWeb/HTML/Navigable.cpp | 19 ++++++++++++++++ Libraries/LibWeb/HTML/Navigable.h | 1 + Libraries/LibWeb/Page/EventHandler.cpp | 8 ++++--- Libraries/LibWeb/Page/Page.h | 7 +++++- Libraries/LibWeb/UIEvents/InputTypes.h | 1 + Libraries/LibWebView/Application.cpp | 6 +++++ Libraries/LibWebView/Application.h | 2 ++ Libraries/LibWebView/Menu.h | 1 + Libraries/LibWebView/ViewImplementation.cpp | 22 ++++++++++++++----- Libraries/LibWebView/ViewImplementation.h | 3 ++- Libraries/LibWebView/WebContentClient.cpp | 4 ++-- Libraries/LibWebView/WebContentClient.h | 2 +- Services/WebContent/ConnectionFromClient.cpp | 7 ++++++ Services/WebContent/ConnectionFromClient.h | 1 + Services/WebContent/PageClient.cpp | 4 ++-- Services/WebContent/PageClient.h | 2 +- Services/WebContent/WebContentClient.ipc | 2 +- Services/WebContent/WebContentServer.ipc | 1 + UI/AppKit/Application/ApplicationDelegate.mm | 5 +---- UI/AppKit/Interface/Menu.mm | 7 ++++++ UI/Gtk/Menu.cpp | 6 +++++ UI/Gtk/WebContentView.cpp | 3 +++ UI/Qt/BrowserWindow.cpp | 1 + UI/Qt/Menu.cpp | 4 ++++ UI/cmake/ResourceFiles.cmake | 1 + 26 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 Base/res/icons/16x16/edit-cut.png diff --git a/Base/res/icons/16x16/edit-cut.png b/Base/res/icons/16x16/edit-cut.png new file mode 100644 index 0000000000000000000000000000000000000000..f58822be58474a0bef577604a500de34edc47303 GIT binary patch literal 1459 zcmV;k1x)&hP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=H6mg6W4MgLjFECERf#Bwm0b9x7}{CQyFN~)4{ zGSy!*WsVI7SrYF8iQE7F)!kpXn0#b3G*%_em?EUK#sfMj(CzJ*pUd@EZNQKGFJ$4Grdzle=X3B z$d{J;tp3^hBtDnbFQ2rGOCvPy4v>E8mop4s2bMD5rAR(8CnD%*0924UOW+LzNR=Cs%gT{QH_j%2 zQ0%M(HdzW_g`ikJDHzsJQPH5PQInd}tR+eeQDclLjzg{_Ng-*9DJ4re6%z}l7A=`s zwvssu&OpsEr))XrQrHy)D)3ccU(mDhCR^Bai(A@k%Uh{fJ~dRWv8HM@*V4F2!!glf zOU+ttWhb}eq}ao*d)(7*d)`aOTIwMF$yd4n1)YCK5|yL3`RuS@CT zg2r*8gBgf%AP`T302VY4X5J_S9^?iypBbA%8AIJ(GqH7`ELE==*Q8IqyLqo9`b1L zR;!anQd^lLJ=89CHTre%p=U4L|eNW=w z;eW~Nt&#gT?C%-5chY~BwR=70i(yO-f>-TZ({>=IK+)>Bh ztL**(YFP#895DVZ0004nX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ?*hmB6d)5 z$WWauii$XD6^c+H)C#RSm|XfHG-*guTpR`0f`cE6RR* znM%$Phl#~}3o9+m3Z_OpNgPo%o$`fromI|ToV8+^HSWn@7|QC)OI)Wph&UFo2niw- zl(B&#Y(#0*NwJWm{g{V;!1YVyQpmLdMvi%uph0&1;D7MDTPrs)UQY_efc6*1`4|EM zyFjhtIN!&PQ#%3t&%l-5^p~o@^e5?+rWQE@y0?Lg>!zmc0hc>K-;*JmvMc#%3b`Ec zen#Jv0eWwN;A*|M#y(CTfE0C=xB(6hf#E!5uX((?v$eN>&ouh`0dQ1urp-+7;Q#;t z24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2jm733mqA; zwjEOd0037>L_t(I%VS`m0r*dfI1`bI|5NG;oCYuvVaR_ #include #include +#include #include namespace Web::HTML { @@ -3010,6 +3011,24 @@ String Navigable::selected_text() const return visible_text_in_range(*range); } +String Navigable::cut_selected_text() const +{ + auto document = active_document(); + if (!document) + return {}; + + auto* target = document->active_input_events_target(); + if (!target) + return {}; + + auto text = selected_text(); + if (text.is_empty()) + return {}; + + target->handle_delete(UIEvents::InputTypes::deleteByCut); + return text; +} + void Navigable::select_all() { auto document = active_document(); diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index 3c26eda858..fc71fd7413 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -208,6 +208,7 @@ public: Page const& page() const { return m_page; } String selected_text() const; + String cut_selected_text() const; void select_all(); void paste(Utf16String const&); diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index bde18945d2..35b6d97526 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -923,12 +923,13 @@ void EventHandler::maybe_show_context_menu(GC::Ref node, MouseEventCo // context menu is open. clear_mousedown_tracking(); + GC::Ref document = *m_navigable->active_document(); + // NB: Event dispatches above may have run JS that invalidated layout. - m_navigable->active_document()->update_layout(DOM::UpdateLayoutReason::EventHandlerShowContextMenu); + document->update_layout(DOM::UpdateLayoutReason::EventHandlerShowContextMenu); auto top_level_viewport_position = m_navigable->to_top_level_position(viewport_position); if (GC::Ptr link = node->enclosing_link_element()) { - GC::Ref document = *m_navigable->active_document(); auto href = link->href(); auto url = document->encoding_parse_url(href); if (url.has_value()) @@ -971,7 +972,8 @@ void EventHandler::maybe_show_context_menu(GC::Ref node, MouseEventCo m_navigable->page().did_request_media_context_menu(media_element.unique_id(), top_level_viewport_position, "", modifiers, menu); } else { - m_navigable->page().client().page_did_request_context_menu(top_level_viewport_position); + auto for_input_events_target = document->active_input_events_target() ? ContextMenuForInputEventsTarget::Yes : ContextMenuForInputEventsTarget::No; + m_navigable->page().client().page_did_request_context_menu(top_level_viewport_position, for_input_events_target); } } } diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index f19ef0596b..cdeb7ea50b 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -364,6 +364,11 @@ enum class DisplayListPlayerType { SkiaCPU, }; +enum class ContextMenuForInputEventsTarget : u8 { + No, + Yes, +}; + class PageClient : public JS::Cell { GC_CELL(PageClient, JS::Cell); @@ -401,7 +406,7 @@ public: virtual void page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document&) { } virtual void page_did_finish_loading(URL::URL const&) { } virtual void page_did_request_cursor_change(Gfx::Cursor const&) { } - virtual void page_did_request_context_menu(CSSPixelPoint) { } + virtual void page_did_request_context_menu(CSSPixelPoint, ContextMenuForInputEventsTarget) { } virtual void page_did_request_link_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { } virtual void page_did_request_image_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Optional) { } virtual void page_did_request_media_context_menu(CSSPixelPoint, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Page::MediaContextMenu const&) { } diff --git a/Libraries/LibWeb/UIEvents/InputTypes.h b/Libraries/LibWeb/UIEvents/InputTypes.h index f532efa3e6..b55fd89076 100644 --- a/Libraries/LibWeb/UIEvents/InputTypes.h +++ b/Libraries/LibWeb/UIEvents/InputTypes.h @@ -12,6 +12,7 @@ namespace Web::UIEvents::InputTypes { // https://w3c.github.io/input-events/#interface-InputEvent-Attributes #define ENUMERATE_INPUT_TYPES \ + __ENUMERATE_INPUT_TYPE(deleteByCut) \ __ENUMERATE_INPUT_TYPE(deleteContentBackward) \ __ENUMERATE_INPUT_TYPE(deleteContentForward) \ __ENUMERATE_INPUT_TYPE(insertFromPaste) \ diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 2158bd2f31..f3c6133268 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -911,6 +911,12 @@ void Application::initialize_actions() insert_clipboard_entry({ move(text), "text/plain"_string }); } }); + m_cut_selection_action = Action::create("Cut"sv, ActionID::CutSelection, [this]() { + if (auto view = active_web_view(); view.has_value()) { + if (auto text = view->cut_selected_text(); !text.is_empty()) + insert_clipboard_entry({ move(text), "text/plain"_string }); + } + }); m_paste_action = Action::create("Paste"sv, ActionID::Paste, [this]() { if (auto view = active_web_view(); view.has_value()) view->paste_text_from_clipboard(); diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index 9427d8533d..3a20062b9b 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -133,6 +133,7 @@ public: Action& reload_action() { return *m_reload_action; } Action& copy_selection_action() { return *m_copy_selection_action; } + Action& cut_selection_action() { return *m_cut_selection_action; } Action& paste_action() { return *m_paste_action; } Action& select_all_action() { return *m_select_all_action; } @@ -287,6 +288,7 @@ private: RefPtr m_reload_action; RefPtr m_copy_selection_action; + RefPtr m_cut_selection_action; RefPtr m_paste_action; RefPtr m_select_all_action; diff --git a/Libraries/LibWebView/Menu.h b/Libraries/LibWebView/Menu.h index 376bb7fa06..f5dad69b7a 100644 --- a/Libraries/LibWebView/Menu.h +++ b/Libraries/LibWebView/Menu.h @@ -29,6 +29,7 @@ enum class ActionID { Reload, CopySelection, + CutSelection, Paste, SelectAll, diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index 897b33762c..58c21f22d7 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -5,8 +5,8 @@ */ #include +#include #include -#include #include #include #include @@ -348,6 +348,11 @@ ByteString ViewImplementation::selected_text() return client().get_selected_text(page_id()); } +ByteString ViewImplementation::cut_selected_text() +{ + return client().cut_selected_text(page_id()); +} + Optional ViewImplementation::selected_text_with_whitespace_collapsed() { auto selected_text = MUST(Web::Infra::strip_and_collapse_whitespace(this->selected_text())); @@ -1069,6 +1074,7 @@ void ViewImplementation::initialize_context_menus() m_page_context_menu->add_action(*m_navigate_forward_action); m_page_context_menu->add_action(application.reload_action()); m_page_context_menu->add_separator(); + m_page_context_menu->add_action(application.cut_selection_action()); m_page_context_menu->add_action(application.copy_selection_action()); m_page_context_menu->add_action(application.paste_action()); m_page_context_menu->add_action(application.select_all_action()); @@ -1111,12 +1117,18 @@ void ViewImplementation::initialize_context_menus() m_media_context_menu->add_action(*m_copy_url_action); } -void ViewImplementation::did_request_page_context_menu(Badge, Gfx::IntPoint content_position) +void ViewImplementation::did_request_page_context_menu(Badge, Gfx::IntPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target) { - auto const& search_engine = Application::settings().search_engine(); + auto& cut_selection_action = Application::the().cut_selection_action(); + cut_selection_action.set_visible(for_input_events_target == Web::ContextMenuForInputEventsTarget::Yes); - auto selected_text = search_engine.has_value() ? selected_text_with_whitespace_collapsed() : OptionalNone {}; - TemporaryChange change_url { m_search_text, move(selected_text) }; + auto const& search_engine = Application::settings().search_engine(); + m_search_text = search_engine.has_value() ? selected_text_with_whitespace_collapsed() : OptionalNone {}; + + ScopeGuard guard { [&]() { + cut_selection_action.set_visible(true); + m_search_text.clear(); + } }; if (m_search_text.has_value()) { m_search_selected_text_action->set_text(search_engine->format_search_query_for_display(*m_search_text)); diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index f75d794027..e00e28176d 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -104,6 +104,7 @@ public: Optional document_cookie_version(URL::URL const&) const; ByteString selected_text(); + ByteString cut_selected_text(); Optional selected_text_with_whitespace_collapsed(); void select_all(); void find_in_page(String const& query, CaseSensitivity = CaseSensitivity::CaseInsensitive); @@ -264,7 +265,7 @@ public: Menu& image_context_menu() { return *m_image_context_menu; } Menu& media_context_menu() { return *m_media_context_menu; } - void did_request_page_context_menu(Badge, Gfx::IntPoint content_position); + void did_request_page_context_menu(Badge, Gfx::IntPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target); void did_request_link_context_menu(Badge, Gfx::IntPoint content_position, URL::URL url); void did_request_image_context_menu(Badge, Gfx::IntPoint content_position, URL::URL url, Optional bitmap); void did_request_media_context_menu(Badge, Gfx::IntPoint content_position, Web::Page::MediaContextMenu menu); diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index b6e58e583e..f3be48b149 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -355,10 +355,10 @@ void WebContentClient::did_middle_click_link(u64, URL::URL url, ByteString, unsi Application::the().open_url_in_new_tab(url, Web::HTML::ActivateTab::No); } -void WebContentClient::did_request_context_menu(u64 page_id, Gfx::IntPoint content_position) +void WebContentClient::did_request_context_menu(u64 page_id, Gfx::IntPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target) { if (auto view = view_for_page_id(page_id); view.has_value()) - view->did_request_page_context_menu({}, content_position); + view->did_request_page_context_menu({}, content_position, for_input_events_target); } void WebContentClient::did_request_link_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString, unsigned) diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 3dc230ae68..24df57e9b7 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -83,7 +83,7 @@ private: virtual void did_click_link(u64 page_id, URL::URL, ByteString, unsigned) override; virtual void did_middle_click_link(u64 page_id, URL::URL, ByteString, unsigned) override; virtual void did_start_loading(u64 page_id, URL::URL, bool) override; - virtual void did_request_context_menu(u64 page_id, Gfx::IntPoint) override; + virtual void did_request_context_menu(u64 page_id, Gfx::IntPoint, Web::ContextMenuForInputEventsTarget) override; virtual void did_request_link_context_menu(u64 page_id, Gfx::IntPoint, URL::URL, ByteString, unsigned) override; virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL, ByteString, unsigned, Optional) override; virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString, unsigned, Web::Page::MediaContextMenu) override; diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 4c967826a0..d331f8a464 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -1088,6 +1088,13 @@ Messages::WebContentServer::GetSelectedTextResponse ConnectionFromClient::get_se return ByteString {}; } +Messages::WebContentServer::CutSelectedTextResponse ConnectionFromClient::cut_selected_text(u64 page_id) +{ + if (auto page = this->page(page_id); page.has_value()) + return page->page().focused_navigable().cut_selected_text().to_byte_string(); + return ByteString {}; +} + void ConnectionFromClient::select_all(u64 page_id) { if (auto page = this->page(page_id); page.has_value()) diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index 3ff5495754..d28b2e5300 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -155,6 +155,7 @@ private: virtual void request_internal_page_info(u64 page_id, WebView::PageInfoType) override; virtual Messages::WebContentServer::GetSelectedTextResponse get_selected_text(u64 page_id) override; + virtual Messages::WebContentServer::CutSelectedTextResponse cut_selected_text(u64 page_id) override; virtual void select_all(u64 page_id) override; virtual void find_in_page(u64 page_id, String query, CaseSensitivity) override; diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 0079d76dec..fd278fb9ff 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -403,9 +403,9 @@ void PageClient::page_did_set_device_pixel_ratio_for_testing(double ratio) set_viewport(m_viewport_size, ratio); } -void PageClient::page_did_request_context_menu(Web::CSSPixelPoint content_position) +void PageClient::page_did_request_context_menu(Web::CSSPixelPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target) { - client().async_did_request_context_menu(m_id, page().css_to_device_point(content_position).to_type()); + client().async_did_request_context_menu(m_id, page().css_to_device_point(content_position).to_type(), for_input_events_target); } void PageClient::page_did_request_link_context_menu(Web::CSSPixelPoint content_position, URL::URL const& url, ByteString const& target, unsigned modifiers) diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index b51c942698..ffc4e181de 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -139,7 +139,7 @@ private: virtual void page_did_unhover_link() override; virtual void page_did_click_link(URL::URL const&, ByteString const& target, unsigned modifiers) override; virtual void page_did_middle_click_link(URL::URL const&, ByteString const& target, unsigned modifiers) override; - virtual void page_did_request_context_menu(Web::CSSPixelPoint) override; + virtual void page_did_request_context_menu(Web::CSSPixelPoint, Web::ContextMenuForInputEventsTarget) override; virtual void page_did_request_link_context_menu(Web::CSSPixelPoint, URL::URL const&, ByteString const& target, unsigned modifiers) override; virtual void page_did_request_image_context_menu(Web::CSSPixelPoint, URL::URL const&, ByteString const& target, unsigned modifiers, Optional) override; virtual void page_did_request_media_context_menu(Web::CSSPixelPoint, ByteString const& target, unsigned modifiers, Web::Page::MediaContextMenu const&) override; diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc index 00fa5ed982..198ff1fefa 100644 --- a/Services/WebContent/WebContentClient.ipc +++ b/Services/WebContent/WebContentClient.ipc @@ -50,7 +50,7 @@ endpoint WebContentClient did_unhover_link(u64 page_id) =| did_click_link(u64 page_id, URL::URL url, ByteString target, unsigned modifiers) =| did_middle_click_link(u64 page_id, URL::URL url, ByteString target, unsigned modifiers) =| - did_request_context_menu(u64 page_id, Gfx::IntPoint content_position) =| + did_request_context_menu(u64 page_id, Gfx::IntPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target) =| did_request_link_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString target, unsigned modifiers) =| did_request_image_context_menu(u64 page_id, Gfx::IntPoint content_position, URL::URL url, ByteString target, unsigned modifiers, Optional bitmap) =| did_request_media_context_menu(u64 page_id, Gfx::IntPoint content_position, ByteString target, unsigned modifiers, Web::Page::MediaContextMenu menu) =| diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index c072b2604a..930695b373 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -89,6 +89,7 @@ endpoint WebContentServer request_internal_page_info(u64 page_id, WebView::PageInfoType type) =| get_selected_text(u64 page_id) => (ByteString selection) + cut_selected_text(u64 page_id) => (ByteString selection) select_all(u64 page_id) =| paste(u64 page_id, Utf16String text) =| diff --git a/UI/AppKit/Application/ApplicationDelegate.mm b/UI/AppKit/Application/ApplicationDelegate.mm index eb66f7ff99..887e0bd393 100644 --- a/UI/AppKit/Application/ApplicationDelegate.mm +++ b/UI/AppKit/Application/ApplicationDelegate.mm @@ -299,10 +299,7 @@ keyEquivalent:@"y"]]; [submenu addItem:[NSMenuItem separatorItem]]; - [submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Cut" - action:@selector(cut:) - keyEquivalent:@"x"]]; - + [submenu addItem:Ladybird::create_application_menu_item(WebView::Application::the().cut_selection_action())]; [submenu addItem:Ladybird::create_application_menu_item(WebView::Application::the().copy_selection_action())]; [submenu addItem:Ladybird::create_application_menu_item(WebView::Application::the().paste_action())]; [submenu addItem:[NSMenuItem separatorItem]]; diff --git a/UI/AppKit/Interface/Menu.mm b/UI/AppKit/Interface/Menu.mm index 236ff30951..50e0f4787e 100644 --- a/UI/AppKit/Interface/Menu.mm +++ b/UI/AppKit/Interface/Menu.mm @@ -43,6 +43,9 @@ case WebView::ActionID::CopySelection: [NSApp sendAction:@selector(copy:) to:nil from:sender]; return; + case WebView::ActionID::CutSelection: + [NSApp sendAction:@selector(cut:) to:nil from:sender]; + return; case WebView::ActionID::Paste: [NSApp sendAction:@selector(paste:) to:nil from:sender]; return; @@ -206,6 +209,10 @@ static void initialize_native_icon(WebView::Action& action, id control) set_control_image(control, @"document.on.document"); [control setKeyEquivalent:@"c"]; break; + case WebView::ActionID::CutSelection: + set_control_image(control, @"scissors"); + [control setKeyEquivalent:@"x"]; + break; case WebView::ActionID::Paste: set_control_image(control, @"document.on.clipboard"); [control setKeyEquivalent:@"v"]; diff --git a/UI/Gtk/Menu.cpp b/UI/Gtk/Menu.cpp index 722d7bc066..d364156ba7 100644 --- a/UI/Gtk/Menu.cpp +++ b/UI/Gtk/Menu.cpp @@ -60,6 +60,8 @@ static char const* primary_accelerator_for_action(WebView::ActionID id) return "r"; case WebView::ActionID::CopySelection: return "c"; + case WebView::ActionID::CutSelection: + return "x"; case WebView::ActionID::Paste: return "v"; case WebView::ActionID::SelectAll: @@ -122,6 +124,10 @@ static void initialize_native_control(WebView::Action& action, GSimpleAction* ga set_icon("edit-copy-symbolic"); set_accel("c"); break; + case WebView::ActionID::CutSelection: + set_icon("edit-cut-symbolic"); + set_accel("x"); + break; case WebView::ActionID::Paste: set_icon("edit-paste-symbolic"); set_accel("v"); diff --git a/UI/Gtk/WebContentView.cpp b/UI/Gtk/WebContentView.cpp index 4a1986a99a..ceb39a5dee 100644 --- a/UI/Gtk/WebContentView.cpp +++ b/UI/Gtk/WebContentView.cpp @@ -121,6 +121,9 @@ void WebContentView::finish_handling_key_event(Web::KeyEvent const& event) case Web::UIEvents::Key_C: app.copy_selection_action().activate(); break; + case Web::UIEvents::Key_X: + app.cut_selection_action().activate(); + break; case Web::UIEvents::Key_V: app.paste_action().activate(); break; diff --git a/UI/Qt/BrowserWindow.cpp b/UI/Qt/BrowserWindow.cpp index 33c23ff2ab..d0482ad1b4 100644 --- a/UI/Qt/BrowserWindow.cpp +++ b/UI/Qt/BrowserWindow.cpp @@ -233,6 +233,7 @@ BrowserWindow::BrowserWindow(Vector const& initial_urls, IsPopupWindow auto* edit_menu = m_hamburger_menu->addMenu("&Edit"); menuBar()->addMenu(edit_menu); + edit_menu->addAction(create_application_action(*this, Application::the().cut_selection_action())); edit_menu->addAction(create_application_action(*this, Application::the().copy_selection_action())); edit_menu->addAction(create_application_action(*this, Application::the().paste_action())); edit_menu->addAction(create_application_action(*this, Application::the().select_all_action())); diff --git a/UI/Qt/Menu.cpp b/UI/Qt/Menu.cpp index 7ff75eee45..dea9d82a50 100644 --- a/UI/Qt/Menu.cpp +++ b/UI/Qt/Menu.cpp @@ -134,6 +134,10 @@ static void initialize_native_control(WebView::Action& action, QAction& qaction, qaction.setIcon(load_icon_from_uri("resource://icons/16x16/edit-copy.png"sv)); qaction.setShortcut(QKeySequence::StandardKey::Copy); break; + case WebView::ActionID::CutSelection: + qaction.setIcon(load_icon_from_uri("resource://icons/16x16/edit-cut.png"sv)); + qaction.setShortcut(QKeySequence::StandardKey::Cut); + break; case WebView::ActionID::Paste: qaction.setIcon(load_icon_from_uri("resource://icons/16x16/paste.png"sv)); qaction.setShortcut(QKeySequence::StandardKey::Paste); diff --git a/UI/cmake/ResourceFiles.cmake b/UI/cmake/ResourceFiles.cmake index 03eafa7072..11e56fb899 100644 --- a/UI/cmake/ResourceFiles.cmake +++ b/UI/cmake/ResourceFiles.cmake @@ -12,6 +12,7 @@ set(16x16_ICONS close-tab.png download.png edit-copy.png + edit-cut.png error.png filetype-css.png filetype-folder-open.png