Removed Focusable interface.

This commit is contained in:
Oliver 2020-11-17 19:33:25 +01:00
parent c3a49506be
commit 675ed5b96b
23 changed files with 79 additions and 79 deletions

View File

@ -99,7 +99,7 @@ I try really hard to keep this project backwards compatible. Your software shoul
- a new version of an imported package (most likely [`tcell`](https://github.com/gdamore/tcell)) changes in such a way that forces me to make changes in `tview` as well,
- I fix something that I consider a bug, rather than a feature, something that does not work as originally intended,
- I make changes to "internal" interfaces such as [`Primitive`](https://pkg.go.dev/github.com/rivo/tview#Primitive) or [`Focusable`](https://pkg.go.dev/github.com/rivo/tview#Focusable). You shouldn't need these interfaces unless you're writing your own primitives for `tview`. (Yes, I realize these are public interfaces. This has advantages as well as disadvantages. For the time being, it is what it is.)
- I make changes to "internal" interfaces such as [`Primitive`](https://pkg.go.dev/github.com/rivo/tview#Primitive). You shouldn't need these interfaces unless you're writing your own primitives for `tview`. (Yes, I realize these are public interfaces. This has advantages as well as disadvantages. For the time being, it is what it is.)
## Your Feedback

View File

@ -325,7 +325,7 @@ EventLoop:
}
// Pass other key events to the root primitive.
if root != nil && root.GetFocusable().HasFocus() {
if root != nil && root.HasFocus() {
if handler := root.InputHandler(); handler != nil {
handler(event, func(p Primitive) {
a.SetFocus(p)

22
box.go
View File

@ -42,10 +42,6 @@ type Box struct {
// The alignment of the title.
titleAlign int
// Provides a way to find out if this box has focus. We always go through
// this interface because it may be overridden by implementing classes.
focus Focusable
// Whether or not this box has focus.
hasFocus bool
@ -74,7 +70,6 @@ func NewBox() *Box {
titleColor: Styles.TitleColor,
titleAlign: AlignCenter,
}
b.focus = b
return b
}
@ -320,6 +315,16 @@ func (b *Box) SetTitleAlign(align int) *Box {
// Draw draws this primitive onto the screen.
func (b *Box) Draw(screen tcell.Screen) {
b.DrawForSubclass(screen, b)
}
// DrawForSubclass draws this box under the assumption that primitive p is a
// subclass of this box. This is needed e.g. to draw proper box frames which
// depend on the subclass's focus.
//
// Only call this function from your own custom primitives. It is not needed in
// applications that have no custom primitives.
func (b *Box) DrawForSubclass(screen tcell.Screen, p Primitive) {
// Don't draw anything if there is no space.
if b.width <= 0 || b.height <= 0 {
return
@ -340,7 +345,7 @@ func (b *Box) Draw(screen tcell.Screen) {
// Draw border.
if b.border && b.width >= 2 && b.height >= 2 {
var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune
if b.focus.HasFocus() {
if p.HasFocus() {
horizontal = Borders.HorizontalFocus
vertical = Borders.VerticalFocus
topLeft = Borders.TopLeftFocus
@ -403,8 +408,3 @@ func (b *Box) Blur() {
func (b *Box) HasFocus() bool {
return b.hasFocus
}
// GetFocusable returns the item's Focusable.
func (b *Box) GetFocusable() Focusable {
return b.focus
}

View File

@ -97,14 +97,14 @@ func (b *Button) Draw(screen tcell.Screen) {
// Draw the box.
borderColor := b.GetBorderColor()
backgroundColor := b.GetBackgroundColor()
if b.focus.HasFocus() {
if b.HasFocus() {
b.SetBackgroundColor(b.backgroundColorActivated)
b.SetBorderColor(b.labelColorActivated)
defer func() {
b.SetBorderColor(borderColor)
}()
}
b.Box.Draw(screen)
b.Box.DrawForSubclass(screen, b)
b.backgroundColor = backgroundColor
// Draw label.
@ -112,7 +112,7 @@ func (b *Button) Draw(screen tcell.Screen) {
if width > 0 && height > 0 {
y = y + height/2
labelColor := b.labelColor
if b.focus.HasFocus() {
if b.HasFocus() {
labelColor = b.labelColorActivated
}
Print(screen, b.label, x, y, width, AlignCenter, labelColor)

View File

@ -144,7 +144,7 @@ func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
// Draw draws this primitive onto the screen.
func (c *Checkbox) Draw(screen tcell.Screen) {
c.Box.Draw(screen)
c.Box.DrawForSubclass(screen, c)
// Prepare
x, y, width, height := c.GetInnerRect()
@ -168,7 +168,7 @@ func (c *Checkbox) Draw(screen tcell.Screen) {
// Draw checkbox.
fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor)
if c.focus.HasFocus() {
if c.HasFocus() {
fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor)
}
checkedRune := 'X'

View File

@ -25,7 +25,7 @@ func NewRadioButtons(options []string) *RadioButtons {
// Draw draws this primitive onto the screen.
func (r *RadioButtons) Draw(screen tcell.Screen) {
r.Box.Draw(screen)
r.Box.DrawForSubclass(screen, r)
x, y, width, height := r.GetInnerRect()
for index, option := range r.options {
@ -59,12 +59,34 @@ func (r *RadioButtons) InputHandler() func(event *tcell.EventKey, setFocus func(
})
}
// MouseHandler returns the mouse handler for this primitive.
func (r *RadioButtons) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
return r.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
x, y := event.Position()
_, rectY, _, _ := r.GetInnerRect()
if !r.InRect(x, y) {
return false, nil
}
if action == tview.MouseLeftClick {
setFocus(r)
index := y - rectY
if index >= 0 && index < len(r.options) {
r.currentOption = index
consumed = true
}
}
return
})
}
func main() {
radioButtons := NewRadioButtons([]string{"Lions", "Elephants", "Giraffes"})
radioButtons.SetBorder(true).
SetTitle("Radio Button Demo").
SetRect(0, 0, 30, 5)
if err := tview.NewApplication().SetRoot(radioButtons, false).Run(); err != nil {
if err := tview.NewApplication().SetRoot(radioButtons, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

3
doc.go
View File

@ -169,8 +169,7 @@ Type Hierarchy
All widgets listed above contain the Box type. All of Box's functions are
therefore available for all widgets, too.
All widgets also implement the Primitive interface. There is also the Focusable
interface which is used to override functions in subclassing types.
All widgets also implement the Primitive interface.
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).

View File

@ -103,8 +103,6 @@ func NewDropDown() *DropDown {
prefixTextColor: Styles.ContrastSecondaryTextColor,
}
d.focus = d
return d
}
@ -288,7 +286,7 @@ func (d *DropDown) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
// Draw draws this primitive onto the screen.
func (d *DropDown) Draw(screen tcell.Screen) {
d.Box.Draw(screen)
d.Box.DrawForSubclass(screen, d)
// Prepare.
x, y, width, height := d.GetInnerRect()
@ -340,7 +338,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
fieldWidth = rightLimit - x
}
fieldStyle := tcell.StyleDefault.Background(d.fieldBackgroundColor)
if d.GetFocusable().HasFocus() && !d.open {
if d.HasFocus() && !d.open {
fieldStyle = fieldStyle.Background(d.fieldTextColor)
}
for index := 0; index < fieldWidth; index++ {
@ -365,7 +363,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
text = d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix
}
// Just show the current selection.
if d.GetFocusable().HasFocus() && !d.open {
if d.HasFocus() && !d.open {
color = d.fieldBackgroundColor
}
Print(screen, text, x, y, fieldWidth, AlignLeft, color)
@ -397,7 +395,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
// If the list has focus, let it process its own key events.
if d.list.GetFocusable().HasFocus() {
if d.list.HasFocus() {
if handler := d.list.InputHandler(); handler != nil {
handler(event, setFocus)
}

View File

@ -53,7 +53,6 @@ func NewFlex() *Flex {
Box: NewBox().SetBackgroundColor(tcell.ColorDefault),
direction: FlexColumn,
}
f.focus = f
return f
}
@ -122,7 +121,7 @@ func (f *Flex) ResizeItem(p Primitive, fixedSize, proportion int) *Flex {
// Draw draws this primitive onto the screen.
func (f *Flex) Draw(screen tcell.Screen) {
f.Box.Draw(screen)
f.Box.DrawForSubclass(screen, f)
// Calculate size and position of the items.
@ -173,7 +172,7 @@ func (f *Flex) Draw(screen tcell.Screen) {
pos += size
if item.Item != nil {
if item.Item.GetFocusable().HasFocus() {
if item.Item.HasFocus() {
defer item.Item.Draw(screen)
} else {
item.Item.Draw(screen)
@ -195,7 +194,7 @@ func (f *Flex) Focus(delegate func(p Primitive)) {
// HasFocus returns whether or not this primitive has focus.
func (f *Flex) HasFocus() bool {
for _, item := range f.items {
if item.Item != nil && item.Item.GetFocusable().HasFocus() {
if item.Item != nil && item.Item.HasFocus() {
return true
}
}
@ -228,7 +227,7 @@ func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
func (f *Flex) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, item := range f.items {
if item.Item != nil && item.Item.GetFocusable().HasFocus() {
if item.Item != nil && item.Item.HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return

View File

@ -1,8 +0,0 @@
package tview
// Focusable provides a method which determines if a primitive has focus.
// Composed primitives may be focused based on the focused state of their
// contained primitives.
type Focusable interface {
HasFocus() bool
}

16
form.go
View File

@ -96,8 +96,6 @@ func NewForm() *Form {
buttonTextColor: Styles.PrimaryTextColor,
}
f.focus = f
return f
}
@ -363,7 +361,7 @@ func (f *Form) SetCancelFunc(callback func()) *Form {
// Draw draws this primitive onto the screen.
func (f *Form) Draw(screen tcell.Screen) {
f.Box.Draw(screen)
f.Box.DrawForSubclass(screen, f)
// Determine the actual item that has focus.
if index := f.focusIndex(); index >= 0 {
@ -430,7 +428,7 @@ func (f *Form) Draw(screen tcell.Screen) {
positions[index].y = y
positions[index].width = itemWidth
positions[index].height = 1
if item.GetFocusable().HasFocus() {
if item.HasFocus() {
focusedPosition = positions[index]
}
@ -524,7 +522,7 @@ func (f *Form) Draw(screen tcell.Screen) {
}
// Draw items with focus last (in case of overlaps).
if item.GetFocusable().HasFocus() {
if item.HasFocus() {
defer item.Draw(screen)
} else {
item.Draw(screen)
@ -608,12 +606,12 @@ func (f *Form) HasFocus() bool {
// has focus.
func (f *Form) focusIndex() int {
for index, item := range f.items {
if item.GetFocusable().HasFocus() {
if item.HasFocus() {
return index
}
}
for index, button := range f.buttons {
if button.focus.HasFocus() {
if button.HasFocus() {
return len(f.items) + index
}
}
@ -666,7 +664,7 @@ func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, item := range f.items {
if item != nil && item.GetFocusable().HasFocus() {
if item != nil && item.HasFocus() {
if handler := item.InputHandler(); handler != nil {
handler(event, setFocus)
return
@ -675,7 +673,7 @@ func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
}
for _, button := range f.buttons {
if button.GetFocusable().HasFocus() {
if button.HasFocus() {
if handler := button.InputHandler(); handler != nil {
handler(event, setFocus)
return

View File

@ -46,8 +46,6 @@ func NewFrame(primitive Primitive) *Frame {
right: 1,
}
f.focus = f
return f
}
@ -83,7 +81,7 @@ func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame
// Draw draws this primitive onto the screen.
func (f *Frame) Draw(screen tcell.Screen) {
f.Box.Draw(screen)
f.Box.DrawForSubclass(screen, f)
// Calculate start positions.
x, top, width, height := f.GetInnerRect()
@ -159,11 +157,7 @@ func (f *Frame) HasFocus() bool {
if f.primitive == nil {
return f.hasFocus
}
focusable, ok := f.primitive.(Focusable)
if ok {
return focusable.HasFocus()
}
return f.hasFocus
return f.primitive.HasFocus()
}
// MouseHandler returns the mouse handler for this primitive.
@ -188,7 +182,7 @@ func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primi
if f.primitive == nil {
return
}
if f.primitive.GetFocusable().HasFocus() {
if f.primitive.HasFocus() {
if handler := f.primitive.InputHandler(); handler != nil {
handler(event, setFocus)
return

View File

@ -71,7 +71,6 @@ func NewGrid() *Grid {
Box: NewBox().SetBackgroundColor(tcell.ColorDefault),
bordersColor: Styles.GraphicsColor,
}
g.focus = g
return g
}
@ -261,7 +260,7 @@ func (g *Grid) Blur() {
// HasFocus returns whether or not this primitive has focus.
func (g *Grid) HasFocus() bool {
for _, item := range g.items {
if item.visible && item.Item.GetFocusable().HasFocus() {
if item.visible && item.Item.HasFocus() {
return true
}
}
@ -274,7 +273,7 @@ func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
if !g.hasFocus {
// Pass event on to child primitive.
for _, item := range g.items {
if item != nil && item.Item.GetFocusable().HasFocus() {
if item != nil && item.Item.HasFocus() {
if handler := item.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return
@ -319,7 +318,7 @@ func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primit
// Draw draws this primitive onto the screen.
func (g *Grid) Draw(screen tcell.Screen) {
g.Box.Draw(screen)
g.Box.DrawForSubclass(screen, g)
x, y, width, height := g.GetInnerRect()
screenWidth, screenHeight := screen.Size()
@ -497,7 +496,7 @@ func (g *Grid) Draw(screen tcell.Screen) {
}
item.x, item.y, item.w, item.h = px, py, pw, ph
item.visible = true
if primitive.GetFocusable().HasFocus() {
if primitive.HasFocus() {
focus = item
}
}

View File

@ -303,7 +303,7 @@ func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
// Draw draws this primitive onto the screen.
func (i *InputField) Draw(screen tcell.Screen) {
i.Box.Draw(screen)
i.Box.DrawForSubclass(screen, i)
// Prepare
x, y, width, height := i.GetInnerRect()
@ -428,7 +428,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
}
// Set cursor.
if i.focus.HasFocus() {
if i.HasFocus() {
screen.ShowCursor(x+cursorScreenPos, y)
}
}

View File

@ -389,7 +389,7 @@ func (l *List) Clear() *List {
// Draw draws this primitive onto the screen.
func (l *List) Draw(screen tcell.Screen) {
l.Box.Draw(screen)
l.Box.DrawForSubclass(screen, l)
// Determine the dimensions.
x, y, width, height := l.GetInnerRect()

View File

@ -49,7 +49,6 @@ func NewModal() *Modal {
m.frame.SetBorder(true).
SetBackgroundColor(Styles.ContrastBackgroundColor).
SetBorderPadding(1, 1, 1, 1)
m.focus = m
return m
}
@ -192,7 +191,7 @@ func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
// InputHandler returns the handler for this primitive.
func (m *Modal) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return m.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
if m.frame.GetFocusable().HasFocus() {
if m.frame.HasFocus() {
if handler := m.frame.InputHandler(); handler != nil {
handler(event, setFocus)
return

View File

@ -37,7 +37,6 @@ func NewPages() *Pages {
p := &Pages{
Box: NewBox(),
}
p.focus = p
return p
}
@ -240,7 +239,7 @@ func (p *Pages) GetFrontPage() (name string, item Primitive) {
// HasFocus returns whether or not this primitive has focus.
func (p *Pages) HasFocus() bool {
for _, page := range p.pages {
if page.Item.GetFocusable().HasFocus() {
if page.Item.HasFocus() {
return true
}
}
@ -266,7 +265,7 @@ func (p *Pages) Focus(delegate func(p Primitive)) {
// Draw draws this primitive onto the screen.
func (p *Pages) Draw(screen tcell.Screen) {
p.Box.Draw(screen)
p.Box.DrawForSubclass(screen, p)
for _, page := range p.pages {
if !page.Visible {
continue
@ -305,7 +304,7 @@ func (p *Pages) MouseHandler() func(action MouseAction, event *tcell.EventMouse,
func (p *Pages) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
return p.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
for _, page := range p.pages {
if page.Item.GetFocusable().HasFocus() {
if page.Item.HasFocus() {
if handler := page.Item.InputHandler(); handler != nil {
handler(event, setFocus)
return

View File

@ -38,12 +38,13 @@ type Primitive interface {
// Implementers may call delegate() to pass the focus on to another primitive.
Focus(delegate func(p Primitive))
// HasFocus determines if the primitive has focus. This function must return
// true also if one of this primitive's child elements has focus.
HasFocus() bool
// Blur is called by the application when the primitive loses focus.
Blur()
// GetFocusable returns the item's Focusable.
GetFocusable() Focusable
// MouseHandler returns a handler which receives mouse events.
// It is called by the Application class.
//

View File

@ -623,7 +623,7 @@ func (t *Table) ScrollToEnd() *Table {
// Draw draws this primitive onto the screen.
func (t *Table) Draw(screen tcell.Screen) {
t.Box.Draw(screen)
t.Box.DrawForSubclass(screen, t)
// What's our available screen space?
_, totalHeight := screen.Size()

View File

@ -845,9 +845,9 @@ func (t *TextView) reindexBuffer(width int) {
// Draw draws this primitive onto the screen.
func (t *TextView) Draw(screen tcell.Screen) {
t.Box.DrawForSubclass(screen, t)
t.Lock()
defer t.Unlock()
t.Box.Draw(screen)
totalWidth, totalHeight := screen.Size()
// Get the available size.

View File

@ -571,7 +571,7 @@ func (t *TreeView) process() {
// Draw draws this primitive onto the screen.
func (t *TreeView) Draw(screen tcell.Screen) {
t.Box.Draw(screen)
t.Box.DrawForSubclass(screen, t)
if t.root == nil {
return
}