mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Find: "Find what:" has the keyboard when the dialog opens (#951)
Ctrl+F and typing searches without clicking the field first, and the field keeps the keyboard after Enter finds a match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
868016ee2f
commit
172952f6e0
@@ -131,6 +131,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-10-18 | Find (Ctrl+F): "Find what:" has the keyboard as soon as the dialog opens, and keeps it after Enter finds a match, so the search can be typed and changed without clicking the field (#951) |
|
||||
| 2026-10-17 | Opening a file caps absurd effect sizes (glow, soft edges, shadows, reflections, on shapes, text and table text) at what Format Shape allows: a glow of 10^18 pt in a .pptx made rendering ask for 34 GB and abort |
|
||||
| 2026-10-17 | Opening a .deckcraft file whose shapes repeat an id gives the repeats fresh ids (groups, layouts and masters too), so the deck validates and edits by id find one shape |
|
||||
| 2026-10-17 | Edit Points: turning a shape into a freeform is one undo step, so Undo brings the original shape back (it used to stay a freeform) |
|
||||
|
||||
@@ -3196,7 +3196,18 @@ fn tabs_dialog(app: &mut SlideApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
/// Home › Find / Replace: step through matches (slide text, table cells, notes) or replace them.
|
||||
fn find_dialog(app: &mut SlideApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
let replace_mode = d.get("replace", "0") == "1";
|
||||
field(ui, d, "Find what:", "text", "");
|
||||
// "Find what:" has the keyboard when the dialog opens, so Ctrl+F and typing finds (#951).
|
||||
let mut what = d.get("text", "");
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_sized(vec2(120.0, 20.0), egui::Label::new("Find what:"));
|
||||
let r = ui.add(egui::TextEdit::singleline(&mut what).desired_width(180.0));
|
||||
// Enter finds the next match and leaves the keyboard here, so the search can be changed.
|
||||
if !d.fields.contains_key("focused") || (r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter))) {
|
||||
r.request_focus();
|
||||
d.fields.insert("focused".into(), "1".into());
|
||||
}
|
||||
});
|
||||
d.fields.insert("text".into(), what);
|
||||
if replace_mode {
|
||||
field(ui, d, "Replace with:", "with", "");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//! Home › Find (Ctrl+F): "Find what:" has the keyboard as soon as the dialog opens (#951).
|
||||
|
||||
use deckcraft_engine::Session;
|
||||
use egui_kittest::Harness;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{Services, SlideApp};
|
||||
|
||||
#[test]
|
||||
fn ctrl_f_puts_the_caret_in_find_what() {
|
||||
let mut s = Session::with_new();
|
||||
s.execute("slide.new", &json!({"layout": "blank"})).unwrap();
|
||||
s.execute("insert.textBox", &json!({"rect": [40, 40, 400, 80], "text": "Hello there"})).unwrap();
|
||||
s.execute("text.exit", &json!({})).unwrap();
|
||||
s.doc_mut().unwrap().selection.shapes.clear();
|
||||
let app = SlideApp::new(s, Services::default());
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_ui_state(
|
||||
|ui, app: &mut SlideApp| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(3);
|
||||
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::F);
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog.as_ref().map(|d| d.id.as_str()), Some("find"));
|
||||
// Typing right away goes into the field, without clicking it first.
|
||||
h.event(egui::Event::Text("there".into()));
|
||||
h.run_steps(3);
|
||||
let typed = h.state().dialog.as_ref().and_then(|d| d.fields.get("text").cloned());
|
||||
assert_eq!(typed.as_deref(), Some("there"));
|
||||
// Enter finds it.
|
||||
h.key_press(egui::Key::Enter);
|
||||
h.run_steps(3);
|
||||
let status = h.state().dialog.as_ref().and_then(|d| d.fields.get("status").cloned());
|
||||
assert!(status.as_deref().is_some_and(|m| m.starts_with("Match 1 of 1")), "Enter didn't find the text: {status:?}");
|
||||
// The keyboard stays in the field afterwards: more typing adds to it.
|
||||
h.event(egui::Event::Text("!".into()));
|
||||
h.run_steps(3);
|
||||
let typed = h.state().dialog.as_ref().and_then(|d| d.fields.get("text").cloned());
|
||||
assert_eq!(typed.as_deref(), Some("there!"));
|
||||
}
|
||||
@@ -2052,6 +2052,8 @@ mod compare_tests;
|
||||
#[cfg(test)]
|
||||
mod eyedropper_tests;
|
||||
#[cfg(test)]
|
||||
mod find_dialog_tests;
|
||||
#[cfg(test)]
|
||||
mod huge_deck_tests;
|
||||
#[cfg(test)]
|
||||
mod ime_tests;
|
||||
|
||||
Reference in New Issue
Block a user