From 4dc257ccdaabb77f81675692ff40e0253d6296d0 Mon Sep 17 00:00:00 2001 From: Leonard Date: Sat, 30 Sep 2023 23:46:10 +0200 Subject: [PATCH 01/12] fix(docs): Update Torin's link in differences with Dioxus (#309) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Torin link and add permalink so it doesn't happen again * Remove permalink and instead add crates.io link * Update differences_with_dioxus.md --------- Co-authored-by: Marc Espín --- book/src/differences_with_dioxus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/differences_with_dioxus.md b/book/src/differences_with_dioxus.md index 72cc46c02..21013e6b9 100644 --- a/book/src/differences_with_dioxus.md +++ b/book/src/differences_with_dioxus.md @@ -7,7 +7,7 @@ These are the main differences between Freya and the different Dioxus renderers | Category | Freya | Dioxus | |--------------------------------------|------------------|---------------------------------| | **Elements, attributes and events** | Custom | HTML | -| **Layout** | Custom ([`torin`](https://github.com/marc2332/freya/tree/main/torin)) | WebView and [`taffy`](https://github.com/DioxusLabs/taffy) | +| **Layout** | [`Torin`](https://github.com/marc2332/freya/tree/main/crates/torin) | WebView and [`Taffy`](https://github.com/DioxusLabs/taffy) | | **Renderer** | Skia | WebView or WGPU | | **Components library** | Custom | None, but can use CSS libraries | | **Devtools** | Custom | Provided in Webview | From 5397df90e4b35bad90ad63af7fa52dd329ed50fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sat, 30 Sep 2023 23:46:35 +0200 Subject: [PATCH 02/12] feat: Pass the initial value of use_animation in a callback (#312) --- book/src/guides/animating.md | 2 +- crates/components/src/accordion.rs | 2 +- crates/components/src/switch.rs | 2 +- crates/hooks/src/use_animation.rs | 9 +++++---- examples/animation.rs | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/book/src/guides/animating.md b/book/src/guides/animating.md index d6ff62cee..d6fdda515 100644 --- a/book/src/guides/animating.md +++ b/book/src/guides/animating.md @@ -19,7 +19,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let animation = use_animation(cx, 0.0); + let animation = use_animation(cx, || 0.0); let progress = animation.value(); diff --git a/crates/components/src/accordion.rs b/crates/components/src/accordion.rs index bd82e459c..a96546579 100644 --- a/crates/components/src/accordion.rs +++ b/crates/components/src/accordion.rs @@ -24,7 +24,7 @@ pub struct AccordionProps<'a> { pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> { let theme = use_get_theme(cx); let accordion_theme = &theme.accordion; - let animation = use_animation(cx, 0.0); + let animation = use_animation(cx, || 0.0); let open = use_state(cx, || false); let (node_ref, size) = use_node(cx); diff --git a/crates/components/src/switch.rs b/crates/components/src/switch.rs index 16ec1b133..341ed8a8b 100644 --- a/crates/components/src/switch.rs +++ b/crates/components/src/switch.rs @@ -40,7 +40,7 @@ pub struct SwitchProps<'a> { /// #[allow(non_snake_case)] pub fn Switch<'a>(cx: Scope<'a, SwitchProps<'a>>) -> Element<'a> { - let animation = use_animation(cx, 0.0); + let animation = use_animation(cx, || 0.0); let theme = use_get_theme(cx); let hovering = use_state(cx, || false); let clicking = use_state(cx, || false); diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index 6b9feacda..d55b22ae2 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -80,7 +80,7 @@ impl<'a> AnimationManager<'a> { /// ```rust /// # use freya::prelude::*; /// fn app(cx: Scope) -> Element { -/// let animation = use_animation(cx, 0.0); +/// let animation = use_animation(cx, || 0.0); /// /// let progress = animation.value(); /// @@ -97,8 +97,9 @@ impl<'a> AnimationManager<'a> { /// } /// ``` /// -pub fn use_animation(cx: &ScopeState, init_value: f64) -> AnimationManager { +pub fn use_animation(cx: &ScopeState, init_value: impl FnOnce() -> f64) -> AnimationManager { let current_animation_id = use_state(cx, || None); + let init_value = *cx.use_hook(init_value); let value = use_state(cx, || init_value); AnimationManager { @@ -122,7 +123,7 @@ mod test { #[tokio::test] pub async fn track_progress() { fn use_animation_app(cx: Scope) -> Element { - let animation = use_animation(cx, 0.0); + let animation = use_animation(cx, || 0.0); let progress = animation.value(); @@ -163,7 +164,7 @@ mod test { #[tokio::test] pub async fn restart_progress() { fn use_animation_app(cx: Scope) -> Element { - let animation = use_animation(cx, 10.0); + let animation = use_animation(cx, || 10.0); let progress = animation.value(); diff --git a/examples/animation.rs b/examples/animation.rs index 91b32f0e0..b43a01106 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -14,7 +14,7 @@ const TIME: i32 = 500; const TARGET: f64 = 500.0; fn app(cx: Scope) -> Element { - let animation = use_animation(cx, 0.0); + let animation = use_animation(cx, || 0.0); let progress = animation.value(); From c6f02de41b251fa4ba1ab9252b9b7a7f356e0027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 1 Oct 2023 14:35:17 +0200 Subject: [PATCH 03/12] feat: ProgressBar component (#300) * feat: WIP Progress Bar * small improvements * Add entry in the book --- book/src/guides/components.md | 1 + crates/components/src/lib.rs | 2 + crates/components/src/progress_bar.rs | 92 +++++++++++++++++++++++++++ crates/hooks/src/use_theme.rs | 16 +++++ examples/progress_bar.rs | 92 +++++++++++++++++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 crates/components/src/progress_bar.rs create mode 100644 examples/progress_bar.rs diff --git a/book/src/guides/components.md b/book/src/guides/components.md index 08005dd77..c869b6205 100644 --- a/book/src/guides/components.md +++ b/book/src/guides/components.md @@ -20,3 +20,4 @@ Freya comes with a set of ready to use components: - [`Slider`](https://docs.rs/freya/latest/freya/components/fn.Slider.html) - [`ThemeProvider`](https://docs.rs/freya/latest/freya/components/fn.ThemeProvider.html) - [`Tooltip`](https://docs.rs/freya/latest/freya/components/fn.Tooltip.html) +- [`ProgressBar`](https://docs.rs/freya/latest/freya/components/fn.ProgressBar.html) diff --git a/crates/components/src/lib.rs b/crates/components/src/lib.rs index 5b940ddbd..ae3ad2aab 100644 --- a/crates/components/src/lib.rs +++ b/crates/components/src/lib.rs @@ -13,6 +13,7 @@ mod graph; mod input; mod loader; mod network_image; +mod progress_bar; mod scroll_views; mod slider; mod switch; @@ -31,6 +32,7 @@ pub use graph::*; pub use input::*; pub use loader::*; pub use network_image::*; +pub use progress_bar::*; pub use scroll_views::*; pub use slider::*; pub use switch::*; diff --git a/crates/components/src/progress_bar.rs b/crates/components/src/progress_bar.rs new file mode 100644 index 000000000..df8df6930 --- /dev/null +++ b/crates/components/src/progress_bar.rs @@ -0,0 +1,92 @@ +use dioxus::prelude::*; +use freya_elements::elements as dioxus_elements; +use freya_hooks::{use_get_theme, ProgressBarTheme}; + +/// [`ProgressBar`] component properties. +#[derive(Props, PartialEq)] +pub struct ProgressBarProps { + /// Show a label with the current progress. Default to false. + #[props(default = false)] + show_progress: bool, + + /// Width of the progress bar. Default to 100%. + #[props(default = "100%".to_string(), into)] + width: String, + + /// Height of the progress bar. Default to 20px. + #[props(default = "20".to_string(), into)] + height: String, + /// Percentage of the progress bar. + pub progress: f32, +} + +/// `ProgressBar` component. +/// +/// # Props +/// See [`ProgressBarProps`]. +/// +/// # Styling +/// Inherits the [`ProgressBarTheme`](freya_hooks::ProgressBarTheme) theme. +/// +/// # Example +/// +/// ```no_run +/// # use freya::prelude::*; +/// fn app(cx: Scope) -> Element { +/// render!( +/// ProgressBar { +/// progress: 75.0 +/// } +/// ) +/// } +/// ``` +/// +#[allow(non_snake_case)] +pub fn ProgressBar(cx: Scope) -> Element { + let theme = use_get_theme(cx); + + let ProgressBarTheme { + background, + progress_background, + } = theme.progress_bar; + let width = &cx.props.width; + let height = &cx.props.height; + let show_progress = cx.props.show_progress; + let progress = cx.props.progress; + + render!( + rect { + width: "{width}", + height: "{height}", + padding: "2", + rect { + corner_radius: "999", + width: "100%", + height: "100%", + shadow: "0 2 10 1 rgb(0, 0, 0, 45)", + background: "{background}", + font_size: "13", + direction: "horizontal", + rect { + corner_radius: "999", + width: "{progress}%", + height: "100%", + background: "{progress_background}", + display: "center", + overflow: "clip", + if show_progress { + rsx!( + label { + align: "center", + width: "100%", + color: "white", + max_lines: "1", + "{progress.floor()}%" + } + ) + } + } + } + } + ) +} diff --git a/crates/hooks/src/use_theme.rs b/crates/hooks/src/use_theme.rs index b4ddb0d37..34b50ecd5 100644 --- a/crates/hooks/src/use_theme.rs +++ b/crates/hooks/src/use_theme.rs @@ -115,6 +115,13 @@ pub struct LoaderTheme { pub secondary_color: &'static str, } +/// Theming properties for ProgressBar component. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ProgressBarTheme { + pub background: &'static str, + pub progress_background: &'static str, +} + /// Theming properties for Themes. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Theme { @@ -130,6 +137,7 @@ pub struct Theme { pub dropdown_item: DropdownItemTheme, pub accordion: AccordionTheme, pub loader: LoaderTheme, + pub progress_bar: ProgressBarTheme, } impl Default for Theme { @@ -198,6 +206,10 @@ pub const LIGHT_THEME: Theme = Theme { primary_color: "rgb(50, 50, 50)", secondary_color: "rgb(150, 150, 150)", }, + progress_bar: ProgressBarTheme { + background: "rgb(210, 210, 210)", + progress_background: "rgb(103, 80, 164)", + }, }; /// `Dark` theme @@ -254,4 +266,8 @@ pub const DARK_THEME: Theme = Theme { primary_color: "rgb(150, 150, 150)", secondary_color: "rgb(255, 255, 255)", }, + progress_bar: ProgressBarTheme { + background: "rgb(60, 60, 60)", + progress_background: "rgb(255, 95, 0)", + }, }; diff --git a/examples/progress_bar.rs b/examples/progress_bar.rs new file mode 100644 index 000000000..2e9ad40c9 --- /dev/null +++ b/examples/progress_bar.rs @@ -0,0 +1,92 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use freya::prelude::*; + +fn main() { + launch(app); +} + +fn app(cx: Scope) -> Element { + let progress_anim = use_animation(cx, 0.0); + let progress = progress_anim.value() as f32; + + let set_to_max = { + to_owned![progress_anim]; + move |_: MouseEvent| { + progress_anim.start(Animation::new_linear(progress_anim.value()..=100.0, 400)); + } + }; + + let onmoved = move |value: f64| { + progress_anim.set_value(value); + }; + + render!( + ProgressBar { + show_progress: true, + progress: progress + } + ProgressBar { + show_progress: true, + progress: progress * 0.75 + } + ProgressBar { + show_progress: true, + progress: progress * 0.50 + } + ProgressBar { + show_progress: true, + progress: progress * 0.35 + } + ProgressBar { + show_progress: true, + progress: progress * 0.15 + } + ProgressBar { + show_progress: true, + progress: progress * 0.90 + } + ProgressBar { + show_progress: true, + progress: progress * 0.70 + } + ProgressBar { + show_progress: true, + progress: progress * 0.65 + } + ProgressBar { + show_progress: true, + progress: progress * 0.30 + } + ProgressBar { + show_progress: true, + progress: progress * 0.85 + } + ProgressBar { + show_progress: true, + progress: progress * 0.60 + } + ProgressBar { + show_progress: true, + progress: progress * 0.45 + } + ProgressBar { + show_progress: true, + progress: progress * 0.20 + } + Slider { + width: 300.0, + value: progress as f64, + onmoved: onmoved + } + Button { + onclick: set_to_max, + label { + "Set to 100%" + } + } + ) +} From 7de24b4bde2035888a3a24e36d4e40cf1c299002 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 1 Oct 2023 14:38:24 +0200 Subject: [PATCH 04/12] fix --- examples/progress_bar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/progress_bar.rs b/examples/progress_bar.rs index 2e9ad40c9..cbe86d4d4 100644 --- a/examples/progress_bar.rs +++ b/examples/progress_bar.rs @@ -10,7 +10,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let progress_anim = use_animation(cx, 0.0); + let progress_anim = use_animation(cx, || 0.0); let progress = progress_anim.value() as f32; let set_to_max = { From 99dcf026aef71300b8ed2aa9567fa0f48e91ee8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 1 Oct 2023 14:42:10 +0200 Subject: [PATCH 05/12] feat: General cleanup of freya's components (#313) --- book/src/guides/animating.md | 3 +-- crates/components/Cargo.toml | 1 + crates/components/src/accordion.rs | 11 +++++------ crates/components/src/dropdown.rs | 3 +-- crates/components/src/external_link.rs | 3 +-- crates/components/src/gesture_area.rs | 4 +--- crates/components/src/graph.rs | 2 +- crates/components/src/input.rs | 13 ++++++++----- crates/components/src/loader.rs | 11 +++++++---- crates/components/src/scroll_views/scroll_bar.rs | 7 ++++--- crates/components/src/scroll_views/scroll_thumb.rs | 9 ++++++--- crates/components/src/slider.rs | 6 +++--- crates/components/src/switch.rs | 3 +-- crates/components/src/tooltip.rs | 9 +++++---- crates/hooks/src/use_accessibility.rs | 5 ++--- crates/hooks/src/use_animation.rs | 11 ++++------- crates/hooks/src/use_animation_transition.rs | 11 ++++------- crates/testing/tests/test.rs | 3 +-- examples/canvas.rs | 8 ++------ 19 files changed, 58 insertions(+), 65 deletions(-) diff --git a/book/src/guides/animating.md b/book/src/guides/animating.md index d6fdda515..9afdf8808 100644 --- a/book/src/guides/animating.md +++ b/book/src/guides/animating.md @@ -23,9 +23,8 @@ fn main() { let progress = animation.value(); - use_effect(cx, (), move |_| { + use_memo(cx, (), move |_| { animation.start(Animation::new_linear(0.0..=100.0, 50)); - async move {} }); render!(rect { diff --git a/crates/components/Cargo.toml b/crates/components/Cargo.toml index 8c6d2eeca..623f65f77 100644 --- a/crates/components/Cargo.toml +++ b/crates/components/Cargo.toml @@ -31,6 +31,7 @@ dioxus-router = { workspace = true } winit = { workspace = true } tokio = { workspace = true } +tracing = { workspace = true } open = "1" reqwest = { version = "0.11.13", features = ["json"] } diff --git a/crates/components/src/accordion.rs b/crates/components/src/accordion.rs index a96546579..9dd41c17e 100644 --- a/crates/components/src/accordion.rs +++ b/crates/components/src/accordion.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; use freya_elements::events::MouseEvent; -use freya_hooks::{use_animation, use_get_theme, use_node, Animation}; +use freya_hooks::{use_animation, use_get_theme, use_node, AccordionTheme, Animation}; /// [`Accordion`] component properties. #[derive(Props)] @@ -23,15 +23,15 @@ pub struct AccordionProps<'a> { #[allow(non_snake_case)] pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> { let theme = use_get_theme(cx); - let accordion_theme = &theme.accordion; let animation = use_animation(cx, || 0.0); let open = use_state(cx, || false); let (node_ref, size) = use_node(cx); let animation_value = animation.value(); + let AccordionTheme { background, color } = theme.accordion; // Adapt the accordion if the body size changes - use_effect( + use_memo( cx, &( size.area.width(), @@ -44,7 +44,6 @@ pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> { if (height as f64) < animation.value() && !animating { animation.set_value(size.area.height() as f64); } - async move {} } }, ); @@ -62,12 +61,12 @@ pub fn Accordion<'a>(cx: Scope<'a, AccordionProps<'a>>) -> Element<'a> { render!( rect { overflow: "clip", - color: "{accordion_theme.color}", + color: "{color}", padding: "10", corner_radius: "3", width: "100%", height: "auto", - background: "{accordion_theme.background}", + background: "{background}", onclick: onclick, &cx.props.summary rect { diff --git a/crates/components/src/dropdown.rs b/crates/components/src/dropdown.rs index 4b09127e7..1c11d6317 100644 --- a/crates/components/src/dropdown.rs +++ b/crates/components/src/dropdown.rs @@ -170,9 +170,8 @@ where let color = theme.dropdown.font_theme.color; // Update the provided value if the passed value changes - use_effect(cx, &cx.props.value, move |value| { + use_memo(cx, &cx.props.value, move |value| { *selected.write() = value; - async move {} }); // Close the dropdown if clicked anywhere diff --git a/crates/components/src/external_link.rs b/crates/components/src/external_link.rs index 3bdd75571..ba35751d6 100644 --- a/crates/components/src/external_link.rs +++ b/crates/components/src/external_link.rs @@ -47,7 +47,6 @@ pub struct ExternalLinkProps<'a> { #[allow(non_snake_case)] pub fn ExternalLink<'a>(cx: Scope<'a, ExternalLinkProps<'a>>) -> Element { let theme = use_get_theme(cx); - let theme = &theme.external_link; let is_hovering = use_state(cx, || false); let show_tooltip = cx.props.show_tooltip.unwrap_or(true); @@ -68,7 +67,7 @@ pub fn ExternalLink<'a>(cx: Scope<'a, ExternalLinkProps<'a>>) -> Element { }; let color = if *is_hovering.get() { - theme.highlight_color + theme.external_link.highlight_color } else { "inherit" }; diff --git a/crates/components/src/gesture_area.rs b/crates/components/src/gesture_area.rs index 112dde036..8d7785274 100644 --- a/crates/components/src/gesture_area.rs +++ b/crates/components/src/gesture_area.rs @@ -62,7 +62,7 @@ type EventsQueue = VecDeque<(Instant, TouchEvent)>; pub fn GestureArea<'a>(cx: Scope<'a, GestureAreaProps<'a>>) -> Element { let touch_events = use_ref::(cx, VecDeque::new); - use_effect(cx, touch_events, move |_| { + use_memo(cx, touch_events, move |_| { // Keep the touch events queue under a certain size if touch_events.read().len() > MAX_EVENTS_QUEUE { touch_events.write_silent().pop_front(); @@ -132,8 +132,6 @@ pub fn GestureArea<'a>(cx: Scope<'a, GestureAreaProps<'a>>) -> Element { _ => {} } } - - async move {} }); let ontouchcancel = |e: TouchEvent| { diff --git a/crates/components/src/graph.rs b/crates/components/src/graph.rs index 9611eb379..3552b5e48 100644 --- a/crates/components/src/graph.rs +++ b/crates/components/src/graph.rs @@ -45,7 +45,7 @@ pub struct GraphProps { pub fn Graph(cx: Scope) -> Element { let platform = use_platform(cx); - use_effect(cx, (cx.props,), move |_| async move { + use_memo(cx, (cx.props,), move |_| { platform.send(EventMessage::RequestRerender) }); diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs index 5ab00d32f..e53143920 100644 --- a/crates/components/src/input.rs +++ b/crates/components/src/input.rs @@ -3,6 +3,8 @@ use crate::ScrollView; use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; use freya_elements::events::{KeyboardData, MouseEvent}; +use freya_hooks::ButtonTheme; +use freya_hooks::FontTheme; use freya_hooks::{ use_editable, use_focus, use_get_theme, EditableConfig, EditableEvent, EditableMode, TextEditor, }; @@ -66,20 +68,18 @@ pub fn Input<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { let focus_manager = use_focus(cx); let text = &cx.props.value; - let button_theme = &theme.button; let cursor_attr = editable.cursor_attr(cx); let highlights_attr = editable.highlights_attr(cx, 0); let width = &cx.props.width; let height = &cx.props.height; let max_lines = &cx.props.max_lines; - use_effect(cx, &(cx.props.value.to_string(),), { + use_memo(cx, &(cx.props.value.to_string(),), { to_owned![editable]; move |(text,)| { editable.editor().with_mut(|editor| { editor.set(&text); }); - async move {} } }); @@ -122,8 +122,11 @@ pub fn Input<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { } else { "none".to_string() }; - let background = button_theme.background; - let color = button_theme.font_theme.color; + let ButtonTheme { + background, + font_theme: FontTheme { color, .. }, + .. + } = theme.button; render!( CursorArea { diff --git a/crates/components/src/loader.rs b/crates/components/src/loader.rs index 0c58e8579..baa72892b 100644 --- a/crates/components/src/loader.rs +++ b/crates/components/src/loader.rs @@ -2,7 +2,7 @@ use std::time::Duration; use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; -use freya_hooks::use_get_theme; +use freya_hooks::{use_get_theme, LoaderTheme}; use tokio::time::interval; /// [`Loader`] component properties. Currently empty. @@ -22,7 +22,10 @@ pub fn Loader(cx: Scope) -> Element { let theme = use_get_theme(cx); let degrees = use_state(cx, || 0); - let loader_theme = theme.loader; + let LoaderTheme { + primary_color, + secondary_color, + } = theme.loader; use_effect(cx, (), move |_| { to_owned![degrees]; @@ -45,8 +48,8 @@ pub fn Loader(cx: Scope) -> Element { height: "31", svg_content: r#" - - + + "# }) diff --git a/crates/components/src/scroll_views/scroll_bar.rs b/crates/components/src/scroll_views/scroll_bar.rs index 66a23f45f..c45477a5f 100644 --- a/crates/components/src/scroll_views/scroll_bar.rs +++ b/crates/components/src/scroll_views/scroll_bar.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; -use freya_hooks::use_get_theme; +use freya_hooks::{use_get_theme, ScrollbarTheme}; #[derive(Props)] pub struct ScrollBarProps<'a> { @@ -22,7 +22,8 @@ pub struct ScrollBarProps<'a> { #[allow(non_snake_case)] pub fn ScrollBar<'a>(cx: Scope<'a, ScrollBarProps<'a>>) -> Element<'a> { let theme = use_get_theme(cx); - let scrollbar_theme = &theme.scrollbar; + let ScrollbarTheme { background, .. } = &theme.scrollbar; + render!( rect { overflow: "clip", @@ -31,7 +32,7 @@ pub fn ScrollBar<'a>(cx: Scope<'a, ScrollBarProps<'a>>) -> Element<'a> { height: "{cx.props.height}", offset_x: "{cx.props.offset_x}", offset_y: "{cx.props.offset_y}", - background: "{scrollbar_theme.background}", + background: "{background}", &cx.props.children } ) diff --git a/crates/components/src/scroll_views/scroll_thumb.rs b/crates/components/src/scroll_views/scroll_thumb.rs index 5aa2f2d1b..4fcc1b6b4 100644 --- a/crates/components/src/scroll_views/scroll_thumb.rs +++ b/crates/components/src/scroll_views/scroll_thumb.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; use freya_elements::events::MouseEvent; -use freya_hooks::use_get_theme; +use freya_hooks::{use_get_theme, ScrollbarTheme}; #[derive(Props)] pub struct ScrollThumbProps<'a> { @@ -15,7 +15,10 @@ pub struct ScrollThumbProps<'a> { #[allow(non_snake_case)] pub fn ScrollThumb<'a>(cx: Scope<'a, ScrollThumbProps<'a>>) -> Element<'a> { let theme = use_get_theme(cx); - let scrollbar_theme = &theme.scrollbar; + let ScrollbarTheme { + thumb_background, .. + } = &theme.scrollbar; + render!( rect { onmousedown: |e| { @@ -28,7 +31,7 @@ pub fn ScrollThumb<'a>(cx: Scope<'a, ScrollThumbProps<'a>>) -> Element<'a> { width: "100%", height: "100%", corner_radius: "8", - background: "{scrollbar_theme.thumb_background}", + background: "{thumb_background}", } } ) diff --git a/crates/components/src/slider.rs b/crates/components/src/slider.rs index 261d15e3f..e54c66235 100644 --- a/crates/components/src/slider.rs +++ b/crates/components/src/slider.rs @@ -2,6 +2,7 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; use freya_elements::events::{MouseEvent, WheelEvent}; use freya_hooks::{use_get_theme, use_node_ref}; +use tracing::info; /// [`Slider`] component properties. #[derive(Props)] @@ -17,11 +18,10 @@ pub struct SliderProps<'a> { #[inline] fn ensure_correct_slider_range(value: f64) -> f64 { if value < 0.0 { - // TODO: Better logging - println!("Slider value is less than 0.0, setting to 0.0"); + info!("Slider value is less than 0.0, setting to 0.0"); 0.0 } else if value > 100.0 { - println!("Slider value is greater than 100.0, setting to 100.0"); + info!("Slider value is greater than 100.0, setting to 100.0"); 100.0 } else { value diff --git a/crates/components/src/switch.rs b/crates/components/src/switch.rs index 341ed8a8b..31ce004b1 100644 --- a/crates/components/src/switch.rs +++ b/crates/components/src/switch.rs @@ -80,13 +80,12 @@ pub fn Switch<'a>(cx: Scope<'a, SwitchProps<'a>>) -> Element<'a> { } }; - use_effect(cx, &cx.props.enabled, move |enabled| { + use_memo(cx, &cx.props.enabled, move |enabled| { if enabled { animation.start(Animation::new_sine_in_out(0.0..=25.0, 200)); } else if animation.value() > 0.0 { animation.start(Animation::new_sine_in_out(25.0..=0.0, 200)); } - async move {} }); render!( diff --git a/crates/components/src/tooltip.rs b/crates/components/src/tooltip.rs index 507a176d8..757103de4 100644 --- a/crates/components/src/tooltip.rs +++ b/crates/components/src/tooltip.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; -use freya_hooks::use_get_theme; +use freya_hooks::{use_get_theme, TooltipTheme}; /// [`Tooltip`] component properties. #[derive(Props)] @@ -20,7 +20,8 @@ pub struct TooltipProps<'a> { #[allow(non_snake_case)] pub fn Tooltip<'a>(cx: Scope<'a, TooltipProps<'a>>) -> Element { let theme = use_get_theme(cx); - let theme = &theme.tooltip; + let TooltipTheme { background, color } = &theme.tooltip; + render!( rect { height: "30", @@ -33,11 +34,11 @@ pub fn Tooltip<'a>(cx: Scope<'a, TooltipProps<'a>>) -> Element { height: "100%", shadow: "0 0 10 5 rgb(0, 0, 0, 50)", corner_radius: "8", - background: "{theme.background}", + background: "{background}", display: "center", label { max_lines: "1", - color: "{theme.color}", + color: "{color}", "{cx.props.url}" } } diff --git a/crates/hooks/src/use_accessibility.rs b/crates/hooks/src/use_accessibility.rs index 84c9bf2a7..5467aa6cb 100644 --- a/crates/hooks/src/use_accessibility.rs +++ b/crates/hooks/src/use_accessibility.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeState; -use dioxus_hooks::{to_owned, use_effect, use_shared_state}; +use dioxus_hooks::{to_owned, use_effect, use_memo, use_shared_state}; use freya_common::EventMessage; use freya_core::FocusReceiver; @@ -11,13 +11,12 @@ pub fn use_init_accessibility(cx: &ScopeState) { let focused_id = use_shared_state::>(cx).unwrap(); let current_focused_id = *focused_id.read(); - use_effect(cx, &(current_focused_id,), move |(focused_id,)| { + use_memo(cx, &(current_focused_id,), move |(focused_id,)| { if let Some(focused_id) = focused_id { platform .send(EventMessage::FocusAccessibilityNode(focused_id)) .unwrap(); } - async move {} }); use_effect(cx, (), { diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index d55b22ae2..672b0eb53 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -84,9 +84,8 @@ impl<'a> AnimationManager<'a> { /// /// let progress = animation.value(); /// -/// use_effect(cx, (), move |_| { +/// use_memo(cx, (), move |_| { /// animation.start(Animation::new_linear(0.0..=100.0, 50)); -/// async move {} /// }); /// /// render!( @@ -115,7 +114,7 @@ mod test { use std::time::Duration; use crate::{use_animation, Animation}; - use dioxus_hooks::{to_owned, use_effect}; + use dioxus_hooks::{to_owned, use_memo}; use freya::prelude::*; use freya_testing::{launch_test, FreyaEvent, MouseButton}; use tokio::time::sleep; @@ -127,9 +126,8 @@ mod test { let progress = animation.value(); - use_effect(cx, (), move |_| { + use_memo(cx, (), move |_| { animation.start(Animation::new_linear(0.0..=100.0, 50)); - async move {} }); render!(rect { @@ -175,9 +173,8 @@ mod test { } }; - use_effect(cx, (), move |_| { + use_memo(cx, (), move |_| { animation.start(Animation::new_linear(10.0..=100.0, 50)); - async move {} }); render!(rect { diff --git a/crates/hooks/src/use_animation_transition.rs b/crates/hooks/src/use_animation_transition.rs index aa5c43035..e456ecb48 100644 --- a/crates/hooks/src/use_animation_transition.rs +++ b/crates/hooks/src/use_animation_transition.rs @@ -1,5 +1,5 @@ use dioxus_core::ScopeState; -use dioxus_hooks::{use_effect, use_memo, use_state, UseFutureDep, UseState}; +use dioxus_hooks::{use_memo, use_state, UseFutureDep, UseState}; use freya_engine::prelude::Color; use freya_node_state::Parse; use std::time::Duration; @@ -252,9 +252,8 @@ impl<'a> TransitionsManager<'a> { /// /// let progress = animation.get(0).unwrap().as_size(); /// -/// use_effect(cx, (), move |_| { +/// use_memo(cx, (), move |_| { /// animation.start(); -/// async move {} /// }); /// /// render!( @@ -278,11 +277,10 @@ where let transitions = use_memo(cx, dependencies.clone(), &mut init); let transitions_storage = use_state(cx, || animations_map(transitions)); - use_effect(cx, dependencies, { + use_memo(cx, dependencies, { let storage_setter = transitions_storage.setter(); move |v| { storage_setter(animations_map(&init(v))); - async move {} } }); @@ -322,9 +320,8 @@ mod test { let progress = animation.get(0).unwrap().as_size(); - use_effect(cx, (), move |_| { + use_memo(cx, (), move |_| { animation.start(); - async move {} }); render!(rect { diff --git a/crates/testing/tests/test.rs b/crates/testing/tests/test.rs index 7fae0ac5f..ca00f5533 100644 --- a/crates/testing/tests/test.rs +++ b/crates/testing/tests/test.rs @@ -24,9 +24,8 @@ async fn with_state() { fn stateful_app(cx: Scope) -> Element { let state = use_state(cx, || false); - use_effect(cx, (), |_| { + use_memo(cx, (), |_| { state.set(true); - async move {} }); render!( diff --git a/examples/canvas.rs b/examples/canvas.rs index b97ace7cd..0e2f1e55f 100644 --- a/examples/canvas.rs +++ b/examples/canvas.rs @@ -174,12 +174,8 @@ fn Editor(cx: Scope) -> Element { let font_style = if *is_italic.get() { "italic" } else { "normal" }; let font_weight = if *is_bold.get() { "bold" } else { "normal" }; - use_effect(cx, (), { - to_owned![focus_manager]; - move |_| { - focus_manager.focus(); - async move {} - } + use_memo(cx, (), |_| { + focus_manager.focus(); }); let onclick = { From 4d57f6a9a4729080909cb82d6d53ea72909a210b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 1 Oct 2023 14:48:56 +0200 Subject: [PATCH 06/12] feat: Skia-safe v0.66.3 (#319) --- Cargo.toml | 2 +- crates/engine/src/mocked.rs | 37 ++++++++++++++++++++++++++++++++++ crates/engine/src/skia.rs | 2 +- crates/renderer/src/window.rs | 4 ++-- crates/state/src/font_style.rs | 2 +- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 041f86eea..3d55e4b51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ dioxus-core = { version = "0.4" } dioxus-hot-reload = { version = "0.4", features = ["file_watcher"] } dioxus-router = { version = "0.4" } -skia-safe = { version = "0.63.0", features = ["gl", "textlayout", "svg"] } +skia-safe = { version = "0.66.3", features = ["gl", "textlayout", "svg"] } gl = "0.14.0" glutin = "0.30.6" diff --git a/crates/engine/src/mocked.rs b/crates/engine/src/mocked.rs index f32e12557..6eb0e1690 100644 --- a/crates/engine/src/mocked.rs +++ b/crates/engine/src/mocked.rs @@ -1435,10 +1435,47 @@ impl DirectContext { use std::ffi::c_void; +#[repr(u8)] +pub enum Protected { + No, + Yes, +} + #[derive(Clone, Copy)] pub struct FramebufferInfo { pub fboid: i32, pub format: Format, + pub protected: Protected, +} + +impl Default for FramebufferInfo { + fn default() -> Self { + Self { + fboid: 0, + format: 0, + protected: Protected::No, + } + } +} + +pub fn wrap_backend_render_target( + context: &mut RecordingContext, + backend_render_target: &BackendRenderTarget, + origin: SurfaceOrigin, + color_type: ColorType, + color_space: impl Into>, + surface_props: Option<&SurfaceProps>, +) -> Option { + Surface::from_ptr(unsafe { + sb::C_SkSurfaces_WrapBackendRenderTarget( + context.native_mut(), + backend_render_target.native(), + origin, + color_type.into_native(), + color_space.into().into_ptr_or_null(), + surface_props.native_ptr_or_null(), + ) + }) } pub struct Interface; diff --git a/crates/engine/src/skia.rs b/crates/engine/src/skia.rs index 22aa0053d..3877e9b54 100644 --- a/crates/engine/src/skia.rs +++ b/crates/engine/src/skia.rs @@ -2,7 +2,7 @@ pub use skia_safe::{ font_style::{Slant, Weight, Width}, gpu::{ gl::{Format, FramebufferInfo, Interface}, - BackendRenderTarget, DirectContext, RecordingContext, SurfaceOrigin, + BackendRenderTarget, DirectContext, RecordingContext, SurfaceOrigin,surfaces::wrap_backend_render_target }, gradient_shader::GradientShaderColors, path::ArcSize, diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index 1d689aef5..f0ced3b0d 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -152,6 +152,7 @@ impl WindowEnv { FramebufferInfo { fboid: fboid.try_into().unwrap(), format: Format::RGBA8.into(), + ..Default::default() } }; @@ -307,8 +308,7 @@ fn create_surface( ); let backend_render_target = BackendRenderTarget::new_gl(size, num_samples, stencil_size, fb_info); - - Surface::from_backend_render_target( + wrap_backend_render_target( gr_context, &backend_render_target, SurfaceOrigin::BottomLeft, diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 9cc0965e6..46949f876 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -62,7 +62,7 @@ impl From<&FontStyleState> for TextStyle { text_style.add_shadow(*shadow); } - *text_style.decoration_mut() = value.decoration; + text_style.set_decoration(&value.decoration); text_style } From 61adbeeb772223174470e4a1a843d8a2349e298b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 1 Oct 2023 14:54:37 +0200 Subject: [PATCH 07/12] feat: Improved measuring of cached layout (#314) * feat: Improved measuring of cached layout * clean up * fixes * fix * clean up * clean up * feat: Skip measuring of cached siblings in layout (#317) * feat: Skip measuring of cached siblings in layout * clean up * remove fxhash * fix test * fixes * . * clean up * new benchmark * add_with_depth * fix * fix * fix * fixes * fixes * fixes * fixes * Update bench.rs * Revert "feat: Skip measuring of cached siblings in layout (#317)" (#320) This reverts commit cc2934656ebeaf1536ea7af65195abdb12e3d395. --- crates/torin/src/torin.rs | 26 ++++++++++++++++++++++---- crates/torin/tests/test.rs | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/torin/src/torin.rs b/crates/torin/src/torin.rs index 121c9ff3f..8b3803580 100644 --- a/crates/torin/src/torin.rs +++ b/crates/torin/src/torin.rs @@ -201,12 +201,30 @@ impl Torin { if parent.does_depend_on_inner() { self.check_dirty_dependants(parent_id, dom_adapter, true); } - // Otherwise we simply mark this Node siblings + // Mark as dirty all the siblings that come after this node else { - // TODO(marc2332): Only mark those who come before this node. + let mut found_node = false; + let mut multiple_children = false; for child_id in dom_adapter.children_of(&parent_id) { - if child_id != node_id { - self.check_dirty_dependants(child_id, dom_adapter, true) + if found_node { + self.check_dirty_dependants(child_id, dom_adapter, true); + } + if child_id == node_id { + found_node = true; + } else { + multiple_children = true; + } + } + + // Try saving using node's parent as root candidate if it has multiple children + if multiple_children { + if let RootNodeCandidate::Valid(root_candidate) = self.root_node_candidate { + let closest_parent = + dom_adapter.closest_common_parent(&parent_id, &root_candidate); + + if let Some(closest_parent) = closest_parent { + self.root_node_candidate = RootNodeCandidate::Valid(closest_parent); + } } } } diff --git a/crates/torin/tests/test.rs b/crates/torin/tests/test.rs index a12172736..a0efbf200 100644 --- a/crates/torin/tests/test.rs +++ b/crates/torin/tests/test.rs @@ -74,7 +74,7 @@ impl DOMAdapter for TestingDOM { } fn closest_common_parent(&self, node_id_a: &usize, _node_id_b: &usize) -> Option { - Some(self.parent_of(node_id_a)?) + Some(self.parent_of(node_id_a).unwrap_or(*node_id_a)) } } From 71c635c945cc808e537446a1cfa9c00d84871021 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 1 Oct 2023 15:40:10 +0200 Subject: [PATCH 08/12] chore: Update the book --- book/src/guides/components.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/book/src/guides/components.md b/book/src/guides/components.md index c869b6205..b279a972e 100644 --- a/book/src/guides/components.md +++ b/book/src/guides/components.md @@ -19,5 +19,4 @@ Freya comes with a set of ready to use components: - [`NetworkImage`](https://docs.rs/freya/latest/freya/components/fn.NetworkImage.html) - [`Slider`](https://docs.rs/freya/latest/freya/components/fn.Slider.html) - [`ThemeProvider`](https://docs.rs/freya/latest/freya/components/fn.ThemeProvider.html) -- [`Tooltip`](https://docs.rs/freya/latest/freya/components/fn.Tooltip.html) -- [`ProgressBar`](https://docs.rs/freya/latest/freya/components/fn.ProgressBar.html) +- [`Tooltip`](https://docs.rs/freya/latest/freya/components/fn.Tooltip.html) \ No newline at end of file From 8e3b2ecad9678585e32af16c11b8e235f9dfade5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Wed, 11 Oct 2023 21:31:04 +0200 Subject: [PATCH 09/12] feat: Avoid extra animations overhead (#321) * feat: Limit animations to 60fps for now * tweak * fix transitions --- crates/engine/src/skia.rs | 3 ++- crates/hooks/src/use_animation.rs | 6 ++++-- crates/hooks/src/use_animation_transition.rs | 7 ++++--- crates/renderer/src/window.rs | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/skia.rs b/crates/engine/src/skia.rs index 3877e9b54..e73b8784e 100644 --- a/crates/engine/src/skia.rs +++ b/crates/engine/src/skia.rs @@ -2,7 +2,8 @@ pub use skia_safe::{ font_style::{Slant, Weight, Width}, gpu::{ gl::{Format, FramebufferInfo, Interface}, - BackendRenderTarget, DirectContext, RecordingContext, SurfaceOrigin,surfaces::wrap_backend_render_target + surfaces::wrap_backend_render_target, + BackendRenderTarget, DirectContext, RecordingContext, SurfaceOrigin, }, gradient_shader::GradientShaderColors, path::ArcSize, diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index 672b0eb53..1caad2148 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -6,6 +6,8 @@ use uuid::Uuid; use crate::Animation; +const ANIMATION_MS: i32 = 16; // Assume 60 FPS for now + /// Manage the lifecyle of an [Animation]. #[derive(Clone)] pub struct AnimationManager<'a> { @@ -29,7 +31,7 @@ impl<'a> AnimationManager<'a> { // Spawn the animation that will run at 1ms speed self.cx.spawn(async move { - let mut ticker = interval(Duration::from_millis(1)); + let mut ticker = interval(Duration::from_millis(ANIMATION_MS as u64)); loop { // Stop running the animation if it was removed if *current_animation_id.current() == Some(new_id) { @@ -41,7 +43,7 @@ impl<'a> AnimationManager<'a> { // Advance one tick value.set(anim.move_value(index)); - index += 1; + index += ANIMATION_MS; // Wait 1m ticker.tick().await; diff --git a/crates/hooks/src/use_animation_transition.rs b/crates/hooks/src/use_animation_transition.rs index e456ecb48..6487e37f0 100644 --- a/crates/hooks/src/use_animation_transition.rs +++ b/crates/hooks/src/use_animation_transition.rs @@ -8,6 +8,8 @@ use uuid::Uuid; use crate::{Animation, TransitionAnimation}; +const ANIMATION_MS: i32 = 16; // Assume 60 FPS for now + /// Configure a `Transition` animation. #[derive(Clone, Debug, Copy, PartialEq)] pub enum Transition { @@ -172,7 +174,7 @@ impl<'a> TransitionsManager<'a> { // Spawn the animation that will run at 1ms speed self.cx.spawn(async move { - let mut ticker = interval(Duration::from_millis(1)); + let mut ticker = interval(Duration::from_millis(ANIMATION_MS as u64)); let mut index = 0; loop { // Stop running the animation if it's no longer selected @@ -193,7 +195,7 @@ impl<'a> TransitionsManager<'a> { } }); - index += 1; + index += ANIMATION_MS; // Wait 1ms ticker.tick().await; @@ -305,7 +307,6 @@ mod test { use std::time::Duration; use crate::{use_animation_transition, Transition, TransitionAnimation}; - use dioxus_hooks::use_effect; use freya::prelude::*; use freya_testing::launch_test; use tokio::time::sleep; diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index f0ced3b0d..29acf0e55 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -308,7 +308,7 @@ fn create_surface( ); let backend_render_target = BackendRenderTarget::new_gl(size, num_samples, stencil_size, fb_info); - wrap_backend_render_target( + wrap_backend_render_target( gr_context, &backend_render_target, SurfaceOrigin::BottomLeft, From f6842a0d1cdc03abd8bc92484252205af7224f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Wed, 11 Oct 2023 21:36:34 +0200 Subject: [PATCH 10/12] chore: Disable web dioxus features (#323) * chore: Disable web dioxus features * docs --- Cargo.toml | 4 ++-- README.md | 2 +- book/src/guides/getting_started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d55e4b51..0efd64d02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ freya-testing = { path = "crates/testing", version = "0.1" } freya-engine = { path = "crates/engine", version = "0.1" } torin = { path = "crates/torin", version = "0.1" } -dioxus = { version = "0.4" } +dioxus = { version = "0.4", default-features = false } dioxus-native-core-macro = { version = "0.4" } dioxus-rsx = { version = "0.4", features = ["hot_reload"] } dioxus-native-core = { version = "0.4", features = ["dioxus"] } @@ -35,7 +35,7 @@ dioxus-core-macro = { version = "0.4" } dioxus-hooks = { version = "0.4" } dioxus-core = { version = "0.4" } dioxus-hot-reload = { version = "0.4", features = ["file_watcher"] } -dioxus-router = { version = "0.4" } +dioxus-router = { version = "0.4", default-features = false } skia-safe = { version = "0.66.3", features = ["gl", "textlayout", "svg"] } diff --git a/README.md b/README.md index e2c0a9dd8..dc2309cb0 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Add Freya and Dioxus as dependencies: ```toml freya = "0.1" -dioxus = { version = "0.4", features = ["macro", "hooks"] } +dioxus = { version = "0.4", features = ["macro", "hooks"], default-features = false } ``` ### Features ✨ diff --git a/book/src/guides/getting_started.md b/book/src/guides/getting_started.md index b8574e5b4..3ce82c631 100644 --- a/book/src/guides/getting_started.md +++ b/book/src/guides/getting_started.md @@ -24,7 +24,7 @@ edition = "2021" [dependencies] freya = "0.1" -dioxus = { version = "0.4", features = ["macro", "hooks"] } +dioxus = { version = "0.4", features = ["macro", "hooks"], default-features = false } ``` ### src/main.rs From 1b8edd7b884bc0866cf89f42f83c0431fcb6b0a1 Mon Sep 17 00:00:00 2001 From: OlshaMB <68439653+OlshaMB@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:09:07 +0300 Subject: [PATCH 11/12] feat: Password mode for `Input` component (#325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adds hidden input * docs: Document the InputIsHidden enum * style: renaming, descriptions, new_password Authored-By: marc2332 Co-authored-by: Marc Espín * fixes --------- Co-authored-by: Marc Espín --- crates/components/src/input.rs | 22 ++++++++++++++- examples/password.rs | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 examples/password.rs diff --git a/crates/components/src/input.rs b/crates/components/src/input.rs index e53143920..6ed575227 100644 --- a/crates/components/src/input.rs +++ b/crates/components/src/input.rs @@ -9,7 +9,21 @@ use freya_hooks::{ use_editable, use_focus, use_get_theme, EditableConfig, EditableEvent, EditableMode, TextEditor, }; use winit::window::CursorIcon; +/// Enum to declare is [`Input`] hidden. +#[derive(Default)] +pub enum InputMode { + /// The input text is shown + #[default] + Shown, + /// The input text is obfuscated with a character + Hidden(char), +} +impl InputMode { + pub fn new_password() -> Self { + Self::Hidden('*') + } +} /// [`Input`] component properties. #[derive(Props)] pub struct InputProps<'a> { @@ -17,6 +31,9 @@ pub struct InputProps<'a> { pub value: String, /// Handler for the `onchange` event. pub onchange: EventHandler<'a, String>, + /// Is input hidden with a character. By default input text is shown. + #[props(default = InputMode::Shown, into)] + hidden: InputMode, /// Width of the Input. Default 100. #[props(default = "150".to_string(), into)] width: String, @@ -67,7 +84,10 @@ pub fn Input<'a>(cx: Scope<'a, InputProps<'a>>) -> Element { let theme = use_get_theme(cx); let focus_manager = use_focus(cx); - let text = &cx.props.value; + let text = match cx.props.hidden { + InputMode::Hidden(ch) => ch.to_string().repeat(cx.props.value.len()), + InputMode::Shown => cx.props.value.clone(), + }; let cursor_attr = editable.cursor_attr(cx); let highlights_attr = editable.highlights_attr(cx, 0); let width = &cx.props.width; diff --git a/examples/password.rs b/examples/password.rs new file mode 100644 index 000000000..75d17c947 --- /dev/null +++ b/examples/password.rs @@ -0,0 +1,51 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use freya::prelude::*; + +fn main() { + launch(app); +} + +fn app(cx: Scope) -> Element { + let password = use_state(cx, || String::new()); + let is_hidden = use_state(cx, || true); + render!( + rect { + overflow: "clip", + padding: "7", + width: "100%", + height: "100%", + label { + color: "black", + "Password:" + } + rect { + direction: "horizontal", + Input { + hidden: if !is_hidden { + InputMode::Shown + } else { + InputMode::new_password() + }, + value: password.get().clone(), + onchange: |e| { + password.set(e) + } + }, + Button { + onclick: |_| is_hidden.modify(|v| !v), + label { + if *is_hidden.get() { + "Show password" + } else { + "Hide password" + } + } + } + } + } + ) +} From d7d689643896d16358348e30e659a735f88ebdb8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:31:45 +0200 Subject: [PATCH 12/12] Configure Renovate (#334) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add renovate.json * Update renovate.json --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Marc Espín --- renovate.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..027bdefd9 --- /dev/null +++ b/renovate.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ], + "packageRules": [ + { + "matchPackagePatterns": [ + "*" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ], + "groupName": "all non-major dependencies", + "groupSlug": "all-minor-patch" + } + ] +}