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

The GModel now merely provides an initial width for the columns. Once that
has been queried, the table view manages width from then on.
This commit is contained in:
Andreas Kling 2019-05-04 23:45:31 +02:00
parent f3aec1a0d9
commit 48e3ea9e5c
Notes: sideshowbarker 2024-07-19 14:15:07 +09:00
2 changed files with 101 additions and 22 deletions

View file

@ -3,6 +3,7 @@
#include <LibGUI/GScrollBar.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GTextBox.h>
#include <LibGUI/GWindow.h>
#include <Kernel/KeyCode.h>
#include <AK/StringBuilder.h>
@ -26,7 +27,7 @@ void GTableView::update_content_size()
int content_width = 0;
int column_count = model()->column_count();
for (int i = 0; i < column_count; ++i)
content_width += model()->column_metadata(i).preferred_width + horizontal_padding() * 2;
content_width += column_width(i) + horizontal_padding() * 2;
int content_height = item_count() * item_height();
set_content_size({ content_width, content_height });
@ -62,7 +63,12 @@ Rect GTableView::row_rect(int item_index) const
int GTableView::column_width(int column_index) const
{
return model()->column_metadata(column_index).preferred_width;
auto& column_data = this->column_data(column_index);
if (!column_data.has_initialized_width) {
column_data.has_initialized_width = true;
column_data.width = model()->column_metadata(column_index).preferred_width;
}
return column_data.width;
}
Rect GTableView::header_rect(int column_index) const
@ -75,9 +81,7 @@ Rect GTableView::header_rect(int column_index) const
continue;
x_offset += column_width(i) + horizontal_padding() * 2;
}
auto column_metadata = model()->column_metadata(column_index);
int column_width = column_metadata.preferred_width;
return { x_offset, 0, column_width + horizontal_padding() * 2, header_height() };
return { x_offset, 0, column_width(column_index) + horizontal_padding() * 2, header_height() };
}
Point GTableView::adjusted_position(const Point& position)
@ -85,14 +89,31 @@ Point GTableView::adjusted_position(const Point& position)
return position.translated(-frame_thickness(), vertical_scrollbar().value() - frame_thickness());
}
Rect GTableView::column_resize_grabbable_rect(int column) const
{
auto header_rect = this->header_rect(column);
return { header_rect.right() - 1, header_rect.top(), 4, header_rect.height() };
}
void GTableView::mousedown_event(GMouseEvent& event)
{
if (!model())
return;
auto adjusted_position = this->adjusted_position(event.position());
if (event.y() < header_height()) {
auto adjusted_position = this->adjusted_position(event.position());
for (int i = 0; i < model()->column_count(); ++i) {
if (event.button() == GMouseButton::Left) {
if (column_resize_grabbable_rect(i).contains(adjusted_position)) {
dbgprintf("now resizing column %d\n", i);
m_resizing_column = i;
m_in_column_resize = true;
m_column_resize_original_width = column_width(i);
m_column_resize_origin = event.position();
return;
}
}
auto header_rect = this->header_rect(i);
if (header_rect.contains(adjusted_position)) {
auto new_sort_order = GSortOrder::Ascending;
@ -108,7 +129,6 @@ void GTableView::mousedown_event(GMouseEvent& event)
}
if (event.button() == GMouseButton::Left) {
auto adjusted_position = this->adjusted_position(event.position());
for (int row = 0, row_count = model()->row_count(); row < row_count; ++row) {
if (!row_rect(row).contains(adjusted_position))
continue;
@ -125,6 +145,41 @@ void GTableView::mousedown_event(GMouseEvent& event)
}
}
void GTableView::mousemove_event(GMouseEvent& event)
{
if (m_in_column_resize) {
auto delta = event.position() - m_column_resize_origin;
int new_width = m_column_resize_original_width + delta.x();
auto& column_data = this->column_data(m_resizing_column);
if (column_data.width != new_width) {
column_data.width = new_width;
update();
}
return;
}
auto adjusted_position = this->adjusted_position(event.position());
if (event.buttons() == 0) {
for (int i = 0; i < model()->column_count(); ++i) {
if (column_resize_grabbable_rect(i).contains(adjusted_position)) {
window()->set_override_cursor(GStandardCursor::ResizeHorizontal);
return;
}
}
}
window()->set_override_cursor(GStandardCursor::None);
}
void GTableView::mouseup_event(GMouseEvent& event)
{
auto adjusted_position = this->adjusted_position(event.position());
if (event.button() == GMouseButton::Left) {
if (!column_resize_grabbable_rect(m_resizing_column).contains(adjusted_position))
window()->set_override_cursor(GStandardCursor::None);
m_in_column_resize = false;
}
}
void GTableView::paint_event(GPaintEvent& event)
{
GFrame::paint_event(event);
@ -167,7 +222,7 @@ void GTableView::paint_event(GPaintEvent& event)
if (is_column_hidden(column_index))
continue;
auto column_metadata = model()->column_metadata(column_index);
int column_width = column_metadata.preferred_width;
int column_width = this->column_width(column_index);
const Font& font = column_metadata.font ? *column_metadata.font : this->font();
bool is_key_column = model()->key_column() == column_index;
Rect cell_rect(horizontal_padding() + x_offset, y, column_width, item_height());
@ -214,8 +269,7 @@ void GTableView::paint_headers(Painter& painter)
for (int column_index = 0; column_index < model()->column_count(); ++column_index) {
if (is_column_hidden(column_index))
continue;
auto column_metadata = model()->column_metadata(column_index);
int column_width = column_metadata.preferred_width;
int column_width = this->column_width(column_index);
bool is_key_column = model()->key_column() == column_index;
Rect cell_rect(x_offset, 0, column_width + horizontal_padding() * 2, header_height());
StylePainter::paint_button(painter, cell_rect, ButtonStyle::Normal, false);
@ -309,23 +363,27 @@ void GTableView::scroll_into_view(const GModelIndex& index, Orientation orientat
GScrollableWidget::scroll_into_view(rect, orientation);
}
GTableView::ColumnData& GTableView::column_data(int column) const
{
ASSERT(model());
ASSERT(column >= 0 && column < model()->column_count());
if (column >= m_column_data.size())
m_column_data.resize(model()->column_count());
return m_column_data.at(column);
}
bool GTableView::is_column_hidden(int column) const
{
if (column >= 0 && column < m_column_visibility.size())
return !m_column_visibility[column];
return false;
return !column_data(column).visibility;
}
void GTableView::set_column_hidden(int column, bool hidden)
{
ASSERT(column >= 0);
if (m_column_visibility.size() <= column) {
int previous_column_count = m_column_visibility.size();
m_column_visibility.resize(column + 1);
for (int i = previous_column_count; i < m_column_visibility.size(); ++i)
m_column_visibility[i] = true;
}
m_column_visibility[column] = !hidden;
auto& column_data = this->column_data(column);
if (column_data.visibility == !hidden)
return;
column_data.visibility = !hidden;
update();
}
void GTableView::doubleclick_event(GMouseEvent& event)
@ -344,3 +402,8 @@ void GTableView::doubleclick_event(GMouseEvent& event)
}
}
}
void GTableView::leave_event(CEvent&)
{
window()->set_override_cursor(GStandardCursor::None);
}