Skip to content

Commit

Permalink
Refactor the GUI layout
Browse files Browse the repository at this point in the history
The Details struct was split into two, LayerDetails and ImageDetails,
Each of the three views (Layer, LayerDetails, ImageDetails) takes up
a third of the available height of the screen, and they are all now
selectable and scrollable.
  • Loading branch information
mark2185 committed Apr 29, 2022
1 parent 2030e74 commit 25194cc
Show file tree
Hide file tree
Showing 10 changed files with 477 additions and 314 deletions.
10 changes: 9 additions & 1 deletion runtime/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func newApp(gui *gocui.Gui, imageName string, analysis *image.AnalysisResult, ca
lm := layout.NewManager()
lm.Add(controller.views.Status, layout.LocationFooter)
lm.Add(controller.views.Filter, layout.LocationFooter)
lm.Add(compound.NewLayerDetailsCompoundLayout(controller.views.Layer, controller.views.Details), layout.LocationColumn)
lm.Add(compound.NewLayerDetailsCompoundLayout(controller.views.Layer, controller.views.LayerDetails, controller.views.ImageDetails), layout.LocationColumn)
lm.Add(controller.views.Tree, layout.LocationColumn)

// todo: access this more programmatically
Expand Down Expand Up @@ -76,6 +76,14 @@ func newApp(gui *gocui.Gui, imageName string, analysis *image.AnalysisResult, ca
OnAction: controller.ToggleView,
Display: "Switch view",
},
{
Key: gocui.KeyArrowRight,
OnAction: controller.NextPane,
},
{
Key: gocui.KeyArrowLeft,
OnAction: controller.PrevPane,
},
{
ConfigKeys: []string{"keybinding.filter-files"},
OnAction: controller.ToggleFilterView,
Expand Down
55 changes: 54 additions & 1 deletion runtime/ui/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,19 @@ func (c *Controller) onFilterEdit(filter string) error {

func (c *Controller) onLayerChange(selection viewmodel.LayerSelection) error {
// update the details
c.views.Details.SetCurrentLayer(selection.Layer)
c.views.LayerDetails.CurrentLayer = selection.Layer

// update the filetree
err := c.views.Tree.SetTree(selection.BottomTreeStart, selection.BottomTreeStop, selection.TopTreeStart, selection.TopTreeStop)
if err != nil {
return err
}

// this is hacky, and I do not like it
if err := c.views.LayerDetails.SetCursor(0, 0); err != nil {
logrus.Debug("Couldn't set cursor to 0,0 for layerDetails")
}

if c.views.Layer.CompareMode() == viewmodel.CompareAllLayers {
c.views.Tree.SetTitle("Aggregated Layer Contents")
} else {
Expand Down Expand Up @@ -141,6 +146,54 @@ func (c *Controller) Render() error {
return nil
}

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()
}

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()
}

// 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()
Expand Down
102 changes: 41 additions & 61 deletions runtime/ui/layout/compound/layer_details_column.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ import (

type LayerDetailsCompoundLayout struct {
layer *view.Layer
details *view.Details
layerDetails *view.LayerDetails
imageDetails *view.ImageDetails
constrainRealEstate bool
}

func NewLayerDetailsCompoundLayout(layer *view.Layer, details *view.Details) *LayerDetailsCompoundLayout {
func NewLayerDetailsCompoundLayout(layer *view.Layer, layerDetails *view.LayerDetails, imageDetails *view.ImageDetails) *LayerDetailsCompoundLayout {
return &LayerDetailsCompoundLayout{
layer: layer,
details: details,
layer: layer,
layerDetails: layerDetails,
imageDetails: imageDetails,
}
}

Expand All @@ -32,87 +34,65 @@ func (cl *LayerDetailsCompoundLayout) OnLayoutChange() error {
return err
}

err = cl.details.OnLayoutChange()
err = cl.layerDetails.OnLayoutChange()
if err != nil {
logrus.Error("unable to setup details controller onLayoutChange", err)
logrus.Error("unable to setup layer details controller onLayoutChange", err)
return err
}

err = cl.imageDetails.OnLayoutChange()
if err != nil {
logrus.Error("unable to setup image details controller onLayoutChange", err)
return err
}
return nil
}

func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, maxY int) error {
logrus.Tracef("view.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, cl.Name())

////////////////////////////////////////////////////////////////////////////////////
// Layers View

func (cl *LayerDetailsCompoundLayout) layoutRow(g *gocui.Gui, minX, minY, maxX, maxY int, viewName string, setup func(*gocui.View, *gocui.View) error) error {
logrus.Tracef("layoutRow(g, minX: %d, minY: %d, maxX: %d, maxY: %d, viewName: %s, <setup func>)", minX, minY, maxX, maxY, viewName)
// header + border
layerHeaderHeight := 2

layersHeight := cl.layer.LayerCount() + layerHeaderHeight + 1 // layers + header + base image layer row
maxLayerHeight := int(0.75 * float64(maxY))
if layersHeight > maxLayerHeight {
layersHeight = maxLayerHeight
}
headerHeight := 2

// TODO: investigate overlap
// note: maxY needs to account for the (invisible) border, thus a +1
header, headerErr := g.SetView(cl.layer.Name()+"header", minX, minY, maxX, minY+layerHeaderHeight+1, 0)
headerView, headerErr := g.SetView(viewName+"Header", minX, minY, maxX, minY+headerHeight+1, 0)

// we are going to overlap the view over the (invisible) border (so minY will be one less than expected)
main, viewErr := g.SetView(cl.layer.Name(), minX, minY+layerHeaderHeight, maxX, minY+layerHeaderHeight+layersHeight, 0)
bodyView, bodyErr := g.SetView(viewName, minX, minY+headerHeight, maxX, maxY, 0)

if utils.IsNewView(viewErr, headerErr) {
err := cl.layer.Setup(main, header)
if utils.IsNewView(bodyErr, headerErr) {
err := setup(bodyView, headerView)
if err != nil {
logrus.Error("unable to setup layer layout", err)
return err
}

if _, err = g.SetCurrentView(cl.layer.Name()); err != nil {
logrus.Error("unable to set view to layer", err)
logrus.Debug("unable to setup row layout for ", viewName, err)
return err
}
}
return nil
}

////////////////////////////////////////////////////////////////////////////////////
// Details
detailsMinY := minY + layersHeight

// header + border
detailsHeaderHeight := 2

v, _ := g.View(cl.details.Name())
if v != nil {
// the view exists already!

// don't show the details pane when there isn't enough room on the screen
if cl.constrainRealEstate {
// take note: deleting a view will invoke layout again, so ensure this call is protected from an infinite loop
err := g.DeleteView(cl.details.Name())
if err != nil {
return err
}
// take note: deleting a view will invoke layout again, so ensure this call is protected from an infinite loop
err = g.DeleteView(cl.details.Name() + "header")
if err != nil {
return err
}

return nil
}
func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, maxY int) error {
logrus.Tracef("LayerDetailsCompountLayout.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, cl.Name())

layouts := []view.IView{
cl.layer,
cl.layerDetails,
cl.imageDetails,
}

header, headerErr = g.SetView(cl.details.Name()+"header", minX, detailsMinY, maxX, detailsMinY+detailsHeaderHeight, 0)
main, viewErr = g.SetView(cl.details.Name(), minX, detailsMinY+detailsHeaderHeight, maxX, maxY, 0)

if utils.IsNewView(viewErr, headerErr) {
err := cl.details.Setup(main, header)
if err != nil {
rowHeight := maxY / 3
for i := 0; i < 3; i++ {
if err := cl.layoutRow(g, minX, i*rowHeight, maxX, (i+1)*rowHeight, layouts[i].Name(), layouts[i].Setup); err != nil {
logrus.Debug("Laying out layers view errored!")
return err
}
}

if g.CurrentView() == nil {
if _, err := g.SetCurrentView(cl.layer.Name()); err != nil {
logrus.Error("unable to set view to layer", err)
return err
}
}
return nil
}

Expand Down
Loading

0 comments on commit 25194cc

Please sign in to comment.