Skip to content
This repository has been archived by the owner on Feb 3, 2023. It is now read-only.

Commit

Permalink
add view widget interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ingbyr committed Sep 8, 2022
1 parent 62b657e commit ab4ce51
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 67 deletions.
97 changes: 33 additions & 64 deletions tui/node_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,51 @@ package tui
import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"gohost/tui/styles"
"gohost/tui/view"
"gohost/tui/widget"
"strings"
)

var _ view.View = (*NodeView)(nil)

type NodeView struct {
model *Model
model *Model
*view.BaseView
preFocusIdx int
focusIdx int
inputs []textinput.Model
nodeTypes list.Model
nodeTypes *widget.List
}

func NewNodeView(model *Model) *NodeView {
// Text inputs
nodeNameTextInput := textinput.New()
nodeNameTextInput.Prompt = "Name: "
nodeNameTextInput := widget.NewTextInput()
nodeNameTextInput.Prompt = "ID: "
nodeNameTextInput.Focus()
nodeNameTextInput.PromptStyle = styles.FocusedModel
nodeNameTextInput.TextStyle = styles.FocusedModel

descTextInput := textinput.New()
descTextInput := widget.NewTextInput()
descTextInput.Prompt = "Description: "

urlTextInput := widget.NewTextInput()
urlTextInput.Prompt = "Url: "

// Node type choices
nodeTypes := list.New([]list.Item{GroupNode, LocalHost, RemoteHost}, list.NewDefaultDelegate(), 20, 20)
//nodeTypes := list.New([]list.Item{GroupNode, LocalHost, RemoteHost}, list.NewDefaultDelegate(), 20, 20)
nodeTypes := widget.NewList([]list.Item{GroupNode, LocalHost, RemoteHost}, list.NewDefaultDelegate(), 20, 20)

view := &NodeView{
nodeView := &NodeView{
model: model,
BaseView: view.New(),
preFocusIdx: 0,
focusIdx: 0,
inputs: []textinput.Model{nodeNameTextInput, descTextInput},
nodeTypes: nodeTypes,
}
nodeView.AddWidget(nodeNameTextInput)
nodeView.AddWidget(descTextInput)
nodeView.AddWidget(urlTextInput)
nodeView.AddWidget(nodeTypes)

return view
return nodeView
}

func (v *NodeView) Init() tea.Cmd {
Expand All @@ -58,68 +66,29 @@ func (v *NodeView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch {
case key.Matches(m, keys.Enter, keys.Up, keys.Down):
if key.Matches(m, keys.Enter, keys.Down) {
cmds = append(cmds, v.setFocusInput(v.idxAfterFocusInput()))
cmds = append(cmds, v.FocusNextWidget()...)
} else {
cmds = append(cmds, v.setFocusInput(v.idxBeforeFocusInput()))
cmds = append(cmds, v.FocusPreWidget()...)
}
}
} else {
return nil, tea.Batch(cmds...)
}
}
for i := range v.inputs {
v.inputs[i], cmd = v.inputs[i].Update(msg)
cmds = append(cmds, cmd)
}
v.nodeTypes, cmd = v.nodeTypes.Update(msg)

_, cmd = v.BaseView.Update(msg)
cmds = append(cmds, cmd)

//v.nodeTypes, cmd = v.nodeTypes.Update(msg)
//cmds = append(cmds, cmd)

return v, tea.Batch(cmds...)
}

func (v *NodeView) View() string {
var b strings.Builder
for i := range v.inputs {
b.WriteString(v.inputs[i].View())
if i < len(v.inputs)-1 {
b.WriteString(cfg.LineBreak)
}
}
b.WriteString("\n")
b.WriteString(v.nodeTypes.View())
b.WriteString(v.BaseView.View())
//b.WriteString("\n")
//b.WriteString(v.nodeTypes.View())
return b.String()
}

func (v *NodeView) idxAfterFocusInput() int {
id := v.focusIdx + 1
if id >= len(v.inputs) {
id = 0
}
return id
}

func (v *NodeView) idxBeforeFocusInput() int {
id := v.focusIdx - 1
if id < 0 {
id = len(v.inputs) - 1
}
return id
}

func (v *NodeView) setFocusInput(idx int) tea.Cmd {
v.preFocusIdx = v.focusIdx
v.focusIdx = idx

preInput := v.inputs[v.preFocusIdx]
preInput.TextStyle = styles.None
preInput.PromptStyle = styles.None
preInput.Blur()
v.inputs[v.preFocusIdx] = preInput

input := v.inputs[v.focusIdx]
input.TextStyle = styles.FocusedModel
input.PromptStyle = styles.FocusedModel
cmd := input.Focus()
v.inputs[v.focusIdx] = input

return cmd
}
6 changes: 3 additions & 3 deletions tui/styles/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/charmbracelet/lipgloss"

