Serenity Operating System
at master 831 lines 26 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Debug.h> 8#include <AK/StringBuilder.h> 9#include <AK/Utf8View.h> 10#include <LibCore/Object.h> 11#include <LibCore/Timer.h> 12#include <LibGUI/AbstractView.h> 13#include <LibGUI/DragOperation.h> 14#include <LibGUI/Model.h> 15#include <LibGUI/ModelEditingDelegate.h> 16#include <LibGUI/Painter.h> 17#include <LibGUI/Scrollbar.h> 18#include <LibGUI/TextBox.h> 19#include <LibGfx/Palette.h> 20 21namespace GUI { 22 23AbstractView::AbstractView() 24 : m_sort_order(SortOrder::Ascending) 25 , m_selection(*this) 26{ 27 REGISTER_BOOL_PROPERTY("activates_on_selection", activates_on_selection, set_activates_on_selection); 28 REGISTER_BOOL_PROPERTY("editable", is_editable, set_editable); 29 REGISTER_BOOL_PROPERTY("searchable", is_searchable, set_searchable); 30 REGISTER_ENUM_PROPERTY("selection_behavior", selection_behavior, set_selection_behavior, SelectionBehavior, 31 { SelectionBehavior::SelectItems, "SelectItems" }, 32 { SelectionBehavior::SelectRows, "SelectRows" }); 33 REGISTER_ENUM_PROPERTY("selection_mode", selection_mode, set_selection_mode, SelectionMode, 34 { SelectionMode::SingleSelection, "SingleSelection" }, 35 { SelectionMode::MultiSelection, "MultiSeleciton" }, 36 { SelectionMode::NoSelection, "NoSelection" }); 37 REGISTER_INT_PROPERTY("key_column", key_column, set_key_column); 38 REGISTER_ENUM_PROPERTY("sort_order", sort_order, set_sort_order, SortOrder, 39 { SortOrder::Ascending, "Ascending" }, 40 { SortOrder::Descending, "Descending" }); 41 REGISTER_BOOL_PROPERTY("tab_key_navigation_enabled", is_tab_key_navigation_enabled, set_tab_key_navigation_enabled); 42 REGISTER_BOOL_PROPERTY("draw_item_text_with_shadow", does_draw_item_text_with_shadow, set_draw_item_text_with_shadow); 43 44 set_focus_policy(GUI::FocusPolicy::StrongFocus); 45} 46 47AbstractView::~AbstractView() 48{ 49 if (m_highlighted_search_timer) 50 m_highlighted_search_timer->stop(); 51 if (m_model) 52 m_model->unregister_view({}, *this); 53} 54 55void AbstractView::set_model(RefPtr<Model> model) 56{ 57 if (model == m_model) 58 return; 59 if (m_model) 60 m_model->unregister_view({}, *this); 61 m_model = move(model); 62 if (m_model) 63 m_model->register_view({}, *this); 64 model_did_update(GUI::Model::InvalidateAllIndices); 65 scroll_to_top(); 66} 67 68void AbstractView::model_did_update(unsigned int flags) 69{ 70 if (!model() || (flags & GUI::Model::InvalidateAllIndices)) { 71 stop_editing(); 72 m_edit_index = {}; 73 m_hovered_index = {}; 74 m_cursor_index = {}; 75 m_drop_candidate_index = {}; 76 clear_selection(); 77 } else { 78 // FIXME: These may no longer point to whatever they did before, 79 // but let's be optimistic until we can be sure about it. 80 if (!model()->is_within_range(m_edit_index)) { 81 stop_editing(); 82 m_edit_index = {}; 83 } 84 if (!model()->is_within_range(m_hovered_index)) 85 m_hovered_index = {}; 86 if (!model()->is_within_range(m_cursor_index)) 87 m_cursor_index = {}; 88 if (!model()->is_within_range(m_drop_candidate_index)) 89 m_drop_candidate_index = {}; 90 selection().remove_all_matching([this](auto& index) { return !model()->is_within_range(index); }); 91 92 auto index = find_next_search_match(m_highlighted_search.view()); 93 if (index.is_valid()) 94 highlight_search(index); 95 } 96 m_selection_start_index = {}; 97} 98 99void AbstractView::clear_selection() 100{ 101 m_selection.clear(); 102} 103 104void AbstractView::set_selection(ModelIndex const& new_index) 105{ 106 m_selection.set(new_index); 107} 108 109void AbstractView::set_selection_start_index(ModelIndex const& new_index) 110{ 111 m_selection_start_index = new_index; 112} 113 114void AbstractView::add_selection(ModelIndex const& new_index) 115{ 116 m_selection.add(new_index); 117} 118 119void AbstractView::remove_selection(ModelIndex const& new_index) 120{ 121 m_selection.remove(new_index); 122} 123 124void AbstractView::toggle_selection(ModelIndex const& new_index) 125{ 126 m_selection.toggle(new_index); 127} 128 129void AbstractView::did_update_selection() 130{ 131 if (!model() || selection().first() != m_edit_index) 132 stop_editing(); 133 if (model() && on_selection_change) 134 on_selection_change(); 135} 136 137void AbstractView::did_scroll() 138{ 139 update_edit_widget_position(); 140} 141 142void AbstractView::update_edit_widget_position() 143{ 144 if (!m_edit_widget) 145 return; 146 m_edit_widget->set_relative_rect(m_edit_widget_content_rect.translated(-horizontal_scrollbar().value(), -vertical_scrollbar().value())); 147} 148 149void AbstractView::begin_editing(ModelIndex const& index) 150{ 151 VERIFY(is_editable()); 152 VERIFY(model()); 153 if (m_edit_index == index) 154 return; 155 if (!model()->is_editable(index)) 156 return; 157 if (m_edit_widget) { 158 remove_child(*m_edit_widget); 159 m_edit_widget = nullptr; 160 } 161 m_edit_index = index; 162 163 VERIFY(aid_create_editing_delegate); 164 m_editing_delegate = aid_create_editing_delegate(index); 165 m_editing_delegate->bind(*model(), index); 166 m_editing_delegate->set_value(index.data()); 167 m_edit_widget = m_editing_delegate->widget(); 168 add_child(*m_edit_widget); 169 m_edit_widget->move_to_back(); 170 m_edit_widget_content_rect = editing_rect(index).translated(frame_thickness(), frame_thickness()); 171 update_edit_widget_position(); 172 m_edit_widget->set_focus(true); 173 m_editing_delegate->will_begin_editing(); 174 m_editing_delegate->on_commit = [this] { 175 VERIFY(model()); 176 model()->set_data(m_edit_index, m_editing_delegate->value()); 177 stop_editing(); 178 }; 179 m_editing_delegate->on_rollback = [this] { 180 VERIFY(model()); 181 stop_editing(); 182 }; 183 m_editing_delegate->on_change = [this, index] { 184 editing_widget_did_change(index); 185 }; 186} 187 188void AbstractView::stop_editing() 189{ 190 bool take_back_focus = false; 191 m_edit_index = {}; 192 if (m_edit_widget) { 193 take_back_focus = m_edit_widget->is_focused(); 194 remove_child(*m_edit_widget); 195 m_edit_widget = nullptr; 196 } 197 if (take_back_focus) 198 set_focus(true); 199} 200 201void AbstractView::activate(ModelIndex const& index) 202{ 203 if (on_activation) 204 on_activation(index); 205} 206 207void AbstractView::activate_selected() 208{ 209 if (!on_activation) 210 return; 211 212 selection().for_each_index([this](auto& index) { 213 on_activation(index); 214 }); 215} 216 217void AbstractView::notify_selection_changed(Badge<ModelSelection>) 218{ 219 did_update_selection(); 220 if (!m_suppress_update_on_selection_change) 221 update(); 222} 223 224NonnullRefPtr<Gfx::Font const> AbstractView::font_for_index(ModelIndex const& index) const 225{ 226 if (!model()) 227 return font(); 228 229 auto font_data = index.data(ModelRole::Font); 230 if (font_data.is_font()) 231 return font_data.as_font(); 232 233 return font(); 234} 235 236void AbstractView::mousedown_event(MouseEvent& event) 237{ 238 AbstractScrollableWidget::mousedown_event(event); 239 240 if (!model()) 241 return; 242 243 if (event.button() == MouseButton::Primary) 244 m_left_mousedown_position = event.position(); 245 246 auto index = index_at_event_position(event.position()); 247 m_might_drag = false; 248 249 if (!index.is_valid()) { 250 clear_selection(); 251 } else if (event.modifiers() & Mod_Ctrl) { 252 set_cursor(index, SelectionUpdate::Ctrl); 253 } else if (event.modifiers() & Mod_Shift) { 254 set_cursor(index, SelectionUpdate::Shift); 255 } else if (event.button() == MouseButton::Primary && m_selection.contains(index) && !m_model->drag_data_type().is_null()) { 256 // We might be starting a drag, so don't throw away other selected items yet. 257 m_might_drag = true; 258 } else if (event.button() == MouseButton::Secondary) { 259 set_cursor(index, SelectionUpdate::ClearIfNotSelected); 260 } else { 261 set_cursor(index, SelectionUpdate::Set); 262 m_might_drag = true; 263 } 264 265 update(); 266} 267 268void AbstractView::set_hovered_index(ModelIndex const& index) 269{ 270 if (m_hovered_index == index) 271 return; 272 auto old_index = m_hovered_index; 273 m_hovered_index = index; 274 did_change_hovered_index(old_index, index); 275 276 if (old_index.is_valid()) 277 update(to_widget_rect(paint_invalidation_rect(old_index))); 278 279 if (index.is_valid()) 280 update(to_widget_rect(paint_invalidation_rect(index))); 281} 282 283void AbstractView::leave_event(Core::Event& event) 284{ 285 AbstractScrollableWidget::leave_event(event); 286 set_hovered_index({}); 287} 288 289void AbstractView::mousemove_event(MouseEvent& event) 290{ 291 if (!model()) 292 return AbstractScrollableWidget::mousemove_event(event); 293 294 auto hovered_index = index_at_event_position(event.position()); 295 set_hovered_index(hovered_index); 296 297 auto data_type = m_model->drag_data_type(); 298 if (data_type.is_null()) 299 return AbstractScrollableWidget::mousemove_event(event); 300 301 if (!m_might_drag) 302 return AbstractScrollableWidget::mousemove_event(event); 303 304 if (!(event.buttons() & MouseButton::Primary) || m_selection.is_empty()) { 305 m_might_drag = false; 306 return AbstractScrollableWidget::mousemove_event(event); 307 } 308 309 auto diff = event.position() - m_left_mousedown_position; 310 auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y(); 311 constexpr int drag_distance_threshold = 5; 312 313 if (distance_travelled_squared <= drag_distance_threshold) 314 return AbstractScrollableWidget::mousemove_event(event); 315 316 VERIFY(!data_type.is_null()); 317 318 if (m_is_dragging) 319 return; 320 321 // An event might sneak in between us constructing the drag operation and the 322 // event loop exec at the end of `drag_operation->exec()' if the user is fast enough. 323 // Prevent this by just ignoring later drag initiations (until the current drag operation ends). 324 TemporaryChange dragging { m_is_dragging, true }; 325 326 dbgln_if(DRAG_DEBUG, "Initiate drag!"); 327 auto drag_operation = DragOperation::construct(); 328 329 drag_operation->set_mime_data(m_model->mime_data(m_selection)); 330 331 auto outcome = drag_operation->exec(); 332 333 switch (outcome) { 334 case DragOperation::Outcome::Accepted: 335 dbgln_if(DRAG_DEBUG, "Drag was accepted!"); 336 break; 337 case DragOperation::Outcome::Cancelled: 338 dbgln_if(DRAG_DEBUG, "Drag was cancelled!"); 339 m_might_drag = false; 340 break; 341 default: 342 VERIFY_NOT_REACHED(); 343 break; 344 } 345} 346 347void AbstractView::mouseup_event(MouseEvent& event) 348{ 349 AbstractScrollableWidget::mouseup_event(event); 350 351 if (!model()) 352 return; 353 354 set_automatic_scrolling_timer_active(false); 355 356 if (m_might_drag) { 357 // We were unsure about unselecting items other than the current one 358 // in mousedown_event(), because we could be seeing a start of a drag. 359 // Since we're here, it was not that; so fix up the selection now. 360 auto index = index_at_event_position(event.position()); 361 if (index.is_valid()) { 362 set_cursor(index, SelectionUpdate::Set, true); 363 } else 364 clear_selection(); 365 m_might_drag = false; 366 update(); 367 } 368 369 if (activates_on_selection()) 370 activate_selected(); 371} 372 373void AbstractView::doubleclick_event(MouseEvent& event) 374{ 375 if (!model()) 376 return; 377 378 if (event.button() != MouseButton::Primary) 379 return; 380 381 m_might_drag = false; 382 383 auto index = index_at_event_position(event.position()); 384 385 if (!index.is_valid()) { 386 clear_selection(); 387 return; 388 } 389 390 if (!m_selection.contains(index)) 391 set_selection(index); 392 393 if (is_editable() && edit_triggers() & EditTrigger::DoubleClicked) 394 begin_editing(cursor_index()); 395 else 396 activate(cursor_index()); 397} 398 399void AbstractView::context_menu_event(ContextMenuEvent& event) 400{ 401 if (!model()) 402 return; 403 404 auto index = index_at_event_position(event.position()); 405 406 if (index.is_valid()) 407 add_selection(index); 408 else 409 clear_selection(); 410 411 if (on_context_menu_request) 412 on_context_menu_request(index, event); 413} 414 415void AbstractView::drop_event(DropEvent& event) 416{ 417 event.accept(); 418 419 if (!model()) 420 return; 421 422 auto index = index_at_event_position(event.position()); 423 if (on_drop) 424 on_drop(index, event); 425} 426 427void AbstractView::set_selection_mode(SelectionMode selection_mode) 428{ 429 if (m_selection_mode == selection_mode) 430 return; 431 m_selection_mode = selection_mode; 432 433 if (m_selection_mode == SelectionMode::NoSelection) 434 m_selection.clear(); 435 else if (m_selection_mode != SelectionMode::SingleSelection && m_selection.size() > 1) { 436 auto first_selected = m_selection.first(); 437 m_selection.clear(); 438 m_selection.set(first_selected); 439 } 440 441 update(); 442} 443 444void AbstractView::set_key_column_and_sort_order(int column, SortOrder sort_order) 445{ 446 m_key_column = column; 447 m_sort_order = sort_order; 448 449 if (model()) 450 model()->sort(column, sort_order); 451 452 update(); 453} 454 455void AbstractView::select_range(ModelIndex const& index) 456{ 457 auto min_row = min(selection_start_index().row(), index.row()); 458 auto max_row = max(selection_start_index().row(), index.row()); 459 auto min_column = min(selection_start_index().column(), index.column()); 460 auto max_column = max(selection_start_index().column(), index.column()); 461 462 clear_selection(); 463 for (auto row = min_row; row <= max_row; ++row) { 464 for (auto column = min_column; column <= max_column; ++column) { 465 auto new_index = model()->index(row, column); 466 if (new_index.is_valid()) 467 toggle_selection(new_index); 468 } 469 } 470} 471 472void AbstractView::set_cursor(ModelIndex index, SelectionUpdate selection_update, bool scroll_cursor_into_view) 473{ 474 if (!model() || !index.is_valid() || selection_mode() == SelectionMode::NoSelection) { 475 m_cursor_index = {}; 476 stop_highlighted_search_timer(); 477 return; 478 } 479 480 if (!m_cursor_index.is_valid() || model()->parent_index(m_cursor_index) != model()->parent_index(index)) 481 stop_highlighted_search_timer(); 482 483 if (selection_mode() == SelectionMode::SingleSelection && (selection_update == SelectionUpdate::Ctrl || selection_update == SelectionUpdate::Shift)) 484 selection_update = SelectionUpdate::Set; 485 486 if (model()->is_within_range(index)) { 487 if (selection_update == SelectionUpdate::Set) { 488 set_selection(index); 489 set_selection_start_index(index); 490 } else if (selection_update == SelectionUpdate::Ctrl) { 491 toggle_selection(index); 492 } else if (selection_update == SelectionUpdate::ClearIfNotSelected) { 493 if (!m_selection.contains(index)) 494 clear_selection(); 495 } else if (selection_update == SelectionUpdate::Shift) { 496 if (!selection_start_index().is_valid()) 497 set_selection_start_index(index); 498 select_range(index); 499 } 500 501 // FIXME: Support the other SelectionUpdate types 502 503 auto old_cursor_index = m_cursor_index; 504 m_cursor_index = index; 505 did_change_cursor_index(old_cursor_index, m_cursor_index); 506 507 if (scroll_cursor_into_view) 508 scroll_into_view(index, true, true); 509 update(); 510 } 511} 512 513void AbstractView::set_edit_triggers(unsigned triggers) 514{ 515 m_edit_triggers = triggers; 516} 517 518void AbstractView::hide_event(HideEvent& event) 519{ 520 stop_editing(); 521 AbstractScrollableWidget::hide_event(event); 522} 523 524void AbstractView::keydown_event(KeyEvent& event) 525{ 526 if (event.alt()) { 527 event.ignore(); 528 return; 529 } 530 531 if (event.key() == KeyCode::Key_F2) { 532 if (is_editable() && edit_triggers() & EditTrigger::EditKeyPressed) { 533 begin_editing(cursor_index()); 534 event.accept(); 535 return; 536 } 537 } 538 539 if (event.key() == KeyCode::Key_Return) { 540 activate_selected(); 541 event.accept(); 542 return; 543 } 544 545 SelectionUpdate selection_update = SelectionUpdate::Set; 546 if (event.modifiers() == KeyModifier::Mod_Shift) { 547 selection_update = SelectionUpdate::Shift; 548 } 549 550 if (event.key() == KeyCode::Key_Left) { 551 move_cursor(CursorMovement::Left, selection_update); 552 event.accept(); 553 return; 554 } 555 if (event.key() == KeyCode::Key_Right) { 556 move_cursor(CursorMovement::Right, selection_update); 557 event.accept(); 558 return; 559 } 560 if (event.key() == KeyCode::Key_Up) { 561 move_cursor(CursorMovement::Up, selection_update); 562 event.accept(); 563 return; 564 } 565 if (event.key() == KeyCode::Key_Down) { 566 move_cursor(CursorMovement::Down, selection_update); 567 event.accept(); 568 return; 569 } 570 if (event.key() == KeyCode::Key_Home) { 571 move_cursor(CursorMovement::Home, selection_update); 572 event.accept(); 573 return; 574 } 575 if (event.key() == KeyCode::Key_End) { 576 move_cursor(CursorMovement::End, selection_update); 577 event.accept(); 578 return; 579 } 580 if (event.key() == KeyCode::Key_PageUp) { 581 move_cursor(CursorMovement::PageUp, selection_update); 582 event.accept(); 583 return; 584 } 585 if (event.key() == KeyCode::Key_PageDown) { 586 move_cursor(CursorMovement::PageDown, selection_update); 587 event.accept(); 588 return; 589 } 590 591 if (is_searchable()) { 592 if (event.key() == KeyCode::Key_Backspace) { 593 if (!m_highlighted_search.is_null()) { 594 // if (event.modifiers() == Mod_Ctrl) { 595 // TODO: delete last word 596 // } 597 Utf8View view(m_highlighted_search); 598 size_t n_code_points = view.length(); 599 if (n_code_points > 1) { 600 n_code_points--; 601 StringBuilder sb; 602 for (auto it = view.begin(); it != view.end(); ++it) { 603 if (n_code_points == 0) 604 break; 605 n_code_points--; 606 sb.append_code_point(*it); 607 } 608 auto index = find_next_search_match(sb.string_view()); 609 if (index.is_valid()) { 610 m_highlighted_search = sb.to_deprecated_string(); 611 highlight_search(index); 612 start_highlighted_search_timer(); 613 } 614 } else { 615 stop_highlighted_search_timer(); 616 } 617 618 event.accept(); 619 return; 620 } 621 } else if (event.key() == KeyCode::Key_Escape) { 622 if (!m_highlighted_search.is_null()) { 623 stop_highlighted_search_timer(); 624 625 event.accept(); 626 return; 627 } 628 } else if (event.key() != KeyCode::Key_Tab && !event.ctrl() && !event.alt() && event.code_point() != 0) { 629 StringBuilder sb; 630 sb.append(m_highlighted_search); 631 sb.append_code_point(event.code_point()); 632 633 auto index = find_next_search_match(sb.string_view()); 634 if (index.is_valid()) { 635 m_highlighted_search = sb.to_deprecated_string(); 636 highlight_search(index); 637 start_highlighted_search_timer(); 638 set_cursor(index, SelectionUpdate::None, true); 639 } 640 641 event.accept(); 642 return; 643 } 644 } 645 646 AbstractScrollableWidget::keydown_event(event); 647} 648 649void AbstractView::stop_highlighted_search_timer() 650{ 651 m_highlighted_search = nullptr; 652 if (m_highlighted_search_timer) 653 m_highlighted_search_timer->stop(); 654 if (m_highlighted_search_index.is_valid()) { 655 m_highlighted_search_index = {}; 656 update(); 657 } 658} 659 660void AbstractView::start_highlighted_search_timer() 661{ 662 if (!m_highlighted_search_timer) { 663 m_highlighted_search_timer = add<Core::Timer>(); 664 m_highlighted_search_timer->set_single_shot(true); 665 m_highlighted_search_timer->on_timeout = [this] { 666 stop_highlighted_search_timer(); 667 }; 668 } 669 m_highlighted_search_timer->set_interval(5 * 1000); 670 m_highlighted_search_timer->restart(); 671} 672 673ModelIndex AbstractView::find_next_search_match(StringView const search) 674{ 675 if (search.is_empty()) 676 return {}; 677 678 auto found_indices = model()->matches(search, Model::MatchesFlag::FirstMatchOnly | Model::MatchesFlag::MatchAtStart | Model::MatchesFlag::CaseInsensitive, model()->parent_index(cursor_index())); 679 680 if (found_indices.is_empty()) 681 return {}; 682 683 return found_indices[0]; 684} 685 686void AbstractView::highlight_search(ModelIndex const index) 687{ 688 m_highlighted_search_index = index; 689 set_selection(index); 690 scroll_into_view(index); 691 update(); 692} 693 694bool AbstractView::is_searchable() const 695{ 696 if (!m_searchable || !model()) 697 return false; 698 return model()->is_searchable(); 699} 700 701void AbstractView::set_searchable(bool searchable) 702{ 703 if (m_searchable == searchable) 704 return; 705 m_searchable = searchable; 706 if (!m_searchable) 707 stop_highlighted_search_timer(); 708} 709 710void AbstractView::draw_item_text(Gfx::Painter& painter, ModelIndex const& index, bool is_selected, Gfx::IntRect const& text_rect, StringView item_text, Gfx::Font const& font, Gfx::TextAlignment alignment, Gfx::TextElision elision, size_t search_highlighting_offset) 711{ 712 if (m_edit_index == index) 713 return; 714 715 Color text_color; 716 if (is_selected) 717 text_color = is_focused() ? palette().selection_text() : palette().inactive_selection_text(); 718 else 719 text_color = index.data(ModelRole::ForegroundColor).to_color(palette().color(foreground_role())); 720 if (index == m_highlighted_search_index) { 721 Utf8View searching_text(m_highlighted_search); 722 auto searching_length = searching_text.length(); 723 if (searching_length > search_highlighting_offset) 724 searching_length -= search_highlighting_offset; 725 else if (search_highlighting_offset > 0) 726 searching_length = 0; 727 728 // Highlight the text background first 729 auto background_searching_length = searching_length; 730 painter.draw_text([&](Gfx::FloatRect const& rect, Utf8CodePointIterator&) { 731 if (background_searching_length > 0) { 732 background_searching_length--; 733 painter.fill_rect(rect.to_type<int>().inflated(0, 2), palette().highlight_searching()); 734 } 735 }, 736 text_rect, item_text, font, alignment, elision); 737 738 // Then draw the text 739 auto text_searching_length = searching_length; 740 auto highlight_text_color = palette().highlight_searching_text(); 741 searching_length = searching_text.length(); 742 painter.draw_text([&](auto const& rect, Utf8CodePointIterator& it) { 743 if (text_searching_length > 0) { 744 text_searching_length--; 745 painter.draw_glyph_or_emoji(rect.location(), it, font, highlight_text_color); 746 } else { 747 painter.draw_glyph_or_emoji(rect.location(), it, font, text_color); 748 } 749 }, 750 text_rect, item_text, font, alignment, elision); 751 } else { 752 if (m_draw_item_text_with_shadow) { 753 painter.draw_text(text_rect.translated(1, 1), item_text, font, alignment, Color::Black, elision); 754 painter.draw_text(text_rect, item_text, font, alignment, Color::White, elision); 755 } else { 756 painter.draw_text(text_rect, item_text, font, alignment, text_color, elision); 757 } 758 } 759} 760 761void AbstractView::focusin_event(FocusEvent& event) 762{ 763 AbstractScrollableWidget::focusin_event(event); 764 765 if (model() && !cursor_index().is_valid()) { 766 move_cursor(CursorMovement::Home, SelectionUpdate::None); 767 clear_selection(); 768 } 769} 770 771void AbstractView::drag_enter_event(DragEvent& event) 772{ 773 if (!model()) 774 return; 775 776 if (!is_editable()) 777 return; 778 779 // NOTE: Right now, AbstractView accepts drags since we won't get "drag move" events 780 // unless we accept the "drag enter" event. 781 // We might be able to reduce event traffic by communicating the set of drag-accepting 782 // rects in this widget to the windowing system somehow. 783 event.accept(); 784 dbgln_if(DRAG_DEBUG, "accepting drag of {}", event.mime_types().first()); 785} 786 787void AbstractView::drag_move_event(DragEvent& event) 788{ 789 if (!model()) 790 return; 791 792 auto index = index_at_event_position(event.position()); 793 ModelIndex new_drop_candidate_index; 794 bool acceptable = model()->accepts_drag(index, event.mime_types()); 795 796 if (acceptable && index.is_valid()) 797 new_drop_candidate_index = index; 798 799 if (acceptable) { 800 m_automatic_scroll_delta = automatic_scroll_delta_from_position(event.position()); 801 set_automatic_scrolling_timer_active(!m_automatic_scroll_delta.is_zero()); 802 } 803 804 if (m_drop_candidate_index != new_drop_candidate_index) { 805 m_drop_candidate_index = new_drop_candidate_index; 806 update(); 807 } 808 if (m_drop_candidate_index.is_valid()) 809 event.accept(); 810} 811 812void AbstractView::drag_leave_event(Event&) 813{ 814 if (m_drop_candidate_index.is_valid()) { 815 m_drop_candidate_index = {}; 816 update(); 817 } 818 819 set_automatic_scrolling_timer_active(false); 820} 821 822void AbstractView::automatic_scrolling_timer_did_fire() 823{ 824 if (m_automatic_scroll_delta.is_zero()) 825 return; 826 827 vertical_scrollbar().increase_slider_by(m_automatic_scroll_delta.y()); 828 horizontal_scrollbar().increase_slider_by(m_automatic_scroll_delta.x()); 829} 830 831}