Skip to content

Commit 0f1dccd

Browse files
authored
Merge pull request #5201 from andydotxyz/feature/partialcheck
Add `Partial` mode to `Check` so we can indicate the indeterminate state
2 parents bfa7219 + 998c0eb commit 0f1dccd

File tree

7 files changed

+64
-4
lines changed

7 files changed

+64
-4
lines changed

cmd/fyne_demo/tutorials/widget.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ func makeInputTab(_ fyne.Window) fyne.CanvasObject {
378378
dateEntry.PlaceHolder = "Choose a date"
379379
disabledCheck := widget.NewCheck("Disabled check", func(bool) {})
380380
disabledCheck.Disable()
381+
partialCheck := widget.NewCheck("Partial check", func(bool) {})
382+
partialCheck.Partial = true
381383
checkGroup := widget.NewCheckGroup([]string{"CheckGroup Item 1", "CheckGroup Item 2"}, func(s []string) { fmt.Println("selected", s) })
382384
checkGroup.Horizontal = true
383385
radio := widget.NewRadioGroup([]string{"Radio Item 1", "Radio Item 2"}, func(s string) { fmt.Println("selected", s) })
@@ -392,10 +394,10 @@ func makeInputTab(_ fyne.Window) fyne.CanvasObject {
392394
selectEntry,
393395
dateEntry,
394396
widget.NewCheck("Check", func(on bool) { fmt.Println("checked", on) }),
395-
disabledCheck,
397+
partialCheck,
396398
checkGroup,
397399
radio,
398-
disabledRadio,
400+
container.NewGridWithColumns(2, disabledCheck, disabledRadio),
399401
container.NewBorder(nil, nil, widget.NewLabel("Slider"), nil, widget.NewSlider(0, 1000)),
400402
container.NewBorder(nil, nil, widget.NewLabel("Disabled slider"), nil, disabledSlider),
401403
)

theme/bundled-icons.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

theme/gen.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func main() {
9999
bundleIcon("check-box", f)
100100
bundleIcon("check-box-checked", f)
101101
bundleIcon("check-box-fill", f)
102+
bundleIcon("check-box-partial", f)
102103
bundleIcon("radio-button", f)
103104
bundleIcon("radio-button-checked", f)
104105
bundleIcon("radio-button-fill", f)

theme/icons.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ const (
5656
// Since: 2.5
5757
IconNameCheckButtonFill fyne.ThemeIconName = "iconNameCheckButtonFill"
5858

59+
// IconNameCheckButtonPartial is the name of theme lookup for "partially" checked check button icon.
60+
//
61+
// Since: 2.6
62+
IconNameCheckButtonPartial fyne.ThemeIconName = "partial"
63+
5964
// IconNameRadioButton is the name of theme lookup for radio button unchecked icon.
6065
//
6166
// Since: 2.0
@@ -510,6 +515,7 @@ var (
510515
IconNameCheckButton: NewThemedResource(checkboxIconRes),
511516
IconNameCheckButtonChecked: NewThemedResource(checkboxcheckedIconRes),
512517
IconNameCheckButtonFill: NewThemedResource(checkboxfillIconRes),
518+
IconNameCheckButtonPartial: NewThemedResource(checkboxpartialIconRes),
513519
IconNameRadioButton: NewThemedResource(radiobuttonIconRes),
514520
IconNameRadioButtonChecked: NewThemedResource(radiobuttoncheckedIconRes),
515521
IconNameRadioButtonFill: NewThemedResource(radiobuttonfillIconRes),

theme/icons/check-box-partial.svg

Lines changed: 9 additions & 0 deletions
Loading

widget/check.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ type Check struct {
1818
Text string
1919
Checked bool
2020

21+
// Partial check is when there is an indeterminate state (usually meaning that child items are some-what checked).
22+
// Turning this on will override the checked state and show a dash icon (neither checked nor unchecked).
23+
// The user interaction cannot turn this on, tapping a partial check state will set `Checked` to true.
24+
//
25+
// Since: 2.6
26+
Partial bool
27+
2128
OnChanged func(bool) `json:"-"`
2229

2330
focused bool
@@ -64,13 +71,15 @@ func (c *Check) Bind(data binding.Bool) {
6471
}
6572

6673
// SetChecked sets the checked state and refreshes widget
74+
// If the `Partial` state is set this will be turned off to respect the `checked` bool passed in here.
6775
func (c *Check) SetChecked(checked bool) {
6876
c.propertyLock.Lock()
69-
if checked == c.Checked {
77+
if checked == c.Checked && !c.Partial {
7078
c.propertyLock.Unlock()
7179
return
7280
}
7381

82+
c.Partial = false
7483
c.Checked = checked
7584
onChanged := c.OnChanged
7685
c.propertyLock.Unlock()
@@ -350,7 +359,11 @@ func (c *checkRenderer) updateResource(th fyne.Theme) {
350359
bgRes := theme.NewThemedResource(th.Icon(theme.IconNameCheckButtonFill))
351360
bgRes.ColorName = theme.ColorNameInputBackground
352361

353-
if c.check.Checked {
362+
if c.check.Partial {
363+
res = theme.NewThemedResource(th.Icon(theme.IconNameCheckButtonPartial))
364+
res.ColorName = theme.ColorNamePrimary
365+
bgRes.ColorName = theme.ColorNameBackground
366+
} else if c.check.Checked {
354367
res = theme.NewThemedResource(th.Icon(theme.IconNameCheckButtonChecked))
355368
res.ColorName = theme.ColorNamePrimary
356369
bgRes.ColorName = theme.ColorNameBackground
@@ -380,6 +393,10 @@ func (c *checkRenderer) updateFocusIndicator(th fyne.Theme, v fyne.ThemeVariant)
380393
}
381394

382395
func focusIfNotMobile(w fyne.Widget) {
396+
if w == nil {
397+
return
398+
}
399+
383400
if !fyne.CurrentDevice().IsMobile() {
384401
if c := fyne.CurrentApp().Driver().CanvasForObject(w); c != nil {
385402
c.Focus(w.(fyne.Focusable))

widget/check_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,23 @@ func TestCheck_SetText(t *testing.T) {
100100

101101
assert.Equal(t, "New", check.Text)
102102
}
103+
104+
func TestCheck_Tapped(t *testing.T) {
105+
check := &widget.Check{Text: "test"}
106+
assert.False(t, check.Checked)
107+
108+
test.Tap(check)
109+
assert.True(t, check.Checked)
110+
test.Tap(check)
111+
assert.False(t, check.Checked)
112+
113+
// and test the resetting from partial as well
114+
check.Partial = true
115+
test.Tap(check)
116+
assert.True(t, check.Checked)
117+
assert.False(t, check.Partial)
118+
test.Tap(check)
119+
assert.False(t, check.Checked)
120+
assert.False(t, check.Partial)
121+
122+
}

0 commit comments

Comments
 (0)