A Windows GUI toolkit for the Go Programming Language

About Walk

Walk is a "Windows Application Library Kit" for the Go Programming Language.

Its primarily useful for Desktop GUI development, but there is some more stuff.

Setup

Make sure you have a working Go installation. See Getting Started

Note

Walk currently requires Go 1.11.x or later.

To Install

Now run go get github.com/lxn/walk

Using Walk

The preferred way to create GUIs with Walk is to use its declarative sub package, as illustrated in this small example:

test.go
package main

import (
	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"
	"strings"
)

func main() {
	var inTE, outTE *walk.TextEdit

	MainWindow{
		Title:   "SCREAMO",
		MinSize: Size{600, 400},
		Layout:  VBox{},
		Children: []Widget{
			HSplitter{
				Children: []Widget{
					TextEdit{AssignTo: &inTE},
					TextEdit{AssignTo: &outTE, ReadOnly: true},
				},
			},
			PushButton{
				Text: "SCREAM",
				OnClicked: func() {
					outTE.SetText(strings.ToUpper(inTE.Text()))
				},
			},
		},
	}.Run()
}
Create Manifest test.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
        </dependentAssembly>
    </dependency>
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
        </windowsSettings>
    </application>
</assembly>

Then either compile the manifest using the rsrc tool, like this:

go get github.com/akavel/rsrc
rsrc -manifest test.manifest -o rsrc.syso

or rename the test.manifest file to test.exe.manifest and distribute it with the application instead.

Build app

In the directory containing test.go run

go build

To get rid of the cmd window, instead run

go build -ldflags="-H windowsgui"
Run app
test.exe
Sample Output (Windows 7)

alt tag

More Examples

There are some examples that should get you started.

Application Manifest Files

Walk requires Common Controls 6. This means that you must put an appropriate application manifest file either next to your executable or embedded as a resource.

You can copy one of the application manifest files that come with the examples.

To embed a manifest file as a resource, you can use the rsrc tool.

IMPORTANT: If you don't embed a manifest as a resource, then you should not launch your executable before the manifest file is in place. If you do anyway, the program will not run properly. And worse, Windows will not recognize a manifest file, you later drop next to the executable. To fix this, rebuild your executable and only launch it with a manifest file in place.

CGo Optimizations

The usual default message loop includes calls to win32 API functions, which incurs a decent amount of runtime overhead coming from Go. As an alternative to this, you may compile Walk using an optional C implementation of the main message loop, by passing the walk_use_cgo build tag:

go build -tags walk_use_cgo

Owner
Alexander Neumann
Alexander Neumann
Comments
  • How to keep HSplitter fixed

    How to keep HSplitter fixed

    	window, _ := walk.NewMainWindow()
    	window.SetTitle(``)
    	window.SetWidth(400)
    	window.SetHeight(200)
    	vbox := walk.NewVBoxLayout()
    	vbox.SetMargins(walk.Margins{
    		HNear: 4,
    		VNear: 4,
    		HFar:  4,
    		VFar:  4,
    	})
    	window.SetLayout(vbox)
    
    	bit32, _ := walk.NewPushButton(window)
    	bit32.SetText(``)
    
    	bit64, _ := walk.NewPushButton(window)
    	bit64.SetText(``)
    
    	button_list, _ := walk.NewHSplitter(window)
    	button_list.Children().Add(bit32)
    	button_list.Children().Add(bit64)
    
    	tip, _ := walk.NewLabel(window)
    	tip.SetText(``)
    
    	window.Children().Add(button_list)
    	window.Children().Add(tip)
    	window.Show()
    	window.Run()
    

    This is my code, but HSplitter moves left and right.

  • Sporadic Crash at form.go#432

    Sporadic Crash at form.go#432

    I see now and then a program crash. It is not systematic but can not be captured (no automatic recovery) and for a commercial program this is not good (as I can not fix it).

    I would love to fix it myself but have no idea yet as to why or when it happens. The culprit is always the same and the stackdump also is always similar. The big common factor seems to be the call to win.IsDialogMessage() by func (fb *FormBase) Run() int {...} See line 432 of \github.com\lxn\walk\form.go

    		if !win.IsDialogMessage(fb.hWnd, &msg) {
    			win.TranslateMessage(&msg)
    			win.DispatchMessage(&msg)
    		}
    
    github.com/lxn/win.IsDialogMessage(0xd0b9c, 0xc0029c0f20, 0x0)
    	C:/Users/Stephan/go/src/github.com/lxn/win/user32.go:2387 +0x69
    github.com/lxn/walk.(*FormBase).Run(0xc000043c00, 0x0)
    	C:/Users/Stephan/go/src/github.com/lxn/walk/form.go:432 +0x42e
    main.RunMainWindow(0x0, 0x0)
    	c:/Build/Accounting/Main.go:304 +0x3a21
    main.main()
    	c:/Build/Accounting/Main.go:51 +0x42
    
  • added template subpackage to make creating GUI easy from code

    added template subpackage to make creating GUI easy from code

    After I did my GUI from code, I decided to put together a simple template subpackage for walk. It is very limited in functionality, as only a few widgets and settings are implemented. An example is also provided.

    Import paths might need fixing because I import "walk" as of now. I'll fix it up/rebase it if you like this and want to merge.

    Features:

    1. it makes making a GUI from code considerably shorter (in my very limited test case it was a 60% reduction)
    2. the GUI created in code has a tree structure in code, making it easier to edit/extend
    3. makes error handling easy also without using panics
  • Fails to run after upgrade to go1.4

    Fails to run after upgrade to go1.4

    Number of examples fail to run after upgrading to go1.4 under Windows GOARCH=386. They work on Windows GOARCH=amd64. Once you downgrade into go1.3 everything works fine.

    There is typical stack trace once you use go1.4 or go1.4.1 and try to run it after build targeting 386:

    C:/_godev/src/github.com/lxn/walk/error.go:79 (0x45ea11) newErr: return &Error{message: message, stack: debug.Stack()} C:/_godev/src/github.com/lxn/walk/error.go:83 (0x45eb50) newError: return processError(newErr(message)) C:/_godev/src/github.com/lxn/walk/tooltip.go:115 (0x485c67) (_ToolTip).AddTool: return newError("TTM_ADDTOOL failed") C:/_godev/src/github.com/lxn/walk/widget.go:106 (0x48a31e) (_WidgetBase).init: if err := globalToolTip.AddTool(wb); err != nil { C:/_godev/src/github.com/lxn/walk/window.go:396 (0x48cc26) InitWindow: if err := wb.init(widget); err != nil { C:/_godev/src/github.com/lxn/walk/widget.go:85 (0x48a16b) InitWidget: if err := InitWindow(widget, parent, className, style|win.WS_CHILD, exStyle); err != nil { C:/_godev/src/github.com/lxn/walk/composite.go:31 (0x45a910) newCompositeWithStyle: win.WS_EX_CONTROLPARENT); err != nil { C:/_godev/src/github.com/lxn/walk/composite.go:39 (0x45aa21) NewComposite: return newCompositeWithStyle(parent, 0) C:/_godev/src/github.com/lxn/walk/form.go:78 (0x4600a3) (*FormBase).init: if fb.clientComposite, err = NewComposite(form); err != nil { C:/_godev/src/github.com/lxn/walk/window.go:388 (0x48cb89) InitWindow: if err := fb.init(form); err != nil { C:/_godev/src/github.com/lxn/walk/mainwindow.go:37 (0x4691e9) NewMainWindow: win.WS_EX_CONTROLPARENT); err != nil { C:/_godev/src/github.com/lxn/walk/declarative/mainwindow.go:37 (0x4c38c5) MainWindow.Create: w, err := walk.NewMainWindow() C:/_godev/src/_desktop/mapper/mapper.go:422 (0x403577) RunMainWindow: }.Create()); err != nil { C:/_godev/src/_desktop/mapper/mapper.go:123 (0x4011d3) main: if err := RunMainWindow(false); err != nil { c:/go/src/runtime/proc.go:63 (0x4181dc) main: main_main() c:/go/src/runtime/asm_386.s:2287 (0x4385c1) goexit:

    Problem instantly disappears if you downgrade into go1.3 and perform go build -a

    This is somehow only GOARCH=386 problem. I hope this can be fixed.

  • Dynamic Annotation Cleanup

    Dynamic Annotation Cleanup

    This PR aims to address as many suggestions reported in #623 as possible.

    @lxn, please do not merge this PR before @zx2c4 takes a look at it. It is much easier for me to have this PR open and fix things inside it to make everyone happy, than reverting master back and forth.

    • HRESULTs are no longer returned to the clients.
    • All win.SOMETHING_UGLY constants have been redeclared in lxn/walk and created as enums. Like MessageBox does it.
    • Constants were declared in accessibility.go to reduce clutter in windows.go.
    • I am not sure about keeping Dynamic Annotation methods in the Window interface, but Dynamic Annotation properties are bound to each HWND like window text and other GDI properties. If you can't see them, it doesn't mean they are less important.
    • All windows of the same windows group now share the same CLSID_AccPropServices object.
    • OLE/COM initialization is no longer required/expected from the client. It is initialized in WindowGroup automatically. Like folder selection dialog or WebView control do. Unlike ProgressIndicator does not.
  • tableview: use 96dpi for image heights

    tableview: use 96dpi for image heights

    Without this, rows get double-sized, having 2x height.

    I don't know how "correct" this commit is, but it seems to maybe work with WireGuard.

    CC @rozmansi

  • Vertical slider in upper position is lower value

    Vertical slider in upper position is lower value

    1. How to swap values, or how to turn off the pop-up pointer to the current value? image
    2. How to exclude the space between two containers? But it is also necessary that TableView could expand when the window size was changed. I need a similar effect as if I swap two containers in this code, need only an expanding table.
    package main
    
    import (
    	. "github.com/lxn/walk/declarative"
    )
    
    func main() {
    	MainWindow{
    		Layout: VBox{SpacingZero: true, MarginsZero: true},
    		Size:   Size{Height: 400, Width: 400},
    		Children: []Widget{
    			Composite{
    				Layout: Flow{},
    				Children: []Widget{
    					Composite{
    						Border: true,
    						Layout: HBox{},
    						Children: []Widget{
    							Slider{
    								Orientation: Vertical,
    								MinSize:     Size{Height: 120},
    							},
    						},
    					},
    				},
    			},
    			Composite{
    				Layout: Grid{},
    				Children: []Widget{
    					Label{
    						Text: "Text",
    					},
    					TableView{
    						RowSpan: 2,
    						Column:  1,
    					},
    					VSpacer{Row: 1},
    				},
    			},
    		},
    	}.Run()
    }
    

    image

  • Label: SetText , The content overlap

    Label: SetText , The content overlap

    image

    // Copyright 2013 The Walk Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package main
    
    import (
    	"log"
    )
    
    import (
    	"github.com/lxn/walk"
    	. "github.com/lxn/walk/declarative"
    )
    
    func main() {
    	var te *walk.TextEdit
    	var lb *walk.Label
    
    	if _, err := (MainWindow{
    		Title:   "Walk Clipboard Example",
    		MinSize: Size{300, 200},
    		Layout:  VBox{},
    		Children: []Widget{
    			PushButton{
    				Text: "Copy",
    				OnClicked: func() {
    					if err := walk.Clipboard().SetText(te.Text()); err != nil {
    						log.Print("Copy: ", err)
    					}
    				},
    			},
    			PushButton{
    				Text: "Paste",
    				OnClicked: func() {
    					if text, err := walk.Clipboard().Text(); err != nil {
    						log.Print("Paste: ", err)
    					} else {
    						te.SetText(text)
    						lb.SetText(text)
    					}
    				},
    			},
    			TextEdit{
    				AssignTo: &te,
    			},
    			Label{
    				AssignTo: &lb,
    			},
    		},
    	}).Run(); err != nil {
    		log.Fatal(err)
    	}
    }
    
    
  • Multiple windows and concurrency

    Multiple windows and concurrency

    What is the proper way of creating multiple windows (mainWindow + dialogs) and changing their content concurrently?

    I found, that every window creates new goroutine to get messages: switch win.GetMessage(&msg, 0, 0, 0) {

    Also, dialog.Synchronize(...) does not do the job, because runSynchronized() is a global function invoked from multiple goroutines.

    Now, when there are 2 windows sometimes the dialog one gets stuck when changing content (setting value of a ProgressBar). Program panicking on concurrent map access is another thing, which can be fixed with mutexes.

    Why the dialog freezes? What is the best method for creating multiple windows, or is it an API bug?

    After the window gets stuck, both window goroutines are in GetMessage state, but the dialog does not respond (stacktrace debugged from another TCP goroutine).

  • form: don't use Go heap for MSG

    form: don't use Go heap for MSG

    It's not entirely clear to me what's going on here, and I'm only half-certain that this fixes the issue, due to it being somewhat hard to reproduce. However, here's what I suspect is going on. Because we're passing this as a pointer to various levels of function calls that might store a reference to the pointer, Go allocates the variable on the heap. Later, various closures are allocated on the same heap, during which time some global Go lock is taken and W^X page permissions are twiddled with. The Go locking then doesn't effect functions that are in win32api calls, and at the critical moment, the variable no longer has writable page permissions. While this might point to deeper Go runtime issues, we just work around this here by allocating the variable using GlobalAlloc. At the very least, I haven't been able to reproduce the bug after applying this patch.

    Fixes: #483

  • Paint (Background Erase) and Scroll events

    Paint (Background Erase) and Scroll events

    No easy way to attach Paint or Scrollbar event handlers to TableView, TreeView and other common components.

    I have TableView. If it changes or gets scrolled I would like to update other part(s) of the screen. Problem is that there seems no easy way to attach event handlers on Paint (Background Erase) and on Scrollbar move events.

    I do understand that most functionality is provided thru the use of Models, and use of Paint (Background Erase) is like a hammer. But sometimes there is a need to update other parts of GUI window when one of its controls gets Scrolled or somehow repainted.

    I just do not feel like "monkey patching" this myself: func (tv *TableView) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { switch msg { case win.WM_ERASEBKGND: /// monkey patch here :-(

    Any advice?

  • How do a get buttons not to take the focus?

    How do a get buttons not to take the focus?

    I want my buttons not get the focus ever, for example: (1) when I press TAB the focus will skip on it (2) not to grab the focus when they are clicked

    How should I develop?

  • [BUG] Crash when trying to resize the control.

    [BUG] Crash when trying to resize the control.

    What I did:

    • Write a file
    • Debug and run the program
    • At the beginning the program works fine
    • But when I move the mouse to the bottom of the TableView and find that I can adjust the height at this point, I click the mouse and drag it up and down image
    • There is a crash

    Crash message:

    "runtime error: invalid memory address or nil pointer dereference"
    Stack:
    	 4  0x0000000000614d3f in github.com/lxn/walk.(*Splitter).onInsertedWidget.func3.3
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/splitter.go:426
    	 5  0x00000000005fe97a in github.com/lxn/walk.(*MouseEventPublisher).Publish
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/mouseevent.go:69
    	 6  0x00000000006579fc in github.com/lxn/walk.(*WindowBase).publishMouseEvent
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/window.go:2054
    	 7  0x000000000065a0c9 in github.com/lxn/walk.(*WindowBase).WndProc
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/window.go:2382
    	 8  0x0000000000616545 in github.com/lxn/walk.(*splitterHandle).WndProc
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/splitterhandle.go:69
    	 9  0x000000000065839e in github.com/lxn/walk.defaultWndProc
    	     at C:/Users/zzlwd/go/pkg/mod/github.com/lxn/[email protected]/window.go:2159
    	(truncated)
    

    image

    My question:

    Is it because I'm dragging the bottom element, causing the bottom element to be larger than the program can display? Or is it related to this issue #597?

    My Code

    func mainWin() {
    	var mainwin *walk.MainWindow
    	mw := &CondomMainWindow{model: NewCondomModel()}
    
    	if err := (MainWindow{
    		AssignTo: &mainwin,
    		Size:     Size{Width: 800, Height: 100},
    		Layout:   VBox{},
    		Children: []Widget{
    			HSplitter{
    				Children: []Widget{
    					VSplitter{
    						Children: []Widget{
    							GroupBox{
    								Layout: HBox{},
    								Title:  "应用设置",
    								Children: []Widget{
    									LinkLabel{
    										Text: "频率:",
    									},
    									NumberEdit{
    										MinSize:  Size{Width: 20},
    										MaxSize:  Size{Width: 30},
    										MinValue: 10,
    										MaxValue: 120,
    									},
    									LinkLabel{
    										Text: "秒",
    									},
    									HSpacer{},
    								},
    							},
    							TableView{
    								MaxSize:  Size{Height: 300},
    								MinSize:  Size{Width: 450},
    								AssignTo: &mw.tv,
    								// CheckBoxes:       true,
    								ColumnsOrderable: true,
    								// MultiSelection:   true,
    								Columns: []TableViewColumn{
    									// ......
    								},
    								Model: mw.model,
    							},
    							VSpacer{},
    						},
    					},
    				},
    			},
    		},
    	}).Create(); err != nil {
    		log.Fatal(err)
    	}
    
    	mainwin.Run()
    }
    
  • ListBox.OnCurrentIndexChanged  in GroupBox  ineffective

    ListBox.OnCurrentIndexChanged in GroupBox ineffective

    when I put ListBox in GroupBox ,OnCurrentIndexChanged and OnItemActivated is Ineffective。

    I modified the listbox example, just like this:

                               GroupBox{
    				Layout: VBox{},
    				Children: []Widget{
    					ListBox{
    						AssignTo:              &mw.lb,
    						Model:                 mw.model,
    						OnCurrentIndexChanged: mw.nglb_CurrentIndexChanged,
    						OnItemActivated:       mw.nglb_ItemActivated,
    					},
    					TextEdit{
    						AssignTo: &mw.te,
    						ReadOnly: true,
    					},
    				},
    		
    
Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase
Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

Kita is a declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase. Inspired by Flutter, React. S

Apr 18, 2022
UIKit - A declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase
 UIKit - A declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

UIKit - A declarative, reactive GUI toolkit for build cross platform apps with web technology with single codebase

Apr 18, 2022
Common library for Go GUI apps on Windows
Common library for Go GUI apps on Windows

winc Common library for Go GUI apps on Windows. It is for Windows OS only. This makes library smaller than some other UI libraries for Go.

Dec 12, 2022
Windows GUI framework for Go.

gform is an easy to use Windows GUI toolkit for Go It provides two approaches to create UI. 1. Pure code. gform.Init() mainWindow := gform.NewForm(ni

Jan 1, 2023
Windows GUI library for Go (Golang). Comes with a graphical UI designer.

Version 2 Please go to Version 2 of this library for the latest version. Windows GUI Library This is a pure Go library to create native Windows GUIs.

Jan 1, 2023
Golang bindings for XCGUI, Windows GUI library, DirectUI design idea.
Golang bindings for XCGUI, Windows GUI library, DirectUI design idea.

XCGUI 项目文档 帮助文档 程序示例 介绍 English | 简体中文 DirectUI设计思想: 在窗口内没有子窗口,界面元素都是逻辑上的区域(无HWND句柄,安全,灵活), 所有UI元素都是自主开发(不受系统限制), 更加灵活的实现各种程序界面,满足不同用户的需求.

Dec 22, 2022
Windows API and GUI in idiomatic Go.
Windows API and GUI in idiomatic Go.

Windigo Win32 API and GUI in idiomatic Go. Overview The UI library is divided in the following packages: Package Description ui High-level UI wrappers

Dec 27, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Dec 3, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Dec 3, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functional build process. This repository is intended as a quick reference to help others start similar projects using the referenced libraries and will not be actively maintained.

Dec 3, 2022
An example desktop system tray application that can launch HTML5 windows. Go source with a build process for Windows, Mac and Linux.

ExampleTrayGUI An example cross-platform (Mac, Windows, Linux) system tray application that can launch HTML5 windows, developed in Go including functi

Dec 3, 2022
W32find - Find parent windows and their children windows using win32api.

w32find Package w32find provides a set of interface to win32 APIs that can be used to find windows and their controls. Install go get -v github.com/mo

Jan 5, 2022
Cross platform GUI in Go based on Material Design
Cross platform GUI in Go based on Material Design

About Fyne is an easy to use UI toolkit and app API written in Go. It is designed to build applications that run on desktop and mobile devices with a

Jan 3, 2023
Go wrapper around the Iup GUI toolset

Iup Go Wrapper iup is a Go wrapper around the Iup GUI toolkit. The project was started on April 27, 2011. Fork https://github.com/grd/iup is a fork of

Nov 28, 2020
Platform-native GUI library for Go.

ui: platform-native GUI library for Go This is a library that aims to provide simple GUI software development in Go. It is based on my libui, a simple

Jan 9, 2023
Go Wrapper for the wxWidgets GUI

This is the source code for wxGo a Go wrapper of the wxWidgets library. The actuall wxWidgets source code is not included and will need to be downloa

Nov 30, 2022
Build cross platform GUI apps with GO and HTML/JS/CSS (powered by Electron)

Thanks to go-astilectron build cross platform GUI apps with GO and HTML/JS/CSS. It is the official GO bindings of astilectron and is powered by Electr

Jan 9, 2023
Build cross platform GUI apps with GO and HTML/JS/CSS (powered by nwjs)
Build cross platform GUI apps with GO and HTML/JS/CSS (powered by nwjs)

gowd Build cross platform GUI apps with GO and HTML/JS/CSS (powered by nwjs) How to use this library: Download and install nwjs Install this library g

Dec 11, 2022
RobotGo, Go Native cross-platform GUI automation @vcaesar

Robotgo Golang Desktop Automation. Control the mouse, keyboard, bitmap, read the screen, Window Handle and global event listener. RobotGo supports Mac

Jan 7, 2023