Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added custom binding 'DisableableBinding' and added it to README.md. #46

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Optionally, Each canvas object can be encapsulated with `Responsive()` function
```go
layout := NewResponsiveLayout(
Responsive(object1), // all sizes to 100%
Responsive(object2, 0.5, 0.75), // small to 50%, medium to 75%, all others to 100%
Responsive(object2, 0.5, 0.75), // small to 50%, medium to 75%, all others to 100%
)
```

Expand All @@ -59,7 +59,7 @@ Community contributed widgets.

### Calendar



A date picker which returns a [time](https://pkg.go.dev/time) object with the selected date.

Expand All @@ -69,7 +69,7 @@ A date picker which returns a [time](https://pkg.go.dev/time) object with the se

</p>



To use create a new calendar with a given time and a callback function:

Expand Down Expand Up @@ -224,6 +224,16 @@ if err := token.Error(); err != nil {
s, err := binding.NewMqttString(client, "fyne.io/x/string")
```

### DisableableBinding

A `DisableableBinding` creates a `Bool` data binding which accepts `Disableable` widgets to control.
When the `bool` binding is changed, all widgets `Enable` or `Disable` methods will be executed depending on the settings.

The binding accepts widgets when created with `NewDisableableBinding` or via the method `AddWidgets`.
The behaviour of the binding can be inverted, so true disables and false enables. This is done through method `Invert`.

All widgets are updated when either `AddWidgets` or `Invert` is executed.

## Data Validation

Community contributed validators.
Expand Down
69 changes: 69 additions & 0 deletions data/binding/disableableBinding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package binding

import (
"log"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
)

// DisableableBinding is a `Bool` binding with accept Disableable widgets that it will control the Disabled status of.
type DisableableBinding interface {
binding.Bool

AddWidgets(...fyne.Disableable)
SetInverted(bool)
}

type boundDisableable struct {
binding.Bool

inverted bool
widgets []fyne.Disableable
}

// NewDisableableBinding returns a `Bool` binding which accepts Disableable widgets.
// When the Bool changes, the widgets Enable or Disable method will be executed.
func NewDisableableBinding(widgets ...fyne.Disableable) DisableableBinding {
newBinding := &boundDisableable{
Bool: binding.NewBool(),
widgets: widgets,
}

// Add default listener
newBinding.AddListener(binding.NewDataListener(newBinding.update))

return newBinding
}

// Adding widgets to the binding.
// This will update the Disable status of the widgets immediately.
func (b *boundDisableable) AddWidgets(widgets ...fyne.Disableable) {
b.widgets = append(b.widgets, widgets...)
b.update()
}

// SetInverted will switch the behavior of when the widgets will be Enabled or Disabled.
// This will update the Disable status of the widgets immediately.
func (b *boundDisableable) SetInverted(inverted bool) {
b.inverted = inverted
b.update()
}

func (b *boundDisableable) update() {
val, err := b.Get()
if err != nil {
log.Println(err)
return
}

if (!b.inverted && val) || (b.inverted && !val) {
for _, widget := range b.widgets {
widget.Enable()
}
} else {
for _, widget := range b.widgets {
widget.Disable()
}
}
}
64 changes: 64 additions & 0 deletions data/binding/disableableBinding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package binding

import (
"testing"
"time"

"fyne.io/fyne/v2/test"
"fyne.io/fyne/v2/widget"
)

// Since it's a callback we're testing, we let it process for some time before checking for result.
const callbackWait = 10 * time.Millisecond

func TestDisableableBinding(t *testing.T) {
test.NewApp()

bound := NewDisableableBinding()
widget1 := widget.NewEntry()
widget2 := widget.NewSelectEntry([]string{"test1", "test2"})

if widget1.Disabled() || widget2.Disabled() {
t.Errorf("test ended early because of wrong initial values on widgets.")
return
}

// Adding widgets
bound.AddWidgets(widget1, widget2)

boundCheck := widget.NewCheckWithData("My bound checkbox", bound)

// Checking not inverted
boundCheck.SetChecked(true)

// Since it's a callback we're testing, we let it process for some time before checking for result.
<-time.After(callbackWait)
if widget1.Disabled() || widget2.Disabled() {
t.Errorf("Widget1 or 2 was not enabled.")
return
}

boundCheck.SetChecked(false)
<-time.After(callbackWait)
if !widget1.Disabled() || !widget2.Disabled() {
t.Errorf("Widget1 or 2 was not disabled.")
return
}

// Checking inverted
bound.SetInverted(true)

boundCheck.SetChecked(true)
<-time.After(callbackWait)
if !widget1.Disabled() || !widget2.Disabled() {
t.Errorf("Widget1 or 2 was not disabled.")
return
}

boundCheck.SetChecked(false)
<-time.After(callbackWait)
if widget1.Disabled() || widget2.Disabled() {
t.Errorf("Widget1 or 2 was not enabled.")
return
}
}