From 38bbeac3a09e45ccaa850a45372424576a95419a Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 9 Sep 2024 06:43:31 -0400 Subject: [PATCH 01/15] Add simple TwoStateToolbarAction widget Displayed icon in widget alternates between 2 different icons. Simple demo app provided. More functionality will be added. --- cmd/twostatetoolbaraction_demo/main.go | 27 +++++++++++ widget/twostatetoolbaraction.go | 63 ++++++++++++++++++++++++++ widget/twostatetoolbaraction_test.go | 17 +++++++ 3 files changed, 107 insertions(+) create mode 100644 cmd/twostatetoolbaraction_demo/main.go create mode 100644 widget/twostatetoolbaraction.go create mode 100644 widget/twostatetoolbaraction_test.go diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go new file mode 100644 index 0000000..ef3c1ce --- /dev/null +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + xwidget "fyne.io/x/fyne/widget" +) + +func main() { + a := app.New() + w := a.NewWindow("Two State Demo") + + twoState0 := xwidget.NewTwoStateToolbarAction(theme.MediaPlayIcon(), + theme.MediaPauseIcon(), func(state xwidget.TwoStateState) { + fmt.Println(state) + }) + sep := widget.NewToolbarSeparator() + ta := widget.NewToolbarAction(theme.MediaPhotoIcon(), nil) + tb := widget.NewToolbar(twoState0, sep, ta) + c := container.NewBorder(tb, nil, nil, nil) + w.SetContent(c) + w.ShowAndRun() +} diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go new file mode 100644 index 0000000..e9cd091 --- /dev/null +++ b/widget/twostatetoolbaraction.go @@ -0,0 +1,63 @@ +package widget + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/widget" +) + +// TwoStateState defines the type for the state of a TwoStateToolbarAction. +type TwoStateState bool + +const ( + TwoState0 TwoStateState = false + TwoState1 TwoStateState = true +) + +// TwoStateToolbarAction is a push button style of ToolbarItem that displays a different +// icon depending on its state +type TwoStateToolbarAction struct { + State TwoStateState + Icon0 fyne.Resource + Icon1 fyne.Resource + OnActivated func(TwoStateState) `json:"-"` + + button widget.Button +} + +// NewTwoStateToolbarAction returns a new push button style of Toolbar item that displays +// a different icon for each of its two states +func NewTwoStateToolbarAction(icon0 fyne.Resource, + icon1 fyne.Resource, + onTapped func(TwoStateState)) *TwoStateToolbarAction { + t := &TwoStateToolbarAction{Icon0: icon0, Icon1: icon1, OnActivated: onTapped} + t.button.SetIcon(t.Icon0) + t.button.OnTapped = t.activated + return t +} + +func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { + t.button.Importance = widget.LowImportance + + // synchronize properties + if t.State == TwoState0 { + t.button.Icon = t.Icon0 + } else { + t.button.Icon = t.Icon1 + } + t.button.OnTapped = t.activated + return &t.button +} + +func (t *TwoStateToolbarAction) activated() { + if t.State == TwoState0 { + t.State = TwoState1 + t.button.Icon = t.Icon1 + } else { + t.State = TwoState0 + t.button.Icon = t.Icon0 + } + if t.OnActivated != nil { + t.OnActivated(t.State) + } + t.button.Refresh() +} diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go new file mode 100644 index 0000000..7b4f871 --- /dev/null +++ b/widget/twostatetoolbaraction_test.go @@ -0,0 +1,17 @@ +package widget + +import ( + "testing" + + "fyne.io/fyne/v2/theme" + "github.com/stretchr/testify/assert" +) + +func TestNewTwoStateToolbarAction(t *testing.T) { + action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), + theme.MediaPauseIcon(), + func(_ TwoStateState) {}) + assert.Equal(t, theme.MediaPlayIcon().Name(), action.Icon0.Name()) + assert.Equal(t, theme.MediaPauseIcon().Name(), action.Icon1.Name()) + assert.Equal(t, action.Icon0.Name(), action.button.Icon.Name()) +} From d3f7596d148824457e9cda325f4671d282d9103f Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 9 Sep 2024 09:04:49 -0400 Subject: [PATCH 02/15] Add more tests --- .../testdata/twostatetoolbaraction/state0.png | Bin 0 -> 307 bytes .../testdata/twostatetoolbaraction/state1.png | Bin 0 -> 203 bytes widget/twostatetoolbaraction.go | 1 + widget/twostatetoolbaraction_test.go | 25 ++++++++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 widget/testdata/twostatetoolbaraction/state0.png create mode 100644 widget/testdata/twostatetoolbaraction/state1.png diff --git a/widget/testdata/twostatetoolbaraction/state0.png b/widget/testdata/twostatetoolbaraction/state0.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e628709f2cc29c118673d7b1590ed20e2461d7 GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u!1SBUuI~xY1etEh$hE&XXb6v5o)j*)(VPd63 zzyeE#RT(UNheZow8krR?aPTOuJeaV9QLMey_<^B?Q-q^hdITku4P0F$+5$xcm2J+iUbRZc`+Dg~f&4<5@3r%ntxS|i z;b16`S+3EwYhCSqb>}^tic6+MPMf*RvyFk_^rlZX?|12OFd4P@&CloEwJuwttx)Fu z?`4i_Uf(h~yYGG0@lwI(m0p_5GppvgS9XXc&3nErxBu}4NhXH2Tjl{AiMPLP(wX)8 zzDR!3?QQGV)c+Rup8|4_TSCtPhzq|-Gcf%BuWN11r+7`m4j2jyp00i_>zopr07$)d Aw*UYD literal 0 HcmV?d00001 diff --git a/widget/testdata/twostatetoolbaraction/state1.png b/widget/testdata/twostatetoolbaraction/state1.png new file mode 100644 index 0000000000000000000000000000000000000000..600e59df624c1459d40acba04390cdbcc16b95c0 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u!1SBUuI~xY1CV9FzhE&XXbA2P{5e131#P=T0 zxz0Ug(Mx)1_s#E#z`F&1MgP|pZ9S@b^mmB7Jxd%1OVQFPckbRRXSYzQ6Hs*F5Oit* zQYT73_++1TTf6o9yE)y4(}Ld2DE{Z}!f~+edtHC6&4-Tbjv!SWf<>Q>$|T>2$i6wF q_}$}1@&AkBVJ3hKssTFe|9{q(VvldA-Qxwij=|H_&t;ucLK6V}P*5uX literal 0 HcmV?d00001 diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index e9cd091..819b3e9 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -35,6 +35,7 @@ func NewTwoStateToolbarAction(icon0 fyne.Resource, return t } +// ToolbarObject gets a button to render this ToolbarAction func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { t.button.Importance = widget.LowImportance diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 7b4f871..4ae9938 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -3,8 +3,11 @@ package widget import ( "testing" + "fyne.io/fyne/v2/test" "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewTwoStateToolbarAction(t *testing.T) { @@ -15,3 +18,25 @@ func TestNewTwoStateToolbarAction(t *testing.T) { assert.Equal(t, theme.MediaPauseIcon().Name(), action.Icon1.Name()) assert.Equal(t, action.Icon0.Name(), action.button.Icon.Name()) } + +func TestTwoStateToolbarAction_Activated(t *testing.T) { + action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), + theme.MediaPauseIcon(), + func(_ TwoStateState) {}) + require.Equal(t, action.Icon0.Name(), action.button.Icon.Name()) + action.button.Tapped(nil) + assert.Equal(t, action.Icon1.Name(), action.button.Icon.Name()) +} + +func TestTwoStateToolbarAction_Tapped(t *testing.T) { + test.NewApp() + action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), + theme.MediaPauseIcon(), + func(_ TwoStateState) {}) + tb := widget.NewToolbar(action) + w := test.NewWindow(tb) + defer w.Close() + test.AssertRendersToImage(t, "twostatetoolbaraction/state0.png", w.Canvas()) + action.button.Tapped(nil) + test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) +} From 10044a699e30ecf31a79dd7635892f4b563ca5cf Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 9 Sep 2024 09:36:04 -0400 Subject: [PATCH 03/15] Add GetState and SetState methods --- cmd/twostatetoolbaraction_demo/main.go | 7 +++++- widget/twostatetoolbaraction.go | 31 +++++++++++++++++++++----- widget/twostatetoolbaraction_test.go | 18 +++++++++++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go index ef3c1ce..97afce6 100644 --- a/cmd/twostatetoolbaraction_demo/main.go +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -21,7 +21,12 @@ func main() { sep := widget.NewToolbarSeparator() ta := widget.NewToolbarAction(theme.MediaPhotoIcon(), nil) tb := widget.NewToolbar(twoState0, sep, ta) - c := container.NewBorder(tb, nil, nil, nil) + + toggleButton := widget.NewButton("Toggle State", func() { + state := twoState0.GetState() + twoState0.SetState(!state) + }) + c := container.NewBorder(tb, toggleButton, nil, nil) w.SetContent(c) w.ShowAndRun() } diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 819b3e9..67bdeda 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -16,7 +16,7 @@ const ( // TwoStateToolbarAction is a push button style of ToolbarItem that displays a different // icon depending on its state type TwoStateToolbarAction struct { - State TwoStateState + state TwoStateState Icon0 fyne.Resource Icon1 fyne.Resource OnActivated func(TwoStateState) `json:"-"` @@ -35,12 +35,31 @@ func NewTwoStateToolbarAction(icon0 fyne.Resource, return t } +// GetState returns the current state of the toolbaraction +func (t *TwoStateToolbarAction) GetState() TwoStateState { + return t.state +} + +// SetState sets the state of the toolbaraction +func (t *TwoStateToolbarAction) SetState(state TwoStateState) { + t.state = state + if t.OnActivated != nil { + t.OnActivated(t.state) + } + if t.state == TwoState0 { + t.button.Icon = t.Icon0 + } else { + t.button.Icon = t.Icon1 + } + t.button.Refresh() +} + // ToolbarObject gets a button to render this ToolbarAction func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { t.button.Importance = widget.LowImportance // synchronize properties - if t.State == TwoState0 { + if t.state == TwoState0 { t.button.Icon = t.Icon0 } else { t.button.Icon = t.Icon1 @@ -50,15 +69,15 @@ func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { } func (t *TwoStateToolbarAction) activated() { - if t.State == TwoState0 { - t.State = TwoState1 + if t.state == TwoState0 { + t.state = TwoState1 t.button.Icon = t.Icon1 } else { - t.State = TwoState0 + t.state = TwoState0 t.button.Icon = t.Icon0 } if t.OnActivated != nil { - t.OnActivated(t.State) + t.OnActivated(t.state) } t.button.Refresh() } diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 4ae9938..335ffc0 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -40,3 +40,21 @@ func TestTwoStateToolbarAction_Tapped(t *testing.T) { action.button.Tapped(nil) test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) } + +func TestTwoStateToolbarAction_GetSetState(t *testing.T) { + var ts TwoStateState + test.NewApp() + action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), + theme.MediaPauseIcon(), + func(state TwoStateState) { + ts = state + }) + tb := widget.NewToolbar(action) + w := test.NewWindow(tb) + defer w.Close() + assert.Equal(t, TwoState0, action.GetState()) + action.SetState(TwoState1) + assert.Equal(t, TwoState1, action.GetState()) + assert.Equal(t, TwoState1, ts) + test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) +} From d7649b0dd8f202107dd928fe01af1d8b39251665 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 9 Sep 2024 09:41:41 -0400 Subject: [PATCH 04/15] Extract common set button icon code --- widget/twostatetoolbaraction.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 67bdeda..b423072 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -46,11 +46,7 @@ func (t *TwoStateToolbarAction) SetState(state TwoStateState) { if t.OnActivated != nil { t.OnActivated(t.state) } - if t.state == TwoState0 { - t.button.Icon = t.Icon0 - } else { - t.button.Icon = t.Icon1 - } + t.setButtonIcon() t.button.Refresh() } @@ -59,11 +55,7 @@ func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { t.button.Importance = widget.LowImportance // synchronize properties - if t.state == TwoState0 { - t.button.Icon = t.Icon0 - } else { - t.button.Icon = t.Icon1 - } + t.setButtonIcon() t.button.OnTapped = t.activated return &t.button } @@ -71,13 +63,20 @@ func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { func (t *TwoStateToolbarAction) activated() { if t.state == TwoState0 { t.state = TwoState1 - t.button.Icon = t.Icon1 } else { t.state = TwoState0 - t.button.Icon = t.Icon0 } + t.setButtonIcon() if t.OnActivated != nil { t.OnActivated(t.state) } t.button.Refresh() } + +func (t *TwoStateToolbarAction) setButtonIcon() { + if t.state == TwoState0 { + t.button.Icon = t.Icon0 + } else { + t.button.Icon = t.Icon1 + } +} From 9fb3ee2e26c878903ad20fc182dacf3cca3216f2 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 9 Sep 2024 09:44:59 -0400 Subject: [PATCH 05/15] Rename NewTwoStateToolbarAction params Give names that better describe what they are. --- widget/twostatetoolbaraction.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index b423072..4a252d1 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -26,10 +26,10 @@ type TwoStateToolbarAction struct { // NewTwoStateToolbarAction returns a new push button style of Toolbar item that displays // a different icon for each of its two states -func NewTwoStateToolbarAction(icon0 fyne.Resource, - icon1 fyne.Resource, +func NewTwoStateToolbarAction(state0Icon fyne.Resource, + state1Icon fyne.Resource, onTapped func(TwoStateState)) *TwoStateToolbarAction { - t := &TwoStateToolbarAction{Icon0: icon0, Icon1: icon1, OnActivated: onTapped} + t := &TwoStateToolbarAction{Icon0: state0Icon, Icon1: state1Icon, OnActivated: onTapped} t.button.SetIcon(t.Icon0) t.button.OnTapped = t.activated return t From f61c85f80c61b54319b8571817e2f226d4321015 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Wed, 11 Sep 2024 07:46:29 -0400 Subject: [PATCH 06/15] Add SetState0Icon method SetState0Icon sets or changes the icon that is displayed when state is TwoState0. --- cmd/twostatetoolbaraction_demo/main.go | 11 +++++++---- widget/twostatetoolbaraction.go | 15 +++++++++++---- widget/twostatetoolbaraction_test.go | 19 ++++++++++++++++--- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go index 97afce6..319ac9c 100644 --- a/cmd/twostatetoolbaraction_demo/main.go +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -14,19 +14,22 @@ func main() { a := app.New() w := a.NewWindow("Two State Demo") - twoState0 := xwidget.NewTwoStateToolbarAction(theme.MediaPlayIcon(), + twoState0 := xwidget.NewTwoStateToolbarAction(nil, theme.MediaPauseIcon(), func(state xwidget.TwoStateState) { fmt.Println(state) }) sep := widget.NewToolbarSeparator() - ta := widget.NewToolbarAction(theme.MediaPhotoIcon(), nil) - tb := widget.NewToolbar(twoState0, sep, ta) + tb := widget.NewToolbar(twoState0, sep) toggleButton := widget.NewButton("Toggle State", func() { state := twoState0.GetState() twoState0.SetState(!state) }) - c := container.NewBorder(tb, toggleButton, nil, nil) + icon0Button := widget.NewButton("Set Icon0", func() { + twoState0.SetState0Icon(theme.MediaPlayIcon()) + }) + vc := container.NewVBox(toggleButton, icon0Button) + c := container.NewBorder(tb, vc, nil, nil) w.SetContent(c) w.ShowAndRun() } diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 4a252d1..b1cf192 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -17,7 +17,7 @@ const ( // icon depending on its state type TwoStateToolbarAction struct { state TwoStateState - Icon0 fyne.Resource + icon0 fyne.Resource Icon1 fyne.Resource OnActivated func(TwoStateState) `json:"-"` @@ -29,8 +29,8 @@ type TwoStateToolbarAction struct { func NewTwoStateToolbarAction(state0Icon fyne.Resource, state1Icon fyne.Resource, onTapped func(TwoStateState)) *TwoStateToolbarAction { - t := &TwoStateToolbarAction{Icon0: state0Icon, Icon1: state1Icon, OnActivated: onTapped} - t.button.SetIcon(t.Icon0) + t := &TwoStateToolbarAction{icon0: state0Icon, Icon1: state1Icon, OnActivated: onTapped} + t.button.SetIcon(t.icon0) t.button.OnTapped = t.activated return t } @@ -50,6 +50,13 @@ func (t *TwoStateToolbarAction) SetState(state TwoStateState) { t.button.Refresh() } +// SetState0Icon sets the icon that is displayed when the state is TwoState0 +func (t *TwoStateToolbarAction) SetState0Icon(icon fyne.Resource) { + t.icon0 = icon + t.setButtonIcon() + t.button.Refresh() +} + // ToolbarObject gets a button to render this ToolbarAction func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { t.button.Importance = widget.LowImportance @@ -75,7 +82,7 @@ func (t *TwoStateToolbarAction) activated() { func (t *TwoStateToolbarAction) setButtonIcon() { if t.state == TwoState0 { - t.button.Icon = t.Icon0 + t.button.Icon = t.icon0 } else { t.button.Icon = t.Icon1 } diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 335ffc0..3cc1b64 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -14,16 +14,16 @@ func TestNewTwoStateToolbarAction(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), func(_ TwoStateState) {}) - assert.Equal(t, theme.MediaPlayIcon().Name(), action.Icon0.Name()) + assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) assert.Equal(t, theme.MediaPauseIcon().Name(), action.Icon1.Name()) - assert.Equal(t, action.Icon0.Name(), action.button.Icon.Name()) + assert.Equal(t, action.icon0.Name(), action.button.Icon.Name()) } func TestTwoStateToolbarAction_Activated(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), func(_ TwoStateState) {}) - require.Equal(t, action.Icon0.Name(), action.button.Icon.Name()) + require.Equal(t, action.icon0.Name(), action.button.Icon.Name()) action.button.Tapped(nil) assert.Equal(t, action.Icon1.Name(), action.button.Icon.Name()) } @@ -58,3 +58,16 @@ func TestTwoStateToolbarAction_GetSetState(t *testing.T) { assert.Equal(t, TwoState1, ts) test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) } + +func TestTwoStateToolbarAction_SetState0Icon(t *testing.T) { + test.NewApp() + action := NewTwoStateToolbarAction(nil, + theme.MediaPauseIcon(), + func(state TwoStateState) {}) + tb := widget.NewToolbar(action) + w := test.NewWindow(tb) + defer w.Close() + + action.SetState0Icon(theme.MediaPlayIcon()) + assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) +} From 371d3fb4725cf4a28e9e5ff90fd2e9636e6d211b Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Wed, 11 Sep 2024 07:57:58 -0400 Subject: [PATCH 07/15] Add SetState1Icon method SetState1Icon sets the icon to display when the state is TwoState1. --- cmd/twostatetoolbaraction_demo/main.go | 7 +++++-- widget/twostatetoolbaraction.go | 13 ++++++++++--- widget/twostatetoolbaraction_test.go | 17 +++++++++++++++-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go index 319ac9c..8abbceb 100644 --- a/cmd/twostatetoolbaraction_demo/main.go +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -15,7 +15,7 @@ func main() { w := a.NewWindow("Two State Demo") twoState0 := xwidget.NewTwoStateToolbarAction(nil, - theme.MediaPauseIcon(), func(state xwidget.TwoStateState) { + nil, func(state xwidget.TwoStateState) { fmt.Println(state) }) sep := widget.NewToolbarSeparator() @@ -28,7 +28,10 @@ func main() { icon0Button := widget.NewButton("Set Icon0", func() { twoState0.SetState0Icon(theme.MediaPlayIcon()) }) - vc := container.NewVBox(toggleButton, icon0Button) + icon1Button := widget.NewButton("Set Icon1", func() { + twoState0.SetState1Icon(theme.MediaPauseIcon()) + }) + vc := container.NewVBox(toggleButton, icon0Button, icon1Button) c := container.NewBorder(tb, vc, nil, nil) w.SetContent(c) w.ShowAndRun() diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index b1cf192..58c8016 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -18,7 +18,7 @@ const ( type TwoStateToolbarAction struct { state TwoStateState icon0 fyne.Resource - Icon1 fyne.Resource + icon1 fyne.Resource OnActivated func(TwoStateState) `json:"-"` button widget.Button @@ -29,7 +29,7 @@ type TwoStateToolbarAction struct { func NewTwoStateToolbarAction(state0Icon fyne.Resource, state1Icon fyne.Resource, onTapped func(TwoStateState)) *TwoStateToolbarAction { - t := &TwoStateToolbarAction{icon0: state0Icon, Icon1: state1Icon, OnActivated: onTapped} + t := &TwoStateToolbarAction{icon0: state0Icon, icon1: state1Icon, OnActivated: onTapped} t.button.SetIcon(t.icon0) t.button.OnTapped = t.activated return t @@ -57,6 +57,13 @@ func (t *TwoStateToolbarAction) SetState0Icon(icon fyne.Resource) { t.button.Refresh() } +// SetState1Icon sets the icon that is displayed when the state is TwoState1 +func (t *TwoStateToolbarAction) SetState1Icon(icon fyne.Resource) { + t.icon1 = icon + t.setButtonIcon() + t.button.Refresh() +} + // ToolbarObject gets a button to render this ToolbarAction func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { t.button.Importance = widget.LowImportance @@ -84,6 +91,6 @@ func (t *TwoStateToolbarAction) setButtonIcon() { if t.state == TwoState0 { t.button.Icon = t.icon0 } else { - t.button.Icon = t.Icon1 + t.button.Icon = t.icon1 } } diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 3cc1b64..9040026 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -15,7 +15,7 @@ func TestNewTwoStateToolbarAction(t *testing.T) { theme.MediaPauseIcon(), func(_ TwoStateState) {}) assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) - assert.Equal(t, theme.MediaPauseIcon().Name(), action.Icon1.Name()) + assert.Equal(t, theme.MediaPauseIcon().Name(), action.icon1.Name()) assert.Equal(t, action.icon0.Name(), action.button.Icon.Name()) } @@ -25,7 +25,7 @@ func TestTwoStateToolbarAction_Activated(t *testing.T) { func(_ TwoStateState) {}) require.Equal(t, action.icon0.Name(), action.button.Icon.Name()) action.button.Tapped(nil) - assert.Equal(t, action.Icon1.Name(), action.button.Icon.Name()) + assert.Equal(t, action.icon1.Name(), action.button.Icon.Name()) } func TestTwoStateToolbarAction_Tapped(t *testing.T) { @@ -71,3 +71,16 @@ func TestTwoStateToolbarAction_SetState0Icon(t *testing.T) { action.SetState0Icon(theme.MediaPlayIcon()) assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) } + +func TestTwoStateToolbarAction_SetState1Icon(t *testing.T) { + test.NewApp() + action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), + nil, + func(state TwoStateState) {}) + tb := widget.NewToolbar(action) + w := test.NewWindow(tb) + defer w.Close() + + action.SetState1Icon(theme.MediaPauseIcon()) + assert.Equal(t, theme.MediaPauseIcon().Name(), action.icon1.Name()) +} From 9d5128f35c51ad73551ecc943786eaed07e056e1 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Wed, 11 Sep 2024 10:26:52 -0400 Subject: [PATCH 08/15] Add TwoState toolbarAction description to README.md --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 72985a4..51c1214 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,29 @@ m := NewMap() ![](img/map.png) +### TwoStateToolbarAction + +A TwoStateToolbarAction displays one of two icons based on the stored state. It is similar +to a regular ToolbarAction except that the icon and state are toggled each time the toolbar +action is activated. The current (new) state is passed to the `onActivated` function. + +One potential use of this toolbar action include displaying the MediaPlayIcon when a media +file is not being played, and the MediaPauseIcon or MediaStopIcon when a media file is +being played. A second use may be seen in an application where a left or right panel is +di8splayed or not. For example, show the left panel open icon when the left panel is +closed, and the left panel close icon when the panel is open. + + +```go +action := NewTwoStateToolBar(theme.MediaPlayIcon(), + theme.MediaPauseIcon, + func(state TwoStateState) { + // do something with `state` + }) +``` + +* [Demo App](cmd/twostatetoolbaraction_demo/main.go) + ## Dialogs ### About From 6af3837cbc7977c876421687f671f51c1a6fe899 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Sun, 13 Oct 2024 11:09:36 -0400 Subject: [PATCH 09/15] Change state names to OnState and OffState TwoState0 and TwoState1 imply that there could be more than 2 states. --- widget/twostatetoolbaraction.go | 12 ++++++------ widget/twostatetoolbaraction_test.go | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 58c8016..6f2c11e 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -9,8 +9,8 @@ import ( type TwoStateState bool const ( - TwoState0 TwoStateState = false - TwoState1 TwoStateState = true + OffState TwoStateState = false + OnState TwoStateState = true ) // TwoStateToolbarAction is a push button style of ToolbarItem that displays a different @@ -75,10 +75,10 @@ func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { } func (t *TwoStateToolbarAction) activated() { - if t.state == TwoState0 { - t.state = TwoState1 + if t.state == OffState { + t.state = OnState } else { - t.state = TwoState0 + t.state = OffState } t.setButtonIcon() if t.OnActivated != nil { @@ -88,7 +88,7 @@ func (t *TwoStateToolbarAction) activated() { } func (t *TwoStateToolbarAction) setButtonIcon() { - if t.state == TwoState0 { + if t.state == OffState { t.button.Icon = t.icon0 } else { t.button.Icon = t.icon1 diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 9040026..5bfa6fd 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -43,6 +43,7 @@ func TestTwoStateToolbarAction_Tapped(t *testing.T) { func TestTwoStateToolbarAction_GetSetState(t *testing.T) { var ts TwoStateState + playState := OffState test.NewApp() action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), @@ -52,10 +53,10 @@ func TestTwoStateToolbarAction_GetSetState(t *testing.T) { tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() - assert.Equal(t, TwoState0, action.GetState()) - action.SetState(TwoState1) - assert.Equal(t, TwoState1, action.GetState()) - assert.Equal(t, TwoState1, ts) + assert.Equal(t, playState, action.GetState()) + action.SetState(OnState) + assert.Equal(t, OnState, action.GetState()) + assert.Equal(t, OnState, ts) test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) } From c1029a0c4f970a8df3f5146bbab09db7eaf4f7f7 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Sun, 13 Oct 2024 11:14:51 -0400 Subject: [PATCH 10/15] Change icon names to offIcon and onIcon Matches offState and onState states. --- widget/twostatetoolbaraction.go | 16 ++++++++-------- widget/twostatetoolbaraction_test.go | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 6f2c11e..10c7dd6 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -17,8 +17,8 @@ const ( // icon depending on its state type TwoStateToolbarAction struct { state TwoStateState - icon0 fyne.Resource - icon1 fyne.Resource + offIcon fyne.Resource + onIcon fyne.Resource OnActivated func(TwoStateState) `json:"-"` button widget.Button @@ -29,8 +29,8 @@ type TwoStateToolbarAction struct { func NewTwoStateToolbarAction(state0Icon fyne.Resource, state1Icon fyne.Resource, onTapped func(TwoStateState)) *TwoStateToolbarAction { - t := &TwoStateToolbarAction{icon0: state0Icon, icon1: state1Icon, OnActivated: onTapped} - t.button.SetIcon(t.icon0) + t := &TwoStateToolbarAction{offIcon: state0Icon, onIcon: state1Icon, OnActivated: onTapped} + t.button.SetIcon(t.offIcon) t.button.OnTapped = t.activated return t } @@ -52,14 +52,14 @@ func (t *TwoStateToolbarAction) SetState(state TwoStateState) { // SetState0Icon sets the icon that is displayed when the state is TwoState0 func (t *TwoStateToolbarAction) SetState0Icon(icon fyne.Resource) { - t.icon0 = icon + t.offIcon = icon t.setButtonIcon() t.button.Refresh() } // SetState1Icon sets the icon that is displayed when the state is TwoState1 func (t *TwoStateToolbarAction) SetState1Icon(icon fyne.Resource) { - t.icon1 = icon + t.onIcon = icon t.setButtonIcon() t.button.Refresh() } @@ -89,8 +89,8 @@ func (t *TwoStateToolbarAction) activated() { func (t *TwoStateToolbarAction) setButtonIcon() { if t.state == OffState { - t.button.Icon = t.icon0 + t.button.Icon = t.offIcon } else { - t.button.Icon = t.icon1 + t.button.Icon = t.onIcon } } diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 5bfa6fd..7c246f6 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -14,18 +14,18 @@ func TestNewTwoStateToolbarAction(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), func(_ TwoStateState) {}) - assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) - assert.Equal(t, theme.MediaPauseIcon().Name(), action.icon1.Name()) - assert.Equal(t, action.icon0.Name(), action.button.Icon.Name()) + assert.Equal(t, theme.MediaPlayIcon().Name(), action.offIcon.Name()) + assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) + assert.Equal(t, action.offIcon.Name(), action.button.Icon.Name()) } func TestTwoStateToolbarAction_Activated(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), func(_ TwoStateState) {}) - require.Equal(t, action.icon0.Name(), action.button.Icon.Name()) + require.Equal(t, action.offIcon.Name(), action.button.Icon.Name()) action.button.Tapped(nil) - assert.Equal(t, action.icon1.Name(), action.button.Icon.Name()) + assert.Equal(t, action.onIcon.Name(), action.button.Icon.Name()) } func TestTwoStateToolbarAction_Tapped(t *testing.T) { @@ -70,7 +70,7 @@ func TestTwoStateToolbarAction_SetState0Icon(t *testing.T) { defer w.Close() action.SetState0Icon(theme.MediaPlayIcon()) - assert.Equal(t, theme.MediaPlayIcon().Name(), action.icon0.Name()) + assert.Equal(t, theme.MediaPlayIcon().Name(), action.offIcon.Name()) } func TestTwoStateToolbarAction_SetState1Icon(t *testing.T) { @@ -83,5 +83,5 @@ func TestTwoStateToolbarAction_SetState1Icon(t *testing.T) { defer w.Close() action.SetState1Icon(theme.MediaPauseIcon()) - assert.Equal(t, theme.MediaPauseIcon().Name(), action.icon1.Name()) + assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) } From 60b932dc60f548249ee4598c1c6faf1c0628a4c9 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Sun, 13 Oct 2024 11:42:28 -0400 Subject: [PATCH 11/15] Rename TwoStateToolbarAction args and methods Name the function args and method names to match OnState and OffState. --- cmd/twostatetoolbaraction_demo/main.go | 8 ++++---- widget/twostatetoolbaraction.go | 12 ++++++------ widget/twostatetoolbaraction_test.go | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go index 8abbceb..22d971e 100644 --- a/cmd/twostatetoolbaraction_demo/main.go +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -25,13 +25,13 @@ func main() { state := twoState0.GetState() twoState0.SetState(!state) }) - icon0Button := widget.NewButton("Set Icon0", func() { - twoState0.SetState0Icon(theme.MediaPlayIcon()) + offIconButton := widget.NewButton("Set OffIcon", func() { + twoState0.SetOffStateIcon(theme.MediaPlayIcon()) }) - icon1Button := widget.NewButton("Set Icon1", func() { + onIconButton := widget.NewButton("Set OnIcon", func() { twoState0.SetState1Icon(theme.MediaPauseIcon()) }) - vc := container.NewVBox(toggleButton, icon0Button, icon1Button) + vc := container.NewVBox(toggleButton, offIconButton, onIconButton) c := container.NewBorder(tb, vc, nil, nil) w.SetContent(c) w.ShowAndRun() diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 10c7dd6..90f7091 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -26,10 +26,10 @@ type TwoStateToolbarAction struct { // NewTwoStateToolbarAction returns a new push button style of Toolbar item that displays // a different icon for each of its two states -func NewTwoStateToolbarAction(state0Icon fyne.Resource, - state1Icon fyne.Resource, +func NewTwoStateToolbarAction(offStateIcon fyne.Resource, + onStateIcon fyne.Resource, onTapped func(TwoStateState)) *TwoStateToolbarAction { - t := &TwoStateToolbarAction{offIcon: state0Icon, onIcon: state1Icon, OnActivated: onTapped} + t := &TwoStateToolbarAction{offIcon: offStateIcon, onIcon: onStateIcon, OnActivated: onTapped} t.button.SetIcon(t.offIcon) t.button.OnTapped = t.activated return t @@ -50,14 +50,14 @@ func (t *TwoStateToolbarAction) SetState(state TwoStateState) { t.button.Refresh() } -// SetState0Icon sets the icon that is displayed when the state is TwoState0 -func (t *TwoStateToolbarAction) SetState0Icon(icon fyne.Resource) { +// SetOffStateIcon sets the icon that is displayed when the state is OffState +func (t *TwoStateToolbarAction) SetOffStateIcon(icon fyne.Resource) { t.offIcon = icon t.setButtonIcon() t.button.Refresh() } -// SetState1Icon sets the icon that is displayed when the state is TwoState1 +// SetOnStateIcon sets the icon that is displayed when the state is OnState func (t *TwoStateToolbarAction) SetState1Icon(icon fyne.Resource) { t.onIcon = icon t.setButtonIcon() diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 7c246f6..64f3072 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -36,9 +36,9 @@ func TestTwoStateToolbarAction_Tapped(t *testing.T) { tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() - test.AssertRendersToImage(t, "twostatetoolbaraction/state0.png", w.Canvas()) + test.AssertRendersToImage(t, "twostatetoolbaraction/offstate.png", w.Canvas()) action.button.Tapped(nil) - test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) + test.AssertRendersToImage(t, "twostatetoolbaraction/onstate.png", w.Canvas()) } func TestTwoStateToolbarAction_GetSetState(t *testing.T) { @@ -57,10 +57,10 @@ func TestTwoStateToolbarAction_GetSetState(t *testing.T) { action.SetState(OnState) assert.Equal(t, OnState, action.GetState()) assert.Equal(t, OnState, ts) - test.AssertRendersToImage(t, "twostatetoolbaraction/state1.png", w.Canvas()) + test.AssertRendersToImage(t, "twostatetoolbaraction/onstate.png", w.Canvas()) } -func TestTwoStateToolbarAction_SetState0Icon(t *testing.T) { +func TestTwoStateToolbarAction_SetOffStateIcon(t *testing.T) { test.NewApp() action := NewTwoStateToolbarAction(nil, theme.MediaPauseIcon(), @@ -69,11 +69,11 @@ func TestTwoStateToolbarAction_SetState0Icon(t *testing.T) { w := test.NewWindow(tb) defer w.Close() - action.SetState0Icon(theme.MediaPlayIcon()) + action.SetOffStateIcon(theme.MediaPlayIcon()) assert.Equal(t, theme.MediaPlayIcon().Name(), action.offIcon.Name()) } -func TestTwoStateToolbarAction_SetState1Icon(t *testing.T) { +func TestTwoStateToolbarAction_SetOnStateIcon(t *testing.T) { test.NewApp() action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), nil, @@ -82,6 +82,6 @@ func TestTwoStateToolbarAction_SetState1Icon(t *testing.T) { w := test.NewWindow(tb) defer w.Close() - action.SetState1Icon(theme.MediaPauseIcon()) + action.SetOffStateIcon(theme.MediaPauseIcon()) assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) } From 4bccdbd71d33a4e53d57d6f89b2f989c65b34bf7 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Tue, 22 Oct 2024 19:24:04 -0400 Subject: [PATCH 12/15] Fix bugs SetState1Icon should have been named SetOnStateIcon. Rename state0.png and state1png to offstate.png and onstate.png to match Tapped test names. --- .../{state0.png => offstate.png} | Bin .../{state1.png => onstate.png} | Bin widget/twostatetoolbaraction.go | 2 +- widget/twostatetoolbaraction_test.go | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename widget/testdata/twostatetoolbaraction/{state0.png => offstate.png} (100%) rename widget/testdata/twostatetoolbaraction/{state1.png => onstate.png} (100%) diff --git a/widget/testdata/twostatetoolbaraction/state0.png b/widget/testdata/twostatetoolbaraction/offstate.png similarity index 100% rename from widget/testdata/twostatetoolbaraction/state0.png rename to widget/testdata/twostatetoolbaraction/offstate.png diff --git a/widget/testdata/twostatetoolbaraction/state1.png b/widget/testdata/twostatetoolbaraction/onstate.png similarity index 100% rename from widget/testdata/twostatetoolbaraction/state1.png rename to widget/testdata/twostatetoolbaraction/onstate.png diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 90f7091..63bb996 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -58,7 +58,7 @@ func (t *TwoStateToolbarAction) SetOffStateIcon(icon fyne.Resource) { } // SetOnStateIcon sets the icon that is displayed when the state is OnState -func (t *TwoStateToolbarAction) SetState1Icon(icon fyne.Resource) { +func (t *TwoStateToolbarAction) SetOnStateIcon(icon fyne.Resource) { t.onIcon = icon t.setButtonIcon() t.button.Refresh() diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 64f3072..581ed6f 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -82,6 +82,6 @@ func TestTwoStateToolbarAction_SetOnStateIcon(t *testing.T) { w := test.NewWindow(tb) defer w.Close() - action.SetOffStateIcon(theme.MediaPauseIcon()) + action.SetOnStateIcon(theme.MediaPauseIcon()) assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) } From 75b58621584bcf4ae1e8c2f1888be29e38705f64 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 28 Oct 2024 10:03:53 -0400 Subject: [PATCH 13/15] Change state to boolean. In methods and functions, state replaced by a boolean called `on`. --- cmd/twostatetoolbaraction_demo/main.go | 12 +++--- widget/twostatetoolbaraction.go | 53 ++++++++++++-------------- widget/twostatetoolbaraction_test.go | 30 +++++++-------- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/cmd/twostatetoolbaraction_demo/main.go b/cmd/twostatetoolbaraction_demo/main.go index 22d971e..b130769 100644 --- a/cmd/twostatetoolbaraction_demo/main.go +++ b/cmd/twostatetoolbaraction_demo/main.go @@ -15,21 +15,21 @@ func main() { w := a.NewWindow("Two State Demo") twoState0 := xwidget.NewTwoStateToolbarAction(nil, - nil, func(state xwidget.TwoStateState) { - fmt.Println(state) + nil, func(on bool) { + fmt.Println(on) }) sep := widget.NewToolbarSeparator() tb := widget.NewToolbar(twoState0, sep) toggleButton := widget.NewButton("Toggle State", func() { - state := twoState0.GetState() - twoState0.SetState(!state) + on := twoState0.GetOn() + twoState0.SetOn(!on) }) offIconButton := widget.NewButton("Set OffIcon", func() { - twoState0.SetOffStateIcon(theme.MediaPlayIcon()) + twoState0.SetOffIcon(theme.MediaPlayIcon()) }) onIconButton := widget.NewButton("Set OnIcon", func() { - twoState0.SetState1Icon(theme.MediaPauseIcon()) + twoState0.SetOnIcon(theme.MediaStopIcon()) }) vc := container.NewVBox(toggleButton, offIconButton, onIconButton) c := container.NewBorder(tb, vc, nil, nil) diff --git a/widget/twostatetoolbaraction.go b/widget/twostatetoolbaraction.go index 63bb996..7897cee 100644 --- a/widget/twostatetoolbaraction.go +++ b/widget/twostatetoolbaraction.go @@ -5,21 +5,18 @@ import ( "fyne.io/fyne/v2/widget" ) -// TwoStateState defines the type for the state of a TwoStateToolbarAction. -type TwoStateState bool - -const ( - OffState TwoStateState = false - OnState TwoStateState = true -) - // TwoStateToolbarAction is a push button style of ToolbarItem that displays a different -// icon depending on its state +// icon depending on its state. +// +// state is a boolean indicating off and on. The actual meaning of the boolean depends on how it is used. For +// example, in a media play app, false might indicate that the medium file is not being played, and true might +// indicate that the file is being played. +// Similarly, the two states could be used to indicate that a panel is being hidden or shown. type TwoStateToolbarAction struct { - state TwoStateState + on bool offIcon fyne.Resource onIcon fyne.Resource - OnActivated func(TwoStateState) `json:"-"` + OnActivated func(bool) `json:"-"` button widget.Button } @@ -28,37 +25,37 @@ type TwoStateToolbarAction struct { // a different icon for each of its two states func NewTwoStateToolbarAction(offStateIcon fyne.Resource, onStateIcon fyne.Resource, - onTapped func(TwoStateState)) *TwoStateToolbarAction { + onTapped func(bool)) *TwoStateToolbarAction { t := &TwoStateToolbarAction{offIcon: offStateIcon, onIcon: onStateIcon, OnActivated: onTapped} t.button.SetIcon(t.offIcon) t.button.OnTapped = t.activated return t } -// GetState returns the current state of the toolbaraction -func (t *TwoStateToolbarAction) GetState() TwoStateState { - return t.state +// GetOn returns the current state of the toolbaraction +func (t *TwoStateToolbarAction) GetOn() bool { + return t.on } -// SetState sets the state of the toolbaraction -func (t *TwoStateToolbarAction) SetState(state TwoStateState) { - t.state = state +// SetOn sets the state of the toolbaraction +func (t *TwoStateToolbarAction) SetOn(on bool) { + t.on = on if t.OnActivated != nil { - t.OnActivated(t.state) + t.OnActivated(t.on) } t.setButtonIcon() t.button.Refresh() } -// SetOffStateIcon sets the icon that is displayed when the state is OffState -func (t *TwoStateToolbarAction) SetOffStateIcon(icon fyne.Resource) { +// SetOffIcon sets the icon that is displayed when the state is false +func (t *TwoStateToolbarAction) SetOffIcon(icon fyne.Resource) { t.offIcon = icon t.setButtonIcon() t.button.Refresh() } -// SetOnStateIcon sets the icon that is displayed when the state is OnState -func (t *TwoStateToolbarAction) SetOnStateIcon(icon fyne.Resource) { +// SetOnIcon sets the icon that is displayed when the state is true +func (t *TwoStateToolbarAction) SetOnIcon(icon fyne.Resource) { t.onIcon = icon t.setButtonIcon() t.button.Refresh() @@ -75,20 +72,20 @@ func (t *TwoStateToolbarAction) ToolbarObject() fyne.CanvasObject { } func (t *TwoStateToolbarAction) activated() { - if t.state == OffState { - t.state = OnState + if !t.on { + t.on = true } else { - t.state = OffState + t.on = false } t.setButtonIcon() if t.OnActivated != nil { - t.OnActivated(t.state) + t.OnActivated(t.on) } t.button.Refresh() } func (t *TwoStateToolbarAction) setButtonIcon() { - if t.state == OffState { + if !t.on { t.button.Icon = t.offIcon } else { t.button.Icon = t.onIcon diff --git a/widget/twostatetoolbaraction_test.go b/widget/twostatetoolbaraction_test.go index 581ed6f..e63ef25 100644 --- a/widget/twostatetoolbaraction_test.go +++ b/widget/twostatetoolbaraction_test.go @@ -13,7 +13,7 @@ import ( func TestNewTwoStateToolbarAction(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), - func(_ TwoStateState) {}) + func(_ bool) {}) assert.Equal(t, theme.MediaPlayIcon().Name(), action.offIcon.Name()) assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) assert.Equal(t, action.offIcon.Name(), action.button.Icon.Name()) @@ -22,7 +22,7 @@ func TestNewTwoStateToolbarAction(t *testing.T) { func TestTwoStateToolbarAction_Activated(t *testing.T) { action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), - func(_ TwoStateState) {}) + func(_ bool) {}) require.Equal(t, action.offIcon.Name(), action.button.Icon.Name()) action.button.Tapped(nil) assert.Equal(t, action.onIcon.Name(), action.button.Icon.Name()) @@ -32,7 +32,7 @@ func TestTwoStateToolbarAction_Tapped(t *testing.T) { test.NewApp() action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), - func(_ TwoStateState) {}) + func(_ bool) {}) tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() @@ -42,21 +42,21 @@ func TestTwoStateToolbarAction_Tapped(t *testing.T) { } func TestTwoStateToolbarAction_GetSetState(t *testing.T) { - var ts TwoStateState - playState := OffState + var ts bool + playState := false test.NewApp() action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), theme.MediaPauseIcon(), - func(state TwoStateState) { - ts = state + func(on bool) { + ts = on }) tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() - assert.Equal(t, playState, action.GetState()) - action.SetState(OnState) - assert.Equal(t, OnState, action.GetState()) - assert.Equal(t, OnState, ts) + assert.Equal(t, playState, action.GetOn()) + action.SetOn(true) + assert.Equal(t, true, action.GetOn()) + assert.Equal(t, true, ts) test.AssertRendersToImage(t, "twostatetoolbaraction/onstate.png", w.Canvas()) } @@ -64,12 +64,12 @@ func TestTwoStateToolbarAction_SetOffStateIcon(t *testing.T) { test.NewApp() action := NewTwoStateToolbarAction(nil, theme.MediaPauseIcon(), - func(state TwoStateState) {}) + func(staone bool) {}) tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() - action.SetOffStateIcon(theme.MediaPlayIcon()) + action.SetOffIcon(theme.MediaPlayIcon()) assert.Equal(t, theme.MediaPlayIcon().Name(), action.offIcon.Name()) } @@ -77,11 +77,11 @@ func TestTwoStateToolbarAction_SetOnStateIcon(t *testing.T) { test.NewApp() action := NewTwoStateToolbarAction(theme.MediaPlayIcon(), nil, - func(state TwoStateState) {}) + func(on bool) {}) tb := widget.NewToolbar(action) w := test.NewWindow(tb) defer w.Close() - action.SetOnStateIcon(theme.MediaPauseIcon()) + action.SetOnIcon(theme.MediaPauseIcon()) assert.Equal(t, theme.MediaPauseIcon().Name(), action.onIcon.Name()) } From e381ca0c1c9f5155ec8301b37bfb7bbb4492d0d2 Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Mon, 28 Oct 2024 10:10:15 -0400 Subject: [PATCH 14/15] Updte README.md Modify TwoStateToolbarAction description to correspond to boolean rather than state. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 51c1214..89aee0a 100644 --- a/README.md +++ b/README.md @@ -232,8 +232,9 @@ closed, and the left panel close icon when the panel is open. ```go action := NewTwoStateToolBar(theme.MediaPlayIcon(), theme.MediaPauseIcon, - func(state TwoStateState) { - // do something with `state` + func(on bool) { + // Do something with state. For example, if on is true, start playback and display + // the pause icon. }) ``` From 22b3787799114cd2436880eb4d72b12810f018ec Mon Sep 17 00:00:00 2001 From: Jim Orcheson Date: Tue, 29 Oct 2024 08:12:26 -0400 Subject: [PATCH 15/15] Update README.md Change comment in example code. The user does not need to display the other icon as this is already done for the user. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 89aee0a..edadfa4 100644 --- a/README.md +++ b/README.md @@ -233,8 +233,7 @@ closed, and the left panel close icon when the panel is open. action := NewTwoStateToolBar(theme.MediaPlayIcon(), theme.MediaPauseIcon, func(on bool) { - // Do something with state. For example, if on is true, start playback and display - // the pause icon. + // Do something with state. For example, if on is true, start playback. }) ```