Browse Source

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.
thankyouverycool 4 years ago
parent
commit
3cc7862487

+ 46 - 0
Userland/Libraries/LibGUI/AbstractTableView.cpp

@@ -48,6 +48,9 @@ AbstractTableView::AbstractTableView()
     m_corner_button->set_fill_with_background_color(true);
     m_column_header = add<HeaderView>(*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<HeaderView>(*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<HeaderView>,
     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);

+ 2 - 0
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;

+ 36 - 0
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;

+ 9 - 0
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<void(int section)> 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<Action> visibility_action;
         Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterLeft };