Add details pane ()

This commit is contained in:
Alex Goodman 2018-10-14 10:56:09 -04:00 committed by GitHub
parent 0ec279788e
commit 85fa13501d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 54 deletions

@ -135,8 +135,8 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
// save this image to disk temporarily to get the content info
fmt.Println("Fetching image...")
imageTarPath, tmpDir := saveImage(imageID)
// imageTarPath := "/tmp/dive932744808/image.tar"
// tmpDir := "/tmp/dive031537738"
// imageTarPath := "/tmp/dive229500681/image.tar"
// tmpDir := "/tmp/dive229500681"
// fmt.Println(tmpDir)
defer os.RemoveAll(tmpDir)

95
ui/detailsview.go Normal file

@ -0,0 +1,95 @@
package ui
import (
"fmt"
"github.com/jroimartin/gocui"
"github.com/lunixbochs/vtclean"
"strings"
)
type DetailsView struct {
Name string
gui *gocui.Gui
view *gocui.View
header *gocui.View
}
func NewStatisticsView(name string, gui *gocui.Gui) (detailsview *DetailsView) {
detailsview = new(DetailsView)
// populate main fields
detailsview.Name = name
detailsview.gui = gui
return detailsview
}
func (view *DetailsView) Setup(v *gocui.View, header *gocui.View) error {
// set view options
view.view = v
view.view.Editable = false
view.view.Wrap = true
view.view.Highlight = false
view.view.Frame = false
view.header = header
view.header.Editable = false
view.header.Wrap = false
view.header.Frame = false
// set keybindings
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
return err
}
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil {
return err
}
return view.Render()
}
func (view *DetailsView) IsVisible() bool {
if view == nil {return false}
return true
}
func (view *DetailsView) Update() error {
return nil
}
func (view *DetailsView) Render() error {
currentLayer := Views.Layer.currentLayer()
view.gui.Update(func(g *gocui.Gui) error {
// update header
view.header.Clear()
width, _ := g.Size()
headerStr := fmt.Sprintf("[Image & Layer Details]%s", strings.Repeat("─",width*2))
fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update contents
view.view.Clear()
fmt.Fprintln(view.view, Formatting.Header("Command"))
fmt.Fprintln(view.view, currentLayer.History.CreatedBy)
return nil
})
return nil
}
func (view *DetailsView) CursorDown() error {
return CursorDown(view.gui, view.view)
}
func (view *DetailsView) CursorUp() error {
return CursorUp(view.gui, view.view)
}
func (view *DetailsView) KeyHelp() string {
return "TBD"
// return renderStatusOption("^L","Layer changes", view.CompareMode == CompareLayer) +
// renderStatusOption("^A","All changes", view.CompareMode == CompareAll)
}

