fix: update input field text selection on changing value during action (#1079)
Changing the value in an input field updates its text selection, so typing over a selected value replaces what's shown.
This commit is contained in:
1 parent
1e1722b7e1
commit
a8258eb63a
2 files changed
+36
-26
No files matched your search
@@ -217,10 +217,12 @@ pub fn show(app: &mut SolveApp, ui: &mut egui::Ui, painter: &egui::Painter, proj
|
||||
// one sets it. The triad is the manipulator here, so the box comes without the drag arrow
|
||||
// every other dialog's value gets (`dir` is None below).
|
||||
if matches!(d.kind, Kind::Move { .. }) {
|
||||
let mut dragging = false;
|
||||
if let Some(t) = crate::dialogs_move::triad(app, &d)
|
||||
&& let Some(g) = crate::gizmo::show(ui, painter, proj, egui::Id::new("sc_move_triad"), &t, snap_step(app.cam.half_height()))
|
||||
{
|
||||
crate::dialogs_move::drag(app, &mut d, g);
|
||||
dragging = true;
|
||||
}
|
||||
// Read the triad again after the drag, so the box follows it in the same frame. Move
|
||||
// types with no triad (Rotate, Point to Point, Point to Position) put the box on the
|
||||
@@ -234,7 +236,7 @@ pub fn show(app: &mut SolveApp, ui: &mut egui::Ui, painter: &egui::Painter, proj
|
||||
// Clear of the triad: its arrows reach LEN_PX and its rings RING of that, so a box
|
||||
// beside the centre would cover the handles the user is reaching for.
|
||||
let off = if triad.is_some() { vec2(crate::gizmo::LEN_PX as f32 + 16.0, -12.0) } else { vec2(16.0, -30.0) };
|
||||
value_box(ui, label, value, bs + off, focus);
|
||||
value_box(ui, label, value, bs + off, focus || dragging);
|
||||
}
|
||||
app.dialog = Some(d);
|
||||
return;
|
||||
@@ -418,7 +420,7 @@ fn draw(app: &SolveApp, ui: &mut egui::Ui, painter: &egui::Painter, proj: &Proj,
|
||||
box_at = ts + vec2(14.0, -12.0);
|
||||
}
|
||||
}
|
||||
value_box(ui, label, value, box_at, focus);
|
||||
value_box(ui, label, value, box_at, focus || *value != before);
|
||||
if *value != before
|
||||
&& let Kind::Extrude { all, .. } = &mut d.kind
|
||||
{
|
||||
@@ -428,8 +430,11 @@ fn draw(app: &SolveApp, ui: &mut egui::Ui, painter: &egui::Painter, proj: &Proj,
|
||||
|
||||
/// The command's main value in the viewport, next to the geometry: the keyboard goes here while
|
||||
/// the window is open, so a number typed straight after a pick sets the value. `at` is its
|
||||
/// top-left corner.
|
||||
fn value_box(ui: &mut egui::Ui, label: &str, value: &mut String, at: Pos2, focus: bool) {
|
||||
/// top-left corner. `select_all` re-selects the whole field, both right after a pick and on every
|
||||
/// frame a drag is changing the value underneath it: without the latter, a drag that grows the
|
||||
/// text (e.g. "4 mm" -> "15 mm") would leave the stale, shorter selection from the pick in place,
|
||||
/// covering only part of the new text.
|
||||
fn value_box(ui: &mut egui::Ui, label: &str, value: &mut String, at: Pos2, select_all: bool) {
|
||||
let t = Tokens::get();
|
||||
let id = egui::Id::new("sc_canvas_value");
|
||||
crate::frame::area(egui::Id::new("sc_canvas_area")).fixed_pos(at).order(egui::Order::Foreground).show(ui.ctx(), |ui| {
|
||||
@@ -438,7 +443,7 @@ fn value_box(ui: &mut egui::Ui, label: &str, value: &mut String, at: Pos2, focus
|
||||
ui.label(egui::RichText::new(label).color(t.text_dim).size(11.5));
|
||||
let r = ui.add(egui::TextEdit::singleline(value).id(id).desired_width(72.0));
|
||||
crate::params_dialog::complete(ui, &r, value);
|
||||
if focus {
|
||||
if select_all {
|
||||
r.request_focus();
|
||||
let n = value.chars().count();
|
||||
let mut st = egui::text_edit::TextEditState::load(ui.ctx(), id).unwrap_or_default();
|
||||
@@ -806,6 +811,7 @@ fn draw_revolve(
|
||||
curved_to: false,
|
||||
});
|
||||
let mut box_at = bs + vec2(16.0, -30.0);
|
||||
let angle_before = if let Kind::Revolve { angle, .. } = &d.kind { angle.clone() } else { String::new() };
|
||||
if !extent.curved_to && extent.sweep.is_finite() {
|
||||
let (origin, direction) = axis;
|
||||
let centre = origin + direction * (base - origin).dot(direction);
|
||||
@@ -898,9 +904,9 @@ fn draw_revolve(
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::frame::area(egui::Id::new("sc_canvas_area")).fixed_pos(box_at).order(egui::Order::Foreground).show(ui.ctx(), |ui| {
|
||||
egui::Frame::popup(ui.style()).inner_margin(egui::Margin::symmetric(6, 3)).show(ui, |ui| {
|
||||
if to_mode {
|
||||
if to_mode {
|
||||
crate::frame::area(egui::Id::new("sc_canvas_area")).fixed_pos(box_at).order(egui::Order::Foreground).show(ui.ctx(), |ui| {
|
||||
egui::Frame::popup(ui.style()).inner_margin(egui::Margin::symmetric(6, 3)).show(ui, |ui| {
|
||||
let text = if extent.curved_to {
|
||||
"To surface".into()
|
||||
} else if extent.sweep.is_finite() {
|
||||
@@ -909,25 +915,12 @@ fn draw_revolve(
|
||||
"Choose target".into()
|
||||
};
|
||||
ui.label(text).on_hover_text(crate::i18n::tr_label("Determined by To Object. Choose Angle to edit the angular extent."));
|
||||
} else if let Kind::Revolve { angle, .. } = &mut d.kind {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(egui::RichText::new(crate::i18n::tr_label("Angle")).color(Tokens::get().text_dim).size(11.5));
|
||||
let id = egui::Id::new("sc_canvas_value");
|
||||
let response = ui.add(egui::TextEdit::singleline(angle).id(id).desired_width(72.0));
|
||||
crate::params_dialog::complete(ui, &response, angle);
|
||||
if focus {
|
||||
response.request_focus();
|
||||
let n = angle.chars().count();
|
||||
let mut state = egui::text_edit::TextEditState::load(ui.ctx(), id).unwrap_or_default();
|
||||
state
|
||||
.cursor
|
||||
.set_char_range(Some(egui::text_selection::CCursorRange::two(egui::text::CCursor::new(0), egui::text::CCursor::new(n))));
|
||||
state.store(ui.ctx(), id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if let Kind::Revolve { angle, .. } = &mut d.kind {
|
||||
let dragging = *angle != angle_before;
|
||||
value_box(ui, crate::i18n::tr_label("Angle"), angle, box_at, focus || dragging);
|
||||
}
|
||||
}
|
||||
|
||||
fn revolve_convention(d: &Dialog) -> solidcraft_engine::doc::RevolveConvention {
|
||||
|
||||
@@ -278,3 +278,20 @@ fn rotated_second_occurrence_keeps_saved_from_and_to_points_exact() {
|
||||
let after: Vec<_> = h.app.session.world_state().bodies.iter().map(|b| (b.name.clone(), b.mesh().bounds())).collect();
|
||||
assert_eq!(after, before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_to_update_value_reselects_whole_value_box_text() {
|
||||
let mut h = make(json!({"distance":"4 mm"}));
|
||||
open(&mut h);
|
||||
let id = egui::Id::new("sc_canvas_value");
|
||||
let selection = |h: &Harness| {
|
||||
let range = egui::text_edit::TextEditState::load(&h.ctx, id).unwrap().cursor.char_range().unwrap();
|
||||
let (a, b) = (range.primary.index, range.secondary.index);
|
||||
(a.min(b).0, a.max(b).0)
|
||||
};
|
||||
assert_eq!(selection(&h), (0, 4), "the pick selects all of \"4 mm\"");
|
||||
h.step(&json!({"drag":[{"handle":"arrow"},{"world":[5,5,15]}]})).unwrap();
|
||||
let Some(crate::dialogs::Dialog { kind: crate::dialogs::Kind::Extrude { distance, .. }, .. }) = &h.app.dialog else { panic!("extrude dialog") };
|
||||
assert_eq!(distance, "15 mm");
|
||||
assert_eq!(selection(&h), (0, 5), "the drag grew the text length; the selection must cover all of it");
|
||||
}
|
||||
Reference in new issue
Block a user