diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index fe9b1f0376..286c510f90 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,7 @@ void GTableView::update_content_size() int content_width = 0; int column_count = model()->column_count(); for (int i = 0; i < column_count; ++i) - content_width += model()->column_metadata(i).preferred_width + horizontal_padding() * 2; + content_width += column_width(i) + horizontal_padding() * 2; int content_height = item_count() * item_height(); set_content_size({ content_width, content_height }); @@ -62,7 +63,12 @@ Rect GTableView::row_rect(int item_index) const int GTableView::column_width(int column_index) const { - return model()->column_metadata(column_index).preferred_width; + auto& column_data = this->column_data(column_index); + if (!column_data.has_initialized_width) { + column_data.has_initialized_width = true; + column_data.width = model()->column_metadata(column_index).preferred_width; + } + return column_data.width; } Rect GTableView::header_rect(int column_index) const @@ -75,9 +81,7 @@ Rect GTableView::header_rect(int column_index) const continue; x_offset += column_width(i) + horizontal_padding() * 2; } - auto column_metadata = model()->column_metadata(column_index); - int column_width = column_metadata.preferred_width; - return { x_offset, 0, column_width + horizontal_padding() * 2, header_height() }; + return { x_offset, 0, column_width(column_index) + horizontal_padding() * 2, header_height() }; } Point GTableView::adjusted_position(const Point& position) @@ -85,14 +89,31 @@ Point GTableView::adjusted_position(const Point& position) return position.translated(-frame_thickness(), vertical_scrollbar().value() - frame_thickness()); } +Rect GTableView::column_resize_grabbable_rect(int column) const +{ + auto header_rect = this->header_rect(column); + return { header_rect.right() - 1, header_rect.top(), 4, header_rect.height() }; +} + void GTableView::mousedown_event(GMouseEvent& event) { if (!model()) return; + auto adjusted_position = this->adjusted_position(event.position()); + if (event.y() < header_height()) { - auto adjusted_position = this->adjusted_position(event.position()); for (int i = 0; i < model()->column_count(); ++i) { + if (event.button() == GMouseButton::Left) { + if (column_resize_grabbable_rect(i).contains(adjusted_position)) { + dbgprintf("now resizing column %d\n", i); + m_resizing_column = i; + m_in_column_resize = true; + m_column_resize_original_width = column_width(i); + m_column_resize_origin = event.position(); + return; + } + } auto header_rect = this->header_rect(i); if (header_rect.contains(adjusted_position)) { auto new_sort_order = GSortOrder::Ascending; @@ -108,7 +129,6 @@ void GTableView::mousedown_event(GMouseEvent& event) } if (event.button() == GMouseButton::Left) { - auto adjusted_position = this->adjusted_position(event.position()); for (int row = 0, row_count = model()->row_count(); row < row_count; ++row) { if (!row_rect(row).contains(adjusted_position)) continue; @@ -125,6 +145,41 @@ void GTableView::mousedown_event(GMouseEvent& event) } } +void GTableView::mousemove_event(GMouseEvent& event) +{ + if (m_in_column_resize) { + auto delta = event.position() - m_column_resize_origin; + int new_width = m_column_resize_original_width + delta.x(); + auto& column_data = this->column_data(m_resizing_column); + if (column_data.width != new_width) { + column_data.width = new_width; + update(); + } + return; + } + + auto adjusted_position = this->adjusted_position(event.position()); + if (event.buttons() == 0) { + for (int i = 0; i < model()->column_count(); ++i) { + if (column_resize_grabbable_rect(i).contains(adjusted_position)) { + window()->set_override_cursor(GStandardCursor::ResizeHorizontal); + return; + } + } + } + window()->set_override_cursor(GStandardCursor::None); +} + +void GTableView::mouseup_event(GMouseEvent& event) +{ + auto adjusted_position = this->adjusted_position(event.position()); + if (event.button() == GMouseButton::Left) { + if (!column_resize_grabbable_rect(m_resizing_column).contains(adjusted_position)) + window()->set_override_cursor(GStandardCursor::None); + m_in_column_resize = false; + } +} + void GTableView::paint_event(GPaintEvent& event) { GFrame::paint_event(event); @@ -167,7 +222,7 @@ void GTableView::paint_event(GPaintEvent& event) if (is_column_hidden(column_index)) continue; auto column_metadata = model()->column_metadata(column_index); - int column_width = column_metadata.preferred_width; + int column_width = this->column_width(column_index); const Font& font = column_metadata.font ? *column_metadata.font : this->font(); bool is_key_column = model()->key_column() == column_index; Rect cell_rect(horizontal_padding() + x_offset, y, column_width, item_height()); @@ -214,8 +269,7 @@ void GTableView::paint_headers(Painter& painter) for (int column_index = 0; column_index < model()->column_count(); ++column_index) { if (is_column_hidden(column_index)) continue; - auto column_metadata = model()->column_metadata(column_index); - int column_width = column_metadata.preferred_width; + int column_width = this->column_width(column_index); bool is_key_column = model()->key_column() == column_index; Rect cell_rect(x_offset, 0, column_width + horizontal_padding() * 2, header_height()); StylePainter::paint_button(painter, cell_rect, ButtonStyle::Normal, false); @@ -309,23 +363,27 @@ void GTableView::scroll_into_view(const GModelIndex& index, Orientation orientat GScrollableWidget::scroll_into_view(rect, orientation); } +GTableView::ColumnData& GTableView::column_data(int column) const +{ + ASSERT(model()); + ASSERT(column >= 0 && column < model()->column_count()); + if (column >= m_column_data.size()) + m_column_data.resize(model()->column_count()); + return m_column_data.at(column); +} + bool GTableView::is_column_hidden(int column) const { - if (column >= 0 && column < m_column_visibility.size()) - return !m_column_visibility[column]; - return false; + return !column_data(column).visibility; } void GTableView::set_column_hidden(int column, bool hidden) { - ASSERT(column >= 0); - if (m_column_visibility.size() <= column) { - int previous_column_count = m_column_visibility.size(); - m_column_visibility.resize(column + 1); - for (int i = previous_column_count; i < m_column_visibility.size(); ++i) - m_column_visibility[i] = true; - } - m_column_visibility[column] = !hidden; + auto& column_data = this->column_data(column); + if (column_data.visibility == !hidden) + return; + column_data.visibility = !hidden; + update(); } void GTableView::doubleclick_event(GMouseEvent& event) @@ -344,3 +402,8 @@ void GTableView::doubleclick_event(GMouseEvent& event) } } } + +void GTableView::leave_event(CEvent&) +{ + window()->set_override_cursor(GStandardCursor::None); +} diff --git a/LibGUI/GTableView.h b/LibGUI/GTableView.h index c526690639..9ca30913f1 100644 --- a/LibGUI/GTableView.h +++ b/LibGUI/GTableView.h @@ -40,19 +40,35 @@ private: virtual void did_update_model() override; virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; + virtual void mouseup_event(GMouseEvent&) override; virtual void doubleclick_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; + virtual void leave_event(CEvent&) override; Rect content_rect(int row, int column) const; void paint_headers(Painter&); int item_count() const; Rect row_rect(int item_index) const; Rect header_rect(int) const; + Rect column_resize_grabbable_rect(int) const; int column_width(int) const; void update_content_size(); - Vector m_column_visibility; + struct ColumnData { + int width { 0 }; + bool has_initialized_width { false }; + bool visibility { true }; + }; + ColumnData& column_data(int column) const; + + mutable Vector m_column_data; int m_horizontal_padding { 5 }; bool m_headers_visible { true }; bool m_alternating_row_colors { true }; + + bool m_in_column_resize { false }; + Point m_column_resize_origin; + int m_column_resize_original_width { 0 }; + int m_resizing_column { -1 }; };