summaryrefslogtreecommitdiffstats
path: root/runtime/ui/layout/compound/layer_details_column.go
blob: 3981442f8a6a192d3532129d9e7cb713a8de3e19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package compound

import (
	"github.com/awesome-gocui/gocui"
	"github.com/sirupsen/logrus"
	"github.com/wagoodman/dive/runtime/ui/view"
	"github.com/wagoodman/dive/utils"
)

type LayerDetailsCompoundLayout struct {
	layer               *view.Layer
	details             *view.Details
	constrainRealEstate bool
}

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

func (cl *LayerDetailsCompoundLayout) Name() string {
	return "layer-details-compound-column"
}

// OnLayoutChange is called whenever the screen dimensions are changed
func (cl *LayerDetailsCompoundLayout) OnLayoutChange() error {
	err := cl.layer.OnLayoutChange()
	if err != nil {
		logrus.Error("unable to setup layer controller onLayoutChange", err)
		return err
	}

	err = cl.details.OnLayoutChange()
	if err != nil {
		logrus.Error("unable to setup 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

	// 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
	}

	// 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)

	// 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)

	if utils.IsNewView(viewErr, headerErr) {
		err := cl.layer.Setup(main, header)
		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)
			return err
		}
	}

	////////////////////////////////////////////////////////////////////////////////////
	// 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
		}

	}

	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 {
			return err
		}
	}

	return nil
}

func (cl *LayerDetailsCompoundLayout) RequestedSize(available int) *int {
	// "available" is the entire screen real estate, so we can guess when its a bit too small and take action.
	// This isn't perfect, but it gets the job done for now without complicated layout constraint solvers
	if available < 90 {
		cl.layer.ConstrainLayout()
		cl.constrainRealEstate = true
		size := 8
		return &size
	}
	cl.layer.ExpandLayout()
	cl.constrainRealEstate = false
	return nil
}

// todo: make this variable based on the nested views
func (cl *LayerDetailsCompoundLayout) IsVisible() bool {
	return true
}