Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Debug.h>
9#include <LibCore/Object.h>
10#include <LibGUI/HeaderView.h>
11#include <LibGUI/Model.h>
12#include <LibGUI/Painter.h>
13#include <LibGUI/TreeView.h>
14#include <LibGfx/Bitmap.h>
15#include <LibGfx/Palette.h>
16
17REGISTER_WIDGET(GUI, TreeView)
18
19namespace GUI {
20
21struct TreeView::MetadataForIndex {
22 bool open { false };
23};
24
25TreeView::MetadataForIndex& TreeView::ensure_metadata_for_index(ModelIndex const& index) const
26{
27 VERIFY(index.is_valid());
28 auto it = m_view_metadata.find(index);
29 if (it != m_view_metadata.end())
30 return *it->value;
31 auto new_metadata = make<MetadataForIndex>();
32 auto& new_metadata_ref = *new_metadata;
33 m_view_metadata.set(index, move(new_metadata));
34 return new_metadata_ref;
35}
36
37TreeView::TreeView()
38{
39 REGISTER_BOOL_PROPERTY("should_fill_selected_rows", should_fill_selected_rows, set_should_fill_selected_rows);
40 set_selection_behavior(SelectionBehavior::SelectItems);
41 set_fill_with_background_color(true);
42 set_background_role(ColorRole::Base);
43 set_foreground_role(ColorRole::BaseText);
44 set_column_headers_visible(false);
45 m_expand_bitmap = Gfx::Bitmap::load_from_file("/res/icons/serenity/treeview-expand.png"sv).release_value_but_fixme_should_propagate_errors();
46 m_collapse_bitmap = Gfx::Bitmap::load_from_file("/res/icons/serenity/treeview-collapse.png"sv).release_value_but_fixme_should_propagate_errors();
47}
48
49ModelIndex TreeView::index_at_event_position(Gfx::IntPoint a_position, bool& is_toggle) const
50{
51 auto position = a_position.translated(0, -column_header().height()).translated(horizontal_scrollbar().value() - frame_thickness(), vertical_scrollbar().value() - frame_thickness());
52 is_toggle = false;
53 if (!model())
54 return {};
55 ModelIndex result;
56 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const& rect, Gfx::IntRect const& toggle_rect, int) {
57 if (toggle_rect.contains(position)) {
58 result = index;
59 is_toggle = true;
60 return IterationDecision::Break;
61 }
62 if (rect.contains_vertically(position.y())) {
63 result = index;
64 return IterationDecision::Break;
65 }
66 return IterationDecision::Continue;
67 });
68 return result;
69}
70
71void TreeView::doubleclick_event(MouseEvent& event)
72{
73 if (!model())
74 return;
75 auto& model = *this->model();
76 bool is_toggle;
77 auto index = index_at_event_position(event.position(), is_toggle);
78 if (!index.is_valid())
79 return;
80
81 if (event.button() == MouseButton::Primary) {
82 set_cursor(index, SelectionUpdate::Set);
83
84 if (model.row_count(index))
85 toggle_index(index);
86 else
87 activate(index);
88 }
89}
90
91void TreeView::set_open_state_of_all_in_subtree(ModelIndex const& root, bool open)
92{
93 if (root.is_valid()) {
94 ensure_metadata_for_index(root).open = open;
95 if (model()->row_count(root)) {
96 if (on_toggle)
97 on_toggle(root, open);
98 }
99 }
100 int row_count = model()->row_count(root);
101 int column = model()->tree_column();
102 for (int row = 0; row < row_count; ++row) {
103 auto index = model()->index(row, column, root);
104 set_open_state_of_all_in_subtree(index, open);
105 }
106}
107
108void TreeView::expand_all_parents_of(ModelIndex const& index)
109{
110 if (!model())
111 return;
112
113 auto current = index;
114 while (current.is_valid()) {
115 ensure_metadata_for_index(current).open = true;
116 if (on_toggle)
117 on_toggle(current, true);
118 current = current.parent();
119 }
120 update_column_sizes();
121 update_content_size();
122 update();
123}
124
125void TreeView::expand_tree(ModelIndex const& root)
126{
127 if (!model())
128 return;
129 set_open_state_of_all_in_subtree(root, true);
130 update_column_sizes();
131 update_content_size();
132 update();
133}
134
135void TreeView::collapse_tree(ModelIndex const& root)
136{
137 if (!model())
138 return;
139 set_open_state_of_all_in_subtree(root, false);
140 update_column_sizes();
141 update_content_size();
142 update();
143}
144
145void TreeView::toggle_index(ModelIndex const& index)
146{
147 VERIFY(model()->row_count(index));
148 auto& metadata = ensure_metadata_for_index(index);
149 metadata.open = !metadata.open;
150
151 if (!metadata.open && index.is_parent_of(cursor_index()))
152 set_cursor(index, SelectionUpdate::Set);
153
154 if (on_toggle)
155 on_toggle(index, metadata.open);
156 update_column_sizes();
157 update_content_size();
158 update();
159}
160
161bool TreeView::is_toggled(ModelIndex const& index)
162{
163 if (model()->row_count(index) == 0) {
164 if (model()->parent_index(index).is_valid())
165 return is_toggled(model()->parent_index(index));
166 return false;
167 }
168
169 auto& metadata = ensure_metadata_for_index(index);
170 return metadata.open;
171}
172
173template<typename Callback>
174void TreeView::traverse_in_paint_order(Callback callback) const
175{
176 VERIFY(model());
177 auto& model = *this->model();
178 auto tree_column = model.tree_column();
179 int indent_level = 1;
180 int y_offset = 0;
181 int tree_column_x_offset = this->tree_column_x_offset();
182
183 Function<IterationDecision(ModelIndex const&)> traverse_index = [&](ModelIndex const& index) {
184 int row_count_at_index = model.row_count(index);
185 if (index.is_valid()) {
186 auto& metadata = ensure_metadata_for_index(index);
187 int x_offset = tree_column_x_offset + horizontal_padding() + indent_level * indent_width_in_pixels();
188 auto node_text = index.data().to_deprecated_string();
189 Gfx::IntRect rect = {
190 x_offset, y_offset,
191 static_cast<int>(ceilf(icon_size() + icon_spacing() + text_padding() + font_for_index(index)->width(node_text) + text_padding())), row_height()
192 };
193 Gfx::IntRect toggle_rect;
194 if (row_count_at_index > 0) {
195 int toggle_x = tree_column_x_offset + horizontal_padding() + (indent_width_in_pixels() * indent_level) - (icon_size() / 2) - 4;
196 toggle_rect = { toggle_x, rect.y(), toggle_size(), toggle_size() };
197 toggle_rect.center_vertically_within(rect);
198 }
199 if (callback(index, rect, toggle_rect, indent_level) == IterationDecision::Break)
200 return IterationDecision::Break;
201 y_offset += row_height();
202 // NOTE: Skip traversing children if this index is closed!
203 if (!metadata.open)
204 return IterationDecision::Continue;
205 }
206
207 if (indent_level > 0 && !index.is_valid())
208 return IterationDecision::Continue;
209
210 ++indent_level;
211 int row_count = model.row_count(index);
212 for (int i = 0; i < row_count; ++i) {
213 if (traverse_index(model.index(i, tree_column, index)) == IterationDecision::Break)
214 return IterationDecision::Break;
215 }
216 --indent_level;
217 return IterationDecision::Continue;
218 };
219 int root_count = model.row_count();
220 for (int root_index = 0; root_index < root_count; ++root_index) {
221 if (traverse_index(model.index(root_index, tree_column, ModelIndex())) == IterationDecision::Break)
222 break;
223 }
224}
225
226void TreeView::paint_event(PaintEvent& event)
227{
228 Frame::paint_event(event);
229 Painter painter(*this);
230 painter.add_clip_rect(frame_inner_rect());
231 painter.add_clip_rect(event.rect());
232 if (fill_with_background_color())
233 painter.fill_rect(event.rect(), palette().color(background_role()));
234
235 if (!model())
236 return;
237 auto& model = *this->model();
238
239 painter.translate(frame_inner_rect().location());
240 painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
241
242 auto visible_content_rect = this->visible_content_rect();
243 int tree_column = model.tree_column();
244 int column_count = model.column_count();
245 int tree_column_x_offset = this->tree_column_x_offset();
246
247 int y_offset = column_header().height();
248
249 int painted_row_index = 0;
250
251 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const& a_rect, Gfx::IntRect const& a_toggle_rect, int indent_level) {
252 if (!a_rect.intersects_vertically(visible_content_rect))
253 return IterationDecision::Continue;
254
255 auto rect = a_rect.translated(0, y_offset);
256 auto toggle_rect = a_toggle_rect.translated(0, y_offset);
257
258 if constexpr (ITEM_RECTS_DEBUG)
259 painter.fill_rect(rect, Color::WarmGray);
260
261 bool is_selected_row = selection().contains(index);
262
263 Color text_color = palette().color(foreground_role());
264 if (is_selected_row && should_fill_selected_rows())
265 text_color = is_focused() ? palette().selection_text() : palette().inactive_selection_text();
266
267 Color background_color;
268 if (is_selected_row) {
269 background_color = is_focused() ? palette().selection() : palette().inactive_selection();
270 } else {
271 if (alternating_row_colors() && (painted_row_index % 2)) {
272 background_color = Color(220, 220, 220);
273 } else {
274 background_color = palette().color(background_role());
275 }
276 }
277
278 int row_width = 0;
279 for (int column_index = 0; column_index < column_count; ++column_index) {
280 if (!column_header().is_section_visible(column_index))
281 continue;
282 row_width += this->column_width(column_index) + horizontal_padding() * 2;
283 }
284 if (frame_inner_rect().width() > row_width) {
285 row_width = frame_inner_rect().width();
286 }
287
288 Gfx::IntRect row_rect { 0, rect.y(), row_width, rect.height() };
289
290 if (!is_selected_row || should_fill_selected_rows())
291 painter.fill_rect(row_rect, background_color);
292
293 int x_offset = 0;
294 for (int column_index = 0; column_index < column_count; ++column_index) {
295 if (!column_header().is_section_visible(column_index))
296 continue;
297 int column_width = this->column_width(column_index);
298
299 painter.draw_rect(toggle_rect, text_color);
300
301 if (column_index != tree_column) {
302 Gfx::IntRect cell_rect(horizontal_padding() + x_offset, rect.y(), column_width, row_height());
303 auto cell_index = model.index(index.row(), column_index, index.parent());
304
305 auto* delegate = column_painting_delegate(column_index);
306 if (delegate && delegate->should_paint(cell_index)) {
307 delegate->paint(painter, cell_rect, palette(), cell_index);
308 } else {
309 auto data = cell_index.data();
310
311 if (data.is_bitmap()) {
312 painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect());
313 } else if (data.is_icon()) {
314 if (auto bitmap = data.as_icon().bitmap_for_size(16)) {
315 auto opacity = cell_index.data(ModelRole::IconOpacity).as_float_or(1.0f);
316 painter.blit(cell_rect.location(), *bitmap, bitmap->rect(), opacity);
317 }
318 } else {
319 auto text_alignment = cell_index.data(ModelRole::TextAlignment).to_text_alignment(Gfx::TextAlignment::CenterLeft);
320 draw_item_text(painter, cell_index, is_selected_row, cell_rect, data.to_deprecated_string(), font_for_index(cell_index), text_alignment, Gfx::TextElision::Right);
321 }
322 }
323 } else {
324 // It's the tree column!
325 int indent_width = indent_width_in_pixels() * indent_level;
326
327 Gfx::IntRect icon_rect = { rect.x(), rect.y(), icon_size(), icon_size() };
328 icon_rect.center_vertically_within(rect);
329 Gfx::IntRect background_rect = {
330 icon_rect.right() + 1 + icon_spacing(), rect.y(),
331 min(rect.width(), column_width - indent_width) - icon_size() - icon_spacing(), rect.height()
332 };
333 Gfx::IntRect text_rect = background_rect.shrunken(text_padding() * 2, 0);
334
335 painter.fill_rect(background_rect, background_color);
336
337 auto icon = index.data(ModelRole::Icon);
338 if (icon.is_icon()) {
339 if (auto* bitmap = icon.as_icon().bitmap_for_size(icon_size())) {
340 if (m_hovered_index.is_valid() && m_hovered_index.parent() == index.parent() && m_hovered_index.row() == index.row()) {
341 painter.blit_brightened(icon_rect.location(), *bitmap, bitmap->rect());
342 } else {
343 auto opacity = index.data(ModelRole::IconOpacity).as_float_or(1.0f);
344 painter.blit(icon_rect.location(), *bitmap, bitmap->rect(), opacity);
345 }
346 }
347 }
348 auto display_data = index.data();
349 if (display_data.is_string() || display_data.is_u32() || display_data.is_i32() || display_data.is_u64() || display_data.is_i64() || display_data.is_bool() || display_data.is_float())
350 draw_item_text(painter, index, is_selected_row, text_rect, display_data.to_deprecated_string(), font_for_index(index), Gfx::TextAlignment::CenterLeft, Gfx::TextElision::Right);
351
352 if (selection_behavior() == SelectionBehavior::SelectItems && is_focused() && index == cursor_index()) {
353 painter.draw_rect(background_rect, palette().color(background_role()));
354 painter.draw_focus_rect(background_rect, palette().focus_outline());
355 }
356
357 auto index_at_indent = index;
358 for (int i = indent_level; i > 0; --i) {
359 auto parent_of_index_at_indent = index_at_indent.parent();
360 bool index_at_indent_is_last_in_parent = index_at_indent.row() == model.row_count(parent_of_index_at_indent) - 1;
361 Gfx::IntPoint a { tree_column_x_offset + horizontal_padding() + indent_width_in_pixels() * i - icon_size() / 2, rect.y() - 2 };
362 Gfx::IntPoint b { a.x(), a.y() + row_height() - 1 };
363 if (index_at_indent_is_last_in_parent)
364 b.set_y(rect.center().y());
365 if (!(i != indent_level && index_at_indent_is_last_in_parent))
366 painter.draw_line(a, b, Color::MidGray);
367
368 if (i == indent_level) {
369 Gfx::IntPoint c { a.x(), rect.center().y() };
370 Gfx::IntPoint d { c.x() + icon_size() / 2, c.y() };
371 painter.draw_line(c, d, Color::MidGray);
372 }
373 index_at_indent = parent_of_index_at_indent;
374 }
375
376 if (!toggle_rect.is_empty()) {
377 auto& metadata = ensure_metadata_for_index(index);
378 if (metadata.open)
379 painter.blit(toggle_rect.location(), *m_collapse_bitmap, m_collapse_bitmap->rect());
380 else
381 painter.blit(toggle_rect.location(), *m_expand_bitmap, m_expand_bitmap->rect());
382 }
383
384 if (has_pending_drop() && index == drop_candidate_index()) {
385 painter.draw_rect(rect, palette().selection(), true);
386 }
387 }
388 x_offset += column_width + horizontal_padding() * 2;
389 }
390
391 if (selection_behavior() == SelectionBehavior::SelectRows && is_focused() && index == cursor_index()) {
392 painter.draw_rect(row_rect, palette().color(background_role()));
393 painter.draw_focus_rect(row_rect, palette().focus_outline());
394 }
395
396 return IterationDecision::Continue;
397 });
398}
399
400void TreeView::scroll_into_view(ModelIndex const& a_index, bool, bool scroll_vertically)
401{
402 if (!a_index.is_valid())
403 return;
404 Gfx::IntRect found_rect;
405 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const& rect, Gfx::IntRect const&, int) {
406 if (index == a_index) {
407 found_rect = rect;
408 return IterationDecision::Break;
409 }
410 return IterationDecision::Continue;
411 });
412 AbstractScrollableWidget::scroll_into_view(found_rect, false, scroll_vertically);
413}
414
415void TreeView::model_did_update(unsigned flags)
416{
417 m_view_metadata.clear();
418 AbstractTableView::model_did_update(flags);
419}
420
421void TreeView::did_update_selection()
422{
423 AbstractView::did_update_selection();
424 if (!model())
425 return;
426 auto index = selection().first();
427 if (!index.is_valid())
428 return;
429
430 if (activates_on_selection())
431 activate(index);
432}
433
434void TreeView::mousedown_event(MouseEvent& event)
435{
436 if (!model())
437 return AbstractView::mousedown_event(event);
438
439 if (event.button() != MouseButton::Primary)
440 return AbstractView::mousedown_event(event);
441
442 bool is_toggle;
443 auto index = index_at_event_position(event.position(), is_toggle);
444
445 if (index.is_valid() && is_toggle && model()->row_count(index)) {
446 if (event.alt()) {
447 if (is_toggled(index)) {
448 collapse_tree(index);
449 } else {
450 expand_tree(index);
451 }
452 return;
453 }
454 toggle_index(index);
455 return;
456 }
457
458 AbstractView::mousedown_event(event);
459}
460
461void TreeView::keydown_event(KeyEvent& event)
462{
463 if (!model())
464 return AbstractTableView::keydown_event(event);
465
466 if (event.key() == KeyCode::Key_Space) {
467 if (model()->row_count(cursor_index()))
468 toggle_index(cursor_index());
469 return;
470 }
471
472 auto open_tree_node = [&](bool open, MetadataForIndex& metadata) {
473 if (on_toggle)
474 on_toggle(cursor_index(), open);
475 metadata.open = open;
476 update_column_sizes();
477 update_content_size();
478 update();
479 };
480
481 if (event.key() == KeyCode::Key_Left) {
482 if (cursor_index().is_valid() && model()->row_count(cursor_index())) {
483 if (event.ctrl()) {
484 collapse_tree(cursor_index());
485 return;
486 }
487
488 auto& metadata = ensure_metadata_for_index(cursor_index());
489 if (metadata.open) {
490 open_tree_node(false, metadata);
491 return;
492 }
493 }
494 if (cursor_index().is_valid() && cursor_index().parent().is_valid()) {
495 set_cursor(cursor_index().parent(), SelectionUpdate::Set);
496 return;
497 }
498 }
499
500 if (event.key() == KeyCode::Key_Right) {
501 if (cursor_index().is_valid() && model()->row_count(cursor_index())) {
502 if (event.ctrl()) {
503 expand_tree(cursor_index());
504 return;
505 }
506
507 auto& metadata = ensure_metadata_for_index(cursor_index());
508 if (!metadata.open) {
509 open_tree_node(true, metadata);
510 return;
511 }
512
513 auto new_cursor = model()->index(0, model()->tree_column(), cursor_index());
514 set_cursor(new_cursor, SelectionUpdate::Set);
515 return;
516 }
517 }
518
519 if (event.key() == KeyCode::Key_Return) {
520 if (cursor_index().is_valid() && model()->row_count(cursor_index())) {
521 toggle_index(cursor_index());
522 return;
523 }
524 }
525
526 AbstractTableView::keydown_event(event);
527}
528
529void TreeView::move_cursor(CursorMovement movement, SelectionUpdate selection_update)
530{
531 auto& model = *this->model();
532
533 if (!cursor_index().is_valid())
534 set_cursor(model.index(0, model.tree_column(), cursor_index()), SelectionUpdate::Set);
535
536 auto find_last_index_in_tree = [&](const ModelIndex tree_index) -> ModelIndex {
537 auto last_index = tree_index;
538 size_t row_count = model.row_count(last_index);
539 while (row_count > 0) {
540 last_index = model.index(row_count - 1, model.tree_column(), last_index);
541
542 if (last_index.is_valid()) {
543 if (model.row_count(last_index) == 0)
544 break;
545 auto& metadata = ensure_metadata_for_index(last_index);
546 if (!metadata.open)
547 break;
548 }
549
550 row_count = model.row_count(last_index);
551 }
552 return last_index;
553 };
554
555 auto step_up = [&](const ModelIndex current_index) -> ModelIndex {
556 // Traverse into parent index if we're at the top of our subtree
557 if (current_index.row() == 0) {
558 auto parent_index = current_index.parent();
559 if (parent_index.is_valid())
560 return parent_index;
561 return current_index;
562 }
563
564 // If previous index is closed, move to it immediately
565 auto previous_index = model.index(current_index.row() - 1, model.tree_column(), current_index.parent());
566 if (model.row_count(previous_index) == 0)
567 return previous_index;
568 auto& metadata = ensure_metadata_for_index(previous_index);
569 if (!metadata.open)
570 return previous_index;
571
572 // Return very last index inside of open previous index
573 return find_last_index_in_tree(previous_index);
574 };
575
576 auto step_down = [&](const ModelIndex current_index) -> ModelIndex {
577 if (!current_index.is_valid())
578 return current_index;
579
580 // Step in when node is open
581 if (model.row_count(current_index) > 0) {
582 auto& metadata = ensure_metadata_for_index(current_index);
583 if (metadata.open)
584 return model.index(0, model.tree_column(), current_index);
585 }
586
587 // Find the parent index in which we must step one down
588 auto child_index = current_index;
589 auto parent_index = child_index.parent();
590 int row_count = model.row_count(parent_index);
591 while (child_index.is_valid() && child_index.row() >= row_count - 1) {
592 child_index = parent_index;
593 parent_index = parent_index.parent();
594 row_count = model.row_count(parent_index);
595 }
596
597 // Step one down
598 if (!child_index.is_valid())
599 return current_index;
600 return model.index(child_index.row() + 1, child_index.column(), parent_index);
601 };
602
603 switch (movement) {
604 case CursorMovement::Up: {
605 auto new_index = step_up(cursor_index());
606 if (new_index.is_valid())
607 set_cursor(new_index, selection_update);
608 break;
609 }
610 case CursorMovement::Down: {
611 auto new_index = step_down(cursor_index());
612 if (new_index.is_valid())
613 set_cursor(new_index, selection_update);
614 return;
615 }
616 case CursorMovement::Home: {
617 ModelIndex first_index = model.index(0, model.tree_column(), ModelIndex());
618 if (first_index.is_valid())
619 set_cursor(first_index, selection_update);
620 return;
621 }
622 case CursorMovement::End: {
623 auto last_index = find_last_index_in_tree({});
624 if (last_index.is_valid())
625 set_cursor(last_index, selection_update);
626 return;
627 }
628 case CursorMovement::PageUp: {
629 int const items_per_page = visible_content_rect().height() / row_height();
630 auto new_index = cursor_index();
631 for (int step = 0; step < items_per_page; ++step)
632 new_index = step_up(new_index);
633 if (new_index.is_valid())
634 set_cursor(new_index, selection_update);
635 return;
636 }
637 case CursorMovement::PageDown: {
638 int const items_per_page = visible_content_rect().height() / row_height();
639 auto new_index = cursor_index();
640 for (int step = 0; step < items_per_page; ++step)
641 new_index = step_down(new_index);
642 if (new_index.is_valid())
643 set_cursor(new_index, selection_update);
644 return;
645 }
646 case CursorMovement::Left:
647 case CursorMovement::Right:
648 // There is no left/right in a treeview, those keys expand/collapse items instead.
649 break;
650 }
651}
652
653int TreeView::item_count() const
654{
655 int count = 0;
656 traverse_in_paint_order([&](ModelIndex const&, Gfx::IntRect const&, Gfx::IntRect const&, int) {
657 ++count;
658 return IterationDecision::Continue;
659 });
660 return count;
661}
662
663void TreeView::auto_resize_column(int column)
664{
665 if (!model())
666 return;
667
668 if (!column_header().is_section_visible(column))
669 return;
670
671 auto& model = *this->model();
672
673 int header_width = column_header().font().width(model.column_name(column));
674 if (column == m_key_column && model.is_column_sortable(column))
675 header_width += HeaderView::sorting_arrow_width + HeaderView::sorting_arrow_offset;
676 int column_width = header_width;
677
678 bool is_empty = true;
679 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const&, Gfx::IntRect const&, int indent_level) {
680 auto cell_data = model.index(index.row(), column, index.parent()).data();
681 int cell_width = 0;
682 if (cell_data.is_icon()) {
683 cell_width = cell_data.as_icon().bitmap_for_size(16)->width();
684 } else if (cell_data.is_bitmap()) {
685 cell_width = cell_data.as_bitmap().width();
686 } else if (cell_data.is_valid()) {
687 cell_width = font().width(cell_data.to_deprecated_string());
688 }
689 if (is_empty && cell_width > 0)
690 is_empty = false;
691 if (column == model.tree_column())
692 cell_width += horizontal_padding() * 2 + indent_level * indent_width_in_pixels() + icon_size() / 2;
693 column_width = max(column_width, cell_width);
694 return IterationDecision::Continue;
695 });
696
697 auto default_column_width = column_header().default_section_size(column);
698 if (is_empty && column_header().is_default_section_size_initialized(column))
699 column_header().set_section_size(column, default_column_width);
700 else
701 column_header().set_section_size(column, column_width);
702}
703
704void TreeView::update_column_sizes()
705{
706 if (!model())
707 return;
708
709 auto& model = *this->model();
710 int column_count = model.column_count();
711 int tree_column = model.tree_column();
712
713 for (int column = 0; column < column_count; ++column) {
714 if (column == tree_column)
715 continue;
716 if (!column_header().is_section_visible(column))
717 continue;
718 int header_width = column_header().font().width(model.column_name(column));
719 if (column == m_key_column && model.is_column_sortable(column))
720 header_width += HeaderView::sorting_arrow_width + HeaderView::sorting_arrow_offset;
721 int column_width = header_width;
722 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const&, Gfx::IntRect const&, int) {
723 auto cell_data = model.index(index.row(), column, index.parent()).data();
724 int cell_width = 0;
725 if (cell_data.is_icon()) {
726 cell_width = cell_data.as_icon().bitmap_for_size(16)->width();
727 } else if (cell_data.is_bitmap()) {
728 cell_width = cell_data.as_bitmap().width();
729 } else if (cell_data.is_valid()) {
730 cell_width = font().width(cell_data.to_deprecated_string());
731 }
732 column_width = max(column_width, cell_width);
733 return IterationDecision::Continue;
734 });
735
736 set_column_width(column, max(this->column_width(column), column_width));
737 }
738
739 int tree_column_header_width = column_header().font().width(model.column_name(tree_column));
740 if (tree_column == m_key_column && model.is_column_sortable(tree_column))
741 tree_column_header_width += HeaderView::sorting_arrow_width + HeaderView::sorting_arrow_offset;
742 int tree_column_width = tree_column_header_width;
743 traverse_in_paint_order([&](ModelIndex const& index, Gfx::IntRect const&, Gfx::IntRect const&, int indent_level) {
744 auto cell_data = model.index(index.row(), tree_column, index.parent()).data();
745 int cell_width = 0;
746 if (cell_data.is_valid()) {
747 cell_width = font().width(cell_data.to_deprecated_string());
748 cell_width += horizontal_padding() * 2 + indent_level * indent_width_in_pixels() + icon_size() / 2 + text_padding() * 2;
749 }
750 tree_column_width = max(tree_column_width, cell_width);
751 return IterationDecision::Continue;
752 });
753
754 set_column_width(tree_column, tree_column_width);
755}
756
757int TreeView::tree_column_x_offset() const
758{
759 int tree_column = model()->tree_column();
760 int offset = 0;
761 for (int i = 0; i < tree_column; ++i) {
762 if (column_header().is_section_visible(i)) {
763 offset += column_width(i);
764 offset += horizontal_padding() * 2;
765 }
766 }
767 return offset;
768}
769
770Gfx::IntRect TreeView::content_rect(ModelIndex const& index) const
771{
772 if (!index.is_valid())
773 return {};
774
775 Gfx::IntRect found_rect;
776 traverse_in_paint_order([&](ModelIndex const& current_index, Gfx::IntRect const& rect, Gfx::IntRect const&, int) {
777 if (index == current_index) {
778 found_rect = rect;
779 found_rect.translate_by(0, column_header().height());
780 return IterationDecision::Break;
781 }
782 return IterationDecision::Continue;
783 });
784 return found_rect;
785}
786
787int TreeView::minimum_column_width(int column)
788{
789 if (column != model()->tree_column()) {
790 return 2;
791 }
792
793 int maximum_indent_level = 1;
794
795 traverse_in_paint_order([&](ModelIndex const&, Gfx::IntRect const&, Gfx::IntRect const&, int indent_level) {
796 maximum_indent_level = max(maximum_indent_level, indent_level);
797 return IterationDecision::Continue;
798 });
799
800 return indent_width_in_pixels() * maximum_indent_level + icon_size() + icon_spacing() + 2;
801}
802
803}