package setup import ( "bubble/setup/ui" "bubble/types" "fmt" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/sirupsen/logrus" ) type ListApplicationItem struct { Application types.PackageApplication } func (i ListApplicationItem) Title() string { return fmt.Sprintf("%s, v%s", i.Application.Name, i.Application.Version) } func (i ListApplicationItem) Description() string { return fmt.Sprintf("%s", i.Application.Description) } func (i ListApplicationItem) FilterValue() string { return i.Application.Name } type ListApplicationsModel struct { List list.Model width int height int } func NewListApplicationsModel(applications []types.PackageApplication, width, height int) *ListApplicationsModel { items := make([]list.Item, len(applications)) for i, app := range applications { items[i] = ListApplicationItem{app} } delegate := list.NewDefaultDelegate() delegate.Styles.SelectedTitle = ui.ListSelectedStyle delegate.Styles.SelectedDesc = ui.ListSelectedStyle delegate.ShowDescription = false l := list.New(items, delegate, width, height) l.Title = "Приложения" l.Styles.Title = ui.ListTitleStyle l.SetShowHelp(false) return &ListApplicationsModel{ List: l, width: width, height: height, } } func (m *ListApplicationsModel) Init() tea.Cmd { return nil } func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height return m, nil case tea.KeyMsg: switch msg.String() { case "enter": selected := m.List.SelectedItem().(ListApplicationItem) return m, func() tea.Msg { return types.ShowModalMsg{ Type: types.ModalConfirm, Title: "Начать установку", Message: fmt.Sprintf("Вы хотите настроить приложение:\n\n%s\n\nПродолжить?", selected.Title()), ModalButtons: []types.ModalButton{ {Text: "Да", Value: true}, {Text: "Нет", Value: false}, }, OnResult: func(result any) tea.Cmd { if result == true { return func() tea.Msg { return types.SelectedAppMsg{App: selected.Application} } } return nil }, } } } } m.List, cmd = m.List.Update(msg) return m, cmd } func (m *ListApplicationsModel) View() string { gapStroke := " " containerWidth, containerHeight, contentWidth, contentHeight := ui.ContentSizes(m.width, m.height) listWidth := int(float64(contentWidth)*ui.ListWithRatio) - 2*1 //ширина листа = пропорция от ширины контента - границы для контейнера листа listHeight := contentHeight - 2*1 //высота листа = высота контента - границы для контейнера листа descWidth := int(float64(contentWidth)*(1-ui.ListWithRatio)) - 2*1 - len(gapStroke) //ширина описания = обратная пропорция листа от ширины контента - границы для контейнера описания - отступ от контейнера листа descHeight := contentHeight - 2*1 //высота листа = высота контента - границы для контейнера описания m.List.SetSize(listWidth, listHeight) //logs.go logrus.DebugLevel set logrus.Debugf( "window: width %d, height %d\ncontainer: width %d, height %d\ncontent: width %d, height %d\nlist: width %d, height %d\ndesc: width %d, height %d\n\n", m.width, m.height, containerWidth, containerHeight, contentWidth, contentHeight, listWidth, listHeight, descWidth, descHeight, ) listView := lipgloss.NewStyle(). Width(listWidth). Height(listHeight). Border(lipgloss.RoundedBorder()). BorderForeground(ui.ColorPurple). AlignVertical(lipgloss.Top). Render(m.List.View()) var desc string if sel := m.List.SelectedItem(); sel != nil { desc = sel.(ListApplicationItem).Description() } descView := ui.ListDescriptionStyle. Width(descWidth). Height(descHeight). Border(lipgloss.RoundedBorder()). BorderForeground(ui.ColorError). AlignVertical(lipgloss.Top). Render( desc, ) body := lipgloss.JoinHorizontal(lipgloss.Center, listView, gapStroke, descView) container := ui.AppContainer. Width(containerWidth). Height(containerHeight). Render(body) return lipgloss.Place( m.width, m.height, lipgloss.Center, lipgloss.Center, container, lipgloss.WithWhitespaceChars("░"), lipgloss.WithWhitespaceForeground(ui.ColorWarning), ) }