mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
add ImageSlider widget
Former-commit-id: 218eb14090365f9ea3c6fb956fcdb989d51aaea2
This commit is contained in:
parent
d7266c286c
commit
ca60b86f53
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2368,6 +2368,7 @@ dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"msgbox 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"portpicker 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -47,3 +47,4 @@ simplelog = "0.5"
|
||||
msgbox = "0.1"
|
||||
directories = "1.0"
|
||||
portpicker = "0.1"
|
||||
num = "0.2"
|
||||
|
@ -12,7 +12,7 @@ mod font_ids;
|
||||
pub use event::Event;
|
||||
pub use graphic::Graphic;
|
||||
pub use scale::ScaleMode;
|
||||
pub use widgets::toggle_button::ToggleButton;
|
||||
pub use widgets::{image_slider::ImageSlider, toggle_button::ToggleButton};
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
|
322
voxygen/src/ui/widgets/image_slider.rs
Normal file
322
voxygen/src/ui/widgets/image_slider.rs
Normal file
@ -0,0 +1,322 @@
|
||||
//! A widget for selecting a single value along some linear range.
|
||||
use conrod_core::{
|
||||
builder_methods, image,
|
||||
position::Range,
|
||||
utils,
|
||||
widget::{self, Image},
|
||||
widget_ids, Color, Colorable, Positionable, Rect, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
use num::{Float, Integer, Num, NumCast};
|
||||
|
||||
pub enum Discrete {}
|
||||
pub enum Continuous {}
|
||||
|
||||
pub trait ValueFromPercent<T> {
|
||||
fn value_from_percent(percent: f32, min: T, max: T) -> T;
|
||||
}
|
||||
|
||||
/// Linear value selection.
|
||||
///
|
||||
/// If the slider's width is greater than it's height, it will automatically become a horizontal
|
||||
/// slider, otherwise it will be a vertical slider.
|
||||
///
|
||||
/// Its reaction is triggered if the value is updated or if the mouse button is released while
|
||||
/// the cursor is above the rectangle.
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct ImageSlider<T, K> {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
value: T,
|
||||
min: T,
|
||||
max: T,
|
||||
/// The amount in which the slider's display should be skewed.
|
||||
///
|
||||
/// Higher skew amounts (above 1.0) will weight lower values.
|
||||
///
|
||||
/// Lower skew amounts (below 1.0) will weight heigher values.
|
||||
///
|
||||
/// All skew amounts should be greater than 0.0.
|
||||
skew: f32,
|
||||
track: Track,
|
||||
slider: Slider,
|
||||
kind: std::marker::PhantomData<K>,
|
||||
}
|
||||
|
||||
struct Track {
|
||||
image_id: image::Id,
|
||||
color: Option<Color>,
|
||||
src_rect: Option<Rect>,
|
||||
breadth: Option<f32>,
|
||||
// Padding on the ends of the track constraining the slider to a smaller area
|
||||
padding: (f32, f32),
|
||||
}
|
||||
|
||||
struct Slider {
|
||||
image_id: image::Id,
|
||||
hover_image_id: Option<image::Id>,
|
||||
press_image_id: Option<image::Id>,
|
||||
color: Option<Color>,
|
||||
src_rect: Option<Rect>,
|
||||
length: Option<f32>,
|
||||
}
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
track,
|
||||
slider,
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the state of the Slider widget.
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
impl<T, K> ImageSlider<T, K> {
|
||||
fn new(
|
||||
value: T,
|
||||
min: T,
|
||||
max: T,
|
||||
slider_image_id: image::Id,
|
||||
track_image_id: image::Id,
|
||||
) -> Self {
|
||||
Self {
|
||||
common: widget::CommonBuilder::default(),
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
skew: 1.0,
|
||||
track: Track {
|
||||
image_id: track_image_id,
|
||||
color: None,
|
||||
src_rect: None,
|
||||
breadth: None,
|
||||
padding: (0.0, 0.0),
|
||||
},
|
||||
slider: Slider {
|
||||
image_id: slider_image_id,
|
||||
hover_image_id: None,
|
||||
press_image_id: None,
|
||||
color: None,
|
||||
src_rect: None,
|
||||
length: None,
|
||||
},
|
||||
kind: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
builder_methods! {
|
||||
pub skew { skew = f32 }
|
||||
pub pad_track { track.padding = (f32, f32) }
|
||||
pub hover_image { slider.hover_image_id = Some(image::Id) }
|
||||
pub press_image { slider.press_image_id = Some(image::Id) }
|
||||
pub track_breadth { track.breadth = Some(f32) }
|
||||
pub slider_length { slider.length = Some(f32) }
|
||||
pub track_color { track.color = Some(Color) }
|
||||
pub slider_color { slider.color = Some(Color) }
|
||||
pub track_src_rect { track.src_rect = Some(Rect) }
|
||||
pub slider_src_rect { slider.src_rect = Some(Rect) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ImageSlider<T, Continuous> {
|
||||
pub fn continuous(
|
||||
value: T,
|
||||
min: T,
|
||||
max: T,
|
||||
slider_image_id: image::Id,
|
||||
track_image_id: image::Id,
|
||||
) -> Self {
|
||||
ImageSlider::new(value, min, max, slider_image_id, track_image_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ImageSlider<T, Discrete> {
|
||||
pub fn discrete(
|
||||
value: T,
|
||||
min: T,
|
||||
max: T,
|
||||
slider_image_id: image::Id,
|
||||
track_image_id: image::Id,
|
||||
) -> Self {
|
||||
ImageSlider::new(value, min, max, slider_image_id, track_image_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float> ValueFromPercent<T> for Continuous {
|
||||
fn value_from_percent(percent: f32, min: T, max: T) -> T {
|
||||
utils::value_from_perc(percent, min, max)
|
||||
}
|
||||
}
|
||||
impl<T: Integer + NumCast> ValueFromPercent<T> for Discrete {
|
||||
fn value_from_percent(percent: f32, min: T, max: T) -> T {
|
||||
NumCast::from(
|
||||
utils::value_from_perc(percent, min.to_f32().unwrap(), max.to_f32().unwrap()).round(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K> Widget for ImageSlider<T, K>
|
||||
where
|
||||
T: NumCast + Num + Copy + PartialOrd,
|
||||
K: ValueFromPercent<T>,
|
||||
{
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<T>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
/// Update the state of the Slider.
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs {
|
||||
id,
|
||||
state,
|
||||
rect,
|
||||
ui,
|
||||
..
|
||||
} = args;
|
||||
let ImageSlider {
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
skew,
|
||||
track,
|
||||
slider,
|
||||
..
|
||||
} = self;
|
||||
let (start_pad, end_pad) = (track.padding.0 as f64, track.padding.1 as f64);
|
||||
|
||||
let is_horizontal = rect.w() > rect.h();
|
||||
|
||||
let new_value = if let Some(mouse) = ui.widget_input(id).mouse() {
|
||||
if mouse.buttons.left().is_down() {
|
||||
let mouse_abs_xy = mouse.abs_xy();
|
||||
let (mouse_offset, track_length) = if is_horizontal {
|
||||
// Horizontal.
|
||||
(
|
||||
mouse_abs_xy[0] - rect.x.start - start_pad,
|
||||
rect.w() - start_pad - end_pad,
|
||||
)
|
||||
} else {
|
||||
// Vertical.
|
||||
(
|
||||
mouse_abs_xy[1] - rect.y.start - start_pad,
|
||||
rect.h() - start_pad - end_pad,
|
||||
)
|
||||
};
|
||||
let perc = utils::clamp(mouse_offset, 0.0, track_length) / track_length;
|
||||
let skewed_perc = (perc).powf(skew as f64);
|
||||
K::value_from_percent(skewed_perc as f32, min, max)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
// Track
|
||||
let track_rect = if is_horizontal {
|
||||
let h = slider.length.map_or(rect.h() / 3.0, |h| h as f64);
|
||||
Rect {
|
||||
y: Range::from_pos_and_len(rect.y(), h),
|
||||
..rect
|
||||
}
|
||||
} else {
|
||||
let w = slider.length.map_or(rect.w() / 3.0, |w| w as f64);
|
||||
Rect {
|
||||
x: Range::from_pos_and_len(rect.x(), w),
|
||||
..rect
|
||||
}
|
||||
};
|
||||
|
||||
let (x, y, w, h) = track_rect.x_y_w_h();
|
||||
Image::new(track.image_id)
|
||||
.x_y(x, y)
|
||||
.w_h(w, h)
|
||||
.parent(id)
|
||||
.graphics_for(id)
|
||||
.color(track.color)
|
||||
.set(state.ids.track, ui);
|
||||
|
||||
// Slider
|
||||
let slider_image = ui
|
||||
.widget_input(id)
|
||||
.mouse()
|
||||
.map(|mouse| {
|
||||
if mouse.buttons.left().is_down() {
|
||||
slider
|
||||
.press_image_id
|
||||
.or(slider.hover_image_id)
|
||||
.unwrap_or(slider.image_id)
|
||||
} else {
|
||||
slider.hover_image_id.unwrap_or(slider.image_id)
|
||||
}
|
||||
})
|
||||
.unwrap_or(slider.image_id);
|
||||
|
||||
// The rectangle for positioning and sizing the slider.
|
||||
let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0);
|
||||
let unskewed_perc = value_perc.powf(1.0 / skew as f64);
|
||||
let slider_rect = if is_horizontal {
|
||||
let pos = utils::map_range(
|
||||
unskewed_perc,
|
||||
0.0,
|
||||
1.0,
|
||||
rect.x.start + start_pad,
|
||||
rect.x.end - end_pad,
|
||||
);
|
||||
let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64);
|
||||
Rect {
|
||||
x: Range::from_pos_and_len(pos, w),
|
||||
..rect
|
||||
}
|
||||
} else {
|
||||
let pos = utils::map_range(
|
||||
unskewed_perc,
|
||||
0.0,
|
||||
1.0,
|
||||
rect.y.start + start_pad,
|
||||
rect.y.end - end_pad,
|
||||
);
|
||||
let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64);
|
||||
Rect {
|
||||
y: Range::from_pos_and_len(pos, h),
|
||||
..rect
|
||||
}
|
||||
};
|
||||
|
||||
let (x, y, w, h) = slider_rect.x_y_w_h();
|
||||
Image::new(slider_image)
|
||||
.x_y(x, y)
|
||||
.w_h(w, h)
|
||||
.parent(id)
|
||||
.graphics_for(id)
|
||||
.color(slider.color)
|
||||
.set(state.ids.slider, ui);
|
||||
|
||||
// If the value has just changed, return the new value.
|
||||
if value != new_value {
|
||||
Some(new_value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K> Colorable for ImageSlider<T, K> {
|
||||
fn color(mut self, color: Color) -> Self {
|
||||
self.slider.color = Some(color);
|
||||
self.track.color = Some(color);
|
||||
self
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod image_slider;
|
||||
pub mod toggle_button;
|
||||
|
Loading…
Reference in New Issue
Block a user