erm/vendor/github.com/lxn/walk/customwidget.go

240 lines
5.6 KiB
Go
Raw Normal View History

2021-07-30 22:29:20 +00:00
// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package walk
import (
"unsafe"
"github.com/lxn/win"
)
const customWidgetWindowClass = `\o/ Walk_CustomWidget_Class \o/`
func init() {
AppendToWalkInit(func() {
MustRegisterWindowClass(customWidgetWindowClass)
})
}
// PaintFunc paints custom widget content. updateBounds is specified in 1/96" or native pixels.
type PaintFunc func(canvas *Canvas, updateBounds Rectangle) error
type PaintMode int
const (
PaintNormal PaintMode = iota // erase background before PaintFunc
PaintNoErase // PaintFunc clears background, single buffered
PaintBuffered // PaintFunc clears background, double buffered
)
type CustomWidget struct {
WidgetBase
paint PaintFunc // in 1/96" units
paintPixels PaintFunc // in native pixels
invalidatesOnResize bool
paintMode PaintMode
}
// NewCustomWidget creates and initializes a new custom draw widget.
//
// Deprecated: PaintFunc is taking updateBounds parameter at 96dpi for backward compatibility with
// clients. On high-DPI displays this is too sparse and may incur a thin unpainted edge around
// control due to rounding errors. Newer applications should use NewCustomWidgetPixels.
func NewCustomWidget(parent Container, style uint, paint PaintFunc) (*CustomWidget, error) {
cw := &CustomWidget{paint: paint}
err := cw.init(parent, style)
if err != nil {
return nil, err
}
return cw, nil
}
// NewCustomWidgetPixels creates and initializes a new custom draw widget.
func NewCustomWidgetPixels(parent Container, style uint, paintPixels PaintFunc) (*CustomWidget, error) {
cw := &CustomWidget{paintPixels: paintPixels}
err := cw.init(parent, style)
if err != nil {
return nil, err
}
return cw, nil
}
func (cw *CustomWidget) init(parent Container, style uint) error {
if err := InitWidget(
cw,
parent,
customWidgetWindowClass,
win.WS_VISIBLE|uint32(style),
0); err != nil {
return err
}
return nil
}
// deprecated, use PaintMode
func (cw *CustomWidget) ClearsBackground() bool {
return cw.paintMode != PaintNormal
}
// deprecated, use SetPaintMode
func (cw *CustomWidget) SetClearsBackground(value bool) {
if value != cw.ClearsBackground() {
if value {
cw.paintMode = PaintNormal
} else {
cw.paintMode = PaintNoErase
}
}
}
func (cw *CustomWidget) InvalidatesOnResize() bool {
return cw.invalidatesOnResize
}
func (cw *CustomWidget) SetInvalidatesOnResize(value bool) {
cw.invalidatesOnResize = value
}
func (cw *CustomWidget) PaintMode() PaintMode {
return cw.paintMode
}
func (cw *CustomWidget) SetPaintMode(value PaintMode) {
cw.paintMode = value
}
func (cw *CustomWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case win.WM_PAINT:
if cw.paint == nil && cw.paintPixels == nil {
newError("paint(Pixels) func is nil")
break
}
var ps win.PAINTSTRUCT
var hdc win.HDC
if wParam == 0 {
hdc = win.BeginPaint(cw.hWnd, &ps)
} else {
hdc = win.HDC(wParam)
}
if hdc == 0 {
newError("BeginPaint failed")
break
}
defer func() {
if wParam == 0 {
win.EndPaint(cw.hWnd, &ps)
}
}()
canvas, err := newCanvasFromHDC(hdc)
if err != nil {
newError("newCanvasFromHDC failed")
break
}
defer canvas.Dispose()
bounds := rectangleFromRECT(ps.RcPaint)
if cw.paintMode == PaintBuffered {
err = cw.bufferedPaint(canvas, bounds)
} else if cw.paintPixels != nil {
err = cw.paintPixels(canvas, bounds)
} else {
err = cw.paint(canvas, RectangleTo96DPI(bounds, cw.DPI()))
}
if err != nil {
newError("paint failed")
break
}
return 0
case win.WM_ERASEBKGND:
if cw.paintMode != PaintNormal {
return 1
}
case win.WM_PRINTCLIENT:
win.SendMessage(hwnd, win.WM_PAINT, wParam, lParam)
case win.WM_WINDOWPOSCHANGED:
wp := (*win.WINDOWPOS)(unsafe.Pointer(lParam))
if wp.Flags&win.SWP_NOSIZE != 0 {
break
}
if cw.invalidatesOnResize {
cw.Invalidate()
}
}
return cw.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
// bufferedPaint draws widget on a memory buffer. updateBounds are in native pixels.
func (cw *CustomWidget) bufferedPaint(canvas *Canvas, updateBounds Rectangle) error {
hdc := win.CreateCompatibleDC(canvas.hdc)
if hdc == 0 {
return newError("CreateCompatibleDC failed")
}
defer win.DeleteDC(hdc)
buffered := Canvas{hdc: hdc, doNotDispose: true}
if _, err := buffered.init(); err != nil {
return err
}
w, h := int32(updateBounds.Width), int32(updateBounds.Height)
if w < 1 {
w = 1
}
if h < 1 {
h = 1
}
hbmp := win.CreateCompatibleBitmap(canvas.hdc, w, h)
if hbmp == 0 {
return lastError("CreateCompatibleBitmap failed")
}
defer win.DeleteObject(win.HGDIOBJ(hbmp))
oldbmp := win.SelectObject(buffered.hdc, win.HGDIOBJ(hbmp))
if oldbmp == 0 {
return newError("SelectObject failed")
}
defer win.SelectObject(buffered.hdc, oldbmp)
win.SetViewportOrgEx(buffered.hdc, -int32(updateBounds.X), -int32(updateBounds.Y), nil)
win.SetBrushOrgEx(buffered.hdc, -int32(updateBounds.X), -int32(updateBounds.Y), nil)
var err error
if cw.paintPixels != nil {
err = cw.paintPixels(&buffered, updateBounds)
} else {
err = cw.paint(&buffered, RectangleTo96DPI(updateBounds, cw.DPI()))
}
if !win.BitBlt(canvas.hdc,
int32(updateBounds.X), int32(updateBounds.Y), w, h,
buffered.hdc,
int32(updateBounds.X), int32(updateBounds.Y), win.SRCCOPY) {
return lastError("buffered BitBlt failed")
}
return err
}
func (*CustomWidget) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
return NewGreedyLayoutItem()
}