ソースを参照

Merge branch 'develop' of 192.168.3.17:zhanghongbo/qfw into develop

李广朋 9 年 前
コミット
478eb05836
100 ファイル変更10853 行追加0 行削除
  1. 24 0
      common/src/github.com/lxn/walk/AUTHORS
  2. 23 0
      common/src/github.com/lxn/walk/LICENSE
  3. 118 0
      common/src/github.com/lxn/walk/README.mdown
  4. 386 0
      common/src/github.com/lxn/walk/action.go
  5. 176 0
      common/src/github.com/lxn/walk/actionlist.go
  6. 87 0
      common/src/github.com/lxn/walk/application.go
  7. 298 0
      common/src/github.com/lxn/walk/bitmap.go
  8. 516 0
      common/src/github.com/lxn/walk/boxlayout.go
  9. 215 0
      common/src/github.com/lxn/walk/brush.go
  10. 147 0
      common/src/github.com/lxn/walk/button.go
  11. 45 0
      common/src/github.com/lxn/walk/cancelevent.go
  12. 382 0
      common/src/github.com/lxn/walk/canvas.go
  13. 125 0
      common/src/github.com/lxn/walk/checkbox.go
  14. 163 0
      common/src/github.com/lxn/walk/clipboard.go
  15. 45 0
      common/src/github.com/lxn/walk/closeevent.go
  16. 25 0
      common/src/github.com/lxn/walk/color.go
  17. 586 0
      common/src/github.com/lxn/walk/combobox.go
  18. 186 0
      common/src/github.com/lxn/walk/commondialogs.go
  19. 42 0
      common/src/github.com/lxn/walk/composite.go
  20. 127 0
      common/src/github.com/lxn/walk/condition.go
  21. 359 0
      common/src/github.com/lxn/walk/container.go
  22. 116 0
      common/src/github.com/lxn/walk/cursor.go
  23. 205 0
      common/src/github.com/lxn/walk/customwidget.go
  24. BIN
      common/src/github.com/lxn/walk/data/drawing.png
  25. BIN
      common/src/github.com/lxn/walk/data/filebrowser.png
  26. 440 0
      common/src/github.com/lxn/walk/databinding.go
  27. 244 0
      common/src/github.com/lxn/walk/dateedit.go
  28. 205 0
      common/src/github.com/lxn/walk/declarative/action.go
  29. 495 0
      common/src/github.com/lxn/walk/declarative/builder.go
  30. 78 0
      common/src/github.com/lxn/walk/declarative/checkbox.go
  31. 100 0
      common/src/github.com/lxn/walk/declarative/combobox.go
  32. 68 0
      common/src/github.com/lxn/walk/declarative/composite.go
  33. 75 0
      common/src/github.com/lxn/walk/declarative/customwidget.go
  34. 45 0
      common/src/github.com/lxn/walk/declarative/databinder.go
  35. 84 0
      common/src/github.com/lxn/walk/declarative/dateedit.go
  36. 135 0
      common/src/github.com/lxn/walk/declarative/dialog.go
  37. 43 0
      common/src/github.com/lxn/walk/declarative/font.go
  38. 77 0
      common/src/github.com/lxn/walk/declarative/groupbox.go
  39. 60 0
      common/src/github.com/lxn/walk/declarative/imageview.go
  40. 112 0
      common/src/github.com/lxn/walk/declarative/interfaces.go
  41. 56 0
      common/src/github.com/lxn/walk/declarative/label.go
  42. 122 0
      common/src/github.com/lxn/walk/declarative/layouts.go
  43. 90 0
      common/src/github.com/lxn/walk/declarative/lineedit.go
  44. 55 0
      common/src/github.com/lxn/walk/declarative/lineerrorpresenter.go
  45. 99 0
      common/src/github.com/lxn/walk/declarative/listbox.go
  46. 137 0
      common/src/github.com/lxn/walk/declarative/mainwindow.go
  47. 7 0
      common/src/github.com/lxn/walk/declarative/nonwin.go
  48. 93 0
      common/src/github.com/lxn/walk/declarative/numberedit.go
  49. 68 0
      common/src/github.com/lxn/walk/declarative/progressbar.go
  50. 80 0
      common/src/github.com/lxn/walk/declarative/pushbutton.go
  51. 64 0
      common/src/github.com/lxn/walk/declarative/radiobutton.go
  52. 108 0
      common/src/github.com/lxn/walk/declarative/radiobuttongroup.go
  53. 88 0
      common/src/github.com/lxn/walk/declarative/radiobuttongroupbox.go
  54. 68 0
      common/src/github.com/lxn/walk/declarative/scrollview.go
  55. 68 0
      common/src/github.com/lxn/walk/declarative/slider.go
  56. 73 0
      common/src/github.com/lxn/walk/declarative/spacer.go
  57. 131 0
      common/src/github.com/lxn/walk/declarative/splitter.go
  58. 107 0
      common/src/github.com/lxn/walk/declarative/tableview.go
  59. 59 0
      common/src/github.com/lxn/walk/declarative/tableviewcolumn.go
  60. 69 0
      common/src/github.com/lxn/walk/declarative/tabpage.go
  61. 82 0
      common/src/github.com/lxn/walk/declarative/tabwidget.go
  62. 62 0
      common/src/github.com/lxn/walk/declarative/textedit.go
  63. 91 0
      common/src/github.com/lxn/walk/declarative/toolbar.go
  64. 75 0
      common/src/github.com/lxn/walk/declarative/toolbutton.go
  65. 75 0
      common/src/github.com/lxn/walk/declarative/treeview.go
  66. 75 0
      common/src/github.com/lxn/walk/declarative/validators.go
  67. 61 0
      common/src/github.com/lxn/walk/declarative/webview.go
  68. 251 0
      common/src/github.com/lxn/walk/dialog.go
  69. 73 0
      common/src/github.com/lxn/walk/dropfilesevent.go
  70. 143 0
      common/src/github.com/lxn/walk/error.go
  71. 45 0
      common/src/github.com/lxn/walk/errorevent.go
  72. 45 0
      common/src/github.com/lxn/walk/event.go
  73. 186 0
      common/src/github.com/lxn/walk/examples/actions/actions.go
  74. 14 0
      common/src/github.com/lxn/walk/examples/actions/actions.manifest
  75. BIN
      common/src/github.com/lxn/walk/examples/actions/rsrc.syso
  76. 49 0
      common/src/github.com/lxn/walk/examples/clipboard/clipboard.go
  77. 14 0
      common/src/github.com/lxn/walk/examples/clipboard/clipboard.manifest
  78. BIN
      common/src/github.com/lxn/walk/examples/clipboard/rsrc.syso
  79. 253 0
      common/src/github.com/lxn/walk/examples/databinding/databinding.go
  80. 14 0
      common/src/github.com/lxn/walk/examples/databinding/databinding.manifest
  81. BIN
      common/src/github.com/lxn/walk/examples/databinding/rsrc.syso
  82. 160 0
      common/src/github.com/lxn/walk/examples/drawing/drawing.go
  83. 14 0
      common/src/github.com/lxn/walk/examples/drawing/drawing.manifest
  84. BIN
      common/src/github.com/lxn/walk/examples/drawing/rsrc.syso
  85. 31 0
      common/src/github.com/lxn/walk/examples/dropfiles/dropfiles.go
  86. 14 0
      common/src/github.com/lxn/walk/examples/dropfiles/dropfiles.manifest
  87. BIN
      common/src/github.com/lxn/walk/examples/dropfiles/rsrc.syso
  88. 117 0
      common/src/github.com/lxn/walk/examples/externalwidgets/externalwidgets.go
  89. 14 0
      common/src/github.com/lxn/walk/examples/externalwidgets/externalwidgets.manifest
  90. BIN
      common/src/github.com/lxn/walk/examples/externalwidgets/rsrc.syso
  91. 299 0
      common/src/github.com/lxn/walk/examples/filebrowser/filebrowser.go
  92. 14 0
      common/src/github.com/lxn/walk/examples/filebrowser/filebrowser.manifest
  93. BIN
      common/src/github.com/lxn/walk/examples/filebrowser/rsrc.syso
  94. 14 0
      common/src/github.com/lxn/walk/examples/imageicon/imageicon.manifest
  95. 98 0
      common/src/github.com/lxn/walk/examples/imageicon/main.go
  96. BIN
      common/src/github.com/lxn/walk/examples/imageicon/rsrc.syso
  97. 153 0
      common/src/github.com/lxn/walk/examples/imageviewer/imageviewer.go
  98. 14 0
      common/src/github.com/lxn/walk/examples/imageviewer/imageviewer.manifest
  99. BIN
      common/src/github.com/lxn/walk/examples/imageviewer/rsrc.syso
  100. 4 0
      common/src/github.com/lxn/walk/examples/img/README

+ 24 - 0
common/src/github.com/lxn/walk/AUTHORS

@@ -0,0 +1,24 @@
+# This is the official list of 'Walk' authors for copyright purposes.
+
+# Names should be added to this file as
+#   Name or Organization <email address>
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+# Contributors
+# ============
+
+Alexander Neumann <an2048@gmail.com>
+Attila Tajti <attila.tajti@gmail.com>
+Benny Siegert <bsiegert@gmail.com>
+Cary Cherng <ccherng@gmail.com>
+Hill <zhu.bicen@gmail.com>
+Joseph Watson <jtwatson@linux-consulting.us>
+ktye <ktye.users.noreply.github.com>
+llxwj <llxwjlove@gmail.com>
+Mateusz Czapliński <czapkofan@gmail.com>
+Michael Teichgräber <mteichgraeber@gmx.de>
+Shawn Sun <datago@yeah.net>
+Tim Dufrane <tim.dufrane@gmail.com>
+Vincent Vanackere <vincent.vanackere@gmail.com>

+ 23 - 0
common/src/github.com/lxn/walk/LICENSE

@@ -0,0 +1,23 @@
+Copyright (c) 2010 The Walk Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The names of the authors may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 118 - 0
common/src/github.com/lxn/walk/README.mdown

