1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 14:28:12 +00:00

LibGUI: Add (optional) row headers to GUI::TableView

You can now get row headers in your TableView by simply calling:

    table_view.row_header().set_visible(true)

Note that rows are not yet resizable.
This commit is contained in:
Andreas Kling 2020-08-26 15:54:03 +02:00
parent 49a5038a1a
commit 447b65bf7b
5 changed files with 92 additions and 18 deletions

View file

@ -42,6 +42,9 @@ AbstractTableView::AbstractTableView()
{ {
m_column_header = add<HeaderView>(*this, Gfx::Orientation::Horizontal); m_column_header = add<HeaderView>(*this, Gfx::Orientation::Horizontal);
m_column_header->move_to_back(); m_column_header->move_to_back();
m_row_header = add<HeaderView>(*this, Gfx::Orientation::Vertical);
m_row_header->move_to_back();
m_row_header->set_visible(false);
set_should_hide_unnecessary_scrollbars(true); set_should_hide_unnecessary_scrollbars(true);
} }
@ -90,6 +93,21 @@ void AbstractTableView::update_column_sizes()
} }
} }
void AbstractTableView::update_row_sizes()
{
if (!model())
return;
auto& model = *this->model();
int row_count = model.row_count();
for (int row = 0; row < row_count; ++row) {
if (!column_header().is_section_visible(row))
continue;
row_header().set_section_size(row, item_height());
}
}
void AbstractTableView::update_content_size() void AbstractTableView::update_content_size()
{ {
if (!model()) if (!model())
@ -105,8 +123,8 @@ void AbstractTableView::update_content_size()
int content_height = item_count() * item_height(); int content_height = item_count() * item_height();
set_content_size({ content_width, content_height }); set_content_size({ content_width, content_height });
column_header().set_width(content_width); set_size_occupied_by_fixed_elements({ row_header().width(), column_header().height() });
set_size_occupied_by_fixed_elements({ 0, m_column_header->height() }); layout_headers();
} }
TableCellPaintingDelegate* AbstractTableView::column_painting_delegate(int column) const TableCellPaintingDelegate* AbstractTableView::column_painting_delegate(int column) const
@ -278,7 +296,7 @@ Gfx::IntRect AbstractTableView::content_rect(const ModelIndex& index) const
Gfx::IntRect AbstractTableView::row_rect(int item_index) const Gfx::IntRect AbstractTableView::row_rect(int item_index) const
{ {
return { 0, m_column_header->height() + (item_index * item_height()), max(content_size().width(), width()), item_height() }; return { row_header().is_visible() ? row_header().width() : 0 , column_header().height() + (item_index * item_height()), max(content_size().width(), width()), item_height() };
} }
Gfx::IntPoint AbstractTableView::adjusted_position(const Gfx::IntPoint& position) const Gfx::IntPoint AbstractTableView::adjusted_position(const Gfx::IntPoint& position) const
@ -289,6 +307,7 @@ Gfx::IntPoint AbstractTableView::adjusted_position(const Gfx::IntPoint& position
void AbstractTableView::did_update_model(unsigned flags) void AbstractTableView::did_update_model(unsigned flags)
{ {
AbstractView::did_update_model(flags); AbstractView::did_update_model(flags);
update_row_sizes();
update_column_sizes(); update_column_sizes();
update_content_size(); update_content_size();
update(); update();
@ -297,9 +316,7 @@ void AbstractTableView::did_update_model(unsigned flags)
void AbstractTableView::resize_event(ResizeEvent& event) void AbstractTableView::resize_event(ResizeEvent& event)
{ {
AbstractView::resize_event(event); AbstractView::resize_event(event);
layout_headers();
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) void AbstractTableView::header_did_change_section_size(Badge<HeaderView>, Gfx::Orientation, int, int)
@ -327,7 +344,22 @@ void AbstractTableView::set_column_headers_visible(bool visible)
void AbstractTableView::did_scroll() void AbstractTableView::did_scroll()
{ {
AbstractView::did_scroll(); AbstractView::did_scroll();
column_header().set_x(frame_thickness() + -horizontal_scrollbar().value()); layout_headers();
}
void AbstractTableView::layout_headers()
{
if (column_header().is_visible()) {
int x = frame_thickness() + (row_header().is_visible() ? row_header().width() : 0) + -horizontal_scrollbar().value();
int y = frame_thickness();
column_header().set_relative_rect(x, y, content_width(), column_header().preferred_size().height());
}
if (row_header().is_visible()) {
int x = frame_thickness();
int y = (frame_thickness() + (column_header().is_visible() ? column_header().height() : 0)) + -vertical_scrollbar().value();
row_header().set_relative_rect(x, y, row_header().preferred_size().width(), content_height());
}
} }
} }

View file

@ -82,6 +82,12 @@ public:
virtual void did_scroll() override; virtual void did_scroll() override;
HeaderView& column_header() { return *m_column_header; }
const HeaderView& column_header() const { return *m_column_header; }
HeaderView& row_header() { return *m_row_header; }
const HeaderView& row_header() const { return *m_row_header; }
protected: protected:
virtual ~AbstractTableView() override; virtual ~AbstractTableView() override;
AbstractTableView(); AbstractTableView();
@ -96,15 +102,16 @@ protected:
void update_content_size(); void update_content_size();
virtual void update_column_sizes(); virtual void update_column_sizes();
virtual void update_row_sizes();
virtual int item_count() const; virtual int item_count() const;
TableCellPaintingDelegate* column_painting_delegate(int column) 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: private:
void layout_headers();
RefPtr<HeaderView> m_column_header; RefPtr<HeaderView> m_column_header;
RefPtr<HeaderView> m_row_header;
HashMap<int, OwnPtr<TableCellPaintingDelegate>> m_column_painting_delegate; HashMap<int, OwnPtr<TableCellPaintingDelegate>> m_column_painting_delegate;

View file

@ -221,11 +221,8 @@ void HeaderView::mouseup_event(MouseEvent& event)
} }
} }
void HeaderView::paint_event(PaintEvent& event) void HeaderView::paint_horizontal(Painter& painter)
{ {
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, 0 }, { rect().right(), 0 }, palette().threed_highlight());
painter.draw_line({ 0, rect().bottom() }, { rect().right(), rect().bottom() }, palette().threed_shadow1()); painter.draw_line({ 0, rect().bottom() }, { rect().right(), rect().bottom() }, palette().threed_shadow1());
int x_offset = 0; int x_offset = 0;
@ -259,6 +256,40 @@ void HeaderView::paint_event(PaintEvent& event)
} }
} }
void HeaderView::paint_vertical(Painter& painter)
{
painter.draw_line(rect().top_left(), rect().bottom_left(), palette().threed_highlight());
painter.draw_line(rect().top_right(), rect().bottom_right(), palette().threed_shadow1());
int y_offset = 0;
int section_count = this->section_count();
for (int section = 0; section < section_count; ++section) {
if (!is_section_visible(section))
continue;
int section_size = this->section_size(section);
Gfx::IntRect cell_rect(0, y_offset, width(), section_size);
bool pressed = section == m_pressed_section && m_pressed_section_is_pressed;
bool hovered = false;
Gfx::StylePainter::paint_button(painter, cell_rect, palette(), Gfx::ButtonStyle::Normal, pressed, hovered);
String text = String::format("%d", 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());
y_offset += section_size;
}
}
void HeaderView::paint_event(PaintEvent& event)
{
Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.fill_rect(rect(), palette().button());
if (orientation() == Gfx::Orientation::Horizontal)
paint_horizontal(painter);
else
paint_vertical(painter);
}
void HeaderView::set_section_visible(int section, bool visible) void HeaderView::set_section_visible(int section, bool visible)
{ {
auto& data = section_data(section); auto& data = section_data(section);

View file

@ -68,6 +68,9 @@ private:
Gfx::IntRect section_resize_grabbable_rect(int) const; Gfx::IntRect section_resize_grabbable_rect(int) const;
void paint_horizontal(Painter&);
void paint_vertical(Painter&);
Menu& ensure_context_menu(); Menu& ensure_context_menu();
RefPtr<Menu> m_context_menu; RefPtr<Menu> m_context_menu;

View file

@ -66,7 +66,8 @@ void TableView::paint_event(PaintEvent& event)
return; return;
int exposed_width = max(content_size().width(), width()); int exposed_width = max(content_size().width(), width());
int y_offset = column_header().height(); int x_offset = row_header().is_visible() ? row_header().width() : 0;
int y_offset = column_header().is_visible() ? column_header().height() : 0;
bool dummy; bool dummy;
int first_visible_row = index_at_event_position(frame_inner_rect().top_left(), dummy).row(); int first_visible_row = index_at_event_position(frame_inner_rect().top_left(), dummy).row();
@ -99,13 +100,13 @@ void TableView::paint_event(PaintEvent& event)
} }
painter.fill_rect(row_rect(painted_item_index), background_color); painter.fill_rect(row_rect(painted_item_index), background_color);
int x_offset = 0; int x = x_offset;
for (int column_index = 0; column_index < model()->column_count(); ++column_index) { for (int column_index = 0; column_index < model()->column_count(); ++column_index) {
if (!column_header().is_section_visible(column_index)) if (!column_header().is_section_visible(column_index))
continue; continue;
int column_width = this->column_width(column_index); int column_width = this->column_width(column_index);
bool is_key_column = m_key_column == column_index; bool is_key_column = m_key_column == column_index;
Gfx::IntRect cell_rect(horizontal_padding() + x_offset, y, column_width, item_height()); Gfx::IntRect cell_rect(horizontal_padding() + x, y, column_width, item_height());
auto cell_rect_for_fill = cell_rect.inflated(horizontal_padding() * 2, 0); auto cell_rect_for_fill = cell_rect.inflated(horizontal_padding() * 2, 0);
if (is_key_column) if (is_key_column)
painter.fill_rect(cell_rect_for_fill, key_column_background_color); painter.fill_rect(cell_rect_for_fill, key_column_background_color);
@ -139,7 +140,7 @@ void TableView::paint_event(PaintEvent& event)
painter.draw_text(cell_rect, data.to_string(), font_for_index(cell_index), text_alignment, text_color, Gfx::TextElision::Right); painter.draw_text(cell_rect, data.to_string(), font_for_index(cell_index), text_alignment, text_color, Gfx::TextElision::Right);
} }
} }
x_offset += column_width + horizontal_padding() * 2; x += column_width + horizontal_padding() * 2;
} }
++painted_item_index; ++painted_item_index;
}; };