From dcf6726487d17f5fb36803e3b8d74ed75ee4fa53 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 31 Mar 2019 23:52:02 +0200 Subject: WindowServer: Add support for per-window override cursors. Use this to implement automatic switching to an I-beam cursor when hovering over a GTextEditor. :^) --- LibGUI/GTextEditor.cpp | 13 +++++++++++++ LibGUI/GTextEditor.h | 3 +++ LibGUI/GWindow.cpp | 11 +++++++++++ LibGUI/GWindow.h | 8 ++++++++ Servers/WindowServer/WSAPITypes.h | 9 +++++++++ Servers/WindowServer/WSClientConnection.cpp | 14 ++++++++++++++ Servers/WindowServer/WSClientConnection.h | 1 + Servers/WindowServer/WSCursor.cpp | 13 +++++++++++++ Servers/WindowServer/WSCursor.h | 7 +++++++ Servers/WindowServer/WSMessage.h | 19 +++++++++++++++++++ Servers/WindowServer/WSMessageLoop.cpp | 4 ++++ Servers/WindowServer/WSWindow.h | 5 +++++ Servers/WindowServer/WSWindowManager.cpp | 3 +++ 13 files changed, 110 insertions(+) diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index c9ca1b4a6e..f409f06a54 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -801,3 +802,15 @@ void GTextEditor::paste() printf("Paste: \"%s\"\n", paste_text.characters()); insert_at_cursor_or_replace_selection(paste_text); } + +void GTextEditor::enter_event(GEvent&) +{ + ASSERT(window()); + window()->set_override_cursor(GStandardCursor::IBeam); +} + +void GTextEditor::leave_event(GEvent&) +{ + ASSERT(window()); + window()->set_override_cursor(GStandardCursor::None); +} diff --git a/LibGUI/GTextEditor.h b/LibGUI/GTextEditor.h index abc529b76f..0e2313c9b5 100644 --- a/LibGUI/GTextEditor.h +++ b/LibGUI/GTextEditor.h @@ -111,6 +111,9 @@ private: virtual void focusout_event(GEvent&) override; virtual void timer_event(GTimerEvent&) override; virtual bool accepts_focus() const override { return true; } + virtual void enter_event(GEvent&) override; + virtual void leave_event(GEvent&) override; + void paint_ruler(Painter&); void update_content_size(); diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 7c8e1461eb..3c9bc8bf73 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -140,6 +140,17 @@ void GWindow::set_rect(const Rect& a_rect) GEventLoop::current().post_message_to_server(request); } +void GWindow::set_override_cursor(GStandardCursor cursor) +{ + if (!m_window_id) + return; + WSAPI_ClientMessage request; + request.type = WSAPI_ClientMessage::Type::SetWindowOverrideCursor; + request.window_id = m_window_id; + request.cursor.cursor = (WSAPI_StandardCursor)cursor; + GEventLoop::current().post_message_to_server(request); +} + void GWindow::event(GEvent& event) { if (event.is_mouse_event()) { diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 27b6d3e68b..86ee57bcaa 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -8,6 +8,12 @@ class GWidget; +enum class GStandardCursor { + None = 0, + Arrow, + IBeam, +}; + class GWindow : public GObject { public: GWindow(GObject* parent = nullptr); @@ -84,6 +90,8 @@ public: Size base_size() const { return m_base_size; } void set_base_size(const Size& size) { m_base_size = size; } + void set_override_cursor(GStandardCursor); + virtual const char* class_name() const override { return "GWindow"; } private: diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index c05acc3f32..31bcc54e99 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -47,6 +47,11 @@ struct WSAPI_KeyModifiers { enum { Ctrl = 1 << 2, }; }; +enum class WSAPI_StandardCursor : unsigned char { + None = 0, + Arrow, + IBeam, +}; struct WSAPI_ServerMessage { enum Type : unsigned { @@ -164,6 +169,7 @@ struct WSAPI_ClientMessage { Greeting, SetWallpaper, GetWallpaper, + SetWindowOverrideCursor, }; Type type { Invalid }; int window_id { -1 }; @@ -203,6 +209,9 @@ struct WSAPI_ClientMessage { int shared_buffer_id; int contents_size; } clipboard; + struct { + WSAPI_StandardCursor cursor; + } cursor; }; }; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 5a5b93a6b2..3ca579d96f 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -491,6 +491,18 @@ void WSClientConnection::handle_request(WSAPISetGlobalCursorTrackingRequest& req window.set_global_cursor_tracking_enabled(request.value()); } +void WSClientConnection::handle_request(WSAPISetWindowOverrideCursorRequest& request) +{ + int window_id = request.window_id(); + auto it = m_windows.find(window_id); + if (it == m_windows.end()) { + post_error("Bad window ID"); + return; + } + auto& window = *(*it).value; + window.set_override_cursor(WSCursor::create(request.cursor())); +} + void WSClientConnection::on_request(WSAPIClientRequest& request) { switch (request.type()) { @@ -542,6 +554,8 @@ void WSClientConnection::on_request(WSAPIClientRequest& request) return handle_request(static_cast(request)); case WSMessage::APIGetWallpaperRequest: return handle_request(static_cast(request)); + case WSMessage::APISetWindowOverrideCursorRequest: + return handle_request(static_cast(request)); default: break; } diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index f92f8db505..f596ac59e1 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -64,6 +64,7 @@ private: void handle_request(WSAPISetWindowOpacityRequest&); void handle_request(WSAPISetWallpaperRequest&); void handle_request(WSAPIGetWallpaperRequest&); + void handle_request(WSAPISetWindowOverrideCursorRequest&); void post_error(const String&); diff --git a/Servers/WindowServer/WSCursor.cpp b/Servers/WindowServer/WSCursor.cpp index 12261a5f54..61854a75a7 100644 --- a/Servers/WindowServer/WSCursor.cpp +++ b/Servers/WindowServer/WSCursor.cpp @@ -19,3 +19,16 @@ Retained WSCursor::create(Retained&& bitmap, const Poi { return adopt(*new WSCursor(move(bitmap), hotspot)); } + +RetainPtr WSCursor::create(WSStandardCursor standard_cursor) +{ + switch (standard_cursor) { + case WSStandardCursor::None: + return nullptr; + case WSStandardCursor::Arrow: + return create(*GraphicsBitmap::load_from_file("/res/cursors/arrow.png")); + case WSStandardCursor::IBeam: + return create(*GraphicsBitmap::load_from_file("/res/cursors/i-beam.png")); + } + ASSERT_NOT_REACHED(); +} diff --git a/Servers/WindowServer/WSCursor.h b/Servers/WindowServer/WSCursor.h index 7125ad2a78..4a07f2b6de 100644 --- a/Servers/WindowServer/WSCursor.h +++ b/Servers/WindowServer/WSCursor.h @@ -2,10 +2,17 @@ #include +enum class WSStandardCursor { + None = 0, + Arrow, + IBeam, +}; + class WSCursor : public Retainable { public: static Retained create(Retained&&, const Point& hotspot); static Retained create(Retained&&); + static RetainPtr create(WSStandardCursor); ~WSCursor(); Point hotspot() const { return m_hotspot; } diff --git a/Servers/WindowServer/WSMessage.h b/Servers/WindowServer/WSMessage.h index 12dd6fcf35..0b4274bd34 100644 --- a/Servers/WindowServer/WSMessage.h +++ b/Servers/WindowServer/WSMessage.h @@ -5,6 +5,7 @@ #include #include #include +#include class WSMessage { public: @@ -49,6 +50,7 @@ public: APIGetClipboardContentsRequest, APISetWallpaperRequest, APIGetWallpaperRequest, + APISetWindowOverrideCursorRequest, __End_API_Client_Requests, }; @@ -229,6 +231,23 @@ private: int m_menu_id { 0 }; }; +class WSAPISetWindowOverrideCursorRequest final : public WSAPIClientRequest { +public: + explicit WSAPISetWindowOverrideCursorRequest(int client_id, int window_id, WSStandardCursor cursor) + : WSAPIClientRequest(WSMessage::APISetWindowOverrideCursorRequest, client_id) + , m_window_id(window_id) + , m_cursor(cursor) + { + } + + int window_id() const { return m_window_id; } + WSStandardCursor cursor() const { return m_cursor; } + +private: + int m_window_id { 0 }; + WSStandardCursor m_cursor { WSStandardCursor::None }; +}; + class WSAPISetWallpaperRequest final : public WSAPIClientRequest { public: explicit WSAPISetWallpaperRequest(int client_id, String&& wallpaper) diff --git a/Servers/WindowServer/WSMessageLoop.cpp b/Servers/WindowServer/WSMessageLoop.cpp index 21ebc80ee7..5b46ae2345 100644 --- a/Servers/WindowServer/WSMessageLoop.cpp +++ b/Servers/WindowServer/WSMessageLoop.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -330,6 +331,9 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess break; case WSAPI_ClientMessage::Type::GetWallpaper: post_message(client, make(client_id)); + break; + case WSAPI_ClientMessage::Type::SetWindowOverrideCursor: + post_message(client, make(client_id, message.window_id, (WSStandardCursor)message.cursor.cursor)); default: break; } diff --git a/Servers/WindowServer/WSWindow.h b/Servers/WindowServer/WSWindow.h index 68a02f4f71..624b951d57 100644 --- a/Servers/WindowServer/WSWindow.h +++ b/Servers/WindowServer/WSWindow.h @@ -8,6 +8,7 @@ #include class WSClientConnection; +class WSCursor; class WSMenu; class WSMouseEvent; @@ -100,6 +101,9 @@ public: const GraphicsBitmap& icon() const { return *m_icon; } void set_icon(Retained&& icon) { m_icon = move(icon); } + const WSCursor* override_cursor() const { return m_override_cursor.ptr(); } + void set_override_cursor(RetainPtr&& cursor) { m_override_cursor = move(cursor); } + // For InlineLinkedList. // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that. WSWindow* m_next { nullptr }; @@ -128,4 +132,5 @@ private: Size m_size_increment; Size m_base_size; Retained m_icon; + RetainPtr m_override_cursor; }; diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index f46b6732ad..3791f66699 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -1261,5 +1261,8 @@ const WSCursor& WSWindowManager::active_cursor() const } } + if (m_hovered_window && m_hovered_window->override_cursor()) + return *m_hovered_window->override_cursor(); + return *m_arrow_cursor; } -- cgit v1.2.3