@ -101,6 +101,13 @@ func (view *FileTreeView) IsVisible() bool {
return true
}
func (view *FileTreeView) resetCursor() {
view.view.SetCursor(0, 0)
view.TreeIndex = 0
view.bufferIndex = 0
view.bufferIndexLowerBound = 0
view.bufferIndexUpperBound = view.height()
}
func (view *FileTreeView) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error {
if topTreeStop > len(view.RefTrees)-1 {
@ -122,8 +129,8 @@ func (view *FileTreeView) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTre
}
view.ModelTree.VisitDepthChildFirst(visitor, nil)
view.view.SetCursor(0, 0)
view.TreeIndex = 0
view.resetCursor()
view.ModelTree = newTree
view.Update()
return view.Render()
@ -227,8 +234,7 @@ func (view *FileTreeView) toggleCollapse() error {
func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error {
view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType]
view.view.SetCursor(0, 0)
view.TreeIndex = 0
view.resetCursor()
Update()
Render()
@ -299,10 +305,22 @@ func (view *FileTreeView) Render() error {
view.doCursorUp()
}
title := "Current Layer Contents"
if Views.Layer.CompareMode == CompareAll {
title = "Aggregated Layer Contents"
}
// indicate when selected
if view.gui.CurrentView() == view.view {
title = "● "+title
}
view.gui.Update(func(g *gocui.Gui) error {
// update the header
view.header.Clear()
headerStr := fmt.Sprintf(filetree.AttributeFormat+" %s", "P", "ermission", "UID:GID", "Size", "Filetree")
width, _ := g.Size()
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
headerStr += fmt.Sprintf(filetree.AttributeFormat+" %s", "P", "ermission", "UID:GID", "Size", "Filetree")
fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update the contents

@ -18,13 +18,6 @@ type FilterView struct {
hidden bool
}
type Input struct {
name string
x, y int
w int
maxLength int
}
func NewFilterView(name string, gui *gocui.Gui) (filterview *FilterView) {
filterview = new(FilterView)

@ -7,6 +7,7 @@ import (
"github.com/wagoodman/dive/image"
"github.com/lunixbochs/vtclean"
"github.com/dustin/go-humanize"
"strings"
)
type LayerView struct {
@ -38,9 +39,6 @@ func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error {
view.view = v
view.view.Editable = false
view.view.Wrap = false
//view.view.Highlight = true
//view.view.SelBgColor = gocui.ColorGreen
//view.view.SelFgColor = gocui.ColorBlack
view.view.Frame = false
view.header = header
@ -70,6 +68,10 @@ func (view *LayerView) IsVisible() bool {
return true
}
func (view *LayerView) currentLayer() *image.Layer {
return view.Layers[(len(view.Layers)-1)-view.LayerIndex]
}
func (view *LayerView) setCompareMode(compareMode CompareType) error {
view.CompareMode = compareMode
Update()
@ -99,12 +101,6 @@ func (view *LayerView) renderCompareBar(layerIdx int) string {
bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop := view.getCompareIndexes()
result := " "
//if debug {
// v, _ := view.gui.View("debug")
// v.Clear()
// _, _ = fmt.Fprintf(v, "bStart: %d bStop: %d tStart: %d tStop: %d", bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
//}
if layerIdx >= bottomTreeStart && layerIdx <= bottomTreeStop {
result = Formatting.CompareBottom(" ")
}
@ -112,18 +108,6 @@ func (view *LayerView) renderCompareBar(layerIdx int) string {
result = Formatting.CompareTop(" ")
}
//if bottomTreeStop == topTreeStart {
// result += " "
//} else {
// if layerIdx == bottomTreeStop {
// result += "─┐"
// } else if layerIdx == topTreeStart {
// result += "─┘"
// } else {
// result += " "
// }
//}
return result
}
@ -132,9 +116,19 @@ func (view *LayerView) Update() error {
}
func (view *LayerView) Render() error {
// indicate when selected
title := "Layers"
if view.gui.CurrentView() == view.view {
title = "● "+title
}
view.gui.Update(func(g *gocui.Gui) error {
// update header
headerStr := fmt.Sprintf("Cmp "+image.LayerFormat, "Image ID", "%Eff.", "Size", "Filter")
view.header.Clear()
width, _ := g.Size()
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
headerStr += fmt.Sprintf("Cmp "+image.LayerFormat, "Image ID", "%Eff.", "Size", "Command")
fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update contents
@ -174,10 +168,7 @@ func (view *LayerView) CursorDown() error {
if view.LayerIndex < len(view.Layers) {
err := CursorDown(view.gui, view.view)
if err == nil {
view.LayerIndex++
Views.Tree.setTreeByLayer(view.getCompareIndexes())
view.Render()
// debugPrint(fmt.Sprintf("%d",len(filetree.Cache)))
view.SetCursor(view.LayerIndex+1)
}
}
return nil
@ -187,25 +178,22 @@ func (view *LayerView) CursorUp() error {
if view.LayerIndex > 0 {
err := CursorUp(view.gui, view.view)
if err == nil {
view.LayerIndex--
Views.Tree.setTreeByLayer(view.getCompareIndexes())
view.Render()
// debugPrint(fmt.Sprintf("%d",len(filetree.Cache)))
view.SetCursor(view.LayerIndex-1)
}
}
return nil
}
func (view *LayerView) SetCursor(layer int) error {
// view.view.SetCursor(0, layer)
view.LayerIndex = layer
Views.Tree.setTreeByLayer(view.getCompareIndexes())
Views.Details.Render()
view.Render()
return nil
}
func (view *LayerView) KeyHelp() string {
return renderStatusOption("^L","Layer changes", view.CompareMode == CompareLayer) +
renderStatusOption("^A","All changes", view.CompareMode == CompareAll)
return renderStatusOption("^L","Show layer changes", view.CompareMode == CompareLayer) +
renderStatusOption("^A","Show aggregated changes", view.CompareMode == CompareAll)
}

@ -41,11 +41,12 @@ var Formatting struct {
}
var Views struct {
Tree *FileTreeView
Layer *LayerView
Status *StatusView
Filter *FilterView
lookup map[string]View
Tree *FileTreeView
Layer *LayerView
Status *StatusView
Filter *FilterView
Details *DetailsView
lookup map[string]View
}
type View interface {
@ -178,7 +179,7 @@ func layout(g *gocui.Gui) error {
}
debugCols := maxX - debugWidth
bottomRows := 1
headerRows := 1
headerRows := 2
filterBarHeight := 1
statusBarHeight := 1
@ -186,6 +187,8 @@ func layout(g *gocui.Gui) error {
statusBarIndex := 1
filterBarIndex := 2
layersHeight := len(Views.Layer.Layers) + headerRows + 1 // layers + header + base image layer row
var view, header *gocui.View
var viewErr, headerErr, err error
@ -204,7 +207,7 @@ func layout(g *gocui.Gui) error {
}
// Layers
view, viewErr = g.SetView(Views.Layer.Name, -1, -1+headerRows, splitCols, maxY-bottomRows)
view, viewErr = g.SetView(Views.Layer.Name, -1, -1+headerRows, splitCols, layersHeight)
header, headerErr = g.SetView(Views.Layer.Name+"header", -1, -1, splitCols, headerRows)
if isNewView(viewErr, headerErr) {
Views.Layer.Setup(view, header)
@ -212,6 +215,15 @@ func layout(g *gocui.Gui) error {
if _, err = g.SetCurrentView(Views.Layer.Name); err != nil {
return err
}
// since we are selecting the view, we should rerender to indicate it is selected
Views.Layer.Render()
}
// Details
view, viewErr = g.SetView(Views.Details.Name, -1, -1+layersHeight+headerRows, splitCols, maxY-bottomRows)
header, headerErr = g.SetView(Views.Details.Name+"header", -1, -1+layersHeight, splitCols, layersHeight+headerRows)
if isNewView(viewErr, headerErr) {
Views.Details.Setup(view, header)
}
// Filetree
@ -291,6 +303,10 @@ func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
Views.Filter = NewFilterView("command", g)
Views.lookup[Views.Filter.Name] = Views.Filter
Views.Details = NewStatisticsView("details", g)
Views.lookup[Views.Details.Name] = Views.Details
g.Cursor = false
//g.Mouse = true
g.SetManagerFunc(layout)