diff options
| author | Kujtim Hoxha <[email protected]> | 2025-04-11 18:54:13 +0200 |
|---|---|---|
| committer | Kujtim Hoxha <[email protected]> | 2025-04-21 13:38:42 +0200 |
| commit | 23e7a95083a8d875420c90e0479647f18a278c5f (patch) | |
| tree | 3f91d3eaad7267030cf079e7f946de480ffb2e54 /internal/tui/layout/split.go | |
| parent | 6b1c64bcc75b89c530294b6a2d4404682b435d56 (diff) | |
| download | opencode-23e7a95083a8d875420c90e0479647f18a278c5f.tar.gz opencode-23e7a95083a8d875420c90e0479647f18a278c5f.zip | |
intiial layout
Diffstat (limited to 'internal/tui/layout/split.go')
| -rw-r--r-- | internal/tui/layout/split.go | 229 |
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 + } +} |
