ki

package
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2024 License: BSD-3-Clause Imports: 21 Imported by: 0

README

Go language (golang) tree structure (ki = 木 = tree in Japanese)

Go Reference

NOTE: A new version of Ki is being developed on this branch, so there may be breaking changes and unstable code. For the last stable version of Ki, import v1.1.16 and see the v1 branch.

Overview

The Tree is one of the most flexible, widely-used data structures in programming, including the DOM structure at the core of a web browser, scene graphs for 3D and 2D graphics systems, JSON, XML, SVG, filesystems, programs themselves, etc. This is because trees can capture most relevant forms of structure (hierarchical groupings, categories, relationships, etc) and are most powerful when they are fully generative -- arbitrary new types can be inserted flexibly.

Cogent Core provides a general-purpose tree container type, that can support all of these applications, by embedding and extending the Node struct type that implements the Ki (Ki = Tree in Japanese) interface. Unlike many cases in Go, the need to be able to arbitrarily extend the type space of nodes in the tree within a consistent API, means that the more traditional object-oriented model works best here, with a single common base type, and derived types that handle diverse cases (e.g., different types of widgets in a GUI). Cogent Core stores a Ki interface of each node, enabling correct virtual function calling on these derived types.

A virtue of using an appropriate data representation is that some important operations can be performed particularly concisely and efficiently when they are naturally supported by the data structure. For example, matrices and vectors as supported by numpy or MATLAB provide a concise high-level language for expressing many algorithms.

In addition, Cogent Core provides functions that traverse the tree in the usual relevant ways ("natural" me-first depth-first, me-last depth-first, and breadth-first) and take a func function argument, so you can easily apply a common operation across the whole tree in a transparent and self-contained manner, like this:

func (n *MyNode) DoSomethingOnMyTree() {
	n.FuncDownMeFirst(0, nil, func(k Ki, level int, d interface{}) bool {
		mn := KiToMyNode(k) // function converts a Ki to relevant node type -- you must write
		mn.DoSomething()
		...
		return ki.Continue // return value determines whether tree traversal continues or not
	})
}

Many operations are naturally expressed in terms of these traversal algorithms.

Three core Cogent Core features include:

  • ConfigChildren uses a list of types and names and performs a minimal, efficient update of the children of a node to configure them to match (including no changes if already configured accordingly). This is used during loading from JSON, and extensively in the Cogent Core GUI system to efficiently re-use existing tree elements. There is often complex logic to determine what elements need to be present in a Widget, so separating that out from then configuring the elements that actually are present is efficient and simplifies the code.

In addition, Ki nodes support a general-purpose Props property map, and the kit (Ki Types) package provides a TypeRegistry and an EnumRegistry, along with various reflect utilities, to enable fully-automatic saving / loading of Ki trees from JSON or XML, including converting const int (enum) values to / from strings so those numeric values can change in the code without invalidating existing files.

Ki Nodes can be used as fields in a struct -- they function much like pre-defined Children elements, and all the standard FuncDown* iterators traverse the fields automatically. The Ki Init function automatically names these structs with their field names, and sets the parent to the parent struct. This was essential in the Cogent Core framework to support separate Widget Parts independent of the larger scenegraph.

Cogent Core Graphical Interface

The first and most important application of ki is the Cogent Core graphical interface system, in the gi package. The scene graph of Ki elements automatically drives minimal refresh updates, allowing Cogent Core to provide a complete interactive 2D and 3D GUI environment in native Go, in a compact codebase. Part of this is the natural elegance of Go, but Cogent Core enhances that by providing the robust natural primitives needed to express all the GUI functionality.

The pi interactive parsing framework also leverages Cogent Core trees to represent the AST (abstract syntax tree) of programs in different languages. Further, the parser grammar itself is written (in a GUI interactive way) as a tree of parsing elements using Ki nodes.

Code Map

  • kit package: kit.Types TypeRegistry provides name-to-type map for looking up types by name, and types can have default properties. kit.Enums EnumRegistry provides enum (const int) <-> string conversion, including bitflag enums. Also has robust generic kit.ToInt kit.ToFloat etc converters from interface{} to specific type, for processing properties, and several utilities in embeds.go for managing embedded structure types (e.g., TypeEmbeds checks if one type embeds another, and EmbeddedStruct returns the embedded struct from a given struct, providing flexible access to elements of an embedded type hierarchy -- there are also methods for navigating the flattened list of all embedded fields within a struct). Also has a kit.Type struct that supports saving / loading of type information using type names.

  • walki package provides tree-walking methods for more ad-hoc, special-case tree traversal, as compared to the standard Func* methods on Ki itself.

  • bitflag package: simple bit flag setting, checking, and clearing methods that take bit position args as ints (from const int eunum iota's) and do the bit shifting from there

  • ki.go = Ki interface for all major tree node functionality.

  • slice.go = ki.Slice []Ki supports saving / loading of Ki objects in a slice, by recording the size and types of elements in the slice -- requires kit.Types type registry to lookup types by name.

  • props.go = ki.Props map[string]interface{} supports saving / loading of property values using actual struct types and named const int enums, using the kit type registries. Used for CSS styling in Cogent Core.

  • signal.go = Signal that calls function on a receiver Ki objects that have been previously Connected to the signal -- also supports signal type so the same signal sender can send different types of signals over the same connection -- used for signaling changes in tree structure, and more general tree updating signals.

Status

  • Feb, 2021: version 1.1.0 reflects major simplification pass to reduce API footprint and remove separate Unique names (names should in general be unique -- add a separate non-unique name where needed). Now that Cogent Core etc is complete, could get rid if quite a few things.

  • April, 2020: version 1.0.0 release -- all stable and well tested.

Simple Example

See ki/node_test.go for lots of simple usage examples. Here's some code from there that makes a tree with a parent and 2 children.

parent := NodeEmbed{}
parent.InitName(&parent, "par1") // root must be initialized -- this also names it.
typ := reflect.TypeOf(parent)
parent.NewChild(typ, "child1") // Add etc methods auto-initialize children
parent.NewChild(typ, "child2")

// traverse the tree calling the parent node first and then its children, recursively
// "natural" order
parent.FuncDownMeFirst(0, nil, func(k Ki, level int, d interface{}) bool {
	fmt.Printf("level: %d  node: %s\n", level, k.Path())
	return ki.Continue
})

Trick for fast finding in a slice

Cogent Core takes an extra starting index arg for all methods that lookup a value in a slice, such as ChildByName. The search for the item starts at that index, and goes up and down from there. Thus, if you have any idea where the item might be in the list, it can save (considerable for large lists) time finding it.

Furthermore, it enables a robust optimized lookup map that remembers these indexes for each item, but then always searches from the index, so it is always correct under list modifications, but if the list is unchanged, then it is very efficient, and does not require saving pointers, which minimizes any impact on the GC, prevents stale pointers, etc.

The IndexInParent() method uses this trick, using the cached Node.index value.

Here's example code for a separate Find method where the indexes are stored in a map:

// FindByName finds item by name, using cached indexes for speed
func (ob *Obj) FindByName(nm string) *Obj {
	if sv.FindIdxs == nil {
		ob.FindIdxs = make(map[string]int) // field on object
	}
	idx, has := ob.FindIdxs[nm]
	if !has {
		idx = len(ob.Kids) / 2 // start in middle first time
	}
	idx, has = ob.Kids.IndexByName(nm, idx)
	if has {
		ob.FindIdxs[nm] = idx
		return ob.Kids[idx].(*Obj)
  	}
	delete(ob.FindIdxs, nm) // must have been deleted
	return nil
}

Documentation

Overview

Package ki provides the base element of Cogent Core Trees: Ki = Tree in Japanese, and "Key" in English -- powerful tree structures supporting scenegraphs, programs, parsing, etc.

The Node struct that implements the Ki interface, which can be used as an embedded type (or a struct field) in other structs to provide core tree functionality, including:

  • Parent / Child Tree structure -- each Node can ONLY have one parent. Node struct's can also have Node fields -- these are functionally like fixed auto-named children.

  • Paths for locating Nodes within the hierarchy -- key for many use-cases, including ability to convert pointers to/from strings for IO and robust deep copy and move functions. The path separator is / for children and . for fields.

  • Apply a function across nodes up or down a tree (natural "me first", breadth-first, depth-first) -- very flexible for tree walking.

  • Generalized I/O -- can Save and Load the Tree as JSON, XML, etc -- including pointers which are saved using paths and automatically cached-out after loading -- enums also bidirectionally convertable to strings using enum type registry in kit package.

  • Robust deep copy, clone, move of nodes.

  • Properties (as a string-keyed map) with property inheritance, including type-level properties via kit type registry.

In general, the names of the children of a given node should all be unique. The following functions defined in ki package can be used:

* UniqueNameCheck(node) to check for unique names on node if uncertain. * UniqueNameCheckAll(node) to check entire tree under given node. * UniquifyNames(node) to add a suffix to name to ensure uniqueness. * UniquifyNamesAll(node) to to uniquify all names in entire tree.

The Ki interface is designed to support virtual method calling in Go and is only intended to be implemented once, by the ki.Node type (as opposed to interfaces that are used for hiding multiple different implementations of a common concept). Thus, all of the fields in ki.Node are exported (have captital names), to be accessed directly in types that embed and extend the ki.Node. The Ki interface has the "formal" name (e.g., Children) while the Node has the "nickname" (e.g., Kids). See the Naming Conventions on the Cogent Core Wiki for more details.

Each Node stores the Ki interface version of itself, as This() / Ths which enables full virtual function calling by calling the method on that interface instead of directly on the receiver Node itself. This requires proper initialization via Init method of the Ki interface.

Index

Constants

View Source
const (
	// Continue = true can be returned from tree iteration functions to continue
	// processing down the tree, as compared to Break = false which stops this branch.
	Continue = true

	// Break = false can be returned from tree iteration functions to stop processing
	// this branch of the tree.
	Break = false

	// Embeds is used for methods that look for children or parents of different types.
	// Passing this argument means to look for embedded types for matches.
	Embeds = true

	// NoEmbeds is used for methods that look for children or parents of different types.
	// Passing this argument means to NOT look for embedded types for matches.
	NoEmbeds = false

	// ShallowCopy is used for Props CopyFrom functions to indicate a shallow copy of
	// Props or PropSlice within Props (points to source props)
	ShallowCopy = true

	// DeepCopy is used for Props CopyFrom functions to indicate a deep copy of
	// Props or PropSlice within Props
	DeepCopy = true

	// Inherit is used for PropInherit to indicate that inherited properties
	// from parent objects should be checked as well.  Otherwise not.
	Inherit = true

	// NoInherit is used for PropInherit to indicate that inherited properties
	// from parent objects should NOT be checked.
	NoInherit = false
)

Named consts for bool args

Variables

View Source
var JSONTypePrefix = []byte("{\"ki.RootType\": ")

JSONTypePrefix is the first thing output in a ki tree JSON output file, specifying the type of the root node of the ki tree -- this info appears all on one { } bracketed line at the start of the file, and can also be used to identify the file as a ki tree JSON file

View Source
var JSONTypeSuffix = []byte("}\n")

JSONTypeSuffix is just the } and \n at the end of the prefix line

View Source
var KiType = reflect.TypeFor[Ki]()

KiType is a Ki reflect.Type, suitable for checking for Type.Implements.

View Source
var NodeType = gti.AddType(&gti.Type{Name: "cogentcore.org/core/ki.Node", IDName: "node", Doc: "The Node struct implements the [Ki] interface and provides the core functionality\nfor the Cogent Core tree system. You can use the Node as an embedded struct or as a struct\nfield; the embedded version supports full JSON saving and loading. All types that\nimplement the [Ki] interface will automatically be added to gti in `core generate`, which\nis required for various pieces of core functionality.", Fields: []gti.Field{{Name: "Nm", Doc: "Nm is the user-supplied name of this node, which can be empty and/or non-unique."}, {Name: "Flags", Doc: "Flags are bit flags for internal node state, which can be extended using the enums package."}, {Name: "Props", Doc: "Props is a property map for arbitrary extensible properties."}, {Name: "Par", Doc: "Par is the parent of this node, which is set automatically when this node is added as a child of a parent."}, {Name: "Kids", Doc: "Kids is the list of children of this node. All of them are set to have this node\nas their parent. They can be reordered, but you should generally use Ki Node methods\nto Add / Delete to ensure proper usage."}, {Name: "Ths", Doc: "Ths is a pointer to ourselves as a Ki. It can always be used to extract the true underlying type\nof an object when [Node] is embedded in other structs; function receivers do not have this ability\nso this is necessary. This is set to nil when deleted. Typically use [Ki.This] convenience accessor\nwhich protects against concurrent access."}, {Name: "NumLifetimeKids", Doc: "NumLifetimeKids is the number of children that have ever been added to this node, which is used for automatic unique naming."}, {Name: "index", Doc: "index is the last value of our index, which is used as a starting point for finding us in our parent next time.\nIt is not guaranteed to be accurate; use the [Ki.IndexInParent] method."}, {Name: "depth", Doc: "depth is an optional depth parameter of this node, which is only valid during specific contexts, not generally.\nFor example, it is used in the WalkBreadth function"}}, Instance: &Node{}})

NodeType is the gti.Type for Node

View Source
var StringElideMax = 38

StringElideMax is the Max width for Node.String path printout of Ki nodes.

View Source
var UniquifyIndexAbove = 1000

UniquifyIndexAbove is the number of children above which UniquifyNamesAddIndex is called -- that is much faster for large numbers of children. Must be < 1000

Functions

func ChildByType

func ChildByType[T Ki](k Ki, embeds bool, startIdx ...int) T

ChildByType is a generic helper function for [Ki.ChildByType]

func CopyFromRaw

func CopyFromRaw(kn, frm Ki)

CopyFromRaw performs a raw copy that just does the deep copy of the bits and doesn't do anything with pointers.

func CopyProps

func CopyProps(dest *map[string]any, src map[string]any, deepCopy bool)

CopyProps copies properties from source to destination map. If deepCopy is true, then any values that are Props or PropSlice are copied too *dest can be nil, in which case it is created.

func DecodeXMLCharData

func DecodeXMLCharData(d *xml.Decoder) (val string, err error)

DecodeXMLCharData reads char data..

func DecodeXMLCharEl

func DecodeXMLCharEl(d *xml.Decoder) (name, val string, err error)

DecodeXMLCharEl reads a start / chardata / end sequence of 3 elements, returning name, val

func DecodeXMLEndEl

func DecodeXMLEndEl(d *xml.Decoder, start xml.StartElement) error

DecodeXMLEndEl reads an end element

func DecodeXMLStartEl

func DecodeXMLStartEl(d *xml.Decoder) (start xml.StartElement, err error)

DecodeXMLStartEl reads a start element token

func Depth

func Depth(kn Ki) int

Depth returns the current depth of the node. This is only valid in a given context, not a stable property of the node (e.g., used in WalkBreadth).

func EscapePathName

func EscapePathName(name string) string

EscapePathName returns a name that replaces any path delimiter symbols . or / with \, and \\ escaped versions.

func InitNode

func InitNode(this Ki)

InitNode initializes the node -- automatically called during Add/Insert Child -- sets the This pointer for this node as a Ki interface (pass pointer to node as this arg) -- Go cannot always access the true underlying type for structs using embedded Ki objects (when these objs are receivers to methods) so we need a This interface pointer that guarantees access to the Ki interface in a way that always reveals the underlying type (e.g., in reflect calls). Calls Init on Ki fields within struct, sets their names to the field name, and sets us as their parent.

func InsertNewChild

func InsertNewChild[T Ki](par Ki, at int, name ...string) T

InsertNewChild is a generic helper function for [Ki.InsertNewChild]

func IsKi

func IsKi(typ reflect.Type) bool

IsKi returns true if the given type implements the Ki interface at any level of embedded structure.

func IsRoot

func IsRoot(k Ki) bool

IsRoot tests if this node is the root node -- checks Parent = nil.

func MoveToParent

func MoveToParent(kid Ki, parent Ki)

MoveToParent deletes given node from its current parent and adds it as a child of given new parent. Parents could be in different trees or not.

func New

func New[T Ki](par Ki, name ...string) T

New adds a new child of the given the type with the given name to the given parent. If the name is unspecified, it defaults to the ID (kebab-case) name of the type, plus the [Ki.NumLifetimeChildren] of its parent. It is a helper function that calls [Ki.NewChild].

func NewRoot

func NewRoot[T Ki](name ...string) T

NewRoot returns a new root node of the given the type with the given name. If the name is unspecified, it defaults to the ID (kebab-case) name of the type. It is a helper function that calls [Ki.InitName].

func ParentAllChildren

func ParentAllChildren(kn Ki)

ParentAllChildren walks the tree down from current node and call SetParent on all children -- needed after an Unmarshal.

func ParentByType

func ParentByType[T Ki](k Ki, embeds bool) T

ParentByType is a generic helper function for [Ki.ParentByType]

func ReadRootTypeJSON

func ReadRootTypeJSON(b []byte) (*gti.Type, []byte, error)

ReadRootTypeJSON reads the type of the root node as encoded by WriteRootTypeJSON, returning the gti.Type for the saved type name (error if not found), the remaining bytes to be decoded using a standard unmarshal, and an error.

func RootTypeJSON

func RootTypeJSON(k Ki) []byte

RootTypeJSON returns the JSON encoding of the type of the root node (this node) which is written first using our custom JSONEncoder type, to enable a file to be loaded de-novo and recreate the proper root type for the tree.

func SaveNewJSON

func SaveNewJSON(k Ki, filename string) error

SaveNewJSON writes JSON-encoded bytes to given writer including key type information at start of file so ReadNewJSON can create an object of the proper type.

func SetDepth

func SetDepth(kn Ki, depth int)

SetDepth sets the current depth of the node to given value.

func SetParent

func SetParent(kid Ki, parent Ki)

SetParent just sets parent of node (and inherits update count from parent, to keep consistent). Assumes not already in a tree or anything.

func SetPropStr

func SetPropStr(pr Props, key, val string)

SetPropStr is a convenience method for e.g., python wrapper that avoids need to deal directly with props interface{} type

func SetSubProps

func SetSubProps(pr Props, key string, sp Props)

SetSubProps is a convenience method for e.g., python wrapper that avoids need to deal directly with props interface{} type

func SliceDeleteAtIndex

func SliceDeleteAtIndex(sl *[]Ki, i int) error

SliceDeleteAtIndex deletes item at index; does not do any further management of deleted item. It is an optimized version for avoiding memory leaks. It returns an error if the index is invalid.

func SliceIndexByFunc

func SliceIndexByFunc(sl *[]Ki, match func(k Ki) bool, startIdx ...int) (int, bool)

SliceIndexByFunc finds index of item based on match function (which must return true for a find match, false for not). Returns false if not found. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func SliceIndexByName

func SliceIndexByName(sl *[]Ki, name string, startIdx ...int) (int, bool)

SliceIndexByName returns index of first element that has given name, false if not found. See Slice.IndexOf for info on startIdx.

func SliceIndexOf

func SliceIndexOf(sl *[]Ki, kid Ki, startIdx ...int) (int, bool)

SliceIndexOf returns index of element in list, false if not there. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func SliceIsValidIndex

func SliceIsValidIndex(sl *[]Ki, idx int) error

SliceIsValidIndex checks whether the given index is a valid index into slice, within range of 0..len-1. Returns error if not.

func SliceMove

func SliceMove(sl *[]Ki, frm, to int) error

SliceMove moves element from one position to another. Returns error if either index is invalid.

func SliceSwap

func SliceSwap(sl *[]Ki, i, j int) error

SliceSwap swaps elements between positions. Returns error if either index is invalid

func ThisCheck

func ThisCheck(k Ki) error

ThisCheck checks that the This pointer is set and issues a warning to log if not -- returns error if not set -- called when nodes are added and inserted.

func UnescapePathName

func UnescapePathName(name string) string

UnescapePathName returns a name that replaces any escaped path delimiter symbols \, or \\ with . and / unescaped versions.

func UniqueNameCheck

func UniqueNameCheck(k Ki) bool

UniqueNameCheck checks if all the children names are unique or not. returns true if all names are unique; false if not if not unique, call UniquifyNames or take other steps to ensure uniqueness.

func UniqueNameCheckAll

func UniqueNameCheckAll(kn Ki) bool

UniqueNameCheckAll checks entire tree from given node, if all the children names are unique or not. returns true if all names are unique; false if not if not unique, call UniquifyNames or take other steps to ensure uniqueness.

func UniquifyNames

func UniquifyNames(kn Ki)

UniquifyNames makes sure that the names are unique. If number of children >= UniquifyIndexAbove, then UniquifyNamesAddIndex is called, for faster performance. Otherwise, existing names are preserved if they are unique, and only duplicates are renamed. This is a bit slower.

func UniquifyNamesAddIndex

func UniquifyNamesAddIndex(kn Ki)

UniquifyNamesAddIndex makes sure that the names are unique by automatically adding a suffix with index number, separated by underbar. Empty names get the parent name as a prefix. if there is an existing underbar, then whatever is after it is replaced with the unique index, ensuring that multiple calls are safe!

func UniquifyNamesAll

func UniquifyNamesAll(kn Ki)

UniquifyNamesAll makes sure that the names are unique for entire tree If number of children >= UniquifyIndexAbove, then UniquifyNamesAddIndex is called, for faster performance. Otherwise, existing names are preserved if they are unique, and only duplicates are renamed. This is a bit slower.

func UnmarshalPost

func UnmarshalPost(kn Ki)

UnmarshalPost must be called after an Unmarshal -- calls ParentAllChildren.

func WriteNewJSON

func WriteNewJSON(k Ki, writer io.Writer) error

WriteNewJSON writes JSON-encoded bytes to given writer including key type information at start of file so ReadNewJSON can create an object of the proper type.

Types

type Config

type Config []TypeAndName

list of type-and-names -- can be created from a string spec

func (*Config) Add

func (t *Config) Add(typ *gti.Type, nm string)

func (Config) GoString

func (t Config) GoString() string

type Flags

type Flags int64 //enums:bitflag

Flags are bit flags for efficient core state of nodes -- see bitflag package for using these ordinal values to manipulate bit flag field.

const (
	// Field indicates a node is a field in its parent node, not a child in children.
	Field Flags = iota
)
const FlagsN Flags = 1

FlagsN is the highest valid value for type Flags, plus one.

func FlagsValues

func FlagsValues() []Flags

FlagsValues returns all possible values for the type Flags.

func (Flags) BitIndexString

func (i Flags) BitIndexString() string

BitIndexString returns the string representation of this Flags value if it is a bit index value (typically an enum constant), and not an actual bit flag value.

func (Flags) Desc

func (i Flags) Desc() string

Desc returns the description of the Flags value.

func (Flags) HasFlag

func (i Flags) HasFlag(f enums.BitFlag) bool

HasFlag returns whether these bit flags have the given bit flag set.

func (Flags) Int64

func (i Flags) Int64() int64

Int64 returns the Flags value as an int64.

func (Flags) MarshalText

func (i Flags) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (*Flags) SetFlag

func (i *Flags) SetFlag(on bool, f ...enums.BitFlag)

SetFlag sets the value of the given flags in these flags to the given value.

func (*Flags) SetInt64

func (i *Flags) SetInt64(in int64)

SetInt64 sets the Flags value from an int64.

func (*Flags) SetString

func (i *Flags) SetString(s string) error

SetString sets the Flags value from its string representation, and returns an error if the string is invalid.

func (*Flags) SetStringOr

func (i *Flags) SetStringOr(s string) error

SetStringOr sets the Flags value from its string representation while preserving any bit flags already set, and returns an error if the string is invalid.

func (Flags) String

func (i Flags) String() string

String returns the string representation of this Flags value.

func (*Flags) UnmarshalText

func (i *Flags) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

func (Flags) Values

func (i Flags) Values() []enums.Enum

Values returns all possible values for the type Flags.

type Ki

type Ki interface {
	// InitName initializes this node to given actual object as a Ki interface
	// and sets its name. The names should be unique among children of a node.
	// This is needed for root nodes -- automatically done for other nodes
	// when they are added to the Ki tree. If the name is unspecified, it
	// defaults to the ID (kebab-case) name of the type.
	// Even though this is a method and gets the method receiver, it needs
	// an "external" version of itself passed as the first arg, from which
	// the proper Ki interface pointer will be obtained.  This is the only
	// way to get virtual functional calling to work within the Go language.
	InitName(this Ki, name ...string)

	// This returns the Ki interface that guarantees access to the Ki
	// interface in a way that always reveals the underlying type
	// (e.g., in reflect calls).  Returns nil if node is nil,
	// has been destroyed, or is improperly constructed.
	This() Ki

	// AsKi returns the *ki.Node base type for this node.
	AsKi() *Node

	// BaseType returns the base node type for all elements within this tree.
	// Used e.g., for determining what types of children can be created.
	BaseType() *gti.Type

	// Name returns the user-defined name of the object (Node.Nm),
	// for finding elements, generating paths, IO, etc.
	Name() string

	// SetName sets the name of this node.
	// Names should generally be unique across children of each node.
	// See Unique* functions to check / fix.
	// If node requires non-unique names, add a separate Label field.
	SetName(name string)

	// KiType returns the gti Type record for this Ki node.
	// This is auto-generated by the gtigen generator for Ki types.
	KiType() *gti.Type

	// New returns a new token of this Ki node.
	// InitName _must_ still be called on this new token.
	// This is auto-generated by the gtigen generator for Ki types.
	New() Ki

	// Parent returns the parent of this Ki (Node.Par) -- Ki has strict
	// one-parent, no-cycles structure -- see SetParent.
	Parent() Ki

	// IndexInParent returns our index within our parent object. It caches the
	// last value and uses that for an optimized search so subsequent calls
	// are typically quite fast. Returns -1 if we don't have a parent.
	IndexInParent() int

	// ParentLevel finds a given potential parent node recursively up the
	// hierarchy, returning level above current node that the parent was
	// found, and -1 if not found.
	ParentLevel(par Ki) int

	// ParentByName finds first parent recursively up hierarchy that matches
	// given name.  Returns nil if not found.
	ParentByName(name string) Ki

	// ParentByType finds parent recursively up hierarchy, by type, and
	// returns nil if not found. If embeds is true, then it looks for any
	// type that embeds the given type at any level of anonymous embedding.
	ParentByType(t *gti.Type, embeds bool) Ki

	// HasChildren tests whether this node has children (i.e., non-terminal).
	HasChildren() bool

	// NumChildren returns the number of children
	NumChildren() int

	// NumLifetimeChildren returns the number of children that this node
	// has ever had added to it (it is not decremented when a child is removed).
	// It is used for unique naming of children.
	NumLifetimeChildren() uint64

	// Children returns a pointer to the slice of children (Node.Kids) -- use
	// methods on ki.Slice for further ways to access (ByName, ByType, etc).
	// Slice can be modified, deleted directly (e.g., sort, reorder) but Add
	// method on parent node should be used to ensure proper init.
	Children() *Slice

	// Child returns the child at given index and returns nil if
	// the index is out of range.
	Child(idx int) Ki

	// ChildByName returns the first element that has given name, and nil
	// if no such element is found. startIdx arg allows for optimized
	// bidirectional find if you have an idea where it might be, which
	// can be a key speedup for large lists. If no value is specified for
	// startIdx, it starts in the middle, which is a good default.
	ChildByName(name string, startIdx ...int) Ki

	// ChildByType returns the first element that has the given type, and nil
	// if not found. If embeds is true, then it also looks for any type that
	// embeds the given type at any level of anonymous embedding.
	// startIdx arg allows for optimized bidirectional find if you have an
	// idea where it might be, which can be a key speedup for large lists. If
	// no value is specified for startIdx, it starts in the middle, which is a
	// good default.
	ChildByType(t *gti.Type, embeds bool, startIdx ...int) Ki

	// Path returns path to this node from the tree root, using node Names
	// separated by / and fields by .
	// Node names escape any existing / and . characters to \\ and \,
	// Path is only valid when child names are unique (see Unique* functions)
	Path() string

	// PathFrom returns path to this node from given parent node, using
	// node Names separated by / and fields by .
	// Node names escape any existing / and . characters to \\ and \,
	// Path is only valid for finding items when child names are unique
	// (see Unique* functions). The paths that it returns exclude the
	// name of the parent and the leading slash; for example, in the tree
	// a/b/c/d/e, the result of d.PathFrom(b) would be c/d. PathFrom
	// automatically gets the [Ki.This] version of the given parent,
	// so a base type can be passed in without manually calling [Ki.This].
	PathFrom(par Ki) string

	// FindPath returns Ki object at given path, starting from this node
	// (e.g., the root).  If this node is not the root, then the path
	// to this node is subtracted from the start of the path if present there.
	// FindPath only works correctly when names are unique.
	// Path has node Names separated by / and fields by .
	// Node names escape any existing / and . characters to \\ and \,
	// There is also support for [idx] index-based access for any given path
	// element, for cases when indexes are more useful than names.
	// Returns nil if not found.
	FindPath(path string) Ki

	// FieldByName returns Ki object that is a direct field.
	// This must be implemented for any types that have Ki fields that
	// are processed as part of the overall Ki tree.  This is only used
	// by FindPath.
	// Returns error if not found.
	FieldByName(field string) (Ki, error)

	// AddChild adds given child at end of children list.
	// The kid node is assumed to not be on another tree (see MoveToParent)
	// and the existing name should be unique among children.
	AddChild(kid Ki) error

	// NewChild creates a new child of the given type and adds it at end
	// of children list. The name should be unique among children. If the
	// name is unspecified, it defaults to the ID (kebab-case) name of the
	// type, plus the [Ki.NumLifetimeChildren] of its parent.
	NewChild(typ *gti.Type, name ...string) Ki

	// SetChild sets child at given index to be the given item; if it is passed
	// a name, then it sets the name of the child as well; just calls Init
	// (or InitName) on the child, and SetParent. Names should be unique
	// among children.
	SetChild(kid Ki, idx int, name ...string) error

	// InsertChild adds given child at position in children list.
	// The kid node is assumed to not be on another tree (see MoveToParent)
	// and the existing name should be unique among children.
	InsertChild(kid Ki, at int) error

	// InsertNewChild creates a new child of given type and add at position
	// in children list. The name should be unique among children. If the
	// name is unspecified, it defaults to the ID (kebab-case) name of the
	// type, plus the [Ki.NumLifetimeChildren] of its parent.
	InsertNewChild(typ *gti.Type, at int, name ...string) Ki

	// SetNChildren ensures that there are exactly n children, deleting any
	// extra, and creating any new ones, using NewChild with given type and
	// naming according to nameStubX where X is the index of the child.
	// If nameStub is not specified, it defaults to the ID (kebab-case)
	// name of the type. It returns whether any changes were made to the
	// children.
	//
	// Note that this does not ensure existing children are of given type, or
	// change their names, or call UniquifyNames -- use ConfigChildren for
	// those cases -- this function is for simpler cases where a parent uses
	// this function consistently to manage children all of the same type.
	SetNChildren(n int, typ *gti.Type, nameStub ...string) bool

	// ConfigChildren configures children according to given list of
	// type-and-name's -- attempts to have minimal impact relative to existing
	// items that fit the type and name constraints (they are moved into the
	// corresponding positions), and any extra children are removed, and new
	// ones added, to match the specified config. It is important that names
	// are unique! It returns whether any changes were made to the children.
	ConfigChildren(config Config) bool

	// DeleteChildAtIndex deletes child at given index. It returns false
	// if there is no child at the given index.
	DeleteChildAtIndex(idx int) bool

	// DeleteChild deletes the given child node, returning false if
	// it can not find it.
	DeleteChild(child Ki) bool

	// DeleteChildByName deletes child node by name, returning false
	// if it can not find it.
	DeleteChildByName(name string) bool

	// DeleteChildren deletes all children nodes.
	DeleteChildren()

	// Delete deletes this node from its parent's children list.
	Delete()

	// Destroy recursively deletes and destroys all children and
	// their children's children, etc.
	Destroy()

	// Is checks if flag is set, using atomic, safe for concurrent access
	Is(f enums.BitFlag) bool

	// SetFlag sets the given flag(s) to given state
	// using atomic, safe for concurrent access
	SetFlag(on bool, f ...enums.BitFlag)

	// FlagType returns the flags of the node as the true flag type of the node,
	// which may be a type that extends the standard [Flags]. Each node type
	// that extends the flag type should define this method; for example:
	//	func (wb *WidgetBase) FlagType() enums.BitFlagSetter {
	//		return (*WidgetFlags)(&wb.Flags)
	//	}
	FlagType() enums.BitFlagSetter

	// Properties (Node.Props) tell the Cogent Core GUI or other frameworks operating
	// on Trees about special features of each node -- functions below support
	// inheritance up Tree -- see kit convert.go for robust convenience
	// methods for converting interface{} values to standard types.
	Properties() *Props

	// SetProp sets given property key to value val -- initializes property
	// map if nil.
	SetProp(key string, val any)

	// Prop returns the property value for the given key.
	// It returns nil if it doesn't exist.
	Prop(key string) any

	// PropInherit gets property value from key with options for inheriting
	// property from parents.  If inherit, then checks all parents.
	// Returns false if not set anywhere.
	PropInherit(key string, inherit bool) (any, bool)

	// DeleteProp deletes property key on this node.
	DeleteProp(key string)

	// PropTag returns the name to look for in type properties, for types
	// that are valid options for values that can be set in Props.  For example
	// in Cogent Core, it is "style-props" which is then set for all types that can
	// be used in a style (colors, enum options, etc)
	PropTag() string

	// WalkUp calls function on given node and all the way up to its parents,
	// and so on -- sequentially all in current go routine (generally
	// necessary for going up, which is typically quite fast anyway) -- level
	// is incremented after each step (starts at 0, goes up), and passed to
	// function -- returns false if fun aborts with false, else true.
	WalkUp(fun func(k Ki) bool) bool

	// WalkUpParent calls function on parent of node and all the way up to its
	// parents, and so on -- sequentially all in current go routine (generally
	// necessary for going up, which is typically quite fast anyway) -- level
	// is incremented after each step (starts at 0, goes up), and passed to
	// function -- returns false if fun aborts with false, else true.
	WalkUpParent(fun func(k Ki) bool) bool

	// WalkPre calls function on this node (MeFirst) and then iterates
	// in a depth-first manner over all the children.
	// The [WalkPreNode] method is called for every node, after the given function,
	// which e.g., enables nodes to also traverse additional Ki Trees (e.g., Fields).
	// The node traversal is non-recursive and uses locally-allocated state -- safe
	// for concurrent calling (modulo conflict management in function call itself).
	// Function calls are sequential all in current go routine.
	// If fun returns false then any further traversal of that branch of the tree is
	// aborted, but other branches continue -- i.e., if fun on current node
	// returns false, children are not processed further.
	WalkPre(fun func(k Ki) bool)

	// WalkPreNode is called for every node during WalkPre with the function
	// passed to WalkPre.  This e.g., enables nodes to also traverse additional
	// Ki Trees (e.g., Fields).
	WalkPreNode(fun func(k Ki) bool)

	// WalkPreLevel calls function on this node (MeFirst) and then iterates
	// in a depth-first manner over all the children.
	// This version has a level var that tracks overall depth in the tree.
	// If fun returns false then any further traversal of that branch of the tree is
	// aborted, but other branches continue -- i.e., if fun on current node
	// returns false, children are not processed further.
	// Because WalkPreLevel is not used within Ki itself, it does not have its
	// own version of WalkPreNode -- that can be handled within the closure.
	WalkPreLevel(fun func(k Ki, level int) bool)

	// WalkPost iterates in a depth-first manner over the children, calling
	// doChildTestFunc on each node to test if processing should proceed (if
	// it returns false then that branch of the tree is not further processed),
	// and then calls given fun function after all of a node's children
	// have been iterated over ("Me Last").
	// This uses node state information to manage the traversal and is very fast,
	// but can only be called by one thread at a time -- use a Mutex if there is
	// a chance of multiple threads running at the same time.
	// Function calls are sequential all in current go routine.
	// The level var tracks overall depth in the tree.
	WalkPost(doChildTestFunc func(k Ki) bool, fun func(k Ki) bool)

	// WalkBreadth calls function on all children in breadth-first order
	// using the standard queue strategy.  This depends on and updates the
	// Depth parameter of the node.  If fun returns false then any further
	// traversal of that branch of the tree is aborted, but other branches continue.
	WalkBreadth(fun func(k Ki) bool)

	// CopyFrom another Ki node.  It is essential that source has Unique names!
	// The Ki copy function recreates the entire tree in the copy, duplicating
	// children etc, copying Props too.  It is very efficient by
	// using the ConfigChildren method which attempts to preserve any existing
	// nodes in the destination if they have the same name and type -- so a
	// copy from a source to a target that only differ minimally will be
	// minimally destructive.  Only copies to same types are supported.
	// Signal connections are NOT copied.  No other Ki pointers are copied,
	// and the field tag copier:"-" can be added for any other fields that
	// should not be copied (unexported, lower-case fields are not copyable).
	CopyFrom(frm Ki) error

	// Clone creates and returns a deep copy of the tree from this node down.
	// Any pointers within the cloned tree will correctly point within the new
	// cloned tree (see Copy info).
	Clone() Ki

	// CopyFieldsFrom copies the fields of the node from the given node.
	// By default, it is [Node.CopyFieldsFrom], which automatically does
	// a deep copy of all of the fields of the node that do not a have a
	// `copier:"-"` struct tag. Node types should only implement a custom
	// CopyFieldsFrom method when they have fields that need special copying
	// logic that can not be automatically handled. All custom CopyFieldsFrom
	// methods should call [Node.CopyFieldsFrom] first and then only do manual
	// handling of specific fields that can not be automatically copied. See
	// [cogentcore.org/core/gi.WidgetBase.CopyFieldsFrom] for an example of a
	// custom CopyFieldsFrom method.
	CopyFieldsFrom(from Ki)

	// OnInit is called when the node is
	// initialized (ie: through InitName).
	// It is called before the node is added to the tree,
	// so it will not have any parents or siblings.
	// It will be called only once in the lifetime of the node.
	// It does nothing by default, but it can be implemented
	// by higher-level types that want to do something.
	OnInit()

	// OnAdd is called when the node is added to a parent.
	// It will be called only once in the lifetime of the node,
	// unless the node is moved. It will not be called on root
	// nodes, as they are never added to a parent.
	// It does nothing by default, but it can be implemented
	// by higher-level types that want to do something.
	OnAdd()

	// OnChildAdded is called when a node is added to
	// this node or any of its children. When a node is added to
	// a tree, it calls [OnAdd] and then this function on each of its parents,
	// going in order from the closest parent to the furthest parent.
	// This function does nothing by default, but it can be
	// implemented by higher-level types that want to do something.
	OnChildAdded(child Ki)
}

The Ki interface provides the core functionality for a Cogent Core tree. Each Ki is a node in the tree and can have child nodes, and no cycles are allowed (i.e., each node can only appear once in the tree). All the usual methods are included for accessing and managing Children, and efficiently traversing the tree and calling functions on the nodes. In addition, Ki nodes can have Fields that are also Ki nodes that are included in all the automatic tree traversal methods -- they are effectively named fixed children that are automatically present.

In general, the names of the children of a given node should all be unique. The following functions defined in ki package can be used: UniqueNameCheck(node) to check for unique names on node if uncertain. UniqueNameCheckAll(node) to check entire tree under given node. UniquifyNames(node) to add a suffix to name to ensure uniqueness. UniquifyNamesAll(node) to to uniquify all names in entire tree.

Use function MoveChild to move a node between trees or within a tree -- otherwise nodes are typically created and deleted but not moved.

The Ki interface is designed to support virtual method calling in Go and is only intended to be implemented once, by the ki.Node type (as opposed to interfaces that are used for hiding multiple different implementations of a common concept). Thus, all of the fields in ki.Node are exported (have captital names), to be accessed directly in types that embed and extend the ki.Node. The Ki interface has the "formal" name (e.g., Children) while the Node has the "nickname" (e.g., Kids). See the Naming Conventions on the Cogent Core Wiki for more details.

Each Node stores the Ki interface version of itself, as This() / Ths which enables full virtual function calling by calling the method on that interface instead of directly on the receiver Node itself. This requires proper initialization via Init method of the Ki interface.

Ki nodes also support the following core functionality:

  • ConfigChildren system for minimally updating children to fit a given Name & Type template.
  • Automatic JSON I/O of entire tree including type information.

func Last

func Last(nd Ki) Ki

Last returns the last node in the tree

func LastChild

func LastChild(nd Ki) Ki

LastChild returns the last child under given node, or node itself if no children

func NewOfType

func NewOfType(typ *gti.Type) Ki

NewOfType makes a new Ki struct of given type -- must be a Ki type -- will return nil if not.

func Next

func Next(nd Ki) Ki

Next returns next node in the tree, nil if end

func NextSibling

func NextSibling(nd Ki) Ki

NextSibling returns next sibling or nil if none

func OpenNewJSON

func OpenNewJSON(filename string) (Ki, error)

OpenNewJSON opens a new Ki tree from a JSON-encoded file, using type information at start of file to create an object of the proper type

func Prev

func Prev(nd Ki) Ki

Prev returns previous node in the tree -- nil if top

func ReadNewJSON

func ReadNewJSON(reader io.Reader) (Ki, error)

ReadNewJSON reads a new Ki tree from a JSON-encoded byte string, using type information at start of file to create an object of the proper type

func Root

func Root(k Ki) Ki

Root returns the root node of given ki node in tree (the node with a nil parent).

type Node

