From 72d7cc8280573096e34ef5d512f1f14525887a61 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Wed, 16 Oct 2019 17:41:29 -0500 Subject: [PATCH] Switching browser screen to buffer data then display --- main.go | 2 +- screen.go | 10 +++- screen_about.go | 51 ++++++++++------ screen_browser.go | 144 +++++++++++++++++++++++++++++----------------- 4 files changed, 133 insertions(+), 74 deletions(-) diff --git a/main.go b/main.go index b235361..0ad828e 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( ) var ProgramName = "boltbrowser" -var VersionNum = 1.2 +var VersionNum = 2.0 var databaseFiles []string var db *bolt.DB diff --git a/screen.go b/screen.go index 2bd175e..7bf5637 100644 --- a/screen.go +++ b/screen.go @@ -19,9 +19,7 @@ const ( ) func defaultScreensForData(db *BoltDB) []Screen { - var viewPort ViewPort - - browserScreen := BrowserScreen{db: db, viewPort: viewPort} + browserScreen := BrowserScreen{db: db, rightViewPort: ViewPort{}, leftViewPort: ViewPort{}} aboutScreen := AboutScreen(0) screens := [...]Screen{ &browserScreen, @@ -41,3 +39,9 @@ func layoutAndDrawScreen(screen Screen, style Style) { screen.drawScreen(style) termbox.Flush() } + +type Line struct { + Text string + Fg termbox.Attribute + Bg termbox.Attribute +} diff --git a/screen_about.go b/screen_about.go index bd2c827..e072fe1 100644 --- a/screen_about.go +++ b/screen_about.go @@ -20,16 +20,9 @@ AboutScreen is just a basic 'int' type that we can extend to make this screen */ type AboutScreen int -func drawCommandsAtPoint(commands []Command, x int, y int, style Style) { - xPos, yPos := x, y - for index, cmd := range commands { - termboxUtil.DrawStringAtPoint(fmt.Sprintf("%6s", cmd.key), xPos, yPos, style.defaultFg, style.defaultBg) - termboxUtil.DrawStringAtPoint(cmd.description, xPos+8, yPos, style.defaultFg, style.defaultBg) - yPos++ - if index > 2 && index%2 == 1 { - yPos++ - } - } +func drawCommandAtPoint(cmd Command, xPos int, yPos int, style Style) { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("%6s", cmd.key), xPos, yPos, style.defaultFg, style.defaultBg) + termboxUtil.DrawStringAtPoint(cmd.description, xPos+8, yPos, style.defaultFg, style.defaultBg) } func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int { @@ -96,36 +89,58 @@ func (screen *AboutScreen) drawScreen(style Style) { yPos++ versionString := fmt.Sprintf("Version: %0.1f", VersionNum) termboxUtil.DrawStringAtPoint(versionString, (width-len(versionString))/2, yPos, style.defaultFg, style.defaultBg) - yPos++ - commands1 := [...]Command{ + commands1 := []Command{ {"h,←", "close parent"}, {"j,↓", "down"}, {"k,↑", "up"}, {"l,→", "open item"}, - + {"J", "scroll right pane down"}, + {"K", "scroll right pane up"}, + {"", ""}, {"g", "goto top"}, {"G", "goto bottom"}, + {"", ""}, {"ctrl+f", "jump down"}, {"ctrl+b", "jump up"}, } - commands2 := [...]Command{ + commands2 := []Command{ {"p,P", "create pair/at parent"}, {"b,B", "create bucket/at parent"}, {"e", "edit value of pair"}, {"r", "rename pair/bucket"}, + {"", ""}, + {"", ""}, {"D", "delete item"}, {"x,X", "export as string/json to file"}, - + {"", ""}, {"?", "this screen"}, {"q", "quit program"}, } - xPos = startX // + 20 + var maxCmd1 int + for k := range commands1 { + tst := len(commands1[k].key) + 1 + len(commands1[k].description) + if tst > maxCmd1 { + maxCmd1 = tst + } + } + var maxCmd2 int + for k := range commands2 { + tst := len(commands2[k].key) + 1 + len(commands2[k].description) + if tst > maxCmd2 { + maxCmd2 = tst + } + } + xPos = (width / 2) - ((maxCmd1 + maxCmd2) / 2) yPos++ - drawCommandsAtPoint(commands1[:], xPos, yPos+1, style) - drawCommandsAtPoint(commands2[:], xPos+20, yPos+1, style) + for k := range commands1 { + drawCommandAtPoint(commands1[k], xPos, yPos+1+k, style) + } + for k := range commands2 { + drawCommandAtPoint(commands2[k], xPos+40, yPos+1+k, style) + } exitTxt := "Press any key to return to browser" termboxUtil.DrawStringAtPoint(exitTxt, (width-len(exitTxt))/2, height-1, style.titleFg, style.titleBg) } diff --git a/screen_browser.go b/screen_browser.go index c0f325f..84a2850 100644 --- a/screen_browser.go +++ b/screen_browser.go @@ -17,6 +17,7 @@ type ViewPort struct { bytesPerRow int numberOfRows int firstRow int + scrollRow int } /* @@ -24,7 +25,8 @@ BrowserScreen holds all that's going on :D */ type BrowserScreen struct { db *BoltDB - viewPort ViewPort + leftViewPort ViewPort + rightViewPort ViewPort queuedCommand string currentPath []string currentType int @@ -37,6 +39,9 @@ type BrowserScreen struct { rightPaneHeight int rightPaneCursor int + + leftPaneBuffer []Line + rightPaneBuffer []Line } /* @@ -480,10 +485,15 @@ func (screen *BrowserScreen) moveCursorDown() bool { return false } func (screen *BrowserScreen) moveRightPaneUp() bool { + if screen.rightViewPort.scrollRow > 0 { + screen.rightViewPort.scrollRow-- + return true + } return false } func (screen *BrowserScreen) moveRightPaneDown() bool { - return false + screen.rightViewPort.scrollRow++ + return true } func (screen *BrowserScreen) performLayout() {} @@ -528,41 +538,35 @@ func (screen *BrowserScreen) drawFooter(style Style) { _, height := termbox.Size() termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, style.defaultFg, style.defaultBg) } + +func (screen *BrowserScreen) buildLeftPane(style Style) { + screen.leftPaneBuffer = nil + if len(screen.currentPath) == 0 { + screen.currentPath = screen.db.getNextVisiblePath(nil) + } + curPathSpot := 0 + visPaths, err := screen.db.buildVisiblePathSlice() + if err == nil { + for idx, pth := range visPaths { + screen.leftPaneBuffer + } + } +} + func (screen *BrowserScreen) drawLeftPane(style Style) { + screen.buildLeftPane(style) w, h := termbox.Size() if w > 80 { w = w / 2 } - screen.viewPort.numberOfRows = h - 2 - + screen.leftViewPort.bytesPerRow = w + screen.leftViewPort.numberOfRows = h - 2 termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg) y := 2 - screen.viewPort.firstRow = y - if len(screen.currentPath) == 0 { - screen.currentPath = screen.db.getNextVisiblePath(nil) - } - - // So we know how much of the tree _wants_ to be visible - // we only have screen.viewPort.numberOfRows of space though - curPathSpot := 0 - visPaths, err := screen.db.buildVisiblePathSlice() - if err == nil { - for idx, pth := range visPaths { - isCurPath := true - for i := range pth { - if len(screen.currentPath) > i && pth[i] != screen.currentPath[i] { - isCurPath = false - break - } - } - if isCurPath { - curPathSpot = idx - } - } - } + screen.leftViewPort.firstRow = y treeOffset := 0 - maxCursor := screen.viewPort.numberOfRows * 2 / 3 + maxCursor := screen.leftViewPort.numberOfRows * 2 / 3 if curPathSpot > maxCursor { treeOffset = curPathSpot - maxCursor } @@ -573,39 +577,71 @@ func (screen *BrowserScreen) drawLeftPane(style Style) { y += bktH } } + +func (screen *BrowserScreen) buildRightPane(style Style) { + screen.rightPaneBuffer = nil + b, p, err := screen.db.getGenericFromPath(screen.currentPath) + if err == nil { + if b != nil { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(b.GetPath()), " → ")), style.defaultFg, style.defaultBg}) + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Buckets: %d", len(b.buckets)), style.defaultFg, style.defaultBg}) + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Pairs: %d", len(b.pairs)), style.defaultFg, style.defaultBg}) + } else if p != nil { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(p.GetPath()), " → ")), style.defaultFg, style.defaultBg}) + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Key: %s", stringify([]byte(p.key))), style.defaultFg, style.defaultBg}) + + value := strings.Split(string(formatValue([]byte(p.val))), "\n") + if len(value) == 1 { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Value: %s", value[0]), style.defaultFg, style.defaultBg}) + } else { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{"Value:", style.defaultFg, style.defaultBg}) + for _, v := range value { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{v, style.defaultFg, style.defaultBg}) + } + } + } + } else { + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(screen.currentPath), " → ")), style.defaultFg, style.defaultBg}) + screen.rightPaneBuffer = append(screen.rightPaneBuffer, + Line{err.Error(), termbox.ColorRed, termbox.ColorBlack}) + } +} + func (screen *BrowserScreen) drawRightPane(style Style) { + screen.buildRightPane(style) w, h := termbox.Size() if w > 80 { + screen.rightViewPort.bytesPerRow = w / 2 + screen.rightViewPort.numberOfRows = h - 2 // Screen is wide enough, split it termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg) - termboxUtil.FillWithChar('|', (w / 2), screen.viewPort.firstRow-1, (w / 2), h, style.defaultFg, style.defaultBg) + termboxUtil.FillWithChar('|', (w / 2), screen.rightViewPort.firstRow-1, (w / 2), h, style.defaultFg, style.defaultBg) // Clear the right pane - termboxUtil.FillWithChar(' ', (w/2)+1, screen.viewPort.firstRow, w, h, style.defaultFg, style.defaultBg) + termboxUtil.FillWithChar(' ', (w/2)+1, screen.rightViewPort.firstRow, w, h, style.defaultFg, style.defaultBg) - b, p, err := screen.db.getGenericFromPath(screen.currentPath) startX := (w / 2) + 2 startY := 2 - if err == nil { - if b != nil { - pathString := fmt.Sprintf("Path: %s", strings.Join(stringifyPath(b.GetPath()), " → ")) - startY += screen.drawMultilineText(pathString, 6, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - bucketString := fmt.Sprintf("Buckets: %d", len(b.buckets)) - startY += screen.drawMultilineText(bucketString, 9, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - pairsString := fmt.Sprintf("Pairs: %d", len(b.pairs)) - startY += screen.drawMultilineText(pairsString, 7, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - } else if p != nil { - pathString := fmt.Sprintf("Path: %s", strings.Join(stringifyPath(p.GetPath()), " → ")) - startY += screen.drawMultilineText(pathString, 6, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - keyString := fmt.Sprintf("Key: %s", stringify([]byte(p.key))) - startY += screen.drawMultilineText(keyString, 5, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - valString := fmt.Sprintf("Value: %s", formatValue([]byte(p.val))) - startY += screen.drawMultilineText(valString, 7, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - } - } else { - pathString := fmt.Sprintf("Path: %s", strings.Join(stringifyPath(screen.currentPath), " → ")) - startY += screen.drawMultilineText(pathString, 6, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - startY += screen.drawMultilineText(err.Error(), 6, startX, startY, (w/2)-1, style.defaultFg, style.defaultBg) - } + maxScroll := len(screen.rightPaneBuffer)-screen.rightViewPort.numberOfRows + if maxScroll < 0 { + maxScroll = 0 + } + if screen.rightViewPort.scrollRow > maxScroll { + screen.rightViewPort.scrollRow = maxScroll + } + if len(screen.rightPaneBuffer) > 0 { + for k, v := range screen.rightPaneBuffer[screen.rightViewPort.scrollRow:] { + termboxUtil.DrawStringAtPoint(v.Text, startX, (startY + k - 1), v.Fg, v.Bg) + } + } } } @@ -631,6 +667,10 @@ func formatValueJSON(val []byte) ([]byte, error) { return out, nil } +func (screen *BrowserScreen) bucketToStrings(bkt *BoltBucket, style Style) []string { + +} + /* drawBucket * @bkt *BoltBucket - The bucket to draw * @style Style - The style to use