very bare bones layout selectable menu

This commit is contained in:
2026-06-09 16:39:13 +01:00
parent d3c4464aa0
commit 24fea285c3
6 changed files with 351 additions and 7 deletions
+23 -1
View File
@@ -8,6 +8,7 @@ import (
"log/slog"
"os"
"path"
"time"
helper "esdi/helpers"
"esdi/peripheral/communication"
@@ -298,7 +299,7 @@ func (d *CDashDisplay) ResizeWindow(wID int16, delta *helper.Vector) error {
func (d *CDashDisplay) SaveLayout(outputPath string) error {
file, err := os.OpenFile(path.Join(layoutsDir, outputPath),
os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
if err != nil {
return err
}
@@ -339,6 +340,27 @@ func (d *CDashDisplay) LoadLayout(layoutName string) error {
return nil
}
func (d *CDashDisplay) UnloadLayout() error {
var err error
for _, w := range d.State.Layout.Windows {
pLogger.Debug(fmt.Sprintf("= Removing %d ==============================================",
w.UIData.IDX))
err = d.DestroyWindow(w.UIData.IDX)
time.Sleep(75 * time.Millisecond)
if err != nil {
pLogger.Error(fmt.Sprintf("failed to destroy window: %+v", err))
// NOTE: Add a way to handle multiple errors ?
return err
}
pLogger.Debug(fmt.Sprintf("= Removing %d ==============================================",
w.UIData.IDX))
}
return nil
}
func (d *CDashDisplay) SendData(data *telemetry.TelemetryData) {
packet := data.Pack()
+193
View File
@@ -0,0 +1,193 @@
Windows:
2:
uiwindow:
Dims:
X0: 470
Y0: 71
Width: 322
Height: 57
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 4
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "9999"
Title: Dig Tacho
uidata:
WID: 2
TelemetryField: RPM
4:
uiwindow:
Dims:
X0: 321
Y0: 195
Width: 134
Height: 80
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 7
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "234"
Title: SPEEDO
uidata:
WID: 4
TelemetryField: Speed
5:
uiwindow:
Dims:
X0: 590
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.5"
Title: Water T
uidata:
WID: 5
TelemetryField: Water Temperature
10:
uiwindow:
Dims:
X0: 470
Y0: 132
Width: 169
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "123.4"
Title: Fuel Level
uidata:
WID: 10
TelemetryField: Fuel Level
11:
uiwindow:
Dims:
X0: 321
Y0: 73
Width: 134
Height: 111
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 12
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: R
Title: Box
uidata:
WID: 11
TelemetryField: Gear
16:
uiwindow:
Dims:
X0: 694
Y0: 423
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "4.56"
Title: Oil P
uidata:
WID: 16
TelemetryField: Oil Pressure
17:
uiwindow:
Dims:
X0: 694
Y0: 367
Width: 100
Height: 50
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 2
TextSize: 3
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 2
PreviewValue: "101.4"
Title: Oil T
uidata:
WID: 17
TelemetryField: Oil Temperature
20:
uiwindow:
Dims:
X0: 142
Y0: 19
Width: 500
Height: 20
Decor:
BGColour: 4161
FGColour: 65535
TitleColour: 65535
BorderColour: 63488
TitleSize: 1
TextSize: 2
HasBorder: 1
Padding: 0
Opts:
ShowID: 0
WinType: 1
PreviewValue: "5678"
Title: TACHO
uidata:
WID: 20
TelemetryField: RPM
+67 -2
View File
@@ -2,6 +2,7 @@ package controllers
import (
"fmt"
"os"
"strconv"
"esdi/cdashdisplay"
@@ -20,6 +21,7 @@ type LayoutController struct {
Messages chan string
DevService *services.CDashService
MoveToolState *windowManipState
SelectedLayout string // NOTE: This should be a struct to handle its own things
}
func NewLayoutController(base *Controller, service *services.CDashService) *LayoutController {
@@ -29,6 +31,7 @@ func NewLayoutController(base *Controller, service *services.CDashService) *Layo
Messages: make(chan string, 10),
DevService: service,
MoveToolState: &windowManipState{Mode: moveMode},
SelectedLayout: "layout.yaml",
}
lc.registerHooks()
@@ -56,6 +59,9 @@ func (lc *LayoutController) registerHooks() {
case 'm':
lc.Messages <- "calling window manipulation tool\n"
lc.moveWindow()
case 'c':
lc.Messages <- "calling change selected layout tool\n"
lc.changeSelectedLoadout()
case 's':
// Save the current layout
lc.Messages <- "calling save layout\n"
@@ -64,6 +70,10 @@ func (lc *LayoutController) registerHooks() {
// Load the layout
lc.Messages <- "calling load layout\n"
lc.loadLayout()
case 'u':
// Unload the layout
lc.Messages <- "calling unload layout\n"
lc.unloadLayout()
case 'g':
// Go -> launches the current set up source
// streamingWindow(bus, doc)
@@ -339,7 +349,7 @@ func (lc *LayoutController) getCurrentTreeNodeModel() (*tview.TreeNode, int16, e
func (lc *LayoutController) loadLayout() {
// We would get the layout path from somewhere but for nots its layout.yaml
err := lc.DevService.LoadLayout("layout.yaml")
err := lc.DevService.LoadLayout(lc.SelectedLayout)
if err != nil {
lc.Messages <- "failed to load layout: " + err.Error()
return
@@ -348,8 +358,16 @@ func (lc *LayoutController) loadLayout() {
lc.displayLoadedLayouts()
}
func (lc *LayoutController) unloadLayout() {
err := lc.DevService.UnloadLayout()
if err != nil {
lc.Logger.Error(fmt.Sprintf("Failed to unload layout: %+v", err))
return
}
}
func (lc *LayoutController) saveLayout() {
err := lc.DevService.SaveLayout("layout.yaml")
err := lc.DevService.SaveLayout(lc.SelectedLayout)
if err != nil {
lc.Messages <- "failed to save layout: " + err.Error()
return
@@ -423,3 +441,50 @@ func (lc *LayoutController) moveWindow() {
lc.App.SetFocus(formView.Form)
}
// changeSelectedLoadout is used to change the currently selected loadout
// its a very bare bones utility to aid development
// NOTE: FOR THE LOVE OF WHATS HOLY ACTUALLY MAKE THIS GOOD BEFORE ADDING MORE FEATURES
func (lc *LayoutController) changeSelectedLoadout() {
// First we get the files in the layouts directory
entries, err := os.ReadDir("./layouts")
if err != nil {
lc.Logger.Error(fmt.Sprintf("Failed to read directory: %v", err))
return
}
// Then we open a modal and pass that list there
lc.Logger.Debug(fmt.Sprintf("Found layouts: %+v", entries))
layoutSelection := views.NewLayoutToolLayoutsList()
layoutSelection.AddEntries(entries)
layoutSelection.Tree.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey {
switch ev.Key() {
case tcell.KeyESC:
// We have to destroy this window, but first we tell the pages to show something else
// NOTE: will this be left hanging and then given to GC? Can we improve that?
lc.LayoutToolView.LayoutActions.LayoutSelection = nil
lc.LayoutToolView.DeleteLayoutList()
lc.App.SetFocus(lc.LayoutToolView.LayoutTree.Tree)
}
return ev
})
layoutSelection.Tree.SetSelectedFunc(func(node *tview.TreeNode) {
ref := node.GetReference()
if ref == nil {
return
}
lc.Messages <- "User selected " + fmt.Sprintf("%+v", ref) + "\n"
lc.SelectedLayout = ref.(string)
lc.LayoutToolView.LayoutTree.Tree.SetTitle(fmt.Sprintf("%s [%s]",
views.LayoutToolTreeViewTitle, ref.(string)))
})
lc.LayoutToolView.LayoutActions.LayoutSelection = layoutSelection
lc.LayoutToolView.ShowLayoutList()
lc.App.SetFocus(lc.LayoutToolView.LayoutActions.LayoutSelection.Tree)
}
+8 -3
View File
@@ -1,13 +1,14 @@
package services
import (
"fmt"
"log/slog"
"sync/atomic"
"esdi/cdashdisplay"
helper "esdi/helpers"
"esdi/peripheral"
"esdi/telemetry"
"fmt"
"log/slog"
"sync/atomic"
)
type CDashService struct {
@@ -65,6 +66,10 @@ func (cds *CDashService) SaveLayout(layoutPath string) error {
return cds.CDash.SaveLayout(layoutPath)
}
func (cds *CDashService) UnloadLayout() error {
return cds.CDash.UnloadLayout()
}
func (cds *CDashService) UpdateWindow(win *cdashdisplay.DesktopUIWindow) error {
cds.Messages <- fmt.Sprintf("Updating a window:\n%+v\n", win)
return cds.CDash.UpdateWindow(win)
+15 -1
View File
@@ -12,6 +12,7 @@ import (
)
const (
LayoutToolTreeViewTitle = "Layout Tree"
LayoutToolFlexID = "layout-tool-flex"
LayoutToolTreeID = "layout-tool-tree"
LayoutToolActionPagesID = "layout-tool-action-pages"
@@ -30,7 +31,7 @@ func NewLayoutTreeView() *LayoutTreeView {
view.Tree = tview.NewTreeView()
view.Tree.SetBorder(true).SetTitle("Layout Tree")
view.Tree.SetBorder(true).SetTitle(LayoutToolTreeViewTitle)
view.Tree.SetInputCapture(view.InputCapture)
// Inject ChangedFunc
// Inject SelectedFunc
@@ -61,6 +62,7 @@ func (lt *LayoutTreeView) AddWindow(win *cdashdisplay.DesktopUIWindow) error {
type LayoutToolActionView struct {
Pages *tview.Pages
CreateWindowView *CreateWindowFormView
LayoutSelection *LayoutToolLayoutsList
}
func NewLayoutToolActionView() *LayoutToolActionView {
@@ -154,6 +156,18 @@ func (ltv *LayoutToolView) ShowWindowFormByID(idx int16) {
)
}
func (ltv *LayoutToolView) ShowLayoutList() {
AddAndShowPage(
ltv.LayoutActions.Pages,
"available-layouts-list",
ltv.LayoutActions.LayoutSelection.Tree,
)
}
func (ltv *LayoutToolView) DeleteLayoutList() {
ltv.LayoutActions.Pages.RemovePage("available-layouts-list")
}
func (ltv *LayoutToolView) DeleteWindowByNode(node *tview.TreeNode) {
root := ltv.LayoutTree.Tree.GetRoot()
if root == nil {
@@ -0,0 +1,45 @@
package views
import (
"os"
"github.com/rivo/tview"
)
type LayoutToolLayoutsList struct {
Tree *tview.TreeView
}
func NewLayoutToolLayoutsList() *LayoutToolLayoutsList {
view := &LayoutToolLayoutsList{}
view.Tree = tview.NewTreeView()
view.Tree.SetBorder(true).SetTitle("Layout List")
// Inject InputCapture
// Inject ChangedFunc
// Inject SelectedFunc
// Create a root element
rootElem := tview.NewTreeNode(".")
view.Tree.SetRoot(rootElem).SetCurrentNode(rootElem)
return view
}
// AddEntries adds the list of files we wish the user to select from
// For now we will send just a list of []string but in the future this should
// receive (or have an alternative to receive) a directory structure
func (ltll *LayoutToolLayoutsList) AddEntries(entries []os.DirEntry) {
root := ltll.Tree.GetRoot()
if root == nil {
// NOTE: shouldn't happen at all
return
}
for _, e := range entries {
node := tview.NewTreeNode(e.Name()).
SetReference(e.Name())
root.AddChild(node)
}
}