package ui import ( "image" "gioui.org/layout" "gioui.org/op/clip" "gioui.org/op/paint" "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" "git.wxccs.org/iceking2nd/winauth-go/internal/i18n" ) // iconPickerDialog shows the full catalog of brand icons in a scrollable // grid. The user clicks an icon to select it, then OK to confirm. A // fixed column count keeps row layout cheap (no FlowWrap needed). type iconPickerDialog struct { icons []string selected string clickables []widget.Clickable list widget.List okBtn widget.Clickable cancelBtn widget.Clickable cols int } func newIconPickerDialog(currentKey string) *iconPickerDialog { keys := allIconKeys() d := &iconPickerDialog{ icons: keys, selected: currentKey, clickables: make([]widget.Clickable, len(keys)), cols: 6, } d.list.Axis = layout.Vertical return d } // iconExistsInCatalog reports whether key is in the published icon // catalog (as opposed to the fallback vendor icons). func iconExistsInCatalog(key string) bool { for _, k := range allIconKeys() { if k == key { return true } } return false } func (d *iconPickerDialog) Layout( gtx layout.Context, th *material.Theme, onDone func(iconKey string, cancel bool), ) layout.Dimensions { for i := range d.icons { if d.clickables[i].Clicked(gtx) { d.selected = d.icons[i] } } if d.cancelBtn.Clicked(gtx) { onDone("", true) return layout.Dimensions{Size: gtx.Constraints.Max} } if d.okBtn.Clicked(gtx) { onDone(d.selected, false) return layout.Dimensions{Size: gtx.Constraints.Max} } body := func(gtx layout.Context) layout.Dimensions { // Cap body height so the picker scrolls instead of overflowing // the screen on small windows. maxH := gtx.Dp(unit.Dp(320)) if gtx.Constraints.Max.Y > maxH { gtx.Constraints.Max.Y = maxH } cellDp := unit.Dp(48) cellPx := gtx.Dp(cellDp) cols := d.cols totalRows := (len(d.icons) + cols - 1) / cols return material.List(th, &d.list).Layout(gtx, totalRows, func(gtx layout.Context, rowIdx int) layout.Dimensions { start := rowIdx * cols end := start + cols if end > len(d.icons) { end = len(d.icons) } rowIcons := d.icons[start:end] children := make([]layout.FlexChild, len(rowIcons)) for j, key := range rowIcons { idx := start + j key := key sel := key == d.selected children[j] = layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Inset{ Right: unit.Dp(4), Bottom: unit.Dp(4), }.Layout(gtx, func(gtx layout.Context) layout.Dimensions { gtx.Constraints.Min = image.Pt(cellPx, cellPx) gtx.Constraints.Max = image.Pt(cellPx, cellPx) return d.clickables[idx].Layout(gtx, func(gtx layout.Context) layout.Dimensions { return drawIconCell(gtx, key, sel) }) }) }) } return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, children...) }) } return modalCard(gtx, th, i18n.T("dialog_icon_picker_title"), i18n.T("btn_ok"), i18n.T("btn_cancel"), &d.okBtn, &d.cancelBtn, body) } // drawIconCell paints one grid cell: optional selection background + // the icon scaled to fit. Returns the fixed cell-size dimensions. func drawIconCell(gtx layout.Context, key string, selected bool) layout.Dimensions { size := gtx.Constraints.Min if selected { rect := clip.Rect{Max: size}.Push(gtx.Ops) paint.ColorOp{Color: activePalette.RingBg}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops) rect.Pop() } img := vendorIconFor(key) if img != nil { w := widget.Image{ Src: paint.NewImageOp(img), Fit: widget.Contain, Position: layout.Center, } w.Layout(gtx) } return layout.Dimensions{Size: size} }