mirror of
https://github.com/wagoodman/dive
synced 2026-03-14 22:35:50 +01:00
add filterview & update filetree drawing
Signed-off-by: dwillist <dthornton@vmware.com>
This commit is contained in:
parent
da7020ad87
commit
e2dbdcd3e7
7 changed files with 368 additions and 71 deletions
|
|
@ -52,9 +52,54 @@ type renderParams struct {
|
|||
isLast bool
|
||||
}
|
||||
|
||||
// renderStringTreeBetween returns a string representing the given tree between the given rows. Since each node
|
||||
// is rendered on its own line, the returned string shows the visible nodes not affected by a collapsed parent.
|
||||
func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttributes bool) string {
|
||||
renderTree := NewFileTree()
|
||||
nodeCount := 0
|
||||
visitFunc := func(curNode *FileNode) error {
|
||||
nodeCount++
|
||||
node, _, err := renderTree.AddPath(curNode.Path(), FileInfo{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node.Data = *curNode.Data.Copy()
|
||||
|
||||
|
||||
if len(curNode.Children) == 0 && curNode.Data.ViewInfo.Collapsed {
|
||||
node.Data.ViewInfo.Collapsed = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
evaluatorFunc := func(curNode *FileNode) bool {
|
||||
switch {
|
||||
case curNode == nil:
|
||||
return false
|
||||
case nodeCount > stopRow:
|
||||
return false
|
||||
case curNode.Data.ViewInfo.Hidden:
|
||||
return false
|
||||
case curNode.Parent == nil: // should only be true for the root node
|
||||
return true
|
||||
case curNode.Parent.Data.ViewInfo.Collapsed || curNode.Parent.Data.ViewInfo.Hidden:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
err := tree.VisitDepthChildFirst(visitFunc, evaluatorFunc)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return renderTree.constructStringBetween(startRow, stopRow, showAttributes)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// renderStringTreeBetween returns a string representing the given tree between the given rows. Since each node
|
||||
// is rendered on its own line, the returned string shows the visible nodes not affected by a collapsed parent.
|
||||
func (tree *FileTree) constructStringBetween(startRow, stopRow int, showAttributes bool) string {
|
||||
// generate a list of nodes to render
|
||||
var params = make([]renderParams, 0)
|
||||
var result string
|
||||
|
|
@ -84,7 +129,7 @@ func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttribu
|
|||
|
||||
// visit this node...
|
||||
isLast := idx == (len(currentParams.node.Children) - 1)
|
||||
showCollapsed := child.Data.ViewInfo.Collapsed && len(child.Children) > 0
|
||||
showCollapsed := child.Data.ViewInfo.Collapsed
|
||||
|
||||
// completely copy the reference slice
|
||||
childSpaces := make([]bool, len(currentParams.childSpaces))
|
||||
|
|
|
|||
|
|
@ -57,7 +57,24 @@ func TestStringCollapsed(t *testing.T) {
|
|||
if expected != actual {
|
||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringHidden(t *testing.T) {
|
||||
tree := NewFileTree()
|
||||
tree.Root.AddChild("1 node!", FileInfo{})
|
||||
tree.Root.AddChild("2 node!", FileInfo{})
|
||||
three := tree.Root.AddChild("3 node!", FileInfo{})
|
||||
three.Data.ViewInfo.Hidden = true
|
||||
|
||||
expected :=
|
||||
`├── 1 node!
|
||||
└── 2 node!
|
||||
`
|
||||
actual := tree.String(false)
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
|
|
@ -113,9 +130,9 @@ func TestStringBetween(t *testing.T) {
|
|||
}
|
||||
|
||||
expected :=
|
||||
`│ └── public
|
||||
├── tmp
|
||||
│ └── nonsense
|
||||
`│ └── public
|
||||
└── tmp
|
||||
└── nonsense
|
||||
`
|
||||
actual := tree.StringBetween(3, 5, false)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wagoodman/dive/dive/filetree"
|
||||
"github.com/wagoodman/dive/dive/image"
|
||||
"github.com/wagoodman/dive/runtime/ui/components"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
|
@ -21,15 +26,36 @@ type diveApp struct {
|
|||
app *tview.Application
|
||||
layers *components.LayerList
|
||||
fileTree *components.TreeView
|
||||
finderFocus tview.Primitive
|
||||
}
|
||||
|
||||
//type Cache interface {
|
||||
// GetTree(key filetree.TreeIndexKey) (*filetree.FileTree, error)
|
||||
//}
|
||||
|
||||
|
||||
//func updateFileTree()
|
||||
//
|
||||
//func NewLayerListHandler(cache filetree.Comparer, analysis image.AnalysisResult,layerDetails tview.TextView) components.LayerListHandler {
|
||||
// return func(i int, stringer fmt.Stringer, r rune) {
|
||||
// bottomStart := intMax(0,i-1) // no values less than zero
|
||||
// bottomStop := intMax(0, i-1)
|
||||
// curTreeIndex := filetree.NewTreeIndexKey(bottomStart,bottomStop,i,i)
|
||||
// curTree, err := cache.GetTree(curTreeIndex)
|
||||
// layerDetails.SetText(components.LayerDetailsText(analysis.Layers[i]))
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// fileTreeView.SetTree(curTree)
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetree.Comparer) (*diveApp, error) {
|
||||
var err error
|
||||
once.Do(func() {
|
||||
|
||||
layersView := components.NewLayerList([]string{})
|
||||
layersView := components.NewLayerList(nil)
|
||||
layersView.SetSubtitle("Cmp Size Command").SetBorder(true).SetTitle("Layers")
|
||||
|
||||
curTreeIndex := filetree.NewTreeIndexKey(0,0,0,0)
|
||||
|
|
@ -47,81 +73,107 @@ func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetr
|
|||
layerDetails.SetText(components.LayerDetailsText(analysis.Layers[0]))
|
||||
|
||||
for _, layer := range analysis.Layers {
|
||||
layersView.AddItem(layer.String()).SetChangedFunc(func(i int, s string, r rune) {
|
||||
bottomStart := intMax(0,i-1) // no values less than zero
|
||||
bottomStop := intMax(0, i-1)
|
||||
curTreeIndex := filetree.NewTreeIndexKey(bottomStart,bottomStop,i,i)
|
||||
curTree, err = cache.GetTree(curTreeIndex)
|
||||
layerDetailText := components.LayerDetailsText(analysis.Layers[i])
|
||||
layerDetails.SetText(layerDetailText)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fileTreeView.SetTree(curTree)
|
||||
})
|
||||
layersView.AddItem(layer)
|
||||
}
|
||||
layersView.SetChangedFunc(func(i int, stringer fmt.Stringer, r rune) {
|
||||
bottomStart := intMax(0,i-1) // no values less than zero
|
||||
bottomStop := intMax(0, i-1)
|
||||
curTreeIndex := filetree.NewTreeIndexKey(bottomStart,bottomStop,i,i)
|
||||
curTree, err = cache.GetTree(curTreeIndex)
|
||||
layerDetailText := components.LayerDetailsText(analysis.Layers[i])
|
||||
layerDetails.SetText(layerDetailText)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fileTreeView.SetTree(curTree)
|
||||
})
|
||||
|
||||
imageDetails := components.NewImageDetailsView(analysis)
|
||||
grid := tview.NewGrid()
|
||||
filterView := components.NewFilterView()
|
||||
filterView.SetChangedFunc(
|
||||
func(textToCheck string) {
|
||||
var filterRegex *regexp.Regexp = nil
|
||||
var err error
|
||||
|
||||
grid := tview.NewGrid().SetRows(-4,-1,-1)
|
||||
if len(textToCheck) > 0 {
|
||||
filterRegex, err = regexp.Compile(textToCheck)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fileTreeView.SetFilterRegex(filterRegex)
|
||||
return
|
||||
}).SetDoneFunc(func(key tcell.Key) {
|
||||
switch {
|
||||
case key == tcell.KeyEnter:
|
||||
app.SetFocus(grid)
|
||||
}
|
||||
})
|
||||
|
||||
grid.SetRows(-4,-1,-1,1).SetColumns(-1,-1, 3)
|
||||
grid.SetBorder(false)
|
||||
grid.AddItem(layersView, 0,0,1,1,5, 10, false).
|
||||
AddItem(layerDetails,1,0,1,1,10,10, false).
|
||||
AddItem(imageDetails,2,0,1, 1,10,10,false)
|
||||
grid.AddItem(layersView, 0,0,1,1,5, 10, true).
|
||||
AddItem(layerDetails,1,0,1,1,10,40, false).
|
||||
AddItem(imageDetails,2,0,1, 1,10,10,false).
|
||||
AddItem(fileTreeView, 0, 1, 3, 1, 0,0, true).
|
||||
AddItem(filterView, 3,0,1,2,0,0,false)
|
||||
|
||||
|
||||
|
||||
flex := tview.NewFlex().
|
||||
AddItem(grid, 0, 1, true).
|
||||
AddItem(fileTreeView, 0, 1, false)
|
||||
|
||||
switchFocus := func(event *tcell.EventKey) *tcell.EventKey {
|
||||
var result *tcell.EventKey = nil
|
||||
switch event.Key() {
|
||||
case tcell.KeyTAB:
|
||||
//fmt.Println("Tab")
|
||||
if appSingleton.layers.HasFocus() {
|
||||
appSingleton.app.SetFocus(appSingleton.fileTree)
|
||||
} else {
|
||||
appSingleton.app.SetFocus(appSingleton.layers)
|
||||
}
|
||||
return nil
|
||||
case tcell.KeyCtrlF:
|
||||
if filterView.HasFocus() {
|
||||
filterView.Blur()
|
||||
appSingleton.app.SetFocus(grid)
|
||||
} else {
|
||||
appSingleton.app.SetFocus(filterView)
|
||||
}
|
||||
|
||||
default:
|
||||
return event
|
||||
result = event
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
app.SetInputCapture(switchFocus)
|
||||
grid.SetInputCapture(switchFocus)
|
||||
|
||||
app.SetRoot(flex,true).SetFocus(layersView)
|
||||
app.SetRoot(grid,true)
|
||||
appSingleton = &diveApp{
|
||||
app: app,
|
||||
fileTree: fileTreeView,
|
||||
layers: layersView,
|
||||
}
|
||||
app.SetFocus(layersView)
|
||||
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
logrus.Debugf("application handling in put %s\n", event.Name())
|
||||
return event
|
||||
})
|
||||
})
|
||||
|
||||
once.Do(func() {
|
||||
curTreeIndex := filetree.NewTreeIndexKey(0,0,0,0)
|
||||
curTree, err := cache.GetTree(curTreeIndex)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileTreeView := components.NewTreeView(curTree)
|
||||
fileTreeView.SetTitle("Files").SetBorder(true)
|
||||
app.SetRoot(fileTreeView, true).SetFocus(fileTreeView)
|
||||
appSingleton = &diveApp{
|
||||
app: app,
|
||||
fileTree: fileTreeView,
|
||||
layers: nil,
|
||||
}
|
||||
})
|
||||
return appSingleton, err
|
||||
}
|
||||
|
||||
// Run is the UI entrypoint.
|
||||
func Run(analysis *image.AnalysisResult, treeStack filetree.Comparer) error {
|
||||
debugFile := filepath.Join("/tmp", "dive","debug.out")
|
||||
LogOutputFile, _ := os.OpenFile(debugFile, os.O_RDWR | os.O_CREATE | os.O_TRUNC, 0666)
|
||||
defer LogOutputFile.Close()
|
||||
logrus.SetOutput(LogOutputFile)
|
||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.Debugln("debug start:")
|
||||
app := tview.NewApplication()
|
||||
|
||||
_, err := newApp(app, analysis, treeStack)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -11,12 +11,18 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// TODO simplify this interface.
|
||||
type TreeModel interface {
|
||||
StringBetween(int, int, bool) string
|
||||
VisitDepthParentFirst(filetree.Visitor, filetree.VisitEvaluator) error
|
||||
VisitDepthChildFirst(filetree.Visitor, filetree.VisitEvaluator) error
|
||||
RemovePath(path string) error
|
||||
VisibleSize() int
|
||||
}
|
||||
|
||||
type TreeView struct {
|
||||
*tview.Box
|
||||
// TODO: make me an interface
|
||||
|
||||
tree *filetree.FileTree
|
||||
|
||||
tree TreeModel
|
||||
|
||||
// Note that the following two fields are distinct
|
||||
// treeIndex is the index about where we are in the current fileTree
|
||||
|
|
@ -28,10 +34,12 @@ type TreeView struct {
|
|||
bufferIndexLowerBound int
|
||||
bufferIndex int
|
||||
|
||||
filterRegex *regexp.Regexp
|
||||
//changed func(index int, mainText string, shortcut rune)
|
||||
|
||||
}
|
||||
|
||||
func NewTreeView(tree *filetree.FileTree) *TreeView {
|
||||
func NewTreeView(tree TreeModel) *TreeView {
|
||||
return &TreeView{
|
||||
Box: tview.NewBox(),
|
||||
tree: tree,
|
||||
|
|
@ -46,6 +54,10 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p tv
|
|||
t.keyUp()
|
||||
case tcell.KeyDown:
|
||||
t.keyDown()
|
||||
case tcell.KeyRight:
|
||||
t.keyRight()
|
||||
case tcell.KeyLeft:
|
||||
t.keyLeft()
|
||||
}
|
||||
switch event.Rune() {
|
||||
case ' ':
|
||||
|
|
@ -55,7 +67,7 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p tv
|
|||
})
|
||||
}
|
||||
|
||||
func (t *TreeView) SetTree(newTree *filetree.FileTree) *TreeView {
|
||||
func (t *TreeView) SetTree(newTree TreeModel) *TreeView {
|
||||
// preserve collapsed nodes based on path
|
||||
collapsedList := map[string]interface{}{}
|
||||
|
||||
|
|
@ -82,11 +94,14 @@ func (t *TreeView) SetTree(newTree *filetree.FileTree) *TreeView {
|
|||
}, evaluateFunc)
|
||||
|
||||
t.tree = newTree
|
||||
if err := t.FilterUpdate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TreeView) GetTree(tree *filetree.FileTree) *filetree.FileTree {
|
||||
func (t *TreeView) GetTree() TreeModel {
|
||||
return t.tree
|
||||
}
|
||||
|
||||
|
|
@ -98,15 +113,29 @@ func (t *TreeView) HasFocus() bool {
|
|||
return t.Box.HasFocus()
|
||||
}
|
||||
|
||||
func (t *TreeView) SetFilterRegex(filterRegex *regexp.Regexp) {
|
||||
t.filterRegex = filterRegex
|
||||
if err := t.FilterUpdate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
func (t *TreeView) spaceDown() bool {
|
||||
node := t.getAbsPositionNode(nil)
|
||||
if node != nil && node.Data.FileInfo.IsDir {
|
||||
logrus.Debugf("collapsing node %s", node.Path())
|
||||
node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed
|
||||
return true
|
||||
}
|
||||
if node != nil {
|
||||
logrus.Debugf("unable to collapse node %s", node.Path())
|
||||
logrus.Debugf(" IsDir: %t", node.Data.FileInfo.IsDir)
|
||||
|
||||
} else {
|
||||
logrus.Debugf("unable to collapse nil node")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +171,6 @@ func (t *TreeView) getAbsPositionNode(filterRegex *regexp.Regexp) (node *filetre
|
|||
}
|
||||
|
||||
func (t *TreeView) keyDown() bool {
|
||||
|
||||
_, _, _, height := t.Box.GetInnerRect()
|
||||
|
||||
// treeIndex is the index about where we are in the current file
|
||||
|
|
@ -174,19 +202,137 @@ func (t *TreeView) keyUp() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// TODO add regex filtering
|
||||
func (t *TreeView) keyRight() bool {
|
||||
node := t.getAbsPositionNode(t.filterRegex)
|
||||
|
||||
_,_, _, height := t.Box.GetInnerRect()
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !node.Data.FileInfo.IsDir {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(node.Children) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if node.Data.ViewInfo.Collapsed {
|
||||
node.Data.ViewInfo.Collapsed = false
|
||||
}
|
||||
|
||||
t.treeIndex++
|
||||
if t.treeIndex > t.bufferIndexUpperBound() {
|
||||
t.bufferIndexLowerBound++
|
||||
}
|
||||
|
||||
t.bufferIndex++
|
||||
if t.bufferIndex > height {
|
||||
t.bufferIndex = height
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *TreeView) keyLeft() bool {
|
||||
var visitor func(*filetree.FileNode) error
|
||||
var evaluator func(*filetree.FileNode) bool
|
||||
var dfsCounter, newIndex int
|
||||
oldIndex := t.treeIndex
|
||||
currentNode := t.getAbsPositionNode(t.filterRegex)
|
||||
|
||||
if currentNode == nil {
|
||||
return true
|
||||
}
|
||||
parentPath := currentNode.Parent.Path()
|
||||
|
||||
visitor = func(curNode *filetree.FileNode) error {
|
||||
if strings.Compare(parentPath, curNode.Path()) == 0 {
|
||||
newIndex = dfsCounter
|
||||
}
|
||||
dfsCounter++
|
||||
return nil
|
||||
}
|
||||
|
||||
evaluator = func(curNode *filetree.FileNode) bool {
|
||||
regexMatch := true
|
||||
if t.filterRegex != nil {
|
||||
match := t.filterRegex.Find([]byte(curNode.Path()))
|
||||
regexMatch = match != nil
|
||||
}
|
||||
return !curNode.Parent.Data.ViewInfo.Collapsed && !curNode.Data.ViewInfo.Hidden && regexMatch
|
||||
}
|
||||
|
||||
err := t.tree.VisitDepthParentFirst(visitor, evaluator)
|
||||
if err != nil {
|
||||
// TODO: remove this panic
|
||||
panic(err)
|
||||
}
|
||||
|
||||
t.treeIndex = newIndex
|
||||
moveIndex := oldIndex - newIndex
|
||||
if newIndex < t.bufferIndexLowerBound {
|
||||
t.bufferIndexLowerBound = t.treeIndex
|
||||
}
|
||||
|
||||
if t.bufferIndex > moveIndex {
|
||||
t.bufferIndex -= moveIndex
|
||||
} else {
|
||||
t.bufferIndex = 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *TreeView) bufferIndexUpperBound() int {
|
||||
_,_, _, height := t.Box.GetInnerRect()
|
||||
return t.bufferIndexLowerBound + height
|
||||
|
||||
}
|
||||
|
||||
func (t *TreeView) FilterUpdate() error {
|
||||
// keep the t selection in parity with the current DiffType selection
|
||||
err := t.tree.VisitDepthChildFirst(func(node *filetree.FileNode) error {
|
||||
// TODO: add hidden datatypes.
|
||||
//node.Data.ViewInfo.Hidden = t.HiddenDiffTypes[node.Data.DiffType]
|
||||
visibleChild := false
|
||||
if t.filterRegex == nil {
|
||||
node.Data.ViewInfo.Hidden = false
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
if !child.Data.ViewInfo.Hidden {
|
||||
visibleChild = true
|
||||
node.Data.ViewInfo.Hidden = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !visibleChild { // hide nodes that do not match the current file filter regex (also don't unhide nodes that are already hidden)
|
||||
match := t.filterRegex.FindString(node.Path())
|
||||
node.Data.ViewInfo.Hidden = len(match) == 0
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to propagate t model tree: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (t *TreeView) Draw(screen tcell.Screen) {
|
||||
t.Box.Draw(screen)
|
||||
|
||||
x, y, width, height := t.Box.GetInnerRect()
|
||||
|
||||
showAttributes := width > 80
|
||||
// TODO add switch for showing attributes.
|
||||
treeString := t.tree.StringBetween(t.bufferIndexLowerBound, t.bufferIndexUpperBound(), false)
|
||||
treeString := t.tree.StringBetween(t.bufferIndexLowerBound, t.bufferIndexUpperBound(), showAttributes)
|
||||
lines := strings.Split(treeString, "\n")
|
||||
|
||||
// update the contents
|
||||
|
|
|
|||
28
runtime/ui/components/filter_primative.go
Normal file
28
runtime/ui/components/filter_primative.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type FilterView struct {
|
||||
*tview.InputField
|
||||
}
|
||||
|
||||
func NewFilterView() *FilterView {
|
||||
inputField := tview.NewInputField()
|
||||
inputField.SetBackgroundColor(tcell.ColorGray)
|
||||
inputField.SetFieldTextColor(tcell.ColorBlack)
|
||||
inputField.SetFieldBackgroundColor(tcell.ColorGray)
|
||||
//inputField.SetPlaceholderTextColor(tcell.ColorBlack)
|
||||
inputField.SetLabelColor(tcell.ColorBlack)
|
||||
inputField.SetLabel("Path Filter: ")
|
||||
//inputField.SetPlaceholder("(regex)" )
|
||||
return &FilterView{
|
||||
InputField: inputField,
|
||||
}
|
||||
}
|
||||
|
||||
func (fv *FilterView) Empty() bool {
|
||||
return fv.GetText() == ""
|
||||
}
|
||||
|
|
@ -8,9 +8,14 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
type ImageDetails struct {
|
||||
*tview.TextView
|
||||
}
|
||||
|
||||
func NewImageDetailsView(analysisResult *image.AnalysisResult) *tview.TextView {
|
||||
result := tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
result := tview.NewTextView()
|
||||
result.SetDynamicColors(true).
|
||||
SetScrollable(true)
|
||||
result.SetBorder(true).
|
||||
SetTitle("Image Details").
|
||||
|
|
|
|||
|
|
@ -9,17 +9,21 @@ import (
|
|||
type LayerList struct {
|
||||
*tview.Box
|
||||
subtitle string
|
||||
// TODO make me an interface
|
||||
layers []string
|
||||
layers []fmt.Stringer
|
||||
cmpIndex int
|
||||
changed func(index int, mainText string, shortcut rune)
|
||||
changed LayerListHandler
|
||||
selectedBackgroundColor tcell.Color
|
||||
}
|
||||
|
||||
func NewLayerList(options []string) *LayerList {
|
||||
type LayerListHandler func(index int, mainText fmt.Stringer, shortcut rune)
|
||||
|
||||
func NewLayerList(layers []fmt.Stringer) *LayerList {
|
||||
if layers == nil {
|
||||
layers = []fmt.Stringer{}
|
||||
}
|
||||
return &LayerList{
|
||||
Box: tview.NewBox(),
|
||||
layers: options,
|
||||
layers: layers,
|
||||
cmpIndex: 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +110,7 @@ func (ll *LayerList) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
})
|
||||
}
|
||||
|
||||
func (ll *LayerList) InsertItem(index int, value string) *LayerList {
|
||||
func (ll *LayerList) InsertItem(index int, value fmt.Stringer) *LayerList {
|
||||
if index < 0 {
|
||||
ll.layers = append(ll.layers, value)
|
||||
return ll
|
||||
|
|
@ -116,7 +120,7 @@ func (ll *LayerList) InsertItem(index int, value string) *LayerList {
|
|||
return ll
|
||||
}
|
||||
|
||||
func (ll *LayerList) AddItem(mainText string) *LayerList {
|
||||
func (ll *LayerList) AddItem(mainText fmt.Stringer) *LayerList {
|
||||
ll.InsertItem(-1, mainText)
|
||||
return ll
|
||||
}
|
||||
|
|
@ -129,7 +133,7 @@ func (ll *LayerList) HasFocus() bool {
|
|||
return ll.Box.HasFocus()
|
||||
}
|
||||
|
||||
func (ll *LayerList) SetChangedFunc(handler func(index int, mainText string, shortcut rune)) *LayerList {
|
||||
func (ll *LayerList) SetChangedFunc(handler LayerListHandler) *LayerList {
|
||||
ll.changed = handler
|
||||
return ll
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue