├── 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 | --------------------------------------------------------------------------------