Skip to content

Commit

Permalink
window. add close button interceptor to react on the window closing b…
Browse files Browse the repository at this point in the history
…efore anything is done (#1180)

Add SetCloseIntercept to be able to veto window close.
Be aware client code should call window.Close() if the action was desired.

Fixes #467
  • Loading branch information
Rustam committed Aug 14, 2020
1 parent eea88d3 commit b210665
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 22 deletions.
37 changes: 25 additions & 12 deletions internal/driver/glfw/window.go
Expand Up @@ -74,6 +74,7 @@ type window struct {
mouseLastClick fyne.CanvasObject
mousePressed fyne.CanvasObject
onClosed func()
onCloseIntercepted func()

xpos, ypos int
width, height int
Expand Down Expand Up @@ -292,6 +293,10 @@ func (w *window) SetOnClosed(closed func()) {
w.onClosed = closed
}

func (w *window) SetCloseIntercept(callback func()) {
w.onCloseIntercepted = callback
}

func (w *window) getMonitorForWindow() *glfw.Monitor {
xOff := w.xpos + (w.width / 2)
yOff := w.ypos + (w.height / 2)
Expand Down Expand Up @@ -404,7 +409,20 @@ func (w *window) Close() {
if w.viewport == nil {
return
}
w.closed(w.viewport)

w.viewport.SetShouldClose(true)

w.canvas.walkTrees(nil, func(node *renderCacheNode) {
switch co := node.obj.(type) {
case fyne.Widget:
cache.DestroyRenderer(co)
}
})

// trigger callbacks
if w.onClosed != nil {
w.queueEvent(w.onClosed)
}
}

func (w *window) ShowAndRun() {
Expand Down Expand Up @@ -446,19 +464,14 @@ func (w *window) Canvas() fyne.Canvas {
}

func (w *window) closed(viewport *glfw.Window) {
viewport.SetShouldClose(true)
viewport.SetShouldClose(false)

w.canvas.walkTrees(nil, func(node *renderCacheNode) {
switch co := node.obj.(type) {
case fyne.Widget:
cache.DestroyRenderer(co)
}
})

// trigger callbacks
if w.onClosed != nil {
w.queueEvent(w.onClosed)
if w.onCloseIntercepted != nil {
w.queueEvent(w.onCloseIntercepted)
return
}

w.Close()
}

// destroy this window and, if it's the last window quit the app
Expand Down
32 changes: 32 additions & 0 deletions internal/driver/glfw/window_test.go
Expand Up @@ -821,6 +821,38 @@ func TestWindow_Clipboard(t *testing.T) {
cb.SetContent(cliboardContent)
}

func TestWindow_CloseInterception(t *testing.T) {
d := NewGLDriver()
w := d.CreateWindow("test").(*window)
w.create()

onIntercepted := false
onClosed := false
w.SetCloseIntercept(func() {
onIntercepted = true
})
w.SetOnClosed(func() {
onClosed = true
})
w.Close()
w.waitForEvents()
assert.False(t, onIntercepted) // The interceptor is not called by the Close.
assert.True(t, onClosed)

onIntercepted = false
onClosed = false
w.closed(w.viewport)
w.waitForEvents()
assert.True(t, onIntercepted) // The interceptor is called by the closed.
assert.False(t, onClosed) // If the interceptor is set Close is not called.

onClosed = false
w.SetCloseIntercept(nil)
w.closed(w.viewport)
w.waitForEvents()
assert.True(t, onClosed) // Close is called if the interceptor is not set.
}

// This test makes our developer screens flash, let's not run it regularly...
//func TestWindow_Shortcut(t *testing.T) {
// w := createWindow("Test")
Expand Down
24 changes: 19 additions & 5 deletions internal/driver/gomobile/window.go
Expand Up @@ -9,10 +9,11 @@ import (
)

type window struct {
title string
visible bool
onClosed func()
isChild bool
title string
visible bool
onClosed func()
onCloseIntercepted func()
isChild bool

clipboard fyne.Clipboard
canvas *mobileCanvas
Expand Down Expand Up @@ -92,6 +93,10 @@ func (w *window) SetOnClosed(callback func()) {
w.onClosed = callback
}

func (w *window) SetCloseIntercept(callback func()) {
w.onCloseIntercepted = callback
}

func (w *window) Show() {
menu := fyne.CurrentApp().Driver().(*mobileDriver).findMenu(w)
menuButton := widget.NewButtonWithIcon("", theme.MenuIcon(), func() {
Expand All @@ -103,7 +108,7 @@ func (w *window) Show() {

if w.isChild {
exit := widget.NewButtonWithIcon("", theme.CancelIcon(), func() {
w.Close()
w.tryClose()
})
title := widget.NewLabel(w.title)
title.Alignment = fyne.TextAlignCenter
Expand All @@ -129,6 +134,15 @@ func (w *window) Hide() {
}
}

func (w *window) tryClose() {
if w.onCloseIntercepted != nil {
w.onCloseIntercepted()
return
}

w.Close()
}

func (w *window) Close() {
d := fyne.CurrentApp().Driver().(*mobileDriver)
pos := -1
Expand Down
15 changes: 10 additions & 5 deletions test/testwindow.go
Expand Up @@ -5,11 +5,12 @@ import (
)

type testWindow struct {
title string
fullScreen bool
fixedSize bool
focused bool
onClosed func()
title string
fullScreen bool
fixedSize bool
focused bool
onClosed func()
onCloseIntercepted func()

canvas *testCanvas
clipboard fyne.Clipboard
Expand Down Expand Up @@ -112,6 +113,10 @@ func (w *testWindow) SetOnClosed(closed func()) {
w.onClosed = closed
}

func (w *testWindow) SetCloseIntercept(callback func()) {
w.onCloseIntercepted = callback
}

func (w *testWindow) SetPadded(padded bool) {
w.canvas.SetPadded(padded)
}
Expand Down
4 changes: 4 additions & 0 deletions window.go
Expand Up @@ -64,6 +64,10 @@ type Window interface {
// SetOnClosed sets a function that runs when the window is closed.
SetOnClosed(func())

// SetCloseIntercept sets a function that runs instead of closing if defined.
// Close() should be called explicitly in the interceptor to close the window.
SetCloseIntercept(func())

// Show the window on screen.
Show()
// Hide the window from the user.
Expand Down

0 comments on commit b210665

Please sign in to comment.