Super minimal, rock-solid foundation for concurrent GUI in Go.

Overview

faiface/gui GoDoc Discord

Super minimal, rock-solid foundation for concurrent GUI in Go.

Installation

go get -u github.com/faiface/gui

Currently uses GLFW under the hood, so have these dependencies.

Why concurrent GUI?

GUI is concurrent by nature. Elements like buttons, text fields, or canvases are conceptually independent. Conventional GUI frameworks solve this by implementing huge architectures: the event loop, call-backs, tickers, you name it.

In a concurrent GUI, the story is different. Each element is actually handled by its own goroutine, or event multiple ones. Elements communicate with each other via channels.

This has several advantages:

  • Make a new element at any time just by spawning a goroutine.
  • Implement animations using simple for-loops.
  • An intenstive computation in one element won't block the whole app.
  • Enables decentralized design - since elements communicate via channels, multiple communications may be going on at once, without any central entity.

Examples

Image Viewer Paint Pexeso
Image Viewer Screenshot Paint Screenshot Pexeso Screenshot

What needs getting done?

This package is solid, but not complete. Here are some of the things that I'd love to get done with your help:

  • Get rid of the C dependencies.
  • Support multiple windows.
  • Mobile support.
  • A widgets/layout package.

Contributions are highly welcome!

Overview

The idea of concurrent GUI pre-dates Go and is found in another language by Rob Pike called Newsqueak. He explains it quite nicely in this talk. Newsqueak was similar to Go, mostly in that it had channels.

Why the hell has no one made a concurrent GUI in Go yet? I have no idea. Go is a perfect language for such a thing. Let's change that!

This package is a minimal foundation for a concurrent GUI in Go. It doesn't include widgets, layout systems, or anything like that. The main reason is that I am not yet sure how to do them most correctly. So, instead of providing a half-assed, "fully-featured" library, I decided to make a small, rock-solid package, where everything is right.

So, how does this work?

The main idea is that different components of the GUI (buttons, text fields, ...) run concurrently and communicate using channels. Furthermore, they receive events from an object called environment and can draw by sending draw commands to it.

Here's Env, short for environment:

type Env interface {
	Events() <-chan Event
	Draw() chan<- func(draw.Image) image.Rectangle
}

It's something that produces events (such as mouse clicks and key presses) and accepts draw commands.

Closing the Draw() channel destroys the environment. When destroyed (either by closing the Draw() channel or by any other reason), the environment will always close the Events() channel.

As you can see, a draw command is a function that draws something onto a draw.Image and returns a rectangle telling which part got changed.

If you're not familiar with the "image" and the "image/draw" packages, go read this short entry in the Go blog.

Draw

Yes, faiface/gui uses CPU for drawing. You won't make AAA games with it, but the performance is enough for most GUI apps. The benefits are outstanding, though:

  1. Drawing is as simple as changing pixels.
  2. No FPS (frames per second), results are immediately on the screen.
  3. No need to organize the API around a GPU library, like OpenGL.
  4. Use all the good packages, like "image", "image/draw", "golang.org/x/image/font" for fonts, or "github.com/fogleman/gg" for shapes.

What is an Event? It's an interface:

type Event interface {
	String() string
}

This purpose of this interface is to hold different kinds of events and be able to discriminate among them using a type switch.

Examples of concrete Event types are: gui.Resize, win.WiClose, win.MoDown, win.KbType (where Wi, Mo, and Kb stand for window, mouse, and keyboard, respectively). When we have an Event, we can type switch on it like this:

switch event := event.(type) {
case gui.Resize:
    // environment resized to event.Rectangle
case win.WiClose:
    // window closed
case win.MoMove:
    // mouse moved to event.Point
case win.MoDown:
    // mouse button event.Button pressed on event.Point
case win.MoUp:
	// mouse button event.Button released on event.Point
case win.MoScroll:
	// mouse scrolled by event.Point
case win.KbType:
    // rune event.Rune typed on the keyboard
case win.KbDown:
    // keyboard key event.Key pressed on the keyboard
case win.KbUp:
    // keyboard key event.Key released on the keyboard
case win.KbRepeat:
    // keyboard key event.Key repeated on the keyboard (happens when held)
}

This shows all the possible events that a window can produce.

The gui.Resize event is not from the package win because it's not window specific. In fact, every Env guarantees to produce gui.Resize as its first event.

