dive/runtime/ui/controller.go

238 lines
5.7 KiB
Go
Raw Normal View History

2019-10-14 02:56:58 +02:00
package ui
import (
"regexp"
2020-12-27 06:56:03 +01:00
"github.com/awesome-gocui/gocui"
2019-10-14 02:56:58 +02:00
"github.com/sirupsen/logrus"
2019-10-14 02:56:58 +02:00
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"github.com/wagoodman/dive/runtime/ui/view"
"github.com/wagoodman/dive/runtime/ui/viewmodel"
)
type Controller struct {
2019-11-22 23:33:13 +01:00
gui *gocui.Gui
views *view.Views
2019-10-14 02:56:58 +02:00
}
func NewCollection(g *gocui.Gui, imageName string, analysis *image.AnalysisResult, cache filetree.Comparer) (*Controller, error) {
views, err := view.NewViews(g, imageName, analysis, cache)
2019-10-14 02:56:58 +02:00
if err != nil {
return nil, err
}
2019-11-22 23:33:13 +01:00
controller := &Controller{
gui: g,
views: views,
2019-10-14 02:56:58 +02:00
}
// layer view cursor down event should trigger an update in the file tree
2019-11-22 23:33:13 +01:00
controller.views.Layer.AddLayerChangeListener(controller.onLayerChange)
2019-10-14 02:56:58 +02:00
// update the status pane when a filetree option is changed by the user
2019-11-22 23:33:13 +01:00
controller.views.Tree.AddViewOptionChangeListener(controller.onFileTreeViewOptionChange)
2019-10-14 02:56:58 +02:00
2019-11-22 23:33:13 +01:00
// update the tree view while the user types into the filter view
controller.views.Filter.AddFilterEditListener(controller.onFilterEdit)
2019-10-14 02:56:58 +02:00
// propagate initial conditions to necessary views
err = controller.onLayerChange(viewmodel.LayerSelection{
2019-11-22 23:33:13 +01:00
Layer: controller.views.Layer.CurrentLayer(),
2019-10-14 02:56:58 +02:00
BottomTreeStart: 0,
BottomTreeStop: 0,
TopTreeStart: 0,
TopTreeStop: 0,
})
if err != nil {
return nil, err
}
return controller, nil
}
func (c *Controller) onFileTreeViewOptionChange() error {
2019-11-22 23:33:13 +01:00
err := c.views.Status.Update()
2019-10-14 02:56:58 +02:00
if err != nil {
return err
}
2019-11-22 23:33:13 +01:00
return c.views.Status.Render()
2019-10-14 02:56:58 +02:00
}
func (c *Controller) onFilterEdit(filter string) error {
var filterRegex *regexp.Regexp
var err error
if len(filter) > 0 {
filterRegex, err = regexp.Compile(filter)
if err != nil {
return err
}
}
2019-11-22 23:33:13 +01:00
c.views.Tree.SetFilterRegex(filterRegex)
2019-10-14 02:56:58 +02:00
2019-11-22 23:33:13 +01:00
err = c.views.Tree.Update()
2019-10-14 02:56:58 +02:00
if err != nil {
return err
}
2019-11-22 23:33:13 +01:00
return c.views.Tree.Render()
2019-10-14 02:56:58 +02:00
}
func (c *Controller) onLayerChange(selection viewmodel.LayerSelection) error {
// update the details
c.views.LayerDetails.CurrentLayer = selection.Layer
2019-10-14 02:56:58 +02:00
// update the filetree
2019-11-22 23:33:13 +01:00
err := c.views.Tree.SetTree(selection.BottomTreeStart, selection.BottomTreeStop, selection.TopTreeStart, selection.TopTreeStop)
2019-10-14 02:56:58 +02:00
if err != nil {
return err
}
2019-12-28 18:28:19 +01:00
if c.views.Layer.CompareMode() == viewmodel.CompareAllLayers {
2019-11-22 23:33:13 +01:00
c.views.Tree.SetTitle("Aggregated Layer Contents")
2019-10-14 02:56:58 +02:00
} else {
2019-11-22 23:33:13 +01:00
c.views.Tree.SetTitle("Current Layer Contents")
2019-10-14 02:56:58 +02:00
}
// update details and filetree panes
return c.UpdateAndRender()
}
func (c *Controller) 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 *Controller) Update() error {
2019-11-22 23:33:13 +01:00
for _, controller := range c.views.All() {
2019-10-14 02:56:58 +02:00
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 *Controller) Render() error {
2019-11-22 23:33:13 +01:00
for _, controller := range c.views.All() {
2019-10-14 02:56:58 +02:00
if controller.IsVisible() {
err := controller.Render()
if err != nil {
return err
}
}
}
return nil
}
//nolint:dupl
func (c *Controller) NextPane() (err error) {
v := c.gui.CurrentView()
if v == nil {
panic("Current view is nil")
}
if v.Name() == c.views.Layer.Name() {
_, err = c.gui.SetCurrentView(c.views.LayerDetails.Name())
c.views.Status.SetCurrentView(c.views.LayerDetails)
} else if v.Name() == c.views.LayerDetails.Name() {
_, err = c.gui.SetCurrentView(c.views.ImageDetails.Name())
c.views.Status.SetCurrentView(c.views.ImageDetails)
} else if v.Name() == c.views.ImageDetails.Name() {
_, err = c.gui.SetCurrentView(c.views.Layer.Name())
c.views.Status.SetCurrentView(c.views.Layer)
}
if err != nil {
logrus.Error("unable to toggle view: ", err)
return err
}
return c.UpdateAndRender()
}
//nolint:dupl
func (c *Controller) PrevPane() (err error) {
v := c.gui.CurrentView()
if v == nil {
panic("Current view is nil")
}
if v.Name() == c.views.Layer.Name() {
_, err = c.gui.SetCurrentView(c.views.ImageDetails.Name())
c.views.Status.SetCurrentView(c.views.ImageDetails)
} else if v.Name() == c.views.LayerDetails.Name() {
_, err = c.gui.SetCurrentView(c.views.Layer.Name())
c.views.Status.SetCurrentView(c.views.Layer)
} else if v.Name() == c.views.ImageDetails.Name() {
_, err = c.gui.SetCurrentView(c.views.LayerDetails.Name())
c.views.Status.SetCurrentView(c.views.LayerDetails)
}
if err != nil {
logrus.Error("unable to toggle view: ", err)
return err
}
return c.UpdateAndRender()
}
2019-10-14 02:56:58 +02:00
// ToggleView switches between the file view and the layer view and re-renders the screen.
func (c *Controller) ToggleView() (err error) {
v := c.gui.CurrentView()
2019-11-22 23:33:13 +01:00
if v == nil || v.Name() == c.views.Layer.Name() {
_, err = c.gui.SetCurrentView(c.views.Tree.Name())
c.views.Status.SetCurrentView(c.views.Tree)
2019-10-14 02:56:58 +02:00
} else {
2019-11-22 23:33:13 +01:00
_, err = c.gui.SetCurrentView(c.views.Layer.Name())
c.views.Status.SetCurrentView(c.views.Layer)
2019-10-14 02:56:58 +02:00
}
if err != nil {
logrus.Error("unable to toggle view: ", err)
return err
}
return c.UpdateAndRender()
}
func (c *Controller) ToggleFilterView() error {
// delete all user input from the tree view
2019-11-22 23:33:13 +01:00
err := c.views.Filter.ToggleVisible()
2019-10-14 02:56:58 +02:00
if err != nil {
logrus.Error("unable to toggle filter visibility: ", err)
return err
}
// we have just hidden the filter view...
2019-11-22 23:33:13 +01:00
if !c.views.Filter.IsVisible() {
2019-10-14 02:56:58 +02:00
// ...remove any filter from the tree
2019-11-22 23:33:13 +01:00
c.views.Tree.SetFilterRegex(nil)
2019-10-14 02:56:58 +02:00
// ...adjust focus to a valid (visible) view
err = c.ToggleView()
if err != nil {
logrus.Error("unable to toggle filter view (back): ", err)
return err
}
}
return c.UpdateAndRender()
}