getlantern / systray

a cross platfrom Go library to place an icon and menu in the notification area
Apache License 2.0
3.24k stars 446 forks source link

Help with ClickedCh in a loop #152

Open konsumer opened 4 years ago

konsumer commented 4 years ago

Forgive me, if this is basic go stuff, I am new.

I am trying to make a menu from a loop over JSON definition.

{
  "title": "My App",
  "icon": "icon.png",
  "debug": true,
  "url": "/",
  "tray": {
    "title": "My App",
    "icon": "icon.png",
    "menu": [
      {"title": "About", "url": "/about", "tooltip": "About this application"},
      {"title": "Quit", "url": "/_api/exit", "tooltip": "Quit this application"}
    ]
  }
}

The idea with tray.menu is that it's a list of items with title, tooltip, and a URL.

I can parse the JSON ok, with this def:

// Config - settings format
type Config struct {
    Title  string `json:"title"`
    Icon   string `json:"icon"`
    URL    string `json:"url"`
    Debug  bool   `json:"debug"`
    Tray   Tray   `json:"tray"`
    Width  int    `json:"width"`
    Height int    `json:"height"`
}

// Tray - definition of Tray-menu
type Tray struct {
    Title string     `json:"title"`
    Icon  string     `json:"icon"`
    Menu  []TrayItem `json:"menu"`
}

// TrayItem - single tray-menu item
type TrayItem struct {
    Title   string `json:"title"`
    URL     string `json:"url"`
    Tooltip string `json:"tooltip"`
}

When I try to loop over the menu, I can build it fine, but if I try to make a click-handler, it fails when I select something:

func onTrayReady() {
    systray.SetTitle(config.Tray.Title)
    for _, item := range config.Tray.Menu {
        mItem := systray.AddMenuItem(item.Title, item.Tooltip)
        go func() {
            <-mItem.ClickedCh
            w.Navigate(item.URL)
        }()
    }
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x6f25be]

goroutine 6 [running]:
main.onTrayReady.func1(0xc000076200, 0xc0000990b0)
        /home/konsumer/.go/src/github.com/konsumer/wui/server/main.go:150 +0x3e
created by main.onTrayReady
        /home/konsumer/.go/src/github.com/konsumer/wui/server/main.go:148 +0x129
exit status 2
make: *** [Makefile:4: run] Error 1
konsumer commented 4 years ago

I realized that if I just put a plain w.Navigate("http://github.com") in same scope, I had same issue, which led me to the idea that I need to complete the init of w (the webview) before the callback. Doing that made it not error, but it still isn't calling Navigate on menu item, and only seems to be calling my callback once:

func onTrayReady() {
    systray.SetTitle(config.Tray.Title)
    for _, item := range config.Tray.Menu {
        mItem := systray.AddMenuItem(item.Title, item.Tooltip)
        go func(item TrayItem) {
            <-mItem.ClickedCh
            fmt.Println(item.Title)
            w.Navigate(item.URL)
        }(item)
    }
}

Peek 2020-06-08 16-03

mattdavis0351 commented 4 years ago

I am running into the same problem. On first click the menu item behaves as expected. All subsequent clicks are useless and do nothing.

@konsumer were you able to find a solution to this yet?

konsumer commented 4 years ago

Nope, I didn't figure it out.

safelock commented 4 years ago

If anyone is still having this issue, I have a solution, at least it works for my case. With regards to this function:

go func() {
      <-mItem.ClickedCh
      w.Navigate(item.URL)
}()

We need to use select to read the channel without blocking it (or at least to get desired behavior). Modify the above function to be...

go func() {
      for {
            select {
            case <-mItem.ClickedCh:
                  w.Navigate(item.URL)
            }
      }
}()

@konsumer see if this works for you.