All checks were successful
ci / Build (push) Successful in 3m40s
- Fixed the 'M' bind to mark all/none items, rather than toggle - Fixed panic which was raised when reqesting @item with an index beyond the length of resultset - Added changing the table when calling @table with a table or string name
233 lines
8.4 KiB
Go
233 lines
8.4 KiB
Go
package ui
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/charmbracelet/bubbles/key"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
bus "github.com/lmika/events"
|
|
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
|
|
"lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/colselector"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dialogprompt"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamoitemedit"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamotableview"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/layout"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/relselector"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/styles"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect"
|
|
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
|
|
)
|
|
|
|
const (
|
|
ViewModeTablePrimary = 0
|
|
ViewModeTableItemEqual = 1
|
|
ViewModeItemPrimary = 2
|
|
ViewModeItemOnly = 3
|
|
ViewModeTableOnly = 4
|
|
|
|
ViewModeCount = 5
|
|
)
|
|
|
|
type Model struct {
|
|
tableReadController *controllers.TableReadController
|
|
tableWriteController *controllers.TableWriteController
|
|
settingsController *controllers.SettingsController
|
|
exportController *controllers.ExportController
|
|
commandController *commandctrl.CommandController
|
|
jobController *controllers.JobsController
|
|
colSelector *colselector.Model
|
|
relSelector *relselector.Model
|
|
itemEdit *dynamoitemedit.Model
|
|
statusAndPrompt *statusandprompt.StatusAndPrompt
|
|
tableSelect *tableselect.Model
|
|
eventBus *bus.Bus
|
|
|
|
mainViewIndex int
|
|
|
|
root tea.Model
|
|
tableView *dynamotableview.Model
|
|
itemView *dynamoitemview.Model
|
|
mainView tea.Model
|
|
keyMap *keybindings.ViewKeyBindings
|
|
keyBindingController *controllers.KeyBindingController
|
|
}
|
|
|
|
func NewModel(
|
|
rc *controllers.TableReadController,
|
|
wc *controllers.TableWriteController,
|
|
columnsController *controllers.ColumnsController,
|
|
exportController *controllers.ExportController,
|
|
settingsController *controllers.SettingsController,
|
|
jobController *controllers.JobsController,
|
|
itemRendererService *itemrenderer.Service,
|
|
cc *commandctrl.CommandController,
|
|
eventBus *bus.Bus,
|
|
keyBindingController *controllers.KeyBindingController,
|
|
pasteboardProvider services.PasteboardProvider,
|
|
defaultKeyMap *keybindings.KeyBindings,
|
|
) Model {
|
|
uiStyles := styles.DefaultStyles
|
|
|
|
dtv := dynamotableview.New(defaultKeyMap.TableView, columnsController, settingsController, eventBus, uiStyles)
|
|
div := dynamoitemview.New(itemRendererService, uiStyles)
|
|
mainView := layout.NewVBox(layout.LastChildFixedAt(14), dtv, div)
|
|
|
|
colSelector := colselector.New(mainView, defaultKeyMap, columnsController)
|
|
relSelector := relselector.New(colSelector)
|
|
itemEdit := dynamoitemedit.NewModel(relSelector)
|
|
statusAndPrompt := statusandprompt.New(itemEdit, pasteboardProvider, "", uiStyles.StatusAndPrompt)
|
|
dialogPrompt := dialogprompt.New(statusAndPrompt)
|
|
tableSelect := tableselect.New(dialogPrompt, uiStyles)
|
|
|
|
root := layout.FullScreen(tableSelect)
|
|
|
|
return Model{
|
|
tableReadController: rc,
|
|
tableWriteController: wc,
|
|
commandController: cc,
|
|
exportController: exportController,
|
|
jobController: jobController,
|
|
itemEdit: itemEdit,
|
|
colSelector: colSelector,
|
|
relSelector: relSelector,
|
|
statusAndPrompt: statusAndPrompt,
|
|
tableSelect: tableSelect,
|
|
root: root,
|
|
tableView: dtv,
|
|
itemView: div,
|
|
mainView: mainView,
|
|
keyMap: defaultKeyMap.View,
|
|
keyBindingController: keyBindingController,
|
|
}
|
|
}
|
|
|
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case controllers.SetTableItemView:
|
|
cmd := m.setMainViewIndex(msg.ViewIndex)
|
|
return m, cmd
|
|
case events.ResultSetUpdated:
|
|
return m, tea.Batch(
|
|
m.tableView.Refresh(),
|
|
events.SetStatus(msg.StatusMessage),
|
|
)
|
|
case tea.KeyMsg:
|
|
// TODO: use modes here
|
|
if !m.statusAndPrompt.InPrompt() && !m.tableSelect.Visible() && !m.colSelector.ColSelectorVisible() && !m.relSelector.SelectorVisible() {
|
|
switch {
|
|
case key.Matches(msg, m.keyMap.Mark):
|
|
if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
|
|
return m, events.SetTeaMessage(m.tableWriteController.ToggleMark(idx))
|
|
}
|
|
case key.Matches(msg, m.keyMap.CopyItemToClipboard):
|
|
if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
|
|
return m, events.SetTeaMessage(m.tableReadController.CopyItemToClipboard(idx))
|
|
}
|
|
case key.Matches(msg, m.keyMap.CopyTableToClipboard):
|
|
return m, events.SetTeaMessage(m.exportController.ExportCSVToClipboard())
|
|
case key.Matches(msg, m.keyMap.Rescan):
|
|
return m, m.tableReadController.Rescan
|
|
case key.Matches(msg, m.keyMap.PromptForQuery):
|
|
return m, m.tableReadController.PromptForQuery
|
|
case key.Matches(msg, m.keyMap.PromptForFilter):
|
|
return m, m.tableReadController.PromptForFilter
|
|
case key.Matches(msg, m.keyMap.FetchNextPage):
|
|
return m, m.tableReadController.NextPage
|
|
case key.Matches(msg, m.keyMap.ViewBack):
|
|
return m, m.tableReadController.ViewBack
|
|
case key.Matches(msg, m.keyMap.ViewForward):
|
|
return m, m.tableReadController.ViewForward
|
|
case key.Matches(msg, m.keyMap.CycleLayoutForward):
|
|
return m, events.SetTeaMessage(controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, 1, ViewModeCount)})
|
|
case key.Matches(msg, m.keyMap.CycleLayoutBackwards):
|
|
return m, events.SetTeaMessage(controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, -1, ViewModeCount)})
|
|
//case "e":
|
|
// m.itemEdit.Visible()
|
|
// return m, nil
|
|
case key.Matches(msg, m.keyMap.ShowColumnOverlay):
|
|
return m, events.SetTeaMessage(controllers.ShowColumnOverlay{})
|
|
//case key.Matches(msg, m.keyMap.ShowRelItemsOverlay):
|
|
// if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
|
|
// return m, events.SetTeaMessage(m.scriptController.LookupRelatedItems(idx))
|
|
// }
|
|
case key.Matches(msg, m.keyMap.PromptForCommand):
|
|
return m, m.commandController.Prompt
|
|
case key.Matches(msg, m.keyMap.PromptForTable):
|
|
return m, events.SetTeaMessage(m.tableReadController.ListTables(false))
|
|
case key.Matches(msg, m.keyMap.CancelRunningJob):
|
|
return m, events.SetTeaMessage(m.jobController.CancelRunningJob(m.promptToQuit))
|
|
case key.Matches(msg, m.keyMap.Quit):
|
|
return m, m.promptToQuit
|
|
default:
|
|
if cmd := m.keyBindingController.LookupCustomBinding(msg.String()); cmd != nil {
|
|
return m, cmd
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var cmd tea.Cmd
|
|
m.root, cmd = m.root.Update(msg)
|
|
return m, cmd
|
|
}
|
|
|
|
func (m Model) Init() tea.Cmd {
|
|
return tea.Batch(
|
|
m.tableReadController.Init,
|
|
m.root.Init(),
|
|
)
|
|
}
|
|
|
|
func (m Model) View() string {
|
|
return m.root.View()
|
|
}
|
|
|
|
func (m *Model) setMainViewIndex(viewIndex int) tea.Cmd {
|
|
log.Printf("setting view index = %v", viewIndex)
|
|
|
|
var newMainView tea.Model
|
|
switch viewIndex {
|
|
case ViewModeTablePrimary:
|
|
newMainView = layout.NewVBox(layout.LastChildFixedAt(14), m.tableView, m.itemView)
|
|
case ViewModeTableItemEqual:
|
|
newMainView = layout.NewVBox(layout.EqualSize(), m.tableView, m.itemView)
|
|
case ViewModeItemPrimary:
|
|
newMainView = layout.NewVBox(layout.FirstChildFixedAt(7), m.tableView, m.itemView)
|
|
case ViewModeItemOnly:
|
|
newMainView = layout.NewZStack(m.itemView, m.tableView)
|
|
case ViewModeTableOnly:
|
|
newMainView = layout.NewZStack(m.tableView, m.tableView)
|
|
default:
|
|
newMainView = m.mainView
|
|
}
|
|
|
|
m.mainViewIndex = viewIndex
|
|
m.mainView = newMainView
|
|
m.itemEdit.SetSubmodel(m.mainView)
|
|
return m.tableView.Refresh()
|
|
}
|
|
|
|
func (m *Model) promptToQuit() tea.Msg {
|
|
return events.Confirm("Quit dynamo-browse? ", func(yes bool) tea.Msg {
|
|
if yes {
|
|
return tea.Quit()
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (m *Model) SelectedItemIndex() int {
|
|
return m.tableView.SelectedItemIndex()
|
|
}
|
|
|
|
func (m *Model) SetSelectedItemIndex(newIdx int) tea.Msg {
|
|
return m.tableView.SetSelectedItemIndex(newIdx)
|
|
}
|