type Node struct {

	// Nm is the user-supplied name of this node, which can be empty and/or non-unique.
	Nm string `copier:"-" set:"-" label:"Name"`

	// Flags are bit flags for internal node state, which can be extended using the enums package.
	Flags Flags `tableview:"-" copier:"-" json:"-" xml:"-" set:"-" max-width:"80" height:"3"`

	// Props is a property map for arbitrary extensible properties.
	Props Props `tableview:"-" xml:"-" copier:"-" set:"-" label:"Properties"`

	// Par is the parent of this node, which is set automatically when this node is added as a child of a parent.
	Par Ki `tableview:"-" copier:"-" json:"-" xml:"-" view:"-" set:"-" label:"Parent"`

	// Kids is the list of children of this node. All of them are set to have this node
	// as their parent. They can be reordered, but you should generally use Ki Node methods
	// to Add / Delete to ensure proper usage.
	Kids Slice `tableview:"-" copier:"-" set:"-" label:"Children"`

	// Ths is a pointer to ourselves as a Ki. It can always be used to extract the true underlying type
	// of an object when [Node] is embedded in other structs; function receivers do not have this ability
	// so this is necessary. This is set to nil when deleted. Typically use [Ki.This] convenience accessor
	// which protects against concurrent access.
	Ths Ki `copier:"-" json:"-" xml:"-" view:"-" set:"-"`

	// NumLifetimeKids is the number of children that have ever been added to this node, which is used for automatic unique naming.
	NumLifetimeKids uint64 `copier:"-" json:"-" xml:"-" view:"-" set:"-"`
	// contains filtered or unexported fields
}

The Node struct implements the Ki interface and provides the core functionality for the Cogent Core tree system. You can use the Node as an embedded struct or as a struct field; the embedded version supports full JSON saving and loading. All types that implement the Ki interface will automatically be added to gti in `core generate`, which is required for various pieces of core functionality.

func NewNode

func NewNode(par Ki, name ...string) *Node

NewNode adds a new Node with the given name to the given parent: The Node struct implements the Ki interface and provides the core functionality for the Cogent Core tree system. You can use the Node as an embedded struct or as a struct field; the embedded version supports full JSON saving and loading. All types that implement the Ki interface will automatically be added to gti in `core generate`, which is required for various pieces of core functionality.

func (*Node) AddChild

func (n *Node) AddChild(kid Ki) error

AddChild adds given child at end of children list. The kid node is assumed to not be on another tree (see MoveToParent) and the existing name should be unique among children.

func (*Node) AsKi

func (n *Node) AsKi() *Node

AsKi returns the *ki.Node base type for this node.

func (*Node) BaseType

func (n *Node) BaseType() *gti.Type

BaseType returns the base node type for all elements within this tree. Used e.g., for determining what types of children can be created.

func (*Node) Child

func (n *Node) Child(idx int) Ki

Child returns the child at given index and returns nil if the index is out of range.

func (*Node) ChildByName

func (n *Node) ChildByName(name string, startIdx ...int) Ki

ChildByName returns the first element that has given name, and nil if no such element is found. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be a key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func (*Node) ChildByType

func (n *Node) ChildByType(t *gti.Type, embeds bool, startIdx ...int) Ki

ChildByType returns the first element that has the given type, and nil if not found. If embeds is true, then it also looks for any type that embeds the given type at any level of anonymous embedding. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be a key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func (*Node) Children

func (n *Node) Children() *Slice

Children returns a pointer to the slice of children (Node.Kids) -- use methods on ki.Slice for further ways to access (ByName, ByType, etc). Slice can be modified directly (e.g., sort, reorder) but Add* / Delete* methods on parent node should be used to ensure proper tracking.

func (*Node) Clone

func (n *Node) Clone() Ki

Clone creates and returns a deep copy of the tree from this node down. Any pointers within the cloned tree will correctly point within the new cloned tree (see Copy info).

func (*Node) ConfigChildren

func (n *Node) ConfigChildren(config Config) bool

ConfigChildren configures children according to given list of type-and-name's -- attempts to have minimal impact relative to existing items that fit the type and name constraints (they are moved into the corresponding positions), and any extra children are removed, and new ones added, to match the specified config. It is important that names are unique! It returns whether any changes were made to the children.

func (*Node) CopyFieldsFrom

func (n *Node) CopyFieldsFrom(from Ki)

CopyFieldsFrom is the base implementation of [Ki.CopyFieldsFrom] that copies the fields of the Node.This from the fields of the given [Ki.This], recursively following anonymous embedded structs. It uses copier.Copy for this. It ignores any fields with a `copier:"-"` struct tag. Other implementations of [Ki.CopyFieldsFrom] should call this method first and then only do manual handling of specific fields that can not be automatically copied.

func (*Node) CopyFrom

func (n *Node) CopyFrom(frm Ki) error

CopyFrom another Ki node. It is essential that source has Unique names! The Ki copy function recreates the entire tree in the copy, duplicating children etc, copying Props too. It is very efficient by using the ConfigChildren method which attempts to preserve any existing nodes in the destination if they have the same name and type -- so a copy from a source to a target that only differ minimally will be minimally destructive. Only copies to same types are supported. Signal connections are NOT copied. No other Ki pointers are copied, and the field tag copier:"-" can be added for any other fields that should not be copied (unexported, lower-case fields are not copyable).

func (*Node) Delete

func (n *Node) Delete()

Delete deletes this node from its parent's children list.

func (*Node) DeleteChild

func (n *Node) DeleteChild(child Ki) bool

DeleteChild deletes the given child node, returning false if it can not find it.

func (*Node) DeleteChildAtIndex

func (n *Node) DeleteChildAtIndex(idx int) bool

DeleteChildAtIndex deletes child at given index. It returns false if there is no child at the given index.

func (*Node) DeleteChildByName

func (n *Node) DeleteChildByName(name string) bool

DeleteChildByName deletes child node by name, returning false if it can not find it.

func (*Node) DeleteChildren

func (n *Node) DeleteChildren()

DeleteChildren deletes all children nodes.

func (*Node) DeleteProp

func (n *Node) DeleteProp(key string)

DeleteProp deletes property key on this node.

func (*Node) Destroy

func (n *Node) Destroy()

Destroy recursively deletes and destroys all children and their children's children, etc.

func (*Node) FieldByName

func (n *Node) FieldByName(field string) (Ki, error)

func (*Node) FindPath

func (n *Node) FindPath(path string) Ki

FindPath returns Ki object at given path, starting from this node (e.g., the root). If this node is not the root, then the path to this node is subtracted from the start of the path if present there. FindPath only works correctly when names are unique. Path has node Names separated by / and fields by . Node names escape any existing / and . characters to \\ and \, There is also support for [idx] index-based access for any given path element, for cases when indexes are more useful than names. Returns nil if not found.

func (*Node) FlagType

func (n *Node) FlagType() enums.BitFlagSetter

FlagType is the base implementation of [Ki.FlagType] that returns a value of type Flags.

func (*Node) HasChildren

func (n *Node) HasChildren() bool

HasChildren tests whether this node has children (i.e., non-terminal).

func (*Node) IndexInParent

func (n *Node) IndexInParent() int

IndexInParent returns our index within our parent object. It caches the last value and uses that for an optimized search so subsequent calls are typically quite fast. Returns -1 if we don't have a parent.

func (*Node) InitName

func (n *Node) InitName(k Ki, name ...string)

InitName initializes this node to given actual object as a Ki interface and sets its name. The names should be unique among children of a node. This is needed for root nodes -- automatically done for other nodes when they are added to the Ki tree. If the name is unspecified, it defaults to the ID (kebab-case) name of the type. Even though this is a method and gets the method receiver, it needs an "external" version of itself passed as the first arg, from which the proper Ki interface pointer will be obtained. This is the only way to get virtual functional calling to work within the Go language.

func (*Node) InsertChild

func (n *Node) InsertChild(kid Ki, at int) error

InsertChild adds given child at position in children list. The kid node is assumed to not be on another tree (see MoveToParent) and the existing name should be unique among children.

func (*Node) InsertNewChild

func (n *Node) InsertNewChild(typ *gti.Type, at int, name ...string) Ki

InsertNewChild creates a new child of given type and add at position in children list. The name should be unique among children. If the name is unspecified, it defaults to the ID (kebab-case) name of the type, plus the [Ki.NumLifetimeChildren] of its parent.

func (*Node) Is

func (n *Node) Is(f enums.BitFlag) bool

Is checks if flag is set, using atomic, safe for concurrent access

func (*Node) KiType

func (t *Node) KiType() *gti.Type

KiType returns the *gti.Type of Node

func (*Node) Name

func (n *Node) Name() string

Name returns the user-defined name of the object (Node.Nm), for finding elements, generating paths, IO, etc.

func (*Node) New

func (t *Node) New() Ki

New returns a new *Node value

func (*Node) NewChild

func (n *Node) NewChild(typ *gti.Type, name ...string) Ki

NewChild creates a new child of the given type and adds it at end of children list. The name should be unique among children. If the name is unspecified, it defaults to the ID (kebab-case) name of the type, plus the [Ki.NumLifetimeChildren] of its parent.

func (*Node) NumChildren

func (n *Node) NumChildren() int

NumChildren returns the number of children of this node.

func (*Node) NumLifetimeChildren

func (n *Node) NumLifetimeChildren() uint64

func (*Node) OnAdd

func (n *Node) OnAdd()

OnAdd is a placeholder implementation of [Ki.OnAdd] that does nothing.

func (*Node) OnChildAdded

func (n *Node) OnChildAdded(child Ki)

OnChildAdded is a placeholder implementation of [Ki.OnChildAdded] that does nothing.

func (*Node) OnInit

func (n *Node) OnInit()

OnInit is a placeholder implementation of [Ki.OnInit] that does nothing.

func (*Node) Parent

func (n *Node) Parent() Ki

Parent returns the parent of this Ki (Node.Par) -- Ki has strict one-parent, no-cycles structure -- see SetParent.

func (*Node) ParentByName

func (n *Node) ParentByName(name string) Ki

ParentByName finds first parent recursively up hierarchy that matches given name -- returns nil if not found.

func (*Node) ParentByType

func (n *Node) ParentByType(t *gti.Type, embeds bool) Ki

ParentByType finds parent recursively up hierarchy, by type, and returns nil if not found. If embeds is true, then it looks for any type that embeds the given type at any level of anonymous embedding.

func (*Node) ParentLevel

func (n *Node) ParentLevel(par Ki) int

ParentLevel finds a given potential parent node recursively up the hierarchy, returning level above current node that the parent was found, and -1 if not found.

func (*Node) Path

func (n *Node) Path() string

Path returns path to this node from the tree root, using node Names separated by / and fields by . Node names escape any existing / and . characters to \\ and \, Path is only valid when child names are unique (see Unique* functions)

func (*Node) PathFrom

func (n *Node) PathFrom(par Ki) string

PathFrom returns path to this node from given parent node, using node Names separated by / and fields by . Node names escape any existing / and . characters to \\ and \, Path is only valid for finding items when child names are unique (see Unique* functions). The paths that it returns exclude the name of the parent and the leading slash; for example, in the tree a/b/c/d/e, the result of d.PathFrom(b) would be c/d. PathFrom automatically gets the [Ki.This] version of the given parent, so a base type can be passed in without manually calling [Ki.This].

func (*Node) Prop

func (n *Node) Prop(key string) any

Prop returns the property value for the given key. It returns nil if it doesn't exist.

func (*Node) PropInherit

func (n *Node) PropInherit(key string, inherit bool) (any, bool)

PropInherit gets property value from key with options for inheriting property from parents. If inherit, then checks all parents. Returns false if not set anywhere.

func (*Node) PropTag

func (n *Node) PropTag() string

PropTag returns the name to look for in type properties, for types that are valid options for values that can be set in Props. For example in Cogent Core, it is "style-props" which is then set for all types that can be used in a style (colors, enum options, etc)

func (*Node) Properties

func (n *Node) Properties() *Props

Properties (Node.Props) tell the Cogent Core GUI or other frameworks operating on Trees about special features of each node -- functions below support inheritance up Tree.

func (*Node) ReadXML

func (n *Node) ReadXML(reader io.Reader) error

ReadXML reads the tree from an XML-encoded byte string over io.Reader, calls UnmarshalPost to recover pointers from paths.

func (*Node) SetChild

func (n *Node) SetChild(kid Ki, idx int, name ...string) error

SetChild sets child at given index to be the given item; if it is passed a name, then it sets the name of the child as well; just calls Init (or InitName) on the child, and SetParent. Names should be unique among children.

func (*Node) SetFlag

func (n *Node) SetFlag(on bool, f ...enums.BitFlag)

SetFlag sets the given flag(s) to given state using atomic, safe for concurrent access

func (*Node) SetNChildren

func (n *Node) SetNChildren(trgn int, typ *gti.Type, nameStub ...string) bool

SetNChildren ensures that there are exactly n children, deleting any extra, and creating any new ones, using NewChild with given type and naming according to nameStubX where X is the index of the child. If nameStub is not specified, it defaults to the ID (kebab-case) name of the type. It returns whether any changes were made to the children.

Note that this does not ensure existing children are of given type, or change their names, or call UniquifyNames -- use ConfigChildren for those cases -- this function is for simpler cases where a parent uses this function consistently to manage children all of the same type.

func (*Node) SetName

func (n *Node) SetName(name string)

SetName sets the name of this node. Names should generally be unique across children of each node. See Unique* functions to check / fix. If node requires non-unique names, add a separate Label field.

func (*Node) SetProp

func (n *Node) SetProp(key string, val any)

SetProp sets given property key to value val. initializes property map if nil.

func (*Node) SetProps

func (n *Node) SetProps(props Props)

SetProps sets a whole set of properties

func (*Node) String

func (n *Node) String() string

String implements the fmt.stringer interface -- returns the Path of the node

func (*Node) This

func (n *Node) This() Ki

This returns the Ki interface that guarantees access to the Ki interface in a way that always reveals the underlying type (e.g., in reflect calls). Returns nil if node is nil, has been destroyed, or is improperly constructed.

func (*Node) WalkBreadth

func (n *Node) WalkBreadth(fun func(k Ki) bool)

WalkBreadth calls function on all children in breadth-first order using the standard queue strategy. This depends on and updates the Depth parameter of the node. If fun returns false then any further traversal of that branch of the tree is aborted, but other branches continue.

func (*Node) WalkPost

func (n *Node) WalkPost(doChildTestFunc func(Ki) bool, fun func(Ki) bool)

WalkPost iterates in a depth-first manner over the children, calling doChildTestFunc on each node to test if processing should proceed (if it returns false then that branch of the tree is not further processed), and then calls given fun function after all of a node's children. have been iterated over ("Me Last"). The node traversal is non-recursive and uses locally-allocated state -- safe for concurrent calling (modulo conflict management in function call itself). Function calls are sequential all in current go routine. The level var tracks overall depth in the tree.

func (*Node) WalkPre

func (n *Node) WalkPre(fun func(Ki) bool)

WalkPre calls function on this node (MeFirst) and then iterates in a depth-first manner over all the children. The [WalkPreNode] method is called for every node, after the given function, which e.g., enables nodes to also traverse additional Ki Trees (e.g., Fields). The node traversal is non-recursive and uses locally-allocated state -- safe for concurrent calling (modulo conflict management in function call itself). Function calls are sequential all in current go routine. If fun returns false then any further traversal of that branch of the tree is aborted, but other branches continue -- i.e., if fun on current node returns false, children are not processed further.

func (*Node) WalkPreLevel

func (n *Node) WalkPreLevel(fun func(k Ki, level int) bool)

WalkPreLevel calls function on this node (MeFirst) and then iterates in a depth-first manner over all the children. This version has a level var that tracks overall depth in the tree. If fun returns false then any further traversal of that branch of the tree is aborted, but other branches continue -- i.e., if fun on current node returns false, children are not processed further. Because WalkPreLevel is not used within Ki itself, it does not have its own version of WalkPreNode -- that can be handled within the closure.

func (*Node) WalkPreNode

func (n *Node) WalkPreNode(fun func(Ki) bool)

WalkPreNode is called for every node during WalkPre with the function passed to WalkPre. This e.g., enables nodes to also traverse additional Ki Trees (e.g., Fields).

func (*Node) WalkUp

func (n *Node) WalkUp(fun func(k Ki) bool) bool

WalkUp calls function on given node and all the way up to its parents, and so on -- sequentially all in current go routine (generally necessary for going up, which is typically quite fast anyway) -- level is incremented after each step (starts at 0, goes up), and passed to function -- returns false if fun aborts with false, else true.

func (*Node) WalkUpParent

func (n *Node) WalkUpParent(fun func(k Ki) bool) bool

WalkUpParent calls function on parent of node and all the way up to its parents, and so on -- sequentially all in current go routine (generally necessary for going up, which is typically quite fast anyway) -- level is incremented after each step (starts at 0, goes up), and passed to function -- returns false if fun aborts with false, else true.

