mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:02:45 +00:00 
			
		
		
		
	WindowServer: Add support for per-window override cursors.
Use this to implement automatic switching to an I-beam cursor when hovering over a GTextEditor. :^)
This commit is contained in:
		
							parent
							
								
									42c95959a8
								
							
						
					
					
						commit
						dcf6726487
					
				
					 13 changed files with 110 additions and 0 deletions
				
			
		|  | @ -3,6 +3,7 @@ | ||||||
| #include <LibGUI/GFontDatabase.h> | #include <LibGUI/GFontDatabase.h> | ||||||
| #include <LibGUI/GClipboard.h> | #include <LibGUI/GClipboard.h> | ||||||
| #include <LibGUI/GPainter.h> | #include <LibGUI/GPainter.h> | ||||||
|  | #include <LibGUI/GWindow.h> | ||||||
| #include <Kernel/KeyCode.h> | #include <Kernel/KeyCode.h> | ||||||
| #include <AK/StringBuilder.h> | #include <AK/StringBuilder.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | @ -801,3 +802,15 @@ void GTextEditor::paste() | ||||||
|     printf("Paste: \"%s\"\n", paste_text.characters()); |     printf("Paste: \"%s\"\n", paste_text.characters()); | ||||||
|     insert_at_cursor_or_replace_selection(paste_text); |     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); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -111,6 +111,9 @@ private: | ||||||
|     virtual void focusout_event(GEvent&) override; |     virtual void focusout_event(GEvent&) override; | ||||||
|     virtual void timer_event(GTimerEvent&) override; |     virtual void timer_event(GTimerEvent&) override; | ||||||
|     virtual bool accepts_focus() const override { return true; } |     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 paint_ruler(Painter&); | ||||||
|     void update_content_size(); |     void update_content_size(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -140,6 +140,17 @@ void GWindow::set_rect(const Rect& a_rect) | ||||||
|     GEventLoop::current().post_message_to_server(request); |     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) | void GWindow::event(GEvent& event) | ||||||
| { | { | ||||||
|     if (event.is_mouse_event()) { |     if (event.is_mouse_event()) { | ||||||
|  |  | ||||||
|  | @ -8,6 +8,12 @@ | ||||||
| 
 | 
 | ||||||
| class GWidget; | class GWidget; | ||||||
| 
 | 
 | ||||||
|  | enum class GStandardCursor { | ||||||
|  |     None = 0, | ||||||
|  |     Arrow, | ||||||
|  |     IBeam, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class GWindow : public GObject { | class GWindow : public GObject { | ||||||
| public: | public: | ||||||
|     GWindow(GObject* parent = nullptr); |     GWindow(GObject* parent = nullptr); | ||||||
|  | @ -84,6 +90,8 @@ public: | ||||||
|     Size base_size() const { return m_base_size; } |     Size base_size() const { return m_base_size; } | ||||||
|     void set_base_size(const Size& size) { m_base_size = 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"; } |     virtual const char* class_name() const override { return "GWindow"; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |  | ||||||
|  | @ -47,6 +47,11 @@ struct WSAPI_KeyModifiers { enum { | ||||||
|     Ctrl  = 1 << 2, |     Ctrl  = 1 << 2, | ||||||
| }; }; | }; }; | ||||||
| 
 | 
 | ||||||
|  | enum class WSAPI_StandardCursor : unsigned char { | ||||||
|  |     None = 0, | ||||||
|  |     Arrow, | ||||||
|  |     IBeam, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| struct WSAPI_ServerMessage { | struct WSAPI_ServerMessage { | ||||||
|     enum Type : unsigned { |     enum Type : unsigned { | ||||||
|  | @ -164,6 +169,7 @@ struct WSAPI_ClientMessage { | ||||||
|         Greeting, |         Greeting, | ||||||
|         SetWallpaper, |         SetWallpaper, | ||||||
|         GetWallpaper, |         GetWallpaper, | ||||||
|  |         SetWindowOverrideCursor, | ||||||
|     }; |     }; | ||||||
|     Type type { Invalid }; |     Type type { Invalid }; | ||||||
|     int window_id { -1 }; |     int window_id { -1 }; | ||||||
|  | @ -203,6 +209,9 @@ struct WSAPI_ClientMessage { | ||||||
|             int shared_buffer_id; |             int shared_buffer_id; | ||||||
|             int contents_size; |             int contents_size; | ||||||
|         } clipboard; |         } clipboard; | ||||||
|  |         struct { | ||||||
|  |             WSAPI_StandardCursor cursor; | ||||||
|  |         } cursor; | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -491,6 +491,18 @@ void WSClientConnection::handle_request(WSAPISetGlobalCursorTrackingRequest& req | ||||||
|     window.set_global_cursor_tracking_enabled(request.value()); |     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) | void WSClientConnection::on_request(WSAPIClientRequest& request) | ||||||
| { | { | ||||||
|     switch (request.type()) { |     switch (request.type()) { | ||||||
|  | @ -542,6 +554,8 @@ void WSClientConnection::on_request(WSAPIClientRequest& request) | ||||||
|         return handle_request(static_cast<WSAPISetWallpaperRequest&>(request)); |         return handle_request(static_cast<WSAPISetWallpaperRequest&>(request)); | ||||||
|     case WSMessage::APIGetWallpaperRequest: |     case WSMessage::APIGetWallpaperRequest: | ||||||
|         return handle_request(static_cast<WSAPIGetWallpaperRequest&>(request)); |         return handle_request(static_cast<WSAPIGetWallpaperRequest&>(request)); | ||||||
|  |     case WSMessage::APISetWindowOverrideCursorRequest: | ||||||
|  |         return handle_request(static_cast<WSAPISetWindowOverrideCursorRequest&>(request)); | ||||||
|     default: |     default: | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ private: | ||||||
|     void handle_request(WSAPISetWindowOpacityRequest&); |     void handle_request(WSAPISetWindowOpacityRequest&); | ||||||
|     void handle_request(WSAPISetWallpaperRequest&); |     void handle_request(WSAPISetWallpaperRequest&); | ||||||
|     void handle_request(WSAPIGetWallpaperRequest&); |     void handle_request(WSAPIGetWallpaperRequest&); | ||||||
|  |     void handle_request(WSAPISetWindowOverrideCursorRequest&); | ||||||
| 
 | 
 | ||||||
|     void post_error(const String&); |     void post_error(const String&); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,3 +19,16 @@ Retained<WSCursor> WSCursor::create(Retained<GraphicsBitmap>&& bitmap, const Poi | ||||||
| { | { | ||||||
|     return adopt(*new WSCursor(move(bitmap), hotspot)); |     return adopt(*new WSCursor(move(bitmap), hotspot)); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | RetainPtr<WSCursor> 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(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,10 +2,17 @@ | ||||||
| 
 | 
 | ||||||
| #include <SharedGraphics/GraphicsBitmap.h> | #include <SharedGraphics/GraphicsBitmap.h> | ||||||
| 
 | 
 | ||||||
|  | enum class WSStandardCursor { | ||||||
|  |     None = 0, | ||||||
|  |     Arrow, | ||||||
|  |     IBeam, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class WSCursor : public Retainable<WSCursor> { | class WSCursor : public Retainable<WSCursor> { | ||||||
| public: | public: | ||||||
|     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&, const Point& hotspot); |     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&, const Point& hotspot); | ||||||
|     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&); |     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&); | ||||||
|  |     static RetainPtr<WSCursor> create(WSStandardCursor); | ||||||
|     ~WSCursor(); |     ~WSCursor(); | ||||||
| 
 | 
 | ||||||
|     Point hotspot() const { return m_hotspot; } |     Point hotspot() const { return m_hotspot; } | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include <AK/AKString.h> | #include <AK/AKString.h> | ||||||
| #include <AK/Types.h> | #include <AK/Types.h> | ||||||
| #include <Kernel/KeyCode.h> | #include <Kernel/KeyCode.h> | ||||||
|  | #include <WindowServer/WSCursor.h> | ||||||
| 
 | 
 | ||||||
| class WSMessage { | class WSMessage { | ||||||
| public: | public: | ||||||
|  | @ -49,6 +50,7 @@ public: | ||||||
|         APIGetClipboardContentsRequest, |         APIGetClipboardContentsRequest, | ||||||
|         APISetWallpaperRequest, |         APISetWallpaperRequest, | ||||||
|         APIGetWallpaperRequest, |         APIGetWallpaperRequest, | ||||||
|  |         APISetWindowOverrideCursorRequest, | ||||||
|         __End_API_Client_Requests, |         __End_API_Client_Requests, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -229,6 +231,23 @@ private: | ||||||
|     int m_menu_id { 0 }; |     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 { | class WSAPISetWallpaperRequest final : public WSAPIClientRequest { | ||||||
| public: | public: | ||||||
|     explicit WSAPISetWallpaperRequest(int client_id, String&& wallpaper) |     explicit WSAPISetWallpaperRequest(int client_id, String&& wallpaper) | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include <WindowServer/WSScreen.h> | #include <WindowServer/WSScreen.h> | ||||||
| #include <WindowServer/WSClientConnection.h> | #include <WindowServer/WSClientConnection.h> | ||||||
| #include <WindowServer/WSAPITypes.h> | #include <WindowServer/WSAPITypes.h> | ||||||
|  | #include <WindowServer/WSCursor.h> | ||||||
| #include <Kernel/KeyCode.h> | #include <Kernel/KeyCode.h> | ||||||
| #include <Kernel/MousePacket.h> | #include <Kernel/MousePacket.h> | ||||||
| #include <LibC/sys/socket.h> | #include <LibC/sys/socket.h> | ||||||
|  | @ -330,6 +331,9 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess | ||||||
|         break; |         break; | ||||||
|     case WSAPI_ClientMessage::Type::GetWallpaper: |     case WSAPI_ClientMessage::Type::GetWallpaper: | ||||||
|         post_message(client, make<WSAPIGetWallpaperRequest>(client_id)); |         post_message(client, make<WSAPIGetWallpaperRequest>(client_id)); | ||||||
|  |         break; | ||||||
|  |     case WSAPI_ClientMessage::Type::SetWindowOverrideCursor: | ||||||
|  |         post_message(client, make<WSAPISetWindowOverrideCursorRequest>(client_id, message.window_id, (WSStandardCursor)message.cursor.cursor)); | ||||||
|     default: |     default: | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| #include <WindowServer/WSWindowType.h> | #include <WindowServer/WSWindowType.h> | ||||||
| 
 | 
 | ||||||
| class WSClientConnection; | class WSClientConnection; | ||||||
|  | class WSCursor; | ||||||
| class WSMenu; | class WSMenu; | ||||||
| class WSMouseEvent; | class WSMouseEvent; | ||||||
| 
 | 
 | ||||||
|  | @ -100,6 +101,9 @@ public: | ||||||
|     const GraphicsBitmap& icon() const { return *m_icon; } |     const GraphicsBitmap& icon() const { return *m_icon; } | ||||||
|     void set_icon(Retained<GraphicsBitmap>&& icon) { m_icon = move(icon); } |     void set_icon(Retained<GraphicsBitmap>&& icon) { m_icon = move(icon); } | ||||||
| 
 | 
 | ||||||
|  |     const WSCursor* override_cursor() const { return m_override_cursor.ptr(); } | ||||||
|  |     void set_override_cursor(RetainPtr<WSCursor>&& cursor) { m_override_cursor = move(cursor); } | ||||||
|  | 
 | ||||||
|     // For InlineLinkedList.
 |     // For InlineLinkedList.
 | ||||||
|     // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
 |     // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
 | ||||||
|     WSWindow* m_next { nullptr }; |     WSWindow* m_next { nullptr }; | ||||||
|  | @ -128,4 +132,5 @@ private: | ||||||
|     Size m_size_increment; |     Size m_size_increment; | ||||||
|     Size m_base_size; |     Size m_base_size; | ||||||
|     Retained<GraphicsBitmap> m_icon; |     Retained<GraphicsBitmap> m_icon; | ||||||
|  |     RetainPtr<WSCursor> m_override_cursor; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -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; |     return *m_arrow_cursor; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling