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
|
// 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).
|
// every other dialog's value gets (`dir` is None below).
|
||||||
if matches!(d.kind, Kind::Move { .. }) {
|
if matches!(d.kind, Kind::Move { .. }) {
|
||||||
|
let mut dragging = false;
|
||||||
if let Some(t) = crate::dialogs_move::triad(app, &d)
|
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()))
|
&& 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);
|
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
|
// 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
|
// 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
|
// 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.
|
// 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) };
|
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);
|
app.dialog = Some(d);
|
||||||
return;
|
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);
|
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
|
if *value != before
|
||||||
&& let Kind::Extrude { all, .. } = &mut d.kind
|
&& 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 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
|
/// the window is open, so a number typed straight after a pick sets the value. `at` is its
|
||||||
/// top-left corner.
|
/// top-left corner. `select_all` re-selects the whole field, both right after a pick and on every
|
||||||
fn value_box(ui: &mut egui::Ui, label: &str, value: &mut String, at: Pos2, focus: bool) {
|
/// 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 t = Tokens::get();
|
||||||
let id = egui::Id::new("sc_canvas_value");
|
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| {
|
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));
|
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));
|
let r = ui.add(egui::TextEdit::singleline(value).id(id).desired_width(72.0));
|
||||||
crate::params_dialog::complete(ui, &r, value);
|
crate::params_dialog::complete(ui, &r, value);
|
||||||
if focus {
|
if select_all {
|
||||||
r.request_focus();
|
r.request_focus();
|
||||||
let n = value.chars().count();
|
let n = value.chars().count();
|
||||||
let mut st = egui::text_edit::TextEditState::load(ui.ctx(), id).unwrap_or_default();
|
let mut st = egui::text_edit::TextEditState::load(ui.ctx(), id).unwrap_or_default();
|
||||||
@@ -806,6 +811,7 @@ fn draw_revolve(
|
|||||||
curved_to: false,
|
curved_to: false,
|
||||||
});
|
});
|
||||||
let mut box_at = bs + vec2(16.0, -30.0);
|
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() {
|
if !extent.curved_to && extent.sweep.is_finite() {
|
||||||
let (origin, direction) = axis;
|
let (origin, direction) = axis;
|
||||||
let centre = origin + direction * (base - origin).dot(direction);
|
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| {
|
if to_mode {
|
||||||
egui::Frame::popup(ui.style()).inner_margin(egui::Margin::symmetric(6, 3)).show(ui, |ui| {
|
crate::frame::area(egui::Id::new("sc_canvas_area")).fixed_pos(box_at).order(egui::Order::Foreground).show(ui.ctx(), |ui| {
|
||||||
if to_mode {
|
egui::Frame::popup(ui.style()).inner_margin(egui::Margin::symmetric(6, 3)).show(ui, |ui| {
|
||||||
let text = if extent.curved_to {
|
let text = if extent.curved_to {
|
||||||
"To surface".into()
|
"To surface".into()
|
||||||
} else if extent.sweep.is_finite() {
|
} else if extent.sweep.is_finite() {
|
||||||
@@ -909,25 +915,12 @@ fn draw_revolve(
|
|||||||
"Choose target".into()
|
"Choose target".into()
|
||||||
};
|
};
|
||||||
ui.label(text).on_hover_text(crate::i18n::tr_label("Determined by To Object. Choose Angle to edit the angular extent."));
|
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 {
|
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();
|
let after: Vec<_> = h.app.session.world_state().bodies.iter().map(|b| (b.name.clone(), b.mesh().bounds())).collect();
|
||||||
assert_eq!(after, before);
|
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