Skip to content

Commit 85b1e2e

Browse files
committed
add subsecond hotreloading
1 parent 7ec0f40 commit 85b1e2e

File tree

21 files changed

+391
-79
lines changed

21 files changed

+391
-79
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ swash = { version = "0.2" }
4343
muda = { version = "0.17.1", default-features = false }
4444
winit = { git = "https://github.com/lapce/winit", rev = "133268de675a4fda70816d78a7a1435eb972fea1", package = "floem-winit" }
4545

46+
dioxus-devtools = "=0.7.0-rc.3"
47+
4648
[dependencies]
4749
slotmap = "1.0"
4850
sha2 = "0.10"
@@ -84,6 +86,11 @@ fluent-bundle = { version = "0.16", optional = true }
8486
unic-langid = { version = "0.9", optional = true }
8587
sys-locale = {version = "0.3.2", optional = true }
8688

89+
#hotpatching
90+
tungstenite = { version = "0.26.2", optional = true }
91+
serde_json = { version = "1.0.140", optional = true }
92+
dioxus-devtools = { workspace = true, optional = true}
93+
8794
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
8895
muda = { workspace = true }
8996

@@ -111,6 +118,7 @@ objc2-app-kit = { version = "0.3", features = [
111118
default = ["editor", "default-image-formats", "vger"]
112119
vello = ["dep:floem_vello_renderer"]
113120
vger = ["dep:floem_vger_renderer"]
121+
hotpatch = ["dep:tungstenite", "dep:serde_json", "dep:dioxus-devtools", "floem_reactive/hotpatch"]
114122
serde = [
115123
"dep:serde",
116124
"winit/serde",
@@ -149,3 +157,6 @@ rfd-async-std = ["dep:rfd", "rfd/async-std"]
149157
rfd-tokio = ["dep:rfd", "rfd/tokio"]
150158
crossbeam = ["dep:crossbeam", "floem_renderer/crossbeam"]
151159
localization = ["dep:fluent-bundle", "dep:unic-langid", "dep:sys-locale"]
160+
161+
162+

examples/localization/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version.workspace = true
66

77
[dependencies]
88
floem = { path = "../..", features = ["vello", "localization"] }
9+
10+
[features]
11+
hotpatch = ["floem/hotpatch"]

examples/localization/src/main.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use floem::action::inspect;
1+
use floem::action::{inspect, set_window_scale};
22
use floem::prelude::*;
33
use localization::*;
44

@@ -56,6 +56,8 @@ fn counter_view() -> impl IntoView {
5656
))
5757
.style(|s| s.size_full().items_center().justify_center().gap(10.));
5858

59+
let mut window_scale = RwSignal::new(1.);
60+
5961
(lang_tabs, value_controls)
6062
.v_stack()
6163
.style(move |s| {
@@ -76,4 +78,34 @@ fn counter_view() -> impl IntoView {
7678
inspect();
7779
},
7880
)
81+
.on_key_down(
82+
floem::keyboard::Key::Character("=".into()),
83+
|m| m.meta(),
84+
move |_| {
85+
set_window_scale({
86+
window_scale.update(|s| *s *= 1.1);
87+
window_scale.get_untracked()
88+
});
89+
},
90+
)
91+
.on_key_down(
92+
floem::keyboard::Key::Character("-".into()),
93+
|m| m.meta(),
94+
move |_| {
95+
set_window_scale({
96+
window_scale.update(|s| *s /= 1.1);
97+
window_scale.get_untracked()
98+
});
99+
},
100+
)
101+
.on_key_down(
102+
floem::keyboard::Key::Character("0".into()),
103+
|m| m.meta(),
104+
move |_| {
105+
set_window_scale({
106+
window_scale.update(|s| *s = 1.);
107+
window_scale.get_untracked()
108+
});
109+
},
110+
)
79111
}

examples/widget-gallery/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ stacks = { path = "../stacks/", optional = true }
1313
default = ["full"]
1414
vello = ["floem/vello"]
1515
full = ["dep:files", "dep:stacks"]
16+
hotpatch = ["floem/hotpatch"]
Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
use floem::{
22
peniko::{color::palette, Color},
3-
prelude::{
4-
palette::css::{DARK_GRAY, WHITE_SMOKE},
5-
RwSignal, SignalGet,
6-
},
73
style::CursorStyle,
8-
theme::StyleThemeExt,
9-
views::{button, toggle_button, Decorators, ToggleButton, ToggleHandleBehavior},
4+
views::{button, toggle_button, Decorators, ToggleHandleBehavior},
105
IntoView,
116
};
127

138
use crate::form::{form, form_item};
149

1510
pub fn button_view() -> impl IntoView {
16-
let state = RwSignal::new(false);
1711
form((
1812
form_item(
1913
"Basic Button:",
@@ -27,8 +21,8 @@ pub fn button_view() -> impl IntoView {
2721
s.border(1.0)
2822
.border_radius(10.0)
2923
.padding(10.0)
30-
.background(palette::css::YELLOW_GREEN)
31-
.color(palette::css::DARK_GREEN)
24+
.background(palette::css::RED)
25+
.color(palette::css::BLACK.with_alpha(0.5))
3226
.cursor(CursorStyle::Pointer)
3327
.active(|s| s.color(palette::css::WHITE).background(palette::css::RED))
3428
.hover(|s| s.background(Color::from_rgb8(244, 67, 54)))
@@ -48,34 +42,12 @@ pub fn button_view() -> impl IntoView {
4842
}),
4943
),
5044
form_item(
51-
"Toggle button - Snap:",
52-
toggle_button(|| true)
53-
.on_toggle(|_| {
54-
println!("Button Toggled");
55-
})
56-
.toggle_style(|s| s.behavior(ToggleHandleBehavior::Snap)),
57-
),
58-
form_item(
59-
"Toggle button - Follow:",
45+
"Toggle button",
6046
toggle_button(|| true)
6147
.on_toggle(|_| {
6248
println!("Button Toggled");
6349
})
6450
.toggle_style(|s| s.behavior(ToggleHandleBehavior::Follow)),
6551
),
66-
form_item(
67-
"Toggle button - toggle background:",
68-
ToggleButton::new_rw(state).toggle_style(move |s| {
69-
s.apply_if(state.get(), |s| {
70-
s.accent_color(DARK_GRAY).handle_color(WHITE_SMOKE)
71-
})
72-
.behavior(ToggleHandleBehavior::Snap)
73-
}),
74-
),
7552
))
76-
.style(move |s| {
77-
s.apply_if(state.get(), |s| {
78-
s.with_theme(|s, t| s.background(t.bg_elevated()))
79-
})
80-
})
8153
}

examples/widget-gallery/src/checkbox.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ pub const CROSS_SVG: &str = r##"
1717
"##;
1818

1919
pub fn checkbox_view() -> impl IntoView {
20-
// let width = 160.0;
2120
let is_checked = RwSignal::new(true);
2221
form((
2322
form_item("Checkbox:", Checkbox::new_rw(is_checked)),

examples/widget-gallery/src/main.rs

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub mod slider;
1818
pub mod tabs;
1919

2020
use floem::{
21-
action::{add_overlay, set_window_menu, toggle_theme},
21+
action::{add_overlay, set_window_menu, set_window_scale, toggle_theme},
2222
event::{Event, EventListener},
2323
keyboard::{Key, Modifiers, NamedKey},
2424
kurbo::Size,
@@ -27,7 +27,7 @@ use floem::{
2727
new_window,
2828
prelude::*,
2929
style::{Background, CursorStyle, TextColor, Transition},
30-
theme::{self, StyleThemeExt},
30+
theme::StyleThemeExt,
3131
window::{WindowConfig, WindowId},
3232
};
3333

@@ -89,8 +89,7 @@ fn app_view(window_id: WindowId) -> impl IntoView {
8989
let side_tab_bar = tabs
9090
.get()
9191
.into_iter()
92-
.enumerate()
93-
.map(move |(idx, item)| {
92+
.map(move |item| {
9493
item.debug_name(item).style(move |s| {
9594
s.flex_row()
9695
.font_size(18.)
@@ -104,12 +103,6 @@ fn app_view(window_id: WindowId) -> impl IntoView {
104103
})
105104
})
106105
.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-
})
113106
})
114107
})
115108
.list()
@@ -118,7 +111,7 @@ fn app_view(window_id: WindowId) -> impl IntoView {
118111
set_active_tab.set(idx);
119112
}
120113
})
121-
.style(|s| s.flex_col().width(140.0).flex_grow(1.))
114+
.style(|s| s.flex_col().width_full().flex_grow(1.))
122115
.scroll()
123116
.debug_name("Side Tab Bar")
124117
.scroll_style(|s| s.shrink_to_fit().handle_thickness(8.))
@@ -309,21 +302,56 @@ fn app_view(window_id: WindowId) -> impl IntoView {
309302
}),
310303
);
311304

312-
add_overlay(svg(include_str!("../assets/floem.svg")).style(|s| {
313-
s.set_style_value(TextColor, floem::style::StyleValue::Unset)
314-
.size(50, 50)
315-
.absolute()
316-
.inset_bottom(20.)
317-
.inset_right(15.)
318-
}));
319-
320305
add_overlay(
321-
button("toggle theme")
322-
.action(toggle_theme)
323-
.style(|s| s.absolute().inset_top(10.).inset_right(22.)),
306+
v_stack((
307+
svg(include_str!("../assets/floem.svg")).style(|s| {
308+
s.set_style_value(TextColor, floem::style::StyleValue::Unset)
309+
.size(50, 50)
310+
}),
311+
button("toggle theme").action(toggle_theme),
312+
))
313+
.style(|s| {
314+
s.absolute()
315+
.inset_bottom(20.)
316+
.inset_right(15.)
317+
.items_center()
318+
.justify_center()
319+
.gap(5)
320+
}),
324321
);
325322

326-
view.on_event_stop(EventListener::KeyUp, move |e| {
323+
let window_scale = RwSignal::new(1.);
324+
view.on_key_down(
325+
floem::keyboard::Key::Character("=".into()),
326+
|m| m.meta(),
327+
move |_| {
328+
set_window_scale({
329+
window_scale.update(|s| *s *= 1.1);
330+
window_scale.get_untracked()
331+
});
332+
},
333+
)
334+
.on_key_down(
335+
floem::keyboard::Key::Character("-".into()),
336+
|m| m.meta(),
337+
move |_| {
338+
set_window_scale({
339+
window_scale.update(|s| *s /= 1.1);
340+
window_scale.get_untracked()
341+
});
342+
},
343+
)
344+
.on_key_down(
345+
floem::keyboard::Key::Character("0".into()),
346+
|m| m.meta(),
347+
move |_| {
348+
set_window_scale({
349+
window_scale.update(|s| *s = 1.);
350+
window_scale.get_untracked()
351+
});
352+
},
353+
)
354+
.on_event_stop(EventListener::KeyUp, move |e| {
327355
if let Event::KeyUp(e) = e {
328356
if e.key.logical_key == Key::Named(NamedKey::F11) {
329357
floem::action::inspect();

examples/widget-gallery/src/radio_buttons.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Display;
22

3-
use floem::{prelude::*, style_class};
3+
use floem::prelude::*;
44
use strum::IntoEnumIterator;
55

66
use crate::form::{form, form_item};
@@ -22,10 +22,7 @@ impl Display for OperatingSystem {
2222
}
2323
}
2424

25-
style_class!(RadioButtonGroupClass);
26-
2725
pub fn radio_buttons_view() -> impl IntoView {
28-
// let width = 160.0;
2926
let operating_system = RwSignal::new(OperatingSystem::Windows);
3027
form((
3128
form_item(
@@ -48,8 +45,7 @@ pub fn radio_buttons_view() -> impl IntoView {
4845
"Labelled Radio Buttons:",
4946
OperatingSystem::iter()
5047
.map(move |os| RadioButton::new_labeled_rw(os, operating_system, move || os))
51-
.v_stack()
52-
.class(RadioButtonGroupClass),
48+
.v_stack(),
5349
),
5450
form_item(
5551
"Disabled Labelled Radio Buttons:",
@@ -58,9 +54,8 @@ pub fn radio_buttons_view() -> impl IntoView {
5854
RadioButton::new_labeled_get(os, operating_system, move || os)
5955
.style(|s| s.set_disabled(true))
6056
})
61-
.v_stack()
62-
.class(RadioButtonGroupClass),
57+
.v_stack(),
6358
),
6459
))
65-
.style(|s| s.class(RadioButtonGroupClass, |s| s.gap(10.).margin_left(5.)))
60+
.style(|s| s.class(RadioButtonGroupClass, |s| s.gap(10.)))
6661
}

reactive/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ license.workspace = true
88

99
[dependencies]
1010
smallvec = "1.10.0"
11+
dioxus-devtools = {workspace = true, optional = true}
12+
13+
[features]
14+
hotpatch = ["dep:dioxus-devtools"]

0 commit comments

Comments
 (0)