Allow changing handle shape of a slider (#3429)
* Closes #1974 <!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to add commits to your PR. * Remember to run `cargo fmt` and `cargo cranky`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review you PR, but my time is limited! --> --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
f20b7b43bf
commit
49eecc4287
|
|
@ -815,6 +815,11 @@ pub struct Visuals {
|
|||
/// Enabling this will affect ALL sliders, and can be enabled/disabled per slider with [`Slider::trailing_fill`].
|
||||
pub slider_trailing_fill: bool,
|
||||
|
||||
/// Shape of the handle for sliders and similar widgets.
|
||||
///
|
||||
/// Changing this will affect ALL sliders, and can be enabled/disabled per slider with [`Slider::handle_shape`].
|
||||
pub handle_shape: HandleShape,
|
||||
|
||||
/// Should the cursor change when the user hovers over an interactive/clickable item?
|
||||
///
|
||||
/// This is consistent with a lot of browser-based applications (vscode, github
|
||||
|
|
@ -880,6 +885,20 @@ pub struct Selection {
|
|||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
/// Shape of the handle for sliders and similar widgets.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum HandleShape {
|
||||
/// Circular handle
|
||||
Circle,
|
||||
|
||||
/// Rectangular handle
|
||||
Rect {
|
||||
/// Aspect ratio of the rectangle. Set to < 1.0 to make it narrower.
|
||||
aspect_ratio: f32,
|
||||
},
|
||||
}
|
||||
|
||||
/// The visuals of widgets for different states of interaction.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
|
|
@ -1125,6 +1144,7 @@ impl Visuals {
|
|||
striped: false,
|
||||
|
||||
slider_trailing_fill: false,
|
||||
handle_shape: HandleShape::Circle,
|
||||
|
||||
interact_cursor: None,
|
||||
|
||||
|
|
@ -1687,6 +1707,7 @@ impl Visuals {
|
|||
striped,
|
||||
|
||||
slider_trailing_fill,
|
||||
handle_shape,
|
||||
interact_cursor,
|
||||
|
||||
image_loading_spinners,
|
||||
|
|
@ -1755,6 +1776,8 @@ impl Visuals {
|
|||
|
||||
ui.checkbox(slider_trailing_fill, "Add trailing color to sliders");
|
||||
|
||||
handle_shape.ui(ui);
|
||||
|
||||
ComboBox::from_label("Interact Cursor")
|
||||
.selected_text(format!("{interact_cursor:?}"))
|
||||
.show_ui(ui, |ui| {
|
||||
|
|
@ -1877,3 +1900,21 @@ fn rounding_ui(ui: &mut Ui, rounding: &mut Rounding) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl HandleShape {
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.label("Widget handle shape");
|
||||
ui.horizontal(|ui| {
|
||||
ui.radio_value(self, HandleShape::Circle, "Circle");
|
||||
if ui
|
||||
.radio(matches!(self, HandleShape::Rect { .. }), "Rectangle")
|
||||
.clicked()
|
||||
{
|
||||
*self = HandleShape::Rect { aspect_ratio: 0.5 };
|
||||
}
|
||||
if let HandleShape::Rect { aspect_ratio } = self {
|
||||
ui.add(Slider::new(aspect_ratio, 0.1..=3.0).text("Aspect ratio"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use crate::*;
|
||||
use crate::{style::HandleShape, *};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -87,6 +87,7 @@ pub struct Slider<'a> {
|
|||
custom_formatter: Option<NumFormatter<'a>>,
|
||||
custom_parser: Option<NumParser<'a>>,
|
||||
trailing_fill: Option<bool>,
|
||||
handle_shape: Option<HandleShape>,
|
||||
}
|
||||
|
||||
impl<'a> Slider<'a> {
|
||||
|
|
@ -133,6 +134,7 @@ impl<'a> Slider<'a> {
|
|||
custom_formatter: None,
|
||||
custom_parser: None,
|
||||
trailing_fill: None,
|
||||
handle_shape: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -302,6 +304,16 @@ impl<'a> Slider<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Change the shape of the slider handle
|
||||
///
|
||||
/// This setting can be enabled globally for all sliders with [`Visuals::handle_shape`].
|
||||
/// Changing it here will override the above setting ONLY for this individual slider.
|
||||
#[inline]
|
||||
pub fn handle_shape(mut self, handle_shape: HandleShape) -> Self {
|
||||
self.handle_shape = Some(handle_shape);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set custom formatter defining how numbers are converted into text.
|
||||
///
|
||||
/// A custom formatter takes a `f64` for the numeric value and a `RangeInclusive<usize>` representing
|
||||
|
|
@ -570,7 +582,10 @@ impl<'a> Slider<'a> {
|
|||
/// Just the slider, no text
|
||||
fn slider_ui(&mut self, ui: &Ui, response: &Response) {
|
||||
let rect = &response.rect;
|
||||
let position_range = self.position_range(rect);
|
||||
let handle_shape = self
|
||||
.handle_shape
|
||||
.unwrap_or_else(|| ui.style().visuals.handle_shape);
|
||||
let position_range = self.position_range(rect, &handle_shape);
|
||||
|
||||
if let Some(pointer_position_2d) = response.interact_pointer_pos() {
|
||||
let position = self.pointer_position(pointer_position_2d);
|
||||
|
|
@ -696,12 +711,37 @@ impl<'a> Slider<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
ui.painter().add(epaint::CircleShape {
|
||||
center,
|
||||
radius: self.handle_radius(rect) + visuals.expansion,
|
||||
fill: visuals.bg_fill,
|
||||
stroke: visuals.fg_stroke,
|
||||
});
|
||||
let radius = self.handle_radius(rect);
|
||||
|
||||
let handle_shape = self
|
||||
.handle_shape
|
||||
.unwrap_or_else(|| ui.style().visuals.handle_shape);
|
||||
match handle_shape {
|
||||
style::HandleShape::Circle => {
|
||||
ui.painter().add(epaint::CircleShape {
|
||||
center,
|
||||
radius: radius + visuals.expansion,
|
||||
fill: visuals.bg_fill,
|
||||
stroke: visuals.fg_stroke,
|
||||
});
|
||||
}
|
||||
style::HandleShape::Rect { aspect_ratio } => {
|
||||
let v = match self.orientation {
|
||||
SliderOrientation::Horizontal => Vec2::new(radius * aspect_ratio, radius),
|
||||
SliderOrientation::Vertical => Vec2::new(radius, radius * aspect_ratio),
|
||||
};
|
||||
let v = v + Vec2::splat(visuals.expansion);
|
||||
let rect = Rect::from_center_size(center, 2.0 * v);
|
||||
ui.painter().add(epaint::RectShape {
|
||||
fill: visuals.bg_fill,
|
||||
stroke: visuals.fg_stroke,
|
||||
rect,
|
||||
rounding: visuals.rounding,
|
||||
fill_texture_id: Default::default(),
|
||||
uv: Rect::ZERO,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -719,8 +759,12 @@ impl<'a> Slider<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn position_range(&self, rect: &Rect) -> Rangef {
|
||||
fn position_range(&self, rect: &Rect, handle_shape: &style::HandleShape) -> Rangef {
|
||||
let handle_radius = self.handle_radius(rect);
|
||||
let handle_radius = match handle_shape {
|
||||
style::HandleShape::Circle => handle_radius,
|
||||
style::HandleShape::Rect { aspect_ratio } => handle_radius * aspect_ratio,
|
||||
};
|
||||
match self.orientation {
|
||||
SliderOrientation::Horizontal => rect.x_range().shrink(handle_radius),
|
||||
// The vertical case has to be flipped because the largest slider value maps to the
|
||||
|
|
@ -842,7 +886,10 @@ impl<'a> Slider<'a> {
|
|||
let slider_response = response.clone();
|
||||
|
||||
let value_response = if self.show_value {
|
||||
let position_range = self.position_range(&response.rect);
|
||||
let handle_shape = self
|
||||
.handle_shape
|
||||
.unwrap_or_else(|| ui.style().visuals.handle_shape);
|
||||
let position_range = self.position_range(&response.rect, &handle_shape);
|
||||
let value_response = self.value_ui(ui, position_range);
|
||||
if value_response.gained_focus()
|
||||
|| value_response.has_focus()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use egui::*;
|
||||
use egui::{style::HandleShape, *};
|
||||
use std::f64::INFINITY;
|
||||
|
||||
/// Showcase sliders
|
||||
|
|
@ -17,6 +17,7 @@ pub struct Sliders {
|
|||
pub vertical: bool,
|
||||
pub value: f64,
|
||||
pub trailing_fill: bool,
|
||||
pub handle_shape: HandleShape,
|
||||
}
|
||||
|
||||
impl Default for Sliders {
|
||||
|
|
@ -33,6 +34,7 @@ impl Default for Sliders {
|
|||
vertical: false,
|
||||
value: 10.0,
|
||||
trailing_fill: false,
|
||||
handle_shape: HandleShape::Circle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +69,7 @@ impl super::View for Sliders {
|
|||
vertical,
|
||||
value,
|
||||
trailing_fill,
|
||||
handle_shape,
|
||||
} = self;
|
||||
|
||||
ui.label("You can click a slider value to edit it with the keyboard.");
|
||||
|
|
@ -99,7 +102,8 @@ impl super::View for Sliders {
|
|||
.orientation(orientation)
|
||||
.text("i32 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
.trailing_fill(*trailing_fill)
|
||||
.handle_shape(*handle_shape),
|
||||
);
|
||||
*value = value_i32 as f64;
|
||||
} else {
|
||||
|
|
@ -111,7 +115,8 @@ impl super::View for Sliders {
|
|||
.orientation(orientation)
|
||||
.text("f64 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
.trailing_fill(*trailing_fill)
|
||||
.handle_shape(*handle_shape),
|
||||
);
|
||||
|
||||
ui.label(
|
||||
|
|
@ -132,20 +137,26 @@ impl super::View for Sliders {
|
|||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("left")
|
||||
.trailing_fill(*trailing_fill),
|
||||
.trailing_fill(*trailing_fill)
|
||||
.handle_shape(*handle_shape),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(max, type_min..=type_max)
|
||||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("right")
|
||||
.trailing_fill(*trailing_fill),
|
||||
.trailing_fill(*trailing_fill)
|
||||
.handle_shape(*handle_shape),
|
||||
);
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(trailing_fill, "Toggle trailing color");
|
||||
ui.label("When enabled, trailing color will be painted up until the circle.");
|
||||
ui.label("When enabled, trailing color will be painted up until the handle.");
|
||||
|
||||
ui.separator();
|
||||
|
||||
handle_shape.ui(ui);
|
||||
|
||||
ui.separator();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue