Bubble-Tea-TUI-组件使用示例

Github 地址

这些预定义的组建都定义了 Model, 也就表示不同的状态. 这些组件也都定义了 Init() Cmd, Update(Msg) (Model, Cmd), View() string 函数供调用.

一般除 Init() tea.Cmd, Update(tea.Msg) (tea.Model, tea.Cmd)View() string 三个函数外, 还要定义一个 initialModel() 函数来初始化一个 model.

Spinner

需导入:

1
2
3
4
5
import (
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

三个包以使用.

spinner 包中预定义了一些 Spinner 可以使用, 如 spinner.Dot, spinner.Line 等.

spinner.Model 类型包含两个成员, Spinner 为显示的字符 (Spinner 类型), Style 为样式 (用 lipgloss 定义):

spinner.New() 创建一个 Spinner 类型的变量.

初始化一个 status model 如:

1
2
3
4
5
6
func initialModel() model {
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
return model{spinner: s}
}

Text Input

需导入:

1
2
3
4
import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)

创建 Model 时, 使用定义在 textinput 包中的 Model 作为成员:

1
2
3
4
type model struct {
textInput textinput.Model
err error
}

textinput.Model 的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
type Model struct {
Err error

// General settings.
Prompt string
Placeholder string
EchoMode EchoMode
EchoCharacter rune
Cursor cursor.Model

// Deprecated: use [cursor.BlinkSpeed] instead.
BlinkSpeed time.Duration

// Styles. These will be applied as inline styles.
//
// For an introduction to styling with Lip Gloss see:
// https://github.com/charmbracelet/lipgloss
PromptStyle lipgloss.Style
TextStyle lipgloss.Style
PlaceholderStyle lipgloss.Style
CompletionStyle lipgloss.Style

// Deprecated: use Cursor.Style instead.
CursorStyle lipgloss.Style

// CharLimit is the maximum amount of characters this input element will
// accept. If 0 or less, there's no limit.
CharLimit int

// Width is the maximum number of characters that can be displayed at once.
// It essentially treats the text field like a horizontally scrolling
// viewport. If 0 or less this setting is ignored.
Width int

// KeyMap encodes the keybindings recognized by the widget.
KeyMap KeyMap

// Validate is a function that checks whether or not the text within the
// input is valid. If it is not valid, the `Err` field will be set to the
// error returned by the function. If the function is not defined, all
// input is considered valid.
Validate ValidateFunc

// Should the input suggest to complete
ShowSuggestions bool
// contains filtered or unexported fields
}

设置 placeholder

修改其 Placeholder 成员:

1
2
ti := textinput.New()
ti.Placeholder = "this is a placeholder"

设置输入框宽度

修改其 Width 成员:

1
2
ti := textinput.New()
ti.Width = 20

Init(), Update(), View() 三个基本函数的使用

如果这里的 model 定义为:

1
2
3
4
type model struct {
textInput textinput.Model
err error
}

Init() 函数中, 需返回 textinput.Blink:

1
2
3
func (m model) Init() tea.Cmd {
return textinput.Blink
}

Update() 函数中, 需有:

1
2
m.textInput, cmd = m.textInput.Update(msg)
return m, cmd

View() 函数中, 需调用如:

1
2
3
4
5
6
7
func (m model) View() string {
return fmt.Sprintf(
"What’s your favorite Pokémon?\n\n%s\n\n%s",
m.textInput.View(),
"(esc to quit)",
) + "\n"
}

textinput.Model 相关的方法

textinput.New() 返回一个 textinput.Model 类型值.

让 model 接受输入

Focus() 方法:

1
2
ti := textinput.New()
ti.Focus()

直接设置输入值

func (m *Model) SetValue(s string)

获取输入值

Value() 方法:

1
v := m.textinput.Value()

重置输入框中的值

Reset() 方法:

1
v.textinput.Reset()

取消 Focus 的状态

func (m *Model) Blur() 方法:

1
2
3
ti := textinput.New()
ti.Focus()
ti.Blur()

获取 Focus 的状态

Focused() bool 方法:

1
isFocused := m.textinput.Focused()

获取 cursor 位置以及设置 cursor 位置

用:

1
func (m model) Position() int

获取 cursor 位置.

用:

1
func (m *Model) SetCursor(pos int)

设置光标位置.

用:

1
func (m *Model) CursorEnd()

移动到输入文本的最后.

用:

1
func (m *Model) CursorStart()

移动到输入文本的开始.

设置提示 suggestion

可以配合 autocompletion 使用.

func (m *Model) SetSuggections(suggestions []string) 来设置.

func (m *Model) AvailableSuggestion() []string 来显示有哪些可用的 suggestion.

func (m *Model) CurrentSuggestion() string 获取当前的 suggestion.

若要显示 suggestion, 需要:

1
2
ti := textinput.New()
ti.ShowSuggestions = true

移除默认 KeyMap

1
2
ti := textinput.New()
ti.KeyMap = textinput.KeyMap{}

Text Area

type Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
type Model struct {
Err error

// Prompt is printed at the beginning of each line.
//
// When changing the value of Prompt after the model has been
// initialized, ensure that SetWidth() gets called afterwards.
//
// See also SetPromptFunc().
Prompt string

// Placeholder is the text displayed when the user
// hasn't entered anything yet.
Placeholder string

// ShowLineNumbers, if enabled, causes line numbers to be printed
// after the prompt.
ShowLineNumbers bool

// EndOfBufferCharacter is displayed at the end of the input.
EndOfBufferCharacter rune

// KeyMap encodes the keybindings recognized by the widget.
KeyMap KeyMap

// Styling. FocusedStyle and BlurredStyle are used to style the textarea in
// focused and blurred states.
FocusedStyle Style
BlurredStyle Style

// Cursor is the text area cursor.
Cursor cursor.Model

// CharLimit is the maximum number of characters this input element will
// accept. If 0 or less, there's no limit.
CharLimit int

// MaxHeight is the maximum height of the text area in rows. If 0 or less,
// there's no limit.
MaxHeight int

// MaxWidth is the maximum width of the text area in columns. If 0 or less,
// there's no limit.
MaxWidth int
// contains filtered or unexported fields
}

type KeyMap 有默认的 KeyMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type KeyMap struct {
CharacterBackward key.Binding
CharacterForward key.Binding
DeleteAfterCursor key.Binding
DeleteBeforeCursor key.Binding
DeleteCharacterBackward key.Binding
DeleteCharacterForward key.Binding
DeleteWordBackward key.Binding
DeleteWordForward key.Binding
InsertNewline key.Binding
LineEnd key.Binding
LineNext key.Binding
LinePrevious key.Binding
LineStart key.Binding
Paste key.Binding
WordBackward key.Binding
WordForward key.Binding
InputBegin key.Binding
InputEnd key.Binding

UppercaseWordForward key.Binding
LowercaseWordForward key.Binding
CapitalizeWordForward key.Binding

TransposeCharacterBackward key.Binding
}

type LineInfo 每一行文本的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type LineInfo struct {
// Width is the number of columns in the line.
Width int
// CharWidth is the number of characters in the line to account for
// double-width runes.
CharWidth int
// Height is the number of rows in the line.
Height int
// StartColumn is the index of the first column of the line.
StartColumn int
// ColumnOffset is the number of columns that the cursor is offset from the
// start of the line.
ColumnOffset int
// RowOffset is the number of rows that the cursor is offset from the start
// of the line.
RowOffset int
// CharOffset is the number of characters that the cursor is offset
// from the start of the line. This will generally be equivalent to
// ColumnOffset, but will be different there are double-width runes before
// the cursor.
CharOffset int
}
1
func Blink() tea.Msg

Paste 将粘贴板内容复制到 textarea

1
func Paste() tea.Msg

New 创建一个包含默认设置的 textarea 模型

1
func New() Model

Focus 可输入状态

1
func (m *Model) Focus() tea.Cmd

Blur 取消 focus 的状态

此时无法向 textarea 中输入内容:

1
func (m *Model) Blur()

Focused 检查是否为 focus 状态

1
func (m Model) Focused() bool

CursorDown 光标下移一行

1
func (m *Model) CursorDown()

CursorUp 光标向上移动一行

1
func (m *Model) CursorUp()

CursorEnd 光标移动至 textarea 输入的末尾

1
func (m *Model) CursorEnd()

CursorStart 光标移动至 textarea 输入的开始

1
func (m *Model) CursorStart()

SetHeight 设置 textarea 的高度

1
func (m *Model) SetHeight(h int)

Height 返回 textarea 的当前高度

注意不是文本行数. 相当于是 viewport 的高度:

1
func (m Model) Height() int

Length 返回 textarea 中的字符数

1
func (m *Model) Length() int

InsertRune 在当前光标位置插入一个 rune

1
func (m *Model) InsertRune(r rune)

InsertString 在当前光标位置插入一个 string

1
func (m *Model) InsertString(s string)

Line 获取当前行号

注意从 0 开始:

1
func (m Model) Line() int

LineCount 获取总行数

1
func (m *Model) LineCount() int

LineInfo 获取当前行的信息

1
func (m Model) LineInfo() LineInfo

Reset 清空输入

1
func (m *Model) Reset()

SetCursor 设置光标的水平位置

1
func (m *Model) SetCursor(col int)

SetPromptFunc 动态生成 Prompt

1
func (m *Model) SetPromptFunc(promptWidth int, fn func(lineIdx int) string)

