mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-05 09:25:38 +00:00
io/router,app: scroll a bit when reaching the end in a focus direction
List was recently changed to include an extra child at each end, to automatically scroll when reaching the end of a focus direction. However, if List includes unfocusable children that strategy may fail. This change adds another fallback where app.Window will scroll a constant amount in the focus direction, to reveal more children. For https://github.com/tailscale/tailscale/issues/4278. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+24
-3
@@ -69,6 +69,8 @@ type Window struct {
|
|||||||
delayedDraw *time.Timer
|
delayedDraw *time.Timer
|
||||||
// viewport is the latest frame size with insets applied.
|
// viewport is the latest frame size with insets applied.
|
||||||
viewport image.Rectangle
|
viewport image.Rectangle
|
||||||
|
// metric is the metric from the most recent frame.
|
||||||
|
metric unit.Metric
|
||||||
|
|
||||||
queue queue
|
queue queue
|
||||||
cursor pointer.Cursor
|
cursor pointer.Cursor
|
||||||
@@ -504,10 +506,28 @@ func (c *callbacks) MoveFocus(dir router.FocusDirection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) moveFocus(dir router.FocusDirection, d driver) {
|
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.setNextFrame(time.Time{})
|
||||||
w.updateAnimation(d)
|
w.updateAnimation(d)
|
||||||
w.queue.q.ScrollFocus(w.viewport)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *callbacks) ClickFocus() {
|
func (c *callbacks) ClickFocus() {
|
||||||
@@ -748,6 +768,7 @@ func (w *Window) processEvent(d driver, e event.Event) {
|
|||||||
// No drawing if not visible.
|
// No drawing if not visible.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
w.metric = e2.Metric
|
||||||
var frameStart time.Time
|
var frameStart time.Time
|
||||||
if w.queue.q.Profiling() {
|
if w.queue.q.Profiling() {
|
||||||
frameStart = time.Now()
|
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.
|
// 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 {
|
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
|
w.viewport = viewport
|
||||||
size := e2.Size // save the initial window size as the decorations will change it.
|
size := e2.Size // save the initial window size as the decorations will change it.
|
||||||
|
|||||||
+6
-1
@@ -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
|
order := 0
|
||||||
if q.focus != nil {
|
if q.focus != nil {
|
||||||
order = q.handlers[q.focus].dirOrder
|
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)
|
order = (order + len(q.order)) % len(q.order)
|
||||||
q.setFocus(q.order[order], events)
|
q.setFocus(q.order[order], events)
|
||||||
|
return true
|
||||||
case FocusRight, FocusLeft:
|
case FocusRight, FocusLeft:
|
||||||
next := order
|
next := order
|
||||||
if q.focus != nil {
|
if q.focus != nil {
|
||||||
@@ -206,6 +208,7 @@ func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) {
|
|||||||
newFocus := q.dirOrder[next]
|
newFocus := q.dirOrder[next]
|
||||||
if newFocus.row == focus.row {
|
if newFocus.row == focus.row {
|
||||||
q.setFocus(newFocus.tag, events)
|
q.setFocus(newFocus.tag, events)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case FocusUp, FocusDown:
|
case FocusUp, FocusDown:
|
||||||
@@ -242,8 +245,10 @@ func (q *keyQueue) MoveFocus(dir FocusDirection, events *handlerEvents) {
|
|||||||
}
|
}
|
||||||
if closest != nil {
|
if closest != nil {
|
||||||
q.setFocus(closest, events)
|
q.setFocus(closest, events)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ func TestFocusScroll(t *testing.T) {
|
|||||||
r.Frame(ops)
|
r.Frame(ops)
|
||||||
|
|
||||||
r.MoveFocus(FocusLeft)
|
r.MoveFocus(FocusLeft)
|
||||||
r.ScrollFocus(image.Rect(0, 0, 15, 40))
|
r.RevealFocus(image.Rect(0, 0, 15, 40))
|
||||||
evts := r.Events(h)
|
evts := r.Events(h)
|
||||||
assertScrollEvent(t, evts[len(evts)-1], f32.Pt(5, -10))
|
assertScrollEvent(t, evts[len(evts)-1], f32.Pt(5, -10))
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -149,13 +149,13 @@ func (q *Router) Queue(events ...event.Event) bool {
|
|||||||
return q.handlers.HadEvents()
|
return q.handlers.HadEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Router) MoveFocus(dir FocusDirection) {
|
func (q *Router) MoveFocus(dir FocusDirection) bool {
|
||||||
q.key.queue.MoveFocus(dir, &q.handlers)
|
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.
|
// 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
|
focus := q.key.queue.focus
|
||||||
if focus == nil {
|
if focus == nil {
|
||||||
return
|
return
|
||||||
@@ -175,11 +175,20 @@ func (q *Router) ScrollFocus(viewport image.Rectangle) {
|
|||||||
if s.Y == 0 {
|
if s.Y == 0 {
|
||||||
s.Y = bottomright.Y
|
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)
|
area := q.key.queue.AreaFor(focus)
|
||||||
q.pointer.queue.Deliver(area, pointer.Event{
|
q.pointer.queue.Deliver(area, pointer.Event{
|
||||||
Type: pointer.Scroll,
|
Type: pointer.Scroll,
|
||||||
Source: pointer.Touch,
|
Source: pointer.Touch,
|
||||||
Scroll: fpt(s),
|
Scroll: fpt(dist),
|
||||||
}, &q.handlers)
|
}, &q.handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user