Fix: apply edited `DragValue` when it looses focus (#3776)
* Closes https://github.com/emilk/egui/issues/2877 * Closes https://github.com/emilk/egui/pull/3451
This commit is contained in:
parent
e13cc69d76
commit
b6fe244810
|
|
@ -1608,8 +1608,7 @@ impl ContextImpl {
|
|||
|
||||
viewport.repaint.frame_nr += 1;
|
||||
|
||||
self.memory
|
||||
.end_frame(&viewport.input, &viewport.frame_state.used_ids);
|
||||
self.memory.end_frame(&viewport.frame_state.used_ids);
|
||||
|
||||
if let Some(fonts) = self.fonts.get(&pixels_per_point.into()) {
|
||||
let tex_mngr = &mut self.tex_manager.0.write();
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ use epaint::{emath::Rangef, vec2, Vec2};
|
|||
use crate::{
|
||||
area,
|
||||
window::{self, WindowInteraction},
|
||||
EventFilter, Id, IdMap, InputState, LayerId, Pos2, Rect, Style, ViewportId, ViewportIdMap,
|
||||
ViewportIdSet,
|
||||
EventFilter, Id, IdMap, LayerId, Pos2, Rect, Style, ViewportId, ViewportIdMap, ViewportIdSet,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -79,9 +78,6 @@ pub struct Memory {
|
|||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
pub(crate) viewport_id: ViewportId,
|
||||
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
pub(crate) drag_value: crate::widgets::drag_value::MonoState,
|
||||
|
||||
/// Which popup-window is open (if any)?
|
||||
/// Could be a combo box, color picker, menu etc.
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
|
|
@ -111,7 +107,6 @@ impl Default for Memory {
|
|||
interactions: Default::default(),
|
||||
viewport_id: Default::default(),
|
||||
window_interactions: Default::default(),
|
||||
drag_value: Default::default(),
|
||||
areas: Default::default(),
|
||||
popup: Default::default(),
|
||||
everything_is_visible: Default::default(),
|
||||
|
|
@ -587,11 +582,10 @@ impl Memory {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn end_frame(&mut self, input: &InputState, used_ids: &IdMap<Rect>) {
|
||||
pub(crate) fn end_frame(&mut self, used_ids: &IdMap<Rect>) {
|
||||
self.caches.update();
|
||||
self.areas_mut().end_frame();
|
||||
self.interaction_mut().focus.end_frame(used_ids);
|
||||
self.drag_value.end_frame(input);
|
||||
}
|
||||
|
||||
pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {
|
||||
|
|
|
|||
|
|
@ -476,13 +476,22 @@ impl IdTypeMap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Remove the state of this type an id.
|
||||
/// Remove the state of this type and id.
|
||||
#[inline]
|
||||
pub fn remove<T: 'static>(&mut self, id: Id) {
|
||||
let hash = hash(TypeId::of::<T>(), id);
|
||||
self.map.remove(&hash);
|
||||
}
|
||||
|
||||
/// Remove and fetch the state of this type and id.
|
||||
#[inline]
|
||||
pub fn remove_temp<T: 'static + Clone>(&mut self, id: Id) -> Option<T> {
|
||||
let hash = hash(TypeId::of::<T>(), id);
|
||||
self.map
|
||||
.remove(&hash)
|
||||
.and_then(|element| element.get_temp().cloned())
|
||||
}
|
||||
|
||||
/// Note all state of the given type.
|
||||
pub fn remove_by_type<T: 'static>(&mut self) {
|
||||
let key = TypeId::of::<T>();
|
||||
|
|
|
|||
|
|
@ -6,28 +6,6 @@ use crate::*;
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Same state for all [`DragValue`]s.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct MonoState {
|
||||
last_dragged_id: Option<Id>,
|
||||
last_dragged_value: Option<f64>,
|
||||
|
||||
/// For temporary edit of a [`DragValue`] value.
|
||||
/// Couples with the current focus id.
|
||||
edit_string: Option<String>,
|
||||
}
|
||||
|
||||
impl MonoState {
|
||||
pub(crate) fn end_frame(&mut self, input: &InputState) {
|
||||
if input.pointer.any_pressed() || input.pointer.any_released() {
|
||||
self.last_dragged_id = None;
|
||||
self.last_dragged_value = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type NumFormatter<'a> = Box<dyn 'a + Fn(f64, RangeInclusive<usize>) -> String>;
|
||||
type NumParser<'a> = Box<dyn 'a + Fn(&str) -> Option<f64>>;
|
||||
|
||||
|
|
@ -402,13 +380,13 @@ impl<'a> Widget for DragValue<'a> {
|
|||
// screen readers.
|
||||
let is_kb_editing = ui.memory_mut(|mem| {
|
||||
mem.interested_in_focus(id);
|
||||
let is_kb_editing = mem.has_focus(id);
|
||||
if mem.gained_focus(id) {
|
||||
mem.drag_value.edit_string = None;
|
||||
}
|
||||
is_kb_editing
|
||||
mem.has_focus(id)
|
||||
});
|
||||
|
||||
if ui.memory_mut(|mem| mem.gained_focus(id)) {
|
||||
ui.data_mut(|data| data.remove::<String>(id));
|
||||
}
|
||||
|
||||
let old_value = get(&mut get_set_value);
|
||||
let mut value = old_value;
|
||||
let aim_rad = ui.input(|i| i.aim_radius() as f64);
|
||||
|
|
@ -467,7 +445,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||
value = clamp_to_range(value, clamp_range.clone());
|
||||
if old_value != value {
|
||||
set(&mut get_set_value, value);
|
||||
ui.memory_mut(|mem| mem.drag_value.edit_string = None);
|
||||
ui.data_mut(|data| data.remove::<String>(id));
|
||||
}
|
||||
|
||||
let value_text = match custom_formatter {
|
||||
|
|
@ -483,11 +461,27 @@ impl<'a> Widget for DragValue<'a> {
|
|||
|
||||
let text_style = ui.style().drag_value_text_style.clone();
|
||||
|
||||
if ui.memory(|mem| mem.lost_focus(id)) {
|
||||
let value_text = ui.data_mut(|data| data.remove_temp::<String>(id));
|
||||
if let Some(value_text) = value_text {
|
||||
// We were editing the value as text last frame, but lost focus.
|
||||
// Make sure we applied the last text value:
|
||||
let parsed_value = match &custom_parser {
|
||||
Some(parser) => parser(&value_text),
|
||||
None => value_text.parse().ok(),
|
||||
};
|
||||
if let Some(parsed_value) = parsed_value {
|
||||
let parsed_value = clamp_to_range(parsed_value, clamp_range.clone());
|
||||
set(&mut get_set_value, parsed_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// some clones below are redundant if AccessKit is disabled
|
||||
#[allow(clippy::redundant_clone)]
|
||||
let mut response = if is_kb_editing {
|
||||
let mut value_text = ui
|
||||
.memory_mut(|mem| mem.drag_value.edit_string.take())
|
||||
.data_mut(|data| data.remove_temp::<String>(id))
|
||||
.unwrap_or_else(|| value_text.clone());
|
||||
let response = ui.add(
|
||||
TextEdit::singleline(&mut value_text)
|
||||
|
|
@ -509,7 +503,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||
response.lost_focus()
|
||||
};
|
||||
if update {
|
||||
let parsed_value = match custom_parser {
|
||||
let parsed_value = match &custom_parser {
|
||||
Some(parser) => parser(&value_text),
|
||||
None => value_text.parse().ok(),
|
||||
};
|
||||
|
|
@ -518,7 +512,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||
set(&mut get_set_value, parsed_value);
|
||||
}
|
||||
}
|
||||
ui.memory_mut(|mem| mem.drag_value.edit_string = Some(value_text));
|
||||
ui.data_mut(|data| data.insert_temp(id, value_text));
|
||||
response
|
||||
} else {
|
||||
let button = Button::new(
|
||||
|
|
@ -533,7 +527,7 @@ impl<'a> Widget for DragValue<'a> {
|
|||
let mut response = response.on_hover_cursor(CursorIcon::ResizeHorizontal);
|
||||
|
||||
if ui.style().explanation_tooltips {
|
||||
response = response .on_hover_text(format!(
|
||||
response = response.on_hover_text(format!(
|
||||
"{}{}{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.",
|
||||
prefix,
|
||||
value as f32, // Show full precision value on-hover. TODO(emilk): figure out f64 vs f32
|
||||
|
|
@ -541,15 +535,18 @@ impl<'a> Widget for DragValue<'a> {
|
|||
));
|
||||
}
|
||||
|
||||
if ui.input(|i| i.pointer.any_pressed() || i.pointer.any_released()) {
|
||||
// Reset memory of preciely dagged value.
|
||||
ui.data_mut(|data| data.remove::<f64>(id));
|
||||
}
|
||||
|
||||
if response.clicked() {
|
||||
ui.memory_mut(|mem| {
|
||||
mem.drag_value.edit_string = None;
|
||||
mem.request_focus(id);
|
||||
});
|
||||
ui.data_mut(|data| data.remove::<String>(id));
|
||||
ui.memory_mut(|mem| mem.request_focus(id));
|
||||
let mut state = TextEdit::load_state(ui.ctx(), id).unwrap_or_default();
|
||||
state.set_ccursor_range(Some(text::CCursorRange::two(
|
||||
epaint::text::cursor::CCursor::default(),
|
||||
epaint::text::cursor::CCursor::new(value_text.chars().count()),
|
||||
text::CCursor::default(),
|
||||
text::CCursor::new(value_text.chars().count()),
|
||||
)));
|
||||
state.store(ui.ctx(), response.id);
|
||||
} else if response.dragged() {
|
||||
|
|
@ -563,28 +560,22 @@ impl<'a> Widget for DragValue<'a> {
|
|||
let delta_value = delta_points as f64 * speed;
|
||||
|
||||
if delta_value != 0.0 {
|
||||
let mut drag_state = ui.memory_mut(|mem| std::mem::take(&mut mem.drag_value));
|
||||
|
||||
// Since we round the value being dragged, we need to store the full precision value in memory:
|
||||
let stored_value = (drag_state.last_dragged_id == Some(response.id))
|
||||
.then_some(drag_state.last_dragged_value)
|
||||
.flatten();
|
||||
let stored_value = stored_value.unwrap_or(value);
|
||||
let stored_value = stored_value + delta_value;
|
||||
let precise_value = ui.data_mut(|data| data.get_temp::<f64>(id));
|
||||
let precise_value = precise_value.unwrap_or(value);
|
||||
let precise_value = precise_value + delta_value;
|
||||
|
||||
let aim_delta = aim_rad * speed;
|
||||
let rounded_new_value = emath::smart_aim::best_in_range_f64(
|
||||
stored_value - aim_delta,
|
||||
stored_value + aim_delta,
|
||||
precise_value - aim_delta,
|
||||
precise_value + aim_delta,
|
||||
);
|
||||
let rounded_new_value =
|
||||
emath::round_to_decimals(rounded_new_value, auto_decimals);
|
||||
let rounded_new_value = clamp_to_range(rounded_new_value, clamp_range.clone());
|
||||
set(&mut get_set_value, rounded_new_value);
|
||||
|
||||
drag_state.last_dragged_id = Some(response.id);
|
||||
drag_state.last_dragged_value = Some(stored_value);
|
||||
ui.memory_mut(|mem| mem.drag_value = drag_state);
|
||||
ui.data_mut(|data| data.insert_temp::<f64>(id, precise_value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -634,11 +634,8 @@ impl<'a> Table<'a> {
|
|||
// Hide first-frame-jitters when auto-sizing.
|
||||
ui.add_visible_ui(!first_frame_auto_size_columns, |ui| {
|
||||
let hovered_row_index_id = self.state_id.with("__table_hovered_row");
|
||||
let hovered_row_index = ui.memory_mut(|w| {
|
||||
let hovered_row = w.data.get_temp(hovered_row_index_id);
|
||||
w.data.remove::<usize>(hovered_row_index_id);
|
||||
hovered_row
|
||||
});
|
||||
let hovered_row_index =
|
||||
ui.data_mut(|data| data.remove_temp::<usize>(hovered_row_index_id));
|
||||
|
||||
let layout = StripLayout::new(ui, CellDirection::Horizontal, cell_layout, sense);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue