File: position.go

package info (click to toggle)
golang-github-charmbracelet-lipgloss 0.12.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 600 kB
  • sloc: makefile: 3
file content (154 lines) | stat: -rw-r--r-- 4,394 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package lipgloss

import (
	"math"
	"strings"

	"github.com/charmbracelet/x/ansi"
)

// Position represents a position along a horizontal or vertical axis. It's in
// situations where an axis is involved, like alignment, joining, placement and
// so on.
//
// A value of 0 represents the start (the left or top) and 1 represents the end
// (the right or bottom). 0.5 represents the center.
//
// There are constants Top, Bottom, Center, Left and Right in this package that
// can be used to aid readability.
type Position float64

func (p Position) value() float64 {
	return math.Min(1, math.Max(0, float64(p)))
}

// Position aliases.
const (
	Top    Position = 0.0
	Bottom Position = 1.0
	Center Position = 0.5
	Left   Position = 0.0
	Right  Position = 1.0
)

// Place places a string or text block vertically in an unstyled box of a given
// width or height.
func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
	return renderer.Place(width, height, hPos, vPos, str, opts...)
}

// Place places a string or text block vertically in an unstyled box of a given
// width or height.
func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
	return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...)
}

// PlaceHorizontal places a string or text block horizontally in an unstyled
// block of a given width. If the given width is shorter than the max width of
// the string (measured by its longest line) this will be a noop.
func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
	return renderer.PlaceHorizontal(width, pos, str, opts...)
}

// PlaceHorizontal places a string or text block horizontally in an unstyled
// block of a given width. If the given width is shorter than the max width of
// the string (measured by its longest line) this will be a noöp.
func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
	lines, contentWidth := getLines(str)
	gap := width - contentWidth

	if gap <= 0 {
		return str
	}

	ws := newWhitespace(r, opts...)

	var b strings.Builder
	for i, l := range lines {
		// Is this line shorter than the longest line?
		short := max(0, contentWidth-ansi.StringWidth(l))

		switch pos { //nolint:exhaustive
		case Left:
			b.WriteString(l)
			b.WriteString(ws.render(gap + short))

		case Right:
			b.WriteString(ws.render(gap + short))
			b.WriteString(l)

		default: // somewhere in the middle
			totalGap := gap + short

			split := int(math.Round(float64(totalGap) * pos.value()))
			left := totalGap - split
			right := totalGap - left

			b.WriteString(ws.render(left))
			b.WriteString(l)
			b.WriteString(ws.render(right))
		}

		if i < len(lines)-1 {
			b.WriteRune('\n')
		}
	}

	return b.String()
}

// PlaceVertical places a string or text block vertically in an unstyled block
// of a given height. If the given height is shorter than the height of the
// string (measured by its newlines) then this will be a noop.
func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
	return renderer.PlaceVertical(height, pos, str, opts...)
}

// PlaceVertical places a string or text block vertically in an unstyled block
// of a given height. If the given height is shorter than the height of the
// string (measured by its newlines) then this will be a noöp.
func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
	contentHeight := strings.Count(str, "\n") + 1
	gap := height - contentHeight

	if gap <= 0 {
		return str
	}

	ws := newWhitespace(r, opts...)

	_, width := getLines(str)
	emptyLine := ws.render(width)
	b := strings.Builder{}

	switch pos { //nolint:exhaustive
	case Top:
		b.WriteString(str)
		b.WriteRune('\n')
		for i := 0; i < gap; i++ {
			b.WriteString(emptyLine)
			if i < gap-1 {
				b.WriteRune('\n')
			}
		}

	case Bottom:
		b.WriteString(strings.Repeat(emptyLine+"\n", gap))
		b.WriteString(str)

	default: // Somewhere in the middle
		split := int(math.Round(float64(gap) * pos.value()))
		top := gap - split
		bottom := gap - top

		b.WriteString(strings.Repeat(emptyLine+"\n", top))
		b.WriteString(str)

		for i := 0; i < bottom; i++ {
			b.WriteRune('\n')
			b.WriteString(emptyLine)
		}
	}

	return b.String()
}