From 3cc7862487d7eefe7f89d4c446c2251497575e5f Mon Sep 17 00:00:00 2001 From: thankyouverycool <66646555+thankyouverycool@users.noreply.github.com> Date: Wed, 17 Mar 2021 20:11:44 -0400 Subject: [PATCH] LibGUI: Support double-click resizing column headers Columns can now be best-fit resized by double-clicking their grabbable edges. When a default width is set and all data is empty, double-clicking will restore the column to its original state. --- .../Libraries/LibGUI/AbstractTableView.cpp | 46 +++++++++++++++++++ Userland/Libraries/LibGUI/AbstractTableView.h | 2 + Userland/Libraries/LibGUI/HeaderView.cpp | 36 +++++++++++++++ Userland/Libraries/LibGUI/HeaderView.h | 9 ++++ 4 files changed, 93 insertions(+) diff --git a/Userland/Libraries/LibGUI/AbstractTableView.cpp b/Userland/Libraries/LibGUI/AbstractTableView.cpp index 64e1093479..a3508bf7a7 100644 --- a/Userland/Libraries/LibGUI/AbstractTableView.cpp +++ b/Userland/Libraries/LibGUI/AbstractTableView.cpp @@ -48,6 +48,9 @@ AbstractTableView::AbstractTableView() m_corner_button->set_fill_with_background_color(true); m_column_header = add(*this, Gfx::Orientation::Horizontal); m_column_header->move_to_back(); + m_column_header->on_resize_doubleclick = [this](auto column) { + auto_resize_column(column); + }; m_row_header = add(*this, Gfx::Orientation::Vertical); m_row_header->move_to_back(); m_row_header->set_visible(false); @@ -67,6 +70,44 @@ void AbstractTableView::select_all() } } +void AbstractTableView::auto_resize_column(int column) +{ + if (!model()) + return; + + if (!column_header().is_section_visible(column)) + return; + + auto& model = *this->model(); + int row_count = model.row_count(); + + int header_width = m_column_header->font().width(model.column_name(column)); + if (column == m_key_column && model.is_column_sortable(column)) + header_width += font().width(" \xE2\xAC\x86"); + + int column_width = header_width; + bool is_empty = true; + for (int row = 0; row < row_count; ++row) { + auto cell_data = model.index(row, column).data(); + int cell_width = 0; + if (cell_data.is_icon()) { + cell_width = cell_data.as_icon().bitmap_for_size(16)->width(); + } else if (cell_data.is_bitmap()) { + cell_width = cell_data.as_bitmap().width(); + } else if (cell_data.is_valid()) { + cell_width = font().width(cell_data.to_string()); + } + if (is_empty && cell_width > 0) + is_empty = false; + column_width = max(column_width, cell_width); + } + + auto default_column_size = column_header().default_section_size(column); + if (is_empty && column_header().is_default_section_size_initialized(column)) + column_header().set_section_size(column, default_column_size); + else + column_header().set_section_size(column, column_width); +} void AbstractTableView::update_column_sizes() { if (!model()) @@ -326,6 +367,11 @@ void AbstractTableView::header_did_change_section_visibility(Badge, update(); } +void AbstractTableView::set_default_column_width(int column, int width) +{ + column_header().set_default_section_size(column, width); +} + void AbstractTableView::set_column_hidden(int column, bool hidden) { column_header().set_section_visible(column, !hidden); diff --git a/Userland/Libraries/LibGUI/AbstractTableView.h b/Userland/Libraries/LibGUI/AbstractTableView.h index d4b2dff341..3b795112d4 100644 --- a/Userland/Libraries/LibGUI/AbstractTableView.h +++ b/Userland/Libraries/LibGUI/AbstractTableView.h @@ -58,6 +58,7 @@ public: int column_width(int column) const; void set_column_width(int column, int width); + void set_default_column_width(int column, int width); Gfx::TextAlignment column_header_alignment(int column) const; void set_column_header_alignment(int column, Gfx::TextAlignment); @@ -106,6 +107,7 @@ protected: virtual void toggle_index(const ModelIndex&) { } void update_content_size(); + virtual void auto_resize_column(int column); virtual void update_column_sizes(); virtual void update_row_sizes(); virtual int item_count() const; diff --git a/Userland/Libraries/LibGUI/HeaderView.cpp b/Userland/Libraries/LibGUI/HeaderView.cpp index c60dbc9e55..8d1413bb86 100644 --- a/Userland/Libraries/LibGUI/HeaderView.cpp +++ b/Userland/Libraries/LibGUI/HeaderView.cpp @@ -117,6 +117,20 @@ int HeaderView::section_count() const return m_orientation == Gfx::Orientation::Horizontal ? model()->column_count() : model()->row_count(); } +void HeaderView::doubleclick_event(MouseEvent& event) +{ + if (!model()) + return; + + int section_count = this->section_count(); + for (int i = 0; i < section_count; ++i) { + if (section_resize_grabbable_rect(i).contains(event.position())) { + if (on_resize_doubleclick) + on_resize_doubleclick(i); + } + } +} + void HeaderView::mousedown_event(MouseEvent& event) { if (!model()) @@ -361,6 +375,28 @@ void HeaderView::set_section_alignment(int section, Gfx::TextAlignment alignment section_data(section).alignment = alignment; } +void HeaderView::set_default_section_size(int section, int size) +{ + if (orientation() == Gfx::Orientation::Horizontal && size < minimum_column_size) + size = minimum_column_size; + + auto& data = section_data(section); + if (data.default_size == size) + return; + data.default_size = size; + data.has_initialized_default_size = true; +} + +int HeaderView::default_section_size(int section) const +{ + return section_data(section).default_size; +} + +bool HeaderView::is_default_section_size_initialized(int section) const +{ + return section_data(section).has_initialized_default_size; +} + bool HeaderView::is_section_visible(int section) const { return section_data(section).visibility; diff --git a/Userland/Libraries/LibGUI/HeaderView.h b/Userland/Libraries/LibGUI/HeaderView.h index 5132be3fb6..7f10ccea6e 100644 --- a/Userland/Libraries/LibGUI/HeaderView.h +++ b/Userland/Libraries/LibGUI/HeaderView.h @@ -45,6 +45,10 @@ public: void set_section_size(int section, int size); int section_size(int section) const; + void set_default_section_size(int section, int size); + int default_section_size(int section) const; + bool is_default_section_size_initialized(int section) const; + Gfx::TextAlignment section_alignment(int section) const; void set_section_alignment(int section, Gfx::TextAlignment); @@ -54,6 +58,8 @@ public: int section_count() const; Gfx::IntRect section_rect(int section) const; + Function on_resize_doubleclick; + private: HeaderView(AbstractTableView&, Gfx::Orientation); @@ -61,6 +67,7 @@ private: virtual void mousedown_event(MouseEvent&) override; virtual void mousemove_event(MouseEvent&) override; virtual void mouseup_event(MouseEvent&) override; + virtual void doubleclick_event(MouseEvent&) override; virtual void context_menu_event(ContextMenuEvent&) override; virtual void leave_event(Core::Event&) override; @@ -78,7 +85,9 @@ private: struct SectionData { int size { 0 }; + int default_size { 0 }; bool has_initialized_size { false }; + bool has_initialized_default_size { false }; bool visibility { true }; RefPtr visibility_action; Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterLeft };