Add explicit header dropdown layout types
This commit is contained in:
@@ -5311,77 +5311,63 @@ func (u *ui) headerActions(gtx layout.Context) layout.Dimensions {
|
|||||||
return layout.Dimensions{}
|
return layout.Dimensions{}
|
||||||
}
|
}
|
||||||
spacing := gtx.Dp(unit.Dp(8))
|
spacing := gtx.Dp(unit.Dp(8))
|
||||||
rowOriginX := 0
|
metrics := headerActionMetrics{Spacing: spacing}
|
||||||
var syncDims, lockDims, mainDims layout.Dimensions
|
|
||||||
row := func(gtx layout.Context) layout.Dimensions {
|
row := func(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
syncDims = u.syncButtonGroup(gtx)
|
metrics.SyncDims = u.syncButtonGroup(gtx)
|
||||||
return syncDims
|
return metrics.SyncDims
|
||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
btn := material.Button(u.theme, &u.lockVault, "Lock")
|
btn := material.Button(u.theme, &u.lockVault, "Lock")
|
||||||
lockDims = btn.Layout(gtx)
|
metrics.LockDims = btn.Layout(gtx)
|
||||||
return lockDims
|
return metrics.LockDims
|
||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Width: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
mainDims = u.mainMenuButtonGroup(gtx)
|
metrics.MainDims = u.mainMenuButtonGroup(gtx)
|
||||||
return mainDims
|
return metrics.MainDims
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
rowOps := op.Record(gtx.Ops)
|
rowOps := op.Record(gtx.Ops)
|
||||||
rowDims := row(gtx)
|
metrics.RowDims = row(gtx)
|
||||||
rowCall := rowOps.Stop()
|
rowCall := rowOps.Stop()
|
||||||
|
|
||||||
if u.mode == "phone" {
|
if u.mode == "phone" {
|
||||||
rowOriginX = max(0, gtx.Constraints.Max.X-rowDims.Size.X)
|
metrics.RowOriginX = max(0, gtx.Constraints.Max.X-metrics.RowDims.Size.X)
|
||||||
}
|
}
|
||||||
|
|
||||||
drawMenu := func(menu layout.Widget, triggerRightX, triggerBottomY int) {
|
surface := dropdownSurface{ContainerWidth: gtx.Constraints.Max.X, LeftInset: 0, TopInset: 0}
|
||||||
menuGTX := gtx
|
|
||||||
menuGTX.Constraints.Min = image.Point{}
|
|
||||||
menuGTX.Constraints.Max.X = gtx.Constraints.Max.X
|
|
||||||
menuOps := op.Record(gtx.Ops)
|
|
||||||
menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, menu)
|
|
||||||
menuCall := menuOps.Stop()
|
|
||||||
menuX := anchoredMenuOriginX(gtx.Constraints.Max.X, rowOriginX, triggerRightX, menuDims.Size.X)
|
|
||||||
stack := op.Offset(image.Pt(menuX, triggerBottomY)).Push(gtx.Ops)
|
|
||||||
menuCall.Add(gtx.Ops)
|
|
||||||
stack.Pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
rowStack := op.Offset(image.Pt(rowOriginX, 0)).Push(gtx.Ops)
|
rowStack := op.Offset(image.Pt(metrics.RowOriginX, 0)).Push(gtx.Ops)
|
||||||
rowCall.Add(gtx.Ops)
|
rowCall.Add(gtx.Ops)
|
||||||
rowStack.Pop()
|
rowStack.Pop()
|
||||||
|
|
||||||
if u.mode == "phone" {
|
if u.mode == "phone" {
|
||||||
if u.syncMenuOpen {
|
if u.syncMenuOpen {
|
||||||
u.phoneSyncMenuVisible = true
|
u.phoneSyncMenuVisible = true
|
||||||
u.phoneSyncMenuAnchor = image.Pt(rowOriginX+syncDims.Size.X, rowDims.Size.Y)
|
u.phoneSyncMenuAnchor = metrics.syncAnchor().point()
|
||||||
}
|
}
|
||||||
if u.mainMenuOpen {
|
if u.mainMenuOpen {
|
||||||
triggerRightX := syncDims.Size.X + spacing + lockDims.Size.X + spacing + mainDims.Size.X
|
|
||||||
u.phoneMainMenuVisible = true
|
u.phoneMainMenuVisible = true
|
||||||
u.phoneMainMenuAnchor = image.Pt(rowOriginX+triggerRightX, rowDims.Size.Y)
|
u.phoneMainMenuAnchor = metrics.mainAnchor().point()
|
||||||
}
|
}
|
||||||
width := gtx.Constraints.Max.X
|
width := gtx.Constraints.Max.X
|
||||||
return layout.Dimensions{Size: image.Pt(width, rowDims.Size.Y)}
|
return layout.Dimensions{Size: image.Pt(width, metrics.RowDims.Size.Y)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.syncMenuOpen {
|
if u.syncMenuOpen {
|
||||||
drawMenu(u.syncMenu, syncDims.Size.X, rowDims.Size.Y)
|
surface.draw(gtx, metrics.syncAnchor(), u.syncMenu)
|
||||||
}
|
}
|
||||||
if u.mainMenuOpen {
|
if u.mainMenuOpen {
|
||||||
triggerRightX := syncDims.Size.X + spacing + lockDims.Size.X + spacing + mainDims.Size.X
|
surface.draw(gtx, metrics.mainAnchor(), u.mainMenu)
|
||||||
drawMenu(u.mainMenu, triggerRightX, rowDims.Size.Y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width := rowDims.Size.X
|
width := metrics.RowDims.Size.X
|
||||||
return layout.Dimensions{Size: image.Pt(width, rowDims.Size.Y)}
|
return layout.Dimensions{Size: image.Pt(width, metrics.RowDims.Size.Y)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
func (u *ui) mainMenu(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -7105,28 +7091,17 @@ func (u *ui) phoneHeaderMenus(gtx layout.Context) layout.Dimensions {
|
|||||||
}
|
}
|
||||||
gtx.Constraints.Min = gtx.Constraints.Max
|
gtx.Constraints.Min = gtx.Constraints.Max
|
||||||
contentInsetPx := gtx.Dp(unit.Dp(16))
|
contentInsetPx := gtx.Dp(unit.Dp(16))
|
||||||
contentWidth := max(0, gtx.Constraints.Max.X-(contentInsetPx*2))
|
surface := dropdownSurface{
|
||||||
|
ContainerWidth: max(0, gtx.Constraints.Max.X-(contentInsetPx*2)),
|
||||||
drawMenu := func(anchor image.Point, menu layout.Widget) layout.Dimensions {
|
LeftInset: contentInsetPx,
|
||||||
menuGTX := gtx
|
TopInset: contentInsetPx,
|
||||||
menuGTX.Constraints.Min = image.Point{}
|
|
||||||
menuGTX.Constraints.Max.X = contentWidth
|
|
||||||
menuOps := op.Record(gtx.Ops)
|
|
||||||
menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, menu)
|
|
||||||
menuCall := menuOps.Stop()
|
|
||||||
menuX := contentInsetPx + anchoredMenuOriginX(contentWidth, 0, anchor.X, menuDims.Size.X)
|
|
||||||
menuY := contentInsetPx + anchor.Y
|
|
||||||
stack := op.Offset(image.Pt(menuX, menuY)).Push(gtx.Ops)
|
|
||||||
menuCall.Add(gtx.Ops)
|
|
||||||
stack.Pop()
|
|
||||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.syncMenuVisibleOnPhone() {
|
if u.syncMenuVisibleOnPhone() {
|
||||||
_ = drawMenu(u.phoneSyncMenuAnchor, u.syncMenu)
|
surface.draw(gtx, dropdownAnchor{TriggerRightX: u.phoneSyncMenuAnchor.X, TriggerBottomY: u.phoneSyncMenuAnchor.Y}, u.syncMenu)
|
||||||
}
|
}
|
||||||
if u.mainMenuVisibleOnPhone() {
|
if u.mainMenuVisibleOnPhone() {
|
||||||
_ = drawMenu(u.phoneMainMenuAnchor, u.mainMenu)
|
surface.draw(gtx, dropdownAnchor{TriggerRightX: u.phoneMainMenuAnchor.X, TriggerBottomY: u.phoneMainMenuAnchor.Y}, u.mainMenu)
|
||||||
}
|
}
|
||||||
return layout.Dimensions{Size: gtx.Constraints.Max}
|
return layout.Dimensions{Size: gtx.Constraints.Max}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,6 +391,42 @@ func TestAnchoredMenuOriginXClampsToVisibleContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHeaderActionMetricsComputeTriggerAnchors(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
metrics := headerActionMetrics{
|
||||||
|
RowOriginX: 24,
|
||||||
|
Spacing: 8,
|
||||||
|
RowDims: layout.Dimensions{Size: image.Pt(180, 40)},
|
||||||
|
SyncDims: layout.Dimensions{Size: image.Pt(52, 40)},
|
||||||
|
LockDims: layout.Dimensions{Size: image.Pt(44, 40)},
|
||||||
|
MainDims: layout.Dimensions{Size: image.Pt(36, 40)},
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := metrics.syncAnchor(); got != (dropdownAnchor{TriggerRightX: 76, TriggerBottomY: 40}) {
|
||||||
|
t.Fatalf("metrics.syncAnchor() = %+v, want right=76 bottom=40", got)
|
||||||
|
}
|
||||||
|
if got := metrics.mainAnchor(); got != (dropdownAnchor{TriggerRightX: 172, TriggerBottomY: 40}) {
|
||||||
|
t.Fatalf("metrics.mainAnchor() = %+v, want right=172 bottom=40", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropdownSurfaceOriginKeepsMenusWithinVisibleArea(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
surface := dropdownSurface{ContainerWidth: 320, LeftInset: 16, TopInset: 16}
|
||||||
|
anchor := dropdownAnchor{TriggerRightX: 300, TriggerBottomY: 42}
|
||||||
|
|
||||||
|
if got := surface.origin(anchor, 140); got != image.Pt(176, 58) {
|
||||||
|
t.Fatalf("surface.origin(anchor, 140) = %v, want (176,58)", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
leftAnchor := dropdownAnchor{TriggerRightX: 36, TriggerBottomY: 42}
|
||||||
|
if got := surface.origin(leftAnchor, 120); got != image.Pt(16, 58) {
|
||||||
|
t.Fatalf("surface.origin(leftAnchor, 120) = %v, want (16,58)", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUICurrentVaultSummary(t *testing.T) {
|
func TestUICurrentVaultSummary(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"gioui.org/layout"
|
||||||
|
"gioui.org/op"
|
||||||
|
"gioui.org/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dropdownAnchor struct {
|
||||||
|
TriggerRightX int
|
||||||
|
TriggerBottomY int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a dropdownAnchor) point() image.Point {
|
||||||
|
return image.Pt(a.TriggerRightX, a.TriggerBottomY)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dropdownSurface struct {
|
||||||
|
ContainerWidth int
|
||||||
|
LeftInset int
|
||||||
|
TopInset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s dropdownSurface) menuConstraints(gtx layout.Context) layout.Context {
|
||||||
|
menuGTX := gtx
|
||||||
|
menuGTX.Constraints.Min = image.Point{}
|
||||||
|
menuGTX.Constraints.Max.X = max(0, s.ContainerWidth)
|
||||||
|
return menuGTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s dropdownSurface) origin(anchor dropdownAnchor, menuWidth int) image.Point {
|
||||||
|
x := s.LeftInset + anchoredMenuOriginX(s.ContainerWidth, 0, anchor.TriggerRightX, menuWidth)
|
||||||
|
y := s.TopInset + anchor.TriggerBottomY
|
||||||
|
return image.Pt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s dropdownSurface) draw(gtx layout.Context, anchor dropdownAnchor, menu layout.Widget) layout.Dimensions {
|
||||||
|
menuGTX := s.menuConstraints(gtx)
|
||||||
|
menuOps := op.Record(gtx.Ops)
|
||||||
|
menuDims := layout.Inset{Top: unit.Dp(6)}.Layout(menuGTX, menu)
|
||||||
|
menuCall := menuOps.Stop()
|
||||||
|
menuOrigin := s.origin(anchor, menuDims.Size.X)
|
||||||
|
stack := op.Offset(menuOrigin).Push(gtx.Ops)
|
||||||
|
menuCall.Add(gtx.Ops)
|
||||||
|
stack.Pop()
|
||||||
|
return layout.Dimensions{Size: gtx.Constraints.Max}
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerActionMetrics struct {
|
||||||
|
RowOriginX int
|
||||||
|
Spacing int
|
||||||
|
RowDims layout.Dimensions
|
||||||
|
SyncDims layout.Dimensions
|
||||||
|
LockDims layout.Dimensions
|
||||||
|
MainDims layout.Dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m headerActionMetrics) syncAnchor() dropdownAnchor {
|
||||||
|
return dropdownAnchor{
|
||||||
|
TriggerRightX: m.RowOriginX + m.SyncDims.Size.X,
|
||||||
|
TriggerBottomY: m.RowDims.Size.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m headerActionMetrics) mainAnchor() dropdownAnchor {
|
||||||
|
triggerRightX := m.SyncDims.Size.X + m.Spacing + m.LockDims.Size.X + m.Spacing + m.MainDims.Size.X
|
||||||
|
return dropdownAnchor{
|
||||||
|
TriggerRightX: m.RowOriginX + triggerRightX,
|
||||||
|
TriggerBottomY: m.RowDims.Size.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user