Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New Border Title API #97

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
**/.idea/
maaslalani marked this conversation as resolved.
Show resolved Hide resolved
33 changes: 31 additions & 2 deletions borders.go
Expand Up @@ -246,11 +246,39 @@ func (s Style) applyBorder(str string) string {
border.BottomLeft = getFirstRuneAsString(border.BottomLeft)

var out strings.Builder
const sideCount = 2

// Render top
if hasTop {
top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
top = styleBorder(top, topFG, topBG)
top := ""
title := s.GetBorderTitle()
if len(strings.TrimSpace(title)) > 0 {
if len(title) > width {
// the truncation algorithm can be provided through the API in the future
title = title[0 : width-1]
}

topBeforeTitle := border.TopLeft
topAfterTitle := border.TopRight
switch s.GetBorderTitleAlignment() {
case Right:
topBeforeTitle = border.TopLeft + strings.Repeat(border.Top, width-1-len(title))
case Center:
noTitleLen := width - 1 - len(title)
noTitleLen2 := noTitleLen / sideCount
topBeforeTitle = border.TopLeft + strings.Repeat(border.Top, noTitleLen2)
topAfterTitle = strings.Repeat(border.Top, noTitleLen-noTitleLen2) + border.TopRight
case Left:
topAfterTitle = strings.Repeat(border.Top, width-1-len(title)) + border.TopRight
}

top = styleBorder(topBeforeTitle, topFG, topBG) +
styleBorder(title, s.GetBorderTitleForeground(), s.GetBorderTitleBackground()) +
styleBorder(topAfterTitle, topFG, topBG)
} else {
top = renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
top = styleBorder(top, topFG, topBG)
}
out.WriteString(top)
out.WriteRune('\n')
}
Expand Down Expand Up @@ -314,6 +342,7 @@ func renderHorizontalEdge(left, middle, right string, width int) string {

out := strings.Builder{}
out.WriteString(left)

for i := leftWidth + rightWidth; i < width+rightWidth; {
out.WriteRune(runes[j])
j++
Expand Down
4 changes: 3 additions & 1 deletion example/main.go
Expand Up @@ -95,6 +95,8 @@ var (
dialogBoxStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("#874BFD")).
BorderTitleForeground(lipgloss.Color("#FFFFFF")).
BorderTitleAlignment(lipgloss.Center).
Padding(1, 0).
BorderTop(true).
BorderLeft(true).
Expand Down Expand Up @@ -239,7 +241,7 @@ func main() {

dialog := lipgloss.Place(width, 9,
lipgloss.Center, lipgloss.Center,
dialogBoxStyle.Render(ui),
dialogBoxStyle.Copy().BorderTitle(" Question ").Render(ui),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably use Padding(0, 1) instead of manually adding spaces to demonstrate the BorderTitleStyle best practices.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this does not work as I use actual text to calculate the required text width.
@maaslalani Do you know of any API to calculate the screen width of styled text?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it might be GetHorizontalFrame to calculate all the padding and margin horizontally and then add the width of the text.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd additionally add logic where, if horizontal padding is unset on the style it defaults to Padding(0, 1):

╭ Title ───

However if horizontal padding is set, accept it at face value. For example, PaddingLeft(0).PaddingRight(3) would render as:

╭Title   ───

lipgloss.WithWhitespaceChars("猫咪"),
lipgloss.WithWhitespaceForeground(subtle),
)
Expand Down
31 changes: 30 additions & 1 deletion get.go
Expand Up @@ -434,6 +434,15 @@ func (s Style) getAsInt(k propKey) int {
return 0
}

func (s Style) getAsString(k propKey) string {
if v, ok := s.rules[k]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}

func (s Style) getAsPosition(k propKey) Position {
v, ok := s.rules[k]
if !ok {
Expand All @@ -456,7 +465,27 @@ func (s Style) getBorderStyle() Border {
return noBorder
}

// Split a string into lines, additionally returning the size of the widest
// GetBorderTitle returns border title if set, otherwise returns empty string.
func (s Style) GetBorderTitle() string {
return s.getAsString(borderTitleKey)
}

// GetBorderTitleAlignment returns border title alignment.
func (s Style) GetBorderTitleAlignment() Position {
return s.getAsPosition(borderTitleAlignmentKey)
}

// GetBorderTitleBackground returns border title background color.
func (s Style) GetBorderTitleBackground() TerminalColor {
return s.getAsColor(borderTitleBackgroundKey)
}

// GetBorderTitleForeground returns border title foreground color.
func (s Style) GetBorderTitleForeground() TerminalColor {
return s.getAsColor(borderTitleForegroundKey)
}

// Split a string into lines, additionally returning the size of the widest.
// line.
func getLines(s string) (lines []string, widest int) {
lines = strings.Split(s, "\n")
Expand Down
24 changes: 24 additions & 0 deletions set.go
Expand Up @@ -454,6 +454,30 @@ func (s Style) BorderLeftBackground(c TerminalColor) Style {
return s
}

// BorderTitle set border title, which is displayed as part of the top border.
func (s Style) BorderTitle(title string) Style {
s.set(borderTitleKey, title)
return s
}

// BorderTitleAlignment set border title alignment.
func (s Style) BorderTitleAlignment(alignment Position) Style {
s.set(borderTitleAlignmentKey, alignment)
return s
}

// BorderTitleBackground set border title background color.
func (s Style) BorderTitleBackground(color TerminalColor) Style {
s.set(borderTitleBackgroundKey, color)
return s
}

// BorderTitleForeground set border title background color.
func (s Style) BorderTitleForeground(color TerminalColor) Style {
s.set(borderTitleForegroundKey, color)
return s
}

// Inline makes rendering output one line and disables the rendering of
// margins, padding and borders. This is useful when you need a style to apply
// only to font rendering and don't want it to change any physical dimensions.
Expand Down
6 changes: 6 additions & 0 deletions style.go
Expand Up @@ -65,6 +65,12 @@ const (
borderBottomBackgroundKey
borderLeftBackgroundKey

// Border title.
borderTitleKey
borderTitleAlignmentKey
borderTitleBackgroundKey
borderTitleForegroundKey

inlineKey
maxWidthKey
maxHeightKey
Expand Down