diff --git a/app/window.go b/app/window.go index abf86447..804b7510 100644 --- a/app/window.go +++ b/app/window.go @@ -69,6 +69,8 @@ type Window struct { delayedDraw *time.Timer // viewport is the latest frame size with insets applied. viewport image.Rectangle + // metric is the metric from the most recent frame. + metric unit.Metric queue queue cursor pointer.Cursor @@ -504,10 +506,28 @@ func (c *callbacks) MoveFocus(dir router.FocusDirection) { } func (w *Window) moveFocus(dir router.FocusDirection, d driver) { - w.queue.q.MoveFocus(dir) + if w.queue.q.MoveFocus(dir) { + w.queue.q.RevealFocus(w.viewport) + } else { + var v image.Point + switch dir { + case router.FocusRight: + v = image.Pt(+1, 0) + case router.FocusLeft: + v = image.Pt(-1, 0) + case router.FocusDown: + v = image.Pt(0, +1) + case router.FocusUp: + v = image.Pt(0, -1) + default: + return + } + const scrollABit = 50 + dist := v.Mul(w.metric.Px(unit.Dp(scrollABit))) + w.queue.q.ScrollFocus(dist) + } w.setNextFrame(time.Time{}) w.updateAnimation(d) - w.queue.q.ScrollFocus(w.viewport) } func (c *callbacks) ClickFocus() { @@ -748,6 +768,7 @@ func (w *Window) processEvent(d driver, e event.Event) { // No drawing if not visible. break } + w.metric = e2.Metric var frameStart time.Time if w.queue.q.Profiling() { frameStart = time.Now() @@ -771,7 +792,7 @@ func (w *Window) processEvent(d driver, e event.Event) { } // Scroll to focus if viewport is shrinking in any dimension. if old, new := w.viewport.Size(), viewport.Size(); new.X < old.X || new.Y < old.Y { - w.queue.q.ScrollFocus(viewport) + w.queue.q.RevealFocus(viewport) } w.viewport = viewport size := e2.Size // save the initial window size as the decorations will change it. diff --git a/io/router/key.go b/io/router/key.go index 3ef60dc2..1bbdb1a4 100644 --- a/io/router/key.go +++ b/io/router/key.go @@ -169,7 +169,8 @@ func (q *keyQueue) updateFocusLayout() { } } -func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) { +// MoveFocus attempts to move the focus in the direction of dir, returning true if it succeeds. +func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) bool { order := 0 if q.focus != nil { order = q.handlers[q.focus].dirOrder @@ -194,6 +195,7 @@ func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) { } order = (order + len(q.order)) % len(q.order) q.setFocus(q.order[order], events) + return true case FocusRight, FocusLeft: next := order if q.focus != nil { @@ -206,6 +208,7 @@ func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) { newFocus := q.dirOrder[next] if newFocus.row == focus.row { q.setFocus(newFocus.tag, events) + return true } } case FocusUp, FocusDown: @@ -242,8 +245,10 @@ func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) { } if closest != nil { q.setFocus(closest, events) + return true } } + return false } func (q *keyQueue) Push(e event.Event, events *handlerEvents) { diff --git a/io/router/key_test.go b/io/router/key_test.go index cddab79b..c3a7510b 100644 --- a/io/router/key_test.go +++ b/io/router/key_test.go @@ -285,7 +285,7 @@ func TestFocusScroll(t *testing.T) { r.Frame(ops) r.MoveFocus(FocusLeft) - r.ScrollFocus(image.Rect(0, 0, 15, 40)) + r.RevealFocus(image.Rect(0, 0, 15, 40)) evts := r.Events(h) assertScrollEvent(t, evts[len(evts)-1], f32.Pt(5, -10)) } diff --git a/io/router/router.go b/io/router/router.go index ebb2b1e7..1fb3788a 100644 --- a/io/router/router.go +++ b/io/router/router.go @@ -149,13 +149,13 @@ func (q *Router) Queue(events ...event.Event) bool { return q.handlers.HadEvents() } -func (q *Router) MoveFocus(dir FocusDirection) { - q.key.queue.MoveFocus(dir, &q.handlers) +func (q *Router) MoveFocus(dir FocusDirection) bool { + return q.key.queue.MoveFocus(dir, &q.handlers) } -// ScrollFocus scrolls the current focus (if any) into viewport +// RevealFocus scrolls the current focus (if any) into viewport // if there are scrollable parent handlers. -func (q *Router) ScrollFocus(viewport image.Rectangle) { +func (q *Router) RevealFocus(viewport image.Rectangle) { focus := q.key.queue.focus if focus == nil { return @@ -175,11 +175,20 @@ func (q *Router) ScrollFocus(viewport image.Rectangle) { if s.Y == 0 { s.Y = bottomright.Y } + q.ScrollFocus(s) +} + +// ScrollFocus scrolls the focused widget, if any, by dist. +func (q *Router) ScrollFocus(dist image.Point) { + focus := q.key.queue.focus + if focus == nil { + return + } area := q.key.queue.AreaFor(focus) q.pointer.queue.Deliver(area, pointer.Event{ Type: pointer.Scroll, Source: pointer.Touch, - Scroll: fpt(s), + Scroll: fpt(dist), }, &q.handlers) }