golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/ui/iconprovider.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package ui
     7  
     8  import (
     9  	"github.com/lxn/walk"
    10  
    11  	"golang.zx2c4.com/wireguard/windows/l18n"
    12  	"golang.zx2c4.com/wireguard/windows/manager"
    13  )
    14  
    15  type widthAndState struct {
    16  	width int
    17  	state manager.TunnelState
    18  }
    19  
    20  type widthAndDllIdx struct {
    21  	width int
    22  	idx   int32
    23  	dll   string
    24  }
    25  
    26  var cachedOverlayIconsForWidthAndState = make(map[widthAndState]walk.Image)
    27  
    28  func iconWithOverlayForState(state manager.TunnelState, size int) (icon walk.Image, err error) {
    29  	icon = cachedOverlayIconsForWidthAndState[widthAndState{size, state}]
    30  	if icon != nil {
    31  		return
    32  	}
    33  
    34  	wireguardIcon, err := loadLogoIcon(size)
    35  	if err != nil {
    36  		return
    37  	}
    38  
    39  	if state == manager.TunnelStopped {
    40  		return wireguardIcon, err // TODO: if we find something prettier than the gray dot, then remove this clause
    41  	}
    42  
    43  	iconSize := wireguardIcon.Size()
    44  	w := int(float64(iconSize.Width) * 0.65)
    45  	h := int(float64(iconSize.Height) * 0.65)
    46  	overlayBounds := walk.Rectangle{iconSize.Width - w, iconSize.Height - h, w, h}
    47  	overlayIcon, err := iconForState(state, overlayBounds.Width)
    48  	if err != nil {
    49  		return
    50  	}
    51  
    52  	icon = walk.NewPaintFuncImage(walk.Size{size, size}, func(canvas *walk.Canvas, bounds walk.Rectangle) error {
    53  		if err := canvas.DrawImageStretched(wireguardIcon, bounds); err != nil {
    54  			return err
    55  		}
    56  		if err := canvas.DrawImageStretched(overlayIcon, overlayBounds); err != nil {
    57  			return err
    58  		}
    59  		return nil
    60  	})
    61  
    62  	cachedOverlayIconsForWidthAndState[widthAndState{size, state}] = icon
    63  
    64  	return
    65  }
    66  
    67  var cachedIconsForWidthAndState = make(map[widthAndState]*walk.Icon)
    68  
    69  func iconForState(state manager.TunnelState, size int) (icon *walk.Icon, err error) {
    70  	icon = cachedIconsForWidthAndState[widthAndState{size, state}]
    71  	if icon != nil {
    72  		return
    73  	}
    74  	switch state {
    75  	case manager.TunnelStarted:
    76  		icon, err = loadSystemIcon("imageres", -106, size)
    77  	case manager.TunnelStopped:
    78  		icon, err = walk.NewIconFromResourceIdWithSize(8, walk.Size{size, size}) // TODO: replace with real icon from imageres/shell32
    79  	default:
    80  		icon, err = loadSystemIcon("shell32", -16739, size) // TODO: this doesn't look that great overlayed on the app icon
    81  	}
    82  	if err == nil {
    83  		cachedIconsForWidthAndState[widthAndState{size, state}] = icon
    84  	}
    85  	return
    86  }
    87  
    88  func textForState(state manager.TunnelState, withEllipsis bool) (text string) {
    89  	switch state {
    90  	case manager.TunnelStarted:
    91  		text = l18n.Sprintf("Active")
    92  	case manager.TunnelStarting:
    93  		text = l18n.Sprintf("Activating")
    94  	case manager.TunnelStopped:
    95  		text = l18n.Sprintf("Inactive")
    96  	case manager.TunnelStopping:
    97  		text = l18n.Sprintf("Deactivating")
    98  	case manager.TunnelUnknown:
    99  		text = l18n.Sprintf("Unknown state")
   100  	}
   101  	if withEllipsis {
   102  		switch state {
   103  		case manager.TunnelStarting, manager.TunnelStopping:
   104  			text += "…"
   105  		}
   106  	}
   107  	return
   108  }
   109  
   110  var cachedSystemIconsForWidthAndDllIdx = make(map[widthAndDllIdx]*walk.Icon)
   111  
   112  func loadSystemIcon(dll string, index int32, size int) (icon *walk.Icon, err error) {
   113  	icon = cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}]
   114  	if icon != nil {
   115  		return
   116  	}
   117  	icon, err = walk.NewIconFromSysDLLWithSize(dll, int(index), size)
   118  	if err == nil {
   119  		cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}] = icon
   120  	}
   121  	return
   122  }
   123  
   124  func loadShieldIcon(size int) (icon *walk.Icon, err error) {
   125  	icon, err = loadSystemIcon("imageres", -1028, size)
   126  	if err != nil {
   127  		icon, err = loadSystemIcon("imageres", 1, size)
   128  	}
   129  	return
   130  }
   131  
   132  var cachedLogoIconsForWidth = make(map[int]*walk.Icon)
   133  
   134  func loadLogoIcon(size int) (icon *walk.Icon, err error) {
   135  	icon = cachedLogoIconsForWidth[size]
   136  	if icon != nil {
   137  		return
   138  	}
   139  	icon, err = walk.NewIconFromResourceIdWithSize(7, walk.Size{size, size})
   140  	if err == nil {
   141  		cachedLogoIconsForWidth[size] = icon
   142  	}
   143  	return
   144  }