summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-08-27 18:36:31 +0200
committerAndreas Kling <kling@serenityos.org>2020-08-27 18:36:31 +0200
commit9cf37901cd4166117f8ed7d381b59da243ea7443 (patch)
tree3025c74ca786b2a7b1438045562a631844c58138
parent76a0acb5bcb2fae9e7a08ea8de02e9873708c255 (diff)
downloadserenity-9cf37901cd4166117f8ed7d381b59da243ea7443.zip
LibGUI: Add a cursor to AbstractView, separate from the selection
Views now have a cursor index (retrievable via cursor_index()) which is separate from the selection. Until now, we've been using the first entry in the selection as "the cursor", which gets messy whenever you want to select more than one index in the model. When setting the cursor, the selection is implicitly updated as well to maintain the old behavior (for the most part.) Going forward, this will make it much easier to implement things like shift-select (extend selection from cursor) and such. :^)
-rw-r--r--Libraries/LibGUI/AbstractTableView.cpp36
-rw-r--r--Libraries/LibGUI/AbstractTableView.h11
-rw-r--r--Libraries/LibGUI/AbstractView.cpp27
-rw-r--r--Libraries/LibGUI/AbstractView.h15
-rw-r--r--Libraries/LibGUI/TableView.cpp33
-rw-r--r--Libraries/LibGUI/TableView.h2
-rw-r--r--Libraries/LibGUI/TreeView.cpp2
-rw-r--r--Libraries/LibGUI/TreeView.h2
8 files changed, 79 insertions, 49 deletions
diff --git a/Libraries/LibGUI/AbstractTableView.cpp b/Libraries/LibGUI/AbstractTableView.cpp
index 82c524a8f0..847faff311 100644
--- a/Libraries/LibGUI/AbstractTableView.cpp
+++ b/Libraries/LibGUI/AbstractTableView.cpp
@@ -223,29 +223,18 @@ int AbstractTableView::item_count() const
return model()->row_count();
}
-void AbstractTableView::move_selection(int vertical_steps, int horizontal_steps)
+void AbstractTableView::move_cursor_relative(int vertical_steps, int horizontal_steps, SelectionUpdate selection_update)
{
if (!model())
return;
auto& model = *this->model();
ModelIndex new_index;
- if (!selection().is_empty()) {
- auto old_index = selection().first();
- new_index = model.index(old_index.row() + vertical_steps, old_index.column() + horizontal_steps);
+ if (cursor_index().is_valid()) {
+ new_index = model.index(cursor_index().row() + vertical_steps, cursor_index().column() + horizontal_steps);
} else {
new_index = model.index(0, 0);
}
- if (model.is_valid(new_index)) {
- selection().set(new_index);
- scroll_into_view(new_index, Orientation::Vertical);
- update();
- }
-}
-
-void AbstractTableView::scroll_into_view(const ModelIndex& index, Orientation orientation)
-{
- auto rect = row_rect(index.row()).translated(0, -m_column_header->height());
- ScrollableWidget::scroll_into_view(rect, orientation);
+ set_cursor(new_index, selection_update);
}
void AbstractTableView::scroll_into_view(const ModelIndex& index, bool scroll_horizontally, bool scroll_vertically)
@@ -382,33 +371,38 @@ void AbstractTableView::set_row_height(int height)
void AbstractTableView::keydown_event(KeyEvent& event)
{
+ SelectionUpdate selection_update = SelectionUpdate::Set;
+ if (event.modifiers() == KeyModifier::Mod_Shift) {
+ selection_update = SelectionUpdate::Shift;
+ }
+
if (event.key() == KeyCode::Key_Left) {
- move_cursor(CursorMovement::Left);
+ move_cursor(CursorMovement::Left, selection_update);
event.accept();
return;
}
if (event.key() == KeyCode::Key_Right) {
- move_cursor(CursorMovement::Right);
+ move_cursor(CursorMovement::Right, selection_update);
event.accept();
return;
}
if (event.key() == KeyCode::Key_Up) {
- move_cursor(CursorMovement::Up);
+ move_cursor(CursorMovement::Up, selection_update);
event.accept();
return;
}
if (event.key() == KeyCode::Key_Down) {
- move_cursor(CursorMovement::Down);
+ move_cursor(CursorMovement::Down, selection_update);
event.accept();
return;
}
if (event.key() == KeyCode::Key_Home) {
- move_cursor(CursorMovement::Home);
+ move_cursor(CursorMovement::Home, selection_update);
event.accept();
return;
}
if (event.key() == KeyCode::Key_End) {
- move_cursor(CursorMovement::End);
+ move_cursor(CursorMovement::End, selection_update);
event.accept();
return;
}
diff --git a/Libraries/LibGUI/AbstractTableView.h b/Libraries/LibGUI/AbstractTableView.h
index c753dba91c..9fc6ea76ac 100644
--- a/Libraries/LibGUI/AbstractTableView.h
+++ b/Libraries/LibGUI/AbstractTableView.h
@@ -68,16 +68,17 @@ public:
Gfx::IntRect content_rect(int row, int column) const;
Gfx::IntRect row_rect(int item_index) const;
- void scroll_into_view(const ModelIndex&, Orientation);
- void scroll_into_view(const ModelIndex&, bool scroll_horizontally, bool scroll_vertically);
+ virtual void scroll_into_view(const ModelIndex&, bool scroll_horizontally = true, bool scroll_vertically = true) override;
+ void scroll_into_view(const ModelIndex& index, Orientation orientation)
+ {
+ scroll_into_view(index, orientation == Gfx::Orientation::Horizontal, orientation == Gfx::Orientation::Vertical);
+ }
virtual ModelIndex index_at_event_position(const Gfx::IntPoint&, bool& is_toggle) const;
virtual ModelIndex index_at_event_position(const Gfx::IntPoint&) const override;
virtual void select_all() override;
- void move_selection(int vertical_steps, int horizontal_steps);
-
void header_did_change_section_visibility(Badge<HeaderView>, Gfx::Orientation, int section, bool visible);
void header_did_change_section_size(Badge<HeaderView>, Gfx::Orientation, int section, int size);
@@ -109,6 +110,8 @@ protected:
TableCellPaintingDelegate* column_painting_delegate(int column) const;
+ void move_cursor_relative(int vertical_steps, int horizontal_steps, SelectionUpdate);
+
private:
void layout_headers();
diff --git a/Libraries/LibGUI/AbstractView.cpp b/Libraries/LibGUI/AbstractView.cpp
index d6fe8c77a7..8f299b247e 100644
--- a/Libraries/LibGUI/AbstractView.cpp
+++ b/Libraries/LibGUI/AbstractView.cpp
@@ -219,7 +219,7 @@ void AbstractView::mousedown_event(MouseEvent& event)
// We might be starting a drag, so don't throw away other selected items yet.
m_might_drag = true;
} else {
- set_selection(index);
+ set_cursor(index, SelectionUpdate::Set);
}
update();
@@ -424,4 +424,29 @@ void AbstractView::set_key_column_and_sort_order(int column, SortOrder sort_orde
update();
}
+void AbstractView::set_cursor(ModelIndex index, SelectionUpdate selection_update)
+{
+ if (m_cursor_index == index)
+ return;
+
+ if (!model()) {
+ m_cursor_index = {};
+ return;
+ }
+
+ if (model()->is_valid(index)) {
+ if (selection_update == SelectionUpdate::Set)
+ selection().set(index);
+ else if (selection_update == SelectionUpdate::Ctrl)
+ selection().toggle(index);
+
+ // FIXME: Support the other SelectionUpdate types
+
+ m_cursor_index = index;
+ // FIXME: We should scroll into view both vertically *and* horizontally.
+ scroll_into_view(index, false, true);
+ update();
+ }
+}
+
}
diff --git a/Libraries/LibGUI/AbstractView.h b/Libraries/LibGUI/AbstractView.h
index 31d31aa8a4..1a648fcd72 100644
--- a/Libraries/LibGUI/AbstractView.h
+++ b/Libraries/LibGUI/AbstractView.h
@@ -47,7 +47,14 @@ public:
PageDown,
};
- virtual void move_cursor(CursorMovement) { }
+ enum class SelectionUpdate {
+ None,
+ Set,
+ Shift,
+ Ctrl,
+ };
+
+ virtual void move_cursor(CursorMovement, SelectionUpdate) { }
void set_model(RefPtr<Model>);
Model* model() { return m_model.ptr(); }
@@ -94,6 +101,11 @@ public:
int key_column() const { return m_key_column; }
SortOrder sort_order() const { return m_sort_order; }
+ virtual void scroll_into_view(const ModelIndex&, [[maybe_unused]] bool scroll_horizontally = true, [[maybe_unused]] bool scroll_vertically = true) { }
+
+ const ModelIndex& cursor_index() const { return m_cursor_index; }
+ void set_cursor(ModelIndex, SelectionUpdate);
+
protected:
AbstractView();
virtual ~AbstractView() override;
@@ -137,6 +149,7 @@ private:
RefPtr<Model> m_model;
OwnPtr<ModelEditingDelegate> m_editing_delegate;
ModelSelection m_selection;
+ ModelIndex m_cursor_index;
bool m_activates_on_selection { false };
bool m_multi_select { true };
};
diff --git a/Libraries/LibGUI/TableView.cpp b/Libraries/LibGUI/TableView.cpp
index b5feb595c0..af3295f75c 100644
--- a/Libraries/LibGUI/TableView.cpp
+++ b/Libraries/LibGUI/TableView.cpp
@@ -136,6 +136,7 @@ void TableView::paint_event(PaintEvent& event)
if (cell_background_color.is_valid())
painter.fill_rect(cell_rect_for_fill, cell_background_color.to_color(background_color));
}
+
auto text_alignment = cell_index.data(ModelRole::TextAlignment).to_text_alignment(Gfx::TextAlignment::CenterLeft);
painter.draw_text(cell_rect, data.to_string(), font_for_index(cell_index), text_alignment, text_color, Gfx::TextElision::Right);
}
@@ -161,56 +162,50 @@ void TableView::keydown_event(KeyEvent& event)
return AbstractTableView::keydown_event(event);
}
-void TableView::move_cursor(CursorMovement movement)
+void TableView::move_cursor(CursorMovement movement, SelectionUpdate selection_update)
{
if (!model())
return;
auto& model = *this->model();
switch (movement) {
case CursorMovement::Left:
- move_selection(0, -1);
+ move_cursor_relative(0, -1, selection_update);
break;
case CursorMovement::Right:
- move_selection(0, 1);
+ move_cursor_relative(0, 1, selection_update);
break;
case CursorMovement::Up:
- move_selection(-1, 0);
+ move_cursor_relative(-1, 0, selection_update);
break;
case CursorMovement::Down:
- move_selection(1, 0);
+ move_cursor_relative(1, 0, selection_update);
break;
case CursorMovement::Home: {
auto index = model.index(0, 0);
- set_selection(index);
- scroll_into_view(index, Gfx::Orientation::Vertical);
+ set_cursor(index, selection_update);
+ scroll_into_view(index, false, true);
break;
}
case CursorMovement::End: {
auto index = model.index(model.row_count() - 1, 0);
- set_selection(index);
- scroll_into_view(index, Gfx::Orientation::Vertical);
+ set_cursor(index, selection_update);
+ scroll_into_view(index, false, true);
break;
}
case CursorMovement::PageUp: {
int items_per_page = visible_content_rect().height() / row_height();
auto old_index = selection().first();
auto new_index = model.index(max(0, old_index.row() - items_per_page), old_index.column());
- if (model.is_valid(new_index)) {
- selection().set(new_index);
- scroll_into_view(new_index, Orientation::Vertical);
- update();
- }
+ if (model.is_valid(new_index))
+ set_cursor(new_index, selection_update);
break;
}
case CursorMovement::PageDown: {
int items_per_page = visible_content_rect().height() / row_height();
auto old_index = selection().first();
auto new_index = model.index(min(model.row_count() - 1, old_index.row() + items_per_page), old_index.column());
- if (model.is_valid(new_index)) {
- selection().set(new_index);
- scroll_into_view(new_index, Orientation::Vertical);
- update();
- }
+ if (model.is_valid(new_index))
+ set_cursor(new_index, selection_update);
break;
}
}
diff --git a/Libraries/LibGUI/TableView.h b/Libraries/LibGUI/TableView.h
index 792b8290ae..f67b89935d 100644
--- a/Libraries/LibGUI/TableView.h
+++ b/Libraries/LibGUI/TableView.h
@@ -38,7 +38,7 @@ public:
protected:
TableView();
- virtual void move_cursor(CursorMovement) override;
+ virtual void move_cursor(CursorMovement, SelectionUpdate) override;
virtual void keydown_event(KeyEvent&) override;
virtual void paint_event(PaintEvent&) override;
diff --git a/Libraries/LibGUI/TreeView.cpp b/Libraries/LibGUI/TreeView.cpp
index b19f89f51b..37afdd18a9 100644
--- a/Libraries/LibGUI/TreeView.cpp
+++ b/Libraries/LibGUI/TreeView.cpp
@@ -476,7 +476,7 @@ void TreeView::keydown_event(KeyEvent& event)
AbstractTableView::keydown_event(event);
}
-void TreeView::move_cursor(CursorMovement movement)
+void TreeView::move_cursor(CursorMovement movement, SelectionUpdate)
{
auto cursor_index = selection().first();
diff --git a/Libraries/LibGUI/TreeView.h b/Libraries/LibGUI/TreeView.h
index 18ad2c8413..4e4e39fb44 100644
--- a/Libraries/LibGUI/TreeView.h
+++ b/Libraries/LibGUI/TreeView.h
@@ -55,7 +55,7 @@ protected:
virtual void did_update_selection() override;
virtual void did_update_model(unsigned flags) override;
- virtual void move_cursor(CursorMovement) override;
+ virtual void move_cursor(CursorMovement, SelectionUpdate) override;
private:
virtual ModelIndex index_at_event_position(const Gfx::IntPoint&, bool& is_toggle) const override;