From 069d21ed7f2cf6d5b5f277602b8a969284298b0f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 27 Jan 2019 08:48:34 +0100 Subject: [PATCH] Make buttons unpress when the cursor leaves the button rect. Implement this functionality by adding global cursor tracking. It's currently only possible for one GWidget per GWindow to track the cursor. --- Kernel/Process.h | 1 + Kernel/ProcessGUI.cpp | 13 +++++++++++++ Kernel/Syscall.cpp | 2 ++ Kernel/Syscall.h | 1 + LibC/gui.cpp | 6 ++++++ LibC/gui.h | 1 + LibGUI/GButton.cpp | 28 +++++++++++++++++++++------- LibGUI/GButton.h | 2 ++ LibGUI/GWidget.cpp | 16 ++++++++++++++++ LibGUI/GWidget.h | 3 +++ LibGUI/GWindow.cpp | 15 +++++++++++++++ LibGUI/GWindow.h | 6 ++++++ WindowServer/WSWindow.cpp | 6 ++++++ WindowServer/WSWindow.h | 4 ++++ WindowServer/WSWindowManager.cpp | 8 ++++++++ 15 files changed, 105 insertions(+), 7 deletions(-) diff --git a/Kernel/Process.h b/Kernel/Process.h index 2d42952bcc..0cd58475b6 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -206,6 +206,7 @@ public: int gui$set_window_title(int window_id, const char* title, size_t size); int gui$get_window_rect(int window_id, GUI_Rect*); int gui$set_window_rect(int window_id, const GUI_Rect*); + int gui$set_global_cursor_tracking_enabled(int window_id, bool enabled); DisplayInfo get_display_info(); diff --git a/Kernel/ProcessGUI.cpp b/Kernel/ProcessGUI.cpp index adb74b06fb..f5aee91423 100644 --- a/Kernel/ProcessGUI.cpp +++ b/Kernel/ProcessGUI.cpp @@ -244,3 +244,16 @@ int Process::gui$set_window_rect(int window_id, const GUI_Rect* rect) WSMessageLoop::the().server_process().request_wakeup(); return 0; } + +int Process::gui$set_global_cursor_tracking_enabled(int window_id, bool enabled) +{ + if (window_id < 0) + return -EINVAL; + auto it = m_windows.find(window_id); + if (it == m_windows.end()) + return -EBADWINDOW; + auto& window = *(*it).value; + WSWindowLocker locker(window); + window.set_global_cursor_tracking_enabled(enabled); + return 0; +} diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index abb350603c..3850117bd3 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -217,6 +217,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->sys$read_tsc((dword*)arg1, (dword*)arg2); case Syscall::SC_gui_notify_paint_finished: return current->gui$notify_paint_finished((int)arg1, (const GUI_Rect*)arg2); + case Syscall::SC_gui_set_global_cursor_tracking_enabled: + return current->gui$set_global_cursor_tracking_enabled((int)arg1, (bool)arg2); default: kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 08c051a1b6..1ef6138015 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -81,6 +81,7 @@ __ENUMERATE_SYSCALL(gui_get_window_rect) \ __ENUMERATE_SYSCALL(gui_set_window_rect) \ __ENUMERATE_SYSCALL(gui_notify_paint_finished) \ + __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \ #ifdef SERENITY diff --git a/LibC/gui.cpp b/LibC/gui.cpp index 641ec3ffdc..6b0516b252 100644 --- a/LibC/gui.cpp +++ b/LibC/gui.cpp @@ -56,3 +56,9 @@ int gui_notify_paint_finished(int window_id, const GUI_Rect* rect) int rc = syscall(SC_gui_notify_paint_finished, window_id, rect); __RETURN_WITH_ERRNO(rc, rc, -1); } + +int gui_set_global_cursor_tracking_enabled(int window_id, bool enabled) +{ + int rc = syscall(SC_gui_set_global_cursor_tracking_enabled, window_id, enabled); + __RETURN_WITH_ERRNO(rc, rc, -1); +} diff --git a/LibC/gui.h b/LibC/gui.h index de4e62f7c9..c486adcd99 100644 --- a/LibC/gui.h +++ b/LibC/gui.h @@ -14,6 +14,7 @@ int gui_get_window_title(int window_id, char*, size_t); int gui_set_window_title(int window_id, const char*, size_t); int gui_get_window_rect(int window_id, GUI_Rect*); int gui_set_window_rect(int window_id, const GUI_Rect*); +int gui_set_global_cursor_tracking_enabled(int window_id, bool); __END_DECLS diff --git a/LibGUI/GButton.cpp b/LibGUI/GButton.cpp index cee19019d0..3fd616da0f 100644 --- a/LibGUI/GButton.cpp +++ b/LibGUI/GButton.cpp @@ -65,12 +65,24 @@ void GButton::paint_event(GPaintEvent&) } } +void GButton::mousemove_event(GMouseEvent& event) +{ + if (m_tracking_cursor) { + bool being_pressed = rect().contains(event.position()); + if (being_pressed != m_being_pressed) { + m_being_pressed = being_pressed; + update(); + } + } + GWidget::mousemove_event(event); +} + void GButton::mousedown_event(GMouseEvent& event) { dbgprintf("Button::mouseDownEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); - m_being_pressed = true; - + m_tracking_cursor = true; + set_global_cursor_tracking(true); update(); GWidget::mousedown_event(event); } @@ -78,13 +90,15 @@ void GButton::mousedown_event(GMouseEvent& event) void GButton::mouseup_event(GMouseEvent& event) { dbgprintf("Button::mouseUpEvent: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); - + bool was_being_pressed = m_being_pressed; m_being_pressed = false; - + m_tracking_cursor = false; + set_global_cursor_tracking(false); update(); GWidget::mouseup_event(event); - - if (on_click) - on_click(*this); + if (was_being_pressed) { + if (on_click) + on_click(*this); + } } diff --git a/LibGUI/GButton.h b/LibGUI/GButton.h index 606311aac0..b5164ca26e 100644 --- a/LibGUI/GButton.h +++ b/LibGUI/GButton.h @@ -18,10 +18,12 @@ private: virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; virtual void mouseup_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; virtual const char* class_name() const override { return "GButton"; } String m_caption; bool m_being_pressed { false }; + bool m_tracking_cursor { false }; }; diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index e2b77e33ec..8695fcda74 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -170,3 +170,19 @@ void GWidget::set_font(RetainPtr&& font) else m_font = move(font); } + +void GWidget::set_global_cursor_tracking(bool enabled) +{ + auto* win = window(); + if (!win) + return; + win->set_global_cursor_tracking_widget(enabled ? this : nullptr); +} + +bool GWidget::global_cursor_tracking() const +{ + auto* win = window(); + if (!win) + return false; + return win->global_cursor_tracking_widget() == this; +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 15f3e1556c..afa10c370b 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -88,6 +88,9 @@ public: const Font& font() const { return *m_font; } void set_font(RetainPtr&&); + void set_global_cursor_tracking(bool); + bool global_cursor_tracking() const; + private: GWindow* m_window { nullptr }; diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index b6de4a7511..9aff94b40a 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -72,6 +72,13 @@ void GWindow::set_rect(const Rect& a_rect) void GWindow::event(GEvent& event) { if (event.is_mouse_event()) { + if (m_global_cursor_tracking_widget) { + // FIXME: This won't work for widgets-within-widgets. + auto& mouse_event = static_cast(event); + Point local_point { mouse_event.x() - m_global_cursor_tracking_widget->relative_rect().x(), mouse_event.y() - m_global_cursor_tracking_widget->relative_rect().y() }; + auto local_event = make(event.type(), local_point, mouse_event.buttons(), mouse_event.button()); + m_global_cursor_tracking_widget->event(*local_event); + } if (!m_main_widget) return; auto& mouse_event = static_cast(event); @@ -158,3 +165,11 @@ void GWindow::set_focused_widget(GWidget* widget) m_focused_widget->update(); } } + +void GWindow::set_global_cursor_tracking_widget(GWidget* widget) +{ + if (widget == m_global_cursor_tracking_widget.ptr()) + return; + m_global_cursor_tracking_widget = widget ? widget->makeWeakPtr() : nullptr; + gui_set_global_cursor_tracking_enabled(m_window_id, widget != nullptr); +} diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index a958207f08..7a53e8a8db 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -4,6 +4,7 @@ #include #include #include +#include class GWidget; @@ -48,11 +49,16 @@ public: void update(const Rect& = Rect()); + void set_global_cursor_tracking_widget(GWidget*); + 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(); } + private: RetainPtr m_backing; int m_window_id { -1 }; bool m_is_active { false }; GWidget* m_main_widget { nullptr }; GWidget* m_focused_widget { nullptr }; + WeakPtr m_global_cursor_tracking_widget; }; diff --git a/WindowServer/WSWindow.cpp b/WindowServer/WSWindow.cpp index ff2432def0..81a24ef20e 100644 --- a/WindowServer/WSWindow.cpp +++ b/WindowServer/WSWindow.cpp @@ -123,3 +123,9 @@ void WSWindow::on_message(WSMessage& message) m_process.gui_events().append(move(gui_event)); } } + +void WSWindow::set_global_cursor_tracking_enabled(bool enabled) +{ + dbgprintf("WSWindow{%p} global_cursor_tracking <- %u\n", enabled); + m_global_cursor_tracking_enabled = enabled; +} diff --git a/WindowServer/WSWindow.h b/WindowServer/WSWindow.h index e91845f02b..12a30da4b7 100644 --- a/WindowServer/WSWindow.h +++ b/WindowServer/WSWindow.h @@ -42,6 +42,9 @@ public: pid_t pid() const { return m_pid; } + void set_global_cursor_tracking_enabled(bool); + bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled; } + // For InlineLinkedList. // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that. WSWindow* m_next { nullptr }; @@ -52,6 +55,7 @@ private: String m_title; Rect m_rect; bool m_is_being_dragged { false }; + bool m_global_cursor_tracking_enabled { false }; RetainPtr m_backing; Process& m_process; diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index bd9a69236e..4679b9afe9 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -287,6 +287,14 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event) } } + for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { + if (!window->global_cursor_tracking()) + continue; + Point position { event.x() - window->rect().x(), event.y() - window->rect().y() }; + auto local_event = make(event.type(), position, event.buttons(), event.button()); + window->on_message(*local_event); + } + for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (title_bar_rect(window->rect()).contains(event.position())) { if (event.type() == WSMessage::MouseDown) {