From bc2473a023a3d3eeb629f392c4fc9c26227be133 Mon Sep 17 00:00:00 2001 From: Florian Rey Date: Mon, 10 Jun 2024 15:35:53 +0200 Subject: [PATCH] feat: Introduce border interface --- borderer.go | 647 ++++++++++++++++++++++++++++++++++++++++++++++++++++ borders.go | 167 -------------- get.go | 79 ++----- set.go | 88 +++---- style.go | 41 +--- unset.go | 42 ++-- 6 files changed, 726 insertions(+), 338 deletions(-) create mode 100644 borderer.go diff --git a/borderer.go b/borderer.go new file mode 100644 index 00000000..1417b3a7 --- /dev/null +++ b/borderer.go @@ -0,0 +1,647 @@ +package lipgloss + +import ( + "github.com/muesli/termenv" + "strings" +) + +// Available properties. +const ( + // Border runes. + borderStyleKey propKey = 1 << iota + + // Border edges. + borderTopKey + borderRightKey + borderBottomKey + borderLeftKey + + // Border foreground colors. + borderTopForegroundKey + borderRightForegroundKey + borderBottomForegroundKey + borderLeftForegroundKey + + // Border background colors. + borderTopBackgroundKey + borderRightBackgroundKey + borderBottomBackgroundKey + borderLeftBackgroundKey +) + +type Borderer interface { + // Get + GetStyle() Border + GetTop() bool + GetRight() bool + GetBottom() bool + GetLeft() bool + GetTopForeground() TerminalColor + GetRightForeground() TerminalColor + GetBottomForeground() TerminalColor + GetLeftForeground() TerminalColor + GetTopBackground() TerminalColor + GetRightBackground() TerminalColor + GetBottomBackground() TerminalColor + GetLeftBackground() TerminalColor + GetTopSize() int + GetRightSize() int + GetBottomSize() int + GetLeftSize() int + + // Set + Style(border Border) Borderer + Top(v bool) Borderer + Right(v bool) Borderer + Bottom(v bool) Borderer + Left(v bool) Borderer + TopForeground(c TerminalColor) Borderer + RightForeground(c TerminalColor) Borderer + BottomForeground(c TerminalColor) Borderer + LeftForeground(c TerminalColor) Borderer + TopBackground(c TerminalColor) Borderer + RightBackground(c TerminalColor) Borderer + BottomBackground(c TerminalColor) Borderer + LeftBackground(c TerminalColor) Borderer + + // Unset + UnsetStyle() Borderer + UnsetTop() Borderer + UnsetRight() Borderer + UnsetBottom() Borderer + UnsetLeft() Borderer + UnsetTopForeground() Borderer + UnsetRightForeground() Borderer + UnsetBottomForeground() Borderer + UnsetLeftForeground() Borderer + UnsetTopBackground() Borderer + UnsetRightBackground() Borderer + UnsetBottomBackground() Borderer + UnsetLeftBackground() Borderer + + Apply(str string) string +} + +type NormalBorderer struct { + r *Renderer + props props + attrs int + style Border + topFgColor TerminalColor + rightFgColor TerminalColor + bottomFgColor TerminalColor + leftFgColor TerminalColor + topBgColor TerminalColor + rightBgColor TerminalColor + bottomBgColor TerminalColor + leftBgColor TerminalColor +} + +// Returns whether or not the given property is set. +func (b *NormalBorderer) isSet(k propKey) bool { + return b.props.has(k) +} + +func (b *NormalBorderer) getAsBool(k propKey, defaultVal bool) bool { + if !b.isSet(k) { + return defaultVal + } + return b.attrs&int(k) != 0 +} + +func (b *NormalBorderer) getAsColor(k propKey) TerminalColor { + if !b.isSet(k) { + return noColor + } + + var c TerminalColor + switch k { //nolint:exhaustive + case borderTopForegroundKey: + c = b.topFgColor + case borderRightForegroundKey: + c = b.rightFgColor + case borderBottomForegroundKey: + c = b.bottomFgColor + case borderLeftForegroundKey: + c = b.leftFgColor + case borderTopBackgroundKey: + c = b.topBgColor + case borderRightBackgroundKey: + c = b.rightBgColor + case borderBottomBackgroundKey: + c = b.bottomBgColor + case borderLeftBackgroundKey: + c = b.leftBgColor + } + + if c != nil { + return c + } + + return noColor +} + +// Set a value on the underlying rules map. +func (b *NormalBorderer) set(key propKey, value interface{}) { + // We don't allow negative integers on any of our other values, so just keep + // them at zero or above. We could use uints instead, but the + // conversions are a little tedious, so we're sticking with ints for + // sake of usability. + switch key { //nolint:exhaustive + case borderStyleKey: + b.style = value.(Border) + case borderTopForegroundKey: + b.topFgColor = colorOrNil(value) + case borderRightForegroundKey: + b.rightFgColor = colorOrNil(value) + case borderBottomForegroundKey: + b.bottomFgColor = colorOrNil(value) + case borderLeftForegroundKey: + b.leftFgColor = colorOrNil(value) + case borderTopBackgroundKey: + b.topBgColor = colorOrNil(value) + case borderRightBackgroundKey: + b.rightBgColor = colorOrNil(value) + case borderBottomBackgroundKey: + b.bottomBgColor = colorOrNil(value) + case borderLeftBackgroundKey: + b.leftBgColor = colorOrNil(value) + default: + if v, ok := value.(bool); ok { //nolint:nestif + if v { + b.attrs |= int(key) + } else { + b.attrs &^= int(key) + } + } else if attrs, ok := value.(int); ok { + // bool attrs + if attrs&int(key) != 0 { + b.attrs |= int(key) + } else { + b.attrs &^= int(key) + } + } + } + + // Set the prop on + b.props = b.props.set(key) +} + +// unset unsets a property from a style. +func (b *NormalBorderer) unset(key propKey) { + b.props = b.props.unset(key) +} + +// GetStyle returns the style's border style (type Border). If no value +// is set Border{} is returned. +func (b *NormalBorderer) GetStyle() Border { + if !b.isSet(borderStyleKey) { + return noBorder + } + return b.style +} + +// GetTop returns the style's top border setting. If no value is set +// false is returned. +func (b *NormalBorderer) GetTop() bool { + return b.getAsBool(borderTopKey, false) +} + +// GetRight returns the style's right border setting. If no value is set +// false is returned. +func (b *NormalBorderer) GetRight() bool { + return b.getAsBool(borderRightKey, false) +} + +// GetBottom returns the style's bottom border setting. If no value is +// set false is returned. +func (b *NormalBorderer) GetBottom() bool { + return b.getAsBool(borderBottomKey, false) +} + +// GetLeft returns the style's left border setting. If no value is +// set false is returned. +func (b *NormalBorderer) GetLeft() bool { + return b.getAsBool(borderLeftKey, false) +} + +// GetTopForeground returns the style's border top foreground color. If +// no value is set NoColor{} is returned. +func (b *NormalBorderer) GetTopForeground() TerminalColor { + return b.getAsColor(borderTopForegroundKey) +} + +// GetRightForeground returns the style's border right foreground color. +// If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetRightForeground() TerminalColor { + return b.getAsColor(borderRightForegroundKey) +} + +// GetBottomForeground returns the style's border bottom foreground +// color. If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetBottomForeground() TerminalColor { + return b.getAsColor(borderBottomForegroundKey) +} + +// GetLeftForeground returns the style's border left foreground +// color. If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetLeftForeground() TerminalColor { + return b.getAsColor(borderLeftForegroundKey) +} + +// GetTopBackground returns the style's border top background color. If +// no value is set NoColor{} is returned. +func (b *NormalBorderer) GetTopBackground() TerminalColor { + return b.getAsColor(borderTopBackgroundKey) +} + +// GetRightBackground returns the style's border right background color. +// If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetRightBackground() TerminalColor { + return b.getAsColor(borderRightBackgroundKey) +} + +// GetBottomBackground returns the style's border bottom background +// color. If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetBottomBackground() TerminalColor { + return b.getAsColor(borderBottomBackgroundKey) +} + +// GetLeftBackground returns the style's border left background +// color. If no value is set NoColor{} is returned. +func (b *NormalBorderer) GetLeftBackground() TerminalColor { + return b.getAsColor(borderLeftBackgroundKey) +} + +// GetTopSize returns the width of the top border. If borders contain +// runes of varying widths, the widest rune is returned. If no border exists on +// the top edge, 0 is returned. +func (b *NormalBorderer) GetTopSize() int { + if !b.getAsBool(borderTopKey, false) { + return 0 + } + return b.GetStyle().GetTopSize() +} + +// GetRightSize returns the width of the right border. If borders +// contain runes of varying widths, the widest rune is returned. If no border +// exists on the right edge, 0 is returned. +func (b *NormalBorderer) GetRightSize() int { + if !b.getAsBool(borderRightKey, false) { + return 0 + } + return b.GetStyle().GetRightSize() +} + +// GetBottomSize returns the width of the bottom border. If borders +// contain runes of varying widths, the widest rune is returned. If no border +// exists on the left edge, 0 is returned. +func (b *NormalBorderer) GetBottomSize() int { + if !b.getAsBool(borderBottomKey, false) { + return 0 + } + return b.GetStyle().GetBottomSize() +} + +// GetLeftSize returns the width of the left border. If borders contain +// runes of varying widths, the widest rune is returned. If no border exists on +// the left edge, 0 is returned. +func (b *NormalBorderer) GetLeftSize() int { + if !b.getAsBool(borderLeftKey, false) { + return 0 + } + return b.GetStyle().GetLeftSize() +} + +func (b *NormalBorderer) Style(border Border) Borderer { + b.set(borderStyleKey, border) + return b +} + +// Top determines whether or not to draw a top border. +func (b *NormalBorderer) Top(v bool) Borderer { + b.set(borderTopKey, v) + return b +} + +// Right determines whether or not to draw a right border. +func (b *NormalBorderer) Right(v bool) Borderer { + b.set(borderRightKey, v) + return b +} + +// Bottom determines whether or not to draw a bottom border. +func (b *NormalBorderer) Bottom(v bool) Borderer { + b.set(borderBottomKey, v) + return b +} + +// Left determines whether or not to draw a left border. +func (b *NormalBorderer) Left(v bool) Borderer { + b.set(borderLeftKey, v) + return b +} + +// TopForeground set the foreground color for the top of the border. +func (b *NormalBorderer) TopForeground(c TerminalColor) Borderer { + b.set(borderTopForegroundKey, c) + return b +} + +// RightForeground sets the foreground color for the right side of the +// border. +func (b *NormalBorderer) RightForeground(c TerminalColor) Borderer { + b.set(borderRightForegroundKey, c) + return b +} + +// BottomForeground sets the foreground color for the bottom of the +// border. +func (b *NormalBorderer) BottomForeground(c TerminalColor) Borderer { + b.set(borderBottomForegroundKey, c) + return b +} + +// LeftForeground sets the foreground color for the left side of the +// border. +func (b *NormalBorderer) LeftForeground(c TerminalColor) Borderer { + b.set(borderLeftForegroundKey, c) + return b +} + +// TopBackground sets the background color of the top of the border. +func (b *NormalBorderer) TopBackground(c TerminalColor) Borderer { + b.set(borderTopBackgroundKey, c) + return b +} + +// RightBackground sets the background color of right side the border. +func (b *NormalBorderer) RightBackground(c TerminalColor) Borderer { + b.set(borderRightBackgroundKey, c) + return b +} + +// BottomBackground sets the background color of the bottom of the +// border. +func (b *NormalBorderer) BottomBackground(c TerminalColor) Borderer { + b.set(borderBottomBackgroundKey, c) + return b +} + +// LeftBackground set the background color of the left side of the +// border. +func (b *NormalBorderer) LeftBackground(c TerminalColor) Borderer { + b.set(borderLeftBackgroundKey, c) + return b +} + +// UnsetStyle removes the border style rule, if set. +func (b *NormalBorderer) UnsetStyle() Borderer { + b.unset(borderStyleKey) + return b +} + +// UnsetTop removes the border top style rule, if set. +func (b *NormalBorderer) UnsetTop() Borderer { + b.unset(borderTopKey) + return b +} + +// UnsetRight removes the border right style rule, if set. +func (b *NormalBorderer) UnsetRight() Borderer { + b.unset(borderRightKey) + return b +} + +// UnsetBottom removes the border bottom style rule, if set. +func (b *NormalBorderer) UnsetBottom() Borderer { + b.unset(borderBottomKey) + return b +} + +// UnsetLeft removes the border left style rule, if set. +func (b *NormalBorderer) UnsetLeft() Borderer { + b.unset(borderLeftKey) + return b +} + +// UnsetTopForeground removes the top border foreground color rule, +// if set. +func (b *NormalBorderer) UnsetTopForeground() Borderer { + b.unset(borderTopForegroundKey) + return b +} + +// UnsetRightForeground removes the right border foreground color rule, +// if set. +func (b *NormalBorderer) UnsetRightForeground() Borderer { + b.unset(borderRightForegroundKey) + return b +} + +// UnsetBottomForeground removes the bottom border foreground color +// rule, if set. +func (b *NormalBorderer) UnsetBottomForeground() Borderer { + b.unset(borderBottomForegroundKey) + return b +} + +// UnsetLeftForeground removes the left border foreground color rule, +// if set. +func (b *NormalBorderer) UnsetLeftForeground() Borderer { + b.unset(borderLeftForegroundKey) + return b +} + +// UnsetTopBackgroundColor removes the top border background color rule, +// if set. +func (b *NormalBorderer) UnsetTopBackground() Borderer { + b.unset(borderTopBackgroundKey) + return b +} + +// UnsetRightBackground removes the right border background color +// rule, if set. +func (b *NormalBorderer) UnsetRightBackground() Borderer { + b.unset(borderRightBackgroundKey) + return b +} + +// UnsetBottomBackground removes the bottom border background color +// rule, if set. +func (b *NormalBorderer) UnsetBottomBackground() Borderer { + b.unset(borderBottomBackgroundKey) + return b +} + +// UnsetLeftBackground removes the left border color rule, if set. +func (b *NormalBorderer) UnsetLeftBackground() Borderer { + b.unset(borderLeftBackgroundKey) + return b +} + +func (b *NormalBorderer) Apply(str string) string { + var ( + topSet = b.isSet(borderTopKey) + rightSet = b.isSet(borderRightKey) + bottomSet = b.isSet(borderBottomKey) + leftSet = b.isSet(borderLeftKey) + + border = b.GetStyle() + hasTop = b.getAsBool(borderTopKey, false) + hasRight = b.getAsBool(borderRightKey, false) + hasBottom = b.getAsBool(borderBottomKey, false) + hasLeft = b.getAsBool(borderLeftKey, false) + + topFG = b.getAsColor(borderTopForegroundKey) + rightFG = b.getAsColor(borderRightForegroundKey) + bottomFG = b.getAsColor(borderBottomForegroundKey) + leftFG = b.getAsColor(borderLeftForegroundKey) + + topBG = b.getAsColor(borderTopBackgroundKey) + rightBG = b.getAsColor(borderRightBackgroundKey) + bottomBG = b.getAsColor(borderBottomBackgroundKey) + leftBG = b.getAsColor(borderLeftBackgroundKey) + ) + + // If a border is set and no sides have been specifically turned on or off + // render borders on all sides. + if border != noBorder && !(topSet || rightSet || bottomSet || leftSet) { + hasTop = true + hasRight = true + hasBottom = true + hasLeft = true + } + + // If no border is set or all borders are been disabled, abort. + if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) { + return str + } + + lines, width := getLines(str) + + if hasLeft { + if border.Left == "" { + border.Left = " " + } + width += maxRuneWidth(border.Left) + } + + if hasRight && border.Right == "" { + border.Right = " " + } + + // If corners should be rendered but are set with the empty string, fill them + // with a single space. + if hasTop && hasLeft && border.TopLeft == "" { + border.TopLeft = " " + } + if hasTop && hasRight && border.TopRight == "" { + border.TopRight = " " + } + if hasBottom && hasLeft && border.BottomLeft == "" { + border.BottomLeft = " " + } + if hasBottom && hasRight && border.BottomRight == "" { + border.BottomRight = " " + } + + // Figure out which corners we should actually be using based on which + // sides are set to show. + if hasTop { + switch { + case !hasLeft && !hasRight: + border.TopLeft = "" + border.TopRight = "" + case !hasLeft: + border.TopLeft = "" + case !hasRight: + border.TopRight = "" + } + } + if hasBottom { + switch { + case !hasLeft && !hasRight: + border.BottomLeft = "" + border.BottomRight = "" + case !hasLeft: + border.BottomLeft = "" + case !hasRight: + border.BottomRight = "" + } + } + + // For now, limit corners to one rune. + border.TopLeft = getFirstRuneAsString(border.TopLeft) + border.TopRight = getFirstRuneAsString(border.TopRight) + border.BottomRight = getFirstRuneAsString(border.BottomRight) + border.BottomLeft = getFirstRuneAsString(border.BottomLeft) + + var out strings.Builder + + // Render top + if hasTop { + top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width) + top = b.applyStyle(top, topFG, topBG) + out.WriteString(top) + out.WriteRune('\n') + } + + leftRunes := []rune(border.Left) + leftIndex := 0 + + rightRunes := []rune(border.Right) + rightIndex := 0 + + // Render sides + for i, l := range lines { + if hasLeft { + r := string(leftRunes[leftIndex]) + leftIndex++ + if leftIndex >= len(leftRunes) { + leftIndex = 0 + } + out.WriteString(b.applyStyle(r, leftFG, leftBG)) + } + out.WriteString(l) + if hasRight { + r := string(rightRunes[rightIndex]) + rightIndex++ + if rightIndex >= len(rightRunes) { + rightIndex = 0 + } + out.WriteString(b.applyStyle(r, rightFG, rightBG)) + } + if i < len(lines)-1 { + out.WriteRune('\n') + } + } + + // Render bottom + if hasBottom { + bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width) + bottom = b.applyStyle(bottom, bottomFG, bottomBG) + out.WriteRune('\n') + out.WriteString(bottom) + } + + return out.String() +} + +// Apply foreground and background styling. +func (b *NormalBorderer) applyStyle(border string, fg, bg TerminalColor) string { + if fg == noColor && bg == noColor { + return border + } + + style := termenv.Style{} + + if fg != noColor { + style = style.Foreground(fg.color(b.r)) + } + if bg != noColor { + style = style.Background(bg.color(b.r)) + } + + return style.Styled(border) +} diff --git a/borders.go b/borders.go index deb6b35a..198207fe 100644 --- a/borders.go +++ b/borders.go @@ -4,7 +4,6 @@ import ( "strings" "github.com/charmbracelet/x/ansi" - "github.com/muesli/termenv" "github.com/rivo/uniseg" ) @@ -226,154 +225,6 @@ func HiddenBorder() Border { return hiddenBorder } -func (s Style) applyBorder(str string) string { - var ( - topSet = s.isSet(borderTopKey) - rightSet = s.isSet(borderRightKey) - bottomSet = s.isSet(borderBottomKey) - leftSet = s.isSet(borderLeftKey) - - border = s.getBorderStyle() - hasTop = s.getAsBool(borderTopKey, false) - hasRight = s.getAsBool(borderRightKey, false) - hasBottom = s.getAsBool(borderBottomKey, false) - hasLeft = s.getAsBool(borderLeftKey, false) - - topFG = s.getAsColor(borderTopForegroundKey) - rightFG = s.getAsColor(borderRightForegroundKey) - bottomFG = s.getAsColor(borderBottomForegroundKey) - leftFG = s.getAsColor(borderLeftForegroundKey) - - topBG = s.getAsColor(borderTopBackgroundKey) - rightBG = s.getAsColor(borderRightBackgroundKey) - bottomBG = s.getAsColor(borderBottomBackgroundKey) - leftBG = s.getAsColor(borderLeftBackgroundKey) - ) - - // If a border is set and no sides have been specifically turned on or off - // render borders on all sides. - if border != noBorder && !(topSet || rightSet || bottomSet || leftSet) { - hasTop = true - hasRight = true - hasBottom = true - hasLeft = true - } - - // If no border is set or all borders are been disabled, abort. - if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) { - return str - } - - lines, width := getLines(str) - - if hasLeft { - if border.Left == "" { - border.Left = " " - } - width += maxRuneWidth(border.Left) - } - - if hasRight && border.Right == "" { - border.Right = " " - } - - // If corners should be rendered but are set with the empty string, fill them - // with a single space. - if hasTop && hasLeft && border.TopLeft == "" { - border.TopLeft = " " - } - if hasTop && hasRight && border.TopRight == "" { - border.TopRight = " " - } - if hasBottom && hasLeft && border.BottomLeft == "" { - border.BottomLeft = " " - } - if hasBottom && hasRight && border.BottomRight == "" { - border.BottomRight = " " - } - - // Figure out which corners we should actually be using based on which - // sides are set to show. - if hasTop { - switch { - case !hasLeft && !hasRight: - border.TopLeft = "" - border.TopRight = "" - case !hasLeft: - border.TopLeft = "" - case !hasRight: - border.TopRight = "" - } - } - if hasBottom { - switch { - case !hasLeft && !hasRight: - border.BottomLeft = "" - border.BottomRight = "" - case !hasLeft: - border.BottomLeft = "" - case !hasRight: - border.BottomRight = "" - } - } - - // For now, limit corners to one rune. - border.TopLeft = getFirstRuneAsString(border.TopLeft) - border.TopRight = getFirstRuneAsString(border.TopRight) - border.BottomRight = getFirstRuneAsString(border.BottomRight) - border.BottomLeft = getFirstRuneAsString(border.BottomLeft) - - var out strings.Builder - - // Render top - if hasTop { - top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width) - top = s.styleBorder(top, topFG, topBG) - out.WriteString(top) - out.WriteRune('\n') - } - - leftRunes := []rune(border.Left) - leftIndex := 0 - - rightRunes := []rune(border.Right) - rightIndex := 0 - - // Render sides - for i, l := range lines { - if hasLeft { - r := string(leftRunes[leftIndex]) - leftIndex++ - if leftIndex >= len(leftRunes) { - leftIndex = 0 - } - out.WriteString(s.styleBorder(r, leftFG, leftBG)) - } - out.WriteString(l) - if hasRight { - r := string(rightRunes[rightIndex]) - rightIndex++ - if rightIndex >= len(rightRunes) { - rightIndex = 0 - } - out.WriteString(s.styleBorder(r, rightFG, rightBG)) - } - if i < len(lines)-1 { - out.WriteRune('\n') - } - } - - // Render bottom - if hasBottom { - bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width) - bottom = s.styleBorder(bottom, bottomFG, bottomBG) - out.WriteRune('\n') - out.WriteString(bottom) - } - - return out.String() -} - // Render the horizontal (top or bottom) portion of a border. func renderHorizontalEdge(left, middle, right string, width int) string { if middle == "" { @@ -401,24 +252,6 @@ func renderHorizontalEdge(left, middle, right string, width int) string { return out.String() } -// Apply foreground and background styling to a border. -func (s Style) styleBorder(border string, fg, bg TerminalColor) string { - if fg == noColor && bg == noColor { - return border - } - - style := termenv.Style{} - - if fg != noColor { - style = style.Foreground(fg.color(s.r)) - } - if bg != noColor { - style = style.Background(bg.color(s.r)) - } - - return style.Styled(border) -} - func maxRuneWidth(str string) int { var width int diff --git a/get.go b/get.go index 9c2f06fe..3cc660c2 100644 --- a/get.go +++ b/get.go @@ -202,89 +202,89 @@ func (s Style) GetVerticalMargins() int { // border style, Border{} is returned. For all other unset values false is // returned. func (s Style) GetBorder() (b Border, top, right, bottom, left bool) { - return s.getBorderStyle(), - s.getAsBool(borderTopKey, false), - s.getAsBool(borderRightKey, false), - s.getAsBool(borderBottomKey, false), - s.getAsBool(borderLeftKey, false) + return s.getBorderer().GetStyle(), + s.getBorderer().GetTop(), + s.getBorderer().GetRight(), + s.getBorderer().GetBottom(), + s.getBorderer().GetLeft() } // GetBorderStyle returns the style's border style (type Border). If no value // is set Border{} is returned. func (s Style) GetBorderStyle() Border { - return s.getBorderStyle() + return s.getBorderer().GetStyle() } // GetBorderTop returns the style's top border setting. If no value is set // false is returned. func (s Style) GetBorderTop() bool { - return s.getAsBool(borderTopKey, false) + return s.getBorderer().GetTop() } // GetBorderRight returns the style's right border setting. If no value is set // false is returned. func (s Style) GetBorderRight() bool { - return s.getAsBool(borderRightKey, false) + return s.getBorderer().GetRight() } // GetBorderBottom returns the style's bottom border setting. If no value is // set false is returned. func (s Style) GetBorderBottom() bool { - return s.getAsBool(borderBottomKey, false) + return s.getBorderer().GetBottom() } // GetBorderLeft returns the style's left border setting. If no value is // set false is returned. func (s Style) GetBorderLeft() bool { - return s.getAsBool(borderLeftKey, false) + return s.getBorderer().GetLeft() } // GetBorderTopForeground returns the style's border top foreground color. If // no value is set NoColor{} is returned. func (s Style) GetBorderTopForeground() TerminalColor { - return s.getAsColor(borderTopForegroundKey) + return s.getBorderer().GetTopForeground() } // GetBorderRightForeground returns the style's border right foreground color. // If no value is set NoColor{} is returned. func (s Style) GetBorderRightForeground() TerminalColor { - return s.getAsColor(borderRightForegroundKey) + return s.getBorderer().GetRightForeground() } // GetBorderBottomForeground returns the style's border bottom foreground // color. If no value is set NoColor{} is returned. func (s Style) GetBorderBottomForeground() TerminalColor { - return s.getAsColor(borderBottomForegroundKey) + return s.getBorderer().GetBottomForeground() } // GetBorderLeftForeground returns the style's border left foreground // color. If no value is set NoColor{} is returned. func (s Style) GetBorderLeftForeground() TerminalColor { - return s.getAsColor(borderLeftForegroundKey) + return s.getBorderer().GetLeftForeground() } // GetBorderTopBackground returns the style's border top background color. If // no value is set NoColor{} is returned. func (s Style) GetBorderTopBackground() TerminalColor { - return s.getAsColor(borderTopBackgroundKey) + return s.getBorderer().GetTopBackground() } // GetBorderRightBackground returns the style's border right background color. // If no value is set NoColor{} is returned. func (s Style) GetBorderRightBackground() TerminalColor { - return s.getAsColor(borderRightBackgroundKey) + return s.getBorderer().GetRightBackground() } // GetBorderBottomBackground returns the style's border bottom background // color. If no value is set NoColor{} is returned. func (s Style) GetBorderBottomBackground() TerminalColor { - return s.getAsColor(borderBottomBackgroundKey) + return s.getBorderer().GetBottomBackground() } // GetBorderLeftBackground returns the style's border left background // color. If no value is set NoColor{} is returned. func (s Style) GetBorderLeftBackground() TerminalColor { - return s.getAsColor(borderLeftBackgroundKey) + return s.getBorderer().GetLeftBackground() } // GetBorderTopWidth returns the width of the top border. If borders contain @@ -300,40 +300,28 @@ func (s Style) GetBorderTopWidth() int { // runes of varying widths, the widest rune is returned. If no border exists on // the top edge, 0 is returned. func (s Style) GetBorderTopSize() int { - if !s.getAsBool(borderTopKey, false) { - return 0 - } - return s.getBorderStyle().GetTopSize() + return s.getBorderer().GetTopSize() } // GetBorderLeftSize returns the width of the left border. If borders contain // runes of varying widths, the widest rune is returned. If no border exists on // the left edge, 0 is returned. func (s Style) GetBorderLeftSize() int { - if !s.getAsBool(borderLeftKey, false) { - return 0 - } - return s.getBorderStyle().GetLeftSize() + return s.getBorderer().GetLeftSize() } // GetBorderBottomSize returns the width of the bottom border. If borders // contain runes of varying widths, the widest rune is returned. If no border // exists on the left edge, 0 is returned. func (s Style) GetBorderBottomSize() int { - if !s.getAsBool(borderBottomKey, false) { - return 0 - } - return s.getBorderStyle().GetBottomSize() + return s.getBorderer().GetBottomSize() } // GetBorderRightSize returns the width of the right border. If borders // contain runes of varying widths, the widest rune is returned. If no border // exists on the right edge, 0 is returned. func (s Style) GetBorderRightSize() int { - if !s.getAsBool(borderRightKey, false) { - return 0 - } - return s.getBorderStyle().GetRightSize() + return s.getBorderer().GetRightSize() } // GetHorizontalBorderSize returns the width of the horizontal borders. If @@ -439,22 +427,6 @@ func (s Style) getAsColor(k propKey) TerminalColor { c = s.bgColor case marginBackgroundKey: c = s.marginBgColor - case borderTopForegroundKey: - c = s.borderTopFgColor - case borderRightForegroundKey: - c = s.borderRightFgColor - case borderBottomForegroundKey: - c = s.borderBottomFgColor - case borderLeftForegroundKey: - c = s.borderLeftFgColor - case borderTopBackgroundKey: - c = s.borderTopBgColor - case borderRightBackgroundKey: - c = s.borderRightBgColor - case borderBottomBackgroundKey: - c = s.borderBottomBgColor - case borderLeftBackgroundKey: - c = s.borderLeftBgColor } if c != nil { @@ -512,13 +484,6 @@ func (s Style) getAsPosition(k propKey) Position { return Position(0) } -func (s Style) getBorderStyle() Border { - if !s.isSet(borderStyleKey) { - return noBorder - } - return s.borderStyle -} - func (s Style) getAsTransform(propKey) func(string) string { if !s.isSet(transformKey) { return nil diff --git a/set.go b/set.go index ed6e272c..b649c167 100644 --- a/set.go +++ b/set.go @@ -37,24 +37,6 @@ func (s *Style) set(key propKey, value interface{}) { s.marginLeft = max(0, value.(int)) case marginBackgroundKey: s.marginBgColor = colorOrNil(value) - case borderStyleKey: - s.borderStyle = value.(Border) - case borderTopForegroundKey: - s.borderTopFgColor = colorOrNil(value) - case borderRightForegroundKey: - s.borderRightFgColor = colorOrNil(value) - case borderBottomForegroundKey: - s.borderBottomFgColor = colorOrNil(value) - case borderLeftForegroundKey: - s.borderLeftFgColor = colorOrNil(value) - case borderTopBackgroundKey: - s.borderTopBgColor = colorOrNil(value) - case borderRightBackgroundKey: - s.borderRightBgColor = colorOrNil(value) - case borderBottomBackgroundKey: - s.borderBottomBgColor = colorOrNil(value) - case borderLeftBackgroundKey: - s.borderLeftBgColor = colorOrNil(value) case maxWidthKey: s.maxWidth = max(0, value.(int)) case maxHeightKey: @@ -119,24 +101,6 @@ func (s *Style) setFrom(key propKey, i Style) { s.set(marginLeftKey, i.marginLeft) case marginBackgroundKey: s.set(marginBackgroundKey, i.marginBgColor) - case borderStyleKey: - s.set(borderStyleKey, i.borderStyle) - case borderTopForegroundKey: - s.set(borderTopForegroundKey, i.borderTopFgColor) - case borderRightForegroundKey: - s.set(borderRightForegroundKey, i.borderRightFgColor) - case borderBottomForegroundKey: - s.set(borderBottomForegroundKey, i.borderBottomFgColor) - case borderLeftForegroundKey: - s.set(borderLeftForegroundKey, i.borderLeftFgColor) - case borderTopBackgroundKey: - s.set(borderTopBackgroundKey, i.borderTopBgColor) - case borderRightBackgroundKey: - s.set(borderRightBackgroundKey, i.borderRightBgColor) - case borderBottomBackgroundKey: - s.set(borderBottomBackgroundKey, i.borderBottomBgColor) - case borderLeftBackgroundKey: - s.set(borderLeftBackgroundKey, i.borderLeftBgColor) case maxWidthKey: s.set(maxWidthKey, i.maxWidth) case maxHeightKey: @@ -411,7 +375,7 @@ func (s Style) MarginBackground(c TerminalColor) Style { // // Applies rounded borders to the right and bottom only // lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false) func (s Style) Border(b Border, sides ...bool) Style { - s.set(borderStyleKey, b) + s.getBorderer().Style(b) top, right, bottom, left, ok := whichSidesBool(sides...) if !ok { @@ -421,10 +385,10 @@ func (s Style) Border(b Border, sides ...bool) Style { left = true } - s.set(borderTopKey, top) - s.set(borderRightKey, right) - s.set(borderBottomKey, bottom) - s.set(borderLeftKey, left) + s.getBorderer().Top(top) + s.getBorderer().Right(right) + s.getBorderer().Bottom(bottom) + s.getBorderer().Left(left) return s } @@ -444,31 +408,31 @@ func (s Style) Border(b Border, sides ...bool) Style { // // lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder()) func (s Style) BorderStyle(b Border) Style { - s.set(borderStyleKey, b) + s.getBorderer().Style(b) return s } // BorderTop determines whether or not to draw a top border. func (s Style) BorderTop(v bool) Style { - s.set(borderTopKey, v) + s.getBorderer().Top(v) return s } // BorderRight determines whether or not to draw a right border. func (s Style) BorderRight(v bool) Style { - s.set(borderRightKey, v) + s.getBorderer().Right(v) return s } // BorderBottom determines whether or not to draw a bottom border. func (s Style) BorderBottom(v bool) Style { - s.set(borderBottomKey, v) + s.getBorderer().Bottom(v) return s } // BorderLeft determines whether or not to draw a left border. func (s Style) BorderLeft(v bool) Style { - s.set(borderLeftKey, v) + s.getBorderer().Left(v) return s } @@ -497,38 +461,38 @@ func (s Style) BorderForeground(c ...TerminalColor) Style { return s } - s.set(borderTopForegroundKey, top) - s.set(borderRightForegroundKey, right) - s.set(borderBottomForegroundKey, bottom) - s.set(borderLeftForegroundKey, left) + s.getBorderer().TopForeground(top) + s.getBorderer().RightForeground(right) + s.getBorderer().BottomForeground(bottom) + s.getBorderer().LeftForeground(left) return s } // BorderTopForeground set the foreground color for the top of the border. func (s Style) BorderTopForeground(c TerminalColor) Style { - s.set(borderTopForegroundKey, c) + s.getBorderer().TopForeground(c) return s } // BorderRightForeground sets the foreground color for the right side of the // border. func (s Style) BorderRightForeground(c TerminalColor) Style { - s.set(borderRightForegroundKey, c) + s.getBorderer().RightForeground(c) return s } // BorderBottomForeground sets the foreground color for the bottom of the // border. func (s Style) BorderBottomForeground(c TerminalColor) Style { - s.set(borderBottomForegroundKey, c) + s.getBorderer().BottomForeground(c) return s } // BorderLeftForeground sets the foreground color for the left side of the // border. func (s Style) BorderLeftForeground(c TerminalColor) Style { - s.set(borderLeftForegroundKey, c) + s.getBorderer().LeftForeground(c) return s } @@ -557,37 +521,37 @@ func (s Style) BorderBackground(c ...TerminalColor) Style { return s } - s.set(borderTopBackgroundKey, top) - s.set(borderRightBackgroundKey, right) - s.set(borderBottomBackgroundKey, bottom) - s.set(borderLeftBackgroundKey, left) + s.getBorderer().TopBackground(top) + s.getBorderer().RightBackground(right) + s.getBorderer().BottomBackground(bottom) + s.getBorderer().LeftBackground(left) return s } // BorderTopBackground sets the background color of the top of the border. func (s Style) BorderTopBackground(c TerminalColor) Style { - s.set(borderTopBackgroundKey, c) + s.getBorderer().TopBackground(c) return s } // BorderRightBackground sets the background color of right side the border. func (s Style) BorderRightBackground(c TerminalColor) Style { - s.set(borderRightBackgroundKey, c) + s.getBorderer().RightBackground(c) return s } // BorderBottomBackground sets the background color of the bottom of the // border. func (s Style) BorderBottomBackground(c TerminalColor) Style { - s.set(borderBottomBackgroundKey, c) + s.getBorderer().BottomBackground(c) return s } // BorderLeftBackground set the background color of the left side of the // border. func (s Style) BorderLeftBackground(c TerminalColor) Style { - s.set(borderLeftBackgroundKey, c) + s.getBorderer().LeftBackground(c) return s } diff --git a/style.go b/style.go index 28ddccbe..3344091d 100644 --- a/style.go +++ b/style.go @@ -48,27 +48,6 @@ const ( marginLeftKey marginBackgroundKey - // Border runes. - borderStyleKey - - // Border edges. - borderTopKey - borderRightKey - borderBottomKey - borderLeftKey - - // Border foreground colors. - borderTopForegroundKey - borderRightForegroundKey - borderBottomForegroundKey - borderLeftForegroundKey - - // Border background colors. - borderTopBackgroundKey - borderRightBackgroundKey - borderBottomBackgroundKey - borderLeftBackgroundKey - inlineKey maxWidthKey maxHeightKey @@ -142,15 +121,7 @@ type Style struct { marginLeft int marginBgColor TerminalColor - borderStyle Border - borderTopFgColor TerminalColor - borderRightFgColor TerminalColor - borderBottomFgColor TerminalColor - borderLeftFgColor TerminalColor - borderTopBgColor TerminalColor - borderRightBgColor TerminalColor - borderBottomBgColor TerminalColor - borderLeftBgColor TerminalColor + borderer Borderer maxWidth int maxHeight int @@ -441,7 +412,7 @@ func (s Style) Render(strs ...string) string { } if !inline { - str = s.applyBorder(str) + str = s.getBorderer().Apply(str) str = s.applyMargins(str, inline) } @@ -468,6 +439,14 @@ func (s Style) Render(strs ...string) string { return str } +func (s *Style) getBorderer() Borderer { + if s.borderer == nil { + s.borderer = &NormalBorderer{r: s.r} + } + + return s.borderer +} + func (s Style) maybeConvertTabs(str string) string { tw := tabWidthDefault if s.isSet(tabWidthKey) { diff --git a/unset.go b/unset.go index 1086e722..342dd6b5 100644 --- a/unset.go +++ b/unset.go @@ -172,78 +172,78 @@ func (s Style) UnsetMarginBackground() Style { // UnsetBorderStyle removes the border style rule, if set. func (s Style) UnsetBorderStyle() Style { - s.unset(borderStyleKey) + s.getBorderer().UnsetStyle() return s } // UnsetBorderTop removes the border top style rule, if set. func (s Style) UnsetBorderTop() Style { - s.unset(borderTopKey) + s.getBorderer().UnsetTop() return s } // UnsetBorderRight removes the border right style rule, if set. func (s Style) UnsetBorderRight() Style { - s.unset(borderRightKey) + s.getBorderer().UnsetRight() return s } // UnsetBorderBottom removes the border bottom style rule, if set. func (s Style) UnsetBorderBottom() Style { - s.unset(borderBottomKey) + s.getBorderer().UnsetBottom() return s } // UnsetBorderLeft removes the border left style rule, if set. func (s Style) UnsetBorderLeft() Style { - s.unset(borderLeftKey) + s.getBorderer().UnsetLeft() return s } // UnsetBorderForeground removes all border foreground color styles, if set. func (s Style) UnsetBorderForeground() Style { - s.unset(borderTopForegroundKey) - s.unset(borderRightForegroundKey) - s.unset(borderBottomForegroundKey) - s.unset(borderLeftForegroundKey) + s.getBorderer().UnsetTop() + s.getBorderer().UnsetRight() + s.getBorderer().UnsetBottom() + s.getBorderer().UnsetLeft() return s } // UnsetBorderTopForeground removes the top border foreground color rule, // if set. func (s Style) UnsetBorderTopForeground() Style { - s.unset(borderTopForegroundKey) + s.getBorderer().UnsetTopForeground() return s } // UnsetBorderRightForeground removes the right border foreground color rule, // if set. func (s Style) UnsetBorderRightForeground() Style { - s.unset(borderRightForegroundKey) + s.getBorderer().UnsetRightForeground() return s } // UnsetBorderBottomForeground removes the bottom border foreground color // rule, if set. func (s Style) UnsetBorderBottomForeground() Style { - s.unset(borderBottomForegroundKey) + s.getBorderer().UnsetBottomForeground() return s } // UnsetBorderLeftForeground removes the left border foreground color rule, // if set. func (s Style) UnsetBorderLeftForeground() Style { - s.unset(borderLeftForegroundKey) + s.getBorderer().UnsetLeftForeground() return s } // UnsetBorderBackground removes all border background color styles, if // set. func (s Style) UnsetBorderBackground() Style { - s.unset(borderTopBackgroundKey) - s.unset(borderRightBackgroundKey) - s.unset(borderBottomBackgroundKey) - s.unset(borderLeftBackgroundKey) + s.getBorderer().UnsetTopBackground() + s.getBorderer().UnsetRightBackground() + s.getBorderer().UnsetBottomBackground() + s.getBorderer().UnsetLeftBackground() return s } @@ -258,27 +258,27 @@ func (s Style) UnsetBorderTopBackgroundColor() Style { // UnsetBorderTopBackground removes the top border background color rule, // if set. func (s Style) UnsetBorderTopBackground() Style { - s.unset(borderTopBackgroundKey) + s.getBorderer().UnsetTopBackground() return s } // UnsetBorderRightBackground removes the right border background color // rule, if set. func (s Style) UnsetBorderRightBackground() Style { - s.unset(borderRightBackgroundKey) + s.getBorderer().UnsetRightBackground() return s } // UnsetBorderBottomBackground removes the bottom border background color // rule, if set. func (s Style) UnsetBorderBottomBackground() Style { - s.unset(borderBottomBackgroundKey) + s.getBorderer().UnsetBottomBackground() return s } // UnsetBorderLeftBackground removes the left border color rule, if set. func (s Style) UnsetBorderLeftBackground() Style { - s.unset(borderLeftBackgroundKey) + s.getBorderer().UnsetLeftBackground() return s }