DragValue: handle slowly dragging a value with limited precision

This commit is contained in:
Emil Ernerfeldt 2021-02-20 17:27:55 +01:00
parent 9a546ff97a
commit 7ac26b84b1
4 changed files with 37 additions and 10 deletions

View file

@ -533,7 +533,8 @@ impl Context {
self.request_repaint(); self.request_repaint();
} }
self.memory().end_frame(&self.frame_state().used_ids); self.memory()
.end_frame(&self.input, &self.frame_state().used_ids);
let mut output: Output = std::mem::take(&mut self.output()); let mut output: Output = std::mem::take(&mut self.output());
if self.repaint_requests.load(SeqCst) > 0 { if self.repaint_requests.load(SeqCst) > 0 {

View file

@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use crate::{ use crate::{
area, collapsing_header, menu, resize, scroll_area, util::Cache, widgets::text_edit, window, area, collapsing_header, menu, resize, scroll_area, util::Cache, widgets::text_edit, window,
Id, LayerId, Pos2, Rect, Style, Vec2, Id, InputState, LayerId, Pos2, Rect, Style, Vec2,
}; };
use epaint::color::{Color32, Hsva}; use epaint::color::{Color32, Hsva};
@ -35,11 +35,14 @@ pub struct Memory {
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) window_interaction: Option<window::WindowInteraction>, pub(crate) window_interaction: Option<window::WindowInteraction>,
/// For temporary edit of e.g. a slider value. /// For temporary edit of e.g. a `DragValue` value.
/// Couples with [`Interaction::kb_focus_id`]. /// Couples with [`Interaction::kb_focus_id`].
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) temp_edit_string: Option<String>, pub(crate) temp_edit_string: Option<String>,
/// Value of the `DragValue` being dragged (if any).
pub(crate) drag_value: Option<(Id, f64)>,
pub(crate) areas: Areas, pub(crate) areas: Areas,
/// Used by color picker /// Used by color picker
@ -182,7 +185,11 @@ impl Memory {
} }
} }
pub(crate) fn end_frame(&mut self, used_ids: &epaint::ahash::AHashMap<Id, Pos2>) { pub(crate) fn end_frame(
&mut self,
input: &InputState,
used_ids: &epaint::ahash::AHashMap<Id, Pos2>,
) {
self.areas.end_frame(); self.areas.end_frame();
if let Some(kb_focus_id) = self.interaction.kb_focus_id { if let Some(kb_focus_id) = self.interaction.kb_focus_id {
@ -195,6 +202,10 @@ impl Memory {
self.interaction.kb_focus_id = None; self.interaction.kb_focus_id = None;
} }
} }
if input.pointer.any_pressed() || input.pointer.any_released() {
self.drag_value = Default::default();
}
} }
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> { pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {

View file

@ -160,6 +160,11 @@ impl Response {
self.dragged self.dragged
} }
/// Did a drag on this widgets begin this frame?
pub fn drag_started(&self) -> bool {
self.dragged && self.ctx.input().pointer.any_pressed()
}
/// The widget was being dragged, but now it has been released. /// The widget was being dragged, but now it has been released.
pub fn drag_released(&self) -> bool { pub fn drag_released(&self) -> bool {
self.drag_released self.drag_released

View file

@ -191,6 +191,7 @@ impl<'a> Widget for DragValue<'a> {
value as f32, // Show full precision value on-hover. TODO: figure out f64 vs f32 value as f32, // Show full precision value on-hover. TODO: figure out f64 vs f32
suffix suffix
)); ));
if response.clicked() { if response.clicked() {
ui.memory().request_kb_focus(kb_edit_id); ui.memory().request_kb_focus(kb_edit_id);
ui.memory().temp_edit_string = None; // Filled in next frame ui.memory().temp_edit_string = None; // Filled in next frame
@ -199,12 +200,21 @@ impl<'a> Widget for DragValue<'a> {
let delta_points = mdelta.x - mdelta.y; // Increase to the right and up let delta_points = mdelta.x - mdelta.y; // Increase to the right and up
let delta_value = speed * delta_points; let delta_value = speed * delta_points;
if delta_value != 0.0 { if delta_value != 0.0 {
let new_value = value + delta_value as f64; // Since we round the value being dragged, we need to store the full precision value in memory:
let new_value = emath::round_to_decimals(new_value, auto_decimals); let stored_value = ui
let new_value = clamp(new_value, clamp_range); .memory()
set(&mut value_function, new_value); .drag_value
// TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere, .filter(|(id, _)| *id == response.id)
// otherwise we will just keep rounding to the same value while moving the mouse. .map(|(_, value)| value);
let stored_value = stored_value.unwrap_or(value);
let stored_value = stored_value + delta_value as f64;
let stored_value = clamp(stored_value, clamp_range.clone());
let rounded_new_value = emath::round_to_decimals(stored_value, auto_decimals);
let rounded_new_value = clamp(rounded_new_value, clamp_range);
set(&mut value_function, rounded_new_value);
ui.memory().drag_value = Some((response.id, stored_value));
} }
} }
response response