package ui import ( "gioui.org/layout" "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" "git.wxccs.org/iceking2nd/winauth-go/internal/authenticator" ) // Dialog is the common contract implemented by every modal dialog. // onDone is invoked with the newly built authenticator + display name on // successful submission, or with (nil, "") on cancel. type Dialog interface { Layout(gtx layout.Context, th *material.Theme, onDone func(authenticator.Authenticator, string)) layout.Dimensions } // modalCard renders the standard backdrop + centered card with the given // title, body widget and an OK + Cancel button row. okLabel allows callers // to override the primary button label (e.g. "Enroll" for Battle.Net). // // The function does not handle the click events itself; callers should // query okBtn.Clicked / cancelBtn.Clicked before laying out so they can // short-circuit the frame. func modalCard( gtx layout.Context, th *material.Theme, title string, okLabel string, cancelLabel string, okBtn *widget.Clickable, cancelBtn *widget.Clickable, body layout.Widget, ) layout.Dimensions { fillBackground(gtx, activePalette.ScrimBg) return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions { gtx.Constraints.Max.X = gtx.Dp(420) return widget.Border{ Color: activePalette.DialogBorder, CornerRadius: unit.Dp(4), Width: unit.Dp(1), }.Layout(gtx, func(gtx layout.Context) layout.Dimensions { fillBackground(gtx, activePalette.DialogBg) return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(material.H6(th, title).Layout), layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), layout.Rigid(body), layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceStart}.Layout(gtx, layout.Rigid(material.Button(th, cancelBtn, cancelLabel).Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Inset{Left: unit.Dp(8)}.Layout(gtx, material.Button(th, okBtn, okLabel).Layout) }), ) }), ) }) }) }) } // modalCardCancel is a variant of modalCard with only a Cancel button. // Useful for dialogs whose primary actions live inside the body itself // (e.g. the QR scan dialog with separate "from file" / "from clipboard" // buttons). func modalCardCancel( gtx layout.Context, th *material.Theme, title string, cancelLabel string, cancelBtn *widget.Clickable, body layout.Widget, ) layout.Dimensions { fillBackground(gtx, activePalette.ScrimBg) return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions { gtx.Constraints.Max.X = gtx.Dp(420) return widget.Border{ Color: activePalette.DialogBorder, CornerRadius: unit.Dp(4), Width: unit.Dp(1), }.Layout(gtx, func(gtx layout.Context) layout.Dimensions { fillBackground(gtx, activePalette.DialogBg) return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(material.H6(th, title).Layout), layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), layout.Rigid(body), layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceStart}.Layout(gtx, layout.Rigid(material.Button(th, cancelBtn, cancelLabel).Layout), ) }), ) }) }) }) } // errorLabel returns a layout widget that renders msg in red, or nothing // when msg is empty. Used by dialogs to display validation errors. func errorLabel(th *material.Theme, msg string) layout.Widget { return func(gtx layout.Context) layout.Dimensions { if msg == "" { return layout.Dimensions{} } lbl := material.Body2(th, msg) lbl.Color = activePalette.ErrorFg return layout.Inset{Top: unit.Dp(8)}.Layout(gtx, lbl.Layout) } } // labeledEditor lays out a small body label above the editor. hint is the // editor placeholder text. func labeledEditor(th *material.Theme, label string, ed *widget.Editor, hint string) layout.Widget { return func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(material.Body2(th, label).Layout), layout.Rigid(material.Editor(th, ed, hint).Layout), layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), ) } }