From 4835f350ffe6b4fa589748e808fb84cc7c53e103 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 8 May 2019 11:52:18 +0200 Subject: ui: get correctly sized system icons Signed-off-by: Jason A. Donenfeld --- ui/aboutdialog.go | 5 +++- ui/confview.go | 24 +++++++++++-------- ui/iconprovider.go | 69 ++++++++++++++++-------------------------------------- ui/listview.go | 2 +- ui/managewindow.go | 7 ++++-- ui/tray.go | 24 +++++++++++++------ ui/tunnelspage.go | 10 ++++---- ui/ui.go | 5 ---- ui/updatepage.go | 9 +++---- 9 files changed, 71 insertions(+), 84 deletions(-) diff --git a/ui/aboutdialog.go b/ui/aboutdialog.go index da8f7f9f..22148ac1 100644 --- a/ui/aboutdialog.go +++ b/ui/aboutdialog.go @@ -25,7 +25,10 @@ func onAbout(owner walk.Form) { dlg, _ := walk.NewDialogWithFixedSize(owner) dlg.SetTitle("About WireGuard") dlg.SetLayout(vbl) - dlg.SetIcon(iconProvider.wireguardIcon) + wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{dlg.DPI()/3, dlg.DPI()/3}) //TODO: calculate DPI dynamically + if err == nil { + dlg.SetIcon(wireguardIcon) + } font, _ := walk.NewFont("Segoe UI", 9, 0) dlg.SetFont(font) diff --git a/ui/confview.go b/ui/confview.go index 515cadde..dbbe0ab4 100644 --- a/ui/confview.go +++ b/ui/confview.go @@ -79,17 +79,17 @@ func (lsl *labelStatusLine) widgets() (walk.Widget, walk.Widget) { } func (lsl *labelStatusLine) update(state service.TunnelState) { - icon, err := iconProvider.IconForState(state) + 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 { - 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 canvas.DrawImageStretched(icon, imageRect) icon.Dispose() canvas.Dispose() @@ -99,7 +99,11 @@ func (lsl *labelStatusLine) update(state service.TunnelState) { prior.Dispose() } } else { + prior := lsl.statusImage.Image() lsl.statusImage.SetImage(nil) + if prior != nil { + prior.Dispose() + } } s, e := lsl.statusLabel.TextSelection() switch state { diff --git a/ui/iconprovider.go b/ui/iconprovider.go index ac373d90..e2647939 100644 --- a/ui/iconprovider.go +++ b/ui/iconprovider.go @@ -14,41 +14,14 @@ import ( "path" ) -type IconProvider struct { - wireguardIcon *walk.Icon - overlayIconsByState map[service.TunnelState]*walk.Icon -} - -func NewIconProvider() (*IconProvider, error) { - tsip := &IconProvider{overlayIconsByState: make(map[service.TunnelState]*walk.Icon)} - var err error - if tsip.wireguardIcon, err = walk.NewIconFromResource("$wireguard.ico"); err != nil { +func iconWithOverlayForState(state service.TunnelState, size int) (*walk.Icon, error) { + wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{size, size}) + if err != nil { return nil, err } - return tsip, nil -} - -func (tsip *IconProvider) Dispose() { - if tsip.overlayIconsByState != nil { - for _, icon := range tsip.overlayIconsByState { - icon.Dispose() - } - tsip.overlayIconsByState = nil - } - if tsip.wireguardIcon != nil { - tsip.wireguardIcon.Dispose() - tsip.wireguardIcon = nil - } -} - -func (tsip *IconProvider) IconWithOverlayForState(state service.TunnelState) (*walk.Icon, error) { - if icon, ok := tsip.overlayIconsByState[state]; ok { - return icon, nil - } - - size := tsip.wireguardIcon.Size() - - bmp, err := walk.NewBitmapWithTransparentPixels(size) + defer wireguardIcon.Dispose() + iconSize := wireguardIcon.Size() + bmp, err := walk.NewBitmapWithTransparentPixels(iconSize) if err != nil { return nil, err } @@ -60,20 +33,18 @@ func (tsip *IconProvider) IconWithOverlayForState(state service.TunnelState) (*w } defer canvas.Dispose() - if err := canvas.DrawImage(tsip.wireguardIcon, walk.Point{}); err != nil { + if err := canvas.DrawImage(wireguardIcon, walk.Point{}); err != nil { return nil, err } - overlayIcon, err := tsip.IconForState(state) + w := int(float64(iconSize.Width) * 0.65) + h := int(float64(iconSize.Height) * 0.65) + bounds := walk.Rectangle{iconSize.Width - w, iconSize.Height - h, w, h} + overlayIcon, err := iconForState(state, bounds.Width) if err != nil { return nil, err } defer overlayIcon.Dispose() - - w := int(float64(size.Width) * 0.65) - h := int(float64(size.Height) * 0.65) - bounds := walk.Rectangle{size.Width - w, size.Height - h, w, h} - if err := canvas.DrawImageStretched(overlayIcon, bounds); err != nil { return nil, err } @@ -83,32 +54,32 @@ func (tsip *IconProvider) IconWithOverlayForState(state service.TunnelState) (*w if err != nil { return nil, err } - tsip.overlayIconsByState[state] = icon return icon, nil } -func (tsip *IconProvider) IconForState(state service.TunnelState) (icon *walk.Icon, err error) { +func iconForState(state service.TunnelState, size int) (icon *walk.Icon, err error) { switch state { case service.TunnelStarted: - icon, err = loadSystemIcon("imageres", 101) + icon, err = loadSystemIcon("imageres", 101, size) case service.TunnelStopped: - icon, err = walk.NewIconFromResource("dot-gray.ico") //TODO: replace with real icon + icon, err = walk.NewIconFromResourceWithSize("dot-gray.ico", walk.Size{size, size}) //TODO: replace with real icon default: - icon, err = loadSystemIcon("shell32", 238) //TODO: this doesn't look that great overlayed on the app icon + icon, err = loadSystemIcon("shell32", 238, size) //TODO: this doesn't look that great overlayed on the app icon } return } -func loadSystemIcon(dll string, index uint) (*walk.Icon, error) { +func loadSystemIcon(dll string, index int32, size int) (*walk.Icon, error) { system32, err := windows.GetSystemDirectory() if err != nil { return nil, err } - hicon := win.ExtractIcon(win.GetModuleHandle(nil), windows.StringToUTF16Ptr(path.Join(system32, dll+".dll")), int32(index)) - if hicon <= 1 { - return nil, fmt.Errorf("Unable to find icon %d of %s", index, dll) + 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 walk.NewIconFromHICON(hicon) } diff --git a/ui/listview.go b/ui/listview.go index 814a799b..0a15c5da 100644 --- a/ui/listview.go +++ b/ui/listview.go @@ -132,7 +132,7 @@ func (tv *ListView) StyleCell(style *walk.CellStyle) { b.Y += margin b.Height -= margin * 2 b.Width = b.Height - icon, err := iconProvider.IconForState(state) + icon, err := iconForState(state, b.Size().Width) if err != nil { return } diff --git a/ui/managewindow.go b/ui/managewindow.go index 6d8c9701..5b8279d9 100644 --- a/ui/managewindow.go +++ b/ui/managewindow.go @@ -36,7 +36,10 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) { } disposables.Add(mtw) - mtw.SetIcon(iconProvider.wireguardIcon) + wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{mtw.DPI()/3, mtw.DPI()/3}) //TODO: calculate DPI dynamically + if err == nil { + mtw.SetIcon(wireguardIcon) + } mtw.SetTitle("WireGuard") font, err := walk.NewFont("Segoe UI", 9, 0) if err != nil { @@ -94,7 +97,7 @@ func (mtw *ManageTunnelsWindow) Dispose() { func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, globalState service.TunnelState, err error) { mtw.Synchronize(func() { - icon, err2 := iconProvider.IconWithOverlayForState(globalState) + icon, err2 := iconWithOverlayForState(globalState, mtw.DPI()/3) //TODO: calculate DPI dynamically if err2 == nil { mtw.SetIcon(icon) } diff --git a/ui/tray.go b/ui/tray.go index 10fb350e..3cf0f8bc 100644 --- a/ui/tray.go +++ b/ui/tray.go @@ -54,7 +54,10 @@ func (tray *Tray) setup() error { tray.SetToolTip("WireGuard: Deactivated") tray.SetVisible(true) - tray.SetIcon(iconProvider.wireguardIcon) + 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) + } tray.MouseDown().Attach(func(x, y int, button walk.MouseButton) { if button == walk.LeftButton { @@ -216,8 +219,12 @@ func (tray *Tray) onTunnelChange(tunnel *service.Tunnel, state service.TunnelSta } func (tray *Tray) updateGlobalState(globalState service.TunnelState) { - if icon, err := iconProvider.IconWithOverlayForState(globalState); err == nil { + 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() @@ -286,14 +293,14 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta tunnelAction.SetEnabled(true) tunnelAction.SetChecked(true) if !wasChecked && showNotifications { - icon, _ := iconProvider.IconWithOverlayForState(state) + icon, _ := iconWithOverlayForState(state, tray.mtw.DPI()*4/3) //TODO: calculate dpi dynamically tray.ShowCustom("WireGuard Activated", fmt.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) } case service.TunnelStopped: tunnelAction.SetChecked(false) if wasChecked && showNotifications { - icon, _ := loadSystemIcon("imageres", 26) //TODO: this icon isn't very good... + 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) } @@ -303,8 +310,10 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta func (tray *Tray) UpdateFound() { action := walk.NewAction() action.SetText("An Update is Available!") - icon, _ := loadSystemIcon("imageres", 1) - bitmap, _ := walk.NewBitmapFromIcon(icon, walk.Size{tray.mtw.DPI() / 6, tray.mtw.DPI() / 6}) //TODO: This should use dynamic DPI. + 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() { @@ -319,8 +328,9 @@ func (tray *Tray) UpdateFound() { tray.ContextMenu().Actions().Insert(tray.ContextMenu().Actions().Len()-2, action) 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) - defer icon.Dispose() + icon.Dispose() } timeSinceStart := time.Now().Sub(startTime) diff --git a/ui/tunnelspage.go b/ui/tunnelspage.go index de7560c4..4df9938b 100644 --- a/ui/tunnelspage.go +++ b/ui/tunnelspage.go @@ -127,7 +127,7 @@ func (tp *TunnelsPage) CreateToolbar() { tp.AddDisposable(addMenu) importAction := walk.NewAction() importAction.SetText("Import tunnel(s) from file...") - importActionIcon, _ := loadSystemIcon("imageres", 3) + importActionIcon, _ := loadSystemIcon("imageres", 3, imageSize.Width) importActionImage, _ := walk.NewBitmapFromIcon(importActionIcon, imageSize) importAction.SetImage(importActionImage) importAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyO}) @@ -135,7 +135,7 @@ func (tp *TunnelsPage) CreateToolbar() { importAction.Triggered().Attach(tp.onImport) addAction := walk.NewAction() addAction.SetText("Add empty tunnel...") - addActionIcon, _ := loadSystemIcon("imageres", 2) + addActionIcon, _ := loadSystemIcon("imageres", 2, imageSize.Width) addActionImage, _ := walk.NewBitmapFromIcon(addActionIcon, imageSize) addAction.SetImage(addActionImage) addAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyN}) @@ -143,7 +143,7 @@ func (tp *TunnelsPage) CreateToolbar() { addMenu.Actions().Add(importAction) addMenu.Actions().Add(addAction) addMenuAction := walk.NewMenuAction(addMenu) - addMenuActionIcon, _ := loadSystemIcon("shell32", 149) + addMenuActionIcon, _ := loadSystemIcon("shell32", 149, imageSize.Width) addMenuActionImage, _ := walk.NewBitmapFromIcon(addMenuActionIcon, imageSize) addMenuAction.SetImage(addMenuActionImage) addMenuAction.SetText("Add Tunnel") @@ -154,7 +154,7 @@ func (tp *TunnelsPage) CreateToolbar() { tp.listToolbar.Actions().Add(walk.NewSeparatorAction()) deleteAction := walk.NewAction() - deleteActionIcon, _ := loadSystemIcon("shell32", 131) + deleteActionIcon, _ := loadSystemIcon("shell32", 131, imageSize.Width) deleteActionImage, _ := walk.NewBitmapFromIcon(deleteActionIcon, imageSize) deleteAction.SetImage(deleteActionImage) deleteAction.SetShortcut(walk.Shortcut{0, walk.KeyDelete}) @@ -164,7 +164,7 @@ func (tp *TunnelsPage) CreateToolbar() { tp.listToolbar.Actions().Add(walk.NewSeparatorAction()) exportAction := walk.NewAction() - exportActionIcon, _ := loadSystemIcon("imageres", 165) // Or "shell32", 45? + exportActionIcon, _ := loadSystemIcon("imageres", 165, imageSize.Width) // Or "shell32", 45? exportActionImage, _ := walk.NewBitmapFromIcon(exportActionIcon, imageSize) exportAction.SetImage(exportActionImage) exportAction.SetToolTip("Export all tunnels to zip...") diff --git a/ui/ui.go b/ui/ui.go index c5fa03c6..b662f96d 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -14,8 +14,6 @@ import ( "time" ) -var iconProvider *IconProvider - var shouldQuitManagerWhenExiting = false var startTime = time.Now() @@ -36,8 +34,6 @@ func RunUI() { tray *Tray ) - iconProvider, _ = NewIconProvider() - for mtw == nil { mtw, err = NewManageTunnelsWindow() if err != nil { @@ -83,7 +79,6 @@ func RunUI() { mtw.Run() tray.Dispose() mtw.Dispose() - iconProvider.Dispose() if shouldQuitManagerWhenExiting { _, err := service.IPCClientQuit(true) diff --git a/ui/updatepage.go b/ui/updatepage.go index 8c01efe4..a5471e9f 100644 --- a/ui/updatepage.go +++ b/ui/updatepage.go @@ -26,9 +26,10 @@ func NewUpdatePage() (*UpdatePage, error) { up.SetTitle("An Update is Available!") - tabIcon, _ := loadSystemIcon("imageres", 1) + iconSize := up.DPI() / 6 + tabIcon, _ := loadSystemIcon("imageres", 1, iconSize) defer tabIcon.Dispose() - bitmap, _ := walk.NewBitmapFromIcon(tabIcon, walk.Size{up.DPI() / 6, up.DPI() / 6}) //TODO: this should use dynamic DPI + bitmap, _ := walk.NewBitmapFromIcon(tabIcon, walk.Size{iconSize, iconSize}) //TODO: this should use dynamic DPI up.SetImage(bitmap) //TODO: make title bold @@ -46,8 +47,8 @@ func NewUpdatePage() (*UpdatePage, error) { bar.SetVisible(false) button, _ := walk.NewPushButton(up) - updateIcon, _ := loadSystemIcon("shell32", 46) - button.SetImage(updateIcon) //TODO: the placement of this looks sort of weird + updateIcon, _ := loadSystemIcon("shell32", 46, bar.HeightPixels()) + button.SetImage(updateIcon) button.SetText("Update Now") walk.NewVSpacer(up) -- cgit v1.2.3-59-g8ed1b