Files
ilovetui/examples/layout/help/main.go
T

137 lines
3.6 KiB
Go

// Command help demonstrates the dynamic help bar: each pane implements
// layout.HelpProvider (or doesn't) and the bottom bar always reflects
// whichever one is currently focused, with no wiring beyond that.
package main
import (
"fmt"
"os"
"charm.land/bubbles/v2/key"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/anotherhadi/ilovetui/layout"
)
// helpfulPane implements layout.HelpProvider with a binding or two of its
// own, on top of the usual Size/Focus/Blur bookkeeping.
type helpfulPane struct {
id string
w, h int
focused bool
bindings []key.Binding
}
func newHelpfulPane(id string, bindings []key.Binding) *helpfulPane {
return &helpfulPane{id: id, bindings: bindings}
}
func (p *helpfulPane) Init() tea.Cmd { return nil }
func (p *helpfulPane) Update(msg tea.Msg) (layout.Pane, tea.Cmd) {
switch msg := msg.(type) {
case layout.SizeMsg:
p.w, p.h = msg.Width, msg.Height
case layout.FocusMsg:
p.focused = true
case layout.BlurMsg:
p.focused = false
}
return p, nil
}
func (p *helpfulPane) View() string {
content := lipgloss.NewStyle().
Width(p.w - 2).Height(p.h - 2).
AlignHorizontal(lipgloss.Center).AlignVertical(lipgloss.Center).
Render(p.id)
return layout.Bordered(p.focused, p.w, p.h, content)
}
// HelpBindings implements layout.HelpProvider.
func (p *helpfulPane) HelpBindings() []key.Binding { return p.bindings }
// silentPane deliberately does NOT implement layout.HelpProvider, to show
// that the help bar just falls back to layout's own controls (ctrl+hjkl,
// ?) instead of disappearing or erroring when it's focused.
type silentPane struct {
w, h int
focused bool
}
func (p *silentPane) Init() tea.Cmd { return nil }
func (p *silentPane) Update(msg tea.Msg) (layout.Pane, tea.Cmd) {
switch msg := msg.(type) {
case layout.SizeMsg:
p.w, p.h = msg.Width, msg.Height
case layout.FocusMsg:
p.focused = true
case layout.BlurMsg:
p.focused = false
}
return p, nil
}
func (p *silentPane) View() string {
content := lipgloss.NewStyle().
Width(p.w - 2).Height(p.h - 2).
AlignHorizontal(lipgloss.Center).AlignVertical(lipgloss.Center).
Render("no help bindings\n(watch the bar below)")
return layout.Bordered(p.focused, p.w, p.h, content)
}
// model is the actual top-level tea.Model: layout itself reserves no quit
// key (that's an app policy, not layout's to make), so the host wraps it
// and handles ctrl+c/q itself, same as any other custom component in this
// repo (see examples/tabs).
type model struct {
layout layout.Model
}
func (m model) Init() tea.Cmd { return m.layout.Init() }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyPressMsg); ok {
switch key.String() {
case "ctrl+c", "q":
return m, tea.Quit
}
}
updated, cmd := m.layout.Update(msg)
m.layout = updated.(layout.Model)
return m, cmd
}
func (m model) View() tea.View {
view := tea.NewView(m.layout.View())
view.AltScreen = true
return view
}
func main() {
writer := newHelpfulPane("writer", []key.Binding{
key.NewBinding(key.WithKeys("ctrl+s"), key.WithHelp("ctrl+s", "save")),
key.NewBinding(key.WithKeys("ctrl+z"), key.WithHelp("ctrl+z", "undo")),
})
browser := newHelpfulPane("browser", []key.Binding{
key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "search")),
})
root := layout.HSplit(0.34,
layout.Leaf("writer", writer),
layout.HSplit(0.5,
layout.Leaf("browser", browser),
layout.Leaf("scratch", &silentPane{}),
),
)
m := model{layout: layout.New(root, layout.AsRoot())}
if _, err := tea.NewProgram(m).Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
}