Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bfd66d6

Browse files
committed
CustomWidget: add support for double buffered painting, fixes lxn#155
1 parent e10ed7b commit bfd66d6

File tree

2 files changed

+96
-12
lines changed

2 files changed

+96
-12
lines changed

customwidget.go

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@ func init() {
1818

1919
type PaintFunc func(canvas *Canvas, updateBounds Rectangle) error
2020

21+
type PaintMode int
22+
23+
const (
24+
PaintNormal PaintMode = iota // erase background before PaintFunc
25+
PaintNoErase // PaintFunc clears background, single buffered
26+
PaintBuffered // PaintFunc clears background, double buffered
27+
)
28+
2129
type CustomWidget struct {
2230
WidgetBase
2331
paint PaintFunc
24-
clearsBackground bool
2532
invalidatesOnResize bool
33+
paintMode PaintMode
2634
}
2735

2836
func NewCustomWidget(parent Container, style uint, paint PaintFunc) (*CustomWidget, error) {
@@ -48,12 +56,20 @@ func (cw *CustomWidget) SizeHint() Size {
4856
return Size{100, 100}
4957
}
5058

59+
// deprecated, use PaintMode
5160
func (cw *CustomWidget) ClearsBackground() bool {
52-
return cw.clearsBackground
61+
return cw.paintMode != PaintNormal
5362
}
5463

64+
// deprecated, use SetPaintMode
5565
func (cw *CustomWidget) SetClearsBackground(value bool) {
56-
cw.clearsBackground = value
66+
if value != cw.ClearsBackground() {
67+
if value {
68+
cw.paintMode = PaintNoErase
69+
} else {
70+
cw.paintMode = PaintNormal
71+
}
72+
}
5773
}
5874

5975
func (cw *CustomWidget) InvalidatesOnResize() bool {
@@ -64,6 +80,14 @@ func (cw *CustomWidget) SetInvalidatesOnResize(value bool) {
6480
cw.invalidatesOnResize = value
6581
}
6682

83+
func (cw *CustomWidget) PaintMode() PaintMode {
84+
return cw.paintMode
85+
}
86+
87+
func (cw *CustomWidget) SetPaintMode(value PaintMode) {
88+
cw.paintMode = value
89+
}
90+
6791
func (cw *CustomWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
6892
switch msg {
6993
case win.WM_PAINT:
@@ -89,14 +113,18 @@ func (cw *CustomWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintpt
89113
defer canvas.Dispose()
90114

91115
r := &ps.RcPaint
92-
err = cw.paint(
93-
canvas,
94-
Rectangle{
95-
int(r.Left),
96-
int(r.Top),
97-
int(r.Right - r.Left),
98-
int(r.Bottom - r.Top),
99-
})
116+
bounds := Rectangle{
117+
int(r.Left),
118+
int(r.Top),
119+
int(r.Right - r.Left),
120+
int(r.Bottom - r.Top),
121+
}
122+
if cw.paintMode == PaintBuffered {
123+
err = cw.bufferedPaint(canvas, bounds)
124+
} else {
125+
err = cw.paint(canvas, bounds)
126+
}
127+
100128
if err != nil {
101129
newError("paint failed")
102130
break
@@ -105,7 +133,7 @@ func (cw *CustomWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintpt
105133
return 0
106134

107135
case win.WM_ERASEBKGND:
108-
if !cw.clearsBackground {
136+
if cw.paintMode != PaintNormal {
109137
return 1
110138
}
111139

@@ -117,3 +145,46 @@ func (cw *CustomWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintpt
117145

118146
return cw.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
119147
}
148+
149+
func (cw *CustomWidget) bufferedPaint(canvas *Canvas, updateBounds Rectangle) error {
150+
hdc := win.CreateCompatibleDC(canvas.hdc)
151+
if hdc == 0 {
152+
return newError("CreateCompatibleDC failed")
153+
}
154+
defer win.DeleteDC(hdc)
155+
156+
buffered := Canvas{hdc: hdc, doNotDispose: true}
157+
if _, err := buffered.init(); err != nil {
158+
return err
159+
}
160+
161+
w, h := int32(updateBounds.Width), int32(updateBounds.Height)
162+
if w < 1 {
163+
w = 1
164+
}
165+
if h < 1 {
166+
h = 1
167+
}
168+
hbmp := win.CreateCompatibleBitmap(canvas.hdc, w, h)
169+
if hbmp == 0 {
170+
return lastError("CreateCompatibleBitmap failed")
171+
}
172+
defer win.DeleteObject(win.HGDIOBJ(hbmp))
173+
174+
oldbmp := win.SelectObject(buffered.hdc, win.HGDIOBJ(hbmp))
175+
if oldbmp == 0 {
176+
return newError("SelectObject failed")
177+
}
178+
defer win.SelectObject(buffered.hdc, oldbmp)
179+
180+
err := cw.paint(&buffered, updateBounds)
181+
182+
if !win.BitBlt(canvas.hdc,
183+
int32(updateBounds.X), int32(updateBounds.Y), w, h,
184+
buffered.hdc,
185+
int32(updateBounds.X), int32(updateBounds.Y), win.SRCCOPY) {
186+
return lastError("buffered BitBlt failed")
187+
}
188+
189+
return err
190+
}

declarative/customwidget.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ import (
1010
"github.com/lxn/walk"
1111
)
1212

13+
type PaintMode int
14+
15+
const (
16+
PaintNormal PaintMode = iota // erase background before PaintFunc
17+
PaintNoErase // PaintFunc clears background, single buffered
18+
PaintBuffered // PaintFunc clears background, double buffered
19+
)
20+
1321
type CustomWidget struct {
1422
AssignTo **walk.CustomWidget
1523
Name string
@@ -37,6 +45,7 @@ type CustomWidget struct {
3745
Paint walk.PaintFunc
3846
ClearsBackground bool
3947
InvalidatesOnResize bool
48+
PaintMode PaintMode
4049
}
4150

4251
func (cw CustomWidget) Create(builder *Builder) error {
@@ -46,8 +55,12 @@ func (cw CustomWidget) Create(builder *Builder) error {
4655
}
4756

4857
return builder.InitWidget(cw, w, func() error {
58+
if cw.PaintMode != PaintNormal && cw.ClearsBackground {
59+
panic("PaintMode and ClearsBackground are incompatible")
60+
}
4961
w.SetClearsBackground(cw.ClearsBackground)
5062
w.SetInvalidatesOnResize(cw.InvalidatesOnResize)
63+
w.SetPaintMode(walk.PaintMode(cw.PaintMode))
5164

5265
if cw.AssignTo != nil {
5366
*cw.AssignTo = w

0 commit comments

Comments
 (0)