dive/runtime/ui/controller/controller_collection.go
2019-10-13 11:42:13 -04:00

166 lines
4.3 KiB
Go

package controller
import (
"errors"
"github.com/jroimartin/gocui"
"github.com/sirupsen/logrus"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
)
// var ccOnce sync.Once
var controllers *ControllerCollection
type ControllerCollection struct {
gui *gocui.Gui
Tree *FileTreeController
Layer *LayerController
Status *StatusController
Filter *FilterController
Details *DetailsController
lookup map[string]Controller
}
func NewControllerCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeCache) (*ControllerCollection, error) {
var err error
controllers = &ControllerCollection{
gui: g,
}
controllers.lookup = make(map[string]Controller)
controllers.Layer, err = NewLayerController("layers", g, analysis.Layers)
if err != nil {
return nil, err
}
controllers.lookup[controllers.Layer.name] = controllers.Layer
treeStack, err := filetree.StackTreeRange(analysis.RefTrees, 0, 0)
if err != nil {
return nil, err
}
controllers.Tree, err = NewFileTreeController("filetree", g, treeStack, analysis.RefTrees, cache)
if err != nil {
return nil, err
}
controllers.lookup[controllers.Tree.name] = controllers.Tree
controllers.Status = NewStatusController("status", g)
controllers.lookup[controllers.Status.name] = controllers.Status
controllers.Filter = NewFilterController("filter", g)
controllers.lookup[controllers.Filter.name] = controllers.Filter
controllers.Details = NewDetailsController("details", g, analysis.Efficiency, analysis.Inefficiencies)
controllers.lookup[controllers.Details.name] = controllers.Details
return controllers, nil
}
func (c *ControllerCollection) UpdateAndRender() error {
err := c.Update()
if err != nil {
logrus.Debug("failed update: ", err)
return err
}
err = c.Render()
if err != nil {
logrus.Debug("failed render: ", err)
return err
}
return nil
}
// Update refreshes the state objects for future rendering.
func (c *ControllerCollection) Update() error {
for _, controller := range c.lookup {
err := controller.Update()
if err != nil {
logrus.Debug("unable to update controller: ")
return err
}
}
return nil
}
// Render flushes the state objects to the screen.
func (c *ControllerCollection) Render() error {
for _, controller := range c.lookup {
if controller.IsVisible() {
err := controller.Render()
if err != nil {
return err
}
}
}
return nil
}
// ToggleView switches between the file view and the layer view and re-renders the screen.
func (c *ControllerCollection) ToggleView() (err error) {
v := c.gui.CurrentView()
if v == nil || v.Name() == c.Layer.Name() {
_, err = c.gui.SetCurrentView(c.Tree.Name())
} else {
_, err = c.gui.SetCurrentView(c.Layer.Name())
}
if err != nil {
logrus.Error("unable to toggle view: ", err)
return err
}
return c.UpdateAndRender()
}
func (c *ControllerCollection) ToggleFilterView() error {
// delete all user input from the tree view
err := c.Filter.ToggleVisible()
if err != nil {
logrus.Error("unable to toggle filter visibility: ", err)
return err
}
// we have just hidden the filter view, adjust focus to a valid (visible) view
if !c.Filter.IsVisible() {
err = c.ToggleView()
if err != nil {
logrus.Error("unable to toggle filter view (back): ", err)
return err
}
}
return c.UpdateAndRender()
}
// CursorDown moves the cursor down in the currently selected gocui pane, scrolling the screen as needed.
func (c *ControllerCollection) CursorDown(g *gocui.Gui, v *gocui.View) error {
return c.CursorStep(g, v, 1)
}
// CursorUp moves the cursor up in the currently selected gocui pane, scrolling the screen as needed.
func (c *ControllerCollection) CursorUp(g *gocui.Gui, v *gocui.View) error {
return c.CursorStep(g, v, -1)
}
// Moves the cursor the given step distance, setting the origin to the new cursor line
func (c *ControllerCollection) CursorStep(g *gocui.Gui, v *gocui.View, step int) error {
cx, cy := v.Cursor()
// if there isn't a next line
line, err := v.Line(cy + step)
if err != nil {
return err
}
if len(line) == 0 {
return errors.New("unable to move the cursor, empty line")
}
if err := v.SetCursor(cx, cy+step); err != nil {
ox, oy := v.Origin()
if err := v.SetOrigin(ox, oy+step); err != nil {
return err
}
}
return nil
}