summaryrefslogtreecommitdiffhomepage
path: root/internal/tui/layout/split.go
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-04-11 18:54:13 +0200
committerKujtim Hoxha <[email protected]>2025-04-21 13:38:42 +0200
commit23e7a95083a8d875420c90e0479647f18a278c5f (patch)
tree3f91d3eaad7267030cf079e7f946de480ffb2e54 /internal/tui/layout/split.go
parent6b1c64bcc75b89c530294b6a2d4404682b435d56 (diff)
downloadopencode-23e7a95083a8d875420c90e0479647f18a278c5f.tar.gz
opencode-23e7a95083a8d875420c90e0479647f18a278c5f.zip
intiial layout
Diffstat (limited to 'internal/tui/layout/split.go')
-rw-r--r--internal/tui/layout/split.go229
1 files changed, 229 insertions, 0 deletions
diff --git a/internal/tui/layout/split.go b/internal/tui/layout/split.go
new file mode 100644
index 000000000..2a6822c7e
--- /dev/null
+++ b/internal/tui/layout/split.go
@@ -0,0 +1,229 @@
+package layout
+
+import (
+ "github.com/charmbracelet/bubbles/key"
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/kujtimiihoxha/termai/internal/tui/styles"
+)
+
+type SplitPaneLayout interface {
+ tea.Model
+ Sizeable
+}
+
+type splitPaneLayout struct {
+ width int
+ height int
+ ratio float64
+ verticalRatio float64
+
+ rightPanel Container
+ leftPanel Container
+ bottomPanel Container
+
+ backgroundColor lipgloss.TerminalColor
+}
+
+type SplitPaneOption func(*splitPaneLayout)
+
+func (s *splitPaneLayout) Init() tea.Cmd {
+ var cmds []tea.Cmd
+
+ if s.leftPanel != nil {
+ cmds = append(cmds, s.leftPanel.Init())
+ }
+
+ if s.rightPanel != nil {
+ cmds = append(cmds, s.rightPanel.Init())
+ }
+
+ if s.bottomPanel != nil {
+ cmds = append(cmds, s.bottomPanel.Init())
+ }
+
+ return tea.Batch(cmds...)
+}
+
+func (s *splitPaneLayout) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+ var cmds []tea.Cmd
+ switch msg := msg.(type) {
+ case tea.WindowSizeMsg:
+ s.SetSize(msg.Width, msg.Height)
+ return s, nil
+ }
+
+ if s.rightPanel != nil {
+ u, cmd := s.rightPanel.Update(msg)
+ s.rightPanel = u.(Container)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ }
+
+ if s.leftPanel != nil {
+ u, cmd := s.leftPanel.Update(msg)
+ s.leftPanel = u.(Container)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ }
+
+ if s.bottomPanel != nil {
+ u, cmd := s.bottomPanel.Update(msg)
+ s.bottomPanel = u.(Container)
+ if cmd != nil {
+ cmds = append(cmds, cmd)
+ }
+ }
+
+ return s, tea.Batch(cmds...)
+}
+
+func (s *splitPaneLayout) View() string {
+ var topSection string
+
+ if s.leftPanel != nil && s.rightPanel != nil {
+ leftView := s.leftPanel.View()
+ rightView := s.rightPanel.View()
+ topSection = lipgloss.JoinHorizontal(lipgloss.Top, leftView, rightView)
+ } else if s.leftPanel != nil {
+ topSection = s.leftPanel.View()
+ } else if s.rightPanel != nil {
+ topSection = s.rightPanel.View()
+ } else {
+ topSection = ""
+ }
+
+ var finalView string
+
+ if s.bottomPanel != nil && topSection != "" {
+ bottomView := s.bottomPanel.View()
+ finalView = lipgloss.JoinVertical(lipgloss.Left, topSection, bottomView)
+ } else if s.bottomPanel != nil {
+ finalView = s.bottomPanel.View()
+ } else {
+ finalView = topSection
+ }
+
+ if s.backgroundColor != nil && finalView != "" {
+ style := lipgloss.NewStyle().
+ Width(s.width).
+ Height(s.height).
+ Background(s.backgroundColor)
+
+ return style.Render(finalView)
+ }
+
+ return finalView
+}
+
+func (s *splitPaneLayout) SetSize(width, height int) {
+ s.width = width
+ s.height = height
+
+ var topHeight, bottomHeight int
+ if s.bottomPanel != nil {
+ topHeight = int(float64(height) * s.verticalRatio)
+ bottomHeight = height - topHeight
+ } else {
+ topHeight = height
+ bottomHeight = 0
+ }
+
+ var leftWidth, rightWidth int
+ if s.leftPanel != nil && s.rightPanel != nil {
+ leftWidth = int(float64(width) * s.ratio)
+ rightWidth = width - leftWidth
+ } else if s.leftPanel != nil {
+ leftWidth = width
+ rightWidth = 0
+ } else if s.rightPanel != nil {
+ leftWidth = 0
+ rightWidth = width
+ }
+
+ if s.leftPanel != nil {
+ s.leftPanel.SetSize(leftWidth, topHeight)
+ }
+
+ if s.rightPanel != nil {
+ s.rightPanel.SetSize(rightWidth, topHeight)
+ }
+
+ if s.bottomPanel != nil {
+ s.bottomPanel.SetSize(width, bottomHeight)
+ }
+}
+
+func (s *splitPaneLayout) GetSize() (int, int) {
+ return s.width, s.height
+}
+
+func (s *splitPaneLayout) BindingKeys() []key.Binding {
+ keys := []key.Binding{}
+ if s.leftPanel != nil {
+ if b, ok := s.leftPanel.(Bindings); ok {
+ keys = append(keys, b.BindingKeys()...)
+ }
+ }
+ if s.rightPanel != nil {
+ if b, ok := s.rightPanel.(Bindings); ok {
+ keys = append(keys, b.BindingKeys()...)
+ }
+ }
+ if s.bottomPanel != nil {
+ if b, ok := s.bottomPanel.(Bindings); ok {
+ keys = append(keys, b.BindingKeys()...)
+ }
+ }
+ return keys
+}
+
+func NewSplitPane(options ...SplitPaneOption) SplitPaneLayout {
+ layout := &splitPaneLayout{
+ ratio: 0.7,
+ verticalRatio: 0.9, // Default 80% for top section, 20% for bottom
+ backgroundColor: styles.Background,
+ }
+ for _, option := range options {
+ option(layout)
+ }
+ return layout
+}
+
+func WithLeftPanel(panel Container) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.leftPanel = panel
+ }
+}
+
+func WithRightPanel(panel Container) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.rightPanel = panel
+ }
+}
+
+func WithRatio(ratio float64) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.ratio = ratio
+ }
+}
+
+func WithSplitBackgroundColor(color lipgloss.TerminalColor) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.backgroundColor = color
+ }
+}
+
+func WithBottomPanel(panel Container) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.bottomPanel = panel
+ }
+}
+
+func WithVerticalRatio(ratio float64) SplitPaneOption {
+ return func(s *splitPaneLayout) {
+ s.verticalRatio = ratio
+ }
+}