Revert "Publishing the new `TextArea` primitive"

revert-759-textarea
rivo 2 years ago committed by GitHub
parent 3d42e9b328
commit 0d48f2a536
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,7 +11,6 @@ Among these components are:
- __Input forms__ (include __input/password fields__, __drop-down selections__, __checkboxes__, and __buttons__) - __Input forms__ (include __input/password fields__, __drop-down selections__, __checkboxes__, and __buttons__)
- Navigable multi-color __text views__ - Navigable multi-color __text views__
- Editable multi-line __text areas__
- Sophisticated navigable __table views__ - Sophisticated navigable __table views__
- Flexible __tree views__ - Flexible __tree views__
- Selectable __lists__ - Selectable __lists__

@ -125,7 +125,7 @@ func (b *Box) GetInnerRect() (int, int, int, int) {
// if this primitive is part of a layout (e.g. Flex, Grid) or if it was added // if this primitive is part of a layout (e.g. Flex, Grid) or if it was added
// like this: // like this:
// //
// application.SetRoot(p, true) // application.SetRoot(b, true)
func (b *Box) SetRect(x, y, width, height int) { func (b *Box) SetRect(x, y, width, height int) {
b.x = x b.x = x
b.y = y b.y = y
@ -215,7 +215,7 @@ func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse,
// MouseHandler returns nil. // MouseHandler returns nil.
func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
if action == MouseLeftDown && b.InRect(event.Position()) { if action == MouseLeftClick && b.InRect(event.Position()) {
setFocus(b) setFocus(b)
consumed = true consumed = true
} }
@ -272,7 +272,7 @@ func (b *Box) SetBorderColor(color tcell.Color) *Box {
// SetBorderAttributes sets the border's style attributes. You can combine // SetBorderAttributes sets the border's style attributes. You can combine
// different attributes using bitmask operations: // different attributes using bitmask operations:
// //
// box.SetBorderAttributes(tcell.AttrUnderline | tcell.AttrBold) // box.SetBorderAttributes(tcell.AttrUnderline | tcell.AttrBold)
func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box { func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box {
b.borderStyle = b.borderStyle.Attributes(attr) b.borderStyle = b.borderStyle.Attributes(attr)
return b return b

@ -145,10 +145,8 @@ func (b *Button) MouseHandler() func(action MouseAction, event *tcell.EventMouse
} }
// Process mouse event. // Process mouse event.
if action == MouseLeftDown { if action == MouseLeftClick {
setFocus(b) setFocus(b)
consumed = true
} else if action == MouseLeftClick {
if b.selected != nil { if b.selected != nil {
b.selected() b.selected()
} }

@ -226,17 +226,13 @@ func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMou
} }
// Process mouse event. // Process mouse event.
if y == rectY { if action == MouseLeftClick && y == rectY {
if action == MouseLeftDown { setFocus(c)
setFocus(c) c.checked = !c.checked
consumed = true if c.changed != nil {
} else if action == MouseLeftClick { c.changed(c.checked)
c.checked = !c.checked
if c.changed != nil {
c.changed(c.checked)
}
consumed = true
} }
consumed = true
} }
return return

@ -1 +0,0 @@
![Screenshot](screenshot.png)

@ -1,133 +0,0 @@
// Demo code for the TextArea primitive.
package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
textArea := tview.NewTextArea().
SetPlaceholder("Enter text here...")
textArea.SetTitle("Text Area").SetBorder(true)
helpInfo := tview.NewTextView().
SetText(" Press F1 for help, press Ctrl-C to exit")
position := tview.NewTextView().
SetDynamicColors(true).
SetTextAlign(tview.AlignRight)
pages := tview.NewPages()
updateInfos := func() {
fromRow, fromColumn, toRow, toColumn := textArea.GetCursor()
if fromRow == toRow && fromColumn == toColumn {
position.SetText(fmt.Sprintf("Row: [yellow]%d[white], Column: [yellow]%d ", fromRow, fromColumn))
} else {
position.SetText(fmt.Sprintf("[red]From[white] Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d ", fromRow, fromColumn, toRow, toColumn))
}
}
textArea.SetMovedFunc(updateInfos)
updateInfos()
mainView := tview.NewGrid().
SetRows(0, 1).
AddItem(textArea, 0, 0, 1, 2, 0, 0, true).
AddItem(helpInfo, 1, 0, 1, 1, 0, 0, false).
AddItem(position, 1, 1, 1, 1, 0, 0, false)
help1 := tview.NewTextView().
SetDynamicColors(true).
SetText(`[green]Navigation
[yellow]Left arrow[white]: Move left.
[yellow]Right arrow[white]: Move right.
[yellow]Down arrow[white]: Move down.
[yellow]Up arrow[white]: Move up.
[yellow]Ctrl-A, Home[white]: Move to the beginning of the current line.
[yellow]Ctrl-E, End[white]: Move to the end of the current line.
[yellow]Ctrl-F, page down[white]: Move down by one page.
[yellow]Ctrl-B, page up[white]: Move up by one page.
[yellow]Alt-Up arrow[white]: Scroll the page up.
[yellow]Alt-Down arrow[white]: Scroll the page down.
[yellow]Alt-Left arrow[white]: Scroll the page to the left.
[yellow]Alt-Right arrow[white]: Scroll the page to the right.
[yellow]Alt-B, Ctrl-Left arrow[white]: Move back by one word.
[yellow]Alt-F, Ctrl-Right arrow[white]: Move forward by one word.
[blue]Press Enter for more help, press Escape to return.`)
help2 := tview.NewTextView().
SetDynamicColors(true).
SetText(`[green]Editing[white]
Type to enter text.
[yellow]Ctrl-H, Backspace[white]: Delete the left character.
[yellow]Ctrl-D, Delete[white]: Delete the right character.
[yellow]Ctrl-K[white]: Delete until the end of the line.
[yellow]Ctrl-W[white]: Delete the rest of the word.
[yellow]Ctrl-U[white]: Delete the current line.
[blue]Press Enter for more help, press Escape to return.`)
help3 := tview.NewTextView().
SetDynamicColors(true).
SetText(`[green]Selecting Text[white]
Move while holding Shift or drag the mouse.
Double-click to select a word.
[green]Clipboard
[yellow]Ctrl-Q[white]: Copy.
[yellow]Ctrl-X[white]: Cut.
[yellow]Ctrl-V[white]: Paste.
[green]Undo
[yellow]Ctrl-Z[white]: Undo.
[yellow]Ctrl-Y[white]: Redo.
[blue]Press Enter for more help, press Escape to return.`)
help := tview.NewFrame(help1).
SetBorders(1, 1, 0, 0, 2, 2)
help.SetBorder(true).
SetTitle("Help").
SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEscape {
pages.SwitchToPage("main")
return nil
} else if event.Key() == tcell.KeyEnter {
switch {
case help.GetPrimitive() == help1:
help.SetPrimitive(help2)
case help.GetPrimitive() == help2:
help.SetPrimitive(help3)
case help.GetPrimitive() == help3:
help.SetPrimitive(help1)
}
return nil
}
return event
})
pages.AddAndSwitchToPage("main", mainView, true).
AddPage("help", tview.NewGrid().
SetColumns(0, 64, 0).
SetRows(0, 22, 0).
AddItem(help, 1, 1, 1, 1, 0, 0, true), true, false)
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyF1 {
pages.ShowPage("help") //TODO: Check when clicking outside help window with the mouse. Then clicking help again.
return nil
}
return event
})
if err := app.SetRoot(pages,
true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

159
doc.go

@ -3,78 +3,77 @@ Package tview implements rich widgets for terminal based user interfaces. The
widgets provided with this package are useful for data exploration and data widgets provided with this package are useful for data exploration and data
entry. entry.
# Widgets Widgets
The package implements the following widgets: The package implements the following widgets:
- [TextView]: A scrollable window that display multi-colored text. Text may - TextView: A scrollable window that display multi-colored text. Text may also
also be highlighted. be highlighted.
- [TextArea]: An editable multi-line text area. - Table: A scrollable display of tabular data. Table cells, rows, or columns
- [Table]: A scrollable display of tabular data. Table cells, rows, or columns
may also be highlighted. may also be highlighted.
- [TreeView]: A scrollable display for hierarchical data. Tree nodes can be - TreeView: A scrollable display for hierarchical data. Tree nodes can be
highlighted, collapsed, expanded, and more. highlighted, collapsed, expanded, and more.
- [List]: A navigable text list with optional keyboard shortcuts. - List: A navigable text list with optional keyboard shortcuts.
- [InputField]: One-line input fields to enter text. - InputField: One-line input fields to enter text.
- [DropDown]: Drop-down selection fields. - DropDown: Drop-down selection fields.
- [Checkbox]: Selectable checkbox for boolean values. - Checkbox: Selectable checkbox for boolean values.
- [Button]: Buttons which get activated when the user selects them. - Button: Buttons which get activated when the user selects them.
- Form: Forms composed of input fields, drop down selections, checkboxes, and - Form: Forms composed of input fields, drop down selections, checkboxes, and
buttons. buttons.
- [Modal]: A centered window with a text message and one or more buttons. - Modal: A centered window with a text message and one or more buttons.
- [Grid]: A grid based layout manager. - Grid: A grid based layout manager.
- [Flex]: A Flexbox based layout manager. - Flex: A Flexbox based layout manager.
- [Pages]: A page based layout manager. - Pages: A page based layout manager.
The package also provides Application which is used to poll the event queue and The package also provides Application which is used to poll the event queue and
draw widgets on screen. draw widgets on screen.
# Hello World Hello World
The following is a very basic example showing a box with the title "Hello, The following is a very basic example showing a box with the title "Hello,
world!": world!":
package main package main
import ( import (
"github.com/rivo/tview" "github.com/rivo/tview"
) )
func main() { func main() {
box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!") box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!")
if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil { if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil {
panic(err) panic(err)
} }
} }
First, we create a box primitive with a border and a title. Then we create an First, we create a box primitive with a border and a title. Then we create an
application, set the box as its root primitive, and run the event loop. The application, set the box as its root primitive, and run the event loop. The
application exits when the application's [Application.Stop] function is called application exits when the application's Stop() function is called or when
or when Ctrl-C is pressed. Ctrl-C is pressed.
If we have a primitive which consumes key presses, we call the application's If we have a primitive which consumes key presses, we call the application's
[Application.SetFocus] function to redirect all key presses to that primitive. SetFocus() function to redirect all key presses to that primitive. Most
Most primitives then offer ways to install handlers that allow you to react to primitives then offer ways to install handlers that allow you to react to any
any actions performed on them. actions performed on them.
# More Demos More Demos
You will find more demos in the "demos" subdirectory. It also contains a You will find more demos in the "demos" subdirectory. It also contains a
presentation (written using tview) which gives an overview of the different presentation (written using tview) which gives an overview of the different
widgets and how they can be used. widgets and how they can be used.
# Colors Colors
Throughout this package, colors are specified using the [tcell.Color] type. Throughout this package, colors are specified using the tcell.Color type.
Functions such as [tcell.GetColor], [tcell.NewHexColor], and [tcell.NewRGBColor] Functions such as tcell.GetColor(), tcell.NewHexColor(), and tcell.NewRGBColor()
can be used to create colors from W3C color names or RGB values. can be used to create colors from W3C color names or RGB values.
Almost all strings which are displayed can contain color tags. Color tags are Almost all strings which are displayed can contain color tags. Color tags are
W3C color names or six hexadecimal digits following a hash tag, wrapped in W3C color names or six hexadecimal digits following a hash tag, wrapped in
square brackets. Examples: square brackets. Examples:
This is a [red]warning[white]! This is a [red]warning[white]!
The sky is [#8080ff]blue[#ffffff]. The sky is [#8080ff]blue[#ffffff].
A color tag changes the color of the characters following that color tag. This A color tag changes the color of the characters following that color tag. This
applies to almost everything from box titles, list text, form item labels, to applies to almost everything from box titles, list text, form item labels, to
@ -85,7 +84,7 @@ Color tags may contain not just the foreground (text) color but also the
background color and additional flags. In fact, the full definition of a color background color and additional flags. In fact, the full definition of a color
tag is as follows: tag is as follows:
[<foreground>:<background>:<flags>] [<foreground>:<background>:<flags>]
Each of the three fields can be left blank and trailing fields can be omitted. Each of the three fields can be left blank and trailing fields can be omitted.
(Empty square brackets "[]", however, are not considered color tags.) Colors (Empty square brackets "[]", however, are not considered color tags.) Colors
@ -95,26 +94,26 @@ means "reset to default".
You can specify the following flags (some flags may not be supported by your You can specify the following flags (some flags may not be supported by your
terminal): terminal):
l: blink l: blink
b: bold b: bold
i: italic i: italic
d: dim d: dim
r: reverse (switch foreground and background color) r: reverse (switch foreground and background color)
u: underline u: underline
s: strike-through s: strike-through
Examples: Examples:
[yellow]Yellow text [yellow]Yellow text
[yellow:red]Yellow text on red background [yellow:red]Yellow text on red background
[:red]Red background, text color unchanged [:red]Red background, text color unchanged
[yellow::u]Yellow text underlined [yellow::u]Yellow text underlined
[::bl]Bold, blinking text [::bl]Bold, blinking text
[::-]Colors unchanged, flags reset [::-]Colors unchanged, flags reset
[-]Reset foreground color [-]Reset foreground color
[-:-:-]Reset everything [-:-:-]Reset everything
[:]No effect [:]No effect
[]Not a valid color tag, will print square brackets as they are []Not a valid color tag, will print square brackets as they are
In the rare event that you want to display a string such as "[red]" or In the rare event that you want to display a string such as "[red]" or
"[#00ff1a]" without applying its effect, you need to put an opening square "[#00ff1a]" without applying its effect, you need to put an opening square
@ -122,26 +121,26 @@ bracket before the closing square bracket. Note that the text inside the
brackets will be matched less strictly than region or colors tags. I.e. any brackets will be matched less strictly than region or colors tags. I.e. any
character that may be used in color or region tags will be recognized. Examples: character that may be used in color or region tags will be recognized. Examples:
[red[] will be output as [red] [red[] will be output as [red]
["123"[] will be output as ["123"] ["123"[] will be output as ["123"]
[#6aff00[[] will be output as [#6aff00[] [#6aff00[[] will be output as [#6aff00[]
[a#"[[[] will be output as [a#"[[] [a#"[[[] will be output as [a#"[[]
[] will be output as [] (see color tags above) [] will be output as [] (see color tags above)
[[] will be output as [[] (not an escaped tag) [[] will be output as [[] (not an escaped tag)
You can use the Escape() function to insert brackets automatically where needed. You can use the Escape() function to insert brackets automatically where needed.
# Styles Styles
When primitives are instantiated, they are initialized with colors taken from When primitives are instantiated, they are initialized with colors taken from
the global Styles variable. You may change this variable to adapt the look and the global Styles variable. You may change this variable to adapt the look and
feel of the primitives to your preferred style. feel of the primitives to your preferred style.
# Unicode Support Unicode Support
This package supports unicode characters including wide characters. This package supports unicode characters including wide characters.
# Concurrency Concurrency
Many functions in this package are not thread-safe. For many applications, this Many functions in this package are not thread-safe. For many applications, this
may not be an issue: If your code makes changes in response to key events, it may not be an issue: If your code makes changes in response to key events, it
@ -149,32 +148,34 @@ will execute in the main goroutine and thus will not cause any race conditions.
If you access your primitives from other goroutines, however, you will need to If you access your primitives from other goroutines, however, you will need to
synchronize execution. The easiest way to do this is to call synchronize execution. The easiest way to do this is to call
[Application.QueueUpdate] or [Application.QueueUpdateDraw] (see the function Application.QueueUpdate() or Application.QueueUpdateDraw() (see the function
documentation for details): documentation for details):
go func() { go func() {
app.QueueUpdateDraw(func() { app.QueueUpdateDraw(func() {
table.SetCellSimple(0, 0, "Foo bar") table.SetCellSimple(0, 0, "Foo bar")
}) })
}() }()
One exception to this is the io.Writer interface implemented by [TextView]. You One exception to this is the io.Writer interface implemented by TextView. You
can safely write to a [TextView] from any goroutine. See the [TextView] can safely write to a TextView from any goroutine. See the TextView
documentation for details. documentation for details.
You can also call [Application.Draw] from any goroutine without having to wrap You can also call Application.Draw() from any goroutine without having to wrap
it in [Application.QueueUpdate]. And, as mentioned above, key event callbacks it in QueueUpdate(). And, as mentioned above, key event callbacks are executed
are executed in the main goroutine and thus should not use in the main goroutine and thus should not use QueueUpdate() as that may lead to
[Application.QueueUpdate] as that may lead to deadlocks. deadlocks.
# Type Hierarchy Type Hierarchy
All widgets listed above contain the [Box] type. All of [Box]'s functions are All widgets listed above contain the Box type. All of Box's functions are
therefore available for all widgets, too. therefore available for all widgets, too.
All widgets also implement the [Primitive] interface. All widgets also implement the Primitive interface.
The tview package is based on https://github.com/gdamore/tcell. It uses types The tview package is based on https://github.com/gdamore/tcell. It uses types
and constants from that package (e.g. colors and keyboard values). and constants from that package (e.g. colors and keyboard values).
This package does not process mouse input (yet).
*/ */
package tview package tview

@ -645,9 +645,9 @@ func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
} }
} }
// A mouse down anywhere else will return the focus to the last selected // A mouse click anywhere else will return the focus to the last selected
// element. // element.
if action == MouseLeftDown && f.InRect(event.Position()) { if action == MouseLeftClick && f.InRect(event.Position()) {
consumed = true consumed = true
} }

@ -27,9 +27,6 @@ type Frame struct {
// Border spacing. // Border spacing.
top, bottom, header, footer, left, right int top, bottom, header, footer, left, right int
// Keep a reference in case we need it when we change the primitive.
setFocus func(p Primitive)
} }
// NewFrame returns a new frame around the given primitive. The primitive's // NewFrame returns a new frame around the given primitive. The primitive's
@ -52,25 +49,6 @@ func NewFrame(primitive Primitive) *Frame {
return f return f
} }
// SetPrimitive replaces the contained primitive with the given one. To remove
// a primitive, set it to nil.
func (f *Frame) SetPrimitive(p Primitive) *Frame {
var hasFocus bool
if f.primitive != nil {
hasFocus = f.primitive.HasFocus()
}
f.primitive = p
if hasFocus && f.setFocus != nil {
f.setFocus(p) // Restore focus.
}
return f
}
// GetPrimitive returns the primitive contained in this frame.
func (f *Frame) GetPrimitive() Primitive {
return f.primitive
}
// AddText adds text to the frame. Set "header" to true if the text is to appear // AddText adds text to the frame. Set "header" to true if the text is to appear
// in the header, above the contained primitive. Set it to false for it to // in the header, above the contained primitive. Set it to false for it to
// appear in the footer, below the contained primitive. "align" must be one of // appear in the footer, below the contained primitive. "align" must be one of
@ -167,7 +145,6 @@ func (f *Frame) Draw(screen tcell.Screen) {
// Focus is called when this primitive receives focus. // Focus is called when this primitive receives focus.
func (f *Frame) Focus(delegate func(p Primitive)) { func (f *Frame) Focus(delegate func(p Primitive)) {
f.setFocus = delegate
if f.primitive != nil { if f.primitive != nil {
delegate(f.primitive) delegate(f.primitive)
} else { } else {
@ -192,19 +169,10 @@ func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
// Pass mouse events on to contained primitive. // Pass mouse events on to contained primitive.
if f.primitive != nil { if f.primitive != nil {
consumed, capture = f.primitive.MouseHandler()(action, event, setFocus) return f.primitive.MouseHandler()(action, event, setFocus)
if consumed {
return true, capture
}
} }
// Clicking on the frame parts. return false, nil
if action == MouseLeftDown {
setFocus(f)
consumed = true
}
return
}) })
} }
@ -214,9 +182,11 @@ func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
if f.primitive == nil { if f.primitive == nil {
return return
} }
if handler := f.primitive.InputHandler(); handler != nil { if f.primitive.HasFocus() {
handler(event, setFocus) if handler := f.primitive.InputHandler(); handler != nil {
return handler(event, setFocus)
return
}
} }
}) })
} }

@ -13,5 +13,5 @@ require (
github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.6 // indirect
) )

@ -16,7 +16,6 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

@ -35,7 +35,7 @@ type Grid struct {
items []*gridItem items []*gridItem
// The definition of the rows and columns of the grid. See // The definition of the rows and columns of the grid. See
// [TextView.SetRows] / [TextView.SetColumns] for details. // SetRows()/SetColumns() for details.
rows, columns []int rows, columns []int
// The minimum sizes for rows and columns. // The minimum sizes for rows and columns.
@ -65,7 +65,7 @@ type Grid struct {
// clear a Grid's background before any items are drawn, reset its Box to one // clear a Grid's background before any items are drawn, reset its Box to one
// with the desired color: // with the desired color:
// //
// grid.Box = NewBox() // grid.Box = NewBox()
func NewGrid() *Grid { func NewGrid() *Grid {
g := &Grid{ g := &Grid{
bordersColor: Styles.GraphicsColor, bordersColor: Styles.GraphicsColor,
@ -93,14 +93,14 @@ func NewGrid() *Grid {
// following call will result in columns with widths of 30, 10, 15, 15, and 30 // following call will result in columns with widths of 30, 10, 15, 15, and 30
// cells: // cells:
// //
// grid.SetColumns(30, 10, -1, -1, -2) // grid.SetColumns(30, 10, -1, -1, -2)
// //
// If a primitive were then placed in the 6th and 7th column, the resulting // If a primitive were then placed in the 6th and 7th column, the resulting
// widths would be: 30, 10, 10, 10, 20, 10, and 10 cells. // widths would be: 30, 10, 10, 10, 20, 10, and 10 cells.
// //
// If you then called SetMinSize() as follows: // If you then called SetMinSize() as follows:
// //
// grid.SetMinSize(15, 20) // grid.SetMinSize(15, 20)
// //
// The resulting widths would be: 30, 15, 15, 15, 20, 15, and 15 cells, a total // The resulting widths would be: 30, 15, 15, 15, 20, 15, and 15 cells, a total
// of 125 cells, 25 cells wider than the available grid width. // of 125 cells, 25 cells wider than the available grid width.
@ -110,8 +110,8 @@ func (g *Grid) SetColumns(columns ...int) *Grid {
} }
// SetRows defines how the rows of the grid are distributed. These values behave // SetRows defines how the rows of the grid are distributed. These values behave
// the same as the column values provided with [TextView.SetColumns], see there // the same as the column values provided with SetColumns(), see there for a
// for a definition and examples. // definition and examples.
// //
// The provided values correspond to row heights, the first value defining // The provided values correspond to row heights, the first value defining
// the height of the topmost row. // the height of the topmost row.
@ -120,9 +120,8 @@ func (g *Grid) SetRows(rows ...int) *Grid {
return g return g
} }
// SetSize is a shortcut for [TextView.SetRows] and [TextView.SetColumns] where // SetSize is a shortcut for SetRows() and SetColumns() where all row and column
// all row and column values are set to the given size values. See // values are set to the given size values. See SetColumns() for details on sizes.
// [TextView.SetColumns] for details on sizes.
func (g *Grid) SetSize(numRows, numColumns, rowSize, columnSize int) *Grid { func (g *Grid) SetSize(numRows, numColumns, rowSize, columnSize int) *Grid {
g.rows = make([]int, numRows) g.rows = make([]int, numRows)
for index := range g.rows { for index := range g.rows {
@ -175,7 +174,7 @@ func (g *Grid) SetBordersColor(color tcell.Color) *Grid {
// the given row and column and will span "rowSpan" rows and "colSpan" columns. // the given row and column and will span "rowSpan" rows and "colSpan" columns.
// For example, for a primitive to occupy rows 2, 3, and 4 and columns 5 and 6: // For example, for a primitive to occupy rows 2, 3, and 4 and columns 5 and 6:
// //
// grid.AddItem(p, 2, 5, 3, 2, 0, 0, true) // grid.AddItem(p, 2, 5, 3, 2, 0, 0, true)
// //
// If rowSpan or colSpan is 0, the primitive will not be drawn. // If rowSpan or colSpan is 0, the primitive will not be drawn.
// //
@ -186,9 +185,9 @@ func (g *Grid) SetBordersColor(color tcell.Color) *Grid {
// primitive apply, the one that has at least one highest minimum value will be // primitive apply, the one that has at least one highest minimum value will be
// used, or the primitive added last if those values are the same. Example: // used, or the primitive added last if those values are the same. Example:
// //
// grid.AddItem(p, 0, 0, 0, 0, 0, 0, true). // Hide in small grids. // grid.AddItem(p, 0, 0, 0, 0, 0, 0, true). // Hide in small grids.
// AddItem(p, 0, 0, 1, 2, 100, 0, true). // One-column layout for medium grids. // AddItem(p, 0, 0, 1, 2, 100, 0, true). // One-column layout for medium grids.
// AddItem(p, 1, 1, 3, 2, 300, 0, true) // Multi-column layout for large grids. // AddItem(p, 1, 1, 3, 2, 300, 0, true) // Multi-column layout for large grids.
// //
// To use the same grid layout for all sizes, simply set minGridWidth and // To use the same grid layout for all sizes, simply set minGridWidth and
// minGridHeight to 0. // minGridHeight to 0.

@ -418,7 +418,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
// We have enough space for the full text. // We have enough space for the full text.
printWithStyle(screen, Escape(text), x, y, 0, fieldWidth, AlignLeft, i.fieldStyle, true) printWithStyle(screen, Escape(text), x, y, 0, fieldWidth, AlignLeft, i.fieldStyle, true)
i.offset = 0 i.offset = 0
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
if textPos >= i.cursorPos { if textPos >= i.cursorPos {
return true return true
} }
@ -440,7 +440,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
shiftLeft = subWidth - fieldWidth + 1 shiftLeft = subWidth - fieldWidth + 1
} }
currentOffset := i.offset currentOffset := i.offset
iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
if textPos >= currentOffset { if textPos >= currentOffset {
if shiftLeft > 0 { if shiftLeft > 0 {
i.offset = textPos + textWidth i.offset = textPos + textWidth
@ -520,7 +520,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
}) })
} }
moveRight := func() { moveRight := func() {
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
i.cursorPos += textWidth i.cursorPos += textWidth
return true return true
}) })
@ -616,7 +616,7 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
i.offset = 0 i.offset = 0
} }
case tcell.KeyDelete, tcell.KeyCtrlD: // Delete character after the cursor. case tcell.KeyDelete, tcell.KeyCtrlD: // Delete character after the cursor.
iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:] i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:]
return true return true
}) })
@ -685,25 +685,21 @@ func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventM
} }
// Process mouse event. // Process mouse event.
if y == rectY { if action == MouseLeftClick && y == rectY {
if action == MouseLeftDown { // Determine where to place the cursor.
setFocus(i) if x >= i.fieldX {
consumed = true if !iterateString(i.text[i.offset:], func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool {
} else if action == MouseLeftClick { if x-i.fieldX < screenPos+screenWidth {
// Determine where to place the cursor. i.cursorPos = textPos + i.offset
if x >= i.fieldX { return true
if !iterateString(i.text[i.offset:], func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth, boundaries int) bool {
if x-i.fieldX < screenPos+screenWidth {
i.cursorPos = textPos + i.offset
return true
}
return false
}) {
i.cursorPos = len(i.text)
} }
return false
}) {
i.cursorPos = len(i.text)
} }
consumed = true
} }
setFocus(i)
consumed = true
} }
return return

