single.go

  1package layout
  2
  3import (
  4	"github.com/charmbracelet/bubbles/key"
  5	tea "github.com/charmbracelet/bubbletea"
  6	"github.com/charmbracelet/lipgloss"
  7)
  8
  9type SinglePaneLayout interface {
 10	tea.Model
 11	Focusable
 12	Sizeable
 13	Bindings
 14}
 15
 16type singlePaneLayout struct {
 17	width  int
 18	height int
 19
 20	focusable bool
 21	focused   bool
 22
 23	bordered   bool
 24	borderText map[BorderPosition]string
 25
 26	content tea.Model
 27
 28	padding []int
 29}
 30
 31type SinglePaneOption func(*singlePaneLayout)
 32
 33func (s *singlePaneLayout) Init() tea.Cmd {
 34	return s.content.Init()
 35}
 36
 37func (s *singlePaneLayout) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 38	switch msg := msg.(type) {
 39	case tea.WindowSizeMsg:
 40		s.SetSize(msg.Width, msg.Height)
 41		return s, nil
 42	}
 43	u, cmd := s.content.Update(msg)
 44	s.content = u
 45	return s, cmd
 46}
 47
 48func (s *singlePaneLayout) View() string {
 49	style := lipgloss.NewStyle().Width(s.width).Height(s.height)
 50	if s.bordered {
 51		style = style.Width(s.width).Height(s.height)
 52	}
 53	if s.padding != nil {
 54		style = style.Padding(s.padding...)
 55	}
 56	content := style.Render(s.content.View())
 57	if s.bordered {
 58		if s.borderText == nil {
 59			s.borderText = map[BorderPosition]string{}
 60		}
 61		if bordered, ok := s.content.(Bordered); ok {
 62			s.borderText = bordered.BorderText()
 63		}
 64		return Borderize(content, s.focused, s.borderText)
 65	}
 66	return content
 67}
 68
 69func (s *singlePaneLayout) Blur() tea.Cmd {
 70	if s.focusable {
 71		s.focused = false
 72	}
 73	if blurable, ok := s.content.(Focusable); ok {
 74		return blurable.Blur()
 75	}
 76	return nil
 77}
 78
 79func (s *singlePaneLayout) Focus() tea.Cmd {
 80	if s.focusable {
 81		s.focused = true
 82	}
 83	if focusable, ok := s.content.(Focusable); ok {
 84		return focusable.Focus()
 85	}
 86	return nil
 87}
 88
 89func (s *singlePaneLayout) SetSize(width, height int) {
 90	s.width = width
 91	s.height = height
 92	if s.bordered {
 93		s.width -= 2
 94		s.height -= 2
 95	}
 96	childWidth, childHeight := s.width, s.height
 97	if s.padding != nil {
 98		if len(s.padding) == 1 {
 99			childWidth -= s.padding[0] * 2
100			childHeight -= s.padding[0] * 2
101		} else if len(s.padding) == 2 {
102			childWidth -= s.padding[0] * 2
103			childHeight -= s.padding[1] * 2
104		} else if len(s.padding) == 3 {
105			childWidth -= s.padding[0] * 2
106			childHeight -= s.padding[1] + s.padding[2]
107		} else if len(s.padding) == 4 {
108			childWidth -= s.padding[0] + s.padding[2]
109			childHeight -= s.padding[1] + s.padding[3]
110		}
111	}
112	if s.content != nil {
113		if c, ok := s.content.(Sizeable); ok {
114			c.SetSize(childWidth, childHeight)
115		}
116	}
117}
118
119func (s *singlePaneLayout) IsFocused() bool {
120	return s.focused
121}
122
123func (s *singlePaneLayout) GetSize() (int, int) {
124	return s.width, s.height
125}
126
127func (s *singlePaneLayout) BindingKeys() []key.Binding {
128	if b, ok := s.content.(Bindings); ok {
129		return b.BindingKeys()
130	}
131	return []key.Binding{}
132}
133
134func NewSinglePane(content tea.Model, opts ...SinglePaneOption) SinglePaneLayout {
135	layout := &singlePaneLayout{
136		content: content,
137	}
138	for _, opt := range opts {
139		opt(layout)
140	}
141	return layout
142}
143
144func WithSignlePaneSize(width, height int) SinglePaneOption {
145	return func(opts *singlePaneLayout) {
146		opts.width = width
147		opts.height = height
148	}
149}
150
151func WithSinglePaneFocusable(focusable bool) SinglePaneOption {
152	return func(opts *singlePaneLayout) {
153		opts.focusable = focusable
154	}
155}
156
157func WithSinglePaneBordered(bordered bool) SinglePaneOption {
158	return func(opts *singlePaneLayout) {
159		opts.bordered = bordered
160	}
161}
162
163func WithSignlePaneBorderText(borderText map[BorderPosition]string) SinglePaneOption {
164	return func(opts *singlePaneLayout) {
165		opts.borderText = borderText
166	}
167}
168
169func WithSinglePanePadding(padding ...int) SinglePaneOption {
170	return func(opts *singlePaneLayout) {
171		opts.padding = padding
172	}
173}