func (*Node) WriteXML

func (n *Node) WriteXML(writer io.Writer, indent bool) error

WriteXML writes the tree to an XML-encoded byte string over io.Writer using MarshalXML.

type PropSlice

type PropSlice []PropStruct

PropSlice is a slice of PropStruct, for when order is important within a subset of properties (maps do not retain order) -- can set the value of a property to a PropSlice to create an ordered list of property values.

func SliceProps

func SliceProps(pr map[string]any, key string) (PropSlice, bool)

SliceProps returns a value that contains a PropSlice, or nil and false if it doesn't exist or isn't a PropSlice

func (*PropSlice) CopyFrom

func (dest *PropSlice) CopyFrom(src PropSlice, deepCopy bool)

CopyFrom copies properties from source to destination propslice. If deepCopy is true, then any values that are Props or PropSlice are copied too *dest can be nil, in which case it is created.

func (*PropSlice) ElemLabel

func (ps *PropSlice) ElemLabel(idx int) string

ElemLabel satisfies the gi.SliceLabeler interface to provide labels for slice elements

type PropStruct

type PropStruct struct {
	Name  string
	Value any
}

PropStruct is a struct of Name and Value, for use in a PropSlice to hold properties that require order information (maps do not retain any order)

type Props

type Props map[string]any

Props is the type used for holding generic properties -- the actual Go type is a mouthful and not very gui-friendly, and we need some special json methods

func SubProps

func SubProps(pr map[string]any, key string) (Props, bool)

SubProps returns a value that contains another props, or nil and false if it doesn't exist or isn't a Props

func (*Props) CopyFrom

func (dest *Props) CopyFrom(src map[string]any, deepCopy bool)

CopyFrom copies properties from source to receiver destination map. If deepCopy is true, then any values that are Props or PropSlice are copied too *dest can be nil, in which case it is created.

func (Props) Delete

func (pr Props) Delete(key string)

Delete deletes props value at given key

func (Props) MarshalJSON

func (p Props) MarshalJSON() ([]byte, error)

MarshalJSON saves the type information for each struct used in props, as a separate key with the __type: prefix -- this allows the Unmarshal to create actual types

func (Props) Prop

func (pr Props) Prop(key string) any

Prop returns property of given key

func (*Props) Set

func (pr *Props) Set(key string, val any)

Set sets props value -- safely creates map

func (*Props) UnmarshalJSON

func (p *Props) UnmarshalJSON(b []byte) error

UnmarshalJSON parses the type information in the map to restore actual objects -- this is super inefficient and really needs a native parser, but props are likely to be relatively small

type Slice

type Slice []Ki

Slice is just a slice of ki elements: []Ki, providing methods for accessing elements in the slice, and JSON marshal / unmarshal with encoding of underlying types

func (*Slice) Config

func (sl *Slice) Config(n Ki, config Config) bool

Config is a major work-horse routine for minimally destructive reshaping of a tree structure to fit a target configuration, specified in terms of a type-and-name list. It returns whether any changes were made to the slice.

func (*Slice) ConfigCopy

func (sl *Slice) ConfigCopy(n Ki, frm Slice)

ConfigCopy uses Config method to copy name / type config of Slice from source If n is != nil then Update etc is called properly. it is essential that child names are unique.

func (*Slice) CopyFrom

func (sl *Slice) CopyFrom(frm Slice)

CopyFrom another Slice. It is efficient by using the Config method which attempts to preserve any existing nodes in the destination if they have the same name and type -- so a copy from a source to a target that only differ minimally will be minimally destructive. it is essential that child names are unique.

func (*Slice) DeleteAtIndex

func (sl *Slice) DeleteAtIndex(idx int) error

DeleteAtIndex deletes item at index; does not do any further management of deleted item. It is an optimized version for avoiding memory leaks. It returns an error if the index is invalid.

func (*Slice) Elem

func (sl *Slice) Elem(idx int) Ki

Elem returns element at index -- panics if index is invalid

func (*Slice) ElemByName

func (sl *Slice) ElemByName(name string, startIdx ...int) Ki

ElemByName returns first element that has given name, nil if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) ElemByNameTry

func (sl *Slice) ElemByNameTry(name string, startIdx ...int) (Ki, error)

ElemByNameTry returns first element that has given name, error if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) ElemByType

func (sl *Slice) ElemByType(t *gti.Type, embeds bool, startIdx ...int) Ki

ElemByType returns index of element that either is that type or embeds that type, nil if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) ElemByTypeTry

func (sl *Slice) ElemByTypeTry(t *gti.Type, embeds bool, startIdx ...int) (Ki, error)

ElemByTypeTry returns index of element that either is that type or embeds that type, error if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) ElemFromEnd

func (sl *Slice) ElemFromEnd(idx int) Ki

ElemFromEnd returns element at index from end of slice (0 = last element, 1 = 2nd to last, etc). Panics if invalid index.

func (*Slice) ElemFromEndTry

func (sl *Slice) ElemFromEndTry(idx int) (Ki, error)

ElemFromEndTry returns element at index from end of slice (0 = last element, 1 = 2nd to last, etc). Try version returns error on invalid index.

func (*Slice) ElemTry

func (sl *Slice) ElemTry(idx int) (Ki, error)

ElemTry returns element at index -- Try version returns error if index is invalid.

func (*Slice) IndexByFunc

func (sl *Slice) IndexByFunc(match func(k Ki) bool, startIdx ...int) (int, bool)

IndexByFunc finds index of item based on match function (which must return true for a find match, false for not). Returns false if not found. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func (*Slice) IndexByName

func (sl *Slice) IndexByName(name string, startIdx ...int) (int, bool)

IndexByName returns index of first element that has given name, false if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) IndexByType

func (sl *Slice) IndexByType(t *gti.Type, embeds bool, startIdx ...int) (int, bool)

IndexByType returns index of element that either is that type or embeds that type, false if not found. See Slice.IndexOf for info on startIdx.

func (*Slice) IndexOf

func (sl *Slice) IndexOf(kid Ki, startIdx ...int) (int, bool)

IndexOf returns index of element in list, false if not there. startIdx arg allows for optimized bidirectional find if you have an idea where it might be, which can be key speedup for large lists. If no value is specified for startIdx, it starts in the middle, which is a good default.

func (*Slice) Insert

func (sl *Slice) Insert(k Ki, i int)

Insert item at index; does not do any parent updating etc; use the Ki or Node method unless you know what you are doing.

func (*Slice) IsValidIndex

func (sl *Slice) IsValidIndex(idx int) error

IsValidIndex checks whether the given index is a valid index into slice, within range of 0..len-1. Returns error if not.

func (Slice) MarshalJSON

func (sl Slice) MarshalJSON() ([]byte, error)

MarshalJSON saves the length and type, name information for each object in a slice, as a separate struct-like record at the start, followed by the structs for each element in the slice -- this allows the Unmarshal to first create all the elements and then load them

func (Slice) MarshalXML

func (sl Slice) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML saves the length and type information for each object in a slice, as a separate struct-like record at the start, followed by the structs for each element in the slice -- this allows the Unmarshal to first create all the elements and then load them

func (*Slice) Move

func (sl *Slice) Move(frm, to int) error

Move element from one position to another. Returns error if either index is invalid.

func (*Slice) Swap

func (sl *Slice) Swap(i, j int) error

Swap elements between positions. Returns error if either index is invalid

func (*Slice) UnmarshalJSON

func (sl *Slice) UnmarshalJSON(b []byte) error

UnmarshalJSON parses the length and type information for each object in the slice, creates the new slice with those elements, and then loads based on the remaining bytes which represent each element

func (*Slice) UnmarshalXML

func (sl *Slice) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML parses the length and type information for each object in the slice, creates the new slice with those elements, and then loads based on the remaining bytes which represent each element

type TravMap

type TravMap map[Ki]int

TravMap is a map for recording the traversal of nodes

func (TravMap) End

func (tm TravMap) End(k Ki)

End deletes node once done at end of traversal

func (TravMap) Get

func (tm TravMap) Get(k Ki) int

Get retrieves current traversal state

func (TravMap) Set

func (tm TravMap) Set(k Ki, curChild int)

Set updates traversal state

func (TravMap) Start

func (tm TravMap) Start(k Ki)

Start is called at start of traversal

type TypeAndName

type TypeAndName struct {
	Type *gti.Type
	Name string
}

TypeAndName holds a type and a name. Used for specifying configurations of children in Ki, for efficiently configuring the chilren.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL