package layout import ( "image" "gioui.org/layout" "gioui.org/op" "gioui.org/unit" ) func AnchoredMenuX(triggerWidth, menuWidth int) int { return triggerWidth - menuWidth } func AnchoredMenuOriginX(containerWidth, rowOriginX, triggerRightX, menuWidth int) int { x := rowOriginX + triggerRightX - menuWidth if x < 0 { return 0 } if x+menuWidth > containerWidth { return max(0, containerWidth-menuWidth) } return x } 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 } type DropdownPlacement struct { Anchor DropdownAnchor Origin image.Point Size image.Point } 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) Place(gtx layout.Context, anchor DropdownAnchor, menu layout.Widget) (DropdownPlacement, op.CallOp) { 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) return DropdownPlacement{ Anchor: anchor, Origin: menuOrigin, Size: menuDims.Size, }, menuCall } func (s DropdownSurface) Draw(gtx layout.Context, anchor DropdownAnchor, menu layout.Widget) layout.Dimensions { placement, menuCall := s.Place(gtx, anchor, menu) stack := op.Offset(placement.Origin).Push(gtx.Ops) menuCall.Add(gtx.Ops) stack.Pop() return layout.Dimensions{Size: gtx.Constraints.Max} } type ActionMetrics struct { RowOriginX int Spacing int SyncInnerSpacing int RowDims layout.Dimensions SyncDims layout.Dimensions SyncPrimaryDims layout.Dimensions SyncToggleDims layout.Dimensions LockDims layout.Dimensions MainDims layout.Dimensions } func (m ActionMetrics) SyncAnchor() DropdownAnchor { return DropdownAnchor{ TriggerRightX: m.RowOriginX + m.SyncDims.Size.X, TriggerBottomY: m.RowDims.Size.Y, } } func (m ActionMetrics) 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, } } type ActionBounds struct { SyncPrimary image.Rectangle SyncToggle image.Rectangle Lock image.Rectangle MainMenu image.Rectangle } func (m ActionMetrics) Bounds() ActionBounds { top := 0 syncLeft := m.RowOriginX syncPrimary := image.Rect(syncLeft, top, syncLeft+m.SyncPrimaryDims.Size.X, top+m.SyncPrimaryDims.Size.Y) syncToggleLeft := syncPrimary.Max.X + m.SyncInnerSpacing syncToggle := image.Rect(syncToggleLeft, top, syncToggleLeft+m.SyncToggleDims.Size.X, top+m.SyncToggleDims.Size.Y) lockLeft := syncLeft + m.SyncDims.Size.X + m.Spacing lock := image.Rect(lockLeft, top, lockLeft+m.LockDims.Size.X, top+m.LockDims.Size.Y) mainLeft := lock.Max.X + m.Spacing mainMenu := image.Rect(mainLeft, top, mainLeft+m.MainDims.Size.X, top+m.MainDims.Size.Y) return ActionBounds{ SyncPrimary: syncPrimary, SyncToggle: syncToggle, Lock: lock, MainMenu: mainMenu, } }