imap-notes/tui/mutliview.go

214 lines
5.0 KiB
Go

package tui
import (
// "git.iamthefij.com/iamthefij/slog"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type MultiViewModel struct {
// Internal state
columns []tea.Model
activeIndex int
mainModel tea.Model
lastWindowSizeMsg tea.WindowSizeMsg
// Styles for system
BaseStyle lipgloss.Style
ActiveStyle lipgloss.Style
NumVisibleSections int
SideColumnWidth int
// Select functionality
SelectKeys []string
OnColumnSelect func(baseModel *MultiViewModel, msg tea.Msg) tea.Cmd
BackKeys []string
OnColumnBack func(baseModel *MultiViewModel, msg tea.Msg) tea.Cmd
QuitKeys []string
}
func NewMultiViewModel(columns []tea.Model, main tea.Model) *MultiViewModel {
baseStyle := lipgloss.NewStyle().Margin(1)
return &MultiViewModel{
columns: columns,
activeIndex: 0,
mainModel: main,
BaseStyle: baseStyle,
ActiveStyle: baseStyle.Copy().UnsetMargins().BorderStyle(lipgloss.NormalBorder()).BorderForeground(
lipgloss.Color("21"),
),
NumVisibleSections: 2,
SideColumnWidth: 40,
SelectKeys: []string{tea.KeyEnter.String(), tea.KeyTab.String()},
OnColumnSelect: func(baseModel *MultiViewModel, msg tea.Msg) tea.Cmd {
baseModel.NextView()
return nil
},
BackKeys: []string{tea.KeyEsc.String(), tea.KeyShiftTab.String()},
OnColumnBack: func(baseModel *MultiViewModel, msg tea.Msg) tea.Cmd {
baseModel.PreviousView()
return nil
},
QuitKeys: []string{"q"},
}
}
func (m MultiViewModel) GetActiveIndex() int {
return m.activeIndex
}
func (m MultiViewModel) GetActiveView() *tea.Model {
if m.activeIndex < len(m.columns) {
return &m.columns[m.activeIndex]
}
return &m.mainModel
}
func (m *MultiViewModel) SetActiveView(viewModel tea.Model) {
if m.activeIndex < len(m.columns) {
m.columns[m.activeIndex] = viewModel
} else {
m.mainModel = viewModel
}
}
func (m *MultiViewModel) NextView() *tea.Model {
if m.activeIndex < len(m.columns) {
m.activeIndex++
}
return m.GetActiveView()
}
func (m *MultiViewModel) PreviousView() *tea.Model {
if m.activeIndex > 0 {
m.activeIndex--
}
return m.GetActiveView()
}
func (m *MultiViewModel) SetMainView(mainView tea.Model) {
top, right, bottom, left := m.BaseStyle.GetMargin()
m.mainModel, _ = mainView.Update(tea.WindowSizeMsg{
Height: m.lastWindowSizeMsg.Height - top - bottom,
Width: m.MainSectionWidth() - right - left,
})
}
func (m *MultiViewModel) ClearMainView() {
m.mainModel = nil
}
func (m MultiViewModel) Init() tea.Cmd { return nil }
func (m MultiViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
// Pass key strokes to active view
var updatedView tea.Model
updatedView, cmd = (*m.GetActiveView()).Update(msg)
switch {
case m.activeIndex < 0:
panic("where are we!?")
case m.activeIndex < len(m.columns):
m.columns[m.activeIndex] = updatedView
case m.activeIndex >= len(m.columns):
m.mainModel = updatedView
}
// Handle global quit
for _, quitKey := range m.QuitKeys {
if quitKey == msg.String() {
return m, tea.Quit
}
}
// Handle select key
for _, selectKey := range m.SelectKeys {
if selectKey == msg.String() {
m.OnColumnSelect(&m, msg)
break
}
}
// Handle back key
for _, selectKey := range m.BackKeys {
if selectKey == msg.String() {
m.OnColumnBack(&m, msg)
break
}
}
case tea.WindowSizeMsg:
// Pass size message to all sub views
top, right, bottom, left := m.BaseStyle.GetMargin()
// top, right, bottom, left := m.BaseStyle.GetBorderStyle().GetTopSize
for i, model := range m.columns {
m.columns[i], _ = model.Update(tea.WindowSizeMsg{
Height: msg.Height - top - bottom,
Width: m.SideColumnWidth - right - left,
})
}
if m.mainModel != nil {
m.mainModel, _ = m.mainModel.Update(tea.WindowSizeMsg{
Height: msg.Height - top - bottom,
Width: m.MainSectionWidth() - right - left,
})
}
m.lastWindowSizeMsg = msg
}
return m, cmd
}
func (m MultiViewModel) MainSectionWidth() int {
return m.lastWindowSizeMsg.Width - (m.SideColumnWidth * m.NumVisibleSections)
}
func (m MultiViewModel) View() string {
allModels := []tea.Model{}
allModels = append(allModels, m.columns...)
if m.mainModel != nil {
allModels = append(allModels, m.mainModel)
}
visibleContent := []string{}
// Add visible columns
sideColStyle := m.BaseStyle.Copy().Width(m.SideColumnWidth)
mainStyle := m.BaseStyle.Copy().Width(m.MainSectionWidth())
for i, column := range allModels {
// Get the correct style
style := sideColStyle
if i >= len(m.columns) {
style = mainStyle
}
isVisible := m.NumVisibleSections < 1 || i < m.activeIndex+m.NumVisibleSections
if isVisible {
if i == m.activeIndex {
visibleContent = append(visibleContent, m.ActiveStyle.Inherit(style).Render(column.View()))
} else {
visibleContent = append(visibleContent, style.Render(column.View()))
}
}
}
return lipgloss.JoinHorizontal(lipgloss.Top, visibleContent...)
}