From 97b9bcefed22df01e76748f2ec09728618a18a78 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 24 Oct 2019 16:54:02 +0200 Subject: 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. --- ui/listview.go | 119 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 46 deletions(-) (limited to 'ui') 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 -- cgit v1.2.3-59-g8ed1b