Refactor: a disabled wrapper alone blocks choice bases
TestBasesBlockedByDisabledWrapper focuses CheckboxBase, RadioBase, SwitchBase and ToggleBase, flips their wrapper to Disabled and presses Space: none changes its value, and each does without the flip. So choiceTarget goes from Switch, Toggle and MultiSelect, and PopupMenu Check drops its copy; TestChoiceDisabledWhileFocused and TestDropdownMenuCheckDisabledThisFrameKeepsValue still pass. ComboboxBase is different: TestComboboxBaseIgnoresDisabledWrapper shows a focused one still takes typing under a disabled wrapper, so Combobox keeps a copy, now inline as its only user.
This commit is contained in:
6 files changed
+70
-22
No files matched your search
@@ -0,0 +1,56 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
)
|
||||
|
||||
// TestBasesBlockedByDisabledWrapper flips a focused MyGo base's wrapper to Disabled in the frame Space arrives.
|
||||
func TestBasesBlockedByDisabledWrapper(t *testing.T) {
|
||||
for name, base := range map[string]func(c *ui.Context, on *bool) *ui.Element{
|
||||
"CheckboxBase": func(c *ui.Context, on *bool) *ui.Element { return ui.CheckboxBase(c, on) },
|
||||
"RadioBase": func(c *ui.Context, on *bool) *ui.Element { return ui.RadioBase(c, on, true) },
|
||||
"SwitchBase": func(c *ui.Context, on *bool) *ui.Element { return ui.SwitchBase(c, on) },
|
||||
"ToggleBase": func(c *ui.Context, on *bool) *ui.Element { return ui.ToggleBase(c, on) },
|
||||
} {
|
||||
for _, disable := range []bool{false, true} {
|
||||
on, off := false, false
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
ui.Row(c).Disabled(off).Children(func() {
|
||||
base(c, &on).Label("Target").Size(40, 20)
|
||||
})
|
||||
}, 200, 100)
|
||||
tt.Key(0, ui.KeyTab)
|
||||
if !tt.Focused("Target") {
|
||||
t.Fatalf("%s: not focused", name)
|
||||
}
|
||||
off = disable
|
||||
tt.Key(0, ui.KeySpace)
|
||||
tt.Frame()
|
||||
if on == disable {
|
||||
t.Errorf("%s: disabled=%v, Space left the value %v", name, disable, on)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestComboboxBaseIgnoresDisabledWrapper shows why Combobox hands its base a copy: a focused one still takes typing.
|
||||
func TestComboboxBaseIgnoresDisabledWrapper(t *testing.T) {
|
||||
q, off := "", false
|
||||
tt := ui.NewTester(func(c *ui.Context) {
|
||||
ui.Row(c).Disabled(off).Children(func() {
|
||||
ui.ComboboxBase(c, &q).Input.Label("Target").Width(120)
|
||||
})
|
||||
}, 200, 100)
|
||||
tt.Key(0, ui.KeyTab)
|
||||
if !tt.Focused("Target") {
|
||||
t.Fatal("not focused")
|
||||
}
|
||||
off = true
|
||||
tt.Type("a")
|
||||
tt.Frame()
|
||||
if q != "a" {
|
||||
t.Fatalf("typing left %q; the wrapper now blocks it and Combobox can drop its copy", q)
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -55,8 +55,14 @@ func Combobox(c *ui.Context, value *string, options []string, opts ComboboxOptio
|
||||
refocus := ui.Local(f, comboboxKey{"refocus"}, func() bool { return false })
|
||||
var p *ui.ComboboxParts
|
||||
f.Children(func() {
|
||||
// A focused ComboboxBase takes typing under a disabled wrapper, so it edits a copy.
|
||||
target := query
|
||||
if opts.Disabled {
|
||||
cp := *query
|
||||
target = &cp
|
||||
}
|
||||
ui.Row(c).Key(*gen).Grow(1).Children(func() {
|
||||
p = ui.ComboboxBase(c, choiceTarget(query, opts.Disabled))
|
||||
p = ui.ComboboxBase(c, target)
|
||||
})
|
||||
if *refocus {
|
||||
p.Input.Focus()
|
||||
|
||||
+2
-2
@@ -180,9 +180,9 @@ func multiSelectPopup[T comparable](c *ui.Context, k theme.Tokens, values *[]T,
|
||||
func multiSelectCheck(c *ui.Context, k theme.Tokens, on *bool, st internal.CheckState, label string, disabled bool) *ui.Element {
|
||||
d := Density(c)
|
||||
var cb *ui.Element
|
||||
// A disabled wrapper and a copy block the base, which toggles as it is built.
|
||||
// A disabled wrapper blocks the base, which toggles as it is built.
|
||||
ui.Row(c).Grow(1).Disabled(disabled).Children(func() {
|
||||
cb = ui.CheckboxBase(c, choiceTarget(on, disabled)).MinHeight(d.ControlHeight()).Padding(0, d.Unit()*2).
|
||||
cb = ui.CheckboxBase(c, on).MinHeight(d.ControlHeight()).Padding(0, d.Unit()*2).
|
||||
Gap(d.Unit() * 2).AlignItems(ui.Center).Radius(theme.ControlRadius).Grow(1)
|
||||
})
|
||||
if st == internal.Mixed {
|
||||
|
||||
+2
-7
@@ -56,14 +56,9 @@ func (m *PopupMenu) Item(label string, opts PopupMenuItemOptions) bool {
|
||||
|
||||
// Check adds an item toggling *checked and reports whether it changed.
|
||||
func (m *PopupMenu) Check(label string, checked *bool, opts PopupMenuItemOptions) bool {
|
||||
// The base toggles as it is built, so a disabled wrapper and a copy come first.
|
||||
value := checked
|
||||
if opts.Disabled {
|
||||
cp := *checked
|
||||
value = &cp
|
||||
}
|
||||
// The base toggles as it is built, so a disabled wrapper comes first.
|
||||
var base *ui.Element
|
||||
ui.Column(m.c).Shrink(0).Disabled(opts.Disabled).Children(func() { base = ui.CheckboxBase(m.c, value) })
|
||||
ui.Column(m.c).Shrink(0).Disabled(opts.Disabled).Children(func() { base = ui.CheckboxBase(m.c, checked) })
|
||||
it := m.row(base, opts.Disabled, false)
|
||||
m.content(it, label, *checked, false)
|
||||
if opts.Disabled || !it.Changed() {
|
||||
|
||||
@@ -30,9 +30,9 @@ func Switch(c *ui.Context, on *bool, label string, opts SwitchOptions) *ui.Eleme
|
||||
d := Density(c)
|
||||
blocked := opts.Disabled || opts.Loading
|
||||
var sw *ui.Element
|
||||
// Disabled on a wrapper, and a copy, block the base before it takes input.
|
||||
// Disabled on a wrapper blocks the base before it takes input.
|
||||
ui.Row(c).Shrink(0).Disabled(blocked).Children(func() {
|
||||
sw = ui.SwitchBase(c, choiceTarget(on, blocked)).Gap(d.Unit() * 2).AlignItems(ui.Center).
|
||||
sw = ui.SwitchBase(c, on).Gap(d.Unit() * 2).AlignItems(ui.Center).
|
||||
MinHeight(d.ControlHeight()).FocusRing(false)
|
||||
})
|
||||
if opts.Description != "" {
|
||||
@@ -82,15 +82,6 @@ func Switch(c *ui.Context, on *bool, label string, opts SwitchOptions) *ui.Eleme
|
||||
return sw
|
||||
}
|
||||
|
||||
// choiceTarget is v, or a throwaway copy of it while input is blocked.
|
||||
func choiceTarget[T any](v *T, blocked bool) *T {
|
||||
if !blocked {
|
||||
return v
|
||||
}
|
||||
cp := *v
|
||||
return &cp
|
||||
}
|
||||
|
||||
func switchFloat(b bool) float32 {
|
||||
if b {
|
||||
return 1
|
||||
|
||||
@@ -25,7 +25,7 @@ func Toggle(c *ui.Context, on *bool, label string, opts ToggleOptions) *ui.Eleme
|
||||
}
|
||||
var b *ui.Element
|
||||
ui.Row(c).Shrink(0).Disabled(opts.Disabled).Children(func() {
|
||||
b = ui.ToggleBase(c, choiceTarget(on, opts.Disabled))
|
||||
b = ui.ToggleBase(c, on)
|
||||
})
|
||||
if b.Changed() {
|
||||
slog.Debug("mujica: toggle changed", "label", label+opts.Label, "on", *on)
|
||||
|
||||
Reference in new issue
Block a user