philbot999
philbot999

Reputation: 65

How to handle GoLang Fyne Window closing as part of larger systray application on Windows

I am using GoLang fyne and systray to develop a UI application on Windows 11. I want the core of the application to be a system tray app that will pop open a GoLang fyne window when the user clicks on it.

It is mostly working, except when the app opens the fyne window and I click on the red 'x' to close the window, the entire application crashes with the following error. I have tried recreating the app in a loop at the bottom of main, however that results in a 'GLFW not initialized' error.

How can I keep the systray overarching application running, while allowing the user to open and close the fyne windows at will? Thanks.

Error:

panic: close of closed channel

goroutine 1 [running, locked to thread]:
fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).runGL(0x7ff756905917?)
    C:/Users/asdf/go/pkg/mod/fyne.io/fyne/[email protected]/internal/driver/glfw/loop.go:111 +0x4e
fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).Run(0xc00030c0d0)
    C:/Users/asdf/go/pkg/mod/fyne.io/fyne/[email protected]/internal/driver/glfw/driver.go:164 +0x73
fyne.io/fyne/v2/app.(*fyneApp).Run(0xc000328000)
    C:/Users/asdf/go/pkg/mod/fyne.io/fyne/[email protected]/app/app.go:68 +0x65
main.main()

Code:

package main

import (
    icon "company/winres"
    "fmt"
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/container"
    "fyne.io/fyne/v2/layout"
    "fyne.io/fyne/v2/widget"
    "github.com/getlantern/systray"
    "github.com/k0kubun/pp/v3"
)

// Create a channel to communicate between goroutines for launching windows
var ch = make(chan string)

// Define the top level app
var mainApp fyne.App

func main() {

    // Create a struct describing your scheme
    scheme := pp.ColorScheme{
        Integer:         pp.Yellow | pp.Bold,
        String:          pp.Green | pp.BackgroundBlack | pp.Bold,
        StringQuotation: pp.Green | pp.BackgroundBlack | pp.Bold,
    }

    // Register it for usage
    pp.Default.SetColorScheme(scheme)

    // Print startup message
    pp.Println("Starting Application now ...")

    // Run the system tray application
    systray.Register(onReady, onExit)

    // Define the main application and initial window
    pp.Println("Creating app and window")
    mainApp = app.New()
    window := mainApp.NewWindow("Backup Information")
    window.Resize(fyne.NewSize(256, 256))

    yes := widget.NewButton("Yes", closeCallback)
    yes.Importance = widget.HighImportance
    window.SetContent(container.NewVBox(
        widget.NewLabel("Are you sure you want to interact with this test dialog?"),
        container.NewHBox(layout.NewSpacer(),
            widget.NewButton("No", closeCallback), yes,
            layout.NewSpacer())))

    // Process commuication from tray inputs
    go func() {

        for {

            // Main goroutine waits for a message from the background goroutine
            msg := <-ch
            fmt.Println(msg)

            // Show the window
            window.Show()

        }

    }()

    // Wait for taskbar inputs
    for {

        // Run the fyne app
        pp.Println("Running now ...")
        mainApp.Run()
        pp.Println("Done running ...")

    }

}

func closeCallback() {
    mainApp.Quit()
}

func onReady() {

    // Setup the tray icons
    systray.SetIcon(icon.Data)
    systray.SetTemplateIcon(icon.Data, icon.Data)
    systray.SetTitle("asdf")

    // Define actions
    mInformation := systray.AddMenuItem("Info", "Information")
    mBackupNow := systray.AddMenuItem("Now", "Immediately start")
    mQuit := systray.AddMenuItem("Quit", "Quit")

    // Handle actions
    go func() {
        for {
            select {
            case <-mInformation.ClickedCh:
                pp.Println("User chose to view information")

                ch <- "Hello from background goroutine"

            case <-mBackupNow.ClickedCh:
                pp.Println("User chose to start backup")
            case <-mQuit.ClickedCh:
                systray.Quit()
            }
        }
    }()

}

func onExit() {
    pp.Println("Exiting now ...")
}

Upvotes: 1

Views: 230

Answers (2)

Abdul-Hameed Riad
Abdul-Hameed Riad

Reputation: 56

You can use "SetCloseIntercept" event to hide the window when the user tries to close it.

window.SetCloseIntercept(func() {
    window.Hide()
})

Upvotes: 0

Andy
Andy

Reputation: 11

Don’t Run and Quit the app multiple times, just call “Run()” at the end of your main…

That way the app will run through window show and hide and only exit when you quit it.

P.s. life will be easier if you use the Fyne abstraction for systray instead of interacting with that library directly (you are multiplexing multiple run loops).

Good example at https://docs.fyne.io/explore/systray

Upvotes: 0

Related Questions