Add keyboard-first accessibility behavior

This commit is contained in:
Joe Julian
2026-03-29 11:28:17 -07:00
parent dd0b0b6f6e
commit 5eb2068d3e
6 changed files with 712 additions and 52 deletions
+26 -17
View File
@@ -138,6 +138,7 @@ type ui struct {
loadingMessage string
statusMessage string
errorMessage string
keyboardFocus focusID
}
var (
@@ -213,6 +214,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession) *ui {
u.eyeOffIcon, _ = widget.NewIcon(icons.ActionVisibilityOff)
u.copyIcon, _ = widget.NewIcon(icons.ContentContentCopy)
u.passwordProfile.SetText("strong")
u.keyboardFocus = focusSearch
u.filter()
return u
}
@@ -772,7 +774,7 @@ func (u *ui) listPanel(gtx layout.Context) layout.Dimensions {
if u.mode == "phone" {
gtx.Constraints.Min.X = gtx.Constraints.Max.X
}
return outlinedField(gtx, func(gtx layout.Context) layout.Dimensions {
return outlinedFieldState(gtx, u.isFocused(focusSearch), func(gtx layout.Context) layout.Dimensions {
editor := material.Editor(u.theme, &u.search, "Search vault")
editor.Color = u.theme.Palette.Fg
editor.HintColor = mutedColor
@@ -876,7 +878,7 @@ func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item
)
})
}
if item.ID == u.state.SelectedEntryID {
if item.ID == u.state.SelectedEntryID || u.isFocused(listFocusID(idx)) {
return layout.Stack{}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions {
size := gtx.Constraints.Min
@@ -886,8 +888,14 @@ func (u *ui) entryRow(gtx layout.Context, click *widget.Clickable, idx int, item
if size.Y == 0 {
size.Y = gtx.Constraints.Max.Y
}
paint.FillShape(gtx.Ops, selectedColor, clip.Rect{Max: size}.Op())
paint.FillShape(gtx.Ops, selectedEdge, clip.Rect{Max: image.Pt(4, size.Y)}.Op())
fillColor := selectedColor
edgeColor := selectedEdge
if u.isFocused(listFocusID(idx)) && item.ID != u.state.SelectedEntryID {
fillColor = color.NRGBA{R: 235, G: 241, B: 238, A: 255}
edgeColor = accentColor
}
paint.FillShape(gtx.Ops, fillColor, clip.Rect{Max: size}.Op())
paint.FillShape(gtx.Ops, edgeColor, clip.Rect{Max: image.Pt(4, size.Y)}.Op())
return layout.Dimensions{Size: size}
}),
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
@@ -1104,8 +1112,7 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
u.filter()
}
btn := material.Button(u.theme, &u.breadcrumbs[index], label)
btn.Background = color.NRGBA{R: 239, G: 236, B: 229, A: 255}
btn.Color = accentColor
btn.Background, btn.Color = buttonFocusColors(u.isFocused(breadcrumbFocusID(index)))
btn.TextSize = unit.Sp(12)
btn.Inset = layout.Inset{Top: 6, Bottom: 6, Left: 10, Right: 10}
return btn.Layout(gtx)
@@ -1213,14 +1220,14 @@ func compactCard(gtx layout.Context, w layout.Widget) layout.Dimensions {
})
}
func outlinedField(gtx layout.Context, w layout.Widget) layout.Dimensions {
border := color.NRGBA{R: 202, G: 194, B: 180, A: 255}
func outlinedFieldState(gtx layout.Context, focused bool, w layout.Widget) layout.Dimensions {
appearance := fieldFocusAppearance(gtx.Metric, focused)
size := gtx.Constraints.Min
if size.X == 0 {
size.X = gtx.Constraints.Max.X
}
if size.Y == 0 {
size.Y = gtx.Dp(unit.Dp(44))
size.Y = appearance.MinHeight
}
gtx.Constraints.Min = size
return layout.Stack{}.Layout(gtx,
@@ -1229,10 +1236,13 @@ func outlinedField(gtx layout.Context, w layout.Widget) layout.Dimensions {
return layout.Dimensions{Size: size}
}),
layout.Expanded(func(gtx layout.Context) layout.Dimensions {
paint.FillShape(gtx.Ops, border, clip.Rect{Max: image.Pt(size.X, 1)}.Op())
paint.FillShape(gtx.Ops, border, clip.Rect{Min: image.Pt(0, size.Y-1), Max: image.Pt(size.X, size.Y)}.Op())
paint.FillShape(gtx.Ops, border, clip.Rect{Max: image.Pt(1, size.Y)}.Op())
paint.FillShape(gtx.Ops, border, clip.Rect{Min: image.Pt(size.X-1, 0), Max: image.Pt(size.X, size.Y)}.Op())
return drawFocusOutline(gtx, appearance, size)
}),
layout.Expanded(func(gtx layout.Context) layout.Dimensions {
paint.FillShape(gtx.Ops, appearance.BorderColor, clip.Rect{Max: image.Pt(size.X, 1)}.Op())
paint.FillShape(gtx.Ops, appearance.BorderColor, clip.Rect{Min: image.Pt(0, size.Y-1), Max: image.Pt(size.X, size.Y)}.Op())
paint.FillShape(gtx.Ops, appearance.BorderColor, clip.Rect{Max: image.Pt(1, size.Y)}.Op())
paint.FillShape(gtx.Ops, appearance.BorderColor, clip.Rect{Min: image.Pt(size.X-1, 0), Max: image.Pt(size.X, size.Y)}.Op())
return layout.Dimensions{Size: size}
}),
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
@@ -1245,8 +1255,8 @@ func outlinedField(gtx layout.Context, w layout.Widget) layout.Dimensions {
if dims.Size.Y < min.Y {
dims.Size.Y = min.Y
}
if dims.Size.Y < gtx.Dp(unit.Dp(44)) {
dims.Size.Y = gtx.Dp(unit.Dp(44))
if dims.Size.Y < appearance.MinHeight {
dims.Size.Y = appearance.MinHeight
}
return dims
}),
@@ -1255,8 +1265,7 @@ func outlinedField(gtx layout.Context, w layout.Widget) layout.Dimensions {
func tonedButton(gtx layout.Context, th *material.Theme, click *widget.Clickable, label string) layout.Dimensions {
btn := material.Button(th, click, label)
btn.Background = color.NRGBA{R: 231, G: 239, B: 235, A: 255}
btn.Color = accentColor
btn.Background, btn.Color = buttonFocusColors(false)
btn.CornerRadius = unit.Dp(10)
btn.TextSize = unit.Sp(15)
return btn.Layout(gtx)