diff --git a/LibGUI/GButton.cpp b/LibGUI/GButton.cpp index 848ed9bcbc..9c60138688 100644 --- a/LibGUI/GButton.cpp +++ b/LibGUI/GButton.cpp @@ -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(); +} diff --git a/LibGUI/GButton.h b/LibGUI/GButton.h index c96c52ddef..2bd69bef74 100644 --- a/LibGUI/GButton.h +++ b/LibGUI/GButton.h @@ -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"; } diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index c6d82b1a1d..a0cdff198c 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -17,10 +17,14 @@ public: MouseMove, MouseDown, MouseUp, + Enter, + Leave, KeyDown, KeyUp, Timer, DeferredDestroy, + WindowEntered, + WindowLeft, WindowBecameInactive, WindowBecameActive, FocusIn, diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index 6120007231..538d1644fe 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -134,6 +134,11 @@ void GEventLoop::handle_window_close_request_event(const WSAPI_ServerMessage&, G post_event(&window, make(GEvent::WindowCloseRequest)); } +void GEventLoop::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window) +{ + post_event(&window, make(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; } diff --git a/LibGUI/GEventLoop.h b/LibGUI/GEventLoop.h index f2c436898d..9435018f81 100644 --- a/LibGUI/GEventLoop.h +++ b/LibGUI/GEventLoop.h @@ -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 { diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index b17eb9aa48..2c8dff4ac0 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -63,6 +63,10 @@ void GWidget::event(GEvent& event) return mousedown_event(static_cast(event)); case GEvent::MouseUp: return mouseup_event(static_cast(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()); diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index f3211e6c6b..1d502cdb12 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -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(); } diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 4ae92562d6..53a0f9a0db 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -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(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::Leave)); + + m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr; + + if (m_hovered_widget) + GEventLoop::main().post_event(m_hovered_widget.ptr(), make(GEvent::Enter)); +} diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 2cb99ecef7..a57f43411a 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -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 m_global_cursor_tracking_widget; + WeakPtr m_hovered_widget; Rect m_rect_when_windowless; String m_title_when_windowless; Vector m_pending_paint_event_rects; diff --git a/WindowServer/WSAPITypes.h b/WindowServer/WSAPITypes.h index 3ed1b55077..8be470a257 100644 --- a/WindowServer/WSAPITypes.h +++ b/WindowServer/WSAPITypes.h @@ -62,6 +62,8 @@ struct WSAPI_ServerMessage { MouseMove, MouseDown, MouseUp, + WindowEntered, + WindowLeft, KeyDown, KeyUp, WindowActivated, diff --git a/WindowServer/WSMessage.h b/WindowServer/WSMessage.h index 0656bc4bd5..3e4f236c70 100644 --- a/WindowServer/WSMessage.h +++ b/WindowServer/WSMessage.h @@ -14,6 +14,8 @@ public: MouseMove, MouseDown, MouseUp, + WindowEntered, + WindowLeft, KeyDown, KeyUp, WindowActivated, diff --git a/WindowServer/WSWindow.cpp b/WindowServer/WSWindow.cpp index 61e9073123..5c74b0899a 100644 --- a/WindowServer/WSWindow.cpp +++ b/WindowServer/WSWindow.cpp @@ -94,6 +94,12 @@ void WSWindow::on_message(WSMessage& message) server_message.mouse.button = to_api(static_cast(message).button()); server_message.mouse.buttons = static_cast(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(message).character(); diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index eafa5f38e7..65db97df6d 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -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(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(message)); + if (message.is_mouse_event()) { + WSWindow* event_window = nullptr; + process_mouse_event(static_cast(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::WindowLeft)); + + m_hovered_window = window ? window->make_weak_ptr() : nullptr; + + if (m_hovered_window) + WSMessageLoop::the().post_message(m_hovered_window.ptr(), make(WSMessage::WindowEntered)); +} + void WSWindowManager::invalidate() { m_dirty_rects.clear_with_capacity(); diff --git a/WindowServer/WSWindowManager.h b/WindowServer/WSWindowManager.h index 40841648a3..aabd31b4e7 100644 --- a/WindowServer/WSWindowManager.h +++ b/WindowServer/WSWindowManager.h @@ -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 IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback); template IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback); template IterationDecision for_each_visible_window_from_front_to_back(Callback); @@ -111,7 +112,7 @@ private: InlineLinkedList m_windows_in_order; WeakPtr m_active_window; - + WeakPtr m_hovered_window; WeakPtr m_drag_window; Point m_drag_origin;