working on implementing viewport model for bubbletea

This commit is contained in:
kayos 2021-05-26 14:19:49 -07:00
parent 957a606aae
commit fb803dc68c

163
tui.go

@ -9,6 +9,7 @@ import (
term "github.com/muesli/termenv"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"io"
"os"
"protomolecule/src/dust"
"protomolecule/src/eros"
@ -20,6 +21,18 @@ import (
var _ScanMgr *scanStuff.Meta
const (
// optional, only works when using the tea.EnterAltScreen;
// which makes the tui utilize the full terminal.
//
// from what I understand this will update the screen more often
// this should account for window resizes properly when combined with rendering all "cmd"s during the "update"
fastrender = true
headerHeight = 3
footerHeight = 3
)
type model struct {
choices []string // items on the device list
cursor int // which device item our cursor is pointing at
@ -27,10 +40,6 @@ type model struct {
spinner spinner.Model
}
func init() {
}
func getSpinner() spinner.Model {
s := spinner.NewModel()
s.Spinner = spinner.Dot
@ -49,8 +58,12 @@ var initialModel = model{
cursor: 1,
}
var reader *PipeReader
var writer *PipeWriter
func init() {
term.ClearScreen()
reader, writer = io.Pipe()
// print banner for style points
// dust.Splash()
@ -94,7 +107,7 @@ func init() {
// define pretty printer
consoleWriter := zerolog.ConsoleWriter{
Out: os.Stderr,
Out: writer,
}
// initialize simultaneous pretty printing and json logging
@ -111,24 +124,12 @@ func init() {
eros.Awaken()
}
// this tomfoolery was taken from the example for viewports
// i think the reasoning is aligned with the dynamic initialization that comes with this type of model
//
// tldr; initialization happens repeatedly in the update function
func (m model) Init() tea.Cmd {
return tea.Batch(
func() tea.Msg {
var scanID int
var scan *scanStuff.Scan
scanID = _ScanMgr.NewScan()
scan = _ScanMgr.Scans[scanID]
//time.Sleep(30 * time.Millisecond)
dust.Must("Scan", scan.Start())
return nil
},
listenForScanResults,
spinner.Tick,
)
return nil
}
func getFormattedRow(dev projVars.DiscoveredDevice) string {
@ -147,35 +148,45 @@ func getFormattedRow(dev projVars.DiscoveredDevice) string {
}(dev.ScanResult.RSSI))
}
// Update is processing "msg"s (messages) which appear to be bubbletea's utilization of channels for IPC
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)
switch msg := msg.(type) {
case projVars.DiscoveredDevice:
for k, v := range m.choices {
if strings.HasPrefix(v, msg.ScanResult.Address.String()) {
m.choices[k] = getFormattedRow(msg)
case tea.WindowSizeMsg:
verticalMargins := headerHeight + footerHeight
return m, listenForScanResults
// getting the size of the window happens asynchronously so
// we wait to receive them (ready) to determine the viewport dimensions
if !m.ready {
m.viewport = viewport.Model{
Width: msg.Width,
Height: msg.Height - verticalMargins,
}
m.viewport.YPosition = headerHeight
m.viewport.HighPerformanceRendering = fastrender
// so it looks like this is fetching data from our io.Pipe (io.reader) for every update
// i defined said pipe on line 65
m.viewport.SetContent(m.content)
m.ready = true
} else {
// now that we've figured out window dimensions we define viewport dimensions
m.viewport.Width = msg.Width
m.viewport.Height = msg.Height - verticalMargins
}
/* m.choices = append(m.choices[func(lLength int) int {
if lLength >= 10 {
return 1
} else {
return 0
}
}(len(m.choices)):],
fmt.Sprintf("address: %s rssi: %d", msg.ScanResult.Address.String(), msg.ScanResult.RSSI)) */
m.choices = append(m.choices, getFormattedRow(msg))
return m, listenForScanResults
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
if fastrender {
// see explanation on line 29
cmds = append(cmds, viewport.Sync(m.viewport))
}
// Is it a key press?
case tea.KeyMsg:
@ -209,14 +220,68 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.selected[m.cursor] = struct{}{}
}
}
// spin that spinner
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
// **************************************************
// actual protomolecule IPC for bubbletea starts here
// **************************************************
case projVars.DiscoveredDevice:
for k, v := range m.choices {
if strings.HasPrefix(v, msg.ScanResult.Address.String()) {
m.choices[k] = getFormattedRow(msg)
return m, listenForScanResults
}
}
/* m.choices = append(m.choices[func(lLength int) int {
if lLength >= 10 {
return 1
} else {
return 0
}
}(len(m.choices)):],
fmt.Sprintf("address: %s rssi: %d", msg.ScanResult.Address.String(), msg.ScanResult.RSSI)) */
m.choices = append(m.choices, getFormattedRow(msg))
return m, listenForScanResults
}
// Return the updated model to the Bubble Tea runtime for processing.
// Note that we're not returning a command.
return m, nil
return tea.Batch(
func() tea.Msg {
var scanID int
var scan *scanStuff.Scan
scanID = _ScanMgr.NewScan()
scan = _ScanMgr.Scans[scanID]
//time.Sleep(30 * time.Millisecond)
dust.Must("Scan", scan.Start())
return nil
},
listenForScanResults,
spinner.Tick,
)
}
func (m model) View() string {
if !m.ready {
return "\n Loading ProtoMolecule..."
}
// The header
s := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FAFAFA")).Background(lipgloss.Color("#7D56F4")).PaddingTop(0).PaddingLeft(0).Width(78).Render("protomolecule"+strings.Repeat(" ", 78-24)+"[q] - exit") + "\n\n"
@ -268,7 +333,7 @@ func listenForScanResults() tea.Msg {
//time.Sleep( 1 * time.Second)
return dev
} else {
time.Sleep(1 * time.Second)
time.Sleep(250 * time.MilliSecond)
}
}
}