Files
esdi/tui/internal/views/layoutTool.go
T
esilva 6a37d7e97c Got a bunch of stuff going on here
the main is thing is that I hammered one a way of showing data from an
ibt
2026-02-20 21:51:57 +00:00

336 lines
8.2 KiB
Go

package views
import (
"esdi/cdashdisplay"
"esdi/tui/internal/dom"
"esdi/tui/internal/events"
"esdi/tui/internal/ui"
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
const (
layoutToolFlexID = "layout-tool-flex"
layoutToolTreeID = "layout-tool-tree"
layoutToolActionPagesID = "layout-tool-action-pages"
)
type windowReference struct {
ID int16
Form *dom.UINode
}
func getWindowRefFromNode(node *tview.TreeNode) *windowReference {
ref := node.GetReference()
wRef, ok := ref.(*windowReference)
if !ok {
return nil
}
return wRef
}
func FindNodeByID(
node *tview.TreeNode,
id int16,
) *tview.TreeNode {
if node == nil {
return nil
}
if ref, ok := node.GetReference().(*windowReference); ok {
if ref.ID == id {
return node
}
}
for _, child := range node.GetChildren() {
if found := FindNodeByID(child, id); found != nil {
return found
}
}
return nil
}
func appendWindow(bus *events.Bus, doc *dom.DOM, tree *tview.TreeView, idx int16,
win *cdashdisplay.UIWindow) {
root := tree.GetRoot()
if root == nil {
bus.Emit(ui.LogEv{Log: "unable to get root of tree view"})
return
}
// Create the update tool
updateForm := windowInfoForm(bus, doc, idx, win)
fmtTitle := fmt.Sprintf("%s [%2d]", win.Title.String(), idx)
ref := windowReference{
ID: idx,
Form: updateForm,
}
newWindow := tview.NewTreeNode(fmtTitle).SetReference(&ref)
root.AddChild(newWindow)
}
func BindWindowEvents(
bus *events.Bus,
doc *dom.DOM,
tree *tview.TreeView,
) {
// I recon I have to change this for some type os event system that
// triggers directly on the UINodes I want them to be triggered on
bus.On(ui.WindowCreatedEv{}, func(e any) {
ev := e.(ui.WindowCreatedEv)
bus.Emit(ui.LogEv{Log: "Received a window created event\n"})
if tree == nil {
bus.Emit(ui.LogEv{Log: "tree view is nil"})
return
}
appendWindow(bus, doc, tree, ev.ID, &ev.Win)
})
bus.On(ui.WindowDestroyedEv{}, func(e any) {
root := tree.GetRoot()
if root == nil {
bus.Emit(ui.LogEv{Log: "unable to get current tree node"})
}
node := FindNodeByID(root, e.(ui.WindowDestroyedEv).ID)
if node != nil {
root.RemoveChild(node)
}
// NODE: add a log here in case it fails so we know whats going on
bus.Emit(ui.ForceRedraw{})
})
bus.On(ui.RegisterLoadedLayout{}, func(e any) {
root := tree.GetRoot()
if root == nil {
return
}
layout := e.(ui.RegisterLoadedLayout)
for idx, w := range layout.Layout.Windows {
appendWindow(bus, doc, tree, idx, w)
}
// bus.Emit(ui.ForceRedraw{})
})
bus.On(ui.ErrorCreateWindowEv{}, func(e any) {
go func() {
bus.Emit(ui.LogEv{
Log: fmt.Sprintf(
"Error performing action: %s\n", e.(ui.ErrorCreateWindowEv).Error.Error(),
)})
}()
})
}
func getCurNodeRef(tree *tview.TreeView) (*windowReference, error) {
curNode := tree.GetCurrentNode()
if curNode == nil {
return nil, fmt.Errorf("unable to get current tree node")
}
winRef := getWindowRefFromNode(curNode)
if winRef == nil {
return nil, fmt.Errorf("unable to get window reference from tree node")
}
return winRef, nil
}
func layoutToolTreeViewEvCapture(bus *events.Bus, doc *dom.DOM,
tree *tview.TreeView) func(event *tcell.EventKey) *tcell.EventKey {
return func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyEsc:
bus.Emit(ui.ChangeFocusEv{Target: doc.GetElemByID(deviceAPIListID)})
}
switch event.Rune() {
case 'n':
// Create a new window
bus.Emit(ui.LogEv{Log: "calling new window form\n"})
createNewWindowForm(bus, doc)
case 'x':
// Delete selected window
bus.Emit(ui.LogEv{Log: "calling delete window\n"})
wRef, err := getCurNodeRef(tree)
if err != nil {
bus.Emit(ui.LogEv{Log: " -> " + err.Error()})
break
}
bus.Emit(ui.DestroyWindowEv{ID: wRef.ID})
case 'm':
// Go into move mode
bus.Emit(ui.LogEv{Log: "calling window manipulation tool\n"})
wRef, err := getCurNodeRef(tree)
if err != nil {
bus.Emit(ui.LogEv{Log: " -> " + err.Error()})
break
}
windowManipulationTool(bus, doc, wRef.ID)
case 'e':
// Go into edit mode
case 's':
// Save the current layout
bus.Emit(ui.SaveLayoutEv{})
case 'l':
// Load the layout
bus.Emit(ui.LoadLayoutEv{})
case 'g':
// Go -> launches the current set up source
streamingWindow(bus, doc)
}
return event
}
}
func layoutToolUIOnSelect(bus *events.Bus, doc *dom.DOM) {
bus.Emit(ui.LogEv{Log: "Opening layout tool UI\n"})
var err error
// Get the api pages
apiToolPages := doc.GetElemByID(apiToolPagesID).(*tview.Pages)
if apiToolPages == nil {
bus.Emit(ui.LogEv{Log: " Failed to retrive apiToolPages UI\n"})
return
}
bus.Emit(ui.LogEv{
Log: fmt.Sprintf(" Checking if `%s` UINode already exists\n", layoutToolFlexID),
})
layoutToolUINode := doc.GetNodeByID(layoutToolFlexID)
if layoutToolUINode == nil {
layoutToolUINode, err = buildLayoutFlexComponent(bus, doc)
if err != nil {
bus.Emit(ui.LogEv{
Log: fmt.Sprintf(" Failed to build layout tool UI: %s\n", err.Error()),
})
}
}
AddAndShowPage(bus, doc, apiToolPages, layoutToolUINode, true)
}
func layoutToolTreeViewOnChange(bus *events.Bus, doc *dom.DOM) func(node *tview.TreeNode) {
return func(node *tview.TreeNode) {
// Here we want to change the current existing form on the action pages
nodeRef := node.GetReference()
if nodeRef == nil {
// its probably root I guess, thats what I'll believe
return
}
nodeWinRef := nodeRef.(*windowReference)
bus.Emit(ui.LogEv{
Log: fmt.Sprintf("changing to -> %d | %s\n", nodeWinRef.ID, nodeWinRef.Form.ID),
})
pages := doc.GetElemByID(layoutToolActionPagesID)
AddAndShowPage(bus, doc, pages.(*tview.Pages), nodeWinRef.Form, false)
}
}
func layoutToolTreeViewOnSelect(bus *events.Bus) func(node *tview.TreeNode) {
return func(node *tview.TreeNode) {
// Get the window reference which will have the ID for the form
bus.Emit(ui.LogEv{Log: "PRESSED ENTER\n"})
ref := node.GetReference()
if ref == nil {
bus.Emit(ui.LogEv{Log: "Couldn't find reference for node\n"})
return
}
nodeWinRef := ref.(*windowReference)
bus.Emit(ui.ChangeFocusEv{Target: nodeWinRef.Form.Self})
}
}
func buildLayoutTreeComponent(bus *events.Bus, doc *dom.DOM) (*dom.UINode, error) {
layoutTree := tview.NewTreeView()
layoutTree.SetBorder(true).SetTitle("Layout Tree").
SetInputCapture(layoutToolTreeViewEvCapture(bus, doc, layoutTree))
layoutTree.SetChangedFunc(layoutToolTreeViewOnChange(bus, doc))
layoutTree.SetSelectedFunc(layoutToolTreeViewOnSelect(bus))
layoutTreeUINode, err := doc.NewUINode(layoutToolTreeID,
doc.GetElemByID(rightFlexID), layoutTree)
if err != nil {
return nil, err
}
// Bind the events for the treeview
BindWindowEvents(bus, doc, layoutTreeUINode.Self.(*tview.TreeView))
// Create a root element
rootElem := tview.NewTreeNode(".")
layoutTree.SetRoot(rootElem).SetCurrentNode(rootElem)
return layoutTreeUINode, nil
}
func buildLayoutActionPagesComponent(bus *events.Bus, doc *dom.DOM) (*dom.UINode, error) {
// Create an empty page for it
emptyPage := tview.NewTextView().
SetTextAlign(tview.AlignCenter).
SetChangedFunc(func() {
bus.Emit(ui.RedrawEv{})
})
emptyPage.SetBorder(true).SetTitle("-- Tool Area --")
fmt.Fprintf(emptyPage, "No tool selected")
actionPages := tview.NewPages().AddPage(emptyPageName, emptyPage, true, true)
actionPagesUINode, err := doc.NewUINode(layoutToolActionPagesID,
doc.GetElemByID("right-flex"), actionPages)
if err != nil {
return nil, err
}
return actionPagesUINode, nil
}
func buildLayoutFlexComponent(bus *events.Bus, doc *dom.DOM) (*dom.UINode, error) {
// Want a TreeView at the top
layoutTreeUINode, err := buildLayoutTreeComponent(bus, doc)
if err != nil {
return nil, err
}
// Want pages below to run actions
actionPagesUINode, err := buildLayoutActionPagesComponent(bus, doc)
if err != nil {
return nil, err
}
layoutToolFlex := tview.NewFlex().SetDirection(tview.FlexRow)
layoutToolFlexNode, err := doc.NewUINode(layoutToolFlexID,
doc.GetElemByID(apiToolPagesID), layoutToolFlex)
if err != nil {
return nil, err
}
layoutToolFlex.
AddItem(layoutTreeUINode.Self, 0, 2, true).
AddItem(actionPagesUINode.Self, 0, 5, false)
return layoutToolFlexNode, nil
}