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: Introduce border interface #316

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nervo
Copy link
Contributor

@nervo nervo commented Jun 10, 2024

While i was working on my pr on a scrollbar bubble component (see: charmbracelet/bubbles#536), i suddenly realized how amazing it would be if such a component could be render as a border.

imagine something like that:

╭─────────╮
│Lorem    █
│ipsum    █
│dolor    █
│sit      ░
│amet     ░
╰─────────╯
╭─────────╮
│Lorem    ┴
│ipsum    █
│dolor    █
│sit      ░
│amet     ┬
╰─────────╯
╭──────────╮
│Lorem    ╭┴╮
│ipsum    │█│
│dolor    │█│
│sit      │░│
│amet     ╰┬╯
╰──────────╯

Anyway, i quickly realized too that the way border are handled, there is no room for custom complex rendering...

So i came up with an elegant solution, in multiple steps. The first one would be to introduce a new interface Borderer, and a first non-breaking implementation: NormalRenderer. Then, it's all about transferring border responsabitilities to borderer.

Here is this first step :)

Before going further, and make this interface able to handle complex borders, i would like to know if you're interested in :)

One of the annoying point to fix first, would be - for instance - the incredible heavy interface weight:

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
}

Somehow related:

@nervo nervo requested review from meowgorithm and muesli as code owners June 10, 2024 14:02
@nervo nervo force-pushed the border-interface branch from bc2473a to ae651ee Compare June 10, 2024 14:15
@meowgorithm
Copy link
Member

@nervo This is SO RAD. We're definitely open to it. Could you share what a Borderer implementation would like (if it's not too much work)?

@nervo
Copy link
Contributor Author

nervo commented Jun 12, 2024

@meowgorithm i'm glad to have your attention :)

So, well, combining at the same time the both constraints of lighten the interface, and give a way to customize border of any direction (top, right, bottom, left), i was thinking about a notion of Region.

A borderer object could have 4 main methods to get 4 regions of 4 directions:

  • TopRegion
  • RightRegion
  • BottomRegion
  • ... (you get it :) )

Each of this regions (also interfaces) should then be able to respond to the current methods of:

  • get/set/unset their presence
  • get/set/unset their foreground and background
  • get their size

Ultimately, a region should have a Render method, with the size and maybe the direction as parameter.

This way, something like that could be possible:

style := lipgloss.NewStyle().
	Borderer(
		NewBorderer(
			NewTitleRegion("This is my amazing and far too long title"), // Top: a title
			NewScrollbarRegion(...), // Right: a scrollbar (linked to a viewport, for instance)
			NewRegion(), // Bottom: Nothing special, a traditionnal border
			NewRegion(), // Left: Nothing special, a traditionnal border too
		)
	)

What do you think ?

@meowgorithm
Copy link
Member

Okay this is cool. To properly understand it a full, runnable example would be really helpful.

Does charmbracelet/bubbles#536 use this API?

@bashbunni
Copy link
Member

Hey @nervo if you have a working example of this, it would be great to test out!

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

Successfully merging this pull request may close these issues.

3 participants