@@ -0,0 +1,118 @@
+About Walk
+==========
+
+Walk is a "Windows Application Library Kit" for the Go Programming Language.
+
+Its primarily useful for Desktop GUI development, but there is some more stuff.
+
+Setup
+=====
+
+Make sure you have a working Go installation.
+See [Getting Started](http://golang.org/doc/install.html)
+
+##### Note
+`Go 1.0.x` doesn't work with walk anymore, Make sure you use `Go 1.1.x` or later.
+
+##### To Install
+Now run `go get github.com/lxn/walk`
+
+Using Walk
+==========
+
+The preferred way to create GUIs with Walk is to use its declarative sub package,
+as illustrated in this small example:
+
+##### `test.go`
+
+```go
+package main
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+	"strings"
+)
+
+func main() {
+	var inTE, outTE *walk.TextEdit
+
+	MainWindow{
+		Title:   "SCREAMO",
+		MinSize: Size{600, 400},
+		Layout:  VBox{},
+		Children: []Widget{
+			HSplitter{
+				Children: []Widget{
+					TextEdit{AssignTo: &inTE},
+					TextEdit{AssignTo: &outTE, ReadOnly: true},
+				},
+			},
+			PushButton{
+				Text: "SCREAM",
+				OnClicked: func() {
+					outTE.SetText(strings.ToUpper(inTE.Text()))
+				},
+			},
+		},
+	}.Run()
+}
+```
+
+##### Create Manifest `test.manifest`
+
+```xml
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+        <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+        <dependency>
+            <dependentAssembly>
+                <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+            </dependentAssembly>
+        </dependency>
+    </assembly>
+```
+
+Then either compile the manifest using the [rsrc tool](https://github.com/akavel/rsrc), like this:
+
+	go get github.com/akavel/rsrc
+	rsrc -manifest test.manifest -o rsrc.syso
+
+or rename the `test.manifest` file to `test.exe.manifest` and distribute it with the application instead.
+
+##### Build app
+
+In the directory containing `test.go` run
+
+	go build
+	
+To get rid of the cmd window, instead run
+
+	go build -ldflags="-H windowsgui"
+
+##### Run app
+	
+	test.exe
+	
+##### Sample Output (Windows 7)
+
+![alt tag](http://i.imgur.com/lUrgE2Q.png)
+
+##### More Examples
+There are some [examples](examples) that should get you started.
+
+Application Manifest Files
+==========================
+Walk requires Common Controls 6. This means that you must put an appropriate
+application manifest file either next to your executable or embedded as a
+resource.
+
+You can copy one of the application manifest files that come with the examples.
+
+To embed a manifest file as a resource, you can use the [rsrc tool](https://github.com/akavel/rsrc).
+
+IMPORTANT: If you don't embed a manifest as a resource, then you should not launch
+your executable before the manifest file is in place.
+If you do anyway, the program will not run properly. And worse, Windows will not
+recognize a manifest file, you later drop next to the executable. To fix this,
+rebuild your executable and only launch it with a manifest file in place.

+ 386 - 0
common/src/github.com/lxn/walk/action.go

@@ -0,0 +1,386 @@
+// 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
+
+type actionChangedHandler interface {
+	onActionChanged(action *Action) error
+	onActionVisibleChanged(action *Action) error
+}
+
+var (
+	// ISSUE: When pressing enter resp. escape,
+	// WM_COMMAND with wParam=1 resp. 2 is sent.
+	// Maybe there is more to consider.
+	nextActionId    uint16 = 3
+	actionsById            = make(map[uint16]*Action)
+	shortcut2Action        = make(map[Shortcut]*Action)
+)
+
+type Action struct {
+	menu                          *Menu
+	triggeredPublisher            EventPublisher
+	changedHandlers               []actionChangedHandler
+	text                          string
+	toolTip                       string
+	image                         *Bitmap
+	enabledCondition              Condition
+	enabledConditionChangedHandle int
+	visibleCondition              Condition
+	visibleConditionChangedHandle int
+	refCount                      int
+	shortcut                      Shortcut
+	enabled                       bool
+	visible                       bool
+	checkable                     bool
+	checked                       bool
+	exclusive                     bool
+	id                            uint16
+}
+
+func NewAction() *Action {
+	a := &Action{
+		enabled: true,
+		id:      nextActionId,
+		visible: true,
+	}
+
+	actionsById[a.id] = a
+
+	nextActionId++
+
+	return a
+}
+
+func NewMenuAction(menu *Menu) *Action {
+	a := NewAction()
+	a.menu = menu
+
+	return a
+}
+
+func NewSeparatorAction() *Action {
+	return &Action{
+		enabled: true,
+		visible: true,
+	}
+}
+
+func (a *Action) addRef() {
+	a.refCount++
+}
+
+func (a *Action) release() {
+	a.refCount--
+
+	if a.refCount == 0 {
+		a.SetEnabledCondition(nil)
+		a.SetVisibleCondition(nil)
+
+		if a.menu != nil {
+			a.menu.actions.Clear()
+			a.menu.Dispose()
+		}
+
+		delete(actionsById, a.id)
+		delete(shortcut2Action, a.shortcut)
+	}
+}
+
+func (a *Action) Checkable() bool {
+	return a.checkable
+}
+
+func (a *Action) SetCheckable(value bool) (err error) {
+	if value != a.checkable {
+		old := a.checkable
+
+		a.checkable = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.checkable = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) Checked() bool {
+	return a.checked
+}
+
+func (a *Action) SetChecked(value bool) (err error) {
+	if value != a.checked {
+		old := a.checked
+
+		a.checked = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.checked = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) Enabled() bool {
+	if a.enabledCondition != nil {
+		return a.enabledCondition.Satisfied()
+	}
+
+	return a.enabled
+}
+
+func (a *Action) SetEnabled(value bool) (err error) {
+	if a.enabledCondition != nil {
+		return newError("EnabledCondition != nil")
+	}
+
+	if value != a.enabled {
+		old := a.enabled
+
+		a.enabled = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.enabled = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) EnabledCondition() Condition {
+	return a.enabledCondition
+}
+
+func (a *Action) SetEnabledCondition(c Condition) {
+	if a.enabledCondition != nil {
+		a.enabledCondition.Changed().Detach(a.enabledConditionChangedHandle)
+	}
+
+	a.enabledCondition = c
+
+	if c != nil {
+		a.enabled = c.Satisfied()
+
+		a.enabledConditionChangedHandle = c.Changed().Attach(func() {
+			if a.enabled != c.Satisfied() {
+				a.enabled = !a.enabled
+
+				a.raiseChanged()
+			}
+		})
+	}
+
+	a.raiseChanged()
+}
+
+func (a *Action) Exclusive() bool {
+	return a.exclusive
+}
+
+func (a *Action) SetExclusive(value bool) (err error) {
+	if value != a.exclusive {
+		old := a.exclusive
+
+		a.exclusive = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.exclusive = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) Image() *Bitmap {
+	return a.image
+}
+
+func (a *Action) SetImage(value *Bitmap) (err error) {
+	if value != a.image {
+		old := a.image
+
+		a.image = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.image = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) Shortcut() Shortcut {
+	return a.shortcut
+}
+
+func (a *Action) SetShortcut(shortcut Shortcut) (err error) {
+	if shortcut != a.shortcut {
+		old := a.shortcut
+
+		a.shortcut = shortcut
+		defer func() {
+			if err != nil {
+				a.shortcut = old
+			}
+		}()
+
+		if err = a.raiseChanged(); err != nil {
+			a.shortcut = old
+			a.raiseChanged()
+		} else {
+			if shortcut.Key == 0 {
+				delete(shortcut2Action, old)
+			} else {
+				shortcut2Action[shortcut] = a
+			}
+		}
+	}
+
+	return
+}
+
+func (a *Action) Text() string {
+	return a.text
+}
+
+func (a *Action) SetText(value string) (err error) {
+	if value != a.text {
+		old := a.text
+
+		a.text = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.text = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) IsSeparator() bool {
+	return a.id == 0 || a.text == "-"
+}
+
+func (a *Action) ToolTip() string {
+	return a.toolTip
+}
+
+func (a *Action) SetToolTip(value string) (err error) {
+	if value != a.toolTip {
+		old := a.toolTip
+
+		a.toolTip = value
+
+		if err = a.raiseChanged(); err != nil {
+			a.toolTip = old
+			a.raiseChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) Visible() bool {
+	if a.visibleCondition != nil {
+		return a.visibleCondition.Satisfied()
+	}
+
+	return a.visible
+}
+
+func (a *Action) SetVisible(value bool) (err error) {
+	if a.visibleCondition != nil {
+		return newError("VisibleCondition != nil")
+	}
+
+	if value != a.visible {
+		old := a.visible
+
+		a.visible = value
+
+		if err = a.raiseVisibleChanged(); err != nil {
+			a.visible = old
+			a.raiseVisibleChanged()
+		}
+	}
+
+	return
+}
+
+func (a *Action) VisibleCondition() Condition {
+	return a.visibleCondition
+}
+
+func (a *Action) SetVisibleCondition(c Condition) {
+	if a.visibleCondition != nil {
+		a.visibleCondition.Changed().Detach(a.visibleConditionChangedHandle)
+	}
+
+	a.visibleCondition = c
+
+	if c != nil {
+		a.visible = c.Satisfied()
+
+		a.visibleConditionChangedHandle = c.Changed().Attach(func() {
+			if a.visible != c.Satisfied() {
+				a.visible = !a.visible
+
+				a.raiseVisibleChanged()
+			}
+		})
+	}
+
+	a.raiseChanged()
+}
+
+func (a *Action) Triggered() *Event {
+	return a.triggeredPublisher.Event()
+}
+
+func (a *Action) raiseTriggered() {
+	a.triggeredPublisher.Publish()
+}
+
+func (a *Action) addChangedHandler(handler actionChangedHandler) {
+	a.changedHandlers = append(a.changedHandlers, handler)
+}
+
+func (a *Action) removeChangedHandler(handler actionChangedHandler) {
+	for i, h := range a.changedHandlers {
+		if h == handler {
+			a.changedHandlers = append(a.changedHandlers[:i], a.changedHandlers[i+1:]...)
+			break
+		}
+	}
+}
+
+func (a *Action) raiseChanged() error {
+	for _, handler := range a.changedHandlers {
+		if err := handler.onActionChanged(a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (a *Action) raiseVisibleChanged() error {
+	for _, handler := range a.changedHandlers {
+		if err := handler.onActionVisibleChanged(a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 176 - 0
common/src/github.com/lxn/walk/actionlist.go

@@ -0,0 +1,176 @@
+// 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
+
+type actionListObserver interface {
+	onInsertedAction(action *Action) error
+	onRemovingAction(action *Action) error
+	onClearingActions() error
+}
+
+type ActionList struct {
+	actions  []*Action
+	observer actionListObserver
+}
+
+func newActionList(observer actionListObserver) *ActionList {
+	if observer == nil {
+		panic("observer == nil")
+	}
+
+	return &ActionList{observer: observer}
+}
+
+func (l *ActionList) Add(action *Action) error {
+	return l.Insert(len(l.actions), action)
+}
+
+func (l *ActionList) AddMenu(menu *Menu) (*Action, error) {
+	return l.InsertMenu(len(l.actions), menu)
+}
+
+func (l *ActionList) At(index int) *Action {
+	return l.actions[index]
+}
+
+func (l *ActionList) Clear() error {
+	if err := l.observer.onClearingActions(); err != nil {
+		return err
+	}
+
+	for _, a := range l.actions {
+		a.release()
+	}
+
+	l.actions = l.actions[:0]
+
+	return nil
+}
+
+func (l *ActionList) Contains(action *Action) bool {
+	return l.Index(action) > -1
+}
+
+func (l *ActionList) Index(action *Action) int {
+	for i, a := range l.actions {
+		if a == action {
+			return i
+		}
+	}
+
+	return -1
+}
+
+func (l *ActionList) indexInObserver(action *Action) int {
+	var idx int
+
+	for _, a := range l.actions {
+		if a == action {
+			return idx
+		}
+		if a.Visible() {
+			idx++
+		}
+	}
+
+	return -1
+}
+
+func (l *ActionList) Insert(index int, action *Action) error {
+	l.actions = append(l.actions, nil)
+	copy(l.actions[index+1:], l.actions[index:])
+	l.actions[index] = action
+
+	if err := l.observer.onInsertedAction(action); err != nil {
+		l.actions = append(l.actions[:index], l.actions[index+1:]...)
+
+		return err
+	}
+
+	action.addRef()
+
+	if action.Visible() {
+		return l.updateSeparatorVisibility()
+	}
+
+	return nil
+}
+
+func (l *ActionList) InsertMenu(index int, menu *Menu) (*Action, error) {
+	action := NewAction()
+	action.menu = menu
+
+	if err := l.Insert(index, action); err != nil {
+		return nil, err
+	}
+
+	return action, nil
+}
+
+func (l *ActionList) Len() int {
+	return len(l.actions)
+}
+
+func (l *ActionList) Remove(action *Action) error {
+	index := l.Index(action)
+	if index == -1 {
+		return nil
+	}
+
+	return l.RemoveAt(index)
+}
+
+func (l *ActionList) RemoveAt(index int) error {
+	action := l.actions[index]
+	if action.Visible() {
+		if err := l.observer.onRemovingAction(action); err != nil {
+			return err
+		}
+	}
+
+	action.release()
+
+	l.actions = append(l.actions[:index], l.actions[index+1:]...)
+
+	if action.Visible() {
+		return l.updateSeparatorVisibility()
+	}
+
+	return nil
+}
+
+func (l *ActionList) updateSeparatorVisibility() error {
+	var hasCurVisAct bool
+	var curVisSep *Action
+
+	for _, a := range l.actions {
+		if visible := a.Visible(); a.IsSeparator() {
+			toggle := visible != hasCurVisAct
+
+			if toggle {
+				visible = !visible
+				if err := a.SetVisible(visible); err != nil {
+					return err
+				}
+			}
+
+			if visible {
+				curVisSep = a
+			}
+
+			hasCurVisAct = false
+		} else if visible {
+			hasCurVisAct = true
+		}
+	}
+
+	if !hasCurVisAct && curVisSep != nil {
+		return curVisSep.SetVisible(false)
+	}
+
+	return nil
+}

+ 87 - 0
common/src/github.com/lxn/walk/application.go

@@ -0,0 +1,87 @@
+// 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 (
+	"time"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type Settings interface {
+	Get(key string) (string, bool)
+	Timestamp(key string) (time.Time, bool)
+	Put(key, value string) error
+	PutExpiring(key, value string) error
+	Remove(key string) error
+	ExpireDuration() time.Duration
+	SetExpireDuration(expireDuration time.Duration)
+	Load() error
+	Save() error
+}
+
+type Persistable interface {
+	Persistent() bool
+	SetPersistent(value bool)
+	SaveState() error
+	RestoreState() error
+}
+
+type Application struct {
+	organizationName   string
+	productName        string
+	settings           Settings
+	exiting            bool
+	exitCode           int
+	panickingPublisher ErrorEventPublisher
+}
+
+var appSingleton *Application = new(Application)
+
+func App() *Application {
+	return appSingleton
+}
+
+func (app *Application) OrganizationName() string {
+	return app.organizationName
+}
+
+func (app *Application) SetOrganizationName(value string) {
+	app.organizationName = value
+}
+
+func (app *Application) ProductName() string {
+	return app.productName
+}
+
+func (app *Application) SetProductName(value string) {
+	app.productName = value
+}
+
+func (app *Application) Settings() Settings {
+	return app.settings
+}
+
+func (app *Application) SetSettings(value Settings) {
+	app.settings = value
+}
+
+func (app *Application) Exit(exitCode int) {
+	app.exiting = true
+	app.exitCode = exitCode
+	win.PostQuitMessage(int32(exitCode))
+}
+
+func (app *Application) ExitCode() int {
+	return app.exitCode
+}
+
+func (app *Application) Panicking() *ErrorEvent {
+	return app.panickingPublisher.Event()
+}

+ 298 - 0
common/src/github.com/lxn/walk/bitmap.go

@@ -0,0 +1,298 @@
+// 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 (
+	"fmt"
+	"image"
+	"syscall"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+func withCompatibleDC(f func(hdc win.HDC) error) error {
+	hdc := win.CreateCompatibleDC(0)
+	if hdc == 0 {
+		return newError("CreateCompatibleDC failed")
+	}
+	defer win.DeleteDC(hdc)
+
+	return f(hdc)
+}
+
+func hPackedDIBFromHBITMAP(hBmp win.HBITMAP) (win.HGLOBAL, error) {
+	var dib win.DIBSECTION
+	if win.GetObject(win.HGDIOBJ(hBmp), unsafe.Sizeof(dib), unsafe.Pointer(&dib)) == 0 {
+		return 0, newError("GetObject failed")
+	}
+
+	bmihSize := uintptr(unsafe.Sizeof(dib.DsBmih))
+	pixelsSize := uintptr(
+		int32(dib.DsBmih.BiBitCount) * dib.DsBmih.BiWidth * dib.DsBmih.BiHeight)
+
+	totalSize := bmihSize + pixelsSize
+
+	hPackedDIB := win.GlobalAlloc(win.GHND, totalSize)
+	dest := win.GlobalLock(hPackedDIB)
+	defer win.GlobalUnlock(hPackedDIB)
+
+	src := unsafe.Pointer(&dib.DsBmih)
+
+	win.MoveMemory(dest, src, bmihSize)
+
+	dest = unsafe.Pointer(uintptr(dest) + bmihSize)
+	src = unsafe.Pointer(uintptr(src) + bmihSize)
+
+	win.MoveMemory(dest, src, pixelsSize)
+
+	return hPackedDIB, nil
+}
+
+func hBitmapFromImage(im image.Image) (win.HBITMAP, error) {
+	var bi win.BITMAPV5HEADER
+	bi.BiSize = uint32(unsafe.Sizeof(bi))
+	bi.BiWidth = int32(im.Bounds().Dx())
+	bi.BiHeight = -int32(im.Bounds().Dy())
+	bi.BiPlanes = 1
+	bi.BiBitCount = 32
+	bi.BiCompression = win.BI_BITFIELDS
+	// The following mask specification specifies a supported 32 BPP
+	// alpha format for Windows XP.
+	bi.BV4RedMask = 0x00FF0000
+	bi.BV4GreenMask = 0x0000FF00
+	bi.BV4BlueMask = 0x000000FF
+	bi.BV4AlphaMask = 0xFF000000
+
+	hdc := win.GetDC(0)
+	defer win.ReleaseDC(0, hdc)
+
+	var lpBits unsafe.Pointer
+
+	// Create the DIB section with an alpha channel.
+	hBitmap := win.CreateDIBSection(hdc, &bi.BITMAPINFOHEADER, win.DIB_RGB_COLORS, &lpBits, 0, 0)
+	switch hBitmap {
+	case 0, win.ERROR_INVALID_PARAMETER:
+		return 0, newError("CreateDIBSection failed")
+	}
+
+	// Fill the image
+	bitmap_array := (*[1 << 30]byte)(unsafe.Pointer(lpBits))
+	i := 0
+	for y := im.Bounds().Min.Y; y != im.Bounds().Max.Y; y++ {
+		for x := im.Bounds().Min.X; x != im.Bounds().Max.X; x++ {
+			r, g, b, a := im.At(x, y).RGBA()
+			bitmap_array[i+3] = byte(a >> 8)
+			bitmap_array[i+2] = byte(r >> 8)
+			bitmap_array[i+1] = byte(g >> 8)
+			bitmap_array[i+0] = byte(b >> 8)
+			i += 4
+		}
+	}
+
+	return hBitmap, nil
+}
+
+func hBitmapFromWindow(window Window) (win.HBITMAP, error) {
+	hdcMem := win.CreateCompatibleDC(0)
+	if hdcMem == 0 {
+		return 0, newError("CreateCompatibleDC failed")
+	}
+	defer win.DeleteDC(hdcMem)
+
+	var r win.RECT
+	if !win.GetWindowRect(window.Handle(), &r) {
+		return 0, newError("GetWindowRect failed")
+	}
+
+	hdc := win.GetDC(window.Handle())
+	width, height := r.Right-r.Left, r.Bottom-r.Top
+	hBmp := win.CreateCompatibleBitmap(hdc, width, height)
+	win.ReleaseDC(window.Handle(), hdc)
+
+	hOld := win.SelectObject(hdcMem, win.HGDIOBJ(hBmp))
+	flags := win.PRF_CHILDREN | win.PRF_CLIENT | win.PRF_ERASEBKGND | win.PRF_NONCLIENT | win.PRF_OWNED
+	window.SendMessage(win.WM_PRINT, uintptr(hdcMem), uintptr(flags))
+
+	win.SelectObject(hdcMem, hOld)
+	return hBmp, nil
+}
+
+type Bitmap struct {
+	hBmp       win.HBITMAP
+	hPackedDIB win.HGLOBAL
+	size       Size
+}
+
+func newBitmapFromHBITMAP(hBmp win.HBITMAP) (bmp *Bitmap, err error) {
+	var dib win.DIBSECTION
+	if win.GetObject(win.HGDIOBJ(hBmp), unsafe.Sizeof(dib), unsafe.Pointer(&dib)) == 0 {
+		return nil, newError("GetObject failed")
+	}
+
+	bmih := &dib.DsBmih
+
+	bmihSize := uintptr(unsafe.Sizeof(*bmih))
+	pixelsSize := uintptr(int32(bmih.BiBitCount)*bmih.BiWidth*bmih.BiHeight) / 8
+
+	totalSize := uintptr(bmihSize + pixelsSize)
+
+	hPackedDIB := win.GlobalAlloc(win.GHND, totalSize)
+	dest := win.GlobalLock(hPackedDIB)
+	defer win.GlobalUnlock(hPackedDIB)
+
+	src := unsafe.Pointer(&dib.DsBmih)
+
+	win.MoveMemory(dest, src, bmihSize)
+
+	dest = unsafe.Pointer(uintptr(dest) + bmihSize)
+	src = dib.DsBm.BmBits
+
+	win.MoveMemory(dest, src, pixelsSize)
+
+	return &Bitmap{
+		hBmp:       hBmp,
+		hPackedDIB: hPackedDIB,
+		size: Size{
+			int(bmih.BiWidth),
+			int(bmih.BiHeight),
+		},
+	}, nil
+}
+
+func NewBitmap(size Size) (bmp *Bitmap, err error) {
+	var hdr win.BITMAPINFOHEADER
+	hdr.BiSize = uint32(unsafe.Sizeof(hdr))
+	hdr.BiBitCount = 24
+	hdr.BiCompression = win.BI_RGB
+	hdr.BiPlanes = 1
+	hdr.BiWidth = int32(size.Width)
+	hdr.BiHeight = int32(size.Height)
+
+	err = withCompatibleDC(func(hdc win.HDC) error {
+		hBmp := win.CreateDIBSection(hdc, &hdr, win.DIB_RGB_COLORS, nil, 0, 0)
+		switch hBmp {
+		case 0, win.ERROR_INVALID_PARAMETER:
+			return newError("CreateDIBSection failed")
+		}
+
+		bmp, err = newBitmapFromHBITMAP(hBmp)
+		return err
+	})
+
+	return
+}
+
+func NewBitmapFromFile(filePath string) (*Bitmap, error) {
+	var si win.GdiplusStartupInput
+	si.GdiplusVersion = 1
+	if status := win.GdiplusStartup(&si, nil); status != win.Ok {
+		return nil, newError(fmt.Sprintf("GdiplusStartup failed with status '%s'", status))
+	}
+	defer win.GdiplusShutdown()
+
+	var gpBmp *win.GpBitmap
+	if status := win.GdipCreateBitmapFromFile(syscall.StringToUTF16Ptr(filePath), &gpBmp); status != win.Ok {
+		return nil, newError(fmt.Sprintf("GdipCreateBitmapFromFile failed with status '%s' for file '%s'", status, filePath))
+	}
+	defer win.GdipDisposeImage((*win.GpImage)(gpBmp))
+
+	var hBmp win.HBITMAP
+	if status := win.GdipCreateHBITMAPFromBitmap(gpBmp, &hBmp, 0); status != win.Ok {
+		return nil, newError(fmt.Sprintf("GdipCreateHBITMAPFromBitmap failed with status '%s' for file '%s'", status, filePath))
+	}
+
+	return newBitmapFromHBITMAP(hBmp)
+}
+
+func NewBitmapFromImage(im image.Image) (*Bitmap, error) {
+	hBmp, err := hBitmapFromImage(im)
+	if err != nil {
+		return nil, err
+	}
+	return newBitmapFromHBITMAP(hBmp)
+}
+
+func (bmp *Bitmap) withSelectedIntoMemDC(f func(hdcMem win.HDC) error) error {
+	return withCompatibleDC(func(hdcMem win.HDC) error {
+		hBmpOld := win.SelectObject(hdcMem, win.HGDIOBJ(bmp.hBmp))
+		if hBmpOld == 0 {
+			return newError("SelectObject failed")
+		}
+		defer win.SelectObject(hdcMem, hBmpOld)
+
+		return f(hdcMem)
+	})
+}
+
+func (bmp *Bitmap) draw(hdc win.HDC, location Point) error {
+	return bmp.withSelectedIntoMemDC(func(hdcMem win.HDC) error {
+		size := bmp.Size()
+
+		if !win.BitBlt(
+			hdc,
+			int32(location.X),
+			int32(location.Y),
+			int32(size.Width),
+			int32(size.Height),
+			hdcMem,
+			0,
+			0,
+			win.SRCCOPY) {
+
+			return lastError("BitBlt")
+		}
+
+		return nil
+	})
+}
+
+func (bmp *Bitmap) drawStretched(hdc win.HDC, bounds Rectangle) error {
+	return bmp.withSelectedIntoMemDC(func(hdcMem win.HDC) error {
+		size := bmp.Size()
+
+		if !win.StretchBlt(
+			hdc,
+			int32(bounds.X),
+			int32(bounds.Y),
+			int32(bounds.Width),
+			int32(bounds.Height),
+			hdcMem,
+			0,
+			0,
+			int32(size.Width),
+			int32(size.Height),
+			win.SRCCOPY) {
+
+			return newError("StretchBlt failed")
+		}
+
+		return nil
+	})
+}
+
+func (bmp *Bitmap) handle() win.HBITMAP {
+	return bmp.hBmp
+}
+
+func (bmp *Bitmap) Dispose() {
+	if bmp.hBmp != 0 {
+		win.DeleteObject(win.HGDIOBJ(bmp.hBmp))
+
+		win.GlobalUnlock(bmp.hPackedDIB)
+		win.GlobalFree(bmp.hPackedDIB)
+
+		bmp.hPackedDIB = 0
+		bmp.hBmp = 0
+	}
+}
+
+func (bmp *Bitmap) Size() Size {
+	return bmp.size
+}

+ 516 - 0
common/src/github.com/lxn/walk/boxlayout.go

@@ -0,0 +1,516 @@
+// 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 (
+	"sort"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type Orientation byte
+
+const (
+	Horizontal Orientation = iota
+	Vertical
+)
+
+type BoxLayout struct {
+	container          Container
+	margins            Margins
+	spacing            int
+	orientation        Orientation
+	hwnd2StretchFactor map[win.HWND]int
+	resetNeeded        bool
+}
+
+func newBoxLayout(orientation Orientation) *BoxLayout {
+	return &BoxLayout{
+		orientation:        orientation,
+		hwnd2StretchFactor: make(map[win.HWND]int),
+	}
+}
+
+func NewHBoxLayout() *BoxLayout {
+	return newBoxLayout(Horizontal)
+}
+
+func NewVBoxLayout() *BoxLayout {
+	return newBoxLayout(Vertical)
+}
+
+func (l *BoxLayout) Container() Container {
+	return l.container
+}
+
+func (l *BoxLayout) SetContainer(value Container) {
+	if value != l.container {
+		if l.container != nil {
+			l.container.SetLayout(nil)
+		}
+
+		l.container = value
+
+		if value != nil && value.Layout() != Layout(l) {
+			value.SetLayout(l)
+
+			l.Update(true)
+		}
+	}
+}
+
+func (l *BoxLayout) Margins() Margins {
+	return l.margins
+}
+
+func (l *BoxLayout) SetMargins(value Margins) error {
+	if value.HNear < 0 || value.VNear < 0 || value.HFar < 0 || value.VFar < 0 {
+		return newError("margins must be positive")
+	}
+
+	l.margins = value
+
+	l.Update(false)
+
+	return nil
+}
+
+func (l *BoxLayout) Orientation() Orientation {
+	return l.orientation
+}
+
+func (l *BoxLayout) SetOrientation(value Orientation) error {
+	if value != l.orientation {
+		switch value {
+		case Horizontal, Vertical:
+
+		default:
+			return newError("invalid Orientation value")
+		}
+
+		l.orientation = value
+
+		l.Update(false)
+	}
+
+	return nil
+}
+
+func (l *BoxLayout) Spacing() int {
+	return l.spacing
+}
+
+func (l *BoxLayout) SetSpacing(value int) error {
+	if value != l.spacing {
+		if value < 0 {
+			return newError("spacing cannot be negative")
+		}
+
+		l.spacing = value
+
+		l.Update(false)
+	}
+
+	return nil
+}
+
+func (l *BoxLayout) StretchFactor(widget Widget) int {
+	if factor, ok := l.hwnd2StretchFactor[widget.Handle()]; ok {
+		return factor
+	}
+
+	return 1
+}
+
+func (l *BoxLayout) SetStretchFactor(widget Widget, factor int) error {
+	if factor != l.StretchFactor(widget) {
+		if l.container == nil {
+			return newError("container required")
+		}
+
+		handle := widget.Handle()
+
+		if !l.container.Children().containsHandle(handle) {
+			return newError("unknown widget")
+		}
+		if factor < 1 {
+			return newError("factor must be >= 1")
+		}
+
+		l.hwnd2StretchFactor[handle] = factor
+
+		l.Update(false)
+	}
+
+	return nil
+}
+
+func (l *BoxLayout) cleanupStretchFactors() {
+	widgets := l.container.Children()
+
+	for handle, _ := range l.hwnd2StretchFactor {
+		if !widgets.containsHandle(handle) {
+			delete(l.hwnd2StretchFactor, handle)
+		}
+	}
+}
+
+type widgetInfo struct {
+	index   int
+	minSize int
+	maxSize int
+	stretch int
+	greedy  bool
+	widget  Widget
+}
+
+type widgetInfoList []widgetInfo
+
+func (l widgetInfoList) Len() int {
+	return len(l)
+}
+
+func (l widgetInfoList) Less(i, j int) bool {
+	_, iIsSpacer := l[i].widget.(*Spacer)
+	_, jIsSpacer := l[j].widget.(*Spacer)
+
+	if l[i].greedy == l[j].greedy {
+		if iIsSpacer == jIsSpacer {
+			minDiff := l[i].minSize - l[j].minSize
+
+			if minDiff == 0 {
+				return l[i].maxSize/l[i].stretch < l[j].maxSize/l[j].stretch
+			}
+
+			return minDiff > 0
+		}
+
+		return jIsSpacer
+	}
+
+	return l[i].greedy
+}
+
+func (l widgetInfoList) Swap(i, j int) {
+	l[i], l[j] = l[j], l[i]
+}
+
+func (l *BoxLayout) widgets() []Widget {
+	children := l.container.Children()
+	widgets := make([]Widget, 0, children.Len())
+
+	for i := 0; i < cap(widgets); i++ {
+		widget := children.At(i)
+
+		if !shouldLayoutWidget(widget) {
+			continue
+		}
+
+		ps := widget.SizeHint()
+		if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
+			continue
+		}
+
+		widgets = append(widgets, widget)
+	}
+
+	return widgets
+}
+
+func (l *BoxLayout) LayoutFlags() LayoutFlags {
+	if l.container == nil {
+		return 0
+	}
+
+	var flags LayoutFlags
+	var hasNonShrinkableHorz bool
+	var hasNonShrinkableVert bool
+
+	children := l.container.Children()
+	count := children.Len()
+	if count == 0 {
+		return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert
+	} else {
+		for i := 0; i < count; i++ {
+			widget := children.At(i)
+
+			if !shouldLayoutWidget(widget) {
+				continue
+			}
+
+			f := widget.LayoutFlags()
+			flags |= f
+			if f&ShrinkableHorz == 0 {
+				hasNonShrinkableHorz = true
+			}
+			if f&ShrinkableVert == 0 {
+				hasNonShrinkableVert = true
+			}
+		}
+	}
+
+	if l.orientation == Horizontal {
+		flags |= GrowableHorz
+
+		if hasNonShrinkableVert {
+			flags &^= ShrinkableVert
+		}
+	} else {
+		flags |= GrowableVert
+
+		if hasNonShrinkableHorz {
+			flags &^= ShrinkableHorz
+		}
+	}
+
+	return flags
+}
+
+func (l *BoxLayout) MinSize() Size {
+	if l.container == nil {
+		return Size{}
+	}
+
+	widgets := l.widgets()
+	var s Size
+
+	for _, widget := range widgets {
+		min := minSizeEffective(widget)
+
+		if l.orientation == Horizontal {
+			s.Width += min.Width
+			s.Height = maxi(s.Height, min.Height)
+		} else {
+			s.Height += min.Height
+			s.Width = maxi(s.Width, min.Width)
+		}
+	}
+
+	if l.orientation == Horizontal {
+		s.Width += l.spacing * (len(widgets) - 1)
+		s.Width += l.margins.HNear + l.margins.HFar
+		s.Height += l.margins.VNear + l.margins.VFar
+	} else {
+		s.Height += l.spacing * (len(widgets) - 1)
+		s.Height += l.margins.VNear + l.margins.VFar
+		s.Width += l.margins.HNear + l.margins.HFar
+	}
+
+	return s
+}
+
+func (l *BoxLayout) Update(reset bool) error {
+	if l.container == nil {
+		return nil
+	}
+
+	if reset {
+		l.resetNeeded = true
+	}
+
+	if l.container.Suspended() {
+		return nil
+	}
+
+	if l.resetNeeded {
+		l.resetNeeded = false
+
+		// Make GC happy.
+		l.cleanupStretchFactors()
+	}
+
+	// Begin by finding out which widgets we care about.
+	widgets := l.widgets()
+
+	// Prepare some useful data.
+	var greedyNonSpacerCount int
+	var greedySpacerCount int
+	var stretchFactorsTotal [3]int
+	stretchFactors := make([]int, len(widgets))
+	var minSizesRemaining int
+	minSizes := make([]int, len(widgets))
+	maxSizes := make([]int, len(widgets))
+	sizes := make([]int, len(widgets))
+	prefSizes2 := make([]int, len(widgets))
+	growable2 := make([]bool, len(widgets))
+	sortedWidgetInfo := widgetInfoList(make([]widgetInfo, len(widgets)))
+
+	for i, widget := range widgets {
+		sf := l.hwnd2StretchFactor[widget.Handle()]
+		if sf == 0 {
+			sf = 1
+		}
+		stretchFactors[i] = sf
+
+		flags := widget.LayoutFlags()
+
+		min := widget.MinSize()
+		max := widget.MaxSize()
+		minHint := widget.MinSizeHint()
+		pref := widget.SizeHint()
+
+		if l.orientation == Horizontal {
+			growable2[i] = flags&GrowableVert > 0
+
+			minSizes[i] = maxi(min.Width, minHint.Width)
+
+			if max.Width > 0 {
+				maxSizes[i] = max.Width
+			} else if pref.Width > 0 && flags&GrowableHorz == 0 {
+				maxSizes[i] = pref.Width
+			} else {
+				maxSizes[i] = 32768
+			}
+
+			prefSizes2[i] = pref.Height
+
+			sortedWidgetInfo[i].greedy = flags&GreedyHorz > 0
+		} else {
+			growable2[i] = flags&GrowableHorz > 0
+
+			minSizes[i] = maxi(min.Height, minHint.Height)
+
+			if max.Height > 0 {
+				maxSizes[i] = max.Height
+			} else if pref.Height > 0 && flags&GrowableVert == 0 {
+				maxSizes[i] = pref.Height
+			} else {
+				maxSizes[i] = 32768
+			}
+
+			prefSizes2[i] = pref.Width
+
+			sortedWidgetInfo[i].greedy = flags&GreedyVert > 0
+		}
+
+		sortedWidgetInfo[i].index = i
+		sortedWidgetInfo[i].minSize = minSizes[i]
+		sortedWidgetInfo[i].maxSize = maxSizes[i]
+		sortedWidgetInfo[i].stretch = sf
+		sortedWidgetInfo[i].widget = widget
+
+		minSizesRemaining += minSizes[i]
+
+		if sortedWidgetInfo[i].greedy {
+			if _, isSpacer := widget.(*Spacer); !isSpacer {
+				greedyNonSpacerCount++
+				stretchFactorsTotal[0] += sf
+			} else {
+				greedySpacerCount++
+				stretchFactorsTotal[1] += sf
+			}
+		} else {
+			stretchFactorsTotal[2] += sf
+		}
+	}
+
+	sort.Stable(sortedWidgetInfo)
+
+	cb := l.container.ClientBounds()
+	var start1, start2, space1, space2 int
+	if l.orientation == Horizontal {
+		start1 = cb.X + l.margins.HNear
+		start2 = cb.Y + l.margins.VNear
+		space1 = cb.Width - l.margins.HNear - l.margins.HFar
+		space2 = cb.Height - l.margins.VNear - l.margins.VFar
+	} else {
+		start1 = cb.Y + l.margins.VNear
+		start2 = cb.X + l.margins.HNear
+		space1 = cb.Height - l.margins.VNear - l.margins.VFar
+		space2 = cb.Width - l.margins.HNear - l.margins.HFar
+	}
+
+	// Now calculate widget primary axis sizes.
+	spacingRemaining := l.spacing * (len(widgets) - 1)
+
+	offsets := [3]int{0, greedyNonSpacerCount, greedyNonSpacerCount + greedySpacerCount}
+	counts := [3]int{greedyNonSpacerCount, greedySpacerCount, len(widgets) - greedyNonSpacerCount - greedySpacerCount}
+
+	for i := 0; i < 3; i++ {
+		stretchFactorsRemaining := stretchFactorsTotal[i]
+
+		for j := 0; j < counts[i]; j++ {
+			info := sortedWidgetInfo[offsets[i]+j]
+			k := info.index
+
+			stretch := stretchFactors[k]
+			min := info.minSize
+			max := info.maxSize
+			size := min
+
+			if min < max {
+				excessSpace := float64(space1 - minSizesRemaining - spacingRemaining)
+				size += int(excessSpace * float64(stretch) / float64(stretchFactorsRemaining))
+				if size < min {
+					size = min
+				} else if size > max {
+					size = max
+				}
+			}
+
+			sizes[k] = size
+
+			minSizesRemaining -= min
+			stretchFactorsRemaining -= stretch
+			space1 -= (size + l.spacing)
+			spacingRemaining -= l.spacing
+		}
+	}
+
+	// Finally position widgets.
+	hdwp := win.BeginDeferWindowPos(int32(len(widgets)))
+	if hdwp == 0 {
+		return lastError("BeginDeferWindowPos")
+	}
+
+	excessTotal := space1 - minSizesRemaining - spacingRemaining
+	excessShare := excessTotal / (len(widgets) + 1)
+	p1 := start1
+	for i, widget := range widgets {
+		p1 += excessShare
+		s1 := sizes[i]
+
+		var s2 int
+		if growable2[i] {
+			s2 = space2
+		} else {
+			s2 = prefSizes2[i]
+		}
+
+		p2 := start2 + (space2-s2)/2
+
+		var x, y, w, h int
+		if l.orientation == Horizontal {
+			x, y, w, h = p1, p2, s1, s2
+		} else {
+			x, y, w, h = p2, p1, s2, s1
+		}
+
+		if hdwp = win.DeferWindowPos(
+			hdwp,
+			widget.Handle(),
+			0,
+			int32(x),
+			int32(y),
+			int32(w),
+			int32(h),
+			win.SWP_NOACTIVATE|win.SWP_NOOWNERZORDER|win.SWP_NOZORDER); hdwp == 0 {
+
+			return lastError("DeferWindowPos")
+		}
+
+		p1 += s1 + l.spacing
+	}
+
+	if !win.EndDeferWindowPos(hdwp) {
+		return lastError("EndDeferWindowPos")
+	}
+
+	return nil
+}

+ 215 - 0
common/src/github.com/lxn/walk/brush.go

@@ -0,0 +1,215 @@
+// 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 (
+	"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 Brush interface {
+	Dispose()
+	handle() win.HBRUSH
+	logbrush() *win.LOGBRUSH
+}
+
+type nullBrush struct {
+	hBrush win.HBRUSH
+}
+
+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{hBrush: hBrush}
+}
+
+func (b *nullBrush) Dispose() {
+	if b.hBrush != 0 {
+		win.DeleteObject(win.HGDIOBJ(b.hBrush))
+
+		b.hBrush = 0
+	}
+}
+
+func (b *nullBrush) handle() win.HBRUSH {
+	return b.hBrush
+}
+
+func (b *nullBrush) logbrush() *win.LOGBRUSH {
+	return &win.LOGBRUSH{LbStyle: win.BS_NULL}
+}
+
+var nullBrushSingleton Brush = newNullBrush()
+
+func NullBrush() Brush {
+	return nullBrushSingleton
+}
+
+type SystemColorBrush struct {
+	hBrush     win.HBRUSH
+	colorIndex int
+}
+
+func NewSystemColorBrush(colorIndex int) (*SystemColorBrush, error) {
+	hBrush := win.GetSysColorBrush(colorIndex)
+	if hBrush == 0 {
+		return nil, newError("GetSysColorBrush failed")
+	}
+
+	return &SystemColorBrush{hBrush, colorIndex}, nil
+}
+
+func (b *SystemColorBrush) ColorIndex() int {
+	return b.colorIndex
+}
+
+func (b *SystemColorBrush) Dispose() {
+	// nop
+}
+
+func (b *SystemColorBrush) handle() win.HBRUSH {
+	return b.hBrush
+}
+
+func (b *SystemColorBrush) logbrush() *win.LOGBRUSH {
+	return &win.LOGBRUSH{
+		LbStyle: win.BS_SOLID,
+		LbColor: win.COLORREF(win.GetSysColor(b.colorIndex)),
+	}
+}
+
+type SolidColorBrush struct {
+	hBrush win.HBRUSH
+	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{hBrush: hBrush, color: color}, nil
+}
+
+func (b *SolidColorBrush) Color() Color {
+	return b.color
+}
+
+func (b *SolidColorBrush) Dispose() {
+	if b.hBrush != 0 {
+		win.DeleteObject(win.HGDIOBJ(b.hBrush))
+
+		b.hBrush = 0
+	}
+}
+
+func (b *SolidColorBrush) handle() win.HBRUSH {
+	return b.hBrush
+}
+
+func (b *SolidColorBrush) logbrush() *win.LOGBRUSH {
+	return &win.LOGBRUSH{LbStyle: win.BS_SOLID, LbColor: win.COLORREF(b.color)}
+}
+
+type HatchBrush struct {
+	hBrush win.HBRUSH
+	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{hBrush: hBrush, color: color, style: style}, nil
+}
+
+func (b *HatchBrush) Color() Color {
+	return b.color
+}
+
+func (b *HatchBrush) Dispose() {
+	if b.hBrush != 0 {
+		win.DeleteObject(win.HGDIOBJ(b.hBrush))
+
+		b.hBrush = 0
+	}
+}
+
+func (b *HatchBrush) handle() win.HBRUSH {
+	return b.hBrush
+}
+
+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
+}
+
+type BitmapBrush struct {
+	hBrush win.HBRUSH
+	bitmap *Bitmap
+}
+
+func NewBitmapBrush(bitmap *Bitmap) (*BitmapBrush, error) {
+	if bitmap == nil {
+		return nil, newError("bitmap cannot be nil")
+	}
+
+	lb := &win.LOGBRUSH{LbStyle: win.BS_DIBPATTERN, LbColor: win.DIB_RGB_COLORS, LbHatch: uintptr(bitmap.hPackedDIB)}
+
+	hBrush := win.CreateBrushIndirect(lb)
+	if hBrush == 0 {
+		return nil, newError("CreateBrushIndirect failed")
+	}
+
+	return &BitmapBrush{hBrush: hBrush, bitmap: bitmap}, nil
+}
+
+func (b *BitmapBrush) Dispose() {
+	if b.hBrush != 0 {
+		win.DeleteObject(win.HGDIOBJ(b.hBrush))
+
+		b.hBrush = 0
+	}
+}
+
+func (b *BitmapBrush) handle() win.HBRUSH {
+	return b.hBrush
+}
+
+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
+}

+ 147 - 0
common/src/github.com/lxn/walk/button.go

@@ -0,0 +1,147 @@
+// 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 (
+	"github.com/lxn/win"
+)
+
+type clickable interface {
+	raiseClicked()
+}
+
+type setCheckeder interface {
+	setChecked(checked bool)
+}
+
+type Button struct {
+	WidgetBase
+	checkedChangedPublisher EventPublisher
+	clickedPublisher        EventPublisher
+	textChangedPublisher    EventPublisher
+	image                   Image
+}
+
+func (b *Button) init() {
+	b.MustRegisterProperty("Checked", NewBoolProperty(
+		func() bool {
+			return b.Checked()
+		},
+		func(v bool) error {
+			b.SetChecked(v)
+			return nil
+		},
+		b.CheckedChanged()))
+
+	b.MustRegisterProperty("Text", NewProperty(
+		func() interface{} {
+			return b.Text()
+		},
+		func(v interface{}) error {
+			return b.SetText(v.(string))
+		},
+		b.textChangedPublisher.Event()))
+}
+
+func (b *Button) Image() Image {
+	return b.image
+}
+
+func (b *Button) SetImage(image Image) error {
+	var typ uintptr
+	var handle uintptr
+	switch img := image.(type) {
+	case nil:
+		// zeroes are good
+
+	case *Bitmap:
+		typ = win.IMAGE_BITMAP
+		handle = uintptr(img.hBmp)
+
+	case *Icon:
+		typ = win.IMAGE_ICON
+		handle = uintptr(img.hIcon)
+
+	default:
+		return newError("image must be either *walk.Bitmap or *walk.Icon")
+	}
+
+	b.SendMessage(win.BM_SETIMAGE, typ, handle)
+
+	b.image = image
+
+	return b.updateParentLayout()
+}
+
+func (b *Button) Text() string {
+	return windowText(b.hWnd)
+}
+
+func (b *Button) SetText(value string) error {
+	if value == b.Text() {
+		return nil
+	}
+
+	if err := setWindowText(b.hWnd, value); err != nil {
+		return err
+	}
+
+	return b.updateParentLayout()
+}
+
+func (b *Button) Checked() bool {
+	return b.SendMessage(win.BM_GETCHECK, 0, 0) == win.BST_CHECKED
+}
+
+func (b *Button) SetChecked(checked bool) {
+	if checked == b.Checked() {
+		return
+	}
+
+	b.window.(setCheckeder).setChecked(checked)
+}
+
+func (b *Button) setChecked(checked bool) {
+	var chk uintptr
+
+	if checked {
+		chk = win.BST_CHECKED
+	} else {
+		chk = win.BST_UNCHECKED
+	}
+
+	b.SendMessage(win.BM_SETCHECK, chk, 0)
+
+	b.checkedChangedPublisher.Publish()
+}
+
+func (b *Button) CheckedChanged() *Event {
+	return b.checkedChangedPublisher.Event()
+}
+
+func (b *Button) Clicked() *Event {
+	return b.clickedPublisher.Event()
+}
+
+func (b *Button) raiseClicked() {
+	b.clickedPublisher.Publish()
+}
+
+func (b *Button) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_COMMAND:
+		switch win.HIWORD(uint32(wParam)) {
+		case win.BN_CLICKED:
+			b.raiseClicked()
+		}
+
+	case win.WM_SETTEXT:
+		b.textChangedPublisher.Publish()
+	}
+
+	return b.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}

+ 45 - 0
common/src/github.com/lxn/walk/cancelevent.go

@@ -0,0 +1,45 @@
+// Copyright 2011 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
+
+type CancelEventHandler func(canceled *bool)
+
+type CancelEvent struct {
+	handlers []CancelEventHandler
+}
+
+func (e *CancelEvent) Attach(handler CancelEventHandler) int {
+	for i, h := range e.handlers {
+		if h == nil {
+			e.handlers[i] = handler
+			return i
+		}
+	}
+
+	e.handlers = append(e.handlers, handler)
+	return len(e.handlers) - 1
+}
+
+func (e *CancelEvent) Detach(handle int) {
+	e.handlers[handle] = nil
+}
+
+type CancelEventPublisher struct {
+	event CancelEvent
+}
+
+func (p *CancelEventPublisher) Event() *CancelEvent {
+	return &p.event
+}
+
+func (p *CancelEventPublisher) Publish(canceled *bool) {
+	for _, handler := range p.event.handlers {
+		if handler != nil {
+			handler(canceled)
+		}
+	}
+}

+ 382 - 0
common/src/github.com/lxn/walk/canvas.go

@@ -0,0 +1,382 @@
+// Copyright 2010 The Walk Authorc. 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 (
+	"syscall"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+// DrawText format flags
+type DrawTextFormat uint
+
+const (
+	TextTop                  DrawTextFormat = win.DT_TOP
+	TextLeft                 DrawTextFormat = win.DT_LEFT
+	TextCenter               DrawTextFormat = win.DT_CENTER
+	TextRight                DrawTextFormat = win.DT_RIGHT
+	TextVCenter              DrawTextFormat = win.DT_VCENTER
+	TextBottom               DrawTextFormat = win.DT_BOTTOM
+	TextWordbreak            DrawTextFormat = win.DT_WORDBREAK
+	TextSingleLine           DrawTextFormat = win.DT_SINGLELINE
+	TextExpandTabs           DrawTextFormat = win.DT_EXPANDTABS
+	TextTabstop              DrawTextFormat = win.DT_TABSTOP
+	TextNoClip               DrawTextFormat = win.DT_NOCLIP
+	TextExternalLeading      DrawTextFormat = win.DT_EXTERNALLEADING
+	TextCalcRect             DrawTextFormat = win.DT_CALCRECT
+	TextNoPrefix             DrawTextFormat = win.DT_NOPREFIX
+	TextInternal             DrawTextFormat = win.DT_INTERNAL
+	TextEditControl          DrawTextFormat = win.DT_EDITCONTROL
+	TextPathEllipsis         DrawTextFormat = win.DT_PATH_ELLIPSIS
+	TextEndEllipsis          DrawTextFormat = win.DT_END_ELLIPSIS
+	TextModifyString         DrawTextFormat = win.DT_MODIFYSTRING
+	TextRTLReading           DrawTextFormat = win.DT_RTLREADING
+	TextWordEllipsis         DrawTextFormat = win.DT_WORD_ELLIPSIS
+	TextNoFullWidthCharBreak DrawTextFormat = win.DT_NOFULLWIDTHCHARBREAK
+	TextHidePrefix           DrawTextFormat = win.DT_HIDEPREFIX
+	TextPrefixOnly           DrawTextFormat = win.DT_PREFIXONLY
+)
+
+var gM = syscall.StringToUTF16Ptr("gM")
+
+type Canvas struct {
+	hdc                 win.HDC
+	hwnd                win.HWND
+	dpix                int
+	dpiy                int
+	doNotDispose        bool
+	recordingMetafile   *Metafile
+	measureTextMetafile *Metafile
+}
+
+func NewCanvasFromImage(image Image) (*Canvas, error) {
+	switch img := image.(type) {
+	case *Bitmap:
+		hdc := win.CreateCompatibleDC(0)
+		if hdc == 0 {
+			return nil, newError("CreateCompatibleDC failed")
+		}
+		succeeded := false
+
+		defer func() {
+			if !succeeded {
+				win.DeleteDC(hdc)
+			}
+		}()
+
+		if win.SelectObject(hdc, win.HGDIOBJ(img.hBmp)) == 0 {
+			return nil, newError("SelectObject failed")
+		}
+
+		succeeded = true
+
+		return (&Canvas{hdc: hdc}).init()
+
+	case *Metafile:
+		c, err := newCanvasFromHDC(img.hdc)
+		if err != nil {
+			return nil, err
+		}
+
+		c.recordingMetafile = img
+
+		return c, nil
+	}
+
+	return nil, newError("unsupported image type")
+}
+
+func newCanvasFromHWND(hwnd win.HWND) (*Canvas, error) {
+	hdc := win.GetDC(hwnd)
+	if hdc == 0 {
+		return nil, newError("GetDC failed")
+	}
+
+	return (&Canvas{hdc: hdc, hwnd: hwnd}).init()
+}
+
+func newCanvasFromHDC(hdc win.HDC) (*Canvas, error) {
+	if hdc == 0 {
+		return nil, newError("invalid hdc")
+	}
+
+	return (&Canvas{hdc: hdc, doNotDispose: true}).init()
+}
+
+func (c *Canvas) init() (*Canvas, error) {
+	c.dpix = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSX))
+	c.dpiy = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSY))
+
+	if win.SetBkMode(c.hdc, win.TRANSPARENT) == 0 {
+		return nil, newError("SetBkMode failed")
+	}
+
+	switch win.SetStretchBltMode(c.hdc, win.HALFTONE) {
+	case 0, win.ERROR_INVALID_PARAMETER:
+		return nil, newError("SetStretchBltMode failed")
+	}
+
+	if !win.SetBrushOrgEx(c.hdc, 0, 0, nil) {
+		return nil, newError("SetBrushOrgEx failed")
+	}
+
+	return c, nil
+}
+
+func (c *Canvas) Dispose() {
+	if !c.doNotDispose && c.hdc != 0 {
+		if c.hwnd == 0 {
+			win.DeleteDC(c.hdc)
+		} else {
+			win.ReleaseDC(c.hwnd, c.hdc)
+		}
+
+		c.hdc = 0
+	}
+
+	if c.recordingMetafile != nil {
+		c.recordingMetafile.ensureFinished()
+		c.recordingMetafile = nil
+	}
+
+	if c.measureTextMetafile != nil {
+		c.measureTextMetafile.Dispose()
+		c.measureTextMetafile = nil
+	}
+}
+
+func (c *Canvas) withGdiObj(handle win.HGDIOBJ, f func() error) error {
+	oldHandle := win.SelectObject(c.hdc, handle)
+	if oldHandle == 0 {
+		return newError("SelectObject failed")
+	}
+	defer win.SelectObject(c.hdc, oldHandle)
+
+	return f()
+}
+
+func (c *Canvas) withBrush(brush Brush, f func() error) error {
+	return c.withGdiObj(win.HGDIOBJ(brush.handle()), f)
+}
+
+func (c *Canvas) withFontAndTextColor(font *Font, color Color, f func() error) error {
+	return c.withGdiObj(win.HGDIOBJ(font.handleForDPI(c.dpiy)), func() error {
+		oldColor := win.SetTextColor(c.hdc, win.COLORREF(color))
+		if oldColor == win.CLR_INVALID {
+			return newError("SetTextColor failed")
+		}
+		defer func() {
+			win.SetTextColor(c.hdc, oldColor)
+		}()
+
+		return f()
+	})
+}
+
+func (c *Canvas) Bounds() Rectangle {
+	return Rectangle{
+		Width:  int(win.GetDeviceCaps(c.hdc, win.HORZRES)),
+		Height: int(win.GetDeviceCaps(c.hdc, win.VERTRES)),
+	}
+}
+
+func (c *Canvas) withPen(pen Pen, f func() error) error {
+	return c.withGdiObj(win.HGDIOBJ(pen.handle()), f)
+}
+
+func (c *Canvas) withBrushAndPen(brush Brush, pen Pen, f func() error) error {
+	return c.withBrush(brush, func() error {
+		return c.withPen(pen, f)
+	})
+}
+
+func (c *Canvas) ellipse(brush Brush, pen Pen, bounds Rectangle, sizeCorrection int) error {
+	return c.withBrushAndPen(brush, pen, func() error {
+		if !win.Ellipse(
+			c.hdc,
+			int32(bounds.X),
+			int32(bounds.Y),
+			int32(bounds.X+bounds.Width+sizeCorrection),
+			int32(bounds.Y+bounds.Height+sizeCorrection)) {
+
+			return newError("Ellipse failed")
+		}
+
+		return nil
+	})
+}
+
+func (c *Canvas) DrawEllipse(pen Pen, bounds Rectangle) error {
+	return c.ellipse(nullBrushSingleton, pen, bounds, 0)
+}
+
+func (c *Canvas) FillEllipse(brush Brush, bounds Rectangle) error {
+	return c.ellipse(brush, nullPenSingleton, bounds, 1)
+}
+
+func (c *Canvas) DrawImage(image Image, location Point) error {
+	if image == nil {
+		return newError("image cannot be nil")
+	}
+
+	return image.draw(c.hdc, location)
+}
+
+func (c *Canvas) DrawImageStretched(image Image, bounds Rectangle) error {
+	if image == nil {
+		return newError("image cannot be nil")
+	}
+
+	return image.drawStretched(c.hdc, bounds)
+}
+
+func (c *Canvas) DrawLine(pen Pen, from, to Point) error {
+	if !win.MoveToEx(c.hdc, from.X, from.Y, nil) {
+		return newError("MoveToEx failed")
+	}
+
+	return c.withPen(pen, func() error {
+		if !win.LineTo(c.hdc, int32(to.X), int32(to.Y)) {
+			return newError("LineTo failed")
+		}
+
+		return nil
+	})
+}
+
+func (c *Canvas) DrawPolyline(pen Pen, points []Point) error {
+	if len(points) < 1 {
+		return nil
+	}
+
+	pts := make([]win.POINT, len(points))
+	for i := range points {
+		pts[i] = win.POINT{X: int32(points[i].X), Y: int32(points[i].Y)}
+	}
+
+	return c.withPen(pen, func() error {
+		if !win.Polyline(c.hdc, unsafe.Pointer(&pts[0].X), int32(len(pts))) {
+			return newError("Polyline failed")
+		}
+
+		return nil
+	})
+}
+
+func (c *Canvas) rectangle(brush Brush, pen Pen, bounds Rectangle, sizeCorrection int) error {
+	return c.withBrushAndPen(brush, pen, func() error {
+		if !win.Rectangle_(
+			c.hdc,
+			int32(bounds.X),
+			int32(bounds.Y),
+			int32(bounds.X+bounds.Width+sizeCorrection),
+			int32(bounds.Y+bounds.Height+sizeCorrection)) {
+
+			return newError("Rectangle_ failed")
+		}
+
+		return nil
+	})
+}
+
+func (c *Canvas) DrawRectangle(pen Pen, bounds Rectangle) error {
+	return c.rectangle(nullBrushSingleton, pen, bounds, 0)
+}
+
+func (c *Canvas) FillRectangle(brush Brush, bounds Rectangle) error {
+	return c.rectangle(brush, nullPenSingleton, bounds, 1)
+}
+
+func (c *Canvas) DrawText(text string, font *Font, color Color, bounds Rectangle, format DrawTextFormat) error {
+	return c.withFontAndTextColor(font, color, func() error {
+		rect := bounds.toRECT()
+		ret := win.DrawTextEx(
+			c.hdc,
+			syscall.StringToUTF16Ptr(text),
+			-1,
+			&rect,
+			uint32(format)|win.DT_EDITCONTROL,
+			nil)
+		if ret == 0 {
+			return newError("DrawTextEx failed")
+		}
+
+		return nil
+	})
+}
+
+func (c *Canvas) fontHeight(font *Font) (height int, err error) {
+	err = c.withFontAndTextColor(font, 0, func() error {
+		var size win.SIZE
+		if !win.GetTextExtentPoint32(c.hdc, gM, 2, &size) {
+			return newError("GetTextExtentPoint32 failed")
+		}
+
+		height = int(size.CY)
+		if height == 0 {
+			return newError("invalid font height")
+		}
+
+		return nil
+	})
+
+	return
+}
+
+func (c *Canvas) MeasureText(text string, font *Font, bounds Rectangle, format DrawTextFormat) (boundsMeasured Rectangle, runesFitted int, err error) {
+	// HACK: We don't want to actually draw on the Canvas here, but if we use
+	// the DT_CALCRECT flag to avoid drawing, DRAWTEXTPARAMc.UiLengthDrawn will
+	// not contain a useful value. To work around this, we create an in-memory
+	// metafile and draw into that instead.
+	if c.measureTextMetafile == nil {
+		c.measureTextMetafile, err = NewMetafile(c)
+		if err != nil {
+			return
+		}
+	}
+
+	hFont := win.HGDIOBJ(font.handleForDPI(c.dpiy))
+	oldHandle := win.SelectObject(c.measureTextMetafile.hdc, hFont)
+	if oldHandle == 0 {
+		err = newError("SelectObject failed")
+		return
+	}
+	defer win.SelectObject(c.measureTextMetafile.hdc, oldHandle)
+
+	rect := &win.RECT{
+		int32(bounds.X),
+		int32(bounds.Y),
+		int32(bounds.X + bounds.Width),
+		int32(bounds.Y + bounds.Height),
+	}
+	var params win.DRAWTEXTPARAMS
+	params.CbSize = uint32(unsafe.Sizeof(params))
+
+	strPtr := syscall.StringToUTF16Ptr(text)
+	dtfmt := uint32(format) | win.DT_EDITCONTROL | win.DT_WORDBREAK
+
+	height := win.DrawTextEx(
+		c.measureTextMetafile.hdc, strPtr, -1, rect, dtfmt, &params)
+	if height == 0 {
+		err = newError("DrawTextEx failed")
+		return
+	}
+
+	boundsMeasured = Rectangle{
+		int(rect.Left),
+		int(rect.Top),
+		int(rect.Right - rect.Left),
+		int(height),
+	}
+	runesFitted = int(params.UiLengthDrawn)
+
+	return
+}

+ 125 - 0
common/src/github.com/lxn/walk/checkbox.go

@@ -0,0 +1,125 @@
+// 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 (
+	"github.com/lxn/win"
+)
+
+type CheckState int
+
+const (
+	CheckUnchecked     CheckState = win.BST_UNCHECKED
+	CheckChecked       CheckState = win.BST_CHECKED
+	CheckIndeterminate CheckState = win.BST_INDETERMINATE
+)
+
+type CheckBox struct {
+	Button
+
+	checkStateChangedPublisher EventPublisher
+}
+
+func NewCheckBox(parent Container) (*CheckBox, error) {
+	cb := new(CheckBox)
+
+	if err := InitWidget(
+		cb,
+		parent,
+		"BUTTON",
+		win.WS_TABSTOP|win.WS_VISIBLE|win.BS_AUTOCHECKBOX,
+		0); err != nil {
+		return nil, err
+	}
+
+	cb.Button.init()
+
+	cb.MustRegisterProperty("CheckState", NewProperty(
+		func() interface{} {
+			return cb.CheckState()
+		},
+		func(v interface{}) error {
+			cb.SetCheckState(v.(CheckState))
+
+			return nil
+		},
+		cb.CheckStateChanged()))
+
+	return cb, nil
+}
+
+func (*CheckBox) LayoutFlags() LayoutFlags {
+	return 0
+}
+
+func (cb *CheckBox) MinSizeHint() Size {
+	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 10})
+	textSize := cb.calculateTextSizeImpl("n" + windowText(cb.hWnd))
+
+	// FIXME: Use GetThemePartSize instead of GetSystemMetrics?
+	w := textSize.Width + int(win.GetSystemMetrics(win.SM_CXMENUCHECK))
+	h := maxi(defaultSize.Height, textSize.Height)
+
+	return Size{w, h}
+}
+
+func (cb *CheckBox) SizeHint() Size {
+	return cb.MinSizeHint()
+}
+
+func (cb *CheckBox) setChecked(checked bool) {
+	cb.Button.setChecked(checked)
+
+	cb.checkStateChangedPublisher.Publish()
+}
+
+func (cb *CheckBox) Tristate() bool {
+	return cb.hasStyleBits(win.BS_AUTO3STATE)
+}
+
+func (cb *CheckBox) SetTristate(tristate bool) error {
+	var set, clear uint32
+	if tristate {
+		set, clear = win.BS_AUTO3STATE, win.BS_AUTOCHECKBOX
+	} else {
+		set, clear = win.BS_AUTOCHECKBOX, win.BS_AUTO3STATE
+	}
+
+	return cb.setAndClearStyleBits(set, clear)
+}
+
+func (cb *CheckBox) CheckState() CheckState {
+	return CheckState(cb.SendMessage(win.BM_GETCHECK, 0, 0))
+}
+
+func (cb *CheckBox) SetCheckState(state CheckState) {
+	if state == cb.CheckState() {
+		return
+	}
+
+	cb.SendMessage(win.BM_SETCHECK, uintptr(state), 0)
+
+	cb.checkedChangedPublisher.Publish()
+	cb.checkStateChangedPublisher.Publish()
+}
+
+func (cb *CheckBox) CheckStateChanged() *Event {
+	return cb.checkStateChangedPublisher.Event()
+}
+
+func (cb *CheckBox) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_COMMAND:
+		switch win.HIWORD(uint32(wParam)) {
+		case win.BN_CLICKED:
+			cb.checkedChangedPublisher.Publish()
+			cb.checkStateChangedPublisher.Publish()
+		}
+	}
+
+	return cb.Button.WndProc(hwnd, msg, wParam, lParam)
+}

+ 163 - 0
common/src/github.com/lxn/walk/clipboard.go

@@ -0,0 +1,163 @@
+// Copyright 2013 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 (
+	"syscall"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+const clipboardWindowClass = `\o/ Walk_Clipboard_Class \o/`
+
+func init() {
+	MustRegisterWindowClass(clipboardWindowClass)
+
+	hwnd := win.CreateWindowEx(
+		0,
+		syscall.StringToUTF16Ptr(clipboardWindowClass),
+		nil,
+		0,
+		0,
+		0,
+		0,
+		0,
+		win.HWND_MESSAGE,
+		0,
+		0,
+		nil)
+
+	if hwnd == 0 {
+		panic("failed to create clipboard window")
+	}
+
+	if !win.AddClipboardFormatListener(hwnd) {
+		lastError("AddClipboardFormatListener")
+	}
+
+	clipboard.hwnd = hwnd
+}
+
+func clipboardWndProc(hwnd win.HWND, msg uint32, wp, lp uintptr) uintptr {
+	switch msg {
+	case win.WM_CLIPBOARDUPDATE:
+		clipboard.contentsChangedPublisher.Publish()
+		return 0
+	}
+
+	return win.DefWindowProc(hwnd, msg, wp, lp)
+}
+
+var clipboard ClipboardService
+
+// Clipboard returns an object that provides access to the system clipboard.
+func Clipboard() *ClipboardService {
+	return &clipboard
+}
+
+// ClipboardService provides access to the system clipboard.
+type ClipboardService struct {
+	hwnd                     win.HWND
+	contentsChangedPublisher EventPublisher
+}
+
+// ContentsChanged returns an Event that you can attach to for handling
+// clipboard content changes.
+func (c *ClipboardService) ContentsChanged() *Event {
+	return c.contentsChangedPublisher.Event()
+}
+
+// Clear clears the contents of the clipboard.
+func (c *ClipboardService) Clear() error {
+	return c.withOpenClipboard(func() error {
+		if !win.EmptyClipboard() {
+			return lastError("EmptyClipboard")
+		}
+
+		return nil
+	})
+}
+
+// ContainsText returns whether the clipboard currently contains text data.
+func (c *ClipboardService) ContainsText() (available bool, err error) {
+	err = c.withOpenClipboard(func() error {
+		available = win.IsClipboardFormatAvailable(win.CF_UNICODETEXT)
+
+		return nil
+	})
+
+	return
+}
+
+// Text returns the current text data of the clipboard.
+func (c *ClipboardService) Text() (text string, err error) {
+	err = c.withOpenClipboard(func() error {
+		hMem := win.HGLOBAL(win.GetClipboardData(win.CF_UNICODETEXT))
+		if hMem == 0 {
+			return lastError("GetClipboardData")
+		}
+
+		p := win.GlobalLock(hMem)
+		if p == nil {
+			return lastError("GlobalLock()")
+		}
+		defer win.GlobalUnlock(hMem)
+
+		text = win.UTF16PtrToString((*uint16)(p))
+
+		return nil
+	})
+
+	return
+}
+
+// SetText sets the current text data of the clipboard.
+func (c *ClipboardService) SetText(s string) error {
+	return c.withOpenClipboard(func() error {
+		utf16, err := syscall.UTF16FromString(s)
+		if err != nil {
+			return err
+		}
+
+		hMem := win.GlobalAlloc(win.GMEM_MOVEABLE, uintptr(len(utf16)*2))
+		if hMem == 0 {
+			return lastError("GlobalAlloc")
+		}
+
+		p := win.GlobalLock(hMem)
+		if p == nil {
+			return lastError("GlobalLock()")
+		}
+
+		win.MoveMemory(p, unsafe.Pointer(&utf16[0]), uintptr(len(utf16)*2))
+
+		win.GlobalUnlock(hMem)
+
+		if 0 == win.SetClipboardData(win.CF_UNICODETEXT, win.HANDLE(hMem)) {
+			// We need to free hMem.
+			defer win.GlobalFree(hMem)
+
+			return lastError("SetClipboardData")
+		}
+
+		// The system now owns the memory referred to by hMem.
+
+		return nil
+	})
+}
+
+func (c *ClipboardService) withOpenClipboard(f func() error) error {
+	if !win.OpenClipboard(c.hwnd) {
+		return lastError("OpenClipboard")
+	}
+	defer win.CloseClipboard()
+
+	return f()
+}

+ 45 - 0
common/src/github.com/lxn/walk/closeevent.go

@@ -0,0 +1,45 @@
+// Copyright 2011 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
+
+type CloseEventHandler func(canceled *bool, reason CloseReason)
+
+type CloseEvent struct {
+	handlers []CloseEventHandler
+}
+
+func (e *CloseEvent) Attach(handler CloseEventHandler) int {
+	for i, h := range e.handlers {
+		if h == nil {
+			e.handlers[i] = handler
+			return i
+		}
+	}
+
+	e.handlers = append(e.handlers, handler)
+	return len(e.handlers) - 1
+}
+
+func (e *CloseEvent) Detach(handle int) {
+	e.handlers[handle] = nil
+}
+
+type CloseEventPublisher struct {
+	event CloseEvent
+}
+
+func (p *CloseEventPublisher) Event() *CloseEvent {
+	return &p.event
+}
+
+func (p *CloseEventPublisher) Publish(canceled *bool, reason CloseReason) {
+	for _, handler := range p.event.handlers {
+		if handler != nil {
+			handler(canceled, reason)
+		}
+	}
+}

+ 25 - 0
common/src/github.com/lxn/walk/color.go

@@ -0,0 +1,25 @@
+// 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
+
+type Color uint32
+
+func RGB(r, g, b byte) Color {
+	return Color(uint32(r) | uint32(g)<<8 | uint32(b)<<16)
+}
+
+func (c Color) R() byte {
+	return byte(c & 0xff)
+}
+
+func (c Color) G() byte {
+	return byte((c >> 8) & 0xff)
+}
+
+func (c Color) B() byte {
+	return byte((c >> 16) & 0xff)
+}

+ 586 - 0
common/src/github.com/lxn/walk/combobox.go

@@ -0,0 +1,586 @@
+// 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 (
+	"fmt"
+	"math/big"
+	"syscall"
+	"time"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type ComboBox struct {
+	WidgetBase
+	bindingValueProvider         BindingValueProvider
+	model                        ListModel
+	providedModel                interface{}
+	bindingMember                string
+	displayMember                string
+	format                       string
+	precision                    int
+	itemsResetHandlerHandle      int
+	itemChangedHandlerHandle     int
+	maxItemTextWidth             int
+	prevCurIndex                 int
+	selChangeIndex               int
+	maxLength                    int
+	currentIndexChangedPublisher EventPublisher
+	textChangedPublisher         EventPublisher
+	editOrigWndProcPtr           uintptr
+}
+
+var comboBoxEditWndProcPtr = syscall.NewCallback(comboBoxEditWndProc)
+
+func comboBoxEditWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	cb := (*ComboBox)(unsafe.Pointer(win.GetWindowLongPtr(hwnd, win.GWLP_USERDATA)))
+
+	switch msg {
+	case win.WM_GETDLGCODE:
+		if form := ancestor(cb); form != nil {
+			if dlg, ok := form.(dialogish); ok {
+				if dlg.DefaultButton() != nil {
+					// If the ComboBox lives in a Dialog that has a
+					// DefaultButton, we won't swallow the return key.
+					break
+				}
+			}
+		}
+
+		if wParam == win.VK_RETURN {
+			return win.DLGC_WANTALLKEYS
+		}
+
+	case win.WM_KEYDOWN:
+		if wParam != win.VK_RETURN || 0 == cb.SendMessage(win.CB_GETDROPPEDSTATE, 0, 0) {
+			cb.handleKeyDown(wParam, lParam)
+		}
+
+	case win.WM_KEYUP:
+		if wParam != win.VK_RETURN || 0 == cb.SendMessage(win.CB_GETDROPPEDSTATE, 0, 0) {
+			cb.handleKeyUp(wParam, lParam)
+		}
+	}
+
+	return win.CallWindowProc(cb.editOrigWndProcPtr, hwnd, msg, wParam, lParam)
+}
+
+func NewComboBox(parent Container) (*ComboBox, error) {
+	cb, err := newComboBoxWithStyle(parent, win.CBS_DROPDOWN)
+	if err != nil {
+		return nil, err
+	}
+
+	editHwnd := win.GetWindow(cb.hWnd, win.GW_CHILD)
+
+	win.SetWindowLongPtr(editHwnd, win.GWLP_USERDATA, uintptr(unsafe.Pointer(cb)))
+	cb.editOrigWndProcPtr = win.SetWindowLongPtr(editHwnd, win.GWLP_WNDPROC, comboBoxEditWndProcPtr)
+
+	return cb, nil
+}
+
+func NewDropDownBox(parent Container) (*ComboBox, error) {
+	return newComboBoxWithStyle(parent, win.CBS_DROPDOWNLIST)
+}
+
+func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
+	cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}
+
+	if err := InitWidget(
+		cb,
+		parent,
+		"COMBOBOX",
+		win.WS_TABSTOP|win.WS_VISIBLE|win.WS_VSCROLL|style,
+		0); err != nil {
+		return nil, err
+	}
+
+	succeeded := false
+	defer func() {
+		if !succeeded {
+			cb.Dispose()
+		}
+	}()
+
+	cb.MustRegisterProperty("CurrentIndex", NewProperty(
+		func() interface{} {
+			return cb.CurrentIndex()
+		},
+		func(v interface{}) error {
+			return cb.SetCurrentIndex(v.(int))
+		},
+		cb.CurrentIndexChanged()))
+
+	cb.MustRegisterProperty("Text", NewProperty(
+		func() interface{} {
+			return cb.Text()
+		},
+		func(v interface{}) error {
+			return cb.SetText(v.(string))
+		},
+		cb.TextChanged()))
+
+	cb.MustRegisterProperty("HasCurrentItem", NewReadOnlyBoolProperty(
+		func() bool {
+			return cb.CurrentIndex() != -1
+		},
+		cb.CurrentIndexChanged()))
+
+	cb.MustRegisterProperty("TextNotEmpty", NewReadOnlyBoolProperty(
+		func() bool {
+			return cb.Text() != ""
+		},
+		cb.CurrentIndexChanged()))
+
+	cb.MustRegisterProperty("Value", NewProperty(
+		func() interface{} {
+			if cb.Editable() {
+				return cb.Text()
+			}
+
+			index := cb.CurrentIndex()
+
+			if cb.bindingValueProvider == nil || index == -1 {
+				return nil
+			}
+
+			return cb.bindingValueProvider.BindingValue(index)
+		},
+		func(v interface{}) error {
+			if cb.Editable() {
+				return cb.SetText(v.(string))
+			}
+
+			if cb.bindingValueProvider == nil {
+				if cb.model == nil {
+					return nil
+				} else {
+					return newError("Data binding is only supported using a model that implements BindingValueProvider.")
+				}
+			}
+
+			index := -1
+
+			count := cb.model.ItemCount()
+			for i := 0; i < count; i++ {
+				if cb.bindingValueProvider.BindingValue(i) == v {
+					index = i
+					break
+				}
+			}
+
+			return cb.SetCurrentIndex(index)
+		},
+		cb.CurrentIndexChanged()))
+
+	succeeded = true
+
+	return cb, nil
+}
+
+func (*ComboBox) LayoutFlags() LayoutFlags {
+	return GrowableHorz
+}
+
+func (cb *ComboBox) MinSizeHint() Size {
+	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})
+
+	if cb.model != nil && cb.maxItemTextWidth <= 0 {
+		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
+	}
+
+	// FIXME: Use GetThemePartSize instead of guessing
+	w := maxi(defaultSize.Width, cb.maxItemTextWidth+30)
+	h := defaultSize.Height + 1
+
+	return Size{w, h}
+}
+
+func (cb *ComboBox) SizeHint() Size {
+	return cb.MinSizeHint()
+}
+
+func (cb *ComboBox) applyFont(font *Font) {
+	cb.WidgetBase.applyFont(font)
+
+	if cb.model != nil {
+		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
+		cb.updateParentLayout()
+	}
+}
+
+func (cb *ComboBox) Editable() bool {
+	return !cb.hasStyleBits(win.CBS_DROPDOWNLIST)
+}
+
+func (cb *ComboBox) itemString(index int) string {
+	switch val := cb.model.Value(index).(type) {
+	case string:
+		return val
+
+	case time.Time:
+		return val.Format(cb.format)
+
+	case *big.Rat:
+		return val.FloatString(cb.precision)
+
+	default:
+		return fmt.Sprintf(cb.format, val)
+	}
+
+	panic("unreachable")
+}
+
+func (cb *ComboBox) insertItemAt(index int) error {
+	str := cb.itemString(index)
+	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
+
+	if win.CB_ERR == cb.SendMessage(win.CB_INSERTSTRING, uintptr(index), lp) {
+		return newError("SendMessage(CB_INSERTSTRING)")
+	}
+
+	return nil
+}
+
+func (cb *ComboBox) resetItems() error {
+	cb.SetSuspended(true)
+	defer cb.SetSuspended(false)
+
+	cb.selChangeIndex = -1
+
+	if win.FALSE == cb.SendMessage(win.CB_RESETCONTENT, 0, 0) {
+		return newError("SendMessage(CB_RESETCONTENT)")
+	}
+
+	cb.maxItemTextWidth = 0
+
+	cb.SetCurrentIndex(-1)
+
+	if cb.model == nil {
+		return nil
+	}
+
+	count := cb.model.ItemCount()
+
+	for i := 0; i < count; i++ {
+		if err := cb.insertItemAt(i); err != nil {
+			return err
+		}
+	}
+
+	cb.updateParentLayout()
+
+	return nil
+}
+
+func (cb *ComboBox) attachModel() {
+	itemsResetHandler := func() {
+		cb.resetItems()
+	}
+	cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)
+
+	itemChangedHandler := func(index int) {
+		if win.CB_ERR == cb.SendMessage(win.CB_DELETESTRING, uintptr(index), 0) {
+			newError("SendMessage(CB_DELETESTRING)")
+		}
+
+		cb.insertItemAt(index)
+
+		cb.SetCurrentIndex(cb.prevCurIndex)
+	}
+	cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
+}
+
+func (cb *ComboBox) detachModel() {
+	cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
+	cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
+}
+
+// Model returns the model of the ComboBox.
+func (cb *ComboBox) Model() interface{} {
+	return cb.providedModel
+}
+
+// SetModel sets the model of the ComboBox.
+//
+// It is required that mdl either implements walk.ListModel or
+// walk.ReflectListModel or be a slice of pointers to struct or a []string.
+func (cb *ComboBox) SetModel(mdl interface{}) error {
+	model, ok := mdl.(ListModel)
+	if !ok && mdl != nil {
+		var err error
+		if model, err = newReflectListModel(mdl); err != nil {
+			return err
+		}
+
+		if _, ok := mdl.([]string); !ok {
+			if badms, ok := model.(bindingAndDisplayMemberSetter); ok {
+				var bindingMember string
+				if cb.Editable() {
+					bindingMember = cb.displayMember
+				} else {
+					bindingMember = cb.bindingMember
+				}
+				badms.setBindingMember(bindingMember)
+				badms.setDisplayMember(cb.displayMember)
+			}
+		}
+	}
+	cb.providedModel = mdl
+
+	if cb.model != nil {
+		cb.detachModel()
+	}
+
+	cb.model = model
+	cb.bindingValueProvider, _ = model.(BindingValueProvider)
+
+	if model != nil {
+		cb.attachModel()
+	}
+
+	if err := cb.resetItems(); err != nil {
+		return err
+	}
+
+	if !cb.Editable() && model != nil && model.ItemCount() == 1 {
+		cb.SetCurrentIndex(0)
+	}
+
+	return cb.Invalidate()
+}
+
+// BindingMember returns the member from the model of the ComboBox that is bound
+// to a field of the data source managed by an associated DataBinder.
+//
+// This is only applicable to walk.ReflectListModel models and simple slices of
+// pointers to struct.
+func (cb *ComboBox) BindingMember() string {
+	return cb.bindingMember
+}
+
+// SetBindingMember sets the member from the model of the ComboBox that is bound
+// to a field of the data source managed by an associated DataBinder.
+//
+// This is only applicable to walk.ReflectListModel models and simple slices of
+// pointers to struct.
+//
+// For a model consisting of items of type S, data source field of type T and
+// bindingMember "Foo", this can be one of the following:
+//
+//	A field		Foo T
+//	A method	func (s S) Foo() T
+//	A method	func (s S) Foo() (T, error)
+//
+// If bindingMember is not a simple member name like "Foo", but a path to a
+// member like "A.B.Foo", members "A" and "B" both must be one of the options
+// mentioned above, but with T having type pointer to struct.
+func (cb *ComboBox) SetBindingMember(bindingMember string) error {
+	if bindingMember != "" {
+		if _, ok := cb.providedModel.([]string); ok {
+			return newError("invalid for []string model")
+		}
+	}
+
+	cb.bindingMember = bindingMember
+
+	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
+		badms.setBindingMember(bindingMember)
+	}
+
+	return nil
+}
+
+// DisplayMember returns the member from the model of the ComboBox that is
+// displayed in the ComboBox.
+//
+// This is only applicable to walk.ReflectListModel models and simple slices of
+// pointers to struct.
+func (cb *ComboBox) DisplayMember() string {
+	return cb.displayMember
+}
+
+// SetDisplayMember sets the member from the model of the ComboBox that is
+// displayed in the ComboBox.
+//
+// This is only applicable to walk.ReflectListModel models and simple slices of
+// pointers to struct.
+//
+// For a model consisting of items of type S, the type of the specified member T
+// and displayMember "Foo", this can be one of the following:
+//
+//	A field		Foo T
+//	A method	func (s S) Foo() T
+//	A method	func (s S) Foo() (T, error)
+//
+// If displayMember is not a simple member name like "Foo", but a path to a
+// member like "A.B.Foo", members "A" and "B" both must be one of the options
+// mentioned above, but with T having type pointer to struct.
+func (cb *ComboBox) SetDisplayMember(displayMember string) error {
+	if displayMember != "" {
+		if _, ok := cb.providedModel.([]string); ok {
+			return newError("invalid for []string model")
+		}
+	}
+
+	cb.displayMember = displayMember
+
+	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
+		badms.setDisplayMember(displayMember)
+	}
+
+	return nil
+}
+
+func (cb *ComboBox) Format() string {
+	return cb.format
+}
+
+func (cb *ComboBox) SetFormat(value string) {
+	cb.format = value
+}
+
+func (cb *ComboBox) Precision() int {
+	return cb.precision
+}
+
+func (cb *ComboBox) SetPrecision(value int) {
+	cb.precision = value
+}
+
+func (cb *ComboBox) MaxLength() int {
+	return cb.maxLength
+}
+
+func (cb *ComboBox) SetMaxLength(value int) {
+	cb.SendMessage(win.CB_LIMITTEXT, uintptr(value), 0)
+
+	cb.maxLength = value
+}
+
+func (cb *ComboBox) calculateMaxItemTextWidth() int {
+	hdc := win.GetDC(cb.hWnd)
+	if hdc == 0 {
+		newError("GetDC failed")
+		return -1
+	}
+	defer win.ReleaseDC(cb.hWnd, hdc)
+
+	hFontOld := win.SelectObject(hdc, win.HGDIOBJ(cb.Font().handleForDPI(0)))
+	defer win.SelectObject(hdc, hFontOld)
+
+	var maxWidth int
+
+	count := cb.model.ItemCount()
+	for i := 0; i < count; i++ {
+		var s win.SIZE
+		str := syscall.StringToUTF16(cb.itemString(i))
+
+		if !win.GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
+			newError("GetTextExtentPoint32 failed")
+			return -1
+		}
+
+		maxWidth = maxi(maxWidth, int(s.CX))
+	}
+
+	return maxWidth
+}
+
+func (cb *ComboBox) CurrentIndex() int {
+	return int(int32(cb.SendMessage(win.CB_GETCURSEL, 0, 0)))
+}
+
+func (cb *ComboBox) SetCurrentIndex(value int) error {
+	index := int(int32(cb.SendMessage(win.CB_SETCURSEL, uintptr(value), 0)))
+
+	if index != value {
+		return newError("invalid index")
+	}
+
+	if value != cb.prevCurIndex {
+		cb.prevCurIndex = value
+		cb.currentIndexChangedPublisher.Publish()
+	}
+
+	return nil
+}
+
+func (cb *ComboBox) CurrentIndexChanged() *Event {
+	return cb.currentIndexChangedPublisher.Event()
+}
+
+func (cb *ComboBox) Text() string {
+	return windowText(cb.hWnd)
+}
+
+func (cb *ComboBox) SetText(value string) error {
+	return setWindowText(cb.hWnd, value)
+}
+
+func (cb *ComboBox) TextSelection() (start, end int) {
+	cb.SendMessage(win.CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
+	return
+}
+
+func (cb *ComboBox) SetTextSelection(start, end int) {
+	cb.SendMessage(win.CB_SETEDITSEL, 0, uintptr(win.MAKELONG(uint16(start), uint16(end))))
+}
+
+func (cb *ComboBox) TextChanged() *Event {
+	return cb.textChangedPublisher.Event()
+}
+
+func (cb *ComboBox) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_COMMAND:
+		code := win.HIWORD(uint32(wParam))
+		selIndex := cb.CurrentIndex()
+
+		switch code {
+		case win.CBN_EDITCHANGE:
+			cb.selChangeIndex = -1
+			cb.textChangedPublisher.Publish()
+			cb.currentIndexChangedPublisher.Publish()
+
+		case win.CBN_SELCHANGE:
+			cb.selChangeIndex = selIndex
+
+		case win.CBN_SELENDCANCEL:
+			if cb.selChangeIndex != -1 {
+				if cb.selChangeIndex < cb.model.ItemCount() {
+					cb.SetCurrentIndex(cb.selChangeIndex)
+				}
+
+				cb.selChangeIndex = -1
+			}
+
+		case win.CBN_SELENDOK:
+			if editable := cb.Editable(); editable || selIndex != cb.prevCurIndex {
+				if editable && selIndex > -1 {
+					cb.Property("Value").Set(cb.model.Value(selIndex))
+				}
+				cb.currentIndexChangedPublisher.Publish()
+				cb.prevCurIndex = selIndex
+				return 0
+			}
+
+			cb.selChangeIndex = -1
+		}
+
+	case win.WM_MOUSEWHEEL:
+		if !cb.Enabled() {
+			return 0
+		}
+	}
+
+	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}

+ 186 - 0
common/src/github.com/lxn/walk/commondialogs.go

@@ -0,0 +1,186 @@
+// 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 (
+	"fmt"
+	"path/filepath"
+	"syscall"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type FileDialog struct {
+	Title          string
+	FilePath       string
+	FilePaths      []string
+	InitialDirPath string
+	Filter         string
+	FilterIndex    int
+}
+
+func (dlg *FileDialog) show(owner Form, fun func(ofn *win.OPENFILENAME) bool, flags uint32) (accepted bool, err error) {
+	ofn := new(win.OPENFILENAME)
+
+	ofn.LStructSize = uint32(unsafe.Sizeof(*ofn))
+	if owner != nil {
+		ofn.HwndOwner = owner.Handle()
+	}
+
+	filter := make([]uint16, len(dlg.Filter)+2)
+	copy(filter, syscall.StringToUTF16(dlg.Filter))
+	// Replace '|' with the expected '\0'.
+	for i, c := range filter {
+		if byte(c) == '|' {
+			filter[i] = uint16(0)
+		}
+	}
+	ofn.LpstrFilter = &filter[0]
+	ofn.NFilterIndex = uint32(dlg.FilterIndex)
+
+	ofn.LpstrInitialDir = syscall.StringToUTF16Ptr(dlg.InitialDirPath)
+	ofn.LpstrTitle = syscall.StringToUTF16Ptr(dlg.Title)
+	ofn.Flags = win.OFN_FILEMUSTEXIST | flags
+
+	var fileBuf []uint16
+	if flags&win.OFN_ALLOWMULTISELECT > 0 {
+		fileBuf = make([]uint16, 65536)
+	} else {
+		fileBuf = make([]uint16, 1024)
+		copy(fileBuf, syscall.StringToUTF16(dlg.FilePath))
+	}
+	ofn.LpstrFile = &fileBuf[0]
+	ofn.NMaxFile = uint32(len(fileBuf))
+
+	if !fun(ofn) {
+		errno := win.CommDlgExtendedError()
+		if errno != 0 {
+			err = newError(fmt.Sprintf("Error %d", errno))
+		}
+		return
+	}
+
+	if flags&win.OFN_ALLOWMULTISELECT > 0 {
+		split := func() [][]uint16 {
+			var parts [][]uint16
+
+			from := 0
+			for i, c := range fileBuf {
+				if c == 0 {
+					if i == from {
+						return parts
+					}
+
+					parts = append(parts, fileBuf[from:i])
+					from = i + 1
+				}
+			}
+
+			return parts
+		}
+
+		parts := split()
+
+		if len(parts) == 1 {
+			dlg.FilePaths = []string{syscall.UTF16ToString(parts[0])}
+		} else {
+			dirPath := syscall.UTF16ToString(parts[0])
+			dlg.FilePaths = make([]string, len(parts)-1)
+
+			for i, fp := range parts[1:] {
+				dlg.FilePaths[i] = filepath.Join(dirPath, syscall.UTF16ToString(fp))
+			}
+		}
+	} else {
+		dlg.FilePath = syscall.UTF16ToString(fileBuf)
+	}
+
+	accepted = true
+
+	return
+}
+
+func (dlg *FileDialog) ShowOpen(owner Form) (accepted bool, err error) {
+	return dlg.show(owner, win.GetOpenFileName, 0)
+}
+
+func (dlg *FileDialog) ShowOpenMultiple(owner Form) (accepted bool, err error) {
+	return dlg.show(owner, win.GetOpenFileName, win.OFN_ALLOWMULTISELECT|win.OFN_EXPLORER)
+}
+
+func (dlg *FileDialog) ShowSave(owner Form) (accepted bool, err error) {
+	return dlg.show(owner, win.GetSaveFileName, 0)
+}
+
+func (dlg *FileDialog) ShowBrowseFolder(owner Form) (accepted bool, err error) {
+	// Calling OleInitialize (or similar) is required for BIF_NEWDIALOGSTYLE.
+	if hr := win.OleInitialize(); hr != win.S_OK && hr != win.S_FALSE {
+		return false, newError(fmt.Sprint("OleInitialize Error: ", hr))
+	}
+	defer win.OleUninitialize()
+
+	pathFromPIDL := func(pidl uintptr) (string, error) {
+		var path [win.MAX_PATH]uint16
+		if !win.SHGetPathFromIDList(pidl, &path[0]) {
+			return "", newError("SHGetPathFromIDList failed")
+		}
+
+		return syscall.UTF16ToString(path[:]), nil
+	}
+
+	// We use this callback to disable the OK button in case of "invalid"
+	// selections.
+	callback := func(hwnd win.HWND, msg uint32, lp, wp uintptr) uintptr {
+		const BFFM_SELCHANGED = 2
+		if msg == BFFM_SELCHANGED {
+			_, err := pathFromPIDL(lp)
+			var enabled uintptr
+			if err == nil {
+				enabled = 1
+			}
+
+			const BFFM_ENABLEOK = win.WM_USER + 101
+
+			win.SendMessage(hwnd, BFFM_ENABLEOK, 0, enabled)
+		}
+
+		return 0
+	}
+
+	var ownerHwnd win.HWND
+	if owner != nil {
+		ownerHwnd = owner.Handle()
+	}
+
+	// We need to put the initial path into a buffer of at least MAX_LENGTH
+	// length, or we may get random crashes.
+	var buf [win.MAX_PATH]uint16
+	copy(buf[:], syscall.StringToUTF16(dlg.InitialDirPath))
+
+	const BIF_NEWDIALOGSTYLE = 0x00000040
+
+	bi := win.BROWSEINFO{
+		HwndOwner:      ownerHwnd,
+		PszDisplayName: &buf[0],
+		LpszTitle:      syscall.StringToUTF16Ptr(dlg.Title),
+		UlFlags:        BIF_NEWDIALOGSTYLE,
+		Lpfn:           syscall.NewCallback(callback),
+	}
+
+	pidl := win.SHBrowseForFolder(&bi)
+	if pidl == 0 {
+		return false, nil
+	}
+	defer win.CoTaskMemFree(pidl)
+
+	dlg.FilePath, err = pathFromPIDL(pidl)
+	accepted = dlg.FilePath != ""
+	return
+}

+ 42 - 0
common/src/github.com/lxn/walk/composite.go

@@ -0,0 +1,42 @@
+// 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 (
+	"github.com/lxn/win"
+)
+
+const compositeWindowClass = `\o/ Walk_Composite_Class \o/`
+
+func init() {
+	MustRegisterWindowClass(compositeWindowClass)
+}
+
+type Composite struct {
+	ContainerBase
+}
+
+func newCompositeWithStyle(parent Window, style uint32) (*Composite, error) {
+	c := new(Composite)
+	c.children = newWidgetList(c)
+	c.SetPersistent(true)
+
+	if err := InitWidget(
+		c,
+		parent,
+		compositeWindowClass,
+		win.WS_CHILD|win.WS_VISIBLE|style,
+		win.WS_EX_CONTROLPARENT); err != nil {
+		return nil, err
+	}
+
+	return c, nil
+}
+
+func NewComposite(parent Container) (*Composite, error) {
+	return newCompositeWithStyle(parent, 0)
+}

+ 127 - 0
common/src/github.com/lxn/walk/condition.go

@@ -0,0 +1,127 @@
+// Copyright 2013 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
+
+type Condition interface {
+	Satisfied() bool
+	Changed() *Event
+}
+
+type MutableCondition struct {
+	satisfied        bool
+	changedPublisher EventPublisher
+}
+
+func NewMutableCondition() *MutableCondition {
+	return new(MutableCondition)
+}
+
+func (mc *MutableCondition) Satisfied() bool {
+	return mc.satisfied
+}
+
+func (mc *MutableCondition) SetSatisfied(satisfied bool) error {
+	if satisfied == mc.satisfied {
+		return nil
+	}
+
+	mc.satisfied = satisfied
+
+	mc.changedPublisher.Publish()
+
+	return nil
+}
+
+func (mc *MutableCondition) Changed() *Event {
+	return mc.changedPublisher.Event()
+}
+
+type DelegateCondition struct {
+	satisfied func() bool
+	changed   *Event
+}
+
+func NewDelegateCondition(satisfied func() bool, changed *Event) *DelegateCondition {
+	return &DelegateCondition{satisfied, changed}
+}
+
+func (dc *DelegateCondition) Satisfied() bool {
+	return dc.satisfied()
+}
+
+func (dc *DelegateCondition) Changed() *Event {
+	return dc.changed
+}
+
+type compositeCondition struct {
+	items               []Condition
+	itemsChangedHandles []int
+	changedPublisher    EventPublisher
+}
+
+func (cc *compositeCondition) init(items []Condition) {
+	cc.items = append(cc.items, items...)
+
+	for _, item := range items {
+		handle := item.Changed().Attach(func() {
+			cc.changedPublisher.Publish()
+		})
+		cc.itemsChangedHandles = append(cc.itemsChangedHandles, handle)
+	}
+}
+
+func (cc *compositeCondition) satisfied(all bool) bool {
+	for _, item := range cc.items {
+		if all != item.Satisfied() {
+			return !all
+		}
+	}
+
+	return all
+}
+
+func (cc *compositeCondition) Changed() *Event {
+	return cc.changedPublisher.Event()
+}
+
+func (cc *compositeCondition) Dispose() {
+	for i, item := range cc.items {
+		item.Changed().Detach(cc.itemsChangedHandles[i])
+	}
+}
+
+type allCondition struct {
+	compositeCondition
+}
+
+func NewAllCondition(items ...Condition) Condition {
+	ac := new(allCondition)
+
+	ac.init(items)
+
+	return ac
+}
+
+func (ac *allCondition) Satisfied() bool {
+	return ac.satisfied(true)
+}
+
+type anyCondition struct {
+	compositeCondition
+}
+
+func NewAnyCondition(items ...Condition) Condition {
+	ac := new(anyCondition)
+
+	ac.init(items)
+
+	return ac
+}
+
+func (ac *anyCondition) Satisfied() bool {
+	return ac.satisfied(false)
+}

+ 359 - 0
common/src/github.com/lxn/walk/container.go

@@ -0,0 +1,359 @@
+// 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"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type Margins struct {
+	HNear, VNear, HFar, VFar int
+}
+
+type Layout interface {
+	Container() Container
+	SetContainer(value Container)
+	Margins() Margins
+	SetMargins(value Margins) error
+	Spacing() int
+	SetSpacing(value int) error
+	LayoutFlags() LayoutFlags
+	MinSize() Size
+	Update(reset bool) error
+}
+
+func shouldLayoutWidget(widget Widget) bool {
+	if widget == nil {
+		return false
+	}
+
+	_, isSpacer := widget.(*Spacer)
+
+	return isSpacer || widget.AsWindowBase().visible || widget.AlwaysConsumeSpace()
+}
+
+func DescendantByName(container Container, name string) Widget {
+	var widget Widget
+
+	walkDescendants(container.AsContainerBase(), func(w Window) bool {
+		if w.Name() == name {
+			widget = w.(Widget)
+			return false
+		}
+
+		return true
+	})
+
+	if widget == nil {
+		return nil
+	}
+
+	return widget
+}
+
+type Container interface {
+	Window
+	AsContainerBase() *ContainerBase
+	Children() *WidgetList
+	Layout() Layout
+	SetLayout(value Layout) error
+	DataBinder() *DataBinder
+	SetDataBinder(dbm *DataBinder)
+}
+
+type ContainerBase struct {
+	WidgetBase
+	layout     Layout
+	children   *WidgetList
+	dataBinder *DataBinder
+	persistent bool
+}
+
+func (cb *ContainerBase) AsWidgetBase() *WidgetBase {
+	return &cb.WidgetBase
+}
+
+func (cb *ContainerBase) AsContainerBase() *ContainerBase {
+	return cb
+}
+
+func (cb *ContainerBase) LayoutFlags() LayoutFlags {
+	if cb.layout == nil {
+		return 0
+	}
+
+	return cb.layout.LayoutFlags()
+}
+
+func (cb *ContainerBase) MinSizeHint() Size {
+	if cb.layout == nil {
+		return Size{}
+	}
+
+	return cb.layout.MinSize()
+}
+
+func (cb *ContainerBase) applyEnabled(enabled bool) {
+	cb.WidgetBase.applyEnabled(enabled)
+
+	applyEnabledToDescendants(cb.window.(Widget), enabled)
+}
+
+func (cb *ContainerBase) applyFont(font *Font) {
+	cb.WidgetBase.applyFont(font)
+
+	applyFontToDescendants(cb.window.(Widget), font)
+}
+
+func (cb *ContainerBase) Children() *WidgetList {
+	return cb.children
+}
+
+func (cb *ContainerBase) Layout() Layout {
+	return cb.layout
+}
+
+func (cb *ContainerBase) SetLayout(value Layout) error {
+	if cb.layout != value {
+		if cb.layout != nil {
+			cb.layout.SetContainer(nil)
+		}
+
+		cb.layout = value
+
+		if value != nil && value.Container() != Container(cb) {
+			value.SetContainer(cb)
+		}
+	}
+
+	return nil
+}
+
+func (cb *ContainerBase) DataBinder() *DataBinder {
+	return cb.dataBinder
+}
+
+func (cb *ContainerBase) SetDataBinder(db *DataBinder) {
+	if db == cb.dataBinder {
+		return
+	}
+
+	if cb.dataBinder != nil {
+		cb.dataBinder.SetBoundWidgets(nil)
+	}
+
+	cb.dataBinder = db
+
+	if db != nil {
+		var boundWidgets []Widget
+
+		walkDescendants(cb.window, func(w Window) bool {
+			if w.Handle() == cb.hWnd {
+				return true
+			}
+
+			if c, ok := w.(Container); ok && c.DataBinder() != nil {
+				return false
+			}
+
+			for _, prop := range w.AsWindowBase().name2Property {
+				if _, ok := prop.Source().(string); ok {
+					boundWidgets = append(boundWidgets, w.(Widget))
+					break
+				}
+			}
+
+			return true
+		})
+
+		db.SetBoundWidgets(boundWidgets)
+	}
+}
+
+func (cb *ContainerBase) forEachPersistableChild(f func(p Persistable) error) error {
+	if cb.children == nil {
+		return nil
+	}
+
+	for _, child := range cb.children.items {
+		if persistable, ok := child.(Persistable); ok && persistable.Persistent() {
+			if err := f(persistable); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (cb *ContainerBase) Persistent() bool {
+	return cb.persistent
+}
+
+func (cb *ContainerBase) SetPersistent(value bool) {
+	cb.persistent = value
+}
+
+func (cb *ContainerBase) SaveState() error {
+	return cb.forEachPersistableChild(func(p Persistable) error {
+		return p.SaveState()
+	})
+}
+
+func (cb *ContainerBase) RestoreState() error {
+	return cb.forEachPersistableChild(func(p Persistable) error {
+		return p.RestoreState()
+	})
+}
+
+func (cb *ContainerBase) SetSuspended(suspend bool) {
+	wasSuspended := cb.Suspended()
+
+	cb.WidgetBase.SetSuspended(suspend)
+
+	if !suspend && wasSuspended && cb.layout != nil {
+		cb.layout.Update(false)
+	}
+}
+
+func (cb *ContainerBase) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_COMMAND:
+		if lParam == 0 {
+			switch win.HIWORD(uint32(wParam)) {
+			case 0:
+				cmdId := win.LOWORD(uint32(wParam))
+				switch cmdId {
+				case win.IDOK, win.IDCANCEL:
+					form := ancestor(cb)
+					if form == nil {
+						break
+					}
+
+					dlg, ok := form.(dialogish)
+					if !ok {
+						break
+					}
+
+					var button *PushButton
+					if cmdId == win.IDOK {
+						button = dlg.DefaultButton()
+					} else {
+						button = dlg.CancelButton()
+					}
+
+					if button != nil && button.Visible() && button.Enabled() {
+						button.raiseClicked()
+					}
+
+					break
+				}
+
+				// Menu
+				actionId := uint16(win.LOWORD(uint32(wParam)))
+				if action, ok := actionsById[actionId]; ok {
+					action.raiseTriggered()
+					return 0
+				}
+
+			case 1:
+				// Accelerator
+			}
+		} else {
+			// The window that sent the notification shall handle it itself.
+			hWnd := win.HWND(lParam)
+			if window := windowFromHandle(hWnd); window != nil {
+				window.WndProc(hwnd, msg, wParam, lParam)
+				return 0
+			}
+		}
+
+	case win.WM_NOTIFY:
+		nmh := (*win.NMHDR)(unsafe.Pointer(lParam))
+		if window := windowFromHandle(nmh.HwndFrom); window != nil {
+			// The window that sent the notification shall handle it itself.
+			return window.WndProc(hwnd, msg, wParam, lParam)
+		}
+
+	case win.WM_HSCROLL, win.WM_VSCROLL:
+		if window := windowFromHandle(win.HWND(lParam)); window != nil {
+			// The window that sent the notification shall handle it itself.
+			return window.WndProc(hwnd, msg, wParam, lParam)
+		}
+
+	case win.WM_SIZE, win.WM_SIZING:
+		if cb.layout != nil {
+			cb.layout.Update(false)
+		}
+	}
+
+	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}
+
+func (cb *ContainerBase) onInsertingWidget(index int, widget Widget) (err error) {
+	return nil
+}
+
+func (cb *ContainerBase) onInsertedWidget(index int, widget Widget) (err error) {
+	if parent := widget.Parent(); parent == nil || parent.Handle() != cb.hWnd {
+		if err = widget.SetParent(cb.window.(Container)); err != nil {
+			return
+		}
+	}
+
+	if cb.layout != nil {
+		cb.layout.Update(true)
+	}
+
+	widget.(applyFonter).applyFont(cb.Font())
+
+	return
+}
+
+func (cb *ContainerBase) onRemovingWidget(index int, widget Widget) (err error) {
+	if widget.Parent() == nil {
+		return
+	}
+
+	if widget.Parent().Handle() == cb.hWnd {
+		err = widget.SetParent(nil)
+	}
+
+	return
+}
+
+func (cb *ContainerBase) onRemovedWidget(index int, widget Widget) (err error) {
+	if cb.layout != nil {
+		cb.layout.Update(true)
+	}
+
+	return
+}
+
+func (cb *ContainerBase) onClearingWidgets() (err error) {
+	for _, widget := range cb.children.items {
+		if widget.Parent().Handle() == cb.hWnd {
+			if err = widget.SetParent(nil); err != nil {
+				return
+			}
+		}
+	}
+
+	return
+}
+
+func (cb *ContainerBase) onClearedWidgets() (err error) {
+	if cb.layout != nil {
+		cb.layout.Update(true)
+	}
+
+	return
+}

+ 116 - 0
common/src/github.com/lxn/walk/cursor.go

@@ -0,0 +1,116 @@
+// Copyright 2011 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 (
+	"image"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type Cursor interface {
+	Dispose()
+	handle() win.HCURSOR
+}
+
+type stockCursor struct {
+	hCursor win.HCURSOR
+}
+
+func (sc stockCursor) Dispose() {
+	// nop
+}
+
+func (sc stockCursor) handle() win.HCURSOR {
+	return sc.hCursor
+}
+
+func CursorArrow() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW))}
+}
+
+func CursorIBeam() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_IBEAM))}
+}
+
+func CursorWait() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_WAIT))}
+}
+
+func CursorCross() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_CROSS))}
+}
+
+func CursorUpArrow() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_UPARROW))}
+}
+
+func CursorSizeNWSE() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZENWSE))}
+}
+
+func CursorSizeNESW() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZENESW))}
+}
+
+func CursorSizeWE() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZEWE))}
+}
+
+func CursorSizeNS() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZENS))}
+}
+
+func CursorSizeAll() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZEALL))}
+}
+
+func CursorNo() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_NO))}
+}
+
+func CursorHand() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_HAND))}
+}
+
+func CursorAppStarting() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_APPSTARTING))}
+}
+
+func CursorHelp() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_HELP))}
+}
+
+func CursorIcon() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ICON))}
+}
+
+func CursorSize() Cursor {
+	return stockCursor{win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_SIZE))}
+}
+
+type customCursor struct {
+	hCursor win.HCURSOR
+}
+
+func NewCursorFromImage(im image.Image, hotspot image.Point) (Cursor, error) {
+	i, err := createAlphaCursorOrIconFromImage(im, hotspot, false)
+	if err != nil {
+		return nil, err
+	}
+	return customCursor{win.HCURSOR(i)}, nil
+}
+
+func (cc customCursor) Dispose() {
+	win.DestroyIcon(win.HICON(cc.hCursor))
+}
+
+func (cc customCursor) handle() win.HCURSOR {
+	return cc.hCursor
+}

+ 205 - 0
common/src/github.com/lxn/walk/customwidget.go

@@ -0,0 +1,205 @@
+// 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 (
+	"github.com/lxn/win"
+)
+
+const customWidgetWindowClass = `\o/ Walk_CustomWidget_Class \o/`
+
+func init() {
+	MustRegisterWindowClass(customWidgetWindowClass)
+}
+
+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
+	invalidatesOnResize bool
+	paintMode           PaintMode
+}
+
+func NewCustomWidget(parent Container, style uint, paint PaintFunc) (*CustomWidget, error) {
+	cw := &CustomWidget{paint: paint}
+
+	if err := InitWidget(
+		cw,
+		parent,
+		customWidgetWindowClass,
+		win.WS_VISIBLE|uint32(style),
+		0); err != nil {
+		return nil, err
+	}
+
+	return cw, nil
+}
+
+func (*CustomWidget) LayoutFlags() LayoutFlags {
+	return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert | GreedyHorz | GreedyVert
+}
+
+func (cw *CustomWidget) SizeHint() Size {
+	return Size{100, 100}
+}
+
+// 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 = PaintNoErase
+		} else {
+			cw.paintMode = PaintNormal
+		}
+	}
+}
+
+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 {
+			newError("paint 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()
+
+		r := &ps.RcPaint
+		bounds := Rectangle{
+			int(r.Left),
+			int(r.Top),
+			int(r.Right - r.Left),
+			int(r.Bottom - r.Top),
+		}
+		if cw.paintMode == PaintBuffered {
+			err = cw.bufferedPaint(canvas, bounds)
+		} else {
+			err = cw.paint(canvas, bounds)
+		}
+
+		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_SIZE, win.WM_SIZING:
+		if cw.invalidatesOnResize {
+			cw.Invalidate()
+		}
+	}
+
+	return cw.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}
+
+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)
+
+	err := cw.paint(&buffered, updateBounds)
+
+	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
+}

BIN
common/src/github.com/lxn/walk/data/drawing.png


BIN
common/src/github.com/lxn/walk/data/filebrowser.png


+ 440 - 0
common/src/github.com/lxn/walk/databinding.go

@@ -0,0 +1,440 @@
+// Copyright 2012 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 (
+	"errors"
+	"fmt"
+	"reflect"
+)
+
+var (
+	errValidationFailed = errors.New("validation failed")
+)
+
+type ErrorPresenter interface {
+	PresentError(err error, widget Widget)
+}
+
+type DataBinder struct {
+	dataSource                interface{}
+	boundWidgets              []Widget
+	properties                []Property
+	property2Widget           map[Property]Widget
+	property2ChangedHandle    map[Property]int
+	errorPresenter            ErrorPresenter
+	canSubmitChangedPublisher EventPublisher
+	submittedPublisher        EventPublisher
+	autoSubmit                bool
+	canSubmit                 bool
+	inReset                   bool
+	dirty                     bool
+}
+
+func NewDataBinder() *DataBinder {
+	return new(DataBinder)
+}
+
+func (db *DataBinder) AutoSubmit() bool {
+	return db.autoSubmit
+}
+
+func (db *DataBinder) SetAutoSubmit(autoSubmit bool) {
+	db.autoSubmit = autoSubmit
+}
+
+func (db *DataBinder) Submitted() *Event {
+	return db.submittedPublisher.Event()
+}
+
+func (db *DataBinder) DataSource() interface{} {
+	return db.dataSource
+}
+
+func (db *DataBinder) SetDataSource(dataSource interface{}) error {
+	if t := reflect.TypeOf(dataSource); t == nil ||
+		t.Kind() != reflect.Ptr ||
+		t.Elem().Kind() != reflect.Struct {
+
+		return newError("dataSource must be pointer to struct")
+	}
+
+	db.dataSource = dataSource
+
+	return nil
+}
+
+func (db *DataBinder) BoundWidgets() []Widget {
+	return db.boundWidgets
+}
+
+func (db *DataBinder) SetBoundWidgets(boundWidgets []Widget) {
+	for prop, handle := range db.property2ChangedHandle {
+		prop.Changed().Detach(handle)
+	}
+
+	db.boundWidgets = boundWidgets
+
+	db.property2Widget = make(map[Property]Widget)
+	db.property2ChangedHandle = make(map[Property]int)
+
+	for _, widget := range boundWidgets {
+		widget := widget
+
+		for _, prop := range widget.AsWindowBase().name2Property {
+			prop := prop
+			if _, ok := prop.Source().(string); !ok {
+				continue
+			}
+
+			db.properties = append(db.properties, prop)
+			db.property2Widget[prop] = widget
+
+			db.property2ChangedHandle[prop] = prop.Changed().Attach(func() {
+				db.dirty = true
+
+				if db.autoSubmit {
+					if prop.Get() == nil {
+						return
+					}
+
+					v := reflect.ValueOf(db.dataSource)
+					field := db.fieldBoundToProperty(v, prop)
+					if field == nil {
+						return
+					}
+
+					if err := db.submitProperty(prop, field); err != nil {
+						return
+					}
+
+					db.submittedPublisher.Publish()
+				} else {
+					if !db.inReset {
+						db.validateProperties()
+					}
+				}
+			})
+		}
+	}
+}
+
+func (db *DataBinder) validateProperties() {
+	var hasError bool
+
+	for _, prop := range db.properties {
+		validator := prop.Validator()
+		if validator == nil {
+			continue
+		}
+
+		err := validator.Validate(prop.Get())
+		if err != nil {
+			hasError = true
+		}
+
+		if db.errorPresenter != nil {
+			widget := db.property2Widget[prop]
+
+			db.errorPresenter.PresentError(err, widget)
+		}
+	}
+
+	if hasError == db.canSubmit {
+		db.canSubmit = !hasError
+		db.canSubmitChangedPublisher.Publish()
+	}
+}
+
+func (db *DataBinder) ErrorPresenter() ErrorPresenter {
+	return db.errorPresenter
+}
+
+func (db *DataBinder) SetErrorPresenter(ep ErrorPresenter) {
+	db.errorPresenter = ep
+}
+
+func (db *DataBinder) CanSubmit() bool {
+	return db.canSubmit
+}
+
+func (db *DataBinder) CanSubmitChanged() *Event {
+	return db.canSubmitChangedPublisher.Event()
+}
+
+func (db *DataBinder) Reset() error {
+	db.inReset = true
+	defer func() {
+		db.inReset = false
+	}()
+
+	if err := db.forEach(func(prop Property, field DataField) error {
+		if f64, ok := prop.Get().(float64); ok {
+			switch v := field.Get().(type) {
+			case float32:
+				f64 = float64(v)
+
+			case float64:
+				f64 = v
+
+			case int:
+				f64 = float64(v)
+
+			case int8:
+				f64 = float64(v)
+
+			case int16:
+				f64 = float64(v)
+
+			case int32:
+				f64 = float64(v)
+
+			case int64:
+				f64 = float64(v)
+
+			case uint:
+				f64 = float64(v)
+
+			case uint8:
+				f64 = float64(v)
+
+			case uint16:
+				f64 = float64(v)
+
+			case uint32:
+				f64 = float64(v)
+
+			case uint64:
+				f64 = float64(v)
+
+			case uintptr:
+				f64 = float64(v)
+
+			default:
+				return newError(fmt.Sprintf("Field '%s': Can't convert %t to float64.", prop.Source().(string), field.Get()))
+			}
+
+			if err := prop.Set(f64); err != nil {
+				return err
+			}
+		} else {
+			if err := prop.Set(field.Get()); err != nil {
+				return err
+			}
+		}
+
+		return nil
+	}); err != nil {
+		return err
+	}
+
+	db.validateProperties()
+
+	db.dirty = false
+
+	return nil
+}
+
+func (db *DataBinder) Submit() error {
+	if !db.CanSubmit() {
+		return errValidationFailed
+	}
+
+	if err := db.forEach(func(prop Property, field DataField) error {
+		return db.submitProperty(prop, field)
+	}); err != nil {
+		return err
+	}
+
+	db.dirty = false
+
+	db.submittedPublisher.Publish()
+
+	return nil
+}
+
+func (db *DataBinder) Dirty() bool {
+	return db.dirty
+}
+
+func (db *DataBinder) submitProperty(prop Property, field DataField) error {
+	if !field.CanSet() {
+		// FIXME: handle properly
+		return nil
+	}
+
+	value := prop.Get()
+	if value == nil {
+		// This happens e.g. if CurrentIndex() of a ComboBox returns -1.
+		// FIXME: Should we handle this differently?
+		return nil
+	}
+	if err, ok := value.(error); ok {
+		return err
+	}
+
+	return field.Set(value)
+}
+
+func (db *DataBinder) forEach(f func(prop Property, field DataField) error) error {
+	dsv := reflect.ValueOf(db.dataSource)
+	if dsv.Kind() == reflect.Ptr && dsv.IsNil() {
+		return nil
+	}
+
+	for _, prop := range db.properties {
+		field := db.fieldBoundToProperty(dsv, prop)
+
+		if err := f(prop, field); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (db *DataBinder) fieldBoundToProperty(v reflect.Value, prop Property) DataField {
+	source := prop.Source().(string)
+
+	f, err := dataFieldFromPath(v, source)
+	if err != nil {
+		panic(fmt.Sprintf("invalid source '%s'", source))
+	}
+
+	return f
+}
+
+func validateBindingMemberSyntax(member string) error {
+	// FIXME
+	return nil
+}
+
+type DataField interface {
+	CanSet() bool
+	Get() interface{}
+	Set(interface{}) error
+}
+
+func dataFieldFromPath(root reflect.Value, path string) (DataField, error) {
+	v, err := reflectValueFromPath(root, path)
+	if err != nil {
+		return nil, err
+	}
+
+	// convert to DataField
+	if i, ok := v.Interface().(DataField); ok {
+		return i, nil
+	}
+
+	return reflectField(v), nil
+}
+
+func reflectValueFromPath(root reflect.Value, path string) (reflect.Value, error) {
+	v := root
+
+	for path != "" {
+		var name string
+		name, path = nextPathPart(path)
+
+		var p reflect.Value
+		for v.Kind() == reflect.Ptr {
+			p = v
+			v = v.Elem()
+		}
+
+		// Try as field first.
+		var f reflect.Value
+		if v.Kind() == reflect.Struct {
+			f = v.FieldByName(name)
+		}
+		if f.IsValid() {
+			v = f
+		} else {
+			// No field, so let's see if we got a method.
+			var m reflect.Value
+			if p.IsValid() {
+				// Try pointer receiver first.
+				m = p.MethodByName(name)
+			}
+
+			if !m.IsValid() {
+				// No pointer, try directly.
+				m = v.MethodByName(name)
+			}
+			if !m.IsValid() {
+				return v, fmt.Errorf("bad member: '%s'", path, ".")
+			}
+
+			// We assume it takes no args and returns one mandatory value plus
+			// maybe an error.
+			rvs := m.Call(nil)
+			switch len(rvs) {
+			case 1:
+				v = rvs[0]
+
+			case 2:
+				rv2 := rvs[1].Interface()
+				if err, ok := rv2.(error); ok {
+					return v, err
+				} else if rv2 != nil {
+					return v, fmt.Errorf("Second method return value must implement error.")
+				}
+
+				v = rvs[0]
+
+			default:
+				return v, fmt.Errorf("Method must return a value plus optionally an error: %s", name)
+			}
+		}
+	}
+
+	return v, nil
+}
+
+func nextPathPart(p string) (next, remaining string) {
+	for i, r := range p {
+		if r == '.' {
+			return p[:i], p[i+1:]
+		}
+	}
+	return p, ""
+}
+
+type reflectField reflect.Value
+
+func (f reflectField) CanSet() bool {
+	return reflect.Value(f).CanSet()
+}
+
+func (f reflectField) Get() interface{} {
+	return reflect.Value(f).Interface()
+}
+
+func (f reflectField) Set(value interface{}) error {
+	v := reflect.Value(f)
+	if f64, ok := value.(float64); ok {
+		switch v.Kind() {
+		case reflect.Float32, reflect.Float64:
+			v.SetFloat(f64)
+
+		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+			v.SetInt(int64(f64))
+
+		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+			v.SetUint(uint64(f64))
+
+		default:
+			return newError(fmt.Sprintf("Can't convert float64 to %s.", v.Type().Name()))
+		}
+
+		return nil
+	}
+
+	v.Set(reflect.ValueOf(value))
+
+	return nil
+}

+ 244 - 0
common/src/github.com/lxn/walk/dateedit.go

@@ -0,0 +1,244 @@
+// Copyright 2011 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 (
+	"strings"
+	"syscall"
+	"time"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+type DateEdit struct {
+	WidgetBase
+	dateChangedPublisher EventPublisher
+	format               string
+}
+
+func newDateEdit(parent Container, style uint32) (*DateEdit, error) {
+	de := new(DateEdit)
+
+	if err := InitWidget(
+		de,
+		parent,
+		"SysDateTimePick32",
+		win.WS_TABSTOP|win.WS_VISIBLE|win.DTS_SHORTDATEFORMAT|style,
+		0); err != nil {
+		return nil, err
+	}
+
+	de.MustRegisterProperty("Date", NewProperty(
+		func() interface{} {
+			return de.Date()
+		},
+		func(v interface{}) error {
+			return de.SetDate(v.(time.Time))
+		},
+		de.dateChangedPublisher.Event()))
+
+	return de, nil
+}
+
+func NewDateEdit(parent Container) (*DateEdit, error) {
+	return newDateEdit(parent, 0)
+}
+
+func NewDateEditWithNoneOption(parent Container) (*DateEdit, error) {
+	return newDateEdit(parent, win.DTS_SHOWNONE)
+}
+
+func (*DateEdit) LayoutFlags() LayoutFlags {
+	return GrowableHorz
+}
+
+func (de *DateEdit) MinSizeHint() Size {
+	return de.dialogBaseUnitsToPixels(Size{80, 12})
+}
+
+func (de *DateEdit) SizeHint() Size {
+	return de.MinSizeHint()
+}
+
+func (de *DateEdit) systemTimeToTime(st *win.SYSTEMTIME) time.Time {
+	if st == nil || !de.hasStyleBits(win.DTS_SHOWNONE) && st.WYear == 1601 && st.WMonth == 1 && st.WDay == 1 {
+		return time.Time{}
+	}
+
+	var hour, minute, second int
+	if de.timeOfDayDisplayed() {
+		hour = int(st.WHour)
+		minute = int(st.WMinute)
+		second = int(st.WSecond)
+	}
+
+	return time.Date(int(st.WYear), time.Month(st.WMonth), int(st.WDay), hour, minute, second, 0, time.Local)
+}
+
+func (de *DateEdit) timeToSystemTime(t time.Time) *win.SYSTEMTIME {
+	if t.Year() < 1601 {
+		if de.hasStyleBits(win.DTS_SHOWNONE) {
+			return nil
+		} else {
+			return &win.SYSTEMTIME{
+				WYear:  uint16(1601),
+				WMonth: uint16(1),
+				WDay:   uint16(1),
+			}
+		}
+	}
+
+	st := &win.SYSTEMTIME{
+		WYear:  uint16(t.Year()),
+		WMonth: uint16(t.Month()),
+		WDay:   uint16(t.Day()),
+	}
+
+	if de.timeOfDayDisplayed() {
+		st.WHour = uint16(t.Hour())
+		st.WMinute = uint16(t.Minute())
+		st.WSecond = uint16(t.Second())
+	}
+
+	return st
+}
+
+func (de *DateEdit) systemTime() (*win.SYSTEMTIME, error) {
+	var st win.SYSTEMTIME
+
+	switch de.SendMessage(win.DTM_GETSYSTEMTIME, 0, uintptr(unsafe.Pointer(&st))) {
+	case win.GDT_VALID:
+		return &st, nil
+
+	case win.GDT_NONE:
+		return nil, nil
+	}
+
+	return nil, newError("SendMessage(DTM_GETSYSTEMTIME)")
+}
+
+func (de *DateEdit) setSystemTime(st *win.SYSTEMTIME) error {
+	var wParam uintptr
+
+	if st != nil {
+		wParam = win.GDT_VALID
+	} else {
+		// Ensure today's date is displayed.
+		de.setSystemTime(de.timeToSystemTime(time.Now()))
+
+		wParam = win.GDT_NONE
+	}
+
+	if 0 == de.SendMessage(win.DTM_SETSYSTEMTIME, wParam, uintptr(unsafe.Pointer(st))) {
+		return newError("SendMessage(DTM_SETSYSTEMTIME)")
+	}
+
+	de.dateChangedPublisher.Publish()
+
+	return nil
+}
+
+func (de *DateEdit) timeOfDayDisplayed() bool {
+	return strings.ContainsAny(de.format, "Hhms")
+}
+
+func (de *DateEdit) Format() string {
+	return de.format
+}
+
+func (de *DateEdit) SetFormat(format string) error {
+	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(format)))
+
+	if 0 == de.SendMessage(win.DTM_SETFORMAT, 0, lp) {
+		return newErr("DTM_SETFORMAT failed")
+	}
+
+	de.format = format
+
+	return nil
+}
+
+func (de *DateEdit) Range() (min, max time.Time) {
+	var st [2]win.SYSTEMTIME
+
+	ret := de.SendMessage(win.DTM_GETRANGE, 0, uintptr(unsafe.Pointer(&st[0])))
+
+	if ret&win.GDTR_MIN > 0 {
+		min = de.systemTimeToTime(&st[0])
+	}
+
+	if ret&win.GDTR_MAX > 0 {
+		max = de.systemTimeToTime(&st[1])
+	}
+
+	return
+}
+
+func (de *DateEdit) SetRange(min, max time.Time) error {
+	if !min.IsZero() && !max.IsZero() {
+		if min.Year() > max.Year() ||
+			min.Year() == max.Year() && min.Month() > max.Month() ||
+			min.Year() == max.Year() && min.Month() == max.Month() && min.Day() > max.Day() {
+			return newError("invalid range")
+		}
+	}
+
+	var st [2]win.SYSTEMTIME
+	var wParam uintptr
+
+	if !min.IsZero() {
+		wParam |= win.GDTR_MIN
+		st[0] = *de.timeToSystemTime(min)
+	}
+
+	if !max.IsZero() {
+		wParam |= win.GDTR_MAX
+		st[1] = *de.timeToSystemTime(max)
+	}
+
+	if 0 == de.SendMessage(win.DTM_SETRANGE, wParam, uintptr(unsafe.Pointer(&st[0]))) {
+		return newError("SendMessage(DTM_SETRANGE)")
+	}
+
+	return nil
+}
+
+func (de *DateEdit) Date() time.Time {
+	st, err := de.systemTime()
+	if err != nil {
+		return time.Time{}
+	}
+
+	if st == nil {
+		return time.Time{}
+	}
+
+	return de.systemTimeToTime(st)
+}
+
+func (de *DateEdit) SetDate(date time.Time) error {
+	return de.setSystemTime(de.timeToSystemTime(date))
+}
+
+func (de *DateEdit) DateChanged() *Event {
+	return de.dateChangedPublisher.Event()
+}
+
+func (de *DateEdit) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_NOTIFY:
+		switch uint32(((*win.NMHDR)(unsafe.Pointer(lParam))).Code) {
+		case win.DTN_DATETIMECHANGE:
+			de.dateChangedPublisher.Publish()
+		}
+	}
+
+	return de.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}

+ 205 - 0
common/src/github.com/lxn/walk/declarative/action.go

@@ -0,0 +1,205 @@
+// Copyright 2012 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 declarative
+
+import (
+	"errors"
+	"fmt"
+)
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Shortcut struct {
+	Modifiers walk.Modifiers
+	Key       walk.Key
+}
+
+type Action struct {
+	AssignTo    **walk.Action
+	Text        string
+	Image       interface{}
+	Enabled     Property
+	Visible     Property
+	Shortcut    Shortcut
+	OnTriggered walk.EventHandler
+	Checkable   bool
+}
+
+func (a Action) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, error) {
+	action := walk.NewAction()
+
+	if err := action.SetText(a.Text); err != nil {
+		return nil, err
+	}
+	if err := setActionImage(action, a.Image); err != nil {
+		return nil, err
+	}
+	if err := action.SetCheckable(a.Checkable); err != nil {
+		return nil, err
+	}
+
+	if a.Enabled != nil {
+		if b, ok := a.Enabled.(bool); ok {
+			if err := action.SetEnabled(b); err != nil {
+				return nil, err
+			}
+		} else if s := builder.conditionOrProperty(a.Enabled); s != nil {
+			if c, ok := s.(walk.Condition); ok {
+				action.SetEnabledCondition(c)
+			} else {
+				return nil, fmt.Errorf("value of invalid type bound to Action.Enabled: %T", s)
+			}
+		}
+	}
+	if a.Visible != nil {
+		if b, ok := a.Visible.(bool); ok {
+			if err := action.SetVisible(b); err != nil {
+				return nil, err
+			}
+		} else if s := builder.conditionOrProperty(a.Visible); s != nil {
+			if c, ok := s.(walk.Condition); ok {
+				action.SetVisibleCondition(c)
+			} else {
+				return nil, fmt.Errorf("value of invalid type bound to Action.Visible: %T", s)
+			}
+		}
+	}
+
+	s := a.Shortcut
+	if err := action.SetShortcut(walk.Shortcut{s.Modifiers, s.Key}); err != nil {
+		return nil, err
+	}
+
+	if a.OnTriggered != nil {
+		action.Triggered().Attach(a.OnTriggered)
+	}
+
+	if menu != nil {
+		if err := menu.Actions().Add(action); err != nil {
+			return nil, err
+		}
+	}
+
+	if a.AssignTo != nil {
+		*a.AssignTo = action
+	}
+
+	return action, nil
+}
+
+type ActionRef struct {
+	Action **walk.Action
+}
+
+func (ar ActionRef) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, error) {
+	if menu != nil {
+		if err := menu.Actions().Add(*ar.Action); err != nil {
+			return nil, err
+		}
+	}
+
+	return *ar.Action, nil
+}
+
+type Menu struct {
+	AssignTo       **walk.Menu
+	AssignActionTo **walk.Action
+	Text           string
+	Image          interface{}
+	Items          []MenuItem
+	OnTriggered    walk.EventHandler
+}
+
+func (m Menu) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, error) {
+	subMenu, err := walk.NewMenu()
+	if err != nil {
+		return nil, err
+	}
+
+	var action *walk.Action
+	if menu == nil {
+		action = walk.NewMenuAction(subMenu)
+	} else if action, err = menu.Actions().AddMenu(subMenu); err != nil {
+		return nil, err
+	}
+
+	if err := action.SetText(m.Text); err != nil {
+		return nil, err
+	}
+	if err := setActionImage(action, m.Image); err != nil {
+		return nil, err
+	}
+
+	for _, item := range m.Items {
+		if _, err := item.createAction(builder, subMenu); err != nil {
+			return nil, err
+		}
+	}
+
+	if m.OnTriggered != nil {
+		action.Triggered().Attach(m.OnTriggered)
+	}
+
+	if m.AssignActionTo != nil {
+		*m.AssignActionTo = action
+	}
+	if m.AssignTo != nil {
+		*m.AssignTo = subMenu
+	}
+
+	return action, nil
+}
+
+type Separator struct {
+}
+
+func (s Separator) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, error) {
+	action := walk.NewSeparatorAction()
+
+	if menu != nil {
+		if err := menu.Actions().Add(action); err != nil {
+			return nil, err
+		}
+	}
+
+	return action, nil
+}
+
+func addToActionList(list *walk.ActionList, actions []*walk.Action) error {
+	for _, a := range actions {
+		if err := list.Add(a); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func setActionImage(action *walk.Action, image interface{}) (err error) {
+	var img walk.Image
+
+	switch image := image.(type) {
+	case nil:
+		return nil
+
+	case *walk.Bitmap:
+		img = image
+
+	case string:
+		if img, err = imageFromFile(image); err != nil {
+			return
+		}
+	}
+
+	if bmp, ok := img.(*walk.Bitmap); ok {
+		return action.SetImage(bmp)
+	}
+
+	return errors.New("invalid type for Image")
+}

+ 495 - 0
common/src/github.com/lxn/walk/declarative/builder.go

@@ -0,0 +1,495 @@
+// Copyright 2012 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 declarative
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+import (
+	"github.com/lxn/walk"
+)
+
+var (
+	conditionsByName = make(map[string]walk.Condition)
+	imagesByFilePath = make(map[string]walk.Image)
+)
+
+func imageFromFile(filePath string) (walk.Image, error) {
+	if image, ok := imagesByFilePath[filePath]; ok {
+		return image, nil
+	}
+
+	image, err := walk.NewImageFromFile(filePath)
+	if err != nil {
+		return nil, err
+	}
+
+	imagesByFilePath[filePath] = image
+
+	return image, nil
+}
+
+func MustRegisterCondition(name string, condition walk.Condition) {
+	if name == "" {
+		panic(`name == ""`)
+	}
+	if condition == nil {
+		panic("condition == nil")
+	}
+	if _, ok := conditionsByName[name]; ok {
+		panic("name already registered")
+	}
+
+	conditionsByName[name] = condition
+}
+
+type declWidget struct {
+	d Widget
+	w walk.Window
+}
+
+type Builder struct {
+	level                    int
+	columns                  int
+	row                      int
+	col                      int
+	parent                   walk.Container
+	declWidgets              []declWidget
+	name2Window              map[string]walk.Window
+	deferredFuncs            []func() error
+	knownCompositeConditions map[string]walk.Condition
+}
+
+func NewBuilder(parent walk.Container) *Builder {
+	return &Builder{
+		parent:                   parent,
+		name2Window:              make(map[string]walk.Window),
+		knownCompositeConditions: make(map[string]walk.Condition),
+	}
+}
+
+func (b *Builder) Parent() walk.Container {
+	return b.parent
+}
+
+func (b *Builder) Defer(f func() error) {
+	b.deferredFuncs = append(b.deferredFuncs, f)
+}
+
+func (b *Builder) deferBuildMenuActions(menu *walk.Menu, items []MenuItem) {
+	if len(items) > 0 {
+		b.Defer(func() error {
+			for _, item := range items {
+				if _, err := item.createAction(b, menu); err != nil {
+					return err
+				}
+			}
+
+			return nil
+		})
+	}
+}
+
+func (b *Builder) deferBuildActions(actionList *walk.ActionList, items []MenuItem) {
+	if len(items) > 0 {
+		b.Defer(func() error {
+			for _, item := range items {
+				action, err := item.createAction(b, nil)
+				if err != nil {
+					return err
+				}
+				if err := actionList.Add(action); err != nil {
+					return err
+				}
+			}
+
+			return nil
+		})
+	}
+}
+
+func (b *Builder) InitWidget(d Widget, w walk.Window, customInit func() error) error {
+	b.level++
+	defer func() {
+		b.level--
+	}()
+
+	var succeeded bool
+	defer func() {
+		if !succeeded {
+			w.Dispose()
+		}
+	}()
+
+	b.declWidgets = append(b.declWidgets, declWidget{d, w})
+
+	// Widget
+	name, _, _, font, toolTipText, minSize, maxSize, stretchFactor, row, rowSpan, column, columnSpan, alwaysConsumeSpace, contextMenuItems, onKeyDown, onKeyPress, onKeyUp, onMouseDown, onMouseMove, onMouseUp, onSizeChanged := d.WidgetInfo()
+
+	w.SetName(name)
+
+	if name != "" {
+		b.name2Window[name] = w
+	}
+
+	if toolTipText != "" {
+		if widget, ok := w.(walk.Widget); ok {
+			if err := widget.SetToolTipText(toolTipText); err != nil {
+				return err
+			}
+		}
+	}
+
+	if err := w.SetMinMaxSize(minSize.toW(), maxSize.toW()); err != nil {
+		return err
+	}
+
+	if len(contextMenuItems) > 0 {
+		cm, err := walk.NewMenu()
+		if err != nil {
+			return err
+		}
+
+		b.deferBuildMenuActions(cm, contextMenuItems)
+
+		w.SetContextMenu(cm)
+	}
+
+	if onKeyDown != nil {
+		w.KeyDown().Attach(onKeyDown)
+	}
+
+	if onKeyPress != nil {
+		w.KeyPress().Attach(onKeyPress)
+	}
+
+	if onKeyUp != nil {
+		w.KeyUp().Attach(onKeyUp)
+	}
+
+	if onMouseDown != nil {
+		w.MouseDown().Attach(onMouseDown)
+	}
+
+	if onMouseMove != nil {
+		w.MouseMove().Attach(onMouseMove)
+	}
+
+	if onMouseUp != nil {
+		w.MouseUp().Attach(onMouseUp)
+	}
+
+	if onSizeChanged != nil {
+		w.SizeChanged().Attach(onSizeChanged)
+	}
+
+	if widget, ok := w.(walk.Widget); ok {
+		if err := widget.SetAlwaysConsumeSpace(alwaysConsumeSpace); err != nil {
+			return err
+		}
+
+		type SetStretchFactorer interface {
+			SetStretchFactor(widget walk.Widget, factor int) error
+		}
+
+		if p := widget.Parent(); p != nil {
+			if stretchFactor < 1 {
+				stretchFactor = 1
+			}
+
+			switch l := p.Layout().(type) {
+			case SetStretchFactorer:
+				if err := l.SetStretchFactor(widget, stretchFactor); err != nil {
+					return err
+				}
+
+			case *walk.GridLayout:
+				csf := l.ColumnStretchFactor(column)
+				if csf < stretchFactor {
+					csf = stretchFactor
+				}
+				l.SetColumnStretchFactor(column, csf)
+
+				rsf := l.RowStretchFactor(row)
+				if rsf < stretchFactor {
+					rsf = stretchFactor
+				}
+				l.SetRowStretchFactor(row, rsf)
+
+				if rowSpan < 1 {
+					rowSpan = 1
+				}
+				if columnSpan < 1 {
+					columnSpan = 1
+				}
+
+				if b.columns > 0 && row == 0 && column == 0 {
+					if b.col+columnSpan > b.columns {
+						b.row++
+						b.col = 0
+					}
+
+					row = b.row
+					column = b.col
+
+					b.col += columnSpan
+				}
+
+				r := walk.Rectangle{column, row, columnSpan, rowSpan}
+
+				if err := l.SetRange(widget, r); err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	oldParent := b.parent
+
+	// Container
+	var db *walk.DataBinder
+	if dc, ok := d.(Container); ok {
+		if wc, ok := w.(walk.Container); ok {
+			dataBinder, layout, children := dc.ContainerInfo()
+
+			if layout != nil {
+				l, err := layout.Create()
+				if err != nil {
+					return err
+				}
+
+				if err := wc.SetLayout(l); err != nil {
+					return err
+				}
+			}
+
+			b.parent = wc
+			defer func() {
+				b.parent = oldParent
+			}()
+
+			if g, ok := layout.(Grid); ok {
+				columns := b.columns
+				defer func() {
+					b.columns, b.row, b.col = columns, row, column+columnSpan
+				}()
+
+				b.columns = g.Columns
+				b.row = 0
+				b.col = 0
+			}
+
+			for _, child := range children {
+				if err := child.Create(b); err != nil {
+					return err
+				}
+			}
+
+			if dataBinder.AssignTo != nil || dataBinder.DataSource != nil {
+				if dataB, err := dataBinder.create(); err != nil {
+					return err
+				} else {
+					db = dataB
+				}
+			}
+		}
+	}
+
+	// Custom
+	if customInit != nil {
+		if err := customInit(); err != nil {
+			return err
+		}
+	}
+
+	b.parent = oldParent
+
+	// Widget continued
+	if font != nil {
+		if f, err := font.Create(); err != nil {
+			return err
+		} else if f != nil {
+			w.SetFont(f)
+		}
+	}
+
+	if b.level == 1 {
+		if err := b.initProperties(); err != nil {
+			return err
+		}
+	}
+
+	// Call Reset on DataBinder after customInit, so a Dialog gets a chance to first
+	// wire up its DefaultButton to the CanSubmitChanged event of a DataBinder.
+	if db != nil {
+		if _, ok := d.(Container); ok {
+			if wc, ok := w.(walk.Container); ok {
+				b.Defer(func() error {
+					// FIXME: Currently SetDataBinder must be called after initProperties.
+					wc.SetDataBinder(db)
+
+					if db.DataSource() == nil {
+						return nil
+					}
+
+					return db.Reset()
+				})
+			}
+		}
+	}
+
+	if b.level == 1 {
+		for _, f := range b.deferredFuncs {
+			if err := f(); err != nil {
+				return err
+			}
+		}
+	}
+
+	succeeded = true
+
+	return nil
+}
+
+func (b *Builder) initProperties() error {
+	for _, dw := range b.declWidgets {
+		d, w := dw.d, dw.w
+
+		sv := reflect.ValueOf(d)
+		st := sv.Type()
+		if st.Kind() != reflect.Struct {
+			panic("d must be a struct value")
+		}
+
+		wb := w.AsWindowBase()
+
+		fieldCount := st.NumField()
+		for i := 0; i < fieldCount; i++ {
+			sf := st.Field(i)
+
+			prop := wb.Property(sf.Name)
+
+			switch val := sv.Field(i).Interface().(type) {
+			case nil:
+				// nop
+
+			case bindData:
+				if prop == nil {
+					panic(sf.Name + " is not a property")
+				}
+
+				src := b.conditionOrProperty(val)
+
+				if src == nil {
+					// No luck so far, so we assume the expression refers to
+					// something in the data source.
+					src = val.expression
+
+					if val.validator != nil {
+						validator, err := val.validator.Create()
+						if err != nil {
+							return err
+						}
+						if err := prop.SetValidator(validator); err != nil {
+							return err
+						}
+					}
+				}
+
+				if err := prop.SetSource(src); err != nil {
+					return err
+				}
+
+			case walk.Condition:
+				if prop == nil {
+					panic(sf.Name + " is not a property")
+				}
+
+				if err := prop.SetSource(val); err != nil {
+					return err
+				}
+
+			default:
+				if prop == nil {
+					continue
+				}
+
+				v := prop.Get()
+				valt, vt := reflect.TypeOf(val), reflect.TypeOf(v)
+
+				if v != nil && valt != vt {
+					panic(fmt.Sprintf("cannot assign value %v of type %T to property %s of type %T", val, val, sf.Name, v))
+				}
+				if err := prop.Set(val); err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func (b *Builder) conditionOrProperty(data Property) interface{} {
+	switch val := data.(type) {
+	case bindData:
+		if c, ok := b.knownCompositeConditions[val.expression]; ok {
+			return c
+		} else if conds := strings.Split(val.expression, "&&"); len(conds) > 1 {
+			// This looks like a composite condition.
+			for i, s := range conds {
+				conds[i] = strings.TrimSpace(s)
+			}
+
+			var conditions []walk.Condition
+
+			for _, cond := range conds {
+				if p := b.property(cond); p != nil {
+					conditions = append(conditions, p.(walk.Condition))
+				} else if c, ok := conditionsByName[cond]; ok {
+					conditions = append(conditions, c)
+				} else {
+					panic("unknown condition or property name: " + cond)
+				}
+			}
+
+			var condition walk.Condition
+			if len(conditions) > 1 {
+				condition = walk.NewAllCondition(conditions...)
+				b.knownCompositeConditions[val.expression] = condition
+			} else {
+				condition = conditions[0]
+			}
+
+			return condition
+		}
+
+		if p := b.property(val.expression); p != nil {
+			return p
+		}
+
+		return conditionsByName[val.expression]
+
+	case walk.Condition:
+		return val
+	}
+
+	return nil
+}
+
+func (b *Builder) property(expression string) walk.Property {
+	if parts := strings.Split(expression, "."); len(parts) == 2 {
+		if sw, ok := b.name2Window[parts[0]]; ok {
+			return sw.AsWindowBase().Property(parts[1])
+		}
+	}
+
+	return nil
+}

+ 78 - 0
common/src/github.com/lxn/walk/declarative/checkbox.go

@@ -0,0 +1,78 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type CheckBox struct {
+	AssignTo            **walk.CheckBox
+	Name                string
+	Enabled             Property
+	Visible             Property
+	Font                Font
+	ToolTipText         Property
+	MinSize             Size
+	MaxSize             Size
+	StretchFactor       int
+	Row                 int
+	RowSpan             int
+	Column              int
+	ColumnSpan          int
+	AlwaysConsumeSpace  bool
+	ContextMenuItems    []MenuItem
+	OnKeyDown           walk.KeyEventHandler
+	OnKeyPress          walk.KeyEventHandler
+	OnKeyUp             walk.KeyEventHandler
+	OnMouseDown         walk.MouseEventHandler
+	OnMouseMove         walk.MouseEventHandler
+	OnMouseUp           walk.MouseEventHandler
+	OnSizeChanged       walk.EventHandler
+	Text                Property
+	Checked             Property
+	CheckState          Property
+	Tristate            bool
+	OnClicked           walk.EventHandler
+	OnCheckedChanged    walk.EventHandler
+	OnCheckStateChanged walk.EventHandler
+}
+
+func (cb CheckBox) Create(builder *Builder) error {
+	w, err := walk.NewCheckBox(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(cb, w, func() error {
+		if err := w.SetTristate(cb.Tristate); err != nil {
+			return err
+		}
+
+		if cb.OnClicked != nil {
+			w.Clicked().Attach(cb.OnClicked)
+		}
+
+		if cb.OnCheckedChanged != nil {
+			w.CheckedChanged().Attach(cb.OnCheckedChanged)
+		}
+
+		if cb.OnCheckStateChanged != nil {
+			w.CheckStateChanged().Attach(cb.OnCheckStateChanged)
+		}
+
+		if cb.AssignTo != nil {
+			*cb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w CheckBox) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 100 - 0
common/src/github.com/lxn/walk/declarative/combobox.go

@@ -0,0 +1,100 @@
+// Copyright 2012 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 declarative
+
+import (
+	"errors"
+)
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ComboBox struct {
+	AssignTo              **walk.ComboBox
+	Name                  string
+	Enabled               Property
+	Visible               Property
+	Font                  Font
+	ToolTipText           Property
+	MinSize               Size
+	MaxSize               Size
+	StretchFactor         int
+	Row                   int
+	RowSpan               int
+	Column                int
+	ColumnSpan            int
+	AlwaysConsumeSpace    bool
+	ContextMenuItems      []MenuItem
+	OnKeyDown             walk.KeyEventHandler
+	OnKeyUp               walk.KeyEventHandler
+	OnMouseDown           walk.MouseEventHandler
+	OnKeyPress            walk.KeyEventHandler
+	OnMouseMove           walk.MouseEventHandler
+	OnMouseUp             walk.MouseEventHandler
+	OnSizeChanged         walk.EventHandler
+	Editable              bool
+	Format                string
+	Precision             int
+	MaxLength             int
+	BindingMember         string
+	DisplayMember         string
+	Model                 interface{}
+	Value                 Property
+	CurrentIndex          Property
+	OnCurrentIndexChanged walk.EventHandler
+}
+
+func (cb ComboBox) Create(builder *Builder) error {
+	if _, ok := cb.Model.([]string); ok &&
+		(cb.BindingMember != "" || cb.DisplayMember != "") {
+
+		return errors.New("ComboBox.Create: BindingMember and DisplayMember must be empty for []string models.")
+	}
+
+	var w *walk.ComboBox
+	var err error
+	if cb.Editable {
+		w, err = walk.NewComboBox(builder.Parent())
+	} else {
+		w, err = walk.NewDropDownBox(builder.Parent())
+	}
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(cb, w, func() error {
+		w.SetFormat(cb.Format)
+		w.SetPrecision(cb.Precision)
+		w.SetMaxLength(cb.MaxLength)
+
+		if err := w.SetBindingMember(cb.BindingMember); err != nil {
+			return err
+		}
+		if err := w.SetDisplayMember(cb.DisplayMember); err != nil {
+			return err
+		}
+
+		if err := w.SetModel(cb.Model); err != nil {
+			return err
+		}
+
+		if cb.OnCurrentIndexChanged != nil {
+			w.CurrentIndexChanged().Attach(cb.OnCurrentIndexChanged)
+		}
+
+		if cb.AssignTo != nil {
+			*cb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ComboBox) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 68 - 0
common/src/github.com/lxn/walk/declarative/composite.go

@@ -0,0 +1,68 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Composite struct {
+	AssignTo           **walk.Composite
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	DataBinder         DataBinder
+	Layout             Layout
+	Children           []Widget
+}
+
+func (c Composite) Create(builder *Builder) error {
+	w, err := walk.NewComposite(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(c, w, func() error {
+		if c.AssignTo != nil {
+			*c.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w Composite) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (c Composite) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return c.DataBinder, c.Layout, c.Children
+}

+ 75 - 0
common/src/github.com/lxn/walk/declarative/customwidget.go

@@ -0,0 +1,75 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+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 {
+	AssignTo            **walk.CustomWidget
+	Name                string
+	Enabled             Property
+	Visible             Property
+	Font                Font
+	ToolTipText         Property
+	MinSize             Size
+	MaxSize             Size
+	StretchFactor       int
+	Row                 int
+	RowSpan             int
+	Column              int
+	ColumnSpan          int
+	AlwaysConsumeSpace  bool
+	ContextMenuItems    []MenuItem
+	OnKeyDown           walk.KeyEventHandler
+	OnKeyPress          walk.KeyEventHandler
+	OnKeyUp             walk.KeyEventHandler
+	OnMouseDown         walk.MouseEventHandler
+	OnMouseMove         walk.MouseEventHandler
+	OnMouseUp           walk.MouseEventHandler
+	OnSizeChanged       walk.EventHandler
+	Style               uint32
+	Paint               walk.PaintFunc
+	ClearsBackground    bool
+	InvalidatesOnResize bool
+	PaintMode           PaintMode
+}
+
+func (cw CustomWidget) Create(builder *Builder) error {
+	w, err := walk.NewCustomWidget(builder.Parent(), uint(cw.Style), cw.Paint)
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(cw, w, func() error {
+		if cw.PaintMode != PaintNormal && cw.ClearsBackground {
+			panic("PaintMode and ClearsBackground are incompatible")
+		}
+		w.SetClearsBackground(cw.ClearsBackground)
+		w.SetInvalidatesOnResize(cw.InvalidatesOnResize)
+		w.SetPaintMode(walk.PaintMode(cw.PaintMode))
+
+		if cw.AssignTo != nil {
+			*cw.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w CustomWidget) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 45 - 0
common/src/github.com/lxn/walk/declarative/databinder.go

@@ -0,0 +1,45 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type DataBinder struct {
+	AssignTo       **walk.DataBinder
+	DataSource     interface{}
+	ErrorPresenter ErrorPresenter
+	AutoSubmit     bool
+	OnSubmitted    walk.EventHandler
+}
+
+func (db DataBinder) create() (*walk.DataBinder, error) {
+	b := walk.NewDataBinder()
+
+	if db.ErrorPresenter != nil {
+		ep, err := db.ErrorPresenter.Create()
+		if err != nil {
+			return nil, err
+		}
+		b.SetErrorPresenter(ep)
+	}
+
+	b.SetDataSource(db.DataSource)
+
+	b.SetAutoSubmit(db.AutoSubmit)
+
+	if db.OnSubmitted != nil {
+		b.Submitted().Attach(db.OnSubmitted)
+	}
+
+	if db.AssignTo != nil {
+		*db.AssignTo = b
+	}
+
+	return b, nil
+}

+ 84 - 0
common/src/github.com/lxn/walk/declarative/dateedit.go

@@ -0,0 +1,84 @@
+// Copyright 2012 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 declarative
+
+import (
+	"time"
+)
+
+import (
+	"github.com/lxn/walk"
+)
+
+type DateEdit struct {
+	AssignTo           **walk.DateEdit
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Format             string
+	NoneOption         bool
+	MinDate            time.Time
+	MaxDate            time.Time
+	Date               Property
+	OnDateChanged      walk.EventHandler
+}
+
+func (de DateEdit) Create(builder *Builder) error {
+	var w *walk.DateEdit
+	var err error
+
+	if de.NoneOption {
+		w, err = walk.NewDateEditWithNoneOption(builder.Parent())
+	} else {
+		w, err = walk.NewDateEdit(builder.Parent())
+	}
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(de, w, func() error {
+		if err := w.SetFormat(de.Format); err != nil {
+			return err
+		}
+
+		if err := w.SetRange(de.MinDate, de.MaxDate); err != nil {
+			return err
+		}
+
+		if de.OnDateChanged != nil {
+			w.DateChanged().Attach(de.OnDateChanged)
+		}
+
+		if de.AssignTo != nil {
+			*de.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w DateEdit) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 135 - 0
common/src/github.com/lxn/walk/declarative/dialog.go

@@ -0,0 +1,135 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Dialog struct {
+	AssignTo         **walk.Dialog
+	Name             string
+	Enabled          Property
+	Visible          Property
+	Font             Font
+	MinSize          Size
+	MaxSize          Size
+	ContextMenuItems []MenuItem
+	OnKeyDown        walk.KeyEventHandler
+	OnKeyPress       walk.KeyEventHandler
+	OnKeyUp          walk.KeyEventHandler
+	OnMouseDown      walk.MouseEventHandler
+	OnMouseMove      walk.MouseEventHandler
+	OnMouseUp        walk.MouseEventHandler
+	OnSizeChanged    walk.EventHandler
+	Title            string
+	Size             Size
+	DataBinder       DataBinder
+	Layout           Layout
+	Children         []Widget
+	DefaultButton    **walk.PushButton
+	CancelButton     **walk.PushButton
+	FixedSize        bool
+}
+
+func (d Dialog) Create(owner walk.Form) error {
+	var w *walk.Dialog
+	var err error
+
+	if d.FixedSize {
+		w, err = walk.NewDialogWithFixedSize(owner)
+	} else {
+		w, err = walk.NewDialog(owner)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	tlwi := topLevelWindowInfo{
+		Name:             d.Name,
+		Font:             d.Font,
+		ToolTipText:      "",
+		MinSize:          d.MinSize,
+		MaxSize:          d.MaxSize,
+		ContextMenuItems: d.ContextMenuItems,
+		DataBinder:       d.DataBinder,
+		Layout:           d.Layout,
+		Children:         d.Children,
+		OnKeyDown:        d.OnKeyDown,
+		OnKeyPress:       d.OnKeyPress,
+		OnKeyUp:          d.OnKeyUp,
+		OnMouseDown:      d.OnMouseDown,
+		OnMouseMove:      d.OnMouseMove,
+		OnMouseUp:        d.OnMouseUp,
+		OnSizeChanged:    d.OnSizeChanged,
+	}
+
+	var db *walk.DataBinder
+	if d.DataBinder.AssignTo == nil {
+		d.DataBinder.AssignTo = &db
+	}
+
+	builder := NewBuilder(nil)
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(tlwi, w, func() error {
+		if err := w.SetTitle(d.Title); err != nil {
+			return err
+		}
+
+		if err := w.SetSize(d.Size.toW()); err != nil {
+			return err
+		}
+
+		if d.DefaultButton != nil {
+			if err := w.SetDefaultButton(*d.DefaultButton); err != nil {
+				return err
+			}
+
+			if db := *d.DataBinder.AssignTo; db != nil {
+				if db.DataSource() != nil {
+					(*d.DefaultButton).SetEnabled(db.CanSubmit())
+				}
+
+				db.CanSubmitChanged().Attach(func() {
+					(*d.DefaultButton).SetEnabled(db.CanSubmit())
+				})
+			}
+		}
+		if d.CancelButton != nil {
+			if err := w.SetCancelButton(*d.CancelButton); err != nil {
+				return err
+			}
+		}
+
+		if d.AssignTo != nil {
+			*d.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (d Dialog) Run(owner walk.Form) (int, error) {
+	var w *walk.Dialog
+
+	if d.AssignTo == nil {
+		d.AssignTo = &w
+	}
+
+	if err := d.Create(owner); err != nil {
+		return 0, err
+	}
+
+	return (*d.AssignTo).Run(), nil
+}

+ 43 - 0
common/src/github.com/lxn/walk/declarative/font.go

@@ -0,0 +1,43 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Font struct {
+	Family    string
+	PointSize int
+	Bold      bool
+	Italic    bool
+	Underline bool
+	StrikeOut bool
+}
+
+func (f Font) Create() (*walk.Font, error) {
+	if f.Family == "" && f.PointSize == 0 {
+		return nil, nil
+	}
+
+	var fs walk.FontStyle
+
+	if f.Bold {
+		fs |= walk.FontBold
+	}
+	if f.Italic {
+		fs |= walk.FontItalic
+	}
+	if f.Underline {
+		fs |= walk.FontUnderline
+	}
+	if f.StrikeOut {
+		fs |= walk.FontStrikeOut
+	}
+
+	return walk.NewFont(f.Family, f.PointSize, fs)
+}

+ 77 - 0
common/src/github.com/lxn/walk/declarative/groupbox.go

@@ -0,0 +1,77 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type GroupBox struct {
+	AssignTo           **walk.GroupBox
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Title              string
+	DataBinder         DataBinder
+	Layout             Layout
+	Children           []Widget
+	Checkable          bool
+	Checked            Property
+}
+
+func (gb GroupBox) Create(builder *Builder) error {
+	w, err := walk.NewGroupBox(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(gb, w, func() error {
+		if err := w.SetTitle(gb.Title); err != nil {
+			return err
+		}
+
+		w.SetCheckable(gb.Checkable)
+
+		if gb.AssignTo != nil {
+			*gb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w GroupBox) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (gb GroupBox) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return gb.DataBinder, gb.Layout, gb.Children
+}

+ 60 - 0
common/src/github.com/lxn/walk/declarative/imageview.go

@@ -0,0 +1,60 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ImageView struct {
+	AssignTo           **walk.ImageView
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Image              walk.Image
+}
+
+func (iv ImageView) Create(builder *Builder) error {
+	w, err := walk.NewImageView(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(iv, w, func() error {
+		if err := w.SetImage(iv.Image); err != nil {
+			return err
+		}
+
+		if iv.AssignTo != nil {
+			*iv.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ImageView) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 112 - 0
common/src/github.com/lxn/walk/declarative/interfaces.go

@@ -0,0 +1,112 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+func tr(source string, context ...string) string {
+	if translation := walk.TranslationFunc(); translation != nil {
+		return translation(source, context...)
+	}
+
+	return source
+}
+
+type Property interface{}
+
+type bindData struct {
+	expression string
+	validator  Validator
+}
+
+func Bind(expression string, validators ...Validator) Property {
+	bd := bindData{expression: expression}
+	switch len(validators) {
+	case 0:
+		// nop
+
+	case 1:
+		bd.validator = validators[0]
+
+	default:
+		bd.validator = dMultiValidator{validators}
+	}
+
+	return bd
+}
+
+type Layout interface {
+	Create() (walk.Layout, error)
+}
+
+type Widget interface {
+	Create(builder *Builder) error
+	WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler)
+}
+
+type Container interface {
+	ContainerInfo() (DataBinder, Layout, []Widget)
+}
+
+type MenuItem interface {
+	createAction(builder *Builder, menu *walk.Menu) (*walk.Action, error)
+}
+
+type Validator interface {
+	Create() (walk.Validator, error)
+}
+
+type ErrorPresenter interface {
+	Create() (walk.ErrorPresenter, error)
+}
+
+type ErrorPresenterRef struct {
+	ErrorPresenter *walk.ErrorPresenter
+}
+
+func (epr ErrorPresenterRef) Create() (walk.ErrorPresenter, error) {
+	if epr.ErrorPresenter != nil {
+		return *epr.ErrorPresenter, nil
+	}
+
+	return nil, nil
+}
+
+type topLevelWindowInfo struct {
+	Name             string
+	Disabled         bool
+	Hidden           bool
+	Font             Font
+	ToolTipText      string
+	MinSize          Size
+	MaxSize          Size
+	ContextMenuItems []MenuItem
+	OnKeyDown        walk.KeyEventHandler
+	OnKeyPress       walk.KeyEventHandler
+	OnKeyUp          walk.KeyEventHandler
+	OnMouseDown      walk.MouseEventHandler
+	OnMouseMove      walk.MouseEventHandler
+	OnMouseUp        walk.MouseEventHandler
+	OnSizeChanged    walk.EventHandler
+	DataBinder       DataBinder
+	Layout           Layout
+	Children         []Widget
+}
+
+func (topLevelWindowInfo) Create(builder *Builder) error {
+	return nil
+}
+
+func (i topLevelWindowInfo) WidgetInfo() (name string, disabled, hidden bool, font *Font, ToolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return i.Name, i.Disabled, i.Hidden, &i.Font, i.ToolTipText, i.MinSize, i.MaxSize, 0, 0, 0, 0, 0, false, i.ContextMenuItems, i.OnKeyDown, i.OnKeyPress, i.OnKeyUp, i.OnMouseDown, i.OnMouseMove, i.OnMouseUp, i.OnSizeChanged
+}
+
+func (i topLevelWindowInfo) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return i.DataBinder, i.Layout, i.Children
+}

+ 56 - 0
common/src/github.com/lxn/walk/declarative/label.go

@@ -0,0 +1,56 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Label struct {
+	AssignTo           **walk.Label
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Text               Property
+}
+
+func (l Label) Create(builder *Builder) error {
+	w, err := walk.NewLabel(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(l, w, func() error {
+		if l.AssignTo != nil {
+			*l.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w Label) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 122 - 0
common/src/github.com/lxn/walk/declarative/layouts.go

@@ -0,0 +1,122 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Orientation byte
+
+const (
+	Horizontal Orientation = Orientation(walk.Horizontal)
+	Vertical   Orientation = Orientation(walk.Vertical)
+)
+
+type Margins struct {
+	Left   int
+	Top    int
+	Right  int
+	Bottom int
+}
+
+func (m Margins) isZero() bool {
+	return m.Left == 0 && m.Top == 0 && m.Right == 0 && m.Bottom == 0
+}
+
+func (m Margins) toW() walk.Margins {
+	return walk.Margins{m.Left, m.Top, m.Right, m.Bottom}
+}
+
+type Size struct {
+	Width  int
+	Height int
+}
+
+func (s Size) toW() walk.Size {
+	return walk.Size{s.Width, s.Height}
+}
+
+func setLayoutMargins(layout walk.Layout, margins Margins, marginsZero bool) error {
+	if !marginsZero && margins.isZero() {
+		margins = Margins{9, 9, 9, 9}
+	}
+
+	return layout.SetMargins(margins.toW())
+}
+
+func setLayoutSpacing(layout walk.Layout, spacing int, spacingZero bool) error {
+	if !spacingZero && spacing == 0 {
+		spacing = 6
+	}
+
+	return layout.SetSpacing(spacing)
+}
+
+type HBox struct {
+	Margins     Margins
+	Spacing     int
+	MarginsZero bool
+	SpacingZero bool
+}
+
+func (hb HBox) Create() (walk.Layout, error) {
+	l := walk.NewHBoxLayout()
+
+	if err := setLayoutMargins(l, hb.Margins, hb.MarginsZero); err != nil {
+		return nil, err
+	}
+
+	if err := setLayoutSpacing(l, hb.Spacing, hb.SpacingZero); err != nil {
+		return nil, err
+	}
+
+	return l, nil
+}
+
+type VBox struct {
+	Margins     Margins
+	Spacing     int
+	MarginsZero bool
+	SpacingZero bool
+}
+
+func (vb VBox) Create() (walk.Layout, error) {
+	l := walk.NewVBoxLayout()
+
+	if err := setLayoutMargins(l, vb.Margins, vb.MarginsZero); err != nil {
+		return nil, err
+	}
+
+	if err := setLayoutSpacing(l, vb.Spacing, vb.SpacingZero); err != nil {
+		return nil, err
+	}
+
+	return l, nil
+}
+
+type Grid struct {
+	Columns     int
+	Margins     Margins
+	Spacing     int
+	MarginsZero bool
+	SpacingZero bool
+}
+
+func (g Grid) Create() (walk.Layout, error) {
+	l := walk.NewGridLayout()
+
+	if err := setLayoutMargins(l, g.Margins, g.MarginsZero); err != nil {
+		return nil, err
+	}
+
+	if err := setLayoutSpacing(l, g.Spacing, g.SpacingZero); err != nil {
+		return nil, err
+	}
+
+	return l, nil
+}

+ 90 - 0
common/src/github.com/lxn/walk/declarative/lineedit.go

@@ -0,0 +1,90 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type CaseMode uint32
+
+const (
+	CaseModeMixed CaseMode = CaseMode(walk.CaseModeMixed)
+	CaseModeUpper CaseMode = CaseMode(walk.CaseModeUpper)
+	CaseModeLower CaseMode = CaseMode(walk.CaseModeLower)
+)
+
+type LineEdit struct {
+	AssignTo           **walk.LineEdit
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Text               Property
+	ReadOnly           Property
+	CueBanner          string
+	MaxLength          int
+	PasswordMode       bool
+	OnEditingFinished  walk.EventHandler
+	OnTextChanged      walk.EventHandler
+	CaseMode           CaseMode
+}
+
+func (le LineEdit) Create(builder *Builder) error {
+	w, err := walk.NewLineEdit(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(le, w, func() error {
+		if le.CueBanner != "" {
+			if err := w.SetCueBanner(le.CueBanner); err != nil {
+				return err
+			}
+		}
+		w.SetMaxLength(le.MaxLength)
+		w.SetPasswordMode(le.PasswordMode)
+
+		if err := w.SetCaseMode(walk.CaseMode(le.CaseMode)); err != nil {
+			return err
+		}
+
+		if le.OnEditingFinished != nil {
+			w.EditingFinished().Attach(le.OnEditingFinished)
+		}
+		if le.OnTextChanged != nil {
+			w.TextChanged().Attach(le.OnTextChanged)
+		}
+
+		if le.AssignTo != nil {
+			*le.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w LineEdit) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 55 - 0
common/src/github.com/lxn/walk/declarative/lineerrorpresenter.go

@@ -0,0 +1,55 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type LineErrorPresenter struct {
+	AssignTo           *walk.ErrorPresenter
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+}
+
+func (lep LineErrorPresenter) Create(builder *Builder) error {
+	w, err := walk.NewLineErrorPresenter(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(lep, w, func() error {
+		if lep.AssignTo != nil {
+			*lep.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w LineErrorPresenter) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 99 - 0
common/src/github.com/lxn/walk/declarative/listbox.go

@@ -0,0 +1,99 @@
+// Copyright 2012 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 declarative
+
+import (
+	"errors"
+)
+
+import (
+	"github.com/lxn/walk"
+	"github.com/lxn/win"
+)
+
+type ListBox struct {
+	AssignTo                 **walk.ListBox
+	Name                     string
+	Enabled                  Property
+	Visible                  Property
+	Font                     Font
+	ToolTipText              Property
+	MinSize                  Size
+	MaxSize                  Size
+	StretchFactor            int
+	Row                      int
+	RowSpan                  int
+	Column                   int
+	ColumnSpan               int
+	AlwaysConsumeSpace       bool
+	ContextMenuItems         []MenuItem
+	OnKeyDown                walk.KeyEventHandler
+	OnKeyPress               walk.KeyEventHandler
+	OnKeyUp                  walk.KeyEventHandler
+	OnMouseDown              walk.MouseEventHandler
+	OnMouseMove              walk.MouseEventHandler
+	OnMouseUp                walk.MouseEventHandler
+	OnSizeChanged            walk.EventHandler
+	MultiSelection           bool
+	Format                   string
+	Precision                int
+	DataMember               string
+	Model                    interface{}
+	OnCurrentIndexChanged    walk.EventHandler
+	OnSelectedIndexesChanged walk.EventHandler
+	OnItemActivated          walk.EventHandler
+}
+
+func (lb ListBox) Create(builder *Builder) error {
+	var w *walk.ListBox
+	var err error
+	if _, ok := lb.Model.([]string); ok && lb.DataMember != "" {
+		return errors.New("ListBox.Create: DataMember must be empty for []string models.")
+	}
+
+	if lb.MultiSelection {
+		w, err = walk.NewListBoxWithStyle(builder.Parent(), win.LBS_EXTENDEDSEL)
+	} else {
+		w, err = walk.NewListBox(builder.Parent())
+	}
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(lb, w, func() error {
+		w.SetFormat(lb.Format)
+		w.SetPrecision(lb.Precision)
+
+		if err := w.SetDataMember(lb.DataMember); err != nil {
+			return err
+		}
+
+		if err := w.SetModel(lb.Model); err != nil {
+			return err
+		}
+
+		if lb.OnCurrentIndexChanged != nil {
+			w.CurrentIndexChanged().Attach(lb.OnCurrentIndexChanged)
+		}
+		if lb.OnSelectedIndexesChanged != nil {
+			w.SelectedIndexesChanged().Attach(lb.OnSelectedIndexesChanged)
+		}
+		if lb.OnItemActivated != nil {
+			w.ItemActivated().Attach(lb.OnItemActivated)
+		}
+
+		if lb.AssignTo != nil {
+			*lb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ListBox) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 137 - 0
common/src/github.com/lxn/walk/declarative/mainwindow.go

@@ -0,0 +1,137 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type MainWindow struct {
+	AssignTo         **walk.MainWindow
+	Name             string
+	Enabled          Property
+	Visible          Property
+	Font             Font
+	MinSize          Size
+	MaxSize          Size
+	ContextMenuItems []MenuItem
+	OnKeyDown        walk.KeyEventHandler
+	OnKeyPress       walk.KeyEventHandler
+	OnKeyUp          walk.KeyEventHandler
+	OnMouseDown      walk.MouseEventHandler
+	OnMouseMove      walk.MouseEventHandler
+	OnMouseUp        walk.MouseEventHandler
+	OnDropFiles      walk.DropFilesEventHandler
+	OnSizeChanged    walk.EventHandler
+	Title            string
+	Size             Size
+	DataBinder       DataBinder
+	Layout           Layout
+	Children         []Widget
+	MenuItems        []MenuItem
+	ToolBarItems     []MenuItem // Deprecated, use ToolBar instead
+	ToolBar          ToolBar
+}
+
+func (mw MainWindow) Create() error {
+	w, err := walk.NewMainWindow()
+	if err != nil {
+		return err
+	}
+
+	tlwi := topLevelWindowInfo{
+		Name:             mw.Name,
+		Font:             mw.Font,
+		ToolTipText:      "",
+		MinSize:          mw.MinSize,
+		MaxSize:          mw.MaxSize,
+		ContextMenuItems: mw.ContextMenuItems,
+		OnKeyDown:        mw.OnKeyDown,
+		OnKeyPress:       mw.OnKeyPress,
+		OnKeyUp:          mw.OnKeyUp,
+		OnMouseDown:      mw.OnMouseDown,
+		OnMouseMove:      mw.OnMouseMove,
+		OnMouseUp:        mw.OnMouseUp,
+		OnSizeChanged:    mw.OnSizeChanged,
+		DataBinder:       mw.DataBinder,
+		Layout:           mw.Layout,
+		Children:         mw.Children,
+	}
+
+	builder := NewBuilder(nil)
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)
+
+	return builder.InitWidget(tlwi, w, func() error {
+		if len(mw.ToolBar.Items) > 0 {
+			var tb *walk.ToolBar
+			if mw.ToolBar.AssignTo == nil {
+				mw.ToolBar.AssignTo = &tb
+			}
+
+			if err := mw.ToolBar.Create(builder); err != nil {
+				return err
+			}
+
+			old := w.ToolBar()
+			w.SetToolBar(*mw.ToolBar.AssignTo)
+			old.Dispose()
+		} else {
+			builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)
+		}
+
+		if err := w.SetTitle(mw.Title); err != nil {
+			return err
+		}
+
+		if err := w.SetSize(mw.Size.toW()); err != nil {
+			return err
+		}
+
+		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
+		if err != nil {
+			return err
+		}
+		w.ToolBar().SetImageList(imageList)
+
+		if mw.OnDropFiles != nil {
+			w.DropFiles().Attach(mw.OnDropFiles)
+		}
+
+		if mw.AssignTo != nil {
+			*mw.AssignTo = w
+		}
+
+		builder.Defer(func() error {
+			w.Show()
+
+			return nil
+		})
+
+		return nil
+	})
+}
+
+func (mw MainWindow) Run() (int, error) {
+	var w *walk.MainWindow
+
+	if mw.AssignTo == nil {
+		mw.AssignTo = &w
+	}
+
+	if err := mw.Create(); err != nil {
+		return 0, err
+	}
+
+	return (*mw.AssignTo).Run(), nil
+}

+ 7 - 0
common/src/github.com/lxn/walk/declarative/nonwin.go

@@ -0,0 +1,7 @@
+// Copyright 2010 The win 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 declarative

+ 93 - 0
common/src/github.com/lxn/walk/declarative/numberedit.go

@@ -0,0 +1,93 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type NumberEdit struct {
+	AssignTo           **walk.NumberEdit
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Decimals           int
+	Prefix             string
+	Suffix             string
+	Increment          float64
+	MinValue           float64
+	MaxValue           float64
+	Value              Property
+	OnValueChanged     walk.EventHandler
+}
+
+func (ne NumberEdit) Create(builder *Builder) error {
+	w, err := walk.NewNumberEdit(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(ne, w, func() error {
+		if err := w.SetDecimals(ne.Decimals); err != nil {
+			return err
+		}
+
+		if err := w.SetPrefix(ne.Prefix); err != nil {
+			return err
+		}
+		if err := w.SetSuffix(ne.Suffix); err != nil {
+			return err
+		}
+
+		inc := ne.Increment
+		if inc <= 0 {
+			inc = 1
+		}
+
+		if err := w.SetIncrement(inc); err != nil {
+			return err
+		}
+
+		if ne.MinValue != 0 || ne.MaxValue != 0 {
+			if err := w.SetRange(ne.MinValue, ne.MaxValue); err != nil {
+				return err
+			}
+		}
+
+		if ne.OnValueChanged != nil {
+			w.ValueChanged().Attach(ne.OnValueChanged)
+		}
+
+		if ne.AssignTo != nil {
+			*ne.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w NumberEdit) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 68 - 0
common/src/github.com/lxn/walk/declarative/progressbar.go

@@ -0,0 +1,68 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ProgressBar struct {
+	AssignTo           **walk.ProgressBar
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	MinValue           int
+	MaxValue           int
+	Value              int
+	MarqueeMode        bool
+}
+
+func (pb ProgressBar) Create(builder *Builder) error {
+	w, err := walk.NewProgressBar(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(pb, w, func() error {
+		if pb.MaxValue > pb.MinValue {
+			w.SetRange(pb.MinValue, pb.MaxValue)
+		}
+		w.SetValue(pb.Value)
+
+		if err := w.SetMarqueeMode(pb.MarqueeMode); err != nil {
+			return err
+		}
+
+		if pb.AssignTo != nil {
+			*pb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ProgressBar) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 80 - 0
common/src/github.com/lxn/walk/declarative/pushbutton.go

@@ -0,0 +1,80 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type PushButton struct {
+	AssignTo           **walk.PushButton
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Text               Property
+	Image              interface{}
+	ImageAboveText     bool
+	OnClicked          walk.EventHandler
+}
+
+func (pb PushButton) Create(builder *Builder) error {
+	w, err := walk.NewPushButton(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(pb, w, func() error {
+		img := pb.Image
+		if s, ok := img.(string); ok {
+			var err error
+			if img, err = imageFromFile(s); err != nil {
+				return err
+			}
+		}
+		if img != nil {
+			if err := w.SetImage(img.(walk.Image)); err != nil {
+				return err
+			}
+		}
+
+		if err := w.SetImageAboveText(pb.ImageAboveText); err != nil {
+			return err
+		}
+
+		if pb.OnClicked != nil {
+			w.Clicked().Attach(pb.OnClicked)
+		}
+
+		if pb.AssignTo != nil {
+			*pb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w PushButton) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 64 - 0
common/src/github.com/lxn/walk/declarative/radiobutton.go

@@ -0,0 +1,64 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type RadioButton struct {
+	AssignTo           **walk.RadioButton
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Text               Property
+	Value              interface{}
+	OnClicked          walk.EventHandler
+}
+
+func (rb RadioButton) Create(builder *Builder) error {
+	w, err := walk.NewRadioButton(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(rb, w, func() error {
+		w.SetValue(rb.Value)
+
+		if rb.OnClicked != nil {
+			w.Clicked().Attach(rb.OnClicked)
+		}
+
+		if rb.AssignTo != nil {
+			*rb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w RadioButton) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 108 - 0
common/src/github.com/lxn/walk/declarative/radiobuttongroup.go

@@ -0,0 +1,108 @@
+// Copyright 2013 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 declarative
+
+import (
+	"bytes"
+	"errors"
+)
+
+import (
+	"github.com/lxn/walk"
+)
+
+type RadioButtonGroup struct {
+	DataMember string
+	Optional   bool
+	Buttons    []RadioButton
+}
+
+func (rbg RadioButtonGroup) Create(builder *Builder) error {
+	if len(rbg.Buttons) == 0 {
+		return nil
+	}
+
+	var first *walk.RadioButton
+
+	for _, rb := range rbg.Buttons {
+		if first == nil {
+			if rb.AssignTo == nil {
+				rb.AssignTo = &first
+			}
+		}
+
+		if err := rb.Create(builder); err != nil {
+			return err
+		}
+
+		if first == nil {
+			first = *rb.AssignTo
+		}
+	}
+
+	parent := builder.Parent()
+
+	builder.Defer(func() error {
+		group := first.Group()
+
+		validator := newRadioButtonGroupValidator(group, parent)
+
+		for _, rb := range group.Buttons() {
+			prop := rb.AsWindowBase().Property("CheckedValue")
+
+			if err := prop.SetSource(rbg.DataMember); err != nil {
+				return err
+			}
+			if err := prop.SetValidator(validator); err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+
+	return nil
+}
+
+func (w RadioButtonGroup) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return "", false, false, nil, "", Size{}, Size{}, 0, 0, 0, 0, 0, false, nil, nil, nil, nil, nil, nil, nil, nil
+}
+
+type radioButtonGroupValidator struct {
+	group *walk.RadioButtonGroup
+	err   error
+}
+
+func newRadioButtonGroupValidator(group *walk.RadioButtonGroup, parent walk.Container) *radioButtonGroupValidator {
+	b := new(bytes.Buffer)
+
+	if gb, ok := parent.(*walk.GroupBox); ok {
+		b.WriteString(gb.Title())
+	} else {
+		for i, rb := range group.Buttons() {
+			if i > 0 {
+				b.WriteString(", ")
+			}
+
+			b.WriteString(rb.Text())
+		}
+	}
+
+	b.WriteString(": ")
+
+	b.WriteString(tr("A selection is required.", "walk"))
+
+	return &radioButtonGroupValidator{group: group, err: errors.New(b.String())}
+}
+
+func (rbgv *radioButtonGroupValidator) Validate(v interface{}) error {
+	if rbgv.group.CheckedButton() == nil {
+		return rbgv.err
+	}
+
+	return nil
+}

+ 88 - 0
common/src/github.com/lxn/walk/declarative/radiobuttongroupbox.go

@@ -0,0 +1,88 @@
+// Copyright 2013 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type RadioButtonGroupBox struct {
+	AssignTo           **walk.GroupBox
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Title              string
+	DataMember         string
+	DataBinder         DataBinder
+	Layout             Layout
+	Children           []Widget
+	Buttons            []RadioButton
+	Optional           bool
+	Checkable          bool
+	Checked            Property
+}
+
+func (rbgb RadioButtonGroupBox) Create(builder *Builder) error {
+	w, err := walk.NewGroupBox(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(rbgb, w, func() error {
+		if err := w.SetTitle(rbgb.Title); err != nil {
+			return err
+		}
+
+		w.SetCheckable(rbgb.Checkable)
+
+		if err := (RadioButtonGroup{
+			DataMember: rbgb.DataMember,
+			Optional:   rbgb.Optional,
+			Buttons:    rbgb.Buttons,
+		}).Create(builder); err != nil {
+			return err
+		}
+
+		if rbgb.AssignTo != nil {
+			*rbgb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w RadioButtonGroupBox) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (rbgb RadioButtonGroupBox) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return rbgb.DataBinder, rbgb.Layout, nil
+}

+ 68 - 0
common/src/github.com/lxn/walk/declarative/scrollview.go

@@ -0,0 +1,68 @@
+// Copyright 2014 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ScrollView struct {
+	AssignTo           **walk.ScrollView
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	DataBinder         DataBinder
+	Layout             Layout
+	Children           []Widget
+}
+
+func (sv ScrollView) Create(builder *Builder) error {
+	w, err := walk.NewScrollView(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(sv, w, func() error {
+		if sv.AssignTo != nil {
+			*sv.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ScrollView) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (sv ScrollView) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return sv.DataBinder, sv.Layout, sv.Children
+}

+ 68 - 0
common/src/github.com/lxn/walk/declarative/slider.go

@@ -0,0 +1,68 @@
+// Copyright 2016 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Slider struct {
+	AssignTo           **walk.Slider
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	MinValue           int
+	MaxValue           int
+	Value              Property
+	OnValueChanged     walk.EventHandler
+	Orientation        Orientation
+}
+
+func (sl Slider) Create(builder *Builder) error {
+	w, err := walk.NewSliderWithOrientation(builder.Parent(), walk.Orientation(sl.Orientation))
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(sl, w, func() error {
+		if sl.MaxValue > sl.MinValue {
+			w.SetRange(sl.MinValue, sl.MaxValue)
+		}
+
+		if sl.AssignTo != nil {
+			*sl.AssignTo = w
+		}
+
+		if sl.OnValueChanged != nil {
+			w.ValueChanged().Attach(sl.OnValueChanged)
+		}
+
+		return nil
+	})
+}
+
+func (w Slider) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 73 - 0
common/src/github.com/lxn/walk/declarative/spacer.go

@@ -0,0 +1,73 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type HSpacer struct {
+	Name          string
+	MinSize       Size
+	MaxSize       Size
+	StretchFactor int
+	Row           int
+	RowSpan       int
+	Column        int
+	ColumnSpan    int
+	Size          int
+}
+
+func (hs HSpacer) Create(builder *Builder) (err error) {
+	var w *walk.Spacer
+	if hs.Size > 0 {
+		if w, err = walk.NewHSpacerFixed(builder.Parent(), hs.Size); err != nil {
+			return
+		}
+	} else {
+		if w, err = walk.NewHSpacer(builder.Parent()); err != nil {
+			return
+		}
+	}
+
+	return builder.InitWidget(hs, w, nil)
+}
+
+func (hs HSpacer) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return hs.Name, false, false, nil, "", hs.MinSize, hs.MaxSize, hs.StretchFactor, hs.Row, hs.RowSpan, hs.Column, hs.ColumnSpan, false, nil, nil, nil, nil, nil, nil, nil, nil
+}
+
+type VSpacer struct {
+	Name          string
+	MinSize       Size
+	MaxSize       Size
+	StretchFactor int
+	Row           int
+	RowSpan       int
+	Column        int
+	ColumnSpan    int
+	Size          int
+}
+
+func (vs VSpacer) Create(builder *Builder) (err error) {
+	var w *walk.Spacer
+	if vs.Size > 0 {
+		if w, err = walk.NewVSpacerFixed(builder.Parent(), vs.Size); err != nil {
+			return
+		}
+	} else {
+		if w, err = walk.NewVSpacer(builder.Parent()); err != nil {
+			return
+		}
+	}
+
+	return builder.InitWidget(vs, w, nil)
+}
+
+func (vs VSpacer) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return vs.Name, false, false, nil, "", vs.MinSize, vs.MaxSize, vs.StretchFactor, vs.Row, vs.RowSpan, vs.Column, vs.ColumnSpan, false, nil, nil, nil, nil, nil, nil, nil, nil
+}

+ 131 - 0
common/src/github.com/lxn/walk/declarative/splitter.go

@@ -0,0 +1,131 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type HSplitter struct {
+	AssignTo           **walk.Splitter
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	DataBinder         DataBinder
+	Children           []Widget
+	HandleWidth        int
+}
+
+func (s HSplitter) Create(builder *Builder) error {
+	w, err := walk.NewHSplitter(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	w.SetSuspended(true)
+	builder.Defer(func() error {
+		w.SetSuspended(false)
+		return nil
+	})
+
+	return builder.InitWidget(s, w, func() error {
+		if s.HandleWidth > 0 {
+			if err := w.SetHandleWidth(s.HandleWidth); err != nil {
+				return err
+			}
+		}
+
+		if s.AssignTo != nil {
+			*s.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w HSplitter) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (s HSplitter) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return s.DataBinder, nil, s.Children
+}
+
+type VSplitter struct {
+	AssignTo           **walk.Splitter
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	DataBinder         DataBinder
+	Children           []Widget
+	HandleWidth        int
+}
+
+func (s VSplitter) Create(builder *Builder) error {
+	w, err := walk.NewVSplitter(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(s, w, func() error {
+		if s.HandleWidth > 0 {
+			if err := w.SetHandleWidth(s.HandleWidth); err != nil {
+				return err
+			}
+		}
+
+		if s.AssignTo != nil {
+			*s.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w VSplitter) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (s VSplitter) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return s.DataBinder, nil, s.Children
+}

+ 107 - 0
common/src/github.com/lxn/walk/declarative/tableview.go

@@ -0,0 +1,107 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+	"github.com/lxn/win"
+)
+
+type TableView struct {
+	AssignTo                   **walk.TableView
+	Name                       string
+	Enabled                    Property
+	Visible                    Property
+	Font                       Font
+	ToolTipText                Property
+	MinSize                    Size
+	MaxSize                    Size
+	StretchFactor              int
+	Row                        int
+	RowSpan                    int
+	Column                     int
+	ColumnSpan                 int
+	AlwaysConsumeSpace         bool
+	ContextMenuItems           []MenuItem
+	OnKeyDown                  walk.KeyEventHandler
+	OnKeyPress                 walk.KeyEventHandler
+	OnKeyUp                    walk.KeyEventHandler
+	OnMouseDown                walk.MouseEventHandler
+	OnMouseMove                walk.MouseEventHandler
+	OnMouseUp                  walk.MouseEventHandler
+	OnSizeChanged              walk.EventHandler
+	Columns                    []TableViewColumn
+	Model                      interface{}
+	AlternatingRowBGColor      walk.Color
+	CheckBoxes                 bool
+	ItemStateChangedEventDelay int
+	LastColumnStretched        bool
+	ColumnsOrderable           Property
+	ColumnsSizable             Property
+	MultiSelection             bool
+	NotSortableByHeaderClick   bool
+	OnCurrentIndexChanged      walk.EventHandler
+	OnSelectedIndexesChanged   walk.EventHandler
+	OnItemActivated            walk.EventHandler
+}
+
+func (tv TableView) Create(builder *Builder) error {
+	var w *walk.TableView
+	var err error
+	if tv.NotSortableByHeaderClick {
+		w, err = walk.NewTableViewWithStyle(builder.Parent(), win.LVS_NOSORTHEADER)
+	} else {
+		w, err = walk.NewTableView(builder.Parent())
+	}
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tv, w, func() error {
+		for i := range tv.Columns {
+			if err := tv.Columns[i].Create(w); err != nil {
+				return err
+			}
+		}
+
+		if err := w.SetModel(tv.Model); err != nil {
+			return err
+		}
+
+		if tv.AlternatingRowBGColor != 0 {
+			w.SetAlternatingRowBGColor(tv.AlternatingRowBGColor)
+		}
+		w.SetCheckBoxes(tv.CheckBoxes)
+		w.SetItemStateChangedEventDelay(tv.ItemStateChangedEventDelay)
+		if err := w.SetLastColumnStretched(tv.LastColumnStretched); err != nil {
+			return err
+		}
+		if err := w.SetMultiSelection(tv.MultiSelection); err != nil {
+			return err
+		}
+
+		if tv.OnCurrentIndexChanged != nil {
+			w.CurrentIndexChanged().Attach(tv.OnCurrentIndexChanged)
+		}
+		if tv.OnSelectedIndexesChanged != nil {
+			w.SelectedIndexesChanged().Attach(tv.OnSelectedIndexesChanged)
+		}
+		if tv.OnItemActivated != nil {
+			w.ItemActivated().Attach(tv.OnItemActivated)
+		}
+
+		if tv.AssignTo != nil {
+			*tv.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w TableView) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 59 - 0
common/src/github.com/lxn/walk/declarative/tableviewcolumn.go

@@ -0,0 +1,59 @@
+// Copyright 2013 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type Alignment1D uint
+
+const (
+	AlignNear Alignment1D = iota
+	AlignCenter
+	AlignFar
+)
+
+type TableViewColumn struct {
+	Name       string
+	DataMember string
+	Format     string
+	Title      string
+	Alignment  Alignment1D
+	Precision  int
+	Width      int
+	Hidden     bool
+}
+
+func (tvc TableViewColumn) Create(tv *walk.TableView) error {
+	w := walk.NewTableViewColumn()
+
+	if err := w.SetAlignment(walk.Alignment1D(tvc.Alignment)); err != nil {
+		return err
+	}
+	w.SetDataMember(tvc.DataMember)
+	if tvc.Format != "" {
+		if err := w.SetFormat(tvc.Format); err != nil {
+			return err
+		}
+	}
+	if err := w.SetPrecision(tvc.Precision); err != nil {
+		return err
+	}
+	w.SetName(tvc.Name)
+	if err := w.SetTitle(tvc.Title); err != nil {
+		return err
+	}
+	if err := w.SetVisible(!tvc.Hidden); err != nil {
+		return err
+	}
+	if err := w.SetWidth(tvc.Width); err != nil {
+		return err
+	}
+
+	return tv.Columns().Add(w)
+}

+ 69 - 0
common/src/github.com/lxn/walk/declarative/tabpage.go

@@ -0,0 +1,69 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type TabPage struct {
+	AssignTo         **walk.TabPage
+	Name             string
+	Enabled          Property
+	Visible          Property
+	Font             Font
+	ToolTipText      Property
+	MinSize          Size
+	MaxSize          Size
+	ContextMenuItems []MenuItem
+	OnKeyDown        walk.KeyEventHandler
+	OnKeyPress       walk.KeyEventHandler
+	OnKeyUp          walk.KeyEventHandler
+	OnMouseDown      walk.MouseEventHandler
+	OnMouseMove      walk.MouseEventHandler
+	OnMouseUp        walk.MouseEventHandler
+	OnSizeChanged    walk.EventHandler
+	DataBinder       DataBinder
+	Layout           Layout
+	Children         []Widget
+	Image            *walk.Bitmap
+	Title            Property
+	Content          Widget
+}
+
+func (tp TabPage) Create(builder *Builder) error {
+	w, err := walk.NewTabPage()
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tp, w, func() error {
+		if err := w.SetImage(tp.Image); err != nil {
+			return err
+		}
+
+		if tp.Content != nil && len(tp.Children) == 0 {
+			if err := tp.Content.Create(builder); err != nil {
+				return err
+			}
+		}
+
+		if tp.AssignTo != nil {
+			*tp.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w TabPage) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, 0, 0, 0, 0, 0, false, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}
+
+func (tp TabPage) ContainerInfo() (DataBinder, Layout, []Widget) {
+	return tp.DataBinder, tp.Layout, tp.Children
+}

+ 82 - 0
common/src/github.com/lxn/walk/declarative/tabwidget.go

@@ -0,0 +1,82 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type TabWidget struct {
+	AssignTo              **walk.TabWidget
+	Name                  string
+	Enabled               Property
+	Visible               Property
+	Font                  Font
+	ToolTipText           Property
+	MinSize               Size
+	MaxSize               Size
+	StretchFactor         int
+	Row                   int
+	RowSpan               int
+	Column                int
+	ColumnSpan            int
+	AlwaysConsumeSpace    bool
+	ContextMenuItems      []MenuItem
+	OnKeyDown             walk.KeyEventHandler
+	OnKeyPress            walk.KeyEventHandler
+	OnKeyUp               walk.KeyEventHandler
+	OnMouseDown           walk.MouseEventHandler
+	OnMouseMove           walk.MouseEventHandler
+	OnMouseUp             walk.MouseEventHandler
+	OnSizeChanged         walk.EventHandler
+	ContentMargins        Margins
+	ContentMarginsZero    bool
+	Pages                 []TabPage
+	OnCurrentIndexChanged walk.EventHandler
+}
+
+func (tw TabWidget) Create(builder *Builder) error {
+	w, err := walk.NewTabWidget(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tw, w, func() error {
+		for _, tp := range tw.Pages {
+			var wp *walk.TabPage
+			if tp.AssignTo == nil {
+				tp.AssignTo = &wp
+			}
+
+			if tp.Content != nil && len(tp.Children) == 0 {
+				tp.Layout = HBox{Margins: tw.ContentMargins, MarginsZero: tw.ContentMarginsZero}
+			}
+
+			if err := tp.Create(builder); err != nil {
+				return err
+			}
+
+			if err := w.Pages().Add(*tp.AssignTo); err != nil {
+				return err
+			}
+		}
+
+		if tw.OnCurrentIndexChanged != nil {
+			w.CurrentIndexChanged().Attach(tw.OnCurrentIndexChanged)
+		}
+
+		if tw.AssignTo != nil {
+			*tw.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w TabWidget) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 62 - 0
common/src/github.com/lxn/walk/declarative/textedit.go

@@ -0,0 +1,62 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type TextEdit struct {
+	AssignTo           **walk.TextEdit
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Text               Property
+	ReadOnly           Property
+	MaxLength          int
+}
+
+func (te TextEdit) Create(builder *Builder) error {
+	w, err := walk.NewTextEdit(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(te, w, func() error {
+		if te.AssignTo != nil {
+			*te.AssignTo = w
+		}
+
+		if te.MaxLength > 0 {
+			w.SetMaxLength(te.MaxLength)
+		}
+
+		return nil
+	})
+}
+
+func (w TextEdit) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 91 - 0
common/src/github.com/lxn/walk/declarative/toolbar.go

@@ -0,0 +1,91 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ToolBarButtonStyle int
+
+const (
+	ToolBarButtonImageOnly ToolBarButtonStyle = iota
+	ToolBarButtonTextOnly
+	ToolBarButtonImageBeforeText
+	ToolBarButtonImageAboveText
+)
+
+type ToolBar struct {
+	AssignTo           **walk.ToolBar
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Actions            []*walk.Action // Deprecated, use Items instead
+	Items              []MenuItem
+	MaxTextRows        int
+	Orientation        Orientation
+	ButtonStyle        ToolBarButtonStyle
+}
+
+func (tb ToolBar) Create(builder *Builder) error {
+	w, err := walk.NewToolBarWithOrientationAndButtonStyle(builder.Parent(), walk.Orientation(tb.Orientation), walk.ToolBarButtonStyle(tb.ButtonStyle))
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tb, w, func() error {
+		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
+		if err != nil {
+			return err
+		}
+		w.SetImageList(imageList)
+
+		mtr := tb.MaxTextRows
+		if mtr < 1 {
+			mtr = 1
+		}
+		if err := w.SetMaxTextRows(mtr); err != nil {
+			return err
+		}
+
+		if len(tb.Items) > 0 {
+			builder.deferBuildActions(w.Actions(), tb.Items)
+		} else {
+			if err := addToActionList(w.Actions(), tb.Actions); err != nil {
+				return err
+			}
+		}
+
+		if tb.AssignTo != nil {
+			*tb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ToolBar) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 75 - 0
common/src/github.com/lxn/walk/declarative/toolbutton.go

@@ -0,0 +1,75 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ToolButton struct {
+	AssignTo           **walk.ToolButton
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	Image              interface{}
+	Text               Property
+	OnClicked          walk.EventHandler
+}
+
+func (tb ToolButton) Create(builder *Builder) error {
+	w, err := walk.NewToolButton(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tb, w, func() error {
+		img := tb.Image
+		if s, ok := img.(string); ok {
+			var err error
+			if img, err = imageFromFile(s); err != nil {
+				return err
+			}
+		}
+		if img != nil {
+			if err := w.SetImage(img.(walk.Image)); err != nil {
+				return err
+			}
+		}
+
+		if tb.OnClicked != nil {
+			w.Clicked().Attach(tb.OnClicked)
+		}
+
+		if tb.AssignTo != nil {
+			*tb.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w ToolButton) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 75 - 0
common/src/github.com/lxn/walk/declarative/treeview.go

@@ -0,0 +1,75 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type TreeView struct {
+	AssignTo             **walk.TreeView
+	Name                 string
+	Enabled              Property
+	Visible              Property
+	Font                 Font
+	ToolTipText          Property
+	MinSize              Size
+	MaxSize              Size
+	StretchFactor        int
+	Row                  int
+	RowSpan              int
+	Column               int
+	ColumnSpan           int
+	AlwaysConsumeSpace   bool
+	ContextMenuItems     []MenuItem
+	OnKeyDown            walk.KeyEventHandler
+	OnKeyPress           walk.KeyEventHandler
+	OnKeyUp              walk.KeyEventHandler
+	OnMouseDown          walk.MouseEventHandler
+	OnMouseMove          walk.MouseEventHandler
+	OnMouseUp            walk.MouseEventHandler
+	OnSizeChanged        walk.EventHandler
+	Model                walk.TreeModel
+	OnCurrentItemChanged walk.EventHandler
+	OnExpandedChanged    walk.TreeItemEventHandler
+	OnItemActivated      walk.EventHandler
+}
+
+func (tv TreeView) Create(builder *Builder) error {
+	w, err := walk.NewTreeView(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(tv, w, func() error {
+		if err := w.SetModel(tv.Model); err != nil {
+			return err
+		}
+
+		if tv.OnCurrentItemChanged != nil {
+			w.CurrentItemChanged().Attach(tv.OnCurrentItemChanged)
+		}
+
+		if tv.OnExpandedChanged != nil {
+			w.ExpandedChanged().Attach(tv.OnExpandedChanged)
+		}
+
+		if tv.OnItemActivated != nil {
+			w.ItemActivated().Attach(tv.OnItemActivated)
+		}
+
+		if tv.AssignTo != nil {
+			*tv.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w TreeView) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 75 - 0
common/src/github.com/lxn/walk/declarative/validators.go

@@ -0,0 +1,75 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type ValidatorRef struct {
+	Validator walk.Validator
+}
+
+func (vr ValidatorRef) Create() (walk.Validator, error) {
+	return vr.Validator, nil
+}
+
+type Range struct {
+	Min float64
+	Max float64
+}
+
+func (r Range) Create() (walk.Validator, error) {
+	return walk.NewRangeValidator(r.Min, r.Max)
+}
+
+type Regexp struct {
+	Pattern string
+}
+
+func (re Regexp) Create() (walk.Validator, error) {
+	return walk.NewRegexpValidator(re.Pattern)
+}
+
+type SelRequired struct {
+}
+
+func (SelRequired) Create() (walk.Validator, error) {
+	return walk.SelectionRequiredValidator(), nil
+}
+
+type dMultiValidator struct {
+	validators []Validator
+}
+
+func (av dMultiValidator) Create() (walk.Validator, error) {
+	var validators []walk.Validator
+
+	for _, dv := range av.validators {
+		if wv, err := dv.Create(); err != nil {
+			return nil, err
+		} else {
+			validators = append(validators, wv)
+		}
+	}
+
+	return &wMultiValidator{validators}, nil
+}
+
+type wMultiValidator struct {
+	validators []walk.Validator
+}
+
+func (av *wMultiValidator) Validate(v interface{}) error {
+	for _, validator := range av.validators {
+		if err := validator.Validate(v); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 61 - 0
common/src/github.com/lxn/walk/declarative/webview.go

@@ -0,0 +1,61 @@
+// Copyright 2012 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 declarative
+
+import (
+	"github.com/lxn/walk"
+)
+
+type WebView struct {
+	AssignTo           **walk.WebView
+	Name               string
+	Enabled            Property
+	Visible            Property
+	Font               Font
+	ToolTipText        Property
+	MinSize            Size
+	MaxSize            Size
+	StretchFactor      int
+	Row                int
+	RowSpan            int
+	Column             int
+	ColumnSpan         int
+	AlwaysConsumeSpace bool
+	ContextMenuItems   []MenuItem
+	OnKeyDown          walk.KeyEventHandler
+	OnKeyPress         walk.KeyEventHandler
+	OnKeyUp            walk.KeyEventHandler
+	OnMouseDown        walk.MouseEventHandler
+	OnMouseMove        walk.MouseEventHandler
+	OnMouseUp          walk.MouseEventHandler
+	OnSizeChanged      walk.EventHandler
+	URL                Property
+	OnURLChanged       walk.EventHandler
+}
+
+func (wv WebView) Create(builder *Builder) error {
+	w, err := walk.NewWebView(builder.Parent())
+	if err != nil {
+		return err
+	}
+
+	return builder.InitWidget(wv, w, func() error {
+		if wv.OnURLChanged != nil {
+			w.URLChanged().Attach(wv.OnURLChanged)
+		}
+
+		if wv.AssignTo != nil {
+			*wv.AssignTo = w
+		}
+
+		return nil
+	})
+}
+
+func (w WebView) WidgetInfo() (name string, disabled, hidden bool, font *Font, toolTipText string, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, alwaysConsumeSpace bool, contextMenuItems []MenuItem, OnKeyDown walk.KeyEventHandler, OnKeyPress walk.KeyEventHandler, OnKeyUp walk.KeyEventHandler, OnMouseDown walk.MouseEventHandler, OnMouseMove walk.MouseEventHandler, OnMouseUp walk.MouseEventHandler, OnSizeChanged walk.EventHandler) {
+	return w.Name, false, false, &w.Font, "", w.MinSize, w.MaxSize, w.StretchFactor, w.Row, w.RowSpan, w.Column, w.ColumnSpan, w.AlwaysConsumeSpace, w.ContextMenuItems, w.OnKeyDown, w.OnKeyPress, w.OnKeyUp, w.OnMouseDown, w.OnMouseMove, w.OnMouseUp, w.OnSizeChanged
+}

+ 251 - 0
common/src/github.com/lxn/walk/dialog.go

@@ -0,0 +1,251 @@
+// 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 (
+	"syscall"
+	"unsafe"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+const (
+	DlgCmdNone     = 0
+	DlgCmdOK       = win.IDOK
+	DlgCmdCancel   = win.IDCANCEL
+	DlgCmdAbort    = win.IDABORT
+	DlgCmdRetry    = win.IDRETRY
+	DlgCmdIgnore   = win.IDIGNORE
+	DlgCmdYes      = win.IDYES
+	DlgCmdNo       = win.IDNO
+	DlgCmdClose    = win.IDCLOSE
+	DlgCmdHelp     = win.IDHELP
+	DlgCmdTryAgain = win.IDTRYAGAIN
+	DlgCmdContinue = win.IDCONTINUE
+	DlgCmdTimeout  = win.IDTIMEOUT
+)
+
+const dialogWindowClass = `\o/ Walk_Dialog_Class \o/`
+
+func init() {
+	MustRegisterWindowClass(dialogWindowClass)
+}
+
+type dialogish interface {
+	DefaultButton() *PushButton
+	CancelButton() *PushButton
+}
+
+type Dialog struct {
+	FormBase
+	result               int
+	defaultButton        *PushButton
+	cancelButton         *PushButton
+	centerInOwnerWhenRun bool
+}
+
+func NewDialog(owner Form) (*Dialog, error) {
+	return newDialogWithStyle(owner, win.WS_THICKFRAME)
+}
+
+func NewDialogWithFixedSize(owner Form) (*Dialog, error) {
+	return newDialogWithStyle(owner, 0)
+}
+
+func newDialogWithStyle(owner Form, style uint32) (*Dialog, error) {
+	dlg := &Dialog{
+		FormBase: FormBase{
+			owner: owner,
+		},
+	}
+
+	if err := InitWindow(
+		dlg,
+		owner,
+		dialogWindowClass,
+		win.WS_CAPTION|win.WS_SYSMENU|style,
+		0); err != nil {
+		return nil, err
+	}
+
+	succeeded := false
+	defer func() {
+		if !succeeded {
+			dlg.Dispose()
+		}
+	}()
+
+	dlg.centerInOwnerWhenRun = owner != nil
+
+	// This forces display of focus rectangles, as soon as the user starts to type.
+	dlg.SendMessage(win.WM_CHANGEUISTATE, win.UIS_INITIALIZE, 0)
+
+	dlg.result = DlgCmdNone
+
+	succeeded = true
+
+	return dlg, nil
+}
+
+func (dlg *Dialog) DefaultButton() *PushButton {
+	return dlg.defaultButton
+}
+
+func (dlg *Dialog) SetDefaultButton(button *PushButton) error {
+	if button != nil && !win.IsChild(dlg.hWnd, button.hWnd) {
+		return newError("not a descendant of the dialog")
+	}
+
+	succeeded := false
+	if dlg.defaultButton != nil {
+		if err := dlg.defaultButton.setAndClearStyleBits(win.BS_PUSHBUTTON, win.BS_DEFPUSHBUTTON); err != nil {
+			return err
+		}
+		defer func() {
+			if !succeeded {
+				dlg.defaultButton.setAndClearStyleBits(win.BS_DEFPUSHBUTTON, win.BS_PUSHBUTTON)
+			}
+		}()
+	}
+
+	if button != nil {
+		if err := button.setAndClearStyleBits(win.BS_DEFPUSHBUTTON, win.BS_PUSHBUTTON); err != nil {
+			return err
+		}
+	}
+
+	dlg.defaultButton = button
+
+	succeeded = true
+
+	return nil
+}
+
+func (dlg *Dialog) CancelButton() *PushButton {
+	return dlg.cancelButton
+}
+
+func (dlg *Dialog) SetCancelButton(button *PushButton) error {
+	if button != nil && !win.IsChild(dlg.hWnd, button.hWnd) {
+		return newError("not a descendant of the dialog")
+	}
+
+	dlg.cancelButton = button
+
+	return nil
+}
+
+func (dlg *Dialog) Result() int {
+	return dlg.result
+}
+
+func (dlg *Dialog) Accept() {
+	dlg.Close(DlgCmdOK)
+}
+
+func (dlg *Dialog) Cancel() {
+	dlg.Close(DlgCmdCancel)
+}
+
+func (dlg *Dialog) Close(result int) {
+	dlg.result = result
+
+	dlg.FormBase.Close()
+}
+
+func firstFocusableDescendantCallback(hwnd win.HWND, lParam uintptr) uintptr {
+	widget := windowFromHandle(hwnd)
+
+	if widget == nil || !widget.Visible() || !widget.Enabled() {
+		return 1
+	}
+
+	style := uint(win.GetWindowLong(hwnd, win.GWL_STYLE))
+	// FIXME: Ugly workaround for NumberEdit
+	_, isTextSelectable := widget.(textSelectable)
+	if style&win.WS_TABSTOP > 0 || isTextSelectable {
+		hwndPtr := (*win.HWND)(unsafe.Pointer(lParam))
+		*hwndPtr = hwnd
+		return 0
+	}
+
+	return 1
+}
+
+var firstFocusableDescendantCallbackPtr = syscall.NewCallback(firstFocusableDescendantCallback)
+
+func firstFocusableDescendant(container Container) Window {
+	var hwnd win.HWND
+
+	win.EnumChildWindows(container.Handle(), firstFocusableDescendantCallbackPtr, uintptr(unsafe.Pointer(&hwnd)))
+
+	return windowFromHandle(hwnd)
+}
+
+type textSelectable interface {
+	SetTextSelection(start, end int)
+}
+
+func (dlg *Dialog) focusFirstCandidateDescendant() {
+	window := firstFocusableDescendant(dlg)
+	if window == nil {
+		return
+	}
+
+	if err := window.SetFocus(); err != nil {
+		return
+	}
+
+	if textSel, ok := window.(textSelectable); ok {
+		textSel.SetTextSelection(0, -1)
+	}
+}
+
+func (dlg *Dialog) Show() {
+	if dlg.owner != nil {
+		var size Size
+		if layout := dlg.Layout(); layout != nil {
+			size = layout.MinSize()
+			min := dlg.MinSize()
+			size.Width = maxi(size.Width, min.Width)
+			size.Height = maxi(size.Height, min.Height)
+		} else {
+			size = dlg.Size()
+		}
+
+		ob := dlg.owner.Bounds()
+
+		if dlg.centerInOwnerWhenRun {
+			dlg.SetBounds(Rectangle{
+				ob.X + (ob.Width-size.Width)/2,
+				ob.Y + (ob.Height-size.Height)/2,
+				size.Width,
+				size.Height,
+			})
+		}
+	} else {
+		dlg.SetBounds(dlg.Bounds())
+	}
+
+	dlg.FormBase.Show()
+
+	dlg.focusFirstCandidateDescendant()
+}
+
+func (dlg *Dialog) Run() int {
+	dlg.Show()
+
+	if dlg.owner != nil {
+		dlg.owner.SetEnabled(false)
+	}
+
+	dlg.FormBase.Run()
+
+	return dlg.result
+}

+ 73 - 0
common/src/github.com/lxn/walk/dropfilesevent.go

@@ -0,0 +1,73 @@
+// Copyright 2011 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 (
+	"github.com/lxn/win"
+	"syscall"
+)
+
+type DropFilesEventHandler func([]string)
+
+type DropFilesEvent struct {
+	hWnd     win.HWND
+	handlers []DropFilesEventHandler
+}
+
+func (e *DropFilesEvent) Attach(handler DropFilesEventHandler) int {
+	if len(e.handlers) == 0 {
+		win.DragAcceptFiles(e.hWnd, true)
+	}
+	for i, h := range e.handlers {
+		if h == nil {
+			e.handlers[i] = handler
+			return i
+		}
+	}
+
+	e.handlers = append(e.handlers, handler)
+	return len(e.handlers) - 1
+}
+
+func (e *DropFilesEvent) Detach(handle int) {
+	e.handlers[handle] = nil
+	for _, h := range e.handlers {
+		if h != nil {
+			return
+		}
+	}
+	win.DragAcceptFiles(e.hWnd, false)
+}
+
+type DropFilesEventPublisher struct {
+	event DropFilesEvent
+}
+
+func (p *DropFilesEventPublisher) Event(hWnd win.HWND) *DropFilesEvent {
+	p.event.hWnd = hWnd
+	return &p.event
+}
+
+func (p *DropFilesEventPublisher) Publish(hDrop win.HDROP) {
+	var files []string
+
+	n := win.DragQueryFile(hDrop, 0xFFFFFFFF, nil, 0)
+	for i := 0; i < int(n); i++ {
+		bufSize := uint(512)
+		buf := make([]uint16, bufSize)
+		if win.DragQueryFile(hDrop, uint(i), &buf[0], bufSize) > 0 {
+			files = append(files, syscall.UTF16ToString(buf))
+		}
+	}
+	win.DragFinish(hDrop)
+
+	for _, handler := range p.event.handlers {
+		if handler != nil {
+			handler(files)
+		}
+	}
+}

+ 143 - 0
common/src/github.com/lxn/walk/error.go

@@ -0,0 +1,143 @@
+// Copyright 2011 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 (
+	"fmt"
+	"log"
+	"runtime/debug"
+)
+
+import (
+	"github.com/lxn/win"
+)
+
+var (
+	logErrors    bool
+	panicOnError bool
+)
+
+type Error struct {
+	inner   error
+	message string
+	stack   []byte
+}
+
+func (err *Error) Inner() error {
+	return err.inner
+}
+
+func (err *Error) Message() string {
+	if err.message != "" {
+		return err.message
+	}
+
+	if err.inner != nil {
+		if walkErr, ok := err.inner.(*Error); ok {
+			return walkErr.Message()
+		} else {
+			return err.inner.Error()
+		}
+	}
+
+	return ""
+}
+
+func (err *Error) Stack() []byte {
+	return err.stack
+}
+
+func (err *Error) Error() string {
+	return fmt.Sprintf("%s\n\nStack:\n%s", err.Message(), err.stack)
+}
+
+func processErrorNoPanic(err error) error {
+	if logErrors {
+		if walkErr, ok := err.(*Error); ok {
+			log.Print(walkErr.Error())
+		} else {
+			log.Printf("%s\n\nStack:\n%s", err, debug.Stack())
+		}
+	}
+
+	return err
+}
+
+func processError(err error) error {
+	processErrorNoPanic(err)
+
+	if panicOnError {
+		panic(err)
+	}
+
+	return err
+}
+
+func newErr(message string) error {
+	return &Error{message: message, stack: debug.Stack()}
+}
+
+func newError(message string) error {
+	return processError(newErr(message))
+}
+
+func newErrorNoPanic(message string) error {
+	return processErrorNoPanic(newErr(message))
+}
+
+func lastError(win32FuncName string) error {
+	if errno := win.GetLastError(); errno != win.ERROR_SUCCESS {
+		return newError(fmt.Sprintf("%s: Error %d", win32FuncName, errno))
+	}
+
+	return newError(win32FuncName)
+}
+
+func errorFromHRESULT(funcName string, hr win.HRESULT) error {
+	return newError(fmt.Sprintf("%s: Error %d", funcName, hr))
+}
+
+func wrapErr(err error) error {
+	if _, ok := err.(*Error); ok {
+		return err
+	}
+
+	return &Error{inner: err, stack: debug.Stack()}
+}
+
+func wrapErrorNoPanic(err error) error {
+	return processErrorNoPanic(wrapErr(err))
+}
+
+func wrapError(err error) error {
+	return processError(wrapErr(err))
+}
+
+func toErrorNoPanic(x interface{}) error {
+	switch x := x.(type) {
+	case *Error:
+		return x
+
+	case error:
+		return wrapErrorNoPanic(x)
+
+	case string:
+		return newErrorNoPanic(x)
+	}
+
+	return newErrorNoPanic(fmt.Sprintf("Error: %v", x))
+}
+
+func toError(x interface{}) error {
+	err := toErrorNoPanic(x)
+
+	if panicOnError {
+		panic(err)
+	}
+
+	return err
+}

+ 45 - 0
common/src/github.com/lxn/walk/errorevent.go

@@ -0,0 +1,45 @@
+// Copyright 2011 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
+
+type ErrorEventHandler func(err error)
+
+type ErrorEvent struct {
+	handlers []ErrorEventHandler
+}
+
+func (e *ErrorEvent) Attach(handler ErrorEventHandler) int {
+	for i, h := range e.handlers {
+		if h == nil {
+			e.handlers[i] = handler
+			return i
+		}
+	}
+
+	e.handlers = append(e.handlers, handler)
+	return len(e.handlers) - 1
+}
+
+func (e *ErrorEvent) Detach(handle int) {
+	e.handlers[handle] = nil
+}
+
+type ErrorEventPublisher struct {
+	event ErrorEvent
+}
+
+func (p *ErrorEventPublisher) Event() *ErrorEvent {
+	return &p.event
+}
+
+func (p *ErrorEventPublisher) Publish(err error) {
+	for _, handler := range p.event.handlers {
+		if handler != nil {
+			handler(err)
+		}
+	}
+}

+ 45 - 0
common/src/github.com/lxn/walk/event.go

@@ -0,0 +1,45 @@
+// Copyright 2011 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
+
+type EventHandler func()
+
+type Event struct {
+	handlers []EventHandler
+}
+
+func (e *Event) Attach(handler EventHandler) int {
+	for i, h := range e.handlers {
+		if h == nil {
+			e.handlers[i] = handler
+			return i
+		}
+	}
+
+	e.handlers = append(e.handlers, handler)
+	return len(e.handlers) - 1
+}
+
+func (e *Event) Detach(handle int) {
+	e.handlers[handle] = nil
+}
+
+type EventPublisher struct {
+	event Event
+}
+
+func (p *EventPublisher) Event() *Event {
+	return &p.event
+}
+
+func (p *EventPublisher) Publish() {
+	for _, handler := range p.event.handlers {
+		if handler != nil {
+			handler()
+		}
+	}
+}

+ 186 - 0
common/src/github.com/lxn/walk/examples/actions/actions.go

@@ -0,0 +1,186 @@
+// Copyright 2013 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.
+
+package main
+
+import (
+	"log"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+var isSpecialMode = walk.NewMutableCondition()
+
+type MyMainWindow struct {
+	*walk.MainWindow
+}
+
+func main() {
+	MustRegisterCondition("isSpecialMode", isSpecialMode)
+
+	mw := new(MyMainWindow)
+
+	var openAction, showAboutBoxAction *walk.Action
+	var recentMenu *walk.Menu
+	var toggleSpecialModePB *walk.PushButton
+
+	if err := (MainWindow{
+		AssignTo: &mw.MainWindow,
+		Title:    "Walk Actions Example",
+		MenuItems: []MenuItem{
+			Menu{
+				Text: "&File",
+				Items: []MenuItem{
+					Action{
+						AssignTo:    &openAction,
+						Text:        "&Open",
+						Image:       "../img/open.png",
+						Enabled:     Bind("enabledCB.Checked"),
+						Visible:     Bind("openVisibleCB.Checked"),
+						Shortcut:    Shortcut{walk.ModControl, walk.KeyO},
+						OnTriggered: mw.openAction_Triggered,
+					},
+					Menu{
+						AssignTo: &recentMenu,
+						Text:     "Recent",
+					},
+					Separator{},
+					Action{
+						Text:        "E&xit",
+						OnTriggered: func() { mw.Close() },
+					},
+				},
+			},
+			Menu{
+				Text: "&Help",
+				Items: []MenuItem{
+					Action{
+						AssignTo:    &showAboutBoxAction,
+						Text:        "About",
+						OnTriggered: mw.showAboutBoxAction_Triggered,
+					},
+				},
+			},
+		},
+		ToolBar: ToolBar{
+			ButtonStyle: ToolBarButtonImageBeforeText,
+			Items: []MenuItem{
+				ActionRef{&openAction},
+				Menu{
+					Text:  "New A",
+					Image: "../img/document-new.png",
+					Items: []MenuItem{
+						Action{
+							Text:        "A",
+							OnTriggered: mw.newAction_Triggered,
+						},
+						Action{
+							Text:        "B",
+							OnTriggered: mw.newAction_Triggered,
+						},
+						Action{
+							Text:        "C",
+							OnTriggered: mw.newAction_Triggered,
+						},
+					},
+					OnTriggered: mw.newAction_Triggered,
+				},
+				Separator{},
+				Menu{
+					Text:  "View",
+					Image: "../img/document-properties.png",
+					Items: []MenuItem{
+						Action{
+							Text:        "X",
+							OnTriggered: mw.changeViewAction_Triggered,
+						},
+						Action{
+							Text:        "Y",
+							OnTriggered: mw.changeViewAction_Triggered,
+						},
+						Action{
+							Text:        "Z",
+							OnTriggered: mw.changeViewAction_Triggered,
+						},
+					},
+				},
+				Separator{},
+				Action{
+					Text:        "Special",
+					Image:       "../img/system-shutdown.png",
+					Enabled:     Bind("isSpecialMode && enabledCB.Checked"),
+					OnTriggered: mw.specialAction_Triggered,
+				},
+			},
+		},
+		ContextMenuItems: []MenuItem{
+			ActionRef{&showAboutBoxAction},
+		},
+		MinSize: Size{300, 200},
+		Layout:  VBox{},
+		Children: []Widget{
+			CheckBox{
+				Name:    "enabledCB",
+				Text:    "Open / Special Enabled",
+				Checked: true,
+			},
+			CheckBox{
+				Name:    "openVisibleCB",
+				Text:    "Open Visible",
+				Checked: true,
+			},
+			PushButton{
+				AssignTo: &toggleSpecialModePB,
+				Text:     "Enable Special Mode",
+				OnClicked: func() {
+					isSpecialMode.SetSatisfied(!isSpecialMode.Satisfied())
+
+					if isSpecialMode.Satisfied() {
+						toggleSpecialModePB.SetText("Disable Special Mode")
+					} else {
+						toggleSpecialModePB.SetText("Enable Special Mode")
+					}
+				},
+			},
+		},
+	}.Create()); err != nil {
+		log.Fatal(err)
+	}
+
+	addRecentFileActions := func(texts ...string) {
+		for _, text := range texts {
+			a := walk.NewAction()
+			a.SetText(text)
+			a.Triggered().Attach(mw.openAction_Triggered)
+			recentMenu.Actions().Add(a)
+		}
+	}
+
+	addRecentFileActions("Foo", "Bar", "Baz")
+
+	mw.Run()
+}
+
+func (mw *MyMainWindow) openAction_Triggered() {
+	walk.MsgBox(mw, "Open", "Pretend to open a file...", walk.MsgBoxIconInformation)
+}
+
+func (mw *MyMainWindow) newAction_Triggered() {
+	walk.MsgBox(mw, "New", "Newing something up... or not.", walk.MsgBoxIconInformation)
+}
+
+func (mw *MyMainWindow) changeViewAction_Triggered() {
+	walk.MsgBox(mw, "Change View", "By now you may have guessed it. Nothing changed.", walk.MsgBoxIconInformation)
+}
+
+func (mw *MyMainWindow) showAboutBoxAction_Triggered() {
+	walk.MsgBox(mw, "About", "Walk Actions Example", walk.MsgBoxIconInformation)
+}
+
+func (mw *MyMainWindow) specialAction_Triggered() {
+	walk.MsgBox(mw, "Special", "Nothing to see here.", walk.MsgBoxIconInformation)
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/actions/actions.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/actions/rsrc.syso


+ 49 - 0
common/src/github.com/lxn/walk/examples/clipboard/clipboard.go

@@ -0,0 +1,49 @@
+// Copyright 2013 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.
+
+package main
+
+import (
+	"log"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	var te *walk.TextEdit
+
+	if _, err := (MainWindow{
+		Title:   "Walk Clipboard Example",
+		MinSize: Size{300, 200},
+		Layout:  VBox{},
+		Children: []Widget{
+			PushButton{
+				Text: "Copy",
+				OnClicked: func() {
+					if err := walk.Clipboard().SetText(te.Text()); err != nil {
+						log.Print("Copy: ", err)
+					}
+				},
+			},
+			PushButton{
+				Text: "Paste",
+				OnClicked: func() {
+					if text, err := walk.Clipboard().Text(); err != nil {
+						log.Print("Paste: ", err)
+					} else {
+						te.SetText(text)
+					}
+				},
+			},
+			TextEdit{
+				AssignTo: &te,
+			},
+		},
+	}).Run(); err != nil {
+		log.Fatal(err)
+	}
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/clipboard/clipboard.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/clipboard/rsrc.syso


+ 253 - 0
common/src/github.com/lxn/walk/examples/databinding/databinding.go

@@ -0,0 +1,253 @@
+// Copyright 2013 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.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"time"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	var mw *walk.MainWindow
+	var outTE *walk.TextEdit
+
+	animal := new(Animal)
+
+	if _, err := (MainWindow{
+		AssignTo: &mw,
+		Title:    "Walk Data Binding Example",
+		MinSize:  Size{300, 200},
+		Layout:   VBox{},
+		Children: []Widget{
+			PushButton{
+				Text: "Edit Animal",
+				OnClicked: func() {
+					if cmd, err := RunAnimalDialog(mw, animal); err != nil {
+						log.Print(err)
+					} else if cmd == walk.DlgCmdOK {
+						outTE.SetText(fmt.Sprintf("%+v", animal))
+					}
+				},
+			},
+			Label{
+				Text: "animal:",
+			},
+			TextEdit{
+				AssignTo: &outTE,
+				ReadOnly: true,
+				Text:     fmt.Sprintf("%+v", animal),
+			},
+		},
+	}.Run()); err != nil {
+		log.Fatal(err)
+	}
+}
+
+type Animal struct {
+	Name          string
+	ArrivalDate   time.Time
+	SpeciesId     int
+	Speed         int
+	Sex           Sex
+	Weight        float64
+	PreferredFood string
+	Domesticated  bool
+	Remarks       string
+	Patience      time.Duration
+}
+
+func (a *Animal) PatienceField() *DurationField {
+	return &DurationField{&a.Patience}
+}
+
+type Species struct {
+	Id   int
+	Name string
+}
+
+func KnownSpecies() []*Species {
+	return []*Species{
+		{1, "Dog"},
+		{2, "Cat"},
+		{3, "Bird"},
+		{4, "Fish"},
+		{5, "Elephant"},
+	}
+}
+
+type DurationField struct {
+	p *time.Duration
+}
+
+func (*DurationField) CanSet() bool       { return true }
+func (f *DurationField) Get() interface{} { return f.p.String() }
+func (f *DurationField) Set(v interface{}) error {
+	x, err := time.ParseDuration(v.(string))
+	if err == nil {
+		*f.p = x
+	}
+	return err
+}
+
+type Sex byte
+
+const (
+	SexMale Sex = 1 + iota
+	SexFemale
+	SexHermaphrodite
+)
+
+func RunAnimalDialog(owner walk.Form, animal *Animal) (int, error) {
+	var dlg *walk.Dialog
+	var db *walk.DataBinder
+	var ep walk.ErrorPresenter
+	var acceptPB, cancelPB *walk.PushButton
+
+	return Dialog{
+		AssignTo:      &dlg,
+		Title:         "Animal Details",
+		DefaultButton: &acceptPB,
+		CancelButton:  &cancelPB,
+		DataBinder: DataBinder{
+			AssignTo:       &db,
+			DataSource:     animal,
+			ErrorPresenter: ErrorPresenterRef{&ep},
+		},
+		MinSize: Size{300, 300},
+		Layout:  VBox{},
+		Children: []Widget{
+			Composite{
+				Layout: Grid{Columns: 2},
+				Children: []Widget{
+					Label{
+						Text: "Name:",
+					},
+					LineEdit{
+						Text: Bind("Name"),
+					},
+
+					Label{
+						Text: "Arrival Date:",
+					},
+					DateEdit{
+						Date: Bind("ArrivalDate"),
+					},
+
+					Label{
+						Text: "Species:",
+					},
+					ComboBox{
+						Value:         Bind("SpeciesId", SelRequired{}),
+						BindingMember: "Id",
+						DisplayMember: "Name",
+						Model:         KnownSpecies(),
+					},
+
+					Label{
+						Text: "Speed:",
+					},
+					Slider{
+						Value: Bind("Speed"),
+					},
+
+					RadioButtonGroupBox{
+						ColumnSpan: 2,
+						Title:      "Sex",
+						Layout:     HBox{},
+						DataMember: "Sex",
+						Buttons: []RadioButton{
+							{Text: "Male", Value: SexMale},
+							{Text: "Female", Value: SexFemale},
+							{Text: "Hermaphrodite", Value: SexHermaphrodite},
+						},
+					},
+
+					Label{
+						Text: "Weight:",
+					},
+					NumberEdit{
+						Value:    Bind("Weight", Range{0.01, 9999.99}),
+						Suffix:   " kg",
+						Decimals: 2,
+					},
+
+					Label{
+						Text: "Preferred Food:",
+					},
+					ComboBox{
+						Editable: true,
+						Value:    Bind("PreferredFood"),
+						Model:    []string{"Fruit", "Grass", "Fish", "Meat"},
+					},
+
+					Label{
+						Text: "Domesticated:",
+					},
+					CheckBox{
+						Checked: Bind("Domesticated"),
+					},
+
+					VSpacer{
+						ColumnSpan: 2,
+						Size:       8,
+					},
+
+					Label{
+						ColumnSpan: 2,
+						Text:       "Remarks:",
+					},
+					TextEdit{
+						ColumnSpan: 2,
+						MinSize:    Size{100, 50},
+						Text:       Bind("Remarks"),
+					},
+
+					Label{
+						ColumnSpan: 2,
+						Text:       "Patience:",
+					},
+					LineEdit{
+						ColumnSpan: 2,
+						Text:       Bind("PatienceField"),
+					},
+
+					LineErrorPresenter{
+						AssignTo:   &ep,
+						ColumnSpan: 2,
+					},
+				},
+			},
+			Composite{
+				Layout: HBox{},
+				Children: []Widget{
+					HSpacer{},
+					PushButton{
+						AssignTo: &acceptPB,
+						Text:     "OK",
+						OnClicked: func() {
+							if err := db.Submit(); err != nil {
+								log.Print(err)
+								return
+							}
+
+							dlg.Accept()
+						},
+					},
+					PushButton{
+						AssignTo:  &cancelPB,
+						Text:      "Cancel",
+						OnClicked: func() { dlg.Cancel() },
+					},
+				},
+			},
+		},
+	}.Run(owner)
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/databinding/databinding.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/databinding/rsrc.syso


+ 160 - 0
common/src/github.com/lxn/walk/examples/drawing/drawing.go

@@ -0,0 +1,160 @@
+// 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.
+
+package main
+
+import (
+	"log"
+	"math"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	mw := new(MyMainWindow)
+
+	if _, err := (MainWindow{
+		AssignTo: &mw.MainWindow,
+		Title:    "Walk Drawing Example",
+		MinSize:  Size{320, 240},
+		Size:     Size{800, 600},
+		Layout:   VBox{MarginsZero: true},
+		Children: []Widget{
+			CustomWidget{
+				AssignTo:            &mw.paintWidget,
+				ClearsBackground:    true,
+				InvalidatesOnResize: true,
+				Paint:               mw.drawStuff,
+			},
+		},
+	}).Run(); err != nil {
+		log.Fatal(err)
+	}
+}
+
+type MyMainWindow struct {
+	*walk.MainWindow
+	paintWidget *walk.CustomWidget
+}
+
+func (mw *MyMainWindow) drawStuff(canvas *walk.Canvas, updateBounds walk.Rectangle) error {
+	bmp, err := createBitmap()
+	if err != nil {
+		return err
+	}
+	defer bmp.Dispose()
+
+	bounds := mw.paintWidget.ClientBounds()
+
+	rectPen, err := walk.NewCosmeticPen(walk.PenSolid, walk.RGB(255, 0, 0))
+	if err != nil {
+		return err
+	}
+	defer rectPen.Dispose()
+
+	if err := canvas.DrawRectangle(rectPen, bounds); err != nil {
+		return err
+	}
+
+	ellipseBrush, err := walk.NewHatchBrush(walk.RGB(0, 255, 0), walk.HatchCross)
+	if err != nil {
+		return err
+	}
+	defer ellipseBrush.Dispose()
+
+	if err := canvas.FillEllipse(ellipseBrush, bounds); err != nil {
+		return err
+	}
+
+	linesBrush, err := walk.NewSolidColorBrush(walk.RGB(0, 0, 255))
+	if err != nil {
+		return err
+	}
+	defer linesBrush.Dispose()
+
+	linesPen, err := walk.NewGeometricPen(walk.PenDash, 8, linesBrush)
+	if err != nil {
+		return err
+	}
+	defer linesPen.Dispose()
+
+	if err := canvas.DrawLine(linesPen, walk.Point{bounds.X, bounds.Y}, walk.Point{bounds.Width, bounds.Height}); err != nil {
+		return err
+	}
+	if err := canvas.DrawLine(linesPen, walk.Point{bounds.X, bounds.Height}, walk.Point{bounds.Width, bounds.Y}); err != nil {
+		return err
+	}
+
+	points := make([]walk.Point, 10)
+	dx := bounds.Width / (len(points) - 1)
+	for i := range points {
+		points[i].X = i * dx
+		points[i].Y = int(float64(bounds.Height) / math.Pow(float64(bounds.Width/2), 2) * math.Pow(float64(i*dx-bounds.Width/2), 2))
+	}
+	if err := canvas.DrawPolyline(linesPen, points); err != nil {
+		return err
+	}
+
+	bmpSize := bmp.Size()
+	if err := canvas.DrawImage(bmp, walk.Point{(bounds.Width - bmpSize.Width) / 2, (bounds.Height - bmpSize.Height) / 2}); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func createBitmap() (*walk.Bitmap, error) {
+	bounds := walk.Rectangle{Width: 200, Height: 200}
+
+	bmp, err := walk.NewBitmap(bounds.Size())
+	if err != nil {
+		return nil, err
+	}
+
+	succeeded := false
+	defer func() {
+		if !succeeded {
+			bmp.Dispose()
+		}
+	}()
+
+	canvas, err := walk.NewCanvasFromImage(bmp)
+	if err != nil {
+		return nil, err
+	}
+	defer canvas.Dispose()
+
+	brushBmp, err := walk.NewBitmapFromFile("../img/plus.png")
+	if err != nil {
+		return nil, err
+	}
+	defer brushBmp.Dispose()
+
+	brush, err := walk.NewBitmapBrush(brushBmp)
+	if err != nil {
+		return nil, err
+	}
+	defer brush.Dispose()
+
+	if err := canvas.FillRectangle(brush, bounds); err != nil {
+		return nil, err
+	}
+
+	font, err := walk.NewFont("Times New Roman", 40, walk.FontBold|walk.FontItalic)
+	if err != nil {
+		return nil, err
+	}
+	defer font.Dispose()
+
+	if err := canvas.DrawText("Walk Drawing Example", font, walk.RGB(0, 0, 0), bounds, walk.TextWordbreak); err != nil {
+		return nil, err
+	}
+
+	succeeded = true
+
+	return bmp, nil
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/drawing/drawing.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/drawing/rsrc.syso


+ 31 - 0
common/src/github.com/lxn/walk/examples/dropfiles/dropfiles.go

@@ -0,0 +1,31 @@
+// Copyright 2013 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.
+
+package main
+
+import (
+	"strings"
+
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	var textEdit *walk.TextEdit
+	MainWindow{
+		Title:   "Walk DropFiles Example",
+		MinSize: Size{320, 240},
+		Layout:  VBox{},
+		OnDropFiles: func(files []string) {
+			textEdit.SetText(strings.Join(files, "\r\n"))
+		},
+		Children: []Widget{
+			TextEdit{
+				AssignTo: &textEdit,
+				ReadOnly: true,
+				Text:     "Drop files here, from windows explorer...",
+			},
+		},
+	}.Run()
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/dropfiles/dropfiles.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/dropfiles/rsrc.syso


+ 117 - 0
common/src/github.com/lxn/walk/examples/externalwidgets/externalwidgets.go

@@ -0,0 +1,117 @@
+// Copyright 2012 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.
+
+package main
+
+import (
+	"log"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+	"github.com/lxn/win"
+)
+
+const myWidgetWindowClass = "MyWidget Class"
+
+func init() {
+	walk.MustRegisterWindowClass(myWidgetWindowClass)
+}
+
+func main() {
+	var mw *walk.MainWindow
+
+	if err := (MainWindow{
+		AssignTo: &mw,
+		Title:    "Walk External Widgets Example",
+		Size:     Size{400, 300},
+		Layout:   HBox{},
+	}).Create(); err != nil {
+		log.Fatal(err)
+	}
+
+	for _, name := range []string{"a", "b", "c"} {
+		if w, err := NewMyWidget(mw); err != nil {
+			log.Fatal(err)
+		} else {
+			w.SetName(name)
+		}
+	}
+
+	mpb, err := NewMyPushButton(mw)
+	if err != nil {
+		log.Fatal(err)
+	}
+	mpb.SetText("MyPushButton")
+
+	mw.Run()
+}
+
+type MyWidget struct {
+	walk.WidgetBase
+}
+
+func NewMyWidget(parent walk.Container) (*MyWidget, error) {
+	w := new(MyWidget)
+
+	if err := walk.InitWidget(
+		w,
+		parent,
+		myWidgetWindowClass,
+		win.WS_VISIBLE,
+		0); err != nil {
+
+		return nil, err
+	}
+
+	bg, err := walk.NewSolidColorBrush(walk.RGB(0, 255, 0))
+	if err != nil {
+		return nil, err
+	}
+	w.SetBackground(bg)
+
+	return w, nil
+}
+
+func (*MyWidget) MinSizeHint() walk.Size {
+	return walk.Size{50, 50}
+}
+
+func (w *MyWidget) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_LBUTTONDOWN:
+		log.Printf("%s: WM_LBUTTONDOWN", w.Name())
+	}
+
+	return w.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
+}
+
+type MyPushButton struct {
+	*walk.PushButton
+}
+
+func NewMyPushButton(parent walk.Container) (*MyPushButton, error) {
+	pb, err := walk.NewPushButton(parent)
+	if err != nil {
+		return nil, err
+	}
+
+	mpb := &MyPushButton{pb}
+
+	if err := walk.InitWrapperWindow(mpb); err != nil {
+		return nil, err
+	}
+
+	return mpb, nil
+}
+
+func (mpb *MyPushButton) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
+	switch msg {
+	case win.WM_LBUTTONDOWN:
+		log.Printf("%s: WM_LBUTTONDOWN", mpb.Text())
+	}
+
+	return mpb.PushButton.WndProc(hwnd, msg, wParam, lParam)
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/externalwidgets/externalwidgets.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/externalwidgets/rsrc.syso


+ 299 - 0
common/src/github.com/lxn/walk/examples/filebrowser/filebrowser.go

@@ -0,0 +1,299 @@
+// Copyright 2011 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.
+
+package main
+
+import (
+	"log"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+type Directory struct {
+	name     string
+	parent   *Directory
+	children []*Directory
+}
+
+func NewDirectory(name string, parent *Directory) *Directory {
+	return &Directory{name: name, parent: parent}
+}
+
+var _ walk.TreeItem = new(Directory)
+
+func (d *Directory) Text() string {
+	return d.name
+}
+
+func (d *Directory) Parent() walk.TreeItem {
+	if d.parent == nil {
+		// We can't simply return d.parent in this case, because the interface
+		// value then would not be nil.
+		return nil
+	}
+
+	return d.parent
+}
+
+func (d *Directory) ChildCount() int {
+	if d.children == nil {
+		// It seems this is the first time our child count is checked, so we
+		// use the opportunity to populate our direct children.
+		if err := d.ResetChildren(); err != nil {
+			log.Print(err)
+		}
+	}
+
+	return len(d.children)
+}
+
+func (d *Directory) ChildAt(index int) walk.TreeItem {
+	return d.children[index]
+}
+
+func (d *Directory) Image() interface{} {
+	return d.Path()
+}
+
+func (d *Directory) ResetChildren() error {
+	d.children = nil
+
+	dirPath := d.Path()
+
+	if err := filepath.Walk(d.Path(), func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			if info == nil {
+				return filepath.SkipDir
+			}
+		}
+
+		name := info.Name()
+
+		if !info.IsDir() || path == dirPath || shouldExclude(name) {
+			return nil
+		}
+
+		d.children = append(d.children, NewDirectory(name, d))
+
+		return filepath.SkipDir
+	}); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (d *Directory) Path() string {
+	elems := []string{d.name}
+
+	dir, _ := d.Parent().(*Directory)
+
+	for dir != nil {
+		elems = append([]string{dir.name}, elems...)
+		dir, _ = dir.Parent().(*Directory)
+	}
+
+	return filepath.Join(elems...)
+}
+
+type DirectoryTreeModel struct {
+	walk.TreeModelBase
+	roots []*Directory
+}
+
+var _ walk.TreeModel = new(DirectoryTreeModel)
+
+func NewDirectoryTreeModel() (*DirectoryTreeModel, error) {
+	model := new(DirectoryTreeModel)
+
+	drives, err := walk.DriveNames()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, drive := range drives {
+		switch drive {
+		case "A:\\", "B:\\":
+			continue
+		}
+
+		model.roots = append(model.roots, NewDirectory(drive, nil))
+	}
+
+	return model, nil
+}
+
+func (*DirectoryTreeModel) LazyPopulation() bool {
+	// We don't want to eagerly populate our tree view with the whole file system.
+	return true
+}
+
+func (m *DirectoryTreeModel) RootCount() int {
+	return len(m.roots)
+}
+
+func (m *DirectoryTreeModel) RootAt(index int) walk.TreeItem {
+	return m.roots[index]
+}
+
+type FileInfo struct {
+	Name     string
+	Size     int64
+	Modified time.Time
+}
+
+type FileInfoModel struct {
+	walk.SortedReflectTableModelBase
+	dirPath string
+	items   []*FileInfo
+}
+
+var _ walk.ReflectTableModel = new(FileInfoModel)
+
+func NewFileInfoModel() *FileInfoModel {
+	return new(FileInfoModel)
+}
+
+func (m *FileInfoModel) Items() interface{} {
+	return m.items
+}
+
+func (m *FileInfoModel) SetDirPath(dirPath string) error {
+	m.dirPath = dirPath
+	m.items = nil
+
+	if err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			if info == nil {
+				return filepath.SkipDir
+			}
+		}
+
+		name := info.Name()
+
+		if path == dirPath || shouldExclude(name) {
+			return nil
+		}
+
+		item := &FileInfo{
+			Name:     name,
+			Size:     info.Size(),
+			Modified: info.ModTime(),
+		}
+
+		m.items = append(m.items, item)
+
+		if info.IsDir() {
+			return filepath.SkipDir
+		}
+
+		return nil
+	}); err != nil {
+		return err
+	}
+
+	m.PublishRowsReset()
+
+	return nil
+}
+
+func (m *FileInfoModel) Image(row int) interface{} {
+	return filepath.Join(m.dirPath, m.items[row].Name)
+}
+
+func shouldExclude(name string) bool {
+	switch name {
+	case "System Volume Information", "pagefile.sys", "swapfile.sys":
+		return true
+	}
+
+	return false
+}
+
+func main() {
+	var mainWindow *walk.MainWindow
+	var treeView *walk.TreeView
+	var tableView *walk.TableView
+	var webView *walk.WebView
+
+	treeModel, err := NewDirectoryTreeModel()
+	if err != nil {
+		log.Fatal(err)
+	}
+	tableModel := NewFileInfoModel()
+
+	if err := (MainWindow{
+		AssignTo: &mainWindow,
+		Title:    "Walk File Browser Example",
+		MinSize:  Size{600, 400},
+		Size:     Size{1024, 640},
+		Layout:   HBox{MarginsZero: true},
+		Children: []Widget{
+			HSplitter{
+				Children: []Widget{
+					TreeView{
+						AssignTo: &treeView,
+						Model:    treeModel,
+						OnCurrentItemChanged: func() {
+							dir := treeView.CurrentItem().(*Directory)
+							if err := tableModel.SetDirPath(dir.Path()); err != nil {
+								walk.MsgBox(
+									mainWindow,
+									"Error",
+									err.Error(),
+									walk.MsgBoxOK|walk.MsgBoxIconError)
+							}
+						},
+					},
+					TableView{
+						AssignTo:      &tableView,
+						StretchFactor: 2,
+						Columns: []TableViewColumn{
+							TableViewColumn{
+								DataMember: "Name",
+								Width:      192,
+							},
+							TableViewColumn{
+								DataMember: "Size",
+								Format:     "%d",
+								Alignment:  AlignFar,
+								Width:      64,
+							},
+							TableViewColumn{
+								DataMember: "Modified",
+								Format:     "2006-01-02 15:04:05",
+								Width:      120,
+							},
+						},
+						Model: tableModel,
+						OnCurrentIndexChanged: func() {
+							var url string
+							if index := tableView.CurrentIndex(); index > -1 {
+								name := tableModel.items[index].Name
+								dir := treeView.CurrentItem().(*Directory)
+								url = filepath.Join(dir.Path(), name)
+							}
+
+							webView.SetURL(url)
+						},
+					},
+					WebView{
+						AssignTo:      &webView,
+						StretchFactor: 2,
+					},
+				},
+			},
+		},
+	}.Create()); err != nil {
+		log.Fatal(err)
+	}
+
+	mainWindow.Run()
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/filebrowser/filebrowser.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/filebrowser/rsrc.syso


+ 14 - 0
common/src/github.com/lxn/walk/examples/imageicon/imageicon.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

+ 98 - 0
common/src/github.com/lxn/walk/examples/imageicon/main.go

@@ -0,0 +1,98 @@
+// Copyright 2012 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.
+
+package main
+
+import (
+	"image"
+	"image/color"
+	"image/draw"
+	"log"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	var mw *walk.MainWindow
+	var windowIcon *walk.Icon
+	counter := 0
+
+	if _, err := (MainWindow{
+		AssignTo: &mw,
+		Title:    "Walk Image Icon Example",
+		Layout:   HBox{},
+		Children: []Widget{
+			HSpacer{},
+			PushButton{
+				Text: "Push me",
+				OnClicked: func() {
+					ic, err := walk.NewIconFromImage(makeDigitImage(counter))
+					if err != nil {
+						return
+					}
+					counter++
+					mw.SetIcon(ic)
+					if windowIcon != nil {
+						windowIcon.Dispose()
+					}
+					windowIcon = ic
+				},
+			},
+			HSpacer{},
+		},
+	}.Run()); err != nil {
+		log.Fatal(err)
+	}
+}
+
+//  A
+// F B
+//  G
+// E C
+//  D
+var hexdigits = []int{0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}
+
+//0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
+
+type seg struct {
+	sx, sy int
+	dx, dy int
+}
+
+var segments = []seg{
+	{0, 0, 1, 0},
+	{1, 0, 0, 1},
+	{1, 1, 0, 1},
+	{0, 2, 1, 0},
+	{0, 1, 0, 1},
+	{0, 0, 0, 1},
+	{0, 1, 1, 0},
+}
+
+func digit(im draw.Image, col color.Color, x, y, size, digit int) {
+	n := hexdigits[digit]
+	for _, s := range segments {
+		if n&1 != 0 {
+			xx, yy := x+s.sx*size, y+s.sy*size
+			for i := 0; i <= size; i++ {
+				im.Set(xx, yy, col)
+				xx += s.dx
+				yy += s.dy
+			}
+		}
+		n >>= 1
+	}
+}
+
+func makeDigitImage(n int) image.Image {
+	im := image.NewRGBA(image.Rect(0, 0, 16, 16))
+	for p := 11; p >= 0; p -= 5 {
+		digit(im, color.Black, p, 5, 3, n%10)
+		n /= 10
+	}
+	return im
+}

BIN
common/src/github.com/lxn/walk/examples/imageicon/rsrc.syso


+ 153 - 0
common/src/github.com/lxn/walk/examples/imageviewer/imageviewer.go

@@ -0,0 +1,153 @@
+// 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.
+
+package main
+
+import (
+	"log"
+	"path"
+	"strings"
+)
+
+import (
+	"github.com/lxn/walk"
+	. "github.com/lxn/walk/declarative"
+)
+
+func main() {
+	mw := new(MyMainWindow)
+	var openAction *walk.Action
+
+	if _, err := (MainWindow{
+		AssignTo: &mw.MainWindow,
+		Title:    "Walk Image Viewer Example",
+		MenuItems: []MenuItem{
+			Menu{
+				Text: "&File",
+				Items: []MenuItem{
+					Action{
+						AssignTo:    &openAction,
+						Text:        "&Open",
+						Image:       "../img/open.png",
+						OnTriggered: mw.openAction_Triggered,
+					},
+					Separator{},
+					Action{
+						Text:        "Exit",
+						OnTriggered: func() { mw.Close() },
+					},
+				},
+			},
+			Menu{
+				Text: "&Help",
+				Items: []MenuItem{
+					Action{
+						Text:        "About",
+						OnTriggered: mw.aboutAction_Triggered,
+					},
+				},
+			},
+		},
+		ToolBarItems: []MenuItem{
+			ActionRef{&openAction},
+		},
+		MinSize: Size{320, 240},
+		Size:    Size{800, 600},
+		Layout:  VBox{MarginsZero: true},
+		Children: []Widget{
+			TabWidget{
+				AssignTo: &mw.tabWidget,
+			},
+		},
+	}.Run()); err != nil {
+		log.Fatal(err)
+	}
+}
+
+type MyMainWindow struct {
+	*walk.MainWindow
+	tabWidget    *walk.TabWidget
+	prevFilePath string
+}
+
+func (mw *MyMainWindow) openAction_Triggered() {
+	if err := mw.openImage(); err != nil {
+		log.Print(err)
+	}
+}
+
+func (mw *MyMainWindow) openImage() error {
+	dlg := new(walk.FileDialog)
+
+	dlg.FilePath = mw.prevFilePath
+	dlg.Filter = "Image Files (*.emf;*.bmp;*.exif;*.gif;*.jpeg;*.jpg;*.png;*.tiff)|*.emf;*.bmp;*.exif;*.gif;*.jpeg;*.jpg;*.png;*.tiff"
+	dlg.Title = "Select an Image"
+
+	if ok, err := dlg.ShowOpen(mw); err != nil {
+		return err
+	} else if !ok {
+		return nil
+	}
+
+	mw.prevFilePath = dlg.FilePath
+
+	img, err := walk.NewImageFromFile(dlg.FilePath)
+	if err != nil {
+		return err
+	}
+
+	var succeeded bool
+	defer func() {
+		if !succeeded {
+			img.Dispose()
+		}
+	}()
+
+	page, err := walk.NewTabPage()
+	if err != nil {
+		return err
+	}
+
+	if page.SetTitle(path.Base(strings.Replace(dlg.FilePath, "\\", "/", -1))); err != nil {
+		return err
+	}
+	page.SetLayout(walk.NewHBoxLayout())
+
+	defer func() {
+		if !succeeded {
+			page.Dispose()
+		}
+	}()
+
+	imageView, err := walk.NewImageView(page)
+	if err != nil {
+		return err
+	}
+
+	defer func() {
+		if !succeeded {
+			imageView.Dispose()
+		}
+	}()
+
+	if err := imageView.SetImage(img); err != nil {
+		return err
+	}
+
+	if err := mw.tabWidget.Pages().Add(page); err != nil {
+		return err
+	}
+
+	if err := mw.tabWidget.SetCurrentIndex(mw.tabWidget.Pages().Len() - 1); err != nil {
+		return err
+	}
+
+	succeeded = true
+
+	return nil
+}
+
+func (mw *MyMainWindow) aboutAction_Triggered() {
+	walk.MsgBox(mw, "About", "Walk Image Viewer Example", walk.MsgBoxIconInformation)
+}

+ 14 - 0
common/src/github.com/lxn/walk/examples/imageviewer/imageviewer.manifest

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+		</dependentAssembly>
+	</dependency>
+	<asmv3:application>
+		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+			<dpiAware>true</dpiAware>
+		</asmv3:windowsSettings>
+	</asmv3:application>
+</assembly>

BIN
common/src/github.com/lxn/walk/examples/imageviewer/rsrc.syso


+ 4 - 0
common/src/github.com/lxn/walk/examples/img/README

@@ -0,0 +1,4 @@
+Most image files in this directory are from the base icon theme of the
+Tango Desktop Project at http://tango.freedesktop.org.
+
+Thanks for releasing those to the Public Domain.

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません