Prechádzať zdrojové kódy

GTableView: Make it possible to resize the columns with the mouse.

The GModel now merely provides an initial width for the columns. Once that
has been queried, the table view manages width from then on.
Andreas Kling 6 rokov pred
rodič
commit
48e3ea9e5c
2 zmenil súbory, kde vykonal 101 pridanie a 22 odobranie
  1. 84 21
      LibGUI/GTableView.cpp
  2. 17 1
      LibGUI/GTableView.h

+ 84 - 21
LibGUI/GTableView.cpp

@@ -3,6 +3,7 @@
 #include <LibGUI/GScrollBar.h>
 #include <LibGUI/GPainter.h>
 #include <LibGUI/GTextBox.h>
+#include <LibGUI/GWindow.h>
 #include <Kernel/KeyCode.h>
 #include <AK/StringBuilder.h>
 
@@ -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);
+}

+ 17 - 1
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<bool> m_column_visibility;
+    struct ColumnData {
+        int width { 0 };
+        bool has_initialized_width { false };
+        bool visibility { true };
+    };
+    ColumnData& column_data(int column) const;
+
+    mutable Vector<ColumnData> 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 };
 };