mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
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:
parent
f3aec1a0d9
commit
48e3ea9e5c
2 changed files with 101 additions and 22 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -40,19 +40,35 @@ private:
|
|||
virtual void did_update_model() override;
|
||||
virtual void paint_event(GPaintEvent&) override;
|
||||
virtual void mousedown_event(GMouseEvent&) override;
|
||||
virtual void mousemove_event(GMouseEvent&) override;
|
||||
virtual void mouseup_event(GMouseEvent&) override;
|
||||
virtual void doubleclick_event(GMouseEvent&) override;
|
||||
virtual void keydown_event(GKeyEvent&) override;
|
||||
virtual void leave_event(CEvent&) override;
|
||||
|
||||
Rect content_rect(int row, int column) const;
|
||||
void paint_headers(Painter&);
|
||||
int item_count() const;
|
||||
Rect row_rect(int item_index) const;
|
||||
Rect header_rect(int) const;
|
||||
Rect column_resize_grabbable_rect(int) const;
|
||||
int column_width(int) const;
|
||||
void update_content_size();
|
||||
|
||||
Vector<bool> m_column_visibility;
|
||||
struct ColumnData {
|
||||
int width { 0 };
|
||||
bool has_initialized_width { false };
|
||||
bool visibility { true };
|
||||
};
|
||||
ColumnData& column_data(int column) const;
|
||||
|
||||
mutable Vector<ColumnData> m_column_data;
|
||||
int m_horizontal_padding { 5 };
|
||||
bool m_headers_visible { true };
|
||||
bool m_alternating_row_colors { true };
|
||||
|
||||
bool m_in_column_resize { false };
|
||||
Point m_column_resize_origin;
|
||||
int m_column_resize_original_width { 0 };
|
||||
int m_resizing_column { -1 };
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue