Add support for embedded scrollbars for scrollable
Co-authored-by: dtzxporter <dtzxporter@users.noreply.github.com>
This commit is contained in:
parent
3c55e07668
commit
8ae4e09db9
4 changed files with 325 additions and 212 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
)
|
||||
.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()
|
||||
)
|
||||
.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()
|
||||
)
|
||||
.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: properties,
|
||||
vertical: properties,
|
||||
horizontal: scrollbar,
|
||||
vertical: scrollbar,
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.id(SCROLLABLE_ID.clone())
|
||||
|
|
|
|||
|
|
@ -200,8 +200,7 @@ where
|
|||
class,
|
||||
} = menu;
|
||||
|
||||
let list = Scrollable::with_direction(
|
||||
List {
|
||||
let list = Scrollable::new(List {
|
||||
options,
|
||||
hovered_option,
|
||||
on_selected,
|
||||
|
|
@ -212,9 +211,7 @@ where
|
|||
text_shaping,
|
||||
padding,
|
||||
class,
|
||||
},
|
||||
scrollable::Direction::default(),
|
||||
);
|
||||
});
|
||||
|
||||
state.tree.diff(&list as &dyn Widget<_, _, _>);
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
debug_assert!(
|
||||
direction.vertical().is_none()
|
||||
|| !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(),
|
||||
"scrollable content must not fill its horizontal scrolling axis"
|
||||
);
|
||||
|
||||
Scrollable {
|
||||
id: None,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
direction,
|
||||
content,
|
||||
direction: Direction::default(),
|
||||
content: content.into(),
|
||||
on_scroll: None,
|
||||
class: Theme::default(),
|
||||
}
|
||||
.validate()
|
||||
}
|
||||
|
||||
fn validate(self) -> Self {
|
||||
debug_assert!(
|
||||
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!(
|
||||
self.direction.horizontal().is_none()
|
||||
|| !self.content.as_widget().size_hint().width.is_fill(),
|
||||
"scrollable content must not fill its horizontal scrolling axis"
|
||||
);
|
||||
|
||||
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,7 +329,26 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout::contained(limits, self.width, self.height, |limits| {
|
||||
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),
|
||||
};
|
||||
|
||||
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(
|
||||
|
|
@ -313,7 +370,8 @@ where
|
|||
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,8 +840,9 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
if scrollbar.scroller.bounds.width > 0.0
|
||||
&& scrollbar.scroller.bounds.height > 0.0
|
||||
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
|
||||
|
|
@ -791,13 +850,14 @@ where
|
|||
{
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: scrollbar.scroller.bounds,
|
||||
bounds: scroller.bounds,
|
||||
border: style.scroller.border,
|
||||
..renderer::Quad::default()
|
||||
},
|
||||
style.scroller.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderer.with_layer(
|
||||
|
|
@ -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,10 +1427,16 @@ impl Scrollbars {
|
|||
};
|
||||
|
||||
let ratio = bounds.height / content_bounds.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_height =
|
||||
(scrollbar_bounds.height * ratio).max(2.0);
|
||||
let scroller_offset =
|
||||
translation.y * ratio * scrollbar_bounds.height / bounds.height;
|
||||
translation.y * ratio * scrollbar_bounds.height
|
||||
/ bounds.height;
|
||||
|
||||
let scroller_bounds = Rectangle {
|
||||
x: bounds.x + bounds.width
|
||||
|
|
@ -1381,12 +1447,15 @@ impl Scrollbars {
|
|||
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,10 +1497,15 @@ impl Scrollbars {
|
|||
};
|
||||
|
||||
let ratio = bounds.width / content_bounds.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;
|
||||
translation.x * ratio * scrollbar_bounds.width
|
||||
/ bounds.width;
|
||||
|
||||
let scroller_bounds = Rectangle {
|
||||
x: (scrollbar_bounds.x + scroller_offset).max(0.0),
|
||||
|
|
@ -1442,12 +1516,15 @@ impl Scrollbars {
|
|||
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| {
|
||||
let scrollbar = self.y?;
|
||||
let scroller = scrollbar.scroller?;
|
||||
|
||||
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
|
||||
Some(if scroller.bounds.contains(cursor_position) {
|
||||
(cursor_position.y - scroller.bounds.y) / scroller.bounds.height
|
||||
} else {
|
||||
0.5
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn grab_x_scroller(&self, cursor_position: Point) -> Option<f32> {
|
||||
self.x.and_then(|scrollbar| {
|
||||
let scrollbar = self.x?;
|
||||
let scroller = scrollbar.scroller?;
|
||||
|
||||
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
|
||||
Some(if scroller.bounds.contains(cursor_position) {
|
||||
(cursor_position.x - scroller.bounds.x) / scroller.bounds.width
|
||||
} else {
|
||||
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,15 +1614,19 @@ pub(super) mod internals {
|
|||
grabbed_at: f32,
|
||||
cursor_position: Point,
|
||||
) -> f32 {
|
||||
if let Some(scroller) = self.scroller {
|
||||
let percentage = (cursor_position.y
|
||||
- self.bounds.y
|
||||
- self.scroller.bounds.height * grabbed_at)
|
||||
/ (self.bounds.height - self.scroller.bounds.height);
|
||||
- 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the x-axis scrolled percentage from the cursor position.
|
||||
|
|
@ -1554,15 +1635,19 @@ pub(super) mod internals {
|
|||
grabbed_at: f32,
|
||||
cursor_position: Point,
|
||||
) -> f32 {
|
||||
if let Some(scroller) = self.scroller {
|
||||
let percentage = (cursor_position.x
|
||||
- self.bounds.x
|
||||
- self.scroller.bounds.width * grabbed_at)
|
||||
/ (self.bounds.width - self.scroller.bounds.width);
|
||||
- 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue