Git Product home page Git Product logo

windigo's Introduction

Go Reference GitHub go.mod Go version of a Go module Lines of code License: MIT

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 for windows and controls.
ui/wm High-level event parameters (Windows message callbacks).

For the Win32 API bindings:

Package Description
win Native Win32 structs, handles and functions.
win/co Native Win32 constants, all typed.
win/errco Native Win32 error codes, with types errco.ERROR and errco.CDERR.

For the COM bindings, there is the main package, and two subpackages – the co suffix contains the constants, and the vt contains the virtual tables:

Packages Description
win/com/autom
win/com/autom/automco
win/com/autom/automvt
Native Win32 Automation COM interfaces.
win/com/com
win/com/com/comco
win/com/com/comvt
Native Win32 COM API base.
win/com/d2d1
win/com/d2d1/d2d1co
win/com/d2d1/d2d1vt
Native Win32 Direct2D COM interfaces.
win/com/dshow
win/com/dshow/dshowco
win/com/dshow/dshowvt
Native Win32 DirectShow COM interfaces.
win/com/shell
win/com/shell/shellco
win/com/shell/shellvt
Native Win32 Shell COM interfaces.

Windigo is designed to be familiar to Win32 programmers, using the same concepts, so most C/C++ Win32 tutorials should be applicable.

Windows and controls can be created in two ways:

  • programmatically, by specifying the options used in the underlying CreateWindowEx;
  • by loading resources from a .rc or a .res file.

CGo is not used, just syscalls.

Error treatment

The native Win32 functions deal with errors in two ways:

  • Recoverable errors will return an errco.ERROR value, which implements the error interface;

  • Unrecoverable errors will simply panic. This avoids the excess of if err != nil with errors that cannot be recovered anyway, like internal Windows errors.

Example

The example below creates a window programmatically, and handles the button click. Also, it uses the minimal.syso provided in the resources folder.

Screen capture

package main

import (
    "fmt"
    "runtime"

    "github.com/rodrigocfd/windigo/ui"
    "github.com/rodrigocfd/windigo/win"
    "github.com/rodrigocfd/windigo/win/co"
)

func main() {
    runtime.LockOSThread()

    myWindow := NewMyWindow() // instantiate
    myWindow.wnd.RunAsMain()  // ...and run
}

// This struct represents our main window.
type MyWindow struct {
    wnd     ui.WindowMain
    lblName ui.Static
    txtName ui.Edit
    btnShow ui.Button
}

// Creates a new instance of our main window.
func NewMyWindow() *MyWindow {
    wnd := ui.NewWindowMain(
        ui.WindowMainOpts().
            Title("Hello you").
            ClientArea(win.SIZE{Cx: 340, Cy: 80}).
            IconId(101), // ID of icon resource, see resources folder
    )

    me := &MyWindow{
        wnd: wnd,
        lblName: ui.NewStatic(wnd,
            ui.StaticOpts().
                Text("Your name").
                Position(win.POINT{X: 10, Y: 22}),
        ),
        txtName: ui.NewEdit(wnd,
            ui.EditOpts().
                Position(win.POINT{X: 80, Y: 20}).
                Size(win.SIZE{Cx: 150}),
        ),
        btnShow: ui.NewButton(wnd,
            ui.ButtonOpts().
                Text("&Show").
                Position(win.POINT{X: 240, Y: 19}),
        ),
    }

    me.btnShow.On().BnClicked(func() {
        msg := fmt.Sprintf("Hello, %s!", me.txtName.Text())
        me.wnd.Hwnd().MessageBox(msg, "Saying hello", co.MB_ICONINFORMATION)
    })

    return me
}

License

Licensed under MIT license, see LICENSE.md for details.

windigo's People

Contributors

hoto-cocoa avatar kjk avatar narrat avatar rodrigocfd avatar rogeecn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

windigo's Issues

README example fails to run on WINE

$ GOOS=windows go build                                    
$ wine limejuice.exe    
002c:err:winediag:getaddrinfo Failed to resolve your host name IP
007c:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
007c:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
007c:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
007c:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
0024:fixme:seh:WerGetFlags (FFFFFFFFFFFFFFFF, 00007FFFFE2FFED8) stub
0024:fixme:seh:WerSetFlags (32) stub
0024:fixme:process:SetProcessPriorityBoost (FFFFFFFFFFFFFFFF,1): stub
0024:fixme:powrprof:PowerRegisterSuspendResumeNotification (0x00000002,00007FFFFE2FFE48,00007FFFFE2FFE40) stub!
panic: [87 0x57] Invalid parameter.

goroutine 1 [running, locked to thread]:
github.com/rodrigocfd/windigo/ui._FirstMainStuff()
        /home/meow/.cache/go/mod/github.com/rodrigocfd/[email protected]/ui/any_gl
