1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 13:37:45 +00:00

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.
This commit is contained in:
Andreas Kling 2019-03-24 15:01:56 +01:00
parent e84823360d
commit b4da451c9a
11 changed files with 66 additions and 56 deletions

View file

@ -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();

View file

@ -39,7 +39,6 @@ private:
RetainPtr<GraphicsBitmap> m_icon;
GButtonStyle m_button_style { GButtonStyle::Normal };
bool m_being_pressed { false };
bool m_tracking_cursor { false };
bool m_hovered { false };
};

View file

@ -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();
}

View file

@ -30,6 +30,5 @@ private:
String m_caption;
bool m_checked { false };
bool m_being_modified { false };
bool m_tracking_cursor { false };
};

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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<GMouseEvent&>(event);
if (m_global_cursor_tracking_widget) {
auto& mouse_event = static_cast<GMouseEvent&>(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<GMouseEvent>(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<GMouseEvent>(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<GMouseEvent&>(event);
if (m_main_widget) {
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(), 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)

View file

@ -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<GWidget> m_global_cursor_tracking_widget;
WeakPtr<GWidget> m_automatic_cursor_tracking_widget;
WeakPtr<GWidget> m_hovered_widget;
Rect m_rect_when_windowless;
String m_title_when_windowless;