diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-10-24 16:54:02 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-10-24 16:54:02 +0200 |
commit | f7a409bb273830b0b22a29ecc37866c19cbefd8c (patch) | |
tree | 3be88d2f15d537aa9a4c98762d8e87756d8dc082 /ui | |
parent | ui: use native styling on list view (diff) | |
download | wireguard-windows-f7a409bb273830b0b22a29ecc37866c19cbefd8c.tar.xz wireguard-windows-f7a409bb273830b0b22a29ecc37866c19cbefd8c.zip |
ui: fix classic theme drawing with gross hack
Classic theme won't draw transparent images. But new theme erases the
text if we draw ourselves, and we want the OS to draw the text so that
we have better accessibility. Support both by hacking classic theme with
a zero-sized transparent image for the indentation, while using the
transparent image normally for new theme.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui')
-rw-r--r-- | ui/listview.go | 119 |
1 files changed, 73 insertions, 46 deletions
diff --git a/ui/listview.go b/ui/listview.go index 4416c063..afcb0ab7 100644 --- a/ui/listview.go +++ b/ui/listview.go @@ -9,6 +9,8 @@ import ( "sort" "sync/atomic" + "github.com/lxn/win" + "golang.zx2c4.com/wireguard/windows/conf" "golang.zx2c4.com/wireguard/windows/manager" @@ -19,11 +21,9 @@ import ( type ListModel struct { walk.TableModelBase walk.SorterBase - walk.ImageProvider tunnels []manager.Tunnel lastObservedState map[manager.Tunnel]manager.TunnelState - view *ListView } var cachedListViewIconsForWidthAndState = make(map[widthAndState]*walk.Bitmap) @@ -39,49 +39,6 @@ func (t *ListModel) Value(row, col int) interface{} { return t.tunnels[row].Name } -func (t *ListModel) Image(row int) interface{} { - if row < 0 || row >= len(t.tunnels) { - return nil - } - tunnel := &t.tunnels[row] - - var state manager.TunnelState - var ok bool - state, ok = t.lastObservedState[t.tunnels[row]] - if !ok { - var err error - state, err = tunnel.State() - if err != nil { - return nil - } - t.lastObservedState[t.tunnels[row]] = state - } - cacheKey := widthAndState{t.view.IntFrom96DPI(16), state} - if cacheValue, ok := cachedListViewIconsForWidthAndState[cacheKey]; ok { - return cacheValue - } - icon, err := iconForState(cacheKey.state, cacheKey.width) - if err != nil { - return nil - } - bitmap, err := walk.NewBitmapWithTransparentPixelsForDPI(icon.Size(), 96) - if err != nil { - return nil - } - canvas, err := walk.NewCanvasFromImage(bitmap) - if err != nil { - return nil - } - margin := t.view.IntFrom96DPI(1) - bounds := walk.Rectangle{X: margin, Y: margin, Height: bitmap.Size().Height - margin*2, Width: bitmap.Size().Width - margin*2} - if err := canvas.DrawImageStretchedPixels(icon, bounds); err != nil { - return nil - } - canvas.Dispose() - cachedListViewIconsForWidthAndState[cacheKey] = bitmap - return bitmap -} - func (t *ListModel) Sort(col int, order walk.SortOrder) error { sort.SliceStable(t.tunnels, func(i, j int) bool { return conf.TunnelNameIsLess(t.tunnels[i].Name, t.tunnels[j].Name) @@ -124,7 +81,7 @@ func NewListView(parent walk.Container) (*ListView, error) { TableView: tv, model: model, } - model.view = tunnelsView + tv.SetCellStyler(tunnelsView) disposables.Spare() @@ -155,6 +112,76 @@ func (tv *ListView) CurrentTunnel() *manager.Tunnel { return &tv.model.tunnels[idx] } +var dummyBitmap *walk.Bitmap + +func (tv *ListView) StyleCell(style *walk.CellStyle) { + row := style.Row() + if row < 0 || row >= len(tv.model.tunnels) { + return + } + tunnel := &tv.model.tunnels[row] + + var state manager.TunnelState + var ok bool + state, ok = tv.model.lastObservedState[tv.model.tunnels[row]] + if !ok { + var err error + state, err = tunnel.State() + if err != nil { + return + } + tv.model.lastObservedState[tv.model.tunnels[row]] = state + } + if win.IsAppThemed() { + cacheKey := widthAndState{tv.IntFrom96DPI(16), state} + if cacheValue, ok := cachedListViewIconsForWidthAndState[cacheKey]; ok { + style.Image = cacheValue + return + } + icon, err := iconForState(cacheKey.state, cacheKey.width) + if err != nil { + return + } + bitmap, err := walk.NewBitmapWithTransparentPixelsForDPI(tv.SizeFrom96DPI(icon.Size()), tv.DPI()) + if err != nil { + return + } + canvas, err := walk.NewCanvasFromImage(bitmap) + if err != nil { + return + } + margin := tv.IntFrom96DPI(1) + bounds := walk.Rectangle{X: margin, Y: margin, Height: bitmap.Size().Height - 2*margin, Width: bitmap.Size().Width - 2*margin} + err = canvas.DrawImageStretchedPixels(icon, bounds) + canvas.Dispose() + if err != nil { + return + } + cachedListViewIconsForWidthAndState[cacheKey] = bitmap + style.Image = bitmap + } else { + if dummyBitmap == nil { + dummyBitmap, _ = walk.NewBitmapForDPI(tv.SizeFrom96DPI(walk.Size{}), 96) + } + style.Image = dummyBitmap + canvas := style.Canvas() + if canvas == nil { + return + } + margin := tv.IntFrom96DPI(1) + bounds := style.Bounds() + bounds.X = margin + bounds.Y += margin + bounds.Width = bounds.Height - 2*margin + bounds.Height = bounds.Width + icon, err := iconForState(state, style.Bounds().Width) + if err != nil { + return + } + canvas.DrawImageStretched(icon, bounds) + } +} + func (tv *ListView) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) { tv.Synchronize(func() { idx := -1 |