1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 01:07:35 +00:00

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. :^)
This commit is contained in:
Andreas Kling 2019-03-08 17:53:02 +01:00
parent 6d172725c0
commit f40d11f06d
11 changed files with 73 additions and 21 deletions

View file

@ -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 {

View file

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

View file

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

View file

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

View file

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