var (
DefaultView = lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).BorderStyle(lipgloss.HiddenBorder())
FocusedView = lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("69"))
FocusedModel = lipgloss.NewStyle().Foreground(lipgloss.Color("69"))
None = lipgloss.NewStyle()
FocusedView = lipgloss.NewStyle().PaddingLeft(1).PaddingRight(1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("69"))
FocusedWidget = lipgloss.NewStyle().Foreground(lipgloss.Color("69"))
None = lipgloss.NewStyle()
)
87 changes: 87 additions & 0 deletions tui/view/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package view

import (
tea "github.com/charmbracelet/bubbletea"
"gohost/tui/widget"
"strings"
)

var _ View = (*BaseView)(nil)

func New() *BaseView {
return &BaseView{
widgets: make([]widget.Widget, 0),
preFocus: 0,
focus: 0,
}
}

type BaseView struct {
widgets []widget.Widget
preFocus int
focus int
}

func (v *BaseView) Init() tea.Cmd {
panic("implement me")
}

func (v *BaseView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
_, cmd := v.widgets[v.focus].Update(msg)
return v, cmd
}

func (v *BaseView) View() string {
var b strings.Builder
for i := 0; i < len(v.widgets); i++ {
b.WriteString(v.widgets[i].View())
b.WriteString("\n")
}
return b.String()
}

func (v *BaseView) AddWidget(widget widget.Widget) {
if widget == nil {
return
}
v.widgets = append(v.widgets, widget)
}

func (v *BaseView) FocusNextWidget() []tea.Cmd {
return v.setFocusWidget(v.idxAfterFocusWidget())
}

func (v *BaseView) FocusPreWidget() []tea.Cmd {
return v.setFocusWidget(v.idxBeforeFocusWidget())
}

func (v *BaseView) idxAfterFocusWidget() int {
if v.widgets[v.focus].HasNext() {
return v.focus
}
idx := v.focus + 1
if idx >= len(v.widgets) {
idx = 0
}
return idx
}

func (v *BaseView) idxBeforeFocusWidget() int {
if v.widgets[v.focus].HasPre() {
return v.focus
}
idx := v.focus - 1
if idx < 0 {
idx = len(v.widgets) - 1
}
return idx
}

func (v *BaseView) setFocusWidget(idx int) []tea.Cmd {
v.preFocus = v.focus
v.focus = idx
return []tea.Cmd{
v.widgets[v.preFocus].Unfocus(),
v.widgets[v.focus].Focus(),
}
}
13 changes: 13 additions & 0 deletions tui/view/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package view

import (
tea "github.com/charmbracelet/bubbletea"
"gohost/tui/widget"
)

type View interface {
tea.Model
AddWidget(widget widget.Widget)
FocusNextWidget() []tea.Cmd
FocusPreWidget() []tea.Cmd
}
44 changes: 44 additions & 0 deletions tui/widget/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package widget

import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
)

var _ Widget = (*List)(nil)

func NewList(items []list.Item, delegate list.ItemDelegate, width, height int) *List {
return &List{
Model: list.New(items, delegate, width, height),
}
}

type List struct {
list.Model
}

func (l *List) Init() tea.Cmd {
return nil
}

func (l *List) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
l.Model, cmd = l.Model.Update(msg)
return l, cmd
}

func (l *List) Focus() tea.Cmd {
return nil
}

func (l *List) Unfocus() tea.Cmd {
return nil
}

func (l *List) HasNext() bool {
return l.Model.Index() != len(l.Model.Items())-1
}

func (l *List) HasPre() bool {
return l.Model.Index() != 0
}
52 changes: 52 additions & 0 deletions tui/widget/textinput.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package widget

import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"gohost/tui/styles"
)

var _ Widget = (*TextInput)(nil)

func NewTextInput() *TextInput {
t := &TextInput{
Model: textinput.New(),
}
t.Unfocus()
return t
}

type TextInput struct {
textinput.Model
}

func (t *TextInput) Init() tea.Cmd {
return nil
}

func (t *TextInput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
t.Model, cmd = t.Model.Update(msg)
return t, cmd
}

func (t *TextInput) Focus() tea.Cmd {
t.TextStyle = styles.FocusedWidget
t.PromptStyle = styles.FocusedWidget
return t.Model.Focus()
}

func (t *TextInput) Unfocus() tea.Cmd {
t.TextStyle = styles.None
t.PromptStyle = styles.None
t.Model.Blur()
return nil
}

func (t *TextInput) HasNext() bool {
return false
}

func (t *TextInput) HasPre() bool {
return false
}
11 changes: 11 additions & 0 deletions tui/widget/widget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package widget

import tea "github.com/charmbracelet/bubbletea"

type Widget interface {
tea.Model
Focus() tea.Cmd
Unfocus() tea.Cmd
HasNext() bool
HasPre() bool
}

0 comments on commit ab4ce51

Please sign in to comment.