SetValue 设置 textarea 中的值

1
func (m *Model) SetValue(s string)

Value 获取 textarea 中的值

1
func (m Model) Value() string

SetWidth 设置 textarea 的宽度

其会在设置 Prompt 以及 ShowLineNumbers 之后调用, 会自动调整大小:

1
func (m *Model) SetWidth(w int)

Width 获取 textarea 的宽度

1
func (m Model) Width() int

Table

Progress

Paginator

Godoc paginator 文档

type Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Model struct {
// Type configures how the pagination is rendered (Arabic, Dots).
Type Type
// Page is the current page number.
Page int
// PerPage is the number of items per page.
PerPage int
// TotalPages is the total number of pages.
TotalPages int
// ActiveDot is used to mark the current page under the Dots display type.
ActiveDot string
// InactiveDot is used to mark inactive pages under the Dots display type.
InactiveDot string
// ArabicFormat is the printf-style format to use for the Arabic display type.
ArabicFormat string

// KeyMap encodes the keybindings recognized by the widget.
KeyMap KeyMap
}

type Type

1
type Type int

定义有:

1
2
3
4
const (
Arabic Type = iota
Dots
)

New 创建一个 paginator model

1
func New() model

GetSliceBounds 获取一个 slice 的开始和结束索引

1
func (m *Model) GetSliceBounds(length int) (start int, end int)

如:

1
2
3
bunchOfStuff := []stuff{...}
start, end := model.GetSliceBounds(len(bunchOfStuff))
sliceToRender := bunchOfStuff[start:end]

ItemsOnPage 获取当前页面的 items 个数

1
func (m Model) ItemsOnPage(totalItems int) int

NextPage 翻到下一页

不会超过最后一页.

1
func (m *Model) NextPage()

PrevPage 翻到前一页

不会超过第一页.

1
func (m *Model) PrevPage()

OnFirstPage 检测是否位于第一页

1
func (m Model) OnFirstPage() bool

OnLastPage 检测是否位于最后一页

1
func (m Model) OnLastPage() bool

SetTotalPages 通过总 items 数确定 page 数

1
func (m *Model) SetTotalPages(items int) int

Viewport

Godoc viewport 文档

type Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type Model struct {
Width int
Height int
KeyMap KeyMap

// Whether or not to respond to the mouse. The mouse must be enabled in
// Bubble Tea for this to work. For details, see the Bubble Tea docs.
MouseWheelEnabled bool

// The number of lines the mouse wheel will scroll. By default, this is 3.
MouseWheelDelta int

// YOffset is the vertical scroll position.
YOffset int

// YPosition is the position of the viewport in relation to the terminal
// window. It's used in high performance rendering only.
YPosition int

// Style applies a lipgloss style to the viewport. Realistically, it's most
// useful for setting borders, margins and padding.
Style lipgloss.Style

// HighPerformanceRendering bypasses the normal Bubble Tea renderer to
// provide higher performance rendering. Most of the time the normal Bubble
// Tea rendering methods will suffice, but if you're passing content with
// a lot of ANSI escape codes you may see improved rendering in certain
// terminals with this enabled.
//
// This should only be used in program occupying the entire terminal,
// which is usually via the alternate screen buffer.
HighPerformanceRendering bool
// contains filtered or unexported fields
}

type KeyMap

有预定义的 keybinding:

1
2
3
4
5
6
7
8
type KeyMap struct {
PageDown key.Binding
PageUp key.Binding
HalfPageUp key.Binding
HalfPageDown key.Binding
Down key.Binding
Up key.Binding
}

ViewDown viewport 向上移动(文本向下移动)

1
func ViewDown(m Model, lines []string) tea.Cmd

如:

1
2
lines := model.ViewDown(1)
cmd := ViewDown(m, lines)

ViewUp viewport 向下移动(文本向上移动)

1
func ViewUp(m Model, lines []string) tea.Cmd

AtBotton 判断 viewport 是否位于底部

1
func (m Model) AtBotton() bool

AtTop 判断 viewport 是否位于顶部

1
func (m Model) AtTop() bool

GotoBottom 将 viewport 移动到底部

1
func (m *Model) GotoBottom() (lines []string)

GotoTop 将 viewport 移动到顶部

1
func (m *Model) GotoTop() (lines []string)

HalfViewDown view 向下移动半个 viewport 高度

1
func (m *Model) HalfViewDown() (lines []string)

HalfViewUp view 向上移动半个 viewport 高度

1
func (m *Model) HalfViewUp() (lines []string)

LineDown view 向下移动指定行数

1
func (m *Model) LineDown(n int)  (lines []string)

LineUp view 向上移动指定行数

1
func (m *Model) LineUp(n int) (lines []string)

