From c17af3b4cfcd9b808bb83c2a7f8600ce0aac5a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 15 Oct 2023 16:39:14 +0200 Subject: [PATCH 1/6] feat: Add support to customize text color inside progress bar component (#338) --- crates/components/src/progress_bar.rs | 3 ++- crates/hooks/src/use_theme.rs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/components/src/progress_bar.rs b/crates/components/src/progress_bar.rs index df8df6930..d2a217399 100644 --- a/crates/components/src/progress_bar.rs +++ b/crates/components/src/progress_bar.rs @@ -46,6 +46,7 @@ pub fn ProgressBar(cx: Scope) -> Element { let theme = use_get_theme(cx); let ProgressBarTheme { + color, background, progress_background, } = theme.progress_bar; @@ -79,7 +80,7 @@ pub fn ProgressBar(cx: Scope) -> Element { label { align: "center", width: "100%", - color: "white", + color: "{color}", max_lines: "1", "{progress.floor()}%" } diff --git a/crates/hooks/src/use_theme.rs b/crates/hooks/src/use_theme.rs index 34b50ecd5..63c60d479 100644 --- a/crates/hooks/src/use_theme.rs +++ b/crates/hooks/src/use_theme.rs @@ -118,6 +118,7 @@ pub struct LoaderTheme { /// Theming properties for ProgressBar component. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ProgressBarTheme { + pub color: &'static str, pub background: &'static str, pub progress_background: &'static str, } @@ -207,6 +208,7 @@ pub const LIGHT_THEME: Theme = Theme { secondary_color: "rgb(150, 150, 150)", }, progress_bar: ProgressBarTheme { + color: "black", background: "rgb(210, 210, 210)", progress_background: "rgb(103, 80, 164)", }, @@ -267,6 +269,7 @@ pub const DARK_THEME: Theme = Theme { secondary_color: "rgb(255, 255, 255)", }, progress_bar: ProgressBarTheme { + color: "white", background: "rgb(60, 60, 60)", progress_background: "rgb(255, 95, 0)", }, From c128ca58be42a9b5c34fe53d47d0cb33e36cac76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 15 Oct 2023 17:17:05 +0200 Subject: [PATCH 2/6] feat: min_width + min_height and max_width + max_height window modifiers (#331) --- crates/renderer/src/config.rs | 60 +++++++++++++++++++++++++++-------- crates/renderer/src/window.rs | 10 +++++- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/crates/renderer/src/config.rs b/crates/renderer/src/config.rs index ae441d365..f631fb732 100644 --- a/crates/renderer/src/config.rs +++ b/crates/renderer/src/config.rs @@ -11,6 +11,14 @@ pub struct WindowConfig { pub width: f64, /// Height of the window. pub height: f64, + /// Minimum width of the Window. + pub min_width: Option, + /// Minimum height of the window. + pub min_height: Option, + /// Maximum width of the Window. + pub max_width: Option, + /// Maximum height of the window. + pub max_height: Option, /// Enable Window decorations. pub decorations: bool, /// Title for the Window. @@ -29,17 +37,7 @@ pub struct WindowConfig { impl Default for WindowConfig { fn default() -> Self { - Self { - width: 600.0, - height: 600.0, - decorations: true, - title: "Freya app", - transparent: false, - state: None, - background: Color::WHITE, - on_setup: None, - on_exit: None, - } + LaunchConfigBuilder::default().build().window } } @@ -63,6 +61,10 @@ pub type WindowCallback = Arc>; pub struct LaunchConfigBuilder<'a, T> { pub width: f64, pub height: f64, + pub min_width: Option, + pub min_height: Option, + pub max_width: Option, + pub max_height: Option, pub decorations: bool, pub title: &'static str, pub transparent: bool, @@ -76,8 +78,12 @@ pub struct LaunchConfigBuilder<'a, T> { impl Default for LaunchConfigBuilder<'_, T> { fn default() -> Self { Self { - width: 350.0, - height: 350.0, + width: 600.0, + height: 600.0, + min_width: None, + min_height: None, + max_height: None, + max_width: None, decorations: true, title: "Freya app", transparent: false, @@ -103,6 +109,30 @@ impl<'a, T: Clone> LaunchConfigBuilder<'a, T> { self } + /// Specify a minimum Window width. + pub fn with_min_width(mut self, min_width: f64) -> Self { + self.min_width = Some(min_width); + self + } + + /// Specify a minimum Window height. + pub fn with_min_height(mut self, min_height: f64) -> Self { + self.min_height = Some(min_height); + self + } + + /// Specify a maximum Window width. + pub fn with_max_width(mut self, max_width: f64) -> Self { + self.max_width = Some(max_width); + self + } + + /// Specify a maximum Window height. + pub fn with_max_height(mut self, max_height: f64) -> Self { + self.max_height = Some(max_height); + self + } + /// Whether the Window will have decorations or not. pub fn with_decorations(mut self, decorations: bool) -> Self { self.decorations = decorations; @@ -157,6 +187,10 @@ impl<'a, T: Clone> LaunchConfigBuilder<'a, T> { window: WindowConfig { width: self.width, height: self.height, + min_width: self.min_width, + min_height: self.min_height, + max_width: self.max_width, + max_height: self.max_height, title: self.title, decorations: self.decorations, transparent: self.transparent, diff --git a/crates/renderer/src/window.rs b/crates/renderer/src/window.rs index 29acf0e55..a166ef4f3 100644 --- a/crates/renderer/src/window.rs +++ b/crates/renderer/src/window.rs @@ -52,7 +52,7 @@ impl WindowEnv { window_config: WindowConfig, event_loop: &EventLoop, ) -> Self { - let window_builder = WindowBuilder::new() + let mut window_builder = WindowBuilder::new() .with_visible(false) .with_title(window_config.title) .with_decorations(window_config.decorations) @@ -62,6 +62,14 @@ impl WindowEnv { window_config.height, )); + if let Some(min_size) = window_config.min_width.zip(window_config.min_height) { + window_builder = window_builder.with_min_inner_size(LogicalSize::::from(min_size)) + } + + if let Some(max_size) = window_config.max_width.zip(window_config.max_height) { + window_builder = window_builder.with_max_inner_size(LogicalSize::::from(max_size)) + } + let template = ConfigTemplateBuilder::new() .with_alpha_size(8) .with_transparency(window_config.transparent); From ebfa9cc7b6609f6700eb1c800f27fcddb2550d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Mon, 16 Oct 2023 23:36:58 +0200 Subject: [PATCH 3/6] feat: Add hover and active effects for the scrollbar thumbs (#333) --- .../src/scroll_views/scroll_thumb.rs | 26 ++++++++++++++++--- .../src/scroll_views/scroll_view.rs | 21 ++++++++++++--- .../src/scroll_views/virtual_scroll_view.rs | 21 ++++++++++++--- crates/hooks/src/use_theme.rs | 6 +++++ 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/crates/components/src/scroll_views/scroll_thumb.rs b/crates/components/src/scroll_views/scroll_thumb.rs index 4fcc1b6b4..cdf501521 100644 --- a/crates/components/src/scroll_views/scroll_thumb.rs +++ b/crates/components/src/scroll_views/scroll_thumb.rs @@ -1,10 +1,11 @@ use dioxus::prelude::*; use freya_elements::elements as dioxus_elements; use freya_elements::events::MouseEvent; -use freya_hooks::{use_get_theme, ScrollbarTheme}; +use freya_hooks::use_get_theme; #[derive(Props)] pub struct ScrollThumbProps<'a> { + clicking_scrollbar: bool, onmousedown: EventHandler<'a, MouseEvent>, #[props(into)] width: String, @@ -12,15 +13,32 @@ pub struct ScrollThumbProps<'a> { height: String, } +#[derive(Debug, Default, PartialEq, Clone, Copy)] +pub enum ScrollThumbState { + #[default] + Idle, + // Thumb is being hovered + Hovering, +} + #[allow(non_snake_case)] pub fn ScrollThumb<'a>(cx: Scope<'a, ScrollThumbProps<'a>>) -> Element<'a> { let theme = use_get_theme(cx); - let ScrollbarTheme { - thumb_background, .. - } = &theme.scrollbar; + let state = use_state(cx, ScrollThumbState::default); + let thumb_background = match state.get() { + _ if cx.props.clicking_scrollbar => theme.scrollbar.active_thumb_background, + ScrollThumbState::Idle => theme.scrollbar.thumb_background, + ScrollThumbState::Hovering => theme.scrollbar.hover_thumb_background, + }; render!( rect { + onmouseenter: |_| { + state.set(ScrollThumbState::Hovering) + }, + onmouseleave: |_| { + state.set(ScrollThumbState::Idle) + }, onmousedown: |e| { cx.props.onmousedown.call(e); }, diff --git a/crates/components/src/scroll_views/scroll_view.rs b/crates/components/src/scroll_views/scroll_view.rs index f4060d4f1..e2a4042f8 100644 --- a/crates/components/src/scroll_views/scroll_view.rs +++ b/crates/components/src/scroll_views/scroll_view.rs @@ -227,18 +227,20 @@ pub fn ScrollView<'a>(cx: Scope<'a, ScrollViewProps<'a>>) -> Element { // Mark the Y axis scrollbar as the one being dragged let onmousedown_y = |e: MouseEvent| { let coordinates = e.get_element_coordinates(); - *clicking_scrollbar.write_silent() = Some((Axis::Y, coordinates.y)); + *clicking_scrollbar.write() = Some((Axis::Y, coordinates.y)); }; // Mark the X axis scrollbar as the one being dragged let onmousedown_x = |e: MouseEvent| { let coordinates = e.get_element_coordinates(); - *clicking_scrollbar.write_silent() = Some((Axis::X, coordinates.x)); + *clicking_scrollbar.write() = Some((Axis::X, coordinates.x)); }; // Unmark any scrollbar let onclick = |_: MouseEvent| { - *clicking_scrollbar.write_silent() = None; + if clicking_scrollbar.read().is_some() { + *clicking_scrollbar.write() = None; + } }; let horizontal_scrollbar_size = if horizontal_scrollbar_is_visible { @@ -252,6 +254,17 @@ pub fn ScrollView<'a>(cx: Scope<'a, ScrollViewProps<'a>>) -> Element { 0 }; + let is_scrolling_x = clicking_scrollbar + .read() + .as_ref() + .map(|f| f.0 == Axis::X) + .unwrap_or_default(); + let is_scrolling_y = clicking_scrollbar + .read() + .as_ref() + .map(|f| f.0 == Axis::Y) + .unwrap_or_default(); + render!( rect { role: "scrollView", @@ -284,6 +297,7 @@ pub fn ScrollView<'a>(cx: Scope<'a, ScrollViewProps<'a>>) -> Element { height: "{horizontal_scrollbar_size}", offset_x: "{scrollbar_x}", ScrollThumb { + clicking_scrollbar: is_scrolling_x, onmousedown: onmousedown_x, width: "{scrollbar_width}", height: "100%", @@ -295,6 +309,7 @@ pub fn ScrollView<'a>(cx: Scope<'a, ScrollViewProps<'a>>) -> Element { height: "100%", offset_y: "{scrollbar_y}", ScrollThumb { + clicking_scrollbar: is_scrolling_y, onmousedown: onmousedown_y, width: "100%", height: "{scrollbar_height}", diff --git a/crates/components/src/scroll_views/virtual_scroll_view.rs b/crates/components/src/scroll_views/virtual_scroll_view.rs index d90ea6e96..da208ba2f 100644 --- a/crates/components/src/scroll_views/virtual_scroll_view.rs +++ b/crates/components/src/scroll_views/virtual_scroll_view.rs @@ -262,18 +262,20 @@ pub fn VirtualScrollView<'a, T>(cx: Scope<'a, VirtualScrollViewProps<'a, T>>) -> // Mark the Y axis scrollbar as the one being dragged let onmousedown_y = |e: MouseEvent| { let coordinates = e.get_element_coordinates(); - *clicking_scrollbar.write_silent() = Some((Axis::Y, coordinates.y)); + *clicking_scrollbar.write() = Some((Axis::Y, coordinates.y)); }; // Mark the X axis scrollbar as the one being dragged let onmousedown_x = |e: MouseEvent| { let coordinates = e.get_element_coordinates(); - *clicking_scrollbar.write_silent() = Some((Axis::X, coordinates.x)); + *clicking_scrollbar.write() = Some((Axis::X, coordinates.x)); }; // Unmark any scrollbar let onclick = |_: MouseEvent| { - *clicking_scrollbar.write_silent() = None; + if clicking_scrollbar.read().is_some() { + *clicking_scrollbar.write() = None; + } }; let horizontal_scrollbar_size = if horizontal_scrollbar_is_visible { @@ -305,6 +307,17 @@ pub fn VirtualScrollView<'a, T>(cx: Scope<'a, VirtualScrollViewProps<'a, T>>) -> let children = render_range.map(|i| (cx.props.builder)((i + 1, i, cx, &cx.props.builder_values))); + let is_scrolling_x = clicking_scrollbar + .read() + .as_ref() + .map(|f| f.0 == Axis::X) + .unwrap_or_default(); + let is_scrolling_y = clicking_scrollbar + .read() + .as_ref() + .map(|f| f.0 == Axis::Y) + .unwrap_or_default(); + render!( rect { role: "scrollView", @@ -335,6 +348,7 @@ pub fn VirtualScrollView<'a, T>(cx: Scope<'a, VirtualScrollViewProps<'a, T>>) -> height: "{horizontal_scrollbar_size}", offset_x: "{scrollbar_x}", ScrollThumb { + clicking_scrollbar: is_scrolling_x, onmousedown: onmousedown_x, width: "{scrollbar_width}", height: "100%", @@ -346,6 +360,7 @@ pub fn VirtualScrollView<'a, T>(cx: Scope<'a, VirtualScrollViewProps<'a, T>>) -> height: "100%", offset_y: "{scrollbar_y}", ScrollThumb { + clicking_scrollbar: is_scrolling_y, onmousedown: onmousedown_y, width: "100%", height: "{scrollbar_height}", diff --git a/crates/hooks/src/use_theme.rs b/crates/hooks/src/use_theme.rs index 63c60d479..ec33780b6 100644 --- a/crates/hooks/src/use_theme.rs +++ b/crates/hooks/src/use_theme.rs @@ -71,6 +71,8 @@ pub struct SwitchTheme { pub struct ScrollbarTheme { pub background: &'static str, pub thumb_background: &'static str, + pub hover_thumb_background: &'static str, + pub active_thumb_background: &'static str, } /// Theming properties for the App body. @@ -175,6 +177,8 @@ pub const LIGHT_THEME: Theme = Theme { scrollbar: ScrollbarTheme { background: "rgb(225, 225, 225)", thumb_background: "rgb(135, 135, 135)", + hover_thumb_background: "rgb(115, 115, 115)", + active_thumb_background: "rgb(95, 95, 95)", }, tooltip: TooltipTheme { background: "rgb(230,230,230)", @@ -240,6 +244,8 @@ pub const DARK_THEME: Theme = Theme { scrollbar: ScrollbarTheme { background: "rgb(35, 35, 35)", thumb_background: "rgb(100, 100, 100)", + hover_thumb_background: "rgb(120, 120, 120)", + active_thumb_background: "rgb(140, 140, 140)", }, tooltip: TooltipTheme { background: "rgb(35,35,35)", From d9bbf926a9574fa0733ed05943c6e44306b84cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Mon, 16 Oct 2023 23:39:16 +0200 Subject: [PATCH 4/6] feat: Table component (#298) * feat: Table component * improvements * sorting * wip * improvements * simplify example * improvements * fixes * tweak * feat: Theming support --- Cargo.toml | 3 +- crates/components/src/lib.rs | 2 + crates/components/src/table.rs | 209 ++++++++++++++++++++++++++++++++ crates/elements/src/elements.rs | 1 + crates/hooks/src/use_theme.rs | 28 +++++ examples/table.rs | 135 +++++++++++++++++++++ 6 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 crates/components/src/table.rs create mode 100644 examples/table.rs diff --git a/Cargo.toml b/Cargo.toml index 0efd64d02..947d13695 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ tracing-subscriber = "0.2.25" dioxus-std = { version = "0.4", features = ["utils", "i18n"] } rand = "0.8.5" dioxus-router = { workspace = true } +itertools = "0.11.0" [profile.release] lto = true @@ -79,4 +80,4 @@ opt-level = 3 [target."cfg(target_os = \"linux\")".dependencies.skia-safe] workspace = true -features = ["gl", "textlayout", "svg", "x11", "wayland"] \ No newline at end of file +features = ["gl", "textlayout", "svg", "x11", "wayland"] diff --git a/crates/components/src/lib.rs b/crates/components/src/lib.rs index ae3ad2aab..d954bf36c 100644 --- a/crates/components/src/lib.rs +++ b/crates/components/src/lib.rs @@ -17,6 +17,7 @@ mod progress_bar; mod scroll_views; mod slider; mod switch; +mod table; mod theme; mod tooltip; @@ -36,5 +37,6 @@ pub use progress_bar::*; pub use scroll_views::*; pub use slider::*; pub use switch::*; +pub use table::*; pub use theme::*; pub use tooltip::*; diff --git a/crates/components/src/table.rs b/crates/components/src/table.rs new file mode 100644 index 000000000..dee8fea24 --- /dev/null +++ b/crates/components/src/table.rs @@ -0,0 +1,209 @@ +use dioxus::prelude::*; +use freya_elements::elements as dioxus_elements; +use freya_elements::events::MouseEvent; +use freya_hooks::{use_get_theme, TableTheme}; + +#[allow(non_snake_case)] +#[inline_props] +fn TableArrow(cx: Scope, order_direction: OrderDirection) -> Element { + let theme = use_get_theme(cx); + let TableTheme { arrow_fill, .. } = theme.table; + let rotate = match order_direction { + OrderDirection::Down => "0", + OrderDirection::Up => "180", + }; + + render!(svg { + height: "10", + width: "10", + rotate: "{rotate}deg", + svg_content: r#" + + + + "# + }) +} + +/// [`TableHead`] component properties. +#[derive(Props)] +pub struct TableHeadProps<'a> { + /// The content of this table head. + children: Element<'a>, +} + +#[allow(non_snake_case)] +pub fn TableHead<'a>(cx: Scope<'a, TableHeadProps<'a>>) -> Element { + render!( + rect { + width: "100%", + &cx.props.children + } + ) +} + +/// [`TableBody`] component properties. +#[derive(Props)] +pub struct TableBodyProps<'a> { + /// The content of this table body. + children: Element<'a>, +} + +#[allow(non_snake_case)] +pub fn TableBody<'a>(cx: Scope<'a, TableBodyProps<'a>>) -> Element { + render!( + rect { + width: "100%", + &cx.props.children + } + ) +} + +/// [`TableRow`] component properties. +#[derive(Props)] +pub struct TableRowProps<'a> { + /// The content of this row. + children: Element<'a>, + /// Show the row with a different background, this allows to have a zebra-style table. + #[props(default = false)] + alternate_colors: bool, +} + +#[allow(non_snake_case)] +pub fn TableRow<'a>(cx: Scope<'a, TableRowProps<'a>>) -> Element { + let theme = use_get_theme(cx); + let TableTheme { + divider_fill, + alternate_row_background, + row_background, + .. + } = theme.table; + let background = if cx.props.alternate_colors { + alternate_row_background + } else { + row_background + }; + render!( + rect { + direction: "horizontal", + width: "100%", + min_height: "35", + background: "{background}", + &cx.props.children + } + rect { + height: "1", + width: "100%", + background: "{divider_fill}" + } + ) +} + +#[derive(Clone, Copy, PartialEq, Debug, Default)] +pub enum OrderDirection { + Up, + #[default] + Down, +} + +/// [`TableCell`] component properties. +#[derive(Props)] +pub struct TableCellProps<'a> { + /// The content of this cell. + children: Element<'a>, + /// Onclick event handler for the TableCell. + onclick: Option>, + /// The direction in which this TableCell's column will be ordered. + #[props(into)] + order_direction: Option>, + /// Show a line divider to the left of this TableCell. + #[props(default = true, into)] + divider: bool, +} + +#[allow(non_snake_case)] +pub fn TableCell<'a>(cx: Scope<'a, TableCellProps<'a>>) -> Element { + let theme = use_get_theme(cx); + let TableTheme { divider_fill, .. } = theme.table; + let config = cx.consume_context::().unwrap(); + let width = 100.0 / config.columns as f32; + + render!( + if cx.props.divider { + rsx!( + rect { + width: "1", + height: "35", + background: "{divider_fill}" + } + ) + } + rect { + width: "0", + height: "0", + padding: "10", + if let Some(Some(order_direction)) = &cx.props.order_direction { + rsx!( + TableArrow { + order_direction: *order_direction + } + ) + } + } + rect { + overflow: "clip", + padding: "5 25", + width: "{width}%", + display: "center", + height: "35", + align: "right", + onclick: |e| { + if let Some(onclick) = &cx.props.onclick { + onclick.call(e); + } + }, + &cx.props.children + } + ) +} + +/// [`Table`] component properties. +#[derive(Props)] +pub struct TableProps<'a> { + /// Number of columns used in the table. + columns: usize, + /// The content of the table. + children: Element<'a>, + /// The height of the table. + #[props(default = "auto".to_string(), into)] + height: String, +} + +#[allow(non_snake_case)] +pub fn Table<'a>(cx: Scope<'a, TableProps<'a>>) -> Element { + let theme = use_get_theme(cx); + let TableTheme { + background, color, .. + } = theme.table; + cx.provide_context(TableConfig { + columns: cx.props.columns, + }); + let height = &cx.props.height; + + render!( + rect { + overflow: "clip", + color: "{color}", + background: "{background}", + corner_radius: "6", + shadow: "0 2 15 5 rgb(35, 35, 35, 70)", + height: "{height}", + &cx.props.children + } + ) +} + +#[derive(Clone)] +pub struct TableConfig { + columns: usize, +} diff --git a/crates/elements/src/elements.rs b/crates/elements/src/elements.rs index b0f2d4488..b34f1a94c 100644 --- a/crates/elements/src/elements.rs +++ b/crates/elements/src/elements.rs @@ -177,6 +177,7 @@ builder_constructors! { font_style: String, font_weight: String, font_width: String, + align: String, display: String, reference: Reference, cursor_reference: CursorReference, diff --git a/crates/hooks/src/use_theme.rs b/crates/hooks/src/use_theme.rs index ec33780b6..310a7a0e3 100644 --- a/crates/hooks/src/use_theme.rs +++ b/crates/hooks/src/use_theme.rs @@ -125,6 +125,17 @@ pub struct ProgressBarTheme { pub progress_background: &'static str, } +/// Theming properties for Table component. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TableTheme { + pub color: &'static str, + pub background: &'static str, + pub arrow_fill: &'static str, + pub alternate_row_background: &'static str, + pub row_background: &'static str, + pub divider_fill: &'static str, +} + /// Theming properties for Themes. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Theme { @@ -141,6 +152,7 @@ pub struct Theme { pub accordion: AccordionTheme, pub loader: LoaderTheme, pub progress_bar: ProgressBarTheme, + pub table: TableTheme, } impl Default for Theme { @@ -216,6 +228,14 @@ pub const LIGHT_THEME: Theme = Theme { background: "rgb(210, 210, 210)", progress_background: "rgb(103, 80, 164)", }, + table: TableTheme { + color: "black", + background: "white", + arrow_fill: "rgb(40, 40, 40)", + row_background: "transparent", + alternate_row_background: "rgb(240, 240, 240)", + divider_fill: "rgb(200, 200, 200)", + }, }; /// `Dark` theme @@ -279,4 +299,12 @@ pub const DARK_THEME: Theme = Theme { background: "rgb(60, 60, 60)", progress_background: "rgb(255, 95, 0)", }, + table: TableTheme { + color: "white", + background: "rgb(25, 25, 25)", + arrow_fill: "rgb(150, 150, 150)", + row_background: "transparent", + alternate_row_background: "rgb(50, 50, 50)", + divider_fill: "rgb(100, 100, 100)", + }, }; diff --git a/examples/table.rs b/examples/table.rs new file mode 100644 index 000000000..40acff1f4 --- /dev/null +++ b/examples/table.rs @@ -0,0 +1,135 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +use std::fmt::Display; + +use freya::prelude::*; +use itertools::{Either, Itertools}; + +fn main() { + launch(app); +} + +#[derive(PartialEq, Clone)] +enum OrderBy { + Name, + OtherName, + MoreData, +} + +impl Display for OrderBy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OrderBy::Name => f.write_str("Name"), + OrderBy::OtherName => f.write_str("Other Name"), + OrderBy::MoreData => f.write_str("More Data"), + } + } +} + +fn app(cx: Scope) -> Element { + let order_direction = use_state(cx, || OrderDirection::Down); + let order = use_state(cx, || OrderBy::Name); + let data = use_state(cx, || { + vec![ + vec!["aaaa".to_string(), "bbbb".to_string(), "111".to_string()], + vec!["bbbb".to_string(), "aaaa".to_string(), "333".to_string()], + vec!["wwww".to_string(), "777".to_string(), "ccc".to_string()], + vec!["dddd".to_string(), "222".to_string(), "111".to_string()], + vec!["hhhh".to_string(), "444".to_string(), "aaa".to_string()], + vec!["555".to_string(), "ffff".to_string(), "zzzz".to_string()], + vec!["llll".to_string(), "999".to_string(), "eeee".to_string()], + vec!["abcd".to_string(), "987".to_string(), "wwww".to_string()], + vec!["rrrr".to_string(), "333".to_string(), "888".to_string()], + ] + }); + let columns = cx.use_hook(|| { + vec![ + ("Name", OrderBy::Name), + ("OtherName", OrderBy::OtherName), + ("MoreData", OrderBy::MoreData), + ] + }); + + let filtered_data = { + let filtered_data = data.iter().sorted_by(|a, b| match *order.get() { + OrderBy::Name => Ord::cmp(&a[0].to_lowercase(), &b[0].to_lowercase()), + OrderBy::OtherName => Ord::cmp(&a[1].to_lowercase(), &b[1].to_lowercase()), + OrderBy::MoreData => Ord::cmp(&a[2].to_lowercase(), &b[2].to_lowercase()), + }); + + if *order_direction.get() == OrderDirection::Down { + Either::Left(filtered_data) + } else { + Either::Right(filtered_data.rev()) + } + }; + + let on_column_head_click = |column_order: &OrderBy| { + // Change order diection + if order.get() == column_order { + if *order_direction.get() == OrderDirection::Up { + order_direction.set(OrderDirection::Down) + } else { + order_direction.set(OrderDirection::Up) + } + // Change order column + } else { + order.set(column_order.clone()); + order_direction.set(OrderDirection::default()) + } + }; + + render!( + rect { + padding: "10", + label { + height: "25", + "Ordering by {order}" + } + Table { + columns: 3, + TableHead { + TableRow { + for (n, (text, order_by)) in columns.iter().enumerate() { + TableCell { + key: "{n}", + divider: n > 0, + order_direction: if *order.get() == *order_by { Some(*order_direction.get()) } else { None }, + onclick: move |_| on_column_head_click(order_by), + label { + width: "100%", + align: "center", + "{text}" + } + } + } + } + } + TableBody { + ScrollView { + for (i, items) in filtered_data.enumerate() { + TableRow { + key: "{i}", + alternate_colors: i % 2 == 0, + for (n, item) in items.iter().enumerate() { + TableCell { + key: "{n}", + divider: n > 0, + label { + width: "100%", + align: "right", + "{item}" + } + } + } + } + } + } + } + } + } + ) +} From 6dda57e8b8c71f62518202c085086f3c9daf9d43 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sat, 21 Oct 2023 16:23:00 +0200 Subject: [PATCH 5/6] fix: Use white for light theme in progress bars --- crates/hooks/src/use_theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hooks/src/use_theme.rs b/crates/hooks/src/use_theme.rs index 310a7a0e3..d5ad94f77 100644 --- a/crates/hooks/src/use_theme.rs +++ b/crates/hooks/src/use_theme.rs @@ -224,7 +224,7 @@ pub const LIGHT_THEME: Theme = Theme { secondary_color: "rgb(150, 150, 150)", }, progress_bar: ProgressBarTheme { - color: "black", + color: "white", background: "rgb(210, 210, 210)", progress_background: "rgb(103, 80, 164)", }, From 4ac85ef421f37fa36a525fe6691b517486a28788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sun, 22 Oct 2023 12:30:41 +0200 Subject: [PATCH 6/6] feat: New torin benchmark (#318) * feat: Improved measuring of cached layout * clean up * fixes * fix * feat: New torin benchmark, deep + wide + cached * Update bench.rs * tweak * 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 * iter_batched * workflow_dispatch * clean up * remove fxhash --- .github/workflows/benchmarks.yml | 1 + .github/workflows/rust.yml | 1 - Cargo.toml | 1 - crates/freya/Cargo.toml | 1 - crates/state/Cargo.toml | 1 - crates/torin/Cargo.toml | 2 +- crates/torin/benches/bench.rs | 598 +++++++++++++++++++------------ crates/torin/src/torin.rs | 24 +- crates/torin/tests/test.rs | 2 +- 9 files changed, 377 insertions(+), 254 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index c086af680..8ca2da80f 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -1,4 +1,5 @@ on: + workflow_dispatch: pull_request: paths: - 'crates/torin/**/*' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1db1c05b0..bdf29eab6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,7 +5,6 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] env: CARGO_TERM_COLOR: always diff --git a/Cargo.toml b/Cargo.toml index 947d13695..5e5abe267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ euclid = "0.22.7" uuid = { version = "1.2.2", features = ["v4"]} futures = "0.3.25" anymap = "0.12.1" -fxhash = "0.2.1" tracing = "0.1" tracing-subscriber = "0.3.17" rustc-hash = "1.1.0" diff --git a/crates/freya/Cargo.toml b/crates/freya/Cargo.toml index e59fbb010..11187017e 100644 --- a/crates/freya/Cargo.toml +++ b/crates/freya/Cargo.toml @@ -49,5 +49,4 @@ tracing = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } anymap = { workspace = true } -fxhash = { workspace = true } diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index bf539410a..d8a0fe6d2 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -35,7 +35,6 @@ accesskit = { workspace = true } shipyard = "0.6.2" anymap = { workspace = true } -fxhash = { workspace = true } uuid = { workspace = true } bytes = "1.3.0" smallvec = "1.10.0" diff --git a/crates/torin/Cargo.toml b/crates/torin/Cargo.toml index 5dcb62445..4b56ad7fa 100644 --- a/crates/torin/Cargo.toml +++ b/crates/torin/Cargo.toml @@ -18,7 +18,7 @@ default = ["dioxus"] [dependencies] tracing = { workspace = true } euclid = { workspace = true } -fxhash = { workspace = true } +rustc-hash = { workspace = true } dioxus-native-core = { workspace = true, optional = true } dioxus-core = { workspace = true, optional = true } diff --git a/crates/torin/benches/bench.rs b/crates/torin/benches/bench.rs index 55c3efcbc..96216772f 100644 --- a/crates/torin/benches/bench.rs +++ b/crates/torin/benches/bench.rs @@ -29,6 +29,17 @@ impl TestingDOM { self.mapper.insert(node_id, (parent, children, depth, node)); } + fn add_with_depth( + &mut self, + node_id: usize, + parent: Option, + children: Vec, + node: Node, + depth: u16, + ) { + self.mapper.insert(node_id, (parent, children, depth, node)); + } + fn set_node(&mut self, node_id: usize, node: Node) { self.mapper.get_mut(&node_id).unwrap().3 = node; } @@ -59,13 +70,13 @@ 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)?) + self.parent_of(node_id_a) } } fn criterion_benchmark(c: &mut Criterion) { let mut g = c.benchmark_group("benchmarks"); - g.significance_level(0.1).sample_size(500); + g.significance_level(0.05).sample_size(500); let params = [ ("big trees (wide) nodes=1000, depth=1", 1000, 1), @@ -80,315 +91,424 @@ fn criterion_benchmark(c: &mut Criterion) { let size_per_layer = size / depth; g.bench_function(name, |b| { - let mut measurer = Some(TestingMeasurer); - let mut mocked_dom = TestingDOM::default(); - - let children_ids = (1..=size_per_layer).into_iter().collect::>(); - - let mut root = 0; - - mocked_dom.add( - 0, - None, - children_ids.clone(), - Node::from_size_and_direction( - Size::Percentage(Length::new(100.0)), - Size::Percentage(Length::new(100.0)), - DirectionMode::Vertical, - ), - ); - - for level in 0..depth { - for i in &children_ids { - let id = (level * size) + *i; - let children = if level == depth - 1 { - vec![] - } else if *i == size_per_layer - 1 { - (1..101) - .map(move |i| i + ((level + 1) * size)) - .collect::>() - } else { - vec![] - }; + b.iter_batched( + || { + let measurer = Some(TestingMeasurer); + let mut mocked_dom = TestingDOM::default(); + + let children_ids = (1..=size_per_layer).into_iter().collect::>(); + + let mut root = 0; mocked_dom.add( - id, - Some(root), - children, + 0, + None, + children_ids.clone(), Node::from_size_and_direction( - Size::Pixels(Length::new(100.0)), - Size::Pixels(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), DirectionMode::Vertical, ), ); - if *i == size_per_layer - 1 { - root = id + for level in 0..depth { + for i in &children_ids { + let id = (level * size) + *i; + let children = if level == depth - 1 { + vec![] + } else if *i == size_per_layer - 1 { + (1..101) + .map(move |i| i + ((level + 1) * size)) + .collect::>() + } else { + vec![] + }; + + mocked_dom.add( + id, + Some(root), + children, + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(100.0)), + DirectionMode::Vertical, + ), + ); + + if *i == size_per_layer - 1 { + root = id + } + } } - } - } - - b.iter(|| { - black_box({ - let mut layout = Torin::::new(); - layout.find_best_root(&mut mocked_dom); - layout.measure( - 0, - Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), - &mut measurer, - &mut mocked_dom, - ) - }); - }) + (mocked_dom, measurer) + }, + |(mut mocked_dom, mut measurer)| { + black_box({ + let mut layout = Torin::::new(); + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ) + }); + }, + criterion::BatchSize::SmallInput, + ) }); } g.bench_function( "big trees (deep + cached) + invalidated node in the top", |b| { - let mut layout = Torin::::new(); - let mut measurer = Some(TestingMeasurer); - let mut mocked_dom = TestingDOM::default(); - - let children_ids = (1..=101).into_iter().collect::>(); - - let mut root = 0; - - mocked_dom.add( - 0, - None, - children_ids.clone(), - Node::from_size_and_direction( - Size::Percentage(Length::new(100.0)), - Size::Percentage(Length::new(100.0)), - DirectionMode::Vertical, - ), - ); - - let levels = 20; - - for level in 0..levels { - for i in &children_ids { - let id = (level * 1000) + *i; - let children = if *i == 101 && level < levels - 1 { - (1..101) - .map(move |i| i + ((level + 1) * 1000)) - .collect::>() - } else { - vec![] - }; + b.iter_batched( + || { + let mut layout = Torin::::new(); + let mut measurer = Some(TestingMeasurer); + let mut mocked_dom = TestingDOM::default(); + + let children_ids = (1..=101).into_iter().collect::>(); + + let mut root = 0; mocked_dom.add( - id, - Some(root), - children, + 0, + None, + children_ids.clone(), Node::from_size_and_direction( - Size::Pixels(Length::new(100.0)), - Size::Pixels(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), DirectionMode::Vertical, ), ); - if *i == 101 { - root = id + let levels = 20; + + for level in 0..levels { + for i in &children_ids { + let id = (level * 1000) + *i; + let children = if *i == 101 && level < levels - 1 { + (1..101) + .map(move |i| i + ((level + 1) * 1000)) + .collect::>() + } else { + vec![] + }; + + mocked_dom.add( + id, + Some(root), + children, + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(100.0)), + DirectionMode::Vertical, + ), + ); + + if *i == 101 { + root = id + } + } } - } - } - - layout.find_best_root(&mut mocked_dom); - layout.measure( - 0, - Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), - &mut measurer, - &mut mocked_dom, - ); - - b.iter(|| { - black_box({ - mocked_dom.set_node( - 1, - Node::from_size_and_direction( - Size::Inner, - Size::Pixels(Length::new(10.0)), - DirectionMode::Vertical, - ), - ); - layout.invalidate(1); + layout.find_best_root(&mut mocked_dom); layout.measure( 0, Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), &mut measurer, &mut mocked_dom, - ) - }); - }) + ); + (mocked_dom, measurer, layout) + }, + |(mut mocked_dom, mut measurer, mut layout)| { + black_box({ + mocked_dom.set_node( + 1, + Node::from_size_and_direction( + Size::Inner, + Size::Pixels(Length::new(10.0)), + DirectionMode::Vertical, + ), + ); + layout.invalidate(1); + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ) + }); + }, + criterion::BatchSize::SmallInput, + ) }, ); g.bench_function( "big trees (deep + cached) + invalidated node in the bottom", |b| { - let mut layout = Torin::::new(); - let mut measurer = Some(TestingMeasurer); - let mut mocked_dom = TestingDOM::default(); - - let children_ids = (1..=101).into_iter().collect::>(); - - let mut root = 0; - - mocked_dom.add( - 0, - None, - children_ids.clone(), - Node::from_size_and_direction( - Size::Percentage(Length::new(100.0)), - Size::Percentage(Length::new(100.0)), - DirectionMode::Vertical, - ), - ); - - let levels = 20; - - for level in 0..levels { - for i in &children_ids { - let id = (level * 1000) + *i; - let children = if *i == 101 && level < levels - 1 { - (1..101) - .map(move |i| i + ((level + 1) * 1000)) - .collect::>() - } else { - vec![] - }; + b.iter_batched( + || { + let mut layout = Torin::::new(); + let mut measurer = Some(TestingMeasurer); + let mut mocked_dom = TestingDOM::default(); + + let children_ids = (1..=101).into_iter().collect::>(); + + let mut root = 0; mocked_dom.add( - id, - Some(root), - children, + 0, + None, + children_ids.clone(), Node::from_size_and_direction( - Size::Pixels(Length::new(100.0)), - Size::Pixels(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), DirectionMode::Vertical, ), ); - if *i == 101 { - root = id + let levels = 20; + + for level in 0..levels { + for i in &children_ids { + let id = (level * 1000) + *i; + let children = if *i == 101 && level < levels - 1 { + (1..101) + .map(move |i| i + ((level + 1) * 1000)) + .collect::>() + } else { + vec![] + }; + + mocked_dom.add( + id, + Some(root), + children, + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(100.0)), + DirectionMode::Vertical, + ), + ); + + if *i == 101 { + root = id + } + } } - } - } - - layout.find_best_root(&mut mocked_dom); - layout.measure( - 0, - Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), - &mut measurer, - &mut mocked_dom, - ); - - b.iter(|| { - black_box({ - mocked_dom.set_node( - 1, - Node::from_size_and_direction( - Size::Inner, - Size::Pixels(Length::new(10.0)), - DirectionMode::Vertical, - ), - ); - layout.invalidate(2001); + layout.find_best_root(&mut mocked_dom); layout.measure( 0, Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), &mut measurer, &mut mocked_dom, - ) - }); - }) + ); + (mocked_dom, measurer, layout) + }, + |(mut mocked_dom, mut measurer, mut layout)| { + black_box({ + mocked_dom.set_node( + 1, + Node::from_size_and_direction( + Size::Inner, + Size::Pixels(Length::new(10.0)), + DirectionMode::Vertical, + ), + ); + layout.invalidate(2001); + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ) + }); + }, + criterion::BatchSize::SmallInput, + ) }, ); g.bench_function( "big trees (deep + cached) + invalidated node in the middle", |b| { - let mut layout = Torin::::new(); - let mut measurer = Some(TestingMeasurer); - let mut mocked_dom = TestingDOM::default(); - - let children_ids = (1..=101).into_iter().collect::>(); - - let mut root = 0; - - mocked_dom.add( - 0, - None, - children_ids.clone(), - Node::from_size_and_direction( - Size::Percentage(Length::new(100.0)), - Size::Percentage(Length::new(100.0)), - DirectionMode::Vertical, - ), - ); - - let levels = 20; - - for level in 0..levels { - for i in &children_ids { - let id = (level * 1000) + *i; - let children = if *i == 101 && level < levels - 1 { - (1..101) - .map(move |i| i + ((level + 1) * 1000)) - .collect::>() - } else { - vec![] - }; + b.iter_batched( + || { + let mut layout = Torin::::new(); + let mut measurer = Some(TestingMeasurer); + let mut mocked_dom = TestingDOM::default(); + + let children_ids = (1..=101).into_iter().collect::>(); + + let mut root = 0; mocked_dom.add( - id, - Some(root), - children, + 0, + None, + children_ids.clone(), Node::from_size_and_direction( - Size::Pixels(Length::new(100.0)), - Size::Pixels(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), DirectionMode::Vertical, ), ); - if *i == 101 { - root = id + let levels = 20; + + for level in 0..levels { + for i in &children_ids { + let id = (level * 1000) + *i; + let children = if *i == 101 && level < levels - 1 { + (1..101) + .map(move |i| i + ((level + 1) * 1000)) + .collect::>() + } else { + vec![] + }; + + mocked_dom.add( + id, + Some(root), + children, + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(100.0)), + DirectionMode::Vertical, + ), + ); + + if *i == 101 { + root = id + } + } } - } - } - - layout.find_best_root(&mut mocked_dom); - layout.measure( - 0, - Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), - &mut measurer, - &mut mocked_dom, - ); - - b.iter(|| { - black_box({ - mocked_dom.set_node( - 1, + + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ); + (mocked_dom, measurer, layout) + }, + |(mut mocked_dom, mut measurer, mut layout)| { + black_box({ + mocked_dom.set_node( + 1001, + Node::from_size_and_direction( + Size::Inner, + Size::Pixels(Length::new(10.0)), + DirectionMode::Vertical, + ), + ); + layout.invalidate(1001); + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ) + }); + }, + criterion::BatchSize::SmallInput, + ) + }, + ); + + g.bench_function( + "big trees (deep + branches + cached) + invalidated node in the middle", + |b| { + b.iter_batched( + || { + let mut layout = Torin::::new(); + let mut measurer = Some(TestingMeasurer); + let mut mocked_dom = TestingDOM::default(); + + mocked_dom.add( + 0, + None, + vec![101, 102], Node::from_size_and_direction( - Size::Inner, - Size::Pixels(Length::new(10.0)), + Size::Percentage(Length::new(100.0)), + Size::Percentage(Length::new(100.0)), DirectionMode::Vertical, ), ); - layout.invalidate(1001); + + const LEVELS: usize = 9; + const WIDE: usize = 2; + + fn build_branch( + mocked_dom: &mut TestingDOM, + root: usize, + level: usize, + ) -> Vec { + if level == LEVELS { + return vec![]; + } + + let nodes = (0..=WIDE) + .map(|i| i + ((level + 1) * 100) + (root * 10)) + .into_iter() + .collect::>(); + for id in nodes.iter() { + let children = build_branch(mocked_dom, *id, level + 1); + mocked_dom.add_with_depth( + *id, + Some(root), + children, + Node::from_size_and_direction( + Size::Pixels(Length::new(100.0)), + Size::Pixels(Length::new(100.0)), + DirectionMode::Vertical, + ), + level as u16, + ); + } + nodes + } + + build_branch(&mut mocked_dom, 0, 0); + layout.find_best_root(&mut mocked_dom); layout.measure( 0, Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), &mut measurer, &mut mocked_dom, - ) - }); - }) + ); + (mocked_dom, measurer, layout) + }, + |(mut mocked_dom, mut measurer, mut layout)| { + black_box({ + mocked_dom.set_node( + 1202, + Node::from_size_and_direction( + Size::Inner, + Size::Pixels(Length::new(10.0)), + DirectionMode::Vertical, + ), + ); + layout.invalidate(12456790001); + layout.find_best_root(&mut mocked_dom); + layout.measure( + 0, + Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1000.0, 1000.0)), + &mut measurer, + &mut mocked_dom, + ) + }); + }, + criterion::BatchSize::SmallInput, + ) }, ); } diff --git a/crates/torin/src/torin.rs b/crates/torin/src/torin.rs index 8b3803580..0e73993dd 100644 --- a/crates/torin/src/torin.rs +++ b/crates/torin/src/torin.rs @@ -1,7 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, mem}; pub use euclid::Rect; -use fxhash::{FxHashMap, FxHashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; use tracing::info; use crate::{ @@ -16,7 +16,7 @@ use crate::{ }; /// Contains the best Root node candidate from where to start measuring -#[derive(PartialEq, Debug, Clone, Copy)] +#[derive(PartialEq, Debug, Clone)] pub enum RootNodeCandidate { /// A valid Node ID Valid(Key), @@ -25,6 +25,12 @@ pub enum RootNodeCandidate { None, } +impl RootNodeCandidate { + pub fn take(&mut self) -> Self { + mem::replace(self, Self::None) + } +} + pub struct Torin { /// Layout results of the registered Nodes pub results: FxHashMap, @@ -175,12 +181,12 @@ impl Torin { if RootNodeCandidate::None == self.root_node_candidate { self.root_node_candidate = RootNodeCandidate::Valid(node_id); - } else if let RootNodeCandidate::Valid(root_candidate) = self.root_node_candidate { - if node_id != root_candidate { - let closest_parent = dom_adapter.closest_common_parent(&node_id, &root_candidate); + } else if let RootNodeCandidate::Valid(root_candidate) = &mut self.root_node_candidate { + if node_id != *root_candidate { + let closest_parent = dom_adapter.closest_common_parent(&node_id, root_candidate); if let Some(closest_parent) = closest_parent { - self.root_node_candidate = RootNodeCandidate::Valid(closest_parent); + *root_candidate = closest_parent; } } } @@ -234,7 +240,7 @@ impl Torin { /// Get the Root Node candidate pub fn get_root_candidate(&self) -> RootNodeCandidate { - self.root_node_candidate + self.root_node_candidate.clone() } /// Find the best root Node from where to start measuring @@ -263,7 +269,7 @@ impl Torin { } // Try the Root candidate otherwise use the provided Root - let root_id = if let RootNodeCandidate::Valid(id) = self.root_node_candidate { + let root_id = if let RootNodeCandidate::Valid(id) = self.root_node_candidate.take() { id } else { suggested_root_id diff --git a/crates/torin/tests/test.rs b/crates/torin/tests/test.rs index a0efbf200..e366fbdf8 100644 --- a/crates/torin/tests/test.rs +++ b/crates/torin/tests/test.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use fxhash::FxHashSet; +use rustc_hash::FxHashSet; use torin::prelude::*; struct TestingMeasurer;