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

Callbacks for Information and Error dialogs #851

Merged
merged 11 commits into from
Apr 19, 2020
Prev Previous commit
Next Next commit
remove constructors, use Dialog interface method instead to fix #851
  • Loading branch information
obsti8383 committed Apr 13, 2020
commit ee12c3f6d9356d76b52ecb0ea887e810887bf929
9 changes: 9 additions & 0 deletions dialog/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Dialog interface {
Show()
Hide()
SetDismissText(label string)
SetOnClosed(closed func())
}

// Declare conformity to Dialog interface
Expand All @@ -39,6 +40,14 @@ type dialog struct {
parent fyne.Window
}

// SetOnClosed allows to set a callback function that is called when
// the dialog is closed
func (d *dialog) SetOnClosed(closed func()) {
d.callback = func(dummy bool) {
obsti8383 marked this conversation as resolved.
Show resolved Hide resolved
closed()
}
}

func (d *dialog) setButtons(buttons fyne.CanvasObject) {
d.bg = canvas.NewRectangle(theme.BackgroundColor())
d.label = widget.NewLabelWithStyle(d.title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
Expand Down
35 changes: 7 additions & 28 deletions dialog/information.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,7 @@ import (
)

func createTextDialog(title, message string, icon fyne.Resource, parent fyne.Window) Dialog {
return createTextDialogWithCallback(title, message, icon, nil, parent)
}

func createTextDialogWithCallback(title, message string, icon fyne.Resource, callback func(), parent fyne.Window) Dialog {

d := newDialog(title, message, icon, func(dummy bool) {
callback()
}, parent)
d := newDialog(title, message, icon, nil, parent)

d.dismiss = &widget.Button{Text: "OK",
OnTapped: d.Hide,
Expand All @@ -31,35 +24,21 @@ func NewInformation(title, message string, parent fyne.Window) Dialog {
return createTextDialog(title, message, theme.InfoIcon(), parent)
}

// NewInformationWithCallback creates a dialog over the specified window for user information.
// The title is used for the dialog window and message is the content. The callback
// is executed when the user closes the dialog. After creation you should call Show().
func NewInformationWithCallback(title, message string, callback func(), parent fyne.Window) Dialog {
return createTextDialogWithCallback(title, message, theme.InfoIcon(), callback, parent)
}

// ShowInformation shows a dialog over the specified window for user
// information. The title is used for the dialog window and message is the content.
func ShowInformation(title, message string, parent fyne.Window) {
NewInformation(title, message, parent).Show()
}

// ShowInformationWithCallback shows a dialog over the specified window for user
// information. The title is used for the dialog window and message is the content.
// The callback is executed when the user closes the dialog.
func ShowInformationWithCallback(title, message string, callback func(), parent fyne.Window) {
NewInformationWithCallback(title, message, callback, parent).Show()
// NewError creates a dialog over the specified window for an application
// error. The title and message are extracted from the provided error.
// After creation you should call Show().
func NewError(err error, parent fyne.Window) Dialog {
return createTextDialog("Error", err.Error(), theme.WarningIcon(), parent)
}

// ShowError shows a dialog over the specified window for an application
// error. The title and message are extracted from the provided error.
func ShowError(err error, parent fyne.Window) {
createTextDialog("Error", err.Error(), theme.WarningIcon(), parent).Show()
}

// ShowErrorWithCallback shows a dialog over the specified window for an application
// error. The title and message are extracted from the provided error. The callback
// is executed when the user closes the dialog.
func ShowErrorWithCallback(err error, callback func(), parent fyne.Window) {
createTextDialogWithCallback("Error", err.Error(), theme.WarningIcon(), callback, parent).Show()
NewError(err, parent).Show()
}
32 changes: 25 additions & 7 deletions dialog/information_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dialog

import (
"errors"
"testing"
"time"

Expand All @@ -18,22 +19,39 @@ func TestDialog_MinSize(t *testing.T) {
assert.Less(t, label.Width, dialogContent.Width)
}

func TestDialog_Callback(t *testing.T) {
func TestDialog_InformationCallback(t *testing.T) {
d := NewInformation("Information", "Hello World", test.NewWindow(nil))
tapped := make(chan bool)

d := NewInformationWithCallback("Information", "Hello World",
func() {
tapped <- true
}, test.NewWindow(nil))
d.SetOnClosed(func() { tapped <- true })
d.Show()

information := d.(*dialog)
assert.False(t, information.win.Hidden)
go test.Tap(information.dismiss)
func() {
select {
case <-tapped:
case <-time.After(1 * time.Second):
assert.Fail(t, "Timed out waiting for button tap")
}
}()
assert.True(t, information.win.Hidden)
}

func TestDialog_ErrorCallback(t *testing.T) {
err := errors.New("Error message")
d := NewError(err, test.NewWindow(nil))
tapped := make(chan bool)
d.SetOnClosed(func() { tapped <- true })
d.Show()

information := d.(*dialog)
assert.False(t, information.win.Hidden)
go test.Tap(information.dismiss)
func() {
select {
case <-tapped:
case <-time.After(10 * time.Second):
case <-time.After(1 * time.Second):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's way too long IMO.

Suggested change
case <-time.After(1 * time.Second):
case <-time.After(100 * time.Millisecond):

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, when using test.Tap you don't have to do concurrency, it's synchronous. However, using a chan and the select makes the test independent from the implementation of test.Tap.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is still 1 second

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, fixed.

assert.Fail(t, "Timed out waiting for button tap")
}
}()
Expand Down