summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-07-11 07:58:33 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-07-11 08:00:58 +0200
commit8ae4e09db9badb801669c15408bc76e8675f9cc8 (patch)
treefc8681143c647742c15b2bb15cb5c27ff023c9ea
parent3c55e076688fd7453e5feb53fccf8139076af584 (diff)
downloadiced-8ae4e09db9badb801669c15408bc76e8675f9cc8.tar.gz
iced-8ae4e09db9badb801669c15408bc76e8675f9cc8.tar.bz2
iced-8ae4e09db9badb801669c15408bc76e8675f9cc8.zip
Add support for embedded scrollbars for `scrollable`
Co-authored-by: dtzxporter <dtzxporter@users.noreply.github.com>
-rw-r--r--core/src/padding.rs36
-rw-r--r--examples/scrollable/src/main.rs61
-rw-r--r--widget/src/overlay/menu.rs27
-rw-r--r--widget/src/scrollable.rs411
4 files changed, 324 insertions, 211 deletions
diff --git a/core/src/padding.rs b/core/src/padding.rs
index a63f6e29..b8c941d8 100644
--- a/core/src/padding.rs
+++ b/core/src/padding.rs
@@ -1,4 +1,4 @@
-use crate::Size;
+use crate::{Pixels, Size};
/// An amount of space to pad for each side of a box
///
@@ -54,7 +54,7 @@ impl Padding {
left: 0.0,
};
- /// Create a Padding that is equal on all sides
+ /// Create a [`Padding`] that is equal on all sides.
pub const fn new(padding: f32) -> Padding {
Padding {
top: padding,
@@ -64,6 +64,38 @@ impl Padding {
}
}
+ /// Create some top [`Padding`].
+ pub fn top(padding: impl Into<Pixels>) -> Self {
+ Self {
+ top: padding.into().0,
+ ..Self::ZERO
+ }
+ }
+
+ /// Create some right [`Padding`].
+ pub fn right(padding: impl Into<Pixels>) -> Self {
+ Self {
+ right: padding.into().0,
+ ..Self::ZERO
+ }
+ }
+
+ /// Create some bottom [`Padding`].
+ pub fn bottom(padding: impl Into<Pixels>) -> Self {
+ Self {
+ bottom: padding.into().0,
+ ..Self::ZERO
+ }
+ }
+
+ /// Create some left [`Padding`].
+ pub fn left(padding: impl Into<Pixels>) -> Self {
+ Self {
+ left: padding.into().0,
+ ..Self::ZERO
+ }
+ }
+
/// Returns the total amount of vertical [`Padding`].
pub fn vertical(self) -> f32 {
self.top + self.bottom
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index f2a853e1..067dcd70 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -1,7 +1,6 @@
-use iced::widget::scrollable::Properties;
use iced::widget::{
button, column, container, horizontal_space, progress_bar, radio, row,
- scrollable, slider, text, vertical_space, Scrollable,
+ scrollable, slider, text, vertical_space,
};
use iced::{Alignment, Border, Color, Element, Length, Task, Theme};
@@ -203,7 +202,7 @@ impl ScrollableDemo {
let scrollable_content: Element<Message> =
Element::from(match self.scrollable_direction {
- Direction::Vertical => Scrollable::with_direction(
+ Direction::Vertical => scrollable(
column![
scroll_to_end_button(),
text("Beginning!"),
@@ -216,19 +215,19 @@ impl ScrollableDemo {
.align_items(Alignment::Center)
.padding([40, 0, 40, 0])
.spacing(40),
- scrollable::Direction::Vertical(
- Properties::new()
- .width(self.scrollbar_width)
- .margin(self.scrollbar_margin)
- .scroller_width(self.scroller_width)
- .alignment(self.alignment),
- ),
)
+ .direction(scrollable::Direction::Vertical(
+ scrollable::Scrollbar::new()
+ .width(self.scrollbar_width)
+ .margin(self.scrollbar_margin)
+ .scroller_width(self.scroller_width)
+ .alignment(self.alignment),
+ ))
.width(Length::Fill)
.height(Length::Fill)
.id(SCROLLABLE_ID.clone())
.on_scroll(Message::Scrolled),
- Direction::Horizontal => Scrollable::with_direction(
+ Direction::Horizontal => scrollable(
row![
scroll_to_end_button(),
text("Beginning!"),
@@ -242,19 +241,19 @@ impl ScrollableDemo {
.align_items(Alignment::Center)
.padding([0, 40, 0, 40])
.spacing(40),
- scrollable::Direction::Horizontal(
- Properties::new()
- .width(self.scrollbar_width)
- .margin(self.scrollbar_margin)
- .scroller_width(self.scroller_width)
- .alignment(self.alignment),
- ),
)
+ .direction(scrollable::Direction::Horizontal(
+ scrollable::Scrollbar::new()
+ .width(self.scrollbar_width)
+ .margin(self.scrollbar_margin)
+ .scroller_width(self.scroller_width)
+ .alignment(self.alignment),
+ ))
.width(Length::Fill)
.height(Length::Fill)
.id(SCROLLABLE_ID.clone())
.on_scroll(Message::Scrolled),
- Direction::Multi => Scrollable::with_direction(
+ Direction::Multi => scrollable(
//horizontal content
row![
column![
@@ -284,19 +283,19 @@ impl ScrollableDemo {
.align_items(Alignment::Center)
.padding([0, 40, 0, 40])
.spacing(40),
- {
- let properties = Properties::new()
- .width(self.scrollbar_width)
- .margin(self.scrollbar_margin)
- .scroller_width(self.scroller_width)
- .alignment(self.alignment);
-
- scrollable::Direction::Both {
- horizontal: properties,
- vertical: properties,
- }
- },
)
+ .direction({
+ let scrollbar = scrollable::Scrollbar::new()
+ .width(self.scrollbar_width)
+ .margin(self.scrollbar_margin)
+ .scroller_width(self.scroller_width)
+ .alignment(self.alignment);
+
+ scrollable::Direction::Both {
+ horizontal: scrollbar,
+ vertical: scrollbar,
+ }
+ })
.width(Length::Fill)
.height(Length::Fill)
.id(SCROLLABLE_ID.clone())
diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs
index 98efe305..a43c8e8b 100644
--- a/widget/src/overlay/menu.rs
+++ b/widget/src/overlay/menu.rs
@@ -200,21 +200,18 @@ where
class,
} = menu;
- let list = Scrollable::with_direction(
- List {
- options,
- hovered_option,
- on_selected,
- on_option_hovered,
- font,
- text_size,
- text_line_height,
- text_shaping,
- padding,
- class,
- },
- scrollable::Direction::default(),
- );
+ let list = Scrollable::new(List {
+ options,
+ hovered_option,
+ on_selected,
+ on_option_hovered,
+ font,
+ text_size,
+ text_line_height,
+ text_shaping,
+ padding,
+ class,
+ });
state.tree.diff(&list as &dyn Widget<_, _, _>);
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index e0875bbf..35613910 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -12,7 +12,7 @@ use crate::core::widget::operation::{self, Operation};
use crate::core::widget::tree::{self, Tree};
use crate::core::{
self, Background, Border, Clipboard, Color, Element, Layout, Length,
- Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget,
+ Padding, Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget,
};
use crate::runtime::task::{self, Task};
use crate::runtime::Action;
@@ -49,37 +49,38 @@ where
pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
- Self::with_direction(content, Direction::default())
+ Scrollable {
+ id: None,
+ width: Length::Shrink,
+ height: Length::Shrink,
+ direction: Direction::default(),
+ content: content.into(),
+ on_scroll: None,
+ class: Theme::default(),
+ }
+ .validate()
}
- /// Creates a new [`Scrollable`] with the given [`Direction`].
- pub fn with_direction(
- content: impl Into<Element<'a, Message, Theme, Renderer>>,
- direction: Direction,
- ) -> Self {
- let content = content.into();
-
+ fn validate(self) -> Self {
debug_assert!(
- direction.vertical().is_none()
- || !content.as_widget().size_hint().height.is_fill(),
+ self.direction.vertical().is_none()
+ || !self.content.as_widget().size_hint().height.is_fill(),
"scrollable content must not fill its vertical scrolling axis"
);
debug_assert!(
- direction.horizontal().is_none()
- || !content.as_widget().size_hint().width.is_fill(),
+ self.direction.horizontal().is_none()
+ || !self.content.as_widget().size_hint().width.is_fill(),
"scrollable content must not fill its horizontal scrolling axis"
);
- Scrollable {
- id: None,
- width: Length::Shrink,
- height: Length::Shrink,
- direction,
- content,
- on_scroll: None,
- class: Theme::default(),
- }
+ self
+ }
+
+ /// Creates a new [`Scrollable`] with the given [`Direction`].
+ pub fn direction(mut self, direction: impl Into<Direction>) -> Self {
+ self.direction = direction.into();
+ self.validate()
}
/// Sets the [`Id`] of the [`Scrollable`].
@@ -108,7 +109,7 @@ where
self
}
- /// Inverts the alignment of the horizontal direction of the [`Scrollable`], if applicable.
+ /// Sets the alignment of the horizontal direction of the [`Scrollable`], if applicable.
pub fn align_x(mut self, alignment: Alignment) -> Self {
match &mut self.direction {
Direction::Horizontal(horizontal)
@@ -134,6 +135,32 @@ where
self
}
+ /// Sets whether the horizontal [`Scrollbar`] should be embedded in the [`Scrollable`].
+ pub fn embed_x(mut self, embedded: bool) -> Self {
+ match &mut self.direction {
+ Direction::Horizontal(horizontal)
+ | Direction::Both { horizontal, .. } => {
+ horizontal.embedded = embedded;
+ }
+ Direction::Vertical(_) => {}
+ }
+
+ self
+ }
+
+ /// Sets whether the vertical [`Scrollbar`] should be embedded in the [`Scrollable`].
+ pub fn embed_y(mut self, embedded: bool) -> Self {
+ match &mut self.direction {
+ Direction::Vertical(vertical)
+ | Direction::Both { vertical, .. } => {
+ vertical.embedded = embedded;
+ }
+ Direction::Horizontal(_) => {}
+ }
+
+ self
+ }
+
/// Sets the style of this [`Scrollable`].
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
@@ -157,21 +184,21 @@ where
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
/// Vertical scrolling
- Vertical(Properties),
+ Vertical(Scrollbar),
/// Horizontal scrolling
- Horizontal(Properties),
+ Horizontal(Scrollbar),
/// Both vertical and horizontal scrolling
Both {
/// The properties of the vertical scrollbar.
- vertical: Properties,
+ vertical: Scrollbar,
/// The properties of the horizontal scrollbar.
- horizontal: Properties,
+ horizontal: Scrollbar,
},
}
impl Direction {
/// Returns the [`Properties`] of the horizontal scrollbar, if any.
- pub fn horizontal(&self) -> Option<&Properties> {
+ pub fn horizontal(&self) -> Option<&Scrollbar> {
match self {
Self::Horizontal(properties) => Some(properties),
Self::Both { horizontal, .. } => Some(horizontal),
@@ -180,7 +207,7 @@ impl Direction {
}
/// Returns the [`Properties`] of the vertical scrollbar, if any.
- pub fn vertical(&self) -> Option<&Properties> {
+ pub fn vertical(&self) -> Option<&Scrollbar> {
match self {
Self::Vertical(properties) => Some(properties),
Self::Both { vertical, .. } => Some(vertical),
@@ -191,31 +218,33 @@ impl Direction {
impl Default for Direction {
fn default() -> Self {
- Self::Vertical(Properties::default())
+ Self::Vertical(Scrollbar::default())
}
}
/// Properties of a scrollbar within a [`Scrollable`].
#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Properties {
+pub struct Scrollbar {
width: f32,
margin: f32,
scroller_width: f32,
alignment: Alignment,
+ embedded: bool,
}
-impl Default for Properties {
+impl Default for Scrollbar {
fn default() -> Self {
Self {
width: 10.0,
margin: 0.0,
scroller_width: 10.0,
alignment: Alignment::Start,
+ embedded: false,
}
}
}
-impl Properties {
+impl Scrollbar {
/// Creates new [`Properties`] for use in a [`Scrollable`].
pub fn new() -> Self {
Self::default()
@@ -244,6 +273,15 @@ impl Properties {
self.alignment = alignment;
self
}
+
+ /// Sets whether the [`Scrollbar`] should be embedded in the [`Scrollable`].
+ ///
+ /// An embedded [`Scrollbar`] will always be displayed, will take layout space,
+ /// and will not float over the contents.
+ pub fn embedded(mut self, embedded: bool) -> Self {
+ self.embedded = embedded;
+ self
+ }
}
/// Alignment of the scrollable's content relative to it's [`Viewport`] in one direction.
@@ -291,29 +329,49 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- layout::contained(limits, self.width, self.height, |limits| {
- let child_limits = layout::Limits::new(
- Size::new(limits.min().width, limits.min().height),
- Size::new(
- if self.direction.horizontal().is_some() {
- f32::INFINITY
- } else {
- limits.max().width
- },
- if self.direction.vertical().is_some() {
- f32::MAX
- } else {
- limits.max().height
- },
- ),
- );
+ let (right_padding, bottom_padding) = match self.direction {
+ Direction::Vertical(scrollbar) if scrollbar.embedded => {
+ (scrollbar.width + scrollbar.margin * 2.0, 0.0)
+ }
+ Direction::Horizontal(scrollbar) if scrollbar.embedded => {
+ (0.0, scrollbar.width + scrollbar.margin * 2.0)
+ }
+ _ => (0.0, 0.0),
+ };
- self.content.as_widget().layout(
- &mut tree.children[0],
- renderer,
- &child_limits,
- )
- })
+ layout::padded(
+ limits,
+ self.width,
+ self.height,
+ Padding {
+ right: right_padding,
+ bottom: bottom_padding,
+ ..Padding::ZERO
+ },
+ |limits| {
+ let child_limits = layout::Limits::new(
+ Size::new(limits.min().width, limits.min().height),
+ Size::new(
+ if self.direction.horizontal().is_some() {
+ f32::INFINITY
+ } else {
+ limits.max().width
+ },
+ if self.direction.vertical().is_some() {
+ f32::MAX
+ } else {
+ limits.max().height
+ },
+ ),
+ );
+
+ self.content.as_widget().layout(
+ &mut tree.children[0],
+ renderer,
+ &child_limits,
+ )
+ },
+ )
}
fn operate(
@@ -762,7 +820,7 @@ where
let draw_scrollbar =
|renderer: &mut Renderer,
- style: Scrollbar,
+ style: Rail,
scrollbar: &internals::Scrollbar| {
if scrollbar.bounds.width > 0.0
&& scrollbar.bounds.height > 0.0
@@ -782,21 +840,23 @@ where
);
}
- if scrollbar.scroller.bounds.width > 0.0
- && scrollbar.scroller.bounds.height > 0.0
- && (style.scroller.color != Color::TRANSPARENT
- || (style.scroller.border.color
- != Color::TRANSPARENT
- && style.scroller.border.width > 0.0))
- {
- renderer.fill_quad(
- renderer::Quad {
- bounds: scrollbar.scroller.bounds,
- border: style.scroller.border,
- ..renderer::Quad::default()
- },
- style.scroller.color,
- );
+ if let Some(scroller) = scrollbar.scroller {
+ if scroller.bounds.width > 0.0
+ && scroller.bounds.height > 0.0
+ && (style.scroller.color != Color::TRANSPARENT
+ || (style.scroller.border.color
+ != Color::TRANSPARENT
+ && style.scroller.border.width > 0.0))
+ {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: scroller.bounds,
+ border: style.scroller.border,
+ ..renderer::Quad::default()
+ },
+ style.scroller.color,
+ );
+ }
}
};
@@ -810,7 +870,7 @@ where
if let Some(scrollbar) = scrollbars.y {
draw_scrollbar(
renderer,
- style.vertical_scrollbar,
+ style.vertical_rail,
&scrollbar,
);
}
@@ -818,7 +878,7 @@ where
if let Some(scrollbar) = scrollbars.x {
draw_scrollbar(
renderer,
- style.horizontal_scrollbar,
+ style.horizontal_rail,
&scrollbar,
);
}
@@ -1324,16 +1384,16 @@ impl Scrollbars {
) -> Self {
let translation = state.translation(direction, bounds, content_bounds);
- let show_scrollbar_x = direction
- .horizontal()
- .filter(|_| content_bounds.width > bounds.width);
+ let show_scrollbar_x = direction.horizontal().filter(|scrollbar| {
+ scrollbar.embedded || content_bounds.width > bounds.width
+ });
- let show_scrollbar_y = direction
- .vertical()
- .filter(|_| content_bounds.height > bounds.height);
+ let show_scrollbar_y = direction.vertical().filter(|scrollbar| {
+ scrollbar.embedded || content_bounds.height > bounds.height
+ });
let y_scrollbar = if let Some(vertical) = show_scrollbar_y {
- let Properties {
+ let Scrollbar {
width,
margin,
scroller_width,
@@ -1367,26 +1427,35 @@ impl Scrollbars {
};
let ratio = bounds.height / content_bounds.height;
- // min height for easier grabbing with super tall content
- let scroller_height = (scrollbar_bounds.height * ratio).max(2.0);
- let scroller_offset =
- translation.y * ratio * scrollbar_bounds.height / bounds.height;
- let scroller_bounds = Rectangle {
- x: bounds.x + bounds.width
- - total_scrollbar_width / 2.0
- - scroller_width / 2.0,
- y: (scrollbar_bounds.y + scroller_offset).max(0.0),
- width: scroller_width,
- height: scroller_height,
+ let scroller = if ratio >= 1.0 {
+ None
+ } else {
+ // min height for easier grabbing with super tall content
+ let scroller_height =
+ (scrollbar_bounds.height * ratio).max(2.0);
+ let scroller_offset =
+ translation.y * ratio * scrollbar_bounds.height
+ / bounds.height;
+
+ let scroller_bounds = Rectangle {
+ x: bounds.x + bounds.width
+ - total_scrollbar_width / 2.0
+ - scroller_width / 2.0,
+ y: (scrollbar_bounds.y + scroller_offset).max(0.0),
+ width: scroller_width,
+ height: scroller_height,
+ };
+
+ Some(internals::Scroller {
+ bounds: scroller_bounds,
+ })
};
Some(internals::Scrollbar {
total_bounds: total_scrollbar_bounds,
bounds: scrollbar_bounds,
- scroller: internals::Scroller {
- bounds: scroller_bounds,
- },
+ scroller,
alignment: vertical.alignment,
})
} else {
@@ -1394,7 +1463,7 @@ impl Scrollbars {
};
let x_scrollbar = if let Some(horizontal) = show_scrollbar_x {
- let Properties {
+ let Scrollbar {
width,
margin,
scroller_width,
@@ -1428,26 +1497,34 @@ impl Scrollbars {
};
let ratio = bounds.width / content_bounds.width;
- // min width for easier grabbing with extra wide content
- let scroller_length = (scrollbar_bounds.width * ratio).max(2.0);
- let scroller_offset =
- translation.x * ratio * scrollbar_bounds.width / bounds.width;
- let scroller_bounds = Rectangle {
- x: (scrollbar_bounds.x + scroller_offset).max(0.0),
- y: bounds.y + bounds.height
- - total_scrollbar_height / 2.0
- - scroller_width / 2.0,
- width: scroller_length,
- height: scroller_width,
+ let scroller = if ratio >= 1.0 {
+ None
+ } else {
+ // min width for easier grabbing with extra wide content
+ let scroller_length = (scrollbar_bounds.width * ratio).max(2.0);
+ let scroller_offset =
+ translation.x * ratio * scrollbar_bounds.width
+ / bounds.width;
+
+ let scroller_bounds = Rectangle {
+ x: (scrollbar_bounds.x + scroller_offset).max(0.0),
+ y: bounds.y + bounds.height
+ - total_scrollbar_height / 2.0
+ - scroller_width / 2.0,
+ width: scroller_length,
+ height: scroller_width,
+ };
+
+ Some(internals::Scroller {
+ bounds: scroller_bounds,
+ })
};
Some(internals::Scrollbar {
total_bounds: total_scrollbar_bounds,
bounds: scrollbar_bounds,
- scroller: internals::Scroller {
- bounds: scroller_bounds,
- },
+ scroller,
alignment: horizontal.alignment,
})
} else {
@@ -1478,33 +1555,33 @@ impl Scrollbars {
}
fn grab_y_scroller(&self, cursor_position: Point) -> Option<f32> {
- self.y.and_then(|scrollbar| {
- if scrollbar.total_bounds.contains(cursor_position) {
- Some(if scrollbar.scroller.bounds.contains(cursor_position) {
- (cursor_position.y - scrollbar.scroller.bounds.y)
- / scrollbar.scroller.bounds.height
- } else {
- 0.5
- })
+ let scrollbar = self.y?;
+ let scroller = scrollbar.scroller?;
+
+ if scrollbar.total_bounds.contains(cursor_position) {
+ Some(if scroller.bounds.contains(cursor_position) {
+ (cursor_position.y - scroller.bounds.y) / scroller.bounds.height
} else {
- None
- }
- })
+ 0.5
+ })
+ } else {
+ None
+ }
}
fn grab_x_scroller(&self, cursor_position: Point) -> Option<f32> {
- self.x.and_then(|scrollbar| {
- if scrollbar.total_bounds.contains(cursor_position) {
- Some(if scrollbar.scroller.bounds.contains(cursor_position) {
- (cursor_position.x - scrollbar.scroller.bounds.x)
- / scrollbar.scroller.bounds.width
- } else {
- 0.5
- })
+ let scrollbar = self.x?;
+ let scroller = scrollbar.scroller?;
+
+ if scrollbar.total_bounds.contains(cursor_position) {
+ Some(if scroller.bounds.contains(cursor_position) {
+ (cursor_position.x - scroller.bounds.x) / scroller.bounds.width
} else {
- None
- }
- })
+ 0.5
+ })
+ } else {
+ None
+ }
}
fn active(&self) -> bool {
@@ -1521,7 +1598,7 @@ pub(super) mod internals {
pub struct Scrollbar {
pub total_bounds: Rectangle,
pub bounds: Rectangle,
- pub scroller: Scroller,
+ pub scroller: Option<Scroller>,
pub alignment: Alignment,
}
@@ -1537,14 +1614,18 @@ pub(super) mod internals {
grabbed_at: f32,
cursor_position: Point,
) -> f32 {
- let percentage = (cursor_position.y
- - self.bounds.y
- - self.scroller.bounds.height * grabbed_at)
- / (self.bounds.height - self.scroller.bounds.height);
-
- match self.alignment {
- Alignment::Start => percentage,
- Alignment::End => 1.0 - percentage,
+ if let Some(scroller) = self.scroller {
+ let percentage = (cursor_position.y
+ - self.bounds.y
+ - scroller.bounds.height * grabbed_at)
+ / (self.bounds.height - scroller.bounds.height);
+
+ match self.alignment {
+ Alignment::Start => percentage,
+ Alignment::End => 1.0 - percentage,
+ }
+ } else {
+ 0.0
}
}
@@ -1554,14 +1635,18 @@ pub(super) mod internals {
grabbed_at: f32,
cursor_position: Point,
) -> f32 {
- let percentage = (cursor_position.x
- - self.bounds.x
- - self.scroller.bounds.width * grabbed_at)
- / (self.bounds.width - self.scroller.bounds.width);
-
- match self.alignment {
- Alignment::Start => percentage,
- Alignment::End => 1.0 - percentage,
+ if let Some(scroller) = self.scroller {
+ let percentage = (cursor_position.x
+ - self.bounds.x
+ - scroller.bounds.width * grabbed_at)
+ / (self.bounds.width - scroller.bounds.width);
+
+ match self.alignment {
+ Alignment::Start => percentage,
+ Alignment::End => 1.0 - percentage,
+ }
+ } else {
+ 0.0
}
}
}
@@ -1595,22 +1680,22 @@ pub enum Status {
},
}
-/// The appearance of a scrolable.
+/// The appearance of a scrollable.
#[derive(Debug, Clone, Copy)]
pub struct Style {
/// The [`container::Style`] of a scrollable.
pub container: container::Style,
- /// The vertical [`Scrollbar`] appearance.
- pub vertical_scrollbar: Scrollbar,
- /// The horizontal [`Scrollbar`] appearance.
- pub horizontal_scrollbar: Scrollbar,
+ /// The vertical [`Rail`] appearance.
+ pub vertical_rail: Rail,
+ /// The horizontal [`Rail`] appearance.
+ pub horizontal_rail: Rail,
/// The [`Background`] of the gap between a horizontal and vertical scrollbar.
pub gap: Option<Background>,
}
/// The appearance of the scrollbar of a scrollable.
#[derive(Debug, Clone, Copy)]
-pub struct Scrollbar {
+pub struct Rail {
/// The [`Background`] of a scrollbar.
pub background: Option<Background>,
/// The [`Border`] of a scrollbar.
@@ -1659,7 +1744,7 @@ impl Catalog for Theme {
pub fn default(theme: &Theme, status: Status) -> Style {
let palette = theme.extended_palette();
- let scrollbar = Scrollbar {
+ let scrollbar = Rail {
background: Some(palette.background.weak.color.into()),
border: Border::rounded(2),
scroller: Scroller {
@@ -1671,15 +1756,15 @@ pub fn default(theme: &Theme, status: Status) -> Style {
match status {
Status::Active => Style {
container: container::Style::default(),
- vertical_scrollbar: scrollbar,
- horizontal_scrollbar: scrollbar,
+ vertical_rail: scrollbar,
+ horizontal_rail: scrollbar,
gap: None,
},
Status::Hovered {
is_horizontal_scrollbar_hovered,
is_vertical_scrollbar_hovered,
} => {
- let hovered_scrollbar = Scrollbar {
+ let hovered_scrollbar = Rail {
scroller: Scroller {
color: palette.primary.strong.color,
..scrollbar.scroller
@@ -1689,12 +1774,12 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Style {
container: container::Style::default(),
- vertical_scrollbar: if is_vertical_scrollbar_hovered {
+ vertical_rail: if is_vertical_scrollbar_hovered {
hovered_scrollbar
} else {
scrollbar
},
- horizontal_scrollbar: if is_horizontal_scrollbar_hovered {
+ horizontal_rail: if is_horizontal_scrollbar_hovered {
hovered_scrollbar
} else {
scrollbar
@@ -1706,7 +1791,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
is_horizontal_scrollbar_dragged,
is_vertical_scrollbar_dragged,
} => {
- let dragged_scrollbar = Scrollbar {
+ let dragged_scrollbar = Rail {
scroller: Scroller {
color: palette.primary.base.color,
..scrollbar.scroller
@@ -1716,12 +1801,12 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Style {
container: container::Style::default(),
- vertical_scrollbar: if is_vertical_scrollbar_dragged {
+ vertical_rail: if is_vertical_scrollbar_dragged {
dragged_scrollbar
} else {
scrollbar
},
- horizontal_scrollbar: if is_horizontal_scrollbar_dragged {
+ horizontal_rail: if is_horizontal_scrollbar_dragged {
dragged_scrollbar
} else {
scrollbar