mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:12:43 +00:00 
			
		
		
		
	LibGUI: Implement enter/leave events (with WindowServer support.)
Windows now learn when the mouse cursor leaves or enters them.
Use this to implement GWidget::{enter,leave}_event() and use that
to implement the CoolBar button effect. :^)
			
			
This commit is contained in:
		
							parent
							
								
									af7eb5c89c
								
							
						
					
					
						commit
						bf30502560
					
				
					 14 changed files with 103 additions and 5 deletions
				
			
		|  | @ -89,3 +89,14 @@ void GButton::mouseup_event(GMouseEvent& event) | |||
|     GWidget::mouseup_event(event); | ||||
| } | ||||
| 
 | ||||
| void GButton::enter_event(GEvent&) | ||||
| { | ||||
|     m_hovered = true; | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
| void GButton::leave_event(GEvent&) | ||||
| { | ||||
|     m_hovered = false; | ||||
|     update(); | ||||
| } | ||||
|  |  | |||
|  | @ -28,6 +28,8 @@ private: | |||
|     virtual void mousedown_event(GMouseEvent&) override; | ||||
|     virtual void mouseup_event(GMouseEvent&) override; | ||||
|     virtual void mousemove_event(GMouseEvent&) override; | ||||
|     virtual void enter_event(GEvent&) override; | ||||
|     virtual void leave_event(GEvent&) override; | ||||
| 
 | ||||
|     virtual const char* class_name() const override { return "GButton"; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,10 +17,14 @@ public: | |||
|         MouseMove, | ||||
|         MouseDown, | ||||
|         MouseUp, | ||||
|         Enter, | ||||
|         Leave, | ||||
|         KeyDown, | ||||
|         KeyUp, | ||||
|         Timer, | ||||
|         DeferredDestroy, | ||||
|         WindowEntered, | ||||
|         WindowLeft, | ||||
|         WindowBecameInactive, | ||||
|         WindowBecameActive, | ||||
|         FocusIn, | ||||
|  |  | |||
|  | @ -134,6 +134,11 @@ void GEventLoop::handle_window_close_request_event(const WSAPI_ServerMessage&, G | |||
|     post_event(&window, make<GEvent>(GEvent::WindowCloseRequest)); | ||||
| } | ||||
| 
 | ||||
| void GEventLoop::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window) | ||||
| { | ||||
|     post_event(&window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft)); | ||||
| } | ||||
| 
 | ||||
| void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window) | ||||
| { | ||||
| #ifdef GEVENTLOOP_DEBUG | ||||
|  | @ -294,6 +299,10 @@ void GEventLoop::wait_for_event() | |||
|         case WSAPI_ServerMessage::Type::KeyUp: | ||||
|             handle_key_event(event, *window); | ||||
|             break; | ||||
|         case WSAPI_ServerMessage::Type::WindowEntered: | ||||
|         case WSAPI_ServerMessage::Type::WindowLeft: | ||||
|             handle_window_entered_or_left_event(event, *window); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ private: | |||
|     void handle_window_activation_event(const WSAPI_ServerMessage&, GWindow&); | ||||
|     void handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow&); | ||||
|     void handle_menu_event(const WSAPI_ServerMessage&); | ||||
|     void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&); | ||||
|     void get_next_timer_expiration(timeval&); | ||||
| 
 | ||||
|     struct QueuedEvent { | ||||
|  |  | |||
|  | @ -63,6 +63,10 @@ void GWidget::event(GEvent& event) | |||
|         return mousedown_event(static_cast<GMouseEvent&>(event)); | ||||
|     case GEvent::MouseUp: | ||||
|         return mouseup_event(static_cast<GMouseEvent&>(event)); | ||||
|     case GEvent::Enter: | ||||
|         return enter_event(event); | ||||
|     case GEvent::Leave: | ||||
|         return leave_event(event); | ||||
|     default: | ||||
|         return GObject::event(event); | ||||
|     } | ||||
|  | @ -173,6 +177,14 @@ void GWidget::focusout_event(GEvent&) | |||
| { | ||||
| } | ||||
| 
 | ||||
