erm/vendor/github.com/lxn/walk/brush.go
2021-07-30 23:29:20 +01:00

535 lines
13 KiB
Go

// 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"
)
type HatchStyle int
const (
HatchHorizontal HatchStyle = win.HS_HORIZONTAL
HatchVertical HatchStyle = win.HS_VERTICAL
HatchForwardDiagonal HatchStyle = win.HS_FDIAGONAL
HatchBackwardDiagonal HatchStyle = win.HS_BDIAGONAL
HatchCross HatchStyle = win.HS_CROSS
HatchDiagonalCross HatchStyle = win.HS_DIAGCROSS
)
type SystemColor int
const (
SysColor3DDkShadow SystemColor = win.COLOR_3DDKSHADOW
SysColor3DFace SystemColor = win.COLOR_3DFACE
SysColor3DHighlight SystemColor = win.COLOR_3DHIGHLIGHT
SysColor3DLight SystemColor = win.COLOR_3DLIGHT
SysColor3DShadow SystemColor = win.COLOR_3DSHADOW
SysColorActiveBorder SystemColor = win.COLOR_ACTIVEBORDER
SysColorActiveCaption SystemColor = win.COLOR_ACTIVECAPTION
SysColorAppWorkspace SystemColor = win.COLOR_APPWORKSPACE
SysColorBackground SystemColor = win.COLOR_BACKGROUND
SysColorDesktop SystemColor = win.COLOR_DESKTOP
SysColorBtnFace SystemColor = win.COLOR_BTNFACE
SysColorBtnHighlight SystemColor = win.COLOR_BTNHIGHLIGHT
SysColorBtnShadow SystemColor = win.COLOR_BTNSHADOW
SysColorBtnText SystemColor = win.COLOR_BTNTEXT
SysColorCaptionText SystemColor = win.COLOR_CAPTIONTEXT
SysColorGrayText SystemColor = win.COLOR_GRAYTEXT
SysColorHighlight SystemColor = win.COLOR_HIGHLIGHT
SysColorHighlightText SystemColor = win.COLOR_HIGHLIGHTTEXT
SysColorInactiveBorder SystemColor = win.COLOR_INACTIVEBORDER
SysColorInactiveCaption SystemColor = win.COLOR_INACTIVECAPTION
SysColorInactiveCaptionText SystemColor = win.COLOR_INACTIVECAPTIONTEXT
SysColorInfoBk SystemColor = win.COLOR_INFOBK
SysColorInfoText SystemColor = win.COLOR_INFOTEXT
SysColorMenu SystemColor = win.COLOR_MENU
SysColorMenuText SystemColor = win.COLOR_MENUTEXT
SysColorScrollBar SystemColor = win.COLOR_SCROLLBAR
SysColorWindow SystemColor = win.COLOR_WINDOW
SysColorWindowFrame SystemColor = win.COLOR_WINDOWFRAME
SysColorWindowText SystemColor = win.COLOR_WINDOWTEXT
SysColorHotLight SystemColor = win.COLOR_HOTLIGHT
SysColorGradientActiveCaption SystemColor = win.COLOR_GRADIENTACTIVECAPTION
SysColorGradientInactiveCaption SystemColor = win.COLOR_GRADIENTINACTIVECAPTION
)
type Brush interface {
Dispose()
handle() win.HBRUSH
logbrush() *win.LOGBRUSH
attachWindow(wb *WindowBase)
detachWindow(wb *WindowBase)
simple() bool
}
type perWindowBrush interface {
Brush
delegateForWindow(wb *WindowBase) Brush
}
type windowBrushInfo struct {
SizeChangedHandle int
Delegate *BitmapBrush
}
type brushBase struct {
hBrush win.HBRUSH
wb2info map[*WindowBase]*windowBrushInfo
}
func (bb *brushBase) Dispose() {
if bb.hBrush != 0 {
win.DeleteObject(win.HGDIOBJ(bb.hBrush))
bb.hBrush = 0
}
}
func (bb *brushBase) handle() win.HBRUSH {
return bb.hBrush
}
func (bb *brushBase) attachWindow(wb *WindowBase) {
if wb == nil {
return
}
if bb.wb2info == nil {
bb.wb2info = make(map[*WindowBase]*windowBrushInfo)
}
bb.wb2info[wb] = nil
}
func (bb *brushBase) detachWindow(wb *WindowBase) {
if bb.wb2info == nil || wb == nil {
return
}
delete(bb.wb2info, wb)
if len(bb.wb2info) == 0 {
bb.Dispose()
}
}
type nullBrush struct {
brushBase
}
func newNullBrush() *nullBrush {
lb := &win.LOGBRUSH{LbStyle: win.BS_NULL}
hBrush := win.CreateBrushIndirect(lb)
if hBrush == 0 {
panic("failed to create null brush")
}
return &nullBrush{brushBase: brushBase{hBrush: hBrush}}
}
func (b *nullBrush) Dispose() {
if b == nullBrushSingleton {
return
}
b.brushBase.Dispose()
}
func (*nullBrush) logbrush() *win.LOGBRUSH {
return &win.LOGBRUSH{LbStyle: win.BS_NULL}
}
func (*nullBrush) simple() bool {
return true
}
var (
nullBrushSingleton Brush
sysColorBtnFaceBrush *SystemColorBrush
)
func NullBrush() Brush {
return nullBrushSingleton
}
type SystemColorBrush struct {
brushBase
sysColor SystemColor
}
func init() {
AppendToWalkInit(func() {
nullBrushSingleton = newNullBrush()
sysColorBtnFaceBrush, _ = NewSystemColorBrush(SysColorBtnFace)
})
}
func NewSystemColorBrush(sysColor SystemColor) (*SystemColorBrush, error) {
hBrush := win.GetSysColorBrush(int(sysColor))
if hBrush == 0 {
return nil, newError("GetSysColorBrush failed")
}
return &SystemColorBrush{brushBase: brushBase{hBrush: hBrush}, sysColor: sysColor}, nil
}
func (b *SystemColorBrush) Color() Color {
return Color(win.GetSysColor(int(b.sysColor)))
}
func (b *SystemColorBrush) SystemColor() SystemColor {
return b.sysColor
}
func (*SystemColorBrush) Dispose() {
// nop
}
func (b *SystemColorBrush) logbrush() *win.LOGBRUSH {
return &win.LOGBRUSH{
LbStyle: win.BS_SOLID,
LbColor: win.COLORREF(win.GetSysColor(int(b.sysColor))),
}
}
func (*SystemColorBrush) simple() bool {
return true
}
type SolidColorBrush struct {
brushBase
color Color
}
func NewSolidColorBrush(color Color) (*SolidColorBrush, error) {
lb := &win.LOGBRUSH{LbStyle: win.BS_SOLID, LbColor: win.COLORREF(color)}
hBrush := win.CreateBrushIndirect(lb)
if hBrush == 0 {
return nil, newError("CreateBrushIndirect failed")
}
return &SolidColorBrush{brushBase: brushBase{hBrush: hBrush}, color: color}, nil
}
func (b *SolidColorBrush) Color() Color {
return b.color
}
func (b *SolidColorBrush) logbrush() *win.LOGBRUSH {
return &win.LOGBRUSH{LbStyle: win.BS_SOLID, LbColor: win.COLORREF(b.color)}
}
func (*SolidColorBrush) simple() bool {
return true
}
type HatchBrush struct {
brushBase
color Color
style HatchStyle
}
func NewHatchBrush(color Color, style HatchStyle) (*HatchBrush, error) {
lb := &win.LOGBRUSH{LbStyle: win.BS_HATCHED, LbColor: win.COLORREF(color), LbHatch: uintptr(style)}
hBrush := win.CreateBrushIndirect(lb)
if hBrush == 0 {
return nil, newError("CreateBrushIndirect failed")
}
return &HatchBrush{brushBase: brushBase{hBrush: hBrush}, color: color, style: style}, nil
}
func (b *HatchBrush) Color() Color {
return b.color
}
func (b *HatchBrush) logbrush() *win.LOGBRUSH {
return &win.LOGBRUSH{LbStyle: win.BS_HATCHED, LbColor: win.COLORREF(b.color), LbHatch: uintptr(b.style)}
}
func (b *HatchBrush) Style() HatchStyle {
return b.style
}
func (b *HatchBrush) simple() bool {
return false
}
type BitmapBrush struct {
brushBase
bitmap *Bitmap
}
func NewBitmapBrush(bitmap *Bitmap) (*BitmapBrush, error) {
if bitmap == nil {
return nil, newError("bitmap cannot be nil")
}
hBrush := win.CreatePatternBrush(bitmap.hBmp)
if hBrush == 0 {
return nil, newError("CreatePatternBrush failed")
}
return &BitmapBrush{brushBase: brushBase{hBrush: hBrush}, bitmap: bitmap}, nil
}
func (b *BitmapBrush) logbrush() *win.LOGBRUSH {
return &win.LOGBRUSH{LbStyle: win.BS_DIBPATTERN, LbColor: win.DIB_RGB_COLORS, LbHatch: uintptr(b.bitmap.hPackedDIB)}
}
func (b *BitmapBrush) Bitmap() *Bitmap {
return b.bitmap
}
func (b *BitmapBrush) simple() bool {
return false
}
type GradientStop struct {
Offset float64
Color Color
}
type GradientVertex struct {
X float64
Y float64
Color Color
}
type GradientTriangle struct {
Vertex1 int
Vertex2 int
Vertex3 int
}
type GradientBrush struct {
brushBase
mainDelegate *BitmapBrush
vertexes []GradientVertex
triangles []GradientTriangle
orientation gradientOrientation
absolute bool
}
type gradientOrientation int
const (
gradientOrientationNone gradientOrientation = iota
gradientOrientationHorizontal
gradientOrientationVertical
)
func NewHorizontalGradientBrush(stops []GradientStop) (*GradientBrush, error) {
return newGradientBrushWithOrientation(stops, gradientOrientationHorizontal)
}
func NewVerticalGradientBrush(stops []GradientStop) (*GradientBrush, error) {
return newGradientBrushWithOrientation(stops, gradientOrientationVertical)
}
func newGradientBrushWithOrientation(stops []GradientStop, orientation gradientOrientation) (*GradientBrush, error) {
if len(stops) < 2 {
return nil, newError("at least 2 stops are required")
}
var vertexes []GradientVertex
var triangles []GradientTriangle
for i, stop := range stops {
var x0, y0, x1, y1 float64
if orientation == gradientOrientationHorizontal {
x0 = stop.Offset
x1 = stop.Offset
y1 = 1.0
} else {
y0 = stop.Offset
x1 = 1.0
y1 = stop.Offset
}
vertexes = append(vertexes, GradientVertex{X: x0, Y: y0, Color: stop.Color})
vertexes = append(vertexes, GradientVertex{X: x1, Y: y1, Color: stop.Color})
if i > 0 {
triangles = append(triangles, GradientTriangle{Vertex1: i*2 - 2, Vertex2: i*2 + 1, Vertex3: i*2 - 1})
triangles = append(triangles, GradientTriangle{Vertex1: i*2 - 2, Vertex2: i * 2, Vertex3: i*2 + 1})
}
}
return newGradientBrush(vertexes, triangles, orientation)
}
func NewGradientBrush(vertexes []GradientVertex, triangles []GradientTriangle) (*GradientBrush, error) {
if len(vertexes) < 3 {
return nil, newError("at least 3 vertexes are required")
}
if len(triangles) < 1 {
return nil, newError("at least 1 triangle is required")
}
return newGradientBrush(vertexes, triangles, gradientOrientationNone)
}
func newGradientBrush(vertexes []GradientVertex, triangles []GradientTriangle, orientation gradientOrientation) (*GradientBrush, error) {
var size Size
for _, v := range vertexes {
size = maxSize(size, Size{int(v.X), int(v.Y)})
}
gb := &GradientBrush{vertexes: vertexes, triangles: triangles, orientation: orientation, absolute: size.Width > 1 || size.Height > 1}
if gb.absolute {
bb, err := gb.create(size)
if err != nil {
return nil, err
}
gb.mainDelegate = bb
gb.hBrush = bb.hBrush
}
return gb, nil
}
func (b *GradientBrush) logbrush() *win.LOGBRUSH {
if b.mainDelegate == nil {
return nil
}
return b.mainDelegate.logbrush()
}
func (*GradientBrush) simple() bool {
return false
}
// create creates a gradient brush at given size in native pixels.
func (b *GradientBrush) create(size Size) (*BitmapBrush, error) {
var disposables Disposables
defer disposables.Treat()
switch b.orientation {
case gradientOrientationHorizontal:
size.Height = 1
case gradientOrientationVertical:
size.Width = 1
}
bitmap, err := NewBitmapForDPI(size, 96) // Size is in native pixels and bitmap is used for brush only => DPI is not used anywhere.
if err != nil {
return nil, err
}
disposables.Add(bitmap)
canvas, err := NewCanvasFromImage(bitmap)
if err != nil {
return nil, err
}
defer canvas.Dispose()
var scaleX, scaleY float64
if b.absolute {
scaleX, scaleY = 1, 1
} else {
scaleX, scaleY = float64(size.Width), float64(size.Height)
}
vertexes := make([]win.TRIVERTEX, len(b.vertexes))
for i, src := range b.vertexes {
dst := &vertexes[i]
dst.X = int32(src.X * scaleX)
dst.Y = int32(src.Y * scaleY)
dst.Red = uint16(src.Color.R()) * 256
dst.Green = uint16(src.Color.G()) * 256
dst.Blue = uint16(src.Color.B()) * 256
}
triangles := make([]win.GRADIENT_TRIANGLE, len(b.triangles))
for i, src := range b.triangles {
dst := &triangles[i]
dst.Vertex1 = uint32(src.Vertex1)
dst.Vertex2 = uint32(src.Vertex2)
dst.Vertex3 = uint32(src.Vertex3)
}
if !win.GradientFill(canvas.hdc, &vertexes[0], uint32(len(vertexes)), unsafe.Pointer(&triangles[0]), uint32(len(triangles)), win.GRADIENT_FILL_TRIANGLE) {
return nil, newError("GradientFill failed")
}
disposables.Spare()
return NewBitmapBrush(bitmap)
}
func (b *GradientBrush) attachWindow(wb *WindowBase) {
b.brushBase.attachWindow(wb)
if b.absolute {
return
}
var info *windowBrushInfo
update := func() {
if bb, err := b.create(wb.window.ClientBoundsPixels().Size()); err == nil {
if info.Delegate != nil {
info.Delegate.bitmap.Dispose()
info.Delegate.Dispose()
}
info.Delegate = bb
wb.Invalidate()
}
}
info = &windowBrushInfo{
SizeChangedHandle: wb.SizeChanged().Attach(update),
}
update()
b.wb2info[wb] = info
}
func (b *GradientBrush) detachWindow(wb *WindowBase) {
if !b.absolute {
if info, ok := b.wb2info[wb]; ok {
if info.Delegate != nil {
info.Delegate.bitmap.Dispose()
info.Delegate.Dispose()
}
wb.SizeChanged().Detach(info.SizeChangedHandle)
}
}
b.brushBase.detachWindow(wb)
}
func (b *GradientBrush) delegateForWindow(wb *WindowBase) Brush {
if b.absolute {
return b.mainDelegate
}
if info, ok := b.wb2info[wb]; ok && info.Delegate != nil {
return info.Delegate
}
return nil
}