mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 21:57:12 -06:00
Fix #4803: Enter or Escape in a floating panel's field stays in the field
This commit is contained in:
@@ -1187,6 +1187,14 @@ fn forward_keys(app: &mut VectorcraftApp, ctx: &egui::Context, events: Vec<egui:
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
/// A field of a floating panel's window that ends its editing on Enter or Escape lets go of the
|
||||
/// keyboard in that frame, so craft-ui would forward the key to the main window's shortcuts: Enter
|
||||
/// in the Transparency panel's Opacity field also opened the Move dialog (#4803). The key was the
|
||||
/// field's, so it stays in the window.
|
||||
fn keep_field_ending_keys(ui: &egui::Ui) {
|
||||
ui.input_mut(|i| i.events.retain(|e| !matches!(e, egui::Event::Key { key: egui::Key::Enter | egui::Key::Escape, pressed: true, .. })));
|
||||
}
|
||||
|
||||
/// VectorCraft's look and wording of craft-ui's docking.
|
||||
fn dock_style(ui: &egui::Ui) -> craft_ui::docking::DockStyle {
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
@@ -1244,9 +1252,11 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
self.app.canvas_area(ui);
|
||||
return;
|
||||
}
|
||||
if ui.ctx().viewport_id() != egui::ViewportId::ROOT {
|
||||
let floating = ui.ctx().viewport_id() != egui::ViewportId::ROOT;
|
||||
if floating {
|
||||
crate::canvas::release_art_in_window(self.app, ui.ctx());
|
||||
}
|
||||
let field_had_keyboard = floating && ui.memory(|m| m.focused().is_some());
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
let back = ui.max_rect();
|
||||
if back.is_positive() {
|
||||
@@ -1263,6 +1273,9 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
crate::dock::panel_body(self.app, ui, id, width, height)
|
||||
});
|
||||
}
|
||||
if field_had_keyboard {
|
||||
keep_field_ending_keys(ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn logical_label(&self, id: &String) -> String {
|
||||
|
||||
@@ -240,6 +240,37 @@ fn keys_typed_in_a_native_window_run_the_main_windows_shortcuts() {
|
||||
assert!(h.app.session.active().unwrap().selection.objects.is_empty());
|
||||
}
|
||||
|
||||
/// Enter that commits a field of a floating panel's window, and Escape that cancels one, are the
|
||||
/// field's: with an object selected and the Selection tool, Enter didn't also open the Move dialog
|
||||
/// (#4803).
|
||||
#[test]
|
||||
fn enter_and_escape_in_a_native_windows_field_stay_in_the_field() {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap();
|
||||
h.app.run("select.all", json!({})).unwrap();
|
||||
let viewport = h.float("transparency");
|
||||
// The window's first text field (Opacity), by the state its text box keeps.
|
||||
let ctx = h.ctx.clone();
|
||||
let field = ctx
|
||||
.viewport_for(viewport, |vp| vp.prev_pass.widgets.layers().flat_map(|(_, w)| w.iter()).map(|w| (w.id, w.rect)).collect::<Vec<_>>())
|
||||
.into_iter()
|
||||
.find(|(id, _)| egui::TextEdit::load_state(&ctx, *id).is_some())
|
||||
.map(|(_, rect)| rect.center())
|
||||
.expect("the panel has a text field");
|
||||
for key in [Key::Enter, Key::Escape] {
|
||||
if let Some(window) = h.host.borrow_mut().windows.get_mut(&viewport) {
|
||||
window.events.extend([button(field, true), button(field, false)]);
|
||||
}
|
||||
h.frames(2);
|
||||
h.key_in(viewport, key);
|
||||
assert!(h.app.ui.dialog.is_none(), "{key:?} in the field opens no dialog");
|
||||
assert_eq!(h.app.session.active().unwrap().selection.objects.len(), 1, "{key:?} in the field keeps the selection");
|
||||
}
|
||||
// Keys typed with no field editing still reach the main window.
|
||||
h.key_in(viewport, Key::Enter);
|
||||
assert!(h.app.ui.dialog.is_some(), "Enter outside a field opens the Move dialog");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_modal_blocks_native_windows_and_takes_the_keyboard_from_them() {
|
||||
let mut h = Harness::new();
|
||||
|
||||
Reference in New Issue
Block a user