AllenDang / cimgui-go

Auto generated Go wrapper for Dear ImGui via cimgui
MIT License
322 stars 51 forks source link

Initial docking example? #216

Open maxsupermanhd opened 1 year ago

maxsupermanhd commented 1 year ago

I would really like to know how to create window within previous window, example seem to not have such functionality

maxsupermanhd commented 9 months ago

Any updates on that?

AllenDang commented 9 months ago

I think you could check the Imgui for a demo, and use almost the same API here.

gucio321 commented 9 months ago

@AllenDang problem is that even imgui doesn't have such an example. They only have a document about it but with no example. So we don't know how to create two windows initially being in the same dockspace (in the demo below I've merged these windows after running an app). image

CC @ocornut

damntourists commented 8 months ago

Perhaps I can help? I've been spending quite a bit of time recently working on getting docking working using cimgui-go with success.

First thing to do is enable it:

imgui.CurrentIO().SetConfigFlags(imgui.ConfigFlagsDockingEnable)

My setup docking function looks like this, and it only executes the portion in the if !u.initialized on the first iteration of the event loop

func setupDocking(u *UI) {
    viewport := imgui.MainViewport()
    dockspaceID := imgui.DockSpaceOverViewport()

    if !u.initialized {
        imgui.InternalDockBuilderRemoveNode(dockspaceID)
        imgui.InternalDockBuilderAddNodeV(dockspaceID, imgui.DockNodeFlagsNone)
        imgui.InternalDockBuilderSetNodeSize(dockspaceID, viewport.Size())

        /*
            Step 1)
                Split root dockspace
                ╔══════════════╗        ╔═══════╤═══════╗
                ║              ║        ║       │       ║
                ║     root     ║    →   ║   L   ↔   R   ║
                ║              ║        ║       │       ║
                ╚══════════════╝        ╚═══════╧═══════╝
        */
        dockspaceIDMainRight := imgui.DockSpace(10)
        dockspaceIDMainLeft := imgui.DockSpace(20)
        imgui.InternalDockBuilderSplitNode(dockspaceID, imgui.DirRight, .2,
            &dockspaceIDMainRight, &dockspaceIDMainLeft)
        /*
            Step 2)
                Split right dockspace
                ╔═══════╦═══════╗       ╔═══════╦═════════╗
                ║       ║       ║       ║       ║    RT   ║
                ║   L   ║   R   ║   →   ║   L   ╟────↕────╢
                ║       ║       ║       ║       ║    RB   ║
                ╚═══════╩═══════╝       ╚═══════╩═════════╝
        */
        dockspaceIDMainRightTop := imgui.DockSpace(30)
        dockspaceIDMainRightBottom := imgui.DockSpace(40)
        imgui.InternalDockBuilderSplitNode(dockspaceIDMainRight, imgui.DirUp, .35,
            &dockspaceIDMainRightTop, &dockspaceIDMainRightBottom)

        /*
            Step 3)
                Dock windows into RT & RB by name
                ╔═══════╦════════╗      ╔════════╦══════════╗
                ║       ║        ║      ║        ║ ╒══════╕ ║
                ║       ║   RT   ║      ║        ║ │ wRT  │ ║
                ║       ║        ║      ║        ║ └──────┘ ║
                ║   L   ╠════════╣  →   ║   L    ╠══════════╣
                ║       ║        ║      ║        ║ ╒══════╕ ║
                ║       ║   RB   ║      ║        ║ │ wRB  │ ║
                ║       ║        ║      ║        ║ └──────┘ ║
                ╚═══════╩════════╝      ╚════════╩══════════╝

        */
        imgui.InternalDockBuilderDockWindow("context", dockspaceIDMainRightTop)
        imgui.InternalDockBuilderDockWindow("status", dockspaceIDMainRightBottom)

        /*
            Step 4)
                Split left dockspace
                ╔════════╦══════════╗       ╔═════════╦══════════╗
                ║        ║ ╒══════╕ ║       ║         ║ ╒══════╕ ║
                ║        ║ │ wRT  │ ║       ║   LT    ║ │ wRT  │ ║
                ║        ║ └──────┘ ║       ║         ║ └──────┘ ║
                ║   L    ╠══════════╣   →   ╟────↕────╠══════════╣
                ║        ║ ╒══════╕ ║       ║         ║ ╒══════╕ ║
                ║        ║ │ wRB  │ ║       ║   LB    ║ │ wRB  │ ║
                ║        ║ └──────┘ ║       ║         ║ └──────┘ ║
                ╚════════╩══════════╝       ╚═════════╩══════════╝
        */

        dockspaceIDMainLeftTop, dockspaceIDMainLeftBottom := imgui.DockSpace(50), imgui.DockSpace(60)
        imgui.InternalDockBuilderSplitNode(dockspaceIDMainLeft, imgui.DirDown, .3,
            &dockspaceIDMainLeftBottom, &dockspaceIDMainLeftTop)
        /*
            Dock 2 windows into LT by name

            Before:                 After:
            ╔════════╦══════════╗       ╔═════════════╦══════════╗
            ║        ║ ╒══════╕ ║       ║  ╒════════╕ ║ ╒══════╕ ║
            ║        ║ │      │ ║       ║ ╒╧═══════╕│ ║ │      │ ║
            ║   LT   ║ │ wRT  │ ║       ║ │ wLT1 & ││ ║ │ wRT  │ ║
            ║        ║ │      │ ║       ║ │ wLT2   ├┘ ║ │      │ ║
            ║        ║ └──────┘ ║       ║ └────────┘  ║ └──────┘ ║
            ╠════════╬══════════╣   →   ╠═════════════╬══════════╣
            ║        ║ ╒══════╕ ║       ║             ║ ╒══════╕ ║
            ║        ║ │      │ ║       ║             ║ │      │ ║
            ║   LB   ║ │ wRB  │ ║       ║      LB     ║ │ wRB  │ ║
            ║        ║ │      │ ║       ║             ║ │      │ ║
            ║        ║ └──────┘ ║       ║             ║ └──────┘ ║
            ╚════════╩══════════╝       ╚═════════════╩══════════╝
        */
        imgui.InternalDockBuilderDockWindow("content", dockspaceIDMainLeftTop)
        imgui.InternalDockBuilderDockWindow("editor", dockspaceIDMainLeftTop)
        imgui.InternalDockBuilderDockWindow("settings", dockspaceIDMainLeftTop)
        /*
            Split LB
            ╔═════════════╦══════════╗      ╔══════════════════╦══════════╗
            ║  ╒════════╕ ║ ╒══════╕ ║      ║   ╒═══════════╕  ║ ╒══════╕ ║
            ║ ╒╧═══════╕│ ║ │      │ ║      ║ ╒═╧═════════╕ │  ║ │      │ ║
            ║ │ wLT1 & ││ ║ │ wRT  │ ║      ║ │   wLT1 &  │ │  ║ │ wRT  │ ║
            ║ │ wLT2   │┘ ║ │      │ ║      ║ │   wLT2    ├─┘  ║ │      │ ║
            ║ └────────┘  ║ └──────┘ ║      ║ └───────────┘    ║ └──────┘ ║
            ╠═════════════╬══════════╣  →   ╠═════════╤════════╬══════════╣
            ║             ║ ╒══════╕ ║      ║         │        ║ ╒══════╕ ║
            ║             ║ │      │ ║      ║         │        ║ │      │ ║
            ║     LB      ║ │ wRB  │ ║      ║   LBL   ↔  LBR   ║ │ wRB  │ ║
            ║             ║ │      │ ║      ║         │        ║ │      │ ║
            ║             ║ └──────┘ ║      ║         │        ║ └──────┘ ║
            ╚═════════════╩══════════╝      ╚═════════╧════════╩══════════╝

        */
        dockspaceIDMainLeftBottomLeft, dockspaceIDMainLeftBottomRight := imgui.DockSpace(70), imgui.DockSpace(80)
        imgui.InternalDockBuilderSplitNode(dockspaceIDMainLeftBottom, imgui.DirLeft, .25,
            &dockspaceIDMainLeftBottomLeft, &dockspaceIDMainLeftBottomRight)
        /*
            Dock 2 windows into LBR by name and one into LBL
            ╔══════════════════╦══════════╗     ╔════════════════════════════╦══════════╗
            ║   ╒═══════════╕  ║ ╒══════╕ ║     ║   ╒══════════════════════╕ ║ ╒══════╕ ║
            ║ ╒═╧═════════╕ │  ║ │      │ ║     ║ ╒═╧════════════════════╕ │ ║ │      │ ║
            ║ │   wLT1 &  │ │  ║ │ wRT  │ ║     ║ │      wLT1 &          │ │ ║ │ wRT  │ ║
            ║ │   wLT2    ├─┘  ║ │      │ ║     ║ │      wLT2            ├─┘ ║ │      │ ║
            ║ └───────────┘    ║ └──────┘ ║     ║ └──────────────────────┘   ║ └──────┘ ║
            ╠═════════╦════════╬══════════╣ →   ╠═════════════╦══════════════╬══════════╣
            ║         ║        ║ ╒══════╕ ║     ║ ╒═════════╕ ║  ╒═════════╕ ║ ╒══════╕ ║
            ║         ║        ║ │      │ ║     ║ │         │ ║ ╒╧════════╕│ ║ │      │ ║
            ║   LBL   ║  LBR   ║ │ wRB  │ ║     ║ │  wLBL   │ ║ │ wLBR1 & ││ ║ │ wRB  │ ║
            ║         ║        ║ │      │ ║     ║ │         │ ║ │ wLBR2   ├┘ ║ │      │ ║
            ║         ║        ║ └──────┘ ║     ║ └─────────┘ ║ └─────────┘  ║ └──────┘ ║
            ╚═════════╩════════╩══════════╝     ╚═════════════╩══════════════╩══════════╝
        */
        imgui.InternalDockBuilderDockWindow("properties", dockspaceIDMainLeftBottomLeft)
        imgui.InternalDockBuilderDockWindow("docker", dockspaceIDMainLeftBottomLeft)
        imgui.InternalDockBuilderDockWindow("console", dockspaceIDMainLeftBottomRight)

        imgui.InternalDockBuilderFinish(dockspaceID)

        u.initialized = true
    }
}

I'm not 100% on the ID assignments, as I ran into panics while putting this together. my guess is that i tried to use an ID that was already in use, but not sure what it belonged to. Maybe wouldn't be an issue if you're using larger more arbitrary numbers?

The windows that get docked are by name, so if you wanted to populate the "console" window for instance, you would create this window after docking had been set up:

type windowFlags struct {
    noTitlebar            bool
    noScrollbar           bool
    noMenu                bool
    noMove                bool
    noResize              bool
    noCollapse            bool
    noNav                 bool
    noBackground          bool
    noBringToFront        bool
    noHorizontalScrollbar bool
    noDocking             bool
}

type Window struct {
    flags     windowFlags
    noClose   bool
    collapsed *bool
    open      *bool
}

func (w windowFlags) combined() imgui.WindowFlags {
    flags := 0
    if w.noTitlebar {
        flags |= imgui.WindowFlagsNoTitleBar
    }
    if w.noScrollbar {
        flags |= imgui.WindowFlagsNoScrollbar
    }
    if !w.noMenu {
        flags |= imgui.WindowFlagsMenuBar
    }
    if w.noMove {
        flags |= imgui.WindowFlagsNoMove
    }
    if w.noResize {
        flags |= imgui.WindowFlagsNoResize
    }
    if w.noCollapse {
        flags |= imgui.WindowFlagsNoCollapse
    }
    if w.noNav {
        flags |= imgui.WindowFlagsNoNav
    }
    if w.noBackground {
        flags |= imgui.WindowFlagsNoBackground
    }
    if w.noBringToFront {
        flags |= imgui.WindowFlagsNoBringToFrontOnFocus
    }
    if !w.noHorizontalScrollbar {
        flags |= imgui.WindowFlagsHorizontalScrollbar
    }
    if w.noDocking {
        flags |= imgui.WindowFlagsNoDocking
    }

    return imgui.WindowFlags(flags)
}

func MakeConsoleWindow() *Window {
    w := &Window{
        flags: windowFlags{
            noTitlebar:            false,
            noScrollbar:           false,
            noMenu:                true,
            noMove:                false,
            noResize:              false,
            noCollapse:            false,
            noNav:                 false,
            noBackground:          false,
            noBringToFront:        false,
            noHorizontalScrollbar: false,
            noDocking:             false,
        },
        noClose:   false,
        collapsed: &consoleWindowCollapsed,
        open:      &consoleWindowOpen,
    }

    imgui.BeginV("console", w.open, w.flags.combined())
    imgui.Text("I'm the console area.")
    imgui.End()
    return w
}
ocornut commented 8 months ago

I'm not 100% on the ID assignments, as I ran into panics while putting this together. my guess is that i tried to use an ID that was already in use, but not sure what it belonged to.

You should use GetID("something") to generate a valid ID. Random integers will technically work but quite not recommended.

damntourists commented 8 months ago

@ocornut Thank you! I was not aware that I could use it that way. That will make things way less confusing.

Edit: just to clarify for those using cimgui-go, this means to use imgui.IDStr("something") instead of imgui.DockSpace(N)