Files
MujicaUI/account/theme_selector.go
T

90 lines
3.1 KiB
Go

package account
import (
"fmt"
"log/slog"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
)
// ThemeSelectorOptions configure a ThemeSelector.
type ThemeSelectorOptions struct {
Disabled bool
}
// ThemeSelector selects an appearance using preview cards.
func ThemeSelector(c *ui.Context, mode *core.Mode, opts ThemeSelectorOptions) core.ChoiceResult {
if *mode != core.System && *mode != core.Light && *mode != core.Dark {
panic(fmt.Sprintf("mujica: ThemeSelector mode %d is not System, Light or Dark", *mode))
}
k := core.Tokens(c)
d := core.Density(c)
var r core.ChoiceResult
r.Element = ui.Row(c).Gap(d.Unit() * 3).Wrap().Role(ui.RoleRadioGroup).Label(core.Msg(c, "theme_selector.label")).
FocusGroup(ui.Horizontal).Disabled(opts.Disabled)
selected := mode
if r.Element.IsDisabled() {
copy := *mode
selected = &copy
}
r.Element.Children(func() {
for _, m := range []core.Mode{core.System, core.Light, core.Dark} {
card := ui.RadioBase(c, selected, m).Column().Gap(d.Unit() * 2).Padding(d.Unit() * 2).Radius(theme.CardRadius).AlignItems(ui.Stretch).Transition(core.ColorFade)
if card.Changed() && !card.IsDisabled() {
r = core.NewChoiceResult(r.Element, true)
slog.Debug("mujica: theme_selector chose", "mode", m.String())
}
on := *mode == m
internal.Ring(card, k.Focus, theme.CardRadius)
border, width := k.Border, theme.BorderWidth
switch {
case on:
border, width = k.AccentText, 2
card.Background(k.Selection)
case card.Hovered():
card.Background(k.SurfaceHover)
}
card.Border(width, border)
card.Children(func() {
ui.Box(c).Size(132, 84).Role(ui.RoleNone).Draw(func(p *ui.Painter, rect ui.Rect) {
switch m {
case core.Light:
themePreview(p, rect, theme.Light())
case core.Dark:
themePreview(p, rect, theme.Dark())
default:
half := ui.Rect{X: rect.X, Y: rect.Y, W: rect.W / 2, H: rect.H}
p.Clip(half, 0, func() { themePreview(p, rect, theme.Light()) })
p.Clip(ui.Rect{X: half.X + half.W, Y: rect.Y, W: rect.W / 2, H: rect.H}, 0, func() { themePreview(p, rect, theme.Dark()) })
}
})
ui.Row(c).Gap(d.Unit()).AlignItems(ui.Center).Children(func() {
if on {
ui.Icon(c, icons.Must("check")).Size(14, 14).TextColor(k.AccentText)
}
ui.Text(c, core.Msg(c, "theme_selector."+m.String())).SingleLine()
})
})
}
})
return r
}
// themePreview paints a miniature themed window.
func themePreview(p *ui.Painter, r ui.Rect, t theme.Tokens) {
p.Fill(r, t.Background, theme.ControlRadius)
p.Stroke(r, t.Border, theme.ControlRadius, 1)
p.Fill(ui.Rect{X: r.X + 1, Y: r.Y + 1, W: r.W - 2, H: 12}, t.Surface, theme.ControlRadius)
p.Fill(ui.Rect{X: r.X + 1, Y: r.Y + 13, W: r.W * 0.28, H: r.H - 14}, t.Surface, 0)
x := r.X + r.W*0.36
for i, w := range []float32{0.5, 0.38, 0.44} {
p.Fill(ui.Rect{X: x, Y: r.Y + 22 + float32(i)*10, W: r.W * w, H: 4}, t.TextMuted, 2)
}
p.Fill(ui.Rect{X: x, Y: r.Y + r.H - 20, W: 30, H: 12}, t.Accent, 2)
}