@ -699,6 +699,7 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
// Process mouse event. // Process mouse event.
switch action { switch action {
case MouseLeftClick: case MouseLeftClick:
setFocus(l)
index := l.indexAtPoint(event.Position()) index := l.indexAtPoint(event.Position())
if index != -1 { if index != -1 {
item := l.items[index] item := l.items[index]
@ -727,7 +728,6 @@ func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
if _, _, _, height := l.GetInnerRect(); lines > height { if _, _, _, height := l.GetInnerRect(); lines > height {
l.itemOffset++ l.itemOffset++
} }
setFocus(l)
consumed = true consumed = true
} }

@ -190,7 +190,7 @@ func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
// Pass mouse events on to the form. // Pass mouse events on to the form.
consumed, capture = m.form.MouseHandler()(action, event, setFocus) consumed, capture = m.form.MouseHandler()(action, event, setFocus)
if !consumed && action == MouseLeftDown && m.InRect(event.Position()) { if !consumed && action == MouseLeftClick && m.InRect(event.Position()) {
setFocus(m) setFocus(m)
consumed = true consumed = true
} }

@ -135,7 +135,7 @@ func (c *TableCell) SetTransparency(transparent bool) *TableCell {
// SetAttributes sets the cell's text attributes. You can combine different // SetAttributes sets the cell's text attributes. You can combine different
// attributes using bitmask operations: // attributes using bitmask operations:
// //
// cell.SetAttributes(tcell.AttrUnderline | tcell.AttrBold) // cell.SetAttributes(tcell.AttrUnderline | tcell.AttrBold)
func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell { func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell {
c.Attributes = attr c.Attributes = attr
return c return c
@ -388,13 +388,13 @@ func (t *tableDefaultContent) GetColumnCount() int {
// Columns will use as much horizontal space as they need. You can constrain // Columns will use as much horizontal space as they need. You can constrain
// their size with the MaxWidth parameter of the TableCell type. // their size with the MaxWidth parameter of the TableCell type.
// //
// # Fixed Columns // Fixed Columns
// //
// You can define fixed rows and rolumns via SetFixed(). They will always stay // You can define fixed rows and rolumns via SetFixed(). They will always stay
// in their place, even when the table is scrolled. Fixed rows are always the // in their place, even when the table is scrolled. Fixed rows are always the
// top rows. Fixed columns are always the leftmost columns. // top rows. Fixed columns are always the leftmost columns.
// //
// # Selections // Selections
// //
// You can call SetSelectable() to set columns and/or rows to "selectable". If // You can call SetSelectable() to set columns and/or rows to "selectable". If
// the flag is set only for columns, entire columns can be selected by the user. // the flag is set only for columns, entire columns can be selected by the user.
@ -402,7 +402,7 @@ func (t *tableDefaultContent) GetColumnCount() int {
// set, individual cells can be selected. The "selected" handler set via // set, individual cells can be selected. The "selected" handler set via
// SetSelectedFunc() is invoked when the user presses Enter on a selection. // SetSelectedFunc() is invoked when the user presses Enter on a selection.
// //
// # Navigation // Navigation
// //
// If the table extends beyond the available space, it can be navigated with // If the table extends beyond the available space, it can be navigated with
// key bindings similar to Vim: // key bindings similar to Vim:
@ -551,7 +551,7 @@ func (t *Table) SetBordersColor(color tcell.Color) *Table {
// //
// To reset a previous setting to its default, make the following call: // To reset a previous setting to its default, make the following call:
// //
// table.SetSelectedStyle(tcell.Style{}) // table.SetSelectedStyle(tcell.Style{})
func (t *Table) SetSelectedStyle(style tcell.Style) *Table { func (t *Table) SetSelectedStyle(style tcell.Style) *Table {
t.selectedStyle = style t.selectedStyle = style
return t return t
@ -1596,9 +1596,6 @@ func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
} }
switch action { switch action {
case MouseLeftDown:
setFocus(t)
consumed = true
case MouseLeftClick: case MouseLeftClick:
selectEvent := true selectEvent := true
row, column := t.cellAt(x, y) row, column := t.cellAt(x, y)
@ -1611,6 +1608,7 @@ func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
if selectEvent && (t.rowsSelectable || t.columnsSelectable) { if selectEvent && (t.rowsSelectable || t.columnsSelectable) {
t.Select(row, column) t.Select(row, column)
} }
setFocus(t)
consumed = true consumed = true
case MouseScrollUp: case MouseScrollUp:
t.trackEnd = false t.trackEnd = false

File diff suppressed because it is too large Load Diff

@ -75,16 +75,12 @@ func (w TextViewWriter) HasFocus() bool {
return w.t.hasFocus return w.t.hasFocus
} }
// TextView is a box which displays text. While the text to be displayed can be // TextView is a box which displays text. It implements the io.Writer interface
// changed or appended to, there is no functionality that allows the user to // so you can stream text to it. This does not trigger a redraw automatically
// edit text. For that, TextArea should be used.
//
// TextView implements the io.Writer interface so you can stream text to it,
// appending to the existing text. This does not trigger a redraw automatically
// but if a handler is installed via SetChangedFunc(), you can cause it to be // but if a handler is installed via SetChangedFunc(), you can cause it to be
// redrawn. (See SetChangedFunc() for more details.) // redrawn. (See SetChangedFunc() for more details.)
// //
// # Navigation // Navigation
// //
// If the text view is scrollable (the default), text is kept in a buffer which // If the text view is scrollable (the default), text is kept in a buffer which
// may be larger than the screen and can be navigated similarly to Vim: // may be larger than the screen and can be navigated similarly to Vim:
@ -103,27 +99,27 @@ func (w TextViewWriter) HasFocus() bool {
// //
// Use SetInputCapture() to override or modify keyboard input. // Use SetInputCapture() to override or modify keyboard input.
// //
// # Colors // Colors
// //
// If dynamic colors are enabled via SetDynamicColors(), text color can be // If dynamic colors are enabled via SetDynamicColors(), text color can be
// changed dynamically by embedding color strings in square brackets. This works // changed dynamically by embedding color strings in square brackets. This works
// the same way as anywhere else. Please see the package documentation for more // the same way as anywhere else. Please see the package documentation for more
// information. // information.
// //
// # Regions and Highlights // Regions and Highlights
// //
// If regions are enabled via SetRegions(), you can define text regions within // If regions are enabled via SetRegions(), you can define text regions within
// the text and assign region IDs to them. Text regions start with region tags. // the text and assign region IDs to them. Text regions start with region tags.
// Region tags are square brackets that contain a region ID in double quotes, // Region tags are square brackets that contain a region ID in double quotes,
// for example: // for example:
// //
// We define a ["rg"]region[""] here. // We define a ["rg"]region[""] here.
// //
// A text region ends with the next region tag. Tags with no region ID ([""]) // A text region ends with the next region tag. Tags with no region ID ([""])
// don't start new regions. They can therefore be used to mark the end of a // don't start new regions. They can therefore be used to mark the end of a
// region. Region IDs must satisfy the following regular expression: // region. Region IDs must satisfy the following regular expression:
// //
// [a-zA-Z0-9_,;: \-\.]+ // [a-zA-Z0-9_,;: \-\.]+
// //
// Regions can be highlighted by calling the Highlight() function with one or // Regions can be highlighted by calling the Highlight() function with one or
// more region IDs. This can be used to display search results, for example. // more region IDs. This can be used to display search results, for example.
@ -131,7 +127,7 @@ func (w TextViewWriter) HasFocus() bool {
// The ScrollToHighlight() function can be used to jump to the currently // The ScrollToHighlight() function can be used to jump to the currently
// highlighted region once when the text view is drawn the next time. // highlighted region once when the text view is drawn the next time.
// //
// # Large Texts // Large Texts
// //
// This widget is not designed for very large texts as word wrapping, color and // This widget is not designed for very large texts as word wrapping, color and
// region tag handling, and proper Unicode handling will result in a significant // region tag handling, and proper Unicode handling will result in a significant
@ -183,8 +179,7 @@ type TextView struct {
// If set to true, the text view will always remain at the end of the content. // If set to true, the text view will always remain at the end of the content.
trackEnd bool trackEnd bool
// The number of characters to be skipped on each line (not used in wrap // The number of characters to be skipped on each line (not in wrap mode).
// mode).
columnOffset int columnOffset int
// The maximum number of lines kept in the line index, effectively the // The maximum number of lines kept in the line index, effectively the
@ -756,13 +751,13 @@ func (t *TextView) write(p []byte) (n int, err error) {
// BatchWriter is called, and will be released when the returned writer is // BatchWriter is called, and will be released when the returned writer is
// closed. Example: // closed. Example:
// //
// tv := tview.NewTextView() // tv := tview.NewTextView()
// w := tv.BatchWriter() // w := tv.BatchWriter()
// defer w.Close() // defer w.Close()
// w.Clear() // w.Clear()
// fmt.Fprintln(w, "To sit in solemn silence") // fmt.Fprintln(w, "To sit in solemn silence")
// fmt.Fprintln(w, "on a dull, dark, dock") // fmt.Fprintln(w, "on a dull, dark, dock")
// fmt.Println(tv.GetText(false)) // fmt.Println(tv.GetText(false))
// //
// Note that using the batch writer requires you to manage any issues that may // Note that using the batch writer requires you to manage any issues that may
// arise from concurrency yourself. See package description for details on // arise from concurrency yourself. See package description for details on
@ -1143,7 +1138,7 @@ func (t *TextView) Draw(screen tcell.Screen) {
// Print the line. // Print the line.
if y+line-t.lineOffset >= 0 { if y+line-t.lineOffset >= 0 {
var colorPos, regionPos, escapePos, tagOffset, skipped int var colorPos, regionPos, escapePos, tagOffset, skipped int
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
// Process tags. // Process tags.
for { for {
if colorPos < len(colorTags) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { if colorPos < len(colorTags) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] {
@ -1317,9 +1312,6 @@ func (t *TextView) MouseHandler() func(action MouseAction, event *tcell.EventMou
} }
switch action { switch action {
case MouseLeftDown:
setFocus(t)
consumed = true
case MouseLeftClick: case MouseLeftClick:
if t.regions { if t.regions {
// Find a region to highlight. // Find a region to highlight.
@ -1334,6 +1326,7 @@ func (t *TextView) MouseHandler() func(action MouseAction, event *tcell.EventMou
break break
} }
} }
setFocus(t)
consumed = true consumed = true
case MouseScrollUp: case MouseScrollUp:
t.trackEnd = false t.trackEnd = false

@ -367,8 +367,8 @@ func (t *TreeView) SetTopLevel(topLevel int) *TreeView {
// //
// For example, to display a hierarchical list with bullet points: // For example, to display a hierarchical list with bullet points:
// //
// treeView.SetGraphics(false). // treeView.SetGraphics(false).
// SetPrefixes([]string{"* ", "- ", "x "}) // SetPrefixes([]string{"* ", "- ", "x "})
func (t *TreeView) SetPrefixes(prefixes []string) *TreeView { func (t *TreeView) SetPrefixes(prefixes []string) *TreeView {
t.prefixes = prefixes t.prefixes = prefixes
return t return t
@ -792,10 +792,8 @@ func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMou
} }
switch action { switch action {
case MouseLeftDown:
setFocus(t)
consumed = true
case MouseLeftClick: case MouseLeftClick:
setFocus(t)
_, rectY, _, _ := t.GetInnerRect() _, rectY, _, _ := t.GetInnerRect()
y += t.offsetY - rectY y += t.offsetY - rectY
if y >= 0 && y < len(t.nodes) { if y >= 0 && y < len(t.nodes) {

@ -262,7 +262,7 @@ func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth,
foregroundColor, backgroundColor, attributes string foregroundColor, backgroundColor, attributes string
) )
originalStyle := style originalStyle := style
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
// Update color/escape tag offset and style. // Update color/escape tag offset and style.
if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] { if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] {
foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos])
@ -305,7 +305,7 @@ func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth,
for rightIndex-1 > leftIndex && strippedWidth-skipWidth-choppedLeft-choppedRight > maxWidth { for rightIndex-1 > leftIndex && strippedWidth-skipWidth-choppedLeft-choppedRight > maxWidth {
if skipWidth > 0 || choppedLeft < choppedRight { if skipWidth > 0 || choppedLeft < choppedRight {
// Iterate on the left by one character. // Iterate on the left by one character.
iterateString(strippedText[leftIndex:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(strippedText[leftIndex:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
if skipWidth > 0 { if skipWidth > 0 {
skipWidth -= screenWidth skipWidth -= screenWidth
strippedWidth -= screenWidth strippedWidth -= screenWidth
@ -369,7 +369,7 @@ func printWithStyle(screen tcell.Screen, text string, x, y, skipWidth, maxWidth,
drawn, drawnWidth, colorPos, escapePos, tagOffset, from, to int drawn, drawnWidth, colorPos, escapePos, tagOffset, from, to int
foregroundColor, backgroundColor, attributes string foregroundColor, backgroundColor, attributes string
) )
iterateString(strippedText, func(main rune, comb []rune, textPos, length, screenPos, screenWidth, boundaries int) bool { iterateString(strippedText, func(main rune, comb []rune, textPos, length, screenPos, screenWidth int) bool {
// Skip character if necessary. // Skip character if necessary.
if skipWidth > 0 { if skipWidth > 0 {
skipWidth -= screenWidth skipWidth -= screenWidth
@ -496,7 +496,7 @@ func WordWrap(text string, width int) (lines []string) {
} }
return substr return substr
} }
iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool { iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool {
// Handle tags. // Handle tags.
for { for {
if colorPos < len(colorTagIndices) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { if colorPos < len(colorTagIndices) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] {
@ -582,18 +582,16 @@ func Escape(text string) string {
// Unicode code points of the character (the first rune and any combining runes // Unicode code points of the character (the first rune and any combining runes
// which may be nil if there aren't any), the starting position (in bytes) // which may be nil if there aren't any), the starting position (in bytes)
// within the original string, its length in bytes, the screen position of the // within the original string, its length in bytes, the screen position of the
// character, the screen width of it, and a boundaries value which includes // character, and the screen width of it. The iteration stops if the callback
// word/sentence boundary or line break information (see the // returns true. This function returns true if the iteration was stopped before
// github.com/rivo/uniseg package, Step() function, for more information). The // the last character.
// iteration stops if the callback returns true. This function returns true if func iterateString(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool {
// the iteration was stopped before the last character. var screenPos, textPos int
func iterateString(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth, boundaries int) bool) bool {
var screenPos, textPos, boundaries int
state := -1 state := -1
for len(text) > 0 { for len(text) > 0 {
var cluster string var cluster string
cluster, text, boundaries, state = uniseg.StepString(text, state) cluster, text, _, state = uniseg.FirstGraphemeClusterInString(text, state)
var width int var width int
runes := make([]rune, 0, len(cluster)) runes := make([]rune, 0, len(cluster))
@ -610,7 +608,7 @@ func iterateString(text string, callback func(main rune, comb []rune, textPos, t
comb = runes[1:] comb = runes[1:]
} }
if callback(runes[0], comb, textPos, len(cluster), screenPos, width, boundaries) { if callback(runes[0], comb, textPos, len(cluster), screenPos, width) {
return true return true
} }
@ -638,7 +636,7 @@ func iterateStringReverse(text string, callback func(main rune, comb []rune, tex
// Create the grapheme clusters. // Create the grapheme clusters.
var clusters []cluster var clusters []cluster
iterateString(text, func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth, boundaries int) bool { iterateString(text, func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool {
clusters = append(clusters, cluster{ clusters = append(clusters, cluster{
main: main, main: main,
comb: comb, comb: comb,

Loading…
Cancel
Save