mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 15:38:10 +00:00
LibGUI: Add AbstractView::move_cursor() and share some movement logic
A view can now be told to move its cursor in one of multiple directions as specified by the CursorMovement enum. View subclasses can override move_cursor(CursorMovement) to implement their own cursor behavior. By default, AbstractView::move_cursor() is a no-op. This patch improves code sharing between TableView and TreeView. :^)
This commit is contained in:
parent
1b3169f405
commit
0b9d765f6b
7 changed files with 154 additions and 55 deletions
|
@ -383,4 +383,39 @@ void AbstractTableView::set_row_height(int height)
|
||||||
update_row_sizes();
|
update_row_sizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractTableView::keydown_event(KeyEvent& event)
|
||||||
|
{
|
||||||
|
if (event.key() == KeyCode::Key_Left) {
|
||||||
|
move_cursor(CursorMovement::Left);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key() == KeyCode::Key_Right) {
|
||||||
|
move_cursor(CursorMovement::Right);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key() == KeyCode::Key_Up) {
|
||||||
|
move_cursor(CursorMovement::Up);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key() == KeyCode::Key_Down) {
|
||||||
|
move_cursor(CursorMovement::Down);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key() == KeyCode::Key_Home) {
|
||||||
|
move_cursor(CursorMovement::Home);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key() == KeyCode::Key_End) {
|
||||||
|
move_cursor(CursorMovement::End);
|
||||||
|
event.accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return AbstractView::keydown_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,7 @@ protected:
|
||||||
virtual void mousedown_event(MouseEvent&) override;
|
virtual void mousedown_event(MouseEvent&) override;
|
||||||
virtual void doubleclick_event(MouseEvent&) override;
|
virtual void doubleclick_event(MouseEvent&) override;
|
||||||
virtual void context_menu_event(ContextMenuEvent&) override;
|
virtual void context_menu_event(ContextMenuEvent&) override;
|
||||||
|
virtual void keydown_event(KeyEvent&) override;
|
||||||
virtual void resize_event(ResizeEvent&) override;
|
virtual void resize_event(ResizeEvent&) override;
|
||||||
|
|
||||||
virtual void did_update_model(unsigned flags) override;
|
virtual void did_update_model(unsigned flags) override;
|
||||||
|
|
|
@ -33,9 +33,22 @@
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
class AbstractView : public ScrollableWidget {
|
class AbstractView : public ScrollableWidget {
|
||||||
C_OBJECT_ABSTRACT(AbstractView)
|
C_OBJECT_ABSTRACT(AbstractView);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class CursorMovement {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Home,
|
||||||
|
End,
|
||||||
|
PageUp,
|
||||||
|
PageDown,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void move_cursor(CursorMovement) { }
|
||||||
|
|
||||||
void set_model(RefPtr<Model>);
|
void set_model(RefPtr<Model>);
|
||||||
Model* model() { return m_model.ptr(); }
|
Model* model() { return m_model.ptr(); }
|
||||||
const Model* model() const { return m_model.ptr(); }
|
const Model* model() const { return m_model.ptr(); }
|
||||||
|
|
|
@ -154,28 +154,44 @@ void TableView::keydown_event(KeyEvent& event)
|
||||||
{
|
{
|
||||||
if (!model())
|
if (!model())
|
||||||
return;
|
return;
|
||||||
auto& model = *this->model();
|
|
||||||
if (event.key() == KeyCode::Key_Return) {
|
if (event.key() == KeyCode::Key_Return) {
|
||||||
activate_or_edit_selected();
|
activate_or_edit_selected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key() == KeyCode::Key_Left) {
|
return AbstractTableView::keydown_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableView::move_cursor(CursorMovement movement)
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
auto& model = *this->model();
|
||||||
|
switch (movement) {
|
||||||
|
case CursorMovement::Left:
|
||||||
move_selection(0, -1);
|
move_selection(0, -1);
|
||||||
return;
|
break;
|
||||||
}
|
case CursorMovement::Right:
|
||||||
if (event.key() == KeyCode::Key_Right) {
|
|
||||||
move_selection(0, 1);
|
move_selection(0, 1);
|
||||||
return;
|
break;
|
||||||
}
|
case CursorMovement::Up:
|
||||||
if (event.key() == KeyCode::Key_Up) {
|
|
||||||
move_selection(-1, 0);
|
move_selection(-1, 0);
|
||||||
return;
|
break;
|
||||||
}
|
case CursorMovement::Down:
|
||||||
if (event.key() == KeyCode::Key_Down) {
|
|
||||||
move_selection(1, 0);
|
move_selection(1, 0);
|
||||||
return;
|
break;
|
||||||
|
case CursorMovement::Home: {
|
||||||
|
auto index = model.index(0, 0);
|
||||||
|
set_selection(index);
|
||||||
|
scroll_into_view(index, Gfx::Orientation::Vertical);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (event.key() == KeyCode::Key_PageUp) {
|
case CursorMovement::End: {
|
||||||
|
auto index = model.index(model.row_count() - 1, 0);
|
||||||
|
set_selection(index);
|
||||||
|
scroll_into_view(index, Gfx::Orientation::Vertical);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CursorMovement::PageUp: {
|
||||||
int items_per_page = visible_content_rect().height() / row_height();
|
int items_per_page = visible_content_rect().height() / row_height();
|
||||||
auto old_index = selection().first();
|
auto old_index = selection().first();
|
||||||
auto new_index = model.index(max(0, old_index.row() - items_per_page), old_index.column());
|
auto new_index = model.index(max(0, old_index.row() - items_per_page), old_index.column());
|
||||||
|
@ -184,9 +200,9 @@ void TableView::keydown_event(KeyEvent& event)
|
||||||
scroll_into_view(new_index, Orientation::Vertical);
|
scroll_into_view(new_index, Orientation::Vertical);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
if (event.key() == KeyCode::Key_PageDown) {
|
case CursorMovement::PageDown: {
|
||||||
int items_per_page = visible_content_rect().height() / row_height();
|
int items_per_page = visible_content_rect().height() / row_height();
|
||||||
auto old_index = selection().first();
|
auto old_index = selection().first();
|
||||||
auto new_index = model.index(min(model.row_count() - 1, old_index.row() + items_per_page), old_index.column());
|
auto new_index = model.index(min(model.row_count() - 1, old_index.row() + items_per_page), old_index.column());
|
||||||
|
@ -195,9 +211,9 @@ void TableView::keydown_event(KeyEvent& event)
|
||||||
scroll_into_view(new_index, Orientation::Vertical);
|
scroll_into_view(new_index, Orientation::Vertical);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Widget::keydown_event(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ public:
|
||||||
protected:
|
protected:
|
||||||
TableView();
|
TableView();
|
||||||
|
|
||||||
|
virtual void move_cursor(CursorMovement) override;
|
||||||
|
|
||||||
virtual void keydown_event(KeyEvent&) override;
|
virtual void keydown_event(KeyEvent&) override;
|
||||||
virtual void paint_event(PaintEvent&) override;
|
virtual void paint_event(PaintEvent&) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -418,43 +418,6 @@ void TreeView::keydown_event(KeyEvent& event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key() == KeyCode::Key_Up) {
|
|
||||||
ModelIndex previous_index;
|
|
||||||
ModelIndex found_index;
|
|
||||||
traverse_in_paint_order([&](const ModelIndex& index, const Gfx::IntRect&, const Gfx::IntRect&, int) {
|
|
||||||
if (index == cursor_index) {
|
|
||||||
found_index = previous_index;
|
|
||||||
return IterationDecision::Break;
|
|
||||||
}
|
|
||||||
previous_index = index;
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
|
||||||
if (found_index.is_valid()) {
|
|
||||||
selection().set(found_index);
|
|
||||||
scroll_into_view(selection().first(), Orientation::Vertical);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.key() == KeyCode::Key_Down) {
|
|
||||||
ModelIndex previous_index;
|
|
||||||
ModelIndex found_index;
|
|
||||||
traverse_in_paint_order([&](const ModelIndex& index, const Gfx::IntRect&, const Gfx::IntRect&, int) {
|
|
||||||
if (previous_index == cursor_index) {
|
|
||||||
found_index = index;
|
|
||||||
return IterationDecision::Break;
|
|
||||||
}
|
|
||||||
previous_index = index;
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
|
||||||
if (found_index.is_valid()) {
|
|
||||||
selection().set(found_index);
|
|
||||||
scroll_into_view(selection().first(), Orientation::Vertical);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto open_tree_node = [&](bool open, MetadataForIndex& metadata) {
|
auto open_tree_node = [&](bool open, MetadataForIndex& metadata) {
|
||||||
if (on_toggle)
|
if (on_toggle)
|
||||||
on_toggle(cursor_index, open);
|
on_toggle(cursor_index, open);
|
||||||
|
@ -509,6 +472,73 @@ void TreeView::keydown_event(KeyEvent& event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbstractTableView::keydown_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TreeView::move_cursor(CursorMovement movement)
|
||||||
|
{
|
||||||
|
auto cursor_index = selection().first();
|
||||||
|
|
||||||
|
switch (movement) {
|
||||||
|
case CursorMovement::Up: {
|
||||||
|
ModelIndex previous_index;
|
||||||
|
ModelIndex found_index;
|
||||||
|
traverse_in_paint_order([&](const ModelIndex& index, const Gfx::IntRect&, const Gfx::IntRect&, int) {
|
||||||
|
if (index == cursor_index) {
|
||||||
|
found_index = previous_index;
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
previous_index = index;
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
if (found_index.is_valid()) {
|
||||||
|
selection().set(found_index);
|
||||||
|
scroll_into_view(selection().first(), Orientation::Vertical);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CursorMovement::Down: {
|
||||||
|
ModelIndex previous_index;
|
||||||
|
ModelIndex found_index;
|
||||||
|
traverse_in_paint_order([&](const ModelIndex& index, const Gfx::IntRect&, const Gfx::IntRect&, int) {
|
||||||
|
if (previous_index == cursor_index) {
|
||||||
|
found_index = index;
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
previous_index = index;
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
if (found_index.is_valid()) {
|
||||||
|
selection().set(found_index);
|
||||||
|
scroll_into_view(selection().first(), Orientation::Vertical);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CursorMovement::Home:
|
||||||
|
// FIXME: Implement.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CursorMovement::End:
|
||||||
|
// FIXME: Implement.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CursorMovement::PageUp:
|
||||||
|
// FIXME: Implement.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CursorMovement::PageDown:
|
||||||
|
// FIXME: Implement.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CursorMovement::Left:
|
||||||
|
case CursorMovement::Right:
|
||||||
|
// There is no left/right in a treeview, those keys expand/collapse items instead.
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TreeView::item_count() const
|
int TreeView::item_count() const
|
||||||
|
|
|
@ -52,8 +52,10 @@ protected:
|
||||||
virtual void paint_event(PaintEvent&) override;
|
virtual void paint_event(PaintEvent&) override;
|
||||||
virtual void doubleclick_event(MouseEvent&) override;
|
virtual void doubleclick_event(MouseEvent&) override;
|
||||||
virtual void keydown_event(KeyEvent&) override;
|
virtual void keydown_event(KeyEvent&) override;
|
||||||
|
|
||||||
virtual void did_update_selection() override;
|
virtual void did_update_selection() override;
|
||||||
virtual void did_update_model(unsigned flags) override;
|
virtual void did_update_model(unsigned flags) override;
|
||||||
|
virtual void move_cursor(CursorMovement) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ModelIndex index_at_event_position(const Gfx::IntPoint&, bool& is_toggle) const override;
|
virtual ModelIndex index_at_event_position(const Gfx::IntPoint&, bool& is_toggle) const override;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue