@@ -10,6 +10,7 @@ import (
1010 "unsafe"
1111
1212 "go.uber.org/zap"
13+ "golang.org/x/sys/windows"
1314
1415 "github.com/y3owk1n/neru/internal/adapter/overlay/render/badge"
1516 gridcomponent "github.com/y3owk1n/neru/internal/adapter/overlay/render/grid"
@@ -26,8 +27,40 @@ const (
2627 winSubgridFontScale = 0.7
2728)
2829
30+ // overlayWindow is the surface the grid overlay paints on: what winOverlay
31+ // asks of a winplatform.OverlayWindow, and nothing it does not. The concrete
32+ // window is the one implementation that ships; the tests in this package
33+ // stand a recording one in for it, because reaching the drawing code through
34+ // the real type means creating a layered HWND on an interactive desktop.
35+ type overlayWindow interface {
36+ HWND () windows.HWND
37+ Healthy () bool
38+ Visible () bool
39+ Bounds () image.Rectangle
40+ Backend () string
41+ Show ()
42+ Hide ()
43+ Clear ()
44+ ResizeToActiveScreen () error
45+ Destroy ()
46+ FillRect (bounds image.Rectangle , color uint32 )
47+ StrokeRect (bounds image.Rectangle , color uint32 , lineWidth float64 )
48+ FillRoundedRect (bounds image.Rectangle , radius float64 , color uint32 )
49+ StrokeRoundedRect (bounds image.Rectangle , radius float64 , color uint32 , lineWidth float64 )
50+ FillTriangle (vertexA , vertexB , vertexC image.Point , color uint32 )
51+ DrawTextCentered (
52+ text string ,
53+ bounds image.Rectangle ,
54+ fontFamily string ,
55+ fontSize float64 ,
56+ color uint32 ,
57+ )
58+ DrawPointerGlyph (center image.Point , size int , char string , fontFamily string , color uint32 )
59+ Flush () error
60+ }
61+
2962type winOverlay struct {
30- window * winplatform. OverlayWindow
63+ window overlayWindow
3164 logger * zap.Logger
3265 // renderMu is the manager's lock, which every draw here runs under; the
3366 // transition goroutine takes it per frame (transition.go).
@@ -275,7 +308,7 @@ func (o *winOverlay) ShowSubgrid(
275308
276309 o .currentSubgrid = cell
277310 o .gridPointer = pointer
278- o .repaintSubgrid ()
311+ o .redrawGrid ()
279312}
280313
281314// SetGridPointer records where grid mode's pointer stand-in belongs and
@@ -288,11 +321,6 @@ func (o *winOverlay) ShowSubgrid(
288321// repainted. The record is kept even when nothing can be painted yet, for the
289322// same reason UpdateGridMatches keeps its prefix while the window is hidden:
290323// the next Show reads it.
291- //
292- // With a subgrid open, the repaint is the subgrid's own: a full grid redraw
293- // here would put the parent cells back under it, which is the repaint
294- // docs/CROSS_PLATFORM.md already reports for a narrowing keystroke and one
295- // this call has no reason to add to.
296324func (o * winOverlay ) SetGridPointer (pointer recursivegridcomponent.VirtualPointerState ) {
297325 if o == nil || pointer == o .gridPointer {
298326 return
@@ -304,12 +332,6 @@ func (o *winOverlay) SetGridPointer(pointer recursivegridcomponent.VirtualPointe
304332 return
305333 }
306334
307- if o .currentSubgrid != nil {
308- o .repaintSubgrid ()
309-
310- return
311- }
312-
313335 o .redrawGrid ()
314336}
315337
@@ -420,6 +442,21 @@ func (o *winOverlay) backendName() string {
420442 return o .window .Backend ()
421443}
422444
445+ // redrawGrid paints the grid surface as it currently stands, which is either
446+ // the subgrid one cell was opened into or the cells themselves, with the
447+ // pointer stand-in on whichever it is. It is the only answer to "what does
448+ // this surface show", so the open, a narrowing keystroke, a pointer move and
449+ // the hide-unmatched toggle's next repaint all arrive at the same screen.
450+ //
451+ // The subgrid is the whole surface while one is open (#1491, #1610). macOS
452+ // settles that: its ShowSubgrid (adapter/overlay/render/grid/overlay_darwin.go)
453+ // clears the overlay window and hands NeruDrawGridCells the nine subgrid cells,
454+ // and that bridge replaces the view's cell array rather than adding to it, so
455+ // the parent cells leave the reference platform's overlay for as long as the
456+ // subgrid is up. Linux agrees since #1491. The guard lives here rather than at
457+ // each caller because every repaint reaches this: a narrowing keystroke that
458+ // put the parent cells back under the subgrid changed what was on screen
459+ // without the user asking for it.
423460func (o * winOverlay ) redrawGrid () {
424461 o .redrawGridWithoutFlush ()
425462 o .flushOverlay ("grid" )
@@ -448,6 +485,27 @@ func (o *winOverlay) redrawGridWithoutFlush() {
448485
449486 o .Clear ()
450487
488+ if o .currentSubgrid != nil {
489+ o .drawSubgrid (o .currentSubgrid .Bounds (), o .cachedStyle )
490+ } else {
491+ o .drawGridCells ()
492+ }
493+
494+ o .drawGridPointer (o .gridPointer )
495+
496+ if o .logger != nil {
497+ o .logger .Debug (
498+ "redraw complete" ,
499+ zap .Int ("cells" , len (o .cachedGrid .AllCells ())),
500+ zap .Bool ("subgrid" , o .currentSubgrid != nil ),
501+ zap .Bool ("healthy" , o .window .Healthy ()),
502+ )
503+ }
504+ }
505+
506+ // drawGridCells paints every cell of the cached grid, marked against the
507+ // prefix typed so far and thinned by hide-unmatched.
508+ func (o * winOverlay ) drawGridCells () {
451509 style := o .cachedStyle
452510 prefix := o .currentPrefix
453511
@@ -482,20 +540,6 @@ func (o *winOverlay) redrawGridWithoutFlush() {
482540 )
483541 }
484542 }
485-
486- if o .currentSubgrid != nil {
487- o .drawSubgrid (o .currentSubgrid .Bounds (), style )
488- }
489-
490- o .drawGridPointer (o .gridPointer )
491-
492- if o .logger != nil {
493- o .logger .Debug (
494- "redraw complete" ,
495- zap .Int ("cells" , len (o .cachedGrid .AllCells ())),
496- zap .Bool ("healthy" , o .window .Healthy ()),
497- )
498- }
499543}
500544
501545func (o * winOverlay ) flushOverlay (context string ) {
@@ -517,17 +561,10 @@ func (o *winOverlay) flushOverlay(context string) {
517561 }
518562}
519563
520- // repaintSubgrid paints the open subgrid and the pointer on it as the whole
521- // surface (#1491). The keys it draws with were handed over by the manager
522- // when the subgrid was opened, and the surface has not been rebuilt since:
523- // only a manager draw rebuilds it, and every one of those syncs the keys.
524- func (o * winOverlay ) repaintSubgrid () {
525- o .Clear ()
526- o .drawSubgrid (o .currentSubgrid .Bounds (), o .cachedStyle )
527- o .drawGridPointer (o .gridPointer )
528- o .flushOverlay ("subgrid" )
529- }
530-
564+ // drawSubgrid paints the finer grid inside one cell. The keys it draws with
565+ // were handed over by the manager when the subgrid was opened, and the
566+ // surface has not been rebuilt since: only a manager draw rebuilds it, and
567+ // every one of those syncs the keys.
531568func (o * winOverlay ) drawSubgrid (bounds image.Rectangle , style gridcomponent.Style ) {
532569 // The keys the subgrid is drawn with, which are the keys the mode layer
533570 // selects on (internal/domain/grid/subgrid_keys.go).
0 commit comments