From b4da451c9ae4c1c8f8c1d0dc1b32fa3aa96fbdeb Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 24 Mar 2019 15:01:56 +0100 Subject: [PATCH] WindowServer+LibGUI: Implement automatic cursor tracking. When a mouse button is pressed inside a window, put that window into an automatic mouse tracking state where all mouse events are sent to that window until all mouse buttons are released. This might feel even better if it only cared about the mouse buttons you actually pressed while *inside* the windows to get released, I don't know. I'll have to use it for a while and see how it's like. --- LibGUI/GButton.cpp | 6 +--- LibGUI/GButton.h | 1 - LibGUI/GCheckBox.cpp | 17 ++++----- LibGUI/GCheckBox.h | 1 - LibGUI/GScrollBar.cpp | 2 -- LibGUI/GTextEditor.cpp | 2 -- LibGUI/GWindow.cpp | 31 ++++++++++------ LibGUI/GWindow.h | 5 +++ Servers/WindowServer/WSWindow.cpp | 46 +++++++++++++----------- Servers/WindowServer/WSWindow.h | 7 +++- Servers/WindowServer/WSWindowManager.cpp | 4 +-- 11 files changed, 66 insertions(+), 56 deletions(-) diff --git a/LibGUI/GButton.cpp b/LibGUI/GButton.cpp index 62cda2255e1..fe7ccc65bf6 100644 --- a/LibGUI/GButton.cpp +++ b/LibGUI/GButton.cpp @@ -46,7 +46,7 @@ void GButton::paint_event(GPaintEvent& event) void GButton::mousemove_event(GMouseEvent& event) { - if (m_tracking_cursor) { + if (event.buttons() == GMouseButton::Left) { bool being_pressed = rect().contains(event.position()); if (being_pressed != m_being_pressed) { m_being_pressed = being_pressed; @@ -63,8 +63,6 @@ void GButton::mousedown_event(GMouseEvent& event) #endif if (event.button() == GMouseButton::Left) { m_being_pressed = true; - m_tracking_cursor = true; - set_global_cursor_tracking(true); update(); } GWidget::mousedown_event(event); @@ -84,8 +82,6 @@ void GButton::mouseup_event(GMouseEvent& event) if (event.button() == GMouseButton::Left) { bool was_being_pressed = m_being_pressed; m_being_pressed = false; - m_tracking_cursor = false; - set_global_cursor_tracking(false); update(); if (was_being_pressed) click(); diff --git a/LibGUI/GButton.h b/LibGUI/GButton.h index 4edb2c719e8..1a5ed4b905d 100644 --- a/LibGUI/GButton.h +++ b/LibGUI/GButton.h @@ -39,7 +39,6 @@ private: RetainPtr m_icon; GButtonStyle m_button_style { GButtonStyle::Normal }; bool m_being_pressed { false }; - bool m_tracking_cursor { false }; bool m_hovered { false }; }; diff --git a/LibGUI/GCheckBox.cpp b/LibGUI/GCheckBox.cpp index 3627e0b5cfe..101ca327072 100644 --- a/LibGUI/GCheckBox.cpp +++ b/LibGUI/GCheckBox.cpp @@ -90,10 +90,10 @@ void GCheckBox::paint_event(GPaintEvent& event) void GCheckBox::mousemove_event(GMouseEvent& event) { - if (m_tracking_cursor) { - bool being_pressed = rect().contains(event.position()); - if (being_pressed != m_being_modified) { - m_being_modified = being_pressed; + if (event.buttons() == GMouseButton::Left) { + bool being_modified = rect().contains(event.position()); + if (being_modified != m_being_modified) { + m_being_modified = being_modified; update(); } } @@ -107,8 +107,6 @@ void GCheckBox::mousedown_event(GMouseEvent& event) #endif if (event.button() == GMouseButton::Left) { m_being_modified = true; - m_tracking_cursor = true; - set_global_cursor_tracking(true); update(); } GWidget::mousedown_event(event); @@ -122,11 +120,8 @@ void GCheckBox::mouseup_event(GMouseEvent& event) if (event.button() == GMouseButton::Left) { bool was_being_pressed = m_being_modified; m_being_modified = false; - m_tracking_cursor = false; - set_global_cursor_tracking(false); - if (was_being_pressed) { + if (was_being_pressed) set_checked(!is_checked()); - } update(); } GWidget::mouseup_event(event); @@ -134,7 +129,7 @@ void GCheckBox::mouseup_event(GMouseEvent& event) void GCheckBox::keydown_event(GKeyEvent& event) { - if (!m_tracking_cursor && event.key() == KeyCode::Key_Space) { + if (event.key() == KeyCode::Key_Space) { set_checked(!is_checked()); update(); } diff --git a/LibGUI/GCheckBox.h b/LibGUI/GCheckBox.h index c31549d67ba..2a85642a5f4 100644 --- a/LibGUI/GCheckBox.h +++ b/LibGUI/GCheckBox.h @@ -30,6 +30,5 @@ private: String m_caption; bool m_checked { false }; bool m_being_modified { false }; - bool m_tracking_cursor { false }; }; diff --git a/LibGUI/GScrollBar.cpp b/LibGUI/GScrollBar.cpp index 7db74c4e70e..6b942f21c51 100644 --- a/LibGUI/GScrollBar.cpp +++ b/LibGUI/GScrollBar.cpp @@ -222,7 +222,6 @@ void GScrollBar::mousedown_event(GMouseEvent& event) m_scrubbing = true; m_scrub_start_value = value(); m_scrub_origin = event.position(); - set_global_cursor_tracking(true); update(); return; } @@ -235,7 +234,6 @@ void GScrollBar::mouseup_event(GMouseEvent& event) if (!m_scrubbing) return; m_scrubbing = false; - set_global_cursor_tracking(false); update(); } diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index e6bb0603189..f3d877eb261 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -88,7 +88,6 @@ void GTextEditor::mousedown_event(GMouseEvent& event) } m_in_drag_select = true; - set_global_cursor_tracking(true); set_cursor(text_position_at(event.position())); @@ -111,7 +110,6 @@ void GTextEditor::mouseup_event(GMouseEvent& event) if (event.button() == GMouseButton::Left) { if (m_in_drag_select) { m_in_drag_select = false; - set_global_cursor_tracking(false); } return; } diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 0001766406f..610e15fe8c1 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -143,21 +143,34 @@ void GWindow::set_rect(const Rect& a_rect) void GWindow::event(GEvent& event) { if (event.is_mouse_event()) { + auto& mouse_event = static_cast(event); if (m_global_cursor_tracking_widget) { - auto& mouse_event = static_cast(event); auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect(); Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; auto local_event = make(event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); m_global_cursor_tracking_widget->event(*local_event); + return; + } + if (m_automatic_cursor_tracking_widget) { + auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect(); + Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; + auto local_event = make(event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers()); + m_automatic_cursor_tracking_widget->event(*local_event); + if (mouse_event.buttons() == 0) { + m_automatic_cursor_tracking_widget = nullptr; + return; + } + return; } if (!m_main_widget) return; - auto& mouse_event = static_cast(event); if (m_main_widget) { 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(), mouse_event.modifiers()); ASSERT(result.widget); set_hovered_widget(result.widget); + if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget) + m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr(); if (result.widget != m_global_cursor_tracking_widget.ptr()) return result.widget->event(*local_event); } @@ -298,18 +311,16 @@ void GWindow::set_focused_widget(GWidget* widget) void GWindow::set_global_cursor_tracking_widget(GWidget* widget) { - ASSERT(m_window_id); if (widget == m_global_cursor_tracking_widget.ptr()) return; m_global_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr; +} - WSAPI_ClientMessage request; - request.type = WSAPI_ClientMessage::Type::SetGlobalCursorTracking; - request.window_id = m_window_id; - request.value = widget != nullptr; - // FIXME: What if the cursor moves out of our interest range before the server can handle this? - // Maybe there could be a response that includes the current cursor location as of enabling. - GEventLoop::current().post_message_to_server(request); +void GWindow::set_automatic_cursor_tracking_widget(GWidget* widget) +{ + if (widget == m_automatic_cursor_tracking_widget.ptr()) + return; + m_automatic_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr; } void GWindow::set_has_alpha_channel(bool value) diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 720de5b408b..27b6d3e68bd 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -65,6 +65,10 @@ public: GWidget* global_cursor_tracking_widget() { return m_global_cursor_tracking_widget.ptr(); } const GWidget* global_cursor_tracking_widget() const { return m_global_cursor_tracking_widget.ptr(); } + void set_automatic_cursor_tracking_widget(GWidget*); + GWidget* automatic_cursor_tracking_widget() { return m_automatic_cursor_tracking_widget.ptr(); } + const GWidget* automatic_cursor_tracking_widget() const { return m_automatic_cursor_tracking_widget.ptr(); } + bool should_exit_event_loop_on_close() const { return m_should_exit_app_on_close; } void set_should_exit_event_loop_on_close(bool b) { m_should_exit_app_on_close = b; } @@ -96,6 +100,7 @@ private: GWidget* m_main_widget { nullptr }; GWidget* m_focused_widget { nullptr }; WeakPtr m_global_cursor_tracking_widget; + WeakPtr m_automatic_cursor_tracking_widget; WeakPtr m_hovered_widget; Rect m_rect_when_windowless; String m_title_when_windowless; diff --git a/Servers/WindowServer/WSWindow.cpp b/Servers/WindowServer/WSWindow.cpp index 385ff24d1ff..bde22c0109e 100644 --- a/Servers/WindowServer/WSWindow.cpp +++ b/Servers/WindowServer/WSWindow.cpp @@ -69,6 +69,28 @@ static WSAPI_MouseButton to_api(MouseButton button) ASSERT_NOT_REACHED(); } +void WSWindow::handle_mouse_event(const WSMouseEvent& event) +{ + set_automatic_cursor_tracking_enabled(event.buttons() != 0); + + WSAPI_ServerMessage server_message; + server_message.window_id = window_id(); + + switch (event.type()) { + case WSMessage::MouseMove: server_message.type = WSAPI_ServerMessage::Type::MouseMove; break; + case WSMessage::MouseDown: server_message.type = WSAPI_ServerMessage::Type::MouseDown; break; + case WSMessage::MouseUp: server_message.type = WSAPI_ServerMessage::Type::MouseUp; break; + default: ASSERT_NOT_REACHED(); + } + + server_message.mouse.position = event.position(); + server_message.mouse.button = to_api(event.button()); + server_message.mouse.buttons = event.buttons(); + server_message.mouse.modifiers = event.modifiers(); + + m_client->post_message(server_message); +} + void WSWindow::on_message(WSMessage& message) { if (m_internal_owner) @@ -80,28 +102,10 @@ void WSWindow::on_message(WSMessage& message) WSAPI_ServerMessage server_message; server_message.window_id = window_id(); + if (message.is_mouse_event()) + return handle_mouse_event(static_cast(message)); + switch (message.type()) { - case WSMessage::MouseMove: - server_message.type = WSAPI_ServerMessage::Type::MouseMove; - server_message.mouse.position = static_cast(message).position(); - server_message.mouse.button = WSAPI_MouseButton::NoButton; - server_message.mouse.buttons = static_cast(message).buttons(); - server_message.mouse.modifiers = static_cast(message).modifiers(); - break; - case WSMessage::MouseDown: - server_message.type = WSAPI_ServerMessage::Type::MouseDown; - server_message.mouse.position = static_cast(message).position(); - server_message.mouse.button = to_api(static_cast(message).button()); - server_message.mouse.buttons = static_cast(message).buttons(); - server_message.mouse.modifiers = static_cast(message).modifiers(); - break; - case WSMessage::MouseUp: - server_message.type = WSAPI_ServerMessage::Type::MouseUp; - server_message.mouse.position = static_cast(message).position(); - server_message.mouse.button = to_api(static_cast(message).button()); - server_message.mouse.buttons = static_cast(message).buttons(); - server_message.mouse.modifiers = static_cast(message).modifiers(); - break; case WSMessage::WindowEntered: server_message.type = WSAPI_ServerMessage::Type::WindowEntered; break; diff --git a/Servers/WindowServer/WSWindow.h b/Servers/WindowServer/WSWindow.h index ff6ef6b5c84..68a02f4f715 100644 --- a/Servers/WindowServer/WSWindow.h +++ b/Servers/WindowServer/WSWindow.h @@ -9,6 +9,7 @@ class WSClientConnection; class WSMenu; +class WSMouseEvent; class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode { public: @@ -78,7 +79,8 @@ public: GraphicsBitmap* last_backing_store() { return m_last_backing_store.ptr(); } void set_global_cursor_tracking_enabled(bool); - bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled; } + void set_automatic_cursor_tracking_enabled(bool enabled) { m_automatic_cursor_tracking_enabled = enabled; } + bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled || m_automatic_cursor_tracking_enabled; } bool has_alpha_channel() const { return m_has_alpha_channel; } void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } @@ -104,12 +106,15 @@ public: WSWindow* m_prev { nullptr }; private: + void handle_mouse_event(const WSMouseEvent&); + WSClientConnection* m_client { nullptr }; WSMessageReceiver* m_internal_owner { nullptr }; String m_title; Rect m_rect; WSWindowType m_type { WSWindowType::Normal }; bool m_global_cursor_tracking_enabled { false }; + bool m_automatic_cursor_tracking_enabled { false }; bool m_visible { true }; bool m_has_alpha_channel { false }; bool m_has_painted_since_last_resize { false }; diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index 8a68f967bcb..f27e241c61b 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -660,7 +660,7 @@ void WSWindowManager::start_window_resize(WSWindow& window, WSMouseEvent& event) invalidate(window); } -bool WSWindowManager::process_ongoing_window_drag(WSMouseEvent& event, WSWindow*& event_window) +bool WSWindowManager::process_ongoing_window_drag(WSMouseEvent& event, WSWindow*&) { if (!m_drag_window) return false; @@ -687,7 +687,7 @@ bool WSWindowManager::process_ongoing_window_drag(WSMouseEvent& event, WSWindow* return false; } -bool WSWindowManager::process_ongoing_window_resize(WSMouseEvent& event, WSWindow*& event_window) +bool WSWindowManager::process_ongoing_window_resize(WSMouseEvent& event, WSWindow*&) { if (!m_resize_window) return false;