| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- package lipgloss
- import (
- "math"
- "strings"
- "github.com/muesli/reflow/ansi"
- )
- // JoinHorizontal is a utility function for horizontally joining two
- // potentially multi-lined strings along a vertical axis. The first argument is
- // the position, with 0 being all the way at the top and 1 being all the way
- // at the bottom.
- //
- // If you just want to align to the left, right or center you may as well just
- // use the helper constants Top, Center, and Bottom.
- //
- // Example:
- //
- // blockB := "...\n...\n..."
- // blockA := "...\n...\n...\n...\n..."
- //
- // // Join 20% from the top
- // str := lipgloss.JoinHorizontal(0.2, blockA, blockB)
- //
- // // Join on the top edge
- // str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB)
- func JoinHorizontal(pos Position, strs ...string) string {
- if len(strs) == 0 {
- return ""
- }
- if len(strs) == 1 {
- return strs[0]
- }
- var (
- // Groups of strings broken into multiple lines
- blocks = make([][]string, len(strs))
- // Max line widths for the above text blocks
- maxWidths = make([]int, len(strs))
- // Height of the tallest block
- maxHeight int
- )
- // Break text blocks into lines and get max widths for each text block
- for i, str := range strs {
- blocks[i], maxWidths[i] = getLines(str)
- if len(blocks[i]) > maxHeight {
- maxHeight = len(blocks[i])
- }
- }
- // Add extra lines to make each side the same height
- for i := range blocks {
- if len(blocks[i]) >= maxHeight {
- continue
- }
- extraLines := make([]string, maxHeight-len(blocks[i]))
- switch pos {
- case Top:
- blocks[i] = append(blocks[i], extraLines...)
- case Bottom:
- blocks[i] = append(extraLines, blocks[i]...)
- default: // Somewhere in the middle
- n := len(extraLines)
- split := int(math.Round(float64(n) * pos.value()))
- top := n - split
- bottom := n - top
- blocks[i] = append(extraLines[top:], blocks[i]...)
- blocks[i] = append(blocks[i], extraLines[bottom:]...)
- }
- }
- // Merge lines
- var b strings.Builder
- for i := range blocks[0] { // remember, all blocks have the same number of members now
- for j, block := range blocks {
- b.WriteString(block[i])
- // Also make lines the same length
- b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.PrintableRuneWidth(block[i])))
- }
- if i < len(blocks[0])-1 {
- b.WriteRune('\n')
- }
- }
- return b.String()
- }
- // JoinVertical is a utility function for vertically joining two potentially
- // multi-lined strings along a horizontal axis. The first argument is the
- // position, with 0 being all the way to the left and 1 being all the way to
- // the right.
- //
- // If you just want to align to the left, right or center you may as well just
- // use the helper constants Left, Center, and Right.
- //
- // Example:
- //
- // blockB := "...\n...\n..."
- // blockA := "...\n...\n...\n...\n..."
- //
- // // Join 20% from the top
- // str := lipgloss.JoinVertical(0.2, blockA, blockB)
- //
- // // Join on the right edge
- // str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB)
- func JoinVertical(pos Position, strs ...string) string {
- if len(strs) == 0 {
- return ""
- }
- if len(strs) == 1 {
- return strs[0]
- }
- var (
- blocks = make([][]string, len(strs))
- maxWidth int
- )
- for i := range strs {
- var w int
- blocks[i], w = getLines(strs[i])
- if w > maxWidth {
- maxWidth = w
- }
- }
- var b strings.Builder
- for i, block := range blocks {
- for j, line := range block {
- w := maxWidth - ansi.PrintableRuneWidth(line)
- switch pos {
- case Left:
- b.WriteString(line)
- b.WriteString(strings.Repeat(" ", w))
- case Right:
- b.WriteString(strings.Repeat(" ", w))
- b.WriteString(line)
- default: // Somewhere in the middle
- if w < 1 {
- b.WriteString(line)
- break
- }
- split := int(math.Round(float64(w) * pos.value()))
- right := w - split
- left := w - right
- b.WriteString(strings.Repeat(" ", left))
- b.WriteString(line)
- b.WriteString(strings.Repeat(" ", right))
- }
- // Write a newline as long as we're not on the last line of the
- // last block.
- if !(i == len(blocks)-1 && j == len(block)-1) {
- b.WriteRune('\n')
- }
- }
- }
- return b.String()
- }
|