From 2612d23032f819acf221281a09c878cd63d157e3 Mon Sep 17 00:00:00 2001 From: networkException Date: Sat, 10 Sep 2022 20:36:50 +0200 Subject: [PATCH] LibGUI: Implement rubber band selection in table view This patch implements rubber band selection in table view while clamping the rubber band rect to the widget inner rect, matching the behavior of IconView and ColumnsView. --- Userland/Libraries/LibGUI/TableView.cpp | 82 +++++++++++++++++++++++++ Userland/Libraries/LibGUI/TableView.h | 8 +++ 2 files changed, 90 insertions(+) diff --git a/Userland/Libraries/LibGUI/TableView.cpp b/Userland/Libraries/LibGUI/TableView.cpp index f1b4481f46..606d16115d 100644 --- a/Userland/Libraries/LibGUI/TableView.cpp +++ b/Userland/Libraries/LibGUI/TableView.cpp @@ -2,6 +2,7 @@ * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2022, Glenford Williams * Copyright (c) 2022, the SerenityOS developers. + * Copyright (c) 2022, Jakob-Niklas See * * SPDX-License-Identifier: BSD-2-Clause */ @@ -161,6 +162,25 @@ void TableView::paint_event(PaintEvent& event) painter.fill_rect(unpainted_rect, widget_background_color); } +void TableView::second_paint_event(PaintEvent& event) +{ + if (!m_rubber_banding) + return; + + Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.add_clip_rect(widget_inner_rect()); + + // The rubber band rect always borders the widget inner to the left and right + auto rubber_band_left = widget_inner_rect().left(); + auto rubber_band_right = widget_inner_rect().right() + 1; + + auto rubber_band_rect = Gfx::IntRect::from_two_points({ rubber_band_left, m_rubber_band_origin }, { rubber_band_right, m_rubber_band_current }); + + painter.fill_rect(rubber_band_rect, palette().rubber_band_fill()); + painter.draw_rect(rubber_band_rect, palette().rubber_band_border()); +} + void TableView::keydown_event(KeyEvent& event) { if (!model()) @@ -196,6 +216,68 @@ void TableView::keydown_event(KeyEvent& event) } } +void TableView::mousedown_event(MouseEvent& event) +{ + AbstractTableView::mousedown_event(event); + + if (!model()) + return; + + if (event.button() != MouseButton::Primary) + return; + + if (m_might_drag) + return; + + if (selection_mode() == SelectionMode::MultiSelection) { + m_rubber_banding = true; + m_rubber_band_origin = event.position().y(); + m_rubber_band_current = event.position().y(); + } +} + +void TableView::mouseup_event(MouseEvent& event) +{ + AbstractTableView::mouseup_event(event); + + if (m_rubber_banding && event.button() == MouseButton::Primary) { + m_rubber_banding = false; + update(); + } +} + +void TableView::mousemove_event(MouseEvent& event) +{ + if (m_rubber_banding) { + // The rubber band rect cannot go outside the bounds of the rect enclosing all rows + m_rubber_band_current = clamp(event.position().y(), widget_inner_rect().top() + column_header().height(), widget_inner_rect().bottom() + 1); + + int row_count = model()->row_count(); + + clear_selection(); + + set_suppress_update_on_selection_change(true); + + for (int row = 0; row < row_count; ++row) { + auto index = model()->index(row); + VERIFY(index.is_valid()); + + int row_top = row * row_height() + column_header().height(); + int row_bottom = row * row_height() + row_height() + column_header().height(); + + if ((m_rubber_band_origin > row_top && m_rubber_band_current < row_top) || (m_rubber_band_origin > row_bottom && m_rubber_band_current < row_bottom)) { + add_selection(index); + } + } + + set_suppress_update_on_selection_change(false); + + update(); + } + + AbstractTableView::mousemove_event(event); +} + void TableView::move_cursor(CursorMovement movement, SelectionUpdate selection_update) { if (!model()) diff --git a/Userland/Libraries/LibGUI/TableView.h b/Userland/Libraries/LibGUI/TableView.h index 94f7f13ae3..d6f65ceb71 100644 --- a/Userland/Libraries/LibGUI/TableView.h +++ b/Userland/Libraries/LibGUI/TableView.h @@ -41,12 +41,20 @@ protected: TableView(); virtual void keydown_event(KeyEvent&) override; + virtual void mousedown_event(MouseEvent&) override; + virtual void mouseup_event(MouseEvent&) override; + virtual void mousemove_event(MouseEvent&) override; virtual void paint_event(PaintEvent&) override; + virtual void second_paint_event(PaintEvent&) override; private: GridStyle m_grid_style { GridStyle::None }; bool m_highlight_key_column { true }; + + bool m_rubber_banding { false }; + int m_rubber_band_origin { 0 }; + int m_rubber_band_current { 0 }; }; }