summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Goodman <wagoodman@gmail.com>2019-10-21 06:54:13 -0400
committerAlex Goodman <wagoodman@gmail.com>2019-10-21 06:54:13 -0400
commit6c2aac7340cbef6b0cd1f50c81c4bf4beb8e2d8c (patch)
treed5d799e55b957b31ddd7255b29504b41a7604874
parent6dd7f7af6ec6d98d81df8ab739d311007c7bfdfb (diff)
exp with vert layoutlayout-exp
-rw-r--r--runtime/ui/app.go9
-rw-r--r--runtime/ui/controller.go21
-rw-r--r--runtime/ui/layout/vertical.go58
-rw-r--r--runtime/ui/layout/view.go10
-rw-r--r--runtime/ui/layout_manager.go76
-rw-r--r--runtime/ui/view/details.go8
-rw-r--r--runtime/ui/view/filetree.go8
-rw-r--r--runtime/ui/view/filter.go9
-rw-r--r--runtime/ui/view/help.go (renamed from runtime/ui/view/status.go)40
-rw-r--r--runtime/ui/view/layer.go9
-rw-r--r--runtime/ui/view/renderer.go9
-rw-r--r--runtime/ui/view/view.go28
12 files changed, 208 insertions, 77 deletions
diff --git a/runtime/ui/app.go b/runtime/ui/app.go
index ec7f5ad..2c9da10 100644
--- a/runtime/ui/app.go
+++ b/runtime/ui/app.go
@@ -16,7 +16,7 @@ const debug = false
type app struct {
gui *gocui.Gui
controllers *Controller
- layout *layoutManager
+ layout *LayoutManager
}
var (
@@ -35,11 +35,11 @@ func newApp(gui *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeC
return
}
- lm := newLayoutManager(theControls)
+ lm := NewLayoutManager(theControls)
gui.Cursor = false
//g.Mouse = true
- gui.SetManagerFunc(lm.layout)
+ gui.SetManagerFunc(lm.Layout)
// var profileObj = profile.Start(profile.CPUProfile, profile.ProfilePath("."), profile.NoShutdownHook)
//
@@ -77,7 +77,7 @@ func newApp(gui *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeC
return
}
- theControls.Status.AddHelpKeys(globalHelpKeys...)
+ theControls.Help.AddHelpKeys(globalHelpKeys...)
// perform the first update and render now that all resources have been loaded
err = theControls.UpdateAndRender()
@@ -106,7 +106,6 @@ func newApp(gui *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeC
// }
// }
-var lastX, lastY int
// quit is the gocui callback invoked when the user hits Ctrl+C
func (a *app) quit() error {
diff --git a/runtime/ui/controller.go b/runtime/ui/controller.go
index 0eef2c2..49d4eb7 100644
--- a/runtime/ui/controller.go
+++ b/runtime/ui/controller.go
@@ -14,10 +14,11 @@ type Controller struct {
gui *gocui.Gui
Tree *view.FileTree
Layer *view.Layer
- Status *view.Status
+ Help *view.Help
Filter *view.Filter
Details *view.Details
- lookup map[string]view.Renderer
+
+ lookup map[string]view.View
}
func NewCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.TreeCache) (*Controller, error) {
@@ -26,7 +27,7 @@ func NewCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.
controller := &Controller{
gui: g,
}
- controller.lookup = make(map[string]view.Renderer)
+ controller.lookup = make(map[string]view.View)
controller.Layer, err = view.NewLayerView("layers", g, analysis.Layers)
if err != nil {
@@ -47,10 +48,10 @@ func NewCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.
// layer view cursor down event should trigger an update in the file tree
controller.Layer.AddLayerChangeListener(controller.onLayerChange)
- controller.Status = view.NewStatusView("status", g)
- controller.lookup[controller.Status.Name()] = controller.Status
+ controller.Help = view.NewHelpView("status", g)
+ controller.lookup[controller.Help.Name()] = controller.Help
// set the layer view as the first selected view
- controller.Status.SetCurrentView(controller.Layer)
+ controller.Help.SetCurrentView(controller.Layer)
// update the status pane when a filetree option is changed by the user
controller.Tree.AddViewOptionChangeListener(controller.onFileTreeViewOptionChange)
@@ -79,11 +80,11 @@ func NewCollection(g *gocui.Gui, analysis *image.AnalysisResult, cache filetree.
}
func (c *Controller) onFileTreeViewOptionChange() error {
- err := c.Status.Update()
+ err := c.Help.Update()
if err != nil {
return err
}
- return c.Status.Render()
+ return c.Help.Render()
}
func (c *Controller) onFilterEdit(filter string) error {
@@ -173,10 +174,10 @@ func (c *Controller) ToggleView() (err error) {
v := c.gui.CurrentView()
if v == nil || v.Name() == c.Layer.Name() {
_, err = c.gui.SetCurrentView(c.Tree.Name())
- c.Status.SetCurrentView(c.Tree)
+ c.Help.SetCurrentView(c.Tree)
} else {
_, err = c.gui.SetCurrentView(c.Layer.Name())
- c.Status.SetCurrentView(c.Layer)
+ c.Help.SetCurrentView(c.Layer)
}
if err != nil {
diff --git a/runtime/ui/layout/vertical.go b/runtime/ui/layout/vertical.go
new file mode 100644
index 0000000..67d12f4
--- /dev/null
+++ b/runtime/ui/layout/vertical.go
@@ -0,0 +1,58 @@
+package layout
+
+import (
+ "fmt"
+ "github.com/wagoodman/dive/runtime/ui/view"
+)
+
+type Vertical struct {
+ visible bool
+ width int
+ elements []View
+}
+
+// how does overrun work? which view gets precidence? how does max possible height work?
+
+func NewVerticalLayout() *Vertical {
+ return &Vertical{
+ visible: true,
+ width: view.WidthFull,
+ elements: make([]View, 0),
+ }
+}
+
+func (v Vertical) SetWidth(w int) {
+ v.width = w
+}
+
+func (v *Vertical) AddView(sub View) error {
+ for _, element := range v.elements {
+ if element.Name() == sub.Name() {
+ return fmt.Errorf("view already added")
+ }
+ }
+ v.elements = append(v.elements, sub)
+ return nil
+}
+
+func (v *Vertical) Name() string {
+ return view.IdentityNone
+}
+
+func (v *Vertical) IsVisible() bool {
+ return v.visible
+}
+
+func (v *Vertical) Height() (height int) {
+ for _, element := range v.elements {
+ height += element.Height()
+ if height == view.HeightFull {
+ return view.HeightFull
+ }
+ }
+ return
+}
+
+func (v *Vertical) Width() int {
+ return v.width
+}
diff --git a/runtime/ui/layout/view.go b/runtime/ui/layout/view.go
new file mode 100644
index 0000000..62c5ef0
--- /dev/null
+++ b/runtime/ui/layout/view.go
@@ -0,0 +1,10 @@
+package layout
+
+import (
+ "github.com/wagoodman/dive/runtime/ui/view"
+)
+
+type View interface {
+ view.Identifiable
+ view.Dimensional
+}
diff --git a/runtime/ui/layout_manager.go b/runtime/ui/layout_manager.go
index 72f8c26..95b227b 100644
--- a/runtime/ui/layout_manager.go
+++ b/runtime/ui/layout_manager.go
@@ -6,13 +6,15 @@ import (
"github.com/spf13/viper"
)
-type layoutManager struct {
+var lastY, lastX int
+
+type LayoutManager struct {
fileTreeSplitRatio float64
- controllers *Controller
+ controller *Controller
}
// todo: this needs a major refactor (derive layout from view obj info, which should not live here)
-func newLayoutManager(c *Controller) *layoutManager {
+func NewLayoutManager(c *Controller) *LayoutManager {
fileTreeSplitRatio := viper.GetFloat64("filetree.pane-width")
if fileTreeSplitRatio >= 1 || fileTreeSplitRatio <= 0 {
@@ -20,14 +22,14 @@ func newLayoutManager(c *Controller) *layoutManager {
fileTreeSplitRatio = 0.5
}
- return &layoutManager{
+ return &LayoutManager{
fileTreeSplitRatio: fileTreeSplitRatio,
- controllers: c,
+ controller: c,
}
}
-// IsNewView determines if a view has already been created based on the set of errors given (a bit hokie)
-func IsNewView(errs ...error) bool {
+// isNewView determines if a view has already been created based on the set of errors given (a bit hokie)
+func isNewView(errs ...error) bool {
for _, err := range errs {
if err == nil {
return false
@@ -41,7 +43,7 @@ func IsNewView(errs ...error) bool {
// layout defines the definition of the window pane size and placement relations to one another. This
// is invoked at application start and whenever the screen dimensions change.
-func (lm *layoutManager) layout(g *gocui.Gui) error {
+func (lm *LayoutManager) Layout(g *gocui.Gui) error {
// TODO: this logic should be refactored into an abstraction that takes care of the math for us
maxX, maxY := g.Size()
@@ -64,12 +66,12 @@ func (lm *layoutManager) layout(g *gocui.Gui) error {
headerRows := 2
filterBarHeight := 1
- statusBarHeight := 1
+ helpBarHeight := 1
- statusBarIndex := 1
+ helpBarIndex := 1
filterBarIndex := 2
- layersHeight := len(lm.controllers.Layer.Layers) + headerRows + 1 // layers + header + base image layer row
+ layersHeight := len(lm.controller.Layer.Layers) + headerRows + 1 // layers + header + base image layer row
maxLayerHeight := int(0.75 * float64(maxY))
if layersHeight > maxLayerHeight {
layersHeight = maxLayerHeight
@@ -78,7 +80,7 @@ func (lm *layoutManager) layout(g *gocui.Gui) error {
var view, header *gocui.View
var viewErr, headerErr, err error
- if !lm.controllers.Filter.IsVisible() {
+ if !lm.controller.Filter.IsVisible() {
bottomRows--
filterBarHeight = 0
}
@@ -93,21 +95,21 @@ func (lm *layoutManager) layout(g *gocui.Gui) error {
}
// Layers
- view, viewErr = g.SetView(lm.controllers.Layer.Name(), -1, -1+headerRows, splitCols, layersHeight)
- header, headerErr = g.SetView(lm.controllers.Layer.Name()+"header", -1, -1, splitCols, headerRows)
- if IsNewView(viewErr, headerErr) {
- err = lm.controllers.Layer.Setup(view, header)
+ view, viewErr = g.SetView(lm.controller.Layer.Name(), -1, -1+headerRows, splitCols, layersHeight)
+ header, headerErr = g.SetView(lm.controller.Layer.Name()+"header", -1, -1, splitCols, headerRows)
+ if isNewView(viewErr, headerErr) {
+ err = lm.controller.Layer.Setup(view, header)
if err != nil {
logrus.Error("unable to setup layer controller", err)
return err
}
- if _, err = g.SetCurrentView(lm.controllers.Layer.Name()); err != nil {
+ if _, err = g.SetCurrentView(lm.controller.Layer.Name()); err != nil {
logrus.Error("unable to set view to layer", err)
return err
}
// since we are selecting the view, we should rerender to indicate it is selected
- err = lm.controllers.Layer.Render()
+ err = lm.controller.Layer.Render()
if err != nil {
logrus.Error("unable to render layer view", err)
return err
@@ -115,10 +117,10 @@ func (lm *layoutManager) layout(g *gocui.Gui) error {
}
// Details
- view, viewErr = g.SetView(lm.controllers.Details.Name(), -1, -1+layersHeight+headerRows, splitCols, maxY-bottomRows)
- header, headerErr = g.SetView(lm.controllers.Details.Name()+"header", -1, -1+layersHeight, splitCols, layersHeight+headerRows)
- if IsNewView(viewErr, headerErr) {
- err = lm.controllers.Details.Setup(view, header)
+ view, viewErr = g.SetView(lm.controller.Details.Name(), -1, -1+layersHeight+headerRows, splitCols, maxY-bottomRows)
+ header, headerErr = g.SetView(lm.controller.Details.Name()+"header", -1, -1+layersHeight, splitCols, layersHeight+headerRows)
+ if isNewView(viewErr, headerErr) {
+ err = lm.controller.Details.Setup(view, header)
if err != nil {
return err
}
@@ -126,39 +128,39 @@ func (lm *layoutManager) layout(g *gocui.Gui) error {
// Filetree
offset := 0
- if !lm.controllers.Tree.AreAttributesVisible() {
+ if !lm.controller.Tree.AreAttributesVisible() {
offset = 1
}
- view, viewErr = g.SetView(lm.controllers.Tree.Name(), splitCols, -1+headerRows-offset, debugCols, maxY-bottomRows)
- header, headerErr = g.SetView(lm.controllers.Tree.Name()+"header", splitCols, -1, debugCols, headerRows-offset)
- if IsNewView(viewErr, headerErr) {
- err = lm.controllers.Tree.Setup(view, header)
+ view, viewErr = g.SetView(lm.controller.Tree.Name(), splitCols, -1+headerRows-offset, debugCols, maxY-bottomRows)
+ header, headerErr = g.SetView(lm.controller.Tree.Name()+"header", splitCols, -1, debugCols, headerRows-offset)
+ if isNewView(viewErr, headerErr) {
+ err = lm.controller.Tree.Setup(view, header)
if err != nil {
logrus.Error("unable to setup tree controller", err)
return err
}
}
- err = lm.controllers.Tree.OnLayoutChange(resized)
+ err = lm.controller.Tree.OnLayoutChange(resized)
if err != nil {
logrus.Error("unable to setup layer controller onLayoutChange", err)
return err
}
- // Status Bar
- view, viewErr = g.SetView(lm.controllers.Status.Name(), -1, maxY-statusBarHeight-statusBarIndex, maxX, maxY-(statusBarIndex-1))
- if IsNewView(viewErr, headerErr) {
- err = lm.controllers.Status.Setup(view, nil)
+ // Help Bar
+ view, viewErr = g.SetView(lm.controller.Help.Name(), -1, maxY-helpBarHeight-helpBarIndex, maxX, maxY-(helpBarIndex-1))
+ if isNewView(viewErr, headerErr) {
+ err = lm.controller.Help.Setup(view, nil)
if err != nil {
- logrus.Error("unable to setup status controller", err)
+ logrus.Error("unable to setup help controller", err)
return err
}
}
// Filter Bar
- view, viewErr = g.SetView(lm.controllers.Filter.Name(), len(lm.controllers.Filter.HeaderStr())-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1))
- header, headerErr = g.SetView(lm.controllers.Filter.Name()+"header", -1, maxY-filterBarHeight-filterBarIndex, len(lm.controllers.Filter.HeaderStr()), maxY-(filterBarIndex-1))
- if IsNewView(viewErr, headerErr) {
- err = lm.controllers.Filter.Setup(view, header)
+ view, viewErr = g.SetView(lm.controller.Filter.Name(), len(lm.controller.Filter.HeaderStr())-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1))
+ header, headerErr = g.SetView(lm.controller.Filter.Name()+"header", -1, maxY-filterBarHeight-filterBarIndex, len(lm.controller.Filter.HeaderStr()), maxY-(filterBarIndex-1))
+ if isNewView(viewErr, headerErr) {
+ err = lm.controller.Filter.Setup(view, header)
if err != nil {
logrus.Error("unable to setup filter controller", err)
return err
diff --git a/runtime/ui/view/details.go b/runtime/ui/view/details.go
index 9cb5ce0..a60de7d 100644
--- a/runtime/ui/view/details.go
+++ b/runtime/ui/view/details.go
@@ -43,6 +43,14 @@ func NewDetailsView(name string, gui *gocui.Gui, efficiency float64, inefficienc
return controller
}
+func (c *Details) Height() int {
+ return HeightFull
+}
+
+func (c *Details) Width() int {
+ return WidthFull
+}
+
func (c *Details) Name() string {
return c.name
}
diff --git a/runtime/ui/view/filetree.go b/runtime/ui/view/filetree.go
index 3185be4..107aeb7 100644
--- a/runtime/ui/view/filetree.go
+++ b/runtime/ui/view/filetree.go
@@ -175,6 +175,14 @@ func (c *FileTree) Setup(v *gocui.View, header *gocui.View) error {
return nil
}
+func (c *FileTree) Height() int {
+ return HeightFull
+}
+
+func (c *FileTree) Width() int {
+ return WidthFull
+}
+
// IsVisible indicates if the file tree view pane is currently initialized
func (c *FileTree) IsVisible() bool {
return c != nil
diff --git a/runtime/ui/view/filter.go b/runtime/ui/view/filter.go
index e32957a..bb4a0ef 100644
--- a/runtime/ui/view/filter.go
+++ b/runtime/ui/view/filter.go
@@ -43,6 +43,15 @@ func (c *Filter) AddFilterEditListener(listener ...FilterEditListener) {
c.filterEditListeners = append(c.filterEditListeners, listener...)
}
+func (c *Filter) Height() int {
+ return 1
+}
+
+func (c *Filter) Width() int {
+ return WidthFull
+}
+
+
func (c *Filter) Name() string {
return c.name
}
diff --git a/runtime/ui/view/status.go b/runtime/ui/view/help.go
index bdd0363..486e328 100644
--- a/runtime/ui/view/status.go
+++ b/runtime/ui/view/help.go
@@ -10,21 +10,21 @@ import (
"github.com/jroimartin/gocui"
)
-// Status holds the UI objects and data models for populating the bottom-most pane. Specifically the panel
+// Help holds the UI objects and data models for populating the bottom-most pane. Specifically the panel
// shows the user a set of possible actions to take in the window and currently selected pane.
-type Status struct {
+type Help struct {
name string
gui *gocui.Gui
view *gocui.View
- selectedView Renderer
+ selectedView View
helpKeys []*key.Binding
}
-// NewStatusView creates a new view object attached the the global [gocui] screen object.
-func NewStatusView(name string, gui *gocui.Gui) (controller *Status) {
- controller = new(Status)
+// NewHelpView creates a new view object attached the the global [gocui] screen object.
+func NewHelpView(name string, gui *gocui.Gui) (controller *Help) {
+ controller = new(Help)
// populate main fields
controller.name = name
@@ -34,20 +34,28 @@ func NewStatusView(name string, gui *gocui.Gui) (controller *Status) {
return controller
}
-func (c *Status) SetCurrentView(r Renderer) {
+func (c *Help) SetCurrentView(r View) {
c.selectedView = r
}
-func (c *Status) Name() string {
+func (c *Help) Height() int {
+ return 1
+}
+
+func (c *Help) Width() int {
+ return WidthFull
+}
+
+func (c *Help) Name() string {
return c.name
}
-func (c *Status) AddHelpKeys(keys ...*key.Binding) {
+func (c *Help) AddHelpKeys(keys ...*key.Binding) {
c.helpKeys = append(c.helpKeys, keys...)
}
// Setup initializes the UI concerns within the context of a global [gocui] view object.
-func (c *Status) Setup(v *gocui.View, header *gocui.View) error {
+func (c *Help) Setup(v *gocui.View, header *gocui.View) error {
// set controller options
c.view = v
@@ -57,27 +65,27 @@ func (c *Status) Setup(v *gocui.View, header *gocui.View) error {
}
// IsVisible indicates if the status view pane is currently initialized.
-func (c *Status) IsVisible() bool {
+func (c *Help) IsVisible() bool {
return c != nil
}
// CursorDown moves the cursor down in the details pane (currently indicates nothing).
-func (c *Status) CursorDown() error {
+func (c *Help) CursorDown() error {
return nil
}
// CursorUp moves the cursor up in the details pane (currently indicates nothing).
-func (c *Status) CursorUp() error {
+func (c *Help) CursorUp() error {
return nil
}
// Update refreshes the state objects for future rendering (currently does nothing).
-func (c *Status) Update() error {
+func (c *Help) Update() error {
return nil
}
// Render flushes the state objects to the screen.
-func (c *Status) Render() error {
+func (c *Help) Render() error {
c.gui.Update(func(g *gocui.Gui) error {
c.view.Clear()
@@ -97,7 +105,7 @@ func (c *Status) Render() error {
}
// KeyHelp indicates all the possible global actions a user can take when any pane is selected.
-func (c *Status) KeyHelp() string {
+func (c *Help) KeyHelp() string {
var help string
for _, binding := range c.helpKeys {
help += binding.RenderKeyHelp()
diff --git a/runtime/ui/view/layer.go b/runtime/ui/view/layer.go
index 5d3c678..e578dc5 100644
--- a/runtime/ui/view/layer.go
+++ b/runtime/ui/view/layer.go
@@ -79,6 +79,15 @@ func (c *Layer) notifyLayerChangeListeners() error {
return nil
}
+func (c *Layer) Height() int {
+ return HeightFull
+}
+
+func (c *Layer) Width() int {
+ return WidthFull
+}
+
+
func (c *Layer) Name() string {
return c.name
}
diff --git a/runtime/ui/view/renderer.go b/runtime/ui/view/renderer.go
deleted file mode 100644
index c3fadf5..0000000
--- a/runtime/ui/view/renderer.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package view
-
-// Controller defines the a renderable terminal screen pane.
-type Renderer interface {
- Update() error
- Render() error
- IsVisible() bool
- KeyHelp() string
-}
diff --git a/runtime/ui/view/view.go b/runtime/ui/view/view.go
new file mode 100644
index 0000000..c6d9d1a
--- /dev/null
+++ b/runtime/ui/view/view.go
@@ -0,0 +1,28 @@
+package view
+
+const (
+ HeightFull = -1
+ WidthFull = -1
+ IdentityNone = ""
+)
+
+
+type Identifiable interface {
+ Name() string
+}
+
+type Dimensional interface {
+ IsVisible() bool
+ Height() int
+ Width() int
+}
+
+
+// View defines the an element with state that can be updated, queried if visible, and render elements to the screen
+type View interface {
+ Identifiable
+ Dimensional
+ Update() error
+ Render() error
+ KeyHelp() string
+}