Stop the find input clearing itself while typing
The main-loop mirror of the X-button clear was level-triggered: 'FindQuery=="" && widget has text'. But the widget updates on every keystroke while the logic only stores the query a round-trip later, so any frame drawn in that window still carried FindQuery=="" and wiped the input - the periodic self-clearing. Make it edge-triggered: FindState.ClearSeq bumps once per clear; the frame carries it as FindClearSeq; main wipes the widget input once per NEW value (lastFindClearSeq handshake). findClear bumps it; findReset preserves it (main tracks it monotonically, so a reset to 0 would swallow a later clear). Tests: unit asserts the bump, monotonicity, and close/reopen preservation.
This commit is contained in:
parent
d726ea7244
commit
c789de4b44
|
|
@ -132,6 +132,10 @@ func run(w *app.Window) error {
|
|||
// main-owned input, so main hands key focus to it exactly once per open
|
||||
// (see the focus handoff below).
|
||||
var findBarFocused bool
|
||||
// lastFindClearSeq is the FindClearSeq value whose wipe has already been
|
||||
// applied to the main-owned find input (edge-triggered, see
|
||||
// Frame.FindClearSeq).
|
||||
var lastFindClearSeq int
|
||||
|
||||
for {
|
||||
switch e := w.Event().(type) {
|
||||
|
|
@ -218,12 +222,15 @@ func run(w *app.Window) error {
|
|||
// The logic goroutine handles filtering and triggers a new frame.
|
||||
newQuery := searchEditor.Text()
|
||||
sendQuery := newQuery != frame.Query
|
||||
// The find bar's X button (editor.FindClear) empties the LOGIC-side
|
||||
// query; the main-owned widget still shows the old text, so mirror
|
||||
// the clear into it before the change comparison below (which then
|
||||
// sees "" == "" and forwards nothing).
|
||||
if frame.FindQuery == "" && findEditor.Text() != "" {
|
||||
// Mirror each NEW find-clear (the bar's X button) into the main-
|
||||
// owned widget input exactly once. Edge-triggered on ClearSeq, NOT
|
||||
// "FindQuery==\"\" && widget has text": that level check also fired on
|
||||
// stale frames between the user's typing and the logic storing it,
|
||||
// wiping the input while typing.
|
||||
if frame.FindClearSeq > lastFindClearSeq {
|
||||
lastFindClearSeq = frame.FindClearSeq
|
||||
findEditor.SetText("")
|
||||
w.Invalidate()
|
||||
}
|
||||
newFind := findEditor.Text()
|
||||
sendFind := newFind != frame.FindQuery
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ type Frame struct {
|
|||
// "find_bar" widget's text and forwards changes via FindQueryChan — the
|
||||
// same contract as Query/searchQueryChan.
|
||||
FindQuery string
|
||||
// FindClearSeq mirrors EditorState.Find.ClearSeq: main wipes the widget
|
||||
// input once per NEW value (the X button cleared the logic-side query).
|
||||
FindClearSeq int
|
||||
// WindowStartByte / WindowStartLine / EditSeq: the editor window this
|
||||
// frame's elements describe. The main goroutine forwards them with the
|
||||
// shaped glyph layout (LayoutFeedback) so the logic goroutine can apply
|
||||
|
|
@ -54,6 +57,7 @@ func (l *Logic) frameOf(elems []ui.Element) Frame {
|
|||
FocusedElementID: l.state.FocusedElementID,
|
||||
Query: l.state.Browser.Query,
|
||||
FindQuery: l.state.Editor.Find.Query,
|
||||
FindClearSeq: l.state.Editor.Find.ClearSeq,
|
||||
WindowStartByte: l.state.Editor.IMEWindowStartByte,
|
||||
WindowStartLine: l.state.WindowStartLine,
|
||||
WindowText: l.state.Editor.IMEWindowText,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,15 @@ type FindState struct {
|
|||
SettleByte int
|
||||
SettleScroll ui.Dp
|
||||
SettlePasses int
|
||||
|
||||
// ClearSeq bumps once per clear (the bar's X button, findClear). Main
|
||||
// mirrors each NEW value into the main-owned widget input exactly once —
|
||||
// an edge-triggered handshake, deliberately NOT a level check: a
|
||||
// "FindQuery==\"\" && the widget has text" level check would also fire on
|
||||
// any stale frame between the user's typing (widget updates immediately)
|
||||
// and the logic storing it (a round-trip away), wiping the input while
|
||||
// typing.
|
||||
ClearSeq int
|
||||
}
|
||||
|
||||
// ToggleFind opens the find bar (or closes it when open). It is the tap
|
||||
|
|
@ -95,6 +104,7 @@ func (e *EditorState) findClear() {
|
|||
f.Cur = -1
|
||||
f.Scanning = false
|
||||
f.Gen++
|
||||
f.ClearSeq++
|
||||
f.SettleByte = -1
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +130,10 @@ func (e *EditorState) findReset() {
|
|||
// Monotonic generation bump (do not zero the counter): a stale
|
||||
// in-flight scan carrying a HIGH generation must never equal a fresh
|
||||
// one, or its result would pass the gen gate in applySearchResult.
|
||||
e.Find = FindState{Query: e.Find.Query, Gen: e.Find.Gen + 1, SettleByte: -1}
|
||||
// ClearSeq is preserved: main tracks it monotonically, so resetting it
|
||||
// to 0 would make a later clear (seq 1) indistinguishable from an old
|
||||
// one already mirrored.
|
||||
e.Find = FindState{Query: e.Find.Query, Gen: e.Find.Gen + 1, ClearSeq: e.Find.ClearSeq, SettleByte: -1}
|
||||
}
|
||||
|
||||
// findSetQuery processes a query forwarded from the main-owned "find_bar"
|
||||
|
|
|
|||
|
|
@ -212,6 +212,20 @@ func TestFindClear_EmptiesQueryKeepsBarOpen(t *testing.T) {
|
|||
if e.Find.Gen != oldGen+1 {
|
||||
t.Fatalf("gen %d, want %d (in-flight scan must be superseded)", e.Find.Gen, oldGen+1)
|
||||
}
|
||||
if e.Find.ClearSeq != 1 {
|
||||
t.Fatalf("ClearSeq %d, want 1 (main mirrors wipes on the edge)", e.Find.ClearSeq)
|
||||
}
|
||||
e.findClear()
|
||||
if e.Find.ClearSeq != 2 {
|
||||
t.Fatalf("ClearSeq %d after second clear, want 2 (monotonic)", e.Find.ClearSeq)
|
||||
}
|
||||
// findReset (bar close + reopen) preserves the sequence.
|
||||
TheState.FocusedElementID = "editor_text"
|
||||
e.findClose()
|
||||
e.findShow()
|
||||
if e.Find.ClearSeq != 2 {
|
||||
t.Fatalf("ClearSeq %d after close/reopen, want 2 (preserved)", e.Find.ClearSeq)
|
||||
}
|
||||
// The in-flight scan of the old query must now be dropped.
|
||||
e.applySearchResult(pool.Result{TaskType: pool.TypeSearch, Success: true, Data: pool.SearchData{Gen: oldGen, Matches: [][2]int{{0, 1}}}})
|
||||
if len(e.Find.Matches) != 0 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user