Find bar X clears the query instead of closing the bar
The X was redundant with the top-bar search icon (both closed the bar).
Now the X empties the query and results but leaves the bar open; closing
is the search icon's toggle.
- editor: new findClear (empties query/matches, bumps Gen so an in-flight
scan of the old query is dropped, disarms the settle) and FindClear
handler; the X icon now runs FindClear.
- main: mirrors the logic-side clear into the main-owned widget input
(frame.FindQuery=="" while the widget still has text -> SetText("")).
- tests: unit findClear (stays open, supersedes in-flight scan); e2e X
clears but keeps the bar open and focus, close now via ToggleFind.
- doc: spec updated (clear button vs icon toggle).
This commit is contained in:
parent
1cbb367b1f
commit
d726ea7244
|
|
@ -218,6 +218,13 @@ 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() != "" {
|
||||
findEditor.SetText("")
|
||||
}
|
||||
newFind := findEditor.Text()
|
||||
sendFind := newFind != frame.FindQuery
|
||||
events := renderer.CheckGestures(e.Source, gtx.Metric)
|
||||
|
|
|
|||
|
|
@ -75,9 +75,12 @@ elsewhere.
|
|||
selection is highlighted in the editor and pushed to the IME. (Shift state
|
||||
is tracked by the app, since Gio's Android bridge drops modifier keys —
|
||||
architecture.md §2.1.)
|
||||
- **In-file search:** a find icon on the top bar (right side) opens a find
|
||||
bar directly below it, with a text input, an "N / M" counter, next/prev
|
||||
navigation, and a close button. The query is a plain substring,
|
||||
- **In-file search:** a find icon on the top bar (right side) toggles a
|
||||
find bar directly below it, with a text input, an "N / M" counter,
|
||||
next/prev navigation, and a clear (X) button. The X empties the query and
|
||||
the results but leaves the bar open; the top-bar icon closes it (and
|
||||
re-opens it). Opening the bar moves key focus to the search input and
|
||||
raises the soft keyboard. The query is a plain substring,
|
||||
case-insensitive, no regex; each change is scanned over the entire
|
||||
in-memory content by a worker (`Search` task), so typing never blocks the
|
||||
UI and stale scans are dropped by generation. Next/prev select and scroll
|
||||
|
|
|
|||
|
|
@ -66,8 +66,12 @@ func FindNext(data any) { findStep(1) }
|
|||
|
||||
func FindPrev(data any) { findStep(-1) }
|
||||
|
||||
// FindClose is the tap handler of the find bar's close button.
|
||||
func FindClose(data any) { TheState.Editor.findClose() }
|
||||
// FindClear is the tap handler of the find bar's X button: it empties the
|
||||
// query (and the results) but leaves the bar open. Closing the bar is the
|
||||
// search icon's toggle (ToggleFind). The main-owned widget's text is
|
||||
// mirrored to empty by the main loop (frame.FindQuery == "" while the
|
||||
// widget still has text, see cmd/pad/main.go).
|
||||
func FindClear(data any) { TheState.Editor.findClear() }
|
||||
|
||||
// findShow opens the find bar. The text input is main-owned, so logic takes
|
||||
// key focus off the editor (FocusedElementID) and, if a previous query has
|
||||
|
|
@ -82,6 +86,18 @@ func (e *EditorState) findShow() {
|
|||
}
|
||||
}
|
||||
|
||||
// findClear empties the query and the results (find bar X button). Gen is
|
||||
// bumped so an in-flight scan of the old query is dropped when it lands.
|
||||
func (e *EditorState) findClear() {
|
||||
f := &e.Find
|
||||
f.Query = ""
|
||||
f.Matches = nil
|
||||
f.Cur = -1
|
||||
f.Scanning = false
|
||||
f.Gen++
|
||||
f.SettleByte = -1
|
||||
}
|
||||
|
||||
// findClose hides the find bar. The query is kept so re-opening shows it
|
||||
// (the main-owned widget keeps its text); matches are dropped — the next
|
||||
// show re-scans, so they are never stale.
|
||||
|
|
|
|||
|
|
@ -193,3 +193,28 @@ func TestFindReset_SupersedesInflight(t *testing.T) {
|
|||
t.Fatal("superseded result was applied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindClear_EmptiesQueryKeepsBarOpen(t *testing.T) {
|
||||
findTestState()
|
||||
e := &TheState.Editor
|
||||
e.Find.Scanning = true // a scan of the old query is in flight
|
||||
e.Find.SettleByte = 5
|
||||
oldGen := e.Find.Gen
|
||||
|
||||
e.findClear()
|
||||
|
||||
if !e.Find.Visible {
|
||||
t.Fatal("findClear closed the bar; it must stay open")
|
||||
}
|
||||
if e.Find.Query != "" || len(e.Find.Matches) != 0 || e.Find.Cur != -1 || e.Find.Scanning || e.Find.SettleByte != -1 {
|
||||
t.Fatalf("state after clear: %+v", e.Find)
|
||||
}
|
||||
if e.Find.Gen != oldGen+1 {
|
||||
t.Fatalf("gen %d, want %d (in-flight scan must be superseded)", e.Find.Gen, oldGen+1)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal("cleared query's in-flight scan was applied")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1862,7 +1862,7 @@ func markDirty() {
|
|||
const FindBarHeight = ui.Dp(40)
|
||||
|
||||
// buildFindBar lays out the in-file search bar: [input][counter][prev][next]
|
||||
// [close], full screen width, directly below the margin-free top bar (the
|
||||
// [clear-X], full screen width, directly below the margin-free top bar (the
|
||||
// bars merge, so this one does too; inner content keeps the margin). The
|
||||
// input is a MAIN-owned GioEditor registered as "find_bar" (the browser
|
||||
// "search_bar" precedent, architecture.md §1); the counter and buttons are
|
||||
|
|
@ -1873,7 +1873,7 @@ func buildFindBar(screenWidth ui.Dp) ui.Element {
|
|||
h := FindBarHeight
|
||||
topY := ui.Dp(32) // top bar height (see EditorLayout)
|
||||
|
||||
// Right-hand button column: prev, next, close.
|
||||
// Right-hand button column: prev, next, clear (X).
|
||||
iconW := ui.IconSize
|
||||
closeX := screenWidth - margin - iconW
|
||||
nextX := closeX - gap - iconW
|
||||
|
|
@ -1904,7 +1904,7 @@ func buildFindBar(screenWidth ui.Dp) ui.Element {
|
|||
ui.NewIcon("chevron_down", ui.Region{X: nextX, Y: ui.Dp(8), W: iconW, H: iconW}, 0,
|
||||
[]ui.Interaction{{Gesture: ui.Tap, Handler: FindNext}}),
|
||||
ui.NewIcon("close", ui.Region{X: closeX, Y: ui.Dp(8), W: iconW, H: iconW}, 0,
|
||||
[]ui.Interaction{{Gesture: ui.Tap, Handler: FindClose}}),
|
||||
[]ui.Interaction{{Gesture: ui.Tap, Handler: FindClear}}),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,13 +177,26 @@ func TestRealFile_FindBarAndNavigation(t *testing.T) {
|
|||
t.Fatalf("cur %d, want last (wrapped)", cur)
|
||||
}
|
||||
|
||||
// The X button clears the query but LEAVES the bar open (closing is the
|
||||
// top-bar icon's toggle).
|
||||
h.SendInput([]ui.InputEvent{{Handler: editor.FindClear, Data: ui.Point{}}})
|
||||
if !frameHasFindBar(h) {
|
||||
t.Fatal("find bar closed by the clear button; it should stay open")
|
||||
}
|
||||
clear, _ := h.Inspect(func(st *editor.State) any {
|
||||
return [3]any{st.Editor.Find.Query, len(st.Editor.Find.Matches), st.FocusedElementID}
|
||||
})
|
||||
if clear.([3]any) != [3]any{"", 0, "find_bar"} {
|
||||
t.Fatalf("after clear: query/matches/focus = %v, want \"\"/0/find_bar", clear)
|
||||
}
|
||||
|
||||
// A query with no hits reports zero matches.
|
||||
h.Logic().FindQueryChan() <- "no-such-text"
|
||||
waitForFindMatches(t, h, 0)
|
||||
|
||||
// Close the find bar: it leaves the frame and focus returns to the
|
||||
// editor.
|
||||
h.SendInput([]ui.InputEvent{{Handler: editor.FindClose, Data: ui.Point{}}})
|
||||
// Close the find bar via the top-bar icon toggle: it leaves the frame
|
||||
// and focus returns to the editor.
|
||||
h.SendInput([]ui.InputEvent{{Handler: editor.ToggleFind, Data: ui.Point{}}})
|
||||
if _, err := h.WaitForFrameCount(h.FrameCount()+1, 5*time.Second); err != nil {
|
||||
t.Fatalf("wait for close frame: %v", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user