How do we create a window? With the "github.com/faiface/gui/win" package:

// import "github.com/faiface/gui/win"
w, err := win.New(win.Title("faiface/win"), win.Size(800, 600), win.Resizable())

The win.New constructor uses the functional options pattern by Dave Cheney. Unsurprisingly, the returned *win.Win is an Env.

Due to stupid limitations imposed by operating systems, the internal code that fetches events from the OS must run on the main thread of the program. To ensure this, we need to call mainthread.Run in the main function:

import "github.com/faiface/mainthread"

func run() {
    // do everything here, this becomes the new main function
}

func main() {
    mainthread.Run(run)
}

How does it all look together? Here's a simple program that displays a nice, big rectangle in the middle of the window:

package main

import (
	"image"
	"image/draw"

	"github.com/faiface/gui/win"
	"github.com/faiface/mainthread"
)

func run() {
	w, err := win.New(win.Title("faiface/gui"), win.Size(800, 600))
	if err != nil {
		panic(err)
	}

	w.Draw() <- func(drw draw.Image) image.Rectangle {
		r := image.Rect(200, 200, 600, 400)
		draw.Draw(drw, r, image.White, image.ZP, draw.Src)
		return r
	}

	for event := range w.Events() {
		switch event.(type) {
		case win.WiClose:
			close(w.Draw())
		}
	}
}

func main() {
	mainthread.Run(run)
}

Muxing

When you receive an event from the Events() channel, it gets removed from the channel and no one else can receive it. But what if you have a button, a text field, four switches, and a bunch of other things that all want to receive the same events?

That's where multiplexing, or muxing comes in.

Mux

A Mux basically lets you split a single Env into multiple ones.

When the original Env produces an event, Mux sends it to each one of the multiple Envs.

When any one of the multiple Envs receives a draw function, Mux sends it to the original Env.

To mux an Env, use gui.NewMux:

mux, env := gui.NewMux(w)

Here we muxed the window Env stored in the w variable.

What's that second return value? That's the master Env. It's the first environment that the mux creates for us. It has a special role: if you close its Draw() channel, you close the Mux, all other Envs created by the Mux, and the original Env. But other than that, it's just like any other Env created by the Mux.

Don't use the original Env after muxing it. The Mux is using it and you'll steal its events at best.

To create more Envs, we can use mux.MakeEnv():

For example, here's a simple program that shows four white rectangles on the screen. Whenever the user clicks on any of them, the rectangle blinks (switches between white and black) 3 times. We use Mux to send events to all of the rectangles independently:

package main

import (
	"image"
	"image/draw"
	"time"

	"github.com/faiface/gui"
	"github.com/faiface/gui/win"
	"github.com/faiface/mainthread"
)

func Blinker(env gui.Env, r image.Rectangle) {
	// redraw takes a bool and produces a draw command
	redraw := func(visible bool) func(draw.Image) image.Rectangle {
		return func(drw draw.Image) image.Rectangle {
			if visible {
				draw.Draw(drw, r, image.White, image.ZP, draw.Src)
			} else {
				draw.Draw(drw, r, image.Black, image.ZP, draw.Src)
			}
			return r
		}
	}

	// first we draw a white rectangle
	env.Draw() <- redraw(true)

	for event := range env.Events() {
		switch event := event.(type) {
		case win.MoDown:
			if event.Point.In(r) {
				// user clicked on the rectangle
				// we blink 3 times
				for i := 0; i < 3; i++ {
					env.Draw() <- redraw(false)
					time.Sleep(time.Second / 3)
					env.Draw() <- redraw(true)
					time.Sleep(time.Second / 3)
				}
			}
		}
	}

	close(env.Draw())
}

func run() {
	w, err := win.New(win.Title("faiface/gui"), win.Size(800, 600))
	if err != nil {
		panic(err)
	}

	mux, env := gui.NewMux(w)

	// we create four blinkers, each with its own Env from the mux
	go Blinker(mux.MakeEnv(), image.Rect(100, 100, 350, 250))
	go Blinker(mux.MakeEnv(), image.Rect(450, 100, 700, 250))
	go Blinker(mux.MakeEnv(), image.Rect(100, 350, 350, 500))
	go Blinker(mux.MakeEnv(), image.Rect(450, 350, 700, 500))

	// we use the master env now, w is used by the mux
	for event := range env.Events() {
		switch event.(type) {
		case win.WiClose:
			close(env.Draw())
		}
	}
}

