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

Conversation

eugener
Copy link

@eugener eugener commented Jul 29, 2022

Adds new Border Title API (requested in the issue #87):

  • func (s Style) BorderTitle(title string) Style
  • func (s Style) BorderTitleAlignment(alignment Position) Style
  • func (s Style) BorderTitleBackground(color TerminalColor) Style
  • func (s Style) BorderTitleForeground(color TerminalColor) Style
  • func (s Style) GetBorderTitle() string
  • func (s Style) GetBorderTitleAlignment() Position
  • func (s Style) GetBorderTitleBackground() TerminalColor
  • func (s Style) GetBorderTitleForeground() TerminalColor

Updated example:
image

@eugener
Copy link
Author

eugener commented Jul 29, 2022

Once the new API is agreed upon and stable, I will also update the documentation

@eugener eugener changed the title Add new Border Title API New Border Title API Jul 29, 2022
@eugener eugener changed the title New Border Title API feat: New Border Title API Jul 29, 2022
@maaslalani
Copy link
Member

This looks really good @eugener, nice work!

@eugener
Copy link
Author

eugener commented Sep 20, 2022

@maaslalani @meowgorithm Is there anything else needed to make this PR a part the next release?

@meowgorithm
Copy link
Member

Hey, @eugener! We just need a little time to give this a proper review. Thanks for your patience with this one; we'll definitely get to it.

@eugener
Copy link
Author

eugener commented Sep 20, 2022

Sounds good! Let me know how I can help.

.gitignore Outdated Show resolved Hide resolved
@maaslalani
Copy link
Member

maaslalani commented Sep 21, 2022

Another question I had, was would we want to support Bold / Italic / Underline / Padding / Margin in the border title?

I almost wonder if the border title should take its own style or take only a string which we expect the user to style before passing in?

I.e:

borderTitle := lipgloss.NewStyle().Bold(true).Underline(true).Render("Hello, Borders")
lipgloss.NewStyle().BorderTitle(borderTitle).Border(lipgloss.DoubleBorder())

@eugener
Copy link
Author

eugener commented Sep 21, 2022

Another question I had, was would we want to support Bold / Italic / Underline / Padding / Margin in the border title?

I almost wonder if the border title should take its own style or take only a string which we expect the user to style before passing in?

I.e:

borderTitle := lipgloss.NewStyle().Bold(true).Underline(true).Render("Hello, Borders")
lipgloss.NewStyle().BorderTitle(borderTitle).Border(lipgloss.DoubleBorder())

The idea of styled text is great and also makes the Title API surface smaller.
Can you suggest what changes are required to implement this, assuming we will remove Fg and Bg colors API?
It seems that the title can now be multiline, if it has its own border for example, which has to be taken into consideration

@maaslalani
Copy link
Member

maaslalani commented Sep 21, 2022

I think there's two ways to do it, either we can get passed the styled text or take the BorderTitleStyle as an argument and then apply the BorderTitle as an Inline Style. I.e. remove the padding and margin.

The current API doesn't prevent people from passing in multi-line text (if I'm not mistaken), so I think it is reasonable to expect the user to pass in a non-multiline string / inline style. But, @meowgorithm may have more thoughts on this.

Personally, I think it would be reasonable to remove the BorderTitleForeground and BorderTitleBackground from the API and expect people to pass in a pre-styled one-line string which includes horizontal padding / fg / bg, etc...

What do you think @meowgorithm @eugener? I'm not too opinionated on this one. Although, the way I have suggested does give the user more ways to break their application's layout, so maybe it's not worth the flexibility.

@maaslalani
Copy link
Member

That being said, I am also fully onboard with the current PR as it is. I think it is currently in a shippable state.

@meowgorithm
Copy link
Member

meowgorithm commented Sep 22, 2022

We will need to offer at least the basic ANSI styling options for this (bold, underline, strikethrough, reverse, and so on) so it probably makes sense to pass a style. It would also give people more control over the spacing around the title.

And we do have mechanisms to keep help prevent people from breaking stuff (see Enforcing Rules in the README as well as the various unset methods).

So if we go down this route you'd either pass a styled string:

borderEverything := lipgloss.NewStyle().
    Bold(true).
    Underline(true).
    Render("Hello, Borders")

style := lipgloss.NewStyle().
    BorderTitle(borderEverything).
    Border(lipgloss.DoubleBorder())

Or you'd separate concerns a bit more and set the style and the string separately:

borderTitleStyle := lipgloss.NewStyle().
    Bold(true).
    Underline(true)

style := lipgloss.NewStyle().
    BorderTitleStyle(borderStyle).
    BorderTitle("Hello, Borders").
    Border(lipgloss.DoubleBorder())

And, in this scenario, I'd keep the very good BorderTitleAlignment.

…inates the need all other methods and add an ability to style the title itself.
@eugener
Copy link
Author

eugener commented Sep 22, 2022

@maaslalani @meowgorithm Thank you both for very insightful suggestions!
Based on your suggestions, I redesigned the API by passing the Style instead of a text, which eliminated the need for all API methods except get/set BorderTitle. Currently, the title text is set using style.SetString, which seems to be fine to me comparing to introducing a separate method for setting border title text on Style, but I'm open for discussion on this topic.
New API allows for full title styling including horizontal alignment. Example and docs are up-to-date also.

