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

MainMenu() Close item does NOT call function defined in SetCloseIntercept #2355

Closed
stuartdd opened this issue Aug 5, 2021 · 8 comments
Closed
Labels
unverified A bug that has been reported but not verified

Comments

@stuartdd
Copy link

stuartdd commented Aug 5, 2021

The Main Menu automagically adds a 'Quit' item. This closes the application.

The SetCloseIntercept method allows a function to be defined that is called before the window closes and if not appropriate the application close can be aborted. This allows the user to save any unsaved data or perform any actions that are required for the proper closing of the application.

I have set a Close Intercept function but it is NOT called when the Quit menu is clicked!

If I click the X (Top right on windows and most Linux Window managers) to close the application the Close Intercept function IS called and the behaviour is as expected.

I would expect the behaviour to be the same for both!

Because of the way the Quit menu is added I cannot alter this behaviour. I have used a work around but it throws exceptions to the console when I close the window via the quit menu.

Note that if I w.Close() from any where outside the main thread I get exceptions. For example if I add another menu item and add an action that calls w.Close(), the application closes but I get loads of exceptions in the console. What is the proper way to close the application from an event thread?

To Reproduce:

Steps to reproduce the behaviour:

  1. Add t a function to the SetCloseIntercept (see below)
  2. Click on the Quit menu
  3. The function is NOT called. 'Closing' is not echoed to the console but the application closes
  4. Run again and click on the X to close the window
  5. The function is called. 'Closing' is echoed to the console and the application closes

Example code:

	w.SetCloseIntercept(func() {
		fmt.Println("Closing")
		w.Close()
	})

Device (please complete the following information):

  • OS: Linux Mint 20.2 Cinnamon
  • Version: 5.11.0-25-generic
  • Go version: go version go1.16.6 linux/amd64
  • Fyne version: fyne.io/fyne v1.4.3

My workaround code:

go func() {
	time.Sleep(2 * time.Second)
	for _, item := range w.MainMenu().Items[0].Items {
		if item.Label == "Quit" {
			item.Action = func() {
				fmt.Println("Closing")
				w.Close()
			}
		}
	}
}()

However when invoked the following is sent to the console

Closing
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0xf8 pc=0x7f1370a31fc4]

runtime stack:
runtime.throw(0x7bf4d9, 0x2a)
        /home/stuart/go/src/runtime/panic.go:1117 +0x72
runtime.sigpanic()
        /home/stuart/go/src/runtime/signal_unix.go:718 +0x2e5

goroutine 23 [syscall, locked to thread]:
runtime.cgocall(0x702360, 0xc000a6dd70, 0x25051d0)
        /home/stuart/go/src/runtime/cgocall.go:154 +0x5b fp=0xc000a6dd40 sp=0xc000a6dd08 pc=0x4297fb
github.com/go-gl/gl/v3.2-core/gl._Cfunc_glowClear(0x7f13707be700, 0x3e4243c500004100)
        _cgo_gotypes.go:3660 +0x3c fp=0xc000a6dd70 sp=0xc000a6dd40 pc=0x63ee1c
github.com/go-gl/gl/v3.2-core/gl.Clear(...)
        /home/stuart/go/pkg/mod/github.com/go-gl/gl@v0.0.0-20190320180904-bf2b1f2f34d7/v3.2-core/gl/package.go:8667
fyne.io/fyne/internal/painter/gl.(*glPainter).glClearBuffer(0xc0001c2330)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/gl/gl_core.go:188 +0xe6 fp=0xc000a6dd98 sp=0xc000a6dd70 pc=0x661fa6
fyne.io/fyne/internal/painter/gl.(*glPainter).Clear(0xc0001c2330)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/gl/painter.go:51 +0x2b fp=0xc000a6ddb0 sp=0xc000a6dd98 pc=0x66268b
fyne.io/fyne/internal/driver/glfw.(*glCanvas).paint(0xc000194000, 0x280, 0x1ca)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/canvas.go:413 +0x93 fp=0xc000a6de30 sp=0xc000a6ddb0 pc=0x6900b3
fyne.io/fyne/internal/driver/glfw.(*gLDriver).repaintWindow.func1()
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/loop.go:164 +0x88 fp=0xc000a6de80 sp=0xc000a6de30 pc=0x69cb28
fyne.io/fyne/internal/driver/glfw.(*window).RunWithContext(0xc000190000, 0xc000a6dea8)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/window.go:1095 +0x38 fp=0xc000a6de98 sp=0xc000a6de80 pc=0x69b3d8
fyne.io/fyne/internal/driver/glfw.(*gLDriver).repaintWindow(0xc0000c80f0, 0xc000190000)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/loop.go:155 +0x65 fp=0xc000a6ded0 sp=0xc000a6de98 pc=0x6933c5
fyne.io/fyne/internal/driver/glfw.(*gLDriver).startDrawThread.func1(0xc0000c80f0, 0xc00009e420, 0xc0000c8190)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/loop.go:208 +0x269 fp=0xc000a6dfc8 sp=0xc000a6ded0 pc=0x69ce69
runtime.goexit()
        /home/stuart/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc000a6dfd0 sp=0xc000a6dfc8 pc=0x4918e1
created by fyne.io/fyne/internal/driver/glfw.(*gLDriver).startDrawThread
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/loop.go:182 +0xbd

goroutine 1 [chan send, locked to thread]:
fyne.io/fyne/internal/driver/glfw.(*gLDriver).runGL(0xc0000c80f0)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/loop.go:93 +0x1e6
fyne.io/fyne/internal/driver/glfw.(*gLDriver).Run(0xc0000c80f0)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/driver.go:74 +0x3a
fyne.io/fyne/internal/driver/glfw.(*window).ShowAndRun(0xc000190000)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/window.go:440 +0x57
main.main()
        /home/stuart/git/golang/enctest/main.go:124 +0xf4d

goroutine 19 [sleep]:
time.Sleep(0x22ecb25c00)
        /home/stuart/go/src/runtime/time.go:193 +0xd2
fyne.io/fyne/internal/painter.svgCacheJanitor.func1()
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/svg_cache.go:57 +0x109
sync.(*Once).doSlow(0xd28fb8, 0xc0000c0300)
        /home/stuart/go/src/sync/once.go:68 +0xec
sync.(*Once).Do(0xd28fb8, 0xc0000c0300)
        /home/stuart/go/src/sync/once.go:59 +0x45
created by fyne.io/fyne/internal/painter.svgCacheJanitor
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/svg_cache.go:55 +0x9b

goroutine 20 [chan receive]:
fyne.io/fyne/app.newAppWithDriver.func1(0xc00009e240, 0xc00015c000)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/app/app.go:126 +0x3d
created by fyne.io/fyne/app.newAppWithDriver
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/app/app.go:124 +0x1df

goroutine 21 [syscall]:
syscall.Syscall6(0xe8, 0x8, 0xc00020fc2c, 0x7, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0)
        /home/stuart/go/src/syscall/asm_linux_amd64.s:43 +0x5
golang.org/x/sys/unix.EpollWait(0x8, 0xc00020fc2c, 0x7, 0x7, 0xffffffffffffffff, 0x0, 0x0, 0x0)
        /home/stuart/go/pkg/mod/golang.org/x/sys@v0.0.0-20210615035016-665e8c7367d1/unix/zsyscall_linux_amd64.go:77 +0x72
github.com/fsnotify/fsnotify.(*fdPoller).wait(0xc00014c080, 0x0, 0x0, 0x0)
        /home/stuart/go/pkg/mod/github.com/fsnotify/fsnotify@v1.4.9/inotify_poller.go:86 +0x91
github.com/fsnotify/fsnotify.(*Watcher).readEvents(0xc0000c8140)
        /home/stuart/go/pkg/mod/github.com/fsnotify/fsnotify@v1.4.9/inotify.go:192 +0x206
