diff --git a/Applications/VisualBuilder/VBForm.cpp b/Applications/VisualBuilder/VBForm.cpp index 74987910af..bd39bf1174 100644 --- a/Applications/VisualBuilder/VBForm.cpp +++ b/Applications/VisualBuilder/VBForm.cpp @@ -1,6 +1,8 @@ #include "VBForm.h" #include "VBWidget.h" #include +#include +#include static VBForm* s_current; VBForm* VBForm::current() @@ -32,6 +34,11 @@ VBForm::VBForm(const String& name, GWidget* parent) auto groupbox1 = VBWidget::create(VBWidgetType::GGroupBox, *this); groupbox1->set_rect({ 300, 150, 161, 51 }); m_widgets.append(move(groupbox1)); + + auto context_menu = make("Context menu"); + context_menu->add_action(GAction::create("Item 1", [] (auto&) { dbgprintf("Item 1 activated!\n"); })); + context_menu->add_action(GAction::create("Item 2", [] (auto&) { dbgprintf("Item 2 activated!\n"); })); + set_context_menu(move(context_menu)); } void VBForm::insert_widget(VBWidgetType type) diff --git a/LibGUI/GMenu.cpp b/LibGUI/GMenu.cpp index 68feb1d0ce..3aab5feaba 100644 --- a/LibGUI/GMenu.cpp +++ b/LibGUI/GMenu.cpp @@ -39,6 +39,17 @@ void GMenu::add_separator() m_items.append(make(m_menu_id, GMenuItem::Separator)); } +void GMenu::popup(const Point& screen_position) +{ + if (!m_menu_id) + realize_menu(); + WSAPI_ClientMessage request; + request.type = WSAPI_ClientMessage::Type::PopupMenu; + request.menu.menu_id = m_menu_id; + request.menu.position = screen_position; + GEventLoop::post_message_to_server(request); +} + int GMenu::realize_menu() { WSAPI_ClientMessage request; diff --git a/LibGUI/GMenu.h b/LibGUI/GMenu.h index d4a38951d1..3c9ffa7814 100644 --- a/LibGUI/GMenu.h +++ b/LibGUI/GMenu.h @@ -5,6 +5,7 @@ #include class GAction; +class Point; class GMenu { public: @@ -18,6 +19,8 @@ public: void add_action(Retained&&); void add_separator(); + void popup(const Point& screen_position); + Function on_item_activation; private: diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 7bb8bd08de..8e27a5785b 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -7,7 +7,7 @@ #include #include #include - +#include #include GWidget::GWidget(GWidget* parent) @@ -161,12 +161,14 @@ void GWidget::handle_mouseup_event(GMouseEvent& event) return; // It's a click.. but is it a doubleclick? // FIXME: This needs improvement. - int elapsed_since_last_click = m_click_clock.elapsed(); - dbgprintf("Click clock elapsed: %d\n", m_click_clock.elapsed()); - if (elapsed_since_last_click < 250) { - doubleclick_event(event); - } else { - m_click_clock.start(); + if (m_click_clock.is_valid()) { + int elapsed_since_last_click = m_click_clock.elapsed(); + dbgprintf("Click clock elapsed: %d\n", m_click_clock.elapsed()); + if (elapsed_since_last_click < 250) { + doubleclick_event(event); + } else { + m_click_clock.start(); + } } } @@ -174,6 +176,13 @@ void GWidget::handle_mousedown_event(GMouseEvent& event) { if (accepts_focus()) set_focus(true); + if (event.button() == GMouseButton::Right) { + if (m_context_menu) { + m_context_menu->popup(screen_relative_rect().location().translated(event.position())); + return; + } + } + // FIXME: Maybe the click clock should be per-button. if (!m_click_clock.is_valid()) m_click_clock.start(); mousedown_event(event); @@ -416,3 +425,10 @@ void GWidget::set_enabled(bool enabled) m_enabled = enabled; update(); } + +void GWidget::set_context_menu(OwnPtr&& context_menu) +{ + // FIXME: Support switching context menus. + ASSERT(!m_context_menu); + m_context_menu = move(context_menu); +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index a7edf79982..d7050b4734 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -11,6 +11,7 @@ class GraphicsBitmap; class GLayout; +class GMenu; class GWindow; enum class SizePolicy { Fixed, Fill }; @@ -41,6 +42,9 @@ public: bool is_enabled() const { return m_enabled; } void set_enabled(bool); + const GMenu* context_menu() const { return m_context_menu.ptr(); } + void set_context_menu(OwnPtr&&); + virtual void event(CEvent&) override; virtual void paint_event(GPaintEvent&); virtual void resize_event(GResizeEvent&); @@ -186,4 +190,5 @@ private: bool m_enabled { true }; CElapsedTimer m_click_clock; + OwnPtr m_context_menu; }; diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index dd243454a5..f77ca19620 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -192,6 +192,7 @@ struct WSAPI_ClientMessage { GetWallpaper, SetWindowOverrideCursor, WM_SetActiveWindow, + PopupMenu, }; Type type { Invalid }; int window_id { -1 }; @@ -214,6 +215,7 @@ struct WSAPI_ClientMessage { char shortcut_text[32]; int shortcut_text_length; bool enabled; + WSAPI_Point position; } menu; struct { WSAPI_Rect rect; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 4231bd7be1..1e6f2b88d5 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -223,6 +223,19 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request) post_message(response); } +void WSClientConnection::handle_request(const WSAPIPopupMenuRequest& request) +{ + int menu_id = request.menu_id(); + auto position = request.position(); + auto it = m_menus.find(menu_id); + if (it == m_menus.end()) { + post_error("WSAPIPopupMenuRequest: Bad menu ID"); + return; + } + auto& menu = *(*it).value; + menu.popup(position); +} + void WSClientConnection::handle_request(const WSAPIUpdateMenuItemRequest& request) { int menu_id = request.menu_id(); @@ -616,6 +629,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request) return handle_request(static_cast(request)); case WSMessage::WMAPISetActiveWindowRequest: return handle_request(static_cast(request)); + case WSMessage::APIPopupMenuRequest: + return handle_request(static_cast(request)); default: break; } diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index 1f4a2da3bc..c9a7680f22 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -70,6 +70,7 @@ private: void handle_request(const WSAPIGetWallpaperRequest&); void handle_request(const WSAPISetWindowOverrideCursorRequest&); void handle_request(const WSWMAPISetActiveWindowRequest&); + void handle_request(const WSAPIPopupMenuRequest&); void post_error(const String&); diff --git a/Servers/WindowServer/WSMenu.cpp b/Servers/WindowServer/WSMenu.cpp index 38d2e7b445..afc6e6023a 100644 --- a/Servers/WindowServer/WSMenu.cpp +++ b/Servers/WindowServer/WSMenu.cpp @@ -178,4 +178,14 @@ WSMenuItem* WSMenu::item_at(const Point& position) void WSMenu::close() { WSWindowManager::the().close_menu(*this); -}; + if (menu_window()) + menu_window()->set_visible(false); +} + +void WSMenu::popup(const Point& position) +{ + ASSERT(!is_empty()); + auto& window = ensure_menu_window(); + window.move_to(position); + window.set_visible(true); +} diff --git a/Servers/WindowServer/WSMenu.h b/Servers/WindowServer/WSMenu.h index b8b46edc14..45d0bc3c07 100644 --- a/Servers/WindowServer/WSMenu.h +++ b/Servers/WindowServer/WSMenu.h @@ -73,6 +73,8 @@ public: void close(); + void popup(const Point&); + private: virtual void on_message(const WSMessage&) override; diff --git a/Servers/WindowServer/WSMessage.h b/Servers/WindowServer/WSMessage.h index 58b8f31fef..7cee53ef55 100644 --- a/Servers/WindowServer/WSMessage.h +++ b/Servers/WindowServer/WSMessage.h @@ -57,6 +57,7 @@ public: APIGetWallpaperRequest, APISetWindowOverrideCursorRequest, WMAPISetActiveWindowRequest, + APIPopupMenuRequest, __End_API_Client_Requests, }; @@ -189,6 +190,24 @@ private: int m_menu_id { 0 }; }; +class WSAPIPopupMenuRequest : public WSAPIClientRequest { +public: + WSAPIPopupMenuRequest(int client_id, int menu_id, const Point& position) + : WSAPIClientRequest(WSMessage::APIPopupMenuRequest, client_id) + , m_menu_id(menu_id) + , m_position(position) + { + } + + int menu_id() const { return m_menu_id; } + Point position() const { return m_position; } + +private: + int m_menu_id; + Point m_position; +}; + + class WSAPICreateMenuRequest : public WSAPIClientRequest { public: WSAPICreateMenuRequest(int client_id, const String& text) diff --git a/Servers/WindowServer/WSMessageLoop.cpp b/Servers/WindowServer/WSMessageLoop.cpp index d2e31a1204..ddb93ad8e8 100644 --- a/Servers/WindowServer/WSMessageLoop.cpp +++ b/Servers/WindowServer/WSMessageLoop.cpp @@ -290,6 +290,9 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess ASSERT(message.text_length < (ssize_t)sizeof(message.text)); post_message(client, make(client_id, String(message.text, message.text_length))); break; + case WSAPI_ClientMessage::Type::PopupMenu: + post_message(client, make(client_id, message.menu.menu_id, message.menu.position)); + break; case WSAPI_ClientMessage::Type::DestroyMenu: post_message(client, make(client_id, message.menu.menu_id)); break;