diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-11-11 14:43:48 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-11-11 15:01:50 +0100 |
commit | ddd7d446b2b202d37edb51442aedb78e622b71e2 (patch) | |
tree | 32878a96004591dd05534a7c86ca1b70c1c4d924 | |
parent | conf: move configuration to C:\ProgramData\WireGuard (diff) | |
download | wireguard-windows-ddd7d446b2b202d37edb51442aedb78e622b71e2.tar.xz wireguard-windows-ddd7d446b2b202d37edb51442aedb78e622b71e2.zip |
ui: remove systray popup menu tunnel list
Should user have a huge list of tunnels, the menu becomes impossible to
navigate. In the absence of any better idea, how to make the popup menu
usable in such tunnel quantities, the tunnel activation via systray
popup menu was removed, and instead we have a single deactivate button.
Signed-off-by: Simon Rozman <simon@rozman.si>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | ui/tray.go | 233 |
1 files changed, 55 insertions, 178 deletions
@@ -6,41 +6,26 @@ package ui import ( - "sort" "strings" "time" - "golang.zx2c4.com/wireguard/windows/conf" "golang.zx2c4.com/wireguard/windows/l18n" "golang.zx2c4.com/wireguard/windows/manager" "github.com/lxn/walk" ) -// Status + active CIDRs + separator -const trayTunnelActionsOffset = 3 - type Tray struct { *walk.NotifyIcon - - // Current known tunnels by name - tunnels map[string]*walk.Action - - mtw *ManageTunnelsWindow - - tunnelChangedCB *manager.TunnelChangeCallback - tunnelsChangedCB *manager.TunnelsChangeCallback - - clicked func() + mtw *ManageTunnelsWindow + tunnelChangedCB *manager.TunnelChangeCallback + clicked func() } func NewTray(mtw *ManageTunnelsWindow) (*Tray, error) { var err error - tray := &Tray{ - mtw: mtw, - tunnels: make(map[string]*walk.Action), - } + tray := &Tray{mtw: mtw} tray.NotifyIcon, err = walk.NewNotifyIcon(mtw) if err != nil { @@ -78,6 +63,7 @@ func (tray *Tray) setup() error { }{ {label: l18n.Sprintf("Status: Unknown")}, {label: l18n.Sprintf("Addresses: None"), hidden: true}, + {label: l18n.Sprintf("&Deactivate"), handler: tray.onDeactivateTunnel, enabled: true, hidden: true}, {separator: true}, {separator: true}, {label: l18n.Sprintf("&Manage tunnels…"), handler: tray.onManageTunnels, enabled: true, defawlt: true}, @@ -103,8 +89,6 @@ func (tray *Tray) setup() error { tray.ContextMenu().Actions().Add(action) } tray.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(tray.onTunnelChange) - tray.tunnelsChangedCB = manager.IPCClientRegisterTunnelsChange(tray.onTunnelsChange) - tray.onTunnelsChange() globalState, _ := manager.IPCClientGlobalState() tray.updateGlobalState(globalState) @@ -116,103 +100,23 @@ func (tray *Tray) Dispose() error { tray.tunnelChangedCB.Unregister() tray.tunnelChangedCB = nil } - if tray.tunnelsChangedCB != nil { - tray.tunnelsChangedCB.Unregister() - tray.tunnelsChangedCB = nil - } return tray.NotifyIcon.Dispose() } -func (tray *Tray) onTunnelsChange() { - tunnels, err := manager.IPCClientTunnels() - if err != nil { - return - } - tray.mtw.Synchronize(func() { - tunnelSet := make(map[string]bool, len(tunnels)) - for _, tunnel := range tunnels { - tunnelSet[tunnel.Name] = true - if tray.tunnels[tunnel.Name] == nil { - tray.addTunnelAction(&tunnel) - } - } - for trayTunnel := range tray.tunnels { - if !tunnelSet[trayTunnel] { - tray.removeTunnelAction(trayTunnel) - } - } - }) -} - -func (tray *Tray) addTunnelAction(tunnel *manager.Tunnel) { - tunnelAction := walk.NewAction() - tunnelAction.SetText(tunnel.Name) - tunnelAction.SetEnabled(true) - tunnelAction.SetCheckable(true) - tclosure := *tunnel - tunnelAction.Triggered().Attach(func() { - tunnelAction.SetChecked(!tunnelAction.Checked()) - go func() { - oldState, err := tclosure.Toggle() - if err != nil { - tray.mtw.Synchronize(func() { - raise(tray.mtw.Handle()) - tray.mtw.tunnelsPage.listView.selectTunnel(tclosure.Name) - tray.mtw.tabs.SetCurrentIndex(0) - if oldState == manager.TunnelUnknown { - showErrorCustom(tray.mtw, l18n.Sprintf("Failed to determine tunnel state"), err.Error()) - } else if oldState == manager.TunnelStopped { - showErrorCustom(tray.mtw, l18n.Sprintf("Failed to activate tunnel"), err.Error()) - } else if oldState == manager.TunnelStarted { - showErrorCustom(tray.mtw, l18n.Sprintf("Failed to deactivate tunnel"), err.Error()) - } - }) - } - }() - }) - tray.tunnels[tunnel.Name] = tunnelAction - - var names []string - for name := range tray.tunnels { - names = append(names, name) - } - sort.SliceStable(names, func(i, j int) bool { - return conf.TunnelNameIsLess(names[i], names[j]) - }) - - var ( - idx int - name string - ) - for idx, name = range names { - if name == tunnel.Name { - break - } - } - - tray.ContextMenu().Actions().Insert(trayTunnelActionsOffset+idx, tunnelAction) - - go func() { - state, err := tclosure.State() - if err != nil { - return - } - tray.mtw.Synchronize(func() { - tray.SetTunnelState(&tclosure, state, false) - }) - }() -} - -func (tray *Tray) removeTunnelAction(tunnelName string) { - tray.ContextMenu().Actions().Remove(tray.tunnels[tunnelName]) - delete(tray.tunnels, tunnelName) -} - func (tray *Tray) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { tray.mtw.Synchronize(func() { tray.updateGlobalState(globalState) - tray.SetTunnelState(tunnel, state, err == nil) - if !tray.mtw.Visible() && err != nil { + if err == nil { + switch state { + case manager.TunnelStarted: + icon, _ := iconWithOverlayForState(state, 128) + tray.ShowCustom(l18n.Sprintf("WireGuard Activated"), l18n.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) + + case manager.TunnelStopped: + icon, _ := loadSystemIcon("imageres", 26, 128) // TODO: this icon isn't very good... + tray.ShowCustom(l18n.Sprintf("WireGuard Deactivated"), l18n.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon) + } + } else if !tray.mtw.Visible() { tray.ShowError(l18n.Sprintf("WireGuard Tunnel Error"), err.Error()) } }) @@ -225,80 +129,34 @@ func (tray *Tray) updateGlobalState(globalState manager.TunnelState) { actions := tray.ContextMenu().Actions() statusAction := actions.At(0) - activeCIDRsAction := actions.At(1) - - setTunnelActionsEnabled := func(enabled bool) { - for i := 0; i < len(tray.tunnels); i++ { - action := actions.At(trayTunnelActionsOffset + i) - action.SetEnabled(enabled) - } - } + deactivateTunnelAction := actions.At(2) tray.SetToolTip(l18n.Sprintf("WireGuard: %s", textForState(globalState, true))) stateText := textForState(globalState, false) statusAction.SetText(l18n.Sprintf("Status: %s", stateText)) - - switch globalState { - case manager.TunnelStarting: - setTunnelActionsEnabled(false) - - case manager.TunnelStarted: - activeCIDRsAction.SetVisible(true) - setTunnelActionsEnabled(true) - - case manager.TunnelStopping: - setTunnelActionsEnabled(false) - - case manager.TunnelStopped: - activeCIDRsAction.SetVisible(false) - setTunnelActionsEnabled(true) - } -} - -func (tray *Tray) SetTunnelState(tunnel *manager.Tunnel, state manager.TunnelState, showNotifications bool) { - tunnelAction := tray.tunnels[tunnel.Name] - if tunnelAction == nil { - return - } - - actions := tray.ContextMenu().Actions() - activeCIDRsAction := actions.At(1) - - wasChecked := tunnelAction.Checked() - - switch state { - case manager.TunnelStarted: - activeCIDRsAction.SetText("") - go func() { - config, err := tunnel.RuntimeConfig() - if err == nil { - var sb strings.Builder - for i, addr := range config.Interface.Addresses { - if i > 0 { - sb.WriteString(l18n.EnumerationSeparator()) + deactivateTunnelAction.SetVisible(globalState == manager.TunnelStarted) + go func() { + var addrs []string + tunnels, err := manager.IPCClientTunnels() + if err == nil { + for i := range tunnels { + state, err := tunnels[i].State() + if err == nil && state == manager.TunnelStarted { + config, err := tunnels[i].RuntimeConfig() + if err == nil { + for _, addr := range config.Interface.Addresses { + addrs = append(addrs, addr.String()) + } } - - sb.WriteString(addr.String()) } - tray.mtw.Synchronize(func() { - activeCIDRsAction.SetText(l18n.Sprintf("Addresses: %s", sb.String())) - }) } - }() - tunnelAction.SetEnabled(true) - tunnelAction.SetChecked(true) - if !wasChecked && showNotifications { - icon, _ := iconWithOverlayForState(state, 128) - tray.ShowCustom(l18n.Sprintf("WireGuard Activated"), l18n.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) } - - case manager.TunnelStopped: - tunnelAction.SetChecked(false) - if wasChecked && showNotifications { - icon, _ := loadSystemIcon("imageres", 26, 128) // TODO: this icon isn't very good... - tray.ShowCustom(l18n.Sprintf("WireGuard Deactivated"), l18n.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon) - } - } + tray.mtw.Synchronize(func() { + activeCIDRsAction := tray.ContextMenu().Actions().At(1) + activeCIDRsAction.SetText(l18n.Sprintf("Addresses: %s", strings.Join(addrs, l18n.EnumerationSeparator()))) + activeCIDRsAction.SetVisible(len(addrs) > 0) + }) + }() } func (tray *Tray) UpdateFound() { @@ -333,6 +191,25 @@ func (tray *Tray) UpdateFound() { } } +func (tray *Tray) onDeactivateTunnel() { + go func() { + tunnels, err := manager.IPCClientTunnels() + if err == nil { + for i := range tunnels { + state, err := tunnels[i].State() + if err == nil && state != manager.TunnelStopped { + err = tunnels[i].Stop() + } + } + } + if err != nil { + tray.mtw.Synchronize(func() { + showErrorCustom(tray.mtw, l18n.Sprintf("Failed to deactivate tunnel"), err.Error()) + }) + } + }() +} + func (tray *Tray) onManageTunnels() { tray.mtw.tunnelsPage.listView.SelectFirstActiveTunnel() tray.mtw.tabs.SetCurrentIndex(0) |