From a1db99a4328a0bac3ad02b1d4b9d768f03d577c8 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 21 May 2026 21:50:33 +0700 Subject: [PATCH] feat(ui): keyboard apply/cancel + larger day-button hit area Close two LOCKED spec deviations from design-decisions.md flagged by code-review of 18020e2: - Q5: Enter key inside the popup now triggers Apply (apply_fake_time + close), Esc cancels without committing. Both checks use consume_key AFTER the inner widgets have rendered so a focused DragValue keeps its own commit-on-Enter / cancel-on-Esc semantics. - "Hit-area sizing" decision: day-grid buttons bumped from 28x22 px (slightly cramped) to 32x32 px (spec minimum). Popup still fits in the 260..=320 px min/max width band. No public API change. apply_fake_time, picked_naive_dt, and the DST/overflow status_msg flow are untouched. --- crates/time-mocker-ui/src/app.rs | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/crates/time-mocker-ui/src/app.rs b/crates/time-mocker-ui/src/app.rs index c71e4d7..580576d 100644 --- a/crates/time-mocker-ui/src/app.rs +++ b/crates/time-mocker-ui/src/app.rs @@ -257,13 +257,6 @@ impl TimeMockerApp { } fn ui_picker_popup(&mut self, ui: &mut egui::Ui) { - // Esc dismisses without committing. CloseOnClickOutside doesn't cover - // keyboard escape on its own. - if ui.input(|i| i.key_pressed(egui::Key::Escape)) { - ui.memory_mut(|mem| mem.close_popup()); - return; - } - // Month-nav header — chevrons clamp at MIN_YEAR-01 / MAX_YEAR-12 so // the user can't browse outside the supported FILETIME range. ui.horizontal(|ui| { @@ -358,7 +351,12 @@ impl TimeMockerApp { ui.separator(); - // Apply row — right-aligned primary action. + // Apply row — right-aligned primary action. Enter (when not already + // consumed by a focused DragValue) is the keyboard shortcut; Esc + // dismisses without committing. Both checks use `consume_key` AFTER + // the inner widgets have rendered so DragValue's own + // commit-on-Enter / cancel-on-Esc behaviors still win when focused. + let mut apply_via_button = false; ui.with_layout( egui::Layout::right_to_left(egui::Align::Center), |ui| { @@ -366,11 +364,24 @@ impl TimeMockerApp { .add(egui::Button::new("Apply").min_size(egui::vec2(80.0, 0.0))) .clicked() { - self.apply_fake_time(); - ui.memory_mut(|mem| mem.close_popup()); + apply_via_button = true; } }, ); + + let apply_via_enter = ui.input_mut(|i| { + i.consume_key(egui::Modifiers::NONE, egui::Key::Enter) + }); + let cancel_via_esc = ui.input_mut(|i| { + i.consume_key(egui::Modifiers::NONE, egui::Key::Escape) + }); + + if apply_via_button || apply_via_enter { + self.apply_fake_time(); + ui.memory_mut(|mem| mem.close_popup()); + } else if cancel_via_esc { + ui.memory_mut(|mem| mem.close_popup()); + } } fn ui_calendar_grid(&mut self, ui: &mut egui::Ui) { @@ -427,7 +438,7 @@ impl TimeMockerApp { text = text.color(egui::Color32::from_gray(110)); } let mut btn = - egui::Button::new(text).min_size(egui::vec2(28.0, 22.0)); + egui::Button::new(text).min_size(egui::vec2(32.0, 32.0)); if is_selected { btn = btn.fill(egui::Color32::from_rgb(70, 130, 220)); }