package ui import ( "gioui.org/layout" "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" "git.wxccs.org/iceking2nd/winauth-go/internal/i18n" ) // preferencesDialog edits the persistent UI prefs: UI language and // theme. Auto-lock and minimize-to-tray live in their own bucket and // will appear here later. type preferencesDialog struct { lang string theme themeMode // Theme selection — three "radio" buttons. themeSystemBt widget.Clickable themeLightBt widget.Clickable themeDarkBt widget.Clickable // Language selection — one button per supported locale. Cheap // substitute for a dropdown until Gio gains a native one. langEnBt widget.Clickable langZhCNBt widget.Clickable langDeBt widget.Clickable okBt widget.Clickable cancelBt widget.Clickable } // preferencesResult is what Layout's callback receives. cancel skips // any persistence. type preferencesResult struct { cancel bool language string theme themeMode } // supportedLanguages is the closed set the prefs dialog exposes. Keep // in sync with internal/i18n/locales/*.toml. var supportedLanguages = []struct { tag string label string }{ {"en", "English"}, {"zh-CN", "中文 (简体)"}, {"de", "Deutsch"}, } func newPreferencesDialog(currentLang string, currentTheme themeMode) *preferencesDialog { if currentLang == "" { currentLang = "en" } if currentTheme == "" { currentTheme = themeSystem } return &preferencesDialog{lang: currentLang, theme: currentTheme} } func (d *preferencesDialog) Layout( gtx layout.Context, th *material.Theme, onDone func(preferencesResult), ) layout.Dimensions { if d.cancelBt.Clicked(gtx) { onDone(preferencesResult{cancel: true}) return layout.Dimensions{Size: gtx.Constraints.Max} } if d.okBt.Clicked(gtx) { onDone(preferencesResult{language: d.lang, theme: d.theme}) return layout.Dimensions{Size: gtx.Constraints.Max} } // Map button clicks to model updates before the next layout. switch { case d.themeSystemBt.Clicked(gtx): d.theme = themeSystem case d.themeLightBt.Clicked(gtx): d.theme = themeLight case d.themeDarkBt.Clicked(gtx): d.theme = themeDark case d.langEnBt.Clicked(gtx): d.lang = "en" case d.langZhCNBt.Clicked(gtx): d.lang = "zh-CN" case d.langDeBt.Clicked(gtx): d.lang = "de" } // Render one labeled "chip" — emphasised when selected, regular // otherwise. Implemented as a material.Button so the click target // stays generous without us hand-rolling a Pointer area. chip := func(btn *widget.Clickable, label string, selected bool) layout.FlexChild { return layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Inset{Right: unit.Dp(8), Bottom: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { b := material.Button(th, btn, label) if !selected { b.Background = activePalette.RingBg b.Color = activePalette.OnBackground } return b.Layout(gtx) }) }) } body := func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(material.Body1(th, i18n.T("label_theme")).Layout), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, chip(&d.themeSystemBt, i18n.T("theme_system"), d.theme == themeSystem), chip(&d.themeLightBt, i18n.T("theme_light"), d.theme == themeLight), chip(&d.themeDarkBt, i18n.T("theme_dark"), d.theme == themeDark), ) }), layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), layout.Rigid(material.Body1(th, i18n.T("label_language")).Layout), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, chip(&d.langEnBt, supportedLanguages[0].label, d.lang == "en"), chip(&d.langZhCNBt, supportedLanguages[1].label, d.lang == "zh-CN"), chip(&d.langDeBt, supportedLanguages[2].label, d.lang == "de"), ) }), ) } return modalCard(gtx, th, i18n.T("dialog_preferences_title"), i18n.T("btn_ok"), i18n.T("btn_cancel"), &d.okBt, &d.cancelBt, body) }