mirror of
https://github.com/wagoodman/dive
synced 2026-03-14 22:35:50 +01:00
updates to layer_primitive
- add custom keybindings - add pagedown functionality (should be factored out evenutally) - add toggleable layer compare modes Signed-off-by: dwillist <dthornton@vmware.com>
This commit is contained in:
parent
92ce00a1a9
commit
be945d3501
7 changed files with 523 additions and 40 deletions
398
'
Normal file
398
'
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wagoodman/dive/dive/filetree"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 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
|
||||
SetLayerIndex(int) bool
|
||||
ToggleHiddenFileType(filetype filetree.DiffType) bool
|
||||
}
|
||||
|
||||
type TreeView struct {
|
||||
*tview.Box
|
||||
tree TreeModel
|
||||
|
||||
// Note that the following two fields are distinct
|
||||
// treeIndex is the index about where we are in the current fileTree
|
||||
// this should be updated every keypress
|
||||
treeIndex int
|
||||
|
||||
bufferIndexLowerBound int
|
||||
|
||||
globalCollapseAll bool
|
||||
|
||||
inputHandler func(event *tcell.EventKey, setFocus func(p tview.Primitive))
|
||||
|
||||
keyBindings map[string]KeyBinding
|
||||
|
||||
showAttributes bool
|
||||
}
|
||||
|
||||
func NewTreeView(tree TreeModel) *TreeView {
|
||||
return &TreeView{
|
||||
Box: tview.NewBox(),
|
||||
tree: tree,
|
||||
globalCollapseAll: true,
|
||||
showAttributes: true,
|
||||
inputHandler: nil,
|
||||
}
|
||||
}
|
||||
|
||||
type KeyBindingConfig interface {
|
||||
GetKeyBinding(key string) (KeyBinding, error)
|
||||
}
|
||||
|
||||
// Implementation notes:
|
||||
// need to set up our input handler here,
|
||||
// Should probably factor out keybinding initialization into a new function
|
||||
//
|
||||
func (t *TreeView) Setup(config KeyBindingConfig) *TreeView {
|
||||
t.tree.SetLayerIndex(0)
|
||||
|
||||
bindingSettings := map[string]keyAction{
|
||||
"keybinding.toggle-collapse-dir": t.collapseDir,
|
||||
"keybinding.toggle-collapse-all-dir": t.collapseOrExpandAll,
|
||||
"keybinding.toggle-filetree-attributes": func() bool { t.showAttributes = !t.showAttributes; return true },
|
||||
"keybinding.toggle-added-files": func() bool { t.tree.ToggleHiddenFileType(filetree.Added); return false },
|
||||
"keybinding.toggle-removed-files": func() bool { return t.tree.ToggleHiddenFileType(filetree.Removed)},
|
||||
"keybinding.toggle-modified-files": func() bool { return t.tree.ToggleHiddenFileType(filetree.Modified)},
|
||||
"keybinding.toggle-unmodified-files": func() bool { return t.tree.ToggleHiddenFileType(filetree.Unmodified)},
|
||||
"keybinding.page-up": func() bool { return t.pageUp() },
|
||||
"keybinding.page-down": func() bool { return t.pageDown() },
|
||||
}
|
||||
|
||||
bindingArray := []KeyBinding{}
|
||||
actionArray := []keyAction{}
|
||||
|
||||
for keybinding, action := range bindingSettings {
|
||||
binding, err := config.GetKeyBinding(keybinding)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("setup error during %s: %w", keybinding, err))
|
||||
// TODO handle this error
|
||||
//return nil
|
||||
}
|
||||
bindingArray = append(bindingArray, binding)
|
||||
actionArray = append(actionArray, action)
|
||||
}
|
||||
|
||||
t.inputHandler = func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
|
||||
switch event.Key() {
|
||||
case tcell.KeyUp:
|
||||
t.keyUp()
|
||||
case tcell.KeyDown:
|
||||
t.keyDown()
|
||||
case tcell.KeyRight:
|
||||
t.keyRight()
|
||||
case tcell.KeyLeft:
|
||||
t.keyLeft()
|
||||
}
|
||||
|
||||
for idx, binding := range bindingArray {
|
||||
if binding.Match(event) {
|
||||
actionArray[idx]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// TODO: do we need all of these?? or is there an alternative API we could use for the wrappers????
|
||||
func (t *TreeView) getBox() *tview.Box {
|
||||
return t.Box
|
||||
}
|
||||
|
||||
func (t *TreeView) getDraw() drawFn {
|
||||
return t.Draw
|
||||
}
|
||||
|
||||
func (t *TreeView) getInputWrapper() inputFn {
|
||||
return t.InputHandler
|
||||
}
|
||||
|
||||
// Implementation note:
|
||||
// what do we want here??? a binding object?? yes
|
||||
func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
|
||||
return t.inputHandler
|
||||
}
|
||||
|
||||
func (t *TreeView) SetInputHandler(handler func(event *tcell.EventKey, setFocus func(p tview.Primitive))) *TreeView {
|
||||
t.inputHandler = handler
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TreeView) WrapInputHandler() func(*tcell.EventKey, func(tview.Primitive)) {
|
||||
return t.Box.WrapInputHandler(t.inputHandler)
|
||||
}
|
||||
|
||||
func (t *TreeView) Focus(delegate func(p tview.Primitive)) {
|
||||
t.Box.Focus(delegate)
|
||||
}
|
||||
|
||||
func (t *TreeView) HasFocus() bool {
|
||||
return t.Box.HasFocus()
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
func (t *TreeView) collapseDir() bool {
|
||||
node := t.getAbsPositionNode()
|
||||
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
|
||||
}
|
||||
|
||||
func (t *TreeView) collapseOrExpandAll() bool {
|
||||
zap.S().Info("collapsing all directories")
|
||||
visitor := func(n *filetree.FileNode) error {
|
||||
if n.Data.FileInfo.IsDir {
|
||||
n.Data.ViewInfo.Collapsed = t.globalCollapseAll
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
evaluator := func(n *filetree.FileNode) bool {
|
||||
return true
|
||||
}
|
||||
if err := t.tree.VisitDepthParentFirst(visitor, evaluator); err != nil {
|
||||
zap.S().Panic("error collapsing all: ", err.Error())
|
||||
panic(fmt.Errorf("error callapsing all dir: %w", err))
|
||||
// TODO log error here
|
||||
//return false
|
||||
}
|
||||
|
||||
zap.S().Info("finished collapsing all directories")
|
||||
|
||||
t.globalCollapseAll = !t.globalCollapseAll
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// getAbsPositionNode determines the selected screen cursor's location in the file tree, returning the selected FileNode.
|
||||
func (t *TreeView) getAbsPositionNode() (node *filetree.FileNode) {
|
||||
var visitor func(*filetree.FileNode) error
|
||||
var evaluator func(*filetree.FileNode) bool
|
||||
var dfsCounter int
|
||||
|
||||
visitor = func(curNode *filetree.FileNode) error {
|
||||
if dfsCounter == t.treeIndex {
|
||||
node = curNode
|
||||
}
|
||||
dfsCounter++
|
||||
return nil
|
||||
}
|
||||
|
||||
evaluator = func(curNode *filetree.FileNode) bool {
|
||||
return !curNode.Parent.Data.ViewInfo.Collapsed && !curNode.Data.ViewInfo.Hidden
|
||||
}
|
||||
|
||||
err := t.tree.VisitDepthParentFirst(visitor, evaluator)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to get node position: %+v", err)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func (t *TreeView) keyDown() bool {
|
||||
_, _, _, height := t.Box.GetInnerRect()
|
||||
|
||||
// treeIndex is the index about where we are in the current file
|
||||
if t.treeIndex >= t.tree.VisibleSize() {
|
||||
return false
|
||||
}
|
||||
t.treeIndex++
|
||||
if (t.treeIndex - t.bufferIndexLowerBound) >= height {
|
||||
t.bufferIndexLowerBound++
|
||||
}
|
||||
|
||||
logrus.Debugf(" treeIndex: %d", t.treeIndex)
|
||||
logrus.Debugf(" bufferIndexLowerBound: %d", t.bufferIndexLowerBound)
|
||||
logrus.Debugf(" height: %d", height)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *TreeView) keyUp() bool {
|
||||
if t.treeIndex <= 0 {
|
||||
return false
|
||||
}
|
||||
t.treeIndex--
|
||||
if t.treeIndex < t.bufferIndexLowerBound {
|
||||
t.bufferIndexLowerBound--
|
||||
}
|
||||
|
||||
logrus.Debugf("keyUp end at: %s", t.getAbsPositionNode().Path())
|
||||
logrus.Debugf(" treeIndex: %d", t.treeIndex)
|
||||
logrus.Debugf(" bufferIndexLowerBound: %d", t.bufferIndexLowerBound)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// TODO add regex filtering
|
||||
func (t *TreeView) keyRight() bool {
|
||||
node := t.getAbsPositionNode()
|
||||
|
||||
_, _, _, 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.bufferIndexLowerBound) >= height {
|
||||
t.bufferIndexLowerBound++
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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 {
|
||||
return !curNode.Parent.Data.ViewInfo.Collapsed && !curNode.Data.ViewInfo.Hidden
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO make all movement rely on a single function (shouldn't be too dificult really)
|
||||
func (t *TreeView) pageDown() bool {
|
||||
|
||||
_,_,_,height := t.GetInnerRect()
|
||||
visibleSize := t.tree.VisibleSize()
|
||||
t.treeIndex = intMin(t.treeIndex + height, visibleSize)
|
||||
if t.treeIndex >= t.bufferIndexUpperBound() {
|
||||
t.bufferIndexLowerBound = intMin(t.treeIndex, visibleSize - height + 1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func (t *TreeView) pageUp() bool {
|
||||
_,_,_,height := t.GetInnerRect()
|
||||
|
||||
t.treeIndex = intMax(0, t.treeIndex - height)
|
||||
if t.treeIndex < t.bufferIndexLowerBound {
|
||||
t.bufferIndexLowerBound = t.treeIndex
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *TreeView) bufferIndexUpperBound() int {
|
||||
_, _, _, height := t.Box.GetInnerRect()
|
||||
return t.bufferIndexLowerBound + height
|
||||
}
|
||||
|
||||
func (t *TreeView) Draw(screen tcell.Screen) {
|
||||
t.Box.Draw(screen)
|
||||
selectedIndex := t.treeIndex - t.bufferIndexLowerBound
|
||||
x, y, width, height := t.Box.GetInnerRect()
|
||||
showAttributes := width > 80 && t.showAttributes
|
||||
// TODO add switch for showing attributes.
|
||||
treeString := t.tree.StringBetween(t.bufferIndexLowerBound, t.bufferIndexUpperBound(), showAttributes)
|
||||
lines := strings.Split(treeString, "\n")
|
||||
|
||||
// update the contents
|
||||
for yIndex, line := range lines {
|
||||
if yIndex >= height {
|
||||
break
|
||||
}
|
||||
// Strip out ansi colors, Tview cannot use these
|
||||
stripLine := bytes.NewBuffer(nil)
|
||||
w := tview.ANSIWriter(stripLine)
|
||||
if _, err := io.Copy(w, strings.NewReader(line)); err != nil {
|
||||
//TODO: handle panic gracefully
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tview.Print(screen, stripLine.String(), x, y+yIndex, width, tview.AlignLeft, tcell.ColorDefault)
|
||||
for xIndex := 0; xIndex < width; xIndex++ {
|
||||
m, c, style, _ := screen.GetContent(x+xIndex, y+yIndex)
|
||||
style = style.Background(tcell.ColorWhite).Foreground(tcell.ColorBlack).Bold(true)
|
||||
if yIndex == selectedIndex {
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
} else if yIndex > selectedIndex {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func intMin(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
10
cmd/root.go
10
cmd/root.go
|
|
@ -88,15 +88,17 @@ func initConfig() {
|
|||
"Compare All",
|
||||
tcell.NewEventKey(tcell.KeyCtrlA, rune(0), tcell.ModNone),
|
||||
))
|
||||
viper.SetDefault("keybinding.compare-layer", "ctrl+l")
|
||||
|
||||
viper.SetDefault("keybinding.compare-layer", components.NewKeyBinding(
|
||||
"Compare Layer",
|
||||
tcell.NewEventKey(tcell.KeyCtrlL, rune(0), tcell.ModNone),
|
||||
))
|
||||
|
||||
// keybindings: filetree view
|
||||
viper.SetDefault("keybinding.toggle-collapse-dir", components.NewKeyBinding(
|
||||
"Collapse",
|
||||
tcell.NewEventKey(tcell.KeyRune, ' ', tcell.ModNone),
|
||||
))
|
||||
viper.SetDefault("keybinding.compare-layer", "ctrl+l")
|
||||
// keybindings: filetree view
|
||||
//viper.SetDefault("keybinding.toggle-collapse-all-dir", "ctrl+space")
|
||||
|
||||
viper.SetDefault("keybinding.toggle-collapse-all-dir", components.NewKeyBinding(
|
||||
"Collapse All",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
|
@ -67,7 +68,7 @@ func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetr
|
|||
|
||||
filterView := components.NewFilterView(treeViewModel).Setup()
|
||||
|
||||
layersView := components.NewLayerList(treeViewModel).Setup()
|
||||
layersView := components.NewLayerList(treeViewModel).Setup(config)
|
||||
layersBox := components.NewWrapper("Layers", "subtitle!", layersView).Setup()
|
||||
|
||||
fileTreeView := components.NewTreeView(treeViewModel)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ func NewTreeView(tree TreeModel) *TreeView {
|
|||
tree: tree,
|
||||
globalCollapseAll: true,
|
||||
showAttributes: true,
|
||||
inputHandler: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,15 +323,14 @@ func (t *TreeView) keyLeft() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// deisred behavior,
|
||||
// move the selected cursor 1 screen height up or down, then reset the screen appropriately
|
||||
// TODO make all movement rely on a single function (shouldn't be too dificult really)
|
||||
func (t *TreeView) pageDown() bool {
|
||||
// two parts of this are moving both the currently selected item & the window as a whole
|
||||
|
||||
_,_,_,height := t.GetInnerRect()
|
||||
t.treeIndex = intMin(t.treeIndex + height, t.tree.VisibleSize() -1)
|
||||
visibleSize := t.tree.VisibleSize()
|
||||
t.treeIndex = intMin(t.treeIndex + height, visibleSize)
|
||||
if t.treeIndex >= t.bufferIndexUpperBound() {
|
||||
t.bufferIndexLowerBound = t.treeIndex
|
||||
t.bufferIndexLowerBound = intMin(t.treeIndex, visibleSize - height + 1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@ import (
|
|||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wagoodman/dive/runtime/ui/viewmodels"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type LayersViewModel interface {
|
||||
SetLayerIndex(int) bool
|
||||
GetPrintableLayers() []fmt.Stringer
|
||||
SwitchMode()
|
||||
GetMode() viewmodels.LayerCompareMode
|
||||
}
|
||||
|
||||
type LayerList struct {
|
||||
|
|
@ -18,6 +22,7 @@ type LayerList struct {
|
|||
bufferIndexLowerBound int
|
||||
cmpIndex int
|
||||
changed LayerListHandler
|
||||
inputHandler func(event *tcell.EventKey, setFocus func(p tview.Primitive))
|
||||
LayersViewModel
|
||||
}
|
||||
|
||||
|
|
@ -28,10 +33,66 @@ func NewLayerList(model LayersViewModel) *LayerList {
|
|||
Box: tview.NewBox(),
|
||||
cmpIndex: 0,
|
||||
LayersViewModel: model,
|
||||
inputHandler: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (ll *LayerList) Setup() *LayerList {
|
||||
func (ll *LayerList) Setup(config KeyBindingConfig) *LayerList {
|
||||
bindingSettings := map[string]keyAction{
|
||||
"keybinding.page-up": func() bool { return ll.pageUp() },
|
||||
"keybinding.page-down": func() bool { return ll.pageDown() },
|
||||
"keybinding.compare-all": func() bool {
|
||||
if ll.GetMode() == viewmodels.CompareSingleLayer {
|
||||
ll.SwitchMode()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
"keybinding.compare-layer": func() bool {
|
||||
if ll.GetMode() == viewmodels.CompareAllLayers {
|
||||
ll.SwitchMode()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
bindingArray := []KeyBinding{}
|
||||
actionArray := []keyAction{}
|
||||
|
||||
for keybinding, action := range bindingSettings {
|
||||
binding, err := config.GetKeyBinding(keybinding)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("setup error during %s: %w", keybinding, err))
|
||||
// TODO handle this error
|
||||
//return nil
|
||||
}
|
||||
bindingArray = append(bindingArray, binding)
|
||||
actionArray = append(actionArray, action)
|
||||
}
|
||||
|
||||
ll.inputHandler = func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
|
||||
switch event.Key() {
|
||||
case tcell.KeyUp, tcell.KeyLeft:
|
||||
if ll.SetLayerIndex(ll.cmpIndex - 1) {
|
||||
ll.keyUp()
|
||||
//ll.cmpIndex--
|
||||
//logrus.Debugf("KeyUp pressed, index: %d", ll.cmpIndex)
|
||||
}
|
||||
case tcell.KeyDown, tcell.KeyRight:
|
||||
if ll.SetLayerIndex(ll.cmpIndex + 1) {
|
||||
ll.keyDown()
|
||||
//ll.cmpIndex++
|
||||
//logrus.Debugf("KeyUp pressed, index: %d", ll.cmpIndex)
|
||||
|
||||
}
|
||||
}
|
||||
for idx, binding := range bindingArray {
|
||||
if binding.Match(event) {
|
||||
actionArray[idx]()
|
||||
}
|
||||
}
|
||||
}
|
||||
return ll
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +123,11 @@ func (ll *LayerList) Draw(screen tcell.Screen) {
|
|||
layer := printableLayers[layerIndex]
|
||||
var cmpColor tcell.Color
|
||||
switch {
|
||||
case yIndex == ll.cmpIndex:
|
||||
case layerIndex == ll.cmpIndex:
|
||||
cmpColor = tcell.ColorRed
|
||||
case yIndex < ll.cmpIndex:
|
||||
case layerIndex > 0 && layerIndex < ll.cmpIndex && ll.GetMode() == viewmodels.CompareAllLayers:
|
||||
cmpColor = tcell.ColorRed
|
||||
case layerIndex < ll.cmpIndex:
|
||||
cmpColor = tcell.ColorBlue
|
||||
default:
|
||||
cmpColor = tcell.ColorDefault
|
||||
|
|
@ -76,10 +139,10 @@ func (ll *LayerList) Draw(screen tcell.Screen) {
|
|||
fg, bg, _ := style.Decompose()
|
||||
style = style.Background(fg).Foreground(bg)
|
||||
switch {
|
||||
case yIndex == ll.cmpIndex:
|
||||
case layerIndex == ll.cmpIndex:
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
case yIndex < ll.cmpIndex && xIndex < len(cmpString):
|
||||
case layerIndex < ll.cmpIndex && xIndex < len(cmpString):
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
screen.SetContent(x+xIndex, y+yIndex, m, c, style)
|
||||
default:
|
||||
|
|
@ -91,23 +154,7 @@ func (ll *LayerList) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
func (ll *LayerList) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
|
||||
return ll.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
|
||||
switch event.Key() {
|
||||
case tcell.KeyUp:
|
||||
if ll.SetLayerIndex(ll.cmpIndex - 1) {
|
||||
ll.keyUp()
|
||||
//ll.cmpIndex--
|
||||
//logrus.Debugf("KeyUp pressed, index: %d", ll.cmpIndex)
|
||||
}
|
||||
case tcell.KeyDown:
|
||||
if ll.SetLayerIndex(ll.cmpIndex + 1) {
|
||||
ll.keyDown()
|
||||
//ll.cmpIndex++
|
||||
//logrus.Debugf("KeyUp pressed, index: %d", ll.cmpIndex)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
return ll.WrapInputHandler(ll.inputHandler)
|
||||
}
|
||||
|
||||
func (ll *LayerList) Focus(delegate func(p tview.Primitive)) {
|
||||
|
|
@ -138,19 +185,17 @@ func (ll *LayerList) keyUp() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// TODO (simplify all page increments to rely an a single function)
|
||||
func (ll *LayerList) keyDown() bool {
|
||||
_, _, _, height := ll.Box.GetInnerRect()
|
||||
adjustedHeight := height - 1
|
||||
|
||||
// treeIndex is the index about where we are in the current file
|
||||
visibleSize := len(ll.GetPrintableLayers())
|
||||
if ll.cmpIndex+1+ll.bufferIndexLowerBound >= visibleSize {
|
||||
if ll.cmpIndex+1 >= visibleSize {
|
||||
return false
|
||||
}
|
||||
if ll.cmpIndex+1 >= adjustedHeight {
|
||||
ll.cmpIndex++
|
||||
if ll.cmpIndex-ll.bufferIndexLowerBound >= height {
|
||||
ll.bufferIndexLowerBound++
|
||||
} else {
|
||||
ll.cmpIndex++
|
||||
}
|
||||
logrus.Debugln("keyDown in layers")
|
||||
logrus.Debugf(" cmpIndex: %d", ll.cmpIndex)
|
||||
|
|
@ -158,3 +203,29 @@ func (ll *LayerList) keyDown() bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func (ll *LayerList) pageUp() bool {
|
||||
zap.S().Info("layer page up call")
|
||||
_, _, _, height := ll.Box.GetInnerRect()
|
||||
|
||||
ll.cmpIndex = intMax(0, ll.cmpIndex-height)
|
||||
if ll.cmpIndex < ll.bufferIndexLowerBound {
|
||||
ll.bufferIndexLowerBound = ll.cmpIndex
|
||||
}
|
||||
|
||||
return ll.SetLayerIndex(ll.cmpIndex)
|
||||
}
|
||||
|
||||
func (ll *LayerList) pageDown() bool {
|
||||
zap.S().Info("layer page down call")
|
||||
// two parts of this are moving both the currently selected item & the window as a whole
|
||||
|
||||
_, _, _, height := ll.Box.GetInnerRect()
|
||||
upperBoundIndex := len(ll.GetPrintableLayers()) - 1
|
||||
ll.cmpIndex = intMin(ll.cmpIndex+height, upperBoundIndex)
|
||||
if ll.cmpIndex >= ll.bufferIndexLowerBound+height {
|
||||
ll.bufferIndexLowerBound = intMin(ll.cmpIndex, upperBoundIndex-height+1)
|
||||
}
|
||||
|
||||
return ll.SetLayerIndex(ll.cmpIndex)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,10 +46,14 @@ func (lm *LayersViewModel) GetCompareIndicies() filetree.TreeIndexKey {
|
|||
|
||||
bottomStart := 0
|
||||
bottomStop := 0
|
||||
topStart := lm.index
|
||||
if lm.mode == CompareSingleLayer {
|
||||
bottomStop = intMax(lm.index-1, 0)
|
||||
} else {
|
||||
topStart = 1
|
||||
}
|
||||
return filetree.NewTreeIndexKey(bottomStart, bottomStop, lm.index, lm.index)
|
||||
|
||||
return filetree.NewTreeIndexKey(bottomStart, bottomStop, topStart, lm.index)
|
||||
}
|
||||
|
||||
func (lm *LayersViewModel) SetLayerIndex(index int) bool {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ type LayersModel interface {
|
|||
GetCompareIndicies() filetree.TreeIndexKey
|
||||
GetCurrentLayer() *image.Layer
|
||||
GetPrintableLayers() []fmt.Stringer
|
||||
GetMode() LayerCompareMode
|
||||
SwitchMode()
|
||||
}
|
||||
|
||||
type TreeViewModel struct {
|
||||
|
|
@ -118,7 +120,6 @@ func (tvm *TreeViewModel) FilterUpdate() error {
|
|||
}
|
||||
|
||||
// Override functions
|
||||
|
||||
func (tvm *TreeViewModel) SetLayerIndex(index int) bool {
|
||||
if tvm.LayersModel.SetLayerIndex(index) {
|
||||
err := tvm.setCurrentTree(tvm.GetCompareIndicies())
|
||||
|
|
@ -167,3 +168,9 @@ func (tvm *TreeViewModel) setCurrentTree(key filetree.TreeIndexKey) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tvm *TreeViewModel) SwitchMode() {
|
||||
tvm.LayersModel.SwitchMode()
|
||||
// TODO: Handle this error
|
||||
tvm.setCurrentTree(tvm.GetCompareIndicies())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue