diff options
Diffstat (limited to 'native/src/widget')
-rw-r--r-- | native/src/widget/checkbox.rs | 22 | ||||
-rw-r--r-- | native/src/widget/helpers.rs | 5 | ||||
-rw-r--r-- | native/src/widget/radio.rs | 37 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 30 | ||||
-rw-r--r-- | native/src/widget/slider.rs | 88 | ||||
-rw-r--r-- | native/src/widget/text_input.rs | 202 | ||||
-rw-r--r-- | native/src/widget/vertical_slider.rs | 86 |
7 files changed, 339 insertions, 131 deletions
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 9b69e574..ad05a8e7 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -14,17 +14,6 @@ use crate::{ pub use iced_style::checkbox::{Appearance, StyleSheet}; -/// The icon in a [`Checkbox`]. -#[derive(Debug, Clone, PartialEq)] -pub struct Icon<Font> { - /// Font that will be used to display the `code_point`, - pub font: Font, - /// The unicode code point that will be used as the icon. - pub code_point: char, - /// Font size of the content. - pub size: Option<f32>, -} - /// A box that can be checked. /// /// # Example @@ -319,3 +308,14 @@ where Element::new(checkbox) } } + +/// The icon in a [`Checkbox`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Icon<Font> { + /// Font that will be used to display the `code_point`, + pub font: Font, + /// The unicode code point that will be used as the icon. + pub code_point: char, + /// Font size of the content. + pub size: Option<f32>, +} diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index d13eca75..b25e064d 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -147,7 +147,7 @@ where Renderer::Theme: widget::radio::StyleSheet, V: Copy + Eq, { - widget::Radio::new(value, label, selected, on_click) + widget::Radio::new(label, value, selected, on_click) } /// Creates a new [`Toggler`]. @@ -171,14 +171,13 @@ where pub fn text_input<'a, Message, Renderer>( placeholder: &str, value: &str, - on_change: impl Fn(String) -> Message + 'a, ) -> widget::TextInput<'a, Message, Renderer> where Message: Clone, Renderer: crate::text::Renderer, Renderer::Theme: widget::text_input::StyleSheet, { - widget::TextInput::new(placeholder, value, on_change) + widget::TextInput::new(placeholder, value) } /// Creates a new [`Slider`]. diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 9daddfbc..3ca041bf 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -21,10 +21,13 @@ pub use iced_style::radio::{Appearance, StyleSheet}; /// # type Radio<Message> = /// # iced_native::widget::Radio<Message, iced_native::renderer::Null>; /// # +/// # use iced_native::column; /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Choice { /// A, /// B, +/// C, +/// All, /// } /// /// #[derive(Debug, Clone, Copy)] @@ -34,12 +37,36 @@ pub use iced_style::radio::{Appearance, StyleSheet}; /// /// let selected_choice = Some(Choice::A); /// -/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected); +/// let a = Radio::new( +/// "A", +/// Choice::A, +/// selected_choice, +/// Message::RadioSelected, +/// ); /// -/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); -/// ``` +/// let b = Radio::new( +/// "B", +/// Choice::B, +/// selected_choice, +/// Message::RadioSelected, +/// ); +/// +/// let c = Radio::new( +/// "C", +/// Choice::C, +/// selected_choice, +/// Message::RadioSelected, +/// ); +/// +/// let all = Radio::new( +/// "All of the above", +/// Choice::All, +/// selected_choice, +/// Message::RadioSelected +/// ); /// -///  +/// let content = column![a, b, c, all]; +/// ``` #[allow(missing_debug_implementations)] pub struct Radio<Message, Renderer> where @@ -78,8 +105,8 @@ where /// * a function that will be called when the [`Radio`] is selected. It /// receives the value of the radio and must produce a `Message`. pub fn new<F, V>( - value: V, label: impl Into<String>, + value: V, selected: Option<V>, f: F, ) -> Self diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 8e8138cd..78dcdca2 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -857,8 +857,8 @@ pub fn draw<Renderer>( if let Some(scrollbar) = scrollbars.y { let style = if state.y_scroller_grabbed_at.is_some() { theme.dragging(style) - } else if mouse_over_y_scrollbar { - theme.hovered(style) + } else if mouse_over_scrollable { + theme.hovered(style, mouse_over_y_scrollbar) } else { theme.active(style) }; @@ -870,8 +870,8 @@ pub fn draw<Renderer>( if let Some(scrollbar) = scrollbars.x { let style = if state.x_scroller_grabbed_at.is_some() { theme.dragging_horizontal(style) - } else if mouse_over_x_scrollbar { - theme.hovered_horizontal(style) + } else if mouse_over_scrollable { + theme.hovered_horizontal(style, mouse_over_x_scrollbar) } else { theme.active_horizontal(style) }; @@ -895,7 +895,7 @@ pub fn draw<Renderer>( } fn notify_on_scroll<Message>( - state: &State, + state: &mut State, on_scroll: &Option<Box<dyn Fn(RelativeOffset) -> Message + '_>>, bounds: Rectangle, content_bounds: Rectangle, @@ -916,7 +916,23 @@ fn notify_on_scroll<Message>( .absolute(bounds.height, content_bounds.height) / (content_bounds.height - bounds.height); - shell.publish(on_scroll(RelativeOffset { x, y })) + let new_offset = RelativeOffset { x, y }; + + // Don't publish redundant offsets to shell + if let Some(prev_offset) = state.last_notified { + let unchanged = |a: f32, b: f32| { + (a - b).abs() <= f32::EPSILON || (a.is_nan() && b.is_nan()) + }; + + if unchanged(prev_offset.x, new_offset.x) + && unchanged(prev_offset.y, new_offset.y) + { + return; + } + } + + shell.publish(on_scroll(new_offset)); + state.last_notified = Some(new_offset); } } @@ -929,6 +945,7 @@ pub struct State { offset_x: Offset, x_scroller_grabbed_at: Option<f32>, keyboard_modifiers: keyboard::Modifiers, + last_notified: Option<RelativeOffset>, } impl Default for State { @@ -940,6 +957,7 @@ impl Default for State { offset_x: Offset::Absolute(0.0), x_scroller_grabbed_at: None, keyboard_modifiers: keyboard::Modifiers::default(), + last_notified: None, } } } diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index d3715b1c..69c06140 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -8,13 +8,15 @@ use crate::renderer; use crate::touch; use crate::widget::tree::{self, Tree}; use crate::{ - Background, Clipboard, Color, Element, Layout, Length, Pixels, Point, - Rectangle, Shell, Size, Widget, + Clipboard, Color, Element, Layout, Length, Pixels, Point, Rectangle, Shell, + Size, Widget, }; use std::ops::RangeInclusive; -pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; +pub use iced_style::slider::{ + Appearance, Handle, HandleShape, Rail, StyleSheet, +}; /// An horizontal bar and a handle that selects a single value from a range of /// values. @@ -368,38 +370,6 @@ pub fn draw<T, R>( style_sheet.active(style) }; - let rail_y = bounds.y + (bounds.height / 2.0).round(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y - 1.0, - width: bounds.width, - height: 2.0, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - style.rail_colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y + 1.0, - width: bounds.width, - height: 2.0, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(style.rail_colors.1), - ); - let (handle_width, handle_height, handle_border_radius) = match style .handle .shape @@ -418,17 +388,61 @@ pub fn draw<T, R>( (start.into() as f32, end.into() as f32) }; - let handle_offset = if range_start >= range_end { + let offset = if range_start >= range_end { 0.0 } else { (bounds.width - handle_width) * (value - range_start) / (range_end - range_start) }; + let rail_y = bounds.y + bounds.height / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: rail_y - style.rail.width / 2.0, + width: offset, + height: style.rail.width, + }, + border_radius: [ + style.rail.border_radius, + 0.0, + 0.0, + style.rail.border_radius, + ] + .into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + style.rail.colors.0, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + offset, + y: rail_y - style.rail.width / 2.0, + width: bounds.width - offset, + height: style.rail.width, + }, + border_radius: [ + 0.0, + style.rail.border_radius, + style.rail.border_radius, + 0.0, + ] + .into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + style.rail.colors.1, + ); + renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + handle_offset.round(), + x: bounds.x + offset.round(), y: rail_y - handle_height / 2.0, width: handle_width, height: handle_height, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ee0473ea..8627aa98 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -46,8 +46,8 @@ pub use iced_style::text_input::{Appearance, StyleSheet}; /// let input = TextInput::new( /// "This is the placeholder...", /// value, -/// Message::TextInputChanged, /// ) +/// .on_input(Message::TextInputChanged) /// .padding(10); /// ``` ///  @@ -65,9 +65,10 @@ where width: Length, padding: Padding, size: Option<f32>, - on_change: Box<dyn Fn(String) -> Message + 'a>, + on_input: Option<Box<dyn Fn(String) -> Message + 'a>>, on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, on_submit: Option<Message>, + icon: Option<Icon<Renderer::Font>>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -81,12 +82,8 @@ where /// /// It expects: /// - a placeholder, - /// - the current value, and - /// - a function that produces a message when the [`TextInput`] changes. - pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self - where - F: 'a + Fn(String) -> Message, - { + /// - the current value + pub fn new(placeholder: &str, value: &str) -> Self { TextInput { id: None, placeholder: String::from(placeholder), @@ -96,9 +93,10 @@ where width: Length::Fill, padding: Padding::new(5.0), size: None, - on_change: Box::new(on_change), + on_input: None, on_paste: None, on_submit: None, + icon: None, style: Default::default(), } } @@ -115,6 +113,25 @@ where self } + /// Sets the message that should be produced when some text is typed into + /// the [`TextInput`]. + /// + /// If this method is not called, the [`TextInput`] will be disabled. + pub fn on_input<F>(mut self, callback: F) -> Self + where + F: 'a + Fn(String) -> Message, + { + self.on_input = Some(Box::new(callback)); + self + } + + /// Sets the message that should be produced when the [`TextInput`] is + /// focused and the enter key is pressed. + pub fn on_submit(mut self, message: Message) -> Self { + self.on_submit = Some(message); + self + } + /// Sets the message that should be produced when some text is pasted into /// the [`TextInput`]. pub fn on_paste( @@ -132,6 +149,13 @@ where self.font = font; self } + + /// Sets the [`Icon`] of the [`TextInput`]. + pub fn icon(mut self, icon: Icon<Renderer::Font>) -> Self { + self.icon = Some(icon); + self + } + /// Sets the width of the [`TextInput`]. pub fn width(mut self, width: impl Into<Length>) -> Self { self.width = width.into(); @@ -150,13 +174,6 @@ where self } - /// Sets the message that should be produced when the [`TextInput`] is - /// focused and the enter key is pressed. - pub fn on_submit(mut self, message: Message) -> Self { - self.on_submit = Some(message); - self - } - /// Sets the style of the [`TextInput`]. pub fn style( mut self, @@ -189,7 +206,9 @@ where &self.placeholder, self.size, &self.font, + self.on_input.is_none(), self.is_secure, + self.icon.as_ref(), &self.style, ) } @@ -210,6 +229,18 @@ where tree::State::new(State::new()) } + fn diff(&self, tree: &mut Tree) { + let state = tree.state.downcast_mut::<State>(); + + // Unfocus text input if it becomes disabled + if self.on_input.is_none() { + state.last_click = None; + state.is_focused = None; + state.is_pasting = None; + state.is_dragging = false; + } + } + fn width(&self) -> Length { self.width } @@ -223,7 +254,14 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout(renderer, limits, self.width, self.padding, self.size) + layout( + renderer, + limits, + self.width, + self.padding, + self.size, + self.icon.as_ref(), + ) } fn operate( @@ -260,7 +298,7 @@ where self.size, &self.font, self.is_secure, - self.on_change.as_ref(), + self.on_input.as_deref(), self.on_paste.as_deref(), &self.on_submit, || tree.state.downcast_mut::<State>(), @@ -287,7 +325,9 @@ where &self.placeholder, self.size, &self.font, + self.on_input.is_none(), self.is_secure, + self.icon.as_ref(), &self.style, ) } @@ -300,7 +340,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse_interaction(layout, cursor_position) + mouse_interaction(layout, cursor_position, self.on_input.is_none()) } } @@ -318,6 +358,30 @@ where } } +/// The content of the [`Icon`]. +#[derive(Debug, Clone)] +pub struct Icon<Font> { + /// The font that will be used to display the `code_point`. + pub font: Font, + /// The unicode code point that will be used as the icon. + pub code_point: char, + /// The font size of the content. + pub size: Option<f32>, + /// The spacing between the [`Icon`] and the text in a [`TextInput`]. + pub spacing: f32, + /// The side of a [`TextInput`] where to display the [`Icon`]. + pub side: Side, +} + +/// The side of a [`TextInput`]. +#[derive(Debug, Clone)] +pub enum Side { + /// The left side of a [`TextInput`]. + Left, + /// The right side of a [`TextInput`]. + Right, +} + /// The identifier of a [`TextInput`]. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Id(widget::Id); @@ -380,6 +444,7 @@ pub fn layout<Renderer>( width: Length, padding: Padding, size: Option<f32>, + icon: Option<&Icon<Renderer::Font>>, ) -> layout::Node where Renderer: text::Renderer, @@ -389,10 +454,51 @@ where let padding = padding.fit(Size::ZERO, limits.max()); let limits = limits.width(width).pad(padding).height(text_size); - let mut text = layout::Node::new(limits.resolve(Size::ZERO)); - text.move_to(Point::new(padding.left, padding.top)); + let text_bounds = limits.resolve(Size::ZERO); + + if let Some(icon) = icon { + let icon_width = renderer.measure_width( + &icon.code_point.to_string(), + icon.size.unwrap_or_else(|| renderer.default_size()), + icon.font.clone(), + ); + + let mut text_node = layout::Node::new( + text_bounds - Size::new(icon_width + icon.spacing, 0.0), + ); + + let mut icon_node = + layout::Node::new(Size::new(icon_width, text_bounds.height)); + + match icon.side { + Side::Left => { + text_node.move_to(Point::new( + padding.left + icon_width + icon.spacing, + padding.top, + )); + + icon_node.move_to(Point::new(padding.left, padding.top)); + } + Side::Right => { + text_node.move_to(Point::new(padding.left, padding.top)); + + icon_node.move_to(Point::new( + padding.left + text_bounds.width - icon_width, + padding.top, + )); + } + }; + + layout::Node::with_children( + text_bounds.pad(padding), + vec![text_node, icon_node], + ) + } else { + let mut text = layout::Node::new(text_bounds); + text.move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children(text_bounds.pad(padding), vec![text]) + } } /// Processes an [`Event`] and updates the [`State`] of a [`TextInput`] @@ -408,7 +514,7 @@ pub fn update<'a, Message, Renderer>( size: Option<f32>, font: &Renderer::Font, is_secure: bool, - on_change: &dyn Fn(String) -> Message, + on_input: Option<&dyn Fn(String) -> Message>, on_paste: Option<&dyn Fn(String) -> Message>, on_submit: &Option<Message>, state: impl FnOnce() -> &'a mut State, @@ -421,7 +527,8 @@ where Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state(); - let is_clicked = layout.bounds().contains(cursor_position); + let is_clicked = + layout.bounds().contains(cursor_position) && on_input.is_some(); state.is_focused = if is_clicked { state.is_focused.or_else(|| { @@ -551,6 +658,8 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { + let Some(on_input) = on_input else { return event::Status::Ignored }; + if state.is_pasting.is_none() && !state.keyboard_modifiers.command() && !c.is_control() @@ -559,7 +668,7 @@ where editor.insert(c); - let message = (on_change)(editor.contents()); + let message = (on_input)(editor.contents()); shell.publish(message); focus.updated_at = Instant::now(); @@ -572,6 +681,8 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { + let Some(on_input) = on_input else { return event::Status::Ignored }; + let modifiers = state.keyboard_modifiers; focus.updated_at = Instant::now(); @@ -597,7 +708,7 @@ where let mut editor = Editor::new(value, &mut state.cursor); editor.backspace(); - let message = (on_change)(editor.contents()); + let message = (on_input)(editor.contents()); shell.publish(message); } keyboard::KeyCode::Delete => { @@ -617,7 +728,7 @@ where let mut editor = Editor::new(value, &mut state.cursor); editor.delete(); - let message = (on_change)(editor.contents()); + let message = (on_input)(editor.contents()); shell.publish(message); } keyboard::KeyCode::Left => { @@ -692,7 +803,7 @@ where let mut editor = Editor::new(value, &mut state.cursor); editor.delete(); - let message = (on_change)(editor.contents()); + let message = (on_input)(editor.contents()); shell.publish(message); } keyboard::KeyCode::V => { @@ -719,7 +830,7 @@ where let message = if let Some(paste) = &on_paste { (paste)(editor.contents()) } else { - (on_change)(editor.contents()) + (on_input)(editor.contents()) }; shell.publish(message); @@ -813,7 +924,9 @@ pub fn draw<Renderer>( placeholder: &str, size: Option<f32>, font: &Renderer::Font, + is_disabled: bool, is_secure: bool, + icon: Option<&Icon<Renderer::Font>>, style: &<Renderer::Theme as StyleSheet>::Style, ) where Renderer: text::Renderer, @@ -823,11 +936,15 @@ pub fn draw<Renderer>( let value = secure_value.as_ref().unwrap_or(value); let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); + + let mut children_layout = layout.children(); + let text_bounds = children_layout.next().unwrap().bounds(); let is_mouse_over = bounds.contains(cursor_position); - let appearance = if state.is_focused() { + let appearance = if is_disabled { + theme.disabled(style) + } else if state.is_focused() { theme.focused(style) } else if is_mouse_over { theme.hovered(style) @@ -845,6 +962,20 @@ pub fn draw<Renderer>( appearance.background, ); + if let Some(icon) = icon { + let icon_layout = children_layout.next().unwrap(); + + renderer.fill_text(Text { + content: &icon.code_point.to_string(), + size: icon.size.unwrap_or_else(|| renderer.default_size()), + font: icon.font.clone(), + color: appearance.icon_color, + bounds: icon_layout.bounds(), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + }); + } + let text = value.to_string(); let size = size.unwrap_or_else(|| renderer.default_size()); @@ -956,6 +1087,8 @@ pub fn draw<Renderer>( content: if text.is_empty() { placeholder } else { &text }, color: if text.is_empty() { theme.placeholder_color(style) + } else if is_disabled { + theme.disabled_color(style) } else { theme.value_color(style) }, @@ -984,9 +1117,14 @@ pub fn draw<Renderer>( pub fn mouse_interaction( layout: Layout<'_>, cursor_position: Point, + is_disabled: bool, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { - mouse::Interaction::Text + if is_disabled { + mouse::Interaction::NotAllowed + } else { + mouse::Interaction::Text + } } else { mouse::Interaction::default() } diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index f1687e38..a06a200f 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -8,8 +8,8 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; use crate::event::{self, Event}; use crate::widget::tree::{self, Tree}; use crate::{ - layout, mouse, renderer, touch, Background, Clipboard, Color, Element, - Layout, Length, Pixels, Point, Rectangle, Shell, Size, Widget, + layout, mouse, renderer, touch, Clipboard, Color, Element, Layout, Length, + Pixels, Point, Rectangle, Shell, Size, Widget, }; /// An vertical bar and a handle that selects a single value from a range of @@ -363,38 +363,6 @@ pub fn draw<T, R>( style_sheet.active(style) }; - let rail_x = bounds.x + (bounds.width / 2.0).round(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - style.rail_colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x + 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(style.rail_colors.1), - ); - let (handle_width, handle_height, handle_border_radius) = match style .handle .shape @@ -413,18 +381,62 @@ pub fn draw<T, R>( (start.into() as f32, end.into() as f32) }; - let handle_offset = if range_start >= range_end { + let offset = if range_start >= range_end { 0.0 } else { (bounds.height - handle_width) * (value - range_end) / (range_start - range_end) }; + let rail_x = bounds.x + bounds.width / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: rail_x - style.rail.width / 2.0, + y: bounds.y, + width: style.rail.width, + height: offset, + }, + border_radius: [ + style.rail.border_radius, + style.rail.border_radius, + 0.0, + 0.0, + ] + .into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + style.rail.colors.1, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: rail_x - style.rail.width / 2.0, + y: bounds.y + offset, + width: style.rail.width, + height: bounds.height - offset, + }, + border_radius: [ + 0.0, + 0.0, + style.rail.border_radius, + style.rail.border_radius, + ] + .into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + style.rail.colors.0, + ); + renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: rail_x - (handle_height / 2.0), - y: bounds.y + handle_offset.round(), + x: rail_x - handle_height / 2.0, + y: bounds.y + offset.round(), width: handle_height, height: handle_width, }, |