image

@maaslalani
Copy link
Member

maaslalani commented Sep 22, 2022

I think using the SetString and the HorizontalAlign API is quite clever, you basically get the best of both worlds with the first (singular entry point) and second approach (flexibility and control of the Style). Awesome work!

@meowgorithm
Copy link
Member

meowgorithm commented Sep 22, 2022

This is great @eugener; thank you. Thinking on this some more, I would argue that it we will want to split out the style from the value both to be explicit, and to separate style from data for re-use as I expect a common use case to be rendering several similar looking boxes with different titles. Consider the following:

titleStyle := lipgloss.NewStyle().
    Bold(true).
    Reverse(true)

mrBoxStyle := lipgloss.NewStyle().
    BorderTitleStyle(borderStyle).
    BorderTitle("Mr. Box").
    Border(lipgloss.RoundedBorder())

mrsBoxStyle := mrBoxStyle.Copy().BorderTitle(“Mrs. Box”)

More realistically, in a view this would look something like:

boxA := boxStyle.Copy().BorderTitle(“Mr. Box”).Render(fancyData)
boxB := boxStyle.Copy().BorderTitle(“Mrs. Box”).Render(moreFancyData)

This is, of course, doable in the current implementation but a bit more roundabout:

mrTitle := lipgloss.NewStyle().
    Bold(true).
    Reverse(true).
    SetString("Mr. Box")

mrsTitle := mrTitle.Copy().SetString(“Mrs. Box”)

mrBox := lipgloss.NewStyle().
    BorderTitle(mrBox).
    Border(lipgloss.RoundedBorder())

mrsBox := mrBox.Copy().BorderTitle(mrsTitle)

And in a view this would look more like:

boxA := boxStyle.BorderTitle(titleStyle.Copy().SetString(“Mr. Box”)).Render(fancyData)
boxB := boxStyle.BorderTitle(titleStyle.Copy().SetString(“Mrs. Box”)).Render(moreFancyData)

@eugener
Copy link
Author

eugener commented Sep 22, 2022

@meowgorithm Agree! This makes border styles a lot more reusable. Will rework it as you suggest.

@meowgorithm
Copy link
Member

Sounds good. Thanks for all your hard work on this, @eugener!

@eugener
Copy link
Author

eugener commented Sep 22, 2022

The API is now reworked, including docs, example and readme. Hopefully I did not miss anything.
@meowgorithm @maaslalani Please review.

Copy link
Member

@maaslalani maaslalani left a comment

Choose a reason for hiding this comment

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

This is fantastic @eugener. Taking a look now!

example/main.go Outdated
@@ -239,7 +246,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   ───

borders.go Outdated
@@ -296,6 +324,13 @@ func (s Style) applyBorder(str string) string {
return out.String()
}

func repeatStr(s string, count int) string {
Copy link
Member

Choose a reason for hiding this comment

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

This is sort of a nitpick so feel free to ignore, but instead of having a wrapper around strings.Repeat called repeatStr I would do something like this:

strings.Repeat(border.Top, max(0, value))

Where max is:

func max(a, b int) int {
  if a > b {
    return a
  }
  return b
}

Copy link
Member

Choose a reason for hiding this comment

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

You can use the max defined here:

lipgloss/style.go

Lines 460 to 465 in 0ce5550

func max(a, b int) int {
if a > b {
return a
}
return b
}

Copy link
Author

Choose a reason for hiding this comment

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

Good suggestion... coming out with the next update :)

@maaslalani
Copy link
Member

maaslalani commented Sep 22, 2022

Not 100% sure on my opinions on this but I have a feeling that if the Border is placed at the lipgloss.Right or lipgloss.Left, it should have one single horizontal (Border Top) character to the left or right of it.

image

@eugener
Copy link
Author

eugener commented Sep 22, 2022

That is a matter of opinion :) Let's vote on this?

@maaslalani
Copy link
Member

maaslalani commented Sep 22, 2022

Hey @eugener, we appreciate the awesome work you've put into this.

We'll play around with this now as it is and some different options we have to achieve this functionality that we were toying around with and review this PR.

Since this is a big change (changing the API), we want to make sure we're doing the correct approach so that we don't need to break it later on, so we might take a bit of extra time reviewing it.

@eugener
Copy link
Author

eugener commented Sep 22, 2022

All the suggestions are implemented. The code is now in your hands (let me know how I can help).
Thank you for all the suggestions again, looking forward to see this functionality in the next release - many users will appreciate this.

@muesli muesli added the enhancement New feature or request label Oct 5, 2022
@danielcmessias
Copy link

Love this! looking forward to seeing it released ❤️

@eugener
Copy link
Author

eugener commented Jan 26, 2023

Is there any plan to merge this or add similar functionality?

@bashbunni
Copy link
Member

Hey, thanks for your patience on this. We're still deciding what the public API should look like; the current solution in this PR includes some nested styles that we need to consider. In the meantime, we've got this gist that demonstrates how you can achieve this with the existing Lipgloss API.

https://gist.github.com/meowgorithm/1777377a43373f563476a2bcb7d89306

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants