mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Numeric: clamp committed values in point units, schedule rejection highlight repaint, and update roadmap/area evidence
This commit is contained in:
committed by
bflatastic
parent
418f1bffc3
commit
5da8c8e495
@@ -2534,9 +2534,34 @@ fn color_halftone_invalid_input_does_not_revert_to_sub_minimum_value() {
|
||||
frame(&mut app, vec![egui::Event::Text("0.5 px".into())]);
|
||||
frame(&mut app, vec![egui::Event::Key { key: egui::Key::Tab, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]);
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["maxRadius"], serde_json::json!(4.0));
|
||||
// 2. Type invalid input "1jgghgh"
|
||||
// 2. Refocus Max Radius before typing invalid text
|
||||
crate::widgets::arm_field_focus(&ctx, ("dlg-len", "maxRadius"));
|
||||
frame(&mut app, vec![]);
|
||||
frame(&mut app, vec![egui::Event::Text("1jgghgh".into())]);
|
||||
frame(&mut app, vec![egui::Event::Key { key: egui::Key::Tab, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]);
|
||||
// The value remains the valid clamped minimum 4.0 pt, never 0.5 px
|
||||
// The value remains the valid clamped minimum 4.0 pt, never 0.5 px, and rejection flash is set
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["maxRadius"], serde_json::json!(4.0));
|
||||
assert!(app.ui.dialog.as_ref().unwrap().fields["maxRadius"] == serde_json::json!(4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bounded_field_with_inches_does_not_clamp_point_bounds_to_inch_numbers() {
|
||||
let mut app = app();
|
||||
let _id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap()["id"].as_u64().unwrap();
|
||||
// Switch document units to Inches
|
||||
app.run("document.setup", json!({"units": "Inches"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
theme::install_fonts(&ctx);
|
||||
theme::apply(&ctx, Default::default());
|
||||
let frame = |app: &mut VectorcraftApp, events: Vec<egui::Event>| {
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, ..Default::default() }, |ui| show(app, ui.ctx()));
|
||||
out.textures_delta.clear();
|
||||
};
|
||||
app.run("select.all", json!({})).unwrap();
|
||||
app.run("effect.dialog", json!({"effect": "pixelate.colorHalftone"})).unwrap();
|
||||
frame(&mut app, vec![]);
|
||||
// Type 1 in: 72 pt is within 4..127 pt range and must not clamp to ~1.76 pt
|
||||
frame(&mut app, vec![egui::Event::Text("1 in".into())]);
|
||||
frame(&mut app, vec![egui::Event::Key { key: egui::Key::Tab, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]);
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["maxRadius"], serde_json::json!(72.0));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl<T> std::fmt::Debug for HashOnly<T> {
|
||||
}
|
||||
|
||||
/// `parent.with(salt)` for a salt that needs only `Hash`.
|
||||
fn id_with(parent: egui::Id, salt: impl std::hash::Hash) -> egui::Id {
|
||||
pub(crate) fn id_with(parent: egui::Id, salt: impl std::hash::Hash) -> egui::Id {
|
||||
parent.with(HashOnly(salt))
|
||||
}
|
||||
|
||||
|
||||
@@ -102,10 +102,11 @@ fn unit_field(
|
||||
// ↑/↓ and the wheel step in the field's unit; a drag on its label scrubs it.
|
||||
let typed = typed_unit(ui.ctx(), unit);
|
||||
let read = |b: &str| typed.parse(&decimal_point(b));
|
||||
let bounds = unit.from_pt(*bounds.start())..=unit.from_pt(*bounds.end());
|
||||
let bounds_unit = unit.from_pt(*bounds.start())..=unit.from_pt(*bounds.end());
|
||||
let stops: Vec<f64> = stops.iter().map(|&s| unit.from_pt(s)).collect();
|
||||
let stepped = step_value(ui, &resp, &mut buf, &bounds, (|b: &str| Some(unit.from_pt(read(b)?)), |v| unit.format(unit.to_pt(v))), None, &stops);
|
||||
let scrubbed = scrub::field(ui, id, rect, &buf, value.map(|v| unit.from_pt(v)), STEP_DECIMALS, &bounds);
|
||||
let stepped =
|
||||
step_value(ui, &resp, &mut buf, &bounds_unit, (|b: &str| Some(unit.from_pt(read(b)?)), |v| unit.format(unit.to_pt(v))), None, &stops);
|
||||
let scrubbed = scrub::field(ui, id, rect, &buf, value.map(|v| unit.from_pt(v)), STEP_DECIMALS, &bounds_unit);
|
||||
let commit = commit && buf != shown;
|
||||
if commit {
|
||||
match read(&buf).or_else(|| if share_of_value { share_of(&decimal_point(&buf), value?) } else { None }) {
|
||||
@@ -117,7 +118,8 @@ fn unit_field(
|
||||
}
|
||||
None => {
|
||||
ui.data_mut(|d| d.remove::<String>(id));
|
||||
ui.data_mut(|d| d.insert_temp(id.with("invalid-flash"), true));
|
||||
ui.data_mut(|d| d.insert_temp(id.with("invalid-flash"), ui.input(|i| i.time + 0.25)));
|
||||
ui.ctx().request_repaint();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,11 @@ pub(super) fn recessed_text(
|
||||
.allocate_ui_with_layout(vec2(width, height), layout, |ui| {
|
||||
ui.set_min_size(vec2(width, height));
|
||||
let rect = ui.max_rect();
|
||||
let invalid = ui.data_mut(|d| d.remove_temp::<bool>(id.with("invalid-flash"))).unwrap_or(false);
|
||||
let now = ui.input(|i| i.time);
|
||||
let invalid = ui.data(|d| d.get_temp::<f64>(id.with("invalid-flash"))).is_some_and(|end| now <= end);
|
||||
if invalid {
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
let stroke = if invalid {
|
||||
Stroke::new(1.5, t.accent_strong)
|
||||
} else if editing {
|
||||
|
||||
Reference in New Issue
Block a user