diff --git a/Userland/Libraries/LibGUI/AbstractView.cpp b/Userland/Libraries/LibGUI/AbstractView.cpp index 8ac69da289..6e41342142 100644 --- a/Userland/Libraries/LibGUI/AbstractView.cpp +++ b/Userland/Libraries/LibGUI/AbstractView.cpp @@ -208,7 +208,8 @@ void AbstractView::notify_selection_changed(Badge) did_update_selection(); if (on_selection_change) on_selection_change(); - update(); + if (!m_suppress_update_on_selection_change) + update(); } NonnullRefPtr AbstractView::font_for_index(const ModelIndex& index) const diff --git a/Userland/Libraries/LibGUI/AbstractView.h b/Userland/Libraries/LibGUI/AbstractView.h index e8ce1f0114..15018ab301 100644 --- a/Userland/Libraries/LibGUI/AbstractView.h +++ b/Userland/Libraries/LibGUI/AbstractView.h @@ -171,6 +171,8 @@ protected: void draw_item_text(Gfx::Painter&, const ModelIndex&, bool, const Gfx::IntRect&, const StringView&, const Gfx::Font&, Gfx::TextAlignment, Gfx::TextElision); + void set_suppress_update_on_selection_change(bool value) { m_suppress_update_on_selection_change = value; } + virtual void did_scroll() override; void set_hovered_index(const ModelIndex&); void activate(const ModelIndex&); @@ -215,6 +217,7 @@ private: bool m_tab_key_navigation_enabled { false }; bool m_is_dragging { false }; bool m_draw_item_text_with_shadow { false }; + bool m_suppress_update_on_selection_change { false }; }; } diff --git a/Userland/Libraries/LibGUI/IconView.cpp b/Userland/Libraries/LibGUI/IconView.cpp index d5108d3a26..5308aa077c 100644 --- a/Userland/Libraries/LibGUI/IconView.cpp +++ b/Userland/Libraries/LibGUI/IconView.cpp @@ -258,7 +258,7 @@ void IconView::mouseup_event(MouseEvent& event) m_rubber_banding = false; if (m_out_of_view_timer) m_out_of_view_timer->stop(); - update(); + update(to_widget_rect(Gfx::IntRect::from_two_points(m_rubber_band_origin, m_rubber_band_current))); } AbstractView::mouseup_event(event); } @@ -268,8 +268,15 @@ bool IconView::update_rubber_banding(const Gfx::IntPoint& position) auto adjusted_position = to_content_position(position); if (m_rubber_band_current != adjusted_position) { auto prev_rect = Gfx::IntRect::from_two_points(m_rubber_band_origin, m_rubber_band_current); + auto prev_rubber_band_fill_rect = prev_rect.shrunken(1, 1); m_rubber_band_current = adjusted_position; auto rubber_band_rect = Gfx::IntRect::from_two_points(m_rubber_band_origin, m_rubber_band_current); + auto rubber_band_fill_rect = rubber_band_rect.shrunken(1, 1); + + for (auto& rect : prev_rubber_band_fill_rect.shatter(rubber_band_fill_rect)) + update(to_widget_rect(rect.inflated(1, 1))); + for (auto& rect : rubber_band_fill_rect.shatter(prev_rubber_band_fill_rect)) + update(to_widget_rect(rect.inflated(1, 1))); // If the rectangle width or height is 0, we still want to be able // to match the items in the path. An easy work-around for this @@ -302,11 +309,16 @@ bool IconView::update_rubber_banding(const Gfx::IntPoint& position) return IterationDecision::Continue; }); + // We're changing the selection and invalidating those items, so + // no need to trigger a full re-render for each item + set_suppress_update_on_selection_change(true); + // Now toggle all items that are no longer in the selected area, once only for_each_item_intersecting_rects(deselect_area, [&](ItemData& item_data) -> IterationDecision { if (!item_data.selection_toggled && item_data.is_intersecting(prev_rect) && !item_data.is_intersecting(rubber_band_rect)) { item_data.selection_toggled = true; toggle_selection(item_data); + update(to_widget_rect(item_data.rect())); } return IterationDecision::Continue; }); @@ -315,11 +327,13 @@ bool IconView::update_rubber_banding(const Gfx::IntPoint& position) if (!item_data.selection_toggled && !item_data.is_intersecting(prev_rect) && item_data.is_intersecting(rubber_band_rect)) { item_data.selection_toggled = true; toggle_selection(item_data); + update(to_widget_rect(item_data.rect())); } return IterationDecision::Continue; }); - update(); + set_suppress_update_on_selection_change(false); + return true; } return false; @@ -491,8 +505,8 @@ void IconView::paint_event(PaintEvent& event) painter.add_clip_rect(widget_inner_rect()); painter.add_clip_rect(event.rect()); - if (fill_with_background_color()) - painter.fill_rect(event.rect(), widget_background_color); + painter.fill_rect(event.rect(), fill_with_background_color() ? widget_background_color : Color::Transparent); + painter.translate(frame_thickness(), frame_thickness()); painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); diff --git a/Userland/Libraries/LibGUI/IconView.h b/Userland/Libraries/LibGUI/IconView.h index 71cf41b8ed..ecc895f645 100644 --- a/Userland/Libraries/LibGUI/IconView.h +++ b/Userland/Libraries/LibGUI/IconView.h @@ -109,6 +109,11 @@ private: VERIFY(valid); return icon_rect.contains(point) || text_rect.contains(point); } + + Gfx::IntRect rect() const + { + return text_rect.united(icon_rect); + } }; template diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 62b5c3de3b..8238fe599e 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -129,6 +129,8 @@ public: m_state_stack.take_last(); } + IntRect clip_rect() const { return state().clip_rect; } + protected: IntPoint translation() const { return state().translation; } IntRect to_physical(const IntRect& r) const { return r.translated(translation()) * scale(); } @@ -139,7 +141,6 @@ protected: void fill_rect_with_draw_op(const IntRect&, Color); void blit_with_opacity(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, float opacity, bool apply_alpha = true); void draw_physical_pixel(const IntPoint&, Color, int thickness = 1); - IntRect clip_rect() const { return state().clip_rect; } struct State { const Font* font;