Refactor: popups share overlayStyle and overlayEnter

internal.PopIn is gone. Breadcrumb, NavigationMenu, Pagination, Menubar
panels, CommandPalette, Select (and the other selectPanel users),
datetime popups, ColorPicker and the StatusBar overflow now style their
panel with overlayStyle(c, panel, overlayEnter(c, panel)): one shadow
(0 6 18, 16% black, scaled by entry) and one 160 ms entry fade.

Snapshots: color_picker and command_palette (light and dark) change only
in the panel shadow. ColorPicker loses its 0 4 12 18% shadow for the
softer shared one; CommandPalette loses its heavy 0 12 32 30% shadow,
and its backdrop now fades by alpha with the panel instead of through
the backdrop's opacity. Fade and reduced-motion tests pass unchanged.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 13:41:29 -04:00
1 parent 21249605fb
commit c22a09d327
14 files changed
+21 -48

No files matched your search

+2 -3
View File
@@ -135,9 +135,8 @@ func breadcrumbMore(c *ui.Context, hidden []BreadcrumbItem, choose func(int)) {
}
b.Children(func() { ui.Icon(c, icons.Must("ellipsis")).Size(16, 16) })
ui.PopoverBase(c, b, open, func(panel *ui.Element) {
internal.PopIn(c, panel, Motion(c, theme.PopupDuration))
panel.Margin(u, 0, 0, 0).Padding(u).Gap(u*0.5).Radius(theme.PopupRadius).Background(k.Surface).
Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.16)).MinWidth(160).MaxWidth(320)
overlayStyle(c, panel, overlayEnter(c, panel))
panel.Margin(u, 0, 0, 0).Padding(u).Gap(u * 0.5).MinWidth(160).MaxWidth(320)
for j, it := range hidden {
item := ui.ButtonBase(c).Role(ui.RoleLink).Justify(ui.Start).Gap(u*2).Padding(u*1.5, u*2).
Radius(theme.ControlRadius).MinHeight(Density(c).ControlHeight() - u*2).Label(it.Label).TextColor(k.Text)
+2 -2
View File
@@ -114,8 +114,8 @@ func ColorPicker(c *ui.Context, color *ui.Color, opts ColorPickerOptions) ColorP
})
opened := st.open && !st.wasOpen
ui.PopoverBase(c, trigger, &st.open, func(panel *ui.Element) {
panel.Margin(d.Unit(), 0, 0, 0).Padding(d.Unit()*3).Gap(d.Unit()*3).Width(260).Radius(theme.PopupRadius).
Background(k.Surface).Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.18))
overlayStyle(c, panel, overlayEnter(c, panel))
panel.Margin(d.Unit(), 0, 0, 0).Padding(d.Unit() * 3).Gap(d.Unit() * 3).Width(260)
colorPickerSquare(c, st, set, opened)
x := st.hsv
h := x.h
+5 -5
View File
@@ -80,11 +80,11 @@ func CommandPalette(c *ui.Context, open *bool, commands []Command, opts CommandP
u := d.Unit()
res.Element = ui.DialogBase(c, open, func(back, panel *ui.Element) {
ww, wh := c.Size()
back.Background(ui.RGBA(0, 0, 0, 0.4)).Justify(ui.Start)
internal.PopIn(c, back, Motion(c, theme.PopupDuration))
panel.Margin(wh*0.12, 0, 0, 0).Width(560).MaxWidth(ww-32).MaxHeight(wh*0.76).
Radius(theme.PopupRadius).Background(k.Surface).Border(theme.BorderWidth, k.Border).
Shadow(0, 12, 32, 0, ui.RGBA(0, 0, 0, 0.3)).AlignItems(ui.Stretch).Label(msg(c, "command_palette.label"))
p := overlayEnter(c, panel)
back.Background(ui.RGBA(0, 0, 0, 0.4*p)).Justify(ui.Start)
overlayStyle(c, panel, p)
panel.Margin(wh*0.12, 0, 0, 0).Width(560).MaxWidth(ww - 32).MaxHeight(wh * 0.76).
AlignItems(ui.Stretch).Label(msg(c, "command_palette.label"))
found := commandPaletteFilter(commands, st.query)
var in *ui.Element
ui.Row(c).AlignItems(ui.Center).Gap(u*3).Padding(u*3, u*4).Children(func() {
+2 -10
View File
@@ -163,22 +163,14 @@ func datetimeTrigger(c *ui.Context, text, placeholder, icon string) *ui.Element
// datetimePopup shows fn below anchor while *open; a disabled or read-only anchor closes it.
func datetimePopup(c *ui.Context, anchor *ui.Element, open *bool, readOnly bool, fn func()) *ui.Element {
k := Tokens(c)
d := Density(c)
if *open && (readOnly || anchor.IsDisabled()) {
slog.Debug("mujica: datetime popup closed by state", "readOnly", readOnly)
*open = false
}
return ui.PopoverBase(c, anchor, open, func(panel *ui.Element) {
born := ui.Local(panel, "datetime-born", func() bool { return true })
if *born {
panel.Animate("datetime-fade", 0, 0)
*born = false
}
fade := panel.Animate("datetime-fade", 1, Motion(c, theme.PopupDuration))
panel.MaxWidth(d.ControlHeight()*7+d.Unit()*6+2*theme.BorderWidth).Margin(d.Unit(), 0, 0, 0).Padding(d.Unit()*3).Gap(d.Unit()*3).Radius(theme.PopupRadius).
Background(k.Surface).Border(theme.BorderWidth, k.Border).
Shadow(0, 6, 18, 0, ui.RGBA(0, 0, 0, 0.16)).Opacity(fade)
overlayStyle(c, panel, overlayEnter(c, panel))
panel.MaxWidth(d.ControlHeight()*7+d.Unit()*6+2*theme.BorderWidth).Margin(d.Unit(), 0, 0, 0).Padding(d.Unit() * 3).Gap(d.Unit() * 3)
fn()
})
}
-12
View File
@@ -17,15 +17,3 @@ func Ease(e *ui.Element, d time.Duration, fg ui.Color) *ui.Element {
}
return e.TextColor(ui.Color{R: ch("mujica.fg.r", fg.R), G: ch("mujica.fg.g", fg.G), B: ch("mujica.fg.b", fg.B), A: ch("mujica.fg.a", fg.A)})
}
// PopIn fades popup e in over d from the frame it appeared and returns the progress, 0 to 1.
func PopIn(c *ui.Context, e *ui.Element, d time.Duration) float32 {
born := ui.Local(e, "mujica.popin", func() time.Time { return c.Now() })
p := float32(1)
if t := float32(c.Now().Sub(*born)) / float32(max(d, 1)); d > 0 && t < 1 {
p = ui.EaseOut(t)
c.AnimationFrame()
}
e.Opacity(p)
return p
}
+2 -4
View File
@@ -67,10 +67,8 @@ func (m *menubarRun) panel(l int, x, y float32, parent ui.Rect) (*ui.Element, *u
items := m.items(l)
ww, wh := c.Size()
// Long menus scroll within the window instead of running off it.
panel := ui.Scroll(c).Absolute().Padding(u).MinWidth(200).MaxHeight(wh-8).AlignItems(ui.Stretch).Role(ui.RolePopup).
Radius(theme.PopupRadius).Background(k.Surface).Border(theme.BorderWidth, k.Border).
Shadow(0, 6, 16, 0, ui.RGBA(0, 0, 0, 0.18))
internal.PopIn(c, panel, Motion(c, theme.PopupDuration))
panel := ui.Scroll(c).Absolute().Padding(u).MinWidth(200).MaxHeight(wh - 8).AlignItems(ui.Stretch).Role(ui.RolePopup)
overlayStyle(c, panel, overlayEnter(c, panel))
pb := panel.Bounds()
if x+pb.W > ww-4 {
x = ww - 4 - pb.W
+2 -3
View File
@@ -147,12 +147,11 @@ func navigationMenuTop(c *ui.Context, l NavigationLink, path string, push func(s
*open = true
}
ui.PopoverBase(c, b, open, func(panel *ui.Element) {
internal.PopIn(c, panel, Motion(c, theme.PopupDuration))
overlayStyle(c, panel, overlayEnter(c, panel))
// Capped to the room beside the anchor; the links scroll inside.
_, wh := c.Size()
r := b.Bounds()
panel.Margin(u, 0, 0, 0).Padding(u*2).Radius(theme.PopupRadius).Background(k.Surface).MaxHeight(max(r.Y, wh-r.Y-r.H)-16).
Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.16)).MinWidth(240).MaxWidth(360)
panel.Margin(u, 0, 0, 0).Padding(u * 2).MaxHeight(max(r.Y, wh-r.Y-r.H) - 16).MinWidth(240).MaxWidth(360)
ui.Scroll(c).Shrink(1).Gap(u).FocusGroup(ui.Vertical).Children(func() {
navigationMenuPanel(c, l.Items, path, open, push)
})
+2 -3
View File
@@ -175,9 +175,8 @@ func paginationSizes(c *ui.Context, page, pageSize *int, sizes []int, res *Choic
ui.Icon(c, icons.Must("chevron-down")).Size(14, 14).TextColor(k.TextMuted)
})
sel.Popup(func(panel *ui.Element) {
internal.PopIn(c, panel, Motion(c, theme.PopupDuration))
panel.Margin(u, 0, 0, 0).Padding(u).Radius(theme.PopupRadius).Background(k.Surface).
Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.16))
overlayStyle(c, panel, overlayEnter(c, panel))
panel.Margin(u, 0, 0, 0).Padding(u)
for _, s := range sizes {
it := sel.Item(s).Padding(u*1.5, u*3).Radius(theme.ControlRadius).Gap(u * 2)
if it.Highlighted() {
+2 -3
View File
@@ -239,9 +239,8 @@ func selectCheck(c *ui.Context, on bool) {
// selectPanel styles a popup panel.
func selectPanel(c *ui.Context, panel *ui.Element) {
k := Tokens(c)
panel.Margin(4, 0, 0, 0).Padding(4).Radius(theme.PopupRadius).Background(k.Surface).
Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.14))
overlayStyle(c, panel, overlayEnter(c, panel))
panel.Margin(4, 0, 0, 0).Padding(4)
}
// selectRow styles an option row: chosen has the selection fill and a check, highlighted a side bar.
+2 -3
View File
@@ -108,9 +108,8 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult {
}
if *open {
ui.PopoverBase(c, more, open, func(panel *ui.Element) {
panel.Margin(d.Unit(), 0, 0, 0).Padding(d.Unit()).Gap(d.Unit()).AlignItems(ui.Stretch).
Radius(theme.PopupRadius).Background(k.Surface).Border(theme.BorderWidth, k.Border).
Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.16))
overlayStyle(c, panel, overlayEnter(c, panel))
panel.Margin(d.Unit(), 0, 0, 0).Padding(d.Unit()).Gap(d.Unit()).AlignItems(ui.Stretch)
for _, it := range statusBarAll(opts) {
if hidden[it.ID] {
item(it)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 21 KiB