central header formatter
This commit is contained in:
parent
14706152a1
commit
8a3642a57f
@ -1,7 +1,39 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"github.com/lunixbochs/vtclean"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
//selectedLeftBracketStr = " "
|
||||
//selectedRightBracketStr = " "
|
||||
//selectedFillStr = " "
|
||||
//
|
||||
//leftBracketStr = "▏"
|
||||
//rightBracketStr = "▕"
|
||||
//fillStr = "─"
|
||||
|
||||
//selectedLeftBracketStr = " "
|
||||
//selectedRightBracketStr = " "
|
||||
//selectedFillStr = "━"
|
||||
//
|
||||
//leftBracketStr = "▏"
|
||||
//rightBracketStr = "▕"
|
||||
//fillStr = "─"
|
||||
|
||||
selectedLeftBracketStr = "┃"
|
||||
selectedRightBracketStr = "┣"
|
||||
selectedFillStr = "━"
|
||||
|
||||
leftBracketStr = "│"
|
||||
rightBracketStr = "├"
|
||||
fillStr = "─"
|
||||
|
||||
selectStr = " ● "
|
||||
//selectStr = " "
|
||||
)
|
||||
|
||||
var (
|
||||
@ -26,6 +58,19 @@ func init() {
|
||||
CompareBottom = color.New(color.BgGreen).SprintFunc()
|
||||
}
|
||||
|
||||
func RenderHeader(title string, width int, selected bool) string {
|
||||
if selected {
|
||||
body := Header(fmt.Sprintf("%s%s ", selectStr, title))
|
||||
bodyLen := len(vtclean.Clean(body, false))
|
||||
return fmt.Sprintf("%s%s%s%s\n", selectedLeftBracketStr, body, selectedRightBracketStr, strings.Repeat(selectedFillStr, width-bodyLen-2))
|
||||
//return fmt.Sprintf("%s%s%s%s\n", Selected(selectedLeftBracketStr), body, Selected(selectedRightBracketStr), Selected(strings.Repeat(selectedFillStr, width-bodyLen-2)))
|
||||
//return fmt.Sprintf("%s%s%s%s\n", Selected(selectedLeftBracketStr), body, Selected(selectedRightBracketStr), strings.Repeat(selectedFillStr, width-bodyLen-2))
|
||||
}
|
||||
body := Header(fmt.Sprintf(" %s ", title))
|
||||
bodyLen := len(vtclean.Clean(body, false))
|
||||
return fmt.Sprintf("%s%s%s%s\n", leftBracketStr, body, rightBracketStr, strings.Repeat(fillStr, width-bodyLen-2))
|
||||
}
|
||||
|
||||
func RenderHelpKey(control, title string, selected bool) string {
|
||||
if selected {
|
||||
return StatusSelected("▏") + StatusControlSelected(control) + StatusSelected(" "+title+" ")
|
||||
|
@ -23,8 +23,24 @@ func (cl *LayerDetailsCompoundLayout) Name() string {
|
||||
return "layer-details-compound-column"
|
||||
}
|
||||
|
||||
func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized bool) error {
|
||||
logrus.Debugf("view.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, cl.Name())
|
||||
// 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
|
||||
@ -55,12 +71,6 @@ func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, max
|
||||
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 = cl.layer.Render()
|
||||
if err != nil {
|
||||
logrus.Error("unable to render layer view", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -83,6 +93,7 @@ func (cl *LayerDetailsCompoundLayout) Layout(g *gocui.Gui, minX, minY, maxX, max
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,8 @@ import "github.com/jroimartin/gocui"
|
||||
|
||||
type Layout interface {
|
||||
Name() string
|
||||
Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized bool) error
|
||||
Layout(g *gocui.Gui, minX, minY, maxX, maxY int) error
|
||||
RequestedSize(available int) *int
|
||||
IsVisible() bool
|
||||
OnLayoutChange() error
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func (lm *Manager) Layout(g *gocui.Gui) error {
|
||||
}
|
||||
|
||||
// layout the header within the allocated space
|
||||
err := element.Layout(g, minX, minY, maxX, minY+height, hasResized)
|
||||
err := element.Layout(g, minX, minY, maxX, minY+height)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to layout '%s' header: %+v", element.Name(), err)
|
||||
}
|
||||
@ -138,7 +138,7 @@ func (lm *Manager) Layout(g *gocui.Gui) error {
|
||||
}
|
||||
|
||||
// layout the column within the allocated space
|
||||
err := element.Layout(g, minX, minY, minX+width, maxY, hasResized)
|
||||
err := element.Layout(g, minX, minY, minX+width, maxY)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to layout '%s' column: %+v", element.Name(), err)
|
||||
}
|
||||
@ -165,12 +165,24 @@ func (lm *Manager) Layout(g *gocui.Gui) error {
|
||||
// layout the footer within the allocated space
|
||||
// note: since the headers and rows are inclusive counting from -1 (to account for a border) we must
|
||||
// do the same vertically, thus a -1 is needed for a starting Y
|
||||
err := element.Layout(g, footerMinX, topY, footerMaxX, bottomY, hasResized)
|
||||
err := element.Layout(g, footerMinX, topY, footerMaxX, bottomY)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to layout '%s' footer: %+v", element.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify everyone of a layout change (allow to update and render)
|
||||
if hasResized {
|
||||
for _, elements := range lm.elements {
|
||||
for _, element := range elements {
|
||||
err := element.OnLayoutChange()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/lunixbochs/vtclean"
|
||||
)
|
||||
|
||||
// Details holds the UI objects and data models for populating the lower-left pane. Specifically the pane that
|
||||
@ -49,7 +48,7 @@ func (v *Details) Name() string {
|
||||
|
||||
// Setup initializes the UI concerns within the context of a global [gocui] view object.
|
||||
func (v *Details) Setup(view *gocui.View, header *gocui.View) error {
|
||||
logrus.Debugf("view.Setup() %s", v.Name())
|
||||
logrus.Tracef("view.Setup() %s", v.Name())
|
||||
|
||||
// set controller options
|
||||
v.view = view
|
||||
@ -99,6 +98,15 @@ func (v *Details) CursorUp() error {
|
||||
return CursorUp(v.gui, v.view)
|
||||
}
|
||||
|
||||
// OnLayoutChange is called whenever the screen dimensions are changed
|
||||
func (v *Details) OnLayoutChange() error {
|
||||
err := v.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.Render()
|
||||
}
|
||||
|
||||
// Update refreshes the state objects for future rendering.
|
||||
func (v *Details) Update() error {
|
||||
return nil
|
||||
@ -114,7 +122,7 @@ func (v *Details) SetCurrentLayer(layer *image.Layer) {
|
||||
// 3. the estimated wasted image space
|
||||
// 4. a list of inefficient file allocations
|
||||
func (v *Details) Render() error {
|
||||
logrus.Debugf("view.Render() %s", v.Name())
|
||||
logrus.Tracef("view.Render() %s", v.Name())
|
||||
|
||||
if v.currentLayer == nil {
|
||||
return fmt.Errorf("no layer selected")
|
||||
@ -149,10 +157,10 @@ func (v *Details) Render() error {
|
||||
v.header.Clear()
|
||||
width, _ := v.view.Size()
|
||||
|
||||
layerHeaderStr := fmt.Sprintf("[Layer Details]%s", strings.Repeat("─", width-15))
|
||||
imageHeaderStr := fmt.Sprintf("[Image Details]%s", strings.Repeat("─", width-15))
|
||||
layerHeaderStr := format.RenderHeader("Layer Details", width, false)
|
||||
imageHeaderStr := format.RenderHeader("Image Details", width, false)
|
||||
|
||||
_, err := fmt.Fprintln(v.header, format.Header(vtclean.Clean(layerHeaderStr, false)))
|
||||
_, err := fmt.Fprintln(v.header, layerHeaderStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -170,7 +178,7 @@ func (v *Details) Render() error {
|
||||
lines = append(lines, format.Header("Digest: ")+v.currentLayer.Digest)
|
||||
lines = append(lines, format.Header("Command:"))
|
||||
lines = append(lines, v.currentLayer.Command)
|
||||
lines = append(lines, "\n"+format.Header(vtclean.Clean(imageHeaderStr, false)))
|
||||
lines = append(lines, "\n"+imageHeaderStr)
|
||||
lines = append(lines, imageSizeStr)
|
||||
lines = append(lines, wastedSpaceStr)
|
||||
lines = append(lines, effStr+"\n")
|
||||
|
@ -2,18 +2,15 @@ package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wagoodman/dive/dive/filetree"
|
||||
"github.com/wagoodman/dive/runtime/ui/format"
|
||||
"github.com/wagoodman/dive/runtime/ui/key"
|
||||
"github.com/wagoodman/dive/runtime/ui/viewmodel"
|
||||
"github.com/wagoodman/dive/utils"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/lunixbochs/vtclean"
|
||||
"github.com/wagoodman/dive/dive/filetree"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -86,7 +83,7 @@ func (v *FileTree) areAttributesVisible() bool {
|
||||
|
||||
// Setup initializes the UI concerns within the context of a global [gocui] view object.
|
||||
func (v *FileTree) Setup(view *gocui.View, header *gocui.View) error {
|
||||
logrus.Debugf("view.Setup() %s", v.Name())
|
||||
logrus.Tracef("view.Setup() %s", v.Name())
|
||||
|
||||
// set controller options
|
||||
v.view = view
|
||||
@ -343,16 +340,12 @@ func (v *FileTree) toggleShowDiffType(diffType filetree.DiffType) error {
|
||||
}
|
||||
|
||||
// OnLayoutChange is called by the UI framework to inform the view-model of the new screen dimensions
|
||||
func (v *FileTree) OnLayoutChange(resized bool) error {
|
||||
func (v *FileTree) OnLayoutChange() error {
|
||||
err := v.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resized {
|
||||
return v.Render()
|
||||
}
|
||||
return nil
|
||||
return v.Render()
|
||||
}
|
||||
|
||||
// Update refreshes the state objects for future rendering.
|
||||
@ -371,24 +364,21 @@ func (v *FileTree) Update() error {
|
||||
|
||||
// Render flushes the state objects (file tree) to the pane.
|
||||
func (v *FileTree) Render() error {
|
||||
logrus.Debugf("view.Render() %s", v.Name())
|
||||
logrus.Tracef("view.Render() %s", v.Name())
|
||||
|
||||
title := v.title
|
||||
// indicate when selected
|
||||
if v.gui.CurrentView() == v.view {
|
||||
title = "● " + v.title
|
||||
}
|
||||
isSelected := v.gui.CurrentView() == v.view
|
||||
|
||||
v.gui.Update(func(g *gocui.Gui) error {
|
||||
// update the header
|
||||
v.header.Clear()
|
||||
width, _ := g.Size()
|
||||
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
|
||||
headerStr := format.RenderHeader(title, width, isSelected)
|
||||
if v.vm.ShowAttributes {
|
||||
headerStr += fmt.Sprintf(filetree.AttributeFormat+" %s", "P", "ermission", "UID:GID", "Size", "Filetree")
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(v.header, format.Header(vtclean.Clean(headerStr, false)))
|
||||
_, _ = fmt.Fprintln(v.header, headerStr, false)
|
||||
|
||||
// update the contents
|
||||
v.view.Clear()
|
||||
@ -412,8 +402,8 @@ func (v *FileTree) KeyHelp() string {
|
||||
return help
|
||||
}
|
||||
|
||||
func (v *FileTree) Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized bool) error {
|
||||
logrus.Debugf("view.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, v.Name())
|
||||
func (v *FileTree) 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, v.Name())
|
||||
attributeRowSize := 0
|
||||
if !v.areAttributesVisible() {
|
||||
attributeRowSize = 1
|
||||
@ -432,11 +422,6 @@ func (v *FileTree) Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized b
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := v.OnLayoutChange(hasResized)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup layer controller onLayoutChange", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ func (v *Filter) Name() string {
|
||||
|
||||
// Setup initializes the UI concerns within the context of a global [gocui] view object.
|
||||
func (v *Filter) Setup(view *gocui.View, header *gocui.View) error {
|
||||
logrus.Debugf("view.Setup() %s", v.Name())
|
||||
logrus.Tracef("view.Setup() %s", v.Name())
|
||||
|
||||
// set controller options
|
||||
v.view = view
|
||||
@ -153,7 +153,7 @@ func (v *Filter) Update() error {
|
||||
|
||||
// Render flushes the state objects to the screen. Currently this is the users path filter input.
|
||||
func (v *Filter) Render() error {
|
||||
logrus.Debugf("view.Render() %s", v.Name())
|
||||
logrus.Tracef("view.Render() %s", v.Name())
|
||||
|
||||
v.gui.Update(func(g *gocui.Gui) error {
|
||||
_, err := fmt.Fprintln(v.header, format.Header(v.labelStr))
|
||||
@ -170,8 +170,17 @@ func (v *Filter) KeyHelp() string {
|
||||
return format.StatusControlNormal("▏Type to filter the file tree ")
|
||||
}
|
||||
|
||||
func (v *Filter) Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized bool) error {
|
||||
logrus.Debugf("view.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, v.Name())
|
||||
// OnLayoutChange is called whenever the screen dimensions are changed
|
||||
func (v *Filter) OnLayoutChange() error {
|
||||
err := v.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.Render()
|
||||
}
|
||||
|
||||
func (v *Filter) 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, v.Name())
|
||||
|
||||
label, labelErr := g.SetView(v.Name()+"label", minX, minY, len(v.labelStr), maxY)
|
||||
view, viewErr := g.SetView(v.Name(), minX+(len(v.labelStr)-1), minY, maxX, maxY)
|
||||
|
@ -2,16 +2,13 @@ package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wagoodman/dive/dive/image"
|
||||
"github.com/wagoodman/dive/runtime/ui/format"
|
||||
"github.com/wagoodman/dive/runtime/ui/key"
|
||||
"github.com/wagoodman/dive/runtime/ui/viewmodel"
|
||||
"strings"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/lunixbochs/vtclean"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type LayerChangeListener func(viewmodel.LayerSelection) error
|
||||
@ -85,7 +82,7 @@ func (v *Layer) Name() string {
|
||||
|
||||
// Setup initializes the UI concerns within the context of a global [gocui] view object.
|
||||
func (v *Layer) Setup(view *gocui.View, header *gocui.View) error {
|
||||
logrus.Debugf("view.Setup() %s", v.Name())
|
||||
logrus.Tracef("view.Setup() %s", v.Name())
|
||||
|
||||
// set controller options
|
||||
v.view = view
|
||||
@ -275,6 +272,15 @@ func (v *Layer) renderCompareBar(layerIdx int) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// OnLayoutChange is called whenever the screen dimensions are changed
|
||||
func (v *Layer) OnLayoutChange() error {
|
||||
err := v.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.Render()
|
||||
}
|
||||
|
||||
// Update refreshes the state objects for future rendering (currently does nothing).
|
||||
func (v *Layer) Update() error {
|
||||
return nil
|
||||
@ -284,21 +290,19 @@ func (v *Layer) Update() error {
|
||||
// 1. the layers of the image + metadata
|
||||
// 2. the current selected image
|
||||
func (v *Layer) Render() error {
|
||||
logrus.Debugf("view.Render() %s", v.Name())
|
||||
logrus.Tracef("view.Render() %s", v.Name())
|
||||
|
||||
// indicate when selected
|
||||
title := "Layers"
|
||||
if v.gui.CurrentView() == v.view {
|
||||
title = "● " + title
|
||||
}
|
||||
isSelected := v.gui.CurrentView() == v.view
|
||||
|
||||
v.gui.Update(func(g *gocui.Gui) error {
|
||||
// update header
|
||||
v.header.Clear()
|
||||
width, _ := g.Size()
|
||||
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
|
||||
headerStr := format.RenderHeader(title, width, isSelected)
|
||||
headerStr += fmt.Sprintf("Cmp"+image.LayerFormat, "Size", "Command")
|
||||
_, err := fmt.Fprintln(v.header, format.Header(vtclean.Clean(headerStr, false)))
|
||||
_, err := fmt.Fprintln(v.header, headerStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (v *Status) AddHelpKeys(keys ...*key.Binding) {
|
||||
|
||||
// Setup initializes the UI concerns within the context of a global [gocui] view object.
|
||||
func (v *Status) Setup(view *gocui.View) error {
|
||||
logrus.Debugf("view.Setup() %s", v.Name())
|
||||
logrus.Tracef("view.Setup() %s", v.Name())
|
||||
|
||||
// set controller options
|
||||
v.view = view
|
||||
@ -80,9 +80,18 @@ func (v *Status) Update() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnLayoutChange is called whenever the screen dimensions are changed
|
||||
func (v *Status) OnLayoutChange() error {
|
||||
err := v.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.Render()
|
||||
}
|
||||
|
||||
// Render flushes the state objects to the screen.
|
||||
func (v *Status) Render() error {
|
||||
logrus.Debugf("view.Render() %s", v.Name())
|
||||
logrus.Tracef("view.Render() %s", v.Name())
|
||||
|
||||
v.gui.Update(func(g *gocui.Gui) error {
|
||||
v.view.Clear()
|
||||
@ -111,8 +120,8 @@ func (v *Status) KeyHelp() string {
|
||||
return help
|
||||
}
|
||||
|
||||
func (v *Status) Layout(g *gocui.Gui, minX, minY, maxX, maxY int, hasResized bool) error {
|
||||
logrus.Debugf("view.Layout(minX: %d, minY: %d, maxX: %d, maxY: %d) %s", minX, minY, maxX, maxY, v.Name())
|
||||
func (v *Status) 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, v.Name())
|
||||
|
||||
view, viewErr := g.SetView(v.Name(), minX, minY, maxX, maxY)
|
||||
if utils.IsNewView(viewErr) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user