func main() {
	mainthread.Run(run)
}

Just for the info, closing the Draw() channel on an Env created by mux.MakeEnv() removes the Env from the Mux.

What if one of the Envs hangs and stops consuming events, or if it simply takes longer to consume them? Will all the other Envs hang as well?

They won't, because the channels of events have unlimited capacity and never block. This is implemented using an intermediate goroutine that handles the queueing.

Events

And that's basically all you need to know about faiface/gui! Happy hacking!

A note on race conditions

There is no guarantee when a function sent to the Draw() channel will be executed, or if at all. Look at this code:

pressed := false

env.Draw() <- func(drw draw.Image) image.Rectangle {
	// use pressed somehow
}

// change pressed somewhere here

The code above has a danger of a race condition. The code that changes the pressed variable and the code that uses it may run concurrently.

My advice is to never enclose a shared variable in a drawing function.

Instead, you can do this:

redraw := func(pressed bool) func(draw.Image) image.Rectangle {
	return func(drw draw.Image) image.Rectangle {
		// use the pressed argument
	}
}

pressed := false

env.Draw() <- redraw(pressed)

// changing pressed here doesn't cause race conditions

Credit

The cutest ever pictures are all drawn by Tori Bane! You can see more of her drawings and follow her on Instagram here.

Licence

MIT

