diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 9fbcf90..756b90d 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -16,7 +16,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
- go-version: ^1.13
+ go-version: ^1.15
id: go
- name: Check out code into the Go module directory
@@ -31,7 +31,7 @@ jobs:
fi
- name: Build
- run: go build -v .
+ run: go build -o box
- name: Test
run: go test -v .
diff --git a/README.md b/README.md
index c973f0d..24aa8b3 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,12 @@
-# Box CLI Maker ๐ฆ
+
+
Box CLI Maker ๐ฆ
+
+
+Box CLI Maker is a Highly Customized Terminal Box Creator.
+
+
+
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/Delta456/box-cli-maker)
[![CI](https://github.com/Delta456/box-cli-maker/workflows/Box%20CLI%20Maker/badge.svg)](https://github.com/Delta456/box-cli-maker/actions?query=workflow%3A"Box+CLI+Maker")
@@ -7,17 +14,16 @@
[![GolangCI](https://golangci.com/badges/github.com/moul/golang-repo-template.svg)](https://golangci.com/r/github.com/Delta456/box-cli-maker)
[![GitHub release](https://img.shields.io/github/release/Delta456/box-cli-maker.svg)](https://github.com/Delta456/box-cli-maker/releases)
-
-Box CLI Maker is a Highly Customized Terminal Box Creator.
+
## Features
-- Make Terminal Box in 8๏ธโฃ inbuilt different style
-- Color Support ๐จ
+- Make Terminal Box in 8๏ธโฃ inbuilt different styles
+- 16 Inbuilt Colors and Custom (24 bit) Colors Support ๐จ
- Custom Title Positions
- Make your own Terminal Box style ๐ฆ
- Align the text according to the need
-- Unicode and Emoji Support ๐
+- Unicode, Emoji and [Windows Console](https://en.wikipedia.org/wiki/Windows_Console) Support ๐
- Written in ๐ฌ ๐ด
## Installation
@@ -132,7 +138,7 @@ type Box struct {
BottomRight string // BottomRight corner used for Symbols
BottomLeft string // BotromLeft corner used for Symbols
Horizontal string // Symbols used for Horizontal Bars
- Con Config // Configuration for the Box to be made
+ Config // Configuration for the Box to be made
}
```
@@ -182,6 +188,12 @@ var fgHiColors = map[string]color.Attribute{
If you want High Intensity Colors then the Color name should start with `Hi`. If Color option is empty or invalid then Box with default Color is formed.
+It can even have custom color which can be provided in `[3]uint` and `uint` though the elements of the array must be in a range of `[0x0, 0xFF]` and `uint` must be in a range of `[0x000000, 0xFFFFFF]`.
+
+If you want to use the string representation of the `Box` and print them for [`Windows Console`](https://en.wikipedia.org/wiki/Windows_Console) then you would have to use `box.Output` as the passing stream to the respective functions.
+
+`Windows Console` is 4 bit (16 colors) so Custom Colors will not work for them but the `Box` will be printed correctly without the Color effect.
+
### Note
#### Vertical Alignment
@@ -203,9 +215,9 @@ I thank the following people and their packages whom I have studied and was able
- [thecodrr/boxx](https://github.com/thecodrr/boxx)
- [Atrox/box](https://github.com/Atrox/box)
-- [sindreorhus-cli-boxs](https://github.com/sindresorhus/cli-boxes)
+- [sindreorhus-cli-boxes](https://github.com/sindresorhus/cli-boxes)
-Special thanks to [@elimsteve](https://github.com/elimisteve) who helped me to optimize and tell me the best ways possible to fix my problems.
+Special thanks to [@elimsteve](https://github.com/elimisteve) who helped me to optimize the code and told me the best possible ways to fix my problems and [@JalonSolov](https://github.com/JalonSolov) for tab lines support.
Kudos to [moul/golang-repo-template](https://github.com/moul/golang-repo-template) for their Go template.
diff --git a/box.go b/box.go
index af67da4..8ac82ff 100644
--- a/box.go
+++ b/box.go
@@ -3,6 +3,7 @@ package box
import (
"fmt"
"os"
+ "runtime"
"strings"
"github.com/fatih/color"
@@ -11,10 +12,11 @@ import (
const (
n1 = "\n"
- // sep = separator, sp = spacing, ln = line; os = oddSpace; s = space
- centerAlign = "{sep}{sp}{ln}{os}{sp}{sep}"
- leftAlign = "{sep}{px}{ln}{os}{sp}{s}{sep}"
- rightAlign = "{sep}{sp}{os}{s}{ln}{px}{sep}"
+
+ // 1 = separator, 2 = spacing, 3 = line; 4 = oddSpace; 5 = space; 6 = sideMargin
+ centerAlign = "%[1]s%[2]s%[3]s%[4]s%[2]s%[1]s"
+ leftAlign = "%[1]s%[6]s%[3]s%[4]s%[2]s%[5]s%[1]s"
+ rightAlign = "%[1]s%[2]s%[4]s%[5]s%[3]s%[6]s%[1]s"
)
// Box struct defines the Box to be made.
@@ -25,44 +27,50 @@ type Box struct {
BottomRight string // BottomRight corner used for Symbols
BottomLeft string // BotromLeft corner used for Symbols
Horizontal string // Symbols used for Horizontal Bars
- Con Config // Config for the Box struct
+ Config // Config for the Box struct
}
// Config is the configuration for the Box struct
type Config struct {
- Py int // Horizontal Padding
- Px int // Vertical Padding
- ContentAlign string // Content Alignment inside Box
- Type string // Type of Box
- TitlePos string // Title Position
- Color string // Color of Box
+ Py int // Horizontal Padding
+ Px int // Vertical Padding
+ ContentAlign string // Content Alignment inside Box
+ Type string // Type of Box
+ TitlePos string // Title Position
+ Color interface{} // Color of Box
}
// New takes struct Config and returns the specified Box struct.
func New(config Config) Box {
- if _, ok := boxs[config.Type]; ok {
- BoxNew := boxs[config.Type]
- BoxNew.Con = config
- return BoxNew
+ // Default Box Type is Single
+ if config.Type == "" {
+ boxNew := boxes["Single"]
+ boxNew.Config = config
+ return boxNew
+ // Check if the Box Type provided is valid else panic
+ } else if _, ok := boxes[config.Type]; ok {
+ boxNew := boxes[config.Type]
+ boxNew.Config = config
+ return boxNew
}
panic("Invalid Box Type provided")
-
}
// String returns the string representation of Box.
func (b Box) String(title, lines string) string {
var lines2 []string
- // Default Position is Inside
- if b.Con.TitlePos == "" {
- b.Con.TitlePos = "Inside"
+ // Default Position is Inside, no warning for invalid TitlePos as it is done
+ // in toString() method
+ if b.TitlePos == "" {
+ b.TitlePos = "Inside"
}
// if Title is empty then TitlePos should be Inside
if title != "" {
- if b.Con.TitlePos != "Inside" && strings.Contains(title, "\n") {
+ if b.TitlePos != "Inside" && strings.Contains(title, "\n") {
panic("Multilines are only supported inside only")
}
- if b.Con.TitlePos == "Inside" {
+ if b.TitlePos == "Inside" {
lines2 = append(lines2, strings.Split(title, n1)...)
lines2 = append(lines2, []string{""}...) // for empty line between title and content
}
@@ -74,76 +82,88 @@ func (b Box) String(title, lines string) string {
// toString is same as String except that it is used for printing Boxes
func (b Box) toString(title string, lines []string) string {
titleLen := len(strings.Split(title, n1))
- sideMargin := strings.Repeat(" ", b.Con.Px)
- longestLine := longestLine(lines)
+ sideMargin := strings.Repeat(" ", b.Px)
+ longestLine, lines2 := longestLine(lines)
- // get padding on one side
- paddingCount := b.Con.Px
+ // Get padding on one side
+ paddingCount := b.Px
n := longestLine + (paddingCount * 2) + 2
- if b.Con.TitlePos != "Inside" && runewidth.StringWidth(title) > n-2 {
- panic("Title must be lower in length than the Top & Bottom Bars")
+ if b.TitlePos != "Inside" && runewidth.StringWidth(title) > n-2 {
+ panic("Title must be shorter than the Top & Bottom Bars")
}
- // create Top and Bottom Bars
+ // Create Top and Bottom Bars
Bar := strings.Repeat(b.Horizontal, n-2)
TopBar := b.TopLeft + Bar + b.TopRight
BottomBar := b.BottomLeft + Bar + b.BottomRight
- if b.Con.TitlePos != "Inside" {
+ if b.TitlePos != "Inside" {
TitleBar := repeatWithString(b.Horizontal, n-2, title)
- if b.Con.TitlePos == "Top" {
+ if b.TitlePos == "Top" {
TopBar = b.TopLeft + TitleBar + b.TopRight
- } else if b.Con.TitlePos == "Bottom" {
+ } else if b.TitlePos == "Bottom" {
BottomBar = b.BottomLeft + TitleBar + b.BottomRight
} else {
- fmt.Fprintln(os.Stderr, color.RedString("[error]: invalid value provided for TitlePos, using default"))
+ // Duplicate warning done here if the String() Method is used
+ // instead of using Print() and Println() methods
+ errorMsg("[warning]: invalid value provided for TitlePos, using default")
+ // Using goto here to inorder to exit the current if branch
+ goto inside
}
}
-
- if b.Con.Color != "" {
- if strings.HasPrefix(b.Con.Color, "Hi") {
- if _, ok := fgHiColors[b.Con.Color]; ok {
- Style := color.New(fgHiColors[b.Con.Color]).SprintfFunc()
+inside:
+ if b.Color != nil {
+ if str, ok := b.Color.(string); ok {
+ if strings.HasPrefix(str, "Hi") {
+ if _, ok := fgHiColors[str]; ok {
+ Style := color.New(fgHiColors[str]).SprintfFunc()
+ TopBar = Style(TopBar)
+ BottomBar = Style(BottomBar)
+ }
+ } else if _, ok := fgColors[str]; ok {
+ Style := color.New(fgColors[str]).SprintfFunc()
TopBar = Style(TopBar)
BottomBar = Style(BottomBar)
-
+ } else {
+ errorMsg("[warning]: invalid value provided to Color, using default")
}
- } else if _, ok := fgColors[b.Con.Color]; ok {
- Style := color.New(fgColors[b.Con.Color]).SprintfFunc()
- TopBar = Style(TopBar)
- BottomBar = Style(BottomBar)
+ } else if hex, ok := b.Color.(uint); ok {
+ TopBar = rgbHex(hex, TopBar)
+ BottomBar = rgbHex(hex, BottomBar)
+ } else if rgb, ok := b.Color.([3]uint); ok {
+ TopBar = rgbArray(rgb, TopBar)
+ BottomBar = rgbArray(rgb, BottomBar)
} else {
- fmt.Fprintln(os.Stderr, color.RedString("[error]: invalid value provided to Color, using default"))
+ fmt.Fprintln(os.Stderr, fmt.Sprintf("expected string, [3]uint or uint not %T using default", b.Color))
}
}
-
- if b.Con.TitlePos == "Inside" && runewidth.StringWidth(TopBar) != runewidth.StringWidth(BottomBar) {
+ if b.TitlePos == "Inside" && runewidth.StringWidth(TopBar) != runewidth.StringWidth(BottomBar) {
panic("cannot create a Box with different sizes of Top and Bottom Bars")
}
- // create lines to print
+ // Create lines to print
var texts []string
texts = b.addVertPadding(n)
- for i, line := range lines {
- length := runewidth.StringWidth(line)
+ for i, line := range lines2 {
+ length := line.len
- // use later
+ // Use later
var space, oddSpace string
- // if current text is shorter than the longest one
+ // If current text is shorter than the longest one
// center the text, so it looks better
if length < longestLine {
- // difference between longest and current one
+ // Difference between longest and current one
diff := longestLine - length
// the spaces to add on each side
toAdd := diff / 2
space = strings.Repeat(" ", toAdd)
- // if the difference between the longest and current one
+ // If the difference between the longest and current one
// is odd, we have to add one additional space before the last vertical separator
if diff%2 != 0 {
oddSpace = " "
@@ -151,78 +171,122 @@ func (b Box) toString(title string, lines []string) string {
}
spacing := space + sideMargin
- format := b.findAlign()
+ var format string
if i < titleLen && title != "" {
format = centerAlign
+ } else {
+ format = b.findAlign()
}
- // obtain color
+
+ // Obtain color
sep := b.obtainColor()
- // TODO: find a better way
- formatted := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(format, "{sep}", sep), "{sp}", spacing), "{ln}", line), "{os}", oddSpace), "{s}", space), "{px}", sideMargin)
+ formatted := fmt.Sprintf(format, sep, spacing, line.line, oddSpace, space, sideMargin)
texts = append(texts, formatted)
}
vertpadding := b.addVertPadding(n)
texts = append(texts, vertpadding...)
- return TopBar + n1 + strings.Join(texts, n1) + n1 + BottomBar + n1
+ // Using strings.Builder is more efficient and faster
+ // than concatenating 6 times
+ var sb strings.Builder
+
+ sb.WriteString(TopBar)
+ sb.WriteString(n1)
+ sb.WriteString(strings.Join(texts, n1))
+ sb.WriteString(n1)
+ sb.WriteString(BottomBar)
+ sb.WriteString(n1)
+
+ return sb.String()
}
+// obtainColor obtains the Color from string, uint and [3]uint respectively
func (b Box) obtainColor() string {
- if strings.HasPrefix(b.Con.Color, "Hi") {
- if _, ok := fgHiColors[b.Con.Color]; ok {
- Style := color.New(fgHiColors[b.Con.Color]).SprintfFunc()
+ if b.Color == nil { // if nil then just return the string
+ return b.Vertical
+ }
+ if str, ok := b.Color.(string); ok {
+ if strings.HasPrefix(str, "Hi") {
+ if _, ok := fgHiColors[str]; ok {
+ Style := color.New(fgHiColors[str]).SprintfFunc()
+ return Style(b.Vertical)
+ }
+ } else if _, ok := fgColors[str]; ok {
+ Style := color.New(fgColors[str]).SprintfFunc()
return Style(b.Vertical)
}
- } else if _, ok := fgColors[b.Con.Color]; ok {
- Style := color.New(fgColors[b.Con.Color]).SprintfFunc()
- return Style(b.Vertical)
+ errorMsg("[warning]: invalid value provided to Color, using default")
+ return b.Vertical
+ } else if hex, ok := b.Color.(uint); ok {
+ return rgbHex(hex, b.Vertical)
+ } else if rgb, ok := b.Color.([3]uint); ok {
+ return rgbArray(rgb, b.Vertical)
}
- fmt.Fprintln(os.Stderr, color.RedString("[error]: invalid value provided to Color, using default"))
- return b.Vertical
+ panic(fmt.Sprintf("expected string, [3]uint or uint not %T", b.Color))
}
-// Print prints the box
+// Print prints the Box
func (b Box) Print(title, lines string) {
var lines2 []string
- // Default Position is Inside
- if b.Con.TitlePos == "" {
- b.Con.TitlePos = "Inside"
+ // Default Position is Inside, if invalid position is given then just raise a warning
+ // then use Default Position which is Inside
+ if b.TitlePos == "" {
+ b.TitlePos = "Inside"
+ } else if b.TitlePos != "Bottom" && b.TitlePos != "Top" {
+ errorMsg("[warning]: invalid value provided for TitlePos, using default")
+ b.TitlePos = "Inside"
}
// if Title is empty then TitlePos should be Inside
if title != "" {
- if b.Con.TitlePos != "Inside" && strings.Contains(title, "\n") {
+ if b.TitlePos != "Inside" && strings.Contains(title, "\n") {
panic("Multilines are only supported inside only")
}
- if b.Con.TitlePos == "Inside" {
+ if b.TitlePos == "Inside" {
lines2 = append(lines2, strings.Split(title, n1)...)
lines2 = append(lines2, []string{""}...) // for empty line between title and content
}
}
lines2 = append(lines2, strings.Split(lines, n1)...)
- fmt.Print(b.toString(title, lines2))
+ if runtime.GOOS == "windows" {
+ // Windows Console is 4 bit (16 colors only supported) so if the custom color
+ // is out of their range then just correctly print the Box without the color effect
+ fmt.Fprint(Output, b.toString(title, lines2))
+ } else {
+ fmt.Print(b.toString(title, lines2))
+ }
}
-// Println adds a newline before and after the box
+// Println adds a newline before and after the Box
func (b Box) Println(title, lines string) {
var lines2 []string
- // Default Position is Inside
- if b.Con.TitlePos == "" {
- b.Con.TitlePos = "Inside"
+ // Default Position is Inside, if invalid position is given then just raise a warning
+ // then use Default Position which is Inside
+ if b.TitlePos == "" {
+ b.TitlePos = "Inside"
+ } else if b.TitlePos != "Bottom" && b.TitlePos != "Top" {
+ errorMsg("[warning]: invalid value provided for TitlePos, using default")
+ b.TitlePos = "Inside"
}
// if Title is empty then TitlePos should be Inside
if title != "" {
- if b.Con.TitlePos != "Inside" && strings.Contains(title, "\n") {
+ if b.TitlePos != "Inside" && strings.Contains(title, "\n") {
panic("Multilines are only supported inside only")
}
- if b.Con.TitlePos == "Inside" {
+ if b.TitlePos == "Inside" {
lines2 = append(lines2, strings.Split(title, n1)...)
lines2 = append(lines2, []string{""}...) // for empty line between title and content
}
}
lines2 = append(lines2, strings.Split(lines, n1)...)
- fmt.Printf("\n%s\n", b.toString(title, lines2))
+ if runtime.GOOS == "windows" {
+ // Windows Console is 4 bit (16 colors only supported) so if the custom color
+ // is out of their range then just correctly print the Box without the color effect
+ fmt.Fprintf(Output, "\n%s\n", b.toString(title, lines2))
+ } else {
+ fmt.Printf("\n%s\n", b.toString(title, lines2))
+ }
}
diff --git a/box_test.go b/box_test.go
new file mode 100644
index 0000000..0700a48
--- /dev/null
+++ b/box_test.go
@@ -0,0 +1,110 @@
+package box
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestInbuiltStyles(t *testing.T) {
+ cases := map[string]string{
+ "Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Single Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Bold": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Round": "โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ\n",
+ "Hidden": "+ +\n \n \n \n \n \n Box CLI Maker \n \n Highly Customized Terminal Box Maker \n \n \n \n \n \n+ +\n",
+ "Classic": "+----------------------------------------+\n| |\n| |\n| |\n| |\n| |\n| Box CLI Maker |\n| |\n| Highly Customized Terminal Box Maker |\n| |\n| |\n| |\n| |\n| |\n+----------------------------------------+\n",
+ }
+ for key := range cases {
+ Box := New(Config{Px: 2, Py: 5, Type: key})
+ box := Box.String("Box CLI Maker", "Highly Customized Terminal Box Maker")
+ if cases[key] != box {
+ t.Fatal(fmt.Sprintf(key, "Style", cases[key], "and", box, "are not same"))
+ }
+ }
+}
+
+func TestPrintColorBox(t *testing.T) {
+ StyleCases := []string{"Single", "Double", "Single Double", "Double Single", "Bold", "Round", "Hidden", "Classic"}
+ ColorTypes := []string{"Black", "Blue", "Red", "Green", "Yellow", "Cyan", "Magenta", "HiBlack", "HiBlue", "HiRed", "HiGreen", "HiYellow", "HiCyan", "HiMagenta"}
+
+ for i := 0; i < len(StyleCases); i++ {
+ for j := 0; j < len(ColorTypes); j++ {
+ Box := New(Config{Px: 2, Py: 5, Type: StyleCases[i], Color: ColorTypes[j]})
+ fmt.Print(fmt.Sprint("Using ", StyleCases[i], " as Style and ", ColorTypes[j], " as Color"))
+ Box.Println("Box CLI Maker", "Highly Customized Terminal Box Maker")
+ }
+ }
+}
+
+func TestTitlePos(t *testing.T) {
+ cases := map[string]map[string]string{
+ "Inside": {
+ "Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Single Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Bold": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Round": "โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ\n",
+ "Hidden": "+ +\n \n \n \n \n \n Box CLI Maker \n \n Highly Customized Terminal Box Maker \n \n \n \n \n \n+ +\n",
+ "Classic": "+----------------------------------------+\n| |\n| |\n| |\n| |\n| |\n| Box CLI Maker |\n| |\n| Highly Customized Terminal Box Maker |\n| |\n| |\n| |\n| |\n| |\n+----------------------------------------+\n",
+ },
+ "Bottom": {
+ "Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Single Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double Single": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Bold": "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Round": "โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโฐ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโฏ\n",
+ "Hidden": "+ +\n \n \n \n \n \n Highly Customized Terminal Box Maker \n \n \n \n \n \n+ Box CLI Maker +\n",
+ "Classic": "+----------------------------------------+\n| |\n| |\n| |\n| |\n| |\n| Highly Customized Terminal Box Maker |\n| |\n| |\n| |\n| |\n| |\n+ Box CLI Maker -------------------------+\n",
+ },
+ "Top": {
+ "Single": "โ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Single Double": "โ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double": "โ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Double Single": "โ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Bold": "โ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n",
+ "Round": "โญ Box CLI Maker โโโโโโโโโโโโโโโโโโโโโโโโโโฎ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ Highly Customized Terminal Box Maker โ\nโ โ\nโ โ\nโ โ\nโ โ\nโ โ\nโฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ\n",
+ "Hidden": "+ Box CLI Maker +\n \n \n \n \n \n Highly Customized Terminal Box Maker \n \n \n \n \n \n+ +\n",
+ "Classic": "+ Box CLI Maker -------------------------+\n| |\n| |\n| |\n| |\n| |\n| Highly Customized Terminal Box Maker |\n| |\n| |\n| |\n| |\n| |\n+----------------------------------------+\n",
+ },
+ }
+ for titlePos, val := range cases {
+ for style := range val {
+ Box := New(Config{Px: 2, Py: 5, Type: style, TitlePos: titlePos})
+ box := Box.String("Box CLI Maker", "Highly Customized Terminal Box Maker")
+ if box != val[style] {
+ t.Error(fmt.Sprintf("Using %s as Style and %s as TitlePos but %s and %s are not same", style, titlePos, box, val[style]))
+ }
+ }
+ }
+}
+
+func TestPrintMultiandTabLineString(t *testing.T) {
+ StyleCases := []string{"Single", "Double", "Single Double", "Double Single", "Bold", "Round", "Hidden", "Classic"}
+ ColorTypes := []string{"Black", "Blue", "Red", "Green", "Yellow", "Cyan", "Magenta", "HiBlack", "HiBlue", "HiRed", "HiGreen", "HiYellow", "HiCyan", "HiMagenta"}
+
+ for i := 0; i < len(StyleCases); i++ {
+ for j := 0; j < len(ColorTypes); j++ {
+ Box := New(Config{Px: 2, Py: 5, Type: StyleCases[i], Color: ColorTypes[j]})
+ fmt.Print(fmt.Sprint("Using ", StyleCases[i], " as Style and ", ColorTypes[j], " as Color"))
+ Box.Println("Box CLI Maker", `Make
+ Highly
+ Customized
+ Terminal
+ Boxes`)
+ }
+ }
+}
+
+func TestUnicodeString(t *testing.T) {
+ // English, Japanese, Chinese(Traditional), Korean, French, Spanish, Latin, Greek
+ titles := []string{"Box CLI Maker", "ใใใฏในใกใผใซใผ", "็ๅญ่ฃฝ้ ๅ", "๋ฐ์ค ๋ฉ์ด์ปค", "Crรฉateur de boรฎte CLI", "Fabricante de cajas", "Qui fecit me arca CLI", "ฮฮฟฯ
ฯฮฏ CLI Maker"}
+ lines := []string{"Make Highly Customized Terminal Boxes", "้ซๅบฆใซใซในใฟใใคใบใใใ็ซฏๅญใใใฏในใไฝๆใใ", "่ฃฝไฝ้ซๅบฆๅฎๅถ็ๆฅ็ท็", "๊ณ ๋๋ก ๋ง์ถคํ ๋ ํฐ๋ฏธ๋ ๋ฐ์ค ๋ง๋ค๊ธฐ", "Crรฉez des boรฎtes ร bornes hautement personnalisรฉes", "Haga cajas de terminales altamente personalizadas", "Fac multum Customized Terminal Pyxidas", "ฮฮทฮผฮนฮฟฯ
ฯฮณฮฎฯฯฮต ฯฮฟฮปฯ ฯฯฮฟฯฮฑฯฮผฮฟฯฮผฮญฮฝฮฑ ฯฮตฯฮผฮฑฯฮนฮบฮฌ ฮบฮฟฯ
ฯฮนฮฌ"}
+ for i := 0; i < len(titles); i++ {
+ Box := New(Config{Px: 2, Py: 5})
+ Box.Println(titles[i], lines[i])
+ }
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..6c96932
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,89 @@
+/*
+Package Box CLI Maker is a Highly Customized Terminal Box Creator written in Go.
+
+It provides many styles and options to make Boxes. There are 8 inbuilt styles and Color support via RGB uint, RGB Array of [3]uint and string (given).
+
+Inbuilt Box Styles:
+Single
+Double
+Bold
+Single Double
+Double Single
+Round
+Hidden
+Classic
+
+Inbuilt Colors:
+Black
+Blue
+Red
+Yellow
+Green
+Cyan
+Magenta
+HiBlack
+HiBlue
+HiRed
+HiYellow
+HiGreen
+HiCyan
+HiMagenta
+
+It also has Unicode and Emoji support which works across all terminals though there might be some terminals which do not support
+Unicode and Emoji like Windows CMD and Powershell. Unlike other terminals makers Box CLI Mkaer supports tab and multi line string.
+
+Basic Example:
+
+Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: "Cyan"})
+Box.Print("Box CLI Maker", "Highly Customized Terminal Box Maker")
+
+You can specify and change the options by changing the above Config struct.
+If "Style" isn't provided in box.Config struct then by default it will be "Single".
+
+You can customize and change the TitlePos to Inside, Top, Bottom and ContentAlign to Left, Right and Center.
+By default TitlePos is Inside and ContentAlign is Left.
+
+Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", TitlePos: "Top", ContentAlign: "Left"})
+
+If you want the string representation of the Box then you can just use String() method of the Box:
+
+If you want the Box to be printed correctly irrespective of it will form the correct color or not on Windows then you will have to add Box.Output
+as the passing stream to Fprintf(), Fprintln() and etc to the passing stream functions:
+
+if runtime.GOOS == "windows" {
+ fmt.Fprintf(box.Output, box_str)
+}
+
+Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: "Cyan"})
+b := Box.String("Box CLI Maker", "Highly Customized Terminal Box Maker")
+... // use it afterwards
+
+If you do not want the title and lines to be there then you can just leave i.e. put an empty in the both places.const
+
+The following two will not be applicable for terminals which don't have 24 bit support i.e. True Color ANSI Code.
+If used then the color effect will not be there.
+
+RBG Uint Example:
+
+Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: uint(0x34562f)})
+Box.Print("Box CLI Maker", "Highly Customized Terminal Box Maker")
+
+Note: Uint must be in a range of [0x000000, 0xFFFFFF] else it will panic.
+
+
+RBG [3]uint Example:
+
+Box := box.New(box.Config{Px: 2, Py: 5, Type: "Single", Color: [3]uint{23, 56, 78}})
+Box.Print("Box CLI Maker", "Highly Customized Terminal Box Maker")
+
+Note: [3]uint array elements must be in a range of [0x0, 0xFF] else it will panic.
+
+
+You can even make your custom Box Style by using box.Box struct:
+
+config := box.Config{Px: 2, Py: 3, Type: "", TitlePos: "Inside"}
+boxNew := box.Box{TopRight: "*", TopLeft: "*", BottomRight: "*", BottomLeft: "*", Horizontal: "-", Vertical: "|", Config: config}
+... // use it afterwards
+*/
+
+package box
diff --git a/go.mod b/go.mod
index 94bb74c..2070067 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,9 @@
module github.com/Delta456/box-cli-maker
-go 1.14
+go 1.15
require (
- github.com/fatih/color v1.9.0
- github.com/mattn/go-colorable v0.1.7 // indirect
+ github.com/fatih/color v1.10.0
github.com/mattn/go-runewidth v0.0.9
- golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
+ golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf // indirect
)
diff --git a/go.sum b/go.sum
index 240b0bf..6cb3e30 100644
--- a/go.sum
+++ b/go.sum
@@ -1,26 +1,14 @@
-github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
-github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
-github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
-golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
+golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s=
+golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/types.go b/types.go
index 0c5a908..5975523 100644
--- a/types.go
+++ b/types.go
@@ -2,89 +2,96 @@ package box
import "github.com/fatih/color"
-var boxs map[string]Box = map[string]Box{
- "Single": {
- TopRight: "โ",
- TopLeft: "โ",
- BottomRight: "โ",
- BottomLeft: "โ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Double": {
- TopRight: "โ",
- TopLeft: "โ",
- BottomRight: "โ",
- BottomLeft: "โ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Round": {
- TopRight: "โฎ",
- TopLeft: "โญ",
- BottomRight: "โฏ",
- BottomLeft: "โฐ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Bold": {
- TopRight: "โ",
- TopLeft: "โ",
- BottomRight: "โ",
- BottomLeft: "โ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Single Double": {
- TopRight: "โ",
- TopLeft: "โ",
- BottomRight: "โ",
- BottomLeft: "โ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Double Single": {
- TopRight: "โ",
- TopLeft: "โ",
- BottomRight: "โ",
- BottomLeft: "โ",
- Horizontal: "โ",
- Vertical: "โ",
- },
- "Classic": {
- TopRight: "+",
- TopLeft: "+",
- BottomRight: "+",
- BottomLeft: "+",
- Horizontal: "-",
- Vertical: "|",
- },
- "Hidden": {
- TopRight: "+",
- TopLeft: "+",
- BottomRight: "+",
- BottomLeft: "+",
- Horizontal: " ",
- Vertical: " ",
- },
-}
-
-var fgColors map[string]color.Attribute = map[string]color.Attribute{
- "Black": color.FgBlack,
- "Blue": color.FgBlue,
- "Red": color.FgRed,
- "Green": color.FgGreen,
- "Yellow": color.FgYellow,
- "Cyan": color.FgCyan,
- "Magenta": color.FgMagenta,
-}
-
-var fgHiColors map[string]color.Attribute = map[string]color.Attribute{
- "HiBlack": color.FgHiBlack,
- "HiBlue": color.FgHiBlue,
- "HiRed": color.FgHiRed,
- "HiGreen": color.FgHiGreen,
- "HiYellow": color.FgHiYellow,
- "HiCyan": color.FgHiCyan,
- "HiMagenta": color.FgHiMagenta,
-}
+var (
+ // boxes are inbuilt styles provided by the module
+ boxes map[string]Box = map[string]Box{
+ "Single": {
+ TopRight: "โ",
+ TopLeft: "โ",
+ BottomRight: "โ",
+ BottomLeft: "โ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Double": {
+ TopRight: "โ",
+ TopLeft: "โ",
+ BottomRight: "โ",
+ BottomLeft: "โ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Round": {
+ TopRight: "โฎ",
+ TopLeft: "โญ",
+ BottomRight: "โฏ",
+ BottomLeft: "โฐ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Bold": {
+ TopRight: "โ",
+ TopLeft: "โ",
+ BottomRight: "โ",
+ BottomLeft: "โ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Single Double": {
+ TopRight: "โ",
+ TopLeft: "โ",
+ BottomRight: "โ",
+ BottomLeft: "โ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Double Single": {
+ TopRight: "โ",
+ TopLeft: "โ",
+ BottomRight: "โ",
+ BottomLeft: "โ",
+ Horizontal: "โ",
+ Vertical: "โ",
+ },
+ "Classic": {
+ TopRight: "+",
+ TopLeft: "+",
+ BottomRight: "+",
+ BottomLeft: "+",
+ Horizontal: "-",
+ Vertical: "|",
+ },
+ "Hidden": {
+ TopRight: "+",
+ TopLeft: "+",
+ BottomRight: "+",
+ BottomLeft: "+",
+ Horizontal: " ",
+ Vertical: " ",
+ },
+ }
+ // fgColors are the inbuilt Foreground Colors provided by the module
+ fgColors map[string]color.Attribute = map[string]color.Attribute{
+ "Black": color.FgBlack,
+ "Blue": color.FgBlue,
+ "Red": color.FgRed,
+ "Green": color.FgGreen,
+ "Yellow": color.FgYellow,
+ "Cyan": color.FgCyan,
+ "Magenta": color.FgMagenta,
+ }
+ // fgHiColors are the inbuilt Hi-intensity Foreground Colors provided by the module
+ fgHiColors map[string]color.Attribute = map[string]color.Attribute{
+ "HiBlack": color.FgHiBlack,
+ "HiBlue": color.FgHiBlue,
+ "HiRed": color.FgHiRed,
+ "HiGreen": color.FgHiGreen,
+ "HiYellow": color.FgHiYellow,
+ "HiCyan": color.FgHiCyan,
+ "HiMagenta": color.FgHiMagenta,
+ }
+ // Output is an io.Writer and instance of
+ // color.Output which is needed
+ // for outputting in windows console
+ Output = color.Output
+)
diff --git a/util.go b/util.go
index f99acf0..cfed9a9 100644
--- a/util.go
+++ b/util.go
@@ -1,21 +1,29 @@
package box
import (
+ "bufio"
"fmt"
"os"
+ "runtime"
"strings"
"github.com/fatih/color"
"github.com/mattn/go-runewidth"
)
+// expandedLine stores a tab-expanded line, and its visible length.
+type expandedLine struct {
+ line string // tab-expanded line
+ len int // line's visible length
+}
+
// addVertPadding adds Vertical Padding
func (b Box) addVertPadding(len int) []string {
padding := strings.Repeat(" ", len-2)
vertical := b.obtainColor()
- var texts []string
- for i := 0; i < b.Con.Py; i++ {
+ var texts = make([]string, 0, b.Py)
+ for i := 0; i < b.Py; i++ {
texts = append(texts, (vertical + padding + vertical))
}
return texts
@@ -23,28 +31,51 @@ func (b Box) addVertPadding(len int) []string {
// findAlign checks ContentAlign and returns Alignment
func (b Box) findAlign() string {
- if b.Con.ContentAlign == "Center" {
+ if b.ContentAlign == "Center" {
return centerAlign
- } else if b.Con.ContentAlign == "Right" {
+ } else if b.ContentAlign == "Right" {
return rightAlign
- } else if b.Con.ContentAlign == "Left" {
+ // If ContentAlign isn't provided then by default Alignment is Left
+ } else if b.ContentAlign == "Left" || b.ContentAlign == "" {
return leftAlign
} else {
- fmt.Fprintln(os.Stderr, color.RedString("[error]: invalid value provided to Alignment, using default"))
+ // Raise a warning if the ContentAlign isn't invalid
+ errorMsg("[warning]: invalid value provided to Alignment, using default")
return leftAlign
}
}
-// longestLine returns longest line
-func longestLine(lines []string) int {
+// longestLine expands tabs in lines and determines longest visible
+// return longest length and array of expanded lines
+func longestLine(lines []string) (int, []expandedLine) {
longest := 0
+ var expandedLines []expandedLine
+ var tmpLine strings.Builder
+ var lineLen int
+
for _, line := range lines {
- length := runewidth.StringWidth(line)
- if length > longest {
- longest = length
+ tmpLine.Reset()
+
+ for _, c := range line {
+ lineLen = runewidth.StringWidth(tmpLine.String())
+
+ if c == '\t' {
+ tmpLine.WriteString(strings.Repeat(" ", 8-(lineLen&7)))
+ } else {
+ tmpLine.WriteRune(c)
+ }
+ }
+
+ lineLen = runewidth.StringWidth(tmpLine.String())
+
+ expandedLines = append(expandedLines, expandedLine{tmpLine.String(), lineLen})
+
+ if lineLen > longest {
+ longest = lineLen
}
}
- return longest
+
+ return longest, expandedLines
}
func repeatWithString(c string, n int, str string) string {
@@ -53,3 +84,43 @@ func repeatWithString(c string, n int, str string) string {
strNew := fmt.Sprintf(" %s %s", str, bar)
return strNew
}
+
+// rgb returns the custom RGB formed string
+// only works with 24 bit color supported terminals
+// Taken from https://github.com/vlang/v/blob/master/vlib/term/colors.v#L10-L12
+func rgb(r, g, b uint, msg, open, close string) string {
+ return fmt.Sprintf("\x1b[%s;2;%s;%s;%sm%s\x1b[%sm", open, fmt.Sprint(r), fmt.Sprint(g), fmt.Sprint(b), msg, close)
+}
+
+// rgbArray returns the custom RGB formed string from [3]uint values
+// All the elements must be in a range of [0x0, 0xFF]
+func rgbArray(r [3]uint, msg string) string {
+ for _, ele := range r {
+ if ele > 0xFF || ele < 0x0 {
+ panic("RGB Array Elements must be in a range of [0x0, 0xFF]")
+ }
+ }
+ return rgb(r[0], r[1], r[2], msg, "38", "39")
+}
+
+// rgbHex returns the custom RGB formed string from hexadecimal
+// Taken from https://github.com/vlang/v/blob/master/vlib/term/colors.v#L22-L24
+// All the elements must be in a range of [0x000000, 0xFFFFFF]
+func rgbHex(hex uint, msg string) string {
+ if hex < 0x00_0000 || hex > 0xFF_FFFF {
+ panic(fmt.Sprint(hex, "must be in a range of [0x000000, 0xFFFFFF]"))
+ }
+ return rgb(hex>>16, hex>>8&0xFF, hex&0xFF, msg, "38", "39")
+}
+
+// errorMsg prints the msg to os.Stderr in Red ANSI Color according to the system
+func errorMsg(msg string) {
+ if runtime.GOOS == "windows" {
+ // Using Output instead of os.Stderr because Output will enable ANSI Color on Winodws Console
+ fmt.Fprintln(Output, color.RedString(msg))
+ // Using bufio.NewWriter for flushing the message to os.Stderr stream
+ bufio.NewWriter(os.Stderr).Flush()
+ } else {
+ fmt.Fprintln(os.Stderr, color.RedString(msg))
+ }
+}