├── l10n
├── update.bat
├── walk-ko.tr
└── walk-de.tr
├── data
├── drawing.png
└── filebrowser.png
├── examples
├── img
│ ├── open.png
│ ├── plus.png
│ ├── stop.ico
│ ├── check.ico
│ ├── document-new.png
│ ├── system-shutdown.png
│ ├── document-properties.png
│ └── README
├── actions
│ ├── rsrc.syso
│ └── actions.exe.manifest
├── drawing
│ ├── rsrc.syso
│ └── drawing.exe.manifest
├── listbox
│ ├── rsrc.syso
│ ├── listbox.exe.manifest
│ └── listbox.go
├── logview
│ ├── rsrc.syso
│ ├── logview.exe.manifest
│ ├── logviewapp.go
│ └── logview.go
├── slider
│ ├── rsrc.syso
│ ├── slider.exe.manifest
│ └── slider.go
├── webview
│ ├── rsrc.syso
│ ├── webview.exe.manifest
│ └── webview.go
├── clipboard
│ ├── rsrc.syso
│ ├── clipboard.exe.manifest
│ └── clipboard.go
├── dropfiles
│ ├── rsrc.syso
│ ├── dropfiles.exe.manifest
│ └── dropfiles.go
├── imageicon
│ ├── rsrc.syso
│ ├── imageicon.exe.manifest
│ └── main.go
├── imageview
│ ├── rsrc.syso
│ ├── imageview.exe.manifest
│ └── imageview.go
├── linklabel
│ ├── rsrc.syso
│ ├── linklabel.exe.manifest
│ └── linklabel.go
├── notifyicon
│ ├── rsrc.syso
│ ├── notifyicon.exe.manifest
│ └── notifyicon.go
├── settings
│ ├── rsrc.syso
│ ├── settings.exe.manifest
│ └── settings.go
├── statusbar
│ ├── rsrc.syso
│ ├── statusbar.exe.manifest
│ └── statusbar.go
├── tableview
│ ├── rsrc.syso
│ └── tableview.exe.manifest
├── databinding
│ ├── rsrc.syso
│ └── databinding.exe.manifest
├── filebrowser
│ ├── rsrc.syso
│ └── filebrowser.exe.manifest
├── imageviewer
│ ├── rsrc.syso
│ └── imageviewer.exe.manifest
├── radiobutton
│ ├── rsrc.syso
│ ├── radiobutton.exe.manifest
│ └── radiobutton.go
├── externalwidgets
│ ├── rsrc.syso
│ └── externalwidgets.exe.manifest
├── multiplepages
│ ├── rsrc.syso
│ └── multiplepages.exe.manifest
├── webview_events
│ ├── rsrc.syso
│ └── webview_events.exe.manifest
├── gradientcomposite
│ ├── rsrc.syso
│ ├── gradientcomposite.exe.manifest
│ └── gradientcomposite.go
├── progressindicator
│ ├── rsrc.syso
│ ├── progressindicator.exe.manifest
│ └── pi.go
├── listbox_ownerdrawing
│ ├── rsrc.syso
│ └── listbox_ownerdrawing.exe.manifest
└── taskdialog
│ ├── manifest_windows_386.syso
│ ├── manifest_windows_amd64.syso
│ ├── manifest_windows_arm64.syso
│ ├── generate.go
│ └── manifest.xml
├── declarative
├── nonwin.go
├── dialogex.go
├── font.go
├── databinder.go
├── validators.go
├── tableviewcolumn.go
├── linklabel.go
├── toolbutton.go
├── spacer.go
├── tabpage.go
├── datelabel.go
├── progressbar.go
├── numberlabel.go
├── scrollview.go
├── radiobutton.go
├── groupbox.go
├── splitbutton.go
├── imageview.go
├── radiobuttongroup.go
├── dateedit.go
├── label.go
├── tabwidget.go
├── composite.go
├── slider.go
├── pushbutton.go
├── treeview.go
├── brush.go
├── radiobuttongroupbox.go
├── textedit.go
├── gradientcomposite.go
├── customwidget.go
├── toolbar.go
├── checkbox.go
└── numberedit.go
├── go.mod
├── color.go
├── point.go
├── simpletypes.go
├── menuownerdraw_test.go
├── go.sum
├── composite.go
├── expression.go
├── intevent.go
├── keyevent.go
├── .github
└── workflows
│ └── build-examples.yml
├── errorevent.go
├── stringevent.go
├── LICENSE
├── AUTHORS
├── cancelevent.go
├── closeevent.go
├── intrangeevent.go
├── treeitemevent.go
├── walk.go
├── size.go
├── toolbutton.go
├── separator.go
├── path.go
├── maptablemodel.go
├── rectangle.go
├── iconcache.go
├── mouseevent.go
├── monitor.go
├── dropfilesevent.go
├── dpicache
└── dpicache.go
├── progressbar.go
├── splitbutton.go
├── label.go
├── registry.go
├── fontresource.go
├── spacer.go
├── messagebox.go
├── event.go
├── datelabel.go
├── idalloc
└── idalloc.go
└── splitterhandle.go
/l10n/update.bat:
--------------------------------------------------------------------------------
1 | polyglot -name="walk" -dir=".." -locales="de,fr,ko"
2 |
--------------------------------------------------------------------------------
/data/drawing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/data/drawing.png
--------------------------------------------------------------------------------
/data/filebrowser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/data/filebrowser.png
--------------------------------------------------------------------------------
/examples/img/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/open.png
--------------------------------------------------------------------------------
/examples/img/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/plus.png
--------------------------------------------------------------------------------
/examples/img/stop.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/stop.ico
--------------------------------------------------------------------------------
/examples/img/check.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/check.ico
--------------------------------------------------------------------------------
/examples/actions/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/actions/rsrc.syso
--------------------------------------------------------------------------------
/examples/drawing/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/drawing/rsrc.syso
--------------------------------------------------------------------------------
/examples/listbox/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/listbox/rsrc.syso
--------------------------------------------------------------------------------
/examples/logview/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/logview/rsrc.syso
--------------------------------------------------------------------------------
/examples/slider/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/slider/rsrc.syso
--------------------------------------------------------------------------------
/examples/webview/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/webview/rsrc.syso
--------------------------------------------------------------------------------
/examples/clipboard/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/clipboard/rsrc.syso
--------------------------------------------------------------------------------
/examples/dropfiles/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/dropfiles/rsrc.syso
--------------------------------------------------------------------------------
/examples/imageicon/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/imageicon/rsrc.syso
--------------------------------------------------------------------------------
/examples/imageview/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/imageview/rsrc.syso
--------------------------------------------------------------------------------
/examples/img/document-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/document-new.png
--------------------------------------------------------------------------------
/examples/linklabel/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/linklabel/rsrc.syso
--------------------------------------------------------------------------------
/examples/notifyicon/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/notifyicon/rsrc.syso
--------------------------------------------------------------------------------
/examples/settings/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/settings/rsrc.syso
--------------------------------------------------------------------------------
/examples/statusbar/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/statusbar/rsrc.syso
--------------------------------------------------------------------------------
/examples/tableview/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/tableview/rsrc.syso
--------------------------------------------------------------------------------
/examples/databinding/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/databinding/rsrc.syso
--------------------------------------------------------------------------------
/examples/filebrowser/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/filebrowser/rsrc.syso
--------------------------------------------------------------------------------
/examples/imageviewer/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/imageviewer/rsrc.syso
--------------------------------------------------------------------------------
/examples/radiobutton/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/radiobutton/rsrc.syso
--------------------------------------------------------------------------------
/examples/externalwidgets/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/externalwidgets/rsrc.syso
--------------------------------------------------------------------------------
/examples/img/system-shutdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/system-shutdown.png
--------------------------------------------------------------------------------
/examples/multiplepages/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/multiplepages/rsrc.syso
--------------------------------------------------------------------------------
/examples/webview_events/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/webview_events/rsrc.syso
--------------------------------------------------------------------------------
/examples/gradientcomposite/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/gradientcomposite/rsrc.syso
--------------------------------------------------------------------------------
/examples/img/document-properties.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/img/document-properties.png
--------------------------------------------------------------------------------
/examples/progressindicator/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/progressindicator/rsrc.syso
--------------------------------------------------------------------------------
/examples/listbox_ownerdrawing/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/listbox_ownerdrawing/rsrc.syso
--------------------------------------------------------------------------------
/examples/taskdialog/manifest_windows_386.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/taskdialog/manifest_windows_386.syso
--------------------------------------------------------------------------------
/examples/taskdialog/manifest_windows_amd64.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/taskdialog/manifest_windows_amd64.syso
--------------------------------------------------------------------------------
/examples/taskdialog/manifest_windows_arm64.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tailscale/walk/HEAD/examples/taskdialog/manifest_windows_arm64.syso
--------------------------------------------------------------------------------
/examples/img/README:
--------------------------------------------------------------------------------
1 | Most image files in this directory are from the base icon theme of the
2 | Tango Desktop Project at http://tango.freedesktop.org.
3 |
4 | Thanks for releasing those to the Public Domain.
5 |
--------------------------------------------------------------------------------
/declarative/nonwin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The win Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !windows
6 |
7 | package declarative
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/tailscale/walk
2 |
3 | go 1.21
4 |
5 | require (
6 | github.com/dblohm7/wingoes v0.0.0-20231019175336-f6e33aa7cc34
7 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35
8 | golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
9 | golang.org/x/sys v0.8.0
10 | gopkg.in/Knetic/govaluate.v3 v3.0.0
11 | )
12 |
--------------------------------------------------------------------------------
/examples/taskdialog/generate.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Tailscale Inc & AUTHORS
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package main
5 |
6 | //go:generate go run tailscale.com/cmd/mkmanifest amd64 manifest.xml manifest_windows_amd64.syso
7 | //go:generate go run tailscale.com/cmd/mkmanifest 386 manifest.xml manifest_windows_386.syso
8 | //go:generate go run tailscale.com/cmd/mkmanifest arm64 manifest.xml manifest_windows_arm64.syso
9 |
--------------------------------------------------------------------------------
/color.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type Color uint32
10 |
11 | func RGB(r, g, b byte) Color {
12 | return Color(uint32(r) | uint32(g)<<8 | uint32(b)<<16)
13 | }
14 |
15 | func (c Color) R() byte {
16 | return byte(c & 0xff)
17 | }
18 |
19 | func (c Color) G() byte {
20 | return byte((c >> 8) & 0xff)
21 | }
22 |
23 | func (c Color) B() byte {
24 | return byte((c >> 16) & 0xff)
25 | }
26 |
--------------------------------------------------------------------------------
/point.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import "github.com/tailscale/win"
10 |
11 | // Point defines 2D coordinate in 1/96" units ot native pixels.
12 | type Point struct {
13 | X, Y int
14 | }
15 |
16 | func (p Point) toPOINT() win.POINT {
17 | return win.POINT{
18 | X: int32(p.X),
19 | Y: int32(p.Y),
20 | }
21 | }
22 |
23 | func pointPixelsFromPOINT(p win.POINT) Point {
24 | return Point{
25 | X: int(p.X),
26 | Y: int(p.Y),
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/simpletypes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type Alignment1D uint
10 |
11 | const (
12 | AlignDefault Alignment1D = iota
13 | AlignNear
14 | AlignCenter
15 | AlignFar
16 | )
17 |
18 | type Alignment2D uint
19 |
20 | const (
21 | AlignHVDefault Alignment2D = iota
22 | AlignHNearVNear
23 | AlignHCenterVNear
24 | AlignHFarVNear
25 | AlignHNearVCenter
26 | AlignHCenterVCenter
27 | AlignHFarVCenter
28 | AlignHNearVFar
29 | AlignHCenterVFar
30 | AlignHFarVFar
31 | )
32 |
--------------------------------------------------------------------------------
/examples/actions/actions.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/drawing/drawing.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/listbox/listbox.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/logview/logview.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/slider/slider.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/webview/webview.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/clipboard/clipboard.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/dropfiles/dropfiles.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/imageicon/imageicon.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/imageview/imageview.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/linklabel/linklabel.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/settings/settings.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/statusbar/statusbar.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/tableview/tableview.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/databinding/databinding.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/filebrowser/filebrowser.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/imageviewer/imageviewer.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/notifyicon/notifyicon.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/radiobutton/radiobutton.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/multiplepages/multiplepages.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/webview_events/webview_events.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/externalwidgets/externalwidgets.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/gradientcomposite/gradientcomposite.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/progressindicator/progressindicator.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/listbox_ownerdrawing/listbox_ownerdrawing.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/dropfiles/dropfiles.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 | "strings"
10 |
11 | "github.com/tailscale/walk"
12 | . "github.com/tailscale/walk/declarative"
13 | )
14 |
15 | func main() {
16 | app, err := walk.InitApp()
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 |
21 | var textEdit *walk.TextEdit
22 | MainWindow{
23 | Title: "Walk DropFiles Example",
24 | MinSize: Size{320, 240},
25 | Layout: VBox{},
26 | OnDropFiles: func(files []string) {
27 | textEdit.SetText(strings.Join(files, "\r\n"))
28 | },
29 | Children: []Widget{
30 | TextEdit{
31 | AssignTo: &textEdit,
32 | ReadOnly: true,
33 | Text: "Drop files here, from windows explorer...",
34 | },
35 | },
36 | }.Create()
37 |
38 | app.Run()
39 | }
40 |
--------------------------------------------------------------------------------
/menuownerdraw_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Tailscale Inc. and AUTHORS
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package walk
9 |
10 | import (
11 | "testing"
12 |
13 | "golang.org/x/sys/windows"
14 | )
15 |
16 | func TestFindExplicitMnemonic(t *testing.T) {
17 | testCases := []struct {
18 | text string
19 | wantKey Key
20 | }{
21 | {"", 0},
22 | {"Law 'N' Order", 0},
23 | {"Law && Order", 0},
24 | {"Law && &Order", KeyO},
25 | {"&Law && &Order && Bacon", KeyL},
26 | }
27 |
28 | for _, c := range testCases {
29 | utext, err := windows.UTF16FromString(c.text)
30 | if err != nil {
31 | t.Fatalf("UTF16FromString error %v", err)
32 | }
33 | k := findExplicitMnemonic(utext)
34 | if k != c.wantKey {
35 | t.Errorf("key for %q got 0x%02X, want 0x%02X", c.text, k, c.wantKey)
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/dblohm7/wingoes v0.0.0-20231019175336-f6e33aa7cc34 h1:FBMro26TLQwBk+n4fbTSmSf3QUKb09pvW4fz49lxpl0=
2 | github.com/dblohm7/wingoes v0.0.0-20231019175336-f6e33aa7cc34/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
3 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 h1:wAZbkTZkqDzWsqxPh2qkBd3KvFU7tcxV0BP0Rnhkxog=
4 | github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
5 | golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
6 | golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
7 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
8 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9 | gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
10 | gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
11 |
--------------------------------------------------------------------------------
/examples/linklabel/linklabel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 |
12 | . "github.com/tailscale/walk/declarative"
13 | )
14 |
15 | func main() {
16 | app, err := walk.InitApp()
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 |
21 | if err := (MainWindow{
22 | Title: "Walk LinkLabel Example",
23 | MinSize: Size{300, 200},
24 | Layout: VBox{},
25 | Children: []Widget{
26 | LinkLabel{
27 | MaxSize: Size{100, 0},
28 | Text: `I can contain multiple links like this or that one.`,
29 | OnLinkActivated: func(link *walk.LinkLabelLink) {
30 | log.Printf("id: '%s', url: '%s'\n", link.Id(), link.URL())
31 | },
32 | },
33 | },
34 | }).Create(); err != nil {
35 | log.Fatal(err)
36 | }
37 |
38 | app.Run()
39 | }
40 |
--------------------------------------------------------------------------------
/examples/logview/logviewapp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 | "time"
10 |
11 | "github.com/tailscale/walk"
12 |
13 | . "github.com/tailscale/walk/declarative"
14 | )
15 |
16 | func main() {
17 | app, err := walk.InitApp()
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 |
22 | var mw *walk.MainWindow
23 |
24 | if err := (MainWindow{
25 | AssignTo: &mw,
26 | Title: "Walk LogView Example",
27 | MinSize: Size{320, 240},
28 | Size: Size{400, 600},
29 | Layout: VBox{MarginsZero: true},
30 | }.Create()); err != nil {
31 | log.Fatal(err)
32 | }
33 |
34 | lv, err := NewLogView(mw)
35 | if err != nil {
36 | log.Fatal(err)
37 | }
38 |
39 | lv.PostAppendText("XXX")
40 | log.SetOutput(lv)
41 |
42 | go func() {
43 | for i := 0; i < 10000; i++ {
44 | time.Sleep(100 * time.Millisecond)
45 | log.Println("Text" + "\r\n")
46 | }
47 | }()
48 |
49 | app.Run()
50 | }
51 |
--------------------------------------------------------------------------------
/composite.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | const compositeWindowClass = `\o/ Walk_Composite_Class \o/`
14 |
15 | func init() {
16 | AppendToWalkInit(func() {
17 | MustRegisterWindowClass(compositeWindowClass)
18 | })
19 | }
20 |
21 | type Composite struct {
22 | ContainerBase
23 | }
24 |
25 | func NewCompositeWithStyle(parent Window, style uint32) (*Composite, error) {
26 | c := new(Composite)
27 | c.children = newWidgetList(c)
28 | c.SetPersistent(true)
29 |
30 | if err := InitWidget(
31 | c,
32 | parent,
33 | compositeWindowClass,
34 | win.WS_CHILD|win.WS_VISIBLE|style,
35 | win.WS_EX_CONTROLPARENT); err != nil {
36 | return nil, err
37 | }
38 |
39 | c.SetBackground(NullBrush())
40 |
41 | return c, nil
42 | }
43 |
44 | func NewComposite(parent Container) (*Composite, error) {
45 | return NewCompositeWithStyle(parent, 0)
46 | }
47 |
--------------------------------------------------------------------------------
/declarative/dialogex.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Tailscale Inc & AUTHORS
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | //go:build windows
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type DialogEx struct {
14 | Background Brush
15 | Layout Layout
16 | Children []Widget
17 | Icon Property
18 | Title string
19 | Size Size
20 |
21 | AssignTo **walk.DialogEx
22 | }
23 |
24 | func (d DialogEx) Create(owner walk.Form) error {
25 | dlg, err := walk.NewDialogEx(owner, d.Title, d.Size.toW())
26 | if err != nil {
27 | return err
28 | }
29 |
30 | if d.AssignTo != nil {
31 | *d.AssignTo = dlg
32 | }
33 |
34 | fi := formInfo{
35 | // Window
36 | Background: d.Background,
37 | Enabled: true,
38 |
39 | // Container
40 | Children: d.Children,
41 | Layout: d.Layout,
42 |
43 | // Form
44 | Icon: d.Icon,
45 | Title: d.Title,
46 | }
47 |
48 | builder := NewBuilder(nil)
49 | dlg.SetSuspended(true)
50 | builder.Defer(func() error {
51 | dlg.SetSuspended(false)
52 | return nil
53 | })
54 |
55 | return builder.InitWidget(fi, dlg, nil)
56 | }
57 |
--------------------------------------------------------------------------------
/expression.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "log"
11 | "reflect"
12 | )
13 |
14 | import _ "gopkg.in/Knetic/govaluate.v3"
15 |
16 | type Expression interface {
17 | Value() interface{}
18 | Changed() *Event
19 | }
20 |
21 | type reflectExpression struct {
22 | root Expression
23 | path string
24 | }
25 |
26 | func NewReflectExpression(root Expression, path string) Expression {
27 | return &reflectExpression{root: root, path: path}
28 | }
29 |
30 | func (re *reflectExpression) Value() interface{} {
31 | rootVal := re.root.Value()
32 | if rootVal == nil {
33 | return nil
34 | }
35 |
36 | _, val, err := reflectValueFromPath(reflect.ValueOf(rootVal), re.path)
37 | if err != nil {
38 | log.Print("walk - reflectExpression.Value - Error: ", err.Error())
39 | }
40 |
41 | if !val.IsValid() {
42 | return nil
43 | }
44 |
45 | return val.Interface()
46 | }
47 |
48 | func (re *reflectExpression) Changed() *Event {
49 | return re.root.Changed()
50 | }
51 |
--------------------------------------------------------------------------------
/declarative/font.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package declarative
9 |
10 | import (
11 | "github.com/tailscale/walk"
12 | )
13 |
14 | type Font struct {
15 | Family string
16 | PointSize int
17 | Light bool
18 | SemiLight bool
19 | SemiBold bool
20 | Bold bool
21 | Italic bool
22 | Underline bool
23 | StrikeOut bool
24 | }
25 |
26 | func (f Font) Create() (*walk.Font, error) {
27 | if f.Family == "" && f.PointSize == 0 {
28 | return nil, nil
29 | }
30 |
31 | var fs walk.FontStyle
32 |
33 | switch {
34 | case f.Light:
35 | fs |= walk.FontLight
36 | case f.SemiLight:
37 | fs |= walk.FontSemiLight
38 | case f.SemiBold:
39 | fs |= walk.FontSemiBold
40 | case f.Bold:
41 | fs |= walk.FontBold
42 | }
43 |
44 | if f.Italic {
45 | fs |= walk.FontItalic
46 | }
47 | if f.Underline {
48 | fs |= walk.FontUnderline
49 | }
50 | if f.StrikeOut {
51 | fs |= walk.FontStrikeOut
52 | }
53 |
54 | return walk.NewFont(f.Family, f.PointSize, fs)
55 | }
56 |
--------------------------------------------------------------------------------
/l10n/walk-ko.tr:
--------------------------------------------------------------------------------
1 | {"Messages":[{"Locations":[{"File":"../validators.go","Line":"87"}],"Source":"Number out of allowed range","Context":["walk"],"Translation":"허용 범위 초과"},{"Locations":[{"File":"../validators.go","Line":"128"}],"Source":"The text does not match the required pattern.","Context":["walk"],"Translation":"문자열이 요구되는 형식에 맞지 않습니다."},{"Locations":[{"File":"../validators.go","Line":"147"}],"Source":"Selection Required","Context":["walk"],"Translation":"선택 필요"},{"Locations":[{"File":"../validators.go","Line":"148"}],"Source":"Please select one of the provided options.","Context":["walk"],"Translation":"옵션 중 하나를 선택하십시오"},{"Locations":[{"File":"../tooltiperrorpresenter.go","Line":"107"}],"Source":"Invalid Input","Context":null,"Translation":"잘못된 입력"},{"Locations":[{"File":"../declarative/radiobuttongroup.go","Line":"93"}],"Source":"A selection is required.","Context":["walk"],"Translation":"항목 선택이 필요합니다."},{"Locations":[{"File":"../validators.go","Line":"80"}],"Source":"Please enter a number from %.f to %.f.","Context":["walk"],"Translation":"%.f에서 %.f 사이의 숫자를 입력하십시오."},{"Locations":[{"File":"../validators.go","Line":"83"}],"Source":"Please enter a number from %s to %s.","Context":["walk"],"Translation":"%s에서 %s 사이의 숫자를 입력하십시오."}]}
--------------------------------------------------------------------------------
/examples/clipboard/clipboard.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 |
12 | . "github.com/tailscale/walk/declarative"
13 | )
14 |
15 | func main() {
16 | app, err := walk.InitApp()
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 |
21 | var te *walk.TextEdit
22 |
23 | if err := (MainWindow{
24 | Title: "Walk Clipboard Example",
25 | MinSize: Size{300, 200},
26 | Layout: VBox{},
27 | Children: []Widget{
28 | PushButton{
29 | Text: "Copy",
30 | OnClicked: func() {
31 | if err := walk.Clipboard().SetText(te.Text()); err != nil {
32 | log.Print("Copy: ", err)
33 | }
34 | },
35 | },
36 | PushButton{
37 | Text: "Paste",
38 | OnClicked: func() {
39 | if text, err := walk.Clipboard().Text(); err != nil {
40 | log.Print("Paste: ", err)
41 | } else {
42 | te.SetText(text)
43 | }
44 | },
45 | },
46 | TextEdit{
47 | AssignTo: &te,
48 | },
49 | },
50 | }).Create(); err != nil {
51 | log.Fatal(err)
52 | }
53 |
54 | app.Run()
55 | }
56 |
--------------------------------------------------------------------------------
/l10n/walk-de.tr:
--------------------------------------------------------------------------------
1 | {"Messages":[{"Locations":[{"File":"../validators.go","Line":"148"}],"Source":"Please select one of the provided options.","Context":["walk"],"Translation":"Bitte wählen Sie eine der angebotenen Optionen."},{"Locations":[{"File":"../declarative/radiobuttongroup.go","Line":"93"}],"Source":"A selection is required.","Context":["walk"],"Translation":"Eine Auswahl wird benötigt."},{"Locations":[{"File":"../tooltiperrorpresenter.go","Line":"107"}],"Source":"Invalid Input","Context":null,"Translation":"Ungültige Eingabe"},{"Locations":[{"File":"../validators.go","Line":"80"}],"Source":"Please enter a number from %.f to %.f.","Context":["walk"],"Translation":"Bitte geben Sie eine Zahl von %.f bis %.f ein."},{"Locations":[{"File":"../validators.go","Line":"83"}],"Source":"Please enter a number from %s to %s.","Context":["walk"],"Translation":"Bitte geben Sie eine Zahl von %s bis %s ein."},{"Locations":[{"File":"../validators.go","Line":"87"}],"Source":"Number out of allowed range","Context":["walk"],"Translation":"Zahl außerhalb des gültigen Bereichs"},{"Locations":[{"File":"../validators.go","Line":"128"}],"Source":"The text does not match the required pattern.","Context":["walk"],"Translation":"Der Text entspricht nicht dem erforderlichen Muster."},{"Locations":[{"File":"../validators.go","Line":"147"}],"Source":"Selection Required","Context":["walk"],"Translation":"Auswahl benötigt"}]}
2 |
--------------------------------------------------------------------------------
/intevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type intEventHandlerInfo struct {
10 | handler IntEventHandler
11 | once bool
12 | }
13 |
14 | type IntEventHandler func(n int)
15 |
16 | type IntEvent struct {
17 | handlers []intEventHandlerInfo
18 | }
19 |
20 | func (e *IntEvent) Attach(handler IntEventHandler) int {
21 | handlerInfo := intEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *IntEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *IntEvent) Once(handler IntEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type IntEventPublisher struct {
45 | event IntEvent
46 | }
47 |
48 | func (p *IntEventPublisher) Event() *IntEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *IntEventPublisher) Publish(n int) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(n)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/keyevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type keyEventHandlerInfo struct {
10 | handler KeyEventHandler
11 | once bool
12 | }
13 |
14 | type KeyEventHandler func(key Key)
15 |
16 | type KeyEvent struct {
17 | handlers []keyEventHandlerInfo
18 | }
19 |
20 | func (e *KeyEvent) Attach(handler KeyEventHandler) int {
21 | handlerInfo := keyEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *KeyEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *KeyEvent) Once(handler KeyEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type KeyEventPublisher struct {
45 | event KeyEvent
46 | }
47 |
48 | func (p *KeyEventPublisher) Event() *KeyEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *KeyEventPublisher) Publish(key Key) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(key)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/examples/webview/webview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 | "strings"
10 |
11 | "github.com/tailscale/walk"
12 | . "github.com/tailscale/walk/declarative"
13 | )
14 |
15 | func main() {
16 | app, err := walk.InitApp()
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 |
21 | var le *walk.LineEdit
22 | var wv *walk.WebView
23 |
24 | MainWindow{
25 | Icon: Bind("'../img/' + icon(wv.URL) + '.ico'"),
26 | Title: "Walk WebView Example'",
27 | MinSize: Size{800, 600},
28 | Layout: VBox{MarginsZero: true},
29 | Children: []Widget{
30 | LineEdit{
31 | AssignTo: &le,
32 | Text: Bind("wv.URL"),
33 | OnKeyDown: func(key walk.Key) {
34 | if key == walk.KeyReturn {
35 | wv.SetURL(le.Text())
36 | }
37 | },
38 | },
39 | WebView{
40 | AssignTo: &wv,
41 | Name: "wv",
42 | URL: "https://github.com/tailscale/walk",
43 | },
44 | },
45 | Functions: map[string]func(args ...interface{}) (interface{}, error){
46 | "icon": func(args ...interface{}) (interface{}, error) {
47 | if strings.HasPrefix(args[0].(string), "https") {
48 | return "check", nil
49 | }
50 |
51 | return "stop", nil
52 | },
53 | },
54 | }.Create()
55 |
56 | app.Run()
57 | }
58 |
--------------------------------------------------------------------------------
/.github/workflows/build-examples.yml:
--------------------------------------------------------------------------------
1 | name: Build Examples
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | # all PRs on all branches
8 |
9 | concurrency:
10 | # For PRs, later CI runs preempt previous ones. e.g. a force push on a PR
11 | # cancels running CI jobs and starts all new ones.
12 | #
13 | # For non-PR pushes, concurrency.group needs to be unique for every distinct
14 | # CI run we want to have happen. Use run_id, which in practice means all
15 | # non-PR CI runs will be allowed to run without preempting each other.
16 | group: ${{ github.workflow }}-$${{ github.pull_request.number || github.run_id }}
17 | cancel-in-progress: true
18 |
19 | jobs:
20 | build:
21 | strategy:
22 | fail-fast: false
23 | matrix:
24 | # Omitting arm64 for now; rsrc.syso produces linker errors
25 | goarch: [ "386", "amd64" ]
26 | runs-on: ubuntu-latest
27 | steps:
28 | - name: Checkout
29 | uses: actions/checkout@v4
30 |
31 | - name: Install Go
32 | uses: actions/setup-go@v4
33 | with:
34 | go-version-file: go.mod
35 |
36 | - name: Set up build directory
37 | run: |
38 | mkdir -p ./examples/bin
39 |
40 | - name: Build Binaries
41 | run: |
42 | go build -v -ldflags="-H windowsgui" -o ./examples/bin ./examples/...
43 | env:
44 | GOARCH: ${{ matrix.goarch }}
45 | GOOS: windows
46 |
--------------------------------------------------------------------------------
/errorevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type errorEventHandlerInfo struct {
10 | handler ErrorEventHandler
11 | once bool
12 | }
13 |
14 | type ErrorEventHandler func(err error)
15 |
16 | type ErrorEvent struct {
17 | handlers []errorEventHandlerInfo
18 | }
19 |
20 | func (e *ErrorEvent) Attach(handler ErrorEventHandler) int {
21 | handlerInfo := errorEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *ErrorEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *ErrorEvent) Once(handler ErrorEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type ErrorEventPublisher struct {
45 | event ErrorEvent
46 | }
47 |
48 | func (p *ErrorEventPublisher) Event() *ErrorEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *ErrorEventPublisher) Publish(err error) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(err)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/stringevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type stringEventHandlerInfo struct {
10 | handler StringEventHandler
11 | once bool
12 | }
13 |
14 | type StringEventHandler func(s string)
15 |
16 | type StringEvent struct {
17 | handlers []stringEventHandlerInfo
18 | }
19 |
20 | func (e *StringEvent) Attach(handler StringEventHandler) int {
21 | handlerInfo := stringEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *StringEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *StringEvent) Once(handler StringEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type StringEventPublisher struct {
45 | event StringEvent
46 | }
47 |
48 | func (p *StringEventPublisher) Event() *StringEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *StringEventPublisher) Publish(s string) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(s)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 The Walk Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions
5 | are met:
6 | 1. Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | 3. The names of the authors may not be used to endorse or promote products
12 | derived from this software without specific prior written permission.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
15 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the official list of 'Walk' authors for copyright purposes.
2 |
3 | # Names should be added to this file as
4 | # Name or Organization
5 | # The email address is not required for organizations.
6 |
7 | # Please keep the list sorted.
8 |
9 | # Contributors
10 | # ============
11 |
12 | Aaron Klotz
13 | Alexander Neumann
14 | Aman Gupta
15 | Anthony Dong
16 | Attila Tajti
17 | Audrius Karabanovas
18 | Benny Siegert
19 | Cary Cherng
20 | Dmitry Bagdanov
21 | Ham Yeongtaek
22 | Hill
23 | iquanxin
24 | James Scholes
25 | Jason A. Donenfeld
26 | Joseph Watson
27 | Joshua D. Sjoding
28 | ktye
29 | llxwj
30 | Mateusz Czapliński
31 | Michael Teichgräber
32 | Paul Wolf
33 | ryujimiya
34 | Semyon Tokarev
35 | Shawn Sun
36 | Simon Rozman
37 | Tim Dufrane
38 | Vincent Vanackere
39 | xoviat
40 | evangwt
41 |
--------------------------------------------------------------------------------
/cancelevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type cancelEventHandlerInfo struct {
10 | handler CancelEventHandler
11 | once bool
12 | }
13 |
14 | type CancelEventHandler func(canceled *bool)
15 |
16 | type CancelEvent struct {
17 | handlers []cancelEventHandlerInfo
18 | }
19 |
20 | func (e *CancelEvent) Attach(handler CancelEventHandler) int {
21 | handlerInfo := cancelEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *CancelEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *CancelEvent) Once(handler CancelEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type CancelEventPublisher struct {
45 | event CancelEvent
46 | }
47 |
48 | func (p *CancelEventPublisher) Event() *CancelEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *CancelEventPublisher) Publish(canceled *bool) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(canceled)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/examples/imageview/imageview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 | . "github.com/tailscale/walk/declarative"
12 | )
13 |
14 | func main() {
15 | app, err := walk.InitApp()
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 |
20 | walk.Resources.SetRootDirPath("../img")
21 |
22 | type Mode struct {
23 | Name string
24 | Value ImageViewMode
25 | }
26 |
27 | modes := []Mode{
28 | {"ImageViewModeIdeal", ImageViewModeIdeal},
29 | {"ImageViewModeCorner", ImageViewModeCorner},
30 | {"ImageViewModeCenter", ImageViewModeCenter},
31 | {"ImageViewModeShrink", ImageViewModeShrink},
32 | {"ImageViewModeZoom", ImageViewModeZoom},
33 | {"ImageViewModeStretch", ImageViewModeStretch},
34 | }
35 |
36 | var widgets []Widget
37 |
38 | for _, mode := range modes {
39 | widgets = append(widgets,
40 | Label{
41 | Text: mode.Name,
42 | },
43 | ImageView{
44 | Background: SolidColorBrush{Color: walk.RGB(255, 191, 0)},
45 | Image: "open.png",
46 | Margin: 10,
47 | Mode: mode.Value,
48 | },
49 | )
50 | }
51 |
52 | MainWindow{
53 | Title: "Walk ImageView Example",
54 | Size: Size{400, 600},
55 | Layout: Grid{Columns: 2},
56 | Children: widgets,
57 | }.Create()
58 |
59 | app.Run()
60 | }
61 |
--------------------------------------------------------------------------------
/closeevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type closeEventHandlerInfo struct {
10 | handler CloseEventHandler
11 | once bool
12 | }
13 |
14 | type CloseEventHandler func(canceled *bool, reason CloseReason)
15 |
16 | type CloseEvent struct {
17 | handlers []closeEventHandlerInfo
18 | }
19 |
20 | func (e *CloseEvent) Attach(handler CloseEventHandler) int {
21 | handlerInfo := closeEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *CloseEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *CloseEvent) Once(handler CloseEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type CloseEventPublisher struct {
45 | event CloseEvent
46 | }
47 |
48 | func (p *CloseEventPublisher) Event() *CloseEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *CloseEventPublisher) Publish(canceled *bool, reason CloseReason) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(canceled, reason)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/examples/taskdialog/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | true/pm
16 |
17 |
18 | permonitorv2, permonitor
19 |
20 |
21 |
22 |
23 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/intrangeevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type intRangeEventHandlerInfo struct {
10 | handler IntRangeEventHandler
11 | once bool
12 | }
13 |
14 | type IntRangeEventHandler func(from, to int)
15 |
16 | type IntRangeEvent struct {
17 | handlers []intRangeEventHandlerInfo
18 | }
19 |
20 | func (e *IntRangeEvent) Attach(handler IntRangeEventHandler) int {
21 | handlerInfo := intRangeEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *IntRangeEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *IntRangeEvent) Once(handler IntRangeEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type IntRangeEventPublisher struct {
45 | event IntRangeEvent
46 | }
47 |
48 | func (p *IntRangeEventPublisher) Event() *IntRangeEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *IntRangeEventPublisher) Publish(from, to int) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(from, to)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/treeitemevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | type treeItemEventHandlerInfo struct {
10 | handler TreeItemEventHandler
11 | once bool
12 | }
13 |
14 | type TreeItemEventHandler func(item TreeItem)
15 |
16 | type TreeItemEvent struct {
17 | handlers []treeItemEventHandlerInfo
18 | }
19 |
20 | func (e *TreeItemEvent) Attach(handler TreeItemEventHandler) int {
21 | handlerInfo := treeItemEventHandlerInfo{handler, false}
22 |
23 | for i, h := range e.handlers {
24 | if h.handler == nil {
25 | e.handlers[i] = handlerInfo
26 | return i
27 | }
28 | }
29 |
30 | e.handlers = append(e.handlers, handlerInfo)
31 |
32 | return len(e.handlers) - 1
33 | }
34 |
35 | func (e *TreeItemEvent) Detach(handle int) {
36 | e.handlers[handle].handler = nil
37 | }
38 |
39 | func (e *TreeItemEvent) Once(handler TreeItemEventHandler) {
40 | i := e.Attach(handler)
41 | e.handlers[i].once = true
42 | }
43 |
44 | type TreeItemEventPublisher struct {
45 | event TreeItemEvent
46 | }
47 |
48 | func (p *TreeItemEventPublisher) Event() *TreeItemEvent {
49 | return &p.event
50 | }
51 |
52 | func (p *TreeItemEventPublisher) Publish(item TreeItem) {
53 | for i, h := range p.event.handlers {
54 | if h.handler != nil {
55 | h.handler(item)
56 |
57 | if h.once {
58 | p.event.Detach(i)
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/walk.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "errors"
11 | )
12 |
13 | var (
14 | ErrInvalidType = errors.New("invalid type")
15 | )
16 |
17 | func LogErrors() bool {
18 | return logErrors
19 | }
20 |
21 | func SetLogErrors(v bool) {
22 | logErrors = v
23 | }
24 |
25 | func PanicOnError() bool {
26 | return panicOnError
27 | }
28 |
29 | func SetPanicOnError(v bool) {
30 | panicOnError = v
31 | }
32 |
33 | func TranslationFunc() TranslationFunction {
34 | return translation
35 | }
36 |
37 | func SetTranslationFunc(f TranslationFunction) {
38 | translation = f
39 | }
40 |
41 | type TranslationFunction func(source string, context ...string) string
42 |
43 | var translation TranslationFunction
44 |
45 | func tr(source string, context ...string) string {
46 | if translation == nil {
47 | return source
48 | }
49 |
50 | return translation(source, context...)
51 | }
52 |
53 | type Disposable interface {
54 | Dispose()
55 | }
56 |
57 | type Disposables struct {
58 | items []Disposable
59 | done bool
60 | }
61 |
62 | func (d *Disposables) Add(item Disposable) {
63 | d.items = append(d.items, item)
64 | }
65 |
66 | func (d *Disposables) Spare() {
67 | d.done = true
68 | }
69 |
70 | func (d *Disposables) Treat() {
71 | if d.done {
72 | return
73 | }
74 |
75 | for _, item := range d.items {
76 | item.Dispose()
77 | }
78 |
79 | d.done = true
80 | }
81 |
--------------------------------------------------------------------------------
/size.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import "github.com/tailscale/win"
10 |
11 | // Size defines width and height in 1/96" units or native pixels, or dialog base units.
12 | //
13 | // When Size is used for DPI metrics, it defines a 1"x1" rectangle in native pixels.
14 | type Size struct {
15 | Width, Height int
16 | }
17 |
18 | func (s Size) IsZero() bool {
19 | return s.Width == 0 && s.Height == 0
20 | }
21 |
22 | func (s Size) toSIZE() win.SIZE {
23 | return win.SIZE{
24 | CX: int32(s.Width),
25 | CY: int32(s.Height),
26 | }
27 | }
28 |
29 | func minSize(a, b Size) Size {
30 | var s Size
31 |
32 | if a.Width < b.Width {
33 | s.Width = a.Width
34 | } else {
35 | s.Width = b.Width
36 | }
37 |
38 | if a.Height < b.Height {
39 | s.Height = a.Height
40 | } else {
41 | s.Height = b.Height
42 | }
43 |
44 | return s
45 | }
46 |
47 | func maxSize(a, b Size) Size {
48 | var s Size
49 |
50 | if a.Width > b.Width {
51 | s.Width = a.Width
52 | } else {
53 | s.Width = b.Width
54 | }
55 |
56 | if a.Height > b.Height {
57 | s.Height = a.Height
58 | } else {
59 | s.Height = b.Height
60 | }
61 |
62 | return s
63 | }
64 |
65 | func sizeFromSIZE(s win.SIZE) Size {
66 | return Size{
67 | Width: int(s.CX),
68 | Height: int(s.CY),
69 | }
70 | }
71 |
72 | func sizeFromRECT(r win.RECT) Size {
73 | return Size{
74 | Width: int(r.Right - r.Left),
75 | Height: int(r.Bottom - r.Top),
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/toolbutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | type ToolButton struct {
14 | Button
15 | }
16 |
17 | func NewToolButton(parent Container) (*ToolButton, error) {
18 | tb := new(ToolButton)
19 |
20 | if err := InitWidget(
21 | tb,
22 | parent,
23 | "BUTTON",
24 | win.WS_TABSTOP|win.WS_VISIBLE|win.BS_BITMAP|win.BS_PUSHBUTTON,
25 | 0); err != nil {
26 | return nil, err
27 | }
28 |
29 | tb.Button.init()
30 |
31 | tb.GraphicsEffects().Add(InteractionEffect)
32 | tb.GraphicsEffects().Add(FocusEffect)
33 |
34 | return tb, nil
35 | }
36 |
37 | func (tb *ToolButton) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
38 | switch msg {
39 | case win.WM_GETDLGCODE:
40 | return win.DLGC_BUTTON
41 | }
42 |
43 | return tb.Button.WndProc(hwnd, msg, wParam, lParam)
44 | }
45 |
46 | func (tb *ToolButton) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
47 | return &toolButtonLayoutItem{
48 | idealSize: tb.dialogBaseUnitsToPixels(Size{16, 12}),
49 | }
50 | }
51 |
52 | type toolButtonLayoutItem struct {
53 | LayoutItemBase
54 | idealSize Size // in native pixels
55 | }
56 |
57 | func (*toolButtonLayoutItem) LayoutFlags() LayoutFlags {
58 | return 0
59 | }
60 |
61 | func (tb *toolButtonLayoutItem) IdealSize() Size {
62 | return tb.idealSize
63 | }
64 |
65 | func (tb *toolButtonLayoutItem) MinSize() Size {
66 | return tb.idealSize
67 | }
68 |
--------------------------------------------------------------------------------
/declarative/databinder.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "time"
11 |
12 | "github.com/tailscale/walk"
13 | )
14 |
15 | type DataBinder struct {
16 | AssignTo **walk.DataBinder
17 | AutoSubmit bool
18 | AutoSubmitDelay time.Duration
19 | DataSource interface{}
20 | ErrorPresenter ErrorPresenter
21 | Name string
22 | OnCanSubmitChanged walk.EventHandler
23 | OnDataSourceChanged walk.EventHandler
24 | OnReset walk.EventHandler
25 | OnSubmitted walk.EventHandler
26 | }
27 |
28 | func (db DataBinder) create() (*walk.DataBinder, error) {
29 | b := walk.NewDataBinder()
30 |
31 | if db.AssignTo != nil {
32 | *db.AssignTo = b
33 | }
34 |
35 | if db.ErrorPresenter != nil {
36 | ep, err := db.ErrorPresenter.Create()
37 | if err != nil {
38 | return nil, err
39 | }
40 | b.SetErrorPresenter(ep)
41 | }
42 |
43 | b.SetDataSource(db.DataSource)
44 |
45 | b.SetAutoSubmit(db.AutoSubmit)
46 | b.SetAutoSubmitDelay(db.AutoSubmitDelay)
47 |
48 | if db.OnCanSubmitChanged != nil {
49 | b.CanSubmitChanged().Attach(db.OnCanSubmitChanged)
50 | }
51 | if db.OnDataSourceChanged != nil {
52 | b.DataSourceChanged().Attach(db.OnDataSourceChanged)
53 | }
54 | if db.OnReset != nil {
55 | b.ResetFinished().Attach(db.OnReset)
56 | }
57 | if db.OnSubmitted != nil {
58 | b.Submitted().Attach(db.OnSubmitted)
59 | }
60 |
61 | return b, nil
62 | }
63 |
--------------------------------------------------------------------------------
/separator.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | type Separator struct {
14 | WidgetBase
15 | vertical bool
16 | }
17 |
18 | func NewHSeparator(parent Container) (*Separator, error) {
19 | return newSeparator(parent, false)
20 | }
21 |
22 | func NewVSeparator(parent Container) (*Separator, error) {
23 | return newSeparator(parent, true)
24 | }
25 |
26 | func newSeparator(parent Container, vertical bool) (*Separator, error) {
27 | s := &Separator{vertical: vertical}
28 |
29 | if err := InitWidget(
30 | s,
31 | parent,
32 | "STATIC",
33 | win.WS_VISIBLE|win.SS_ETCHEDHORZ,
34 | 0); err != nil {
35 | return nil, err
36 | }
37 |
38 | return s, nil
39 | }
40 |
41 | func (s *Separator) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
42 | var layoutFlags LayoutFlags
43 | if s.vertical {
44 | layoutFlags = GrowableHorz | GreedyHorz
45 | } else {
46 | layoutFlags = GrowableVert | GreedyVert
47 | }
48 |
49 | return &separatorLayoutItem{
50 | layoutFlags: layoutFlags,
51 | }
52 | }
53 |
54 | type separatorLayoutItem struct {
55 | LayoutItemBase
56 | layoutFlags LayoutFlags
57 | }
58 |
59 | func (li *separatorLayoutItem) LayoutFlags() LayoutFlags {
60 | return li.layoutFlags
61 | }
62 |
63 | func (li *separatorLayoutItem) IdealSize() Size {
64 | return li.MinSize()
65 | }
66 |
67 | func (li *separatorLayoutItem) MinSize() Size {
68 | return SizeFrom96DPI(Size{2, 2}, li.ctx.dpi)
69 | }
70 |
--------------------------------------------------------------------------------
/path.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "syscall"
11 |
12 | "github.com/tailscale/win"
13 | )
14 |
15 | func knownFolderPath(id win.CSIDL) (string, error) {
16 | var buf [win.MAX_PATH]uint16
17 |
18 | if !win.SHGetSpecialFolderPath(0, &buf[0], id, false) {
19 | return "", newError("SHGetSpecialFolderPath failed")
20 | }
21 |
22 | return syscall.UTF16ToString(buf[0:]), nil
23 | }
24 |
25 | func AppDataPath() (string, error) {
26 | return knownFolderPath(win.CSIDL_APPDATA)
27 | }
28 |
29 | func CommonAppDataPath() (string, error) {
30 | return knownFolderPath(win.CSIDL_COMMON_APPDATA)
31 | }
32 |
33 | func LocalAppDataPath() (string, error) {
34 | return knownFolderPath(win.CSIDL_LOCAL_APPDATA)
35 | }
36 |
37 | func PersonalPath() (string, error) {
38 | return knownFolderPath(win.CSIDL_PERSONAL)
39 | }
40 |
41 | func SystemPath() (string, error) {
42 | return knownFolderPath(win.CSIDL_SYSTEM)
43 | }
44 |
45 | func DriveNames() ([]string, error) {
46 | bufLen := win.GetLogicalDriveStrings(0, nil)
47 | if bufLen == 0 {
48 | return nil, lastError("GetLogicalDriveStrings")
49 | }
50 | buf := make([]uint16, bufLen+1)
51 |
52 | bufLen = win.GetLogicalDriveStrings(bufLen+1, &buf[0])
53 | if bufLen == 0 {
54 | return nil, lastError("GetLogicalDriveStrings")
55 | }
56 |
57 | var names []string
58 |
59 | for i := 0; i < len(buf)-2; {
60 | name := syscall.UTF16ToString(buf[i:])
61 | names = append(names, name)
62 | i += len(name) + 1
63 | }
64 |
65 | return names, nil
66 | }
67 |
--------------------------------------------------------------------------------
/declarative/validators.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ValidatorRef struct {
14 | Validator walk.Validator
15 | }
16 |
17 | func (vr ValidatorRef) Create() (walk.Validator, error) {
18 | return vr.Validator, nil
19 | }
20 |
21 | type Range struct {
22 | Min float64
23 | Max float64
24 | }
25 |
26 | func (r Range) Create() (walk.Validator, error) {
27 | return walk.NewRangeValidator(r.Min, r.Max)
28 | }
29 |
30 | type Regexp struct {
31 | Pattern string
32 | }
33 |
34 | func (re Regexp) Create() (walk.Validator, error) {
35 | return walk.NewRegexpValidator(re.Pattern)
36 | }
37 |
38 | type SelRequired struct {
39 | }
40 |
41 | func (SelRequired) Create() (walk.Validator, error) {
42 | return walk.SelectionRequiredValidator(), nil
43 | }
44 |
45 | type dMultiValidator struct {
46 | validators []Validator
47 | }
48 |
49 | func (av dMultiValidator) Create() (walk.Validator, error) {
50 | var validators []walk.Validator
51 |
52 | for _, dv := range av.validators {
53 | if wv, err := dv.Create(); err != nil {
54 | return nil, err
55 | } else {
56 | validators = append(validators, wv)
57 | }
58 | }
59 |
60 | return &wMultiValidator{validators}, nil
61 | }
62 |
63 | type wMultiValidator struct {
64 | validators []walk.Validator
65 | }
66 |
67 | func (av *wMultiValidator) Validate(v interface{}) error {
68 | for _, validator := range av.validators {
69 | if err := validator.Validate(v); err != nil {
70 | return err
71 | }
72 | }
73 |
74 | return nil
75 | }
76 |
--------------------------------------------------------------------------------
/declarative/tableviewcolumn.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type Alignment1D uint
14 |
15 | const (
16 | AlignDefault = Alignment1D(walk.AlignDefault)
17 | AlignNear = Alignment1D(walk.AlignNear)
18 | AlignCenter = Alignment1D(walk.AlignCenter)
19 | AlignFar = Alignment1D(walk.AlignFar)
20 | )
21 |
22 | type TableViewColumn struct {
23 | Name string
24 | DataMember string
25 | Format string
26 | Title string
27 | Alignment Alignment1D
28 | Precision int
29 | Width int
30 | Hidden bool
31 | Frozen bool
32 | StyleCell func(style *walk.CellStyle)
33 | LessFunc func(i, j int) bool
34 | FormatFunc func(value interface{}) string
35 | }
36 |
37 | func (tvc TableViewColumn) Create(tv *walk.TableView) error {
38 | w := walk.NewTableViewColumn()
39 |
40 | if err := w.SetAlignment(walk.Alignment1D(tvc.Alignment)); err != nil {
41 | return err
42 | }
43 | w.SetDataMember(tvc.DataMember)
44 | if tvc.Format != "" {
45 | if err := w.SetFormat(tvc.Format); err != nil {
46 | return err
47 | }
48 | }
49 | if err := w.SetPrecision(tvc.Precision); err != nil {
50 | return err
51 | }
52 | w.SetName(tvc.Name)
53 | if err := w.SetTitle(tvc.Title); err != nil {
54 | return err
55 | }
56 | if err := w.SetVisible(!tvc.Hidden); err != nil {
57 | return err
58 | }
59 | if err := w.SetFrozen(tvc.Frozen); err != nil {
60 | return err
61 | }
62 | if err := w.SetWidth(tvc.Width); err != nil {
63 | return err
64 | }
65 | w.SetLessFunc(tvc.LessFunc)
66 | w.SetFormatFunc(tvc.FormatFunc)
67 |
68 | return tv.Columns().Add(w)
69 | }
70 |
--------------------------------------------------------------------------------
/examples/statusbar/statusbar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // This example demonstrates the status bar, including a size gripper
6 | // attached to the bottom of the main window.
7 | // The status bar has two items, one is dynamically updated and one includes an icon.
8 | package main
9 |
10 | import (
11 | "log"
12 |
13 | "github.com/tailscale/walk"
14 | . "github.com/tailscale/walk/declarative"
15 | )
16 |
17 | func main() {
18 | app, err := walk.InitApp()
19 | if err != nil {
20 | log.Fatal(err)
21 | }
22 |
23 | icon1, err := walk.NewIconFromFile("../img/check.ico")
24 | if err != nil {
25 | log.Fatal(err)
26 | }
27 | icon2, err := walk.NewIconFromFile("../img/stop.ico")
28 | if err != nil {
29 | log.Fatal(err)
30 | }
31 |
32 | var sbi *walk.StatusBarItem
33 |
34 | MainWindow{
35 | Title: "Walk Statusbar Example",
36 | MinSize: Size{600, 200},
37 | Layout: VBox{MarginsZero: true},
38 | StatusBarItems: []StatusBarItem{
39 | StatusBarItem{
40 | AssignTo: &sbi,
41 | Icon: icon1,
42 | Text: "click",
43 | Width: 80,
44 | OnClicked: func() {
45 | if sbi.Text() == "click" {
46 | sbi.SetText("again")
47 | sbi.SetIcon(icon2)
48 | } else {
49 | sbi.SetText("click")
50 | sbi.SetIcon(icon1)
51 | }
52 | },
53 | },
54 | StatusBarItem{
55 | Text: "left",
56 | ToolTipText: "no tooltip for me",
57 | },
58 | StatusBarItem{
59 | Text: "\tcenter",
60 | },
61 | StatusBarItem{
62 | Text: "\t\tright",
63 | },
64 | StatusBarItem{
65 | Icon: icon1,
66 | ToolTipText: "An icon with a tooltip",
67 | },
68 | },
69 | }.Create()
70 |
71 | app.Run()
72 | }
73 |
--------------------------------------------------------------------------------
/maptablemodel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "sort"
11 | )
12 |
13 | type mapTableModel struct {
14 | TableModelBase
15 | SorterBase
16 | dataMembers []string
17 | dataSource interface{}
18 | items []map[string]interface{}
19 | }
20 |
21 | func newMapTableModel(dataSource interface{}) (TableModel, error) {
22 | items, ok := dataSource.([]map[string]interface{})
23 | if !ok {
24 | return nil, newError("dataSource must be assignable to []map[string]interface{}")
25 | }
26 |
27 | return &mapTableModel{dataSource: dataSource, items: items}, nil
28 | }
29 |
30 | func (m *mapTableModel) setDataMembers(dataMembers []string) {
31 | m.dataMembers = dataMembers
32 | }
33 |
34 | func (m *mapTableModel) RowCount() int {
35 | return len(m.items)
36 | }
37 |
38 | func (m *mapTableModel) Value(row, col int) interface{} {
39 | if m.items[row] == nil {
40 | if populator, ok := m.dataSource.(Populator); ok {
41 | if err := populator.Populate(row); err != nil {
42 | return err
43 | }
44 | }
45 |
46 | if m.items[row] == nil {
47 | return nil
48 | }
49 | }
50 |
51 | return m.items[row][m.dataMembers[col]]
52 | }
53 |
54 | func (m *mapTableModel) Sort(col int, order SortOrder) error {
55 | m.col, m.order = col, order
56 |
57 | sort.Stable(m)
58 |
59 | m.changedPublisher.Publish()
60 |
61 | return nil
62 | }
63 |
64 | func (m *mapTableModel) Len() int {
65 | return m.RowCount()
66 | }
67 |
68 | func (m *mapTableModel) Less(i, j int) bool {
69 | col := m.SortedColumn()
70 |
71 | return less(m.Value(i, col), m.Value(j, col), m.SortOrder())
72 | }
73 |
74 | func (m *mapTableModel) Swap(i, j int) {
75 | m.items[i], m.items[j] = m.items[j], m.items[i]
76 | }
77 |
--------------------------------------------------------------------------------
/rectangle.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package walk
9 |
10 | import (
11 | "github.com/tailscale/win"
12 | )
13 |
14 | // Rectangle defines upper left corner with width and height region in 1/96" units, or native
15 | // pixels, or grid rows and columns.
16 | type Rectangle struct {
17 | X, Y, Width, Height int
18 | }
19 |
20 | func (r Rectangle) IsZero() bool {
21 | return r.X == 0 && r.Y == 0 && r.Width == 0 && r.Height == 0
22 | }
23 |
24 | // RectangleFromRECT converts r from a win.RECT to a Rectangle.
25 | func RectangleFromRECT(r win.RECT) Rectangle {
26 | return rectangleFromRECT(r)
27 | }
28 |
29 | func rectangleFromRECT(r win.RECT) Rectangle {
30 | return Rectangle{
31 | X: int(r.Left),
32 | Y: int(r.Top),
33 | Width: int(r.Right - r.Left),
34 | Height: int(r.Bottom - r.Top),
35 | }
36 | }
37 |
38 | func (r Rectangle) Left() int {
39 | return r.X
40 | }
41 |
42 | func (r Rectangle) Top() int {
43 | return r.Y
44 | }
45 |
46 | func (r Rectangle) Right() int {
47 | return r.X + r.Width - 1
48 | }
49 |
50 | func (r Rectangle) Bottom() int {
51 | return r.Y + r.Height - 1
52 | }
53 |
54 | func (r Rectangle) Location() Point {
55 | return Point{r.X, r.Y}
56 | }
57 |
58 | func (r *Rectangle) SetLocation(p Point) Rectangle {
59 | r.X = p.X
60 | r.Y = p.Y
61 |
62 | return *r
63 | }
64 |
65 | func (r Rectangle) Size() Size {
66 | return Size{r.Width, r.Height}
67 | }
68 |
69 | func (r *Rectangle) SetSize(s Size) Rectangle {
70 | r.Width = s.Width
71 | r.Height = s.Height
72 |
73 | return *r
74 | }
75 |
76 | func (r Rectangle) toRECT() win.RECT {
77 | return win.RECT{
78 | int32(r.X),
79 | int32(r.Y),
80 | int32(r.X + r.Width),
81 | int32(r.Y + r.Height),
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/declarative/linklabel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type LinkLabel struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // LinkLabel
50 |
51 | AssignTo **walk.LinkLabel
52 | OnLinkActivated walk.LinkLabelLinkEventHandler
53 | Text Property
54 | }
55 |
56 | func (ll LinkLabel) Create(builder *Builder) error {
57 | w, err := walk.NewLinkLabel(builder.Parent())
58 | if err != nil {
59 | return err
60 | }
61 |
62 | if ll.AssignTo != nil {
63 | *ll.AssignTo = w
64 | }
65 |
66 | return builder.InitWidget(ll, w, func() error {
67 | if ll.OnLinkActivated != nil {
68 | w.LinkActivated().Attach(ll.OnLinkActivated)
69 | }
70 |
71 | return nil
72 | })
73 | }
74 |
--------------------------------------------------------------------------------
/declarative/toolbutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ToolButton struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Button
50 |
51 | Image Property
52 | OnClicked walk.EventHandler
53 | Text Property
54 |
55 | // ToolButton
56 |
57 | AssignTo **walk.ToolButton
58 | }
59 |
60 | func (tb ToolButton) Create(builder *Builder) error {
61 | w, err := walk.NewToolButton(builder.Parent())
62 | if err != nil {
63 | return err
64 | }
65 |
66 | if tb.AssignTo != nil {
67 | *tb.AssignTo = w
68 | }
69 |
70 | return builder.InitWidget(tb, w, func() error {
71 | if tb.OnClicked != nil {
72 | w.Clicked().Attach(tb.OnClicked)
73 | }
74 |
75 | return nil
76 | })
77 | }
78 |
--------------------------------------------------------------------------------
/examples/progressindicator/pi.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "fmt"
9 | "log"
10 | "os"
11 | "time"
12 |
13 | "github.com/tailscale/walk"
14 | )
15 |
16 | func main() {
17 | _, err := walk.InitApp()
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 |
22 | if err := RunMyDialog(nil); err != nil {
23 | log.Fatal(err)
24 | }
25 | }
26 |
27 | type MyDialog struct {
28 | *walk.Dialog
29 | ui myDialogUI
30 | }
31 |
32 | func (dlg *MyDialog) setState(state walk.PIState) {
33 | if err := dlg.ProgressIndicator().SetState(state); err != nil {
34 | log.Print(err)
35 | }
36 | }
37 |
38 | func RunMyDialog(owner walk.Form) error {
39 | dlg := new(MyDialog)
40 | if err := dlg.init(owner); err != nil {
41 | return err
42 | }
43 |
44 | dlg.ui.indeterminateBtn.Clicked().Attach(func() {
45 | fmt.Println("SetState indeterminate")
46 | dlg.setState(walk.PIIndeterminate)
47 | })
48 | dlg.ui.noProgressBtn.Clicked().Attach(func() {
49 | fmt.Println("SetState noprogress")
50 | dlg.setState(walk.PINoProgress)
51 | })
52 |
53 | dlg.ui.normalBtn.Clicked().Attach(func() {
54 | fmt.Println("SetState normal")
55 | dlg.setState(walk.PINormal)
56 | })
57 |
58 | dlg.ui.errBtn.Clicked().Attach(func() {
59 | fmt.Println("SetState error")
60 | dlg.setState(walk.PIError)
61 | })
62 |
63 | dlg.ui.pausedBtn.Clicked().Attach(func() {
64 | fmt.Println("SetState paused")
65 | dlg.setState(walk.PIPaused)
66 | })
67 |
68 | dlg.ui.startBtn.Clicked().Attach(func() {
69 | go func() {
70 | dlg.ProgressIndicator().SetTotal(100)
71 | var i uint32
72 | for i = 0; i < 100; i++ {
73 | fmt.Println("SetProgress", i)
74 | time.Sleep(100 * time.Millisecond)
75 | if err := dlg.ProgressIndicator().SetCompleted(i); err != nil {
76 | log.Print(err)
77 | }
78 | }
79 | }()
80 | })
81 |
82 | os.Exit(dlg.Run())
83 | return nil
84 | }
85 |
--------------------------------------------------------------------------------
/declarative/spacer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type HSpacer struct {
14 | // Window
15 |
16 | MaxSize Size
17 | MinSize Size
18 | Name string
19 |
20 | // Widget
21 |
22 | Column int
23 | ColumnSpan int
24 | Row int
25 | RowSpan int
26 | StretchFactor int
27 |
28 | // Spacer
29 |
30 | GreedyLocallyOnly bool
31 | Size int
32 | }
33 |
34 | func (hs HSpacer) Create(builder *Builder) (err error) {
35 | var flags walk.LayoutFlags
36 | if hs.Size == 0 {
37 | flags = walk.ShrinkableHorz | walk.GrowableHorz | walk.GreedyHorz
38 | }
39 |
40 | var w *walk.Spacer
41 | if w, err = walk.NewSpacerWithCfg(builder.Parent(), &walk.SpacerCfg{
42 | LayoutFlags: flags,
43 | SizeHint: Size{Width: hs.Size}.toW(),
44 | GreedyLocallyOnly: hs.GreedyLocallyOnly,
45 | }); err != nil {
46 | return
47 | }
48 |
49 | return builder.InitWidget(hs, w, nil)
50 | }
51 |
52 | type VSpacer struct {
53 | // Window
54 |
55 | MaxSize Size
56 | MinSize Size
57 | Name string
58 |
59 | // Widget
60 |
61 | Column int
62 | ColumnSpan int
63 | Row int
64 | RowSpan int
65 | StretchFactor int
66 |
67 | // Spacer
68 |
69 | GreedyLocallyOnly bool
70 | Size int
71 | }
72 |
73 | func (vs VSpacer) Create(builder *Builder) (err error) {
74 | var flags walk.LayoutFlags
75 | if vs.Size == 0 {
76 | flags = walk.ShrinkableVert | walk.GrowableVert | walk.GreedyVert
77 | }
78 |
79 | var w *walk.Spacer
80 | if w, err = walk.NewSpacerWithCfg(builder.Parent(), &walk.SpacerCfg{
81 | LayoutFlags: flags,
82 | SizeHint: Size{Height: vs.Size}.toW(),
83 | GreedyLocallyOnly: vs.GreedyLocallyOnly,
84 | }); err != nil {
85 | return
86 | }
87 |
88 | return builder.InitWidget(vs, w, nil)
89 | }
90 |
--------------------------------------------------------------------------------
/declarative/tabpage.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type TabPage struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | AlwaysConsumeSpace bool
41 | Column int
42 | ColumnSpan int
43 | GraphicsEffects []walk.WidgetGraphicsEffect
44 | Row int
45 | RowSpan int
46 | StretchFactor int
47 |
48 | // Container
49 |
50 | Children []Widget
51 | DataBinder DataBinder
52 | Layout Layout
53 |
54 | // TabPage
55 |
56 | AssignTo **walk.TabPage
57 | Content Widget
58 | Image Property
59 | Title Property
60 | }
61 |
62 | func (tp TabPage) Create(builder *Builder) error {
63 | w, err := walk.NewTabPage()
64 | if err != nil {
65 | return err
66 | }
67 |
68 | if tp.AssignTo != nil {
69 | *tp.AssignTo = w
70 | }
71 |
72 | return builder.InitWidget(tp, w, func() error {
73 | if tp.Content != nil && len(tp.Children) == 0 {
74 | if err := tp.Content.Create(builder); err != nil {
75 | return err
76 | }
77 | }
78 |
79 | return nil
80 | })
81 | }
82 |
--------------------------------------------------------------------------------
/declarative/datelabel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type DateLabel struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // static
50 |
51 | TextColor walk.Color
52 |
53 | // DateLabel
54 |
55 | AssignTo **walk.DateLabel
56 | Date Property
57 | Format Property
58 | TextAlignment Alignment1D
59 | }
60 |
61 | func (dl DateLabel) Create(builder *Builder) error {
62 | w, err := walk.NewDateLabel(builder.Parent())
63 | if err != nil {
64 | return err
65 | }
66 |
67 | if dl.AssignTo != nil {
68 | *dl.AssignTo = w
69 | }
70 |
71 | return builder.InitWidget(dl, w, func() error {
72 | if err := w.SetTextAlignment(walk.Alignment1D(dl.TextAlignment)); err != nil {
73 | return err
74 | }
75 |
76 | w.SetTextColor(dl.TextColor)
77 |
78 | return nil
79 | })
80 | }
81 |
--------------------------------------------------------------------------------
/iconcache.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | var iconCache *IconCache
10 |
11 | func init() {
12 | AppendToWalkInit(func() {
13 | iconCache = NewIconCache()
14 | })
15 | }
16 |
17 | type IconCache struct {
18 | imageAndDPI2Bitmap map[imageAndDPI]*Bitmap
19 | imageAndDPI2Icon map[imageAndDPI]*Icon
20 | }
21 |
22 | type imageAndDPI struct {
23 | image Image
24 | dpi int
25 | }
26 |
27 | func NewIconCache() *IconCache {
28 | return &IconCache{
29 | imageAndDPI2Bitmap: make(map[imageAndDPI]*Bitmap),
30 | imageAndDPI2Icon: make(map[imageAndDPI]*Icon),
31 | }
32 | }
33 |
34 | func (ic *IconCache) Clear() {
35 | for key, bmp := range ic.imageAndDPI2Bitmap {
36 | bmp.Dispose()
37 | delete(ic.imageAndDPI2Bitmap, key)
38 | }
39 | for key, ico := range ic.imageAndDPI2Icon {
40 | ico.Dispose()
41 | delete(ic.imageAndDPI2Icon, key)
42 | }
43 | }
44 |
45 | func (ic *IconCache) Dispose() {
46 | ic.Clear()
47 | }
48 |
49 | func (ic *IconCache) Bitmap(image Image, dpi int) (*Bitmap, error) {
50 | key := imageAndDPI{image, dpi}
51 |
52 | if bmp, ok := ic.imageAndDPI2Bitmap[key]; ok {
53 | return bmp, nil
54 | }
55 |
56 | size := SizeFrom96DPI(image.Size(), dpi)
57 |
58 | bmp, err := NewBitmapFromImageWithSize(image, size)
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | ic.imageAndDPI2Bitmap[key] = bmp
64 |
65 | return bmp, nil
66 | }
67 |
68 | func (ic *IconCache) Icon(image Image, dpi int) (*Icon, error) {
69 | key := imageAndDPI{image, dpi}
70 |
71 | if ico, ok := ic.imageAndDPI2Icon[key]; ok {
72 | return ico, nil
73 | }
74 |
75 | if ico, ok := image.(*Icon); ok {
76 | if ico.handleForDPI(dpi) != 0 {
77 | ic.imageAndDPI2Icon[key] = ico
78 | return ico, nil
79 | }
80 | }
81 |
82 | ico, err := newIconFromImageForDPI(image, dpi)
83 | if err != nil {
84 | return nil, err
85 | }
86 |
87 | ic.imageAndDPI2Icon[key] = ico
88 |
89 | return ico, nil
90 | }
91 |
--------------------------------------------------------------------------------
/declarative/progressbar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ProgressBar struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // ProgressBar
50 |
51 | AssignTo **walk.ProgressBar
52 | MarqueeMode bool
53 | MaxValue int
54 | MinValue int
55 | Value int
56 | }
57 |
58 | func (pb ProgressBar) Create(builder *Builder) error {
59 | w, err := walk.NewProgressBar(builder.Parent())
60 | if err != nil {
61 | return err
62 | }
63 |
64 | if pb.AssignTo != nil {
65 | *pb.AssignTo = w
66 | }
67 |
68 | return builder.InitWidget(pb, w, func() error {
69 | if pb.MaxValue > pb.MinValue {
70 | w.SetRange(pb.MinValue, pb.MaxValue)
71 | }
72 | w.SetValue(pb.Value)
73 |
74 | if err := w.SetMarqueeMode(pb.MarqueeMode); err != nil {
75 | return err
76 | }
77 |
78 | return nil
79 | })
80 | }
81 |
--------------------------------------------------------------------------------
/mouseevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | type MouseButton int
14 |
15 | const (
16 | LeftButton MouseButton = win.MK_LBUTTON
17 | RightButton MouseButton = win.MK_RBUTTON
18 | MiddleButton MouseButton = win.MK_MBUTTON
19 | )
20 |
21 | type mouseEventHandlerInfo struct {
22 | handler MouseEventHandler
23 | once bool
24 | }
25 |
26 | // MouseEventHandler is called for mouse events. x and y are measured in native pixels.
27 | type MouseEventHandler func(x, y int, button MouseButton)
28 |
29 | type MouseEvent struct {
30 | handlers []mouseEventHandlerInfo
31 | }
32 |
33 | func (e *MouseEvent) Attach(handler MouseEventHandler) int {
34 | handlerInfo := mouseEventHandlerInfo{handler, false}
35 |
36 | for i, h := range e.handlers {
37 | if h.handler == nil {
38 | e.handlers[i] = handlerInfo
39 | return i
40 | }
41 | }
42 |
43 | e.handlers = append(e.handlers, handlerInfo)
44 |
45 | return len(e.handlers) - 1
46 | }
47 |
48 | func (e *MouseEvent) Detach(handle int) {
49 | e.handlers[handle].handler = nil
50 | }
51 |
52 | func (e *MouseEvent) Once(handler MouseEventHandler) {
53 | i := e.Attach(handler)
54 | e.handlers[i].once = true
55 | }
56 |
57 | type MouseEventPublisher struct {
58 | event MouseEvent
59 | }
60 |
61 | func (p *MouseEventPublisher) Event() *MouseEvent {
62 | return &p.event
63 | }
64 |
65 | // Publish publishes mouse event. x and y are measured in native pixels.
66 | func (p *MouseEventPublisher) Publish(x, y int, button MouseButton) {
67 | for i, h := range p.event.handlers {
68 | if h.handler != nil {
69 | h.handler(x, y, button)
70 |
71 | if h.once {
72 | p.event.Detach(i)
73 | }
74 | }
75 | }
76 | }
77 |
78 | func MouseWheelEventDelta(button MouseButton) int {
79 | return int(int32(button) >> 16)
80 | }
81 |
82 | func MouseWheelEventKeyState(button MouseButton) int {
83 | return int(int32(button) & 0xFFFF)
84 | }
85 |
--------------------------------------------------------------------------------
/declarative/numberlabel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type NumberLabel struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // static
50 |
51 | TextColor walk.Color
52 |
53 | // NumberLabel
54 |
55 | AssignTo **walk.NumberLabel
56 | Decimals Property
57 | Suffix Property
58 | TextAlignment Alignment1D
59 | Value Property
60 | }
61 |
62 | func (nl NumberLabel) Create(builder *Builder) error {
63 | w, err := walk.NewNumberLabel(builder.Parent())
64 | if err != nil {
65 | return err
66 | }
67 |
68 | if nl.AssignTo != nil {
69 | *nl.AssignTo = w
70 | }
71 |
72 | return builder.InitWidget(nl, w, func() error {
73 | if err := w.SetTextAlignment(walk.Alignment1D(nl.TextAlignment)); err != nil {
74 | return err
75 | }
76 |
77 | w.SetTextColor(nl.TextColor)
78 |
79 | return nil
80 | })
81 | }
82 |
--------------------------------------------------------------------------------
/declarative/scrollview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ScrollView struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Container
50 |
51 | Children []Widget
52 | DataBinder DataBinder
53 | Layout Layout
54 |
55 | // ScrollView
56 |
57 | AssignTo **walk.ScrollView
58 | HorizontalFixed bool
59 | VerticalFixed bool
60 | }
61 |
62 | func (sv ScrollView) Create(builder *Builder) error {
63 | w, err := walk.NewScrollView(builder.Parent())
64 | if err != nil {
65 | return err
66 | }
67 |
68 | if sv.AssignTo != nil {
69 | *sv.AssignTo = w
70 | }
71 |
72 | w.SetSuspended(true)
73 | builder.Defer(func() error {
74 | w.SetSuspended(false)
75 | return nil
76 | })
77 |
78 | w.SetScrollbars(!sv.HorizontalFixed, !sv.VerticalFixed)
79 |
80 | return builder.InitWidget(sv, w, func() error {
81 | return nil
82 | })
83 | }
84 |
--------------------------------------------------------------------------------
/examples/notifyicon/notifyicon.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | func main() {
14 | app, err := walk.InitApp()
15 | if err != nil {
16 | log.Fatal(err)
17 | }
18 |
19 | // We load our icon from a file.
20 | icon, err := walk.Resources.Icon("../img/stop.ico")
21 | if err != nil {
22 | log.Fatal(err)
23 | }
24 |
25 | // Create the notify icon and make sure we clean it up on exit.
26 | ni, err := walk.NewNotifyIcon()
27 | if err != nil {
28 | log.Fatal(err)
29 | }
30 | defer ni.Dispose()
31 |
32 | // Set the icon and a tool tip text.
33 | if err := ni.SetIcon(icon); err != nil {
34 | log.Fatal(err)
35 | }
36 | if err := ni.SetToolTip("Click for info or use the context menu to exit."); err != nil {
37 | log.Fatal(err)
38 | }
39 |
40 | // When the left mouse button is pressed, bring up our balloon.
41 | ni.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
42 | if button != walk.LeftButton {
43 | return
44 | }
45 |
46 | if err := ni.ShowCustom(
47 | "Walk NotifyIcon Example",
48 | "There are multiple ShowX methods sporting different icons.",
49 | icon); err != nil {
50 |
51 | log.Fatal(err)
52 | }
53 | })
54 |
55 | // We put an exit action into the context menu.
56 | exitAction := walk.NewAction()
57 | if err := exitAction.SetText("E&xit"); err != nil {
58 | log.Fatal(err)
59 | }
60 | exitAction.Triggered().Attach(func() { walk.App().Exit(0) })
61 | if err := ni.ContextMenu().Actions().Add(exitAction); err != nil {
62 | log.Fatal(err)
63 | }
64 |
65 | // The notify icon is hidden initially, so we have to make it visible.
66 | if err := ni.SetVisible(true); err != nil {
67 | log.Fatal(err)
68 | }
69 |
70 | // Now that the icon is visible, we can bring up an info balloon.
71 | if err := ni.ShowInfo("Walk NotifyIcon Example", "Click the icon to show again."); err != nil {
72 | log.Fatal(err)
73 | }
74 |
75 | // Run the message loop.
76 | app.Run()
77 | }
78 |
--------------------------------------------------------------------------------
/declarative/radiobutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type RadioButton struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Button
50 |
51 | OnClicked walk.EventHandler
52 | Text Property
53 |
54 | // RadioButton
55 |
56 | AssignTo **walk.RadioButton
57 | TextOnLeftSide bool
58 | Value interface{}
59 | }
60 |
61 | func (rb RadioButton) Create(builder *Builder) error {
62 | w, err := walk.NewRadioButton(builder.Parent())
63 | if err != nil {
64 | return err
65 | }
66 |
67 | if rb.AssignTo != nil {
68 | *rb.AssignTo = w
69 | }
70 |
71 | return builder.InitWidget(rb, w, func() error {
72 | w.SetValue(rb.Value)
73 |
74 | if err := w.SetTextOnLeftSide(rb.TextOnLeftSide); err != nil {
75 | return err
76 | }
77 |
78 | if rb.OnClicked != nil {
79 | w.Clicked().Attach(rb.OnClicked)
80 | }
81 |
82 | return nil
83 | })
84 | }
85 |
--------------------------------------------------------------------------------
/monitor.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Tailscale Inc & AUTHORS
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | //go:build windows
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "unsafe"
11 |
12 | "github.com/tailscale/win"
13 | )
14 |
15 | // Monitor is a reference to an individual monitor attached to the current machine.
16 | type Monitor win.HMONITOR
17 |
18 | // WorkArea returns the rectangle representing the bounds of the monitor in
19 | // virtual screen coordinates, excluding taskbars and application bars.
20 | func (m Monitor) WorkArea() Rectangle {
21 | mi := m.getInfo()
22 | return rectangleFromRECT(mi.RcWork)
23 | }
24 |
25 | // Rectangle returns the rectangle representing the bounds of the monitor in
26 | // virtual screen coordinates.
27 | func (m Monitor) Rectangle() Rectangle {
28 | mi := m.getInfo()
29 | return rectangleFromRECT(mi.RcMonitor)
30 | }
31 |
32 | // IsPrimary returns whether m is classified as the primary monitor.
33 | func (m Monitor) IsPrimary() bool {
34 | if !m.IsValid() {
35 | return false
36 | }
37 | mi := m.getInfo()
38 | return mi.DwFlags&win.MONITORINFOF_PRIMARY != 0
39 | }
40 |
41 | // DPI returns m's DPI. If m does not refer to a valid monitor, then the legacy
42 | // screen DPI is returned.
43 | func (m Monitor) DPI() (int, error) {
44 | if !m.IsValid() {
45 | return screenDPI(), nil
46 | }
47 |
48 | var dpiX, dpiY uint32
49 | if hr := win.GetDpiForMonitor(win.HMONITOR(m), win.MDT_EFFECTIVE_DPI, &dpiX, &dpiY); win.FAILED(hr) {
50 | return 0, errorFromHRESULT("GetDpiForMonitor", hr)
51 | }
52 |
53 | // X and Y are always identical, so we only need to return one of them.
54 | return int(dpiX), nil
55 | }
56 |
57 | // IsValid returns whether m refers to a valid monitor.
58 | func (m Monitor) IsValid() bool {
59 | return m != 0
60 | }
61 |
62 | func (m Monitor) getInfo() (mi win.MONITORINFO) {
63 | mi.CbSize = uint32(unsafe.Sizeof(mi))
64 | win.GetMonitorInfo(win.HMONITOR(m), &mi)
65 | return mi
66 | }
67 |
68 | // PrimaryMonitor obtains the Monitor associated with the primary monitor.
69 | func PrimaryMonitor() Monitor {
70 | return Monitor(win.MonitorFromWindow(0, win.MONITOR_DEFAULTTOPRIMARY))
71 | }
72 |
--------------------------------------------------------------------------------
/declarative/groupbox.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type GroupBox struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Container
50 |
51 | Children []Widget
52 | DataBinder DataBinder
53 | Layout Layout
54 |
55 | // GroupBox
56 |
57 | AssignTo **walk.GroupBox
58 | Checkable bool
59 | Checked Property
60 | Title string
61 | }
62 |
63 | func (gb GroupBox) Create(builder *Builder) error {
64 | w, err := walk.NewGroupBox(builder.Parent())
65 | if err != nil {
66 | return err
67 | }
68 |
69 | if gb.AssignTo != nil {
70 | *gb.AssignTo = w
71 | }
72 |
73 | w.SetSuspended(true)
74 | builder.Defer(func() error {
75 | w.SetSuspended(false)
76 | return nil
77 | })
78 |
79 | return builder.InitWidget(gb, w, func() error {
80 | if err := w.SetTitle(gb.Title); err != nil {
81 | return err
82 | }
83 |
84 | w.SetCheckable(gb.Checkable)
85 |
86 | return nil
87 | })
88 | }
89 |
--------------------------------------------------------------------------------
/declarative/splitbutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type SplitButton struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Button
50 |
51 | Image Property
52 | Text Property
53 | OnClicked walk.EventHandler
54 |
55 | // SplitButton
56 |
57 | AssignTo **walk.SplitButton
58 | ImageAboveText bool
59 | MenuItems []MenuItem
60 | }
61 |
62 | func (sb SplitButton) Create(builder *Builder) error {
63 | w, err := walk.NewSplitButton(builder.Parent())
64 | if err != nil {
65 | return err
66 | }
67 |
68 | if sb.AssignTo != nil {
69 | *sb.AssignTo = w
70 | }
71 |
72 | builder.deferBuildMenuActions(w.Menu(), sb.MenuItems)
73 |
74 | return builder.InitWidget(sb, w, func() error {
75 | if err := w.SetImageAboveText(sb.ImageAboveText); err != nil {
76 | return err
77 | }
78 |
79 | if sb.OnClicked != nil {
80 | w.Clicked().Attach(sb.OnClicked)
81 | }
82 |
83 | return nil
84 | })
85 | }
86 |
--------------------------------------------------------------------------------
/dropfilesevent.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "syscall"
11 |
12 | "github.com/tailscale/win"
13 | )
14 |
15 | type dropFilesEventHandlerInfo struct {
16 | handler DropFilesEventHandler
17 | once bool
18 | }
19 |
20 | type DropFilesEventHandler func([]string)
21 |
22 | type DropFilesEvent struct {
23 | hWnd win.HWND
24 | handlers []dropFilesEventHandlerInfo
25 | }
26 |
27 | func (e *DropFilesEvent) Attach(handler DropFilesEventHandler) int {
28 | if len(e.handlers) == 0 {
29 | win.DragAcceptFiles(e.hWnd, true)
30 | }
31 |
32 | handlerInfo := dropFilesEventHandlerInfo{handler, false}
33 |
34 | for i, h := range e.handlers {
35 | if h.handler == nil {
36 | e.handlers[i] = handlerInfo
37 | return i
38 | }
39 | }
40 |
41 | e.handlers = append(e.handlers, handlerInfo)
42 |
43 | return len(e.handlers) - 1
44 | }
45 |
46 | func (e *DropFilesEvent) Detach(handle int) {
47 | e.handlers[handle].handler = nil
48 |
49 | for _, h := range e.handlers {
50 | if h.handler != nil {
51 | return
52 | }
53 | }
54 |
55 | win.DragAcceptFiles(e.hWnd, false)
56 | }
57 |
58 | func (e *DropFilesEvent) Once(handler DropFilesEventHandler) {
59 | i := e.Attach(handler)
60 | e.handlers[i].once = true
61 | }
62 |
63 | type DropFilesEventPublisher struct {
64 | event DropFilesEvent
65 | }
66 |
67 | func (p *DropFilesEventPublisher) Event(hWnd win.HWND) *DropFilesEvent {
68 | p.event.hWnd = hWnd
69 | return &p.event
70 | }
71 |
72 | func (p *DropFilesEventPublisher) Publish(hDrop win.HDROP) {
73 | var files []string
74 |
75 | n := win.DragQueryFile(hDrop, 0xFFFFFFFF, nil, 0)
76 | for i := 0; i < int(n); i++ {
77 | bufSize := uint(512)
78 | buf := make([]uint16, bufSize)
79 | if win.DragQueryFile(hDrop, uint(i), &buf[0], bufSize) > 0 {
80 | files = append(files, syscall.UTF16ToString(buf))
81 | }
82 | }
83 | win.DragFinish(hDrop)
84 |
85 | for i, h := range p.event.handlers {
86 | if h.handler != nil {
87 | h.handler(files)
88 |
89 | if h.once {
90 | p.event.Detach(i)
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/dpicache/dpicache.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Tailscale Inc. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | // Package dpicache provides a type-agnostic cache for structures whose data
9 | // must be adjusted for various DPI settings.
10 | package dpicache
11 |
12 | import (
13 | "sync"
14 | )
15 |
16 | var (
17 | // -> dpi ->
18 | cache map[any]map[int]any
19 | mu sync.Mutex
20 | )
21 |
22 | // DPICopier is the interface that must be implemented by any type that is
23 | // intended to work with dpicache.
24 | type DPICopier[T any] interface {
25 | // CopyForDPI creates a copy of the receiver that is appropriate for dpi.
26 | // Its return value must not require a separate method call to release its
27 | // resources; use a finalizer if necessary.
28 | CopyForDPI(dpi int) T
29 | }
30 |
31 | // DPIGetter is an optional interface that returns the DPI of an existing
32 | // value. When available, InstanceForDPI uses DPIGetter as an optimization hint.
33 | type DPIGetter interface {
34 | DPI() int
35 | }
36 |
37 | // InstanceForDPI returns an instance of inst that is appropriate for dpi. If
38 | // inst is already appropriate for dpi, InstanceForDPI may simply return inst.
39 | // Otherwise a copy of inst may be made and cached for future use, keyed on
40 | // inst itself.
41 | func InstanceForDPI[T DPICopier[T]](inst T, dpi int) T {
42 | if getter, ok := any(inst).(DPIGetter); ok && getter.DPI() == dpi {
43 | // The DPI is already what we want; nothing needs to be done.
44 | return inst
45 | }
46 |
47 | mu.Lock()
48 | defer mu.Unlock()
49 |
50 | if cache == nil {
51 | cache = make(map[any]map[int]any)
52 | }
53 |
54 | sub := cache[inst]
55 | if sub == nil {
56 | sub = make(map[int]any)
57 | cache[inst] = sub
58 | }
59 |
60 | if s := sub[dpi]; s != nil {
61 | return s.(T)
62 | }
63 |
64 | t := inst.CopyForDPI(dpi)
65 | sub[dpi] = t
66 |
67 | return t
68 | }
69 |
70 | // Delete removes any cached variants of inst from the DPI cache, if present.
71 | func Delete[T DPICopier[T]](inst T) {
72 | mu.Lock()
73 | defer mu.Unlock()
74 |
75 | delete(cache, inst)
76 | }
77 |
--------------------------------------------------------------------------------
/progressbar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | type ProgressBar struct {
14 | WidgetBase
15 | }
16 |
17 | func NewProgressBar(parent Container) (*ProgressBar, error) {
18 | pb := new(ProgressBar)
19 |
20 | if err := InitWidget(
21 | pb,
22 | parent,
23 | "msctls_progress32",
24 | win.WS_VISIBLE,
25 | 0); err != nil {
26 | return nil, err
27 | }
28 |
29 | return pb, nil
30 | }
31 |
32 | func (pb *ProgressBar) MinValue() int {
33 | return int(pb.SendMessage(win.PBM_GETRANGE, 1, 0))
34 | }
35 |
36 | func (pb *ProgressBar) MaxValue() int {
37 | return int(pb.SendMessage(win.PBM_GETRANGE, 0, 0))
38 | }
39 |
40 | func (pb *ProgressBar) SetRange(min, max int) {
41 | pb.SendMessage(win.PBM_SETRANGE32, uintptr(min), uintptr(max))
42 | }
43 |
44 | func (pb *ProgressBar) Value() int {
45 | return int(pb.SendMessage(win.PBM_GETPOS, 0, 0))
46 | }
47 |
48 | func (pb *ProgressBar) SetValue(value int) {
49 | pb.SendMessage(win.PBM_SETPOS, uintptr(value), 0)
50 | }
51 |
52 | func (pb *ProgressBar) MarqueeMode() bool {
53 | return pb.hasStyleBits(win.PBS_MARQUEE)
54 | }
55 |
56 | func (pb *ProgressBar) SetMarqueeMode(marqueeMode bool) error {
57 | if err := pb.ensureStyleBits(win.PBS_MARQUEE, marqueeMode); err != nil {
58 | return err
59 | }
60 |
61 | pb.SendMessage(win.PBM_SETMARQUEE, uintptr(win.BoolToBOOL(marqueeMode)), 0)
62 |
63 | return nil
64 | }
65 |
66 | func (pb *ProgressBar) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
67 | return &progressBarLayoutItem{
68 | idealSize: pb.dialogBaseUnitsToPixels(Size{50, 14}),
69 | minSize: pb.dialogBaseUnitsToPixels(Size{10, 14}),
70 | }
71 | }
72 |
73 | type progressBarLayoutItem struct {
74 | LayoutItemBase
75 | idealSize Size // in native pixels
76 | minSize Size // in native pixels
77 | }
78 |
79 | func (*progressBarLayoutItem) LayoutFlags() LayoutFlags {
80 | return ShrinkableHorz | GrowableHorz | GreedyHorz
81 | }
82 |
83 | func (li *progressBarLayoutItem) IdealSize() Size {
84 | return li.idealSize
85 | }
86 |
87 | func (li *progressBarLayoutItem) MinSize() Size {
88 | return li.minSize
89 | }
90 |
--------------------------------------------------------------------------------
/declarative/imageview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ImageViewMode int
14 |
15 | const (
16 | ImageViewModeIdeal = ImageViewMode(walk.ImageViewModeIdeal)
17 | ImageViewModeCorner = ImageViewMode(walk.ImageViewModeCorner)
18 | ImageViewModeCenter = ImageViewMode(walk.ImageViewModeCenter)
19 | ImageViewModeShrink = ImageViewMode(walk.ImageViewModeShrink)
20 | ImageViewModeZoom = ImageViewMode(walk.ImageViewModeZoom)
21 | ImageViewModeStretch = ImageViewMode(walk.ImageViewModeStretch)
22 | )
23 |
24 | type ImageView struct {
25 | // Window
26 |
27 | Accessibility Accessibility
28 | Background Brush
29 | ContextMenuItems []MenuItem
30 | DoubleBuffering bool
31 | Enabled Property
32 | Font Font
33 | MaxSize Size
34 | MinSize Size
35 | Name string
36 | OnBoundsChanged walk.EventHandler
37 | OnKeyDown walk.KeyEventHandler
38 | OnKeyPress walk.KeyEventHandler
39 | OnKeyUp walk.KeyEventHandler
40 | OnMouseDown walk.MouseEventHandler
41 | OnMouseMove walk.MouseEventHandler
42 | OnMouseUp walk.MouseEventHandler
43 | OnSizeChanged walk.EventHandler
44 | Persistent bool
45 | RightToLeftReading bool
46 | ToolTipText Property
47 | Visible Property
48 |
49 | // Widget
50 |
51 | Alignment Alignment2D
52 | AlwaysConsumeSpace bool
53 | Column int
54 | ColumnSpan int
55 | GraphicsEffects []walk.WidgetGraphicsEffect
56 | Row int
57 | RowSpan int
58 | StretchFactor int
59 |
60 | // ImageView
61 |
62 | AssignTo **walk.ImageView
63 | Image Property
64 | Margin Property
65 | Mode ImageViewMode
66 | }
67 |
68 | func (iv ImageView) Create(builder *Builder) error {
69 | w, err := walk.NewImageView(builder.Parent())
70 | if err != nil {
71 | return err
72 | }
73 |
74 | if iv.AssignTo != nil {
75 | *iv.AssignTo = w
76 | }
77 |
78 | return builder.InitWidget(iv, w, func() error {
79 | w.SetMode(walk.ImageViewMode(iv.Mode))
80 |
81 | return nil
82 | })
83 | }
84 |
--------------------------------------------------------------------------------
/examples/imageicon/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "image"
9 | "image/color"
10 | "image/draw"
11 | "log"
12 |
13 | "github.com/tailscale/walk"
14 |
15 | . "github.com/tailscale/walk/declarative"
16 | )
17 |
18 | func main() {
19 | app, err := walk.InitApp()
20 | if err != nil {
21 | log.Fatal(err)
22 | }
23 |
24 | var mw *walk.MainWindow
25 | var windowIcon *walk.Icon
26 | counter := 0
27 |
28 | if err := (MainWindow{
29 | AssignTo: &mw,
30 | Title: "Walk Image Icon Example",
31 | Layout: HBox{},
32 | Children: []Widget{
33 | HSpacer{},
34 | PushButton{
35 | Text: "Push me",
36 | OnClicked: func() {
37 | ic, err := walk.NewIconFromImage(makeDigitImage(counter))
38 | if err != nil {
39 | return
40 | }
41 | counter++
42 | mw.SetIcon(ic)
43 | if windowIcon != nil {
44 | windowIcon.Dispose()
45 | }
46 | windowIcon = ic
47 | },
48 | },
49 | HSpacer{},
50 | },
51 | }.Create()); err != nil {
52 | log.Fatal(err)
53 | }
54 |
55 | app.Run()
56 | }
57 |
58 | // A
59 | // F B
60 | // G
61 | // E C
62 | // D
63 | var hexdigits = []int{0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}
64 |
65 | //0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
66 |
67 | type seg struct {
68 | sx, sy int
69 | dx, dy int
70 | }
71 |
72 | var segments = []seg{
73 | {0, 0, 1, 0},
74 | {1, 0, 0, 1},
75 | {1, 1, 0, 1},
76 | {0, 2, 1, 0},
77 | {0, 1, 0, 1},
78 | {0, 0, 0, 1},
79 | {0, 1, 1, 0},
80 | }
81 |
82 | func digit(im draw.Image, col color.Color, x, y, size, digit int) {
83 | n := hexdigits[digit]
84 | for _, s := range segments {
85 | if n&1 != 0 {
86 | xx, yy := x+s.sx*size, y+s.sy*size
87 | for i := 0; i <= size; i++ {
88 | im.Set(xx, yy, col)
89 | xx += s.dx
90 | yy += s.dy
91 | }
92 | }
93 | n >>= 1
94 | }
95 | }
96 |
97 | func makeDigitImage(n int) image.Image {
98 | im := image.NewRGBA(image.Rect(0, 0, 16, 16))
99 | for p := 11; p >= 0; p -= 5 {
100 | digit(im, color.Black, p, 5, 3, n%10)
101 | n /= 10
102 | }
103 | return im
104 | }
105 |
--------------------------------------------------------------------------------
/examples/radiobutton/radiobutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "fmt"
9 | "log"
10 |
11 | "github.com/tailscale/walk"
12 |
13 | . "github.com/tailscale/walk/declarative"
14 | )
15 |
16 | type Foo struct {
17 | Bar string
18 | Baz int
19 | }
20 |
21 | func main() {
22 | app, err := walk.InitApp()
23 | if err != nil {
24 | log.Fatal(err)
25 | }
26 |
27 | foo := &Foo{"b", 0}
28 |
29 | MainWindow{
30 | Title: "Walk RadioButton Example",
31 | MinSize: Size{320, 240},
32 | Layout: VBox{},
33 | DataBinder: DataBinder{
34 | DataSource: foo,
35 | AutoSubmit: true,
36 | OnSubmitted: func() {
37 | fmt.Println(foo)
38 | },
39 | },
40 | Children: []Widget{
41 | // RadioButtonGroup is needed for data binding only.
42 | RadioButtonGroup{
43 | DataMember: "Bar",
44 | Buttons: []RadioButton{
45 | RadioButton{
46 | Name: "aRB",
47 | Text: "A",
48 | Value: "a",
49 | },
50 | RadioButton{
51 | Name: "bRB",
52 | Text: "B",
53 | Value: "b",
54 | },
55 | RadioButton{
56 | Name: "cRB",
57 | Text: "C",
58 | Value: "c",
59 | },
60 | },
61 | },
62 | Label{
63 | Text: "A",
64 | Enabled: Bind("aRB.Checked"),
65 | },
66 | Label{
67 | Text: "B",
68 | Enabled: Bind("bRB.Checked"),
69 | },
70 | Label{
71 | Text: "C",
72 | Enabled: Bind("cRB.Checked"),
73 | },
74 | RadioButtonGroup{
75 | DataMember: "Baz",
76 | Buttons: []RadioButton{
77 | RadioButton{
78 | Name: "oneRB",
79 | Text: "1",
80 | Value: 1,
81 | },
82 | RadioButton{
83 | Name: "twoRB",
84 | Text: "2",
85 | Value: 2,
86 | },
87 | RadioButton{
88 | Name: "threeRB",
89 | Text: "3",
90 | Value: 3,
91 | },
92 | },
93 | },
94 | Label{
95 | Text: "1",
96 | Enabled: Bind("oneRB.Checked"),
97 | },
98 | Label{
99 | Text: "2",
100 | Enabled: Bind("twoRB.Checked"),
101 | },
102 | Label{
103 | Text: "3",
104 | Enabled: Bind("threeRB.Checked"),
105 | },
106 | },
107 | }.Create()
108 |
109 | app.Run()
110 | }
111 |
--------------------------------------------------------------------------------
/splitbutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "unsafe"
11 | )
12 |
13 | import (
14 | "github.com/tailscale/win"
15 | )
16 |
17 | type SplitButton struct {
18 | Button
19 | menu *Menu
20 | }
21 |
22 | func NewSplitButton(parent Container) (*SplitButton, error) {
23 | sb := new(SplitButton)
24 |
25 | var disposables Disposables
26 | defer disposables.Treat()
27 |
28 | if err := InitWidget(
29 | sb,
30 | parent,
31 | "BUTTON",
32 | win.WS_TABSTOP|win.WS_VISIBLE|win.BS_SPLITBUTTON,
33 | 0); err != nil {
34 | return nil, err
35 | }
36 | disposables.Add(sb)
37 |
38 | sb.Button.init()
39 |
40 | menu, err := NewMenu()
41 | if err != nil {
42 | return nil, err
43 | }
44 | disposables.Add(menu)
45 | menu.window = sb
46 | sb.menu = menu
47 |
48 | sb.GraphicsEffects().Add(InteractionEffect)
49 | sb.GraphicsEffects().Add(FocusEffect)
50 |
51 | disposables.Spare()
52 |
53 | return sb, nil
54 | }
55 |
56 | func (sb *SplitButton) Dispose() {
57 | sb.Button.Dispose()
58 |
59 | sb.menu.Dispose()
60 | }
61 |
62 | func (sb *SplitButton) ImageAboveText() bool {
63 | return sb.hasStyleBits(win.BS_TOP)
64 | }
65 |
66 | func (sb *SplitButton) SetImageAboveText(value bool) error {
67 | if err := sb.ensureStyleBits(win.BS_TOP, value); err != nil {
68 | return err
69 | }
70 |
71 | // We need to set the image again, or Windows will fail to calculate the
72 | // button control size correctly.
73 | return sb.SetImage(sb.image)
74 | }
75 |
76 | func (sb *SplitButton) Menu() *Menu {
77 | return sb.menu
78 | }
79 |
80 | func (sb *SplitButton) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
81 | switch msg {
82 | case win.WM_NOTIFY:
83 | switch ((*win.NMHDR)(unsafe.Pointer(lParam))).Code {
84 | case win.BCN_DROPDOWN:
85 | dd := (*win.NMBCDROPDOWN)(unsafe.Pointer(lParam))
86 |
87 | p := win.POINT{dd.RcButton.Left, dd.RcButton.Bottom}
88 |
89 | win.ClientToScreen(sb.hWnd, &p)
90 |
91 | win.TrackPopupMenuEx(
92 | sb.menu.hMenu,
93 | win.TPM_NOANIMATION,
94 | p.X,
95 | p.Y,
96 | sb.hWnd,
97 | nil)
98 | return 0
99 | }
100 | }
101 |
102 | return sb.Button.WndProc(hwnd, msg, wParam, lParam)
103 | }
104 |
--------------------------------------------------------------------------------
/declarative/radiobuttongroup.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "bytes"
11 | "errors"
12 | )
13 |
14 | import (
15 | "github.com/tailscale/walk"
16 | )
17 |
18 | type RadioButtonGroup struct {
19 | Buttons []RadioButton
20 | DataMember string
21 | Optional bool
22 | }
23 |
24 | func (rbg RadioButtonGroup) Create(builder *Builder) error {
25 | if len(rbg.Buttons) == 0 {
26 | return nil
27 | }
28 |
29 | var first *walk.RadioButton
30 |
31 | for _, rb := range rbg.Buttons {
32 | if first == nil {
33 | if rb.AssignTo == nil {
34 | rb.AssignTo = &first
35 | }
36 | }
37 |
38 | if err := rb.Create(builder); err != nil {
39 | return err
40 | }
41 |
42 | if first == nil {
43 | first = *rb.AssignTo
44 | }
45 | }
46 |
47 | parent := builder.Parent()
48 |
49 | builder.Defer(func() error {
50 | group := first.Group()
51 |
52 | validator := newRadioButtonGroupValidator(group, parent)
53 |
54 | for _, rb := range group.Buttons() {
55 | prop := rb.AsWindowBase().Property("CheckedValue")
56 |
57 | if err := prop.SetSource(rbg.DataMember); err != nil {
58 | return err
59 | }
60 | if err := prop.SetValidator(validator); err != nil {
61 | return err
62 | }
63 | }
64 |
65 | return nil
66 | })
67 |
68 | return nil
69 | }
70 |
71 | type radioButtonGroupValidator struct {
72 | group *walk.RadioButtonGroup
73 | err error
74 | }
75 |
76 | func newRadioButtonGroupValidator(group *walk.RadioButtonGroup, parent walk.Container) *radioButtonGroupValidator {
77 | b := new(bytes.Buffer)
78 |
79 | if gb, ok := parent.(*walk.GroupBox); ok {
80 | b.WriteString(gb.Title())
81 | } else {
82 | for i, rb := range group.Buttons() {
83 | if i > 0 {
84 | b.WriteString(", ")
85 | }
86 |
87 | b.WriteString(rb.Text())
88 | }
89 | }
90 |
91 | b.WriteString(": ")
92 |
93 | b.WriteString(tr("A selection is required.", "walk"))
94 |
95 | return &radioButtonGroupValidator{group: group, err: errors.New(b.String())}
96 | }
97 |
98 | func (rbgv *radioButtonGroupValidator) Validate(v interface{}) error {
99 | if rbgv.group.CheckedButton() == nil {
100 | return rbgv.err
101 | }
102 |
103 | return nil
104 | }
105 |
--------------------------------------------------------------------------------
/declarative/dateedit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "time"
11 | )
12 |
13 | import (
14 | "github.com/tailscale/walk"
15 | )
16 |
17 | type DateEdit struct {
18 | // Window
19 |
20 | Accessibility Accessibility
21 | Background Brush
22 | ContextMenuItems []MenuItem
23 | DoubleBuffering bool
24 | Enabled Property
25 | Font Font
26 | MaxSize Size
27 | MinSize Size
28 | Name string
29 | OnBoundsChanged walk.EventHandler
30 | OnKeyDown walk.KeyEventHandler
31 | OnKeyPress walk.KeyEventHandler
32 | OnKeyUp walk.KeyEventHandler
33 | OnMouseDown walk.MouseEventHandler
34 | OnMouseMove walk.MouseEventHandler
35 | OnMouseUp walk.MouseEventHandler
36 | OnSizeChanged walk.EventHandler
37 | Persistent bool
38 | RightToLeftReading bool
39 | ToolTipText Property
40 | Visible Property
41 |
42 | // Widget
43 |
44 | Alignment Alignment2D
45 | AlwaysConsumeSpace bool
46 | Column int
47 | ColumnSpan int
48 | GraphicsEffects []walk.WidgetGraphicsEffect
49 | Row int
50 | RowSpan int
51 | StretchFactor int
52 |
53 | // DateEdit
54 |
55 | AssignTo **walk.DateEdit
56 | Date Property
57 | Format string
58 | MaxDate time.Time
59 | MinDate time.Time
60 | NoneOption bool // Deprecated: use Optional instead
61 | OnDateChanged walk.EventHandler
62 | Optional bool
63 | }
64 |
65 | func (de DateEdit) Create(builder *Builder) error {
66 | var w *walk.DateEdit
67 | var err error
68 |
69 | if de.Optional || de.NoneOption {
70 | w, err = walk.NewDateEditWithNoneOption(builder.Parent())
71 | } else {
72 | w, err = walk.NewDateEdit(builder.Parent())
73 | }
74 | if err != nil {
75 | return err
76 | }
77 |
78 | if de.AssignTo != nil {
79 | *de.AssignTo = w
80 | }
81 |
82 | return builder.InitWidget(de, w, func() error {
83 | if err := w.SetFormat(de.Format); err != nil {
84 | return err
85 | }
86 |
87 | if err := w.SetRange(de.MinDate, de.MaxDate); err != nil {
88 | return err
89 | }
90 |
91 | if de.OnDateChanged != nil {
92 | w.DateChanged().Attach(de.OnDateChanged)
93 | }
94 |
95 | return nil
96 | })
97 | }
98 |
--------------------------------------------------------------------------------
/label.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import "github.com/tailscale/win"
10 |
11 | type EllipsisMode int
12 |
13 | const (
14 | EllipsisNone EllipsisMode = 0
15 | EllipsisEnd = EllipsisMode(win.SS_ENDELLIPSIS)
16 | EllipsisPath = EllipsisMode(win.SS_PATHELLIPSIS)
17 | )
18 |
19 | type Label struct {
20 | static
21 | textChangedPublisher EventPublisher
22 | }
23 |
24 | func NewLabel(parent Container) (*Label, error) {
25 | return NewLabelWithStyle(parent, 0)
26 | }
27 |
28 | func NewLabelWithStyle(parent Container, style uint32) (*Label, error) {
29 | l := new(Label)
30 |
31 | if err := l.init(l, parent, style); err != nil {
32 | return nil, err
33 | }
34 |
35 | l.SetTextAlignment(AlignNear)
36 |
37 | l.MustRegisterProperty("Text", NewProperty(
38 | func() interface{} {
39 | return l.Text()
40 | },
41 | func(v interface{}) error {
42 | return l.SetText(assertStringOr(v, ""))
43 | },
44 | l.textChangedPublisher.Event()))
45 |
46 | return l, nil
47 | }
48 |
49 | func (l *Label) asStatic() *static {
50 | return &l.static
51 | }
52 |
53 | func (l *Label) EllipsisMode() EllipsisMode {
54 | return EllipsisMode(win.GetWindowLong(l.hwndStatic, win.GWL_STYLE) & (win.SS_ENDELLIPSIS | win.SS_PATHELLIPSIS))
55 | }
56 |
57 | func (l *Label) SetEllipsisMode(mode EllipsisMode) error {
58 | oldMode := l.EllipsisMode()
59 |
60 | if mode == oldMode {
61 | return nil
62 | }
63 |
64 | if err := setAndClearWindowLongBits(l.hwndStatic, win.GWL_STYLE, uint32(mode), uint32(oldMode)); err != nil {
65 | return err
66 | }
67 |
68 | l.RequestLayout()
69 |
70 | return nil
71 | }
72 |
73 | func (l *Label) TextAlignment() Alignment1D {
74 | return l.textAlignment1D()
75 | }
76 |
77 | func (l *Label) SetTextAlignment(alignment Alignment1D) error {
78 | if alignment == AlignDefault {
79 | alignment = AlignNear
80 | }
81 |
82 | return l.setTextAlignment1D(alignment)
83 | }
84 |
85 | func (l *Label) Text() string {
86 | return l.text()
87 | }
88 |
89 | func (l *Label) SetText(text string) error {
90 | if changed, err := l.setText(text); err != nil {
91 | return err
92 | } else if !changed {
93 | return nil
94 | }
95 |
96 | l.textChangedPublisher.Publish()
97 |
98 | return nil
99 | }
100 |
--------------------------------------------------------------------------------
/declarative/label.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | "github.com/tailscale/win"
12 | )
13 |
14 | type EllipsisMode int
15 |
16 | const (
17 | EllipsisNone = EllipsisMode(walk.EllipsisNone)
18 | EllipsisEnd = EllipsisMode(walk.EllipsisEnd)
19 | EllipsisPath = EllipsisMode(walk.EllipsisPath)
20 | )
21 |
22 | type Label struct {
23 | // Window
24 |
25 | Accessibility Accessibility
26 | Background Brush
27 | ContextMenuItems []MenuItem
28 | DoubleBuffering bool
29 | Enabled Property
30 | Font Font
31 | MaxSize Size
32 | MinSize Size
33 | Name string
34 | OnBoundsChanged walk.EventHandler
35 | OnKeyDown walk.KeyEventHandler
36 | OnKeyPress walk.KeyEventHandler
37 | OnKeyUp walk.KeyEventHandler
38 | OnMouseDown walk.MouseEventHandler
39 | OnMouseMove walk.MouseEventHandler
40 | OnMouseUp walk.MouseEventHandler
41 | OnSizeChanged walk.EventHandler
42 | Persistent bool
43 | RightToLeftReading bool
44 | ToolTipText Property
45 | Visible Property
46 |
47 | // Widget
48 |
49 | Alignment Alignment2D
50 | AlwaysConsumeSpace bool
51 | Column int
52 | ColumnSpan int
53 | GraphicsEffects []walk.WidgetGraphicsEffect
54 | Row int
55 | RowSpan int
56 | StretchFactor int
57 |
58 | // Label
59 |
60 | AssignTo **walk.Label
61 | EllipsisMode EllipsisMode
62 | NoPrefix bool
63 | Text Property
64 | TextAlignment Alignment1D
65 | TextColor walk.Color
66 | }
67 |
68 | func (l Label) Create(builder *Builder) error {
69 | var style uint32
70 | if l.NoPrefix {
71 | style |= win.SS_NOPREFIX
72 | }
73 |
74 | w, err := walk.NewLabelWithStyle(builder.Parent(), style)
75 | if err != nil {
76 | return err
77 | }
78 |
79 | if l.AssignTo != nil {
80 | *l.AssignTo = w
81 | }
82 |
83 | return builder.InitWidget(l, w, func() error {
84 | if err := w.SetEllipsisMode(walk.EllipsisMode(l.EllipsisMode)); err != nil {
85 | return err
86 | }
87 |
88 | if err := w.SetTextAlignment(walk.Alignment1D(l.TextAlignment)); err != nil {
89 | return err
90 | }
91 |
92 | w.SetTextColor(l.TextColor)
93 |
94 | return nil
95 | })
96 | }
97 |
--------------------------------------------------------------------------------
/declarative/tabwidget.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type TabWidget struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // TabWidget
50 |
51 | AssignTo **walk.TabWidget
52 | ContentMargins Margins
53 | ContentMarginsZero bool
54 | OnCurrentIndexChanged walk.EventHandler
55 | Pages []TabPage
56 | }
57 |
58 | func (tw TabWidget) Create(builder *Builder) error {
59 | w, err := walk.NewTabWidget(builder.Parent())
60 | if err != nil {
61 | return err
62 | }
63 |
64 | if tw.AssignTo != nil {
65 | *tw.AssignTo = w
66 | }
67 |
68 | return builder.InitWidget(tw, w, func() error {
69 | for _, tp := range tw.Pages {
70 | var wp *walk.TabPage
71 | if tp.AssignTo == nil {
72 | tp.AssignTo = &wp
73 | }
74 |
75 | if tp.Content != nil && len(tp.Children) == 0 {
76 | tp.Layout = HBox{Margins: tw.ContentMargins, MarginsZero: tw.ContentMarginsZero}
77 | }
78 |
79 | if err := tp.Create(builder); err != nil {
80 | return err
81 | }
82 |
83 | if err := w.Pages().Add(*tp.AssignTo); err != nil {
84 | return err
85 | }
86 | }
87 |
88 | if tw.OnCurrentIndexChanged != nil {
89 | w.CurrentIndexChanged().Attach(tw.OnCurrentIndexChanged)
90 | }
91 |
92 | return nil
93 | })
94 | }
95 |
--------------------------------------------------------------------------------
/declarative/composite.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | "github.com/tailscale/win"
12 | )
13 |
14 | type Composite struct {
15 | // Window
16 |
17 | Accessibility Accessibility
18 | Background Brush
19 | ContextMenuItems []MenuItem
20 | DoubleBuffering bool
21 | Enabled Property
22 | Font Font
23 | MaxSize Size
24 | MinSize Size
25 | Name string
26 | OnBoundsChanged walk.EventHandler
27 | OnKeyDown walk.KeyEventHandler
28 | OnKeyPress walk.KeyEventHandler
29 | OnKeyUp walk.KeyEventHandler
30 | OnMouseDown walk.MouseEventHandler
31 | OnMouseMove walk.MouseEventHandler
32 | OnMouseUp walk.MouseEventHandler
33 | OnSizeChanged walk.EventHandler
34 | Persistent bool
35 | RightToLeftReading bool
36 | ToolTipText Property
37 | Visible Property
38 |
39 | // Widget
40 |
41 | Alignment Alignment2D
42 | AlwaysConsumeSpace bool
43 | Column int
44 | ColumnSpan int
45 | GraphicsEffects []walk.WidgetGraphicsEffect
46 | Row int
47 | RowSpan int
48 | StretchFactor int
49 |
50 | // Container
51 |
52 | Children []Widget
53 | DataBinder DataBinder
54 | Layout Layout
55 |
56 | // Composite
57 |
58 | AssignTo **walk.Composite
59 | Border bool
60 | Expressions func() map[string]walk.Expression
61 | Functions map[string]func(args ...interface{}) (interface{}, error)
62 | }
63 |
64 | func (c Composite) Create(builder *Builder) error {
65 | var style uint32
66 | if c.Border {
67 | style |= win.WS_BORDER
68 | }
69 | w, err := walk.NewCompositeWithStyle(builder.Parent(), style)
70 | if err != nil {
71 | return err
72 | }
73 |
74 | if c.AssignTo != nil {
75 | *c.AssignTo = w
76 | }
77 |
78 | w.SetSuspended(true)
79 | builder.Defer(func() error {
80 | w.SetSuspended(false)
81 | return nil
82 | })
83 |
84 | return builder.InitWidget(c, w, func() error {
85 | if c.Expressions != nil {
86 | for name, expr := range c.Expressions() {
87 | builder.expressions[name] = expr
88 | }
89 | }
90 | if c.Functions != nil {
91 | for name, fn := range c.Functions {
92 | builder.functions[name] = fn
93 | }
94 | }
95 |
96 | return nil
97 | })
98 | }
99 |
--------------------------------------------------------------------------------
/declarative/slider.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type Slider struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Slider
50 |
51 | AssignTo **walk.Slider
52 | LineSize int
53 | MaxValue int
54 | MinValue int
55 | Orientation Orientation
56 | OnValueChanged walk.EventHandler
57 | PageSize int
58 | ToolTipsHidden bool
59 | Tracking bool
60 | Value Property
61 | }
62 |
63 | func (sl Slider) Create(builder *Builder) error {
64 | w, err := walk.NewSliderWithCfg(builder.Parent(), &walk.SliderCfg{
65 | Orientation: walk.Orientation(sl.Orientation),
66 | ToolTipsHidden: sl.ToolTipsHidden,
67 | })
68 | if err != nil {
69 | return err
70 | }
71 |
72 | if sl.AssignTo != nil {
73 | *sl.AssignTo = w
74 | }
75 |
76 | return builder.InitWidget(sl, w, func() error {
77 | w.SetPersistent(sl.Persistent)
78 | if sl.LineSize > 0 {
79 | w.SetLineSize(sl.LineSize)
80 | }
81 | if sl.PageSize > 0 {
82 | w.SetPageSize(sl.PageSize)
83 | }
84 | w.SetTracking(sl.Tracking)
85 |
86 | if sl.MaxValue > sl.MinValue {
87 | w.SetRange(sl.MinValue, sl.MaxValue)
88 | }
89 |
90 | if sl.OnValueChanged != nil {
91 | w.ValueChanged().Attach(sl.OnValueChanged)
92 | }
93 |
94 | return nil
95 | })
96 | }
97 |
--------------------------------------------------------------------------------
/declarative/pushbutton.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package declarative
9 |
10 | import (
11 | "github.com/tailscale/walk"
12 | )
13 |
14 | type PushButton struct {
15 | // Window
16 |
17 | Accessibility Accessibility
18 | Background Brush
19 | ContextMenuItems []MenuItem
20 | DoubleBuffering bool
21 | Enabled Property
22 | Font Font
23 | MaxSize Size
24 | MinSize Size
25 | Name string
26 | OnBoundsChanged walk.EventHandler
27 | OnKeyDown walk.KeyEventHandler
28 | OnKeyPress walk.KeyEventHandler
29 | OnKeyUp walk.KeyEventHandler
30 | OnMouseDown walk.MouseEventHandler
31 | OnMouseMove walk.MouseEventHandler
32 | OnMouseUp walk.MouseEventHandler
33 | OnSizeChanged walk.EventHandler
34 | Persistent bool
35 | RightToLeftReading bool
36 | ToolTipText Property
37 | Visible Property
38 |
39 | // Widget
40 |
41 | Alignment Alignment2D
42 | AlwaysConsumeSpace bool
43 | Column int
44 | ColumnSpan int
45 | GraphicsEffects []walk.WidgetGraphicsEffect
46 | Row int
47 | RowSpan int
48 | StretchFactor int
49 |
50 | // Button
51 |
52 | Image Property
53 | OnClicked walk.EventHandler
54 | Text Property
55 | LayoutFlags walk.LayoutFlags // ignored unless UseLayoutFlags is true
56 | UseLayoutFlags bool
57 |
58 | // PushButton
59 |
60 | AssignTo **walk.PushButton
61 | PredefinedID int
62 | ImageAboveText bool
63 | Default bool
64 | }
65 |
66 | func (pb PushButton) Create(builder *Builder) (err error) {
67 | opts := walk.PushButtonOptions{
68 | LayoutFlags: pb.LayoutFlags,
69 | Default: pb.Default,
70 | PredefinedID: pb.PredefinedID,
71 | }
72 | if !pb.UseLayoutFlags {
73 | opts.LayoutFlags = walk.GrowableHorz
74 | }
75 |
76 | w, err := walk.NewPushButtonWithOptions(builder.Parent(), opts)
77 | if err != nil {
78 | return err
79 | }
80 |
81 | if pb.AssignTo != nil {
82 | *pb.AssignTo = w
83 | }
84 |
85 | return builder.InitWidget(pb, w, func() error {
86 | if err := w.SetImageAboveText(pb.ImageAboveText); err != nil {
87 | return err
88 | }
89 |
90 | if pb.OnClicked != nil {
91 | w.Clicked().Attach(pb.OnClicked)
92 | }
93 |
94 | return nil
95 | })
96 | }
97 |
--------------------------------------------------------------------------------
/declarative/treeview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type TreeView struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // TreeView
50 |
51 | AssignTo **walk.TreeView
52 | ItemHeight int
53 | Model walk.TreeModel
54 | OnCurrentItemChanged walk.EventHandler
55 | OnExpandedChanged walk.TreeItemEventHandler
56 | OnItemActivated walk.EventHandler
57 | }
58 |
59 | func (tv TreeView) Create(builder *Builder) error {
60 | w, err := walk.NewTreeView(builder.Parent())
61 | if err != nil {
62 | return err
63 | }
64 |
65 | if tv.AssignTo != nil {
66 | *tv.AssignTo = w
67 | }
68 |
69 | return builder.InitWidget(tv, w, func() error {
70 | if tv.ItemHeight > 0 {
71 | w.SetItemHeight(w.IntFrom96DPI(tv.ItemHeight)) // VERIFY: Item height should resize on DPI change.
72 | }
73 |
74 | if err := w.SetModel(tv.Model); err != nil {
75 | return err
76 | }
77 |
78 | if tv.OnCurrentItemChanged != nil {
79 | w.CurrentItemChanged().Attach(tv.OnCurrentItemChanged)
80 | }
81 |
82 | if tv.OnExpandedChanged != nil {
83 | w.ExpandedChanged().Attach(tv.OnExpandedChanged)
84 | }
85 |
86 | if tv.OnItemActivated != nil {
87 | w.ItemActivated().Attach(tv.OnItemActivated)
88 | }
89 |
90 | return nil
91 | })
92 | }
93 |
--------------------------------------------------------------------------------
/declarative/brush.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package declarative
9 |
10 | import (
11 | "strconv"
12 |
13 | "github.com/tailscale/walk"
14 | )
15 |
16 | type BlackBrush struct {
17 | }
18 |
19 | func (BlackBrush) Create() (walk.Brush, error) {
20 | return walk.BlackBrush(), nil
21 | }
22 |
23 | type TransparentBrush struct {
24 | }
25 |
26 | func (TransparentBrush) Create() (walk.Brush, error) {
27 | return walk.NullBrush(), nil
28 | }
29 |
30 | type WhiteBrush struct {
31 | }
32 |
33 | func (WhiteBrush) Create() (walk.Brush, error) {
34 | return walk.WhiteBrush(), nil
35 | }
36 |
37 | type SolidColorBrush struct {
38 | Color walk.Color
39 | }
40 |
41 | func (scb SolidColorBrush) Create() (walk.Brush, error) {
42 | return walk.NewSolidColorBrush(scb.Color)
43 | }
44 |
45 | type SystemColorBrush struct {
46 | Color walk.SystemColor
47 | }
48 |
49 | func (scb SystemColorBrush) Create() (walk.Brush, error) {
50 | return walk.NewSystemColorBrush(scb.Color)
51 | }
52 |
53 | type BitmapBrush struct {
54 | Image interface{}
55 | }
56 |
57 | func (bb BitmapBrush) Create() (walk.Brush, error) {
58 | var bmp *walk.Bitmap
59 | var err error
60 |
61 | switch img := bb.Image.(type) {
62 | case *walk.Bitmap:
63 | bmp = img
64 |
65 | case string:
66 | if bmp, err = walk.Resources.Bitmap(img); err != nil {
67 | return nil, err
68 | }
69 |
70 | case int:
71 | if bmp, err = walk.Resources.Bitmap(strconv.Itoa(img)); err != nil {
72 | return nil, err
73 | }
74 |
75 | default:
76 | return nil, walk.ErrInvalidType
77 | }
78 |
79 | return walk.NewBitmapBrush(bmp)
80 | }
81 |
82 | type GradientBrush struct {
83 | Vertexes []walk.GradientVertex
84 | Triangles []walk.GradientTriangle
85 | }
86 |
87 | func (gb GradientBrush) Create() (walk.Brush, error) {
88 | return walk.NewGradientBrush(gb.Vertexes, gb.Triangles)
89 | }
90 |
91 | type HorizontalGradientBrush struct {
92 | Stops []walk.GradientStop
93 | }
94 |
95 | func (hgb HorizontalGradientBrush) Create() (walk.Brush, error) {
96 | return walk.NewHorizontalGradientBrush(hgb.Stops)
97 | }
98 |
99 | type VerticalGradientBrush struct {
100 | Stops []walk.GradientStop
101 | }
102 |
103 | func (vgb VerticalGradientBrush) Create() (walk.Brush, error) {
104 | return walk.NewVerticalGradientBrush(vgb.Stops)
105 | }
106 |
--------------------------------------------------------------------------------
/registry.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "syscall"
11 | "unsafe"
12 | )
13 |
14 | import (
15 | "github.com/tailscale/win"
16 | )
17 |
18 | type RegistryKey struct {
19 | hKey win.HKEY
20 | }
21 |
22 | func ClassesRootKey() *RegistryKey {
23 | return &RegistryKey{win.HKEY_CLASSES_ROOT}
24 | }
25 |
26 | func CurrentUserKey() *RegistryKey {
27 | return &RegistryKey{win.HKEY_CURRENT_USER}
28 | }
29 |
30 | func LocalMachineKey() *RegistryKey {
31 | return &RegistryKey{win.HKEY_LOCAL_MACHINE}
32 | }
33 |
34 | func RegistryKeyString(rootKey *RegistryKey, subKeyPath, valueName string) (value string, err error) {
35 | var hKey win.HKEY
36 | if win.RegOpenKeyEx(
37 | rootKey.hKey,
38 | syscall.StringToUTF16Ptr(subKeyPath),
39 | 0,
40 | win.KEY_READ,
41 | &hKey) != win.ERROR_SUCCESS {
42 |
43 | return "", newError("RegistryKeyString: Failed to open subkey.")
44 | }
45 | defer win.RegCloseKey(hKey)
46 |
47 | var typ uint32
48 | var data []uint16
49 | var bufSize uint32
50 |
51 | if win.ERROR_SUCCESS != win.RegQueryValueEx(
52 | hKey,
53 | syscall.StringToUTF16Ptr(valueName),
54 | nil,
55 | &typ,
56 | nil,
57 | &bufSize) {
58 |
59 | return "", newError("RegQueryValueEx #1")
60 | }
61 |
62 | data = make([]uint16, bufSize/2+1)
63 |
64 | if win.ERROR_SUCCESS != win.RegQueryValueEx(
65 | hKey,
66 | syscall.StringToUTF16Ptr(valueName),
67 | nil,
68 | &typ,
69 | (*byte)(unsafe.Pointer(&data[0])),
70 | &bufSize) {
71 |
72 | return "", newError("RegQueryValueEx #2")
73 | }
74 |
75 | return syscall.UTF16ToString(data), nil
76 | }
77 |
78 | func RegistryKeyUint32(rootKey *RegistryKey, subKeyPath, valueName string) (value uint32, err error) {
79 | var hKey win.HKEY
80 | if win.RegOpenKeyEx(
81 | rootKey.hKey,
82 | syscall.StringToUTF16Ptr(subKeyPath),
83 | 0,
84 | win.KEY_READ,
85 | &hKey) != win.ERROR_SUCCESS {
86 |
87 | return 0, newError("RegistryKeyUint32: Failed to open subkey.")
88 | }
89 | defer win.RegCloseKey(hKey)
90 |
91 | bufSize := uint32(4)
92 |
93 | if win.ERROR_SUCCESS != win.RegQueryValueEx(
94 | hKey,
95 | syscall.StringToUTF16Ptr(valueName),
96 | nil,
97 | nil,
98 | (*byte)(unsafe.Pointer(&value)),
99 | &bufSize) {
100 |
101 | return 0, newError("RegQueryValueEx")
102 | }
103 |
104 | return
105 | }
106 |
--------------------------------------------------------------------------------
/declarative/radiobuttongroupbox.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type RadioButtonGroupBox struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Container
50 |
51 | Children []Widget
52 | DataBinder DataBinder
53 | Layout Layout
54 |
55 | // GroupBox
56 |
57 | AssignTo **walk.GroupBox
58 | Checkable bool
59 | Checked Property
60 | Title string
61 |
62 | // RadioButtonGroupBox
63 |
64 | Buttons []RadioButton
65 | DataMember string
66 | Optional bool
67 | }
68 |
69 | func (rbgb RadioButtonGroupBox) Create(builder *Builder) error {
70 | w, err := walk.NewGroupBox(builder.Parent())
71 | if err != nil {
72 | return err
73 | }
74 |
75 | if rbgb.AssignTo != nil {
76 | *rbgb.AssignTo = w
77 | }
78 |
79 | w.SetSuspended(true)
80 | builder.Defer(func() error {
81 | w.SetSuspended(false)
82 | return nil
83 | })
84 |
85 | return builder.InitWidget(rbgb, w, func() error {
86 | if err := w.SetTitle(rbgb.Title); err != nil {
87 | return err
88 | }
89 |
90 | w.SetCheckable(rbgb.Checkable)
91 |
92 | if err := (RadioButtonGroup{
93 | DataMember: rbgb.DataMember,
94 | Optional: rbgb.Optional,
95 | Buttons: rbgb.Buttons,
96 | }).Create(builder); err != nil {
97 | return err
98 | }
99 |
100 | return nil
101 | })
102 | }
103 |
--------------------------------------------------------------------------------
/examples/logview/logview.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 | package main
5 |
6 | import (
7 | "errors"
8 | "syscall"
9 | "unsafe"
10 | )
11 |
12 | import (
13 | "github.com/tailscale/walk"
14 | "github.com/tailscale/win"
15 | )
16 |
17 | type LogView struct {
18 | walk.WidgetBase
19 | logChan chan string
20 | }
21 |
22 | const (
23 | TEM_APPENDTEXT = win.WM_USER + 6
24 | )
25 |
26 | func NewLogView(parent walk.Container) (*LogView, error) {
27 | lc := make(chan string, 1024)
28 | lv := &LogView{logChan: lc}
29 |
30 | if err := walk.InitWidget(
31 | lv,
32 | parent,
33 | "EDIT",
34 | win.WS_TABSTOP|win.WS_VISIBLE|win.WS_VSCROLL|win.ES_MULTILINE|win.ES_WANTRETURN,
35 | win.WS_EX_CLIENTEDGE); err != nil {
36 | return nil, err
37 | }
38 | lv.setReadOnly(true)
39 | lv.SendMessage(win.EM_SETLIMITTEXT, 4294967295, 0)
40 | return lv, nil
41 | }
42 |
43 | func (*LogView) CreateLayoutItem(ctx *walk.LayoutContext) walk.LayoutItem {
44 | return walk.NewGreedyLayoutItem()
45 | }
46 |
47 | func (lv *LogView) setTextSelection(start, end int) {
48 | lv.SendMessage(win.EM_SETSEL, uintptr(start), uintptr(end))
49 | }
50 |
51 | func (lv *LogView) textLength() int {
52 | return int(lv.SendMessage(0x000E, uintptr(0), uintptr(0)))
53 | }
54 |
55 | func (lv *LogView) AppendText(value string) {
56 | textLength := lv.textLength()
57 | lv.setTextSelection(textLength, textLength)
58 | lv.SendMessage(win.EM_REPLACESEL, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))))
59 | }
60 |
61 | func (lv *LogView) setReadOnly(readOnly bool) error {
62 | if 0 == lv.SendMessage(win.EM_SETREADONLY, uintptr(win.BoolToBOOL(readOnly)), 0) {
63 | return errors.New("fail to call EM_SETREADONLY")
64 | }
65 |
66 | return nil
67 | }
68 |
69 | func (lv *LogView) PostAppendText(value string) {
70 | lv.logChan <- value
71 | win.PostMessage(lv.Handle(), TEM_APPENDTEXT, 0, 0)
72 | }
73 |
74 | func (lv *LogView) Write(p []byte) (int, error) {
75 | lv.PostAppendText(string(p) + "\r\n")
76 | return len(p), nil
77 | }
78 |
79 | func (lv *LogView) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
80 | switch msg {
81 | case win.WM_GETDLGCODE:
82 | if wParam == win.VK_RETURN {
83 | return win.DLGC_WANTALLKEYS
84 | }
85 |
86 | return win.DLGC_HASSETSEL | win.DLGC_WANTARROWS | win.DLGC_WANTCHARS
87 | case TEM_APPENDTEXT:
88 | select {
89 | case value := <-lv.logChan:
90 | lv.AppendText(value)
91 | default:
92 | return 0
93 | }
94 | }
95 |
96 | return lv.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
97 | }
98 |
--------------------------------------------------------------------------------
/fontresource.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package walk
9 |
10 | import (
11 | "syscall"
12 |
13 | "github.com/tailscale/win"
14 | )
15 |
16 | // FontMemResource represents a font resource loaded into memory from
17 | // the application's resources.
18 | type FontMemResource struct {
19 | hFontResource win.HANDLE
20 | }
21 |
22 | func newFontMemResource(resourceName *uint16) (*FontMemResource, error) {
23 | hModule := win.HMODULE(win.GetModuleHandle(nil))
24 | if hModule == win.HMODULE(0) {
25 | return nil, lastError("GetModuleHandle")
26 | }
27 |
28 | hres := win.FindResource(hModule, resourceName, win.MAKEINTRESOURCE(win.RT_FONT))
29 | if hres == win.HRSRC(0) {
30 | return nil, lastError("FindResource")
31 | }
32 |
33 | size := win.SizeofResource(hModule, hres)
34 | if size == 0 {
35 | return nil, lastError("SizeofResource")
36 | }
37 |
38 | hResLoad := win.LoadResource(hModule, hres)
39 | if hResLoad == win.HGLOBAL(0) {
40 | return nil, lastError("LoadResource")
41 | }
42 |
43 | ptr := win.LockResource(hResLoad)
44 | if ptr == 0 {
45 | return nil, lastError("LockResource")
46 | }
47 |
48 | numFonts := uint32(0)
49 | hFontResource := win.AddFontMemResourceEx(ptr, size, nil, &numFonts)
50 |
51 | if hFontResource == win.HANDLE(0) || numFonts == 0 {
52 | return nil, lastError("AddFontMemResource")
53 | }
54 |
55 | return &FontMemResource{hFontResource: hFontResource}, nil
56 | }
57 |
58 | // NewFontMemResourceByName function loads a font resource from the executable's resources
59 | // using the resource name.
60 | // The font must be embedded into resources using corresponding operator in the
61 | // application's RC script.
62 | func NewFontMemResourceByName(name string) (*FontMemResource, error) {
63 | lpstr, err := syscall.UTF16PtrFromString(name)
64 | if err != nil {
65 | return nil, err
66 | }
67 |
68 | return newFontMemResource(lpstr)
69 | }
70 |
71 | // NewFontMemResourceById function loads a font resource from the executable's resources
72 | // using the resource ID.
73 | // The font must be embedded into resources using corresponding operator in the
74 | // application's RC script.
75 | func NewFontMemResourceById(id int) (*FontMemResource, error) {
76 | return newFontMemResource(win.MAKEINTRESOURCE(uintptr(id)))
77 | }
78 |
79 | // Dispose removes the font resource from memory
80 | func (fmr *FontMemResource) Dispose() {
81 | if fmr.hFontResource != 0 {
82 | win.RemoveFontMemResourceEx(fmr.hFontResource)
83 | fmr.hFontResource = 0
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/spacer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | const spacerWindowClass = `\o/ Walk_Spacer_Class \o/`
10 |
11 | func init() {
12 | AppendToWalkInit(func() {
13 | MustRegisterWindowClass(spacerWindowClass)
14 | })
15 | }
16 |
17 | type Spacer struct {
18 | WidgetBase
19 | sizeHint96dpi Size
20 | layoutFlags LayoutFlags
21 | greedyLocallyOnly bool
22 | }
23 |
24 | type SpacerCfg struct {
25 | LayoutFlags LayoutFlags
26 | SizeHint Size // in 1/96" units
27 | GreedyLocallyOnly bool
28 | }
29 |
30 | func NewSpacerWithCfg(parent Container, cfg *SpacerCfg) (*Spacer, error) {
31 | return newSpacer(parent, cfg.LayoutFlags, cfg.SizeHint, cfg.GreedyLocallyOnly)
32 | }
33 |
34 | func newSpacer(parent Container, layoutFlags LayoutFlags, sizeHint96dpi Size, greedyLocallyOnly bool) (*Spacer, error) {
35 | s := &Spacer{
36 | layoutFlags: layoutFlags,
37 | sizeHint96dpi: sizeHint96dpi,
38 | greedyLocallyOnly: greedyLocallyOnly,
39 | }
40 |
41 | if err := InitWidget(
42 | s,
43 | parent,
44 | spacerWindowClass,
45 | 0,
46 | 0); err != nil {
47 | return nil, err
48 | }
49 |
50 | return s, nil
51 | }
52 |
53 | func NewHSpacer(parent Container) (*Spacer, error) {
54 | return newSpacer(parent, ShrinkableHorz|ShrinkableVert|GrowableHorz|GreedyHorz, Size{}, false)
55 | }
56 |
57 | func NewHSpacerFixed(parent Container, width int) (*Spacer, error) {
58 | return newSpacer(parent, 0, Size{width, 0}, false)
59 | }
60 |
61 | func NewVSpacer(parent Container) (*Spacer, error) {
62 | return newSpacer(parent, ShrinkableHorz|ShrinkableVert|GrowableVert|GreedyVert, Size{}, false)
63 | }
64 |
65 | func NewVSpacerFixed(parent Container, height int) (*Spacer, error) {
66 | return newSpacer(parent, 0, Size{0, height}, false)
67 | }
68 |
69 | func (s *Spacer) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
70 | return &spacerLayoutItem{
71 | idealSize96dpi: s.sizeHint96dpi,
72 | layoutFlags: s.layoutFlags,
73 | greedyLocallyOnly: s.greedyLocallyOnly,
74 | }
75 | }
76 |
77 | type spacerLayoutItem struct {
78 | LayoutItemBase
79 | idealSize96dpi Size
80 | layoutFlags LayoutFlags
81 | greedyLocallyOnly bool
82 | }
83 |
84 | func (li *spacerLayoutItem) LayoutFlags() LayoutFlags {
85 | return li.layoutFlags
86 | }
87 |
88 | func (li *spacerLayoutItem) IdealSize() Size {
89 | return SizeFrom96DPI(li.idealSize96dpi, li.ctx.dpi)
90 | }
91 |
92 | func (li *spacerLayoutItem) MinSize() Size {
93 | return SizeFrom96DPI(li.idealSize96dpi, li.ctx.dpi)
94 | }
95 |
--------------------------------------------------------------------------------
/examples/listbox/listbox.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "fmt"
9 | "log"
10 | "os"
11 | "strings"
12 |
13 | "github.com/tailscale/walk"
14 |
15 | . "github.com/tailscale/walk/declarative"
16 | )
17 |
18 | func main() {
19 | app, err := walk.InitApp()
20 | if err != nil {
21 | log.Fatal(err)
22 | }
23 |
24 | mw := &MyMainWindow{model: NewEnvModel()}
25 |
26 | if err := (MainWindow{
27 | AssignTo: &mw.MainWindow,
28 | Title: "Walk ListBox Example",
29 | MinSize: Size{240, 320},
30 | Size: Size{300, 400},
31 | Layout: VBox{MarginsZero: true},
32 | Children: []Widget{
33 | HSplitter{
34 | Children: []Widget{
35 | ListBox{
36 | AssignTo: &mw.lb,
37 | Model: mw.model,
38 | OnCurrentIndexChanged: mw.lb_CurrentIndexChanged,
39 | OnItemActivated: mw.lb_ItemActivated,
40 | },
41 | TextEdit{
42 | AssignTo: &mw.te,
43 | ReadOnly: true,
44 | },
45 | },
46 | },
47 | },
48 | }.Create()); err != nil {
49 | log.Fatal(err)
50 | }
51 |
52 | app.Run()
53 | }
54 |
55 | type MyMainWindow struct {
56 | *walk.MainWindow
57 | model *EnvModel
58 | lb *walk.ListBox
59 | te *walk.TextEdit
60 | }
61 |
62 | func (mw *MyMainWindow) lb_CurrentIndexChanged() {
63 | i := mw.lb.CurrentIndex()
64 | item := &mw.model.items[i]
65 |
66 | mw.te.SetText(item.value)
67 |
68 | fmt.Println("CurrentIndex: ", i)
69 | fmt.Println("CurrentEnvVarName: ", item.name)
70 | }
71 |
72 | func (mw *MyMainWindow) lb_ItemActivated() {
73 | value := mw.model.items[mw.lb.CurrentIndex()].value
74 |
75 | walk.MsgBox(mw, "Value", value, walk.MsgBoxIconInformation)
76 | }
77 |
78 | type EnvItem struct {
79 | name string
80 | value string
81 | }
82 |
83 | type EnvModel struct {
84 | walk.ListModelBase
85 | items []EnvItem
86 | }
87 |
88 | func NewEnvModel() *EnvModel {
89 | env := os.Environ()
90 |
91 | m := &EnvModel{items: make([]EnvItem, len(env))}
92 |
93 | for i, e := range env {
94 | j := strings.Index(e, "=")
95 | if j == 0 {
96 | continue
97 | }
98 |
99 | name := e[0:j]
100 | value := strings.Replace(e[j+1:], ";", "\r\n", -1)
101 |
102 | m.items[i] = EnvItem{name, value}
103 | }
104 |
105 | return m
106 | }
107 |
108 | func (m *EnvModel) ItemCount() int {
109 | return len(m.items)
110 | }
111 |
112 | func (m *EnvModel) Value(index int) interface{} {
113 | return m.items[index].name
114 | }
115 |
--------------------------------------------------------------------------------
/declarative/textedit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | "github.com/tailscale/win"
12 | )
13 |
14 | type TextEdit struct {
15 | // Window
16 |
17 | Accessibility Accessibility
18 | Background Brush
19 | ContextMenuItems []MenuItem
20 | DoubleBuffering bool
21 | Enabled Property
22 | Font Font
23 | MaxSize Size
24 | MinSize Size
25 | Name string
26 | OnBoundsChanged walk.EventHandler
27 | OnKeyDown walk.KeyEventHandler
28 | OnKeyPress walk.KeyEventHandler
29 | OnKeyUp walk.KeyEventHandler
30 | OnMouseDown walk.MouseEventHandler
31 | OnMouseMove walk.MouseEventHandler
32 | OnMouseUp walk.MouseEventHandler
33 | OnSizeChanged walk.EventHandler
34 | Persistent bool
35 | RightToLeftReading bool
36 | ToolTipText Property
37 | Visible Property
38 |
39 | // Widget
40 |
41 | Alignment Alignment2D
42 | AlwaysConsumeSpace bool
43 | Column int
44 | ColumnSpan int
45 | GraphicsEffects []walk.WidgetGraphicsEffect
46 | Row int
47 | RowSpan int
48 | StretchFactor int
49 |
50 | // TextEdit
51 |
52 | AssignTo **walk.TextEdit
53 | CompactHeight bool
54 | HScroll bool
55 | MaxLength int
56 | OnTextChanged walk.EventHandler
57 | ReadOnly Property
58 | Text Property
59 | TextAlignment Alignment1D
60 | TextColor walk.Color
61 | VScroll bool
62 | }
63 |
64 | func (te TextEdit) Create(builder *Builder) error {
65 | var style uint32
66 | if te.HScroll {
67 | style |= win.WS_HSCROLL
68 | }
69 | if te.VScroll {
70 | style |= win.WS_VSCROLL
71 | }
72 |
73 | w, err := walk.NewTextEditWithStyle(builder.Parent(), style)
74 | if err != nil {
75 | return err
76 | }
77 |
78 | if te.AssignTo != nil {
79 | *te.AssignTo = w
80 | }
81 |
82 | return builder.InitWidget(te, w, func() error {
83 | w.SetCompactHeight(te.CompactHeight)
84 | w.SetTextColor(te.TextColor)
85 |
86 | if err := w.SetTextAlignment(walk.Alignment1D(te.TextAlignment)); err != nil {
87 | return err
88 | }
89 |
90 | if te.MaxLength > 0 {
91 | w.SetMaxLength(te.MaxLength)
92 | }
93 |
94 | if te.OnTextChanged != nil {
95 | w.TextChanged().Attach(te.OnTextChanged)
96 | }
97 |
98 | return nil
99 | })
100 | }
101 |
--------------------------------------------------------------------------------
/messagebox.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "strings"
11 | "syscall"
12 | )
13 |
14 | import (
15 | "github.com/tailscale/win"
16 | )
17 |
18 | type MsgBoxStyle uint
19 |
20 | const (
21 | MsgBoxOK MsgBoxStyle = win.MB_OK
22 | MsgBoxOKCancel MsgBoxStyle = win.MB_OKCANCEL
23 | MsgBoxAbortRetryIgnore MsgBoxStyle = win.MB_ABORTRETRYIGNORE
24 | MsgBoxYesNoCancel MsgBoxStyle = win.MB_YESNOCANCEL
25 | MsgBoxYesNo MsgBoxStyle = win.MB_YESNO
26 | MsgBoxRetryCancel MsgBoxStyle = win.MB_RETRYCANCEL
27 | MsgBoxCancelTryContinue MsgBoxStyle = win.MB_CANCELTRYCONTINUE
28 | MsgBoxIconHand MsgBoxStyle = win.MB_ICONHAND
29 | MsgBoxIconQuestion MsgBoxStyle = win.MB_ICONQUESTION
30 | MsgBoxIconExclamation MsgBoxStyle = win.MB_ICONEXCLAMATION
31 | MsgBoxIconAsterisk MsgBoxStyle = win.MB_ICONASTERISK
32 | MsgBoxUserIcon MsgBoxStyle = win.MB_USERICON
33 | MsgBoxIconWarning MsgBoxStyle = win.MB_ICONWARNING
34 | MsgBoxIconError MsgBoxStyle = win.MB_ICONERROR
35 | MsgBoxIconInformation MsgBoxStyle = win.MB_ICONINFORMATION
36 | MsgBoxIconStop MsgBoxStyle = win.MB_ICONSTOP
37 | MsgBoxDefButton1 MsgBoxStyle = win.MB_DEFBUTTON1
38 | MsgBoxDefButton2 MsgBoxStyle = win.MB_DEFBUTTON2
39 | MsgBoxDefButton3 MsgBoxStyle = win.MB_DEFBUTTON3
40 | MsgBoxDefButton4 MsgBoxStyle = win.MB_DEFBUTTON4
41 | MsgBoxApplModal MsgBoxStyle = win.MB_APPLMODAL
42 | MsgBoxSystemModal MsgBoxStyle = win.MB_SYSTEMMODAL
43 | MsgBoxTaskModal MsgBoxStyle = win.MB_TASKMODAL
44 | MsgBoxHelp MsgBoxStyle = win.MB_HELP
45 | MsgBoxSetForeground MsgBoxStyle = win.MB_SETFOREGROUND
46 | MsgBoxDefaultDesktopOnly MsgBoxStyle = win.MB_DEFAULT_DESKTOP_ONLY
47 | MsgBoxTopMost MsgBoxStyle = win.MB_TOPMOST
48 | MsgBoxRight MsgBoxStyle = win.MB_RIGHT
49 | MsgBoxRTLReading MsgBoxStyle = win.MB_RTLREADING
50 | MsgBoxServiceNotification MsgBoxStyle = win.MB_SERVICE_NOTIFICATION
51 | )
52 |
53 | // MsgBox shows a simple modal dialog box.
54 | //
55 | // Deprecated: Use TaskDialog instead.
56 | func MsgBox(owner Form, title, message string, style MsgBoxStyle) int {
57 | var ownerHWnd win.HWND
58 |
59 | if owner != nil {
60 | ownerHWnd = owner.Handle()
61 | }
62 |
63 | return int(win.MessageBox(
64 | ownerHWnd,
65 | syscall.StringToUTF16Ptr(strings.ReplaceAll(message, "\x00", "␀")),
66 | syscall.StringToUTF16Ptr(strings.ReplaceAll(title, "\x00", "␀")),
67 | uint32(style)))
68 | }
69 |
--------------------------------------------------------------------------------
/event.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build windows
6 | // +build windows
7 |
8 | package walk
9 |
10 | type eventHandlerInfo struct {
11 | handler EventHandler
12 | once bool
13 | }
14 |
15 | type EventHandler func()
16 |
17 | type Event struct {
18 | handlers []eventHandlerInfo
19 | }
20 |
21 | func (e *Event) Attach(handler EventHandler) int {
22 | handlerInfo := eventHandlerInfo{handler, false}
23 |
24 | for i, h := range e.handlers {
25 | if h.handler == nil {
26 | e.handlers[i] = handlerInfo
27 | return i
28 | }
29 | }
30 |
31 | e.handlers = append(e.handlers, handlerInfo)
32 |
33 | return len(e.handlers) - 1
34 | }
35 |
36 | func (e *Event) Detach(handle int) {
37 | e.handlers[handle].handler = nil
38 | }
39 |
40 | func (e *Event) Once(handler EventHandler) {
41 | i := e.Attach(handler)
42 | e.handlers[i].once = true
43 | }
44 |
45 | type EventPublisher struct {
46 | event Event
47 | }
48 |
49 | func (p *EventPublisher) Event() *Event {
50 | return &p.event
51 | }
52 |
53 | func (p *EventPublisher) Publish() {
54 | for i, h := range p.event.handlers {
55 | if h.handler != nil {
56 | h.handler()
57 |
58 | if h.once {
59 | p.event.Detach(i)
60 | }
61 | }
62 | }
63 | }
64 |
65 | type genericEventHandlerInfo[T any] struct {
66 | handler GenericEventHandler[T]
67 | once bool
68 | }
69 |
70 | type GenericEventHandler[T any] func(param T)
71 |
72 | type GenericEvent[T any] struct {
73 | handlers []genericEventHandlerInfo[T]
74 | }
75 |
76 | func (e *GenericEvent[T]) Attach(handler GenericEventHandler[T]) int {
77 | handlerInfo := genericEventHandlerInfo[T]{handler, false}
78 |
79 | for i, h := range e.handlers {
80 | if h.handler == nil {
81 | e.handlers[i] = handlerInfo
82 | return i
83 | }
84 | }
85 |
86 | e.handlers = append(e.handlers, handlerInfo)
87 |
88 | return len(e.handlers) - 1
89 | }
90 |
91 | func (e *GenericEvent[T]) Detach(handle int) {
92 | e.handlers[handle].handler = nil
93 | }
94 |
95 | func (e *GenericEvent[T]) Once(handler GenericEventHandler[T]) {
96 | i := e.Attach(handler)
97 | e.handlers[i].once = true
98 | }
99 |
100 | type GenericEventPublisher[T any] struct {
101 | event GenericEvent[T]
102 | }
103 |
104 | func (p *GenericEventPublisher[T]) Event() *GenericEvent[T] {
105 | return &p.event
106 | }
107 |
108 | func (p *GenericEventPublisher[T]) Publish(param T) {
109 | for i, h := range p.event.handlers {
110 | if h.handler != nil {
111 | h.handler(param)
112 |
113 | if h.once {
114 | p.event.Detach(i)
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/datelabel.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "time"
11 | )
12 |
13 | type DateLabel struct {
14 | static
15 | date time.Time
16 | dateChangedPublisher EventPublisher
17 | format string
18 | formatChangedPublisher EventPublisher
19 | }
20 |
21 | func NewDateLabel(parent Container) (*DateLabel, error) {
22 | dl := new(DateLabel)
23 |
24 | if err := dl.init(dl, parent, 0); err != nil {
25 | return nil, err
26 | }
27 |
28 | dl.SetTextAlignment(AlignFar)
29 | if _, err := dl.updateText(); err != nil {
30 | return nil, err
31 | }
32 |
33 | dl.MustRegisterProperty("Date", NewProperty(
34 | func() interface{} {
35 | return dl.Date()
36 | },
37 | func(v interface{}) error {
38 | return dl.SetDate(assertTimeOr(v, time.Time{}))
39 | },
40 | dl.dateChangedPublisher.Event()))
41 |
42 | dl.MustRegisterProperty("Format", NewProperty(
43 | func() interface{} {
44 | return dl.Format()
45 | },
46 | func(v interface{}) error {
47 | return dl.SetFormat(assertStringOr(v, ""))
48 | },
49 | dl.formatChangedPublisher.Event()))
50 |
51 | return dl, nil
52 | }
53 |
54 | func (dl *DateLabel) asStatic() *static {
55 | return &dl.static
56 | }
57 |
58 | func (dl *DateLabel) TextAlignment() Alignment1D {
59 | return dl.textAlignment1D()
60 | }
61 |
62 | func (dl *DateLabel) SetTextAlignment(alignment Alignment1D) error {
63 | if alignment == AlignDefault {
64 | alignment = AlignNear
65 | }
66 |
67 | return dl.setTextAlignment1D(alignment)
68 | }
69 |
70 | func (dl *DateLabel) Date() time.Time {
71 | return dl.date
72 | }
73 |
74 | func (dl *DateLabel) SetDate(date time.Time) error {
75 | if date == dl.date {
76 | return nil
77 | }
78 |
79 | old := dl.date
80 |
81 | dl.date = date
82 |
83 | if _, err := dl.updateText(); err != nil {
84 | dl.date = old
85 | return err
86 | }
87 |
88 | dl.dateChangedPublisher.Publish()
89 |
90 | return nil
91 | }
92 |
93 | func (dl *DateLabel) Format() string {
94 | return dl.format
95 | }
96 |
97 | func (dl *DateLabel) SetFormat(format string) error {
98 | if format == dl.format {
99 | return nil
100 | }
101 |
102 | old := dl.format
103 |
104 | dl.format = format
105 |
106 | if _, err := dl.updateText(); err != nil {
107 | dl.format = old
108 | return err
109 | }
110 |
111 | dl.formatChangedPublisher.Publish()
112 |
113 | return nil
114 | }
115 |
116 | func (dl *DateLabel) updateText() (changed bool, err error) {
117 | return dl.setText(dl.date.Format(dl.format))
118 | }
119 |
--------------------------------------------------------------------------------
/declarative/gradientcomposite.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | "github.com/tailscale/win"
12 | )
13 |
14 | type GradientComposite struct {
15 | // Window
16 |
17 | Accessibility Accessibility
18 | Background Brush
19 | ContextMenuItems []MenuItem
20 | DoubleBuffering bool
21 | Enabled Property
22 | Font Font
23 | MaxSize Size
24 | MinSize Size
25 | Name string
26 | OnBoundsChanged walk.EventHandler
27 | OnKeyDown walk.KeyEventHandler
28 | OnKeyPress walk.KeyEventHandler
29 | OnKeyUp walk.KeyEventHandler
30 | OnMouseDown walk.MouseEventHandler
31 | OnMouseMove walk.MouseEventHandler
32 | OnMouseUp walk.MouseEventHandler
33 | OnSizeChanged walk.EventHandler
34 | Persistent bool
35 | RightToLeftReading bool
36 | ToolTipText Property
37 | Visible Property
38 |
39 | // Widget
40 |
41 | Alignment Alignment2D
42 | AlwaysConsumeSpace bool
43 | Column int
44 | ColumnSpan int
45 | GraphicsEffects []walk.WidgetGraphicsEffect
46 | Row int
47 | RowSpan int
48 | StretchFactor int
49 |
50 | // Container
51 |
52 | Children []Widget
53 | Layout Layout
54 | DataBinder DataBinder
55 |
56 | // GradientComposite
57 |
58 | AssignTo **walk.GradientComposite
59 | Border bool
60 | Color1 Property
61 | Color2 Property
62 | Expressions func() map[string]walk.Expression
63 | Functions map[string]func(args ...interface{}) (interface{}, error)
64 | Vertical Property
65 | }
66 |
67 | func (gc GradientComposite) Create(builder *Builder) error {
68 | var style uint32
69 | if gc.Border {
70 | style |= win.WS_BORDER
71 | }
72 | w, err := walk.NewGradientCompositeWithStyle(builder.Parent(), style)
73 | if err != nil {
74 | return err
75 | }
76 |
77 | if gc.AssignTo != nil {
78 | *gc.AssignTo = w
79 | }
80 |
81 | w.SetSuspended(true)
82 | builder.Defer(func() error {
83 | w.SetSuspended(false)
84 | return nil
85 | })
86 |
87 | return builder.InitWidget(gc, w, func() error {
88 | if gc.Expressions != nil {
89 | for name, expr := range gc.Expressions() {
90 | builder.expressions[name] = expr
91 | }
92 | }
93 | if gc.Functions != nil {
94 | for name, fn := range gc.Functions {
95 | builder.functions[name] = fn
96 | }
97 | }
98 |
99 | return nil
100 | })
101 | }
102 |
--------------------------------------------------------------------------------
/declarative/customwidget.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type PaintMode int
14 |
15 | const (
16 | PaintNormal PaintMode = iota // erase background before PaintFunc
17 | PaintNoErase // PaintFunc clears background, single buffered
18 | PaintBuffered // PaintFunc clears background, double buffered
19 | )
20 |
21 | type CustomWidget struct {
22 | // Window
23 |
24 | Accessibility Accessibility
25 | Background Brush
26 | ContextMenuItems []MenuItem
27 | DoubleBuffering bool
28 | Enabled Property
29 | Font Font
30 | MaxSize Size
31 | MinSize Size
32 | Name string
33 | OnBoundsChanged walk.EventHandler
34 | OnKeyDown walk.KeyEventHandler
35 | OnKeyPress walk.KeyEventHandler
36 | OnKeyUp walk.KeyEventHandler
37 | OnMouseDown walk.MouseEventHandler
38 | OnMouseMove walk.MouseEventHandler
39 | OnMouseUp walk.MouseEventHandler
40 | OnSizeChanged walk.EventHandler
41 | Persistent bool
42 | RightToLeftReading bool
43 | ToolTipText Property
44 | Visible Property
45 |
46 | // Widget
47 |
48 | Alignment Alignment2D
49 | AlwaysConsumeSpace bool
50 | Column int
51 | ColumnSpan int
52 | GraphicsEffects []walk.WidgetGraphicsEffect
53 | Row int
54 | RowSpan int
55 | StretchFactor int
56 |
57 | // CustomWidget
58 |
59 | AssignTo **walk.CustomWidget
60 | ClearsBackground bool
61 | InvalidatesOnResize bool
62 | Paint walk.PaintFunc
63 | PaintPixels walk.PaintFunc
64 | PaintMode PaintMode
65 | Style uint32
66 | }
67 |
68 | func (cw CustomWidget) Create(builder *Builder) error {
69 | var w *walk.CustomWidget
70 | var err error
71 | if cw.PaintPixels != nil {
72 | w, err = walk.NewCustomWidgetPixels(builder.Parent(), uint(cw.Style), cw.PaintPixels)
73 | } else {
74 | w, err = walk.NewCustomWidget(builder.Parent(), uint(cw.Style), cw.Paint)
75 | }
76 | if err != nil {
77 | return err
78 | }
79 |
80 | if cw.AssignTo != nil {
81 | *cw.AssignTo = w
82 | }
83 |
84 | return builder.InitWidget(cw, w, func() error {
85 | if cw.PaintMode != PaintNormal && cw.ClearsBackground {
86 | panic("PaintMode and ClearsBackground are incompatible")
87 | }
88 | w.SetClearsBackground(cw.ClearsBackground)
89 | w.SetInvalidatesOnResize(cw.InvalidatesOnResize)
90 | w.SetPaintMode(walk.PaintMode(cw.PaintMode))
91 |
92 | return nil
93 | })
94 | }
95 |
--------------------------------------------------------------------------------
/idalloc/idalloc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Tailscale Inc. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package idalloc provides a simple bitmap allocator for ID values.
6 | package idalloc
7 |
8 | import (
9 | "errors"
10 | "fmt"
11 | "math"
12 | "math/bits"
13 | )
14 |
15 | // IDMaxLimit is the maximum possible ID value that could be returned by an
16 | // IDAllocator.
17 | const IDMaxLimit = math.MaxUint32
18 |
19 | // ErrIDsExhausted is returned when an IDAllocator is unable to fulfill an
20 | // allocation request.
21 | var ErrIDsExhausted = errors.New("no more IDs available")
22 |
23 | // IDAllocator is an allocator for ID values. It is implemented using a bitmap
24 | // that is grown as necessary.
25 | type IDAllocator struct {
26 | bits []uint
27 | maxBlocks uint32
28 | }
29 |
30 | const initialSize = uint32(64)
31 |
32 | // New creates a new IDAllocator that may allocate up to numIDs values. numIDs
33 | // must be a multiple of 64.
34 | func New(numIDs uint32) IDAllocator {
35 | // For this check we use initialSize (64) instead of bits.UintSize so that we
36 | // can be consistent between CPU architectures.
37 | if numIDs == 0 || numIDs%initialSize != 0 {
38 | panic(fmt.Sprintf("numIDs must be non-zero and divisible by %d", initialSize))
39 | }
40 |
41 | numBlocks := (initialSize + bits.UintSize - 1) / bits.UintSize
42 | return IDAllocator{
43 | bits: make([]uint, numBlocks),
44 | maxBlocks: (numIDs + bits.UintSize - 1) / bits.UintSize,
45 | }
46 | }
47 |
48 | // Allocate finds an unused ID, sets it as used, and returns its value.
49 | // If the IDAllocator is full and there are no more IDs available, id
50 | // will be set to IDMaxLimit and err will be set to ErrIDsExhausted.
51 | func (a *IDAllocator) Allocate() (id uint32, err error) {
52 | i := uint32(0)
53 | for {
54 | curBlock := a.bits[i]
55 | if curBlock != ^uint(0) {
56 | bb := uint32(bits.TrailingZeros(^curBlock))
57 | a.bits[i] = curBlock | (uint(1) << bb)
58 | return uint32(i*bits.UintSize + bb), nil
59 | }
60 |
61 | i++
62 | if i == uint32(len(a.bits)) && !a.grow() {
63 | return IDMaxLimit, ErrIDsExhausted
64 | }
65 | }
66 | }
67 |
68 | // Free marks id as unused. id must have been previously returned by a
69 | // successful call to Allocate.
70 | func (a *IDAllocator) Free(id uint32) {
71 | i, mask := id/bits.UintSize, uint(1)<<(id%bits.UintSize)
72 | a.bits[i] &= ^mask
73 | }
74 |
75 | func (a *IDAllocator) grow() bool {
76 | n, m := uint32(len(a.bits)), a.maxBlocks
77 | if n >= m {
78 | return false
79 | }
80 |
81 | // Try to double the size, but if that would exceed our maximum then just
82 | // allocate up to the max.
83 | if 2*n > m {
84 | n = m - n
85 | }
86 |
87 | a.bits = append(a.bits, make([]uint, n)...)
88 | return true
89 | }
90 |
--------------------------------------------------------------------------------
/declarative/toolbar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type ToolBarButtonStyle int
14 |
15 | const (
16 | ToolBarButtonImageOnly ToolBarButtonStyle = iota
17 | ToolBarButtonTextOnly
18 | ToolBarButtonImageBeforeText
19 | ToolBarButtonImageAboveText
20 | )
21 |
22 | type ToolBar struct {
23 | // Window
24 |
25 | Accessibility Accessibility
26 | Background Brush
27 | ContextMenuItems []MenuItem
28 | DoubleBuffering bool
29 | Enabled Property
30 | Font Font
31 | MaxSize Size
32 | MinSize Size
33 | Name string
34 | OnBoundsChanged walk.EventHandler
35 | OnKeyDown walk.KeyEventHandler
36 | OnKeyPress walk.KeyEventHandler
37 | OnKeyUp walk.KeyEventHandler
38 | OnMouseDown walk.MouseEventHandler
39 | OnMouseMove walk.MouseEventHandler
40 | OnMouseUp walk.MouseEventHandler
41 | OnSizeChanged walk.EventHandler
42 | Persistent bool
43 | RightToLeftReading bool
44 | ToolTipText Property
45 | Visible Property
46 |
47 | // Widget
48 |
49 | Alignment Alignment2D
50 | AlwaysConsumeSpace bool
51 | Column int
52 | ColumnSpan int
53 | GraphicsEffects []walk.WidgetGraphicsEffect
54 | Row int
55 | RowSpan int
56 | StretchFactor int
57 |
58 | // ToolBar
59 |
60 | Actions []*walk.Action // Deprecated, use Items instead
61 | AssignTo **walk.ToolBar
62 | ButtonStyle ToolBarButtonStyle
63 | Items []MenuItem
64 | MaxTextRows int
65 | Orientation Orientation
66 | }
67 |
68 | func (tb ToolBar) Create(builder *Builder) error {
69 | w, err := walk.NewToolBarWithOrientationAndButtonStyle(builder.Parent(), walk.Orientation(tb.Orientation), walk.ToolBarButtonStyle(tb.ButtonStyle))
70 | if err != nil {
71 | return err
72 | }
73 |
74 | if tb.AssignTo != nil {
75 | *tb.AssignTo = w
76 | }
77 |
78 | return builder.InitWidget(tb, w, func() error {
79 | imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
80 | if err != nil {
81 | return err
82 | }
83 | w.SetImageList(imageList)
84 |
85 | mtr := tb.MaxTextRows
86 | if mtr < 1 {
87 | mtr = 1
88 | }
89 | if err := w.SetMaxTextRows(mtr); err != nil {
90 | return err
91 | }
92 |
93 | if len(tb.Items) > 0 {
94 | builder.deferBuildActions(w.Actions(), tb.Items)
95 | } else {
96 | if err := addToActionList(w.Actions(), tb.Actions); err != nil {
97 | return err
98 | }
99 | }
100 |
101 | return nil
102 | })
103 | }
104 |
--------------------------------------------------------------------------------
/declarative/checkbox.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type CheckBox struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // Button
50 |
51 | Checked Property
52 | OnCheckedChanged walk.EventHandler
53 | OnClicked walk.EventHandler
54 | Text Property
55 |
56 | // CheckBox
57 |
58 | AssignTo **walk.CheckBox
59 | CheckState Property
60 | OnCheckStateChanged walk.EventHandler
61 | TextOnLeftSide bool
62 | Tristate bool
63 | }
64 |
65 | func (cb CheckBox) Create(builder *Builder) error {
66 | w, err := walk.NewCheckBox(builder.Parent())
67 | if err != nil {
68 | return err
69 | }
70 |
71 | if cb.AssignTo != nil {
72 | *cb.AssignTo = w
73 | }
74 |
75 | return builder.InitWidget(cb, w, func() error {
76 | w.SetPersistent(cb.Persistent)
77 |
78 | if err := w.SetTextOnLeftSide(cb.TextOnLeftSide); err != nil {
79 | return err
80 | }
81 |
82 | if err := w.SetTristate(cb.Tristate); err != nil {
83 | return err
84 | }
85 |
86 | if _, isBindData := cb.CheckState.(bindData); cb.Tristate && (cb.CheckState == nil || isBindData) {
87 | w.SetCheckState(walk.CheckIndeterminate)
88 | }
89 |
90 | if cb.OnClicked != nil {
91 | w.Clicked().Attach(cb.OnClicked)
92 | }
93 |
94 | if cb.OnCheckedChanged != nil {
95 | w.CheckedChanged().Attach(cb.OnCheckedChanged)
96 | }
97 |
98 | if cb.OnCheckStateChanged != nil {
99 | w.CheckStateChanged().Attach(cb.OnCheckStateChanged)
100 | }
101 |
102 | return nil
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/examples/slider/slider.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 | . "github.com/tailscale/walk/declarative"
12 | )
13 |
14 | func main() {
15 | app, err := walk.InitApp()
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 |
20 | var slv, slh *walk.Slider
21 | var maxEdit, minEdit, valueEdit *walk.NumberEdit
22 |
23 | data := struct{ Min, Max, Value int }{0, 100, 30}
24 |
25 | MainWindow{
26 | Title: "Walk Slider Example",
27 | MinSize: Size{320, 240},
28 | Layout: HBox{},
29 | Children: []Widget{
30 | Slider{
31 | AssignTo: &slv,
32 | MinValue: data.Min,
33 | MaxValue: data.Max,
34 | Value: data.Value,
35 | Orientation: Vertical,
36 | OnValueChanged: func() {
37 | data.Value = slv.Value()
38 | valueEdit.SetValue(float64(data.Value))
39 |
40 | },
41 | },
42 | Composite{
43 | Layout: Grid{Columns: 3},
44 | StretchFactor: 4,
45 | Children: []Widget{
46 | Label{Text: "Min value"},
47 | Label{Text: "Value"},
48 | Label{Text: "Max value"},
49 | NumberEdit{
50 | AssignTo: &minEdit,
51 | Value: float64(data.Min),
52 | OnValueChanged: func() {
53 | data.Min = int(minEdit.Value())
54 | slh.SetRange(data.Min, data.Max)
55 | slv.SetRange(data.Min, data.Max)
56 | },
57 | },
58 | NumberEdit{
59 | AssignTo: &valueEdit,
60 | Value: float64(data.Value),
61 | OnValueChanged: func() {
62 | data.Value = int(valueEdit.Value())
63 | slh.SetValue(data.Value)
64 | slv.SetValue(data.Value)
65 | },
66 | },
67 | NumberEdit{
68 | AssignTo: &maxEdit,
69 | Value: float64(data.Max),
70 | OnValueChanged: func() {
71 | data.Max = int(maxEdit.Value())
72 | slh.SetRange(data.Min, data.Max)
73 | slv.SetRange(data.Min, data.Max)
74 | },
75 | },
76 | Slider{
77 | ColumnSpan: 3,
78 | AssignTo: &slh,
79 | MinValue: data.Min,
80 | MaxValue: data.Max,
81 | Value: data.Value,
82 | OnValueChanged: func() {
83 | data.Value = slh.Value()
84 | valueEdit.SetValue(float64(data.Value))
85 | },
86 | },
87 | VSpacer{},
88 | PushButton{
89 | ColumnSpan: 3,
90 | Text: "Print state",
91 | OnClicked: func() {
92 | log.Printf("H: < %d | %d | %d >\n", slh.MinValue(), slh.Value(), slh.MaxValue())
93 | log.Printf("V: < %d | %d | %d >\n", slv.MinValue(), slv.Value(), slv.MaxValue())
94 | },
95 | },
96 | },
97 | },
98 | },
99 | }.Create()
100 |
101 | app.Run()
102 | }
103 |
--------------------------------------------------------------------------------
/declarative/numberedit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package declarative
8 |
9 | import (
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | type NumberEdit struct {
14 | // Window
15 |
16 | Accessibility Accessibility
17 | Background Brush
18 | ContextMenuItems []MenuItem
19 | DoubleBuffering bool
20 | Enabled Property
21 | Font Font
22 | MaxSize Size
23 | MinSize Size
24 | Name string
25 | OnBoundsChanged walk.EventHandler
26 | OnKeyDown walk.KeyEventHandler
27 | OnKeyPress walk.KeyEventHandler
28 | OnKeyUp walk.KeyEventHandler
29 | OnMouseDown walk.MouseEventHandler
30 | OnMouseMove walk.MouseEventHandler
31 | OnMouseUp walk.MouseEventHandler
32 | OnSizeChanged walk.EventHandler
33 | Persistent bool
34 | RightToLeftReading bool
35 | ToolTipText Property
36 | Visible Property
37 |
38 | // Widget
39 |
40 | Alignment Alignment2D
41 | AlwaysConsumeSpace bool
42 | Column int
43 | ColumnSpan int
44 | GraphicsEffects []walk.WidgetGraphicsEffect
45 | Row int
46 | RowSpan int
47 | StretchFactor int
48 |
49 | // NumberEdit
50 |
51 | AssignTo **walk.NumberEdit
52 | Decimals int
53 | Increment float64
54 | MaxValue float64
55 | MinValue float64
56 | Prefix Property
57 | OnValueChanged walk.EventHandler
58 | ReadOnly Property
59 | SpinButtonsVisible bool
60 | Suffix Property
61 | TextColor walk.Color
62 | Value Property
63 | }
64 |
65 | func (ne NumberEdit) Create(builder *Builder) error {
66 | w, err := walk.NewNumberEdit(builder.Parent())
67 | if err != nil {
68 | return err
69 | }
70 |
71 | if ne.AssignTo != nil {
72 | *ne.AssignTo = w
73 | }
74 |
75 | return builder.InitWidget(ne, w, func() error {
76 | w.SetTextColor(ne.TextColor)
77 |
78 | if err := w.SetDecimals(ne.Decimals); err != nil {
79 | return err
80 | }
81 |
82 | inc := ne.Increment
83 | if inc == 0 {
84 | inc = 1
85 | }
86 |
87 | if err := w.SetIncrement(inc); err != nil {
88 | return err
89 | }
90 |
91 | if ne.MinValue != 0 || ne.MaxValue != 0 {
92 | if err := w.SetRange(ne.MinValue, ne.MaxValue); err != nil {
93 | return err
94 | }
95 | }
96 |
97 | if err := w.SetSpinButtonsVisible(ne.SpinButtonsVisible); err != nil {
98 | return err
99 | }
100 |
101 | if ne.OnValueChanged != nil {
102 | w.ValueChanged().Attach(ne.OnValueChanged)
103 | }
104 |
105 | return nil
106 | })
107 | }
108 |
--------------------------------------------------------------------------------
/examples/settings/settings.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 | "math/rand"
10 | "strings"
11 | "time"
12 |
13 | "github.com/tailscale/walk"
14 |
15 | . "github.com/tailscale/walk/declarative"
16 | )
17 |
18 | func main() {
19 | app, err := walk.InitApp()
20 | if err != nil {
21 | log.Fatal(err)
22 | }
23 |
24 | // These specify the app data sub directory for the settings file.
25 | app.SetOrganizationName("The Walk Authors")
26 | app.SetProductName("Walk Settings Example")
27 |
28 | // Settings file name.
29 | settings := walk.NewIniFileSettings("settings.ini")
30 |
31 | // All settings marked as expiring will expire after this duration w/o use.
32 | // This applies to all widgets settings.
33 | settings.SetExpireDuration(time.Hour * 24 * 30 * 3)
34 |
35 | if err := settings.Load(); err != nil {
36 | log.Fatal(err)
37 | }
38 |
39 | app.SetSettings(settings)
40 |
41 | if err := RunMainWindow(); err != nil {
42 | log.Fatal(err)
43 | }
44 |
45 | if err := settings.Save(); err != nil {
46 | log.Fatal(err)
47 | }
48 | }
49 |
50 | func RunMainWindow() error {
51 | if err := (MainWindow{
52 | Name: "mainWindow", // Name is needed for settings persistence
53 | Title: "Walk Settings Example",
54 | MinSize: Size{800, 600},
55 | Layout: VBox{MarginsZero: true},
56 | Children: []Widget{
57 | TableView{
58 | Name: "tableView", // Name is needed for settings persistence
59 | AlternatingRowBG: true,
60 | ColumnsOrderable: true,
61 | Columns: []TableViewColumn{
62 | // Name is needed for settings persistence
63 | {Name: "#", DataMember: "Index"}, // Use DataMember, if names differ
64 | {Name: "Bar"},
65 | {Name: "Baz", Format: "%.2f", Alignment: AlignFar},
66 | {Name: "Quux", Format: "2006-01-02 15:04:05", Width: 150},
67 | },
68 | Model: NewFooModel(),
69 | }},
70 | }.Create()); err != nil {
71 | return err
72 | }
73 |
74 | walk.App().Run()
75 |
76 | return nil
77 | }
78 |
79 | func NewFooModel() *FooModel {
80 | now := time.Now()
81 |
82 | rand.Seed(now.UnixNano())
83 |
84 | m := &FooModel{items: make([]*Foo, 1000)}
85 |
86 | for i := range m.items {
87 | m.items[i] = &Foo{
88 | Index: i,
89 | Bar: strings.Repeat("*", rand.Intn(5)+1),
90 | Baz: rand.Float64() * 1000,
91 | Quux: time.Unix(rand.Int63n(now.Unix()), 0),
92 | }
93 | }
94 |
95 | return m
96 | }
97 |
98 | type FooModel struct {
99 | walk.SortedReflectTableModelBase
100 | items []*Foo
101 | }
102 |
103 | func (m *FooModel) Items() interface{} {
104 | return m.items
105 | }
106 |
107 | type Foo struct {
108 | Index int
109 | Bar string
110 | Baz float64
111 | Quux time.Time
112 | }
113 |
--------------------------------------------------------------------------------
/examples/gradientcomposite/gradientcomposite.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "log"
9 |
10 | "github.com/tailscale/walk"
11 | . "github.com/tailscale/walk/declarative"
12 | )
13 |
14 | func main() {
15 | app, err := walk.InitApp()
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 |
20 | MainWindow{
21 | Title: "Walk GradientComposite Example",
22 | MinSize: Size{400, 0},
23 | Background: GradientBrush{
24 | Vertexes: []walk.GradientVertex{
25 | {X: 0, Y: 0, Color: walk.RGB(255, 255, 127)},
26 | {X: 1, Y: 0, Color: walk.RGB(127, 191, 255)},
27 | {X: 0.5, Y: 0.5, Color: walk.RGB(255, 255, 255)},
28 | {X: 1, Y: 1, Color: walk.RGB(127, 255, 127)},
29 | {X: 0, Y: 1, Color: walk.RGB(255, 127, 127)},
30 | },
31 | Triangles: []walk.GradientTriangle{
32 | {0, 1, 2},
33 | {1, 3, 2},
34 | {3, 4, 2},
35 | {4, 0, 2},
36 | },
37 | },
38 | Layout: HBox{Margins: Margins{100, 100, 100, 100}},
39 | Children: []Widget{
40 | GradientComposite{
41 | Border: true,
42 | Vertical: Bind("verticalCB.Checked"),
43 | Color1: Bind("rgb(c1RedSld.Value, c1GreenSld.Value, c1BlueSld.Value)"),
44 | Color2: Bind("rgb(c2RedSld.Value, c2GreenSld.Value, c2BlueSld.Value)"),
45 | Layout: HBox{},
46 | Children: []Widget{
47 | GroupBox{
48 | Title: "Gradient Parameters",
49 | Layout: VBox{},
50 | Children: []Widget{
51 | CheckBox{Name: "verticalCB", Text: "Vertical", Checked: true},
52 | GroupBox{
53 | Title: "Color1",
54 | Layout: Grid{Columns: 2},
55 | Children: []Widget{
56 | Label{Text: "Red:"},
57 | Slider{Name: "c1RedSld", Tracking: true, MaxValue: 255, Value: 95},
58 | Label{Text: "Green:"},
59 | Slider{Name: "c1GreenSld", Tracking: true, MaxValue: 255, Value: 191},
60 | Label{Text: "Blue:"},
61 | Slider{Name: "c1BlueSld", Tracking: true, MaxValue: 255, Value: 255},
62 | },
63 | },
64 | GroupBox{
65 | Title: "Color2",
66 | Layout: Grid{Columns: 2},
67 | Children: []Widget{
68 | Label{Text: "Red:"},
69 | Slider{Name: "c2RedSld", Tracking: true, MaxValue: 255, Value: 239},
70 | Label{Text: "Green:"},
71 | Slider{Name: "c2GreenSld", Tracking: true, MaxValue: 255, Value: 63},
72 | Label{Text: "Blue:"},
73 | Slider{Name: "c2BlueSld", Tracking: true, MaxValue: 255, Value: 0},
74 | },
75 | },
76 | },
77 | },
78 | },
79 | },
80 | },
81 | Functions: map[string]func(args ...interface{}) (interface{}, error){
82 | "rgb": func(args ...interface{}) (interface{}, error) {
83 | return walk.RGB(byte(args[0].(float64)), byte(args[1].(float64)), byte(args[2].(float64))), nil
84 | },
85 | },
86 | }.Create()
87 |
88 | app.Run()
89 | }
90 |
--------------------------------------------------------------------------------
/splitterhandle.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Walk Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package walk
8 |
9 | import (
10 | "github.com/tailscale/win"
11 | )
12 |
13 | const splitterHandleWindowClass = `\o/ Walk_SplitterHandle_Class \o/`
14 |
15 | func init() {
16 | AppendToWalkInit(func() {
17 | MustRegisterWindowClass(splitterHandleWindowClass)
18 | })
19 | }
20 |
21 | type splitterHandle struct {
22 | WidgetBase
23 | }
24 |
25 | func newSplitterHandle(splitter *Splitter) (*splitterHandle, error) {
26 | if splitter == nil {
27 | return nil, newError("splitter cannot be nil")
28 | }
29 |
30 | sh := new(splitterHandle)
31 | sh.parent = splitter
32 |
33 | if err := InitWindow(
34 | sh,
35 | splitter,
36 | splitterHandleWindowClass,
37 | win.WS_CHILD|win.WS_VISIBLE,
38 | 0); err != nil {
39 | return nil, err
40 | }
41 |
42 | sh.SetBackground(NullBrush())
43 |
44 | if err := sh.setAndClearStyleBits(0, win.WS_CLIPSIBLINGS); err != nil {
45 | return nil, err
46 | }
47 |
48 | return sh, nil
49 | }
50 |
51 | func (sh *splitterHandle) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
52 | switch msg {
53 | case win.WM_ERASEBKGND:
54 | if sh.Background() == nullBrushSingleton {
55 | return 1
56 | }
57 |
58 | case win.WM_PAINT:
59 | if sh.Background() == nullBrushSingleton {
60 | var ps win.PAINTSTRUCT
61 |
62 | win.BeginPaint(hwnd, &ps)
63 | defer win.EndPaint(hwnd, &ps)
64 |
65 | return 0
66 | }
67 | }
68 |
69 | return sh.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
70 | }
71 |
72 | func (sh *splitterHandle) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
73 | var orientation Orientation
74 | var handleWidth int
75 |
76 | if splitter, ok := sh.Parent().(*Splitter); ok {
77 | orientation = splitter.Orientation()
78 | handleWidth = splitter.HandleWidth()
79 | }
80 |
81 | return &splitterHandleLayoutItem{
82 | orientation: orientation,
83 | handleWidth: handleWidth,
84 | }
85 | }
86 |
87 | type splitterHandleLayoutItem struct {
88 | LayoutItemBase
89 | orientation Orientation
90 | handleWidth int
91 | }
92 |
93 | func (li *splitterHandleLayoutItem) LayoutFlags() LayoutFlags {
94 | if li.orientation == Horizontal {
95 | return ShrinkableVert | GrowableVert | GreedyVert
96 | }
97 |
98 | return ShrinkableHorz | GrowableHorz | GreedyHorz
99 | }
100 |
101 | func (li *splitterHandleLayoutItem) IdealSize() Size {
102 | var size Size
103 | dpi := int(win.GetDpiForWindow(li.handle))
104 |
105 | if li.orientation == Horizontal {
106 | size.Width = IntFrom96DPI(li.handleWidth, dpi)
107 | } else {
108 | size.Height = IntFrom96DPI(li.handleWidth, dpi)
109 | }
110 |
111 | return size
112 | }
113 |
114 | func (li *splitterHandleLayoutItem) MinSize() Size {
115 | return li.IdealSize()
116 | }
117 |
--------------------------------------------------------------------------------