Repository: pterm/pterm
Pull request #119 (2020-11-02 23:26:04)

feat: add `PanelPrinter`
### Description
Describe your work here.

### Scope
> What is affected by this pull request?

- [ ] Bug Fix
- [x] New Feature
- [ ] Documentation
- [ ] Other

### To-Do Checklist
- [x] I tested my changes
- [x] I have commented every method that I created/changed
- [x] I updated the examples to fit with my changes
- [x] I have added tests for my newly created methods



Patch:
diff --git a/_examples/panel/main.go b/_examples/panel/main.go
new file mode 100644
index 00000000..48f1dd42
--- /dev/null
+++ b/_examples/panel/main.go
@@ -0,0 +1,14 @@
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+	// Declare panels in a two dimensional grid system.
+	panels := pterm.Panels{
+		{{Data: "This is the first panel"}, {Data: "This is the second panel"}, {Data: "This\npanel\ncontains\nmultiple\nlines"}},
+		{{Data: "This is another panel line"}, {Data: "This is the second panel\nwith a new line"}},
+	}
+
+	// Print panels.
+	_ = pterm.DefaultPanel.WithPanels(panels).WithPadding(5).Render()
+}
diff --git a/bigtext_printer.go b/bigtext_printer.go
index b5615b41..8967ee5c 100644
--- a/bigtext_printer.go
+++ b/bigtext_printer.go
@@ -98,7 +98,7 @@ func (p BigTextPrinter) Srender() (string, error) {
 		for _, letter := range bigLetters {
 			var letterLine string
 			letterLines := strings.Split(letter.String, "\n")
-			maxLetterWidth := internal.GetMaxStringMaxWidth(letter.String)
+			maxLetterWidth := internal.GetStringMaxWidth(letter.String)
 			if len(letterLines) > i {
 				letterLine = letterLines[i]
 			}
diff --git a/internal/max_text_width.go b/internal/max_text_width.go
index a6391ad7..f57d9baa 100644
--- a/internal/max_text_width.go
+++ b/internal/max_text_width.go
@@ -5,8 +5,8 @@ import (
 	"strings"
 )
 
-// GetMaxStringMaxWidth returns the maximum width of a string with multiple lines.
-func GetMaxStringMaxWidth(s string) int {
+// GetStringMaxWidth returns the maximum width of a string with multiple lines.
+func GetStringMaxWidth(s string) int {
 	var max int
 	ss := strings.Split(s, "\n")
 	for _, s2 := range ss {
diff --git a/panel_printer.go b/panel_printer.go
new file mode 100644
index 00000000..498861a2
--- /dev/null
+++ b/panel_printer.go
@@ -0,0 +1,90 @@
+package pterm
+
+import (
+	"strings"
+
+	"github.com/mattn/go-runewidth"
+
+	"github.com/pterm/pterm/internal"
+)
+
+// Panel contains the data, which should be printed inside a PanelPrinter.
+type Panel struct {
+	Data string
+}
+
+// Panels is a two dimensional coordinate system for Panel.
+type Panels [][]Panel
+
+// DefaultPanel is the default PanelPrinter.
+var DefaultPanel = PanelPrinter{
+	Padding: 1,
+}
+
+// PanelPrinter prints content in boxes.
+type PanelPrinter struct {
+	Panels  Panels
+	Padding int
+}
+
+// WithPanels returns a new PanelPrinter with specific options.
+func (p PanelPrinter) WithPanels(panels Panels) *PanelPrinter {
+	p.Panels = panels
+	return &p
+}
+
+// WithPadding returns a new PanelPrinter with specific options.
+func (p PanelPrinter) WithPadding(padding int) *PanelPrinter {
+	p.Padding = padding
+	return &p
+}
+
+// Srender renders the Template as a string.
+func (p PanelPrinter) Srender() (string, error) {
+	var ret string
+
+	for _, boxLine := range p.Panels {
+		var maxHeight int
+
+		for _, box := range boxLine {
+			height := len(strings.Split(box.Data, "\n"))
+			if height > maxHeight {
+				maxHeight = height
+			}
+		}
+
+		var renderedPanels []string
+
+		for _, box := range boxLine {
+			renderedPanels = append(renderedPanels, box.Data)
+		}
+
+		for i := 0; i <= maxHeight; i++ {
+			for _, letter := range renderedPanels {
+				var letterLine string
+				letterLines := strings.Split(letter, "\n")
+				maxLetterWidth := internal.GetStringMaxWidth(letter)
+				if len(letterLines) > i {
+					letterLine = letterLines[i]
+				}
+				letterLineLength := runewidth.StringWidth(letterLine)
+				if letterLineLength < maxLetterWidth {
+					letterLine += strings.Repeat(" ", maxLetterWidth-letterLineLength)
+				}
+				letterLine += strings.Repeat(" ", p.Padding)
+				ret += letterLine
+			}
+			ret += "\n"
+		}
+	}
+
+	return ret, nil
+}
+
+// Render prints the Template to the terminal.
+func (p PanelPrinter) Render() error {
+	s, _ := p.Srender()
+	Println(s)
+
+	return nil
+}


Test patch:
diff --git a/panel_printer_test.go b/panel_printer_test.go
new file mode 100644
index 00000000..1bc90542
--- /dev/null
+++ b/panel_printer_test.go
@@ -0,0 +1,62 @@
+package pterm
+
+import (
+	"io"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/pterm/pterm/internal"
+)
+
+func TestPanelPrinterNilPrint(t *testing.T) {
+	p := PanelPrinter{}
+	err := p.Render()
+	assert.NoError(t, err)
+}
+
+func TestPanelPrinterNilPrintWithPanels(t *testing.T) {
+	panels := Panels{
+		{
+			{Data: "Hello, World"},
+		},
+	}
+	p := PanelPrinter{}.WithPanels(panels)
+	err := p.Render()
+	assert.NoError(t, err)
+}
+
+func TestPanelPrinter_Render(t *testing.T) {
+	internal.TestPrintContains(t, func(w io.Writer, a interface{}) {
+		panels := Panels{
+			{
+				{Data: Sprint(a)},
+			},
+		}
+		p := PanelPrinter{}.WithPanels(panels)
+		err := p.Render()
+		assert.NoError(t, err)
+	})
+}
+
+func TestPanelPrinter_WithPanels(t *testing.T) {
+	panels := Panels{
+		{
+			{Data: "Hello, World!"},
+		},
+	}
+	p := PanelPrinter{}
+	p2 := p.WithPanels(panels)
+
+	assert.Equal(t, panels, p2.Panels)
+	assert.Empty(t, p.Panels)
+}
+
+func TestPanelPrinter_WithPadding(t *testing.T) {
+	padding := 1337
+	p := PanelPrinter{}
+	p2 := p.WithPadding(padding)
+
+	assert.Equal(t, padding, p2.Padding)
+	assert.Empty(t, p.Padding)
+}