PastBottom 判断 viewport 是否滚动超过最后一行

1
func (m Model) PastBottom() bool

ScrollPercent 百分比形式返回滚动的长度

1
func (m Model) ScrollPercent() float64

SetContent 设置 pager 中的文本内容

1
func (m *Model) SetContent(s string)

SetYOffset 设置 Y Offset

1
func (m *Model) SetYOffset(n int)

TotalLineCount 返回一个 viewport 内的文本行数

1
func (m model) TotalLineCount() int

ViewDown 移动到下一页

1
func (m *Model) ViewDown() []string

ViewUp 移动到上一页

1
func (m *Model) ViewUp() []string

VisibleLineCount 返回当前一个 viewport 可见的文本行数

1
func (m Model) VisibleLineCount() int

List

File Picker

Timer

Stopwatch

Help

type KeyMap

1
2
3
4
5
6
7
8
9
10
11
12
type KeyMap interface {

// ShortHelp returns a slice of bindings to be displayed in the short
// version of the help. The help bubble will render help in the order in
// which the help items are returned here.
ShortHelp() []key.Binding

// FullHelp returns an extended group of help items, grouped by columns.
// The help bubble will render the help in the order in which the help
// items are returned here.
FullHelp() [][]key.Binding
}

一个接口, 后续的函数中有的需要用实现了这个接口的类型作为参数. (如 func (m Model) View(k KeyMap) string)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// keyMap defines a set of keybindings. To work for help it must satisfy
// key.Map. It could also very easily be a map[string]key.Binding.
type keyMap struct {
Up key.Binding
Down key.Binding
Left key.Binding
Right key.Binding
Help key.Binding
Quit key.Binding
}

// ShortHelp returns keybindings to be shown in the mini help view. It's part
// of the key.Map interface.
func (k keyMap) ShortHelp() []key.Binding {
return []key.Binding{k.Help, k.Quit}
}

// FullHelp returns keybindings for the expanded help view. It's part of the
// key.Map interface.
func (k keyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{
{k.Up, k.Down, k.Left, k.Right}, // first column
{k.Help, k.Quit}, // second column
}
}

type Model

1
2
3
4
5
6
7
8
9
10
11
12
13
type Model struct {
Width int
ShowAll bool // if true, render the "full" help menu

ShortSeparator string
FullSeparator string

// The symbol we use in the short help when help items have been truncated
// due to width. Periods of ellipsis by default.
Ellipsis string

Styles Styles
}

主要是设置样式.

Key

godoc 文档

包含的类型

type Binding

似乎是一个空结构体:

1
2
3
type Binding struct {
// contains filtered or unexported fields
}

这种空结构体相当于只用于标记类型, 而不占用内存空间.

type BindingOpt

表示接受 *Binding 为参数的函数类型:

1
type BindingOpt func(*Binding)

type Help

包含一个键值以及一个描述:

1
2
3
4
type Help struct {
Key string
Desc string
}

创建一个 Binding 类型的值

func NewBinding(opts ...BindingOpt) Binding 函数.

返回 BindingOpt 类型的函数

设置为 Disabled:

1
func WithDisabled() BindingOpt

设置 key 的 help 信息:

1
func WithHelp(key, desc string) BindingOpt

设置 key:

1
func WithKey(keys ...string) BindingOpt

设置 Binding 的 opts

设置键值

1
func (b *Binding) SetKeys(keys ...string)

移除键值和 help 信息

似乎是移除所有的:

1
func (b *Binding) unbind()

设置为 enable

1
func (b *Binding) SetEnabled(v bool)

设置 help 信息

1
func (b *Binding) SetHelp(key, desc string)

Binding 相关方法

检验 binding 是否启用

1
func (b Binding) Enabled() bool

未启用的 binding 不会显示在 Help 中.

返回 Help information

1
func (b Binding) Help() Help

返回键值

1
func (b Binding) Keys() []string

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type KeyMap struct {
Up key.Binding
Down key.Binding
}

var DefaultKeyMap = KeyMap{
Up: key.NewBinding(
key.WithKeys("k", "up"), // actual keybindings
key.WithHelp("↑/k", "move up"), // corresponding help text
),
Down: key.NewBinding(
key.WithKeys("j", "down"),
key.WithHelp("↓/j", "move down"),
),
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, DefaultKeyMap.Up):
// The user pressed up
case key.Matches(msg, DefaultKeyMap.Down):
// The user pressed down
}
}

// ...
}

key.Matches() 函数的原型为:

1
func Matches(k tea.KeyMsg, b ...Binding) bool

Bubble-Tea-TUI-组件使用示例
http://example.com/2024/01/29/Bubble-Tea-TUI-组件使用示例/
作者
Jie
发布于
2024年1月29日
许可协议