Repository: pterm/pterm (go)

Issue:
Add title to `BoxPrinter`
The `BoxPrinter` should support a title property, set with `.WithTitle(title string)`.

It should also be supported to set the title position (`.WithTitlePosition(position string)`)

Valid positions should be:
```go
"tl", "top-left", "top left",
"tc", "top-center", "top center",
"tr", "top-right", "top right",
"bl", "bottom-left", "bottom left",
"bc", "bottom-center", "bottom center",
"br", "bottom-right", "bottom right"
```

If an invalid position is set, the printer should fallback to the default position, which is `"tl"`.

Current behavior:

```
┌──────────────────────────────────────────────────────────────────────────────────────────┐
| Lorem ipsum dolor sit amet, consetetur sadipscing elitr,                                 |
| sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,          |
| sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.              |
| Stet clita kasd gubergren, no sea takimata sanctus                                       |
| est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, |
| sed diam nonumy eirmod tempor invidunt ut labore et                                      |
| dolore magna aliquyam erat, sed diam voluptua.                                           |
└──────────────────────────────────────────────────────────────────────────────────────────┘
```

With title (`tl` position, for example):

```
┌─ This is the title ──────────────────────────────────────────────────────────────────────┐
| Lorem ipsum dolor sit amet, consetetur sadipscing elitr,                                 |
| sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,          |
| sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.              |
| Stet clita kasd gubergren, no sea takimata sanctus                                       |
| est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, |
| sed diam nonumy eirmod tempor invidunt ut labore et                                      |
| dolore magna aliquyam erat, sed diam voluptua.                                           |
└──────────────────────────────────────────────────────────────────────────────────────────┘
```

Pull request:
feat(boxprinter): add title & title position to `BoxPrinter`
### Description
Added a title & title position to `BoxPrinter`.

### Scope
> What is affected by this pull request?

- [ ] Bug Fix
- [x] New Feature
- [ ] Documentation
- [ ] Other

### Related Issue
Fixes #221 


### 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/box/main.go b/_examples/box/main.go
index 7d0f3738..f25a80e3 100644
--- a/_examples/box/main.go
+++ b/_examples/box/main.go
@@ -6,13 +6,13 @@ func main() {
 	pterm.Info.Println("This might not be rendered correctly on GitHub,\nbut it will work in a real terminal.\nThis is because GitHub does not use a monospaced font by default for SVGs.")
 
 	panel1 := pterm.DefaultBox.Sprint("Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt\nut labore et dolore\nmagna aliqua.")
-	panel2 := pterm.DefaultBox.Sprint("Ut enim ad minim veniam,\nquis nostrud exercitation\nullamco laboris\nnisi ut aliquip\nex ea commodo\nconsequat.")
-	panel3 := pterm.DefaultBox.Sprint("Duis aute irure\ndolor in reprehenderit\nin voluptate velit esse cillum\ndolore eu fugiat\nnulla pariatur.")
+	panel2 := pterm.DefaultBox.WithTitle("title").Sprint("Ut enim ad minim veniam,\nquis nostrud exercitation\nullamco laboris\nnisi ut aliquip\nex ea commodo\nconsequat.")
+	panel3 := pterm.DefaultBox.WithTitle("bottom center title").WithTitleBottomCenter().Sprint("Duis aute irure\ndolor in reprehenderit\nin voluptate velit esse cillum\ndolore eu fugiat\nnulla pariatur.")
 
 	panels, _ := pterm.DefaultPanel.WithPanels(pterm.Panels{
 		{{Data: panel1}, {Data: panel2}},
 		{{Data: panel3}},
 	}).Srender()
 
-	pterm.DefaultBox.WithRightPadding(0).WithBottomPadding(0).Println(panels)
+	pterm.DefaultBox.WithTitle("Lorem Ipsum").WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println(panels)
 }
