diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-03-08 17:53:02 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-03-08 17:53:02 +0100 |
commit | f40d11f06d78734d080cd1103c143da52204d20f (patch) | |
tree | ccc73b4a3a18409fdfd7d56f90b26d384f21a0a3 | |
parent | 6d172725c077ff0b94279816ed810c3c19fad865 (diff) | |
download | serenity-f40d11f06d78734d080cd1103c143da52204d20f.zip |
LibGUI+WindowServer: Implement drag-to-select behavior in GTextEditor.
To make this feel right, I needed to start passing keyboard modifiers along
with mouse events. That allows shift-clicking to extend the selection. :^)
-rw-r--r-- | LibGUI/GEvent.h | 5 | ||||
-rw-r--r-- | LibGUI/GEventLoop.cpp | 2 | ||||
-rw-r--r-- | LibGUI/GTextEditor.cpp | 60 | ||||
-rw-r--r-- | LibGUI/GTextEditor.h | 4 | ||||
-rw-r--r-- | LibGUI/GWindow.cpp | 4 | ||||
-rw-r--r-- | WindowServer/WSAPITypes.h | 1 | ||||
-rw-r--r-- | WindowServer/WSMessage.h | 5 | ||||
-rw-r--r-- | WindowServer/WSScreen.cpp | 5 | ||||
-rw-r--r-- | WindowServer/WSScreen.h | 1 | ||||
-rw-r--r-- | WindowServer/WSWindow.cpp | 3 | ||||
-rw-r--r-- | WindowServer/WSWindowManager.cpp | 4 |
11 files changed, 73 insertions, 21 deletions
diff --git a/LibGUI/GEvent.h b/LibGUI/GEvent.h index ad1681817b..b858539abe 100644 --- a/LibGUI/GEvent.h +++ b/LibGUI/GEvent.h @@ -137,11 +137,12 @@ private: class GMouseEvent final : public GEvent { public: - GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button = GMouseButton::None) + GMouseEvent(Type type, const Point& position, unsigned buttons, GMouseButton button, unsigned modifiers) : GEvent(type) , m_position(position) , m_buttons(buttons) , m_button(button) + , m_modifiers(modifiers) { } @@ -150,11 +151,13 @@ public: int y() const { return m_position.y(); } GMouseButton button() const { return m_button; } unsigned buttons() const { return m_buttons; } + unsigned modifiers() const { return m_modifiers; } private: Point m_position; unsigned m_buttons { 0 }; GMouseButton m_button { GMouseButton::None }; + unsigned m_modifiers { 0 }; }; class GTimerEvent final : public GEvent { diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index b3a3fa711e..890e0893bb 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -187,7 +187,7 @@ void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& w case WSAPI_MouseButton::Middle: button = GMouseButton::Middle; break; default: ASSERT_NOT_REACHED(); break; } - post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button)); + post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers)); } void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index 278b9cc279..f9eaffbb67 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -107,11 +107,47 @@ GTextPosition GTextEditor::text_position_at(const Point& a_position) const void GTextEditor::mousedown_event(GMouseEvent& event) { - set_cursor(text_position_at(event.position())); - // FIXME: Allow mouse selection! - if (m_selection_start.is_valid()) { - m_selection_start = { }; + if (event.button() == GMouseButton::Left) { + if (event.modifiers() & Mod_Shift) { + if (!has_selection()) + m_selection_start = m_cursor; + } else { + m_selection_start = { }; + } + + m_in_drag_select = true; + set_global_cursor_tracking(true); + + set_cursor(text_position_at(event.position())); + + if (!(event.modifiers() & Mod_Shift)) { + if (!has_selection()) + m_selection_start = m_cursor; + } + + // FIXME: Only update the relevant rects. update(); + return; + } +} + +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; + } +} + +void GTextEditor::mousemove_event(GMouseEvent& event) +{ + if (m_in_drag_select) { + set_cursor(text_position_at(event.position())); + update(); + return; } } @@ -475,14 +511,14 @@ void GTextEditor::set_cursor(const GTextPosition& position) ASSERT(!m_lines.is_empty()); ASSERT(position.line() < m_lines.size()); ASSERT(position.column() <= m_lines[position.line()]->length()); - if (m_cursor == position) - return; - auto old_cursor_line_rect = line_widget_rect(m_cursor.line()); - m_cursor = position; - m_cursor_state = true; - scroll_cursor_into_view(); - update(old_cursor_line_rect); - update_cursor(); + if (m_cursor != position) { + auto old_cursor_line_rect = line_widget_rect(m_cursor.line()); + m_cursor = position; + m_cursor_state = true; + scroll_cursor_into_view(); + update(old_cursor_line_rect); + update_cursor(); + } if (on_cursor_change) on_cursor_change(*this); } diff --git a/LibGUI/GTextEditor.h b/LibGUI/GTextEditor.h index 29db7f3d79..4fd230822a 100644 --- a/LibGUI/GTextEditor.h +++ b/LibGUI/GTextEditor.h @@ -24,6 +24,7 @@ public: void set_column(int column) { m_column = column; } bool operator==(const GTextPosition& other) const { return m_line == other.m_line && m_column == other.m_column; } + bool operator!=(const GTextPosition& other) const { return m_line != other.m_line || m_column != other.m_column; } bool operator<(const GTextPosition& other) const { return m_line < other.m_line || (m_line == other.m_line && m_column < other.m_column); } private: @@ -64,6 +65,8 @@ private: virtual void paint_event(GPaintEvent&) override; virtual void resize_event(GResizeEvent&) override; virtual void mousedown_event(GMouseEvent&) override; + virtual void mouseup_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; virtual void focusin_event(GEvent&) override; virtual void focusout_event(GEvent&) override; @@ -116,6 +119,7 @@ private: Vector<OwnPtr<Line>> m_lines; GTextPosition m_cursor; bool m_cursor_state { true }; + bool m_in_drag_select { false }; int m_line_spacing { 2 }; GTextPosition m_selection_start; }; diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 39cd74d179..b079e2e988 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -143,7 +143,7 @@ void GWindow::event(GEvent& event) 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()); + 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); } if (!m_main_widget) @@ -151,7 +151,7 @@ void GWindow::event(GEvent& event) 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()); + 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 (result.widget != m_global_cursor_tracking_widget.ptr()) diff --git a/WindowServer/WSAPITypes.h b/WindowServer/WSAPITypes.h index 535ce6b76a..15291790fe 100644 --- a/WindowServer/WSAPITypes.h +++ b/WindowServer/WSAPITypes.h @@ -109,6 +109,7 @@ struct WSAPI_ServerMessage { WSAPI_Point position; WSAPI_MouseButton button; unsigned buttons; + byte modifiers; } mouse; struct { char character; diff --git a/WindowServer/WSMessage.h b/WindowServer/WSMessage.h index 98d16acb36..eaebfb24d1 100644 --- a/WindowServer/WSMessage.h +++ b/WindowServer/WSMessage.h @@ -513,11 +513,12 @@ private: class WSMouseEvent final : public WSMessage { public: - WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button = MouseButton::None) + WSMouseEvent(Type type, const Point& position, unsigned buttons, MouseButton button, unsigned modifiers) : WSMessage(type) , m_position(position) , m_buttons(buttons) , m_button(button) + , m_modifiers(modifiers) { } @@ -526,11 +527,13 @@ public: int y() const { return m_position.y(); } MouseButton button() const { return m_button; } unsigned buttons() const { return m_buttons; } + unsigned modifiers() const { return m_modifiers; } private: Point m_position; unsigned m_buttons { 0 }; MouseButton m_button { MouseButton::None }; + unsigned m_modifiers { 0 }; }; class WSResizeEvent final : public WSMessage { diff --git a/WindowServer/WSScreen.cpp b/WindowServer/WSScreen.cpp index 6148df8864..f491b014c6 100644 --- a/WindowServer/WSScreen.cpp +++ b/WindowServer/WSScreen.cpp @@ -69,14 +69,14 @@ void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons) auto post_mousedown_or_mouseup_if_needed = [&] (MouseButton button) { if (!(changed_buttons & (unsigned)button)) return; - auto message = make<WSMouseEvent>(buttons & (unsigned)button ? WSMessage::MouseDown : WSMessage::MouseUp, m_cursor_location, buttons, button); + auto message = make<WSMouseEvent>(buttons & (unsigned)button ? WSMessage::MouseDown : WSMessage::MouseUp, m_cursor_location, buttons, button, m_modifiers); WSMessageLoop::the().post_message(WSWindowManager::the(), move(message)); }; post_mousedown_or_mouseup_if_needed(MouseButton::Left); post_mousedown_or_mouseup_if_needed(MouseButton::Right); post_mousedown_or_mouseup_if_needed(MouseButton::Middle); if (m_cursor_location != prev_location) { - auto message = make<WSMouseEvent>(WSMessage::MouseMove, m_cursor_location, buttons); + auto message = make<WSMouseEvent>(WSMessage::MouseMove, m_cursor_location, buttons, MouseButton::None, m_modifiers); WSMessageLoop::the().post_message(WSWindowManager::the(), move(message)); } // NOTE: Invalidate the cursor if it moved, or if the left button changed state (for the cursor color inversion.) @@ -86,6 +86,7 @@ void WSScreen::on_receive_mouse_data(int dx, int dy, unsigned buttons) void WSScreen::on_receive_keyboard_data(KeyEvent kernel_event) { + m_modifiers = kernel_event.modifiers(); auto message = make<WSKeyEvent>(kernel_event.is_press() ? WSMessage::KeyDown : WSMessage::KeyUp, kernel_event.key, kernel_event.character, kernel_event.modifiers()); WSMessageLoop::the().post_message(WSWindowManager::the(), move(message)); } diff --git a/WindowServer/WSScreen.h b/WindowServer/WSScreen.h index 90aba3aac9..0d71f816ac 100644 --- a/WindowServer/WSScreen.h +++ b/WindowServer/WSScreen.h @@ -38,6 +38,7 @@ private: Point m_cursor_location; unsigned m_mouse_button_state { 0 }; + unsigned m_modifiers { 0 }; }; inline RGBA32* WSScreen::scanline(int y) diff --git a/WindowServer/WSWindow.cpp b/WindowServer/WSWindow.cpp index bcebe450cb..28b2816a40 100644 --- a/WindowServer/WSWindow.cpp +++ b/WindowServer/WSWindow.cpp @@ -82,18 +82,21 @@ void WSWindow::on_message(WSMessage& message) server_message.mouse.position = static_cast<WSMouseEvent&>(message).position(); server_message.mouse.button = WSAPI_MouseButton::NoButton; server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons(); + server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers(); break; case WSMessage::MouseDown: server_message.type = WSAPI_ServerMessage::Type::MouseDown; server_message.mouse.position = static_cast<WSMouseEvent&>(message).position(); server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button()); server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons(); + server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers(); break; case WSMessage::MouseUp: server_message.type = WSAPI_ServerMessage::Type::MouseUp; server_message.mouse.position = static_cast<WSMouseEvent&>(message).position(); server_message.mouse.button = to_api(static_cast<WSMouseEvent&>(message).button()); server_message.mouse.buttons = static_cast<WSMouseEvent&>(message).buttons(); + server_message.mouse.modifiers = static_cast<WSMouseEvent&>(message).modifiers(); break; case WSMessage::WindowEntered: server_message.type = WSAPI_ServerMessage::Type::WindowEntered; diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index 85170b0f5c..7f6dffc617 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -762,7 +762,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ 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<WSMouseEvent>(event.type(), position, event.buttons(), event.button()); + auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button(), event.modifiers()); window->on_message(*local_event); } @@ -815,7 +815,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ if (!window.global_cursor_tracking()) { // 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<WSMouseEvent>(event.type(), position, event.buttons(), event.button()); + auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button(), event.modifiers()); window.on_message(*local_event); } return IterationDecision::Abort; |