Serenity Operating System
at master 803 lines 30 kB view raw
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}