Работа над стилями, работа над модальными окнами, изменение списка приложений, работа с размерами окон

This commit is contained in:
XeRo 2025-12-29 17:46:48 +05:00
parent 5203032b83
commit ada7db46cf
12 changed files with 1161 additions and 130 deletions

185
setup/modal.go Normal file
View file

@ -0,0 +1,185 @@
package setup
import (
"bubble/setup/ui"
"bubble/types"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type ModalModel struct {
modalType types.ModalType
title string
message string
buttons []types.ModalButton
focus int
width int
height int
onResult func(result any) tea.Cmd
}
func NewModalModel(msg types.ShowModalMsg, width, height int) *ModalModel {
if len(msg.ModalButtons) == 0 {
msg.ModalButtons = []types.ModalButton{{Text: "Ok", Value: true}}
}
return &ModalModel{
modalType: msg.Type,
title: msg.Title,
message: msg.Message,
buttons: msg.ModalButtons,
focus: 0,
width: width,
height: height,
onResult: msg.OnResult,
}
}
func (m *ModalModel) Init() tea.Cmd {
return nil
}
func (m *ModalModel) Update(msg tea.Msg) (tea.Model, 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 "left", "h", "shift+tab":
m.focus--
if m.focus < 0 {
m.focus = len(m.buttons) - 1
}
case "right", "l", "tab":
m.focus++
if m.focus >= len(m.buttons) {
m.focus = 0
}
case "enter", " ":
//вытаскиваем значение кнопки
result := m.buttons[m.focus].Value
var cmd tea.Cmd
if m.onResult != nil { //если функция кнопки не пустая, мы передаем в функцию значение кнопки
cmd = m.onResult(result)
}
return m, tea.Batch( //объединяем 2 действия, функцию кнопки и закрытие модального окна
cmd,
func() tea.Msg { return types.CloseModalMsg{} },
)
}
}
return m, nil
}
func (m *ModalModel) View() string {
contentWidth := ui.ContentWidth(m.width)
modalWidth := min(contentWidth-10, ui.ModalMaxHeight)
if modalWidth < 60 {
modalWidth = 60
}
content := lipgloss.NewStyle().
Width(modalWidth-8).
Padding(1, 3).
Render(m.message)
var buttons []string
for i, btn := range m.buttons {
style := ui.ModalButtonNormal
if i == m.focus {
style = ui.ModalButtonFocused
}
buttons = append(buttons, style.Render(btn.Text))
}
buttonRow := lipgloss.JoinHorizontal(lipgloss.Center, buttons...)
body := lipgloss.JoinVertical(lipgloss.Center, content, "\n\n", buttonRow)
// Бордер и цвет в зависимости от типа
var modalStyle lipgloss.Style
var titleStyle lipgloss.Style
switch m.modalType {
case types.ModalError:
modalStyle = ui.ModalBorderError
titleStyle = ui.ModalTitleError
case types.ModalWarning, types.ModalConfirm:
modalStyle = ui.ModalBorderWarning
titleStyle = ui.ModalTitleWarning
default:
modalStyle = ui.ModalBorderInfo
titleStyle = ui.ModalTitleDefault
}
modal := lipgloss.NewStyle().
Width(modalWidth).
Padding(2, 4).
Border(lipgloss.DoubleBorder()).
BorderForeground(modalStyle.GetBorderBottomForeground()).
Render(body)
if m.title != "" {
title := titleStyle.Width(modalWidth).Align(lipgloss.Center).Render(m.title)
modal = lipgloss.JoinVertical(lipgloss.Center, title, modal)
}
return lipgloss.Place(m.width, m.height,
lipgloss.Center, lipgloss.Center,
modal,
lipgloss.WithWhitespaceChars("."),
lipgloss.WithWhitespaceForeground(ui.ColorGrey))
//modalWidth := min(m.width-20, ui.ModalMaxHeight)
//if modalWidth < 60 {
// modalWidth = 60
//}
//var borderColor lipgloss.Color
//var titleStyle lipgloss.Style
//
//switch m.modalType {
//case types.ModalError:
// borderColor = ui.ColorRed
// titleStyle = lipgloss.NewStyle().Foreground(ui.ColorRed).Bold(true)
//case types.ModalWarning, types.ModalConfirm:
// borderColor = ui.ColorPurple
// titleStyle = lipgloss.NewStyle().Foreground(ui.ColorPurple).Bold(true)
//default:
// borderColor = ui.ColorBlue
// titleStyle = lipgloss.NewStyle().Foreground(ui.ColorBlue).Bold(true)
//}
//content := lipgloss.NewStyle().Width(m.width-10).Padding(1, 2).Render(m.message)
//var buttons []string
//for i, btn := range m.buttons {
// style := lipgloss.NewStyle().
// Padding(0, 2).
// Border(lipgloss.RoundedBorder()).
// BorderForeground(ui.ColorGrey)
// if i == m.focus {
// style = style.Foreground(ui.ColorRed).BorderForeground(ui.ColorRed).Bold(true)
// } else {
// style = style.Foreground(ui.ColorGreenCyan)
// }
// buttons = append(buttons, style.Render(btn.Text))
//}
//buttonShow := lipgloss.JoinHorizontal(lipgloss.Center, buttons...)
//body := lipgloss.JoinVertical(lipgloss.Center, content, "\n", buttonShow)
//
//modal := lipgloss.NewStyle().
// Width(modalWidth).
// Padding(1, 3).
// Border(lipgloss.DoubleBorder()).
// BorderForeground(borderColor).
// Render(body)
//
//if m.title != "" {
// title := titleStyle.Width(modalWidth - 6).Render(m.title)
// modal = lipgloss.JoinVertical(lipgloss.Center, title, modal)
//}
//
//return lipgloss.Place(m.width, m.height,
// lipgloss.Center, lipgloss.Center,
// modal,
// lipgloss.WithWhitespaceChars("."),
// lipgloss.WithWhitespaceForeground(ui.ColorPurple),
//)
}