diff --git a/ui/filetreeview.go b/ui/filetreeview.go index 12d9517..681310b 100644 --- a/ui/filetreeview.go +++ b/ui/filetreeview.go @@ -82,16 +82,17 @@ func (view *FileTreeView) Setup(v *gocui.View, header *gocui.View) error { if err := view.gui.SetKeybinding(view.Name, gocui.KeyCtrlU, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.toggleShowDiffType(filetree.Unchanged) }); err != nil { return err } - if err := view.gui.SetKeybinding(view.Name, gocui.KeyCtrlSlash, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return nil }); err != nil { - return err - } - view.updateViewTree() + view.Update() view.Render() return nil } +func (view *FileTreeView) IsVisible() bool { + if view == nil {return false} + return true +} func (view *FileTreeView) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) error { @@ -117,7 +118,7 @@ func (view *FileTreeView) setTreeByLayer(bottomTreeStart, bottomTreeStop, topTre view.view.SetCursor(0, 0) view.TreeIndex = 0 view.ModelTree = newTree - view.updateViewTree() + view.Update() return view.Render() } @@ -155,7 +156,7 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) { } var filterBytes []byte var filterRegex *regexp.Regexp - read, err := Views.Command.view.Read(filterBytes) + read, err := Views.Filter.view.Read(filterBytes) if read > 0 && err == nil { regex, err := regexp.Compile(string(filterBytes)) if err == nil { @@ -185,7 +186,7 @@ func (view *FileTreeView) toggleCollapse() error { if node != nil { node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed } - view.updateViewTree() + view.Update() return view.Render() } @@ -194,15 +195,15 @@ func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error { view.view.SetCursor(0, 0) view.TreeIndex = 0 - view.updateViewTree() + view.Update() return view.Render() } func filterRegex() *regexp.Regexp { - if Views.Command == nil || Views.Command.view == nil { + if Views.Filter == nil || Views.Filter.view == nil { return nil } - filterString := strings.TrimSpace(Views.Command.view.Buffer()) + filterString := strings.TrimSpace(Views.Filter.view.Buffer()) if len(filterString) < 1 { return nil } @@ -215,7 +216,7 @@ func filterRegex() *regexp.Regexp { return regex } -func (view *FileTreeView) updateViewTree() { +func (view *FileTreeView) Update() error { regex := filterRegex() // keep the view selection in parity with the current DiffType selection @@ -242,6 +243,7 @@ func (view *FileTreeView) updateViewTree() { } return nil }, nil) + return nil; } func (view *FileTreeView) KeyHelp() string { @@ -275,8 +277,3 @@ func (view *FileTreeView) Render() error { }) return nil } - -func (view *FileTreeView) ReRender() error { - view.updateViewTree() - return view.Render() -} diff --git a/ui/commandview.go b/ui/filterview.go similarity index 52% rename from ui/commandview.go rename to ui/filterview.go index 14fe2be..a989cd3 100644 --- a/ui/commandview.go +++ b/ui/filterview.go @@ -8,11 +8,14 @@ import ( // with special thanks to https://gist.github.com/jroimartin/3b2e943a3811d795e0718b4a95b89bec -type CommandView struct { +type FilterView struct { Name string gui *gocui.Gui view *gocui.View + header *gocui.View + headerStr string maxLength int + hidden bool } type Input struct { @@ -22,17 +25,19 @@ type Input struct { maxLength int } -func NewCommandView(name string, gui *gocui.Gui) (commandview *CommandView) { - commandview = new(CommandView) +func NewFilterView(name string, gui *gocui.Gui) (filterview *FilterView) { + filterview = new(FilterView) // populate main fields - commandview.Name = name - commandview.gui = gui + filterview.Name = name + filterview.gui = gui + filterview.headerStr = "Path Filter: " + filterview.hidden = true - return commandview + return filterview } -func (view *CommandView) Setup(v *gocui.View, header *gocui.View) error { +func (view *FilterView) Setup(v *gocui.View, header *gocui.View) error { // set view options view.view = v @@ -41,6 +46,13 @@ func (view *CommandView) Setup(v *gocui.View, header *gocui.View) error { view.view.BgColor = gocui.ColorDefault + gocui.AttrReverse view.view.Editable = true view.view.Editor = view + + view.header = header + view.header.BgColor = gocui.ColorDefault + gocui.AttrReverse + view.header.Editable = false + view.header.Wrap = false + view.header.Frame = false + // set keybindings // if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil { // return err @@ -54,18 +66,27 @@ func (view *CommandView) Setup(v *gocui.View, header *gocui.View) error { return nil } -func (view *CommandView) CursorDown() error { +func (view *FilterView) IsVisible() bool { + if view == nil {return false} + return !view.hidden +} + +func (view *FilterView) CursorDown() error { return nil } -func (view *CommandView) CursorUp() error { +func (view *FilterView) CursorUp() error { return nil } -func (i *CommandView) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { +func (view *FilterView) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { + if !view.IsVisible() { + return + } + cx, _ := v.Cursor() ox, _ := v.Origin() - limit := ox+cx+1 > i.maxLength + limit := ox+cx+1 > view.maxLength switch { case ch != 0 && mod == 0 && !limit: v.EditWrite(ch) @@ -75,17 +96,23 @@ func (i *CommandView) Edit(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modi v.EditDelete(true) } if Views.Tree != nil { - Views.Tree.ReRender() + Views.Tree.Update() + Views.Tree.Render() } } -func (view *CommandView) KeyHelp() string { - return "Type string to filter" +func (view *FilterView) KeyHelp() string { + return Formatting.Control("Type string to filter the file tree") } -func (view *CommandView) Render() error { +func (view *FilterView) Update() error { + return nil +} + +func (view *FilterView) Render() error { view.gui.Update(func(g *gocui.Gui) error { - fmt.Fprintln(view.view, "") + // render the header + fmt.Fprintln(view.header, Formatting.Header(view.headerStr)) return nil }) diff --git a/ui/layerview.go b/ui/layerview.go index 4b9f30f..a2b28b3 100644 --- a/ui/layerview.go +++ b/ui/layerview.go @@ -64,6 +64,11 @@ func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error { return view.Render() } +func (view *LayerView) IsVisible() bool { + if view == nil {return false} + return true +} + func (view *LayerView) setCompareMode(compareMode CompareType) error { view.CompareMode = compareMode view.Render() @@ -120,10 +125,14 @@ func (view *LayerView) renderCompareBar(layerIdx int) string { return result } +func (view *LayerView) Update() error { + return nil +} + func (view *LayerView) Render() error { view.gui.Update(func(g *gocui.Gui) error { // update header - headerStr := fmt.Sprintf("Cmp "+image.LayerFormat, "Image ID", "Size", "Command") + headerStr := fmt.Sprintf("Cmp "+image.LayerFormat, "Image ID", "Size", "Filter") fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false))) // update contents diff --git a/ui/statusview.go b/ui/statusview.go index f6fd39b..67a3b29 100644 --- a/ui/statusview.go +++ b/ui/statusview.go @@ -29,19 +29,16 @@ func (view *StatusView) Setup(v *gocui.View, header *gocui.View) error { view.view.Frame = false view.view.BgColor = gocui.ColorDefault + gocui.AttrReverse - // set keybindings - // if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil { - // return err - // } - // if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil { - // return err - // } - view.Render() return nil } +func (view *StatusView) IsVisible() bool { + if view == nil {return false} + return true +} + func (view *StatusView) CursorDown() error { return nil } @@ -53,7 +50,11 @@ func (view *StatusView) CursorUp() error { func (view *StatusView) KeyHelp() string { return Formatting.Control("[^C]") + ": Quit " + Formatting.Control("[^Space]") + ": Switch View " + - Formatting.Control("[^/]") + ": Filter files" + Formatting.Control("[^/]") + ": Filter files" +} + +func (view *StatusView) Update() error { + return nil } func (view *StatusView) Render() error { diff --git a/ui/ui.go b/ui/ui.go index fa824e3..63b14fc 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -34,11 +34,11 @@ var Formatting struct { } var Views struct { - Tree *FileTreeView - Layer *LayerView - Status *StatusView - Command *CommandView - lookup map[string]View + Tree *FileTreeView + Layer *LayerView + Status *StatusView + Filter *FilterView + lookup map[string]View } type View interface { @@ -46,32 +46,44 @@ type View interface { CursorDown() error CursorUp() error Render() error + Update() error KeyHelp() string + IsVisible() bool } func toggleView(g *gocui.Gui, v *gocui.View) error { if v == nil || v.Name() == Views.Layer.Name { _, err := g.SetCurrentView(Views.Tree.Name) + Update() Render() return err } _, err := g.SetCurrentView(Views.Layer.Name) + Update() Render() return err } -func focusFilterView(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetCurrentView(Views.Command.Name) - Render() - return err -} +func toggleFilterView(g *gocui.Gui, v *gocui.View) error { + // delete all user input from the tree view + Views.Filter.view.Clear() + Views.Filter.view.SetCursor(0,0) -func returnToTreeView(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetCurrentView(Views.Tree.Name) - if Views.Tree != nil { - Views.Tree.ReRender() + // toggle hiding + Views.Filter.hidden = !Views.Filter.hidden + + if !Views.Filter.hidden { + _, err := g.SetCurrentView(Views.Filter.Name) + if err != nil { + return err + } + Update() + Render() + } else { + toggleView(g, v) } - return err + + return nil } func CursorDown(g *gocui.Gui, v *gocui.View) error { @@ -116,16 +128,10 @@ func keybindings(g *gocui.Gui) error { //if err := g.SetKeybinding("main", gocui.MouseLeft, gocui.ModNone, toggleCollapse); err != nil { // return err //} - if err := g.SetKeybinding("side", gocui.KeyCtrlSpace, gocui.ModNone, toggleView); err != nil { + if err := g.SetKeybinding("", gocui.KeyCtrlSpace, gocui.ModNone, toggleView); err != nil { return err } - if err := g.SetKeybinding("main", gocui.KeyCtrlSpace, gocui.ModNone, toggleView); err != nil { - return err - } - if err := g.SetKeybinding("", gocui.KeyCtrlSlash, gocui.ModNone, focusFilterView); err != nil { - return err - } - if err := g.SetKeybinding("command", gocui.KeyEnter, gocui.ModNone, returnToTreeView); err != nil { + if err := g.SetKeybinding("", gocui.KeyCtrlSlash, gocui.ModNone, toggleFilterView); err != nil { return err } @@ -144,6 +150,7 @@ func isNewView(errs ...error) bool { return true } +// TODO: this logic should be refactored into an abstraction that takes care of the math for us func layout(g *gocui.Gui) error { maxX, maxY := g.Size() splitCols := maxX / 2 @@ -154,9 +161,21 @@ func layout(g *gocui.Gui) error { debugCols := maxX - debugWidth bottomRows := 1 headerRows := 1 + + filterBarHeight := 1 + statusBarHeight := 1 + + statusBarIndex := 1 + filterBarIndex := 2 + var view, header *gocui.View var viewErr, headerErr, err error + if Views.Filter.hidden { + bottomRows-- + filterBarHeight = 0 + } + // Debug pane if debug { if _, err := g.SetView("debug", debugCols, -1, maxX, maxY-bottomRows); err != nil { @@ -185,24 +204,33 @@ func layout(g *gocui.Gui) error { } // Status Bar - view, viewErr = g.SetView(Views.Status.Name, -1, maxY-bottomRows-1, maxX, maxY) + view, viewErr = g.SetView(Views.Status.Name, -1, maxY-statusBarHeight-statusBarIndex, maxX, maxY-(statusBarIndex-1)) if isNewView(viewErr, headerErr) { Views.Status.Setup(view, nil) } - // Command Bar - view, viewErr = g.SetView(Views.Command.Name, -1, maxY-bottomRows-2, maxX, maxY-1) + // Filter Bar + view, viewErr = g.SetView(Views.Filter.Name, len(Views.Filter.headerStr)-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1)) + header, headerErr = g.SetView(Views.Filter.Name+"header", -1, maxY-filterBarHeight - filterBarIndex, len(Views.Filter.headerStr), maxY-(filterBarIndex-1)) if isNewView(viewErr, headerErr) { - debugPrint("Setup...") - Views.Command.Setup(view, nil) + Views.Filter.Setup(view, header) } + return nil } +func Update() { + for _, view := range Views.lookup { + view.Update() + } +} + func Render() { for _, view := range Views.lookup { - view.Render() + if view.IsVisible() { + view.Render() + } } } @@ -230,8 +258,8 @@ func Run(layers []*image.Layer, refTrees []*filetree.FileTree) { Views.Status = NewStatusView("status", g) Views.lookup[Views.Status.Name] = Views.Status - Views.Command = NewCommandView("command", g) - Views.lookup[Views.Command.Name] = Views.Command + Views.Filter = NewFilterView("command", g) + Views.lookup[Views.Filter.Name] = Views.Filter g.Cursor = false //g.Mouse = true