Fixed a number of bugs and added missing useful functions.

This commit is contained in:
Oliver 2017-12-27 16:04:21 +01:00
parent 91d78f146b
commit 3670319cd6
7 changed files with 151 additions and 52 deletions

View File

@ -23,6 +23,9 @@ type Application struct {
// The root primitive to be seen on the screen.
root Primitive
// Whether or not the application resizes the root primitive.
rootAutoSize bool
// Key overrides.
keyOverrides map[tcell.Key]func(p Primitive) bool
@ -103,6 +106,10 @@ func (a *Application) Run() error {
}()
// Draw the screen for the first time.
if a.rootAutoSize && a.root != nil {
width, height := a.screen.Size()
a.root.SetRect(0, 0, width, height)
}
a.Unlock()
a.Draw()
@ -158,6 +165,13 @@ func (a *Application) Run() error {
}
}
case *tcell.EventResize:
if a.rootAutoSize && a.root != nil {
a.Lock()
width, height := a.screen.Size()
a.root.SetRect(0, 0, width, height)
a.Unlock()
a.Draw()
}
a.Draw()
}
}
@ -198,15 +212,26 @@ func (a *Application) Draw() *Application {
// SetRoot sets the root primitive for this application. This function must be
// called or nothing will be displayed when the application starts.
func (a *Application) SetRoot(root Primitive) *Application {
func (a *Application) SetRoot(root Primitive, autoSize bool) *Application {
a.Lock()
defer a.Unlock()
a.root = root
a.rootAutoSize = autoSize
return a
}
// ResizeToFullScreen resizes the given primitive such that it fills the entire
// screen.
func (a *Application) ResizeToFullScreen(p Primitive) *Application {
a.RLock()
width, height := a.screen.Size()
a.RUnlock()
p.SetRect(0, 0, width, height)
return a
}
// SetFocus sets the focus on a new primitive. All key events will be redirected
// to that primitive. Callers must ensure that the primitive will handle key
// events.

View File

@ -31,6 +31,7 @@ type Button struct {
// NewButton returns a new input field.
func NewButton(label string) *Button {
box := NewBox().SetBackgroundColor(tcell.ColorBlue)
box.SetRect(0, 0, len([]rune(label))+4, 1)
return &Button{
Box: box,
label: label,
@ -92,21 +93,28 @@ func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button {
// Draw draws this primitive onto the screen.
func (b *Button) Draw(screen tcell.Screen) {
// Draw the box.
borderColor := b.borderColor
backgroundColor := b.backgroundColor
if b.focus.HasFocus() {
b.backgroundColor = b.backgroundColorActivated
b.borderColor = b.labelColorActivated
defer func() {
b.borderColor = borderColor
}()
}
b.Box.Draw(screen)
b.backgroundColor = backgroundColor
// Draw label.
x, y, width, height := b.GetInnerRect()
y = y + height/2
labelColor := b.labelColor
if b.focus.HasFocus() {
labelColor = b.labelColorActivated
if width > 0 && height > 0 {
y = y + height/2
labelColor := b.labelColor
if b.focus.HasFocus() {
labelColor = b.labelColorActivated
}
Print(screen, b.label, x, y, width, AlignCenter, labelColor)
}
Print(screen, b.label, x, y, width, AlignCenter, labelColor)
if b.focus.HasFocus() {
screen.HideCursor()

View File

@ -12,7 +12,7 @@ type frameText struct {
Color tcell.Color // The text color.
}
// Frame is a wrapper which adds a border around another box. The top area
// Frame is a wrapper which adds a border around another primitive. The top area
// (header) and the bottom area (footer) may also contain text.
type Frame struct {
*Box

View File

@ -3,52 +3,10 @@ package tview
import (
"math"
"regexp"
"strconv"
"github.com/gdamore/tcell"
)
var (
// InputFieldInteger accepts integers.
InputFieldInteger func(text string, ch rune) bool
// InputFieldFloat accepts floating-point numbers.
InputFieldFloat func(text string, ch rune) bool
// InputFieldMaxLength returns an input field accept handler which accepts
// input strings up to a given length. Use it like this:
//
// inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters.
InputFieldMaxLength func(maxLength int) func(text string, ch rune) bool
)
// Package initialization.
func init() {
// Initialize the predefined handlers.
InputFieldInteger = func(text string, ch rune) bool {
if text == "-" {
return true
}
_, err := strconv.Atoi(text)
return err == nil
}
InputFieldFloat = func(text string, ch rune) bool {
if text == "-" || text == "." {
return true
}
_, err := strconv.ParseFloat(text, 64)
return err == nil
}
InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool {
return func(text string, ch rune) bool {
return len([]rune(text)) <= maxLength
}
}
}
// InputField is a one-line box (three lines if there is a title) where the
// user can enter text.
type InputField struct {
@ -229,7 +187,7 @@ func (i *InputField) setCursor(screen tcell.Screen) {
rightLimit -= 2
}
fieldLength := len([]rune(i.text))
if fieldLength > i.fieldLength-1 {
if i.fieldLength > 0 && fieldLength > i.fieldLength-1 {
fieldLength = i.fieldLength - 1
}
x += len([]rune(i.label)) + fieldLength

View File

@ -1,6 +1,8 @@
package tview
import "github.com/gdamore/tcell"
import (
"github.com/gdamore/tcell"
)
// page represents one page of a Pages object.
type page struct {
@ -18,6 +20,10 @@ type Pages struct {
// The contained pages.
pages []*page
// We keep a reference to the function which allows us to set the focus to
// a newly visible page.
setFocus func(p Primitive)
// An optional handler which is called whenever the visibility or the order of
// pages changes.
changed func()
@ -46,10 +52,11 @@ func (p *Pages) SetChangedFunc(handler func()) *Pages {
// Visible pages will be drawn in the order they were added (unless that order
// was changed in one of the other functions).
func (p *Pages) AddPage(name string, item Primitive, visible bool) *Pages {
p.pages = append(p.pages, &page{Item: item, Name: name, Visible: true})
p.pages = append(p.pages, &page{Item: item, Name: name, Visible: visible})
if p.changed != nil {
p.changed()
}
p.refocus()
return p
}
@ -64,6 +71,7 @@ func (p *Pages) RemovePage(name string) *Pages {
break
}
}
p.refocus()
return p
}
@ -79,6 +87,7 @@ func (p *Pages) ShowPage(name string) *Pages {
break
}
}
p.refocus()
return p
}
@ -93,6 +102,7 @@ func (p *Pages) HidePage(name string) *Pages {
break
}
}
p.refocus()
return p
}
@ -109,6 +119,7 @@ func (p *Pages) SwitchToPage(name string) *Pages {
if p.changed != nil {
p.changed()
}
p.refocus()
return p
}
@ -127,6 +138,7 @@ func (p *Pages) SendToFront(name string) *Pages {
break
}
}
p.refocus()
return p
}
@ -145,6 +157,7 @@ func (p *Pages) SendToBack(name string) *Pages {
break
}
}
p.refocus()
return p
}
@ -158,6 +171,36 @@ func (p *Pages) HasFocus() bool {
return false
}
// Focus is called by the application when the primitive receives focus.
func (p *Pages) Focus(delegate func(p Primitive)) {
p.setFocus = delegate
var topItem Primitive
for _, page := range p.pages {
if page.Visible {
topItem = page.Item
}
}
if topItem != nil {
delegate(topItem)
}
}
// refocus sets the focus to the topmost visible page but only if we have focus.
func (p *Pages) refocus() {
if !p.HasFocus() || p.setFocus == nil {
return
}
var topItem Primitive
for _, page := range p.pages {
if page.Visible {
topItem = page.Item
}
}
if topItem != nil {
p.setFocus(topItem)
}
}
// Draw draws this primitive onto the screen.
func (p *Pages) Draw(screen tcell.Screen) {
for _, page := range p.pages {
@ -167,3 +210,8 @@ func (p *Pages) Draw(screen tcell.Screen) {
page.Item.Draw(screen)
}
}
// InputHandler returns the handler for this primitive.
func (p *Pages) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return func(event *tcell.EventKey, setFocus func(p Primitive)) {}
}

View File

@ -272,6 +272,14 @@ func (t *TextView) Highlight(regionIDs ...string) *TextView {
return t
}
// GetHighlights returns the IDs of all currently highlighted regions.
func (t *TextView) GetHighlights() (regionIDs []string) {
for id := range t.highlights {
regionIDs = append(regionIDs, id)
}
return
}
// ScrollToHighlight will cause the visible area to be scrolled so that the
// highlighted regions appear in the visible area of the text view. This
// repositioning happens the next time the text view is drawn. It happens only
@ -452,6 +460,16 @@ func (t *TextView) reindexBuffer(width int) {
regions = regionPattern.FindAllStringSubmatch(str, -1)
}
// We also keep a reference to empty lines.
if len(str) == 0 {
t.index = append(t.index, &textViewIndex{
Line: index,
Pos: 0,
Color: color,
Region: regionID,
})
}
// Break down the line.
var currentTag, currentRegion, currentWidth int
for pos := range str {

42
util.go
View File

@ -2,6 +2,7 @@ package tview
import (
"math"
"strconv"
"strings"
"github.com/gdamore/tcell"
@ -36,6 +37,47 @@ const (
GraphicsEllipsis = '\u2026'
)
var (
// InputFieldInteger accepts integers.
InputFieldInteger func(text string, ch rune) bool
// InputFieldFloat accepts floating-point numbers.
InputFieldFloat func(text string, ch rune) bool
// InputFieldMaxLength returns an input field accept handler which accepts
// input strings up to a given length. Use it like this:
//
// inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters.
InputFieldMaxLength func(maxLength int) func(text string, ch rune) bool
)
// Package initialization.
func init() {
// Initialize the predefined handlers.
InputFieldInteger = func(text string, ch rune) bool {
if text == "-" {
return true
}
_, err := strconv.Atoi(text)
return err == nil
}
InputFieldFloat = func(text string, ch rune) bool {
if text == "-" || text == "." {
return true
}
_, err := strconv.ParseFloat(text, 64)
return err == nil
}
InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool {
return func(text string, ch rune) bool {
return len([]rune(text)) <= maxLength
}
}
}
// Print prints text onto the screen into the given box at (x,y,maxWidth,1),
// no exceeding that box. "align" is one of AlignLeft, AlignCenter, or
// AlignRight. The screen's background color will be maintained.