From 2a18e9d0b3c2b68c122bcb87efb92e37d5993c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 00:35:46 -0400 Subject: [PATCH] =?UTF-8?q?Forms=20=C2=B7=20An=20IME=20composition=20resta?= =?UTF-8?q?rts=20the=20caret's=20blink,=20as=20every=20edit=20does,=20and?= =?UTF-8?q?=20logs=20its=20ranges=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/forms/tests/mod.rs | 30 ++++++++++++++++++++++++++++++ src/forms/text/ime.rs | 8 +++++++- tasks/github.md | 7 +++++++ tasks/reviews.md | 1 + 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/forms/tests/mod.rs b/src/forms/tests/mod.rs index f7d9fcf..4be0489 100644 --- a/src/forms/tests/mod.rs +++ b/src/forms/tests/mod.rs @@ -389,3 +389,33 @@ fn only_a_focused_field_blinks(cx: &mut TestAppContext) { cx.run_until_parked(); assert!(redraws.get() > 2, "a focused one blinks"); } + +#[gpui::test] +fn a_composition_shows_the_caret_as_typing_does(cx: &mut TestAppContext) { + let (fields, cx) = open(1, false, cx); + let field = fields[0].clone(); + cx.update(|window, _| window.activate_window()); + focus(&field, cx); + cx.run_until_parked(); + let caret = |cx: &mut VisualTestContext| field.read_with(cx, |input, _| input.caret_on()); + let blink_off = |cx: &mut VisualTestContext| { + cx.executor() + .advance_clock(std::time::Duration::from_millis(530)); + cx.run_until_parked(); + }; + blink_off(cx); + assert!(!caret(cx), "the caret blinks off"); + cx.update(|window, cx| { + field.update(cx, |input, cx| { + input.replace_text_in_range(None, "a", window, cx) + }) + }); + assert!(caret(cx), "typing shows the caret"); + blink_off(cx); + cx.update(|window, cx| { + field.update(cx, |input, cx| { + input.replace_and_mark_text_in_range(None, "n", None, window, cx) + }) + }); + assert!(caret(cx), "a composition shows it too"); +} diff --git a/src/forms/text/ime.rs b/src/forms/text/ime.rs index e40df66..2d4706a 100644 --- a/src/forms/text/ime.rs +++ b/src/forms/text/ime.rs @@ -94,6 +94,12 @@ impl EntityInputHandler for TextInput { if self.disabled { return; } + log::debug!( + "text input {:?}: compose {range_utf16:?} selecting {selected_utf16:?} marked {:?} selection {:?}", + cx.entity_id(), + self.marked, + self.selection + ); let range = range_utf16 .map(|range| self.range_from_utf16(&range)) .or(self.marked.clone()) @@ -114,7 +120,7 @@ impl EntityInputHandler for TextInput { None => range.start + text.len()..range.start + text.len(), }; self.reversed = false; - cx.notify(); + self.restart_blink(cx); } fn bounds_for_range( diff --git a/tasks/github.md b/tasks/github.md index af67251..62373c9 100644 --- a/tasks/github.md +++ b/tasks/github.md @@ -5,3 +5,10 @@ - [x] G3 Issue #3: Checkbox, Switch, Radio, Slider and Select name their role, label and state - [x] G4 Push, comment on #2 and #3, close #2 - [x] G5 PR #4: `Icon::from_path` draws an app's SVG; a path the asset source lacks fails at render + +# GitHub round, 2026-10-05 + +- [x] G6 Issue #7: an IME composition restarts the caret's blink, as every edit does; its ranges are logged; tested +- [ ] G7 Issue #6: `layout::SidebarShell`, a sidebar that runs to the window's top with the traffic lights, beside one or two panes that bring their own headers; tested +- [ ] G8 Issue #6: a Layout story, `stories.json`, AGENTS.md and the PRD line; the story runs live on the site +- [ ] G9 Push, deploy, reply on #6 and #7, close #6 diff --git a/tasks/reviews.md b/tasks/reviews.md index 5d9a590..d62e1de 100644 --- a/tasks/reviews.md +++ b/tasks/reviews.md @@ -382,3 +382,4 @@ | L3 | 1 | PASS | | | L4 | 1 | PASS | | | L5 | 1 | PASS | | +| G6 | 1 | PASS | codex gpt-6.1-sol high |