[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, *};
|
use crate::{layout::Direction, *};
|
||||||
|
|
||||||
pub mod color_picker;
|
pub mod color_picker;
|
||||||
|
mod drag_value;
|
||||||
mod image;
|
mod image;
|
||||||
mod slider;
|
mod slider;
|
||||||
pub(crate) mod text_edit;
|
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 paint::*;
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Anything implementing Widget can be added to a Ui with `Ui::add`
|
/// Anything implementing Widget can be added to a Ui with `Ui::add`
|
||||||
|
@ -529,162 +528,3 @@ impl Widget for Separator {
|
||||||
ui.interact_hover(rect)
|
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