mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:52:43 +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/GClipboard.h> | ||||
| #include <LibGUI/GPainter.h> | ||||
| #include <LibGUI/GWindow.h> | ||||
| #include <Kernel/KeyCode.h> | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <unistd.h> | ||||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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()) { | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<WSAPISetWallpaperRequest&>(request)); | ||||
|     case WSMessage::APIGetWallpaperRequest: | ||||
|         return handle_request(static_cast<WSAPIGetWallpaperRequest&>(request)); | ||||
|     case WSMessage::APISetWindowOverrideCursorRequest: | ||||
|         return handle_request(static_cast<WSAPISetWindowOverrideCursorRequest&>(request)); | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  |  | |||
|  | @ -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&); | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,3 +19,16 @@ Retained<WSCursor> WSCursor::create(Retained<GraphicsBitmap>&& bitmap, const Poi | |||
| { | ||||
|     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> | ||||
| 
 | ||||
| enum class WSStandardCursor { | ||||
|     None = 0, | ||||
|     Arrow, | ||||
|     IBeam, | ||||
| }; | ||||
| 
 | ||||
| class WSCursor : public Retainable<WSCursor> { | ||||
| public: | ||||
|     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&, const Point& hotspot); | ||||
|     static Retained<WSCursor> create(Retained<GraphicsBitmap>&&); | ||||
|     static RetainPtr<WSCursor> create(WSStandardCursor); | ||||
|     ~WSCursor(); | ||||
| 
 | ||||
|     Point hotspot() const { return m_hotspot; } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <AK/AKString.h> | ||||
| #include <AK/Types.h> | ||||
| #include <Kernel/KeyCode.h> | ||||
| #include <WindowServer/WSCursor.h> | ||||
| 
 | ||||
| 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) | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <WindowServer/WSScreen.h> | ||||
| #include <WindowServer/WSClientConnection.h> | ||||
| #include <WindowServer/WSAPITypes.h> | ||||
| #include <WindowServer/WSCursor.h> | ||||
| #include <Kernel/KeyCode.h> | ||||
| #include <Kernel/MousePacket.h> | ||||
| #include <LibC/sys/socket.h> | ||||
|  | @ -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<WSAPIGetWallpaperRequest>(client_id)); | ||||
|         break; | ||||
|     case WSAPI_ClientMessage::Type::SetWindowOverrideCursor: | ||||
|         post_message(client, make<WSAPISetWindowOverrideCursorRequest>(client_id, message.window_id, (WSStandardCursor)message.cursor.cursor)); | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include <WindowServer/WSWindowType.h> | ||||
| 
 | ||||
| class WSClientConnection; | ||||
| class WSCursor; | ||||
| class WSMenu; | ||||
| class WSMouseEvent; | ||||
| 
 | ||||
|  | @ -100,6 +101,9 @@ public: | |||
|     const GraphicsBitmap& icon() const { return *m_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.
 | ||||
|     // 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<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; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling