@effindomv2/fui-rs 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +1 -1
- package/README.md +69 -0
- package/package.json +1 -1
- package/scripts/hostgen/rust-host-events.ts +43 -13
- package/src/app.rs +7 -0
- package/src/assets.rs +2 -2
- package/src/bindings/ui.rs +2 -0
- package/src/bridge_callbacks.rs +27 -17
- package/src/controls/button.rs +1 -0
- package/src/controls/checkbox.rs +1 -0
- package/src/controls/combobox.rs +5 -3
- package/src/controls/dropdown.rs +3 -1
- package/src/controls/internal/pressable_labeled_control.rs +1 -0
- package/src/controls/internal/text_input_core.rs +7 -3
- package/src/controls/radio_button.rs +1 -0
- package/src/controls/slider.rs +1 -0
- package/src/controls/switch.rs +1 -0
- package/src/controls/tests.rs +19 -7
- package/src/debug.rs +8 -5
- package/src/drag_drop.rs +1 -3
- package/src/drawing.rs +8 -0
- package/src/event.rs +39 -27
- package/src/fetch.rs +21 -7
- package/src/ffi.rs +3 -1
- package/src/file.rs +88 -59
- package/src/host_events.rs +41 -0
- package/src/lib.rs +52 -9
- package/src/node/core.rs +2 -21
- package/src/node/helpers.rs +1 -1
- package/src/node/mod.rs +1 -1
- package/src/node/scroll_bar.rs +101 -0
- package/src/node/scroll_box.rs +7 -0
- package/src/node/scroll_view.rs +20 -14
- package/src/node/virtual_list.rs +120 -33
- package/src/platform.rs +3 -0
- package/src/popup_presenter.rs +1 -0
- package/src/theme.rs +2 -2
- package/src/timers.rs +1 -1
- package/src/typography.rs +6 -14
- package/src/worker.rs +25 -9
- package/src/worker_runtime.rs +61 -17
package/src/node/core.rs
CHANGED
|
@@ -262,7 +262,7 @@ pub(crate) struct LinearGradient {
|
|
|
262
262
|
pub(crate) offsets: Vec<f32>,
|
|
263
263
|
pub(crate) colors: Vec<u32>,
|
|
264
264
|
}
|
|
265
|
-
#[derive(Clone)]
|
|
265
|
+
#[derive(Clone, Default)]
|
|
266
266
|
pub(crate) struct FlexBoxProps {
|
|
267
267
|
pub(crate) width: Option<(f32, Unit)>,
|
|
268
268
|
pub(crate) height: Option<(f32, Unit)>,
|
|
@@ -278,25 +278,6 @@ pub(crate) struct FlexBoxProps {
|
|
|
278
278
|
pub(crate) transitions: Option<NodeTransitions>,
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
impl Default for FlexBoxProps {
|
|
282
|
-
fn default() -> Self {
|
|
283
|
-
Self {
|
|
284
|
-
width: None,
|
|
285
|
-
height: None,
|
|
286
|
-
bg_color: None,
|
|
287
|
-
padding: None,
|
|
288
|
-
flex_direction: None,
|
|
289
|
-
box_style: None,
|
|
290
|
-
opacity: None,
|
|
291
|
-
blur_sigma: None,
|
|
292
|
-
drop_shadow: None,
|
|
293
|
-
background_blur_sigma: None,
|
|
294
|
-
linear_gradient: None,
|
|
295
|
-
transitions: None,
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
281
|
#[derive(Clone, Default)]
|
|
301
282
|
pub(crate) struct TextProps {
|
|
302
283
|
pub(crate) content: String,
|
|
@@ -2219,7 +2200,7 @@ pub trait Node: Clone {
|
|
|
2219
2200
|
}
|
|
2220
2201
|
}
|
|
2221
2202
|
|
|
2222
|
-
impl<T: Node
|
|
2203
|
+
impl<T: Node> Node for &T {
|
|
2223
2204
|
fn retained_node_ref(&self) -> NodeRef {
|
|
2224
2205
|
(*self).retained_node_ref()
|
|
2225
2206
|
}
|
package/src/node/helpers.rs
CHANGED
|
@@ -31,7 +31,7 @@ pub fn scroll_box() -> ScrollBox {
|
|
|
31
31
|
ScrollBox::new()
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
pub fn virtual_list(total_items: i32, item_height: f32) -> VirtualList {
|
|
34
|
+
pub fn virtual_list(total_items: i32, item_height: f32) -> VirtualList<FlexBox> {
|
|
35
35
|
VirtualList::new(total_items, item_height)
|
|
36
36
|
}
|
|
37
37
|
|
package/src/node/mod.rs
CHANGED
|
@@ -48,7 +48,7 @@ pub use helpers::{
|
|
|
48
48
|
pub use image::ImageNode;
|
|
49
49
|
pub(crate) use presenter_host_style::HostStyleLayers;
|
|
50
50
|
pub use presenter_host_style::{Corners, EdgeInsets, PresenterHostStyle, Shadow};
|
|
51
|
-
pub use scroll_bar::{ScrollBar, ScrollBarVisibility};
|
|
51
|
+
pub use scroll_bar::{ScrollBar, ScrollBarStyle, ScrollBarVisibility};
|
|
52
52
|
pub use scroll_box::ScrollBox;
|
|
53
53
|
pub use scroll_state::ScrollState;
|
|
54
54
|
pub use scroll_view::ScrollView;
|
package/src/node/scroll_bar.rs
CHANGED
|
@@ -12,6 +12,91 @@ const DEFAULT_TRACK_THICKNESS: f32 = 8.0;
|
|
|
12
12
|
const DEFAULT_THUMB_THICKNESS: f32 = 8.0;
|
|
13
13
|
const DEFAULT_MIN_THUMB_SIZE: f32 = 18.0;
|
|
14
14
|
|
|
15
|
+
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
16
|
+
pub struct ScrollBarStyle {
|
|
17
|
+
track_width: f32,
|
|
18
|
+
thumb_width: f32,
|
|
19
|
+
thumb_min_height: f32,
|
|
20
|
+
track_corner_radius: f32,
|
|
21
|
+
thumb_corner_radius: f32,
|
|
22
|
+
track_color: Option<u32>,
|
|
23
|
+
thumb_color: Option<u32>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl ScrollBarStyle {
|
|
27
|
+
pub fn new() -> Self {
|
|
28
|
+
Self::default()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub fn track_width(mut self, value: f32) -> Self {
|
|
32
|
+
self.track_width = value;
|
|
33
|
+
self
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn thumb_width(mut self, value: f32) -> Self {
|
|
37
|
+
self.thumb_width = value;
|
|
38
|
+
self
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn thumb_min_height(mut self, value: f32) -> Self {
|
|
42
|
+
self.thumb_min_height = value;
|
|
43
|
+
self
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn track_corner_radius(mut self, value: f32) -> Self {
|
|
47
|
+
self.track_corner_radius = value;
|
|
48
|
+
self
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn thumb_corner_radius(mut self, value: f32) -> Self {
|
|
52
|
+
self.thumb_corner_radius = value;
|
|
53
|
+
self
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub fn track_color(mut self, value: u32) -> Self {
|
|
57
|
+
self.track_color = Some(value);
|
|
58
|
+
self
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub fn thumb_color(mut self, value: u32) -> Self {
|
|
62
|
+
self.thumb_color = Some(value);
|
|
63
|
+
self
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub(crate) fn apply_to(self, scroll_bar: &ScrollBar) {
|
|
67
|
+
scroll_bar
|
|
68
|
+
.track_width(self.track_width)
|
|
69
|
+
.thumb_width(self.thumb_width)
|
|
70
|
+
.thumb_min_height(self.thumb_min_height)
|
|
71
|
+
.track_corner_radius(self.track_corner_radius)
|
|
72
|
+
.thumb_corner_radius(self.thumb_corner_radius);
|
|
73
|
+
if let Some(color) = self.track_color {
|
|
74
|
+
scroll_bar.track_color(color);
|
|
75
|
+
} else {
|
|
76
|
+
scroll_bar.clear_track_color();
|
|
77
|
+
}
|
|
78
|
+
if let Some(color) = self.thumb_color {
|
|
79
|
+
scroll_bar.thumb_color(color);
|
|
80
|
+
} else {
|
|
81
|
+
scroll_bar.clear_thumb_color();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
impl Default for ScrollBarStyle {
|
|
87
|
+
fn default() -> Self {
|
|
88
|
+
Self {
|
|
89
|
+
track_width: DEFAULT_TRACK_THICKNESS,
|
|
90
|
+
thumb_width: DEFAULT_THUMB_THICKNESS,
|
|
91
|
+
thumb_min_height: DEFAULT_MIN_THUMB_SIZE,
|
|
92
|
+
track_corner_radius: 0.0,
|
|
93
|
+
thumb_corner_radius: 0.0,
|
|
94
|
+
track_color: None,
|
|
95
|
+
thumb_color: None,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
15
100
|
fn clamp(value: f32, min: f32, max: f32) -> f32 {
|
|
16
101
|
if value < min {
|
|
17
102
|
return min;
|
|
@@ -237,6 +322,14 @@ impl ScrollBar {
|
|
|
237
322
|
self
|
|
238
323
|
}
|
|
239
324
|
|
|
325
|
+
fn clear_track_color(&self) {
|
|
326
|
+
self.inner.track_color_overridden.set(false);
|
|
327
|
+
self.inner
|
|
328
|
+
.track_color
|
|
329
|
+
.set(current_theme().colors.scrollbar_track);
|
|
330
|
+
self.apply_color_style();
|
|
331
|
+
}
|
|
332
|
+
|
|
240
333
|
pub fn thumb_color(&self, color: u32) -> &Self {
|
|
241
334
|
self.inner.thumb_color_overridden.set(true);
|
|
242
335
|
self.inner.thumb_color.set(color);
|
|
@@ -244,6 +337,14 @@ impl ScrollBar {
|
|
|
244
337
|
self
|
|
245
338
|
}
|
|
246
339
|
|
|
340
|
+
fn clear_thumb_color(&self) {
|
|
341
|
+
self.inner.thumb_color_overridden.set(false);
|
|
342
|
+
self.inner
|
|
343
|
+
.thumb_color
|
|
344
|
+
.set(current_theme().colors.scrollbar_thumb);
|
|
345
|
+
self.apply_color_style();
|
|
346
|
+
}
|
|
347
|
+
|
|
247
348
|
pub fn bind_scroll_handle(&self, handle: u64) {
|
|
248
349
|
self.inner.target_handle.set(handle);
|
|
249
350
|
self.inner
|
package/src/node/scroll_box.rs
CHANGED
|
@@ -219,6 +219,13 @@ impl ScrollBox {
|
|
|
219
219
|
self
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
pub fn scrollbar_style(&self, style: ScrollBarStyle) -> &Self {
|
|
223
|
+
style.apply_to(&self.inner.vertical_scrollbar);
|
|
224
|
+
style.apply_to(&self.inner.horizontal_scrollbar);
|
|
225
|
+
self.refresh_chrome();
|
|
226
|
+
self
|
|
227
|
+
}
|
|
228
|
+
|
|
222
229
|
fn attach_listeners(&self) {
|
|
223
230
|
let weak = Rc::downgrade(&self.inner);
|
|
224
231
|
self.inner
|
package/src/node/scroll_view.rs
CHANGED
|
@@ -16,6 +16,12 @@ pub struct ScrollView {
|
|
|
16
16
|
active_animations: Rc<RefCell<ScrollViewAnimationState>>,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
impl Default for ScrollView {
|
|
20
|
+
fn default() -> Self {
|
|
21
|
+
Self::new()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
impl ScrollView {
|
|
20
26
|
pub fn new() -> Self {
|
|
21
27
|
let core = Rc::new(RefCell::new(NodeCore::new(NodeKind::ScrollView)));
|
|
@@ -473,6 +479,20 @@ impl ScrollView {
|
|
|
473
479
|
}
|
|
474
480
|
}
|
|
475
481
|
|
|
482
|
+
impl Node for ScrollView {
|
|
483
|
+
fn retained_node_ref(&self) -> NodeRef {
|
|
484
|
+
NodeRef::from_node(self.core.clone(), self.clone())
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
fn build_self(&self) {
|
|
488
|
+
apply_scroll_view_props(
|
|
489
|
+
self.handle(),
|
|
490
|
+
&self.props.borrow(),
|
|
491
|
+
self.core.borrow().behavior.clone(),
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
476
496
|
#[cfg(test)]
|
|
477
497
|
mod tests {
|
|
478
498
|
use super::*;
|
|
@@ -544,17 +564,3 @@ mod tests {
|
|
|
544
564
|
)));
|
|
545
565
|
}
|
|
546
566
|
}
|
|
547
|
-
|
|
548
|
-
impl Node for ScrollView {
|
|
549
|
-
fn retained_node_ref(&self) -> NodeRef {
|
|
550
|
-
NodeRef::from_node(self.core.clone(), self.clone())
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
fn build_self(&self) {
|
|
554
|
-
apply_scroll_view_props(
|
|
555
|
-
self.handle(),
|
|
556
|
-
&self.props.borrow(),
|
|
557
|
-
self.core.borrow().behavior.clone(),
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
}
|
package/src/node/virtual_list.rs
CHANGED
|
@@ -4,6 +4,7 @@ use crate::controls::selection_area;
|
|
|
4
4
|
use crate::controls::SelectionArea;
|
|
5
5
|
use crate::frame_scheduler::mark_needs_commit;
|
|
6
6
|
use crate::signal::Subscription;
|
|
7
|
+
use std::any::Any;
|
|
7
8
|
use std::cell::{Cell, RefCell};
|
|
8
9
|
use std::rc::Rc;
|
|
9
10
|
|
|
@@ -11,9 +12,9 @@ const FULL_SIZE: f32 = 100.0;
|
|
|
11
12
|
const DEFAULT_MAX_VISIBLE_ITEMS: i32 = 20;
|
|
12
13
|
const POOL_OVERSCAN_ITEMS: i32 = 2;
|
|
13
14
|
const MISSING_BIND_ITEM_MESSAGE: &str =
|
|
14
|
-
"VirtualList: item renderer not configured. Call .
|
|
15
|
+
"VirtualList: item renderer not configured. Call .on_bind_item() after construction.";
|
|
15
16
|
|
|
16
|
-
type VirtualListBinder = Rc<dyn Fn(
|
|
17
|
+
type VirtualListBinder = Rc<dyn Fn(usize, i32)>;
|
|
17
18
|
|
|
18
19
|
struct VirtualListInner {
|
|
19
20
|
root: FlexBox,
|
|
@@ -27,18 +28,28 @@ struct VirtualListInner {
|
|
|
27
28
|
pool_size: i32,
|
|
28
29
|
pool_rows: Vec<SelectionArea>,
|
|
29
30
|
pool_containers: Vec<FlexBox>,
|
|
31
|
+
row_state_owner: RefCell<Option<Rc<dyn Any>>>,
|
|
30
32
|
pool_item_index_by_row: RefCell<Vec<i32>>,
|
|
31
33
|
subscriptions: RefCell<Vec<Subscription>>,
|
|
32
34
|
current_first_visible_index: Cell<i32>,
|
|
33
35
|
current_last_visible_index: Cell<i32>,
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
pub struct VirtualList {
|
|
38
|
+
pub struct VirtualList<T = FlexBox> {
|
|
38
39
|
inner: Rc<VirtualListInner>,
|
|
40
|
+
row_states: Rc<Vec<T>>,
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
impl VirtualList {
|
|
43
|
+
impl<T> Clone for VirtualList<T> {
|
|
44
|
+
fn clone(&self) -> Self {
|
|
45
|
+
Self {
|
|
46
|
+
inner: self.inner.clone(),
|
|
47
|
+
row_states: self.row_states.clone(),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl VirtualList<FlexBox> {
|
|
42
53
|
pub fn new(total_items: i32, item_height: f32) -> Self {
|
|
43
54
|
Self::with_max_visible(total_items, item_height, DEFAULT_MAX_VISIBLE_ITEMS)
|
|
44
55
|
}
|
|
@@ -105,13 +116,43 @@ impl VirtualList {
|
|
|
105
116
|
bottom_spacer,
|
|
106
117
|
pool_size,
|
|
107
118
|
pool_rows,
|
|
108
|
-
pool_containers,
|
|
119
|
+
pool_containers: pool_containers.clone(),
|
|
120
|
+
row_state_owner: RefCell::new(None),
|
|
109
121
|
pool_item_index_by_row: RefCell::new(pool_item_index_by_row),
|
|
110
122
|
subscriptions: RefCell::new(Vec::new()),
|
|
111
123
|
current_first_visible_index: Cell::new(-1),
|
|
112
124
|
current_last_visible_index: Cell::new(-1),
|
|
113
125
|
});
|
|
114
|
-
let
|
|
126
|
+
let row_states = Rc::new(pool_containers);
|
|
127
|
+
inner
|
|
128
|
+
.row_state_owner
|
|
129
|
+
.replace(Some(row_states.clone() as Rc<dyn Any>));
|
|
130
|
+
let list = Self { inner, row_states };
|
|
131
|
+
list.attach_listeners();
|
|
132
|
+
list
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
impl<T: 'static> VirtualList<T> {
|
|
137
|
+
pub fn item_template<U: 'static>(self, template: impl Fn(&FlexBox) -> U) -> VirtualList<U> {
|
|
138
|
+
let row_states = Rc::new(
|
|
139
|
+
self.inner
|
|
140
|
+
.pool_containers
|
|
141
|
+
.iter()
|
|
142
|
+
.map(template)
|
|
143
|
+
.collect::<Vec<_>>(),
|
|
144
|
+
);
|
|
145
|
+
self.inner
|
|
146
|
+
.row_state_owner
|
|
147
|
+
.replace(Some(row_states.clone() as Rc<dyn Any>));
|
|
148
|
+
*self.inner.bind_item.borrow_mut() = Rc::new(|_, _| panic!("{MISSING_BIND_ITEM_MESSAGE}"));
|
|
149
|
+
self.inner.current_first_visible_index.set(-1);
|
|
150
|
+
self.inner.current_last_visible_index.set(-1);
|
|
151
|
+
self.inner.subscriptions.borrow_mut().clear();
|
|
152
|
+
let list = VirtualList {
|
|
153
|
+
inner: self.inner,
|
|
154
|
+
row_states,
|
|
155
|
+
};
|
|
115
156
|
list.attach_listeners();
|
|
116
157
|
list
|
|
117
158
|
}
|
|
@@ -181,19 +222,17 @@ impl VirtualList {
|
|
|
181
222
|
self
|
|
182
223
|
}
|
|
183
224
|
|
|
184
|
-
pub fn on_bind_item(&self, renderer: impl Fn(&
|
|
185
|
-
|
|
225
|
+
pub fn on_bind_item(&self, renderer: impl Fn(&T, i32) + 'static) -> &Self {
|
|
226
|
+
let row_states = self.row_states.clone();
|
|
227
|
+
*self.inner.bind_item.borrow_mut() = Rc::new(move |pool_index, item_index| {
|
|
228
|
+
renderer(&row_states[pool_index], item_index);
|
|
229
|
+
});
|
|
186
230
|
self.inner.current_first_visible_index.set(-1);
|
|
187
231
|
self.inner.current_last_visible_index.set(-1);
|
|
188
232
|
self.rebuild_visible_range(true);
|
|
189
233
|
self
|
|
190
234
|
}
|
|
191
235
|
|
|
192
|
-
#[allow(non_snake_case)]
|
|
193
|
-
pub fn onBindItem(&self, renderer: impl Fn(&FlexBox, i32) + 'static) -> &Self {
|
|
194
|
-
self.on_bind_item(renderer)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
236
|
pub fn update_item_count(&self, next: i32) {
|
|
198
237
|
self.inner.total_items.set(next.max(0));
|
|
199
238
|
self.inner
|
|
@@ -217,27 +256,33 @@ impl VirtualList {
|
|
|
217
256
|
|
|
218
257
|
fn attach_listeners(&self) {
|
|
219
258
|
let weak = Rc::downgrade(&self.inner);
|
|
259
|
+
let weak_row_states = Rc::downgrade(&self.row_states);
|
|
220
260
|
self.inner
|
|
221
261
|
.subscriptions
|
|
222
262
|
.borrow_mut()
|
|
223
263
|
.push(self.inner.scroll_state.subscribe_offset_y(move || {
|
|
224
|
-
if let Some(inner) = weak.upgrade()
|
|
225
|
-
|
|
264
|
+
if let (Some(inner), Some(row_states)) = (weak.upgrade(), weak_row_states.upgrade())
|
|
265
|
+
{
|
|
266
|
+
VirtualList { inner, row_states }.handle_scroll_offset_changed();
|
|
226
267
|
}
|
|
227
268
|
}));
|
|
228
269
|
let weak = Rc::downgrade(&self.inner);
|
|
270
|
+
let weak_row_states = Rc::downgrade(&self.row_states);
|
|
229
271
|
self.inner.subscriptions.borrow_mut().push(
|
|
230
272
|
self.inner.scroll_state.subscribe_viewport_height(move || {
|
|
231
|
-
if let Some(inner) = weak.upgrade()
|
|
232
|
-
|
|
273
|
+
if let (Some(inner), Some(row_states)) = (weak.upgrade(), weak_row_states.upgrade())
|
|
274
|
+
{
|
|
275
|
+
VirtualList { inner, row_states }.handle_metrics_changed();
|
|
233
276
|
}
|
|
234
277
|
}),
|
|
235
278
|
);
|
|
236
279
|
let weak = Rc::downgrade(&self.inner);
|
|
280
|
+
let weak_row_states = Rc::downgrade(&self.row_states);
|
|
237
281
|
self.inner.subscriptions.borrow_mut().push(
|
|
238
282
|
self.inner.scroll_state.subscribe_content_height(move || {
|
|
239
|
-
if let Some(inner) = weak.upgrade()
|
|
240
|
-
|
|
283
|
+
if let (Some(inner), Some(row_states)) = (weak.upgrade(), weak_row_states.upgrade())
|
|
284
|
+
{
|
|
285
|
+
VirtualList { inner, row_states }.handle_metrics_changed();
|
|
241
286
|
}
|
|
242
287
|
}),
|
|
243
288
|
);
|
|
@@ -325,8 +370,12 @@ impl VirtualList {
|
|
|
325
370
|
let previous_item_index_by_row = self.inner.pool_item_index_by_row.borrow().clone();
|
|
326
371
|
let visible_items = (last_visible_index - first_visible_index) as usize;
|
|
327
372
|
|
|
328
|
-
for pool_index in
|
|
329
|
-
|
|
373
|
+
for (pool_index, previous_item_index) in previous_item_index_by_row
|
|
374
|
+
.iter()
|
|
375
|
+
.copied()
|
|
376
|
+
.enumerate()
|
|
377
|
+
.take(self.inner.pool_size as usize)
|
|
378
|
+
{
|
|
330
379
|
if previous_item_index != -1
|
|
331
380
|
&& (previous_item_index < first_visible_index
|
|
332
381
|
|| previous_item_index >= last_visible_index)
|
|
@@ -340,8 +389,7 @@ impl VirtualList {
|
|
|
340
389
|
if pool_index < visible_items {
|
|
341
390
|
let next_item_index = first_visible_index + pool_index as i32;
|
|
342
391
|
row_area.height(self.inner.item_height, Unit::Pixel);
|
|
343
|
-
|
|
344
|
-
self.render_item(container, next_item_index);
|
|
392
|
+
self.render_item(pool_index, next_item_index);
|
|
345
393
|
self.inner.pool_item_index_by_row.borrow_mut()[pool_index] = next_item_index;
|
|
346
394
|
} else {
|
|
347
395
|
self.hide_pool_item(pool_index);
|
|
@@ -450,9 +498,9 @@ impl VirtualList {
|
|
|
450
498
|
mark_needs_commit();
|
|
451
499
|
}
|
|
452
500
|
|
|
453
|
-
fn render_item(&self,
|
|
501
|
+
fn render_item(&self, pool_index: usize, index: i32) {
|
|
454
502
|
let binder = self.inner.bind_item.borrow().clone();
|
|
455
|
-
binder(
|
|
503
|
+
binder(pool_index, index);
|
|
456
504
|
}
|
|
457
505
|
|
|
458
506
|
fn max_offset_for_current_viewport(&self) -> f32 {
|
|
@@ -478,7 +526,7 @@ impl VirtualList {
|
|
|
478
526
|
}
|
|
479
527
|
}
|
|
480
528
|
|
|
481
|
-
impl Node for VirtualList {
|
|
529
|
+
impl<T: 'static> Node for VirtualList<T> {
|
|
482
530
|
fn retained_node_ref(&self) -> NodeRef {
|
|
483
531
|
let list = self.clone();
|
|
484
532
|
self.inner
|
|
@@ -507,22 +555,24 @@ impl Node for VirtualList {
|
|
|
507
555
|
}
|
|
508
556
|
}
|
|
509
557
|
|
|
510
|
-
impl HasFlexBoxRoot for VirtualList {
|
|
558
|
+
impl<T: 'static> HasFlexBoxRoot for VirtualList<T> {
|
|
511
559
|
fn flex_box_root(&self) -> &FlexBox {
|
|
512
560
|
&self.inner.root
|
|
513
561
|
}
|
|
514
562
|
}
|
|
515
563
|
|
|
516
|
-
impl ThemeBindable for VirtualList {
|
|
564
|
+
impl<T: 'static> ThemeBindable for VirtualList<T> {
|
|
517
565
|
fn theme_binding_node(&self) -> NodeRef {
|
|
518
566
|
self.inner.root.retained_node_ref()
|
|
519
567
|
}
|
|
520
568
|
|
|
521
569
|
fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
|
|
522
570
|
let weak = Rc::downgrade(&self.inner);
|
|
571
|
+
let weak_row_states = Rc::downgrade(&self.row_states);
|
|
523
572
|
Box::new(move || {
|
|
524
573
|
Some(VirtualList {
|
|
525
574
|
inner: weak.upgrade()?,
|
|
575
|
+
row_states: weak_row_states.upgrade()?,
|
|
526
576
|
})
|
|
527
577
|
})
|
|
528
578
|
}
|
|
@@ -583,7 +633,7 @@ mod tests {
|
|
|
583
633
|
let list = VirtualList::new(10_000, 20.0);
|
|
584
634
|
let captured_indices = rendered_indices.clone();
|
|
585
635
|
let captured_labels = labels.clone();
|
|
586
|
-
list.
|
|
636
|
+
list.on_bind_item(move |container, index| {
|
|
587
637
|
tracked_bind_virtual_list_item(&captured_indices, &captured_labels, container, index);
|
|
588
638
|
});
|
|
589
639
|
list.width(180.0, Unit::Pixel);
|
|
@@ -608,7 +658,7 @@ mod tests {
|
|
|
608
658
|
let list = VirtualList::new(10_000, 20.0);
|
|
609
659
|
let captured_indices = rendered_indices.clone();
|
|
610
660
|
let captured_labels = labels.clone();
|
|
611
|
-
list.
|
|
661
|
+
list.on_bind_item(move |container, index| {
|
|
612
662
|
tracked_bind_virtual_list_item(&captured_indices, &captured_labels, container, index);
|
|
613
663
|
});
|
|
614
664
|
list.width(180.0, Unit::Pixel);
|
|
@@ -653,7 +703,7 @@ mod tests {
|
|
|
653
703
|
let labels = Rc::new(RefCell::new(HashMap::new()));
|
|
654
704
|
let list = VirtualList::new(10_000, 24.0);
|
|
655
705
|
let captured_labels = labels.clone();
|
|
656
|
-
list.
|
|
706
|
+
list.on_bind_item(move |container, index| {
|
|
657
707
|
static_bind_virtual_list_item(&captured_labels, container, index);
|
|
658
708
|
});
|
|
659
709
|
list.height(120.0, Unit::Pixel);
|
|
@@ -676,7 +726,7 @@ mod tests {
|
|
|
676
726
|
let list = VirtualList::new(10_000, 20.0);
|
|
677
727
|
let captured_indices = rendered_indices.clone();
|
|
678
728
|
let captured_labels = labels.clone();
|
|
679
|
-
list.
|
|
729
|
+
list.on_bind_item(move |container, index| {
|
|
680
730
|
tracked_bind_virtual_list_item(&captured_indices, &captured_labels, container, index);
|
|
681
731
|
});
|
|
682
732
|
list.width(180.0, Unit::Pixel);
|
|
@@ -690,4 +740,41 @@ mod tests {
|
|
|
690
740
|
assert_eq!(rendered[rendered.len() - 1], 5);
|
|
691
741
|
list.dispose();
|
|
692
742
|
}
|
|
743
|
+
|
|
744
|
+
#[test]
|
|
745
|
+
fn typed_item_template_creates_one_retained_state_per_pool_row_and_rebinds_it() {
|
|
746
|
+
#[derive(Clone)]
|
|
747
|
+
struct Row {
|
|
748
|
+
label: TextNode,
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
ffi::test::reset();
|
|
752
|
+
let template_count = Rc::new(Cell::new(0));
|
|
753
|
+
let captured_template_count = template_count.clone();
|
|
754
|
+
let list = VirtualList::with_max_visible(10_000, 20.0, 4).item_template(move |container| {
|
|
755
|
+
captured_template_count.set(captured_template_count.get() + 1);
|
|
756
|
+
let label = text("");
|
|
757
|
+
container.child(&label);
|
|
758
|
+
Row { label }
|
|
759
|
+
});
|
|
760
|
+
let bound_indices = Rc::new(RefCell::new(Vec::new()));
|
|
761
|
+
let captured_indices = bound_indices.clone();
|
|
762
|
+
list.on_bind_item(move |row, index| {
|
|
763
|
+
captured_indices.borrow_mut().push(index);
|
|
764
|
+
row.label.text(format!("typed item {index}"));
|
|
765
|
+
});
|
|
766
|
+
list.height(60.0, Unit::Pixel);
|
|
767
|
+
Application::mount(list.clone());
|
|
768
|
+
|
|
769
|
+
assert_eq!(template_count.get(), 6);
|
|
770
|
+
bound_indices.borrow_mut().clear();
|
|
771
|
+
ffi::test::reset();
|
|
772
|
+
list.scroll_state().set_offset_y(40.0);
|
|
773
|
+
|
|
774
|
+
assert_eq!(&*bound_indices.borrow(), &[2, 3, 4, 5]);
|
|
775
|
+
assert!(!ffi::test::take_calls()
|
|
776
|
+
.iter()
|
|
777
|
+
.any(|call| matches!(call, Call::CreateNode { .. } | Call::DeleteNode { .. })));
|
|
778
|
+
Application::unmount();
|
|
779
|
+
}
|
|
693
780
|
}
|
package/src/platform.rs
CHANGED
|
@@ -260,6 +260,9 @@ pub fn show_open_folder_dialog(
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
#[cfg(feature = "native-runtime")]
|
|
263
|
+
/// # Safety
|
|
264
|
+
/// `payload` must reference at least `payload_length` readable bytes when
|
|
265
|
+
/// `payload_length` is non-zero.
|
|
263
266
|
#[no_mangle]
|
|
264
267
|
pub unsafe extern "C" fn __fui_complete_native_file_dialog(
|
|
265
268
|
request_id: u64,
|
package/src/popup_presenter.rs
CHANGED
package/src/theme.rs
CHANGED
|
@@ -517,12 +517,12 @@ trait Pipe: Sized {
|
|
|
517
517
|
|
|
518
518
|
impl<T> Pipe for T {}
|
|
519
519
|
|
|
520
|
-
#[no_mangle]
|
|
520
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
521
521
|
pub extern "C" fn __fui_on_system_dark_mode_changed(is_dark: bool) {
|
|
522
522
|
handle_system_dark_mode_changed(is_dark);
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
#[no_mangle]
|
|
525
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
526
526
|
pub extern "C" fn __fui_on_system_accent_color_changed(color: u32) {
|
|
527
527
|
handle_system_accent_color_changed(color);
|
|
528
528
|
}
|
package/src/timers.rs
CHANGED
|
@@ -58,7 +58,7 @@ pub fn cancel_all_timers() {
|
|
|
58
58
|
ACTIVE_TIMERS.with(|timers| timers.borrow_mut().clear());
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
#[no_mangle]
|
|
61
|
+
#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
|
|
62
62
|
pub extern "C" fn __fui_on_timer(timer_id: u32) {
|
|
63
63
|
let callback = ACTIVE_TIMERS.with(|timers| timers.borrow_mut().remove(&timer_id));
|
|
64
64
|
if let Some(callback) = callback {
|
package/src/typography.rs
CHANGED
|
@@ -15,32 +15,22 @@ thread_local! {
|
|
|
15
15
|
static REGISTERED_FONT_FALLBACKS: RefCell<Vec<(u32, u32)>> = const { RefCell::new(Vec::new()) };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
18
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
|
19
19
|
pub enum FontStyle {
|
|
20
|
+
#[default]
|
|
20
21
|
Normal = 0,
|
|
21
22
|
Italic = 1,
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
fn default() -> Self {
|
|
26
|
-
Self::Normal
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
25
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
|
31
26
|
pub enum FontWeight {
|
|
27
|
+
#[default]
|
|
32
28
|
Regular = 400,
|
|
33
29
|
Medium = 500,
|
|
34
30
|
Semibold = 600,
|
|
35
31
|
Bold = 700,
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
impl Default for FontWeight {
|
|
39
|
-
fn default() -> Self {
|
|
40
|
-
Self::Regular
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
34
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
45
35
|
pub struct FontFaceLoadedEventArgs {
|
|
46
36
|
pub font: FontFace,
|
|
@@ -308,6 +298,7 @@ pub struct FontFamily {
|
|
|
308
298
|
}
|
|
309
299
|
|
|
310
300
|
impl FontFamily {
|
|
301
|
+
#[allow(clippy::too_many_arguments)]
|
|
311
302
|
pub fn new(
|
|
312
303
|
regular_stack: FontStack,
|
|
313
304
|
bold_stack: Option<FontStack>,
|
|
@@ -331,6 +322,7 @@ impl FontFamily {
|
|
|
331
322
|
}
|
|
332
323
|
|
|
333
324
|
#[cfg(test)]
|
|
325
|
+
#[allow(clippy::too_many_arguments)]
|
|
334
326
|
pub(crate) fn from_ids(
|
|
335
327
|
regular: u32,
|
|
336
328
|
bold: u32,
|