diff --git a/box_printer.go b/box_printer.go
index 57094754..d9e005fd 100644
--- a/box_printer.go
+++ b/box_printer.go
@@ -10,6 +10,13 @@ import (
 
 // BoxPrinter is able to render a box around printables.
 type BoxPrinter struct {
+	Title                   string
+	TitleTopLeft            bool
+	TitleTopRight           bool
+	TitleTopCenter          bool
+	TitleBottomLeft         bool
+	TitleBottomRight        bool
+	TitleBottomCenter       bool
 	TextStyle               *Style
 	VerticalString          string
 	BoxStyle                *Style
@@ -38,6 +45,85 @@ var DefaultBox = BoxPrinter{
 	LeftPadding:             1,
 	TopPadding:              0,
 	BottomPadding:           0,
+	TitleTopLeft:            true,
+}
+
+// WithTitle returns a new box with a specific Title.
+func (p BoxPrinter) WithTitle(str string) *BoxPrinter {
+	p.Title = str
+	return &p
+}
+
+// WithTitleTopLeft returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleTopLeft(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = b2
+	p.TitleTopRight = false
+	p.TitleTopCenter = false
+	p.TitleBottomLeft = false
+	p.TitleBottomRight = false
+	p.TitleBottomCenter = false
+	return &p
+}
+
+// WithTitleTopRight returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleTopRight(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = false
+	p.TitleTopRight = b2
+	p.TitleTopCenter = false
+	p.TitleBottomLeft = false
+	p.TitleBottomRight = false
+	p.TitleBottomCenter = false
+	return &p
+}
+
+// WithTitleTopCenter returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleTopCenter(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = false
+	p.TitleTopRight = false
+	p.TitleTopCenter = b2
+	p.TitleBottomLeft = false
+	p.TitleBottomRight = false
+	p.TitleBottomCenter = false
+	return &p
+}
+
+// WithTitleBottomLeft returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleBottomLeft(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = false
+	p.TitleTopRight = false
+	p.TitleTopCenter = false
+	p.TitleBottomLeft = b2
+	p.TitleBottomRight = false
+	p.TitleBottomCenter = false
+	return &p
+}
+
+// WithTitleBottomRight returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleBottomRight(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = false
+	p.TitleTopRight = false
+	p.TitleTopCenter = false
+	p.TitleBottomLeft = false
+	p.TitleBottomRight = b2
+	p.TitleBottomCenter = false
+	return &p
+}
+
+// WithTitleBottomCenter returns a new box with a specific Title alignment.
+func (p BoxPrinter) WithTitleBottomCenter(b ...bool) *BoxPrinter {
+	b2 := internal.WithBoolean(b)
+	p.TitleTopLeft = false
+	p.TitleTopRight = false
+	p.TitleTopCenter = false
+	p.TitleBottomLeft = false
+	p.TitleBottomRight = false
+	p.TitleBottomCenter = b2
+	return &p
 }
 
 // WithBoxStyle returns a new box with a specific box Style.
@@ -134,10 +220,46 @@ func (p BoxPrinter) Sprint(a ...interface{}) string {
 		p.TextStyle = &ThemeDefault.BoxTextStyle
 	}
 	maxWidth := internal.GetStringMaxWidth(Sprint(a...))
-	topLine := p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
-		maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
-	bottomLine := p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
-		maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+
+	var topLine string
+	var bottomLine string
+
+	if p.Title == "" {
+		topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+			maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+		bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+			maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+	} else {
+		p.Title = strings.ReplaceAll(p.Title, "\n", " ")
+		if (maxWidth + p.RightPadding + p.LeftPadding - 4) < len(RemoveColorFromString(p.Title)) {
+			p.RightPadding = len(RemoveColorFromString(p.Title)) - (maxWidth + p.RightPadding + p.LeftPadding - 5)
+		}
+		if p.TitleTopLeft {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, true) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		} else if p.TitleTopRight {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, false) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		} else if p.TitleTopCenter {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLineCenter(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		} else if p.TitleBottomLeft {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, true) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		} else if p.TitleBottomRight {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, false) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		} else if p.TitleBottomCenter {
+			topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + strings.Repeat(p.BoxStyle.Sprint(p.HorizontalString),
+				maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.BottomLeftCornerString)
+			bottomLine = p.BoxStyle.Sprint(p.TopRightCornerString) + internal.AddTitleToLineCenter(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString)
+		}
+	}
 
 	boxString := strings.Repeat("\n", p.TopPadding) + Sprint(a...) + strings.Repeat("\n", p.BottomPadding)
 
