Skip to content
gucio321 edited this page Oct 18, 2024 · 26 revisions

Frequently Asked Questions

In this chapter you find many issues, new users may face with.

Work in progress! Feel free to post any suggestions in this issue

Building

Q: I see build constraints exclude all Go files in <some cimgui-go path> when trying to build.
A: Check the following:

  1. ensure you have CGO enabled (CGO_ENABLED=1). Also make sure you have C/C++ compilers installed.
  2. there was a bug for giu versions prior to v0.9.0. If your project requires giu <= v0.8.x and cimgui-go >= v1.0.0, you will experience this error. Upgrade giu to v0.9.0 or later to fix this issue.

Master Window

Q: What is Master Window (giu.MasterWindow)?
A: Master Window is a native application window in you OS.This is hosted (currently) by GLFW. Master window is a workspace for imgui windows created by giu.


Q: Is there a way to open two (or more) Master Windows?
A: No. Current implementation does not allow to have more than one native windows in paralel. You can however have many imgui windows.


Q: What is Run function?
A: It is the most important method of "MasterWindow". You use this to specify, which function should be treated as loop function.


Q: So what is the loop function?
A: This function is responsible for doing everything about imgui. Its may task is to draw widgets that will be visible in MasterWindow. For more details, refer to examples


Q: How to close Master Window from code?
A: There is (*MasterWindow).Close() method. You can also use (*MasterWindow).SetShouldClose(true)


Q: How to use (*MasterWindow).SetCloseCallback?
A: Use it before calling Run. func() bool argument, is a callback called, whnever window needs to be closed (by user of via SetShouldClose). Return value of this callback says, whether the window really should be closed. If false is returned, window remains opened.


Q: My app is refreshed only when I move my mouse or type something, so my constantly changing Widgets doesn't work
A: Giu implements Power Saving Mode. UI is not re-rendered if no event happens. To baypass this, you need to call giu.Update() whenever you want to redraw UI.


Windows

Q: What is a Window?
A: Window is a place where widgets are actually rendered. Windows are placed inside of MasterWindow.


Q: What are the types of Windows?
A: Dear ImGui has 3 types of windows:

  • Window - couldb be moved around workspace. You can have as many Windows as you wish.
  • Popup - is a special kind of window. It appears only when some specified action happens (e.g. Tooltip)
  • Popup Modal - when this window appears, everything else becomes grayed as long as it is open. (e.g. Yes/No dialog)

GIU separates one more window type:

  • Single Window - covers whole workspace. May be used as an alternative to many Windows in simple apps.

Q: How to use popups? I can't open them anyhow...
A: Generally poups are being refactored. Some useful information you can find in this issue. Especially, the code here describes a nice in use wrapper for cimgui-go popups system.


Q: I can drag my Window out of the MasterWindow! I don't want that!
A: This is a new Dear ImGui feature from the docking branch that is enabled by default in cimgui-go. To disable it, do the following after NewMasterWindow:

io := imgui.CurrentIO()
io.SetConfigFlags(io.ConfigFlags() & ^imgui.ConfigFlagsViewportsEnable)

General/Widgets

Q: What is Widget?
A: Widget is a single unit of GIU system. It usually represens a single item (like button, text field) or some more complex structure (like SplitLayout or Date Picker) Refer to wiki.


Q: What is Layout?
A: Layout is a group of Widgets. Technically it is a widget too.


Q: How to handle double-click event on any widget?
A: To do this, you simply need to use EventHandler

giu.Button("Double-click me"),
giu.Event().OnDClick(giu.MouseButtonLeft, func() {fmt.Println("I was double clicked!")}),

Q: Is it possible to wrap text in the InputTextMultilineWidget?
A: Unfortunately, there is no such a way implemented in giu yet. However there is a code written by @rasteric postend here. You can implement it in your project!


Q: Whats the difference between MenuBar and MainMenuBar?
A: You use MainMenuBar with a standard setup when you use multiple WindowWidgets. However if you use SingleWindow and you want a menubar, you should use SingleWiowWithMenuBar and MenuBar.

Check out this code
package main

import "github.com/AllenDang/giu"

var singleWindow bool

func menubarLayout() giu.Widget {
	return giu.Layout{
		giu.Menu("File").Layout(
			giu.MenuItem("Save"),
		),
	}
}

func loop() {
	if singleWindow {
		giu.SingleWindowWithMenuBar().Layout(
			giu.MenuBar().Layout(menubarLayout()),
			giu.Checkbox("Show single window", &singleWindow),
		)
	} else {
		giu.MainMenuBar().Layout(
			menubarLayout(),
		).Build()
		giu.Window("Window 1").Layout(
			giu.Label("I am a window 1"),
		)
		giu.Window("Window 2").Layout(
			giu.Label("I am a window 2"),
			giu.Checkbox("Show single window", &singleWindow),
		)
	}
}

func main() {
	wnd := giu.NewMasterWindow("Menubar usage", 640, 480, 0)
	wnd.Run(loop)
}

Q: CustomWidget inside of RowWidget?
A: Generally, you should avoid using CustomWidget as long as you can, especially inside of RowWidget.

Reason for that is, that our Layout system has no control over what happens inside of the CustomWidget.

What you need to know about RowWidget is that this widget is only a wrap for imgui.SameLine call after each widget inside it. The opposite to imgui.SameLine is imgui.NewLine which you can use to undo the effect of imgui.SameLine if you really need it. Here is an issue about RowWidget


State

Q: What is state stystem?
A: You may notice some widgets uses Context.Get/SetState. Widgets in giu are created dynamically - every frame giu calls widget's creator e.g. giu.Button(...). Some widgets (most probably your custom widgets, but some giu's widgets too) needs to store their internal data somewhere. Consider the example below.

Example

You want to create widget called CatWidget and whenever you call Cat() in giu.Layout you want to see "The name of the cat is: [input field]".

To do this you can of course create a global variable for name of each cat, but assume you want to avoid createing so many global variables (we don't like them) for each of your cats (what if you have 100 cats? 🐱 🐱 🐱 ... 🐱).

You cannot do like this:

type CatWidget struct {
    catName string // DON'T DO THIS
}

func Cat() CatWidget {
    return CatWidget{}
}

func (c *CatWidget) Build() {
    giu.Layout{
        giu.Label("The name of the cat is:"),
        giu.InputText(&c.catName),
    }.Build()
}

you can't do this because you'd probably want to use this widget like:

giu.SingleWindow().Layout(Cat(), Cat(), Cat())

If you run the above code you'll notice, that you can't type anything in the input field. This is because every time Cat() is called, new CatWidget is created and catName is empty. And this is called every frame so pretty often 😄.

This is where state system comes. Go to the next example for olution.

Q: How to use state system?
A: In short: Create struct, use Context.SetState to set it and Context.GetState to get it when you need. You also need to have a unique key called ID. Consider the next part of an example below.

Example part. 2

So, you have our cat 🐱.

now create a new struct called catState (it does not need to be public)

type catState struct {
    catName string
}

This state also needs to have a Dispose function (explained in the next question). Our Dispose could be empty.

func (c *catState) Dispose() {
    // nothing to do here
}

Below, is a really useful code that you may really want to include in your programm:

func (w *CatWidget) getState() *catState {
	if s := giu.GetState[catState](giu.Context, w.stateID()); s != nil {
		return s
	}

	newState := w.newState()
	giu.SetState(giu.Context, w.stateID(), newState)

	return w.getState()
}

func (w *CatWidget) newState() *catState {
	return &catState{
        // initialize here
    }
}

func (w *CatWidget) stateID() giu.ID {
    return w.id
}

We also need to edit our CatWidget struct in the following way:

type CatWidget struct {
    id giu.ID // this should be unique for each cat
}

func Cat() *CatWidget {
    return &CatWidget{
        id: giu.GenAutoID("cat"), // this guarants an unique value
    }
}

It is also recommended to add the following method:

func (w *CatWidget) ID(id giu.ID) *CatWidget {
    w.id = id
    return w
}

Because it may happen that giu.AutoID system doesn't work as expected.

ℹ️ Alternatively you can request id in the Cat method but we don't recommend it.

The Build method should look as follows:

func (c *CatWidget) Build() {
    state := c.getState()
    giu.Layout{
        giu.Label("The name of the cat is:"),
        giu.InputText(&state.catName),
    }.Build()
}

Below is the whole code. Note that it also adds an example usage of newState

Full code
package main

import (
	"github.com/AllenDang/giu"
)

type catState struct {
	catName string
}

func (c *catState) Dispose() {
	// nothing to do here
}

func (w *CatWidget) getState() *catState {
	if s := giu.GetState[catState](giu.Context, w.stateID()); s != nil {
		return s
	}

	newState := w.newState()
	giu.Context.SetState(w.stateID(), newState)

	return w.getState()
}

func (w *CatWidget) newState() *catState {
	return &catState{
		catName: w.defaultName,
	}
}

func (w *CatWidget) stateID() giu.ID {
	return w.id
}

type CatWidget struct {
	id          giu.ID // this should be unique for each cat
	defaultName string
}

func Cat() *CatWidget {
	return &CatWidget{
		id: giu.GenAutoID("cat"), // this guarants an unique value
	}
}

func (w *CatWidget) ID(id giu.ID) *CatWidget {
	w.id = id
	return w
}

func (w *CatWidget) DefaultName(name string) *CatWidget {
	w.defaultName = name
	return w
}

func (c *CatWidget) Build() {
	state := c.getState()
	giu.Layout{
		giu.Label("The name of the cat is:"),
		giu.InputText(&state.catName),
	}.Build()
}

func loop() {
	giu.SingleWindow().Layout(
		Cat().DefaultName("Luna"),
		Cat(),
		Cat(),
	)
}

func main() {
	giu.NewMasterWindow("Cats example [state, custom widet].", 800, 600, 0).Run(loop)
}

Q: How Dispose function works?
A: Dispose is called when state is getting removed. It happens, when it wasn't accessed in some frame. At the beginning of the frame a bool value on all states in Context is set to false. During the loop call, whenever GetState is called on a specific state this bool is set to true. At the end of the frame, bools are checked and if some state still has false it gets removed.


Clone this wiki locally