summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Applications/IRCClient/IRCAppWindow.cpp2
-rw-r--r--Applications/IRCClient/IRCWindow.cpp2
-rw-r--r--Applications/Spreadsheet/SpreadsheetWidget.cpp1
-rw-r--r--Applications/SystemMonitor/main.cpp2
-rw-r--r--DevTools/HackStudio/Locator.cpp2
-rw-r--r--DevTools/Profiler/main.cpp2
-rw-r--r--DevTools/VisualBuilder/VBPropertiesWindow.cpp2
-rw-r--r--Libraries/LibGUI/AbstractTableView.cpp334
-rw-r--r--Libraries/LibGUI/AbstractTableView.h60
-rw-r--r--Libraries/LibGUI/AbstractView.h7
-rw-r--r--Libraries/LibGUI/CMakeLists.txt1
-rw-r--r--Libraries/LibGUI/Forward.h1
-rw-r--r--Libraries/LibGUI/HeaderView.cpp342
-rw-r--r--Libraries/LibGUI/HeaderView.h100
-rw-r--r--Libraries/LibGUI/TableView.cpp14
-rw-r--r--Libraries/LibGUI/TreeView.cpp34
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();
}