Skip to content

Commit b7aa52c

Browse files
authored
Refactor async signal (#964)
* future signals take optional initial * update async signals to better match resource * refactor modules * fmt * make fields private * move async signals out of ext_event module * fix tokio import * fix imports
1 parent 70ba622 commit b7aa52c

File tree

20 files changed

+3085
-1200
lines changed

20 files changed

+3085
-1200
lines changed

examples/async-resource/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use std::time::Duration;
22

33
use floem::{
44
action::debounce_action,
5-
async_signal::Resource,
65
prelude::{palette::css, *},
76
reactive::Trigger,
7+
receiver_signal::Resource,
88
style::{Background, CursorStyle, Transition},
99
text::Weight,
1010
};
@@ -180,7 +180,7 @@ fn stat_item(label: &str, value: u32) -> impl IntoView {
180180
.style(|s| s.items_center())
181181
}
182182

183-
fn user_display(user_resource: Resource<UserResult>) -> impl IntoView {
183+
fn user_display(user_resource: Resource<Option<UserResult>>) -> impl IntoView {
184184
dyn_container(
185185
move || user_resource.get(),
186186
|result| match result {
@@ -295,7 +295,7 @@ fn app_view() -> impl IntoView {
295295
token_changed.notify()
296296
});
297297

298-
let user_resource = Resource::on_event_loop(
298+
let user_resource = Resource::new(
299299
move || (username.get(), token.get()),
300300
|(username, token): (String, String)| async move {
301301
if username.trim().is_empty() {

examples/tokio-timer/src/main.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use std::time::Duration;
22

3+
use floem::action::inspect;
4+
use floem::prelude::*;
35
use floem::reactive::{create_memo, DerivedRwSignal};
6+
use floem::receiver_signal::StreamSignal;
7+
use floem::theme::StyleThemeExt;
48
use floem::unit::Pct;
5-
use floem::{async_signal::StreamSignal, prelude::*};
9+
use floem::views::slider::SliderCustomStyle;
610
use tokio::runtime::Runtime;
711
use tokio::time::Instant;
812
use tokio_stream::wrappers::IntervalStream;
@@ -24,11 +28,10 @@ fn app_view() -> impl IntoView {
2428
let stream = IntervalStream::new(tokio::time::interval(Duration::from_millis(4)));
2529
let now = Instant::now();
2630
let started_at = RwSignal::new(now);
27-
let current_instant = StreamSignal::on_event_loop(stream);
31+
let current_instant = StreamSignal::with_initial(stream, now);
2832
let elapsed_time = create_memo(move |_| {
2933
current_instant
3034
.get()
31-
.unwrap_or(Instant::now())
3235
.duration_since(started_at.get())
3336
.as_secs_f64()
3437
.min(target_pct.get().0)
@@ -52,33 +55,31 @@ fn app_view() -> impl IntoView {
5255
)
5356
});
5457

55-
let elapsed_time_bar = gauge(progress);
56-
let el_label_bar = ("Elapsed Time: ", elapsed_time_bar)
58+
let el_label_bar = ("Elapsed Time: ", gauge(progress))
5759
.h_stack()
5860
.style(|s| s.justify_between());
5961

60-
let duration_slider = thin_slider(target_pct);
61-
let dur_label_slider = ("Duration: ", duration_slider)
62+
let dur_label_slider = ("Duration: ", thin_slider(target_pct))
6263
.h_stack()
6364
.style(|s| s.justify_between());
6465

6566
let reset_button = button("Reset").action(move || started_at.set(Instant::now()));
6667

67-
let view = (
68+
v_stack((
6869
el_label_bar,
6970
elapsed_time_label,
7071
dur_label_slider,
7172
reset_button,
72-
)
73-
.v_stack()
74-
.style(|s| s.gap(5));
75-
76-
view.container().style(|s| {
73+
))
74+
.style(|s| s.gap(5))
75+
.container()
76+
.style(|s| {
7777
s.size(100.pct(), 100.pct())
7878
.flex_col()
7979
.items_center()
8080
.justify_center()
8181
})
82+
.on_key_down(Key::Named(NamedKey::F11), |_| true, |_| inspect())
8283
}
8384

8485
/// A slider with a thin bar instead of the default thick bar.
@@ -91,12 +92,19 @@ fn thin_slider(
9192
}
9293

9394
/// A non-interactive slider that has been repurposed into a progress bar.
94-
fn gauge(fill_percent: impl SignalGet<Pct> + 'static) -> slider::Slider {
95+
fn gauge(fill_percent: impl SignalGet<Pct> + 'static + Copy) -> slider::Slider {
9596
slider::Slider::new(move || fill_percent.get())
9697
.slider_style(|s| {
9798
s.handle_radius(0)
9899
.bar_radius(25.pct())
99100
.accent_bar_radius(25.pct())
100101
})
101-
.style(|s| s.width(200).set_disabled(true))
102+
.style(move |s| {
103+
let fill_percent = fill_percent.get().0;
104+
s.width(200).set_disabled(true).with_theme(move |s, t| {
105+
s.apply_if(fill_percent == 100., |s| {
106+
s.custom(|s: SliderCustomStyle| s.accent_bar_color(t.success()))
107+
})
108+
})
109+
})
102110
}

examples/widget-gallery/src/main.rs

Lines changed: 35 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod radio_buttons;
1616
pub mod rich_text;
1717
pub mod slider;
1818
pub mod tabs;
19+
pub mod texteditor;
1920

2021
use floem::{
2122
action::{add_overlay, set_window_menu, toggle_theme},
@@ -26,7 +27,7 @@ use floem::{
2627
new_window,
2728
prelude::*,
2829
style::{Background, CursorStyle, TextColor, Transition},
29-
theme::{self, StyleThemeExt},
30+
theme::StyleThemeExt,
3031
ui_events::keyboard::{Key, KeyState, KeyboardEvent, Modifiers, NamedKey},
3132
window::{WindowConfig, WindowId},
3233
};
@@ -35,22 +36,22 @@ fn app_view(window_id: WindowId) -> impl IntoView {
3536
let tabs: Vec<&'static str> = vec![
3637
"Label",
3738
"Button",
38-
"Checkbox",
39-
"Radio",
4039
"Input",
41-
"Canvas",
42-
"Stacks",
40+
"Text Editor",
4341
"Lists",
42+
"Image",
43+
"Dropdown",
44+
"Checkbox",
45+
"Radio",
4446
"Tabs",
47+
"Slider",
48+
"Canvas",
4549
"Menu",
46-
"RichText",
47-
"Image",
50+
"Rich Text",
4851
"Clipboard",
49-
"Slider",
50-
"Dropdown",
5152
"Animation",
5253
"Draggable",
53-
"DroppedFile",
54+
"Dropped File",
5455
"Files",
5556
];
5657

@@ -62,35 +63,31 @@ fn app_view(window_id: WindowId) -> impl IntoView {
6263
"Radio" => radio_buttons::radio_buttons_view().into_any(),
6364
"Input" => inputs::text_input_view().into_any(),
6465
"Canvas" => canvas::canvas_view().into_any(),
65-
#[cfg(feature = "full")]
66-
"Stacks" => stacks::stacks_view().into_any(),
6766
"Lists" => lists::list_view().into_any(),
6867
"Tabs" => tabs::tab_view().into_any(),
6968
"Menu" => context_menu::menu_view().into_any(),
70-
"RichText" => rich_text::rich_text_view().into_any(),
69+
"Rich Text" => rich_text::rich_text_view().into_any(),
7170
"Image" => images::img_view().into_any(),
7271
"Clipboard" => clipboard::clipboard_view().into_any(),
7372
"Slider" => slider::slider_view().into_any(),
7473
"Dropdown" => dropdown::dropdown_view().into_any(),
7574
"Animation" => animation::animation_view().into_any(),
7675
"Draggable" => draggable::draggable_view().into_any(),
77-
"DroppedFile" => dropped_file::dropped_file_view().into_any(),
76+
"Dropped File" => dropped_file::dropped_file_view().into_any(),
7877
#[cfg(feature = "full")]
7978
"Files" => files::files_view().into_any(),
79+
"Text Editor" => texteditor::editor_view().into_any(),
8080
_ => label(|| "Not implemented".to_owned()).into_any(),
8181
}
8282
.debug_name(it.to_string())
8383
};
8484

8585
let tabs = RwSignal::new(tabs);
8686

87-
let (active_tab, set_active_tab) = create_signal(0);
88-
89-
let side_tab_bar = tabs
87+
let side_bar_list = tabs
9088
.get()
9189
.into_iter()
92-
.enumerate()
93-
.map(move |(idx, item)| {
90+
.map(move |item| {
9491
item.debug_name(item).style(move |s| {
9592
s.flex_row()
9693
.font_size(18.)
@@ -104,21 +101,14 @@ fn app_view(window_id: WindowId) -> impl IntoView {
104101
})
105102
})
106103
.hover(|s| s.cursor(CursorStyle::Pointer))
107-
.apply_if(idx != active_tab.get(), |s| {
108-
s.apply(
109-
theme::hover_style()
110-
.with_theme(|s, t| s.border_radius(t.border_radius())),
111-
)
112-
})
113104
})
114105
})
115106
.list()
116-
.on_select(move |idx| {
117-
if let Some(idx) = idx {
118-
set_active_tab.set(idx);
119-
}
120-
})
121-
.style(|s| s.flex_col().width(140.0).flex_grow(1.))
107+
.style(|s| s.flex_col().width(140.0).flex_grow(1.));
108+
109+
let active_tab = side_bar_list.selection();
110+
111+
let side_tab_bar = side_bar_list
122112
.scroll()
123113
.debug_name("Side Tab Bar")
124114
.scroll_style(|s| s.shrink_to_fit().handle_thickness(8.))
@@ -133,7 +123,7 @@ fn app_view(window_id: WindowId) -> impl IntoView {
133123
let inspector = button("Open Inspector").action(floem::action::inspect);
134124

135125
let new_window_button = button("Open In Window").action(move || {
136-
let name = tabs.with(|tabs| tabs.get(active_tab.get()).copied());
126+
let name = tabs.with(|tabs| tabs.get(active_tab.get().unwrap_or(0)).copied());
137127
new_window(
138128
move |_| {
139129
create_view(name.unwrap_or_default())
@@ -154,13 +144,13 @@ fn app_view(window_id: WindowId) -> impl IntoView {
154144
.style(|s| s.height_full().row_gap(5.0));
155145

156146
let tab = tab(
157-
move || Some(active_tab.get()),
147+
move || Some(active_tab.get().unwrap_or(0)),
158148
move || tabs.get(),
159149
|it| *it,
160150
create_view,
161151
)
162152
.debug_name("Active Tab")
163-
.style(|s| s.flex_col().items_start());
153+
.style(|s| s.flex_col().flex_grow(1.).items_start());
164154

165155
let tab = tab.scroll().style(|s| s.size_full());
166156

@@ -189,30 +179,11 @@ fn app_view(window_id: WindowId) -> impl IntoView {
189179
};
190180

191181
let widget_submenu = |m: SubMenu| {
192-
m.item("Labels", |i| i.action(move || set_active_tab.set(0)))
193-
.item("Buttons", |i| i.action(move || set_active_tab.set(1)))
194-
.item("Checkboxes", |i| i.action(move || set_active_tab.set(2)))
195-
.item("Radio Buttons", |i| i.action(move || set_active_tab.set(3)))
196-
.item("Text Input", |i| i.action(move || set_active_tab.set(4)))
197-
.separator()
198-
.item("Canvas", |i| i.action(move || set_active_tab.set(5)))
199-
.item("Stacks", |i| i.action(move || set_active_tab.set(6)))
200-
.item("Lists", |i| i.action(move || set_active_tab.set(7)))
201-
.item("Tabs", |i| i.action(move || set_active_tab.set(8)))
202-
.item("Context Menu", |i| i.action(move || set_active_tab.set(9)))
203-
.item("Rich Text", |i| i.action(move || set_active_tab.set(10)))
204-
.separator()
205-
.item("Images", |i| i.action(move || set_active_tab.set(11)))
206-
.item("Clipboard", |i| i.action(move || set_active_tab.set(12)))
207-
.item("Slider", |i| i.action(move || set_active_tab.set(13)))
208-
.item("Dropdown", |i| i.action(move || set_active_tab.set(14)))
209-
.separator()
210-
.item("Animation", |i| i.action(move || set_active_tab.set(15)))
211-
.item("Draggable", |i| i.action(move || set_active_tab.set(16)))
212-
.item("Dropped Files", |i| {
213-
i.action(move || set_active_tab.set(17))
182+
tabs.with(|tabs| {
183+
tabs.iter().enumerate().fold(m, |menu, (idx, &tab)| {
184+
menu.item(tab, move |i| i.action(move || active_tab.set(Some(idx))))
214185
})
215-
.item("File Browser", |i| i.action(move || set_active_tab.set(18)))
186+
})
216187
};
217188

218189
let view_submenu = |m: SubMenu| {
@@ -226,19 +197,19 @@ fn app_view(window_id: WindowId) -> impl IntoView {
226197
.separator()
227198
.item("Next Tab", |i| {
228199
i.action(move || {
229-
let current = active_tab.get();
200+
let current = active_tab.get().unwrap_or(0);
230201
let tab_count = tabs.get().len();
231-
set_active_tab.set((current + 1) % tab_count);
202+
active_tab.set(Some((current + 1) % tab_count));
232203
})
233204
})
234205
.item("Previous Tab", |i| {
235206
i.action(move || {
236-
let current = active_tab.get();
207+
let current = active_tab.get().unwrap_or(0);
237208
let tab_count = tabs.get().len();
238-
set_active_tab.set(if current == 0 {
239-
tab_count - 1
209+
active_tab.set(if current == 0 {
210+
Some(tab_count - 1)
240211
} else {
241-
current - 1
212+
Some(current - 1)
242213
});
243214
})
244215
})
@@ -247,7 +218,7 @@ fn app_view(window_id: WindowId) -> impl IntoView {
247218
let window_submenu = |m: SubMenu| {
248219
m.item("Open Current Tab in New Window", |i| {
249220
i.action(move || {
250-
let name = tabs.with(|tabs| tabs.get(active_tab.get()).copied());
221+
let name = tabs.with(|tabs| tabs.get(active_tab.get().unwrap_or(0)).copied());
251222
new_window(
252223
move |_| {
253224
create_view(name.unwrap_or_default())

0 commit comments

Comments
 (0)