diff --git a/docs/components/003-status-bar.md b/docs/components/003-status-bar.md index 620b910..2610f7a 100644 --- a/docs/components/003-status-bar.md +++ b/docs/components/003-status-bar.md @@ -47,6 +47,7 @@ if bar.Clicked == "bell" { - 操作条目:悬停 SurfaceHover,按下 Selection,键盘焦点环。 - 折叠:按“右组末项 → 左组首项”的顺序隐藏 `Secondary` 条目,直到放得下(按实际宽度计算,操作条目至少 28 DIP,含条目间距);显示 `+N` 按钮,无障碍名称为“另有 N 项”,点击或 Enter/Space 打开弹层,列出被隐藏的条目,其中的操作照常可用,执行后弹层关闭。主要条目永不隐藏。 - 文本放大时状态栏随文字增高。 +- 焦点所在条目被折叠时,焦点移到收纳它的 `+N` 按钮上,不会丢失。 - 进度:AccentText 填充,Border 轨道,描述为百分比。 ## 事件 diff --git a/status_bar.go b/status_bar.go index 5786410..8e9d1a2 100644 --- a/status_bar.go +++ b/status_bar.go @@ -70,19 +70,25 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult { hidden := statusBarHide(c, opts, was.W-d.Unit()*4, gap) open := ui.Local(e, "status_bar.open", func() bool { return false }) *open = *open && len(hidden) > 0 + // focused is the bar item that had the focus last frame, for moving it to "+N" when that item folds. + focused := ui.Local(e, "status_bar.focused", func() string { return "" }) + wasFocused := *focused + *focused = "" var res StatusBarResult - item := func(it StatusItem) { - if statusBarItem(c, it) { + item := func(it StatusItem) bool { + clicked, hasFocus := statusBarItem(c, it) + if clicked { res.Clicked = it.ID *open = false slog.Debug("mujica: status_bar clicked", "id", it.ID) } + return hasFocus } group := func(key string, items []StatusItem) { ui.Row(c).Key(key).Gap(gap).Shrink(0).Dividers(theme.BorderWidth, k.Border).Children(func() { for _, it := range items { - if !hidden[it.ID] { - item(it) + if !hidden[it.ID] && item(it) { + *focused = it.ID } } }) @@ -92,6 +98,10 @@ func StatusBar(c *ui.Context, opts StatusBarOptions) StatusBarResult { ui.Spacer(c).Key("status_bar.spacer") if len(hidden) > 0 { more := statusBarMore(c, len(hidden)) + if wasFocused != "" && hidden[wasFocused] { + more.Focus() + slog.Debug("mujica: status_bar focus folded", "id", wasFocused) + } if more.Clicked() { *open = !*open slog.Debug("mujica: status_bar hidden items", "open", *open) @@ -217,8 +227,8 @@ func statusBarWidth(c *ui.Context, it StatusItem) float32 { return w } -// statusBarItem builds one item and reports whether its action was clicked. -func statusBarItem(c *ui.Context, it StatusItem) bool { +// statusBarItem builds one item and reports whether its action was clicked and whether it has the focus. +func statusBarItem(c *ui.Context, it StatusItem) (clicked, focused bool) { k := Tokens(c) d := Density(c) fg := k.TextMuted @@ -265,7 +275,7 @@ func statusBarItem(c *ui.Context, it StatusItem) bool { statusBarProgress(c, it.Progress) } }) - return it.Action && e.Clicked() + return it.Action && e.Clicked(), e.FocusWithin() } func statusBarProgress(c *ui.Context, v float32) { diff --git a/status_bar_test.go b/status_bar_test.go index 6c2fc4e..541f099 100644 --- a/status_bar_test.go +++ b/status_bar_test.go @@ -286,3 +286,39 @@ func TestStatusBarKeepsFocusAcrossDensity(t *testing.T) { t.Fatalf("Enter after the switch activated %d times, want 1", n) } } + +func TestStatusBarFocusMovesToMoreWhenFocusedItemFolds(t *testing.T) { + density := theme.Default + var clicked []string + o := StatusBarOptions{ + Left: []StatusItem{{ID: "main", Text: "main"}, {ID: "fmt", Text: "Format", Action: true, Secondary: true}}, + Right: []StatusItem{{ID: "bell", Label: "Notifications", Icon: icons.Must("bell"), Action: true}}, + } + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{Density: density}) + if id := StatusBar(c, o).Clicked; id != "" { + clicked = append(clicked, id) + } + }, 152, 300) + tt.Key(0, ui.KeyTab) + if !tt.Focused("Format") { + t.Fatal("Tab did not focus Format at Default density") + } + density = theme.Comfortable + tt.Frame() + if tt.HasText("Format") { + t.Fatal("Format did not fold at Comfortable density") + } + if !tt.Focused("1 more items") { + t.Fatal("focus did not move to the +N button that holds Format") + } + tt.Key(0, ui.KeyEnter) + if !tt.HasText("Format") { + t.Fatal("Enter on +N did not open the list with Format") + } + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnter) + if len(clicked) != 1 || clicked[0] != "fmt" { + t.Fatalf("clicked %v, want [fmt]", clicked) + } +}