From 20f7d7ec675386ae5d3287d01048aeeda04e2b94 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 25 Mar 2019 01:42:15 +0100 Subject: [PATCH] LibGUI: Add GWidget::doubleclick_event(). Now double-clicking an item in a GTableView or GItemView will activate it. --- LibGUI/GElapsedTimer.cpp | 25 +++++++++++++++++++++++ LibGUI/GElapsedTimer.h | 14 +++++++++++++ LibGUI/GItemView.cpp | 10 +++++++++ LibGUI/GItemView.h | 1 + LibGUI/GTableView.cpp | 10 +++++++++ LibGUI/GTableView.h | 1 + LibGUI/GWidget.cpp | 26 +++++++++++++++++++++++- LibGUI/GWidget.h | 10 +++++++-- LibGUI/GWindow.cpp | 4 +--- LibGUI/Makefile | 1 + Servers/WindowServer/WSWindowManager.cpp | 5 ++++- 11 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 LibGUI/GElapsedTimer.cpp create mode 100644 LibGUI/GElapsedTimer.h diff --git a/LibGUI/GElapsedTimer.cpp b/LibGUI/GElapsedTimer.cpp new file mode 100644 index 0000000000..1d6f6eb943 --- /dev/null +++ b/LibGUI/GElapsedTimer.cpp @@ -0,0 +1,25 @@ +#include + +void GElapsedTimer::start() +{ + gettimeofday(&m_start_time, nullptr); +} + +inline void timersub(const struct timeval* a, const struct timeval* b, struct timeval* result) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + result->tv_usec = a->tv_usec - b->tv_usec; + if (result->tv_usec < 0) { + --result->tv_sec; + result->tv_usec += 1000000; + } +} + +int GElapsedTimer::elapsed() const +{ + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval diff; + timersub(&now, &m_start_time, &diff); + return diff.tv_sec * 1000 + diff.tv_usec / 1000; +} diff --git a/LibGUI/GElapsedTimer.h b/LibGUI/GElapsedTimer.h new file mode 100644 index 0000000000..434f2050b0 --- /dev/null +++ b/LibGUI/GElapsedTimer.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class GElapsedTimer { +public: + GElapsedTimer() { } + + void start(); + int elapsed() const; + +private: + struct timeval m_start_time { 0, 0 }; +}; diff --git a/LibGUI/GItemView.cpp b/LibGUI/GItemView.cpp index 39c2f2cb04..a5d14e8e19 100644 --- a/LibGUI/GItemView.cpp +++ b/LibGUI/GItemView.cpp @@ -80,6 +80,16 @@ void GItemView::mousedown_event(GMouseEvent& event) } } +void GItemView::doubleclick_event(GMouseEvent& event) +{ + if (!model()) + return; + if (event.button() == GMouseButton::Left) { + mousedown_event(event); + model()->activate(model()->selected_index()); + } +} + void GItemView::paint_event(GPaintEvent& event) { Painter painter(*this); diff --git a/LibGUI/GItemView.h b/LibGUI/GItemView.h index f6a52a84ae..19f9e30f92 100644 --- a/LibGUI/GItemView.h +++ b/LibGUI/GItemView.h @@ -30,6 +30,7 @@ private: virtual void resize_event(GResizeEvent&) override; virtual void mousedown_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; + virtual void doubleclick_event(GMouseEvent&) override; int item_count() const; Rect item_rect(int item_index) const; diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index 81797be626..4cf7652b5e 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -292,3 +292,13 @@ void GTableView::set_column_hidden(int column, bool hidden) } m_column_visibility[column] = !hidden; } + +void GTableView::doubleclick_event(GMouseEvent& event) +{ + if (!model()) + return; + if (event.button() == GMouseButton::Left) { + mousedown_event(event); + model()->activate(model()->selected_index()); + } +} diff --git a/LibGUI/GTableView.h b/LibGUI/GTableView.h index bbd021fbeb..ea75381056 100644 --- a/LibGUI/GTableView.h +++ b/LibGUI/GTableView.h @@ -36,6 +36,7 @@ private: virtual void did_update_model() override; virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; + virtual void doubleclick_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; void paint_headers(Painter&); diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 8303aad9c8..0dcb5e7ca5 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -71,7 +71,7 @@ void GWidget::event(GEvent& event) set_focus(true); return mousedown_event(static_cast(event)); case GEvent::MouseUp: - return mouseup_event(static_cast(event)); + return handle_mouseup_event(static_cast(event)); case GEvent::Enter: return enter_event(event); case GEvent::Leave: @@ -144,6 +144,30 @@ void GWidget::handle_resize_event(GResizeEvent& event) return resize_event(event); } +void GWidget::handle_mouseup_event(GMouseEvent& event) +{ + mouseup_event(event); + + if (!rect().contains(event.position())) + return; + // It's a click.. but is it a doubleclick? + // FIXME: This needs improvement. + int elapsed_since_last_click = m_click_clock.elapsed(); + if (elapsed_since_last_click < 250) { + doubleclick_event(event); + } else { + m_click_clock.start(); + } +} + +void GWidget::click_event(GMouseEvent&) +{ +} + +void GWidget::doubleclick_event(GMouseEvent&) +{ +} + void GWidget::resize_event(GResizeEvent&) { } diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 6a6dc8ee80..6dfb00fde6 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -1,7 +1,8 @@ #pragma once -#include "GEvent.h" -#include "GObject.h" +#include +#include +#include #include #include #include @@ -43,6 +44,8 @@ public: virtual void mousemove_event(GMouseEvent&); virtual void mousedown_event(GMouseEvent&); virtual void mouseup_event(GMouseEvent&); + virtual void click_event(GMouseEvent&); + virtual void doubleclick_event(GMouseEvent&); virtual void focusin_event(GEvent&); virtual void focusout_event(GEvent&); virtual void enter_event(GEvent&); @@ -141,6 +144,7 @@ private: void handle_paint_event(GPaintEvent&); void handle_resize_event(GResizeEvent&); + void handle_mouseup_event(GMouseEvent&); void do_layout(); void invalidate_layout(); @@ -158,4 +162,6 @@ private: bool m_fill_with_background_color { false }; bool m_visible { true }; + + GElapsedTimer m_click_clock; }; diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 610e15fe8c..09e316ed7b 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -156,10 +156,8 @@ void GWindow::event(GEvent& event) 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) { + if (mouse_event.buttons() == 0) m_automatic_cursor_tracking_widget = nullptr; - return; - } return; } if (!m_main_widget) diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 6d67ef550f..72bf1f4c11 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -51,6 +51,7 @@ LIBGUI_OBJS = \ GAbstractView.o \ GItemView.o \ GIcon.o \ + GElapsedTimer.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS) diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index f27e241c61..6e7fd3b593 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -798,12 +798,15 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ if (process_ongoing_window_resize(event, event_window)) return; + HashTable windows_who_received_mouse_event_due_to_cursor_tracking; + for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (!window->global_cursor_tracking()) continue; ASSERT(window->is_visible()); // Maybe this should be supported? Idk. Let's catch it and think about it later. Point position { event.x() - window->rect().x(), event.y() - window->rect().y() }; auto local_event = make(event.type(), position, event.buttons(), event.button(), event.modifiers()); + windows_who_received_mouse_event_due_to_cursor_tracking.set(window); window->on_message(*local_event); } @@ -849,7 +852,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ if (window.type() == WSWindowType::Normal && event.type() == WSMessage::MouseDown) move_to_front_and_make_active(window); event_window = &window; - if (!window.global_cursor_tracking()) { + if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&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(), event.modifiers());