package setup import ( "bubble/setup/ui" "bubble/types" "fmt" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" ) // FinalModel структура описывающая модель отрисовки таблицы переменных type FinalModel struct { variables []types.Variable //Список переменных table *table.Table //Таблица для вывода viewport viewport.Model //Объект куда помещена таблица для возможности прокручивать её width int //Ширина окна height int //Высота окна } // NewFinalModel создается новую модель таблицы переменных func NewFinalModel(variables []types.Variable, width, height int) *FinalModel { m := &FinalModel{ variables: variables, width: width, height: height, } if width > 0 && height > 0 { m.initViewport(width, height) } return m } func (m *FinalModel) Init() tea.Cmd { return nil } // Update отслеживает действия со таблицей func (m *FinalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: //Отслеживаем нажатие клавиш switch { case key.Matches(msg, types.MainKeys.Top): //Обработчик клавиши перехода в самый низ m.viewport.GotoTop() case key.Matches(msg, types.MainKeys.Bottom): //Обработчик клавиши перехода на самый верх m.viewport.GotoBottom() case key.Matches(msg, types.MainKeys.Confirm): return m, func() tea.Msg { return types.NextSectionMsg{} } } case tea.WindowSizeMsg: //Отслеживаем и записываем новые размер окна, вносим изменения в размеры viewport m.height = msg.Height m.width = msg.Width m.initViewport(msg.Width, msg.Height) } //Если сообщение было иным, передаем его напрямую в viewport m.viewport, cmd = m.viewport.Update(msg) return m, cmd } // View формирует строку для вывода таблицы переменных func (m *FinalModel) View() string { //Получаем размеры контейнера и ширину контента containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height) //Формируем шапку с заголовком header := ui.FinalHeader. Width(contentWidth). Render("Итоговые значения переменных") //Формируем "подвал" footer := ui.FinalFooter. Width(contentWidth). Render(fmt.Sprintf("Количество переменных: %d", len(m.variables))) //Обновляем отображение таблицы m.rebuildTable(contentWidth) //Формируем единый контент из шапки, видимой области и подвала fullContent := lipgloss.JoinVertical(lipgloss.Top, header, m.viewport.View(), footer) //Формируем главный контейнер container := ui.AppContainer. Width(containerWidth). Height(containerHeight). Render(fullContent) return lipgloss.Place( m.width, m.height, lipgloss.Center, lipgloss.Center, container, lipgloss.WithWhitespaceChars("+"), lipgloss.WithWhitespaceForeground(ui.ColorError), ) } // initViewport создание видимой области таблицы func (m *FinalModel) initViewport(width, height int) { _, _, contentWidth, contentHeight := ui.ContentSizes(width, height) tableWidth := contentWidth - 2*1 tableHeight := int(float64(contentHeight)*ui.TableWithRatio) - 2*1 //Создание видимой области, куда будет помещена таблица m.viewport = viewport.New(contentWidth, tableHeight) m.viewport.SetHorizontalStep(1) //Обновление таблицы m.rebuildTable(tableWidth) } func (m *FinalModel) rebuildTable(width int) { //Если переменных нет, то делаем спец таблицу if len(m.variables) == 0 { m.table = table.New(). Border(lipgloss.RoundedBorder()). BorderStyle(ui.FinalTableBorder). StyleFunc(func(row, col int) lipgloss.Style { return ui.FinalTableHeader }).Headers("Нет переменных") m.viewport.SetContent(m.table.Render()) return } //Вычисляем размеры колонок variableNameColWidth := 20 defaultValueColWidth := 25 usedValueColWidth := width - variableNameColWidth - defaultValueColWidth - 4*1 //ширина таблицы - ширина первой колонки - ширина второй колонки - 4 границы (2 крайние, 2 между колонками) if usedValueColWidth < 30 { //если значение сильно меньше usedValueColWidth = 30 } //Создаем модель таблицы t := table.New(). Width(variableNameColWidth+defaultValueColWidth+usedValueColWidth+4). Border(lipgloss.RoundedBorder()). BorderStyle(ui.FinalTableBorder). StyleFunc(func(row, col int) lipgloss.Style { var style lipgloss.Style if row == table.HeaderRow { return ui.FinalTableHeader } if row%2 == 0 { style = ui.FinalTableEvenRow } else { style = ui.FinalTableOddRow } switch col { case 0: return style.Width(variableNameColWidth).Align(lipgloss.Left) case 1: return style.Width(defaultValueColWidth).Align(lipgloss.Left) case 2: return style.Width(usedValueColWidth).Align(lipgloss.Left) } return style }). Headers("Variable name", "Default value", "Used value") //Заполняем таблицу данными for _, v := range m.variables { used := fmt.Sprintf("%v", v.Value) if used == "" { used = "<Пусто>" } t.Row(v.Name, v.Default, used) } //записываем таблицу в модель m.table = t //Помещение в видимую область таблицы m.viewport.SetContent(m.table.Render()) }