fix: modal dialogs capture the keyboard
Modals blocked the mouse but not the keyboard: keystrokes from the global key subscription leaked past an open dialog into the command line and fired as commands once it closed. Guard `update()` so that while a modal is open, Escape closes it and every other keystroke-derived message (command-line input/edit, F-key toggles, edit shortcuts, undo/redo) is swallowed. A modal's own text fields keep working — they emit their own messages, which are not in the blocked set. (#126) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
aafced76d7
commit
5ce3aaf260
1 changed files with 49 additions and 0 deletions
|
|
@ -15,6 +15,40 @@ use acadrust::{EntityType as AcadEntityType, Handle};
|
||||||
use iced::time::Instant;
|
use iced::time::Instant;
|
||||||
use iced::{mouse, Task};
|
use iced::{mouse, Task};
|
||||||
|
|
||||||
|
/// Keystroke-derived messages that an open modal dialog must swallow so the
|
||||||
|
/// keyboard can't reach the main window (command line, F-key toggles, edit
|
||||||
|
/// shortcuts) while a dialog is up. `CommandEscape` is handled separately (it
|
||||||
|
/// closes the modal); a modal's own text fields emit their own messages, which
|
||||||
|
/// are not in this set. See [`OpenCADStudio::update`] and #126.
|
||||||
|
fn is_modal_blocked_key_msg(msg: &Message) -> bool {
|
||||||
|
matches!(
|
||||||
|
msg,
|
||||||
|
Message::CommandInput(_)
|
||||||
|
| Message::CommandAppendChar(_)
|
||||||
|
| Message::CommandSpace
|
||||||
|
| Message::CommandFinalize
|
||||||
|
| Message::CommandBackspace
|
||||||
|
| Message::CommandHistoryPrev
|
||||||
|
| Message::CommandHistoryNext
|
||||||
|
| Message::DynTabNext
|
||||||
|
| Message::MTextCaretMove(_)
|
||||||
|
| Message::DeleteSelected
|
||||||
|
| Message::ToggleSnapEnabled
|
||||||
|
| Message::ToggleGrid
|
||||||
|
| Message::ToggleOrtho
|
||||||
|
| Message::ToggleGridSnap
|
||||||
|
| Message::TogglePolar
|
||||||
|
| Message::ToggleOTrack
|
||||||
|
| Message::ToggleDynInput
|
||||||
|
| Message::TabNew
|
||||||
|
| Message::OpenFile
|
||||||
|
| Message::SaveFile
|
||||||
|
| Message::SaveAs
|
||||||
|
| Message::Undo
|
||||||
|
| Message::Redo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
||||||
/// Pixel distance from a Model-tile inner divider that still registers as
|
/// Pixel distance from a Model-tile inner divider that still registers as
|
||||||
/// a resize grip on the press.
|
/// a resize grip on the press.
|
||||||
|
|
@ -96,6 +130,21 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
||||||
|
// A modal dialog must capture the keyboard the same way it already
|
||||||
|
// captures the mouse. Otherwise keystrokes from the global key
|
||||||
|
// subscription leak past the modal into the command line and fire as
|
||||||
|
// commands once the dialog closes. While a modal is open, Escape
|
||||||
|
// closes it and every other keystroke-derived message is swallowed;
|
||||||
|
// the modal's own text fields keep working because they emit their own
|
||||||
|
// (non-blocked) messages. (#126)
|
||||||
|
if self.active_modal.is_some() {
|
||||||
|
if matches!(msg, Message::CommandEscape) {
|
||||||
|
return self.update(Message::CloseModal);
|
||||||
|
}
|
||||||
|
if is_modal_blocked_key_msg(&msg) {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
}
|
||||||
let task = self.update_inner(msg);
|
let task = self.update_inner(msg);
|
||||||
// After every message, mirror the active command step's prompt so
|
// After every message, mirror the active command step's prompt so
|
||||||
// its history line stays pinned (non-fading) until the step changes.
|
// its history line stays pinned (non-fading) until the step changes.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue