Refactor: share iconAction for borderless 16 DIP icon buttons

datetimeIconButton moves to icon_button.go as iconAction(c, icon, label)
and replaces the identical copies in Dialog close, PasswordInput toggle
and AppShell collapse. Hover and press now set Text on every caller,
including the date and time pickers, which kept TextMuted on hover.
Image retry (bordered, danger ink), TagsInput remove (14 DIP icon, no
press state) and Tag close (12 DIP icon, fade) are different buttons
and stay as they are.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 13:37:34 -04:00
1 parent dd1e9597af
commit 21249605fb
9 files changed
+36 -67

No files matched your search

+1 -11
View File
@@ -6,7 +6,6 @@ import (
"github.com/egoist/mygo/ui"
"mujicaui/icons"
"mujicaui/internal"
"mujicaui/theme"
)
@@ -100,16 +99,7 @@ func appShellSidebar(c *ui.Context, collapsed bool, opts AppShellOptions) bool {
if collapsed {
label = msg(c, "app_shell.expand")
}
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius).
Label(label).Tooltip(label).TextColor(k.TextMuted)
internal.Ring(b, k.Focus, theme.ControlRadius)
if b.Pressed() {
b.Background(k.Selection).TextColor(k.Text)
} else if b.Hovered() {
b.Background(k.SurfaceHover).TextColor(k.Text)
}
b.Children(func() { ui.Icon(c, icons.Must("panel-left")).Size(16, 16) })
pressed = b.Clicked()
pressed = iconAction(c, icons.Must("panel-left"), label).Tooltip(label).Clicked()
})
if !collapsed {
ScrollArea(c, nil, ScrollAreaOptions{}, opts.Sidebar).Grow(1).MinHeight(0)
+5 -4
View File
@@ -8,6 +8,7 @@ import (
"github.com/egoist/mygo/ui"
"mujicaui/icons"
"mujicaui/internal"
"mujicaui/theme"
)
@@ -132,18 +133,18 @@ func calendarMonth(c *ui.Context, cursor *Date, cfg calendarConfig) (picked, hov
}
root.Children(func() {
ui.Row(c).AlignItems(ui.Center).Gap(d.Unit()).Children(func() {
if datetimeIconButton(c, "chevrons-left", msg(c, "calendar.prev_year")).Clicked() {
if iconAction(c, icons.Must("chevrons-left"), msg(c, "calendar.prev_year")).Clicked() {
move(cur.AddMonths(-12), -1, false)
}
if datetimeIconButton(c, "chevron-left", msg(c, "calendar.prev_month")).Clicked() {
if iconAction(c, icons.Must("chevron-left"), msg(c, "calendar.prev_month")).Clicked() {
move(cur.AddMonths(-1), -1, false)
}
ui.Text(c, title).Font(theme.SerifFont).FontSize(FontSize(c, theme.BodySize+2)).Bold().
TextColor(k.Text).Grow(1).TextAlign(ui.Center).SingleLine()
if datetimeIconButton(c, "chevron-right", msg(c, "calendar.next_month")).Clicked() {
if iconAction(c, icons.Must("chevron-right"), msg(c, "calendar.next_month")).Clicked() {
move(cur.AddMonths(1), 1, false)
}
if datetimeIconButton(c, "chevrons-right", msg(c, "calendar.next_year")).Clicked() {
if iconAction(c, icons.Must("chevrons-right"), msg(c, "calendar.next_year")).Clicked() {
move(cur.AddMonths(12), 1, false)
}
})
+4 -2
View File
@@ -7,6 +7,8 @@ import (
"time"
"github.com/egoist/mygo/ui"
"mujicaui/icons"
)
// DatePickerOptions configure a DatePicker.
@@ -121,10 +123,10 @@ func DatePicker(c *ui.Context, value *Date, opts DatePickerOptions) DateTimeResu
if opts.ReadOnly {
return
}
if opts.Clearable && !value.IsZero() && datetimeIconButton(c, "x", msg(c, "calendar.clear")).Clicked() {
if opts.Clearable && !value.IsZero() && iconAction(c, icons.Must("x"), msg(c, "calendar.clear")).Clicked() {
commit(Date{}, "clear")
}
if datetimeIconButton(c, "calendar", msg(c, "date_picker.open")).Role(ui.RolePopUpButton).Clicked() {
if iconAction(c, icons.Must("calendar"), msg(c, "date_picker.open")).Role(ui.RolePopUpButton).Clicked() {
if *open {
*open = false
} else {
-15
View File
@@ -122,21 +122,6 @@ func datetimeDayLabel(c *ui.Context, d Date) string {
return fmt.Sprintf(msg(c, "calendar.day_label"), wd, datetimeMonthName(c, d.Month), d.Day, d.Year)
}
// datetimeIconButton is a square, borderless button showing icon, named label.
func datetimeIconButton(c *ui.Context, icon, label string) *ui.Element {
k := Tokens(c)
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius).Label(label)
internal.Ring(b, k.Focus, theme.ControlRadius)
switch {
case b.Pressed():
b.Background(k.Selection)
case b.Hovered():
b.Background(k.SurfaceHover)
}
b.TextColor(k.TextMuted).Children(func() { ui.Icon(c, icons.Must(icon)).Size(16, 16) })
return b
}
// datetimeField styles row as a single-line field: solid fill and fine border.
func datetimeField(c *ui.Context, row *ui.Element, invalid, readOnly bool) *ui.Element {
k := Tokens(c)
+4 -3
View File
@@ -3,6 +3,7 @@ package mujica
import (
"github.com/egoist/mygo/ui"
"mujicaui/icons"
"mujicaui/internal"
"mujicaui/theme"
)
@@ -36,12 +37,12 @@ func datetimeGrid(c *ui.Context, cursor *int, cfg datetimeGridConfig) (picked in
start, _ := cfg.pageOf(*cursor)
ui.Row(c).AlignItems(ui.Center).Gap(d.Unit()).BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).
PaddingY(d.Unit()).Children(func() {
if datetimeIconButton(c, "chevron-left", cfg.prev).Clicked() {
if iconAction(c, icons.Must("chevron-left"), cfg.prev).Clicked() {
*cursor, *keyed = *cursor-cfg.page, false
}
ui.Text(c, cfg.title(start)).Font(theme.SerifFont).FontSize(FontSize(c, theme.BodySize+2)).Bold().
TextColor(k.Text).Grow(1).TextAlign(ui.Center).SingleLine()
if datetimeIconButton(c, "chevron-right", cfg.next).Clicked() {
if iconAction(c, icons.Must("chevron-right"), cfg.next).Clicked() {
*cursor, *keyed = *cursor+cfg.page, false
}
})
@@ -182,7 +183,7 @@ func datetimeGridPicker(c *ui.Context, f datetimeGridField, cfg datetimeGridConf
if !f.readOnly && (t.Clicked() || !*open && t.Shortcut(0, ui.KeyDown)) {
*open, *cursor = !*open, f.home
}
if f.clearable && f.hasValue && !f.readOnly && datetimeIconButton(c, "x", msg(c, "calendar.clear")).Clicked() {
if f.clearable && f.hasValue && !f.readOnly && iconAction(c, icons.Must("x"), msg(c, "calendar.clear")).Clicked() {
cleared = true
}
})
+1 -16
View File
@@ -117,7 +117,7 @@ func dialogHeader(c *ui.Context, title string, decorated, closable bool, closeKe
ui.Row(c).Gap(u * 3).AlignItems(ui.Start).Children(func() {
ui.Text(c, title).Font(theme.SerifFont).FontSize(FontSize(c, theme.H3Size)).FontWeight(600).Grow(1)
if closable {
clicked = dialogCloseButton(c, msg(c, closeKey)).Clicked()
clicked = iconAction(c, icons.Must("x"), msg(c, closeKey)).Margin(-4, -4, 0, 0).Clicked()
}
})
if decorated {
@@ -137,21 +137,6 @@ func dialogRule(c *ui.Context, ink ui.Color) {
})
}
// dialogCloseButton is the 28 DIP icon button closing a panel.
func dialogCloseButton(c *ui.Context, label string) *ui.Element {
k := Tokens(c)
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius).Label(label).Margin(-4, -4, 0, 0)
internal.Ring(b, k.Focus, theme.ControlRadius)
b.TextColor(k.TextMuted)
if b.Pressed() {
b.Background(k.Selection).TextColor(k.Text)
} else if b.Hovered() {
b.Background(k.SurfaceHover).TextColor(k.Text)
}
b.Children(func() { ui.Icon(c, icons.Must("x")).Size(16, 16) })
return b
}
// dialogActions lays out a dialog footer.
func dialogActions(c *ui.Context, actions func()) {
u := Density(c).Unit()
+18
View File
@@ -2,6 +2,9 @@ package mujica
import (
"github.com/egoist/mygo/ui"
"mujicaui/internal"
"mujicaui/theme"
)
// IconButtonOptions configure an IconButton.
@@ -31,3 +34,18 @@ func IconButton(c *ui.Context, icon *ui.SVG, label string, opts IconButtonOption
}
return b.Tooltip(tip)
}
// iconAction is a square, borderless button showing icon, named label.
func iconAction(c *ui.Context, icon *ui.SVG, label string) *ui.Element {
k := Tokens(c)
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius).Label(label).TextColor(k.TextMuted)
internal.Ring(b, k.Focus, theme.ControlRadius)
switch {
case b.Pressed():
b.Background(k.Selection).TextColor(k.Text)
case b.Hovered():
b.Background(k.SurfaceHover).TextColor(k.Text)
}
b.Children(func() { ui.Icon(c, icon).Size(16, 16) })
return b
}
+1 -15
View File
@@ -6,8 +6,6 @@ import (
"github.com/egoist/mygo/ui"
"mujicaui/icons"
"mujicaui/internal"
"mujicaui/theme"
)
// PasswordInputOptions configure a PasswordInput.
@@ -55,21 +53,9 @@ func PasswordInput(c *ui.Context, value *string, opts PasswordInputOptions) Inpu
// passwordInputToggle builds the show or hide button and reports a click.
func passwordInputToggle(c *ui.Context, shown bool) bool {
k := Tokens(c)
name, icon := msg(c, "password_input.show"), "eye"
if shown {
name, icon = msg(c, "password_input.hide"), "eye-off"
}
b := ui.ButtonBase(c).Size(theme.MinHitSize, theme.MinHitSize).Radius(theme.ControlRadius).Label(name).Tooltip(name)
internal.Ring(b, k.Focus, theme.ControlRadius)
fg := k.TextMuted
if b.Pressed() {
b.Background(k.Selection)
fg = k.Text
} else if b.Hovered() {
b.Background(k.SurfaceHover)
fg = k.Text
}
b.Children(func() { ui.Icon(c, icons.Must(icon)).Size(16, 16).TextColor(fg) })
return b.Clicked()
return iconAction(c, icons.Must(icon), name).Tooltip(name).Clicked()
}
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/egoist/mygo/ui"
"mujicaui/icons"
"mujicaui/internal"
"mujicaui/theme"
)
@@ -221,7 +222,7 @@ func timeEditor(c *ui.Context, draft *TimeOfDay, cfg timeConfig) (field *ui.Elem
if cfg.readOnly {
return
}
b := datetimeIconButton(c, "clock", msg(c, "time_picker.open")).Role(ui.RolePopUpButton)
b := iconAction(c, icons.Must("clock"), msg(c, "time_picker.open")).Role(ui.RolePopUpButton)
if b.Clicked() {
*open, *fresh = !*open, true
}