Skip to content
Draft
1 change: 1 addition & 0 deletions MasterWindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func NewMasterWindow(title string, width, height int, flags MasterWindowFlags) *

implot.CreateContext()
imnodes.CreateContext()
imnodes.StyleColorsDark()

io := imgui.CurrentIO()

Expand Down
211 changes: 211 additions & 0 deletions Nodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package giu

import (
"fmt"

"github.com/AllenDang/cimgui-go/imnodes"
)

type nodeEditorState struct {
context *imnodes.EditorContext
links []*LinkWidget
linksCounter int32
}

func newNodeEditorState() *nodeEditorState {
return &nodeEditorState{
context: imnodes.EditorContextCreate(),
links: make([]*LinkWidget, 0),
}
}

// Dispose implements Disposable interface.
func (n *nodeEditorState) Dispose() {
imnodes.EditorContextFree(n.context)
}

func (n *NodeEditorWidget) getState() *nodeEditorState {
if state := GetState[nodeEditorState](Context, n.id); state != nil {
return state
}

state := newNodeEditorState()
SetState[nodeEditorState](Context, n.id, state)

return state
}

// NodeElementType represents a type of node element.
type NodeElementType int

const (
// NodeElementInput describes a lyout associated with an input "point".
NodeElementInput NodeElementType = iota
// NodeElementOutput describes a lyout associated with an output "point".
NodeElementOutput
// NodeElementBody describes just a plain layout.
NodeElementBody
// NodeElementTitleBar should be rendered before any other element.
// It describes a layout of a node title bar.
NodeElementTitleBar
)

type nodeElement struct {
elementType NodeElementType
layout Layout
id string
}

type NodeEditorWidget struct {
nodes []*NodeWidget
idCounter int32
inputAlias map[string]int32
outputAlias map[string]int32
id ID
}

func NodeEditor() *NodeEditorWidget {
return &NodeEditorWidget{
idCounter: 0,
inputAlias: make(map[string]int32),
outputAlias: make(map[string]int32),
id: GenAutoID("NodeEditor"),
}
}

func (n *NodeEditorWidget) Nodes(nodes ...*NodeWidget) *NodeEditorWidget {
n.nodes = nodes

return n
}

func (n *NodeEditorWidget) Build() {
n.idCounter = 0
state := n.getState()
imnodes.EditorContextSet(state.context)

imnodes.BeginNodeEditor()
for _, node := range n.nodes {
node.BuildNode(n)
}

for _, link := range state.links {
id := n.idCounter
n.idCounter++
start := n.inputAlias[link.startID]

Check failure on line 95 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

link.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 95 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

link.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 95 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

link.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 95 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

link.startID undefined (type *LinkWidget has no field or method startID)
end := n.outputAlias[link.endID]

Check failure on line 96 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

link.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 96 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

link.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 96 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

link.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 96 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

link.endID undefined (type *LinkWidget has no field or method endID)
imnodes.Link(id, start, end)
}

imnodes.EndNodeEditor()

potentialNewLink := NodeLink(state.linksCounter, 0, 0)

Check failure on line 102 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

too many arguments in call to NodeLink

Check failure on line 102 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

too many arguments in call to NodeLink

Check failure on line 102 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to NodeLink

Check failure on line 102 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

too many arguments in call to NodeLink
if imnodes.IsLinkCreatedBoolPtr(&potentialNewLink.startID, &potentialNewLink.endID) {

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

potentialNewLink.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

potentialNewLink.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

potentialNewLink.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

potentialNewLink.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

potentialNewLink.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

potentialNewLink.startID undefined (type *LinkWidget has no field or method startID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

potentialNewLink.endID undefined (type *LinkWidget has no field or method endID)

Check failure on line 103 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

potentialNewLink.startID undefined (type *LinkWidget has no field or method startID)
state.links = append(state.links, potentialNewLink)
state.linksCounter++
}

var id int32
if imnodes.IsLinkDestroyed(&id) {
for i, link := range state.links {
if link.linkID == id {

Check failure on line 111 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

link.linkID undefined (type *LinkWidget has no field or method linkID)

Check failure on line 111 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

link.linkID undefined (type *LinkWidget has no field or method linkID)

Check failure on line 111 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

link.linkID undefined (type *LinkWidget has no field or method linkID)

Check failure on line 111 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

link.linkID undefined (type *LinkWidget has no field or method linkID)
state.links = append(state.links[:i], state.links[i+1:]...)
break
}
}
}
}

type NodeWidget struct {
elements []nodeElement
}

func Node() *NodeWidget {
return &NodeWidget{}
}

func (n *NodeWidget) Static(widgets ...Widget) *NodeWidget {
n.elements = append(n.elements, nodeElement{NodeElementBody, Layout(widgets), ""})

return n
}

func (n *NodeWidget) TitleBar(widgets ...Widget) *NodeWidget {
for i := range n.elements {
if n.elements[i].elementType != NodeElementTitleBar {
n.elements = append(n.elements[:i], append([]nodeElement{{NodeElementTitleBar, Layout(widgets), ""}}, n.elements[i:]...)...)
}
}

return n
}

func (n *NodeWidget) Input(id string, widgets ...Widget) *NodeWidget {
n.elements = append(n.elements, nodeElement{NodeElementInput, Layout(widgets), id})

return n
}

func (n *NodeWidget) Output(id string, widgets ...Widget) *NodeWidget {
n.elements = append(n.elements, nodeElement{NodeElementOutput, Layout(widgets), id})

return n
}

// ElementID allows you to manually specify an ID for the last added element.
// Appliable only for NodeElementInput and NodeElementOutput.
func (n *NodeWidget) ElementID(id int32) *NodeWidget {
// n.elements[len(n.elements)-1].id = id

return n
}

func (n *NodeWidget) BuildNode(s *NodeEditorWidget) {
fMap := map[NodeElementType]struct {
begin func(int32, string)
end func()
}{
NodeElementInput: {func(id int32, alias string) {
imnodes.BeginInputAttribute(id)
s.inputAlias[s.idCounter] = alias

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 170 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

cannot use s.idCounter (variable of type int32) as string value in map index
}, imnodes.EndInputAttribute},
NodeElementOutput: {func(id int32, alias string) {
imnodes.BeginOutputAttribute(id)
s.outputAlias[s.idCounter] = alias

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (macos-latest)

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (ubuntu-latest)

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Lint

cannot use s.idCounter (variable of type int32) as string value in map index

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

cannot use alias (variable of type string) as int32 value in assignment

Check failure on line 174 in Nodes.go

View workflow job for this annotation

GitHub Actions / Building (windows-latest)

cannot use s.idCounter (variable of type int32) as string value in map index
}, imnodes.EndOutputAttribute},
NodeElementBody: {func(id int32, _ string) { imnodes.BeginStaticAttribute(id) }, imnodes.EndStaticAttribute},
NodeElementTitleBar: {func(int32, string) { imnodes.BeginNodeTitleBar() }, imnodes.EndNodeTitleBar},
}

// Assert(n.layout != nil && len(n.layout) > 0, "NodeWidget", "BuildNode", "Node layout is required")
imnodes.BeginNode(s.idCounter)
s.idCounter++

for _, element := range n.elements {
if element.layout != nil {
f, ok := fMap[element.elementType]
if !ok {
panic(fmt.Sprintf("NodeWidget:BuildNode: Unknown node element type", element.elementType))
}
f.begin(s.idCounter, element.id)
s.idCounter++
element.layout.Build()
f.end()
}
}

imnodes.EndNode()
}

type NodeLinkWidget struct {
linkID int32
startID string
endID string
}

func NodeLink(startID, endID string) *LinkWidget {
return &NodeLinkWidget{
startID: startID,
endID: endID,
}
}
36 changes: 36 additions & 0 deletions examples/nodes/nodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

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

func loop() {
giu.SingleWindow().Layout(
giu.Label("hehehe"),
giu.NodeEditor().Nodes(
giu.Node().
Static(
giu.Label("Main content"),
).Output(
"out",
giu.Label("Output attribute"),
).Input(
"in",
giu.Label("Input attribute"),
),
giu.Node().Static(
giu.Label("Main content"),
).TitleBar(
giu.Label("This is a title bar"),
).Input(
"in2",
giu.Label("Iput attribute"),
),
),
)
}

func main() {
wnd := giu.NewMasterWindow("ImNodes demo", 1280, 720, 0)
wnd.Run(loop)
}
Loading