Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
virtual_list.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <atomic>
5#include <cstddef>
6#include <cstdint>
7#include <functional>
8#include <memory>
9#include <optional>
10#include <string_view>
11#include <typeindex>
12#include <vector>
13
14#include <pulp/view/view.hpp>
16
17namespace pulp::view {
18
20public:
21 struct Rect {
22 float x = 0.0f;
23 float y = 0.0f;
24 float w = 0.0f;
25 float h = 0.0f;
26
27 bool intersects(const Rect& other) const {
28 return x < other.x + other.w && x + w > other.x &&
29 y < other.y + other.h && y + h > other.y;
30 }
31
32 Rect merged(const Rect& other) const {
33 const float nx = std::min(x, other.x);
34 const float ny = std::min(y, other.y);
35 return {
36 nx,
37 ny,
38 std::max(x + w, other.x + other.w) - nx,
39 std::max(y + h, other.y + other.h) - ny
40 };
41 }
42
43 float area() const { return w * h; }
44 };
45
46 void invalidate(const Rect& rect) {
47 if (rect.w <= 0.0f || rect.h <= 0.0f) return;
48 dirty_rects_.push_back(rect);
49 coalesce_if_needed();
50 }
51
52 void invalidate(float x, float y, float w, float h) {
53 invalidate({x, y, w, h});
54 }
55
57 full_repaint_ = true;
58 dirty_rects_.clear();
59 }
60
61 bool is_dirty() const { return full_repaint_ || !dirty_rects_.empty(); }
62 bool needs_full_repaint() const { return full_repaint_; }
63 const std::vector<Rect>& dirty_rects() const { return dirty_rects_; }
64
65 void clear() {
66 dirty_rects_.clear();
67 full_repaint_ = false;
68 ++frame_count_;
69 }
70
71 Rect bounds() const {
72 if (dirty_rects_.empty()) return {};
73 Rect bounds = dirty_rects_[0];
74 for (std::size_t i = 1; i < dirty_rects_.size(); ++i) {
75 bounds = bounds.merged(dirty_rects_[i]);
76 }
77 return bounds;
78 }
79
80 void set_viewport(float width, float height, float full_repaint_threshold = 0.6f) {
81 viewport_w_ = width;
82 viewport_h_ = height;
83 full_repaint_threshold_ = full_repaint_threshold;
84 }
85
86 std::uint64_t frame_count() const { return frame_count_; }
87
88private:
89 void coalesce_if_needed() {
90 if (dirty_rects_.size() > max_rects_) coalesce();
91 if (viewport_w_ <= 0.0f || viewport_h_ <= 0.0f) return;
92
93 float total_area = 0.0f;
94 for (const auto& rect : dirty_rects_) total_area += rect.area();
95 if (total_area > viewport_w_ * viewport_h_ * full_repaint_threshold_) {
97 }
98 }
99
100 void coalesce() {
101 bool merged = true;
102 while (merged) {
103 merged = false;
104 for (std::size_t i = 0; i < dirty_rects_.size(); ++i) {
105 for (std::size_t j = i + 1; j < dirty_rects_.size(); ++j) {
106 auto combined = dirty_rects_[i].merged(dirty_rects_[j]);
107 const float sum_area = dirty_rects_[i].area() + dirty_rects_[j].area();
108 if (combined.area() < sum_area * 1.5f ||
109 dirty_rects_[i].intersects(dirty_rects_[j])) {
110 dirty_rects_[i] = combined;
111 dirty_rects_.erase(dirty_rects_.begin() + static_cast<long>(j));
112 merged = true;
113 break;
114 }
115 }
116 if (merged) break;
117 }
118 }
119 }
120
121 std::vector<Rect> dirty_rects_;
122 bool full_repaint_ = true;
123 float viewport_w_ = 0.0f;
124 float viewport_h_ = 0.0f;
125 float full_repaint_threshold_ = 0.6f;
126 std::uint64_t frame_count_ = 0;
127 static constexpr std::size_t max_rects_ = 16;
128};
129
135class VirtualList : public View {
136public:
137 enum class SelectionMode { none, single, multi };
138
139 using RowFactory = std::function<std::unique_ptr<View>(std::size_t slot)>;
140 using RowBinder = std::function<void(View& row, std::size_t index)>;
141 using RowReleaser = std::function<void(View& row)>;
143 std::function<std::optional<std::size_t>(std::string_view query,
144 std::size_t from_index)>;
145
147 ~VirtualList() override;
148
149 void set_row_count(std::size_t n);
150 std::size_t row_count() const { return row_count_; }
151
152 void set_row_height(float px);
153 float row_height() const { return row_height_; }
154
155 void set_overscan(int rows);
156 int overscan() const { return overscan_rows_; }
157
162
164 SelectionMode selection_mode() const { return selection_mode_; }
165 const std::vector<std::size_t>& selection() const { return selection_; }
166 bool is_selected(std::size_t index) const;
168 void select_row(std::size_t index);
169 void toggle_row_selection(std::size_t index);
170
171 std::optional<std::size_t> focused_index() const { return focused_index_; }
172 void set_focused_index(std::size_t index);
173
174 void scroll_to_row(std::size_t index);
175 void set_scroll_y(float y);
176 void scroll_by(float dy);
177 float scroll_y() const { return scroll_y_; }
178 float content_height() const;
179
180 void on_row_activated(std::function<void(std::size_t)> cb) {
181 on_row_activated_ = std::move(cb);
182 }
183 void on_selection_changed(std::function<void(const std::vector<std::size_t>&)> cb) {
184 on_selection_changed_ = std::move(cb);
185 }
187 type_to_search_ = std::move(handler);
188 }
189 void clear_type_to_search_buffer() { type_buffer_.clear(); }
190
191 std::size_t realized_row_count() const { return row_slots_.size(); }
192 View* realized_row_at_slot(std::size_t slot);
193 const View* realized_row_at_slot(std::size_t slot) const;
194 std::optional<std::size_t> bound_index_for_slot(std::size_t slot) const;
195 std::size_t first_realized_index() const { return first_realized_index_; }
196 std::vector<std::size_t> realized_indices() const;
197
198 const VirtualListDirtyTracker& dirty_tracker() const { return dirty_tracker_; }
200
201 void on_resized() override;
202 View* hit_test(Point local_point) override;
203 void layout_children() override;
204 bool owns_child_layout() const override { return true; }
205 void paint(canvas::Canvas& canvas) override;
206 void on_mouse_event(const MouseEvent& event) override;
207 void on_mouse_drag(Point pos) override;
208 bool on_key_event(const KeyEvent& event) override;
209 void on_text_input(const TextInputEvent& event) override;
210 bool wants_wheel_scroll() const override;
211
212private:
213 enum class UpdateResult { unchanged, changed, interrupted };
214 struct SelectionUpdate {
215 bool changed = false;
216 bool interrupted = false;
217 };
218
219 struct RowSlot {
220 View* row = nullptr;
221 std::optional<std::size_t> index;
222 };
223
224 std::size_t desired_pool_size() const;
225 bool clear_pool();
226 bool ensure_pool_size(std::size_t desired);
227 bool release_row(RowSlot& slot);
228 bool update_window(bool force_rebind);
229 bool bind_slot(RowSlot& slot, std::size_t index, bool force_rebind,
230 std::uint64_t update_generation);
231 void position_slot(RowSlot& slot);
232 void update_row_accessibility(RowSlot& slot);
233 float max_scroll_y() const;
234 bool scroll_to_row_internal(std::size_t index);
235 UpdateResult set_scroll_y_internal(float y, bool request);
236 bool apply_pending_scroll_to_row();
237 bool apply_pending_selected_row();
238 void mark_scroll_dirty(float old_y, float new_y);
239 float row_width() const;
240
241 std::optional<std::size_t> row_at(Point local) const;
242 void choose_row(std::size_t index, bool extend, bool toggle, bool claim_focus = false);
243 SelectionUpdate set_selection_sanitized(std::vector<std::size_t> next, bool notify = true);
244 void move_focus_to(std::size_t index, bool extend);
245 std::optional<std::size_t> keyboard_anchor_index() const;
246 std::size_t page_row_delta() const;
247
248 bool scrollbar_visible() const;
249 float scrollbar_width() const;
250 float scrollbar_thumb_length() const;
251 float scrollbar_thumb_y() const;
252 bool point_in_scrollbar(Point local) const;
253 bool point_in_scrollbar_thumb(Point local) const;
254
255 std::size_t row_count_ = 0;
256 float row_height_ = 24.0f;
257 int overscan_rows_ = 3;
258 float scroll_y_ = 0.0f;
259 std::optional<std::size_t> pending_scroll_to_row_;
260 std::optional<std::size_t> pending_selected_row_;
261
262 RowFactory row_factory_;
263 RowBinder row_binder_;
264 RowReleaser row_releaser_;
265 std::vector<RowSlot> row_slots_;
266 std::size_t first_realized_index_ = 0;
267
268 // Per-class recycling pool for released rows (planning 2.3). Rows that opt
269 // in via View::supports_reuse() are parked here on release and re-acquired
270 // (in place of a fresh factory() call) when the pool grows. Behavior-neutral
271 // for the default View rows the built-in list uses: they never opt in, so
272 // release() destroys them and acquire() always falls through to the factory.
273 // `row_type_` remembers the concrete row type the factory produces so the
274 // type-keyed pool can be queried before the type is otherwise known.
275 ViewPool row_pool_;
276 std::optional<std::type_index> row_type_;
277
278 SelectionMode selection_mode_ = SelectionMode::single;
279 std::vector<std::size_t> selection_;
280 std::optional<std::size_t> focused_index_;
281 std::optional<std::size_t> selection_anchor_;
282 std::function<void(std::size_t)> on_row_activated_;
283 std::function<void(const std::vector<std::size_t>&)> on_selection_changed_;
284 TypeToSearchHandler type_to_search_;
285 std::string type_buffer_;
286
287 VirtualListDirtyTracker dirty_tracker_;
288 bool dragging_scrollbar_ = false;
289 float scrollbar_drag_offset_ = 0.0f;
290 bool pool_resize_in_progress_ = false;
291 bool destroying_ = false;
292 std::uint64_t window_generation_ = 0;
293 std::shared_ptr<std::atomic<bool>> alive_ = std::make_shared<std::atomic<bool>>(true);
294};
295
296} // namespace pulp::view
Definition canvas.hpp:240
@ toggle
Definition view.hpp:989
@ row
Definition view.hpp:991
Definition virtual_list.hpp:19
bool needs_full_repaint() const
Definition virtual_list.hpp:62
void invalidate(float x, float y, float w, float h)
Definition virtual_list.hpp:52
void invalidate(const Rect &rect)
Definition virtual_list.hpp:46
void invalidate_all()
Definition virtual_list.hpp:56
void clear()
Definition virtual_list.hpp:65
const std::vector< Rect > & dirty_rects() const
Definition virtual_list.hpp:63
void set_viewport(float width, float height, float full_repaint_threshold=0.6f)
Definition virtual_list.hpp:80
Rect bounds() const
Definition virtual_list.hpp:71
bool is_dirty() const
Definition virtual_list.hpp:61
std::uint64_t frame_count() const
Definition virtual_list.hpp:86
bool owns_child_layout() const override
Definition virtual_list.hpp:204
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
void layout_children() override
SelectionMode
Definition virtual_list.hpp:137
@ none
Definition virtual_list.hpp:137
@ single
Definition virtual_list.hpp:137
@ multi
Definition virtual_list.hpp:137
void set_row_binder(RowBinder binder)
void scroll_to_row(std::size_t index)
std::size_t realized_row_count() const
Definition virtual_list.hpp:191
float row_height() const
Definition virtual_list.hpp:153
void set_row_releaser(RowReleaser releaser)
std::function< std::unique_ptr< View >(std::size_t slot)> RowFactory
Definition virtual_list.hpp:139
std::optional< std::size_t > focused_index() const
Definition virtual_list.hpp:171
bool on_key_event(const KeyEvent &event) override
Key event with modifiers and up/down state.
SelectionMode selection_mode() const
Definition virtual_list.hpp:164
bool is_selected(std::size_t index) const
void paint(canvas::Canvas &canvas) override
void on_row_activated(std::function< void(std::size_t)> cb)
Definition virtual_list.hpp:180
std::optional< std::size_t > bound_index_for_slot(std::size_t slot) const
void set_scroll_y(float y)
float content_height() const
bool wants_wheel_scroll() const override
True if this widget is itself a scroll container that should consume wheel input before an ancestor s...
std::size_t first_realized_index() const
Definition virtual_list.hpp:195
void set_focused_index(std::size_t index)
void set_row_factory(RowFactory factory)
void set_row_height(float px)
const View * realized_row_at_slot(std::size_t slot) const
View * hit_test(Point local_point) override
void on_mouse_drag(Point pos) override
void clear_type_to_search_buffer()
Definition virtual_list.hpp:189
void on_resized() override
void toggle_row_selection(std::size_t index)
void set_selection_mode(SelectionMode mode)
std::function< void(View &row, std::size_t index)> RowBinder
Definition virtual_list.hpp:140
void scroll_by(float dy)
std::vector< std::size_t > realized_indices() const
void on_selection_changed(std::function< void(const std::vector< std::size_t > &)> cb)
Definition virtual_list.hpp:183
void set_type_to_search(TypeToSearchHandler handler)
Definition virtual_list.hpp:186
std::function< std::optional< std::size_t >(std::string_view query, std::size_t from_index)> TypeToSearchHandler
Definition virtual_list.hpp:142
void on_text_input(const TextInputEvent &event) override
Text input from keyboard/IME (separate from key events).
const std::vector< std::size_t > & selection() const
Definition virtual_list.hpp:165
std::function< void(View &row)> RowReleaser
Definition virtual_list.hpp:141
void select_row(std::size_t index)
void set_row_count(std::size_t n)
void set_overscan(int rows)
float scroll_y() const
Definition virtual_list.hpp:177
View * realized_row_at_slot(std::size_t slot)
int overscan() const
Definition virtual_list.hpp:156
const VirtualListDirtyTracker & dirty_tracker() const
Definition virtual_list.hpp:198
std::size_t row_count() const
Definition virtual_list.hpp:150
Definition affine_transform.hpp:24
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ px
Absolute pixels.
Definition geometry.hpp:393
@ h
Definition input_events.hpp:39
@ j
Definition input_events.hpp:40
@ y
Definition input_events.hpp:42
@ n
Definition input_events.hpp:40
@ i
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
@ w
Definition input_events.hpp:41
@ height
Definition css_animation.hpp:128
@ width
Definition css_animation.hpp:127
@ changed
Definition gesture.hpp:17
Rich key event with key code, modifiers, and repeat state.
Definition input_events.hpp:193
Rich mouse/pointer event with position, modifiers, button state, pointer ID, and stylus properties.
Definition input_events.hpp:104
Definition geometry.hpp:20
Text input from IME or keyboard composition (separate from KeyEvent).
Definition input_events.hpp:210
Definition virtual_list.hpp:21
bool intersects(const Rect &other) const
Definition virtual_list.hpp:27
Rect merged(const Rect &other) const
Definition virtual_list.hpp:32
float w
Definition virtual_list.hpp:24
float y
Definition virtual_list.hpp:23
float x
Definition virtual_list.hpp:22
float h
Definition virtual_list.hpp:25
float area() const
Definition virtual_list.hpp:43