aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-05-10 11:00:16 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2019-05-10 11:50:34 +0200
commit0ba51d151035eb80512d20eb5285ff84f1b4deab (patch)
tree8f65c9637aff82014ce02adb1d6420734464bcf1 /ui
parentservice: fix user logout (diff)
downloadwireguard-windows-0ba51d151035eb80512d20eb5285ff84f1b4deab.tar.xz
wireguard-windows-0ba51d151035eb80512d20eb5285ff84f1b4deab.zip
ui: somewhat aggressively cache icons
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui')
-rw-r--r--ui/aboutdialog.go10
-rw-r--r--ui/confview.go50
-rw-r--r--ui/iconprovider.go92
-rw-r--r--ui/listview.go1
-rw-r--r--ui/managewindow.go5
-rw-r--r--ui/tray.go12
-rw-r--r--ui/updatepage.go2
7 files changed, 107 insertions, 65 deletions
diff --git a/ui/aboutdialog.go b/ui/aboutdialog.go
index fe0639d5..1c4528dc 100644
--- a/ui/aboutdialog.go
+++ b/ui/aboutdialog.go
@@ -25,23 +25,23 @@ func onAbout(owner walk.Form) {
dlg, _ := walk.NewDialogWithFixedSize(owner)
dlg.SetTitle("About WireGuard")
dlg.SetLayout(vbl)
- wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{dlg.DPI() / 3, dlg.DPI() / 3}) //TODO: calculate DPI dynamically
- if err == nil {
- dlg.SetIcon(wireguardIcon)
+ if icon, err := loadLogoIcon(dlg.DPI() / 3); err == nil { //TODO: calculate DPI dynamically
+ dlg.SetIcon(icon)
}
font, _ := walk.NewFont("Segoe UI", 9, 0)
dlg.SetFont(font)
iv, _ := walk.NewImageView(dlg)
- logo, _ := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{dlg.DPI() * 4 / 3, dlg.DPI() * 4 / 3}) //TODO: calculate DPI dynamically
iv.SetCursor(walk.CursorHand())
iv.MouseUp().Attach(func(x, y int, button walk.MouseButton) {
if button == walk.LeftButton {
win.ShellExecute(dlg.Handle(), nil, windows.StringToUTF16Ptr("https://www.wireguard.com/"), nil, nil, win.SW_SHOWNORMAL)
}
})
- iv.SetImage(logo)
+ if logo, err := loadLogoIcon(dlg.DPI() * 4 / 3); err == nil { //TODO: calculate DPI dynamically
+ iv.SetImage(logo)
+ }
wgLbl, _ := walk.NewTextLabel(dlg)
wgFont, _ := walk.NewFont("Segoe UI", 16, walk.FontBold)
diff --git a/ui/confview.go b/ui/confview.go
index ca14abea..b157406e 100644
--- a/ui/confview.go
+++ b/ui/confview.go
@@ -25,11 +25,18 @@ type widgetsLinesView interface {
widgetsLines() []widgetsLine
}
+type rectAndSizeAndState struct {
+ rect walk.Rectangle
+ size walk.Size
+ state service.TunnelState
+}
+
type labelStatusLine struct {
label *walk.TextLabel
statusComposite *walk.Composite
statusImage *walk.ImageView
statusLabel *walk.LineEdit
+ cachedImages map[rectAndSizeAndState]walk.Image
}
type labelTextLine struct {
@@ -80,31 +87,26 @@ func (lsl *labelStatusLine) widgets() (walk.Widget, walk.Widget) {
func (lsl *labelStatusLine) update(state service.TunnelState) {
margin := lsl.label.DPI() / 48 //TODO: Do some sort of dynamic DPI calculation here
- labelSize := lsl.label.SizeHint()
- imageRect := walk.Rectangle{0, 0, labelSize.Height, labelSize.Height}
- img, _ := walk.NewBitmapWithTransparentPixels(imageRect.Size())
- canvas, _ := walk.NewCanvasFromImage(img)
- imageRect.X += margin
- imageRect.Y += margin * 2 //TODO: the *2 here fixes weird alignment bugs. Why?
- imageRect.Height -= margin * 2
- imageRect.Width -= margin * 2
- icon, err := iconForState(state, imageRect.Size().Width)
- if err == nil {
- canvas.DrawImageStretched(icon, imageRect)
- icon.Dispose()
- canvas.Dispose()
- prior := lsl.statusImage.Image()
- lsl.statusImage.SetImage(img)
- if prior != nil {
- prior.Dispose()
- }
- } else {
- prior := lsl.statusImage.Image()
- lsl.statusImage.SetImage(nil)
- if prior != nil {
- prior.Dispose()
+ imageSize := lsl.label.SizeHint()
+ imageSize.Width = imageSize.Height
+ //TODO: the *2 in the Y coordinate fixes weird alignment bugs. Why?
+ imageRect := walk.Rectangle{margin, margin * 2, imageSize.Width - margin*2, imageSize.Height - margin*2}
+ img := lsl.cachedImages[rectAndSizeAndState{imageRect, imageSize, state}]
+ if img == nil {
+ img, _ = walk.NewBitmapWithTransparentPixels(imageSize)
+ canvas, _ := walk.NewCanvasFromImage(img)
+ icon, err := iconForState(state, imageRect.Size().Width)
+ if err == nil {
+ canvas.DrawImageStretched(icon, imageRect)
+ canvas.Dispose()
+ lsl.cachedImages[rectAndSizeAndState{imageRect, imageSize, state}] = img
+ } else {
+ canvas.Dispose()
+ img.Dispose()
+ img = nil
}
}
+ lsl.statusImage.SetImage(img)
s, e := lsl.statusLabel.TextSelection()
switch state {
case service.TunnelStarted:
@@ -128,6 +130,8 @@ func (lsl *labelStatusLine) update(state service.TunnelState) {
func newLabelStatusLine(parent walk.Container) *labelStatusLine {
lsl := new(labelStatusLine)
+ lsl.cachedImages = make(map[rectAndSizeAndState]walk.Image)
+
lsl.label, _ = walk.NewTextLabel(parent)
lsl.label.SetText("Status:")
lsl.label.SetTextAlignment(walk.AlignHFarVNear)
diff --git a/ui/iconprovider.go b/ui/iconprovider.go
index e2647939..9f6cc433 100644
--- a/ui/iconprovider.go
+++ b/ui/iconprovider.go
@@ -12,29 +12,47 @@ import (
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/service"
"path"
+ "syscall"
)
-func iconWithOverlayForState(state service.TunnelState, size int) (*walk.Icon, error) {
- wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{size, size})
+type widthAndState struct {
+ width int
+ state service.TunnelState
+}
+
+type widthAndDllIdx struct {
+ width int
+ idx int32
+ dll string
+}
+
+var cachedOverlayIconsForWidthAndState = make(map[widthAndState]*walk.Icon)
+
+func iconWithOverlayForState(state service.TunnelState, size int) (icon *walk.Icon, err error) {
+ icon = cachedOverlayIconsForWidthAndState[widthAndState{size, state}]
+ if icon != nil {
+ return
+ }
+ wireguardIcon, err := loadLogoIcon(size)
if err != nil {
- return nil, err
+ return
}
- defer wireguardIcon.Dispose()
iconSize := wireguardIcon.Size()
bmp, err := walk.NewBitmapWithTransparentPixels(iconSize)
if err != nil {
- return nil, err
+ return
}
defer bmp.Dispose()
canvas, err := walk.NewCanvasFromImage(bmp)
if err != nil {
- return nil, err
+ return
}
defer canvas.Dispose()
- if err := canvas.DrawImage(wireguardIcon, walk.Point{}); err != nil {
- return nil, err
+ err = canvas.DrawImage(wireguardIcon, walk.Point{})
+ if err != nil {
+ return
}
w := int(float64(iconSize.Width) * 0.65)
@@ -42,44 +60,76 @@ func iconWithOverlayForState(state service.TunnelState, size int) (*walk.Icon, e
bounds := walk.Rectangle{iconSize.Width - w, iconSize.Height - h, w, h}
overlayIcon, err := iconForState(state, bounds.Width)
if err != nil {
- return nil, err
+ return
}
defer overlayIcon.Dispose()
- if err := canvas.DrawImageStretched(overlayIcon, bounds); err != nil {
- return nil, err
+ err = canvas.DrawImageStretched(overlayIcon, bounds)
+ if err != nil {
+ return
}
canvas.Dispose()
- icon, err := walk.NewIconFromBitmap(bmp)
- if err != nil {
- return nil, err
+ icon, err = walk.NewIconFromBitmap(bmp)
+ if err == nil {
+ cachedOverlayIconsForWidthAndState[widthAndState{size, state}] = icon
}
- return icon, nil
+ return
}
+var cachedIconsForWidthAndState = make(map[widthAndState]*walk.Icon)
+
func iconForState(state service.TunnelState, size int) (icon *walk.Icon, err error) {
+ icon = cachedIconsForWidthAndState[widthAndState{size, state}]
+ if icon != nil {
+ return
+ }
switch state {
case service.TunnelStarted:
icon, err = loadSystemIcon("imageres", 101, size)
-
case service.TunnelStopped:
icon, err = walk.NewIconFromResourceWithSize("dot-gray.ico", walk.Size{size, size}) //TODO: replace with real icon
-
default:
icon, err = loadSystemIcon("shell32", 238, size) //TODO: this doesn't look that great overlayed on the app icon
}
+ if err == nil {
+ cachedIconsForWidthAndState[widthAndState{size, state}] = icon
+ }
return
}
-func loadSystemIcon(dll string, index int32, size int) (*walk.Icon, error) {
+var cachedSystemIconsForWidthAndDllIdx = make(map[widthAndDllIdx]*walk.Icon)
+
+func loadSystemIcon(dll string, index int32, size int) (icon *walk.Icon, err error) {
+ icon = cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}]
+ if icon != nil {
+ return
+ }
system32, err := windows.GetSystemDirectory()
if err != nil {
- return nil, err
+ return
}
var hicon win.HICON
ret := win.SHDefExtractIcon(windows.StringToUTF16Ptr(path.Join(system32, dll+".dll")), index, 0, &hicon, nil, uint32(size))
if ret != 0 {
- return nil, fmt.Errorf("Unable to find icon %d of %s due to error %d", index, dll, ret)
+ return nil, fmt.Errorf("Unable to find icon %d of %s: %v", index, dll, syscall.Errno(ret))
+ }
+ icon, err = walk.NewIconFromHICON(hicon)
+ if err == nil {
+ cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}] = icon
+ }
+ return
+}
+
+var cachedLogoIconsForWidth = make(map[int]*walk.Icon)
+
+func loadLogoIcon(size int) (icon *walk.Icon, err error) {
+ icon = cachedLogoIconsForWidth[size]
+ if icon != nil {
+ return
+ }
+ icon, err = walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{size, size})
+ if err == nil {
+ cachedLogoIconsForWidth[size] = icon
}
- return walk.NewIconFromHICON(hicon)
+ return
}
diff --git a/ui/listview.go b/ui/listview.go
index 0a15c5da..6870e91c 100644
--- a/ui/listview.go
+++ b/ui/listview.go
@@ -137,7 +137,6 @@ func (tv *ListView) StyleCell(style *walk.CellStyle) {
return
}
canvas.DrawImageStretched(icon, b)
- icon.Dispose()
}
func (tv *ListView) CurrentTunnel() *service.Tunnel {
diff --git a/ui/managewindow.go b/ui/managewindow.go
index 54d6b1e9..b13e0afd 100644
--- a/ui/managewindow.go
+++ b/ui/managewindow.go
@@ -36,9 +36,8 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) {
}
disposables.Add(mtw)
- wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{mtw.DPI() / 3, mtw.DPI() / 3}) //TODO: calculate DPI dynamically
- if err == nil {
- mtw.SetIcon(wireguardIcon)
+ if icon, err := loadLogoIcon(mtw.DPI() / 3); err == nil { //TODO: calculate DPI dynamically
+ mtw.SetIcon(icon)
}
mtw.SetTitle("WireGuard")
font, err := walk.NewFont("Segoe UI", 9, 0)
diff --git a/ui/tray.go b/ui/tray.go
index 3861f9c9..668163da 100644
--- a/ui/tray.go
+++ b/ui/tray.go
@@ -54,9 +54,8 @@ func (tray *Tray) setup() error {
tray.SetToolTip("WireGuard: Deactivated")
tray.SetVisible(true)
- wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{tray.mtw.DPI() / 6, tray.mtw.DPI() / 6}) //TODO: calculate DPI dynamically
- if err == nil {
- tray.SetIcon(wireguardIcon)
+ if icon, err := loadLogoIcon(tray.mtw.DPI() / 6); err == nil { //TODO: calculate DPI dynamically
+ tray.SetIcon(icon)
}
tray.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
@@ -220,11 +219,7 @@ func (tray *Tray) onTunnelChange(tunnel *service.Tunnel, state service.TunnelSta
func (tray *Tray) updateGlobalState(globalState service.TunnelState) {
if icon, err := iconWithOverlayForState(globalState, tray.mtw.DPI()/6); err == nil { //TODO: calculate DPI dynamically
- prior := tray.Icon()
tray.SetIcon(icon)
- if prior != nil {
- prior.Dispose()
- }
}
actions := tray.ContextMenu().Actions()
@@ -304,7 +299,6 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta
tunnelAction.SetChecked(false)
if wasChecked && showNotifications {
icon, _ := loadSystemIcon("imageres", 26, tray.mtw.DPI()*4/3) //TODO: this icon isn't very good..., also calculate dpi dynamically
- defer icon.Dispose()
tray.ShowCustom("WireGuard Deactivated", fmt.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon)
}
}
@@ -316,7 +310,6 @@ func (tray *Tray) UpdateFound() {
iconSize := tray.mtw.DPI() / 6 //TODO: This should use dynamic DPI.
menuIcon, _ := loadSystemIcon("imageres", 1, iconSize)
bitmap, _ := walk.NewBitmapFromIcon(menuIcon, walk.Size{iconSize, iconSize})
- menuIcon.Dispose()
action.SetImage(bitmap)
action.SetDefault(true)
showUpdateTab := func() {
@@ -333,7 +326,6 @@ func (tray *Tray) UpdateFound() {
showUpdateBalloon := func() {
icon, _ := loadSystemIcon("imageres", 1, tray.mtw.DPI()*4/3) //TODO: calculate DPI dynamically
tray.ShowCustom("WireGuard Update Available", "An update to WireGuard is now available. You are advised to update as soon as possible.", icon)
- icon.Dispose()
}
timeSinceStart := time.Now().Sub(startTime)
diff --git a/ui/updatepage.go b/ui/updatepage.go
index a5471e9f..b228fb2d 100644
--- a/ui/updatepage.go
+++ b/ui/updatepage.go
@@ -28,11 +28,9 @@ func NewUpdatePage() (*UpdatePage, error) {
iconSize := up.DPI() / 6
tabIcon, _ := loadSystemIcon("imageres", 1, iconSize)
- defer tabIcon.Dispose()
bitmap, _ := walk.NewBitmapFromIcon(tabIcon, walk.Size{iconSize, iconSize}) //TODO: this should use dynamic DPI
up.SetImage(bitmap)
- //TODO: make title bold
up.SetLayout(walk.NewVBoxLayout())
instructions, _ := walk.NewTextLabel(up)