mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 00:57:43 +00:00
LibGUI: Support double-click resizing column headers
Columns can now be best-fit resized by double-clicking their grabbable edges. When a default width is set and all data is empty, double-clicking will restore the column to its original state.
This commit is contained in:
parent
663fd9abb4
commit
3cc7862487
4 changed files with 93 additions and 0 deletions
|
@ -48,6 +48,9 @@ AbstractTableView::AbstractTableView()
|
||||||
m_corner_button->set_fill_with_background_color(true);
|
m_corner_button->set_fill_with_background_color(true);
|
||||||
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_column_header->on_resize_doubleclick = [this](auto column) {
|
||||||
|
auto_resize_column(column);
|
||||||
|
};
|
||||||
m_row_header = add<HeaderView>(*this, Gfx::Orientation::Vertical);
|
m_row_header = add<HeaderView>(*this, Gfx::Orientation::Vertical);
|
||||||
m_row_header->move_to_back();
|
m_row_header->move_to_back();
|
||||||
m_row_header->set_visible(false);
|
m_row_header->set_visible(false);
|
||||||
|
@ -67,6 +70,44 @@ void AbstractTableView::select_all()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractTableView::auto_resize_column(int column)
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!column_header().is_section_visible(column))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& model = *this->model();
|
||||||
|
int row_count = model.row_count();
|
||||||
|
|
||||||
|
int header_width = m_column_header->font().width(model.column_name(column));
|
||||||
|
if (column == m_key_column && model.is_column_sortable(column))
|
||||||
|
header_width += font().width(" \xE2\xAC\x86");
|
||||||
|
|
||||||
|
int column_width = header_width;
|
||||||
|
bool is_empty = true;
|
||||||
|
for (int row = 0; row < row_count; ++row) {
|
||||||
|
auto cell_data = model.index(row, column).data();
|
||||||
|
int cell_width = 0;
|
||||||
|
if (cell_data.is_icon()) {
|
||||||
|
cell_width = cell_data.as_icon().bitmap_for_size(16)->width();
|
||||||
|
} else if (cell_data.is_bitmap()) {
|
||||||
|
cell_width = cell_data.as_bitmap().width();
|
||||||
|
} else if (cell_data.is_valid()) {
|
||||||
|
cell_width = font().width(cell_data.to_string());
|
||||||
|
}
|
||||||
|
if (is_empty && cell_width > 0)
|
||||||
|
is_empty = false;
|
||||||
|
column_width = max(column_width, cell_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto default_column_size = column_header().default_section_size(column);
|
||||||
|
if (is_empty && column_header().is_default_section_size_initialized(column))
|
||||||
|
column_header().set_section_size(column, default_column_size);
|
||||||
|
else
|
||||||
|
column_header().set_section_size(column, column_width);
|
||||||
|
}
|
||||||
void AbstractTableView::update_column_sizes()
|
void AbstractTableView::update_column_sizes()
|
||||||
{
|
{
|
||||||
if (!model())
|
if (!model())
|
||||||
|
@ -326,6 +367,11 @@ void AbstractTableView::header_did_change_section_visibility(Badge<HeaderView>,
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractTableView::set_default_column_width(int column, int width)
|
||||||
|
{
|
||||||
|
column_header().set_default_section_size(column, width);
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractTableView::set_column_hidden(int column, bool hidden)
|
void AbstractTableView::set_column_hidden(int column, bool hidden)
|
||||||
{
|
{
|
||||||
column_header().set_section_visible(column, !hidden);
|
column_header().set_section_visible(column, !hidden);
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
int column_width(int column) const;
|
int column_width(int column) const;
|
||||||
void set_column_width(int column, int width);
|
void set_column_width(int column, int width);
|
||||||
|
void set_default_column_width(int column, int width);
|
||||||
|
|
||||||
Gfx::TextAlignment column_header_alignment(int column) const;
|
Gfx::TextAlignment column_header_alignment(int column) const;
|
||||||
void set_column_header_alignment(int column, Gfx::TextAlignment);
|
void set_column_header_alignment(int column, Gfx::TextAlignment);
|
||||||
|
@ -106,6 +107,7 @@ protected:
|
||||||
virtual void toggle_index(const ModelIndex&) { }
|
virtual void toggle_index(const ModelIndex&) { }
|
||||||
|
|
||||||
void update_content_size();
|
void update_content_size();
|
||||||
|
virtual void auto_resize_column(int column);
|
||||||
virtual void update_column_sizes();
|
virtual void update_column_sizes();
|
||||||
virtual void update_row_sizes();
|
virtual void update_row_sizes();
|
||||||
virtual int item_count() const;
|
virtual int item_count() const;
|
||||||
|
|
|
@ -117,6 +117,20 @@ int HeaderView::section_count() const
|
||||||
return m_orientation == Gfx::Orientation::Horizontal ? model()->column_count() : model()->row_count();
|
return m_orientation == Gfx::Orientation::Horizontal ? model()->column_count() : model()->row_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeaderView::doubleclick_event(MouseEvent& event)
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int section_count = this->section_count();
|
||||||
|
for (int i = 0; i < section_count; ++i) {
|
||||||
|
if (section_resize_grabbable_rect(i).contains(event.position())) {
|
||||||
|
if (on_resize_doubleclick)
|
||||||
|
on_resize_doubleclick(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HeaderView::mousedown_event(MouseEvent& event)
|
void HeaderView::mousedown_event(MouseEvent& event)
|
||||||
{
|
{
|
||||||
if (!model())
|
if (!model())
|
||||||
|
@ -361,6 +375,28 @@ void HeaderView::set_section_alignment(int section, Gfx::TextAlignment alignment
|
||||||
section_data(section).alignment = alignment;
|
section_data(section).alignment = alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeaderView::set_default_section_size(int section, int size)
|
||||||
|
{
|
||||||
|
if (orientation() == Gfx::Orientation::Horizontal && size < minimum_column_size)
|
||||||
|
size = minimum_column_size;
|
||||||
|
|
||||||
|
auto& data = section_data(section);
|
||||||
|
if (data.default_size == size)
|
||||||
|
return;
|
||||||
|
data.default_size = size;
|
||||||
|
data.has_initialized_default_size = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HeaderView::default_section_size(int section) const
|
||||||
|
{
|
||||||
|
return section_data(section).default_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeaderView::is_default_section_size_initialized(int section) const
|
||||||
|
{
|
||||||
|
return section_data(section).has_initialized_default_size;
|
||||||
|
}
|
||||||
|
|
||||||
bool HeaderView::is_section_visible(int section) const
|
bool HeaderView::is_section_visible(int section) const
|
||||||
{
|
{
|
||||||
return section_data(section).visibility;
|
return section_data(section).visibility;
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
void set_section_size(int section, int size);
|
void set_section_size(int section, int size);
|
||||||
int section_size(int section) const;
|
int section_size(int section) const;
|
||||||
|
|
||||||
|
void set_default_section_size(int section, int size);
|
||||||
|
int default_section_size(int section) const;
|
||||||
|
bool is_default_section_size_initialized(int section) const;
|
||||||
|
|
||||||
Gfx::TextAlignment section_alignment(int section) const;
|
Gfx::TextAlignment section_alignment(int section) const;
|
||||||
void set_section_alignment(int section, Gfx::TextAlignment);
|
void set_section_alignment(int section, Gfx::TextAlignment);
|
||||||
|
|
||||||
|
@ -54,6 +58,8 @@ public:
|
||||||
int section_count() const;
|
int section_count() const;
|
||||||
Gfx::IntRect section_rect(int section) const;
|
Gfx::IntRect section_rect(int section) const;
|
||||||
|
|
||||||
|
Function<void(int section)> on_resize_doubleclick;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HeaderView(AbstractTableView&, Gfx::Orientation);
|
HeaderView(AbstractTableView&, Gfx::Orientation);
|
||||||
|
|
||||||
|
@ -61,6 +67,7 @@ private:
|
||||||
virtual void mousedown_event(MouseEvent&) override;
|
virtual void mousedown_event(MouseEvent&) override;
|
||||||
virtual void mousemove_event(MouseEvent&) override;
|
virtual void mousemove_event(MouseEvent&) override;
|
||||||
virtual void mouseup_event(MouseEvent&) override;
|
virtual void mouseup_event(MouseEvent&) override;
|
||||||
|
virtual void doubleclick_event(MouseEvent&) override;
|
||||||
virtual void context_menu_event(ContextMenuEvent&) override;
|
virtual void context_menu_event(ContextMenuEvent&) override;
|
||||||
virtual void leave_event(Core::Event&) override;
|
virtual void leave_event(Core::Event&) override;
|
||||||
|
|
||||||
|
@ -78,7 +85,9 @@ private:
|
||||||
|
|
||||||
struct SectionData {
|
struct SectionData {
|
||||||
int size { 0 };
|
int size { 0 };
|
||||||
|
int default_size { 0 };
|
||||||
bool has_initialized_size { false };
|
bool has_initialized_size { false };
|
||||||
|
bool has_initialized_default_size { false };
|
||||||
bool visibility { true };
|
bool visibility { true };
|
||||||
RefPtr<Action> visibility_action;
|
RefPtr<Action> visibility_action;
|
||||||
Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterLeft };
|
Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterLeft };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue