feat(slider): add handle bounds to avoid handle shifting on hover (#357)
This commit is contained in:
parent
5a6cd771d1
commit
65b5dbc671
1 changed files with 58 additions and 47 deletions
|
|
@ -110,6 +110,8 @@ where
|
||||||
height: f32,
|
height: f32,
|
||||||
class: Theme::Class<'a>,
|
class: Theme::Class<'a>,
|
||||||
status: Option<Status>,
|
status: Option<Status>,
|
||||||
|
handle_width: f32,
|
||||||
|
handle_height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
|
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
|
||||||
|
|
@ -119,7 +121,13 @@ where
|
||||||
Theme: Catalog,
|
Theme: Catalog,
|
||||||
{
|
{
|
||||||
/// The default height of a [`Slider`].
|
/// The default height of a [`Slider`].
|
||||||
pub const DEFAULT_HEIGHT: f32 = 16.0;
|
pub const DEFAULT_HEIGHT: f32 = 26.0;
|
||||||
|
|
||||||
|
/// The default max height of a [`Handle`].
|
||||||
|
pub const DEFAULT_MAX_HANDLE_HEIGHT: f32 = 26.0;
|
||||||
|
|
||||||
|
/// The default max width of a [`Handle`].
|
||||||
|
pub const DEFAULT_MAX_HANDLE_WIDTH: f32 = 26.0;
|
||||||
|
|
||||||
/// Creates a new [`Slider`].
|
/// Creates a new [`Slider`].
|
||||||
///
|
///
|
||||||
|
|
@ -165,6 +173,8 @@ where
|
||||||
height: Self::DEFAULT_HEIGHT,
|
height: Self::DEFAULT_HEIGHT,
|
||||||
class: Theme::default(),
|
class: Theme::default(),
|
||||||
status: None,
|
status: None,
|
||||||
|
handle_width: Self::DEFAULT_MAX_HANDLE_WIDTH,
|
||||||
|
handle_height: Self::DEFAULT_MAX_HANDLE_HEIGHT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +217,24 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the max width of the [`Handle`].
|
||||||
|
///
|
||||||
|
/// Theme can be used to change the size and shape of handle
|
||||||
|
/// but width of handle defined in theme will not exceed this width
|
||||||
|
pub fn handle_width(mut self, width: impl Into<Pixels>) -> Self {
|
||||||
|
self.handle_width = width.into().0;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the max height of the [`Handle`].
|
||||||
|
///
|
||||||
|
/// Theme can be used to change the size and shape of handle
|
||||||
|
/// but height of handle defined in theme will not exceed this height
|
||||||
|
pub fn handle_height(mut self, height: impl Into<Pixels>) -> Self {
|
||||||
|
self.handle_height = height.into().0;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the step size of the [`Slider`].
|
/// Sets the step size of the [`Slider`].
|
||||||
pub fn step(mut self, step: impl Into<T>) -> Self {
|
pub fn step(mut self, step: impl Into<T>) -> Self {
|
||||||
self.step = step.into();
|
self.step = step.into();
|
||||||
|
|
@ -323,8 +351,15 @@ where
|
||||||
let mut update = || {
|
let mut update = || {
|
||||||
let current_value = self.value;
|
let current_value = self.value;
|
||||||
|
|
||||||
|
self.handle_width = self.handle_width.min(layout.bounds().width);
|
||||||
|
self.handle_height = self.handle_height.min(layout.bounds().height);
|
||||||
|
|
||||||
let locate = |cursor_position: Point| -> Option<T> {
|
let locate = |cursor_position: Point| -> Option<T> {
|
||||||
let bounds = layout.bounds();
|
let bounds = Rectangle {
|
||||||
|
x: layout.bounds().x + self.handle_width / 2.0,
|
||||||
|
width: layout.bounds().width - self.handle_width,
|
||||||
|
..layout.bounds()
|
||||||
|
};
|
||||||
|
|
||||||
if cursor_position.x <= bounds.x {
|
if cursor_position.x <= bounds.x {
|
||||||
Some(*self.range.start())
|
Some(*self.range.start())
|
||||||
|
|
@ -509,54 +544,30 @@ where
|
||||||
_cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
let bounds = layout.bounds();
|
let bounds = Rectangle {
|
||||||
|
x: layout.bounds().x + self.handle_width / 2.0,
|
||||||
|
width: layout.bounds().width - self.handle_width,
|
||||||
|
..layout.bounds()
|
||||||
|
};
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
theme.style(&self.class, self.status.unwrap_or(Status::Active));
|
theme.style(&self.class, self.status.unwrap_or(Status::Active));
|
||||||
|
|
||||||
let border_width = style
|
let (mut handle_width, mut handle_height, handle_border_radius) =
|
||||||
.handle
|
|
||||||
.border_width
|
|
||||||
.min(bounds.height / 2.0)
|
|
||||||
.min(bounds.width / 2.0);
|
|
||||||
|
|
||||||
let (handle_width, handle_height, handle_border_radius) =
|
|
||||||
match style.handle.shape {
|
match style.handle.shape {
|
||||||
HandleShape::Circle { radius } => {
|
HandleShape::Circle { radius } => {
|
||||||
let radius = (radius)
|
|
||||||
.max(2.0 * border_width)
|
|
||||||
.min(bounds.height / 2.0)
|
|
||||||
.min(bounds.width / 2.0 + 2.0 * border_width);
|
|
||||||
(radius * 2.0, radius * 2.0, Radius::from(radius))
|
(radius * 2.0, radius * 2.0, Radius::from(radius))
|
||||||
}
|
}
|
||||||
HandleShape::Rectangle {
|
HandleShape::Rectangle {
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
border_radius,
|
border_radius,
|
||||||
} => {
|
} => (f32::from(width), f32::from(height), border_radius),
|
||||||
let width = (f32::from(width)).max(2.0 * border_width);
|
|
||||||
let height = (f32::from(height)).max(2.0 * border_width);
|
|
||||||
let mut border_radius: [f32; 4] = border_radius.into();
|
|
||||||
for r in &mut border_radius {
|
|
||||||
*r = (*r)
|
|
||||||
.min(height / 2.0)
|
|
||||||
.min(width / 2.0)
|
|
||||||
.max(*r * (width + border_width * 2.0) / width);
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
Radius {
|
|
||||||
top_left: border_radius[0],
|
|
||||||
top_right: border_radius[1],
|
|
||||||
bottom_right: border_radius[2],
|
|
||||||
bottom_left: border_radius[3],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handle_width = handle_width.min(self.handle_width);
|
||||||
|
handle_height = handle_height.min(self.handle_height);
|
||||||
|
|
||||||
let value = self.value.into() as f32;
|
let value = self.value.into() as f32;
|
||||||
let (range_start, range_end) = {
|
let (range_start, range_end) = {
|
||||||
let (start, end) = self.range.clone().into_inner();
|
let (start, end) = self.range.clone().into_inner();
|
||||||
|
|
@ -567,8 +578,7 @@ where
|
||||||
let offset = if range_start >= range_end {
|
let offset = if range_start >= range_end {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
(bounds.width - handle_width) * (value - range_start)
|
bounds.width * (value - range_start) / (range_end - range_start)
|
||||||
/ (range_end - range_start)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let rail_y = bounds.y + bounds.height / 2.0;
|
let rail_y = bounds.y + bounds.height / 2.0;
|
||||||
|
|
@ -590,7 +600,8 @@ where
|
||||||
x: bounds.x + offset,
|
x: bounds.x + offset,
|
||||||
y: rail_y + 6.0,
|
y: rail_y + 6.0,
|
||||||
width: BREAKPOINT_WIDTH,
|
width: BREAKPOINT_WIDTH,
|
||||||
height: 8.0,
|
height: 8.0_f32
|
||||||
|
.min(bounds.y + bounds.height - (rail_y + 6.0)),
|
||||||
},
|
},
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
|
|
@ -607,9 +618,9 @@ where
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x,
|
x: bounds.x,
|
||||||
y: rail_y - style.rail.width / 2.0,
|
y: rail_y - (style.rail.width).min(bounds.height) / 2.0,
|
||||||
width: offset + handle_width / 2.0,
|
width: offset,
|
||||||
height: style.rail.width,
|
height: (style.rail.width).min(bounds.height),
|
||||||
},
|
},
|
||||||
border: style.rail.border,
|
border: style.rail.border,
|
||||||
..renderer::Quad::default()
|
..renderer::Quad::default()
|
||||||
|
|
@ -622,10 +633,10 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x + offset + handle_width / 2.0,
|
x: bounds.x + offset,
|
||||||
y: rail_y - style.rail.width / 2.0,
|
y: rail_y - (style.rail.width).min(bounds.height) / 2.0,
|
||||||
width: bounds.width - offset - handle_width / 2.0,
|
width: bounds.width - offset,
|
||||||
height: style.rail.width,
|
height: (style.rail.width).min(bounds.height),
|
||||||
},
|
},
|
||||||
border: style.rail.border,
|
border: style.rail.border,
|
||||||
..renderer::Quad::default()
|
..renderer::Quad::default()
|
||||||
|
|
@ -637,7 +648,7 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x + offset,
|
x: bounds.x + offset - handle_width / 2.0,
|
||||||
y: rail_y - (handle_height / 2.0),
|
y: rail_y - (handle_height / 2.0),
|
||||||
width: handle_width,
|
width: handle_width,
|
||||||
height: handle_height,
|
height: handle_height,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue