GoSetup-bubble/setup/form.go
2025-12-26 17:45:37 +05:00

205 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package setup
import (
"bubble/setup/fields"
"bubble/setup/ui"
"bubble/types"
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/sirupsen/logrus"
"strings"
)
type FormModel struct {
CurrentSection *types.Section
FieldModels []fields.FieldModel
focus int
renderedContent string
}
func NewFormModel(application *types.PackageApplication, sectionIdx, focus int) *FormModel {
currentSection := &application.Sections[sectionIdx]
var fieldModels []fields.FieldModel
for _, sectionField := range currentSection.Fields {
if !CheckCondition(sectionField.Condition, application) {
continue
}
switch sectionField.FieldType {
case "select":
var options []string
for j := range sectionField.Options {
options = append(options, fmt.Sprintf("%v", sectionField.Options[j]))
}
if sectionField.SpecialValue != "" {
specialOptions := SpecialValueHandle(sectionField.SpecialValue)
if strSpecial, ok := specialOptions.([]string); ok {
options = strSpecial
}
}
selected := fmt.Sprintf("%v", sectionField.Variable.Value)
fieldModels = append(fieldModels, fields.NewSelectModel(sectionField.Name, selected, options))
case "check":
checked := false
if value, ok := sectionField.Variable.Value.(bool); ok {
checked = value
} else {
checked = sectionField.Variable.Value == "true"
}
fieldModels = append(fieldModels, fields.NewCheckboxModel(sectionField.Name, checked))
case "compute":
value, err := ComputeValue(sectionField.Compute, application)
if err != nil {
value = fmt.Sprintf("Ошибка вычисления: %v", err)
}
fieldModels = append(fieldModels, fields.NewComputeModel(sectionField.Name, fmt.Sprintf("%v", value)))
//compute расчет
case "file":
fieldModels = append(fieldModels, fields.NewFileSelectModel(sectionField.Name, fmt.Sprintf("%v", sectionField.Variable.Value), sectionField.FileFieldExtensions))
case "directory":
fieldModels = append(fieldModels, fields.NewDirSelectModel(sectionField.Name, fmt.Sprintf("%v", sectionField.Variable.Value)))
default:
fieldModels = append(fieldModels, fields.NewInputModel(sectionField.Name, fmt.Sprintf("%v", sectionField.Variable.Value)))
}
}
if len(fieldModels) > focus {
fieldModels[focus].Focus()
}
m := &FormModel{
FieldModels: fieldModels,
focus: focus,
CurrentSection: currentSection,
}
if currentSection.Type == "abouta" { //"about" {
md := strings.Join(currentSection.Paragraphs, "\n\n")
renderer, _ := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(80))
rendered, _ := renderer.Render(md)
m.renderedContent = rendered
m.focus = 0
}
return m
}
func (m *FormModel) Init() tea.Cmd {
return nil
}
func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "esc":
//вернуться к списку приложений, чтобы выбрать другое приложение.
case "enter":
logrus.Infof("sections %s, focus %d, fields len %d", m.CurrentSection.Name, m.focus, len(m.FieldModels))
if len(m.FieldModels) == m.focus {
return m, func() tea.Msg {
return types.NextSectionMsg{}
}
}
case "tab", "down", "shift+tab", "up":
key := msg.String()
if m.focus != len(m.FieldModels) {
logrus.Infof("1")
//todo исправить фокус
field := m.FieldModels[m.focus]
logrus.Infof("field %s, capture keys %t", field.GetLabel(), field.CaptureKeys())
if !field.CaptureKeys() {
if key == "tab" || key == "down" {
m.focus++
} else if key == "shift+tab" || key == "up" {
m.focus--
}
}
} else {
logrus.Infof("2")
if key == "tab" || key == "down" {
m.focus++
} else if key == "shift+tab" || key == "up" {
m.focus--
}
}
if m.focus == len(m.FieldModels)+1 {
m.focus = 0
} else if m.focus < 0 {
m.focus = len(m.FieldModels)
}
}
case types.SelectedOptionMsg:
logrus.Infof("chose %+v", msg)
}
cmd := m.updateFields(msg)
return m, cmd
}
func (m *FormModel) View() string {
res := ""
if len(m.FieldModels) > 0 {
for i := range m.FieldModels {
inputView := m.FieldModels[i].View()
if m.focus == i {
res += ui.FormFocusedStyle.Render(inputView)
} else {
res += ui.FormUnfocusedStyle.Render(inputView)
}
res += "\n"
}
}
button := "\n[ next section ]"
if m.CurrentSection.Type == "about" {
res += m.renderedContent
}
if m.focus == len(m.FieldModels) {
res += ui.FormFocusedStyle.Render(button)
} else {
res += ui.FormUnfocusedStyle.Render(button)
}
return ui.FormStyle.Render(res)
}
func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
var cmds []tea.Cmd
for i, fieldModel := range m.FieldModels {
var fieldCmd tea.Cmd
var updated fields.FieldModel
if i == m.focus { //если фокус установлен на текущем поле, то текущее поле активируем и передаем ему сообщения для обновления
fieldModel.Focus()
updated, fieldCmd = fieldModel.Update(msg)
} else { //если фокус не установлен на текущем поле, то убираем с неё фокус, передаем сообщения, но только те, что не содержат действий клавиатуры
fieldModel.Blur()
if _, ok := msg.(tea.KeyMsg); !ok {
updated, fieldCmd = fieldModel.Update(msg)
}
}
if updated != nil {
m.FieldModels[i] = updated
for _, sectionField := range m.CurrentSection.Fields {
if sectionField.Name == fieldModel.GetLabel() {
oldValue := sectionField.Variable.Value
newValue := fieldModel.GetValue()
if oldValue != newValue {
cmds = append(cmds, func() tea.Msg {
return types.SugarMsg{
VariableName: sectionField.VariableName,
FieldName: sectionField.Name,
Compute: sectionField.Compute,
Focus: m.focus,
}
})
sectionField.Variable.Value = newValue //recompute вызывать.
}
}
}
}
if fieldCmd != nil {
cmds = append(cmds, fieldCmd)
}
}
return tea.Batch(cmds...)
}