| void GWidget::enter_event(GEvent&) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void GWidget::leave_event(GEvent&) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void GWidget::update() | ||||
| { | ||||
|     update(rect()); | ||||
|  |  | |||
|  | @ -43,6 +43,8 @@ public: | |||
|     virtual void mouseup_event(GMouseEvent&); | ||||
|     virtual void focusin_event(GEvent&); | ||||
|     virtual void focusout_event(GEvent&); | ||||
|     virtual void enter_event(GEvent&); | ||||
|     virtual void leave_event(GEvent&); | ||||
| 
 | ||||
|     Rect relative_rect() const { return m_relative_rect; } | ||||
|     Point relative_position() const { return m_relative_rect.location(); } | ||||
|  |  | |||
|  | @ -157,6 +157,7 @@ void GWindow::event(GEvent& event) | |||
|             auto result = m_main_widget->hit_test(mouse_event.x(), mouse_event.y()); | ||||
|             auto local_event = make<GMouseEvent>(event.type(), Point { result.localX, result.localY }, mouse_event.buttons(), mouse_event.button()); | ||||
|             ASSERT(result.widget); | ||||
|             set_hovered_widget(result.widget); | ||||
|             return result.widget->event(*local_event); | ||||
|         } | ||||
|         return; | ||||
|  | @ -203,6 +204,11 @@ void GWindow::event(GEvent& event) | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (event.type() == GEvent::WindowLeft) { | ||||
|         set_hovered_widget(nullptr); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     GObject::event(event); | ||||
| } | ||||
| 
 | ||||
|  | @ -301,3 +307,17 @@ void GWindow::set_opacity(float opacity) | |||
|     m_opacity_when_windowless = opacity; | ||||
|     GEventLoop::main().post_message_to_server(request); | ||||
| } | ||||
| 
 | ||||
| void GWindow::set_hovered_widget(GWidget* widget) | ||||
| { | ||||
|     if (widget == m_hovered_widget.ptr()) | ||||
|         return; | ||||
| 
 | ||||
|     if (m_hovered_widget) | ||||
|         GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Leave)); | ||||
| 
 | ||||
|     m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr; | ||||
| 
 | ||||
|     if (m_hovered_widget) | ||||
|         GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Enter)); | ||||
| } | ||||
|  |  | |||
|  | @ -64,6 +64,10 @@ public: | |||
|     bool should_exit_app_on_close() const { return m_should_exit_app_on_close; } | ||||
|     void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; } | ||||
| 
 | ||||
|     GWidget* hovered_widget() { return m_hovered_widget.ptr(); } | ||||
|     const GWidget* hovered_widget() const { return m_hovered_widget.ptr(); } | ||||
|     void set_hovered_widget(GWidget*); | ||||
| 
 | ||||
| private: | ||||
|     virtual const char* class_name() const override { return "GWindow"; } | ||||
| 
 | ||||
|  | @ -73,6 +77,7 @@ private: | |||
|     GWidget* m_main_widget { nullptr }; | ||||
|     GWidget* m_focused_widget { nullptr }; | ||||
|     WeakPtr<GWidget> m_global_cursor_tracking_widget; | ||||
|     WeakPtr<GWidget> m_hovered_widget; | ||||
|     Rect m_rect_when_windowless; | ||||
|     String m_title_when_windowless; | ||||
|     Vector<Rect> m_pending_paint_event_rects; | ||||
|  |  | |||
|  | @ -62,6 +62,8 @@ struct WSAPI_ServerMessage { | |||
|         MouseMove, | ||||
|         MouseDown, | ||||
|         MouseUp, | ||||
|         WindowEntered, | ||||
|         WindowLeft, | ||||
|         KeyDown, | ||||
|         KeyUp, | ||||
|         WindowActivated, | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ public: | |||
|         MouseMove, | ||||
|         MouseDown, | ||||
|         MouseUp, | ||||
|         WindowEntered, | ||||
|         WindowLeft, | ||||
|         KeyDown, | ||||
|         KeyUp, | ||||
|         WindowActivated, | ||||
|  |  | |||
|  | @ -94,6 +94,12 @@ void WSWindow::on_message(WSMessage& message) | |||
|         server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button()); | ||||
|         server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons(); | ||||
|         break; | ||||
|     case WSMessage::WindowEntered: | ||||
|         server_message.type = WSAPI_ServerMessage::Type::WindowEntered; | ||||
|         break; | ||||
|     case WSMessage::WindowLeft: | ||||
|         server_message.type = WSAPI_ServerMessage::Type::WindowLeft; | ||||
|         break; | ||||
|     case WSMessage::KeyDown: | ||||
|         server_message.type = WSAPI_ServerMessage::Type::KeyDown; | ||||
|         server_message.key.character = static_cast<WSKeyEvent&>(message).character(); | ||||
|  |  | |||
|  | @ -488,8 +488,10 @@ void WSWindowManager::handle_close_button_mouse_event(WSWindow& window, WSMouseE | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::process_mouse_event(WSMouseEvent& event) | ||||
| void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_window) | ||||
| { | ||||
|     event_window = nullptr; | ||||
| 
 | ||||
|     if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) { | ||||
|         if (m_drag_window) { | ||||
| #ifdef DRAG_DEBUG | ||||
|  | @ -561,6 +563,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event) | |||
|                 move_to_front(window); | ||||
|                 set_active_window(&window); | ||||
|             } | ||||
|             event_window = &window; | ||||
|             // FIXME: Should we just alter the coordinates of the existing MouseEvent and pass it through?
 | ||||
