[refactor] move DragValue to own file drag_value.rs
This commit is contained in:
parent
dd8c298cb5
commit
05316b7045
2 changed files with 162 additions and 162 deletions
160
egui/src/widgets/drag_value.rs
Normal file
160
egui/src/widgets/drag_value.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use std::ops::RangeInclusive;
|
||||
|
||||
use crate::{paint::*, *};
|
||||
|
||||
/// Combined into one function (rather than two) to make it easier
|
||||
/// for the borrow checker.
|
||||
type GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;
|
||||
|
||||
fn get(value_function: &mut GetSetValue<'_>) -> f64 {
|
||||
(value_function)(None)
|
||||
}
|
||||
|
||||
fn set(value_function: &mut GetSetValue<'_>, value: f64) {
|
||||
(value_function)(Some(value));
|
||||
}
|
||||
|
||||
/// A floating point value that you can change by dragging the number. More compact than a slider.
|
||||
pub struct DragValue<'a> {
|
||||
value_function: GetSetValue<'a>,
|
||||
speed: f32,
|
||||
prefix: String,
|
||||
suffix: String,
|
||||
range: RangeInclusive<f64>,
|
||||
}
|
||||
|
||||
impl<'a> DragValue<'a> {
|
||||
fn from_get_set(value_function: impl 'a + FnMut(Option<f64>) -> f64) -> Self {
|
||||
Self {
|
||||
value_function: Box::new(value_function),
|
||||
speed: 1.0,
|
||||
prefix: Default::default(),
|
||||
suffix: Default::default(),
|
||||
range: f64::NEG_INFINITY..=f64::INFINITY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f32(value: &'a mut f32) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v as f32
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn u8(value: &'a mut u8) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v.round() as u8;
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn i32(value: &'a mut i32) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v.round() as i32;
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// How much the value changes when dragged one point (logical pixel).
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
|
||||
/// Clamp the value to this range
|
||||
pub fn range(mut self, range: RangeInclusive<f64>) -> Self {
|
||||
self.range = range;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show a prefix before the number, e.g. "x: "
|
||||
pub fn prefix(mut self, prefix: impl ToString) -> Self {
|
||||
self.prefix = prefix.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a suffix to the number, this can be e.g. a unit ("°" or " m")
|
||||
pub fn suffix(mut self, suffix: impl ToString) -> Self {
|
||||
self.suffix = suffix.to_string();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for DragValue<'a> {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let Self {
|
||||
mut value_function,
|
||||
speed,
|
||||
range,
|
||||
prefix,
|
||||
suffix,
|
||||
} = self;
|
||||
let value = get(&mut value_function);
|
||||
let aim_rad = ui.input().physical_pixel_size(); // ui.input().aim_radius(); // TODO
|
||||
let precision = (aim_rad / speed.abs()).log10().ceil().at_least(0.0) as usize;
|
||||
let value_text = format_with_minimum_precision(value as f32, precision); // TODO: full precision
|
||||
|
||||
let kb_edit_id = ui.make_position_id().with("edit");
|
||||
let is_kb_editing = ui.memory().has_kb_focus(kb_edit_id);
|
||||
|
||||
if is_kb_editing {
|
||||
let mut value_text = ui
|
||||
.memory()
|
||||
.temp_edit_string
|
||||
.take()
|
||||
.unwrap_or_else(|| value_text);
|
||||
let response = ui.add(
|
||||
TextEdit::new(&mut value_text)
|
||||
.id(kb_edit_id)
|
||||
.multiline(false)
|
||||
.desired_width(0.0)
|
||||
.text_style(TextStyle::Monospace),
|
||||
);
|
||||
if let Ok(parsed_value) = value_text.parse() {
|
||||
let parsed_value = clamp(parsed_value, range);
|
||||
set(&mut value_function, parsed_value)
|
||||
}
|
||||
if ui.input().key_pressed(Key::Enter) {
|
||||
ui.memory().surrender_kb_focus(kb_edit_id);
|
||||
} else {
|
||||
ui.memory().temp_edit_string = Some(value_text);
|
||||
}
|
||||
response
|
||||
} else {
|
||||
let button = Button::new(format!("{}{}{}", prefix, value_text, suffix))
|
||||
.sense(Sense::click_and_drag())
|
||||
.text_style(TextStyle::Monospace);
|
||||
let response = ui.add(button);
|
||||
// response.tooltip_text("Drag to edit, click to enter a value"); // TODO: may clash with users own tooltips
|
||||
if response.clicked {
|
||||
ui.memory().request_kb_focus(kb_edit_id);
|
||||
ui.memory().temp_edit_string = None; // Filled in next frame
|
||||
} else if response.active {
|
||||
let mdelta = ui.input().mouse.delta;
|
||||
let delta_points = mdelta.x - mdelta.y; // Increase to the right and up
|
||||
let delta_value = speed * delta_points;
|
||||
if delta_value != 0.0 {
|
||||
let new_value = value + delta_value as f64;
|
||||
let new_value = round_to_precision(new_value, precision);
|
||||
let new_value = clamp(new_value, range);
|
||||
set(&mut value_function, new_value);
|
||||
// TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere,
|
||||
// otherwise we will just keep rounding to the same value while moving the mouse.
|
||||
}
|
||||
}
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,16 +9,15 @@
|
|||
use crate::{layout::Direction, *};
|
||||
|
||||
pub mod color_picker;
|
||||
mod drag_value;
|
||||
mod image;
|
||||
mod slider;
|
||||
pub(crate) mod text_edit;
|
||||
|
||||
pub use {image::Image, slider::*, text_edit::*};
|
||||
pub use {drag_value::DragValue, image::Image, slider::*, text_edit::*};
|
||||
|
||||
use paint::*;
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Anything implementing Widget can be added to a Ui with `Ui::add`
|
||||
|
@ -529,162 +528,3 @@ impl Widget for Separator {
|
|||
ui.interact_hover(rect)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Combined into one function (rather than two) to make it easier
|
||||
/// for the borrow checker.
|
||||
type GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;
|
||||
|
||||
fn get(value_function: &mut GetSetValue<'_>) -> f64 {
|
||||
(value_function)(None)
|
||||
}
|
||||
|
||||
fn set(value_function: &mut GetSetValue<'_>, value: f64) {
|
||||
(value_function)(Some(value));
|
||||
}
|
||||
|
||||
/// A floating point value that you can change by dragging the number. More compact than a slider.
|
||||
pub struct DragValue<'a> {
|
||||
value_function: GetSetValue<'a>,
|
||||
speed: f32,
|
||||
prefix: String,
|
||||
suffix: String,
|
||||
range: RangeInclusive<f64>,
|
||||
}
|
||||
|
||||
impl<'a> DragValue<'a> {
|
||||
fn from_get_set(value_function: impl 'a + FnMut(Option<f64>) -> f64) -> Self {
|
||||
Self {
|
||||
value_function: Box::new(value_function),
|
||||
speed: 1.0,
|
||||
prefix: Default::default(),
|
||||
suffix: Default::default(),
|
||||
range: f64::NEG_INFINITY..=f64::INFINITY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f32(value: &'a mut f32) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v as f32
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn u8(value: &'a mut u8) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v.round() as u8;
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn i32(value: &'a mut i32) -> Self {
|
||||
Self {
|
||||
..Self::from_get_set(move |v: Option<f64>| {
|
||||
if let Some(v) = v {
|
||||
*value = v.round() as i32;
|
||||
}
|
||||
*value as f64
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// How much the value changes when dragged one point (logical pixel).
|
||||
pub fn speed(mut self, speed: f32) -> Self {
|
||||
self.speed = speed;
|
||||
self
|
||||
}
|
||||
|
||||
/// Clamp the value to this range
|
||||
pub fn range(mut self, range: RangeInclusive<f64>) -> Self {
|
||||
self.range = range;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show a prefix before the number, e.g. "x: "
|
||||
pub fn prefix(mut self, prefix: impl ToString) -> Self {
|
||||
self.prefix = prefix.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a suffix to the number, this can be e.g. a unit ("°" or " m")
|
||||
pub fn suffix(mut self, suffix: impl ToString) -> Self {
|
||||
self.suffix = suffix.to_string();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for DragValue<'a> {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let Self {
|
||||
mut value_function,
|
||||
speed,
|
||||
range,
|
||||
prefix,
|
||||
suffix,
|
||||
} = self;
|
||||
let value = get(&mut value_function);
|
||||
let aim_rad = ui.input().physical_pixel_size(); // ui.input().aim_radius(); // TODO
|
||||
let precision = (aim_rad / speed.abs()).log10().ceil().at_least(0.0) as usize;
|
||||
let value_text = format_with_minimum_precision(value as f32, precision); // TODO: full precision
|
||||
|
||||
let kb_edit_id = ui.make_position_id().with("edit");
|
||||
let is_kb_editing = ui.memory().has_kb_focus(kb_edit_id);
|
||||
|
||||
if is_kb_editing {
|
||||
let mut value_text = ui
|
||||
.memory()
|
||||
.temp_edit_string
|
||||
.take()
|
||||
.unwrap_or_else(|| value_text);
|
||||
let response = ui.add(
|
||||
TextEdit::new(&mut value_text)
|
||||
.id(kb_edit_id)
|
||||
.multiline(false)
|
||||
.desired_width(0.0)
|
||||
.text_style(TextStyle::Monospace),
|
||||
);
|
||||
if let Ok(parsed_value) = value_text.parse() {
|
||||
let parsed_value = clamp(parsed_value, range);
|
||||
set(&mut value_function, parsed_value)
|
||||
}
|
||||
if ui.input().key_pressed(Key::Enter) {
|
||||
ui.memory().surrender_kb_focus(kb_edit_id);
|
||||
} else {
|
||||
ui.memory().temp_edit_string = Some(value_text);
|
||||
}
|
||||
response
|
||||
} else {
|
||||
let button = Button::new(format!("{}{}{}", prefix, value_text, suffix))
|
||||
.sense(Sense::click_and_drag())
|
||||
.text_style(TextStyle::Monospace);
|
||||
let response = ui.add(button);
|
||||
// response.tooltip_text("Drag to edit, click to enter a value"); // TODO: may clash with users own tooltips
|
||||
if response.clicked {
|
||||
ui.memory().request_kb_focus(kb_edit_id);
|
||||
ui.memory().temp_edit_string = None; // Filled in next frame
|
||||
} else if response.active {
|
||||
let mdelta = ui.input().mouse.delta;
|
||||
let delta_points = mdelta.x - mdelta.y; // Increase to the right and up
|
||||
let delta_value = speed * delta_points;
|
||||
if delta_value != 0.0 {
|
||||
let new_value = value + delta_value as f64;
|
||||
let new_value = round_to_precision(new_value, precision);
|
||||
let new_value = clamp(new_value, range);
|
||||
set(&mut value_function, new_value);
|
||||
// TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere,
|
||||
// otherwise we will just keep rounding to the same value while moving the mouse.
|
||||
}
|
||||
}
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue