From 571b96f5f01c4011d6939768673b8d3f5a455a38 Mon Sep 17 00:00:00 2001 From: Jared Moulton Date: Thu, 24 Aug 2023 02:50:29 -0600 Subject: [PATCH 1/5] Add round scroll bar option --- src/views/scroll.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 855f2105..943af234 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -24,6 +24,7 @@ enum ScrollState { ScrollDelta(Vec2), ScrollTo(Point), ScrollBarColor(Color), + RoundedBar(bool), HiddenBar(bool), PropagatePointerWheel(bool), VerticalScrollAsHorizontal(bool), @@ -62,10 +63,12 @@ pub struct Scroll { propagate_pointer_wheel: bool, vertical_scroll_as_horizontal: bool, scroll_bar_color: Color, + rounded_bar: bool, } pub fn scroll(child: impl FnOnce() -> V) -> Scroll { let (id, child) = ViewContext::new_id_with_child(child); + let rounded_bar = cfg!(target_os = "macos"); Scroll { id, child, @@ -81,6 +84,7 @@ pub fn scroll(child: impl FnOnce() -> V) -> Scroll { vertical_scroll_as_horizontal: false, // 179 is 70% of 255 so a 70% alpha factor is the default scroll_bar_color: Color::rgba8(0, 0, 0, 179), + rounded_bar, } } @@ -95,6 +99,16 @@ impl Scroll { self } + pub fn scroll_bar_round(self, round: impl Fn() -> bool + 'static) -> Self { + let id = self.id; + create_effect(move |_| { + let round = round(); + id.update_state(ScrollState::RoundedBar(round), false); + }); + + self + } + pub fn on_scroll(mut self, onscroll: impl Fn(Rect) + 'static) -> Self { self.onscroll = Some(Box::new(onscroll)); self @@ -311,11 +325,22 @@ impl Scroll { fn draw_bars(&self, cx: &mut PaintCx) { let edge_width = 0.0; let scroll_offset = self.child_viewport.origin().to_vec2(); + let radius = |rect: Rect, vertical| { + if self.rounded_bar { + if vertical { + (rect.x1 - rect.x0) / 2. + } else { + (rect.y1 - rect.y0) / 2. + } + } else { + 0. + } + }; let color = self.scroll_bar_color; if let Some(bounds) = self.calc_vertical_bar_bounds(cx.app_state) { let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); - cx.fill(&rect, color, 0.0); + cx.fill(&rect.to_rounded_rect(radius(rect, true)), color, 0.0); if edge_width > 0.0 { cx.stroke(&rect, color, edge_width); } @@ -326,7 +351,11 @@ impl Scroll { let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); cx.fill(&rect, color, 0.0); if edge_width > 0.0 { - cx.stroke(&rect, color, edge_width); + cx.stroke( + &rect.to_rounded_rect(radius(rect, false)), + color, + edge_width, + ); } } } @@ -528,6 +557,9 @@ impl View for Scroll { ScrollState::ScrollBarColor(color) => { self.scroll_bar_color = color; } + ScrollState::RoundedBar(round) => { + self.rounded_bar = round; + } ScrollState::HiddenBar(value) => { self.hide_bar = value; } From 26c44fd300beb3bee61be09eb9d06aaa2a541269 Mon Sep 17 00:00:00 2001 From: Jared Moulton Date: Thu, 24 Aug 2023 15:42:36 -0600 Subject: [PATCH 2/5] Add ScrollBarStyle struct --- src/views/scroll.rs | 171 +++++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 80 deletions(-) diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 943af234..e08405d6 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -23,13 +23,14 @@ enum ScrollState { EnsureVisible(Rect), ScrollDelta(Vec2), ScrollTo(Point), - ScrollBarColor(Color), - RoundedBar(bool), - HiddenBar(bool), PropagatePointerWheel(bool), VerticalScrollAsHorizontal(bool), } +enum UpdateState { + Scroll(ScrollState), + Style(ScrollBarStyle), +} /// Minimum length for any scrollbar to be when measured on that /// scrollbar's primary axis. const SCROLLBAR_MIN_SIZE: f64 = 10.0; @@ -47,6 +48,45 @@ enum BarHeldState { Horizontal(f64, Vec2), } +pub struct ScrollBarStyle { + color: Color, + rounded: bool, + hide: bool, + thickness: f32, + edge_width: f32, +} +impl ScrollBarStyle { + pub const BASE: Self = ScrollBarStyle { + // 179 is 70% of 255 so a 70% alpha factor is the default + color: Color::rgba8(0, 0, 0, 179), + rounded: cfg!(target_os = "macos"), + hide: false, + thickness: 10., + edge_width: 0., + }; + + pub fn color(mut self, color: Color) -> Self { + self.color = color; + self + } + pub fn rounded(mut self, rounded: bool) -> Self { + self.rounded = rounded; + self + } + pub fn hide(mut self, hide: bool) -> Self { + self.hide = hide; + self + } + pub fn thickness(mut self, thickness: f32) -> Self { + self.thickness = thickness; + self + } + pub fn edge_width(mut self, edge_width: f32) -> Self { + self.edge_width = edge_width; + self + } +} + pub struct Scroll { id: Id, child: V, @@ -59,16 +99,13 @@ pub struct Scroll { onscroll: Option>, held: BarHeldState, virtual_node: Option, - hide_bar: bool, propagate_pointer_wheel: bool, vertical_scroll_as_horizontal: bool, - scroll_bar_color: Color, - rounded_bar: bool, + scroll_bar_style: ScrollBarStyle, } pub fn scroll(child: impl FnOnce() -> V) -> Scroll { let (id, child) = ViewContext::new_id_with_child(child); - let rounded_bar = cfg!(target_os = "macos"); Scroll { id, child, @@ -79,31 +116,18 @@ pub fn scroll(child: impl FnOnce() -> V) -> Scroll { onscroll: None, held: BarHeldState::None, virtual_node: None, - hide_bar: false, propagate_pointer_wheel: false, vertical_scroll_as_horizontal: false, - // 179 is 70% of 255 so a 70% alpha factor is the default - scroll_bar_color: Color::rgba8(0, 0, 0, 179), - rounded_bar, + scroll_bar_style: ScrollBarStyle::BASE, } } impl Scroll { - pub fn scroll_bar_color(self, color: impl Fn() -> Color + 'static) -> Self { - let id = self.id; - create_effect(move |_| { - let color = color(); - id.update_state(ScrollState::ScrollBarColor(color), false); - }); - - self - } - - pub fn scroll_bar_round(self, round: impl Fn() -> bool + 'static) -> Self { + pub fn scroll_bar_style(self, style: impl Fn() -> ScrollBarStyle + 'static) -> Self { let id = self.id; create_effect(move |_| { - let round = round(); - id.update_state(ScrollState::RoundedBar(round), false); + let style = style(); + id.update_state(UpdateState::Style(style), false); }); self @@ -128,7 +152,7 @@ impl Scroll { let id = self.id; create_effect(move |_| { let delta = delta(); - id.update_state(ScrollState::ScrollDelta(delta), false); + id.update_state(UpdateState::Scroll(ScrollState::ScrollDelta(delta)), false); }); self @@ -138,25 +162,20 @@ impl Scroll { let id = self.id; create_effect(move |_| { if let Some(origin) = origin() { - id.update_state(ScrollState::ScrollTo(origin), true); + id.update_state(UpdateState::Scroll(ScrollState::ScrollTo(origin)), true); } }); self } - pub fn hide_bar(self, value: impl Fn() -> bool + 'static) -> Self { - let id = self.id; - create_effect(move |_| { - id.update_state(ScrollState::HiddenBar(value()), false); - }); - self - } - pub fn propagate_pointer_wheel(self, value: impl Fn() -> bool + 'static) -> Self { let id = self.id; create_effect(move |_| { - id.update_state(ScrollState::PropagatePointerWheel(value()), false); + id.update_state( + UpdateState::Scroll(ScrollState::PropagatePointerWheel(value())), + false, + ); }); self } @@ -164,7 +183,10 @@ impl Scroll { pub fn vertical_scroll_as_horizontal(self, value: impl Fn() -> bool + 'static) -> Self { let id = self.id; create_effect(move |_| { - id.update_state(ScrollState::VerticalScrollAsHorizontal(value()), false); + id.update_state( + UpdateState::Scroll(ScrollState::VerticalScrollAsHorizontal(value())), + false, + ); }); self } @@ -323,10 +345,10 @@ impl Scroll { } fn draw_bars(&self, cx: &mut PaintCx) { - let edge_width = 0.0; + let edge_width = self.scroll_bar_style.edge_width as f64; let scroll_offset = self.child_viewport.origin().to_vec2(); let radius = |rect: Rect, vertical| { - if self.rounded_bar { + if self.scroll_bar_style.rounded { if vertical { (rect.x1 - rect.x0) / 2. } else { @@ -337,10 +359,11 @@ impl Scroll { } }; - let color = self.scroll_bar_color; + let color = self.scroll_bar_style.color; if let Some(bounds) = self.calc_vertical_bar_bounds(cx.app_state) { let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); - cx.fill(&rect.to_rounded_rect(radius(rect, true)), color, 0.0); + let rect = rect.to_rounded_rect(radius(rect, true)); + cx.fill(&rect, color, 0.0); if edge_width > 0.0 { cx.stroke(&rect, color, edge_width); } @@ -349,13 +372,10 @@ impl Scroll { // Horizontal bar if let Some(bounds) = self.calc_horizontal_bar_bounds(cx.app_state) { let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); + let rect = rect.to_rounded_rect(radius(rect, false)); cx.fill(&rect, color, 0.0); if edge_width > 0.0 { - cx.stroke( - &rect.to_rounded_rect(radius(rect, false)), - color, - edge_width, - ); + cx.stroke(&rect, color, edge_width); } } } @@ -369,14 +389,15 @@ impl Scroll { return None; } - let bar_width = 10.0; + let bar_width = self.scroll_bar_style.thickness as f64; let bar_pad = 0.0; let percent_visible = viewport_size.height / content_size.height; let percent_scrolled = scroll_offset.y / (content_size.height - viewport_size.height); let length = (percent_visible * viewport_size.height).ceil(); - let length = length.max(SCROLLBAR_MIN_SIZE); + // Vertical scroll bar must have ast least the same height as it's width + let length = length.max(self.scroll_bar_style.thickness as f64); let top_y_offset = ((viewport_size.height - length) * percent_scrolled).ceil(); let bottom_y_offset = top_y_offset + length; @@ -399,11 +420,7 @@ impl Scroll { return None; } - let bar_width = if viewport_size.height < 40.0 { - 5.0 - } else { - 10.0 - }; + let bar_width = self.scroll_bar_style.thickness as f64; let bar_pad = 0.0; let percent_visible = viewport_size.width / content_size.width; @@ -543,32 +560,26 @@ impl View for Scroll { cx: &mut crate::context::UpdateCx, state: Box, ) -> crate::view::ChangeFlags { - if let Ok(state) = state.downcast::() { + if let Ok(state) = state.downcast::() { match *state { - ScrollState::EnsureVisible(rect) => { - self.pan_to_visible(cx.app_state, rect); - } - ScrollState::ScrollDelta(delta) => { - self.scroll_delta(cx.app_state, delta); - } - ScrollState::ScrollTo(origin) => { - self.scroll_to(cx.app_state, origin); - } - ScrollState::ScrollBarColor(color) => { - self.scroll_bar_color = color; - } - ScrollState::RoundedBar(round) => { - self.rounded_bar = round; - } - ScrollState::HiddenBar(value) => { - self.hide_bar = value; - } - ScrollState::PropagatePointerWheel(value) => { - self.propagate_pointer_wheel = value; - } - ScrollState::VerticalScrollAsHorizontal(value) => { - self.vertical_scroll_as_horizontal = value; - } + UpdateState::Scroll(scroll_state) => match scroll_state { + ScrollState::EnsureVisible(rect) => { + self.pan_to_visible(cx.app_state, rect); + } + ScrollState::ScrollDelta(delta) => { + self.scroll_delta(cx.app_state, delta); + } + ScrollState::ScrollTo(origin) => { + self.scroll_to(cx.app_state, origin); + } + ScrollState::PropagatePointerWheel(value) => { + self.propagate_pointer_wheel = value; + } + ScrollState::VerticalScrollAsHorizontal(value) => { + self.vertical_scroll_as_horizontal = value; + } + }, + UpdateState::Style(style) => self.scroll_bar_style = style, } cx.request_layout(self.id()); ChangeFlags::LAYOUT @@ -628,7 +639,7 @@ impl View for Scroll { match &event { Event::PointerDown(event) => { - if !self.hide_bar && event.button.is_primary() { + if !self.scroll_bar_style.hide && event.button.is_primary() { self.held = BarHeldState::None; let pos = event.pos + scroll_offset; @@ -676,7 +687,7 @@ impl View for Scroll { } Event::PointerUp(_event) => self.held = BarHeldState::None, Event::PointerMove(event) => { - if !self.hide_bar { + if !self.scroll_bar_style.hide { if self.are_bars_held() { match self.held { BarHeldState::Vertical(offset, initial_scroll_offset) => { @@ -750,7 +761,7 @@ impl View for Scroll { self.child.paint_main(cx); cx.restore(); - if !self.hide_bar { + if !self.scroll_bar_style.hide { self.draw_bars(cx); } } From 4208df099dca5a155edec8a857dab2327ef7456c Mon Sep 17 00:00:00 2001 From: Jared Moulton Date: Fri, 25 Aug 2023 22:29:42 -0600 Subject: [PATCH 3/5] Add scroll bar styles to view style --- src/app_handle.rs | 9 ++++ src/context.rs | 84 ++++++++++++++++++++++++++++++++++ src/style.rs | 24 ++++++++++ src/view.rs | 24 ++++++++++ src/views/decorator.rs | 36 +++++++-------- src/views/label.rs | 4 +- src/views/scroll.rs | 100 ++++++++++++++++++++--------------------- src/views/svg.rs | 6 +-- 8 files changed, 212 insertions(+), 75 deletions(-) diff --git a/src/app_handle.rs b/src/app_handle.rs index 79d7172a..9c76c886 100644 --- a/src/app_handle.rs +++ b/src/app_handle.rs @@ -302,6 +302,15 @@ impl AppHandle { saved_font_styles: Vec::new(), saved_line_heights: Vec::new(), saved_z_indexes: Vec::new(), + scroll_bar_color: None, + scroll_bar_rounded: None, + scroll_bar_thickness: None, + scroll_bar_edge_width: None, + + saved_scroll_bar_colors: Vec::new(), + saved_scroll_bar_roundeds: Vec::new(), + saved_scroll_bar_thicknesses: Vec::new(), + saved_scroll_bar_edge_widths: Vec::new(), }; cx.paint_state.renderer.as_mut().unwrap().begin(); self.view.paint_main(&mut cx); diff --git a/src/context.rs b/src/context.rs index 771e1fda..9c0728eb 100644 --- a/src/context.rs +++ b/src/context.rs @@ -681,6 +681,10 @@ pub struct LayoutCx<'a> { app_state: &'a mut AppState, pub(crate) viewport: Option, pub(crate) color: Option, + pub(crate) scroll_bar_color: Option, + pub(crate) scroll_bar_rounded: Option, + pub(crate) scroll_bar_thickness: Option, + pub(crate) scroll_bar_edge_width: Option, pub(crate) font_size: Option, pub(crate) font_family: Option, pub(crate) font_weight: Option, @@ -689,6 +693,10 @@ pub struct LayoutCx<'a> { pub(crate) window_origin: Point, pub(crate) saved_viewports: Vec>, pub(crate) saved_colors: Vec>, + pub(crate) saved_scroll_bar_colors: Vec>, + pub(crate) saved_scroll_bar_roundeds: Vec>, + pub(crate) saved_scroll_bar_thicknesses: Vec>, + pub(crate) saved_scroll_bar_edge_widths: Vec>, pub(crate) saved_font_sizes: Vec>, pub(crate) saved_font_families: Vec>, pub(crate) saved_font_weights: Vec>, @@ -717,15 +725,31 @@ impl<'a> LayoutCx<'a> { saved_font_styles: Vec::new(), saved_line_heights: Vec::new(), saved_window_origins: Vec::new(), + scroll_bar_color: None, + scroll_bar_rounded: None, + scroll_bar_thickness: None, + scroll_bar_edge_width: None, + saved_scroll_bar_colors: Vec::new(), + saved_scroll_bar_roundeds: Vec::new(), + saved_scroll_bar_thicknesses: Vec::new(), + saved_scroll_bar_edge_widths: Vec::new(), } } pub(crate) fn clear(&mut self) { self.viewport = None; + self.scroll_bar_color = None; + self.scroll_bar_rounded = None; + self.scroll_bar_thickness = None; + self.scroll_bar_edge_width = None; self.font_size = None; self.window_origin = Point::ZERO; self.saved_colors.clear(); self.saved_viewports.clear(); + self.saved_scroll_bar_colors.clear(); + self.saved_scroll_bar_roundeds.clear(); + self.saved_scroll_bar_thicknesses.clear(); + self.saved_scroll_bar_edge_widths.clear(); self.saved_font_sizes.clear(); self.saved_font_families.clear(); self.saved_font_weights.clear(); @@ -737,6 +761,12 @@ impl<'a> LayoutCx<'a> { pub fn save(&mut self) { self.saved_viewports.push(self.viewport); self.saved_colors.push(self.color); + self.saved_scroll_bar_colors.push(self.scroll_bar_color); + self.saved_scroll_bar_roundeds.push(self.scroll_bar_rounded); + self.saved_scroll_bar_thicknesses + .push(self.scroll_bar_thickness); + self.saved_scroll_bar_edge_widths + .push(self.scroll_bar_edge_width); self.saved_font_sizes.push(self.font_size); self.saved_font_families.push(self.font_family.clone()); self.saved_font_weights.push(self.font_weight); @@ -748,6 +778,10 @@ impl<'a> LayoutCx<'a> { pub fn restore(&mut self) { self.viewport = self.saved_viewports.pop().unwrap_or_default(); self.color = self.saved_colors.pop().unwrap_or_default(); + self.scroll_bar_color = self.saved_scroll_bar_colors.pop().unwrap_or_default(); + self.scroll_bar_rounded = self.saved_scroll_bar_roundeds.pop().unwrap_or_default(); + self.scroll_bar_thickness = self.saved_scroll_bar_thicknesses.pop().unwrap_or_default(); + self.scroll_bar_edge_width = self.saved_scroll_bar_edge_widths.pop().unwrap_or_default(); self.font_size = self.saved_font_sizes.pop().unwrap_or_default(); self.font_family = self.saved_font_families.pop().unwrap_or_default(); self.font_weight = self.saved_font_weights.pop().unwrap_or_default(); @@ -764,6 +798,22 @@ impl<'a> LayoutCx<'a> { self.app_state } + pub fn current_scroll_bar_color(&self) -> Option { + self.scroll_bar_color + } + + pub fn current_scroll_bar_rounded(&self) -> Option { + self.scroll_bar_rounded + } + + pub fn current_scroll_bar_thickness(&self) -> Option { + self.scroll_bar_thickness + } + + pub fn current_scroll_bar_edge_width(&self) -> Option { + self.scroll_bar_edge_width + } + pub fn current_font_size(&self) -> Option { self.font_size } @@ -862,6 +912,10 @@ pub struct PaintCx<'a> { pub(crate) transform: Affine, pub(crate) clip: Option, pub(crate) color: Option, + pub(crate) scroll_bar_color: Option, + pub(crate) scroll_bar_rounded: Option, + pub(crate) scroll_bar_thickness: Option, + pub(crate) scroll_bar_edge_width: Option, pub(crate) font_size: Option, pub(crate) font_family: Option, pub(crate) font_weight: Option, @@ -871,6 +925,10 @@ pub struct PaintCx<'a> { pub(crate) saved_transforms: Vec, pub(crate) saved_clips: Vec>, pub(crate) saved_colors: Vec>, + pub(crate) saved_scroll_bar_colors: Vec>, + pub(crate) saved_scroll_bar_roundeds: Vec>, + pub(crate) saved_scroll_bar_thicknesses: Vec>, + pub(crate) saved_scroll_bar_edge_widths: Vec>, pub(crate) saved_font_sizes: Vec>, pub(crate) saved_font_families: Vec>, pub(crate) saved_font_weights: Vec>, @@ -884,6 +942,12 @@ impl<'a> PaintCx<'a> { self.saved_transforms.push(self.transform); self.saved_clips.push(self.clip); self.saved_colors.push(self.color); + self.saved_scroll_bar_colors.push(self.scroll_bar_color); + self.saved_scroll_bar_roundeds.push(self.scroll_bar_rounded); + self.saved_scroll_bar_thicknesses + .push(self.scroll_bar_thickness); + self.saved_scroll_bar_edge_widths + .push(self.scroll_bar_edge_width); self.saved_font_sizes.push(self.font_size); self.saved_font_families.push(self.font_family.clone()); self.saved_font_weights.push(self.font_weight); @@ -896,6 +960,10 @@ impl<'a> PaintCx<'a> { self.transform = self.saved_transforms.pop().unwrap_or_default(); self.clip = self.saved_clips.pop().unwrap_or_default(); self.color = self.saved_colors.pop().unwrap_or_default(); + self.scroll_bar_color = self.saved_scroll_bar_colors.pop().unwrap_or_default(); + self.scroll_bar_rounded = self.saved_scroll_bar_roundeds.pop().unwrap_or_default(); + self.scroll_bar_thickness = self.saved_scroll_bar_thicknesses.pop().unwrap_or_default(); + self.scroll_bar_edge_width = self.saved_scroll_bar_edge_widths.pop().unwrap_or_default(); self.font_size = self.saved_font_sizes.pop().unwrap_or_default(); self.font_family = self.saved_font_families.pop().unwrap_or_default(); self.font_weight = self.saved_font_weights.pop().unwrap_or_default(); @@ -920,6 +988,22 @@ impl<'a> PaintCx<'a> { self.color } + pub fn current_scroll_bar_color(&self) -> Option { + self.scroll_bar_color + } + + pub fn current_scroll_bar_rounded(&self) -> Option { + self.scroll_bar_rounded + } + + pub fn current_scroll_bar_thickness(&self) -> Option { + self.scroll_bar_thickness + } + + pub fn current_scroll_bar_edge_width(&self) -> Option { + self.scroll_bar_edge_width + } + pub fn current_font_size(&self) -> Option { self.font_size } diff --git a/src/style.rs b/src/style.rs index b8d473c0..af55ba9f 100644 --- a/src/style.rs +++ b/src/style.rs @@ -265,6 +265,10 @@ define_styles!( color nocb: Option = None, background nocb: Option = None, box_shadow nocb: Option = None, + scroll_bar_color nocb: Option = None, + scroll_bar_rounded nocb: Option = None, + scroll_bar_thickness nocb: Option = None, + scroll_bar_edge_width nocb: Option = None, font_size nocb: Option = None, font_family nocb: Option = None, font_weight nocb: Option = None, @@ -612,6 +616,26 @@ impl Style { self } + pub fn scroll_bar_color(mut self, color: impl Into>) -> Self { + self.scroll_bar_color = color.into().map(Some); + self + } + + pub fn scroll_bar_rounded(mut self, rounded: impl Into>) -> Self { + self.scroll_bar_rounded = rounded.into().map(Some); + self + } + + pub fn scroll_bar_thickness(mut self, thickness: impl Into>) -> Self { + self.scroll_bar_thickness = thickness.into().map(Some); + self + } + + pub fn scroll_bar_edge_width(mut self, edge_width: impl Into>) -> Self { + self.scroll_bar_edge_width = edge_width.into().map(Some); + self + } + pub fn font_size(mut self, size: impl Into>) -> Self { self.font_size = size.into().map(Some); self diff --git a/src/view.rs b/src/view.rs index 8f17c5cf..344c6d1c 100644 --- a/src/view.rs +++ b/src/view.rs @@ -196,6 +196,18 @@ pub trait View { if style.color.is_some() { cx.color = style.color; } + if style.scroll_bar_color.is_some() { + cx.scroll_bar_color = style.scroll_bar_color; + } + if style.scroll_bar_rounded.is_some() { + cx.scroll_bar_rounded = style.scroll_bar_rounded; + } + if style.scroll_bar_thickness.is_some() { + cx.scroll_bar_thickness = style.scroll_bar_thickness; + } + if style.scroll_bar_edge_width.is_some() { + cx.scroll_bar_edge_width = style.scroll_bar_edge_width; + } if style.font_size.is_some() { cx.font_size = style.font_size; } @@ -639,6 +651,18 @@ pub trait View { if style.color.is_some() { cx.color = style.color; } + if style.scroll_bar_color.is_some() { + cx.scroll_bar_color = style.scroll_bar_color; + } + if style.scroll_bar_rounded.is_some() { + cx.scroll_bar_rounded = style.scroll_bar_rounded; + } + if style.scroll_bar_thickness.is_some() { + cx.scroll_bar_thickness = style.scroll_bar_thickness; + } + if style.scroll_bar_edge_width.is_some() { + cx.scroll_bar_edge_width = style.scroll_bar_edge_width; + } if style.font_size.is_some() { cx.font_size = style.font_size; } diff --git a/src/views/decorator.rs b/src/views/decorator.rs index ab782d1b..e9fe0da2 100644 --- a/src/views/decorator.rs +++ b/src/views/decorator.rs @@ -35,10 +35,10 @@ pub trait Decorators: View + Sized { /// ``` /// If you are returning from a function that produces a view, you may want /// to use `base_style` for the returned [`View`] instead. - fn style(self, style: impl Fn() -> Style + 'static) -> Self { + fn style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style(style); }); self @@ -61,49 +61,49 @@ pub trait Decorators: View + Sized { /// )) /// } /// ``` - fn base_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn base_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_base_style(style); }); self } /// The visual style to apply when the mouse hovers over the element - fn hover_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn hover_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::Hover); }); self } /// The visual style to apply when the mouse hovers over the element - fn dragging_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn dragging_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::Dragging); }); self } - fn focus_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn focus_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::Focus); }); self } /// Similar to the `:focus-visible` css selector, this style only activates when tab navigation is used. - fn focus_visible_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn focus_visible_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::FocusVisible); }); self @@ -122,28 +122,28 @@ pub trait Decorators: View + Sized { self } - fn active_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn active_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::Active); }); self } - fn disabled_style(self, style: impl Fn() -> Style + 'static) -> Self { + fn disabled_style(self, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_style_selector(style, StyleSelector::Disabled); }); self } - fn responsive_style(self, size: ScreenSize, style: impl Fn() -> Style + 'static) -> Self { + fn responsive_style(self, size: ScreenSize, style: impl Fn(Style) -> Style + 'static) -> Self { let id = self.id(); create_effect(move |_| { - let style = style(); + let style = style(Style::BASE); id.update_responsive_style(style, size); }); self diff --git a/src/views/label.rs b/src/views/label.rs index 82777450..8fdaa8a2 100644 --- a/src/views/label.rs +++ b/src/views/label.rs @@ -153,7 +153,7 @@ impl View for Label { } else { let text_overflow = cx.app_state_mut().get_computed_style(self.id).text_overflow; if self.color != cx.color - || self.font_size != cx.current_font_size() + || self.font_size != cx.font_size || self.font_family.as_deref() != cx.current_font_family() || self.font_weight != cx.font_weight || self.font_style != cx.font_style @@ -161,7 +161,7 @@ impl View for Label { || self.text_overflow != text_overflow { self.color = cx.color; - self.font_size = cx.current_font_size(); + self.font_size = cx.font_size; self.font_family = cx.current_font_family().map(|s| s.to_string()); self.font_weight = cx.font_weight; self.font_style = cx.font_style; diff --git a/src/views/scroll.rs b/src/views/scroll.rs index e08405d6..1e36c400 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -27,10 +27,6 @@ enum ScrollState { VerticalScrollAsHorizontal(bool), } -enum UpdateState { - Scroll(ScrollState), - Style(ScrollBarStyle), -} /// Minimum length for any scrollbar to be when measured on that /// scrollbar's primary axis. const SCROLLBAR_MIN_SIZE: f64 = 10.0; @@ -60,9 +56,9 @@ impl ScrollBarStyle { // 179 is 70% of 255 so a 70% alpha factor is the default color: Color::rgba8(0, 0, 0, 179), rounded: cfg!(target_os = "macos"), - hide: false, thickness: 10., edge_width: 0., + hide: false, }; pub fn color(mut self, color: Color) -> Self { @@ -73,10 +69,6 @@ impl ScrollBarStyle { self.rounded = rounded; self } - pub fn hide(mut self, hide: bool) -> Self { - self.hide = hide; - self - } pub fn thickness(mut self, thickness: f32) -> Self { self.thickness = thickness; self @@ -123,16 +115,6 @@ pub fn scroll(child: impl FnOnce() -> V) -> Scroll { } impl Scroll { - pub fn scroll_bar_style(self, style: impl Fn() -> ScrollBarStyle + 'static) -> Self { - let id = self.id; - create_effect(move |_| { - let style = style(); - id.update_state(UpdateState::Style(style), false); - }); - - self - } - pub fn on_scroll(mut self, onscroll: impl Fn(Rect) + 'static) -> Self { self.onscroll = Some(Box::new(onscroll)); self @@ -152,7 +134,7 @@ impl Scroll { let id = self.id; create_effect(move |_| { let delta = delta(); - id.update_state(UpdateState::Scroll(ScrollState::ScrollDelta(delta)), false); + id.update_state(ScrollState::ScrollDelta(delta), false); }); self @@ -162,7 +144,7 @@ impl Scroll { let id = self.id; create_effect(move |_| { if let Some(origin) = origin() { - id.update_state(UpdateState::Scroll(ScrollState::ScrollTo(origin)), true); + id.update_state(ScrollState::ScrollTo(origin), true); } }); @@ -172,10 +154,7 @@ impl Scroll { pub fn propagate_pointer_wheel(self, value: impl Fn() -> bool + 'static) -> Self { let id = self.id; create_effect(move |_| { - id.update_state( - UpdateState::Scroll(ScrollState::PropagatePointerWheel(value())), - false, - ); + id.update_state(ScrollState::PropagatePointerWheel(value()), false); }); self } @@ -183,10 +162,7 @@ impl Scroll { pub fn vertical_scroll_as_horizontal(self, value: impl Fn() -> bool + 'static) -> Self { let id = self.id; create_effect(move |_| { - id.update_state( - UpdateState::Scroll(ScrollState::VerticalScrollAsHorizontal(value())), - false, - ); + id.update_state(ScrollState::VerticalScrollAsHorizontal(value()), false); }); self } @@ -345,14 +321,14 @@ impl Scroll { } fn draw_bars(&self, cx: &mut PaintCx) { - let edge_width = self.scroll_bar_style.edge_width as f64; + let edge_width = cx.scroll_bar_edge_width.unwrap_or(0.) as f64; let scroll_offset = self.child_viewport.origin().to_vec2(); let radius = |rect: Rect, vertical| { if self.scroll_bar_style.rounded { if vertical { - (rect.x1 - rect.x0) / 2. + (rect.x1 - rect.x0) / 1. } else { - (rect.y1 - rect.y0) / 2. + (rect.y1 - rect.y0) / 1. } } else { 0. @@ -560,26 +536,23 @@ impl View for Scroll { cx: &mut crate::context::UpdateCx, state: Box, ) -> crate::view::ChangeFlags { - if let Ok(state) = state.downcast::() { + if let Ok(state) = state.downcast::() { match *state { - UpdateState::Scroll(scroll_state) => match scroll_state { - ScrollState::EnsureVisible(rect) => { - self.pan_to_visible(cx.app_state, rect); - } - ScrollState::ScrollDelta(delta) => { - self.scroll_delta(cx.app_state, delta); - } - ScrollState::ScrollTo(origin) => { - self.scroll_to(cx.app_state, origin); - } - ScrollState::PropagatePointerWheel(value) => { - self.propagate_pointer_wheel = value; - } - ScrollState::VerticalScrollAsHorizontal(value) => { - self.vertical_scroll_as_horizontal = value; - } - }, - UpdateState::Style(style) => self.scroll_bar_style = style, + ScrollState::EnsureVisible(rect) => { + self.pan_to_visible(cx.app_state, rect); + } + ScrollState::ScrollDelta(delta) => { + self.scroll_delta(cx.app_state, delta); + } + ScrollState::ScrollTo(origin) => { + self.scroll_to(cx.app_state, origin); + } + ScrollState::PropagatePointerWheel(value) => { + self.propagate_pointer_wheel = value; + } + ScrollState::VerticalScrollAsHorizontal(value) => { + self.vertical_scroll_as_horizontal = value; + } } cx.request_layout(self.id()); ChangeFlags::LAYOUT @@ -590,6 +563,19 @@ impl View for Scroll { fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node { cx.layout_node(self.id, true, |cx| { + if let Some(color) = cx.scroll_bar_color { + self.scroll_bar_style.color = color; + } + if let Some(rounded) = cx.scroll_bar_rounded { + self.scroll_bar_style.rounded = rounded; + } + if let Some(thickness) = cx.scroll_bar_thickness { + self.scroll_bar_style.thickness = thickness; + } + if let Some(edge_width) = cx.scroll_bar_edge_width { + self.scroll_bar_style.edge_width = edge_width; + } + let child_id = self.child.id(); let child_view = cx.app_state_mut().view_state(child_id); child_view.style.position = StyleValue::Val(Position::Absolute); @@ -756,6 +742,18 @@ impl View for Scroll { fn paint(&mut self, cx: &mut crate::context::PaintCx) { cx.save(); + if let Some(color) = cx.scroll_bar_color { + self.scroll_bar_style.color = color; + } + if let Some(rounded) = cx.scroll_bar_rounded { + self.scroll_bar_style.rounded = rounded; + } + if let Some(thickness) = cx.scroll_bar_thickness { + self.scroll_bar_style.thickness = thickness; + } + if let Some(edge_width) = cx.scroll_bar_edge_width { + self.scroll_bar_style.edge_width = edge_width; + } cx.clip(&self.actual_rect); cx.offset((-self.child_viewport.x0, -self.child_viewport.y0)); self.child.paint_main(cx); diff --git a/src/views/svg.rs b/src/views/svg.rs index 56fe03e1..637bebd0 100644 --- a/src/views/svg.rs +++ b/src/views/svg.rs @@ -11,7 +11,6 @@ use sha2::{Digest, Sha256}; use crate::{ app_handle::ViewContext, id::Id, - style::Style, view::{ChangeFlags, View}, views::Decorators, }; @@ -43,9 +42,8 @@ pub fn checkbox(checked: crate::reactive::ReadSignal) -> Svg { let svg_str = move || if checked.get() { CHECKBOX_SVG } else { "" }.to_string(); svg(svg_str) - .style(|| { - Style::BASE - .width_px(20.) + .style(|base| { + base.width_px(20.) .height_px(20.) .border_color(Color::BLACK) .border(1.) From fdce3d059f79fa90a6c605c7413dc430448e096b Mon Sep 17 00:00:00 2001 From: Jared Moulton Date: Fri, 25 Aug 2023 22:33:19 -0600 Subject: [PATCH 4/5] Fix scroll bar edge width from self not cx --- src/views/scroll.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 1e36c400..2174486b 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -321,14 +321,14 @@ impl Scroll { } fn draw_bars(&self, cx: &mut PaintCx) { - let edge_width = cx.scroll_bar_edge_width.unwrap_or(0.) as f64; + let edge_width = self.scroll_bar_style.edge_width as f64; let scroll_offset = self.child_viewport.origin().to_vec2(); let radius = |rect: Rect, vertical| { if self.scroll_bar_style.rounded { if vertical { - (rect.x1 - rect.x0) / 1. + (rect.x1 - rect.x0) / 2. } else { - (rect.y1 - rect.y0) / 1. + (rect.y1 - rect.y0) / 2. } } else { 0. From 07e7d1b0356a49375e5a5ddc46d7f7dac93f5d73 Mon Sep 17 00:00:00 2001 From: Jared Moulton Date: Fri, 25 Aug 2023 23:56:49 -0600 Subject: [PATCH 5/5] Add back hide method --- src/views/scroll.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 2174486b..3f8e0c36 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -23,6 +23,7 @@ enum ScrollState { EnsureVisible(Rect), ScrollDelta(Vec2), ScrollTo(Point), + HiddenBar(bool), PropagatePointerWheel(bool), VerticalScrollAsHorizontal(bool), } @@ -151,6 +152,14 @@ impl Scroll { self } + pub fn hide_bar(self, hide: impl Fn() -> bool + 'static) -> Self { + let id = self.id; + create_effect(move |_| { + id.update_state(ScrollState::HiddenBar(hide()), false); + }); + self + } + pub fn propagate_pointer_wheel(self, value: impl Fn() -> bool + 'static) -> Self { let id = self.id; create_effect(move |_| { @@ -547,6 +556,9 @@ impl View for Scroll { ScrollState::ScrollTo(origin) => { self.scroll_to(cx.app_state, origin); } + ScrollState::HiddenBar(hide) => { + self.scroll_bar_style.hide = hide; + } ScrollState::PropagatePointerWheel(value) => { self.propagate_pointer_wheel = value; }