diff --git a/setup/fields/checkbox.go b/setup/fields/checkbox.go index 406dc74..876714b 100644 --- a/setup/fields/checkbox.go +++ b/setup/fields/checkbox.go @@ -3,6 +3,7 @@ package fields import ( "bubble/types" "fmt" + "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" ) @@ -36,9 +37,9 @@ func (m *CheckboxModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: //Отслеживает нажатия клавиш. - switch msg.String() { - case "enter": - //Если был нажат enter, то меняем текущее значение на противоположное + switch { + case key.Matches(msg, types.MainKeys.Confirm): + //Если была нажата клавиша подтверждения, то меняем текущее значение на противоположное m.checked = !m.checked m.CheckboxValidate() } diff --git a/setup/fields/dirSelect.go b/setup/fields/dirSelect.go index a7235ce..6a2efea 100644 --- a/setup/fields/dirSelect.go +++ b/setup/fields/dirSelect.go @@ -3,7 +3,9 @@ package fields import ( "bubble/setup/ui" "bubble/setup/utils" + "bubble/types" "fmt" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/sirupsen/logrus" @@ -54,24 +56,24 @@ func (m *DirSelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { //Отслеживает нажатия клавиш. //Если список открыт, то отслеживаем клавиши отдельно if m.open { - switch msg.String() { - case "esc": - //Закрываем список + switch { + case key.Matches(msg, types.MainKeys.Close): + //Обработчик клавиши закрытия списка опций m.open = false - case "up": - //Уменьшаем фокус, поднимаем его + case key.Matches(msg, types.MainKeys.Up): + //Обработчик клавиши перехода на прошлую опцию m.focus-- if m.focus < 0 { m.focus = len(m.options) - 1 } - case "down": - //Увеличиваем фокус, опуская его + case key.Matches(msg, types.MainKeys.Down): + //Обработчик клавиши перехода на следующую опцию m.focus++ if m.focus == len(m.options) { m.focus = 0 } - case "enter": - //Выбираем директорию из списка. Если список пуст ничего не делаем. + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши выбора опции. Если список пуст, ничего не делаем. //Получаем имя файла. //Если имя файла ".." возвращаемся в родительскую папку. //Если файл является директорией, то записываем его и закрываем список. @@ -102,10 +104,10 @@ func (m *DirSelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { } } } else { - //Если список не открыт и был нажат enter, открываем его и рисуем опции списка. - //Иначе просто передаем сообщения нажатия клавиш в input. - switch msg.String() { - case "enter": + //Если список не открыт и была нажата клавиша подтверждения, открываем его и рисуем опции списка. + //Иначе просто передаем сообщение нажатия клавиш в input. + switch { + case key.Matches(msg, types.MainKeys.Confirm): m.open = true newDir := strings.TrimSpace(m.input.Value()) if len(newDir) > 1 && strings.HasSuffix(newDir, string(filepath.Separator)) { diff --git a/setup/fields/fileSelect.go b/setup/fields/fileSelect.go index 540de0c..03cb19a 100644 --- a/setup/fields/fileSelect.go +++ b/setup/fields/fileSelect.go @@ -3,7 +3,9 @@ package fields import ( "bubble/setup/ui" "bubble/setup/utils" + "bubble/types" "fmt" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/sirupsen/logrus" @@ -58,24 +60,24 @@ func (m *FileSelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { //Отслеживает нажатия клавиш. //Если список открыт, то отслеживаем клавиши отдельно if m.open { - switch msg.String() { - case "esc": - //Закрываем список + switch { + case key.Matches(msg, types.MainKeys.Close): + //Обработчик клавиши закрытия списка опций m.open = false - case "up": - //Уменьшаем фокус, поднимаем его. + case key.Matches(msg, types.MainKeys.Up): + //Обработчик клавиши перехода на прошлую опцию m.focus-- if m.focus < 0 { m.focus = len(m.options) - 1 } - case "down": - //Увеличиваем фокус, опускаем его. + case key.Matches(msg, types.MainKeys.Down): + //Обработчик клавиши перехода на следующую опцию m.focus++ if m.focus == len(m.options) { m.focus = 0 } - case "enter": - //Выбираем файл из списка. Если список пуст ничего не делаем. + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши выбора опции. Если список пуст, ничего не делаем. //Получаем имя файла. //Если имя файла ".." возвращаемся в родительскую папку. //Если файл является директорией, то переходим в эту директорию. @@ -114,10 +116,10 @@ func (m *FileSelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { } } } else { - //Если список не открыт и был нажат enter, открываем его и рисуем опции списка. + //Если список не открыт и была нажата клавиша подтверждения, открываем его и рисуем опции списка. //Иначе просто передаем сообщение нажатия клавиш в input. - switch msg.String() { - case "enter": + switch { + case key.Matches(msg, types.MainKeys.Confirm): m.open = true m.RenderOptions() return m, nil diff --git a/setup/fields/select.go b/setup/fields/select.go index 3f15863..030b426 100644 --- a/setup/fields/select.go +++ b/setup/fields/select.go @@ -2,7 +2,9 @@ package fields import ( "bubble/setup/utils" + "bubble/types" "fmt" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) @@ -56,24 +58,24 @@ func (m *SelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { //Отслеживаем нажатия клавиш. //Если список открыт, то отслеживаем клавиши отдельно if m.open { - switch msg.String() { - case "esc": - //Закрываем список + switch { + case key.Matches(msg, types.MainKeys.Close): + //Обработчик клавиши закрытия списка опций m.open = false - case "up": - //Уменьшаем фокус, поднимаем его + case key.Matches(msg, types.MainKeys.Up): + //Обработчик клавиши перехода на прошлую опцию m.focus-- if m.focus < 0 { m.focus = len(m.options) - 1 } - case "down": - //Увеличиваем фокус, опускаем его + case key.Matches(msg, types.MainKeys.Down): + //Обработчик клавиши перехода на следующую опцию m.focus++ if m.focus == len(m.options) { m.focus = 0 } - case "enter": - //Выбираем опцию из списка + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши выбора опции. Если список пуст, ничего не делаем. if len(m.options) == 0 || m.focus >= len(m.options) { return m, nil } @@ -84,9 +86,10 @@ func (m *SelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) { return m, nil } } else { - //Если список не открыт и был нажат enter, открываем его и устанавливаем фокус на основе опций. - switch msg.String() { - case "enter": + //Если список не открыт и была нажата клавиша подтверждения, открываем его и рисуем опции списка. + //Иначе просто передаем сообщение нажатия клавиш в input. + switch { + case key.Matches(msg, types.MainKeys.Confirm): m.open = true for optionIdx := range m.options { if m.options[optionIdx] == m.chosen { diff --git a/setup/final.go b/setup/final.go index 744642e..5e2d35b 100644 --- a/setup/final.go +++ b/setup/final.go @@ -4,6 +4,7 @@ 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" @@ -42,12 +43,12 @@ func (m *FinalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: //Отслеживаем нажатие клавиш - switch msg.String() { - case "home": - //Обработчик home для перехода в самый вверх + switch { + case key.Matches(msg, types.MainKeys.Top): + //Обработчик клавиши перехода в самый низ m.viewport.GotoTop() - case "end": - //Обработчик end для перехода в самый низ + case key.Matches(msg, types.MainKeys.Bottom): + //Обработчик клавиши перехода на самый верх m.viewport.GotoBottom() } case tea.WindowSizeMsg: diff --git a/setup/form.go b/setup/form.go index 6a36191..f705527 100644 --- a/setup/form.go +++ b/setup/form.go @@ -6,6 +6,7 @@ import ( "bubble/setup/utils" "bubble/types" "fmt" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -115,9 +116,9 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tea.KeyMsg: //Отслеживаем нажатие клавиш формы - switch msg.String() { - case "esc": - //Обработчик клавиши escape на форме нужен для закрытия полей поддерживающих открытие. + switch { + case key.Matches(msg, types.MainKeys.Close): + //Обработчик клавиши закрытия на форме нужен для закрытия полей поддерживающих открытие или закрытие формы настройки. //Обходим все доступные в текущей форме поля и закрываем те, что были открыты. //Проверять на фокус не нужно, так как не предполагается, что у нас может быть открыто сразу несколько полей. //Поэтому просто возвращаем команду по возврату сообщение об обновлении полей. @@ -147,8 +148,8 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { }, } } - case "enter": - //Обработчик клавиши enter на форме нужен для перехода на следующую секцию. + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши подтверждения на форме, нужен для перехода на следующую секцию. //Проверяем где сейчас находится фокус, если его позиция равняется количеству полей, //значит он вышел за пределы формы и находится на кнопке next section, что ставится последней. //После этого мы сбрасываем фокус и возвращаем команду по возврату сообщения о переходе на следующую секцию @@ -158,53 +159,23 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return types.NextSectionMsg{} } } - case "tab", "shift+tab", "down", "up": - //Обработчик клавиш tab, shift+tab, down, up на форме нужен для перехода между объектами формы. - //Получаем точное имя клавиши нажатия, после проверяем где сейчас находится фокус. + case key.Matches(msg, types.MainKeys.Up): + //Обработчик клавиши перехода на предыдущий элемент формы, нужен для смены фокуса на прошлый элемент. //Если фокус меньше количества полей, то значит фокус сейчас на поле формы. - // Подробнее. У нас фокус в двух состояниях так сказать, - // фокус равен количество полей, значит он на кнопке следующей секции, - // если меньше количества полей или равен 0, значит фокус на поле формы. - // Если у нас нет полей на форме (секция About), значит количество полей равно 0, а фокус по умолчанию устанавливается 0. - // Чтобы m.FieldModels[m.focus] не вызвало ошибку "индекс вне массива", - // нужно чтобы в условие m.focus < len(m.FieldModels) попал фокус меньше 0 (-1,-2 и тд). - // Чтобы этого избежать, есть проверка, которая устанавливает m.focus на последнюю позицию (количество полей на форме). - // Чтобы эта проверка дала сбой, нужно после неё прописать изменение фокуса формы на отрицательный принудительно, так как такое не может произойти в текущем коде. - //Получаем имя поля (так как это старый фокус, то и поле на котором мы находимся это предыдущее поле, отсюда и название переменной), - //Если поле не открыто, то мы можем перейти на следующее поле, увеличив или уменьшив фокус. - //Если фокус равен количеству полей, значит он находится на кнопке переключения секций. Увеличиваем или уменьшаем фокус. - key := msg.String() + //Если поле не открыто, то уменьшаем фокус. + //Если поле открыто, то передаем сообщение в это поле. + //Если фокус стал меньше 0, то присваиваем ему индекс кнопки секции и + //возвращаем команду по возврату сообщения прокручивания в самый низ. if m.focus < len(m.FieldModels) { - prevField := m.FieldModels[m.focus] - if !prevField.CaptureKeys() { - if key == "tab" || key == "down" { - m.focus++ - } else if key == "shift+tab" || key == "up" { - m.focus-- - } + if !m.FieldModels[m.focus].CaptureKeys() { + m.focus-- } else { return m, m.updateFields(msg) } } else { - if key == "tab" || key == "down" { - m.focus++ - } else if key == "shift+tab" || key == "up" { - m.focus-- - } + m.focus-- } - //Проверка правильности фокуса, чтобы не выйти за границы массива полей формы, необходимо проверить новое значение фокуса. - //Если фокус стал больше чем количество полей формы, значит он вышел за пределы всей формы и находится уже даже ниже кнопки, - //переводим его на первую позицию, то есть на 0 и перемещаем видимую область наверх. - //Если фокус стал меньше 0, значит он вышел за пределы формы и находится выше первого поля формы, - //переводим его на последнюю позицию, то есть на кнопку переключения секции, равную положение которой = количество полей на форме и перемещаем видимую область вниз. - if m.focus == len(m.FieldModels)+1 { - m.focus = 0 - return m, func() tea.Msg { - return types.ViewportScrollMsg{ - Direction: "up", - } - } - } else if m.focus < 0 { + if m.focus < 0 { m.focus = len(m.FieldModels) return m, func() tea.Msg { return types.ViewportScrollMsg{ @@ -212,7 +183,129 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } } + case key.Matches(msg, types.MainKeys.Down): + //Обработчик клавиши перехода на следующий элемент формы, нужен для смены фокуса на следующий элемент. + //Если фокус меньше количества полей, то значит фокус сейчас на поле формы. + //Если поле не открыто, то увеличиваем фокус. + //Если поле открыто, то передаем сообщение в это поле. + //Если фокус стал больше количества полей формы, то присваиваем ему стартовый индекс и + //возвращаем команду по возврату сообщения прокручивания в самый верх. + if m.focus < len(m.FieldModels) { + if !m.FieldModels[m.focus].CaptureKeys() { + m.focus++ + } else { + return m, m.updateFields(msg) + } + } else { + m.focus++ + } + if m.focus == len(m.FieldModels)+1 { + m.focus = 0 + return m, func() tea.Msg { + return types.ViewportScrollMsg{ + Direction: "up", + } + } + } } + //switch msg.String() { + //case "esc": + // //Обработчик клавиши escape на форме нужен для закрытия полей поддерживающих открытие. + // //Обходим все доступные в текущей форме поля и закрываем те, что были открыты. + // //Проверять на фокус не нужно, так как не предполагается, что у нас может быть открыто сразу несколько полей. + // //Поэтому просто возвращаем команду по возврату сообщение об обновлении полей. + // for _, field := range m.FieldModels { + // //проверяем открыто ли поле + // if field.CaptureKeys() { + // //обновляем поля если оно открыто + // return m, m.updateFields(msg) + // } + // } + // //Если открытых полей не было найдено, значит escape была нажата для закрытия настройки, + // //возвращаем команду по возврату сообщения об открытии модального окна + // return m, func() tea.Msg { + // return types.ShowModalMsg{ + // Type: types.ModalWarning, + // Title: "Прервать настройку", + // Message: "Вы уверены, что хотите прервать настройку?\nВсе введенные данные будут потеряны.", + // ModalButtons: []types.ModalButton{ + // {Text: "Да, выйти", Value: true}, + // {Text: "Продолжить настройку", Value: false}, + // }, + // OnResult: func(result any) tea.Cmd { + // if result == true { + // return func() tea.Msg { return types.BackToListMsg{} } + // } + // return nil + // }, + // } + // } + //case "enter": + // //Обработчик клавиши enter на форме нужен для перехода на следующую секцию. + // //Проверяем где сейчас находится фокус, если его позиция равняется количеству полей, + // //значит он вышел за пределы формы и находится на кнопке next section, что ставится последней. + // //После этого мы сбрасываем фокус и возвращаем команду по возврату сообщения о переходе на следующую секцию + // if len(m.FieldModels) == m.focus { + // m.focus = 0 + // return m, func() tea.Msg { + // return types.NextSectionMsg{} + // } + // } + //case "tab", "shift+tab", "down", "up": + // //Обработчик клавиш tab, shift+tab, down, up на форме нужен для перехода между объектами формы. + // //Получаем точное имя клавиши нажатия, после проверяем где сейчас находится фокус. + // //Если фокус меньше количества полей, то значит фокус сейчас на поле формы. + // // Подробнее. У нас фокус в двух состояниях так сказать, + // // фокус равен количество полей, значит он на кнопке следующей секции, + // // если меньше количества полей или равен 0, значит фокус на поле формы. + // // Если у нас нет полей на форме (секция About), значит количество полей равно 0, а фокус по умолчанию устанавливается 0. + // // Чтобы m.FieldModels[m.focus] не вызвало ошибку "индекс вне массива", + // // нужно чтобы в условие m.focus < len(m.FieldModels) попал фокус меньше 0 (-1,-2 и тд). + // // Чтобы этого избежать, есть проверка, которая устанавливает m.focus на последнюю позицию (количество полей на форме). + // // Чтобы эта проверка дала сбой, нужно после неё прописать изменение фокуса формы на отрицательный принудительно, так как такое не может произойти в текущем коде. + // //Получаем имя поля (так как это старый фокус, то и поле на котором мы находимся это предыдущее поле, отсюда и название переменной), + // //Если поле не открыто, то мы можем перейти на следующее поле, увеличив или уменьшив фокус. + // //Если фокус равен количеству полей, значит он находится на кнопке переключения секций. Увеличиваем или уменьшаем фокус. + // key := msg.String() + // if m.focus < len(m.FieldModels) { + // prevField := m.FieldModels[m.focus] + // if !prevField.CaptureKeys() { + // if key == "tab" || key == "down" { + // m.focus++ + // } else if key == "shift+tab" || key == "up" { + // m.focus-- + // } + // } else { + // return m, m.updateFields(msg) + // } + // } else { + // if key == "tab" || key == "down" { + // m.focus++ + // } else if key == "shift+tab" || key == "up" { + // m.focus-- + // } + // } + // //Проверка правильности фокуса, чтобы не выйти за границы массива полей формы, необходимо проверить новое значение фокуса. + // //Если фокус стал больше чем количество полей формы, значит он вышел за пределы всей формы и находится уже даже ниже кнопки, + // //переводим его на первую позицию, то есть на 0 и перемещаем видимую область наверх. + // //Если фокус стал меньше 0, значит он вышел за пределы формы и находится выше первого поля формы, + // //переводим его на последнюю позицию, то есть на кнопку переключения секции, равную положение которой = количество полей на форме и перемещаем видимую область вниз. + // if m.focus == len(m.FieldModels)+1 { + // m.focus = 0 + // return m, func() tea.Msg { + // return types.ViewportScrollMsg{ + // Direction: "up", + // } + // } + // } else if m.focus < 0 { + // m.focus = len(m.FieldModels) + // return m, func() tea.Msg { + // return types.ViewportScrollMsg{ + // Direction: "down", + // } + // } + // } + //} case types.ViewportScrollMsg: if msg.Direction == "up" { m.viewport.GotoTop() diff --git a/setup/help.go b/setup/help.go index 33291cb..9bca696 100644 --- a/setup/help.go +++ b/setup/help.go @@ -1,99 +1 @@ package setup - -import "github.com/charmbracelet/bubbles/key" - -//todo интегрировать нормально клавиши - -// MainKeyMap структура главных клавиш -type MainKeyMap struct { - Help key.Binding //Клавиша помощи - Confirm key.Binding //Клавиша подтверждения - Exit key.Binding //Клавиша выхода -} - -// ArrowKeyMap структура навигации -type ArrowKeyMap struct { - Up key.Binding //Клавиша перемещения наверх - Down key.Binding //Клавиша перемещения вниз - Left key.Binding //Клавиша перемещения влево - Right key.Binding //Клавиша перемещения вниз - Top key.Binding //Клавиша перемещения в самый верх - Bottom key.Binding //Клавиша перемещения в самый низ -} - -// FormKeyMap структура клавиш формы -type FormKeyMap struct { - MainKeyMap //Главные клавиши - ArrowKeyMap //Клавиши навигации - LogWindow key.Binding //Клавиша окна логирования - Close key.Binding //Клавиша закрытия -} - -// ListKeyMap структура клавиш списка приложений -type ListKeyMap struct { - MainKeyMap //Главные клавиши - ArrowKeyMap //Клавиши навигации -} - -type BaseKeyMap struct { - Up key.Binding - Down key.Binding - Left key.Binding - Right key.Binding - Confirm key.Binding - Help key.Binding - Close key.Binding - Quit key.Binding - Log key.Binding -} - -func (k BaseKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Help, k.Log, k.Close, k.Quit} -} - -func (k BaseKeyMap) FullHelp() [][]key.Binding { - return [][]key.Binding{ - {k.Up, k.Down, k.Right, k.Left}, - {k.Confirm, k.Help, k.Log}, - {k.Close, k.Quit}, - } -} - -var MainKeys = MainKeyMap{ - Help: key.NewBinding(key.WithKeys("?", "ctrl+h"), key.WithHelp("?/ctrl+h", "help")), - Confirm: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm")), - Exit: key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "exit")), -} - -var ArrowKeys = ArrowKeyMap{ - Up: key.NewBinding(key.WithKeys("up"), key.WithHelp("up", "move up")), - Down: key.NewBinding(key.WithKeys("down"), key.WithHelp("down", "move down")), - Left: key.NewBinding(key.WithKeys("left"), key.WithHelp("left", "move left")), - Right: key.NewBinding(key.WithKeys("right"), key.WithHelp("right", "move right")), - Top: key.NewBinding(key.WithKeys("home"), key.WithHelp("home", "to the top")), - Bottom: key.NewBinding(key.WithKeys("end"), key.WithHelp("end", "to the end")), -} - -var FormKeys = FormKeyMap{ - MainKeyMap: MainKeys, - ArrowKeyMap: ArrowKeys, - LogWindow: key.NewBinding(key.WithKeys("ctrl+l"), key.WithHelp("ctrl+l", "show/hide logs")), - Close: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "close select/close app setting")), -} - -var ListKeys = ListKeyMap{ - MainKeyMap: MainKeys, - ArrowKeyMap: ArrowKeys, -} - -var keys = BaseKeyMap{ - Up: key.NewBinding(key.WithKeys("up"), key.WithHelp("up", "move up")), - Down: key.NewBinding(key.WithKeys("down"), key.WithHelp("down", "move down")), - Left: key.NewBinding(key.WithKeys("left"), key.WithHelp("left", "move left")), - Right: key.NewBinding(key.WithKeys("right"), key.WithHelp("right", "move right")), - Confirm: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm")), - Help: key.NewBinding(key.WithKeys("?", "ctrl+h"), key.WithHelp("?/ctrl+h", "help")), - Close: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "close")), - Quit: key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "quit")), - Log: key.NewBinding(key.WithKeys("ctrl+l"), key.WithHelp("ctrl+l", "show/hide log window")), -} diff --git a/setup/list.go b/setup/list.go index 04900ee..56cd6a1 100644 --- a/setup/list.go +++ b/setup/list.go @@ -78,8 +78,8 @@ func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: //Отслеживаем нажатия клавиш списка switch { - case key.Matches(msg, keys.Confirm): - //Обработчик выбора нужен для подтверждения элемента списка. + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши подтверждения приложения, нужен для подтверждения элемента списка. //Получаем информацию о выбранном приложении, //возвращаем команду по возврату сообщения о показе модального окна подтверждения настройки приложения. selected := m.List.SelectedItem().(ListApplicationItem) @@ -103,7 +103,6 @@ func (m *ListApplicationsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } } - } //Если ранее не было возвращено команд, то передаем сообщение напрямую в список m.List, cmd = m.List.Update(msg) diff --git a/setup/main.go b/setup/main.go index 9281a82..7563386 100644 --- a/setup/main.go +++ b/setup/main.go @@ -5,7 +5,6 @@ import ( "bubble/setup/utils" "bubble/types" "fmt" - "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" "github.com/sirupsen/logrus" @@ -34,8 +33,6 @@ type Main struct { Width int //Ширина окна Height int //Высота окна Program *tea.Program //Программа для отправки данных между собой - Keys BaseKeyMap - help help.Model } // NewMain создаем новый объект настройки @@ -45,10 +42,7 @@ func NewMain() *Main { m := &Main{ Applications: apps, CurrentSectionIdx: 0, - Keys: keys, - help: help.New(), } - m.help.ShowAll = false //Записываем в текущую модель список приложений m.current = NewListApplicationsModel(m.Applications, m.Width, m.Height) m.log = NewLogModel(m.Width, m.Height) @@ -101,13 +95,12 @@ func (m *Main) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: //Отслеживаем нажатие клавиш приложения switch { - case key.Matches(msg, m.Keys.Help): + case key.Matches(msg, types.MainKeys.Help): //Обработчик клавиш помощи, показывает/скрывает полное меню подсказок - m.help.ShowAll = !m.help.ShowAll - case key.Matches(msg, m.Keys.Log): - //Обработчик клавиш логов показывает/скрывает окно логирования действий + case key.Matches(msg, types.MainKeys.Log): + //Обработчик клавиш логов, показывает/скрывает окно логирования действий m.showLog = !m.showLog - case key.Matches(msg, m.Keys.Quit): + case key.Matches(msg, types.MainKeys.Exit): //Обработчик клавиш закрытия приложения. //Возвращаем команду по возврату сообщения о показе модального окна return m, func() tea.Msg { @@ -259,7 +252,8 @@ func (m *Main) View() string { if m.overlay != nil { view = m.overlay.View() } - return view + "\n\n" + m.help.View(m.Keys) + //return view + "\n\n" + m.help.View(MainKeys) + return view } // Sugar проверяет поле указанное в сообщение на важность в форме и приложении. diff --git a/setup/modal.go b/setup/modal.go index a7eddda..1af7739 100644 --- a/setup/modal.go +++ b/setup/modal.go @@ -3,6 +3,7 @@ package setup import ( "bubble/setup/ui" "bubble/types" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -60,25 +61,25 @@ func (m *ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tea.KeyMsg: //Отслеживаем нажатие клавиш модального окна - switch msg.String() { - case "left", "shift+tab": - //Обработчик нажатия клавиш left и shift+tab нужен для перевода фокуса на прошлую кнопку. + switch { + case key.Matches(msg, types.MainKeys.Up) || key.Matches(msg, types.MainKeys.Left): + //Обработчик нажатия клавиш перехода на прошлую кнопку. //Уменьшаем фокус, если фокус стал меньше 0, значит он вышел за пределы кнопок, //переводим его на последнюю кнопку. m.focus-- if m.focus < 0 { m.focus = len(m.buttons) - 1 } - case "right", "tab": - //Обработчик нажатия клавиш right и tab нужен для перевода фокуса на следующую кнопку. + case key.Matches(msg, types.MainKeys.Down) || key.Matches(msg, types.MainKeys.Right): + //Обработчик нажатия клавиш перехода на следующую кнопку. //Увеличиваем фокус, если фокус стал равен количество кнопок, значит он вышел за их пределы, //переводим его на первую позицию, то есть на 0 m.focus++ if m.focus >= len(m.buttons) { m.focus = 0 } - case "enter": - //Обработчик клавиши enter нужен для подтверждения кнопки. + case key.Matches(msg, types.MainKeys.Confirm): + //Обработчик клавиши подтверждения выбранной кнопки. //Если у модального окна есть кнопки. //Получаем значение нажатой кнопки, а после если у модального окна прописана функция, //то передаем значение кнопки в функцию модального окна. diff --git a/types/types.go b/types/types.go index 61ad43c..bb995b9 100644 --- a/types/types.go +++ b/types/types.go @@ -1,5 +1,7 @@ package types +import "github.com/charmbracelet/bubbles/key" + // PackageApplication структура описывающая настраиваемое приложение type PackageApplication struct { Name string `yaml:"name"` //Имя приложения @@ -78,3 +80,33 @@ type SectionAction struct { // BoolValidateFunc функция, которая возвращает ошибку, если данные checkbox неверны type BoolValidateFunc func(bool) error + +// MainKeyMap структура главных клавиш +type MainKeyMap struct { + Up key.Binding //Вверх, прошлый элемент + Down key.Binding //Вниз, следующий элемент + Left key.Binding //Влево + Right key.Binding //Вправо + Top key.Binding //На самый верх + Bottom key.Binding //В самый низ + Confirm key.Binding //Клавиша подтверждения + Help key.Binding //Отображение помощи + Log key.Binding //Отображение лого + Close key.Binding //Закрыть + Exit key.Binding //Выйти +} + +// MainKeys используемые клавиши +var MainKeys = MainKeyMap{ + Up: key.NewBinding(key.WithKeys("up", "shift+tab"), key.WithHelp("up", "move up")), + Down: key.NewBinding(key.WithKeys("down", "tab"), key.WithHelp("down", "move down")), + Left: key.NewBinding(key.WithKeys("left"), key.WithHelp("left", "move left")), + Right: key.NewBinding(key.WithKeys("right"), key.WithHelp("right", "move right")), + Top: key.NewBinding(key.WithKeys("home"), key.WithHelp("top", "move top")), + Bottom: key.NewBinding(key.WithKeys("end"), key.WithHelp("bottom", "move bottom")), + Confirm: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm")), + Help: key.NewBinding(key.WithKeys("?", "ctrl+h"), key.WithHelp("?/ctrl+h", "help")), + Log: key.NewBinding(key.WithKeys("ctrl+l"), key.WithHelp("ctrl+l", "show/hide log window")), + Close: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "close")), + Exit: key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "quit")), +}