wip: refactoring tui
This commit is contained in:
230
packages/tui/internal/layout/container.go
Normal file
230
packages/tui/internal/layout/container.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package layout
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/theme"
|
||||
)
|
||||
|
||||
type Container interface {
|
||||
tea.Model
|
||||
Sizeable
|
||||
Bindings
|
||||
Focus()
|
||||
Blur()
|
||||
}
|
||||
|
||||
type container struct {
|
||||
width int
|
||||
height int
|
||||
|
||||
content tea.Model
|
||||
|
||||
paddingTop int
|
||||
paddingRight int
|
||||
paddingBottom int
|
||||
paddingLeft int
|
||||
|
||||
borderTop bool
|
||||
borderRight bool
|
||||
borderBottom bool
|
||||
borderLeft bool
|
||||
borderStyle lipgloss.Border
|
||||
|
||||
focused bool
|
||||
}
|
||||
|
||||
func (c *container) Init() tea.Cmd {
|
||||
return c.content.Init()
|
||||
}
|
||||
|
||||
func (c *container) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
u, cmd := c.content.Update(msg)
|
||||
c.content = u
|
||||
return c, cmd
|
||||
}
|
||||
|
||||
func (c *container) View() string {
|
||||
t := theme.CurrentTheme()
|
||||
style := lipgloss.NewStyle()
|
||||
width := c.width
|
||||
height := c.height
|
||||
|
||||
style = style.Background(t.Background())
|
||||
|
||||
// Apply border if any side is enabled
|
||||
if c.borderTop || c.borderRight || c.borderBottom || c.borderLeft {
|
||||
// Adjust width and height for borders
|
||||
if c.borderTop {
|
||||
height--
|
||||
}
|
||||
if c.borderBottom {
|
||||
height--
|
||||
}
|
||||
if c.borderLeft {
|
||||
width--
|
||||
}
|
||||
if c.borderRight {
|
||||
width--
|
||||
}
|
||||
style = style.Border(c.borderStyle, c.borderTop, c.borderRight, c.borderBottom, c.borderLeft)
|
||||
|
||||
// Use primary color for border if focused
|
||||
if c.focused {
|
||||
style = style.BorderBackground(t.Background()).BorderForeground(t.Primary())
|
||||
} else {
|
||||
style = style.BorderBackground(t.Background()).BorderForeground(t.BorderNormal())
|
||||
}
|
||||
}
|
||||
style = style.
|
||||
Width(width).
|
||||
Height(height).
|
||||
PaddingTop(c.paddingTop).
|
||||
PaddingRight(c.paddingRight).
|
||||
PaddingBottom(c.paddingBottom).
|
||||
PaddingLeft(c.paddingLeft)
|
||||
|
||||
return style.Render(c.content.View())
|
||||
}
|
||||
|
||||
func (c *container) SetSize(width, height int) tea.Cmd {
|
||||
c.width = width
|
||||
c.height = height
|
||||
|
||||
// If the content implements Sizeable, adjust its size to account for padding and borders
|
||||
if sizeable, ok := c.content.(Sizeable); ok {
|
||||
// Calculate horizontal space taken by padding and borders
|
||||
horizontalSpace := c.paddingLeft + c.paddingRight
|
||||
if c.borderLeft {
|
||||
horizontalSpace++
|
||||
}
|
||||
if c.borderRight {
|
||||
horizontalSpace++
|
||||
}
|
||||
|
||||
// Calculate vertical space taken by padding and borders
|
||||
verticalSpace := c.paddingTop + c.paddingBottom
|
||||
if c.borderTop {
|
||||
verticalSpace++
|
||||
}
|
||||
if c.borderBottom {
|
||||
verticalSpace++
|
||||
}
|
||||
|
||||
// Set content size with adjusted dimensions
|
||||
contentWidth := max(0, width-horizontalSpace)
|
||||
contentHeight := max(0, height-verticalSpace)
|
||||
return sizeable.SetSize(contentWidth, contentHeight)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *container) GetSize() (int, int) {
|
||||
return c.width, c.height
|
||||
}
|
||||
|
||||
func (c *container) BindingKeys() []key.Binding {
|
||||
if b, ok := c.content.(Bindings); ok {
|
||||
return b.BindingKeys()
|
||||
}
|
||||
return []key.Binding{}
|
||||
}
|
||||
|
||||
// Focus sets the container as focused
|
||||
func (c *container) Focus() {
|
||||
c.focused = true
|
||||
// Pass focus to content if it supports it
|
||||
if focusable, ok := c.content.(interface{ Focus() }); ok {
|
||||
focusable.Focus()
|
||||
}
|
||||
}
|
||||
|
||||
// Blur removes focus from the container
|
||||
func (c *container) Blur() {
|
||||
c.focused = false
|
||||
// Remove focus from content if it supports it
|
||||
if blurable, ok := c.content.(interface{ Blur() }); ok {
|
||||
blurable.Blur()
|
||||
}
|
||||
}
|
||||
|
||||
type ContainerOption func(*container)
|
||||
|
||||
func NewContainer(content tea.Model, options ...ContainerOption) Container {
|
||||
c := &container{
|
||||
content: content,
|
||||
borderStyle: lipgloss.NormalBorder(),
|
||||
}
|
||||
for _, option := range options {
|
||||
option(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Padding options
|
||||
func WithPadding(top, right, bottom, left int) ContainerOption {
|
||||
return func(c *container) {
|
||||
c.paddingTop = top
|
||||
c.paddingRight = right
|
||||
c.paddingBottom = bottom
|
||||
c.paddingLeft = left
|
||||
}
|
||||
}
|
||||
|
||||
func WithPaddingAll(padding int) ContainerOption {
|
||||
return WithPadding(padding, padding, padding, padding)
|
||||
}
|
||||
|
||||
func WithPaddingHorizontal(padding int) ContainerOption {
|
||||
return func(c *container) {
|
||||
c.paddingLeft = padding
|
||||
c.paddingRight = padding
|
||||
}
|
||||
}
|
||||
|
||||
func WithPaddingVertical(padding int) ContainerOption {
|
||||
return func(c *container) {
|
||||
c.paddingTop = padding
|
||||
c.paddingBottom = padding
|
||||
}
|
||||
}
|
||||
|
||||
func WithBorder(top, right, bottom, left bool) ContainerOption {
|
||||
return func(c *container) {
|
||||
c.borderTop = top
|
||||
c.borderRight = right
|
||||
c.borderBottom = bottom
|
||||
c.borderLeft = left
|
||||
}
|
||||
}
|
||||
|
||||
func WithBorderAll() ContainerOption {
|
||||
return WithBorder(true, true, true, true)
|
||||
}
|
||||
|
||||
func WithBorderHorizontal() ContainerOption {
|
||||
return WithBorder(true, false, true, false)
|
||||
}
|
||||
|
||||
func WithBorderVertical() ContainerOption {
|
||||
return WithBorder(false, true, false, true)
|
||||
}
|
||||
|
||||
func WithBorderStyle(style lipgloss.Border) ContainerOption {
|
||||
return func(c *container) {
|
||||
c.borderStyle = style
|
||||
}
|
||||
}
|
||||
|
||||
func WithRoundedBorder() ContainerOption {
|
||||
return WithBorderStyle(lipgloss.RoundedBorder())
|
||||
}
|
||||
|
||||
func WithThickBorder() ContainerOption {
|
||||
return WithBorderStyle(lipgloss.ThickBorder())
|
||||
}
|
||||
|
||||
func WithDoubleBorder() ContainerOption {
|
||||
return WithBorderStyle(lipgloss.DoubleBorder())
|
||||
}
|
||||
Reference in New Issue
Block a user