Comments
  • Maybe change Event to be an interface instead of struct?

    Maybe change Event to be an interface instead of struct?

    I'm thinking something along these lines:

    type Event interface {
    	Name() string
    	Args() interface{}
    }
    

    This would look like this when in use:

    package main
    
    import (
    	"image"
    	"image/draw"
    
    	"github.com/peterhellberg/gfx"
    	"github.com/peterhellberg/gui"
    )
    
    func main() {
    	gui.Run(loop)
    }
    
    func loop() {
    	win, err := gui.New(gui.Title("gui-xor"), gui.Size(512, 512))
    	if err != nil {
    		return
    	}
    
    	win.Draw() <- func(dst draw.Image) image.Rectangle {
    		r := dst.Bounds()
    
    		gfx.EachPixel(r, func(x, y int) {
    			c := uint8(x ^ y)
    
    			dst.Set(x, y, gfx.ColorNRGBA(c, c%192, c, 255))
    		})
    
    		return r
    	}
    
    	for event := range win.Events() {
    		switch event.Name() {
    		case gui.EventClose, gui.EventKeyboardUp:
    			close(win.Draw())
    		default:
    			gfx.Log("Event: %+v", event)
    		}
    	}
    }
    
    gui-xor

    I will experiment a bit more, but so far I've reduced the code in this project to a single file (in order to help with the experimentation)

    opened by peterhellberg 15
  • Adding the options borderless and maximized to win

    Adding the options borderless and maximized to win

    This pull request adds two options to the win package:

    1. Borderless this makes the window have no top bar and sets the hint GLFW_DECORATED to GLFW_FALSE
    2. Maximized which, when true makes the window start maximized using the hint GLFW_MAXIMIZED at GLFW_TRUE

    This pr also adds a fix for w.ratio being 0

    opened by Id405 6
  • Fixes divide by zero exception when w.ratio = 0

    Fixes divide by zero exception when w.ratio = 0

    I have no idea what the code does, this might fuck shit up, but I edited my local copy of the file with these changes and it fixed a divide by zero issue I would get randomly.

    opened by Id405 5
  • Adding the ability to get basic information about the monitor

    Adding the ability to get basic information about the monitor

    This pr adds the ability to get basic information about the primary monitor through the function win.GetPrimaryMonitor() which returns a new struct monitor. This is more a proposal of a feature that should be added than a pull request that should be immediatly merged as theres probably a way to do this that fits much better with the library however I do not have the programming skills or familiarity with this project to do it. Please note this runs glfw.init() when called which means the functions probably do bad things if called after the window is created and theres probably some sneaky side effects.

    opened by Id405 2
  • examples/imageviewer: DrawText does not draw to a draw.Image

    examples/imageviewer: DrawText does not draw to a draw.Image

    The DrawText function in examples/imageviewer/util.go creates a new image with the given text, it does not draw it on an image like the other Draw* functions.

    https://github.com/faiface/gui/blob/4324b480c2170ac435ee85988d0260e5f38259b3/examples/imageviewer/util.go#L66-L82

    opened by peterhellberg 2
  • Go imports: github.com/faiface/gui/win: no matching versions for query

    Go imports: github.com/faiface/gui/win: no matching versions for query "latest"

    I would like to have a go at developing graphical programs in your GUI framework, and to begin, I have downloaded your Pexeso example and have installed the required libraries as per the "Installation" section in the README and the GLFW setup instructions. That is, I have installed both your GUI library, your mainthread library, and the GLFW library (all through go get -u), and the Linux-specific X11 and mesa packages (from my Linux distro's package manager, which is Alpine Linux, if that helps).

    In the same folder as the Pexeso main.go, I have created a go.mod file with go mod init github.com/faiface/gui, and ran go mod tidy. Upon this, I receive success for the mainthread library but not the gui/win library:

    go: finding module for package github.com/faiface/mainthread
    go: finding module for package github.com/faiface/gui/win
    go: found github.com/faiface/mainthread in github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
    go: finding module for package github.com/faiface/gui/win
    github.com/faiface/gui imports
    	github.com/faiface/gui/win: no matching versions for query "latest"
    

    I have tried to find relevant information and similar issues on the Web to fix the issue. Unfortunately I have not found anything useful so far.

    This is the version of Go I have, if that helps:

    go version go1.16.8 linux/amd64
    
    opened by ghost 1
  • Legacy in GUI developments

    Legacy in GUI developments

    Hi @faiface, thank you so much for writing this inspiring and awesome package.

    I read your README and got a few questions:

    1. You mentioned that you'd like to get rid of C dependencies, such as glfw. I have huge interests in how that could be possibly done. As I partially read your code, the glfw is for cross-platform window, kb/mo events managements, and gl is for rendering. To get rid of those C dependencies, on macOS, one must use cocoa and metal for those purposes, and on Linux, X11 and OpenGL seems to be the only way for those purposes. It seems to me that no matter what we do, there must be some C dependencies. Could you maybe share a few of your thoughts on how to get rid of C in Go for doing GUI stuff?
    2. You also mentioned that "Due to stupid limitations imposed by operating systems, the internal code that fetches events from the OS must run on the main thread of the program." I learned the constraint from the golang-nuts mailing list but never dig into the historical reason about this. Perhaps you know more about how the limitation was introduced and why is it still exists today? Furthermore, if we get rid of the C dependencies from Go, how could the main thread limitation be improved from here?

    Thank you very much for your reading and happy new year :)

    opened by changkun 1
  • Added WiRefresh event & Go Modules integration

    Added WiRefresh event & Go Modules integration

    When the window is opening after be minimized, it receives no events, so I added WiRefresh to handle this action. Also I added Go Modules integration to manage dependencies.

    opened by ukhobotov 1
  • Removed key validation in SetKeyCallback()

    Removed key validation in SetKeyCallback()

    If a key is not specified in the key map it will not be passed as an event. This prevents things like text entry... I have disabled the validation and just let all keys be registered as events.

    opened by Cupcakus 0
  • Need clarification on events

    Need clarification on events

    image

    package main
    
    import (
    	"github.com/faiface/gui/win"
    	"github.com/faiface/mainthread"
    	"github.com/vova616/screenshot"
    	"image"
    	"image/draw"
    	"time"
    )
    
    func run() {
    	w, err := win.New(win.Title("faiface/gui"), win.Size(800, 600))
    	if err != nil {
    		panic(err)
    	}
    	redraw := func(drw draw.Image) image.Rectangle {
    		r := image.Rect(0, 0, 2560, 1440)
    		img2, _ := screenshot.CaptureScreen()
    		myImg := image.Image(img2)
    		draw.Draw(drw, r, myImg, image.ZP, draw.Src)
    		return r
    	}
    	for event := range w.Events() {
    		switch event.(type) {
    		case win.WiClose:
    			close(w.Draw())
    		default:
    			time.Sleep(17 * time.Millisecond)
    			w.Draw() <- redraw
    		}
    
    	}
    }
    
    func main() {
    	mainthread.Run(run)
    }
    
    

    I have coded this example. It grabs a screenshot of my screen and displays it just fine at around 60Hz. It runs for a coupe of seconds until it crashes with the message attached below. I assume im running out of somthing.

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x4e00a0]
    
    goroutine 18 [running, locked to thread]:
    image.(*RGBA).Bounds(0x54d8de)
            /snap/go/9360/src/image/image.go:98
    image/draw.clip({0x60e608, 0xc000356000}, 0xc00497be08, {0x60db08, 0x0}, 0xc00497bdc8, {0x0, 0x0}, 0xc00497bde8)
            /snap/go/9360/src/image/draw/draw.go:86 +0xd3
    image/draw.DrawMask({0x60e608, 0xc000356000}, {{0x0, 0x0}, {0x320, 0x258}}, {0x60db08, 0x0}, {0x0, 0x0}, ...)
            /snap/go/9360/src/image/draw/draw.go:117 +0xa5
    image/draw.Draw(...)
            /snap/go/9360/src/image/draw/draw.go:111
    main.run.func1({0x60e608, 0xc000356000})
    
    opened by PiecePaperCode 0
  • [WIP] added layout basics

    [WIP] added layout basics

    Definitely don't merge this. This is an attempt of mine at creating a layout system for this GUI system. This is largely uncommented code, with some bits taken from the examples. My take is that the layouts take an Env as parameter, and give back a map of string to Env. The map keys are like "tags" for the sub Envs. You could have a Scroller with "body", "header" and "footer" areas for example. The Layouts are then basically acting as sophisticated Muxes.

    If this idea takes on, some refactoring is needed because a lot of code for Layout is pasted from Mux.

    opened by Keitio 16
  • Keyboard key combinations for shortcuts

    Keyboard key combinations for shortcuts

    Do keyboard events allow the capture of key combinations, such as "CTRL+R"?

    Simultaneously pressing this combination generates a "kb/down/ctrl" followed by a kb/up/ctrl when the CTRL key is released. However, no kb/down/ nor kb/up/ events are produced while pressing "R" until after the CTRL key is released.

    BTW, thank you for a wonderfully minimal, concurrent GUI framework! My graphical requirements matched its minimalism allowing me to encode a prototype in just a couple hours avoiding the time sink of just understanding a qt implementation written in go.

    Noticed your roadmap and was wondering if the distributed nature of concurrency requires disseminating the "intelligence" of drawing widgets and their interaction among the cooperating goroutines, as opposed to the notion of a "controller" in a hierarchical GUI where the "smarts" are concentrated in it, such that it dictates the rendering/behavior of its subordinate widgets?

    opened by WhisperingChaos 10
  • Crash when resizing examples/imageviewer

    Crash when resizing examples/imageviewer

    $ go run ./imageviewer/                                                                                                                                                                                                                       
    fatal error: unexpected signal during runtime execution                                                                                                                                                                                       
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x7fff2df567b4]                                                                                                                                                                  
                                                                                                                                                                                                                                                  
    runtime stack:                                                                                                                                                                                                                                
    runtime.throw(0x4217912, 0x2a)                                                                                                                                                                                                                
            /usr/local/go/src/runtime/panic.go:617 +0x72                                                                                                                                                                                          
    runtime.sigpanic()                                                                                                                                                                                                                            
            /usr/local/go/src/runtime/signal_unix.go:374 +0x4a9                                                                                                                                                                                   
                                                                                                                                                                                                                                                  
    goroutine 3 [syscall, locked to thread]:                                                                                                                                                                                                      
    runtime.cgocall(0x413c830, 0xc000376c40, 0xc000001200)                                                                                                                                                                                        
            /usr/local/go/src/runtime/cgocall.go:128 +0x5b fp=0xc000376c10 sp=0xc000376bd8 pc=0x400591b                                                                                                                                           
    github.com/go-gl/gl/v2.1/gl._Cfunc_glowDrawPixels(0x7fff3e7ab7d1, 0x25800000332, 0x140100001908, 0xc001186000)                                                                                                                                
            _cgo_gotypes.go:13546 +0x45 fp=0xc000376c40 sp=0xc000376c10 pc=0x40baf55                                                                                                                                                              
    github.com/go-gl/gl/v2.1/gl.DrawPixels.func1(0x25800000332, 0x140100001908, 0xc001186000)                                                                                                                                                     
            /Users/hajimehoshi/go/pkg/mod/github.com/go-gl/[email protected]/v2.1/gl/package.go:20431 +0x90 fp=0xc000376c80 sp=0xc000376c40 pc=0x40fa730                                                                      
    github.com/go-gl/gl/v2.1/gl.DrawPixels(0x25800000332, 0x140100001908, 0xc001186000)                                                                                                                                                           
            /Users/hajimehoshi/go/pkg/mod/github.com/go-gl/[email protected]/v2.1/gl/package.go:20431 +0x4b fp=0xc000376ca8 sp=0xc000376c80 pc=0x40bb56b                                                                      
    github.com/faiface/gui/win.(*Win).openGLFlush(0xc000152040, 0x0, 0x0, 0x332, 0x258)                                                                                                                                                           
            /Users/hajimehoshi/gui/win/win.go:324 +0x3d3 fp=0xc000376dc0 sp=0xc000376ca8 pc=0x4101943                                                                                                                                             
    github.com/faiface/gui/win.(*Win).openGLThread(0xc000152040)                                                                                                                                                                                  
            /Users/hajimehoshi/gui/win/win.go:280 +0x547 fp=0xc000376fc0 sp=0xc000376dc0 pc=0x41011b7                                                                                                                                             
    github.com/faiface/gui/win.New.func2(0xc000152040)                                                                                                                                                                                            
            /Users/hajimehoshi/gui/win/win.go:84 +0x30 fp=0xc000376fd8 sp=0xc000376fc0 pc=0x4101b10                                                                                                                                               
    runtime.goexit()                                                                                                                                                                                                                              
            /usr/local/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc000376fe0 sp=0xc000376fd8 pc=0x40569b1                                                                                                                                          
    created by github.com/faiface/gui/win.New                                                                                                                                                                                                     
            /Users/hajimehoshi/gui/win/win.go:82 +0x331      
    
    opened by hajimehoshi 5
Owner
Michal Štrba
Infinity is smaller than 380012893427665492.
Michal Štrba
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

grd 24 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

Pietro Gagliardi 8.2k Sep 21, 2022
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

Jeroen Dirks 56 Jul 24, 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

Quentin Renard 4.5k Sep 26, 2022
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

Danny 377 Sep 16, 2022
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

Alexander Neumann 6.2k Sep 26, 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

vgo 7.8k Sep 19, 2022
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.

Tad Vizbaras 139 Aug 23, 2022
Cross-platform GUI for go is never this easy and clean.

gimu Strongly suggest NOT to use this project anymore, the auto-generated cgo wrapper of Nuklear has a random crash issue which is hard to fix (becaus

Allen Dang 66 Jul 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

Allen Dang 275 Sep 24, 2022
Easy Go GUI wrapper for interactive manipulation of visual algorithms/backend code.

RenderView ================ Install: go get github.com/TheGrum/renderview Needs either Shiny (limited functionality), Gio, go-gtk, or gotk3. The latt

null 28 Aug 4, 2022
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.

null 146 Aug 27, 2022
Cross platform rapid GUI framework for golang based on Dear ImGui.

giu Cross platform rapid GUI framework for golang based on Dear ImGui and the great golang binding imgui-go. Any contribution (features, widgets, tuto

Allen Dang 1.5k Sep 19, 2022
This project provides Go bindings for nuklear.h — a small ANSI C GUI library.

Nuklear Package nk provides Go bindings for nuklear.h — a small ANSI C gui library. See github.com/vurtun/nuklear. All the binding code has automatica

null 1.5k Sep 23, 2022
GUI toolkit for go

Mostly-immediate-mode GUI library for Go. Source port to go of an early version of nuklear. ⚠️ Subject to backwards incompatible changes. ⚠️ ⚠️ Featur

Alessandro Arzilli 745 Sep 18, 2022
A list of Go GUI projects

(Please follow @Go100and1 for updates on this page, and all kinds of details and facts in Go). A list of Go GUI/graphics/image related projects native

null 974 Sep 19, 2022
Odile is a simple GUI for the croc utility by Schollz.

Odile Odile is a simple GUI for the croc utility by Schollz. This program uses Fyne, a UI toolkit written in Go, as the graphical interface. Effort wa

null 12 Aug 18, 2022
Golang bindings for XCGUI, Windows GUI library, DirectUI design idea.

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

twgh 184 Sep 20, 2022
Tutorials for Gio, the GUI framwork in Go.

#go, #golang, #gui, #gioui You want a Gui. Of course you do. Did you know that Go has a great GUI library called Gio? In a 10-part tutorial we will st

Jon Egil Strand 98 Aug 29, 2022