created by github.com/fsnotify/fsnotify.NewWatcher
        /home/stuart/go/pkg/mod/github.com/fsnotify/fsnotify@v1.4.9/inotify.go:59 +0x1ab

goroutine 22 [chan receive]:
fyne.io/fyne/app.watchFile.func1(0xc0000c8140, 0xc0000bc1e0, 0x27, 0xc0000a9350, 0xc0000a9360)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/app/settings_desktop.go:42 +0x5a
created by fyne.io/fyne/app.watchFile
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/app/settings_desktop.go:41 +0xef

goroutine 35 [chan receive]:
fyne.io/fyne/internal/driver/glfw.(*glCanvas).setupThemeListener.func1(0xc0001b4000, 0xc000194000)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/canvas.go:462 +0x47
created by fyne.io/fyne/internal/driver/glfw.(*glCanvas).setupThemeListener
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/driver/glfw/canvas.go:460 +0xa6

goroutine 25 [chan receive]:
fyne.io/fyne/internal/painter.SvgCacheMonitorTheme.func1(0xc000034180)
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/svg_cache.go:109 +0x34
created by fyne.io/fyne/internal/painter.SvgCacheMonitorTheme
        /home/stuart/go/pkg/mod/fyne.io/fyne@v1.4.3/internal/painter/svg_cache.go:107 +0x9c
exit status 2
@stuartdd stuartdd added the unverified A bug that has been reported but not verified label Aug 5, 2021
@stuartdd
Copy link
Author

stuartdd commented Aug 5, 2021

I have updated to fyne version - fyne.io/fyne/v2 v2.0.3 and the problem still exists.

@andydotxyz
Copy link
Member

I can confirm the CloseIntercept is not called, good catch.
However I cannot replicate the crash - is that still happening with v2.0.3 as well?

@andydotxyz
Copy link
Member

For added info it seems that the macOS hard-coded quit item does work as expected, so it's just the Fyne window menus that need fixed.

@stuartdd
Copy link
Author

stuartdd commented Aug 6, 2021 via email

@andydotxyz
Copy link
Member

We can't make Quit optional and have real cross-platfom parity because Quit is mandatory on macOS.
Changing it's text is part of an internationalisation project which will start later for a future release.

@stuartdd
Copy link
Author

stuartdd commented Aug 7, 2021

Hi.

I just noticed that with the code below:

The dialog is shown in a modal window as expected, however if I close using X (close window button) the shouldClose method is called recursively!

I can press X many times while the dialog is visible, each time the background gets darker and a new dialog is shown. For each press 'Yes' I am returned to the previous dialog.

Am I doing something silly or is this unexpected behaviour.

I have overcome at the moment by having a global lock value to prevent the recursion but this is ugly.

Also I cannot get the exception messages to show in the logs. I will keep an eye in it :-)

window.SetCloseIntercept(shouldClose)
...
func shouldClose() {
	if dataHasChanged() {
		d := dialog.NewConfirm("Save Changes", "There are unsaved changes\nDo you want to save them before closing?", saveChangesConfirm, window)
		d.Show()
	} else {
		window.Close()
	}
}

func saveChangesConfirm(option bool) {
	if !option {
		fmt.Println("Quit without saving changes")
		window.Close()
	}
}

@andydotxyz
Copy link
Member

Am I doing something silly or is this unexpected behaviour.

Every time the user tries to close the window it is running your callback - and displaying a new dialog.
I think it is probably up to the application code to decide what to do if it happens twice in a row.

I don't think the toolkit can do this, as we have no idea what your close intercept handler will do.

Also my guess is that some recursive calling may be due to your code, not certain as the link to SetCloseIntercept is not shown in your excerpt.

I think that the main problem of this ticket will be resolved by us landing #2356

@andydotxyz
Copy link
Member

Fixed the underlying problem on develop so we close windows instead of just hard-closing the app when Quit menu is tapped.

@andydotxyz andydotxyz added this to the Aberlour (2.1) milestone Aug 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
unverified A bug that has been reported but not verified
Projects
None yet
Development

No branches or pull requests

2 participants