From b8ef0bb05dc14bc4291f3d156b199fa125cdb9d7 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Thu, 20 Oct 2022 06:55:42 -0500 Subject: Squashed commit of the following: all non binary tree structs are gone (almost all) Use names from https://en.wikipedia.org/wiki/Graphical_widget toolkit andlabs/ui is isolated from being accessable all direct references to andlabs are removed working dropdown widgets add debugging more buttons and windows --- structs.go | 422 ++++++++++++++++++++++++------------------------------------- 1 file changed, 162 insertions(+), 260 deletions(-) (limited to 'structs.go') diff --git a/structs.go b/structs.go index fedb34a..d9b5bef 100644 --- a/structs.go +++ b/structs.go @@ -1,31 +1,38 @@ package gui import ( - "image/color" "log" - "github.com/andlabs/ui" - "golang.org/x/image/font" - - // "github.com/davecgh/go-spew/spew" - - // _ "github.com/andlabs/ui/winmanifest" ) +import toolkit "git.wit.org/wit/gui/toolkit/andlabs" + // // All GUI Data Structures and functions that are external -// If you need cross platform support, these might only -// be the safe way to interact with the GUI +// within the toolkit/ abstraction layer +// +// More than one Window is not supported in a cross platform +// sense & may never be. On many toolkits you have to have 'tabs' +// Native Windows and MacOS toolkits work with tabs // -var Data GuiData +// If that is the case, this code should abstract the concept of +// windows and make everything 'tabs' +// + var Config GuiConfig type GuiConfig struct { + // This is the master node. The Binary Tree starts here + master *Node + + // These are shortcuts to pass default values to make a new window Title string Width int Height int Exit func(*Node) + // These are global debugging settings + // TODO: move to a standard logging system Debug bool DebugNode bool DebugTabs bool @@ -33,295 +40,190 @@ type GuiConfig struct { DebugWindow bool DebugToolkit bool + // hacks depth int counter int // used to make unique ID's prefix string } -type GuiData struct { - // a fallback default function to handle mouse events - // if nothing else is defined to handle them - MouseClick func(*GuiButton) - - // A map of all the entry boxes - AllEntries []*GuiEntry - WindowMap map[string]*GuiWindow +type Widget int - // Store access to everything via binary tree's - NodeMap map[string]*Node - NodeArray []*Node - NodeSlice []*Node - - // A map of all buttons everywhere on all - // windows, all tabs, across all goroutines - // This is "GLOBAL" - // - // This has to work this way because of how - // andlabs/ui & andlabs/libui work - AllButtons []*GuiButton - buttonMap map[*ui.Button]*GuiButton -} +// https://ieftimov.com/post/golang-datastructures-trees/ +const ( + Unknown Widget = iota + Window + Tab + Frame + Dropbox + Spinner + Label +) -type GuiTab struct { - Name string // field for human readable name - Number int // the andlabs/ui tab index - Window *GuiWindow // the parent Window +func (s Widget) String() string { + switch s { + case Window: + return "Window" + case Tab: + return "Tab" + case Frame: + return "Frame" + case Label: + return "Label" + case Dropbox: + return "Dropbox" + } + return "unknown" } -// -// stores information on the 'window' -// -// This merges the concept of andlabs/ui *Window and *Tab -// -// More than one Window is not supported in a cross platform -// sense & may never be. On Windows and MacOS, you have to have -// 'tabs'. Even under Linux, more than one Window is currently -// unstable -// -// This code will make a 'GuiWindow' regardless of if it is -// a stand alone window (which is more or less working on Linux) -// or a 'tab' inside a window (which is all that works on MacOS -// and MSWindows. -// -// This struct keeps track of what is in the window so you -// can destroy and replace it with something else -// -type GuiWindow struct { - Name string // field for human readable name - Width int - Height int - Axis int // does it add items to the X or Y axis - TabNumber *int // the andlabs/ui tab index +// The Node is simply the name and the size of whatever GUI element exists +type Node struct { + id string - // the callback function to make the window contents - // MakeWindow func(*GuiBox) *GuiBox + Name string + Width int + Height int - // the components of the window - BoxMap map[string]*GuiBox - EntryMap map[string]*GuiEntry - Area *GuiArea + parent *Node + children []*Node - node *Node + custom func(*Node) + OnChanged func(*Node) - // andlabs/ui abstraction mapping - UiWindow *ui.Window - UiTab *ui.Tab // if this != nil, the window is 'tabbed' + toolkit *toolkit.Toolkit } -func (w *GuiWindow) Dump() { - log.Println("gui.GuiWindow.Dump() Name = ", w.Name) - log.Println("gui.GuiWindow.Dump() node = ", w.node) - log.Println("gui.GuiWindow.Dump() Width = ", w.Width) - log.Println("gui.GuiWindow.Dump() Height = ", w.Height) +func (n *Node) Parent() *Node { + return n.parent } -// GuiBox is any type of ui.Hbox or ui.Vbox -// There can be lots of these for each GuiWindow -type GuiBox struct { - Name string // field for human readable name - Axis int // does it add items to the X or Y axis - Window *GuiWindow // the parent Window - - node *Node - - // andlabs/ui abstraction mapping - UiBox *ui.Box +func (n *Node) Window() *Node { + return n.parent } -func (b *GuiBox) Dump() { - log.Println("gui.GuiBox.Dump() Name = ", b.Name) - log.Println("gui.GuiBox.Dump() Axis = ", b.Axis) - log.Println("gui.GuiBox.Dump() GuiWindow = ", b.Window) - log.Println("gui.GuiBox.Dump() node = ", b.node) - log.Println("gui.GuiBox.Dump() UiBox = ", b.UiBox) -} +func (n *Node) Dump() { + IndentPrintln("id = ", n.id) + IndentPrintln("Name = ", n.Name) + IndentPrintln("Width = ", n.Width) + IndentPrintln("Height = ", n.Height) -func (b *GuiBox) SetTitle(title string) { - log.Println("DID IT!", title) - if b.Window == nil { - return - } - if b.Window.UiWindow == nil { - return + if (n.parent == nil) { + IndentPrintln("parent = nil") + } else { + IndentPrintln("parent =", n.parent.id) } - b.Window.UiWindow.SetTitle(title) - return -} - -func (w *GuiWindow) SetNode(n *Node) { - if (w.node != nil) { - w.Dump() - panic("gui.SetNode() Error not nil") + if (n.children != nil) { + IndentPrintln("children = ", n.children) } - w.node = n - if (w.node == nil) { - w.Dump() - panic("gui.SetNode() node == nil") + if (n.toolkit != nil) { + IndentPrintln("toolkit = ", n.toolkit) + n.toolkit.Dump() } -} - -func (b *GuiBox) SetNode(n *Node) { - if (b.node != nil) { - b.Dump() - panic("gui.SetNode() Error not nil") + if (n.custom != nil) { + IndentPrintln("custom = ", n.custom) } - b.node = n - if (b.node == nil) { - b.Dump() - panic("gui.SetNode() node == nil") + if (n.OnChanged != nil) { + IndentPrintln("OnChanged = ", n.OnChanged) } -} - -func (b *GuiBox) Append(child ui.Control, x bool) { - if b.UiBox == nil { - // spew.Dump(b) - b.Dump() - panic("GuiBox.Append() can't work. UiBox == nil") - return + if (n.id == "") { + // Node structs should never have a nil id. + // I probably shouldn't panic here, but this is just to check the sanity of + // the gui package to make sure it's not exiting + panic("gui.Node.Dump() id == nil TODO: make a unigue id here in the golang gui library") } - b.UiBox.Append(child, x) -} - -// Note: every mouse click is handled -// as a 'Button' regardless of where -// the user clicks it. You could probably -// call this 'GuiMouseClick' -type GuiButton struct { - Name string // field for human readable name - Box *GuiBox // what box the button click was in - - // a callback function for the main application - Custom func(*GuiButton) - Values interface{} - Color color.RGBA - - // andlabs/ui abstraction mapping - B *ui.Button - FB *ui.FontButton - CB *ui.ColorButton } -// text entry fields -type GuiEntry struct { - Name string // field for human readable name - Edit bool - Last string // the last value - Normalize func(string) string // function to 'normalize' the data - - B *GuiButton - Box *GuiBox - - // andlabs/ui abstraction mapping - UiEntry *ui.Entry +func (n *Node) SetName(name string) { + n.toolkit.SetWindowTitle(name) + return } -// -// AREA STRUCTURES START -// AREA STRUCTURES START -// AREA STRUCTURES START -// -type GuiArea struct { - Button *GuiButton // what button handles mouse events - Box *GuiBox - - UiAttrstr *ui.AttributedString - UiArea *ui.Area +func (n *Node) Append(child *Node) { + n.children = append(n.children, child) + if (Config.Debug) { + log.Println("child node:") + child.Dump() + log.Println("parent node:") + n.Dump() + } + // time.Sleep(3 * time.Second) } -type FontString struct { - S string - Size int - F font.Face - W font.Weight +/* +func (n *Node) List() { + findByIdDFS(n, "test") } +*/ -// -// AREA STRUCTURES END -// AREA STRUCTURES END -// AREA STRUCTURES END -// - -// -// TABLE DATA STRUCTURES START -// TABLE DATA STRUCTURES START -// TABLE DATA STRUCTURES START -// - -// -// This is the structure that andlabs/ui uses to pass information -// to the GUI. This is the "authoritative" data. -// -type TableData struct { - RowCount int // This is the number of 'rows' which really means data elements not what the human sees - RowWidth int // This is how wide each row is - Rows []RowData // This is all the table data by row - generatedColumnTypes []ui.TableValue // generate this dynamically - - Cells [20]CellData - Human [20]HumanMap +var listChildrenParent *Node +var listChildrenDepth int = 0 +var defaultPadding = " " - Box *GuiBox - - lastRow int - lastColumn int +func IndentPrintln(a ...interface{}) { + indentPrintln(listChildrenDepth, defaultPadding, a) } -// -// This maps the andlabs/ui & libui components into a "human" -// readable cell reference list. The reason is that there -// are potentially 3 values for each cell. The Text, the Color -// and an image. These are not always needed so the number -// of fields varies between 1 and 3. Internally, the toolkit -// GUI abstraction needs to list all of them, but it's then -// hard to figure out which column goes with the columns that -// you see when you visually are looking at it like a spreadsheet -// -// This makes a map so that we can say "give me the value at -// row 4 and column 2" and find the fields that are needed -// -// TODO: re-add images and the progress bar (works in andlabs/ui) -// -type HumanCellData struct { - Name string // what kind of row is this? - Text string - TextID int - Color color.RGBA - ColorID int - Button *GuiButton -} - -type HumanMap struct { - Name string // what kind of row is this? - TextID int - ColorID int -} +func indentPrintln(depth int, format string, a ...interface{}) { + var tabs string + for i := 0; i < depth; i++ { + tabs = tabs + format + } -type TableColumnData struct { - Index int - CellType string - Heading string - Color string + // newFormat := tabs + strconv.Itoa(depth) + " " + format + newFormat := tabs + format + log.Println(newFormat, a) } -type CellData struct { - Index int - HumanID int - Name string // what type of cell is this? -} +func (n *Node) ListChildren(dump bool) { + indentPrintln(listChildrenDepth, defaultPadding, n.id, n.Width, n.Height, n.Name) -// hmm. will this stand the test of time? -type RowData struct { - Name string // what kind of row is this? - Status string // status of the row? - /* - // TODO: These may or may not be implementable - // depending on if it's possible to detect the bgcolor or what row is selected - click func() // what function to call if the user clicks on it - doubleclick func() // what function to call if the user double clicks on it - */ - HumanData [20]HumanCellData + if (dump == true) { + n.Dump() + } + if len(n.children) == 0 { + if (n.parent == nil) { + } else { + if (Config.DebugNode) { + log.Println("\t\t\tparent =",n.parent.id) + } + if (listChildrenParent != nil) { + if (Config.DebugNode) { + log.Println("\t\t\tlistChildrenParent =",listChildrenParent.id) + } + if (listChildrenParent.id != n.parent.id) { + log.Println("parent.child does not match child.parent") + panic("parent.child does not match child.parent") + } + } + } + if (Config.DebugNode) { + log.Println("\t\t", n.id, "has no children") + } + return + } + for _, child := range n.children { + // log.Println("\t\t", child.id, child.Width, child.Height, child.Name) + if (child.parent != nil) { + if (Config.DebugNode) { + log.Println("\t\t\tparent =",child.parent.id) + } + } else { + log.Println("\t\t\tno parent") + panic("no parent") + } + if (dump == true) { + child.Dump() + } + if (Config.DebugNode) { + if (child.children == nil) { + log.Println("\t\t", child.id, "has no children") + } else { + log.Println("\t\t\tHas children:", child.children) + } + } + listChildrenParent = n + listChildrenDepth += 1 + child.ListChildren(dump) + listChildrenDepth -= 1 + } + return } - -// -// TABLE DATA STRUCTURES END -// -- cgit v1.2.3