This file is project context for coding agents (and humans). Keep it concise and factual.
- Windows only (Win32 dialogs + controls).
- If you have a
.sysofile: usego run .(NOTgo run main.go), otherwise resources won't load. - For keyboard navigation (Tab/accelerators): call
wingui.SetCurrentDialog(dlg.Handle())for the active dialog (single global). - If you are building/using wingui as a DLL: call
wingui.InitHInstance("your.dll")before creating dialogs.
- Module:
github.com/whtiehack/wingui - Go:
go 1.10(seego.mod) - Platform: Windows only (no build tags; non-Windows builds will fail)
- Main deps:
github.com/lxn/win(Win32 APIs/constants/types)golang.org/x/sys/windows(low-level Windows syscall support)
wingui.go-InitHInstance,MessageLoop,SetCurrentDialogdialog.go-Dialog, message routing (dialogWndProc), widget bindingwindows.base.go-WindowBase(common operations + subclassing support)- Controls:
button.go- Button/CheckBox/RadioButtonedit.go- Edit controlstatic.go- Static textcombobox.go- ComboBoxlistbox.go- ListBoxprogressbar.go- ProgressBartrackbar.go- TrackBar/Slidertabcontrol.go- TabControl (partial; see notes)image.go,bitmap.go- image/bitmap helpers
winapi/- extra Win32 wrappers/constants not inlxn/wintools/genids/- generate Go consts fromresource.hexamples/- runnable samples (many include prebuilt.syso)
Widget(dialog.go):WndProc(msg, wParam, lParam) uintptrAsWindowBase() *WindowBase
WindowBase(windows.base.go): embedded by widgets; ownshwnd,idd, and subclassing state.Dialog(dialog.go):- Modeless:
CreateDialogParam - Modal:
DialogBoxParam - Routes messages to widgets via
dialogWndProc
- Modeless:
Dialog.dialogWndProc routes to Widget.WndProc in three ways:
- Subclassed child controls: if
hwnd != dlg.hwndanddlg.items[hwnd]exists, route to that widget. - Notifications sent to the dialog (e.g.
WM_COMMAND): for messages wherelParamis the child HWND, route todlg.items[HWND(lParam)]. WM_NOTIFY: routes usingNMHDR.HwndFrom.
Guideline for widget WndProc:
- Handle what you need, then call
w.AsWindowBase().WndProc(...)to fall through to the previous WndProc when subclassing is active.
wingui.MessageLoop():
- Calls
runtime.LockOSThread()(Windows UI should stay on one OS thread) - Runs
GetMessage/TranslateMessage/DispatchMessage - If a current dialog is set (global), runs
IsDialogMessageto support tab/keyboard navigation
wingui.SetCurrentDialog(hwnd):
- Single global handle used by
IsDialogMessage - If multiple dialogs are shown, you must update the current one when focus changes (library does not manage this automatically)
Dialog lifetime:
Dialogtracks a global dialog count; when the last dialog is destroyed, it callsPostQuitMessage(0).
Typical (MinGW/TDM-GCC) via windres:
windres -i ui/ui.rc -O coff -o ui.syso
# or
windres -i resource.res -O coff -o vsui.sysoNotes:
- Use a
windresthat matches your target arch (32-bit vs 64-bit). - The
.sysomust be in the package directory you build/run (commonly themainpackage).
Controls are referenced by numeric IDs. Optional helper to generate Go consts:
go run tools/genids/genids.go -filename=ui/resource.h -packagename=maintools/genids behavior:
- Output file name:
path.Base(filename) + ".go"(e.g.resource.h.go) - Output location: current working directory (where you run the command)
- Regex only captures positive integer IDs; things like
#define IDC_STATIC (-1)are ignored.
dlg, _ := wingui.NewDialog(IDD_DIALOG, 0, nil)
dlg.SetIcon(IDI_ICON)
btn, _ := wingui.BindNewButton(IDC_BUTTON, dlg)
btn.OnClicked = func() { dlg.Close() }
dlg.Show()
// Optional but recommended for tab/keyboard navigation:
wingui.SetCurrentDialog(dlg.Handle())
wingui.MessageLoop()ret := wingui.NewModalDialog(IDD_DIALOG, 0, nil, func(dlg *wingui.Dialog) {
// Bind widgets here (WM_INITDIALOG time)
})
_ = retNotes:
- Modal dialogs run their own message loop internally; do not call
wingui.MessageLoop()just for a modal dialog.
edit := wingui.NewEdit(IDC_EDIT)
edit.Subclassing = true // must set BEFORE binding
dlg.BindWidgets(edit)NewEdit/BindNewEditNewButton/BindNewButton/BindNewButtonsNewStatic/BindNewStaticNewImage/BindNewImageNewComboBox/BindNewComboBoxNewListBox/BindNewListBoxNewProgressBar/BindNewProgressBarNewTrackBar/BindNewTrackBarNewTabControl/BindTabControl(partial implementation)
- Create
xxx.gowithtype X struct { WindowBase ... } - Implement
WndProc:- handle notifications/messages you care about
- call
x.AsWindowBase().WndProc(msg, wParam, lParam)as fallback
- Provide
NewX(idd)andBindNewX(idd, dlg)helpers - Keep behavior consistent with existing widgets (mostly
WM_COMMAND/WM_NOTIFY-based callbacks) - Update docs/examples if it is user-facing
Create Dialog error:<idd>:- resource not linked (missing
.syso, wrong package dir, usedgo run main.go) - wrong
hInstance(DLL scenario: callInitHInstance("your.dll"))
- resource not linked (missing
GetDlgItem Errorwhile binding:- wrong control ID
- binding before dialog
WM_INITDIALOG(for modal dialogs, bind inside callback)
- Tab key/accelerators not working:
- call
wingui.SetCurrentDialog(dlg.Handle()) - ensure controls have tab-stop styles in the dialog resource
- call
- Widget callback not firing:
- ensure widget is bound (
dlg.BindWidgets(...)succeeded) - for
WM_COMMAND-based handlers, many widgets checklParam == uintptr(widget.hwnd)
- ensure widget is bound (
Implemented:
- Edit, Static, Image
- ComboBox, ListBox
- Button (Push/Check/Radio)
- TrackBar, ProgressBar
Partial / TODO:
- TabControl (partial)
- ListView, TreeView, DateTimePicker, Menu, etc.