very bare bones layout selectable menu
This commit is contained in:
+23
-1
@@ -8,6 +8,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
helper "esdi/helpers"
|
helper "esdi/helpers"
|
||||||
"esdi/peripheral/communication"
|
"esdi/peripheral/communication"
|
||||||
@@ -298,7 +299,7 @@ func (d *CDashDisplay) ResizeWindow(wID int16, delta *helper.Vector) error {
|
|||||||
|
|
||||||
func (d *CDashDisplay) SaveLayout(outputPath string) error {
|
func (d *CDashDisplay) SaveLayout(outputPath string) error {
|
||||||
file, err := os.OpenFile(path.Join(layoutsDir, outputPath),
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -339,6 +340,27 @@ func (d *CDashDisplay) LoadLayout(layoutName string) error {
|
|||||||
return nil
|
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) {
|
func (d *CDashDisplay) SendData(data *telemetry.TelemetryData) {
|
||||||
packet := data.Pack()
|
packet := data.Pack()
|
||||||
|
|
||||||
|
|||||||
Executable
+193
@@ -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
|
||||||
@@ -2,6 +2,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"esdi/cdashdisplay"
|
"esdi/cdashdisplay"
|
||||||
@@ -20,6 +21,7 @@ type LayoutController struct {
|
|||||||
Messages chan string
|
Messages chan string
|
||||||
DevService *services.CDashService
|
DevService *services.CDashService
|
||||||
MoveToolState *windowManipState
|
MoveToolState *windowManipState
|
||||||
|
SelectedLayout string // NOTE: This should be a struct to handle its own things
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLayoutController(base *Controller, service *services.CDashService) *LayoutController {
|
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),
|
Messages: make(chan string, 10),
|
||||||
DevService: service,
|
DevService: service,
|
||||||
MoveToolState: &windowManipState{Mode: moveMode},
|
MoveToolState: &windowManipState{Mode: moveMode},
|
||||||
|
SelectedLayout: "layout.yaml",
|
||||||
}
|
}
|
||||||
|
|
||||||
lc.registerHooks()
|
lc.registerHooks()
|
||||||
@@ -56,6 +59,9 @@ func (lc *LayoutController) registerHooks() {
|
|||||||
case 'm':
|
case 'm':
|
||||||
lc.Messages <- "calling window manipulation tool\n"
|
lc.Messages <- "calling window manipulation tool\n"
|
||||||
lc.moveWindow()
|
lc.moveWindow()
|
||||||
|
case 'c':
|
||||||
|
lc.Messages <- "calling change selected layout tool\n"
|
||||||
|
lc.changeSelectedLoadout()
|
||||||
case 's':
|
case 's':
|
||||||
// Save the current layout
|
// Save the current layout
|
||||||
lc.Messages <- "calling save layout\n"
|
lc.Messages <- "calling save layout\n"
|
||||||
@@ -64,6 +70,10 @@ func (lc *LayoutController) registerHooks() {
|
|||||||
// Load the layout
|
// Load the layout
|
||||||
lc.Messages <- "calling load layout\n"
|
lc.Messages <- "calling load layout\n"
|
||||||
lc.loadLayout()
|
lc.loadLayout()
|
||||||
|
case 'u':
|
||||||
|
// Unload the layout
|
||||||
|
lc.Messages <- "calling unload layout\n"
|
||||||
|
lc.unloadLayout()
|
||||||
case 'g':
|
case 'g':
|
||||||
// Go -> launches the current set up source
|
// Go -> launches the current set up source
|
||||||
// streamingWindow(bus, doc)
|
// streamingWindow(bus, doc)
|
||||||
@@ -339,7 +349,7 @@ func (lc *LayoutController) getCurrentTreeNodeModel() (*tview.TreeNode, int16, e
|
|||||||
|
|
||||||
func (lc *LayoutController) loadLayout() {
|
func (lc *LayoutController) loadLayout() {
|
||||||
// We would get the layout path from somewhere but for nots its layout.yaml
|
// 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 {
|
if err != nil {
|
||||||
lc.Messages <- "failed to load layout: " + err.Error()
|
lc.Messages <- "failed to load layout: " + err.Error()
|
||||||
return
|
return
|
||||||
@@ -348,8 +358,16 @@ func (lc *LayoutController) loadLayout() {
|
|||||||
lc.displayLoadedLayouts()
|
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() {
|
func (lc *LayoutController) saveLayout() {
|
||||||
err := lc.DevService.SaveLayout("layout.yaml")
|
err := lc.DevService.SaveLayout(lc.SelectedLayout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lc.Messages <- "failed to save layout: " + err.Error()
|
lc.Messages <- "failed to save layout: " + err.Error()
|
||||||
return
|
return
|
||||||
@@ -423,3 +441,50 @@ func (lc *LayoutController) moveWindow() {
|
|||||||
|
|
||||||
lc.App.SetFocus(formView.Form)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"esdi/cdashdisplay"
|
"esdi/cdashdisplay"
|
||||||
helper "esdi/helpers"
|
helper "esdi/helpers"
|
||||||
"esdi/peripheral"
|
"esdi/peripheral"
|
||||||
"esdi/telemetry"
|
"esdi/telemetry"
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CDashService struct {
|
type CDashService struct {
|
||||||
@@ -65,6 +66,10 @@ func (cds *CDashService) SaveLayout(layoutPath string) error {
|
|||||||
return cds.CDash.SaveLayout(layoutPath)
|
return cds.CDash.SaveLayout(layoutPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cds *CDashService) UnloadLayout() error {
|
||||||
|
return cds.CDash.UnloadLayout()
|
||||||
|
}
|
||||||
|
|
||||||
func (cds *CDashService) UpdateWindow(win *cdashdisplay.DesktopUIWindow) error {
|
func (cds *CDashService) UpdateWindow(win *cdashdisplay.DesktopUIWindow) error {
|
||||||
cds.Messages <- fmt.Sprintf("Updating a window:\n%+v\n", win)
|
cds.Messages <- fmt.Sprintf("Updating a window:\n%+v\n", win)
|
||||||
return cds.CDash.UpdateWindow(win)
|
return cds.CDash.UpdateWindow(win)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
LayoutToolTreeViewTitle = "Layout Tree"
|
||||||
LayoutToolFlexID = "layout-tool-flex"
|
LayoutToolFlexID = "layout-tool-flex"
|
||||||
LayoutToolTreeID = "layout-tool-tree"
|
LayoutToolTreeID = "layout-tool-tree"
|
||||||
LayoutToolActionPagesID = "layout-tool-action-pages"
|
LayoutToolActionPagesID = "layout-tool-action-pages"
|
||||||
@@ -30,7 +31,7 @@ func NewLayoutTreeView() *LayoutTreeView {
|
|||||||
|
|
||||||
view.Tree = tview.NewTreeView()
|
view.Tree = tview.NewTreeView()
|
||||||
|
|
||||||
view.Tree.SetBorder(true).SetTitle("Layout Tree")
|
view.Tree.SetBorder(true).SetTitle(LayoutToolTreeViewTitle)
|
||||||
view.Tree.SetInputCapture(view.InputCapture)
|
view.Tree.SetInputCapture(view.InputCapture)
|
||||||
// Inject ChangedFunc
|
// Inject ChangedFunc
|
||||||
// Inject SelectedFunc
|
// Inject SelectedFunc
|
||||||
@@ -61,6 +62,7 @@ func (lt *LayoutTreeView) AddWindow(win *cdashdisplay.DesktopUIWindow) error {
|
|||||||
type LayoutToolActionView struct {
|
type LayoutToolActionView struct {
|
||||||
Pages *tview.Pages
|
Pages *tview.Pages
|
||||||
CreateWindowView *CreateWindowFormView
|
CreateWindowView *CreateWindowFormView
|
||||||
|
LayoutSelection *LayoutToolLayoutsList
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLayoutToolActionView() *LayoutToolActionView {
|
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) {
|
func (ltv *LayoutToolView) DeleteWindowByNode(node *tview.TreeNode) {
|
||||||
root := ltv.LayoutTree.Tree.GetRoot()
|
root := ltv.LayoutTree.Tree.GetRoot()
|
||||||
if root == nil {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user