From 222b9135c8c03a1a3400dd0e31ee33054ed29abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 12:25:18 -0400 Subject: [PATCH] Navigation: components 059-068 --- breadcrumb.go | 157 ++++++ breadcrumb_test.go | 110 +++++ command_palette.go | 212 ++++++++ command_palette_test.go | 142 ++++++ docs/components/059-tabs.md | 63 +++ docs/components/060-segmented-control.md | 55 +++ docs/components/061-breadcrumb.md | 57 +++ docs/components/062-pagination.md | 53 ++ docs/components/063-steps.md | 59 +++ docs/components/064-sidebar.md | 71 +++ docs/components/065-navigation-menu.md | 61 +++ docs/components/066-menubar.md | 74 +++ docs/components/067-toolbar.md | 67 +++ docs/components/068-command-palette.md | 65 +++ example_navigation_test.go | 155 ++++++ examples/gallery/pages_navigation.go | 461 +++++++++++++++++- menubar.go | 279 +++++++++++ menubar_panel.go | 188 +++++++ menubar_test.go | 191 ++++++++ messages_navigation.go | 36 ++ navigation_menu.go | 206 ++++++++ navigation_menu_test.go | 139 ++++++ pagination.go | 247 ++++++++++ pagination_test.go | 166 +++++++ segmented_control.go | 97 ++++ segmented_control_test.go | 112 +++++ sidebar.go | 182 +++++++ sidebar_test.go | 135 +++++ steps.go | 181 +++++++ steps_test.go | 109 +++++ tabs.go | 174 +++++++ tabs_test.go | 162 ++++++ testdata/snapshots/darwin/breadcrumb-dark.png | Bin 0 -> 10907 bytes .../snapshots/darwin/breadcrumb-light.png | Bin 0 -> 10645 bytes .../snapshots/darwin/command_palette-dark.png | Bin 0 -> 21093 bytes .../darwin/command_palette-light.png | Bin 0 -> 24341 bytes testdata/snapshots/darwin/menubar-dark.png | Bin 0 -> 2853 bytes testdata/snapshots/darwin/menubar-light.png | Bin 0 -> 2661 bytes .../snapshots/darwin/navigation_menu-dark.png | Bin 0 -> 5444 bytes .../darwin/navigation_menu-light.png | Bin 0 -> 5296 bytes testdata/snapshots/darwin/pagination-dark.png | Bin 0 -> 10720 bytes .../snapshots/darwin/pagination-light.png | Bin 0 -> 10327 bytes .../darwin/segmented_control-dark.png | Bin 0 -> 10155 bytes .../darwin/segmented_control-light.png | Bin 0 -> 9852 bytes testdata/snapshots/darwin/sidebar-dark.png | Bin 0 -> 16031 bytes testdata/snapshots/darwin/sidebar-light.png | Bin 0 -> 15608 bytes testdata/snapshots/darwin/steps-dark.png | Bin 0 -> 27341 bytes testdata/snapshots/darwin/steps-light.png | Bin 0 -> 26621 bytes .../darwin/tabs-comfortable-dark.png | Bin 0 -> 17886 bytes .../darwin/tabs-comfortable-light.png | Bin 0 -> 17158 bytes .../snapshots/darwin/tabs-compact-dark.png | Bin 0 -> 17650 bytes .../snapshots/darwin/tabs-compact-light.png | Bin 0 -> 16986 bytes testdata/snapshots/darwin/tabs-dark.png | Bin 0 -> 17371 bytes testdata/snapshots/darwin/tabs-light.png | Bin 0 -> 16645 bytes testdata/snapshots/darwin/toolbar-dark.png | Bin 0 -> 6137 bytes testdata/snapshots/darwin/toolbar-light.png | Bin 0 -> 6097 bytes toolbar.go | 80 +++ toolbar_test.go | 105 ++++ 58 files changed, 4650 insertions(+), 1 deletion(-) create mode 100644 breadcrumb.go create mode 100644 breadcrumb_test.go create mode 100644 command_palette.go create mode 100644 command_palette_test.go create mode 100644 docs/components/059-tabs.md create mode 100644 docs/components/060-segmented-control.md create mode 100644 docs/components/061-breadcrumb.md create mode 100644 docs/components/062-pagination.md create mode 100644 docs/components/063-steps.md create mode 100644 docs/components/064-sidebar.md create mode 100644 docs/components/065-navigation-menu.md create mode 100644 docs/components/066-menubar.md create mode 100644 docs/components/067-toolbar.md create mode 100644 docs/components/068-command-palette.md create mode 100644 example_navigation_test.go create mode 100644 menubar.go create mode 100644 menubar_panel.go create mode 100644 menubar_test.go create mode 100644 messages_navigation.go create mode 100644 navigation_menu.go create mode 100644 navigation_menu_test.go create mode 100644 pagination.go create mode 100644 pagination_test.go create mode 100644 segmented_control.go create mode 100644 segmented_control_test.go create mode 100644 sidebar.go create mode 100644 sidebar_test.go create mode 100644 steps.go create mode 100644 steps_test.go create mode 100644 tabs.go create mode 100644 tabs_test.go create mode 100644 testdata/snapshots/darwin/breadcrumb-dark.png create mode 100644 testdata/snapshots/darwin/breadcrumb-light.png create mode 100644 testdata/snapshots/darwin/command_palette-dark.png create mode 100644 testdata/snapshots/darwin/command_palette-light.png create mode 100644 testdata/snapshots/darwin/menubar-dark.png create mode 100644 testdata/snapshots/darwin/menubar-light.png create mode 100644 testdata/snapshots/darwin/navigation_menu-dark.png create mode 100644 testdata/snapshots/darwin/navigation_menu-light.png create mode 100644 testdata/snapshots/darwin/pagination-dark.png create mode 100644 testdata/snapshots/darwin/pagination-light.png create mode 100644 testdata/snapshots/darwin/segmented_control-dark.png create mode 100644 testdata/snapshots/darwin/segmented_control-light.png create mode 100644 testdata/snapshots/darwin/sidebar-dark.png create mode 100644 testdata/snapshots/darwin/sidebar-light.png create mode 100644 testdata/snapshots/darwin/steps-dark.png create mode 100644 testdata/snapshots/darwin/steps-light.png create mode 100644 testdata/snapshots/darwin/tabs-comfortable-dark.png create mode 100644 testdata/snapshots/darwin/tabs-comfortable-light.png create mode 100644 testdata/snapshots/darwin/tabs-compact-dark.png create mode 100644 testdata/snapshots/darwin/tabs-compact-light.png create mode 100644 testdata/snapshots/darwin/tabs-dark.png create mode 100644 testdata/snapshots/darwin/tabs-light.png create mode 100644 testdata/snapshots/darwin/toolbar-dark.png create mode 100644 testdata/snapshots/darwin/toolbar-light.png create mode 100644 toolbar.go create mode 100644 toolbar_test.go diff --git a/breadcrumb.go b/breadcrumb.go new file mode 100644 index 0000000..1bfd93b --- /dev/null +++ b/breadcrumb.go @@ -0,0 +1,157 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// BreadcrumbItem is one level of a Breadcrumb path. +type BreadcrumbItem struct { + Label string + Icon *ui.SVG +} + +// BreadcrumbOptions configure a Breadcrumb. +type BreadcrumbOptions struct { + // MaxItems shown before middle items fold into a menu; zero means 4, minimum 3. + MaxItems int + // MaxItemWidth cuts longer labels with an ellipsis; zero means 160 DIPs. + MaxItemWidth float32 + // Label names the path for assistive technology; empty uses the built-in name. + Label string +} + +// BreadcrumbResult is the built Breadcrumb. +type BreadcrumbResult struct { + *ui.Element + chosen int +} + +// Chosen returns the index of the item the user activated in this frame. +func (r BreadcrumbResult) Chosen() (int, bool) { return r.chosen, r.chosen >= 0 } + +// Breadcrumb shows a path whose last item is the current one; earlier items are links. +func Breadcrumb(c *ui.Context, items []BreadcrumbItem, opts BreadcrumbOptions) BreadcrumbResult { + if len(items) == 0 { + panic("mujica: Breadcrumb needs at least one item") + } + limit := opts.MaxItems + if limit == 0 { + limit = 4 + } + if limit < 3 { + panic("mujica: Breadcrumb MaxItems must be at least 3") + } + width := opts.MaxItemWidth + if width == 0 { + width = 160 + } + label := opts.Label + if label == "" { + label = msg(c, "breadcrumb.label") + } + k := Tokens(c) + u := Density(c).Unit() + row := ui.Row(c).AlignItems(ui.Center).Gap(u).MinWidth(0).Role(ui.RoleGroup).Label(label) + res := BreadcrumbResult{Element: row, chosen: -1} + choose := func(i int) { + res.chosen = i + slog.Debug("mujica: breadcrumb choose", "index", i, "label", items[i].Label) + } + // Folded items are those between the first and the last limit-2. + fold := len(items) > limit + hideFrom, hideTo := 1, len(items)-(limit-2) + row.Children(func() { + for i, it := range items { + if it.Label == "" { + panic("mujica: BreadcrumbItem needs a Label") + } + if fold && i >= hideFrom && i < hideTo { + if i == hideFrom { + breadcrumbSeparator(c) + breadcrumbMore(c, items[hideFrom:hideTo], func(j int) { choose(hideFrom + j) }) + } + continue + } + if i > 0 { + breadcrumbSeparator(c) + } + if i == len(items)-1 { + cur := ui.Row(c).AlignItems(ui.Center).Gap(u).Padding(u, u*1.5).MinWidth(0). + FontWeight(600).TextColor(k.Text).Tooltip(it.Label).Label(it.Label + ", " + msg(c, "breadcrumb.current")) + cur.Children(func() { breadcrumbContent(c, it, width) }) + continue + } + link := ui.ButtonBase(c).Role(ui.RoleLink).Gap(u).Padding(u, u*1.5).Radius(theme.ControlRadius). + Shrink(1).MinWidth(0).Cursor(ui.CursorPointer).Tooltip(it.Label).Label(it.Label) + internal.Ring(link, k.Focus, theme.ControlRadius) + link.TextColor(k.TextMuted) + if link.Hovered() { + link.Background(k.SurfaceHover).TextColor(k.Text) + } + link.Children(func() { breadcrumbContent(c, it, width) }) + if link.Clicked() { + choose(i) + } + } + }) + return res +} + +func breadcrumbContent(c *ui.Context, it BreadcrumbItem, width float32) { + if it.Icon != nil { + ui.Icon(c, it.Icon).Size(14, 14).Shrink(0) + } + ui.Text(c, it.Label).SingleLine().MaxWidth(width).MinWidth(0) +} + +func breadcrumbSeparator(c *ui.Context) { + ui.Icon(c, icons.Must("chevron-right")).Size(12, 12).Shrink(0).TextColor(Tokens(c).Ornament).Role(ui.RoleNone) +} + +// breadcrumbMore is the ellipsis button listing the folded items. +func breadcrumbMore(c *ui.Context, hidden []BreadcrumbItem, choose func(int)) { + k := Tokens(c) + u := Density(c).Unit() + b := ui.ButtonBase(c).Padding(u, u*1.5).Radius(theme.ControlRadius).MinWidth(theme.MinHitSize). + Label(msg(c, "breadcrumb.more")).Tooltip(msg(c, "breadcrumb.more")).TextColor(k.TextMuted).Cursor(ui.CursorPointer) + internal.Ring(b, k.Focus, theme.ControlRadius) + open := ui.Local(b, "open", func() bool { return false }) + if b.Hovered() || *open { + b.Background(k.SurfaceHover).TextColor(k.Text) + } + if b.Clicked() { + *open = !*open + } + b.Children(func() { ui.Icon(c, icons.Must("ellipsis")).Size(16, 16) }) + ui.PopoverBase(c, b, open, func(panel *ui.Element) { + 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) + 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) + if j == 0 { + item.AutoFocus() + } + internal.Ring(item, k.Focus, theme.ControlRadius) + if item.Hovered() { + item.Background(k.SurfaceHover) + } + item.Children(func() { + if it.Icon != nil { + ui.Icon(c, it.Icon).Size(14, 14).Shrink(0) + } + ui.Text(c, it.Label).SingleLine().MinWidth(0) + }) + if item.Clicked() { + *open = false + choose(j) + } + } + }) +} diff --git a/breadcrumb_test.go b/breadcrumb_test.go new file mode 100644 index 0000000..326abd8 --- /dev/null +++ b/breadcrumb_test.go @@ -0,0 +1,110 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +func breadcrumbPath(names ...string) []BreadcrumbItem { + var items []BreadcrumbItem + for _, n := range names { + items = append(items, BreadcrumbItem{Label: n}) + } + return items +} + +func TestBreadcrumbChoose(t *testing.T) { + got := -1 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if i, ok := Breadcrumb(c, breadcrumbPath("Home", "Library", "Poems"), BreadcrumbOptions{}).Chosen(); ok { + got = i + } + }, 500, 100) + if err := tt.Click("Library"); err != nil { + t.Fatal(err) + } + if got != 1 { + t.Fatalf("chose %d, want 1", got) + } + got = -1 + _ = tt.Click("Poems") + if got != -1 { + t.Fatalf("current item acted as a link: %d", got) + } + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnter) + if got != 0 { + t.Fatalf("Enter on first link chose %d", got) + } +} + +func TestBreadcrumbFolds(t *testing.T) { + got := -1 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if i, ok := Breadcrumb(c, breadcrumbPath("Root", "Alpha", "Beta", "Gamma", "Omega", "Leaf"), BreadcrumbOptions{}).Chosen(); ok { + got = i + } + }, 600, 300) + for _, hidden := range []string{"Alpha", "Beta", "Gamma"} { + if tt.HasText(hidden) { + t.Fatalf("%s should be folded: %q", hidden, tt.Texts()) + } + } + if !tt.HasText("Omega") || !tt.HasText("Root") { + t.Fatalf("ends missing: %q", tt.Texts()) + } + if err := tt.Click("Show hidden path"); err != nil { + t.Fatal(err) + } + if !tt.HasText("Beta") || !tt.Focused("Alpha") { + t.Fatalf("menu did not open with focus on first: %q", tt.Texts()) + } + tt.Key(0, ui.KeyEscape) + if tt.HasText("Beta") { + t.Fatal("Escape did not close the menu") + } + _ = tt.Click("Show hidden path") + if err := tt.Click("Gamma"); err != nil { + t.Fatal(err) + } + if got != 3 || tt.HasText("Beta") { + t.Fatalf("chose %d, want 3, menu closed", got) + } +} + +func TestBreadcrumbInvalid(t *testing.T) { + for name, tc := range map[string]struct { + items []BreadcrumbItem + opts BreadcrumbOptions + }{ + "empty": {nil, BreadcrumbOptions{}}, + "max": {breadcrumbPath("a", "b"), BreadcrumbOptions{MaxItems: 2}}, + "label": {[]BreadcrumbItem{{Icon: icons.Must("house")}}, BreadcrumbOptions{}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Breadcrumb(c, tc.items, tc.opts) + }, 300, 100, 1) + }) + } +} + +func TestBreadcrumbSnapshot(t *testing.T) { + snapshot(t, "breadcrumb", 560, 120, func(c *ui.Context) { + ui.Column(c).Padding(16).Gap(12).AlignItems(ui.Start).Children(func() { + Breadcrumb(c, []BreadcrumbItem{{Label: "Home", Icon: icons.Must("house")}, {Label: "Library"}, {Label: "Collected poems"}}, BreadcrumbOptions{}) + Breadcrumb(c, breadcrumbPath("Archive", "2026", "October", "Correspondence of the court", "Letter to the Duchess of Mujica"), BreadcrumbOptions{MaxItemWidth: 140}) + }) + }) +} diff --git a/command_palette.go b/command_palette.go new file mode 100644 index 0000000..e49f66f --- /dev/null +++ b/command_palette.go @@ -0,0 +1,212 @@ +package mujica + +import ( + "log/slog" + "slices" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// Command is an action of a CommandPalette. +type Command struct { + ID string + Label string + // Group heads the command in the list; commands keep their order within groups. + Group string + // Keywords also match the search. + Keywords []string + Icon *ui.SVG +} + +// CommandPaletteOptions configure a CommandPalette. +type CommandPaletteOptions struct { + // OpenMods and OpenKey open the palette; zero key means Cmd+K (Ctrl+K off macOS). + OpenMods ui.Modifiers + OpenKey ui.Key +} + +// CommandPaletteResult is the built CommandPalette; Element is nil while closed. +type CommandPaletteResult struct { + *ui.Element + chosen string +} + +// Chosen returns the ID of the command run in this frame; running closes the palette. +func (r CommandPaletteResult) Chosen() (string, bool) { return r.chosen, r.chosen != "" } + +type commandPaletteKey struct{ open *bool } + +type commandPaletteState struct { + query string + hl int + ptr [2]float32 + wasOpen bool + reveal bool +} + +// CommandPalette is a searchable command dialog opened by a shortcut or by setting *open. +func CommandPalette(c *ui.Context, open *bool, commands []Command, opts CommandPaletteOptions) CommandPaletteResult { + seen := map[string]bool{} + for _, cmd := range commands { + if cmd.ID == "" || cmd.Label == "" { + panic("mujica: Command needs an ID and a Label") + } + if seen[cmd.ID] { + panic("mujica: duplicate Command ID " + cmd.ID) + } + seen[cmd.ID] = true + } + mods, key := opts.OpenMods, opts.OpenKey + if key == 0 { + mods, key = ui.Cmd, ui.KeyK + } + st := ui.Local(c.Root(), commandPaletteKey{open}, func() commandPaletteState { return commandPaletteState{} }) + if c.Shortcut(mods, key) && !*open { + *open = true + } + if *open && !st.wasOpen { + st.query, st.hl = "", 0 + slog.Debug("mujica: command palette open") + } + st.wasOpen = *open + res := CommandPaletteResult{} + k := Tokens(c) + d := Density(c) + 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) + 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")) + 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() { + ui.Icon(c, icons.Must("search")).Size(16, 16).TextColor(k.TextMuted) + in = ui.TextInputBase(c, &st.query).FillWidth().AutoFocus().Label(msg(c, "command_palette.search")) + }) + ui.Box(c).Height(1).Background(k.Ornament).Role(ui.RoleNone) + if in.Changed() { + st.hl = 0 + } + n := len(found) + switch { + case n == 0: + case in.Shortcut(0, ui.KeyDown): + st.hl, st.reveal = (st.hl+1)%n, true + case in.Shortcut(0, ui.KeyUp): + st.hl, st.reveal = (st.hl-1+n)%n, true + } + st.hl = max(0, min(st.hl, n-1)) + run := -1 + if in.Submitted() && n > 0 { + run = st.hl + } + list := ui.Scroll(c).Padding(u * 2).Gap(2).MaxHeight(360).Role(ui.RoleList) + list.Children(func() { + if n == 0 { + ui.Text(c, msg(c, "command_palette.empty")).TextColor(k.TextMuted).Padding(u*6, u*2).TextAlign(ui.Center).FillWidth() + return + } + gutter := slices.ContainsFunc(found, func(cmd Command) bool { return cmd.Icon != nil }) + group := "\x00" + for i, cmd := range found { + if cmd.Group != group { + group = cmd.Group + if group != "" { + ui.Text(c, group).Font(theme.SerifFont).FontSize(theme.CaptionSize). + TextColor(k.Ornament.Mix(k.Text, 0.35)).Padding(u*2, u*2, u, u*2).SingleLine() + } + } + if commandPaletteRow(c, cmd, i, st, gutter) { + run = i + } + } + }) + ui.Row(c).Gap(u*4).Padding(u*2, u*4).Background(k.Background).Children(func() { + for _, hint := range []string{"command_palette.hint_move", "command_palette.hint_run", "command_palette.hint_close"} { + ui.Text(c, msg(c, hint)).FontSize(theme.CaptionSize).TextColor(k.TextMuted).SingleLine() + } + }) + if run >= 0 { + res.chosen = found[run].ID + *open = false + slog.Debug("mujica: command palette run", "id", res.chosen) + } + }) + if !*open { + st.wasOpen = false + } + return res +} + +// commandPaletteRow builds row i and reports a click on it. +func commandPaletteRow(c *ui.Context, cmd Command, i int, st *commandPaletteState, gutter bool) bool { + k := Tokens(c) + d := Density(c) + u := d.Unit() + row := ui.Row(c).AlignItems(ui.Center).Gap(u*3).MinHeight(d.ControlHeight()).Padding(0, u*3). + Radius(theme.ControlRadius).Role(ui.RoleListItem).Label(cmd.Label).TextColor(k.Text).Cursor(ui.CursorPointer) + on := i == st.hl + if on { + row.Background(k.Selection).FontWeight(600) + row.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + 4, W: 3, H: r.H - 8}, k.AccentText, 1.5) + }) + if st.reveal { + row.ScrollIntoView() + st.reveal = false + } + } + px, py, over := row.PointerPosition() + b := row.Bounds() + if at := [2]float32{b.X + px, b.Y + py}; over && at != st.ptr { + st.ptr = at + if !on { + st.hl = i + c.Invalidate() + } + } + row.Children(func() { + if cmd.Icon != nil { + ui.Icon(c, cmd.Icon).Size(16, 16).TextColor(k.TextMuted) + } else if gutter { + ui.Box(c).Size(16, 16).Role(ui.RoleNone) + } + ui.Text(c, cmd.Label).SingleLine() + }) + return row.Clicked() +} + +// commandPaletteFilter keeps commands matching every word of query, grouped by first appearance. +func commandPaletteFilter(commands []Command, query string) []Command { + words := strings.Fields(strings.ToLower(query)) + var groups []string + byGroup := map[string][]Command{} + for _, cmd := range commands { + hay := strings.ToLower(cmd.Label + " " + cmd.Group + " " + strings.Join(cmd.Keywords, " ")) + ok := true + for _, w := range words { + if !strings.Contains(hay, w) { + ok = false + break + } + } + if !ok { + continue + } + if _, seen := byGroup[cmd.Group]; !seen { + groups = append(groups, cmd.Group) + } + byGroup[cmd.Group] = append(byGroup[cmd.Group], cmd) + } + var out []Command + for _, g := range groups { + out = append(out, byGroup[g]...) + } + return out +} diff --git a/command_palette_test.go b/command_palette_test.go new file mode 100644 index 0000000..724250f --- /dev/null +++ b/command_palette_test.go @@ -0,0 +1,142 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +func commandPaletteSample() []Command { + return []Command{ + {ID: "new", Label: "New document", Group: "File", Icon: icons.Must("file")}, + {ID: "open", Label: "Open recent", Group: "File", Icon: icons.Must("folder")}, + {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"theme", "night"}, Icon: icons.Must("moon")}, + {ID: "zoom", Label: "Zoom in", Group: "View", Icon: icons.Must("zoom-in")}, + {ID: "save", Label: "Save all", Group: "File"}, + } +} + +type paletteApp struct { + open bool + ran []string +} + +func (a *paletteApp) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Fill().Children(func() { + ui.Text(c, "Editor") + if id, ok := CommandPalette(c, &a.open, commandPaletteSample(), CommandPaletteOptions{}).Chosen(); ok { + a.ran = append(a.ran, id) + } + }) +} + +func TestCommandPaletteFilter(t *testing.T) { + ids := func(cs []Command) (out []string) { + for _, c := range cs { + out = append(out, c.ID) + } + return + } + got := ids(commandPaletteFilter(commandPaletteSample(), "")) + // Groups keep first-appearance order: File's Save joins its group. + want := []string{"new", "open", "save", "theme", "zoom"} + if len(got) != len(want) { + t.Fatalf("got %v", got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("got %v want %v", got, want) + } + } + if got := ids(commandPaletteFilter(commandPaletteSample(), "NIGHT")); len(got) != 1 || got[0] != "theme" { + t.Fatalf("keyword search %v", got) + } + if got := ids(commandPaletteFilter(commandPaletteSample(), "file save")); len(got) != 1 || got[0] != "save" { + t.Fatalf("multi-word search %v", got) + } +} + +func TestCommandPaletteShortcutSearchRun(t *testing.T) { + a := &paletteApp{} + tt := ui.NewTester(a.view, 800, 600) + tt.Key(ui.Cmd, ui.KeyK) + if !a.open || !tt.HasText("Zoom in") { + t.Fatalf("Cmd+K did not open: %q", tt.Texts()) + } + tt.Type("zo") + if tt.HasText("New document") || !tt.HasText("Zoom in") { + t.Fatalf("search did not filter: %q", tt.Texts()) + } + tt.Key(0, ui.KeyEnter) + if a.open || len(a.ran) != 1 || a.ran[0] != "zoom" { + t.Fatalf("Enter: open %v ran %q", a.open, a.ran) + } + tt.Key(ui.Cmd, ui.KeyK) + if !tt.HasText("New document") { + t.Fatal("query not reset on reopen") + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyUp) + tt.Key(0, ui.KeyUp) + tt.Key(0, ui.KeyUp) // wraps to the last + tt.Key(0, ui.KeyEnter) + if len(a.ran) != 2 || a.ran[1] != "zoom" { + t.Fatalf("arrows ran %q", a.ran) + } +} + +func TestCommandPaletteEmptyClickEscape(t *testing.T) { + a := &paletteApp{open: true} + tt := ui.NewTester(a.view, 800, 600) + tt.Type("qqq") + if !tt.HasText("No matching commands") { + t.Fatalf("no empty state: %q", tt.Texts()) + } + tt.Key(0, ui.KeyEnter) + if !a.open || len(a.ran) != 0 { + t.Fatal("Enter with no results ran something") + } + tt.Key(0, ui.KeyEscape) + if a.open { + t.Fatal("Escape did not close") + } + a.open = true + tt.Frame() + if err := tt.Click("Open recent"); err != nil { + t.Fatal(err) + } + if a.open || len(a.ran) != 1 || a.ran[0] != "open" { + t.Fatalf("click: open %v ran %q", a.open, a.ran) + } +} + +func TestCommandPaletteInvalid(t *testing.T) { + for name, cmds := range map[string][]Command{ + "id": {{Label: "A"}}, + "dup": {{ID: "a", Label: "A"}, {ID: "a", Label: "B"}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + open := true + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + CommandPalette(c, &open, cmds, CommandPaletteOptions{}) + }, 300, 100, 1) + }) + } +} + +func TestCommandPaletteSnapshot(t *testing.T) { + open := true + snapshot(t, "command_palette", 640, 460, func(c *ui.Context) { + CommandPalette(c, &open, commandPaletteSample(), CommandPaletteOptions{}) + }) +} diff --git a/docs/components/059-tabs.md b/docs/components/059-tabs.md new file mode 100644 index 0000000..f99cb30 --- /dev/null +++ b/docs/components/059-tabs.md @@ -0,0 +1,63 @@ +# 059 Tabs 标签页 + +## 用途 + +在同一区域内切换若干面板。标签列表与当前标签的面板一起构建;支持横向与纵向布局、禁用项和溢出滚动。Tabs 是视觉回归基准组件,快照覆盖三种密度。 + +## 最小示例 + +```go +mujica.Tabs(c, &tab, []mujica.Tab{ + {Label: "General", Panel: func() { ui.Text(c, "General settings") }}, + {Label: "Fonts", Icon: icons.Must("italic"), Panel: func() { ui.Text(c, "Fonts") }}, + {Label: "Archive", Disabled: true}, +}, mujica.TabsOptions{Label: "Settings"}) +``` + +签名:`func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) TabsResult` + +## 参数 + +`selected`:当前标签下标(从 0 开始)。越界或指向禁用标签时 panic。`tabs` 为空时 panic。 + +`Tab` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | 必填 | 标签文字,也是面板的无障碍名称;为空时 panic。 | +| `Icon` | `*ui.SVG` | nil | 标签文字前的 16 DIP 图标。 | +| `Disabled` | `bool` | false | 禁用:不可点击、不进入方向键序列。 | +| `Panel` | `func()` | nil | 选中时构建的面板内容;nil 显示空面板。 | + +`TabsOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Vertical` | `bool` | false | 标签竖排在左侧,面板在右侧。 | +| `Label` | `string` | "" | 标签列表的无障碍名称。 | + +返回 `TabsResult`,内嵌包含标签与面板的 `*ui.Element`。 + +## 状态 + +- 选中:Selection 背景、2 DIP AccentText 边线(横向在底边,纵向在右边)、600 字重,三者同时标记。 +- 悬停:SurfaceHover 背景;按下:Selection 背景。 +- 焦点:2 DIP Focus 焦点环,外留 2 DIP。 +- 禁用:MyGo 统一降低不透明度,不响应指针与键盘。 +- 溢出:标签条横向(纵向时竖向)滚动;键盘或点击切换后,选中标签滚入视野。 + +## 事件 + +- `Changed()`:本帧用户选择了新标签(点击或方向键)。 + +## 键盘操作 + +- Tab:进入标签列表,落在当前标签;再按 Tab 离开。 +- ←/→、↑/↓:选择上一个/下一个可用标签,跳过禁用项,首尾循环;焦点随选择移动。 +- Home / End:第一个 / 最后一个可用标签。 + +## 限制 + +- MyGo `TabsBase` 的方向键不知道禁用项(`ui/base.go:192` 直接选择 i±1)。本组件只把可用标签交给 `TabsBase`,禁用标签以普通 `RoleTab` 行显示,因此禁用标签不在 MyGo 的标签计数内。 +- MyGo 没有 tabpanel 角色与 aria-controls 式关联;面板以 `RoleGroup` 呈现,名称取自所选标签。 +- 不提供标签关闭、拖动排序。 diff --git a/docs/components/060-segmented-control.md b/docs/components/060-segmented-control.md new file mode 100644 index 0000000..1a93ae4 --- /dev/null +++ b/docs/components/060-segmented-control.md @@ -0,0 +1,55 @@ +# 060 SegmentedControl 分段控件 + +## 用途 + +在少量互斥选项之间切换视图或范围,如“日 / 周 / 月”或“列表 / 网格”。基于 MyGo `SegmentedBase`,辅助技术看到单选组。 + +## 最小示例 + +```go +mujica.SegmentedControl(c, &view, []mujica.Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: true}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: true}, +}, mujica.SegmentedControlOptions{Label: "View"}) +``` + +签名:`func SegmentedControl(c *ui.Context, selected *int, segments []Segment, opts SegmentedControlOptions) *ui.Element` + +## 参数 + +`selected`:当前分段下标;越界时 panic。`segments` 为空时 panic。 + +`Segment` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | 必填 | 分段文字与无障碍名称;为空时 panic。 | +| `Icon` | `*ui.SVG` | nil | 文字前的图标。 | +| `HideLabel` | `bool` | false | 只显示图标,`Label` 作为名称与提示文字;没有 `Icon` 时 panic。 | + +`SegmentedControlOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Equal` | `bool` | false | 所有分段等宽(取最宽者);否则按内容自适应。 | +| `Label` | `string` | "" | 单选组的无障碍名称。 | +| `Disabled` | `bool` | false | 整组禁用。 | + +## 状态 + +- 选中:Accent 填充、OnAccent 文字、600 字重。 +- 悬停 SurfaceHover,按下 Selection;焦点环 2 DIP。 +- 禁用:整组降低不透明度,点击与方向键不改变值。 + +## 事件 + +- 返回元素的 `Changed()`:本帧用户选择了新分段。 + +## 键盘操作 + +- Tab:进入控件,落在当前分段(一个 Tab 停靠点)。 +- 方向键:选择上一个/下一个分段并循环;Home / End 到两端。 + +## 限制 + +- 不支持单个分段禁用:MyGo `SegmentedBase` 的焦点组在方向键移动时直接点击目标分段。 diff --git a/docs/components/061-breadcrumb.md b/docs/components/061-breadcrumb.md new file mode 100644 index 0000000..15240cd --- /dev/null +++ b/docs/components/061-breadcrumb.md @@ -0,0 +1,57 @@ +# 061 Breadcrumb 面包屑 + +## 用途 + +显示当前位置的层级路径。最后一项为当前页(非链接),前面各项可点击返回;层级过多时中间项折叠到“…”菜单;过长名称以省略号截断,悬停显示完整名称。 + +## 最小示例 + +```go +if i, ok := mujica.Breadcrumb(c, path, mujica.BreadcrumbOptions{}).Chosen(); ok { + path = path[:i+1] +} +``` + +签名:`func Breadcrumb(c *ui.Context, items []BreadcrumbItem, opts BreadcrumbOptions) BreadcrumbResult` + +## 参数 + +`items` 为空时 panic。 + +`BreadcrumbItem` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | 必填 | 名称;为空时 panic。 | +| `Icon` | `*ui.SVG` | nil | 名称前的 14 DIP 图标。 | + +`BreadcrumbOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `MaxItems` | `int` | 0(即 4) | 最多显示的项数;超出时保留第一项和最后 `MaxItems-2` 项,其余折叠。小于 3 时 panic。 | +| `MaxItemWidth` | `float32` | 0(即 160 DIP) | 单项文字最大宽度,超出以省略号截断。 | +| `Label` | `string` | 内置“路径导航” | 整组的无障碍名称。 | + +返回 `BreadcrumbResult`,内嵌 `*ui.Element`。 + +## 状态 + +- 链接:TextMuted 文字,悬停 SurfaceHover 背景与 Text 文字,焦点环 2 DIP。 +- 当前项:Text 文字、600 字重,无障碍名称附“当前页”。 +- 分隔:旧金(Ornament)小箭头,不进入无障碍树。 +- 折叠按钮“…”:打开弹层列出被折叠的项,弹层打开时焦点在第一项。 + +## 事件 + +- `Chosen() (int, bool)`:本帧被激活的项下标(含折叠菜单中的项)。组件不修改路径,由调用方处理。 + +## 键盘操作 + +- Tab / Shift+Tab:在各链接与折叠按钮之间移动;Enter 或 Space 激活。 +- 折叠菜单:Tab 在项之间移动,Enter 选择,Escape 关闭并把焦点还给“…”按钮。 + +## 限制 + +- 完整名称提示对每一项都提供,MyGo 不报告文字是否被截断。 +- 未复用 MyGo `ui.Breadcrumbs`:它不支持折叠与提示,本组件用 `ButtonBase`、`PopoverBase` 组合实现。 diff --git a/docs/components/062-pagination.md b/docs/components/062-pagination.md new file mode 100644 index 0000000..36943b7 --- /dev/null +++ b/docs/components/062-pagination.md @@ -0,0 +1,53 @@ +# 062 Pagination 分页 + +## 用途 + +在分页数据之间移动:页码、上一页、下一页、页容量选择与跳页。总数变化后,超出范围的页码自动修正到最后一页并报告变化。 + +## 最小示例 + +```go +if mujica.Pagination(c, &page, &size, mujica.PaginationOptions{Total: 420, PageSizes: []int{10, 20, 50}, Jump: true}).Changed() { + load(page, size) +} +``` + +签名:`func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) PaginationResult` + +## 参数 + +`page`:当前页,从 1 开始;每帧被限制在 `[1, 总页数]`。`pageSize`:每页条数,必须为正,否则 panic。 + +`PaginationOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Total` | `int` | 0 | 记录总数;为负 panic。为 0 时仍显示 1 页。 | +| `PageSizes` | `[]int` | nil | 可选的页容量;nil 隐藏选择器。非 nil 时须包含 `*pageSize`,否则 panic。 | +| `Jump` | `bool` | false | 显示跳页输入框。 | +| `Siblings` | `int` | 0(即 1) | 当前页两侧显示的页码数;为负 panic。 | +| `Disabled` | `bool` | false | 整体禁用。 | + +返回 `PaginationResult`,内嵌 `*ui.Element`。 + +## 状态 + +- 当前页:Accent 填充、OnAccent 文字、600 字重,名称附“当前”。 +- 页码按钮:Border 细边框,悬停 SurfaceHover 与 ControlBorder 边框;首页时“上一页”禁用,末页时“下一页”禁用。 +- 页码多时首尾保留,中间以“…”省略,按钮数固定为 `2×Siblings+5`,位置稳定。 +- 跳页错误:输入非数字或超出范围时边框变为 Danger,并在旁显示有效范围;输入保留,可直接修改。 +- 修改页容量时,保持原页第一条记录可见。 + +## 事件 + +- `Changed()`:本帧页码或页容量改变(用户操作或总数变化导致的修正)。 + +## 键盘操作 + +- 页码按钮组是一个 Tab 停靠点:←/→ 移动,Home / End 到两端,Enter 或 Space 跳到该页。 +- 页容量:↑/↓ 打开并移动,Enter 选择,Escape 关闭。 +- 跳页框:输入页码后按 Enter。 + +## 限制 + +- 页容量选择基于 MyGo `SelectBase`,弹层由 MujicaUI 绘制。 diff --git a/docs/components/063-steps.md b/docs/components/063-steps.md new file mode 100644 index 0000000..b9e7051 --- /dev/null +++ b/docs/components/063-steps.md @@ -0,0 +1,59 @@ +# 063 Steps 步骤条 + +## 用途 + +显示多步流程的进度:已完成、当前、等待和出错四种状态;横向或纵向排列;每一步是否可点击由调用方决定。 + +## 最小示例 + +```go +mujica.Steps(c, ¤t, []mujica.Step{ + {Title: "Account", Clickable: true}, + {Title: "Profile", Description: "Name and seal"}, + {Title: "Confirm"}, +}, mujica.StepsOptions{Label: "Sign-up"}) +``` + +签名:`func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) StepsResult` + +## 参数 + +`current`:进行中的步骤下标;之前的步骤视为已完成。等于 `len(steps)` 表示全部完成;超出范围 panic。`steps` 为空时 panic。 + +`Step` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Title` | `string` | 必填 | 步骤标题;为空时 panic。 | +| `Description` | `string` | "" | 标题下的说明文字。 | +| `Error` | `bool` | false | 标为出错,优先于位置推导的状态。 | +| `Clickable` | `bool` | false | 允许用户点击跳到该步。 | + +`StepsOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Vertical` | `bool` | false | 纵向排列,连接线在标记下方。 | +| `Label` | `string` | "" | 整组的无障碍名称。 | + +返回 `StepsResult`,内嵌 `*ui.Element`。 + +## 状态 + +- 已完成:Accent 圆形标记内 OnAccent 勾号;之后的连接线为 AccentText。 +- 当前:AccentText 圆环与序号,标题 600 字重。 +- 等待:ControlBorder 圆环与 TextMuted 序号、标题。 +- 出错:Danger 圆环与叉号,标题 Danger 色。 +- 每步的无障碍名称为“第 n 步,共 m 步:标题,状态”,状态不只靠颜色表达。 + +## 事件 + +- `Changed()`:本帧用户点击了可点击的步骤,`*current` 已更新。 + +## 键盘操作 + +- 可点击的步骤组成一个 Tab 停靠点,方向键在其间移动,Enter 或 Space 跳到该步。不可点击的步骤不获取焦点。 + +## 限制 + +- MyGo 没有 step 或 aria-current 角色;步骤以列表项(可点击时为按钮)呈现,状态写入名称。 diff --git a/docs/components/064-sidebar.md b/docs/components/064-sidebar.md new file mode 100644 index 0000000..f32e7b8 --- /dev/null +++ b/docs/components/064-sidebar.md @@ -0,0 +1,71 @@ +# 064 Sidebar 侧边栏 + +## 用途 + +应用的源列表导航:分组、嵌套、图标、计数和选中项,分组与嵌套组可展开折叠;紧凑模式只显示图标。基于 MyGo `ui.Sidebar`,保留其键盘、首字母跳转与树形无障碍结构。 + +## 最小示例 + +```go +mujica.Sidebar(c, &selected, []mujica.SidebarSection{ + {Title: "Mail", Items: []mujica.SidebarItem{ + {ID: "inbox", Label: "Inbox", Icon: icons.Must("inbox"), Count: 3}, + {Label: "Folders", Items: []mujica.SidebarItem{{ID: "work", Label: "Work", Icon: icons.Must("folder")}}}, + }}, +}, mujica.SidebarOptions{}) +``` + +签名:`func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) SidebarResult` + +## 参数 + +`selected`:选中项的 ID。 + +`SidebarSection` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Title` | `string` | 必填 | 分组标题,点击展开折叠;为空时 panic(紧凑模式不显示)。 | +| `Items` | `[]SidebarItem` | nil | 分组内的项。 | + +`SidebarItem` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `ID` | `string` | 叶子项必填 | 选择时写入 `*selected`;叶子项为空时 panic。 | +| `Label` | `string` | 必填 | 名称;为空时 panic。 | +| `Icon` | `*ui.SVG` | nil | 名称前图标;紧凑模式下必填。 | +| `Count` | `int` | 0 | 大于 0 时在右侧显示计数,超过 999 显示 “999+”。 | +| `Items` | `[]SidebarItem` | nil | 非 nil 时该项成为可展开折叠的嵌套组,子项缩进。 | + +`SidebarOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Compact` | `bool` | false | 图标栏:隐藏文字与组标题,组之间以旧金细线分隔,名称作为悬停提示。 | +| `Width` | `float32` | 0(240,紧凑时 56) | 宽度,DIP。高度填满父容器。 | + +返回 `SidebarResult`,内嵌 `*ui.Element`。展开折叠状态由组件保存。 + +## 状态 + +- 选中:Selection 背景、左侧 3 DIP AccentText 边线、600 字重,三者同时标记。 +- 悬停:SurfaceHover 背景。 +- 焦点:侧边栏获得焦点时,在选中项上画焦点环。 +- 计数:等宽数字,名称附计数(如 “Inbox (12)”)。 + +## 事件 + +- `Changed()`:本帧用户选择了新项。 + +## 键盘操作 + +- Tab:进入侧边栏(一个停靠点)。 +- ↑/↓:上一项/下一项;Home / End:首项/末项;输入名称开头字母跳到匹配项。 +- 分组与嵌套组标题用指针点击展开折叠。 + +## 限制 + +- 复用 MyGo `ui.SidebarItem`,它用主题 Accent 绘制图标(`ui/sidebar.go:182`);深色主题 Accent 对比度不足,本组件在构建侧边栏时临时把 MyGo 主题的 Accent 换成 AccentText。 +- MyGo `SidebarItem` 总是追加名称文字(`ui/sidebar.go:212`);紧凑模式传入空名称,名称改由 `Label` 与提示提供,因此紧凑模式下首字母跳转不可用。 +- 嵌套组用 MyGo `SidebarSection` 实现,组标题本身不能被选中,且只能用指针展开折叠(MyGo 不给标题注册按键)。 diff --git a/docs/components/065-navigation-menu.md b/docs/components/065-navigation-menu.md new file mode 100644 index 0000000..630ca64 --- /dev/null +++ b/docs/components/065-navigation-menu.md @@ -0,0 +1,61 @@ +# 065 NavigationMenu 导航菜单 + +## 用途 + +站点式的顶层导航:直接链接与下拉分组(分组内可再分小节),用应用已有的 `*ui.Router` 标记当前路径并跳转;可选显示 MyGo 的后退、前进按钮。 + +## 最小示例 + +```go +mujica.NavigationMenu(c, router, []mujica.NavigationLink{ + {Label: "Home", Path: "/"}, + {Label: "Library", Items: []mujica.NavigationLink{ + {Label: "Poems", Path: "/library/poems", Description: "Verse of the court"}, + }}, +}, mujica.NavigationMenuOptions{History: true}) +router.View(c, func(r *ui.Route) { ui.Text(c, r.Path()) }) +``` + +签名:`func NavigationMenu(c *ui.Context, router *ui.Router, links []NavigationLink, opts NavigationMenuOptions) *ui.Element` + +## 参数 + +`router` 为 nil 或 `links` 为空时 panic。路由状态由应用持有,组件只读 `Path()` 并调用 `Push`。 + +`NavigationLink` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | 必填 | 名称;为空时 panic。 | +| `Path` | `string` | 叶子必填 | 点击后 `router.Push(Path)`;既无 `Path` 也无 `Items` 时 panic。 | +| `Icon` | `*ui.SVG` | nil | 名称前图标。 | +| `Description` | `string` | "" | 下拉面板中名称下方的说明。 | +| `Items` | `[]NavigationLink` | nil | 非 nil 时为分组:顶层显示为下拉按钮,面板内显示为小节标题。 | + +`NavigationMenuOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `History` | `bool` | false | 在前面显示后退、前进按钮(MyGo `BackButton` / `ForwardButton`)。 | +| `Label` | `string` | 内置“导航菜单” | 链接组的无障碍名称。 | + +## 状态 + +- 当前路径:路径等于链接,或位于其下(`/library` 覆盖 `/library/poems`;`/` 只匹配自身)。顶层项以 600 字重与底部 AccentText 线标记;面板中的当前项为 Selection 背景、左侧边线与 600 字重。 +- 悬停或展开:SurfaceHover 背景。焦点环 2 DIP。 +- 历史按钮在历史两端禁用;右键列出可前往的页面(MyGo 行为)。 + +## 事件 + +- 无返回事件:跳转直接作用于 `router`,调用方读取 `router.Path()`。 + +## 键盘操作 + +- Tab:进入顶层链接组;←/→ 在顶层项之间移动。 +- 在分组上按 ↓、Enter 或 Space 打开面板,焦点在第一个链接;↑/↓ 在面板内移动,Enter 跳转并关闭,Escape 关闭并把焦点还给分组按钮。 +- 历史:Cmd+[ / Cmd+](macOS),Alt+←/→(其他平台),以及鼠标侧键,由 MyGo 路由处理。 + +## 限制 + +- MyGo 历史按钮的提示文字固定为英文 “Back” / “Forward”(`ui/router.go:713`);本组件覆盖了无障碍名称,但提示仍为英文。 +- 历史按钮的外观来自 MyGo 主题按钮样式(经 `mujica.Use` 映射为 MujicaUI 颜色)。 diff --git a/docs/components/066-menubar.md b/docs/components/066-menubar.md new file mode 100644 index 0000000..6f45bba --- /dev/null +++ b/docs/components/066-menubar.md @@ -0,0 +1,74 @@ +# 066 Menubar 菜单栏 + +## 用途 + +应用窗口内由 MujicaUI 自绘的菜单栏(不是系统菜单):多级子菜单、勾选项、单选项、分隔线、快捷键提示和完整键盘导航。 + +## 最小示例 + +```go +res := mujica.Menubar(c, []mujica.MenubarMenu{ + {Label: "File", Items: []mujica.MenubarItem{ + {ID: "new", Label: "New", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyN}}, + {Label: "Export", Items: []mujica.MenubarItem{{ID: "pdf", Label: "PDF"}}}, + }}, + {Label: "View", Items: []mujica.MenubarItem{ + {ID: "wrap", Label: "Word wrap", Kind: mujica.MenubarCheck, Checked: wrap}, + }}, +}, mujica.MenubarOptions{}) +if id, ok := res.Chosen(); ok && id == "wrap" { + wrap = !wrap +} +``` + +签名:`func Menubar(c *ui.Context, menus []MenubarMenu, opts MenubarOptions) MenubarResult` + +## 参数 + +`menus` 为空或标题为空时 panic。 + +`MenubarItem` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `ID` | `string` | 必填(子菜单与分隔线除外) | `Chosen` 报告的标识。 | +| `Label` | `string` | 必填(分隔线除外) | 名称。 | +| `Kind` | `MenubarItemKind` | `MenubarAction` | `MenubarAction`、`MenubarCheck`、`MenubarRadio`、`MenubarSeparator`。子菜单必须为 `MenubarAction`。 | +| `Checked` | `bool` | false | 勾选项与单选项的当前值,由调用方持有。 | +| `Disabled` | `bool` | false | 禁用:不可选择,方向键跳过。 | +| `Shortcut` | `MenubarShortcut` | 无 | 仅显示的快捷键提示:macOS 显示 ⌘⇧ 等符号,其他平台显示 Ctrl+Shift+。键名无法表示时 panic。 | +| `Items` | `[]MenubarItem` | nil | 非 nil 时为子菜单。 | + +`MenubarOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | 内置“菜单栏” | 菜单栏的无障碍名称。 | + +返回 `MenubarResult`,内嵌菜单栏 `*ui.Element`。 + +## 状态 + +- 打开的菜单标题:Selection 背景;高亮项:Selection 背景。 +- 勾选项:AccentText 勾号;单选项:AccentText 菱形标记;名称附“已勾选”。 +- 禁用项:降低不透明度,指针与键盘均跳过。 +- 弹层:6 DIP 圆角、Border 边框;靠近窗口边缘时左移,子菜单放不下时翻到父菜单左侧,过高时上移。 + +## 事件 + +- `Chosen() (string, bool)`:本帧被选择的项 ID。选择后菜单关闭;勾选与单选由调用方更新 `Checked`。 + +## 键盘操作 + +- Tab:进入菜单栏(一个停靠点);←/→ 在标题间移动;↓、↑、Enter 或 Space 打开菜单。 +- 菜单中:↑/↓ 移动(跳过分隔线与禁用项并循环),Home / End 到首末项。 +- →:打开子菜单;在普通项上则打开右侧下一个菜单。←:关闭子菜单;在顶层则打开左侧菜单。 +- Enter / Space:选择或打开子菜单。Escape:关闭一级。 +- 指针:点击标题打开;菜单打开时移到其他标题即切换;悬停子菜单项展开;点击外部关闭。 + +## 限制 + +- 快捷键只做提示,不注册;用 `c.Shortcut` 处理。 +- MyGo 没有 menu / menuitem 角色(`ui/access.go:25-79`),项以按钮呈现;勾选状态无法通过公开接口写入(`ui/base.go:56` 的 checked 为内部字段),因此写入名称。 +- 指针点击非可聚焦元素会清除焦点(`ui/input.go:238`);选择后组件把焦点还给对应标题。 +- 菜单打开期间,焦点保持在标题上,高亮项由组件管理(与 MyGo `SelectBase` 相同的方式)。 diff --git a/docs/components/067-toolbar.md b/docs/components/067-toolbar.md new file mode 100644 index 0000000..7d05381 --- /dev/null +++ b/docs/components/067-toolbar.md @@ -0,0 +1,67 @@ +# 067 Toolbar 工具栏 + +## 用途 + +一行操作按钮:分组、分隔线;宽度不足时,放不下的按钮从末尾起移入溢出菜单。基于 MyGo `ui.Toolbar`,保留其焦点组与溢出行为。 + +## 最小示例 + +```go +mujica.Toolbar(c, mujica.ToolbarOptions{Label: "Format"}, func() { + mujica.ToolbarGroup(c, func() { + mujica.ToolbarButton(c, "Bold", mujica.ToolbarButtonOptions{Icon: icons.Must("bold")}) + mujica.ToolbarButton(c, "Italic", mujica.ToolbarButtonOptions{Icon: icons.Must("italic")}) + }) + mujica.ToolbarSeparator(c) + if mujica.ToolbarButton(c, "Copy", mujica.ToolbarButtonOptions{Icon: icons.Must("copy"), ShowLabel: true}).Clicked() { + copySelection() + } +}) +``` + +签名: + +- `func Toolbar(c *ui.Context, opts ToolbarOptions, fn func()) *ui.Element` +- `func ToolbarButton(c *ui.Context, label string, opts ToolbarButtonOptions) *ui.Element` +- `func ToolbarGroup(c *ui.Context, fn func()) *ui.Element` +- `func ToolbarSeparator(c *ui.Context)` + +## 参数 + +`ToolbarOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Label` | `string` | "" | 工具栏的无障碍名称。 | + +`ToolbarButton` 的 `label` 必填(为空时 panic):它是按钮名称、仅图标时的提示文字和溢出菜单中的文字。 + +`ToolbarButtonOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `Icon` | `*ui.SVG` | nil | 16 DIP 图标。 | +| `ShowLabel` | `bool` | false | 图标旁显示文字;没有图标时总是显示。 | +| `Disabled` | `bool` | false | 禁用;溢出菜单中同样禁用。 | + +## 状态 + +- 工具栏:Surface 背景、Border 边框、4 DIP 圆角。 +- 按钮:无底色,悬停 SurfaceHover,按下 Selection,焦点环 2 DIP。 +- 分隔线:旧金细竖线,不进入无障碍树。 +- 溢出:末尾显示 “»” 按钮,菜单中按原顺序列出被收起的操作,组之间以分隔线隔开。 + +## 事件 + +- `ToolbarButton(...).Clicked()`:点击、Enter / Space,或在溢出菜单中选择。 + +## 键盘操作 + +- Tab:进入工具栏(一个停靠点),再按 Tab 离开。 +- ←/→:在操作之间移动(跳过禁用项并循环);Home / End 到首末。 +- Enter / Space:执行。溢出按钮用 Enter、Space 或 ↓ 打开菜单。 + +## 限制 + +- 溢出菜单是 MyGo 打开的系统菜单(`ui/toolbar.go:45`),外观为系统样式,不是 MujicaUI 自绘。 +- 溢出按钮的无障碍名称固定为英文 “More”(`ui/toolbar.go:44`),按钮样式来自 MyGo 主题。 diff --git a/docs/components/068-command-palette.md b/docs/components/068-command-palette.md new file mode 100644 index 0000000..a249aeb --- /dev/null +++ b/docs/components/068-command-palette.md @@ -0,0 +1,65 @@ +# 068 CommandPalette 命令面板 + +## 用途 + +用快捷键(默认 Cmd+K,非 macOS 为 Ctrl+K)打开的模态命令搜索框:输入过滤、分组显示、无结果提示、方向键选择,执行后关闭。 + +## 最小示例 + +```go +res := mujica.CommandPalette(c, &open, []mujica.Command{ + {ID: "new", Label: "New document", Group: "File"}, + {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"night"}}, +}, mujica.CommandPaletteOptions{}) +if id, ok := res.Chosen(); ok { + run(id) +} +``` + +签名:`func CommandPalette(c *ui.Context, open *bool, commands []Command, opts CommandPaletteOptions) CommandPaletteResult` + +## 参数 + +`open`:是否显示;快捷键把它设为 true,执行、Escape 或点击遮罩设为 false。调用方也可直接设置。 + +`Command` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `ID` | `string` | 必填 | `Chosen` 报告的标识;为空或重复时 panic。 | +| `Label` | `string` | 必填 | 显示名称;为空时 panic。 | +| `Group` | `string` | "" | 分组标题;分组按首次出现的顺序排列,组内保持原顺序。 | +| `Keywords` | `[]string` | nil | 额外的搜索词。 | +| `Icon` | `*ui.SVG` | nil | 名称前图标。 | + +`CommandPaletteOptions` + +| 字段 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `OpenMods` | `ui.Modifiers` | 0 | 打开快捷键的修饰键。 | +| `OpenKey` | `ui.Key` | 0(即 `ui.Cmd`+K) | 打开快捷键;为 0 时忽略 `OpenMods`。 | + +返回 `CommandPaletteResult`;关闭时其 `Element` 为 nil。 + +## 状态 + +- 打开:遮罩变暗,面板在窗口上部,焦点在搜索框;每次打开都清空搜索。 +- 搜索:不区分大小写,按空格分词,每个词须出现在名称、分组或关键词中。 +- 高亮项:Selection 背景、左侧 AccentText 边线、600 字重;键盘移动时滚入视野。 +- 无结果:显示“没有匹配的命令”,Enter 不执行任何命令。 +- 底部提示栏说明 ↑↓、Enter、Esc。 + +## 事件 + +- `Chosen() (string, bool)`:本帧执行的命令 ID(Enter 或点击),同时关闭面板。 + +## 键盘操作 + +- Cmd+K / Ctrl+K:打开(窗口级快捷键)。 +- 输入:过滤。↑/↓:移动高亮并循环。Enter:执行并关闭。Escape:关闭不执行,焦点回到打开前的元素。 +- Tab 限制在面板内(MyGo 模态对话框行为)。 + +## 限制 + +- MyGo `TextInputBase` 没有占位文字(`ui/editor.go:734`);为避免与输入法组合文字重叠,说明放在底部提示栏,搜索框以无障碍名称“搜索命令”标识。 +- 搜索是子串匹配,不做模糊匹配或排序打分。 diff --git a/example_navigation_test.go b/example_navigation_test.go new file mode 100644 index 0000000..5b5a611 --- /dev/null +++ b/example_navigation_test.go @@ -0,0 +1,155 @@ +package mujica_test + +import ( + "fmt" + + "github.com/egoist/mygo/ui" + + mujica "mujicaui" + "mujicaui/icons" +) + +func ExampleTabs() { + tab := 0 + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.Tabs(c, &tab, []mujica.Tab{ + {Label: "General", Panel: func() { ui.Text(c, "General settings") }}, + {Label: "Fonts", Icon: icons.Must("italic"), Panel: func() { ui.Text(c, "Fonts") }}, + {Label: "Archive", Disabled: true}, + }, mujica.TabsOptions{Label: "Settings"}).Changed() { + fmt.Println("tab", tab) + } + } + ui.Render(view, 400, 300, 1) +} + +func ExampleSegmentedControl() { + view := 0 + build := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.SegmentedControl(c, &view, []mujica.Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: true}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: true}, + }, mujica.SegmentedControlOptions{Label: "View"}).Changed() { + fmt.Println("view", view) + } + } + ui.Render(build, 400, 300, 1) +} + +func ExampleBreadcrumb() { + path := []mujica.BreadcrumbItem{{Label: "Home", Icon: icons.Must("house")}, {Label: "Library"}, {Label: "Poems"}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if i, ok := mujica.Breadcrumb(c, path, mujica.BreadcrumbOptions{}).Chosen(); ok { + path = path[:i+1] + } + } + ui.Render(view, 400, 300, 1) +} + +func ExamplePagination() { + page, size := 1, 20 + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.Pagination(c, &page, &size, mujica.PaginationOptions{Total: 420, PageSizes: []int{10, 20, 50}, Jump: true}).Changed() { + fmt.Println("load page", page, "of size", size) + } + } + ui.Render(view, 400, 300, 1) +} + +func ExampleSteps() { + current := 1 + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Steps(c, ¤t, []mujica.Step{ + {Title: "Account", Clickable: true}, + {Title: "Profile", Description: "Name and seal"}, + {Title: "Confirm"}, + }, mujica.StepsOptions{Label: "Sign-up"}) + } + ui.Render(view, 400, 300, 1) +} + +func ExampleSidebar() { + selected := "inbox" + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Sidebar(c, &selected, []mujica.SidebarSection{ + {Title: "Mail", Items: []mujica.SidebarItem{ + {ID: "inbox", Label: "Inbox", Icon: icons.Must("inbox"), Count: 3}, + {Label: "Folders", Items: []mujica.SidebarItem{{ID: "work", Label: "Work", Icon: icons.Must("folder")}}}, + }}, + }, mujica.SidebarOptions{}) + } + ui.Render(view, 400, 300, 1) +} + +func ExampleNavigationMenu() { + router := ui.NewRouter("/") + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.NavigationMenu(c, router, []mujica.NavigationLink{ + {Label: "Home", Path: "/"}, + {Label: "Library", Items: []mujica.NavigationLink{ + {Label: "Poems", Path: "/library/poems", Description: "Verse of the court"}, + }}, + }, mujica.NavigationMenuOptions{History: true}) + router.View(c, func(r *ui.Route) { ui.Text(c, r.Path()) }) + } + ui.Render(view, 400, 300, 1) +} + +func ExampleMenubar() { + wrap := false + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + res := mujica.Menubar(c, []mujica.MenubarMenu{ + {Label: "File", Items: []mujica.MenubarItem{ + {ID: "new", Label: "New", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyN}}, + {Label: "Export", Items: []mujica.MenubarItem{{ID: "pdf", Label: "PDF"}}}, + }}, + {Label: "View", Items: []mujica.MenubarItem{ + {ID: "wrap", Label: "Word wrap", Kind: mujica.MenubarCheck, Checked: wrap}, + }}, + }, mujica.MenubarOptions{}) + if id, ok := res.Chosen(); ok && id == "wrap" { + wrap = !wrap + } + } + ui.Render(view, 400, 300, 1) +} + +func ExampleToolbar() { + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Toolbar(c, mujica.ToolbarOptions{Label: "Format"}, func() { + mujica.ToolbarGroup(c, func() { + mujica.ToolbarButton(c, "Bold", mujica.ToolbarButtonOptions{Icon: icons.Must("bold")}) + mujica.ToolbarButton(c, "Italic", mujica.ToolbarButtonOptions{Icon: icons.Must("italic")}) + }) + mujica.ToolbarSeparator(c) + if mujica.ToolbarButton(c, "Copy", mujica.ToolbarButtonOptions{Icon: icons.Must("copy"), ShowLabel: true}).Clicked() { + fmt.Println("copy") + } + }) + } + ui.Render(view, 400, 300, 1) +} + +func ExampleCommandPalette() { + open := false + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + res := mujica.CommandPalette(c, &open, []mujica.Command{ + {ID: "new", Label: "New document", Group: "File"}, + {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"night"}}, + }, mujica.CommandPaletteOptions{}) + if id, ok := res.Chosen(); ok { + fmt.Println("run", id) + } + } + ui.Render(view, 400, 300, 1) +} diff --git a/examples/gallery/pages_navigation.go b/examples/gallery/pages_navigation.go index 1f53a12..3809106 100644 --- a/examples/gallery/pages_navigation.go +++ b/examples/gallery/pages_navigation.go @@ -1,3 +1,462 @@ package main -var navigationPages = []page{} +import ( + "fmt" + "math" + + "github.com/egoist/mygo/ui" + + mujica "mujicaui" + "mujicaui/icons" +) + +var navigationPages = []page{ + {ID: "059", Name: "Tabs", ZhName: "标签页", Category: "Navigation", Demo: navigationTabsDemo, Code: navigationTabsCode, + Keys: "Tab enters the tab list at the chosen tab. Left/Right (Up/Down when vertical) choose the next enabled tab, skipping disabled ones and wrapping; Home and End choose the first and last."}, + {ID: "060", Name: "SegmentedControl", ZhName: "分段控件", Category: "Navigation", Demo: navigationSegmentedDemo, Code: navigationSegmentedCode, + Keys: "One Tab stop. Arrow keys choose the previous or next segment and wrap; Home and End choose the ends."}, + {ID: "061", Name: "Breadcrumb", ZhName: "面包屑", Category: "Navigation", Demo: navigationBreadcrumbDemo, Code: navigationBreadcrumbCode, + Keys: "Tab moves through the links and the fold button; Enter or Space activates. In the fold menu, Tab moves between items and Escape closes it, returning focus to the button."}, + {ID: "062", Name: "Pagination", ZhName: "分页", Category: "Navigation", Demo: navigationPaginationDemo, Code: navigationPaginationCode, + Keys: "The page buttons are one Tab stop: Left/Right move, Home/End jump to the ends, Enter or Space goes to the page. The size select opens with Up/Down; the jump field goes on Enter."}, + {ID: "063", Name: "Steps", ZhName: "步骤条", Category: "Navigation", Demo: navigationStepsDemo, Code: navigationStepsCode, + Keys: "Clickable steps are one Tab stop; arrow keys move between them and Enter or Space goes to the step."}, + {ID: "064", Name: "Sidebar", ZhName: "侧边栏", Category: "Navigation", Demo: navigationSidebarDemo, Code: navigationSidebarCode, + Keys: "One Tab stop. Up/Down choose the previous or next item, Home/End the ends, typing the first letters of a label jumps to it. Section and group titles open and close with a click."}, + {ID: "065", Name: "NavigationMenu", ZhName: "导航菜单", Category: "Navigation", Demo: navigationMenuDemo, Code: navigationMenuCode, + Keys: "Left/Right move between top links; Down or Enter opens a group with focus on its first link, Up/Down move inside, Escape closes. Back/Forward also answer Cmd+[ / Cmd+] (Alt+Left/Right off macOS)."}, + {ID: "066", Name: "Menubar", ZhName: "菜单栏", Category: "Navigation", Demo: navigationMenubarDemo, Code: navigationMenubarCode, + Keys: "Tab reaches the bar; Left/Right move between menus, Down opens one. Up/Down/Home/End move through items, Right opens a submenu (or the next menu), Left closes it, Enter/Space chooses, Escape closes one level."}, + {ID: "067", Name: "Toolbar", ZhName: "工具栏", Category: "Navigation", Demo: navigationToolbarDemo, Code: navigationToolbarCode, + Keys: "One Tab stop. Left/Right move between actions, Home/End to the ends, Enter or Space runs one. Actions that do not fit move to the » menu at the end."}, + {ID: "068", Name: "CommandPalette", ZhName: "命令面板", Category: "Navigation", Demo: navigationPaletteDemo, Code: navigationPaletteCode, + Keys: "Cmd+K (Ctrl+K off macOS) opens it with focus in the search. Type to filter, Up/Down choose (wrapping), Enter runs and closes, Escape closes without running."}, +} + +// navigationCheck is a check box that logs its changes. +func navigationCheck(c *ui.Context, v *bool, label string, log func(string)) { + was := *v + ui.Checkbox(c, v, label) + if *v != was { + log(fmt.Sprintf("%s: %v", label, *v)) + } +} + +// navigationSlider is a labeled whole-number slider that logs its changes. +func navigationSlider(c *ui.Context, v *float64, label string, lo, hi float64, log func(string)) { + was := math.Round(*v) + ui.Row(c).Gap(8).AlignItems(ui.Center).Children(func() { + ui.Text(c, fmt.Sprintf("%s %.0f", label, math.Round(*v))) + ui.StepSlider(c, v, lo, hi, 1).Width(160) + }) + if math.Round(*v) != was { + log(fmt.Sprintf("%s: %.0f", label, math.Round(*v))) + } +} + +func navigationControls(c *ui.Context, fn func()) { ui.Row(c).Wrap().Gap(16).Children(fn) } + +func navigationDemo(c *ui.Context, controls, body func()) { + ui.Column(c).Gap(20).AlignItems(ui.Stretch).Children(func() { + navigationControls(c, controls) + body() + }) +} + +const navigationTabsCode = `mujica.Tabs(c, &tab, []mujica.Tab{ + {Label: "General", Panel: func() { ui.Text(c, "General settings") }}, + {Label: "Fonts", Icon: icons.Must("italic"), Panel: func() { ui.Text(c, "Fonts") }}, + {Label: "Archive", Disabled: true}, +}, mujica.TabsOptions{Label: "Settings"})` + +func navigationTabsDemo(c *ui.Context, log func(string)) { + type state struct { + tab int + vertical, archive, many bool + } + s := ui.Local(c.Root(), "059", func() state { return state{archive: true} }) + navigationDemo(c, func() { + navigationCheck(c, &s.vertical, "Vertical", log) + navigationCheck(c, &s.archive, "Disable Archive", log) + navigationCheck(c, &s.many, "Many tabs (overflow)", log) + }, func() { + names := []string{"General", "Fonts", "Archive", "Advanced"} + if s.many { + names = append(names, "Seals", "Heraldry", "Ledgers", "Envoys", "Banquets", "Chapel") + } + var tabs []mujica.Tab + for i, n := range names { + tabs = append(tabs, mujica.Tab{Label: n, Disabled: n == "Archive" && s.archive, Panel: func() { + ui.Text(c, fmt.Sprintf("The %s panel (tab %d).", n, i+1)) + }}) + } + tabs[1].Icon = icons.Must("italic") + s.tab = min(s.tab, len(tabs)-1) + if tabs[s.tab].Disabled { + s.tab = 0 + } + ui.Column(c).Width(420).Children(func() { + if mujica.Tabs(c, &s.tab, tabs, mujica.TabsOptions{Vertical: s.vertical, Label: "Settings"}).Changed() { + log("Changed: tab " + names[s.tab]) + } + }) + }) +} + +const navigationSegmentedCode = `mujica.SegmentedControl(c, &view, []mujica.Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: true}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: true}, +}, mujica.SegmentedControlOptions{Label: "View"})` + +func navigationSegmentedDemo(c *ui.Context, log func(string)) { + type state struct { + text, icon int + equal, iconOnly, disab bool + } + s := ui.Local(c.Root(), "060", func() state { return state{} }) + navigationDemo(c, func() { + navigationCheck(c, &s.equal, "Equal widths", log) + navigationCheck(c, &s.iconOnly, "Icons only", log) + navigationCheck(c, &s.disab, "Disabled", log) + }, func() { + opts := mujica.SegmentedControlOptions{Equal: s.equal, Disabled: s.disab, Label: "Range"} + ui.Row(c).Gap(24).AlignItems(ui.Center).Children(func() { + if mujica.SegmentedControl(c, &s.text, []mujica.Segment{{Label: "Day"}, {Label: "Week"}, {Label: "Fortnight"}}, opts).Changed() { + log(fmt.Sprintf("Changed: range %d", s.text)) + } + opts.Label = "View" + if mujica.SegmentedControl(c, &s.icon, []mujica.Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: s.iconOnly}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: s.iconOnly}, + }, opts).Changed() { + log(fmt.Sprintf("Changed: view %d", s.icon)) + } + }) + }) +} + +const navigationBreadcrumbCode = `if i, ok := mujica.Breadcrumb(c, path, mujica.BreadcrumbOptions{}).Chosen(); ok { + path = path[:i+1] +}` + +var navigationBreadcrumbNames = []string{"Home", "Library", "Manuscripts", "Seventeenth century", "Correspondence of the court", "Letters to the Duchess", "Draft of the winter decree"} + +func navigationBreadcrumbDemo(c *ui.Context, log func(string)) { + type state struct{ depth, max float64 } + s := ui.Local(c.Root(), "061", func() state { return state{depth: 6, max: 4} }) + navigationDemo(c, func() { + navigationSlider(c, &s.depth, "Depth", 1, 7, log) + navigationSlider(c, &s.max, "MaxItems", 3, 7, log) + }, func() { + var path []mujica.BreadcrumbItem + for i, n := range navigationBreadcrumbNames[:int(s.depth)] { + it := mujica.BreadcrumbItem{Label: n} + if i == 0 { + it.Icon = icons.Must("house") + } + path = append(path, it) + } + if i, ok := mujica.Breadcrumb(c, path, mujica.BreadcrumbOptions{MaxItems: int(s.max)}).Chosen(); ok { + log("Chosen: " + path[i].Label) + s.depth = float64(i + 1) + } + }) +} + +const navigationPaginationCode = `if mujica.Pagination(c, &page, &size, mujica.PaginationOptions{Total: 420, PageSizes: []int{10, 20, 50}, Jump: true}).Changed() { + load(page, size) +}` + +func navigationPaginationDemo(c *ui.Context, log func(string)) { + type state struct { + page, size int + total float64 + sizes, jump, disab bool + } + s := ui.Local(c.Root(), "062", func() state { return state{page: 1, size: 20, total: 420, sizes: true, jump: true} }) + navigationDemo(c, func() { + navigationSlider(c, &s.total, "Total", 0, 1000, log) + navigationCheck(c, &s.sizes, "Page sizes", log) + navigationCheck(c, &s.jump, "Jump", log) + navigationCheck(c, &s.disab, "Disabled", log) + }, func() { + opts := mujica.PaginationOptions{Total: int(s.total), Jump: s.jump, Disabled: s.disab} + if s.sizes { + opts.PageSizes = []int{10, 20, 50} + } else { + s.size = 20 + } + if mujica.Pagination(c, &s.page, &s.size, opts).Changed() { + log(fmt.Sprintf("Changed: page %d, size %d", s.page, s.size)) + } + }) +} + +const navigationStepsCode = `mujica.Steps(c, ¤t, []mujica.Step{ + {Title: "Account", Clickable: true}, + {Title: "Profile", Description: "Name and seal"}, + {Title: "Confirm"}, +}, mujica.StepsOptions{Label: "Sign-up"})` + +func navigationStepsDemo(c *ui.Context, log func(string)) { + type state struct { + current int + vertical, failed, clickOK bool + } + s := ui.Local(c.Root(), "063", func() state { return state{current: 1, clickOK: true} }) + steps := []mujica.Step{ + {Title: "Account", Description: "Name and seal"}, + {Title: "Profile", Description: "Titles and house"}, + {Title: "Payment", Description: "Treasury"}, + {Title: "Confirm"}, + } + navigationDemo(c, func() { + navigationCheck(c, &s.vertical, "Vertical", log) + navigationCheck(c, &s.failed, "Payment failed", log) + navigationCheck(c, &s.clickOK, "Done steps clickable", log) + if mujica.Button(c, "Back", mujica.ButtonOptions{Variant: mujica.Secondary, Disabled: s.current == 0}).Clicked() { + s.current-- + log(fmt.Sprintf("Back: step %d", s.current+1)) + } + if mujica.Button(c, "Next", mujica.ButtonOptions{Disabled: s.current == len(steps)}).Clicked() { + s.current++ + log(fmt.Sprintf("Next: step %d", s.current+1)) + } + }, func() { + for i := range steps { + steps[i].Clickable = s.clickOK && i < s.current + } + steps[2].Error = s.failed + if mujica.Steps(c, &s.current, steps, mujica.StepsOptions{Vertical: s.vertical, Label: "Sign-up"}).Changed() { + log("Changed: step " + steps[s.current].Title) + } + }) +} + +const navigationSidebarCode = `mujica.Sidebar(c, &selected, []mujica.SidebarSection{ + {Title: "Mail", Items: []mujica.SidebarItem{ + {ID: "inbox", Label: "Inbox", Icon: icons.Must("inbox"), Count: 3}, + {Label: "Folders", Items: []mujica.SidebarItem{{ID: "work", Label: "Work", Icon: icons.Must("folder")}}}, + }}, +}, mujica.SidebarOptions{})` + +func navigationSidebarDemo(c *ui.Context, log func(string)) { + type state struct { + selected string + compact bool + unread float64 + } + s := ui.Local(c.Root(), "064", func() state { return state{selected: "inbox", unread: 12} }) + navigationDemo(c, func() { + navigationCheck(c, &s.compact, "Compact", log) + navigationSlider(c, &s.unread, "Unread", 0, 1200, log) + }, func() { + sections := []mujica.SidebarSection{ + {Title: "Library", Items: []mujica.SidebarItem{ + {ID: "inbox", Label: "Inbox", Icon: icons.Must("inbox"), Count: int(s.unread)}, + {ID: "starred", Label: "Starred", Icon: icons.Must("star")}, + {Label: "Projects", Items: []mujica.SidebarItem{ + {ID: "opera", Label: "Opera", Icon: icons.Must("folder")}, + {ID: "ballet", Label: "Ballet", Icon: icons.Must("folder")}, + }}, + }}, + {Title: "System", Items: []mujica.SidebarItem{ + {ID: "settings", Label: "Settings", Icon: icons.Must("settings")}, + {ID: "trash", Label: "Trash", Icon: icons.Must("trash-2")}, + }}, + } + ui.Row(c).Height(360).Border(1, mujica.Tokens(c).Border).Radius(4).Clip().Children(func() { + if mujica.Sidebar(c, &s.selected, sections, mujica.SidebarOptions{Compact: s.compact}).Changed() { + log("Changed: " + s.selected) + } + ui.Text(c, "Showing "+s.selected).Padding(24) + }) + }) +} + +const navigationMenuCode = `mujica.NavigationMenu(c, router, []mujica.NavigationLink{ + {Label: "Home", Path: "/"}, + {Label: "Library", Items: []mujica.NavigationLink{ + {Label: "Poems", Path: "/library/poems", Description: "Verse of the court"}, + }}, +}, mujica.NavigationMenuOptions{History: true}) +router.View(c, func(r *ui.Route) { ui.Text(c, r.Path()) })` + +func navigationMenuDemo(c *ui.Context, log func(string)) { + type state struct { + router *ui.Router + history bool + last string + } + s := ui.Local(c.Root(), "065", func() state { return state{router: ui.NewRouter("/"), history: true, last: "/"} }) + navigationDemo(c, func() { + navigationCheck(c, &s.history, "Back and forward", log) + }, func() { + mujica.NavigationMenu(c, s.router, []mujica.NavigationLink{ + {Label: "Home", Path: "/", Icon: icons.Must("house")}, + {Label: "Library", Items: []mujica.NavigationLink{ + {Label: "Poems", Path: "/library/poems", Description: "Verse of the court"}, + {Label: "Letters", Path: "/library/letters", Description: "Correspondence"}, + {Label: "Archive", Items: []mujica.NavigationLink{ + {Label: "Older letters", Path: "/archive/letters"}, + }}, + }}, + {Label: "About", Path: "/about"}, + }, mujica.NavigationMenuOptions{History: s.history}) + if p := s.router.Path(); p != s.last { + log(fmt.Sprintf("Path: %s → %s", s.last, p)) + s.last = p + } + ui.Column(c).Height(120).Children(func() { + s.router.View(c, func(r *ui.Route) { + r.Title(r.Path()) + ui.Text(c, "Page "+r.Path()).Padding(16) + }) + }) + }) +} + +const navigationMenubarCode = `res := mujica.Menubar(c, []mujica.MenubarMenu{ + {Label: "File", Items: []mujica.MenubarItem{ + {ID: "new", Label: "New", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyN}}, + {Label: "Export", Items: []mujica.MenubarItem{{ID: "pdf", Label: "PDF"}}}, + }}, + {Label: "View", Items: []mujica.MenubarItem{ + {ID: "wrap", Label: "Word wrap", Kind: mujica.MenubarCheck, Checked: wrap}, + }}, +}, mujica.MenubarOptions{}) +if id, ok := res.Chosen(); ok && id == "wrap" { + wrap = !wrap +}` + +func navigationMenubarDemo(c *ui.Context, log func(string)) { + type state struct { + wrap, ruler, locked bool + zoom string + } + s := ui.Local(c.Root(), "066", func() state { return state{wrap: true, zoom: "fit"} }) + navigationDemo(c, func() { + navigationCheck(c, &s.locked, "Lock editing (disables Edit items)", log) + }, func() { + menus := []mujica.MenubarMenu{ + {Label: "File", Items: []mujica.MenubarItem{ + {ID: "new", Label: "New", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyN}}, + {ID: "open", Label: "Open…", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyO}}, + {Label: "Export", Items: []mujica.MenubarItem{ + {ID: "pdf", Label: "PDF"}, + {ID: "png", Label: "Image"}, + {Label: "Archive", Items: []mujica.MenubarItem{{ID: "zip", Label: "Zip"}, {ID: "tar", Label: "Tarball"}}}, + }}, + {Kind: mujica.MenubarSeparator}, + {ID: "quit", Label: "Quit", Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyQ}}, + }}, + {Label: "Edit", Items: []mujica.MenubarItem{ + {ID: "undo", Label: "Undo", Disabled: s.locked, Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd, Key: ui.KeyZ}}, + {ID: "redo", Label: "Redo", Disabled: s.locked, Shortcut: mujica.MenubarShortcut{Mods: ui.Cmd | ui.Shift, Key: ui.KeyZ}}, + }}, + {Label: "View", Items: []mujica.MenubarItem{ + {ID: "wrap", Label: "Word wrap", Kind: mujica.MenubarCheck, Checked: s.wrap}, + {ID: "ruler", Label: "Ruler", Kind: mujica.MenubarCheck, Checked: s.ruler}, + {Kind: mujica.MenubarSeparator}, + {ID: "fit", Label: "Fit to window", Kind: mujica.MenubarRadio, Checked: s.zoom == "fit"}, + {ID: "actual", Label: "Actual size", Kind: mujica.MenubarRadio, Checked: s.zoom == "actual"}, + }}, + } + ui.Column(c).Height(260).Border(1, mujica.Tokens(c).Border).Radius(4).Clip().Children(func() { + if id, ok := mujica.Menubar(c, menus, mujica.MenubarOptions{}).Chosen(); ok { + switch id { + case "wrap": + s.wrap = !s.wrap + case "ruler": + s.ruler = !s.ruler + case "fit", "actual": + s.zoom = id + } + log("Chosen: " + id) + } + ui.Text(c, fmt.Sprintf("Word wrap %v · Ruler %v · Zoom %s", s.wrap, s.ruler, s.zoom)).Padding(16) + }) + }) +} + +const navigationToolbarCode = `mujica.Toolbar(c, mujica.ToolbarOptions{Label: "Format"}, func() { + mujica.ToolbarGroup(c, func() { + mujica.ToolbarButton(c, "Bold", mujica.ToolbarButtonOptions{Icon: icons.Must("bold")}) + mujica.ToolbarButton(c, "Italic", mujica.ToolbarButtonOptions{Icon: icons.Must("italic")}) + }) + mujica.ToolbarSeparator(c) + if mujica.ToolbarButton(c, "Copy", mujica.ToolbarButtonOptions{Icon: icons.Must("copy"), ShowLabel: true}).Clicked() { + copySelection() + } +})` + +func navigationToolbarDemo(c *ui.Context, log func(string)) { + type state struct { + width float64 + labels bool + noDeletes bool + } + s := ui.Local(c.Root(), "067", func() state { return state{width: 520, noDeletes: true} }) + navigationDemo(c, func() { + navigationSlider(c, &s.width, "Width", 120, 640, log) + navigationCheck(c, &s.labels, "Show labels", log) + navigationCheck(c, &s.noDeletes, "Disable Delete", log) + }, func() { + ui.Column(c).Width(float32(s.width)).Children(func() { + mujica.Toolbar(c, mujica.ToolbarOptions{Label: "Format"}, func() { + button := func(label, icon string, disabled bool) { + if mujica.ToolbarButton(c, label, mujica.ToolbarButtonOptions{Icon: icons.Must(icon), ShowLabel: s.labels, Disabled: disabled}).Clicked() { + log("Clicked: " + label) + } + } + mujica.ToolbarGroup(c, func() { + button("Bold", "bold", false) + button("Italic", "italic", false) + button("Underline", "underline", false) + }) + mujica.ToolbarSeparator(c) + button("Copy", "copy", false) + button("Image", "image", false) + button("Delete", "trash-2", s.noDeletes) + mujica.ToolbarSeparator(c) + button("Settings", "settings", false) + }) + }) + }) +} + +const navigationPaletteCode = `res := mujica.CommandPalette(c, &open, []mujica.Command{ + {ID: "new", Label: "New document", Group: "File"}, + {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"night"}}, +}, mujica.CommandPaletteOptions{}) +if id, ok := res.Chosen(); ok { + run(id) +}` + +func navigationPaletteDemo(c *ui.Context, log func(string)) { + type state struct{ open, wasOpen bool } + s := ui.Local(c.Root(), "068", func() state { return state{} }) + navigationDemo(c, func() { + if mujica.Button(c, "Open command palette", mujica.ButtonOptions{Icon: icons.Must("command")}).Clicked() { + s.open = true + } + ui.Text(c, "or press Cmd+K / Ctrl+K").TextColor(mujica.Tokens(c).TextMuted) + }, func() { + res := mujica.CommandPalette(c, &s.open, []mujica.Command{ + {ID: "new", Label: "New document", Group: "File", Icon: icons.Must("file")}, + {ID: "open", Label: "Open recent", Group: "File", Icon: icons.Must("folder")}, + {ID: "save", Label: "Save all", Group: "File", Icon: icons.Must("file-text")}, + {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"theme", "night"}, Icon: icons.Must("moon")}, + {ID: "zoom-in", Label: "Zoom in", Group: "View", Icon: icons.Must("zoom-in")}, + {ID: "zoom-out", Label: "Zoom out", Group: "View", Icon: icons.Must("zoom-out")}, + {ID: "settings", Label: "Open settings", Group: "Application", Keywords: []string{"preferences"}, Icon: icons.Must("settings")}, + }, mujica.CommandPaletteOptions{}) + if s.open != s.wasOpen { + log(fmt.Sprintf("Open: %v", s.open)) + s.wasOpen = s.open + } + if id, ok := res.Chosen(); ok { + log("Run: " + id) + } + }) +} diff --git a/menubar.go b/menubar.go new file mode 100644 index 0000000..00f63f2 --- /dev/null +++ b/menubar.go @@ -0,0 +1,279 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// MenubarItemKind is what a menu item does. +type MenubarItemKind int + +const ( + // MenubarAction runs a command. + MenubarAction MenubarItemKind = iota + // MenubarCheck toggles a setting the caller keeps in Checked. + MenubarCheck + // MenubarRadio picks one of a group; the caller sets Checked on one. + MenubarRadio + // MenubarSeparator draws a line between groups of items. + MenubarSeparator +) + +// MenubarShortcut is the key hint shown beside an item; Key zero shows none. +type MenubarShortcut struct { + Mods ui.Modifiers + Key ui.Key +} + +// MenubarItem is an item of a menu, or with Items a submenu. +type MenubarItem struct { + // ID is reported by Chosen; required for actions, checks and radios. + ID string + Label string + Kind MenubarItemKind + Checked bool + Disabled bool + // Shortcut is only shown; handle the key with c.Shortcut. + Shortcut MenubarShortcut + Items []MenubarItem +} + +// MenubarMenu is a title of the bar and the menu it opens. +type MenubarMenu struct { + Label string + Items []MenubarItem +} + +// MenubarOptions configure a Menubar. +type MenubarOptions struct { + // Label names the bar; empty uses the built-in name. + Label string +} + +// MenubarResult is the built Menubar. +type MenubarResult struct { + *ui.Element + chosen string +} + +// Chosen returns the ID of the item the user chose in this frame. +func (r MenubarResult) Chosen() (string, bool) { return r.chosen, r.chosen != "" } + +type menubarState struct { + open int + hl []int + focusTo int + ptr [2]float32 +} + +// Menubar is an in-window menu bar drawn by MujicaUI, with submenus and full keyboard control. +func Menubar(c *ui.Context, menus []MenubarMenu, opts MenubarOptions) MenubarResult { + if len(menus) == 0 { + panic("mujica: Menubar needs menus") + } + for _, m := range menus { + if m.Label == "" { + panic("mujica: MenubarMenu needs a Label") + } + menubarCheck(m.Items) + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + label := opts.Label + if label == "" { + label = msg(c, "menubar.label") + } + bar := ui.Row(c).AlignItems(ui.Center).Gap(u * 0.5).Padding(u).FocusGroup(ui.Horizontal). + Role(ui.RoleToolbar).Label(label).Background(k.Surface) + bar.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + r.H - 1, W: r.W, H: 1}, k.Border, 0) + }) + st := ui.Local(bar, "state", func() *menubarState { return &menubarState{open: -1, focusTo: -1} }) + m := &menubarRun{c: c, menus: menus, st: *st} + res := MenubarResult{Element: bar} + titles := make([]ui.Rect, len(menus)) + bar.Children(func() { + for i, menu := range menus { + t := m.title(i, menu.Label) + titles[i] = t.Bounds() + } + }) + if m.st.open >= 0 { + m.overlay(titles) + } + res.chosen = m.chosen + return res +} + +// menubarCheck panics on items that cannot work. +func menubarCheck(items []MenubarItem) { + for _, it := range items { + if it.Kind == MenubarSeparator { + continue + } + if it.Label == "" { + panic("mujica: MenubarItem needs a Label") + } + if it.Items != nil { + if it.Kind != MenubarAction { + panic("mujica: a submenu item must be of kind MenubarAction") + } + menubarCheck(it.Items) + } else if it.ID == "" { + panic("mujica: MenubarItem " + it.Label + " needs an ID") + } + } +} + +// menubarRun is one frame of a Menubar. +type menubarRun struct { + c *ui.Context + menus []MenubarMenu + st *menubarState + chosen string +} + +func (m *menubarRun) title(i int, label string) *ui.Element { + c, st := m.c, m.st + k := Tokens(c) + d := Density(c) + open := st.open == i + t := ui.ButtonBase(c).MinHeight(d.ControlHeight()-4).Padding(0, d.Unit()*3).Radius(theme.ControlRadius). + Role(ui.RoleMenuButton).Label(label).TextColor(k.Text) + internal.Ring(t, k.Focus, theme.ControlRadius) + switch { + case open: + t.Background(k.Selection) + case t.Hovered(): + t.Background(k.SurfaceHover) + } + if st.focusTo == i { + t.Focus() + st.focusTo = -1 + } + if st.open < 0 { + switch { + case t.Shortcut(0, ui.KeyRight): + m.focus((i + 1) % len(m.menus)) + case t.Shortcut(0, ui.KeyLeft): + m.focus((i - 1 + len(m.menus)) % len(m.menus)) + case t.Shortcut(0, ui.KeyDown), t.Shortcut(0, ui.KeyUp): + m.openMenu(i, true) + } + } else { + m.keys(t) + } + if t.Clicked() { + if open { + m.close() + } else { + m.openMenu(i, t.FocusVisible()) + } + } + t.Children(func() { ui.Text(c, label).SingleLine() }) + return t +} + +// keys drives an open menu from the focused title. +func (m *menubarRun) keys(t *ui.Element) { + st := m.st + l := len(st.hl) - 1 + items := m.items(l) + h := st.hl[l] + switch { + case t.Shortcut(0, ui.KeyDown): + st.hl[l] = menubarStep(items, h, 1) + case t.Shortcut(0, ui.KeyUp): + st.hl[l] = menubarStep(items, h, -1) + case t.Shortcut(0, ui.KeyHome): + st.hl[l] = menubarStep(items, -1, 1) + case t.Shortcut(0, ui.KeyEnd): + st.hl[l] = menubarStep(items, len(items), -1) + case t.Shortcut(0, ui.KeyRight): + if h >= 0 && items[h].Items != nil { + st.hl = append(st.hl, menubarStep(items[h].Items, -1, 1)) + } else { + m.openMenu((st.open+1)%len(m.menus), true) + } + case t.Shortcut(0, ui.KeyLeft): + if l > 0 { + st.hl = st.hl[:l] + } else { + m.openMenu((st.open-1+len(m.menus))%len(m.menus), true) + } + case t.Shortcut(0, ui.KeyEnter), t.Shortcut(0, ui.KeySpace): + if h >= 0 { + m.activate(l, h) + } + case t.Shortcut(0, ui.KeyEscape): + if l > 0 { + st.hl = st.hl[:l] + } else { + m.close() + } + } +} + +// items returns the items shown at level l of the open menu. +func (m *menubarRun) items(l int) []MenubarItem { + items := m.menus[m.st.open].Items + for i := 0; i < l; i++ { + items = items[m.st.hl[i]].Items + } + return items +} + +func (m *menubarRun) focus(i int) { + m.st.focusTo = i + m.c.Invalidate() +} + +func (m *menubarRun) openMenu(i int, highlight bool) { + first := -1 + if highlight { + first = menubarStep(m.menus[i].Items, -1, 1) + } + m.st.open, m.st.hl = i, []int{first} + m.focus(i) + slog.Debug("mujica: menubar open", "menu", m.menus[i].Label) +} + +func (m *menubarRun) close() { + if m.st.open >= 0 { + m.focus(m.st.open) + } + m.st.open, m.st.hl = -1, nil + slog.Debug("mujica: menubar close") +} + +// activate chooses item h of level l, or opens it when it is a submenu. +func (m *menubarRun) activate(l, h int) { + it := m.items(l)[h] + if it.Disabled || it.Kind == MenubarSeparator { + return + } + if it.Items != nil { + m.st.hl = append(m.st.hl[:l+1], menubarStep(it.Items, -1, 1)) + return + } + m.chosen = it.ID + slog.Debug("mujica: menubar choose", "id", it.ID) + m.close() +} + +// menubarStep returns the next enabled item from h in direction dir, wrapping. +func menubarStep(items []MenubarItem, h, dir int) int { + n := len(items) + for k := 1; k <= n; k++ { + j := ((h+dir*k)%n + n) % n + if it := items[j]; it.Kind != MenubarSeparator && !it.Disabled { + return j + } + } + return -1 +} diff --git a/menubar_panel.go b/menubar_panel.go new file mode 100644 index 0000000..11e7185 --- /dev/null +++ b/menubar_panel.go @@ -0,0 +1,188 @@ +package mujica + +import ( + "runtime" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// overlay draws the open menu and its submenus above the window. +func (m *menubarRun) overlay(titles []ui.Rect) { + c, st := m.c, m.st + ui.Overlay(c, func() { + back := ui.Box(c).Absolute().Left(0).Top(0).Right(0).Bottom(0) + if back.Clicked() { + m.close() + return + } + // Title hotspots above the backdrop switch menus as the pointer moves. + for i, r := range titles { + hot := ui.Box(c).Absolute().Left(r.X).Top(r.Y).Size(r.W, r.H).Cursor(ui.CursorPointer) + if hot.Clicked() { + if i == st.open { + m.close() + } else { + m.openMenu(i, false) + } + return + } + if hot.Hovered() && i != st.open { + m.openMenu(i, false) + } + } + if st.open < 0 { + return + } + r := titles[st.open] + x, y := r.X, r.Y+r.H+2 + var prev ui.Rect + for l := 0; l < len(st.hl) && st.open >= 0; l++ { + panel, anchor := m.panel(l, x, y, prev) + if anchor == nil || l+1 >= len(st.hl) { + break + } + a := anchor.Bounds() + prev = panel.Bounds() + x, y = prev.X+prev.W-2, a.Y-Density(c).Unit() + } + }) +} + +// panel builds level l at (x, y), flipping left of parent when it does not fit. +// It returns the panel and the row of its highlighted item. +func (m *menubarRun) panel(l int, x, y float32, parent ui.Rect) (*ui.Element, *ui.Element) { + c, st := m.c, m.st + k := Tokens(c) + u := Density(c).Unit() + items := m.items(l) + panel := ui.Column(c).Absolute().Padding(u).MinWidth(200).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)) + pb := panel.Bounds() + ww, wh := c.Size() + if x+pb.W > ww-4 { + x = ww - 4 - pb.W + if l > 0 { + x = parent.X - pb.W + 2 + } + } + if y+pb.H > wh-4 { + y = max(4, wh-4-pb.H) + } + panel.Left(max(4, x)).Top(y) + var anchor *ui.Element + panel.Children(func() { + for j, it := range items { + if it.Kind == MenubarSeparator { + ui.Box(c).Height(1).Margin(u, u*2).Background(k.Border).Role(ui.RoleNone) + continue + } + row := m.item(it, st.hl[l] == j) + if st.hl[l] == j { + anchor = row + } + if row.Clicked() { + st.hl = append(st.hl[:l], j) + m.activate(l, j) + return + } + px, py, over := row.PointerPosition() + b := row.Bounds() + at := [2]float32{b.X + px, b.Y + py} + if over && at != st.ptr && !it.Disabled { + st.ptr = at + if st.hl[l] != j || it.Items != nil && len(st.hl) == l+1 { + st.hl = append(st.hl[:l], j) + if it.Items != nil { + st.hl = append(st.hl, -1) + } + c.Invalidate() + } + } + } + }) + return panel, anchor +} + +func (m *menubarRun) item(it MenubarItem, hl bool) *ui.Element { + c := m.c + k := Tokens(c) + d := Density(c) + u := d.Unit() + name := it.Label + if (it.Kind == MenubarCheck || it.Kind == MenubarRadio) && it.Checked { + name += ", " + msg(c, "menubar.checked") + } + row := ui.Row(c).AlignItems(ui.Center).Gap(u*2).MinHeight(d.ControlHeight()-4).Padding(0, u*2). + Radius(theme.ControlRadius).Label(name).TextColor(k.Text).Disabled(it.Disabled).Role(ui.RoleButton) + if hl { + row.Background(k.Selection) + } + row.Children(func() { + mark := ui.Box(c).Size(16, 16).Shrink(0).Center().Role(ui.RoleNone) + if it.Checked && it.Kind == MenubarCheck { + mark.TextColor(k.AccentText).Children(func() { ui.Icon(c, icons.Must("check")).Size(14, 14) }) + } + if it.Checked && it.Kind == MenubarRadio { + mark.Draw(func(p *ui.Painter, r ui.Rect) { + p.FillPath(internal.Diamond(r.X+r.W/2, r.Y+r.H/2, 8), k.AccentText) + }) + } + ui.Text(c, it.Label).SingleLine() + ui.Box(c).Grow(1).MinWidth(u * 6) + if it.Shortcut.Key != 0 { + ui.Text(c, menubarShortcutText(it.Shortcut)).FontSize(theme.CaptionSize).TextColor(k.TextMuted).SingleLine() + } + if it.Items != nil { + ui.Icon(c, icons.Must("chevron-right")).Size(14, 14).TextColor(k.TextMuted) + } + }) + return row +} + +var menubarKeyNames = map[ui.Key]string{ + ui.KeyEnter: "Enter", ui.KeyEscape: "Esc", ui.KeyBackspace: "Backspace", ui.KeyTab: "Tab", ui.KeySpace: "Space", + ui.KeyDelete: "Del", ui.KeyHome: "Home", ui.KeyEnd: "End", ui.KeyPageUp: "PgUp", ui.KeyPageDown: "PgDn", + ui.KeyLeft: "←", ui.KeyRight: "→", ui.KeyUp: "↑", ui.KeyDown: "↓", ui.KeyMinus: "-", ui.KeyEqual: "=", + ui.KeyComma: ",", ui.KeyPeriod: ".", ui.KeySlash: "/", ui.KeySemicolon: ";", ui.KeyQuote: "'", + ui.KeyF1: "F1", ui.KeyF2: "F2", ui.KeyF3: "F3", ui.KeyF4: "F4", ui.KeyF5: "F5", ui.KeyF6: "F6", + ui.KeyF7: "F7", ui.KeyF8: "F8", ui.KeyF9: "F9", ui.KeyF10: "F10", ui.KeyF11: "F11", ui.KeyF12: "F12", + ui.KeyBracketLeft: "[", ui.KeyBracketRight: "]", ui.KeyBackslash: "\\", ui.KeyBackquote: "`", +} + +// menubarShortcutText spells a shortcut the platform's way: ⌘S on macOS, Ctrl+S elsewhere. +func menubarShortcutText(s MenubarShortcut) string { + key, ok := menubarKeyNames[s.Key] + switch { + case ok: + case s.Key >= ui.KeyA && s.Key <= ui.KeyZ: + key = string(rune('A' + s.Key - ui.KeyA)) + case s.Key >= ui.Key0 && s.Key <= ui.Key9: + key = string(rune('0' + s.Key - ui.Key0)) + default: + panic("mujica: MenubarShortcut key has no name") + } + mac := runtime.GOOS == "darwin" + var parts []string + for _, m := range []struct { + mod ui.Modifiers + mac, name string + }{{ui.Ctrl, "⌃", "Ctrl"}, {ui.Alt, "⌥", "Alt"}, {ui.Shift, "⇧", "Shift"}, {ui.Super, "⌘", "Win"}} { + if s.Mods&m.mod != 0 { + if mac { + parts = append(parts, m.mac) + } else { + parts = append(parts, m.name) + } + } + } + if mac { + return strings.Join(parts, "") + key + } + return strings.Join(append(parts, key), "+") +} diff --git a/menubar_test.go b/menubar_test.go new file mode 100644 index 0000000..1309072 --- /dev/null +++ b/menubar_test.go @@ -0,0 +1,191 @@ +package mujica + +import ( + "runtime" + "testing" + + "github.com/egoist/mygo/ui" +) + +type menubarApp struct { + wrap bool + zoom string + chosen []string +} + +func (a *menubarApp) menus() []MenubarMenu { + return []MenubarMenu{ + {Label: "File", Items: []MenubarItem{ + {ID: "new", Label: "New", Shortcut: MenubarShortcut{ui.Cmd, ui.KeyN}}, + {ID: "open", Label: "Open…", Shortcut: MenubarShortcut{ui.Cmd, ui.KeyO}}, + {Label: "Export", Items: []MenubarItem{ + {ID: "pdf", Label: "PDF"}, + {ID: "png", Label: "Image", Disabled: true}, + {ID: "svg", Label: "Vector"}, + }}, + {Kind: MenubarSeparator}, + {ID: "quit", Label: "Quit"}, + }}, + {Label: "View", Items: []MenubarItem{ + {ID: "wrap", Label: "Word wrap", Kind: MenubarCheck, Checked: a.wrap}, + {Kind: MenubarSeparator}, + {ID: "zoom-fit", Label: "Fit", Kind: MenubarRadio, Checked: a.zoom == "zoom-fit"}, + {ID: "zoom-100", Label: "Actual size", Kind: MenubarRadio, Checked: a.zoom == "zoom-100"}, + }}, + {Label: "Help", Items: []MenubarItem{{ID: "about", Label: "About"}}}, + } +} + +func (a *menubarApp) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Fill().Children(func() { + if id, ok := Menubar(c, a.menus(), MenubarOptions{}).Chosen(); ok { + a.chosen = append(a.chosen, id) + switch id { + case "wrap": + a.wrap = !a.wrap + case "zoom-fit", "zoom-100": + a.zoom = id + } + } + }) +} + +func TestMenubarPointer(t *testing.T) { + a := &menubarApp{zoom: "zoom-fit"} + tt := ui.NewTester(a.view, 700, 500) + if err := tt.Click("View"); err != nil { + t.Fatal(err) + } + if err := tt.Click("Word wrap"); err != nil { + t.Fatal(err) + } + if !a.wrap || tt.HasText("Actual size") { + t.Fatalf("check item: wrap %v, menu still open %v", a.wrap, tt.HasText("Actual size")) + } + _ = tt.Click("View") + if _, ok := tt.Find("Word wrap, checked"); !ok { + t.Fatal("checked state not named") + } + if err := tt.Click("Actual size"); err != nil { + t.Fatal(err) + } + if a.zoom != "zoom-100" { + t.Fatalf("radio chose %q", a.zoom) + } + _ = tt.Click("File") + r, _ := tt.Find("Export") + tt.Move(r.X+r.W/2, r.Y+r.H/2) + tt.Move(r.X+r.W/2+1, r.Y+r.H/2) + if !tt.HasText("Vector") { + t.Fatalf("hover did not open submenu: %q", tt.Texts()) + } + _ = tt.Click("Image") + if err := tt.Click("Vector"); err != nil { + t.Fatal(err) + } + if got := a.chosen; len(got) != 3 || got[2] != "svg" { + t.Fatalf("chosen %q", got) + } + _ = tt.Click("Help") + tt.ClickAt(600, 400) + if tt.HasText("About") { + t.Fatal("click outside did not close") + } +} + +func TestMenubarHoverSwitchesMenus(t *testing.T) { + a := &menubarApp{} + tt := ui.NewTester(a.view, 700, 500) + _ = tt.Click("File") + r, _ := tt.Find("Help") + tt.Move(r.X+2, r.Y+2) + if tt.HasText("Quit") || !tt.HasText("About") { + t.Fatalf("hover did not switch: %q", tt.Texts()) + } +} + +func TestMenubarKeyboard(t *testing.T) { + a := &menubarApp{} + tt := ui.NewTester(a.view, 700, 500) + tt.Key(0, ui.KeyTab) + if !tt.Focused("File") { + t.Fatal("Tab did not reach the bar") + } + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyLeft) + if !tt.Focused("File") { + t.Fatal("arrows did not move between titles") + } + tt.Key(0, ui.KeyDown) // opens File on New + tt.Key(0, ui.KeyDown) // Open + tt.Key(0, ui.KeyDown) // Export + tt.Key(0, ui.KeyRight) + if !tt.HasText("Vector") { + t.Fatal("Right did not open the submenu") + } + tt.Key(0, ui.KeyDown) // skips disabled Image to Vector + tt.Key(0, ui.KeyEnter) + if len(a.chosen) != 1 || a.chosen[0] != "svg" { + t.Fatalf("chosen %q", a.chosen) + } + if tt.HasText("Quit") || !tt.Focused("File") { + t.Fatal("menu did not close back to its title") + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyUp) // wraps past the separator to Quit + tt.Key(0, ui.KeyRight) + if !tt.HasText("Word wrap") { + t.Fatal("Right on a plain item did not move to the next menu") + } + tt.Key(0, ui.KeySpace) + if !a.wrap { + t.Fatal("Space did not toggle the check item") + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyEscape) + if tt.HasText("Word wrap") { + t.Fatal("Escape did not close") + } +} + +func TestMenubarShortcutText(t *testing.T) { + got := menubarShortcutText(MenubarShortcut{ui.Ctrl | ui.Shift, ui.KeyF5}) + want := "Ctrl+Shift+F5" + if runtime.GOOS == "darwin" { + want = "⌃⇧F5" + } + if got != want { + t.Fatalf("got %q want %q", got, want) + } +} + +func TestMenubarInvalid(t *testing.T) { + for name, menus := range map[string][]MenubarMenu{ + "empty": nil, + "title": {{Items: []MenubarItem{{ID: "a", Label: "A"}}}}, + "id": {{Label: "M", Items: []MenubarItem{{Label: "A"}}}}, + "submenu": {{Label: "M", Items: []MenubarItem{{Label: "A", Kind: MenubarCheck, Items: []MenubarItem{}}}}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Menubar(c, menus, MenubarOptions{}) + }, 300, 100, 1) + }) + } +} + +func TestMenubarSnapshot(t *testing.T) { + a := &menubarApp{wrap: true, zoom: "zoom-fit"} + snapshot(t, "menubar", 480, 100, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + Menubar(c, a.menus(), MenubarOptions{}) + }) + }) +} diff --git a/messages_navigation.go b/messages_navigation.go new file mode 100644 index 0000000..8474db7 --- /dev/null +++ b/messages_navigation.go @@ -0,0 +1,36 @@ +package mujica + +func init() { + addMessages(map[string][2]string{ + "breadcrumb.label": {"Breadcrumb", "路径导航"}, + "breadcrumb.current": {"current page", "当前页"}, + "breadcrumb.more": {"Show hidden path", "显示折叠的路径"}, + "pagination.label": {"Pagination", "分页"}, + "pagination.total": {"%d items", "共 %d 条"}, + "pagination.prev": {"Previous page", "上一页"}, + "pagination.next": {"Next page", "下一页"}, + "pagination.page": {"Page %d", "第 %d 页"}, + "pagination.current": {"current", "当前"}, + "pagination.size": {"Page size", "每页条数"}, + "pagination.per_page": {"%d / page", "%d 条/页"}, + "pagination.jump": {"Go to", "跳至"}, + "pagination.field": {"Page number", "页码"}, + "pagination.range": {"1–%d", "1–%d"}, + "steps.item": {"Step %d of %d: %s, %s", "第 %d 步,共 %d 步:%s,%s"}, + "steps.waiting": {"waiting", "等待"}, + "steps.current": {"current", "进行中"}, + "steps.done": {"done", "已完成"}, + "steps.error": {"failed", "出错"}, + "navigation_menu.label": {"Site navigation", "导航菜单"}, + "navigation_menu.back": {"Back", "后退"}, + "navigation_menu.forward": {"Forward", "前进"}, + "menubar.label": {"Menu bar", "菜单栏"}, + "menubar.checked": {"checked", "已勾选"}, + "command_palette.label": {"Command palette", "命令面板"}, + "command_palette.search": {"Search commands", "搜索命令"}, + "command_palette.empty": {"No matching commands", "没有匹配的命令"}, + "command_palette.hint_move": {"↑↓ choose", "↑↓ 选择"}, + "command_palette.hint_run": {"Enter run", "Enter 执行"}, + "command_palette.hint_close": {"Esc close", "Esc 关闭"}, + }) +} diff --git a/navigation_menu.go b/navigation_menu.go new file mode 100644 index 0000000..da0d3cc --- /dev/null +++ b/navigation_menu.go @@ -0,0 +1,206 @@ +package mujica + +import ( + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// NavigationLink is a page of a NavigationMenu, or with Items a group of pages. +type NavigationLink struct { + Label string + // Path is pushed on the router; groups need none. + Path string + Icon *ui.SVG + Description string + Items []NavigationLink +} + +// NavigationMenuOptions configure a NavigationMenu. +type NavigationMenuOptions struct { + // History shows back and forward buttons for the router. + History bool + // Label names the menu for assistive technology. + Label string +} + +// NavigationMenu shows links pushing pages on router; the router's path marks the current one. +func NavigationMenu(c *ui.Context, router *ui.Router, links []NavigationLink, opts NavigationMenuOptions) *ui.Element { + if router == nil { + panic("mujica: NavigationMenu needs a router") + } + if len(links) == 0 { + panic("mujica: NavigationMenu needs links") + } + k := Tokens(c) + u := Density(c).Unit() + path := router.Path() + label := opts.Label + if label == "" { + label = msg(c, "navigation_menu.label") + } + push := func(p string) { + if p != path { + slog.Debug("mujica: navigation menu push", "from", path, "to", p) + router.Push(p) + } + } + row := ui.Row(c).AlignItems(ui.Center).Gap(u * 2) + row.Children(func() { + if opts.History { + ui.Row(c).Gap(u).Children(func() { + navigationMenuHistory(c, ui.BackButton(c, router), msg(c, "navigation_menu.back")) + navigationMenuHistory(c, ui.ForwardButton(c, router), msg(c, "navigation_menu.forward")) + }) + ui.Box(c).Size(1, 20).Background(k.Ornament).Role(ui.RoleNone) + } + ui.Row(c).AlignItems(ui.Center).Gap(u).FocusGroup(ui.Horizontal).Role(ui.RoleToolbar).Label(label).Children(func() { + for _, l := range links { + navigationMenuTop(c, l, path, push) + } + }) + }) + return row +} + +func navigationMenuHistory(c *ui.Context, b *ui.Element, name string) { + k := Tokens(c) + d := Density(c) + b.Label(name).MinHeight(d.ControlHeight()).MinWidth(d.ControlHeight()).Radius(theme.ControlRadius).Background(k.Surface) + internal.Ring(b, k.Focus, theme.ControlRadius) +} + +// navigationMenuActive reports whether l or a link inside it is the page at path. +func navigationMenuActive(l NavigationLink, path string) bool { + // The root page is current only on itself, not under every path. + if l.Path != "" && (path == l.Path || l.Path != "/" && strings.HasPrefix(path, strings.TrimSuffix(l.Path, "/")+"/")) { + return true + } + for _, it := range l.Items { + if navigationMenuActive(it, path) { + return true + } + } + return false +} + +func navigationMenuTop(c *ui.Context, l NavigationLink, path string, push func(string)) { + if l.Label == "" { + panic("mujica: NavigationLink needs a Label") + } + if l.Items == nil && l.Path == "" { + panic("mujica: NavigationLink needs a Path or Items") + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + on := navigationMenuActive(l, path) + b := ui.ButtonBase(c).MinHeight(d.ControlHeight()).Padding(0, u*3).Gap(u * 1.5).Radius(theme.ControlRadius). + Label(l.Label).TextColor(k.TextMuted).Cursor(ui.CursorPointer) + open := ui.Local(b, "open", func() bool { return false }) + if l.Items == nil { + b.Role(ui.RoleLink) + } else { + b.Role(ui.RoleMenuButton) + } + if on { + b.TextColor(k.Text).FontWeight(600) + } + if b.Hovered() || *open { + b.Background(k.SurfaceHover).TextColor(k.Text) + } + b.FocusRing(false).DrawOver(func(p *ui.Painter, r ui.Rect) { + if on { + p.Fill(ui.Rect{X: r.X + u*3, Y: r.Y + r.H - 2, W: r.W - u*6, H: 2}, k.AccentText, 1) + } + if b.FocusVisible() { + internal.FocusRing(p, r, theme.ControlRadius, k.Focus) + } + }) + b.Children(func() { + if l.Icon != nil { + ui.Icon(c, l.Icon).Size(16, 16) + } + ui.Text(c, l.Label).SingleLine() + if l.Items != nil { + ui.Icon(c, icons.Must("chevron-down")).Size(12, 12).TextColor(k.TextMuted) + } + }) + if l.Items == nil { + if b.Clicked() { + push(l.Path) + } + return + } + if b.Clicked() { + *open = !*open + } else if b.Shortcut(0, ui.KeyDown) { + *open = true + } + ui.PopoverBase(c, b, open, func(panel *ui.Element) { + panel.Margin(u, 0, 0, 0).Padding(u*2).Gap(u).Radius(theme.PopupRadius).Background(k.Surface). + Border(theme.BorderWidth, k.Border).Shadow(0, 4, 12, 0, ui.RGBA(0, 0, 0, 0.16)).MinWidth(240).MaxWidth(360) + panel.FocusGroup(ui.Vertical) + first := true + var list func(items []NavigationLink) + list = func(items []NavigationLink) { + for _, it := range items { + if it.Label == "" { + panic("mujica: NavigationLink needs a Label") + } + if it.Items != nil { + ui.Text(c, it.Label).Font(theme.SerifFont).FontSize(theme.CaptionSize).TextColor(k.Ornament.Mix(k.Text, 0.35)). + Padding(u*2, u*2, u, u*2).SingleLine() + list(it.Items) + continue + } + if it.Path == "" { + panic("mujica: NavigationLink needs a Path or Items") + } + if navigationMenuItem(c, it, it.Path == path, first).Clicked() { + *open = false + push(it.Path) + } + first = false + } + } + list(l.Items) + }) +} + +func navigationMenuItem(c *ui.Context, l NavigationLink, on, first bool) *ui.Element { + k := Tokens(c) + u := Density(c).Unit() + e := ui.ButtonBase(c).Role(ui.RoleLink).Justify(ui.Start).AlignItems(ui.Start).Gap(u*3). + Padding(u*2, u*3).Radius(theme.ControlRadius).Label(l.Label).TextColor(k.Text).Cursor(ui.CursorPointer) + if first { + e.AutoFocus() + } + internal.Ring(e, k.Focus, theme.ControlRadius) + switch { + case on: + e.Background(k.Selection).FontWeight(600) + e.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + 4, W: 3, H: r.H - 8}, k.AccentText, 1.5) + }) + case e.Hovered(): + e.Background(k.SurfaceHover) + } + e.Children(func() { + if l.Icon != nil { + ui.Icon(c, l.Icon).Size(16, 16).Margin(2, 0, 0, 0) + } + ui.Column(c).Gap(2).MinWidth(0).Children(func() { + ui.Text(c, l.Label).SingleLine() + if l.Description != "" { + ui.Text(c, l.Description).FontSize(theme.CaptionSize).TextColor(k.TextMuted).FontWeight(400) + } + }) + }) + return e +} diff --git a/navigation_menu_test.go b/navigation_menu_test.go new file mode 100644 index 0000000..137e42f --- /dev/null +++ b/navigation_menu_test.go @@ -0,0 +1,139 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +func navigationMenuSample() []NavigationLink { + return []NavigationLink{ + {Label: "Home", Path: "/", Icon: icons.Must("house")}, + {Label: "Library", Items: []NavigationLink{ + {Label: "Poems", Path: "/library/poems", Description: "Verse of the court"}, + {Label: "Letters", Path: "/library/letters"}, + {Label: "Archive", Items: []NavigationLink{ + {Label: "Older letters", Path: "/archive/letters"}, + }}, + }}, + {Label: "About", Path: "/about"}, + } +} + +func TestNavigationMenuPush(t *testing.T) { + r := ui.NewRouter("/") + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + NavigationMenu(c, r, navigationMenuSample(), NavigationMenuOptions{History: true}) + }, 700, 400) + if err := tt.Click("About"); err != nil { + t.Fatal(err) + } + if r.Path() != "/about" { + t.Fatalf("path %q", r.Path()) + } + if err := tt.Click("Library"); err != nil { + t.Fatal(err) + } + if !tt.HasText("Verse of the court") || !tt.HasText("Older letters") { + t.Fatalf("group did not open: %q", tt.Texts()) + } + if err := tt.Click("Older letters"); err != nil { + t.Fatal(err) + } + if r.Path() != "/archive/letters" || tt.HasText("Verse of the court") { + t.Fatalf("path %q, panel open %v", r.Path(), tt.HasText("Verse of the court")) + } + if err := tt.Click("Back"); err != nil { + t.Fatal(err) + } + if r.Path() != "/about" { + t.Fatalf("Back went to %q", r.Path()) + } + if err := tt.Click("Forward"); err != nil { + t.Fatal(err) + } + if r.Path() != "/archive/letters" { + t.Fatalf("Forward went to %q", r.Path()) + } +} + +func TestNavigationMenuKeyboard(t *testing.T) { + r := ui.NewRouter("/") + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + NavigationMenu(c, r, navigationMenuSample(), NavigationMenuOptions{}) + }, 700, 400) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyRight) + if !tt.Focused("Library") { + t.Fatal("Right did not reach Library") + } + tt.Key(0, ui.KeyDown) + if !tt.Focused("Poems") { + t.Fatalf("Down did not open into the panel: %q", tt.Texts()) + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyEnter) + if r.Path() != "/library/letters" { + t.Fatalf("keyboard went to %q", r.Path()) + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyEscape) + if tt.HasText("Poems") || !tt.Focused("Library") { + t.Fatalf("Escape: open %v, focus on Library %v", tt.HasText("Poems"), tt.Focused("Library")) + } +} + +func TestNavigationMenuActive(t *testing.T) { + l := navigationMenuSample() + for _, tc := range []struct { + path string + want [3]bool + }{ + {"/", [3]bool{true, false, false}}, + {"/library/poems", [3]bool{false, true, false}}, + {"/archive/letters/7", [3]bool{false, true, false}}, + {"/about", [3]bool{false, false, true}}, + {"/aboutx", [3]bool{false, false, false}}, + } { + for i := range l { + got := navigationMenuActive(l[i], tc.path) + if got != tc.want[i] { + t.Errorf("%s: link %s active %v", tc.path, l[i].Label, got) + } + } + } +} + +func TestNavigationMenuInvalid(t *testing.T) { + for name, links := range map[string][]NavigationLink{ + "empty": nil, + "path": {{Label: "X"}}, + "label": {{Path: "/x"}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + NavigationMenu(c, ui.NewRouter("/"), links, NavigationMenuOptions{}) + }, 300, 100, 1) + }) + } +} + +func TestNavigationMenuSnapshot(t *testing.T) { + r := ui.NewRouter("/") + r.Push("/library/poems") + snapshot(t, "navigation_menu", 620, 120, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + NavigationMenu(c, r, navigationMenuSample(), NavigationMenuOptions{History: true}) + }) + }) +} diff --git a/pagination.go b/pagination.go new file mode 100644 index 0000000..05a6fa3 --- /dev/null +++ b/pagination.go @@ -0,0 +1,247 @@ +package mujica + +import ( + "fmt" + "log/slog" + "slices" + "strconv" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// PaginationOptions configure a Pagination. +type PaginationOptions struct { + // Total is the number of records; it must not be negative. + Total int + // PageSizes lists the page sizes offered; nil hides the choice. + PageSizes []int + // Jump shows a field going to a typed page. + Jump bool + // Siblings is how many pages show each side of the current; zero means 1. + Siblings int + Disabled bool +} + +// PaginationResult is the built Pagination. +type PaginationResult struct { + *ui.Element + changed bool +} + +// Changed reports a new page or page size in this frame, from the user or a correction. +func (r PaginationResult) Changed() bool { return r.changed } + +// Pagination moves *page (from 1) through Total records, *pageSize per page. +// A page past the end after Total shrinks is corrected to the last page. +func Pagination(c *ui.Context, page, pageSize *int, opts PaginationOptions) PaginationResult { + if opts.Total < 0 { + panic("mujica: Pagination Total is negative") + } + if *pageSize <= 0 { + panic("mujica: Pagination pageSize must be positive") + } + if opts.PageSizes != nil && !slices.Contains(opts.PageSizes, *pageSize) { + panic("mujica: Pagination pageSize is not one of PageSizes") + } + sib := opts.Siblings + if sib == 0 { + sib = 1 + } + if sib < 0 { + panic("mujica: Pagination Siblings is negative") + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + row := ui.Row(c).AlignItems(ui.Center).Gap(u * 3).Wrap().Role(ui.RoleGroup).Label(msg(c, "pagination.label")).Disabled(opts.Disabled) + res := PaginationResult{Element: row} + pages := paginationPages(opts.Total, *pageSize) + if p := max(1, min(*page, pages)); p != *page { + slog.Debug("mujica: pagination correct", "from", *page, "to", p, "pages", pages) + *page, res.changed = p, true + } + set := func(p int) { + if p != *page { + slog.Debug("mujica: pagination page", "from", *page, "to", p) + *page, res.changed = p, true + } + } + row.Children(func() { + ui.Text(c, fmt.Sprintf(msg(c, "pagination.total"), opts.Total)).TextColor(k.TextMuted).SingleLine() + ui.Row(c).AlignItems(ui.Center).Gap(u).FocusGroup(ui.Horizontal).Children(func() { + if paginationButton(c, "", icons.Must("chevron-left"), msg(c, "pagination.prev"), false, *page <= 1).Clicked() { + set(*page - 1) + } + for _, p := range paginationItems(*page, pages, sib) { + if p == 0 { + ui.Text(c, "…").TextColor(k.TextMuted).Padding(0, u).Role(ui.RoleNone) + continue + } + n := strconv.Itoa(p) + name := fmt.Sprintf(msg(c, "pagination.page"), p) + if p == *page { + name += ", " + msg(c, "pagination.current") + } + if paginationButton(c, n, nil, name, p == *page, false).Clicked() { + set(p) + } + } + if paginationButton(c, "", icons.Must("chevron-right"), msg(c, "pagination.next"), false, *page >= pages).Clicked() { + set(*page + 1) + } + }) + if opts.PageSizes != nil { + paginationSizes(c, page, pageSize, opts.PageSizes, &res) + } + if opts.Jump { + paginationJump(c, pages, set) + } + }) + return res +} + +func paginationPages(total, size int) int { return max(1, (total+size-1)/size) } + +// paginationItems lists the page numbers to show, 0 standing for an ellipsis. +func paginationItems(cur, pages, sib int) []int { + var out []int + if pages <= 2*sib+5 { + for p := 1; p <= pages; p++ { + out = append(out, p) + } + return out + } + start := max(min(cur-sib, pages-2*sib-2), 3) + end := min(max(cur+sib, 2*sib+3), pages-2) + out = append(out, 1) + if start > 3 { + out = append(out, 0) + } else { + out = append(out, 2) + } + for p := start; p <= end; p++ { + out = append(out, p) + } + if end < pages-2 { + out = append(out, 0) + } else { + out = append(out, pages-1) + } + return append(out, pages) +} + +func paginationButton(c *ui.Context, text string, icon *ui.SVG, name string, on, disabled bool) *ui.Element { + k := Tokens(c) + h := Density(c).ControlHeight() + b := ui.ButtonBase(c).MinHeight(h).MinWidth(h).Padding(0, Density(c).Unit()*2).Radius(theme.ControlRadius). + Label(name).Disabled(disabled) + internal.Ring(b, k.Focus, theme.ControlRadius) + switch { + case on: + b.Background(k.Accent).TextColor(k.OnAccent).FontWeight(600) + case b.Pressed(): + b.Background(k.Selection).TextColor(k.Text) + case b.Hovered(): + b.Background(k.SurfaceHover).TextColor(k.Text).Border(theme.BorderWidth, k.ControlBorder) + default: + b.TextColor(k.Text).Border(theme.BorderWidth, k.Border) + } + if icon != nil { + b.Tooltip(name) + } + b.Children(func() { + if icon != nil { + ui.Icon(c, icon).Size(16, 16) + } else { + ui.Text(c, text).SingleLine().FontFeatures("tnum") + } + }) + return b +} + +func paginationSizes(c *ui.Context, page, pageSize *int, sizes []int, res *PaginationResult) { + k := Tokens(c) + d := Density(c) + u := d.Unit() + old := *pageSize + sel := ui.SelectBase(c, pageSize) + sel.Trigger.MinHeight(d.ControlHeight()).Padding(0, u*3).Gap(u*2).Radius(theme.ControlRadius). + Background(k.Surface).Border(theme.BorderWidth, k.ControlBorder).Label(msg(c, "pagination.size")) + internal.Ring(sel.Trigger, k.Focus, theme.ControlRadius) + if sel.Trigger.Hovered() { + sel.Trigger.Background(k.SurfaceHover) + } + sel.Trigger.Children(func() { + ui.Text(c, fmt.Sprintf(msg(c, "pagination.per_page"), *pageSize)).SingleLine() + ui.Icon(c, icons.Must("chevron-down")).Size(14, 14).TextColor(k.TextMuted) + }) + sel.Popup(func(panel *ui.Element) { + 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)) + for _, s := range sizes { + it := sel.Item(s).Padding(u*1.5, u*3).Radius(theme.ControlRadius).Gap(u * 2) + if it.Highlighted() { + it.Background(k.Selection) + } + it.Children(func() { + ui.Text(c, fmt.Sprintf(msg(c, "pagination.per_page"), s)).SingleLine().Grow(1) + if s == *pageSize { + ui.Icon(c, icons.Must("check")).Size(14, 14).TextColor(k.AccentText) + } + }) + } + }) + if *pageSize != old { + // Keep the first record of the old page in view. + first := (*page-1)*old + 1 + *page = (first-1) / *pageSize + 1 + res.changed = true + slog.Debug("mujica: pagination size", "from", old, "to", *pageSize, "page", *page) + } +} + +func paginationJump(c *ui.Context, pages int, set func(int)) { + k := Tokens(c) + d := Density(c) + u := d.Unit() + ui.Row(c).AlignItems(ui.Center).Gap(u * 2).Children(func() { + ui.Text(c, msg(c, "pagination.jump")).TextColor(k.TextMuted).SingleLine() + box := ui.Row(c).AlignItems(ui.Center).MinHeight(d.ControlHeight()).Width(64).Padding(0, u*2). + Radius(theme.ControlRadius).Background(k.Surface) + text := ui.Local(box, "text", func() string { return "" }) + bad := ui.Local(box, "bad", func() bool { return false }) + var in *ui.Element + box.Children(func() { + in = ui.TextInputBase(c, text).FillWidth().Label(msg(c, "pagination.field")) + }) + if in.Changed() { + *bad = false + } + if in.Submitted() { + p, err := strconv.Atoi(strings.TrimSpace(*text)) + if err != nil || p < 1 || p > pages { + *bad = true + slog.Debug("mujica: pagination jump rejected", "text", *text, "pages", pages) + } else { + *text, *bad = "", false + set(p) + } + } + border := k.ControlBorder + if *bad { + border = k.Danger + in.Error(fmt.Sprintf(msg(c, "pagination.range"), pages)) + } else if in.Focused() { + border = k.Focus + } + box.Border(theme.BorderWidth, border) + if *bad { + ui.Text(c, fmt.Sprintf(msg(c, "pagination.range"), pages)).TextColor(k.Danger).SingleLine() + } + }) +} diff --git a/pagination_test.go b/pagination_test.go new file mode 100644 index 0000000..0e7d427 --- /dev/null +++ b/pagination_test.go @@ -0,0 +1,166 @@ +package mujica + +import ( + "slices" + "testing" + + "github.com/egoist/mygo/ui" +) + +func TestPaginationItems(t *testing.T) { + for _, tc := range []struct { + cur, pages int + want []int + }{ + {1, 1, []int{1}}, + {3, 7, []int{1, 2, 3, 4, 5, 6, 7}}, + {1, 20, []int{1, 2, 3, 4, 5, 0, 20}}, + {10, 20, []int{1, 0, 9, 10, 11, 0, 20}}, + {20, 20, []int{1, 0, 16, 17, 18, 19, 20}}, + } { + if got := paginationItems(tc.cur, tc.pages, 1); !slices.Equal(got, tc.want) { + t.Errorf("items(%d, %d) = %v, want %v", tc.cur, tc.pages, got, tc.want) + } + } +} + +func TestPaginationNavigate(t *testing.T) { + page, size, changes := 1, 10, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if Pagination(c, &page, &size, PaginationOptions{Total: 95}).Changed() { + changes++ + } + }, 700, 120) + // Previous is disabled on page 1, so Tab lands on "1". + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyEnter) + if page != 2 || changes != 1 { + t.Fatalf("keyboard: page %d changes %d", page, changes) + } + if err := tt.Click("Next page"); err != nil { + t.Fatal(err) + } + if page != 3 || changes != 2 { + t.Fatalf("next: page %d changes %d", page, changes) + } + if err := tt.Click("10"); err != nil { + t.Fatal(err) + } + if page != 10 { + t.Fatalf("click 10: page %d", page) + } + _ = tt.Click("Next page") + if page != 10 { + t.Fatalf("next past the end: page %d", page) + } +} + +func TestPaginationCorrectsOnTotalChange(t *testing.T) { + page, size, total, changes := 9, 10, 95, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if Pagination(c, &page, &size, PaginationOptions{Total: total}).Changed() { + changes++ + } + }, 700, 120) + total = 31 + tt.Frame() + if page != 4 || changes != 1 { + t.Fatalf("page %d changes %d, want 4 and 1", page, changes) + } + total = 0 + tt.Frame() + if page != 1 { + t.Fatalf("empty total: page %d", page) + } +} + +func TestPaginationPageSizeKeepsRecord(t *testing.T) { + page, size := 3, 10 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + Pagination(c, &page, &size, PaginationOptions{Total: 200, PageSizes: []int{10, 20, 50}}) + }, 800, 300) + if err := tt.Click("10 / page"); err != nil { + t.Fatal(err) + } + if err := tt.Click("50 / page"); err != nil { + t.Fatal(err) + } + // Record 21 was first on page 3; with 50 per page it is on page 1. + if size != 50 || page != 1 { + t.Fatalf("size %d page %d", size, page) + } +} + +func TestPaginationJump(t *testing.T) { + page, size := 1, 10 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{Locale: ZhCN}) + Pagination(c, &page, &size, PaginationOptions{Total: 100, Jump: true}) + }, 900, 120) + if err := tt.Click("页码"); err != nil { + t.Fatal(err) + } + tt.Type("42") + tt.Key(0, ui.KeyEnter) + if page != 1 || !tt.HasText("1–10") { + t.Fatalf("out of range accepted: page %d texts %q", page, tt.Texts()) + } + tt.Key(0, ui.KeyBackspace) + tt.Key(0, ui.KeyBackspace) + tt.Type("7") + tt.Key(0, ui.KeyEnter) + if page != 7 || tt.HasText("1–10") { + t.Fatalf("jump: page %d texts %q", page, tt.Texts()) + } +} + +func TestPaginationDisabled(t *testing.T) { + page, size := 2, 10 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + Pagination(c, &page, &size, PaginationOptions{Total: 50, Disabled: true}) + }, 700, 120) + _ = tt.Click("Next page") + _ = tt.Click("4") + if page != 2 { + t.Fatalf("disabled pagination moved to %d", page) + } +} + +func TestPaginationInvalid(t *testing.T) { + for name, tc := range map[string]struct { + size int + opts PaginationOptions + }{ + "total": {10, PaginationOptions{Total: -1}}, + "size": {0, PaginationOptions{}}, + "sizes": {15, PaginationOptions{PageSizes: []int{10, 20}}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + page := 1 + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Pagination(c, &page, &tc.size, tc.opts) + }, 300, 100, 1) + }) + } +} + +func TestPaginationSnapshot(t *testing.T) { + snapshot(t, "pagination", 760, 140, func(c *ui.Context) { + a, b, s := 10, 1, 20 + ui.Column(c).Padding(16).Gap(16).Children(func() { + Pagination(c, &a, &s, PaginationOptions{Total: 400, PageSizes: []int{10, 20, 50}, Jump: true}) + Pagination(c, &b, &s, PaginationOptions{Total: 45, Disabled: true}) + }) + }) +} diff --git a/segmented_control.go b/segmented_control.go new file mode 100644 index 0000000..f20cfe9 --- /dev/null +++ b/segmented_control.go @@ -0,0 +1,97 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// Segment is one choice of a SegmentedControl. +type Segment struct { + // Label is required: it is shown, or with HideLabel names the icon. + Label string + Icon *ui.SVG + HideLabel bool +} + +// SegmentedControlOptions configure a SegmentedControl. +type SegmentedControlOptions struct { + // Equal gives every segment the width of the widest. + Equal bool + // Label names the group for assistive technology. + Label string + Disabled bool +} + +// SegmentedControl chooses one of segments into *selected; Changed reports a new choice. +func SegmentedControl(c *ui.Context, selected *int, segments []Segment, opts SegmentedControlOptions) *ui.Element { + if len(segments) == 0 { + panic("mujica: SegmentedControl needs at least one segment") + } + if *selected < 0 || *selected >= len(segments) { + panic("mujica: SegmentedControl selected index out of range") + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + inset := float32(2) + prev := *selected + parts := ui.SegmentedBase(c, selected, len(segments)) + track := parts.Track.Padding(inset).Gap(inset).Radius(theme.ControlRadius+inset). + Background(k.Background).Border(theme.BorderWidth, k.ControlBorder).AlignItems(ui.Stretch). + MinHeight(d.ControlHeight()).Disabled(opts.Disabled) + if opts.Equal { + track.Grid().Columns(len(segments)) + } + if opts.Label != "" { + track.Label(opts.Label) + } + track.Children(func() { + for i, s := range segments { + if s.Label == "" { + panic("mujica: Segment needs a Label") + } + if s.HideLabel && s.Icon == nil { + panic("mujica: Segment with HideLabel needs an Icon") + } + on := i == *selected + seg := parts.Segment(i).Padding(0, u*3).Gap(u * 1.5).Radius(theme.ControlRadius). + MinWidth(theme.MinHitSize).Label(s.Label) + if !opts.Disabled { + seg.Cursor(ui.CursorPointer) + } + fg := k.TextMuted + switch { + case on: + seg.Background(k.Accent).FontWeight(600) + fg = k.OnAccent + case seg.Pressed(): + seg.Background(k.Selection) + fg = k.Text + case seg.Hovered(): + seg.Background(k.SurfaceHover) + fg = k.Text + } + seg.TextColor(fg) + internal.Ring(seg, k.Focus, theme.ControlRadius) + if s.HideLabel { + seg.Tooltip(s.Label) + } + seg.Children(func() { + if s.Icon != nil { + ui.Icon(c, s.Icon).Size(16, 16) + } + if !s.HideLabel { + ui.Text(c, s.Label).SingleLine() + } + }) + } + }) + if track.Changed() { + slog.Debug("mujica: segmented control change", "from", prev, "to", *selected) + } + return track +} diff --git a/segmented_control_test.go b/segmented_control_test.go new file mode 100644 index 0000000..56ea8c2 --- /dev/null +++ b/segmented_control_test.go @@ -0,0 +1,112 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +func TestSegmentedControlClickAndKeys(t *testing.T) { + sel, changes := 0, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if SegmentedControl(c, &sel, []Segment{{Label: "Day"}, {Label: "Week"}, {Label: "Month"}}, SegmentedControlOptions{Label: "Range"}).Changed() { + changes++ + } + }, 400, 100) + if err := tt.Click("Month"); err != nil { + t.Fatal(err) + } + if sel != 2 || changes != 1 { + t.Fatalf("click: sel %d changes %d", sel, changes) + } + tt.Key(0, ui.KeyRight) + if sel != 0 || changes != 2 { + t.Fatalf("Right wraps to %d, changes %d", sel, changes) + } + tt.Key(0, ui.KeyLeft) + if sel != 2 { + t.Fatalf("Left wraps to %d", sel) + } + tt.Key(0, ui.KeyHome) + if sel != 0 || !tt.Focused("Day") { + t.Fatalf("Home chose %d", sel) + } +} + +func TestSegmentedControlIconsAndDisabled(t *testing.T) { + sel := 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + SegmentedControl(c, &sel, []Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: true}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: true}, + }, SegmentedControlOptions{Disabled: true}) + }, 400, 100) + if _, ok := tt.Find("Grid"); !ok { + t.Fatal("icon segment has no name") + } + _ = tt.Click("Grid") + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyRight) + if sel != 0 { + t.Fatalf("disabled control changed to %d", sel) + } +} + +func TestSegmentedControlEqualWidths(t *testing.T) { + sel := 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).AlignItems(ui.Start).Children(func() { + SegmentedControl(c, &sel, []Segment{{Label: "A"}, {Label: "Much longer"}}, SegmentedControlOptions{Equal: true}) + }) + }, 400, 100) + a, _ := tt.Find("A") + b, _ := tt.Find("Much longer") + // Equal segments center their text: A's center sits a segment width before the other's. + da := (b.X + b.W/2) - (a.X + a.W/2) + if da < b.W { + t.Fatalf("segments not equal: A %+v, long %+v", a, b) + } +} + +func TestSegmentedControlInvalid(t *testing.T) { + for name, segs := range map[string][]Segment{ + "empty": nil, + "label": {{Icon: icons.Must("list")}}, + "noicon": {{Label: "X", HideLabel: true}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + sel := 0 + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + SegmentedControl(c, &sel, segs, SegmentedControlOptions{}) + }, 300, 100, 1) + }) + } +} + +func TestSegmentedControlSnapshot(t *testing.T) { + snapshot(t, "segmented_control", 480, 180, func(c *ui.Context) { + a, b, e := 1, 0, 2 + ui.Column(c).Padding(16).Gap(16).AlignItems(ui.Start).Children(func() { + SegmentedControl(c, &a, []Segment{{Label: "Day"}, {Label: "Week"}, {Label: "Month"}}, SegmentedControlOptions{}) + ui.Row(c).Gap(16).Children(func() { + SegmentedControl(c, &b, []Segment{ + {Label: "List", Icon: icons.Must("list"), HideLabel: true}, + {Label: "Grid", Icon: icons.Must("layout-grid"), HideLabel: true}, + }, SegmentedControlOptions{}) + SegmentedControl(c, &b, []Segment{{Label: "Light", Icon: icons.Must("sun")}, {Label: "Dark", Icon: icons.Must("moon")}}, SegmentedControlOptions{Disabled: true}) + }) + SegmentedControl(c, &e, []Segment{{Label: "S"}, {Label: "Medium"}, {Label: "XL"}}, SegmentedControlOptions{Equal: true}) + }) + }) +} diff --git a/sidebar.go b/sidebar.go new file mode 100644 index 0000000..339400e --- /dev/null +++ b/sidebar.go @@ -0,0 +1,182 @@ +package mujica + +import ( + "fmt" + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// SidebarItem is a destination of a Sidebar, or with Items a nested group. +type SidebarItem struct { + // ID is what choosing the item stores; unused for groups. + ID string + Label string + Icon *ui.SVG + // Count shows beside the label when positive. + Count int + // Items nest under the item, which then expands and collapses. + Items []SidebarItem +} + +// SidebarSection is a titled group of a Sidebar; its title collapses it. +type SidebarSection struct { + Title string + Items []SidebarItem +} + +// SidebarOptions configure a Sidebar. +type SidebarOptions struct { + // Compact shows only icons, labels as tooltips; every item needs an Icon. + Compact bool + // Width is 240 DIPs when zero, 56 when Compact. + Width float32 +} + +// SidebarResult is the built Sidebar. +type SidebarResult struct { + *ui.Element + changed bool +} + +// Changed reports a new choice in this frame. +func (r SidebarResult) Changed() bool { return r.changed } + +// Sidebar is a source list choosing an item ID into *selected, built on MyGo's Sidebar. +func Sidebar(c *ui.Context, selected *string, sections []SidebarSection, opts SidebarOptions) SidebarResult { + k := Tokens(c) + width := opts.Width + if width == 0 { + width = 240 + if opts.Compact { + width = 56 + } + } + wrap := ui.Column(c).Width(width).FillHeight().AlignItems(ui.Stretch).Shrink(0) + closed := ui.Local(wrap, "closed", func() map[string]*bool { return map[string]*bool{} }) + res := SidebarResult{Element: wrap} + prev := *selected + wrap.Children(func() { + // MyGo draws icons in Accent: swap in readable state colors for the sidebar. + saved := c.Theme() + t := *saved + t.Accent, t.AccentText, t.Surface = k.AccentText, k.AccentText, k.Surface + c.SetTheme(&t) + sb := ui.Sidebar(c, selected, func() { + for si, s := range sections { + if opts.Compact { + if si > 0 { + sidebarRule(c) + } + sidebarFlat(c, *selected, s.Items) + continue + } + if s.Title == "" { + panic("mujica: SidebarSection needs a Title") + } + ui.SidebarSection(c, s.Title, sidebarOpen(*closed, "s:"+s.Title), func() { + sidebarItems(c, *selected, s.Items, *closed, 0) + }) + } + }).FillHeight().FillWidth() + sb.DrawOver(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X + r.W - 1, Y: r.Y, W: 1, H: r.H}, k.Border, 0) + }) + c.SetTheme(saved) + if sb.Changed() { + res.changed = true + slog.Debug("mujica: sidebar choose", "from", prev, "to", *selected) + } + }) + return res +} + +// sidebarOpen returns the shown state of a collapsible part, open at first. +func sidebarOpen(closed map[string]*bool, key string) *bool { + o, ok := closed[key] + if !ok { + o = new(bool) + *o = true + closed[key] = o + } + return o +} + +func sidebarItems(c *ui.Context, selected string, items []SidebarItem, closed map[string]*bool, depth int) { + u := Density(c).Unit() + for _, it := range items { + if it.Label == "" { + panic("mujica: SidebarItem needs a Label") + } + if it.Items != nil { + ui.Column(c).Padding(0, 0, 0, float32(depth)*u*4).Children(func() { + ui.SidebarSection(c, it.Label, sidebarOpen(closed, fmt.Sprintf("g%d:%s", depth, it.Label)), func() { + sidebarItems(c, selected, it.Items, closed, depth+1) + }) + }) + continue + } + ui.Column(c).Padding(0, 0, 0, float32(depth)*u*4).Children(func() { + sidebarItem(c, selected, it, false) + }) + } +} + +// sidebarFlat lists every leaf for the compact rail. +func sidebarFlat(c *ui.Context, selected string, items []SidebarItem) { + for _, it := range items { + if it.Items != nil { + sidebarFlat(c, selected, it.Items) + continue + } + if it.Icon == nil { + panic("mujica: compact Sidebar items need an Icon") + } + sidebarItem(c, selected, it, true) + } +} + +func sidebarItem(c *ui.Context, selected string, it SidebarItem, compact bool) { + if it.ID == "" { + panic("mujica: SidebarItem needs an ID") + } + k := Tokens(c) + d := Density(c) + on := it.ID == selected + label := it.Label + if compact { + label = "" + } + e := ui.SidebarItem(c, it.ID, it.Icon, label).MinHeight(d.ControlHeight()).Radius(theme.ControlRadius) + name := it.Label + if it.Count > 0 { + name = fmt.Sprintf("%s (%d)", it.Label, it.Count) + } + if compact { + e.Tooltip(name).Label(name) + } else if it.Count > 0 { + e.Label(name).Children(func() { + ui.Text(c, sidebarCount(it.Count)).FontSize(theme.CaptionSize).FontFeatures("tnum"). + TextColor(k.TextMuted).Padding(0, d.Unit()*1.5).Radius(theme.ControlRadius).Background(k.Background) + }) + } + if on { + e.Background(k.Selection).TextColor(k.Text).FontWeight(600) + e.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + 4, W: 3, H: r.H - 8}, k.AccentText, 1.5) + }) + } +} + +func sidebarCount(n int) string { + if n > 999 { + return "999+" + } + return fmt.Sprint(n) +} + +func sidebarRule(c *ui.Context) { + ui.Box(c).Height(1).Margin(Density(c).Unit()*2, 4).Background(Tokens(c).Ornament).Role(ui.RoleNone) +} diff --git a/sidebar_test.go b/sidebar_test.go new file mode 100644 index 0000000..9eb9f63 --- /dev/null +++ b/sidebar_test.go @@ -0,0 +1,135 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +func sidebarSample() []SidebarSection { + return []SidebarSection{ + {Title: "Library", Items: []SidebarItem{ + {ID: "inbox", Label: "Inbox", Icon: icons.Must("inbox"), Count: 12}, + {ID: "starred", Label: "Starred", Icon: icons.Must("star")}, + {Label: "Projects", Items: []SidebarItem{ + {ID: "opera", Label: "Opera", Icon: icons.Must("folder")}, + {ID: "ballet", Label: "Ballet", Icon: icons.Must("folder")}, + }}, + }}, + {Title: "System", Items: []SidebarItem{ + {ID: "settings", Label: "Settings", Icon: icons.Must("settings")}, + }}, + } +} + +func TestSidebarChooseAndKeys(t *testing.T) { + sel, changes := "inbox", 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if Sidebar(c, &sel, sidebarSample(), SidebarOptions{}).Changed() { + changes++ + } + }, 400, 500) + if err := tt.Click("Opera"); err != nil { + t.Fatal(err) + } + if sel != "opera" || changes != 1 { + t.Fatalf("click: %q %d", sel, changes) + } + tt.Key(0, ui.KeyDown) + if sel != "ballet" { + t.Fatalf("Down chose %q", sel) + } + tt.Key(0, ui.KeyEnd) + if sel != "settings" { + t.Fatalf("End chose %q", sel) + } + tt.Key(0, ui.KeyHome) + if sel != "inbox" || changes != 4 { + t.Fatalf("Home chose %q, changes %d", sel, changes) + } +} + +func TestSidebarCollapse(t *testing.T) { + sel := "inbox" + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + Sidebar(c, &sel, sidebarSample(), SidebarOptions{}) + }, 400, 500) + if err := tt.Click("Projects"); err != nil { + t.Fatal(err) + } + if tt.HasText("Opera") { + t.Fatal("nested group did not collapse") + } + if err := tt.Click("Library"); err != nil { + t.Fatal(err) + } + if tt.HasText("Starred") || !tt.HasText("Settings") { + t.Fatalf("section did not collapse: %q", tt.Texts()) + } + _ = tt.Click("Library") + if !tt.HasText("Starred") || tt.HasText("Opera") { + t.Fatalf("collapse state lost: %q", tt.Texts()) + } +} + +func TestSidebarCompact(t *testing.T) { + sel := "inbox" + var w float32 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Row(c).FillHeight().Children(func() { + w = Sidebar(c, &sel, sidebarSample(), SidebarOptions{Compact: true}).Bounds().W + }) + }, 400, 500) + tt.Frame() + if w != 56 { + t.Fatalf("compact width %v", w) + } + if err := tt.Click("Ballet"); err != nil { + t.Fatal(err) + } + if sel != "ballet" { + t.Fatalf("compact click chose %q", sel) + } + if _, ok := tt.Find("Inbox (12)"); !ok { + t.Fatalf("compact item lost its name: %q", tt.Texts()) + } +} + +func TestSidebarInvalid(t *testing.T) { + for name, tc := range map[string]struct { + s []SidebarSection + opts SidebarOptions + }{ + "title": {[]SidebarSection{{Items: []SidebarItem{{ID: "a", Label: "A"}}}}, SidebarOptions{}}, + "id": {[]SidebarSection{{Title: "T", Items: []SidebarItem{{Label: "A"}}}}, SidebarOptions{}}, + "compact": {[]SidebarSection{{Title: "T", Items: []SidebarItem{{ID: "a", Label: "A"}}}}, SidebarOptions{Compact: true}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + sel := "" + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Sidebar(c, &sel, tc.s, tc.opts) + }, 300, 300, 1) + }) + } +} + +func TestSidebarSnapshot(t *testing.T) { + snapshot(t, "sidebar", 420, 360, func(c *ui.Context) { + a, b := "opera", "starred" + ui.Row(c).Fill().Gap(24).Children(func() { + Sidebar(c, &a, sidebarSample(), SidebarOptions{}) + Sidebar(c, &b, sidebarSample(), SidebarOptions{Compact: true}) + }) + }) +} diff --git a/steps.go b/steps.go new file mode 100644 index 0000000..8971b9a --- /dev/null +++ b/steps.go @@ -0,0 +1,181 @@ +package mujica + +import ( + "fmt" + "log/slog" + "strconv" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// Step is one stage of Steps. +type Step struct { + Title string + Description string + // Error marks the step as failed, whatever its place. + Error bool + // Clickable lets the user go to the step. + Clickable bool +} + +// StepsOptions configure Steps. +type StepsOptions struct { + Vertical bool + // Label names the steps for assistive technology. + Label string +} + +// StepsResult is the built Steps. +type StepsResult struct { + *ui.Element + changed bool +} + +// Changed reports that the user chose a clickable step in this frame. +func (r StepsResult) Changed() bool { return r.changed } + +type stepsState int + +const ( + stepsWaiting stepsState = iota + stepsCurrent + stepsDone + stepsFailed +) + +// Steps shows a process whose step *current is under way: earlier steps are done. +func Steps(c *ui.Context, current *int, steps []Step, opts StepsOptions) StepsResult { + if len(steps) == 0 { + panic("mujica: Steps needs at least one step") + } + if *current < 0 || *current > len(steps) { + panic("mujica: Steps current out of range") + } + u := Density(c).Unit() + root := ui.Row(c).AlignItems(ui.Start).Gap(u * 2).FocusGroup(ui.Horizontal | ui.Vertical).Role(ui.RoleList) + if opts.Vertical { + root.Column().AlignItems(ui.Stretch).Gap(0) + } + if opts.Label != "" { + root.Label(opts.Label) + } + res := StepsResult{Element: root} + root.Children(func() { + for i, s := range steps { + if s.Title == "" { + panic("mujica: Step needs a Title") + } + st := stepsWaiting + switch { + case s.Error: + st = stepsFailed + case i < *current: + st = stepsDone + case i == *current: + st = stepsCurrent + } + if !opts.Vertical && i > 0 { + stepsLine(c, steps, i-1, *current) + } + if stepsItem(c, i, len(steps), s, st, opts.Vertical, i < len(steps)-1 && opts.Vertical, i < *current) && i != *current { + slog.Debug("mujica: steps choose", "from", *current, "to", i) + *current, res.changed = i, true + } + } + }) + return res +} + +// stepsItem builds one step and reports a click on it. +func stepsItem(c *ui.Context, i, n int, s Step, st stepsState, vertical, tail, doneLine bool) bool { + k := Tokens(c) + u := Density(c).Unit() + var e *ui.Element + if s.Clickable { + e = ui.ButtonBase(c).Cursor(ui.CursorPointer) + internal.Ring(e, k.Focus, theme.ControlRadius) + if e.Hovered() { + e.Background(k.SurfaceHover) + } + } else { + e = ui.Row(c).Shrink(0).Role(ui.RoleListItem) + } + e.AlignItems(ui.Stretch).Justify(ui.Start).Gap(u * 3).Padding(u).Radius(theme.ControlRadius). + Label(fmt.Sprintf(msg(c, "steps.item"), i+1, n, s.Title, msg(c, stepsStateKey[st]))) + e.Children(func() { + ui.Column(c).AlignItems(ui.Center).Gap(u).Shrink(0).Children(func() { + stepsMarker(c, i, st) + if tail { + ui.Box(c).Width(2).Grow(1).MinHeight(u * 4).Background(stepsLineColor(k, doneLine)).Radius(1) + } + }) + texts := ui.Column(c).Gap(u*0.5).MinWidth(0).Padding(2, 0, 0, 0) + if vertical { + texts.Padding(2, 0, u*4, 0) + } + texts.Children(func() { + title := ui.Text(c, s.Title).SingleLine() + switch st { + case stepsCurrent: + title.FontWeight(600).TextColor(k.Text) + case stepsFailed: + title.FontWeight(600).TextColor(k.Danger) + case stepsDone: + title.TextColor(k.Text) + default: + title.TextColor(k.TextMuted) + } + if s.Description != "" { + ui.Text(c, s.Description).FontSize(theme.CaptionSize).TextColor(k.TextMuted).MaxWidth(240) + } + }) + }) + return s.Clickable && e.Clicked() +} + +var stepsStateKey = map[stepsState]string{ + stepsWaiting: "steps.waiting", stepsCurrent: "steps.current", stepsDone: "steps.done", stepsFailed: "steps.error", +} + +func stepsMarker(c *ui.Context, i int, st stepsState) { + k := Tokens(c) + m := ui.Box(c).Size(24, 24).Radius(12).Center().Shrink(0).Role(ui.RoleNone) + var icon string + switch st { + case stepsDone: + m.Background(k.Accent).TextColor(k.OnAccent) + icon = "check" + case stepsCurrent: + m.Background(k.Surface).Border(2, k.AccentText).TextColor(k.AccentText).FontWeight(700) + case stepsFailed: + m.Background(k.Surface).Border(2, k.Danger).TextColor(k.Danger) + icon = "x" + default: + m.Background(k.Surface).Border(theme.BorderWidth, k.ControlBorder).TextColor(k.TextMuted) + } + m.Children(func() { + if icon != "" { + ui.Icon(c, icons.Must(icon)).Size(14, 14) + } else { + ui.Text(c, strconv.Itoa(i+1)).FontSize(theme.CaptionSize).SingleLine() + } + }) +} + +// stepsLine joins step i to the next in the horizontal layout. +func stepsLine(c *ui.Context, steps []Step, i, current int) { + k := Tokens(c) + ui.Box(c).Height(2).Grow(1).MinWidth(24).Margin(Density(c).Unit()+11, 0, 0, 0).Radius(1). + Background(stepsLineColor(k, i < current && !steps[i].Error)).Role(ui.RoleNone) +} + +func stepsLineColor(k theme.Tokens, done bool) ui.Color { + if done { + return k.AccentText + } + return k.Border +} diff --git a/steps_test.go b/steps_test.go new file mode 100644 index 0000000..201e346 --- /dev/null +++ b/steps_test.go @@ -0,0 +1,109 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func stepsSample() []Step { + return []Step{ + {Title: "Account", Clickable: true}, + {Title: "Profile", Clickable: true}, + {Title: "Payment"}, + {Title: "Confirm"}, + } +} + +func TestStepsClickable(t *testing.T) { + cur, changes := 2, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if Steps(c, &cur, stepsSample(), StepsOptions{}).Changed() { + changes++ + } + }, 700, 120) + _ = tt.Click("Confirm") + if cur != 2 || changes != 0 { + t.Fatalf("non-clickable step chosen: %d", cur) + } + if err := tt.Click("Account"); err != nil { + t.Fatal(err) + } + if cur != 0 || changes != 1 { + t.Fatalf("click: cur %d changes %d", cur, changes) + } + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyEnter) + if cur != 1 || changes != 2 { + t.Fatalf("keyboard: cur %d changes %d", cur, changes) + } +} + +func TestStepsStatesNamed(t *testing.T) { + cur := 1 + steps := stepsSample() + steps[2].Error = true + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + Steps(c, &cur, steps, StepsOptions{Vertical: true}) + }, 400, 400) + for _, name := range []string{ + "Step 1 of 4: Account, done", + "Step 2 of 4: Profile, current", + "Step 3 of 4: Payment, failed", + "Step 4 of 4: Confirm, waiting", + } { + if _, ok := tt.Find(name); !ok { + t.Errorf("missing %q in %q", name, tt.Texts()) + } + } + // All done: current past the last step. + cur = 4 + tt.Frame() + if _, ok := tt.Find("Step 4 of 4: Confirm, done"); !ok { + t.Errorf("finished steps: %q", tt.Texts()) + } +} + +func TestStepsInvalid(t *testing.T) { + for name, tc := range map[string]struct { + cur int + steps []Step + }{ + "empty": {0, nil}, + "range": {5, stepsSample()}, + "title": {0, []Step{{}}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Steps(c, &tc.cur, tc.steps, StepsOptions{}) + }, 300, 100, 1) + }) + } +} + +func TestStepsSnapshot(t *testing.T) { + snapshot(t, "steps", 640, 380, func(c *ui.Context) { + a, b := 2, 1 + steps := []Step{ + {Title: "Account", Description: "Name and seal"}, + {Title: "Profile", Description: "Titles and house"}, + {Title: "Payment", Description: "Treasury"}, + {Title: "Confirm"}, + } + ui.Column(c).Padding(16).Gap(24).Children(func() { + Steps(c, &a, steps, StepsOptions{}) + v := append([]Step(nil), steps...) + v[2].Error = true + v[2].Description = "The card was declined" + Steps(c, &b, v, StepsOptions{Vertical: true}) + }) + }) +} diff --git a/tabs.go b/tabs.go new file mode 100644 index 0000000..c3b022e --- /dev/null +++ b/tabs.go @@ -0,0 +1,174 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// Tab is one tab of Tabs. +type Tab struct { + // Label is required; it names the tab and its panel. + Label string + Icon *ui.SVG + Disabled bool + // Panel builds the tab's content while it is chosen; nil shows none. + Panel func() +} + +// TabsOptions configure Tabs. +type TabsOptions struct { + // Vertical stacks the tabs at the start with the panel beside them. + Vertical bool + // Label names the tab list for assistive technology. + Label string +} + +// TabsResult is the built Tabs: the element holding tabs and panel. +type TabsResult struct { + *ui.Element + changed bool +} + +// Changed reports a new choice made by the user in this frame. +func (r TabsResult) Changed() bool { return r.changed } + +// Tabs shows tabs choosing *selected and the chosen tab's panel. Disabled tabs are skipped by the arrows. +func Tabs(c *ui.Context, selected *int, tabs []Tab, opts TabsOptions) TabsResult { + if len(tabs) == 0 { + panic("mujica: Tabs needs at least one tab") + } + if *selected < 0 || *selected >= len(tabs) { + panic("mujica: Tabs selected index out of range") + } + if tabs[*selected].Disabled { + panic("mujica: Tabs selected tab is disabled") + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + pad := theme.FocusGap + theme.FocusWidth + // TabsBase only sees enabled tabs, so the arrows skip disabled ones. + var enabled []int + at := 0 + for i, t := range tabs { + if !t.Disabled { + if i == *selected { + at = len(enabled) + } + enabled = append(enabled, i) + } + } + root := ui.Column(c).AlignItems(ui.Stretch) + if opts.Vertical { + root.Row() + } + res := TabsResult{Element: root} + root.Children(func() { + var strip *ui.Element + if opts.Vertical { + strip = ui.Scroll(c).Shrink(0).Padding(pad) + } else { + strip = ui.ScrollHorizontal(c).Shrink(0).MinWidth(0).Padding(pad, pad, 0, pad) + } + strip.Draw(func(p *ui.Painter, r ui.Rect) { + if opts.Vertical { + p.Fill(ui.Rect{X: r.X + r.W - 1, Y: r.Y, W: 1, H: r.H}, k.Border, 0) + } else { + p.Fill(ui.Rect{X: r.X, Y: r.Y + r.H - 1, W: r.W, H: 1}, k.Border, 0) + } + }) + strip.Children(func() { + base := ui.TabsBase(c, &at, len(enabled)) + list := base.List.Gap(u) + if opts.Vertical { + list.Column().AlignItems(ui.Stretch) + } + if opts.Label != "" { + list.Label(opts.Label) + } + built := make([]*ui.Element, len(tabs)) + list.Children(func() { + n := 0 + for i, t := range tabs { + var tab *ui.Element + if t.Disabled { + tab = ui.Row(c).Shrink(0).Role(ui.RoleTab).Disabled(true) + } else { + tab = base.Tab(n) + n++ + } + tabsStyle(c, tab, t, i == enabled[at], opts.Vertical) + built[i] = tab + } + }) + if list.Changed() { + prev := *selected + *selected = enabled[at] + res.changed = true + built[*selected].ScrollIntoView() + slog.Debug("mujica: tabs change", "from", prev, "to", *selected) + } + }) + panel := ui.Column(c).MinWidth(0).Padding(u * 4).Role(ui.RoleGroup).Label(tabs[*selected].Label) + if opts.Vertical { + panel.Grow(1) + } + if p := tabs[*selected].Panel; p != nil { + panel.Children(p) + } + }) + return res +} + +func tabsStyle(c *ui.Context, tab *ui.Element, t Tab, on, vertical bool) { + k := Tokens(c) + d := Density(c) + u := d.Unit() + tab.MinHeight(d.ControlHeight()).Padding(0, u*4).Gap(u * 2).AlignItems(ui.Center).Cursor(ui.CursorPointer) + if vertical { + tab.Radius(theme.ControlRadius, 0, 0, theme.ControlRadius) + } else { + tab.Radius(theme.ControlRadius, theme.ControlRadius, 0, 0) + } + if t.Disabled { + tab.Cursor(ui.CursorNotAllowed) + } + if t.Label == "" { + panic("mujica: Tab needs a Label") + } + fg := k.TextMuted + switch { + case on: + tab.Background(k.Selection).FontWeight(600) + fg = k.Text + case !t.Disabled && tab.Pressed(): + tab.Background(k.Selection) + fg = k.Text + case !t.Disabled && tab.Hovered(): + tab.Background(k.SurfaceHover) + fg = k.Text + } + tab.TextColor(fg) + tab.FocusRing(false).DrawOver(func(p *ui.Painter, r ui.Rect) { + if on { + if vertical { + p.Fill(ui.Rect{X: r.X + r.W - 2, Y: r.Y, W: 2, H: r.H}, k.AccentText, 0) + } else { + p.Fill(ui.Rect{X: r.X, Y: r.Y + r.H - 2, W: r.W, H: 2}, k.AccentText, 0) + } + } + if tab.FocusVisible() { + internal.FocusRing(p, r, theme.ControlRadius, k.Focus) + } + }) + tab.Children(func() { + if t.Icon != nil { + ui.Icon(c, t.Icon).Size(16, 16) + } + ui.Text(c, t.Label).SingleLine() + }) +} diff --git a/tabs_test.go b/tabs_test.go new file mode 100644 index 0000000..f5ba7f2 --- /dev/null +++ b/tabs_test.go @@ -0,0 +1,162 @@ +package mujica + +import ( + "strings" + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +func tabsSample() []Tab { + return []Tab{ + {Label: "General", Panel: func() {}}, + {Label: "Fonts", Disabled: true}, + {Label: "Colors"}, + {Label: "Advanced"}, + } +} + +func TestTabsClickAndPanel(t *testing.T) { + sel, changes := 0, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ts := tabsSample() + ts[0].Panel = func() { ui.Text(c, "General page") } + ts[2].Panel = func() { ui.Text(c, "Colors page") } + if Tabs(c, &sel, ts, TabsOptions{Label: "Settings"}).Changed() { + changes++ + } + }, 500, 200) + if !tt.HasText("General page") || tt.HasText("Colors page") { + t.Fatalf("panel of tab 0 not shown: %q", tt.Texts()) + } + if err := tt.Click("Colors"); err != nil { + t.Fatal(err) + } + if sel != 2 || changes != 1 || !tt.HasText("Colors page") { + t.Fatalf("click Colors: sel %d changes %d texts %q", sel, changes, tt.Texts()) + } + _ = tt.Click("Fonts") + if sel != 2 || changes != 1 { + t.Fatalf("disabled tab chosen: sel %d changes %d", sel, changes) + } +} + +func TestTabsKeyboardSkipsDisabled(t *testing.T) { + for _, vertical := range []bool{false, true} { + sel := 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + Tabs(c, &sel, tabsSample(), TabsOptions{Vertical: vertical}) + }, 500, 300) + tt.Key(0, ui.KeyTab) + if !tt.Focused("General") { + t.Fatalf("vertical=%v: Tab did not focus the chosen tab", vertical) + } + next := ui.KeyRight + if vertical { + next = ui.KeyDown + } + tt.Key(0, next) + if sel != 2 || !tt.Focused("Colors") { + t.Fatalf("vertical=%v: arrow chose %d, want 2 skipping disabled", vertical, sel) + } + tt.Key(0, ui.KeyEnd) + if sel != 3 { + t.Fatalf("vertical=%v: End chose %d", vertical, sel) + } + tt.Key(0, next) + if sel != 0 { + t.Fatalf("vertical=%v: wrap chose %d", vertical, sel) + } + } +} + +func TestTabsOverflowScrolls(t *testing.T) { + sel := 0 + var tabs []Tab + for _, l := range []string{"Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel"} { + tabs = append(tabs, Tab{Label: l}) + } + var w float32 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Width(240).Children(func() { + w = Tabs(c, &sel, tabs, TabsOptions{}).Bounds().W + }) + }, 400, 200) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnd) + tt.Frame() + r, ok := tt.Find("Hotel") + if sel != 7 || !ok || r.X+r.W > 240 { + t.Fatalf("last tab not scrolled into view: sel %d rect %+v", sel, r) + } + if w > 240 { + t.Fatalf("tabs widened their parent to %v", w) + } +} + +func TestTabsInvalid(t *testing.T) { + for name, tc := range map[string]struct { + sel int + tabs []Tab + }{ + "empty": {0, nil}, + "range": {4, tabsSample()}, + "disabled": {1, tabsSample()}, + "label": {0, []Tab{{Icon: icons.Must("star")}}}, + } { + t.Run(name, func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Tabs(c, &tc.sel, tc.tabs, TabsOptions{}) + }, 300, 100, 1) + }) + } +} + +func tabsView(c *ui.Context) { + sel, vsel := 0, 2 + ui.Column(c).Padding(16).Gap(16).Children(func() { + Tabs(c, &sel, []Tab{ + {Label: "Overview", Icon: icons.Must("house"), Panel: func() { + ui.Text(c, "The chosen tab's panel.") + }}, + {Label: "Members", Icon: icons.Must("user")}, + {Label: "Archive", Disabled: true}, + {Label: "Settings", Icon: icons.Must("settings")}, + }, TabsOptions{}) + Tabs(c, &vsel, []Tab{ + {Label: "Profile"}, + {Label: "Security", Disabled: true}, + {Label: "Billing", Panel: func() { ui.Text(c, "Vertical layout.") }}, + }, TabsOptions{Vertical: true}) + }) +} + +func TestTabsSnapshot(t *testing.T) { + snapshot(t, "tabs", 520, 300, tabsView) + for _, d := range []theme.Density{theme.Compact, theme.Comfortable} { + snapshot(t, "tabs-"+strings.ToLower(d.String()), 520, 340, func(c *ui.Context) { + Use(c, Settings{Mode: tabsMode(c), Density: d}) + tabsView(c) + }) + } +} + +// tabsMode recovers the mode snapshot applied, to add a density. +func tabsMode(c *ui.Context) Mode { + if Tokens(c).Dark { + return Dark + } + return Light +} diff --git a/testdata/snapshots/darwin/breadcrumb-dark.png b/testdata/snapshots/darwin/breadcrumb-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad11a0ef4d895df18940fea9339eb476240dc09 GIT binary patch literal 10907 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%0A+YkS)PMelw5 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 fn)?bY3sV>~ literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/breadcrumb-light.png b/testdata/snapshots/darwin/breadcrumb-light.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d94e333ea91e8545abc28ccd33559cdca1e005 GIT binary patch literal 10645 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 U|NmJzJ_32h)78&qol`;+0Is8ZQ2+n{ literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/command_palette-light.png b/testdata/snapshots/darwin/command_palette-light.png new file mode 100644 index 0000000000000000000000000000000000000000..f76ffd4bc1c266059716d00d39950590e5b50d47 GIT binary patch literal 24341 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/menubar-light.png b/testdata/snapshots/darwin/menubar-light.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e0343fbfc088bcb2b037c3d3e3490dd5c3ddda GIT binary patch literal 2661 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/pagination-dark.png b/testdata/snapshots/darwin/pagination-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..25c2935d749d27b37b950a057c84329b3fdd1087 GIT binary patch literal 10720 zcmeHtbx@qaw&x^}K!R&2M`@}n;60~$4gdh~loVyP0f5Js5B)FL zk00uAS7l`YfTKW3R!Y||eSdb?W!#bi>zI{!=m*5CN8jM{hn^LfUr~0`l>>B1$_k5! zz+6l&5Z9OYQo4kvQy|yq~x3l!-MqoJN|nVy_@8E4v<|N{9ar%lrQU zTK%X_gumWoYBWz#Mo?0jW@lu0(^Ir}y;d+DCLB?*L>FE&wadC#y|cS{Zn?l)+>B^E zZOjakSoE*E_wX6HMze~S&ZPQ4!Ct^w;~nm;2D4?0qwChet*V@a#U2fMu>j(!RW*2( zdP!e)0gMl8mL!KH>p-PTP7Ku7jRi%WzFj)*3{FQxU0t!}b2h0he-_O^ z-XyFQ?{{ko@8qLDbJ7TxOI@${a5G5u18&a!AIJLk-#V`zoLxKy*j&j}U3F6jPwj(7Qt?$04Q$g(vp7Dyg4EkYI)M;Qa4z|VeR~W3dCh zYOIDL4<{$X!nbeVVEAh4wedQOx>ra7)h=BNmq>qzo07kBZRjxDf^`C%aeSsX`{&Q~ zTl(6E*_Gd{wZT+zYyxh&iW?vEPv5ZWC*;MW5Bky&92BR((1NO{%OeGpWq55^nSS=h`)V#qe^vfIJdM! zM_5mTMJrBwkSNJx&v}Lc5Ui`Fk?>>{;l{wq@XS2aVO7i4>;;5=SVLB zx3#o%06}5so6P0mI7N< zw8gvnn^65%H^ITba9319;oC_dETmbzG--uCyx?6@NH zx3ma?&qEB#xpJ9wa|}hF{0;Fo_jBlneS!d5U}66M^P13Ui!DS zXuoZLTJ9nLWp$=q$ZoLhL?wh;;1jI^j+X$zG?A>*GNHQ2ywAr#1pfAPCewW@9dO3_ zotGER${sb<&hA@DOZkNX1M`GdbP7MOaD~UgF1?ysOeEMdx~O0Wx+HJf`l~r{ zKtEiRj;`g~*>CU|(PZc6KL&VExKY1$4|rHJA<*7^rdmZ!&Ccc67@qk9a?qUt5|!4! zt+Yj!x?@{ZI4z|+VLbiex=6QrDDuW{?-7VQ=85#2&1+j>km>I=RPbM?pkM7-B4m0l z`cIsli%HN+z1`iFEiFuMxD!0iTEF1o$PEGeMle*B)xmZny)Cc| zQPhW=>t7zeF&f0#st6w#+tIKwXD+ZC{M&6HXB4G}6d@02{nGUm@Pm^2>PCHRYRuKu zIhZKRZ{x^BOu_>E{gkCC^TC7Y=vY+X%+Xc43p}VB>_deQFl-1Qq3biGuf84YvXMyW z0WJG~oso6b9?`cc?ee z6QdUppbv$Lt^mN`FQ*O!a^g`43R^cfH&atnpWE)mp96-y3QAE8fwPPzCMrfiao#q^ z>BD6KbBDF>&8|DrCe&Q--+7?t4v3h-C4;V4dq*_^zs{VNYsW}UW;8zcj(@o3?MXO54*?82WXtyWmn2JY?ImD8Raqdg+l@m`l-sngR@xZU``=9 z9(9o>);p-HpOl$k;PK|Kx)S9cQ#$590DAh2D5GeJgrf3`&KA#eO!sUlm6@nM=I)a! zuhYvwiNlF8O$RT|26V>l^z^Jqy$*Ajf|=eYX&5tWL1GfZU+gZYuwd5z3J`j7ZVY zNB@!ogTcveAGcnRt5K}&tZu&(WEZ-+3MezDy!^z01t0`(ca}D&w7&ECJQMROkeD|Z z^aOBxIrFk5vs_4vN8M9JgdhxGatT~owxV`M1bA5iQ;ml`!~D?BHglU@9;s&#X~sx~ z*LnVi^?Gow;bvrBFwO+5-2DS}cX8+|(8$1|Z)muBAber(=TMxIF#h`eyRXR>ky~Vm zNtMdVqV8;be5m?_LeAg}{9Bj#*sqgZ7`K5f*cQBwixr~5<|q>fX;@KwYO%7pIT0Yc z?L80RB@fRt4fa5y3+3&J2bZcWXH?Yo-fgu+w|$iI$@KX6olkhh<0^~GGAz~n_Togz z`X-S_0R0M6exENUV6eQbZFNeKR`1;q_6N<#>{B*)%{i zc@-IP1`e_Ung!01Vhn?_9*&-_QLEZ}Qoyg4UiL;@(C<=8pDF#$@E?hY0g?p7S$^)s znFE3XgF92QXK0p>t?lKt++Tn|!iHpEkFMrRp$H;ciqWetk+HD%t!Y(jLrnyR8%N%O zZzd)HfXSK+4tz0*wpf!2SnT`Q@KocX*R3D!BiwUf<@%ZnLu%I>CAm_Y`PYrWI(k_E zKv(bkL1|g}Vj~p23EDq(asC^`<|{Rpf@6h1MS*d<;}=cF1*(E2A>@0w9Gu1kwE%!) zamlEit@RoSy+rUe`|j-LOIh_i*>BGRy6P7Kitd8x^#iR>yhE}q9&Dz@zUm7uXmK}> z;?miPaAQ?B$Ve{8NVwxFI=~nZnncBurHJWPTBy%IVYM4|;4(1s*A! z6UC2GeDd0J7*2eJJT z%4+lno9&!iu8Y1ub8^;@VUcB0CE}(Eoop?$HY|3fGnWR3JV?(dC61knNj<$%Zf>{5 zkdF>t`S!Nue^JnRh9n}5oIJ5Kk`J-fVfY_jR^?w!PE0M92n?RlJRb-*gkCOMcIhUH^li71yM_V zO`3WrwZ+9$@V_e9pbmLL#qYue0HYs%11?8@(RV#~!pTJVE(W|q{m8{;J9meEAFt1) zrLVMNhlGeTLx~58JF1pz-6TD(l;5uGy%}|^UK9_DtLJLVrpSUk0%Q-2q5AZ( zKlF~-Eor1xQ9Adnp|rIvNMePfV}qkK{#YYKc6N;HH@g9$mmf*#k38q=%Jp<@cefp_ zlhvq(Oy3%wv#(O>07xIe9f`h<}|2 zpH>vZ7iKRNQ=c@dd-kKY#?;qouH zIe6*mq3^kkMYey+PYNfaFSzWFcGS#tU&<(-lU|h5FC#e z*JJNl>^QoB-zLrwCPov-$a8U$n@M#{b)TU3(k~ui14bqVFag!R-kKaUWJl{L@oxF2 zvm#d_B2TbCIO42jsI^Pc`}>P9b}1w=(>%%7W@jcI8=09PGi;;`Zu|MOAZ*T6y(?8s z^q7Zng{18**~}f^#55Gw8UQFjMq~i#buDe-M%-_nAe z<}28TB0Q$}dB!WDp5VfoA_Rj(%*d=2MKnB7Z?dz%hl&5ZiVb!7S9%35>QqrhCokT0 zrKDldR9~v9BXb*;_^fYir?|iWf+^?0f(y%wiJRRL*E~d(LXq>O9UD_2h$w?^0+>?A zG*rYL3enDStiaV8_Y2s%JuN`iBvkLBCMxA~nSjS;-Kgs3uyNkJ4M*u=UeDHl4 zRx~m^Qy;ROmHeznl*-;+4W_9{s;Bw2j>0x6A$E<6GH8;Z>8vJ#RB_y)qUPi7N_?V51lO(XOdS`l;l{H*F zVQQtK3%=Ky?h2_d!;4Yo=CP}(tE&U0>mkRNee4EXq3*X8;mdq)_yvXeTK|sDYHW}tl0NtEx2<3sw)i<9(v@d- z*P2kMt851ux*W~MUC_@#UF!`LynGIRkB?NfJ%Tsq3 zVtHFqGw@hXFEQlVbD@c8ldH9hX}3kZwhNEZW+Q#_Fntj2-AI6=xPcSL{CLOY;Nay* zypDZ-_jrN7gWc9$&?%kP-KVpY%Sj^1;o1ygqaHWH)>QRwG&ZU{XIz0Gsr1$D<>+E@ zF((1NIO@y;edh55Am&e=mYE5;9<T$+vx0QWLrJ|jVgYaEP zr_2UFC1ym1ju8hTwtQ1Vbxln{3-T2w2RbeCAU(W0KgveT#AN>4_*fK(Vf)?4Rb4g{ zqeMQkLBLRVEpUbaXY=|-U7j%aXZ?Ddz?Mhq%9Jn-j`;lnX%-94CY+s$I*TH>PT+Xn z&Tj^AA7a|h)dZ7i^`TeF(;N;rnS}AYne-f`ybLZxjC|mfDQN;xELJ)89F#ztH>}LpQyY*nlZkx zoscgv`csb*_Kue%ZD{HKLQR{SS>0vPP{w)H!Y~`(3g%m>K<2(MSERTMat5Jakt15SzDf%vm5I1i^G9?#|BZ`+}t~7;4zd z;Mjjh)R*3fs@`&__}2rj3Wm+=zj*v|kUp5bP*@nqwK|`oWxr-$okit{qAbmOVF|cT zZO$AG0tW^R+%n!mw5s!#YD*WIK{xowERmp1;ouo4fZyvxJ)?CIXVx6K=y<uVS(B<`|^|cP?;t-lLNf_xf3f_d^7{%(Sjv&m+-nQw_}p@IVe??{ z?zQ0`h@X#YPcxf;R#ScQgg<2h^h#GpCS#!N$m*VeKKM|Yl8P*$H=tjf4^wJ=VC&ePv9tBO78Y)$I7eX3VPk&iSJqnC zI=zBGj!HFuZA>L6BG~-=W3T37de%mF_~p9tF6d>6HfC(O;yK5h7Ot-e7FjVL|L)-^ z;5z!@hjq6Y^a$VVH>@NR-%RQsB}n%1`F_Age6{U*oyZsA;9w=cdG9m(gh4DtPl8@w4NW-y}Sles^CgBot@w_86AWK z^ZR#%M3gbS%-Bz!Op?TRo-1koXVGTG2L5mJ|BVkjcq9H$&^O*$P4^nEnylG`@Jowg zv-t3Uy`#jr;e92UmdV42P7(>qXg@4;pETk6!DL#lQP73q1uO$ss(|;sx(k$Ar5OXD+CKCD%R%Ty51aaEyg~7xc|9FxV`}M%tNt<*PG#)Ml~H z=Okp?$Y3N}D(op9m7wF8C3F4O-9~2EKNawA2=HG@{09#S4DY+F7U;bbquCnZZm>B+ zJ!NV8(`Qp-6Rk%Lk)fDh6G(=Y)R6@C7rcwIL)`<;3!h6$jixpj9f_WQH?KdFR&h#! zW#Ij+U>{YRf;^u)y12Cq%-=~!vDh5<$K}n&*aiSY@&7MwPJOd&RHzLWxbUKa1LMjm+sXdfC8vJpP8HQFGiQyDEjwQXBo zdwu=*U#dWIyhrxES{!kgjFQLx@DdvgWLjOynrTrW#FQfJ{PW+aGrdO{A!h}9a$42K zWG$ucTc=o0w7*lGpVwjlH)`X~jpvRWtPKL7aD<;==AW8jUh@i*X|yf>5Kg!PshmMe zgkrBNvr4zSiDx~tk~47pkI%Fl>o~#?ZXHBvgNHVEC=*CPmKy8^@S48uk@Y;>5aa#V z=JW_M4r9-~9)%R?Hu`VfNxcupy3W8Cm27KsTWYvJ4^!q>8gig|$mY69^yQpzo|3n# zxTl{Pm3DUrSh$>8`=z|V)hL@5_Xz6TDQAHzEH9{)xt|C;?Y6H9zMZ@`t3if^Xo(?w z_RfPQFUxY$@o!?N@1f(uDXNQ&LGGgNmq{tAr1xGys!1EmYz}^S|I}QO_4dTHf7qk@ z^5T@4(Z4E58^_AFc5nJ|y!W^KV1~^%$MKC#_szP_ueOla#Cv;LMBey`Rm4$lZux#g zGnl~P3$rH9$aXAAt3j&9fUcdN%QuHNip)YI%U%t2B{?l1uyazDFeg(-7=F|3?+$u{ z9Mmc3_h|g@%uFPkM##oiO#i}wzfn>}j5!OVA=cD;h$XGz)@KA#L0aKk?(fzmTh2tJ z>yz77$vPoY%-L6Y0x7n;VFI*Lgx%p0n=i=UfGW1Mv53$YK3k`vl46v?d{dLNOPX6` zn4U~2>6sZidNEO}A@}0sDy9AlJWWGMv;FGGL38J}Kj?a2JxDMEjk!4kW8D1Y;;38Q zysn-}OqYYjCvP)hb&;q}UQFusDK&N#1pI2Mi14OGJ69exnT-Y?)!{8Eg)K&7cG#cl5zi|@=k zR}){_YCcW(x(eZZ_ycL0fBYyR3dB0@1-tr-M8U$;v9U>jG zD=t)RI>wZ9O?j~XO628lqAmrb2uq{E5)_89rS`RzQf)`qZSqyK3xB0&mgm3?H`-!| ze5bkX5lfh1Ac}t-(!^8yHVYD{DbiZ!tT>d{|>`SWs=g-#!BC zh87=+sCTh;eM4?xYHa>MF$R^VS+$O0 z{ASM-ezCRpC2jGOgKau$!#8VdHjvIw?C`zDwGAH{wdT2(esFc3RtqgMFP( zob0-wbaG0<5!wkJi-&^VW8Cyogw=kgdNs0)ENprzX5VBvh@)g}5a&rulZoabmUeDd ze5Cj$%WoZ?Vd~o%sb2`4EKK-+*3&lqyYcB1KOzvdwLC=}1v*5UnYDr!H#pth-1c`} zaUz&54)2}J5W+nHjp`wR;O$M?Igx5F$E^`0EJFuyknVauT6CsQbL`lv7CYV9nUk5E>N4%#IvpO8EUPFfDn2pZdM@(tuPdLuqE{Qi z5JxAcSs=)rnFB3sWewi^eMAs)iDJ{!69pQVeLiO`OHZql|B>B+Nz1zZS}Nih@DraD zTUu=FE2U><7xiRuqLTsfEK0;(o894-?78**P-vq&?y^5QZvGToa$?j6!YEJ`oU`Q! zS!Jn6hFfaVSa^-ESiWBO2HvRCm}Kdb zA)t+_j4)m?GiP^~QOIH)jbmX&S!pT7zyM7~u9azvP-pmiSBl<$RhQNszs_XxVnR@B zE-oER7Y`Ud%i2~OE}(3M-fxJ5Rrj>M7dwqtc|qPxwumpbnNl{t0oNT}B9Q0$N~Dv1 zf$P8N;~jOAHezndR)k`8+EP9=R9+8nciO7x*g*28Uk}MGpIok2ze(lx=W3xDe~l^i z8kgzxgLqMylK7ohQzC&z&`*SeCEex|i$+1e^Zj=Q4)ZsQw2kF)k||Wp!2^wsHe)zD z&lo0ek9= z)4)9b+12kYQGyNFQv-`vbH!+V-b;`_JXwnp!;Y`~)c^}i7ve`F)6DI?XO+s*Q+SSy zLPRV+EiDjub-3NkFKV`UYmv%4Z4)$Sw0ho_9lFleXUqQ(B>hw1?9uS0(%e^Vv4jt9 z!FC#J8$g?l;T?)U@(B3u-x(mA9@CBj*?+wMFRZF?U^a*Q=QMb>u6K?W{7>g*bmtEF zhs4Es{%Qd|0{zfqwT)~v9!GpPX__mr**M82UKi8*@$gITDXjNB>pJF{~!DxlEEoy b##4aRLh2jMzG}^Xh+j!gUAA1>^22`st#o^% literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/pagination-light.png b/testdata/snapshots/darwin/pagination-light.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f1911577c883c5864175d6489405ac03b8e1cd GIT binary patch literal 10327 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 ZK&zW|tpiUI%t literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/segmented_control-dark.png b/testdata/snapshots/darwin/segmented_control-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6d9110c2119f6fd955922c10eaa92379b9fce0fa GIT binary patch literal 10155 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/sidebar-light.png b/testdata/snapshots/darwin/sidebar-light.png new file mode 100644 index 0000000000000000000000000000000000000000..3cd875d47449eecc16b5febe3952252d4d8642aa GIT binary patch literal 15608 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 UYr$Lp__wInvMMjjq)mhV3#8%gJOBUy literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/steps-light.png b/testdata/snapshots/darwin/steps-light.png new file mode 100644 index 0000000000000000000000000000000000000000..73818915f1ab43e07bdd75979d564051e09d14ca GIT binary patch literal 26621 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 ANB{r; literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/tabs-comfortable-light.png b/testdata/snapshots/darwin/tabs-comfortable-light.png new file mode 100644 index 0000000000000000000000000000000000000000..a366b76c6c9e1dd3dcf6f9242276c444a1c10402 GIT binary patch literal 17158 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/tabs-compact-dark.png b/testdata/snapshots/darwin/tabs-compact-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d2f037cc5cf37b2aa283bf28959ac0c509ca926f GIT binary patch literal 17650 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/tabs-light.png b/testdata/snapshots/darwin/tabs-light.png new file mode 100644 index 0000000000000000000000000000000000000000..01b2495bd6af5f52273fa8f04409b5aaa2a63012 GIT binary patch literal 16645 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 Rkx%s~O7iM*)iR*a{{Z#R=h^@O literal 0 HcmV?d00001 diff --git a/toolbar.go b/toolbar.go new file mode 100644 index 0000000..712fefd --- /dev/null +++ b/toolbar.go @@ -0,0 +1,80 @@ +package mujica + +import ( + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// ToolbarOptions configure a Toolbar. +type ToolbarOptions struct { + // Label names the toolbar for assistive technology. + Label string +} + +// Toolbar is a row of actions built by fn, one Tab stop with arrow keys inside. +// Actions that do not fit move, from the last, into an overflow menu. +func Toolbar(c *ui.Context, opts ToolbarOptions, fn func()) *ui.Element { + k := Tokens(c) + tb := ui.Toolbar(c, fn).Background(k.Surface).Border(theme.BorderWidth, k.Border).Radius(theme.CardRadius). + MinHeight(Density(c).ControlHeight() + 8) + if opts.Label != "" { + tb.Label(opts.Label) + } + return tb +} + +// ToolbarButtonOptions configure a ToolbarButton. +type ToolbarButtonOptions struct { + Icon *ui.SVG + // ShowLabel shows the label beside the icon; without an icon it always shows. + ShowLabel bool + Disabled bool +} + +// ToolbarButton is an action of a Toolbar; label names it and its overflow menu item. +func ToolbarButton(c *ui.Context, label string, opts ToolbarButtonOptions) *ui.Element { + if label == "" { + panic("mujica: ToolbarButton needs a label") + } + k := Tokens(c) + d := Density(c) + u := d.Unit() + text := opts.ShowLabel || opts.Icon == nil + b := ui.ButtonBase(c).MinHeight(d.ControlHeight()).MinWidth(d.ControlHeight()).Gap(u * 2). + Radius(theme.ControlRadius).Label(label).Disabled(opts.Disabled).TextColor(k.Text) + if text { + b.Padding(0, u*3) + } + if !text { + b.Tooltip(label) + } + internal.Ring(b, k.Focus, theme.ControlRadius) + switch { + case b.Pressed(): + b.Background(k.Selection) + case b.Hovered(): + b.Background(k.SurfaceHover) + } + b.Children(func() { + if opts.Icon != nil { + ui.Icon(c, opts.Icon).Size(16, 16) + } + if text { + ui.Text(c, label).SingleLine() + } + }) + return b +} + +// ToolbarGroup keeps related actions together; the overflow menu sets groups apart. +func ToolbarGroup(c *ui.Context, fn func()) *ui.Element { + return ui.Row(c).AlignItems(ui.Center).Gap(Density(c).Unit() * 0.5).Shrink(0).Role(ui.RoleGroup).Children(fn) +} + +// ToolbarSeparator draws a thin rule between actions. +func ToolbarSeparator(c *ui.Context) { + ui.Box(c).Size(1, Density(c).ControlHeight()-12).Margin(0, Density(c).Unit()).Shrink(0). + Background(Tokens(c).Ornament).Role(ui.RoleNone) +} diff --git a/toolbar_test.go b/toolbar_test.go new file mode 100644 index 0000000..125e65f --- /dev/null +++ b/toolbar_test.go @@ -0,0 +1,105 @@ +package mujica + +import ( + "slices" + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +type toolbarLog struct{ clicks []string } + +func (l *toolbarLog) view(width float32) func(c *ui.Context) { + return func(c *ui.Context) { + ui.Column(c).Width(width).Children(func() { + Toolbar(c, ToolbarOptions{Label: "Format"}, func() { + ToolbarGroup(c, func() { + for _, n := range []string{"Bold", "Italic", "Underline"} { + if ToolbarButton(c, n, ToolbarButtonOptions{Icon: icons.Must(map[string]string{"Bold": "bold", "Italic": "italic", "Underline": "underline"}[n])}).Clicked() { + l.clicks = append(l.clicks, n) + } + } + }) + ToolbarSeparator(c) + if ToolbarButton(c, "Copy", ToolbarButtonOptions{Icon: icons.Must("copy"), ShowLabel: true}).Clicked() { + l.clicks = append(l.clicks, "Copy") + } + if ToolbarButton(c, "Delete", ToolbarButtonOptions{Icon: icons.Must("trash-2"), Disabled: true}).Clicked() { + l.clicks = append(l.clicks, "Delete") + } + if ToolbarButton(c, "Share", ToolbarButtonOptions{}).Clicked() { + l.clicks = append(l.clicks, "Share") + } + }) + }) + } +} + +func TestToolbarClickAndArrows(t *testing.T) { + l := &toolbarLog{} + tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}); l.view(600)(c) }, 700, 100) + if err := tt.Click("Italic"); err != nil { + t.Fatal(err) + } + tt.Key(0, ui.KeyRight) // from Italic to Underline + tt.Key(0, ui.KeyRight) // Copy + tt.Key(0, ui.KeyRight) // Share, skipping disabled Delete + tt.Key(0, ui.KeyEnter) + tt.Key(0, ui.KeyHome) + tt.Key(0, ui.KeySpace) + _ = tt.Click("Delete") + if want := []string{"Italic", "Share", "Bold"}; !slices.Equal(l.clicks, want) { + t.Fatalf("clicks %q, want %q", l.clicks, want) + } +} + +func TestToolbarOverflow(t *testing.T) { + l := &toolbarLog{} + // The snapshot's layout: MyGo caches the overflow chevrons by position. + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(16).Gap(16).Children(func() { l.view(180)(c) }) + }, 520, 300) + tt.Frame() + if r, ok := tt.Find("Share"); ok && r.W > 0 { + t.Fatalf("Share should overflow, at %+v", r) + } + if err := tt.Click("More"); err != nil { + t.Fatal(err) + } + menu := tt.Menu() + if !slices.Contains(menu, "Share") || !slices.Contains(menu, "Copy") { + t.Fatalf("overflow menu %q", menu) + } + if err := tt.ChooseMenuItem("Share"); err != nil { + t.Fatal(err) + } + tt.Frame() + if !slices.Equal(l.clicks, []string{"Share"}) { + t.Fatalf("overflow choice clicked %q", l.clicks) + } +} + +func TestToolbarButtonNeedsLabel(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + ToolbarButton(c, "", ToolbarButtonOptions{Icon: icons.Must("copy")}) + }, 200, 100, 1) +} + +func TestToolbarSnapshot(t *testing.T) { + l := &toolbarLog{} + snapshot(t, "toolbar", 520, 140, func(c *ui.Context) { + ui.Column(c).Padding(16).Gap(16).Children(func() { + l.view(180)(c) + l.view(420)(c) + }) + }) +}