obals.go:175 +0x5c
github.com/rodrigocfd/windigo/ui.(*_WindowRawMain).RunAsMain(0xc000114000)
        /home/meow/.cache/go/mod/github.com/rodrigocfd/[email protected]/ui/Window
RawMain.go:49 +0x45
main.main()
        /home/meow/src/test/main.go:16 +0x25

How to create Tabs?

I've been looking around quite a bit, but I've been unable to figure out how to create a Tab control. It seems like Windigo doesn't have any support for them?

GetWindowLongPtrW&SetWindowLongPtrW Not found.

Failed to find GetWindowLongPtrW procedure in user32.dll: The specified procedure could not be found.
Failed to find SetWindowLongPtrW procedure in user32.dll: The specified procedure could not be found.

[Feature] DDE support/bindings

DDE (dynamic data exchange, DDE docs) is used widely in Windows and it'd be great if there were a way to create (at least) DDE clients and (possibly) DDE servers in Go using windigo.

System tray example?

Hello,

I am researching GUI frameworks for Golang and came across yours.

Is there any popup system tray example?

Thanks

LVM_SETITEMSTATE failed when pressing Ctrl-A to co.LVS_SINGLESEL flagged ListView

I created ListView using following code:

ui.NewListView(parent, ui.ListViewOpts().
		Position(location).
		Size(size).
		CtrlStyles(co.LVS_REPORT|co.LVS_NOSORTHEADER|co.LVS_SHOWSELALWAYS|co.LVS_SINGLESEL).
		CtrlExStyles(co.LVS_EX_FULLROWSELECT|co.LVS_EX_GRIDLINES))

And If I pressing Ctrl-A, the program crashes with message: LVM_SETITEMSTATE failed.

I can confirm It crashes without any items to select.

Any more Golang examples?

Hello,

Do you have any more Golang examples?

Perhaps some types of controls, forms with tabs, dropdowns, etc.

Thanks,

"Cannot add event handling after the window is created" error when trying to add button to visible window

I was trying to add button when some situation. However, It makes panic always.

Here is reproducible code:

package main

import (
	"fmt"
	"runtime"

	"github.com/rodrigocfd/windigo/ui"
	"github.com/rodrigocfd/windigo/win"

	"internal/modules/utils"
)

func main() {
	defer utils.PanicHandler()

	runtime.LockOSThread()

	window := ui.NewWindowMain(
		ui.WindowMainOpts().
			Title("App"),
	)

	button := ui.NewButton(
		window,
		ui.ButtonOpts().
			Position(win.POINT{X: 100, Y: 100}),
	)

	button.On().BnClicked(func() {
		fmt.Println("Clicked")

		window.Hwnd().EnumChildWindows(func(childHwnd win.HWND) bool {
			childHwnd.DestroyWindow()

			button := ui.NewButton(
				window,
				ui.ButtonOpts().
					Position(win.POINT{X: 100, Y: 100}),
			)

			button.On().BnClicked(func() {
				fmt.Println("Clicked")
			})

			return true
		})
	})

	window.RunAsMain()
}

When I click the button, It panic. I tried to omit WS_VISIBLE but not works. Also I tried dummy window to create button, and changing parent using SetParent, but not works.

I don't know where to I should ask this question. Please let me know If this issue not attended...

Thanks.

How to add List Column to ListView?

Demo:

func NewMyWindow() *MyWindow {
	wnd := ui.NewWindowMain(
		ui.WindowMainOpts().
			Title("Title").
			ClientArea(win.SIZE{Cx: 340, Cy: 500}),
	)
	icx := win.INITCOMMONCONTROLSEX{
		DwICC: co.ICC_LISTVIEW_CLASSES,
	}
	icx.SetDwSize()
	win.InitCommonControlsEx(&icx)
	me := &MyWindow{
		wnd: wnd,
		lblName: ui.NewStatic(wnd,
			ui.StaticOpts().
				Text("YourName").
				Position(win.POINT{X: 10, Y: 22}),
		),
		txtName: ui.NewEdit(wnd,
			ui.EditOpts().
				Position(win.POINT{X: 80, Y: 20}).
				Size(win.SIZE{Cx: 150}),
		),
		btnShow: ui.NewButton(wnd,
			ui.ButtonOpts().
				Text("&Show").
				Position(win.POINT{X: 240, Y: 19}),
		),
		**table: ui.NewListView(wnd, ui.ListViewOpts().
			Position(win.POINT{
				X: 10,
				Y: 50,
			}).Size(win.SIZE{Cx: 400, Cy: 200}).
			CtrlStyles(co.LVS_REPORT).WndStyles(co.WS_CHILD|co.WS_VISIBLE))**,
	}

	me.btnShow.On().BnClicked(func() {
		msg := fmt.Sprintf("Hello, %s!", me.txtName.Text())
		print(me.wnd.Hwnd().MessageBox(msg, "Saying hello", co.MB_OKCANCEL|co.MB_ICONINFORMATION))
	})
	**dwStyle := me.table.ExtendedStyle()
	dwStyle |= co.LVS_EX_FULLROWSELECT
	dwStyle |= co.LVS_EX_GRIDLINES
	me.table.SetExtendedStyle(true, dwStyle)
	me.table.Columns().Add([]int{20, 5}, "1", "2")
	me.table.Columns().Add([]int{1}, "aaa")**
	return me

image

I'm using ListView.Add().Columns() but nothing shown:(

And if call me.table.Items().Add("value1", "value2")

A panic occured :panic: LVM_SETITEMTEXT col 1, "value2" failed.

How to set environment variables in `CreateProcess`?

  proxy := readConfig()
  env := []struct {
    name string
    val string
  }{{"HTTPS_PROXY", proxy}, {"HTTP_PROXY", proxy}}
  
  win.CreateProcess(
    win.StrOptSome("C:\\Program Files (x86)\\AppFlowy\\AppFlowy.exe"),
    win.StrOptSome(""), &mySecurityAttr, &mySecurityAttr, true, co.CREATE_NO_WINDOW,
    env,
    win.StrOptSome("."), &myStartupInfo, &myProcessInfo)
go version go1.21.6 windows/amd64

.\appflowy_proxifier.go:59:5: cannot use env (variable of type []struct{name string; val string}) as []struct{name string; val string} value in argument to win.CreateProcess

This error is confusing.

GetTextExtentPoint32 len(lpString) is not calculated correctly

bounds := hdcCloned.GetTextExtentPoint32(text)

windigo/win/hdc.go

Lines 250 to 259 in 0151ff0

func (hdc HDC) GetTextExtentPoint32(lpString string) SIZE {
sz := SIZE{}
ret, _, err := syscall.Syscall6(proc.GetTextExtentPoint32.Addr(), 4,
uintptr(hdc), uintptr(unsafe.Pointer(Str.ToUint16Ptr(lpString))),
uintptr(len(lpString)), uintptr(unsafe.Pointer(&sz)), 0, 0)
if ret == 0 {
panic(errco.ERROR(err))
}
return sz
}

GetTextExtentPoint32 = gdi32.NewProc("GetTextExtentPoint32W")

https://docs.microsoft.com/en-us/windows/win32/gdi/specifying-length-of-text-output-string

Each of these functions has both an "ANSI" version and a Unicode version (for example, DrawTextExA and DrawTextExW, respectively). For the "ANSI" version of each function, the length is specified as a BYTE count and for the Unicode function it is specified as a WORD count.

It is traditional to think of this as a "character count". That is generally accurate for many languages, including English, but it is not accurate in general. In "ANSI" strings, characters in SBCS code pages take one byte each, but most characters in DBCS code pages take two bytes. Similarly, most currently defined Unicode characters reside in the Basic Multilingual Plane (BMP) and their UTF-16 representations fit in one WORD, but supplementary characters are represented in Unicode by ''surrogates'', which require two WORDs.

GetWindowThreadProcessId() Can't get process id due to remove second param

in this function

func (hWnd HWND) GetWindowThreadProcessId() uint32 {
	ret, _, _ := syscall.Syscall(proc.GetWindowThreadProcessId.Addr(), 2,
		uintptr(hWnd), 0, 0)
	return uint32(ret)
}

after changed to

func (hWnd HWND) GetWindowThreadProcessId() (uint32,uint32) {
	var processId uint32
	ret, _, _ := syscall.Syscall(proc.GetWindowThreadProcessId.Addr(), 2,
		uintptr(hWnd), uintptr(unsafe.Pointer(&processId)), 0)
	return uint32(ret), processId
}

i got the right processID

Documentation?

Is there any documentation to use the library? I tried running the example and it worked (although...), and I'd like to build on top of it. But I can't seem to find any documentation anywhere. For example, I wanted to make my window draggable, but can't find out how to do that 😅

Also, is there a way to catch runtime errors (in the console for example)? The window seems to freeze after just one interaction.

MapViewOfFile fails when arch is 386

When I used win.IniLoad, It fails with error: [5 0x05] Access is denied., but not with amd64.

I tried to fix this issue but failed.

How can I fix this? INI file size is just 1,536 bytes.

Thanks.

Compiling, resource syso workarounds?

Compiling with provided minimal.syso

GOROOT=C:\Program Files\Go #gosetup
GOPATH=C:\Users\XXX\go #gosetup
"C:\Program Files\Go\bin\go.exe" build -ldflags -H=windowsgui -o C:\Users\XXX\AppData\Local\JetBrains\IntelliJIdea2023.2\tmp\GoLand\___go_build_XXX.exe XXX #gosetup
# XXX
C:\Program Files\Go\pkg\tool\windows_amd64\link.exe: running gcc failed: exit status 1
e:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: i386 architecture of input file `C:\Users\XXX\AppData\Local\Temp\go-link-947727318\000000.o' is incompatible with i386:x86-64 output
collect2.exe: error: ld returned 1 exit status

I fixed it using rsrc compiling a new amd64 syso,then..

_RunModalLoop doesn't break

If I show modal window, modal loop doesn't break even window closed/destroyed.

Here is reproducible code:

package main

import (
	"github.com/rodrigocfd/windigo/ui"
	"github.com/rodrigocfd/windigo/win"
	"github.com/rodrigocfd/windigo/win/co"
)

func main() {
	window := ui.NewWindowMain(
		ui.WindowMainOpts().
			Title("Test").
			ClientArea(win.SIZE{Cx: 800, Cy: 600}),
	)

	button := ui.NewButton(window, ui.ButtonOpts().
		Position(win.POINT{X: 10, Y: 10}).
		Size(win.SIZE{Cx: 100, Cy: 30}).
		Text("Open Modal"))

	button.On().BnClicked(func() {
		modal := ui.NewWindowModal(ui.WindowModalOpts().
			Title("Modal").
			ClientArea(win.SIZE{Cx: 300, Cy: 300}))

		button := ui.NewButton(modal, ui.ButtonOpts().
			Position(win.POINT{X: 10, Y: 10}).
			Size(win.SIZE{Cx: 100, Cy: 30}).
			Text("Close"))

		edit := ui.NewEdit(modal, ui.EditOpts().
			Position(win.POINT{X: 10, Y: 50}).
			Size(win.SIZE{Cx: 280, Cy: 20}).
			Text("Hello, world!"))

		button.On().BnClicked(func() {
			modal.Hwnd().DestroyWindow()
		})

		modal.On().WmDestroy(func() {
			window.Hwnd().EnableWindow(true)

			window.Hwnd().MessageBox(edit.Text(), "Input", co.MB_OK|co.MB_ICONINFORMATION)
		})

		modal.ShowModal(window)

		window.Hwnd().MessageBox("Closed", "Modal", co.MB_OK|co.MB_ICONINFORMATION)
	})

	window.RunAsMain()
}

I can see Input message box, but not Modal message box. I was trying to implement a function that returns input value as string after modal closed, but It never returns.

I digged down windigo, and I could check the hWnd value doesn't changed in _RunModalLoop function even modal closed. Maybe this is the problem, but I don't know how to fix this, so I open this Issue instead of Pull Request.

Please let me know If my code is wrong. Thanks for the creating windigo.

Doesn't work in Windows 7 and 8.1

That is mainly because of these changes:

  1. Go version was upgraded to 1.21 which drops support for Windows versions below 10.
  2. There is a SetUserObjectInformation() call in _FirstMainStuff() function (inside any_globals.go file) which will make RunAsMain() calls panic in:
  • Windows 7 SP1
  • Windows 8.1
  • Wine (tested in Wine v8)

I confirmed this by forking this repository and removing the SetUserObjectInformation() call. Can that SetUserObjectInformation() call optionally be skipped so that win32 apps built with windigo work fine in those platforms?

Can't get WM_KEYDOWN event when button focused

Hi. I trying to create a app that has some buttons with keyboard shortcuts.

I can't get WM_KEYDOWN event when button focused. As windigo makes first child as focused this meaning I can't use keyboard shortcuts at any time. (WindowsRawMain.go#92L)

I tried comment out that line and works normally but not after clicking or focusing any button.

Here is some reproducible code:

package main

import (
	"fmt"

	"github.com/rodrigocfd/windigo/ui"
	"github.com/rodrigocfd/windigo/ui/wm"
)

func main() {
	window := ui.NewWindowMain(
		ui.WindowMainOpts(),
	)

	ui.NewButton(window,
		ui.ButtonOpts().
			Text("Button"),
	)

	window.On().WmKeyDown(func(p wm.Key) {
		fmt.Println(p.ScanCode())
	})

	window.RunAsMain()
}

After removing button, WM_KEYDOWN works perfectly.

How can I resolve this?

How to I enable "clickthrough" in windigo?

If I have a Window, I need to be able to "clickthrough" it, such that mouse actions affects the background Window like as if this Window does not exist.

In WPF I achieve this via :

       protected override System.Windows.Forms.CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT;
                return cp;
            }
        }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.