|             Point position { event.x() - window.rect().x(), event.y() - window.rect().y() }; | ||||
|             auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button()); | ||||
|  | @ -753,8 +756,12 @@ void WSWindowManager::draw_cursor() | |||
| 
 | ||||
| void WSWindowManager::on_message(WSMessage& message) | ||||
| { | ||||
|     if (message.is_mouse_event()) | ||||
|         return process_mouse_event(static_cast<WSMouseEvent&>(message)); | ||||
|     if (message.is_mouse_event()) { | ||||
|         WSWindow* event_window = nullptr; | ||||
|         process_mouse_event(static_cast<WSMouseEvent&>(message), event_window); | ||||
|         set_hovered_window(event_window); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (message.is_key_event()) { | ||||
|         // FIXME: This is a good place to hook key events globally. :)
 | ||||
|  | @ -795,6 +802,20 @@ void WSWindowManager::set_active_window(WSWindow* window) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::set_hovered_window(WSWindow* window) | ||||
| { | ||||
|     if (m_hovered_window.ptr() == window) | ||||
|         return; | ||||
| 
 | ||||
|     if (m_hovered_window) | ||||
|         WSMessageLoop::the().post_message(m_hovered_window.ptr(), make<WSMessage>(WSMessage::WindowLeft)); | ||||
| 
 | ||||
|     m_hovered_window = window ? window->make_weak_ptr() : nullptr; | ||||
| 
 | ||||
|     if (m_hovered_window) | ||||
|         WSMessageLoop::the().post_message(m_hovered_window.ptr(), make<WSMessage>(WSMessage::WindowEntered)); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::invalidate() | ||||
| { | ||||
|     m_dirty_rects.clear_with_capacity(); | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ public: | |||
|     void set_resolution(int width, int height); | ||||
| 
 | ||||
| private: | ||||
|     void process_mouse_event(WSMouseEvent&); | ||||
|     void process_mouse_event(WSMouseEvent&, WSWindow*& event_window); | ||||
|     void handle_menu_mouse_event(WSMenu&, WSMouseEvent&); | ||||
|     void handle_menubar_mouse_event(WSMouseEvent&); | ||||
|     void handle_titlebar_mouse_event(WSWindow&, WSMouseEvent&); | ||||
|  | @ -79,6 +79,7 @@ private: | |||
|     void handle_client_request(WSAPIClientRequest&); | ||||
| 
 | ||||
|     void set_active_window(WSWindow*); | ||||
|     void set_hovered_window(WSWindow*); | ||||
|     template<typename Callback> IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback); | ||||
|     template<typename Callback> IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback); | ||||
|     template<typename Callback> IterationDecision for_each_visible_window_from_front_to_back(Callback); | ||||
|  | @ -111,7 +112,7 @@ private: | |||
|     InlineLinkedList<WSWindow> m_windows_in_order; | ||||
| 
 | ||||
|     WeakPtr<WSWindow> m_active_window; | ||||
| 
 | ||||
|     WeakPtr<WSWindow> m_hovered_window; | ||||
|     WeakPtr<WSWindow> m_drag_window; | ||||
| 
 | ||||
|     Point m_drag_origin; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling