diff options
-rw-r--r-- | Applications/IRCClient/IRCAppWindow.cpp | 2 | ||||
-rw-r--r-- | Applications/IRCClient/IRCWindow.cpp | 2 | ||||
-rw-r--r-- | Applications/Spreadsheet/SpreadsheetWidget.cpp | 1 | ||||
-rw-r--r-- | Applications/SystemMonitor/main.cpp | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/Locator.cpp | 2 | ||||
-rw-r--r-- | DevTools/Profiler/main.cpp | 2 | ||||
-rw-r--r-- | DevTools/VisualBuilder/VBPropertiesWindow.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibGUI/AbstractTableView.cpp | 334 | ||||
-rw-r--r-- | Libraries/LibGUI/AbstractTableView.h | 60 | ||||
-rw-r--r-- | Libraries/LibGUI/AbstractView.h | 7 | ||||
-rw-r--r-- | Libraries/LibGUI/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibGUI/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibGUI/HeaderView.cpp | 342 | ||||
-rw-r--r-- | Libraries/LibGUI/HeaderView.h | 100 | ||||
-rw-r--r-- | Libraries/LibGUI/TableView.cpp | 14 | ||||
-rw-r--r-- | Libraries/LibGUI/TreeView.cpp | 34 |
16 files changed, 555 insertions, 351 deletions
diff --git a/Applications/IRCClient/IRCAppWindow.cpp b/Applications/IRCClient/IRCAppWindow.cpp index 9b447f0b33..5e731be250 100644 --- a/Applications/IRCClient/IRCAppWindow.cpp +++ b/Applications/IRCClient/IRCAppWindow.cpp @@ -330,7 +330,7 @@ void IRCAppWindow::setup_widgets() auto& horizontal_container = outer_container.add<GUI::HorizontalSplitter>(); m_window_list = horizontal_container.add<GUI::TableView>(); - m_window_list->set_headers_visible(false); + m_window_list->set_column_headers_visible(false); m_window_list->set_alternating_row_colors(false); m_window_list->set_model(m_client->client_window_list_model()); m_window_list->set_activates_on_selection(true); diff --git a/Applications/IRCClient/IRCWindow.cpp b/Applications/IRCClient/IRCWindow.cpp index 60e2ab839e..3a9fe5b48f 100644 --- a/Applications/IRCClient/IRCWindow.cpp +++ b/Applications/IRCClient/IRCWindow.cpp @@ -56,7 +56,7 @@ IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& na if (m_type == Channel) { auto& member_view = container.add<GUI::TableView>(); - member_view.set_headers_visible(false); + member_view.set_column_headers_visible(false); member_view.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); member_view.set_preferred_size(100, 0); member_view.set_alternating_row_colors(false); diff --git a/Applications/Spreadsheet/SpreadsheetWidget.cpp b/Applications/Spreadsheet/SpreadsheetWidget.cpp index 52eeadb50c..777062c9e1 100644 --- a/Applications/Spreadsheet/SpreadsheetWidget.cpp +++ b/Applications/Spreadsheet/SpreadsheetWidget.cpp @@ -123,7 +123,6 @@ void SpreadsheetWidget::setup_tabs() m_current_cell_label->set_enabled(false); m_current_cell_label->set_text(""); }; - m_selected_view->set_focus(true); }; if (first_tab_widget) diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp index 804891f150..f75a5a9d7e 100644 --- a/Applications/SystemMonitor/main.cpp +++ b/Applications/SystemMonitor/main.cpp @@ -180,7 +180,7 @@ int main(int argc, char** argv) process_table_container.layout()->set_spacing(0); auto& process_table_view = process_table_container.add<GUI::TableView>(); - process_table_view.set_headers_visible(true); + process_table_view.set_column_headers_visible(true); process_table_view.set_model(GUI::SortingProxyModel::create(ProcessModel::create())); process_table_view.set_key_column_and_sort_order(ProcessModel::Column::CPU, GUI::SortOrder::Descending); process_table_view.model()->update(); diff --git a/DevTools/HackStudio/Locator.cpp b/DevTools/HackStudio/Locator.cpp index dd2c814452..b3bd2a49b8 100644 --- a/DevTools/HackStudio/Locator.cpp +++ b/DevTools/HackStudio/Locator.cpp @@ -130,7 +130,7 @@ Locator::Locator() m_popup_window->set_rect(0, 0, 500, 200); m_suggestion_view = m_popup_window->set_main_widget<GUI::TableView>(); - m_suggestion_view->set_headers_visible(false); + m_suggestion_view->set_column_headers_visible(false); m_suggestion_view->on_activation = [this](auto& index) { open_suggestion(index); diff --git a/DevTools/Profiler/main.cpp b/DevTools/Profiler/main.cpp index 8412843f20..daccbb9798 100644 --- a/DevTools/Profiler/main.cpp +++ b/DevTools/Profiler/main.cpp @@ -92,7 +92,7 @@ int main(int argc, char** argv) auto& bottom_splitter = main_widget.add<GUI::VerticalSplitter>(); auto& tree_view = bottom_splitter.add<GUI::TreeView>(); - tree_view.set_headers_visible(true); + tree_view.set_column_headers_visible(true); tree_view.set_model(profile->model()); auto& disassembly_view = bottom_splitter.add<GUI::TableView>(); diff --git a/DevTools/VisualBuilder/VBPropertiesWindow.cpp b/DevTools/VisualBuilder/VBPropertiesWindow.cpp index 3f3117e3c8..445183c6df 100644 --- a/DevTools/VisualBuilder/VBPropertiesWindow.cpp +++ b/DevTools/VisualBuilder/VBPropertiesWindow.cpp @@ -87,7 +87,7 @@ VBPropertiesWindow::VBPropertiesWindow() widget.layout()->set_margins({ 2, 2, 2, 2 }); m_table_view = widget.add<GUI::TableView>(); - m_table_view->set_headers_visible(false); + m_table_view->set_column_headers_visible(false); m_table_view->set_editable(true); m_table_view->aid_create_editing_delegate = [this](auto& index) -> OwnPtr<GUI::ModelEditingDelegate> { diff --git a/Libraries/LibGUI/AbstractTableView.cpp b/Libraries/LibGUI/AbstractTableView.cpp index f312cc7d28..eceffc57ab 100644 --- a/Libraries/LibGUI/AbstractTableView.cpp +++ b/Libraries/LibGUI/AbstractTableView.cpp @@ -28,6 +28,7 @@ #include <AK/Vector.h> #include <LibGUI/AbstractTableView.h> #include <LibGUI/Action.h> +#include <LibGUI/HeaderView.h> #include <LibGUI/Menu.h> #include <LibGUI/Model.h> #include <LibGUI/Painter.h> @@ -37,10 +38,10 @@ namespace GUI { -static const int minimum_column_width = 2; - AbstractTableView::AbstractTableView() { + m_column_header = add<HeaderView>(*this, Gfx::Orientation::Horizontal); + m_column_header->move_to_back(); set_should_hide_unnecessary_scrollbars(true); } @@ -67,9 +68,9 @@ void AbstractTableView::update_column_sizes() int row_count = model.row_count(); for (int column = 0; column < column_count; ++column) { - if (is_column_hidden(column)) + if (!column_header().is_section_visible(column)) continue; - int header_width = header_font().width(model.column_name(column)); + 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"); // UPWARDS BLACK ARROW int column_width = header_width; @@ -85,9 +86,7 @@ void AbstractTableView::update_column_sizes() } column_width = max(column_width, cell_width); } - auto& column_data = this->column_data(column); - column_data.width = max(column_data.width, column_width); - column_data.has_initialized_width = true; + column_header().set_section_size(column, max(m_column_header->section_size(column), column_width)); } } @@ -98,264 +97,54 @@ void AbstractTableView::update_content_size() int content_width = 0; int column_count = model()->column_count(); + for (int i = 0; i < column_count; ++i) { - if (!is_column_hidden(i)) + if (column_header().is_section_visible(i)) content_width += column_width(i) + horizontal_padding() * 2; } int content_height = item_count() * item_height(); set_content_size({ content_width, content_height }); - set_size_occupied_by_fixed_elements({ 0, header_height() }); -} - -Gfx::IntRect AbstractTableView::header_rect(int column_index) const -{ - if (!model()) - return {}; - if (is_column_hidden(column_index)) - return {}; - int x_offset = 0; - for (int i = 0; i < column_index; ++i) { - if (is_column_hidden(i)) - continue; - x_offset += column_width(i) + horizontal_padding() * 2; - } - return { x_offset, 0, column_width(column_index) + horizontal_padding() * 2, header_height() }; -} - -void AbstractTableView::set_hovered_header_index(int index) -{ - if (m_hovered_column_header_index == index) - return; - m_hovered_column_header_index = index; - update_headers(); -} - -void AbstractTableView::paint_headers(Painter& painter) -{ - if (!headers_visible()) - return; - int exposed_width = max(content_size().width(), width()); - painter.fill_rect({ 0, 0, exposed_width, header_height() }, palette().button()); - painter.draw_line({ 0, 0 }, { exposed_width - 1, 0 }, palette().threed_highlight()); - painter.draw_line({ 0, header_height() - 1 }, { exposed_width - 1, header_height() - 1 }, palette().threed_shadow1()); - int x_offset = 0; - int column_count = model()->column_count(); - for (int column_index = 0; column_index < column_count; ++column_index) { - if (is_column_hidden(column_index)) - continue; - int column_width = this->column_width(column_index); - bool is_key_column = m_key_column == column_index; - Gfx::IntRect cell_rect(x_offset, 0, column_width + horizontal_padding() * 2, header_height()); - bool pressed = column_index == m_pressed_column_header_index && m_pressed_column_header_is_pressed; - bool hovered = column_index == m_hovered_column_header_index && model()->is_column_sortable(column_index); - Gfx::StylePainter::paint_button(painter, cell_rect, palette(), Gfx::ButtonStyle::Normal, pressed, hovered); - String text; - if (is_key_column) { - StringBuilder builder; - builder.append(model()->column_name(column_index)); - if (m_sort_order == SortOrder::Ascending) - builder.append(" \xE2\xAC\x86"); // UPWARDS BLACK ARROW - else if (m_sort_order == SortOrder::Descending) - builder.append(" \xE2\xAC\x87"); // DOWNWARDS BLACK ARROW - text = builder.to_string(); - } else { - text = model()->column_name(column_index); - } - auto text_rect = cell_rect.shrunken(horizontal_padding() * 2, 0); - if (pressed) - text_rect.move_by(1, 1); - painter.draw_text(text_rect, text, header_font(), column_header_alignment(column_index), palette().button_text()); - x_offset += column_width + horizontal_padding() * 2; - } + column_header().set_width(content_width); + set_size_occupied_by_fixed_elements({ 0, m_column_header->height() }); } -bool AbstractTableView::is_column_hidden(int column) const +TableCellPaintingDelegate* AbstractTableView::column_painting_delegate(int column) const { - return !column_data(column).visibility; + // FIXME: This should return a const pointer I think.. + return const_cast<TableCellPaintingDelegate*>(m_column_painting_delegate.get(column).value_or(nullptr)); } -void AbstractTableView::set_column_hidden(int column, bool hidden) +void AbstractTableView::set_cell_painting_delegate(int column, OwnPtr<TableCellPaintingDelegate> delegate) { - auto& column_data = this->column_data(column); - if (column_data.visibility == !hidden) - return; - column_data.visibility = !hidden; - if (column_data.visibility_action) { - column_data.visibility_action->set_checked(!hidden); - } - update_content_size(); - update(); -} - -Menu& AbstractTableView::ensure_header_context_menu() -{ - // FIXME: This menu needs to be rebuilt if the model is swapped out, - // or if the column count/names change. - if (!m_header_context_menu) { - ASSERT(model()); - m_header_context_menu = Menu::construct(); - - for (int column = 0; column < model()->column_count(); ++column) { - auto& column_data = this->column_data(column); - auto name = model()->column_name(column); - column_data.visibility_action = Action::create_checkable(name, [this, column](auto& action) { - set_column_hidden(column, !action.is_checked()); - }); - column_data.visibility_action->set_checked(column_data.visibility); - - m_header_context_menu->add_action(*column_data.visibility_action); - } - } - return *m_header_context_menu; -} - -const Gfx::Font& AbstractTableView::header_font() -{ - return Gfx::Font::default_bold_font(); -} - -void AbstractTableView::set_cell_painting_delegate(int column, OwnPtr<TableCellPaintingDelegate>&& delegate) -{ - column_data(column).cell_painting_delegate = move(delegate); -} - -void AbstractTableView::update_headers() -{ - Gfx::IntRect rect { 0, 0, frame_inner_rect().width(), header_height() }; - rect.move_by(frame_thickness(), frame_thickness()); - update(rect); -} - -AbstractTableView::ColumnData& AbstractTableView::column_data(int column) const -{ - if (static_cast<size_t>(column) >= m_column_data.size()) - m_column_data.resize(column + 1); - return m_column_data.at(column); -} - -Gfx::IntRect AbstractTableView::column_resize_grabbable_rect(int column) const -{ - if (!model()) - return {}; - auto header_rect = this->header_rect(column); - return { header_rect.right() - 1, header_rect.top(), 4, header_rect.height() }; + if (!delegate) + m_column_painting_delegate.remove(column); + else + m_column_painting_delegate.set(column, move(delegate)); } int AbstractTableView::column_width(int column_index) const { if (!model()) return 0; - return column_data(column_index).width; + return m_column_header->section_size(column_index); } void AbstractTableView::set_column_width(int column, int width) { - column_data(column).width = width; + column_header().set_section_size(column, width); } Gfx::TextAlignment AbstractTableView::column_header_alignment(int column_index) const { if (!model()) return Gfx::TextAlignment::CenterLeft; - return column_data(column_index).header_alignment; + return m_column_header->section_alignment(column_index); } void AbstractTableView::set_column_header_alignment(int column, Gfx::TextAlignment alignment) { - column_data(column).header_alignment = alignment; -} - -void AbstractTableView::mousemove_event(MouseEvent& event) -{ - if (!model()) - return AbstractView::mousemove_event(event); - - auto adjusted_position = this->adjusted_position(event.position()); - Gfx::IntPoint horizontally_adjusted_position(adjusted_position.x(), event.position().y()); - - if (m_in_column_resize) { - auto delta = adjusted_position - m_column_resize_origin; - int new_width = m_column_resize_original_width + delta.x(); - if (new_width <= minimum_column_width) - new_width = minimum_column_width; - ASSERT(m_resizing_column >= 0 && m_resizing_column < model()->column_count()); - auto& column_data = this->column_data(m_resizing_column); - if (column_data.width != new_width) { - column_data.width = new_width; - update_content_size(); - update(); - } - return; - } - - if (m_pressed_column_header_index != -1) { - auto header_rect = this->header_rect(m_pressed_column_header_index); - if (header_rect.contains(horizontally_adjusted_position)) { - set_hovered_header_index(m_pressed_column_header_index); - if (!m_pressed_column_header_is_pressed) - update_headers(); - m_pressed_column_header_is_pressed = true; - } else { - set_hovered_header_index(-1); - if (m_pressed_column_header_is_pressed) - update_headers(); - m_pressed_column_header_is_pressed = false; - } - return; - } - - if (event.buttons() == 0) { - int column_count = model()->column_count(); - bool found_hovered_header = false; - for (int i = 0; i < column_count; ++i) { - if (column_resize_grabbable_rect(i).contains(horizontally_adjusted_position)) { - window()->set_override_cursor(StandardCursor::ResizeColumn); - set_hovered_header_index(-1); - return; - } - if (header_rect(i).contains(horizontally_adjusted_position)) { - set_hovered_header_index(i); - found_hovered_header = true; - } - } - if (!found_hovered_header) - set_hovered_header_index(-1); - } - window()->set_override_cursor(StandardCursor::None); - - AbstractView::mousemove_event(event); -} - -void AbstractTableView::mouseup_event(MouseEvent& event) -{ - auto adjusted_position = this->adjusted_position(event.position()); - Gfx::IntPoint horizontally_adjusted_position(adjusted_position.x(), event.position().y()); - if (event.button() == MouseButton::Left) { - if (m_in_column_resize) { - if (!column_resize_grabbable_rect(m_resizing_column).contains(horizontally_adjusted_position)) - window()->set_override_cursor(StandardCursor::None); - m_in_column_resize = false; - return; - } - if (m_pressed_column_header_index != -1) { - auto header_rect = this->header_rect(m_pressed_column_header_index); - if (header_rect.contains(horizontally_adjusted_position)) { - auto new_sort_order = SortOrder::Ascending; - if (m_key_column == m_pressed_column_header_index) - new_sort_order = m_sort_order == SortOrder::Ascending - ? SortOrder::Descending - : SortOrder::Ascending; - set_key_column_and_sort_order(m_pressed_column_header_index, new_sort_order); - } - m_pressed_column_header_index = -1; - m_pressed_column_header_is_pressed = false; - update_headers(); - return; - } - } - - AbstractView::mouseup_event(event); + column_header().set_section_alignment(column, alignment); } void AbstractTableView::mousedown_event(MouseEvent& event) @@ -369,27 +158,6 @@ void AbstractTableView::mousedown_event(MouseEvent& event) auto adjusted_position = this->adjusted_position(event.position()); Gfx::IntPoint horizontally_adjusted_position(adjusted_position.x(), event.position().y()); - if (event.y() < header_height()) { - int column_count = model()->column_count(); - for (int i = 0; i < column_count; ++i) { - if (column_resize_grabbable_rect(i).contains(horizontally_adjusted_position)) { - m_resizing_column = i; - m_in_column_resize = true; - m_column_resize_original_width = column_width(i); - m_column_resize_origin = adjusted_position; - return; - } - auto header_rect = this->header_rect(i); - if (header_rect.contains(horizontally_adjusted_position) && model()->is_column_sortable(i)) { - m_pressed_column_header_index = i; - m_pressed_column_header_is_pressed = true; - update_headers(); - return; - } - } - return; - } - bool is_toggle; auto index = index_at_event_position(event.position(), is_toggle); @@ -456,13 +224,13 @@ void AbstractTableView::move_selection(int vertical_steps, int horizontal_steps) void AbstractTableView::scroll_into_view(const ModelIndex& index, Orientation orientation) { - auto rect = row_rect(index.row()).translated(0, -header_height()); + auto rect = row_rect(index.row()).translated(0, -m_column_header->height()); ScrollableWidget::scroll_into_view(rect, orientation); } void AbstractTableView::scroll_into_view(const ModelIndex& index, bool scroll_horizontally, bool scroll_vertically) { - auto rect = row_rect(index.row()).translated(0, -header_height()); + auto rect = row_rect(index.row()).translated(0, -m_column_header->height()); ScrollableWidget::scroll_into_view(rect, scroll_horizontally, scroll_vertically); } @@ -471,11 +239,8 @@ void AbstractTableView::doubleclick_event(MouseEvent& event) if (!model()) return; if (event.button() == MouseButton::Left) { - if (event.y() < header_height()) - return; - if (!selection().is_empty()) { + if (!selection().is_empty()) activate_or_edit_selected(); - } } } @@ -483,10 +248,6 @@ void AbstractTableView::context_menu_event(ContextMenuEvent& event) { if (!model()) return; - if (event.position().y() < header_height()) { - ensure_header_context_menu().popup(event.screen_position()); - return; - } bool is_toggle; auto index = index_at_event_position(event.position(), is_toggle); @@ -500,13 +261,6 @@ void AbstractTableView::context_menu_event(ContextMenuEvent& event) on_context_menu_request(index, event); } -void AbstractTableView::leave_event(Core::Event& event) -{ - AbstractView::leave_event(event); - window()->set_override_cursor(StandardCursor::None); - set_hovered_header_index(-1); -} - Gfx::IntRect AbstractTableView::content_rect(int row, int column) const { auto row_rect = this->row_rect(row); @@ -524,7 +278,7 @@ Gfx::IntRect AbstractTableView::content_rect(const ModelIndex& index) const Gfx::IntRect AbstractTableView::row_rect(int item_index) const { - return { 0, header_height() + (item_index * item_height()), max(content_size().width(), width()), item_height() }; + return { 0, m_column_header->height() + (item_index * item_height()), max(content_size().width(), width()), item_height() }; } Gfx::IntPoint AbstractTableView::adjusted_position(const Gfx::IntPoint& position) const @@ -540,4 +294,40 @@ void AbstractTableView::did_update_model(unsigned flags) update(); } +void AbstractTableView::resize_event(ResizeEvent& event) +{ + AbstractView::resize_event(event); + + if (column_header().is_visible()) + column_header().set_relative_rect(frame_thickness(), frame_thickness(), content_width(), column_header().preferred_size().height()); +} + +void AbstractTableView::header_did_change_section_size(Badge<HeaderView>, Gfx::Orientation, int, int) +{ + update_content_size(); + update(); +} + +void AbstractTableView::header_did_change_section_visibility(Badge<HeaderView>, Gfx::Orientation, int, bool) +{ + update_content_size(); + update(); +} + +void AbstractTableView::set_column_hidden(int column, bool hidden) +{ + column_header().set_section_visible(column, !hidden); +} + +void AbstractTableView::set_column_headers_visible(bool visible) +{ + column_header().set_visible(visible); +} + +void AbstractTableView::did_scroll() +{ + AbstractView::did_scroll(); + column_header().set_x(frame_thickness() + -horizontal_scrollbar().value()); +} + } diff --git a/Libraries/LibGUI/AbstractTableView.h b/Libraries/LibGUI/AbstractTableView.h index cfcbf7c9a7..aa1a20ccc2 100644 --- a/Libraries/LibGUI/AbstractTableView.h +++ b/Libraries/LibGUI/AbstractTableView.h @@ -46,12 +46,9 @@ public: bool highlight_selected_rows() const { return m_highlight_selected_rows; } void set_highlight_selected_rows(bool b) { m_highlight_selected_rows = b; } - int header_height() const { return m_headers_visible ? 16 : 0; } + bool column_headers_visible() const; + void set_column_headers_visible(bool); - bool headers_visible() const { return m_headers_visible; } - void set_headers_visible(bool headers_visible) { m_headers_visible = headers_visible; } - - bool is_column_hidden(int) const; void set_column_hidden(int, bool); int column_width(int column) const; @@ -60,7 +57,7 @@ public: Gfx::TextAlignment column_header_alignment(int column) const; void set_column_header_alignment(int column, Gfx::TextAlignment); - void set_cell_painting_delegate(int column, OwnPtr<TableCellPaintingDelegate>&&); + void set_cell_painting_delegate(int column, OwnPtr<TableCellPaintingDelegate>); int horizontal_padding() const { return m_horizontal_padding; } @@ -80,59 +77,40 @@ public: void move_selection(int vertical_steps, int horizontal_steps); + void header_did_change_section_visibility(Badge<HeaderView>, Gfx::Orientation, int section, bool visible); + void header_did_change_section_size(Badge<HeaderView>, Gfx::Orientation, int section, int size); + + virtual void did_scroll() override; + protected: virtual ~AbstractTableView() override; AbstractTableView(); - virtual void did_update_model(unsigned flags) override; - virtual void mouseup_event(MouseEvent&) override; virtual void mousedown_event(MouseEvent&) override; - virtual void mousemove_event(MouseEvent&) override; virtual void doubleclick_event(MouseEvent&) override; - virtual void leave_event(Core::Event&) override; virtual void context_menu_event(ContextMenuEvent&) override; + virtual void resize_event(ResizeEvent&) override; + virtual void did_update_model(unsigned flags) override; virtual void toggle_index(const ModelIndex&) { } - void paint_headers(Painter&); - Gfx::IntRect header_rect(int column) const; - - static const Gfx::Font& header_font(); - void update_headers(); - void set_hovered_header_index(int); - - struct ColumnData { - int width { 0 }; - bool has_initialized_width { false }; - bool visibility { true }; - RefPtr<Action> visibility_action; - Gfx::TextAlignment header_alignment { Gfx::TextAlignment::CenterLeft }; - OwnPtr<TableCellPaintingDelegate> cell_painting_delegate; - }; - ColumnData& column_data(int column) const; - - mutable Vector<ColumnData> m_column_data; - - Menu& ensure_header_context_menu(); - RefPtr<Menu> m_header_context_menu; - - Gfx::IntRect column_resize_grabbable_rect(int) const; void update_content_size(); virtual void update_column_sizes(); virtual int item_count() const; + TableCellPaintingDelegate* column_painting_delegate(int column) const; + + HeaderView& column_header() { return *m_column_header; } + const HeaderView& column_header() const { return *m_column_header; } + private: - bool m_headers_visible { true }; - bool m_in_column_resize { false }; + RefPtr<HeaderView> m_column_header; + + HashMap<int, OwnPtr<TableCellPaintingDelegate>> m_column_painting_delegate; + bool m_alternating_row_colors { true }; bool m_highlight_selected_rows { true }; int m_horizontal_padding { 5 }; - Gfx::IntPoint m_column_resize_origin; - int m_column_resize_original_width { 0 }; - int m_resizing_column { -1 }; - int m_pressed_column_header_index { -1 }; - bool m_pressed_column_header_is_pressed { false }; - int m_hovered_column_header_index { -1 }; }; } diff --git a/Libraries/LibGUI/AbstractView.h b/Libraries/LibGUI/AbstractView.h index 3fb219a721..a60e81346f 100644 --- a/Libraries/LibGUI/AbstractView.h +++ b/Libraries/LibGUI/AbstractView.h @@ -42,7 +42,7 @@ public: ModelSelection& selection() { return m_selection; } const ModelSelection& selection() const { return m_selection; } - virtual void select_all() = 0; + virtual void select_all() { } bool is_editable() const { return m_editable; } void set_editable(bool editable) { m_editable = editable; } @@ -55,7 +55,7 @@ public: virtual void did_update_selection(); virtual Gfx::IntRect content_rect(const ModelIndex&) const { return {}; } - virtual ModelIndex index_at_event_position(const Gfx::IntPoint&) const = 0; + virtual ModelIndex index_at_event_position(const Gfx::IntPoint&) const { return {}; } void begin_editing(const ModelIndex&); void stop_editing(); @@ -78,6 +78,9 @@ public: void set_key_column_and_sort_order(int column, SortOrder); + int key_column() const { return m_key_column; } + SortOrder sort_order() const { return m_sort_order; } + protected: AbstractView(); virtual ~AbstractView() override; diff --git a/Libraries/LibGUI/CMakeLists.txt b/Libraries/LibGUI/CMakeLists.txt index 3d3b9a5c57..ea801c392d 100644 --- a/Libraries/LibGUI/CMakeLists.txt +++ b/Libraries/LibGUI/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES FontDatabase.cpp Frame.cpp GroupBox.cpp + HeaderView.cpp INILexer.cpp INISyntaxHighlighter.cpp Icon.cpp diff --git a/Libraries/LibGUI/Forward.h b/Libraries/LibGUI/Forward.h index 610e211dd6..14d53a785c 100644 --- a/Libraries/LibGUI/Forward.h +++ b/Libraries/LibGUI/Forward.h @@ -43,6 +43,7 @@ class DropEvent; class FileSystemModel; class Frame; class GroupBox; +class HeaderView; class HorizontalBoxLayout; class HorizontalSlider; class Icon; diff --git a/Libraries/LibGUI/HeaderView.cpp b/Libraries/LibGUI/HeaderView.cpp new file mode 100644 index 0000000000..31caa71b5b --- /dev/null +++ b/Libraries/LibGUI/HeaderView.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <LibGUI/AbstractTableView.h> +#include <LibGUI/Action.h> +#include <LibGUI/HeaderView.h> +#include <LibGUI/Menu.h> +#include <LibGUI/Model.h> +#include <LibGUI/Painter.h> +#include <LibGUI/Window.h> +#include <LibGfx/Palette.h> +#include <LibGfx/StylePainter.h> + +namespace GUI { + +static constexpr int minimum_column_size = 2; + +HeaderView::HeaderView(AbstractTableView& table_view, Gfx::Orientation orientation) + : m_table_view(table_view) + , m_orientation(orientation) +{ + set_font(Gfx::Font::default_bold_font()); + + if (m_orientation == Gfx::Orientation::Horizontal) { + set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + set_preferred_size(0, 16); + } else { + set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); + set_preferred_size(40, 0); + } +} + +HeaderView::~HeaderView() +{ +} + +void HeaderView::set_section_size(int section, int size) +{ + auto& data = section_data(section); + data.size = size; + data.has_initialized_size = true; +} + +int HeaderView::section_size(int section) const +{ + return section_data(section).size; +} + +HeaderView::SectionData& HeaderView::section_data(int section) const +{ + if (static_cast<size_t>(section) >= m_section_data.size()) + m_section_data.resize(section + 1); + return m_section_data.at(section); +} + +Gfx::IntRect HeaderView::section_rect(int section) const +{ + if (!model()) + return {}; + auto& data = section_data(section); + if (!data.visibility) + return {}; + int offset = 0; + for (int i = 0; i < section; ++i) { + if (!is_section_visible(i)) + continue; + offset += section_data(i).size + horizontal_padding() * 2; + } + if (orientation() == Gfx::Orientation::Horizontal) + return { offset, 0, section_size(section) + horizontal_padding() * 2, height() }; + return { 0, offset, width(), section_size(section) }; +} + +Gfx::IntRect HeaderView::section_resize_grabbable_rect(int section) const +{ + if (!model()) + return {}; + auto rect = section_rect(section); + return { rect.right() - 1, rect.top(), 4, rect.height() }; +} + +int HeaderView::section_count() const +{ + if (!model()) + return 0; + return m_orientation == Gfx::Orientation::Horizontal ? model()->column_count() : model()->row_count(); +} + +void HeaderView::mousedown_event(MouseEvent& event) +{ + if (!model()) + return; + + auto& model = *this->model(); + int section_count = this->section_count(); + + for (int i = 0; i < section_count; ++i) { + if (section_resize_grabbable_rect(i).contains(event.position())) { + m_resizing_section = i; + m_in_section_resize = true; + m_section_resize_original_width = section_size(i); + m_section_resize_origin = event.position(); + return; + } + auto rect = this->section_rect(i); + if (rect.contains(event.position()) && model.is_column_sortable(i)) { + m_pressed_section = i; + m_pressed_section_is_pressed = true; + update(); + return; + } + } +} + +void HeaderView::mousemove_event(MouseEvent& event) +{ + if (!model()) + return; + + if (m_in_section_resize) { + auto delta = event.position() - m_section_resize_origin; + int new_size = m_section_resize_original_width + delta.primary_offset_for_orientation(m_orientation); + if (new_size <= minimum_column_size) + new_size = minimum_column_size; + ASSERT(m_resizing_section >= 0 && m_resizing_section < model()->column_count()); + auto& data = this->section_data(m_resizing_section); + if (data.size != new_size) { + data.size = new_size; + m_table_view.header_did_change_section_size({}, m_orientation, m_resizing_section, new_size); + update(); + } + return; + } + + if (m_pressed_section != -1) { + auto header_rect = this->section_rect(m_pressed_section); + if (header_rect.contains(event.position())) { + set_hovered_section(m_pressed_section); + if (!m_pressed_section_is_pressed) + update(); + m_pressed_section_is_pressed = true; + } else { + set_hovered_section(-1); + if (m_pressed_section_is_pressed) + update(); + m_pressed_section_is_pressed = false; + } + return; + } + + if (event.buttons() == 0) { + int section_count = this->section_count(); + bool found_hovered_header = false; + for (int i = 0; i < section_count; ++i) { + if (section_resize_grabbable_rect(i).contains(event.position())) { + window()->set_override_cursor(StandardCursor::ResizeColumn); + set_hovered_section(-1); + return; + } + if (section_rect(i).contains(event.position())) { + set_hovered_section(i); + found_hovered_header = true; + } + } + if (!found_hovered_header) + set_hovered_section(-1); + } + window()->set_override_cursor(StandardCursor::None); +} + +void HeaderView::mouseup_event(MouseEvent& event) +{ + Gfx::IntPoint horizontally_adjusted_position(event.x(), event.y()); + if (event.button() == MouseButton::Left) { + if (m_in_section_resize) { + if (!section_resize_grabbable_rect(m_resizing_section).contains(horizontally_adjusted_position)) + window()->set_override_cursor(StandardCursor::None); + m_in_section_resize = false; + return; + } + if (m_pressed_section != -1) { + auto header_rect = this->section_rect(m_pressed_section); + if (header_rect.contains(horizontally_adjusted_position)) { + auto new_sort_order = SortOrder::Ascending; + if (m_table_view.key_column() == m_pressed_section) + new_sort_order = m_table_view.sort_order() == SortOrder::Ascending + ? SortOrder::Descending + : SortOrder::Ascending; + m_table_view.set_key_column_and_sort_order(m_pressed_section, new_sort_order); + } + m_pressed_section = -1; + m_pressed_section_is_pressed = false; + update(); + return; + } + } +} + +void HeaderView::paint_event(PaintEvent& event) +{ + Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.fill_rect(rect(), palette().button()); + painter.draw_line({ 0, 0 }, { rect().right(), 0 }, palette().threed_highlight()); + painter.draw_line({ 0, rect().bottom() }, { rect().right(), rect().bottom() }, palette().threed_shadow1()); + int x_offset = 0; + int section_count = this->section_count(); + for (int section = 0; section < section_count; ++section) { + if (!is_section_visible(section)) + continue; + int section_width = section_size(section); + bool is_key_column = m_table_view.key_column() == section; + Gfx::IntRect cell_rect(x_offset, 0, section_width + horizontal_padding() * 2, height()); + bool pressed = section == m_pressed_section && m_pressed_section_is_pressed; + bool hovered = section == m_hovered_section && model()->is_column_sortable(section); + Gfx::StylePainter::paint_button(painter, cell_rect, palette(), Gfx::ButtonStyle::Normal, pressed, hovered); + String text; + if (is_key_column) { + StringBuilder builder; + builder.append(model()->column_name(section)); + if (m_table_view.sort_order() == SortOrder::Ascending) + builder.append(" \xE2\xAC\x86"); // UPWARDS BLACK ARROW + else if (m_table_view.sort_order() == SortOrder::Descending) + builder.append(" \xE2\xAC\x87"); // DOWNWARDS BLACK ARROW + text = builder.to_string(); + } else { + text = model()->column_name(section); + } + auto text_rect = cell_rect.shrunken(horizontal_padding() * 2, 0); + if (pressed) + text_rect.move_by(1, 1); + painter.draw_text(text_rect, text, font(), section_alignment(section), palette().button_text()); + x_offset += section_width + horizontal_padding() * 2; + } +} + +void HeaderView::set_section_visible(int section, bool visible) +{ + auto& data = section_data(section); + if (data.visibility == visible) + return; + data.visibility = visible; + if (data.visibility_action) { + data.visibility_action->set_checked(visible); + } + m_table_view.header_did_change_section_visibility({}, m_orientation, section, visible); + update(); +} + +Menu& HeaderView::ensure_context_menu() +{ + // FIXME: This menu needs to be rebuilt if the model is swapped out, + // or if the column count/names change. + if (!m_context_menu) { + ASSERT(model()); + m_context_menu = Menu::construct(); + + int section_count = this->section_count(); + for (int section = 0; section < section_count; ++section) { + auto& column_data = this->section_data(section); + // FIXME: Vertical support + ASSERT(m_orientation == Gfx::Orientation::Horizontal); + auto name = model()->column_name(section); + column_data.visibility_action = Action::create_checkable(name, [this, section](auto& action) { + set_section_visible(section, action.is_checked()); + }); + column_data.visibility_action->set_checked(column_data.visibility); + + m_context_menu->add_action(*column_data.visibility_action); + } + } + return *m_context_menu; +} + +void HeaderView::context_menu_event(ContextMenuEvent& event) +{ + ensure_context_menu().popup(event.screen_position()); +} + +void HeaderView::leave_event(Core::Event& event) +{ + Widget::leave_event(event); + window()->set_override_cursor(StandardCursor::None); +} + +Gfx::TextAlignment HeaderView::section_alignment(int section) const +{ + return section_data(section).alignment; +} + +void HeaderView::set_section_alignment(int section, Gfx::TextAlignment alignment) +{ + section_data(section).alignment = alignment; +} + +bool HeaderView::is_section_visible(int section) const +{ + return section_data(section).visibility; +} + +void HeaderView::set_hovered_section(int section) +{ + if (m_hovered_section == section) + return; + m_hovered_section = section; + update(); +} + +Model* HeaderView::model() +{ + return m_table_view.model(); +} + +const Model* HeaderView::model() const +{ + return m_table_view.model(); +} + +} diff --git a/Libraries/LibGUI/HeaderView.h b/Libraries/LibGUI/HeaderView.h new file mode 100644 index 0000000000..a4cb1d4a7c --- /dev/null +++ b/Libraries/LibGUI/HeaderView.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibGUI/Widget.h> +#include <LibGfx/Orientation.h> + +namespace GUI { + +class HeaderView final : public Widget { + C_OBJECT(HeaderView); + +public: + virtual ~HeaderView() override; + + Gfx::Orientation orientation() const { return m_orientation; } + + Model* model(); + const Model* model() const; + + void set_section_size(int section, int size); + int section_size(int section) const; + + Gfx::TextAlignment section_alignment(int section) const; + void set_section_alignment(int section, Gfx::TextAlignment); + + bool is_section_visible(int section) const; + void set_section_visible(int section, bool); + + int section_count() const; + Gfx::IntRect section_rect(int section) const; + +private: + HeaderView(AbstractTableView&, Gfx::Orientation); + + virtual void paint_event(PaintEvent&) override; + virtual void mousedown_event(MouseEvent&) override; + virtual void mousemove_event(MouseEvent&) override; + virtual void mouseup_event(MouseEvent&) override; + virtual void context_menu_event(ContextMenuEvent&) override; + virtual void leave_event(Core::Event&) override; + + int horizontal_padding() const { return 5; } + + Gfx::IntRect section_resize_grabbable_rect(int) const; + + Menu& ensure_context_menu(); + RefPtr<Menu> m_context_menu; + + AbstractTableView& m_table_view; + + Gfx::Orientation m_orientation { Gfx::Orientation::Horizontal }; + + struct SectionData { + int size { 0 }; + bool has_initialized_size { false }; + bool visibility { true }; + RefPtr<Action> visibility_action; + Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterLeft }; + }; + SectionData& section_data(int section) const; + + void set_hovered_section(int); + + mutable Vector<SectionData> m_section_data; + + bool m_in_section_resize { false }; + Gfx::IntPoint m_section_resize_origin; + int m_section_resize_original_width { 0 }; + int m_resizing_section { -1 }; + int m_pressed_section { -1 }; + bool m_pressed_section_is_pressed { false }; + int m_hovered_section { -1 }; +}; + +} diff --git a/Libraries/LibGUI/TableView.cpp b/Libraries/LibGUI/TableView.cpp index b30161e7aa..cd7aec09cb 100644 --- a/Libraries/LibGUI/TableView.cpp +++ b/Libraries/LibGUI/TableView.cpp @@ -26,6 +26,7 @@ #include <AK/StringBuilder.h> #include <LibGUI/Action.h> +#include <LibGUI/HeaderView.h> #include <LibGUI/Menu.h> #include <LibGUI/Model.h> #include <LibGUI/Painter.h> @@ -65,7 +66,7 @@ void TableView::paint_event(PaintEvent& event) return; int exposed_width = max(content_size().width(), width()); - int y_offset = header_height(); + int y_offset = column_header().height(); bool dummy; int first_visible_row = index_at_event_position(frame_inner_rect().top_left(), dummy).row(); @@ -100,7 +101,7 @@ void TableView::paint_event(PaintEvent& event) int x_offset = 0; for (int column_index = 0; column_index < model()->column_count(); ++column_index) { - if (is_column_hidden(column_index)) + if (!column_header().is_section_visible(column_index)) continue; int column_width = this->column_width(column_index); bool is_key_column = m_key_column == column_index; @@ -110,7 +111,7 @@ void TableView::paint_event(PaintEvent& event) painter.fill_rect(cell_rect_for_fill, key_column_background_color); auto cell_index = model()->index(row_index, column_index); - if (auto* delegate = column_data(column_index).cell_painting_delegate.ptr()) { + if (auto* delegate = column_painting_delegate(column_index)) { delegate->paint(painter, cell_rect, palette(), cell_index); } else { auto data = cell_index.data(); @@ -143,14 +144,9 @@ void TableView::paint_event(PaintEvent& event) ++painted_item_index; }; - Gfx::IntRect unpainted_rect(0, header_height() + painted_item_index * item_height(), exposed_width, height()); + Gfx::IntRect unpainted_rect(0, column_header().height() + painted_item_index * item_height(), exposed_width, height()); if (fill_with_background_color()) painter.fill_rect(unpainted_rect, widget_background_color); - - // Untranslate the painter vertically and do the column headers. - painter.translate(0, vertical_scrollbar().value()); - if (headers_visible()) - paint_headers(painter); } void TableView::keydown_event(KeyEvent& event) diff --git a/Libraries/LibGUI/TreeView.cpp b/Libraries/LibGUI/TreeView.cpp index d228ad7abf..76194c125c 100644 --- a/Libraries/LibGUI/TreeView.cpp +++ b/Libraries/LibGUI/TreeView.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <LibGUI/HeaderView.h> #include <LibGUI/Model.h> #include <LibGUI/Painter.h> #include <LibGUI/ScrollBar.h> @@ -56,7 +57,7 @@ TreeView::TreeView() set_fill_with_background_color(true); set_background_role(ColorRole::Base); set_foreground_role(ColorRole::BaseText); - set_headers_visible(false); + set_column_headers_visible(false); m_expand_bitmap = Gfx::Bitmap::load_from_file("/res/icons/treeview-expand.png"); m_collapse_bitmap = Gfx::Bitmap::load_from_file("/res/icons/treeview-collapse.png"); } @@ -67,7 +68,7 @@ TreeView::~TreeView() ModelIndex TreeView::index_at_event_position(const Gfx::IntPoint& a_position, bool& is_toggle) const { - auto position = a_position.translated(0, -header_height()).translated(horizontal_scrollbar().value() - frame_thickness(), vertical_scrollbar().value() - frame_thickness()); + auto position = a_position.translated(0, -column_header().height()).translated(horizontal_scrollbar().value() - frame_thickness(), vertical_scrollbar().value() - frame_thickness()); is_toggle = false; if (!model()) return {}; @@ -229,7 +230,7 @@ void TreeView::paint_event(PaintEvent& event) int tree_column = model.tree_column(); int tree_column_x_offset = this->tree_column_x_offset(); - int y_offset = header_height(); + int y_offset = column_header().height(); int painted_row_index = 0; @@ -267,7 +268,7 @@ void TreeView::paint_event(PaintEvent& event) int row_width = 0; for (int column_index = 0; column_index < model.column_count(); ++column_index) { - if (is_column_hidden(column_index)) + if (!column_header().is_section_visible(column_index)) continue; row_width += this->column_width(column_index) + horizontal_padding() * 2; } @@ -280,7 +281,7 @@ void TreeView::paint_event(PaintEvent& event) int x_offset = 0; for (int column_index = 0; column_index < model.column_count(); ++column_index) { - if (is_column_hidden(column_index)) + if (!column_header().is_section_visible(column_index)) continue; int column_width = this->column_width(column_index); @@ -290,7 +291,7 @@ void TreeView::paint_event(PaintEvent& event) Gfx::IntRect cell_rect(horizontal_padding() + x_offset, rect.y(), column_width, item_height()); auto cell_index = model.index(index.row(), column_index, index.parent()); - if (auto* delegate = column_data(column_index).cell_painting_delegate.ptr()) { + if (auto* delegate = column_painting_delegate(column_index)) { delegate->paint(painter, cell_rect, palette(), cell_index); } else { auto data = cell_index.data(); @@ -357,10 +358,6 @@ void TreeView::paint_event(PaintEvent& event) return IterationDecision::Continue; }); - - // Untranslate the painter vertically and do the column headers. - painter.translate(0, vertical_scrollbar().value()); - paint_headers(painter); } void TreeView::scroll_into_view(const ModelIndex& a_index, Orientation orientation) @@ -538,9 +535,9 @@ void TreeView::update_column_sizes() for (int column = 0; column < column_count; ++column) { if (column == tree_column) continue; - if (is_column_hidden(column)) + if (!column_header().is_section_visible(column)) continue; - int header_width = header_font().width(model.column_name(column)); + int header_width = column_header().font().width(model.column_name(column)); int column_width = header_width; for (int row = 0; row < row_count; ++row) { @@ -553,24 +550,21 @@ void TreeView::update_column_sizes() } column_width = max(column_width, cell_width); } - auto& column_data = this->column_data(column); - column_data.width = max(column_data.width, column_width); - column_data.has_initialized_width = true; + + set_column_width(column, max(this->column_width(column), column_width)); if (column < tree_column) tree_column_x_offset += column_width; } - int tree_column_header_width = header_font().width(model.column_name(tree_column)); + int tree_column_header_width = column_header().font().width(model.column_name(tree_column)); int tree_column_width = tree_column_header_width; traverse_in_paint_order([&](const ModelIndex&, const Gfx::IntRect& rect, const Gfx::IntRect&, int) { tree_column_width = max(rect.right() - tree_column_x_offset, tree_column_width); return IterationDecision::Continue; }); - auto& column_data = this->column_data(tree_column); - column_data.width = max(column_data.width, tree_column_width); - column_data.has_initialized_width = true; + set_column_width(tree_column, max(this->column_width(tree_column), tree_column_width)); } int TreeView::tree_column_x_offset() const @@ -578,7 +572,7 @@ int TreeView::tree_column_x_offset() const int tree_column = model()->tree_column(); int offset = 0; for (int i = 0; i < tree_column; ++i) { - if (!is_column_hidden(i)) { + if (column_header().is_section_visible(i)) { offset += column_width(i); offset += horizontal_padding(); } |