diff --git a/docs/docs/printer/box.md b/docs/docs/printer/box.md
index 2ad5ce50..f44f73b7 100644
--- a/docs/docs/printer/box.md
+++ b/docs/docs/printer/box.md
@@ -40,6 +40,13 @@ pterm.DefaultBox.Println("test")
 |[RightPadding](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithRightPadding)|int|
 |[Text](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithText)|string|
 |[TextStyle](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTextStyle)|[*Style](https://pkg.go.dev/github.com/pterm/pterm#Style)|
+|[Title](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitle)|string|
+|[TitleBottomCenter](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleBottomCenter)|boolean|
+|[TitleBottomLeft](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleBottomLeft)|boolean|
+|[TitleBottomRight](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleBottomRight)|boolean|
+|[TitleTopCenter](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleTopCenter)|boolean|
+|[TitleTopLeft](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleTopLeft)|boolean|
+|[TitleTopRight](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTitleTopRight)|boolean|
 |[TopLeftCornerString](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTopLeftCornerString)|string|
 |[TopPadding](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTopPadding)|int|
 |[TopRightCornerString](https://pkg.go.dev/github.com/pterm/pterm#BoxPrinter.WithTopRightCornerString)|string|
diff --git a/internal/title_in_line.go b/internal/title_in_line.go
new file mode 100644
index 00000000..3aae546b
--- /dev/null
+++ b/internal/title_in_line.go
@@ -0,0 +1,30 @@
+package internal
+
+import (
+	"strings"
+
+	"github.com/gookit/color"
+)
+
+// AddTitleToLine adds a title to a site of a line ex: "─ This is the title ──────"
+func AddTitleToLine(title, line string, length int, left bool) string {
+	var ret string
+	if left {
+		ret += line + " " + title + " " + line + strings.Repeat(line, length-(4+len(color.ClearCode(title))))
+	} else {
+		ret += strings.Repeat(line, length-(4+len(color.ClearCode(title)))) + line + " " + title + " " + line
+	}
+
+	return ret
+}
+
+// AddTitleToLineCenter adds a title to the center of a line ex: "─ This is the title ──────"
+func AddTitleToLineCenter(title, line string, length int) string {
+	var ret string
+	repeatString := length - (4 + len(color.ClearCode(title)))
+	unevenRepeatString := repeatString % 2
+
+	ret += strings.Repeat(line, repeatString/2) + line + " " + title + " " + line + strings.Repeat(line, repeatString/2+unevenRepeatString)
+
+	return ret
+}


Test patch:
diff --git a/box_printer_test.go b/box_printer_test.go
index 4fb9fa5b..4cd25f1d 100644
--- a/box_printer_test.go
+++ b/box_printer_test.go
@@ -46,6 +46,12 @@ func TestBoxPrinterPrintMethods(t *testing.T) {
 		})
 	})
 
+	t.Run("SprintWithTitle", func(t *testing.T) {
+		testSprintContains(t, func(a interface{}) string {
+			return p.WithTitle("a").Sprint(a)
+		})
+	})
+
 	t.Run("Sprintf", func(t *testing.T) {
 		testSprintfContains(t, func(format string, a interface{}) string {
 			return p.Sprintf(format, a)
@@ -109,6 +115,104 @@ func TestBoxPrinter_WithBottomRightCornerString(t *testing.T) {
 	assert.Empty(t, p.BottomRightCornerString)
 }
 
+func TestBoxPrinter_WithTitle(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitle("-")
+
+	assert.Equal(t, "-", p2.Title)
+	assert.Empty(t, p.Title)
+}
+
+func TestBoxPrinter_WithTitleTopLeft(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopLeft()
+
+	assert.Equal(t, true, p2.TitleTopLeft)
+	assert.Equal(t, false, p.TitleTopLeft)
+}
+
+func TestBoxPrinter_WithTitleTopRight(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopRight()
+
+	assert.Equal(t, true, p2.TitleTopRight)
+	assert.Equal(t, false, p.TitleTopRight)
+}
+
+func TestBoxPrinter_WithTitleTopCenter(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopCenter()
+
+	assert.Equal(t, true, p2.TitleTopCenter)
+	assert.Equal(t, false, p.TitleTopCenter)
+}
+
+func TestBoxPrinter_WithTitleBottomRight(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomRight()
+
+	assert.Equal(t, true, p2.TitleBottomRight)
+	assert.Equal(t, false, p.TitleBottomRight)
+}
+
+func TestBoxPrinter_WithTitleBottomLeft(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomLeft()
+
+	assert.Equal(t, true, p2.TitleBottomLeft)
+	assert.Equal(t, false, p.TitleBottomLeft)
+}
+
+func TestBoxPrinter_WithTitleBottomCenter(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomCenter()
+
+	assert.Equal(t, true, p2.TitleBottomCenter)
+	assert.Equal(t, false, p.TitleBottomCenter)
+}
+
+func TestBoxPrinter_WithTitleWithTitleBottomLeft(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomLeft().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
+func TestBoxPrinter_WithTitleWithTitleTopLeft(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopLeft().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
+func TestBoxPrinter_WithTitleWithTitleBottomRight(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomRight().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
+func TestBoxPrinter_WithTitleWithTitleTopRight(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopRight().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
+func TestBoxPrinter_WithTitleWithTitleTopCenter(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleTopCenter().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
+func TestBoxPrinter_WithTitleWithTitleBottomCenter(t *testing.T) {
+	p := BoxPrinter{}
+	p2 := p.WithTitleBottomCenter().WithTitle("a").Sprint("Lorem Ipsum")
+
+	assert.Equal(t, "\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39mLorem Ipsum\x1b[0m\x1b[39m\x1b[0m\n\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m a \x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m\x1b[39m\x1b[0m", p2)
+}
+
 func TestBoxPrinter_WithBoxStyle(t *testing.T) {
 	p := BoxPrinter{}
 	s := NewStyle(FgRed, BgRed, Bold)