1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-24 13:48:51 +08:00

fixes #74 Make Application an object/class

fixes #73 Views package has many go lint issues
This commit is contained in:
Garrett D'Amore 2015-11-04 11:24:00 -08:00
parent ed2fa2d51c
commit b727b9f424
14 changed files with 255 additions and 136 deletions

View File

@ -22,6 +22,9 @@ import (
"github.com/gdamore/tcell/views"
)
var app = &views.Application{}
var window = &mainWindow{}
type model struct {
x int
y int
@ -97,7 +100,7 @@ func (m *model) GetCell(x, y int) (rune, tcell.Style, []rune, int) {
return ch, style, nil, 1
}
type MyApp struct {
type mainWindow struct {
view views.View
main *views.CellView
keybar *views.SimpleStyledText
@ -107,18 +110,18 @@ type MyApp struct {
views.Panel
}
func (a *MyApp) HandleEvent(ev tcell.Event) bool {
func (a *mainWindow) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventKey:
switch ev.Key() {
case tcell.KeyCtrlL:
views.AppRedraw()
app.Refresh()
return true
case tcell.KeyRune:
switch ev.Rune() {
case 'Q', 'q':
views.AppQuit()
app.Quit()
return true
case 'S', 's':
a.model.hide = false
@ -142,12 +145,12 @@ func (a *MyApp) HandleEvent(ev tcell.Event) bool {
return a.Panel.HandleEvent(ev)
}
func (a *MyApp) Draw() {
func (a *mainWindow) Draw() {
a.status.SetMarkup(a.model.loc)
a.Panel.Draw()
}
func (a *MyApp) updateKeys() {
func (a *mainWindow) updateKeys() {
m := a.model
w := "[%AQ%N] Quit"
if !m.enab {
@ -161,19 +164,12 @@ func (a *MyApp) updateKeys() {
}
}
a.keybar.SetMarkup(w)
views.AppDraw()
app.Update()
}
func main() {
app := &MyApp{}
app.model = &model{endx: 60, endy: 15}
if e := views.AppInit(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
window.model = &model{endx: 60, endy: 15}
title := views.NewTextBar()
title.SetStyle(tcell.StyleDefault.
@ -182,35 +178,39 @@ func main() {
title.SetCenter("CellView Test", tcell.StyleDefault)
title.SetRight("Example v1.0", tcell.StyleDefault)
app.keybar = views.NewSimpleStyledText()
app.keybar.SetStyleN(tcell.StyleDefault.
window.keybar = views.NewSimpleStyledText()
window.keybar.SetStyleN(tcell.StyleDefault.
Background(tcell.ColorSilver).
Foreground(tcell.ColorBlack))
app.keybar.SetStyleA(tcell.StyleDefault.
window.keybar.SetStyleA(tcell.StyleDefault.
Background(tcell.ColorSilver).
Foreground(tcell.ColorRed))
app.keybar.SetMarkup("[%AQ%N] Quit")
window.keybar.SetMarkup("[%AQ%N] Quit")
app.status = views.NewSimpleStyledText()
app.status.SetStyleN(tcell.StyleDefault.
window.status = views.NewSimpleStyledText()
window.status.SetStyleN(tcell.StyleDefault.
Background(tcell.ColorYellow).
Foreground(tcell.ColorBlack))
app.status.SetMarkup("My status is here.")
window.status.SetMarkup("My status is here.")
app.main = views.NewCellView()
app.main.SetModel(app.model)
app.main.SetStyle(tcell.StyleDefault.
window.main = views.NewCellView()
window.main.SetModel(window.model)
window.main.SetStyle(tcell.StyleDefault.
Background(tcell.ColorBlack))
app.SetMenu(app.keybar)
app.SetTitle(title)
app.SetContent(app.main)
app.SetStatus(app.status)
window.SetMenu(window.keybar)
window.SetTitle(title)
window.SetContent(window.main)
window.SetStatus(window.status)
app.updateKeys()
views.AppSetStyle(tcell.StyleDefault.
window.updateKeys()
app.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorWhite).
Background(tcell.ColorBlack))
views.SetApplication(app)
views.RunApplication()
app.SetRootWidget(window)
if e := app.Run(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
}

View File

@ -22,15 +22,18 @@ import (
"github.com/gdamore/tcell/views"
)
type MyBox struct {
type boxL struct {
views.BoxLayout
}
func (m *MyBox) HandleEvent(ev tcell.Event) bool {
var app = &views.Application{}
var box = &boxL{}
func (m *boxL) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
views.AppQuit()
app.Quit()
return true
}
}
@ -39,14 +42,6 @@ func (m *MyBox) HandleEvent(ev tcell.Event) bool {
func main() {
if e := views.AppInit(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
outer := &MyBox{}
outer.SetOrientation(views.Vertical)
title := &views.TextBar{}
title.SetStyle(tcell.StyleDefault.
Background(tcell.ColorYellow).
@ -57,7 +52,7 @@ func main() {
Foreground(tcell.ColorWhite))
title.SetRight("==>X", tcell.StyleDefault)
box := views.NewBoxLayout(views.Horizontal)
inner := views.NewBoxLayout(views.Horizontal)
l := views.NewText()
m := views.NewText()
@ -76,12 +71,16 @@ func main() {
m.SetAlignment(views.AlignMiddle)
r.SetAlignment(views.AlignEnd)
box.AddWidget(l, 0)
box.AddWidget(m, 0.7)
box.AddWidget(r, 0.3)
inner.AddWidget(l, 0)
inner.AddWidget(m, 0.7)
inner.AddWidget(r, 0.3)
outer.AddWidget(title, 0)
outer.AddWidget(box, 1)
views.SetApplication(outer)
views.RunApplication()
box.SetOrientation(views.Vertical)
box.AddWidget(title, 0)
box.AddWidget(inner, 1)
app.SetRootWidget(box)
if e := app.Run(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
}

View File

@ -22,15 +22,18 @@ import (
"github.com/gdamore/tcell/views"
)
type MyBox struct {
type boxL struct {
views.BoxLayout
}
func (m *MyBox) HandleEvent(ev tcell.Event) bool {
var box = &boxL{}
var app = &views.Application{}
func (m *boxL) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape {
views.AppQuit()
app.Quit()
return true
}
}
@ -39,14 +42,6 @@ func (m *MyBox) HandleEvent(ev tcell.Event) bool {
func main() {
if e := views.AppInit(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
box := &MyBox{}
box.SetOrientation(views.Vertical)
title := views.NewTextBar()
title.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorBlack).
@ -70,11 +65,15 @@ func main() {
mid.SetAlignment(views.VAlignCenter | views.HAlignCenter)
bot.SetAlignment(views.VAlignBottom | views.HAlignLeft)
box.SetOrientation(views.Vertical)
box.AddWidget(title, 0)
box.AddWidget(top, 0)
box.AddWidget(mid, 0.7)
box.AddWidget(bot, 0.3)
views.SetApplication(box)
views.RunApplication()
app.SetRootWidget(box)
if e := app.Run(); e != nil {
fmt.Fprintln(os.Stderr, e.Error())
os.Exit(1)
}
}

View File

@ -15,117 +15,167 @@
package views
import (
"sync"
"github.com/gdamore/tcell"
)
var appWidget Widget
var appScreen tcell.Screen
func SetApplication(app Widget) {
appWidget = app
// Application represents an event-driven application running on a screen.
type Application struct {
widget Widget
screen tcell.Screen
style tcell.Style
err error
wg sync.WaitGroup
}
func AppInit() error {
if appScreen == nil {
if scr, e := tcell.NewScreen(); e != nil {
return e
} else {
appScreen = scr
// SetRootWidget sets the primary (root, main) Widget to be displayed.
func (app *Application) SetRootWidget(widget Widget) {
app.widget = widget
}
// initialize initializes the application. It will normally attempt to
// allocate a default screen if one is not already established.
func (app *Application) initialize() error {
if app.screen == nil {
if app.screen, app.err = tcell.NewScreen(); app.err != nil {
return app.err
}
app.screen.SetStyle(app.style)
}
return nil
}
// AppQuit causes the application to shutdown gracefully.
func AppQuit() {
// Quit causes the application to shutdown gracefully. It does not wait
// for the application to exit, but returns immediately.
func (app *Application) Quit() {
ev := &eventAppQuit{}
ev.SetEventNow()
if appScreen != nil {
go func() { appScreen.PostEventWait(ev) }()
if scr := app.screen; scr != nil {
go func() { scr.PostEventWait(ev) }()
}
}
// AppRedraw causes the application forcibly redraw everything. Use this
// Refresh causes the application forcibly redraw everything. Use this
// to clear up screen corruption, etc.
func AppRedraw() {
ev := &eventAppRedraw{}
func (app *Application) Refresh() {
ev := &eventAppRefresh{}
ev.SetEventNow()
if appScreen != nil {
go func() { appScreen.PostEventWait(ev) }()
if scr := app.screen; scr != nil {
go func() { scr.PostEventWait(ev) }()
}
}
// AppDraw asks the application to redraw itself, but only parts of it that
// are changed are updated.
func AppDraw() {
ev := &eventAppDraw{}
// Update asks the application to draw any screen updates that have not
// been drawn yet.
func (app *Application) Update() {
ev := &eventAppUpdate{}
ev.SetEventNow()
if appScreen != nil {
go func() { appScreen.PostEventWait(ev) }()
if scr := app.screen; scr != nil {
go func() { scr.PostEventWait(ev) }()
}
}
// AppPostFunc posts a function to be executed in the context of the
// PostFunc posts a function to be executed in the context of the
// application's event loop. Functions that need to update displayed
// state, etc. can do this to avoid holding locks.
func AppPostFunc(fn func()) {
func (app *Application) PostFunc(fn func()) {
ev := &eventAppFunc{fn: fn}
ev.SetEventNow()
if appScreen != nil {
go func() { appScreen.PostEventWait(ev) }()
if scr := app.screen; scr != nil {
go func() { scr.PostEventWait(ev) }()
}
}
func SetScreen(scr tcell.Screen) {
appScreen = scr
}
func AppSetStyle(style tcell.Style) {
if appScreen != nil {
appScreen.SetStyle(style)
// SetScreen sets the screen to use for the application. This must be
// done before the application starts to run or is initialized.
func (app *Application) SetScreen(scr tcell.Screen) {
if app.screen == nil {
app.screen = scr
app.err = nil
}
}
func RunApplication() {
// SetStyle sets the default style (background) to be used for Widgets
// that have not specified any other style.
func (app *Application) SetStyle(style tcell.Style) {
app.style = style
if app.screen != nil {
app.screen.SetStyle(style)
}
}
if appScreen == nil {
func (app *Application) run() {
screen := app.screen
widget := app.widget
if widget == nil {
app.wg.Done()
return
}
if appWidget == nil {
return
if screen == nil {
if e := app.initialize(); e != nil {
app.wg.Done()
return
}
screen = app.screen
}
scr := appScreen
scr.Init()
scr.Clear()
appWidget.SetView(appScreen)
screen.Init()
screen.Clear()
widget.SetView(screen)
loop:
for {
appWidget.Draw()
scr.Show()
if widget = app.widget; widget == nil {
break
}
widget.Draw()
screen.Show()
ev := scr.PollEvent()
ev := screen.PollEvent()
switch nev := ev.(type) {
case *eventAppQuit:
break loop
case *eventAppDraw:
scr.Show()
case *eventAppRedraw:
scr.Sync()
case *eventAppUpdate:
screen.Show()
case *eventAppRefresh:
screen.Sync()
case *eventAppFunc:
nev.fn()
case *tcell.EventResize:
scr.Sync()
appWidget.Resize()
screen.Sync()
widget.Resize()
default:
appWidget.HandleEvent(ev)
widget.HandleEvent(ev)
}
}
scr.Fini()
screen.Fini()
app.wg.Done()
}
type eventAppDraw struct {
// Start starts the application loop, initializing the screen
// and starting the Event loop. The application will run in a goroutine
// until Quit is called.
func (app *Application) Start() {
app.wg.Add(1)
go app.run()
}
// Wait waits until the application finishes.
func (app *Application) Wait() error {
app.wg.Wait()
return app.err
}
// Run runs the application, waiting until the application loop exits.
// It is equivalent to app.Start() followed by app.Wait()
func (app *Application) Run() error {
app.Start()
return app.Wait()
}
type eventAppUpdate struct {
tcell.EventTime
}
@ -133,7 +183,7 @@ type eventAppQuit struct {
tcell.EventTime
}
type eventAppRedraw struct {
type eventAppRefresh struct {
tcell.EventTime
}

View File

@ -95,7 +95,7 @@ func (b *BoxLayout) layout() {
// the cells with the highest residual fraction. It should be
// the case that no single cell gets more than one more cell.
for resid > 0 {
var best *boxLayoutCell = nil
var best *boxLayoutCell
for _, c := range b.cells {
if c.fill == 0 {
continue
@ -135,6 +135,7 @@ func (b *BoxLayout) layout() {
b.changed = false
}
// Resize adjusts the layout when the underlying View changes size.
func (b *BoxLayout) Resize() {
b.layout()
@ -199,7 +200,7 @@ func (b *BoxLayout) HandleEvent(ev tcell.Event) bool {
return false
}
// Add() adds a widget to the end of the BoxLayout.
// AddWidget adds a widget to the end of the BoxLayout.
func (b *BoxLayout) AddWidget(widget Widget, fill float64) {
c := &boxLayoutCell{
widget: widget,
@ -238,6 +239,7 @@ func (b *BoxLayout) InsertWidget(index int, widget Widget, fill float64) {
b.PostEventWidgetContent(b)
}
// RemoveWidget removes a Widget from the layout.
func (b *BoxLayout) RemoveWidget(widget Widget) {
for i := 0; i < len(b.cells); i++ {
if b.cells[i].widget == widget {
@ -251,6 +253,7 @@ func (b *BoxLayout) RemoveWidget(widget Widget) {
b.PostEventWidgetContent(b)
}
// Widgets returns the list of Widgets for this BoxLayout.
func (b *BoxLayout) Widgets() []Widget {
w := make([]Widget, 0, len(b.cells))
for _, c := range b.cells {
@ -259,6 +262,7 @@ func (b *BoxLayout) Widgets() []Widget {
return w
}
// SetOrientation sets the orientation as either Horizontal or Vertical.
func (b *BoxLayout) SetOrientation(orient Orientation) {
if b.orient != orient {
b.orient = orient

View File

@ -28,7 +28,7 @@ type CellModel interface {
MoveCursor(offx, offy int)
}
// A CellView is a flexible view of a CellModel, offering both cursor
// CellView is a flexible view of a CellModel, offering both cursor
// management and a panning.
type CellView struct {
port *ViewPort
@ -44,6 +44,7 @@ type CellView struct {
WidgetWatchers
}
// Draw draws the content.
func (a *CellView) Draw() {
port := a.port
@ -125,6 +126,8 @@ func (a *CellView) keyRight() {
a.MakeCursorVisible()
}
// MakeCursorVisible ensures that the cursor is visible, panning the ViewPort
// as necessary, if the cursor is enabled.
func (a *CellView) MakeCursorVisible() {
if a.model == nil {
return
@ -135,6 +138,8 @@ func (a *CellView) MakeCursorVisible() {
}
}
// HandleEvent handles events. In particular, it handles certain key events
// to move the cursor or pan the view.
func (a *CellView) HandleEvent(e tcell.Event) bool {
if a.model == nil {
return false
@ -173,6 +178,7 @@ func (a *CellView) Size() (int, int) {
return w, h
}
// SetModel sets the model for this CellView.
func (a *CellView) SetModel(model CellModel) {
w, h := model.GetBounds()
model.SetCursor(0, 0)
@ -181,6 +187,7 @@ func (a *CellView) SetModel(model CellModel) {
a.port.ValidateView()
}
// SetView sets the View context.
func (a *CellView) SetView(view View) {
port := a.port
port.SetView(view)
@ -197,6 +204,8 @@ func (a *CellView) SetView(view View) {
a.Resize()
}
// Resize is called when the View is resized. It will ensure that the
// cursor is visible, if present.
func (a *CellView) Resize() {
// We might want to reflow text
width, height := a.view.Size()
@ -205,20 +214,25 @@ func (a *CellView) Resize() {
a.MakeCursorVisible()
}
// SetCursor sets the the cursor position.
func (a *CellView) SetCursor(x, y int) {
a.cursorX = x
a.cursorY = y
a.model.SetCursor(x, y)
}
// SetCursorX sets the the cursor column.
func (a *CellView) SetCursorX(x int) {
a.SetCursor(x, a.cursorY)
}
// SetCursorY sets the the cursor row.
func (a *CellView) SetCursorY(y int) {
a.SetCursor(a.cursorX, y)
}
// MakeVisible makes the given coordinates visible, if they are not already.
// It does this by moving the ViewPort for the CellView.
func (a *CellView) MakeVisible(x, y int) {
a.port.MakeVisible(x, y)
}
@ -228,6 +242,7 @@ func (a *CellView) SetStyle(s tcell.Style) {
a.style = s
}
// NewCellView creates a CellView.
func NewCellView() *CellView {
return &CellView{
port: NewViewPort(nil, 0, 0, 0, 0),

View File

@ -14,25 +14,47 @@
package views
// Alignment represents the alignment of an object, and consists of
// either or both of horizontal and vertical alignment.
type Alignment int
const (
// HAlignLeft indicates alignment on the left edge.
HAlignLeft Alignment = 1 << iota
// HAlignCenter indicates horizontally centered.
HAlignCenter
// HAlignRight indicates alignment on the right edge.
HAlignRight
// VAlignTop indicates alignment on the top edge.
VAlignTop
// VAlignCenter indicates vertically centered.
VAlignCenter
// VAlignBottom indicates alignment on the bottom edge.
VAlignBottom
)
const (
AlignBegin = HAlignLeft | VAlignTop
AlignEnd = HAlignRight | VAlignBottom
// AlignBegin indicates alignment at the top left corner.
AlignBegin = HAlignLeft | VAlignTop
// AlignEnd indicates alignment at the bottom right corner.
AlignEnd = HAlignRight | VAlignBottom
// AlignMiddle indicates full centering.
AlignMiddle = HAlignCenter | VAlignCenter
)
// Orientation represents the direction of a widget or layout.
type Orientation int
const (
// Horizontal indicates left to right orientation.
Horizontal = iota
// Vertical indicates top to bottom orientation.
Vertical
)

View File

@ -40,11 +40,13 @@ type Panel struct {
BoxLayout
}
// Draw draws the Panel.
func (p *Panel) Draw() {
p.BoxLayout.SetOrientation(Vertical)
p.BoxLayout.Draw()
}
// SetTitle sets the Widget to display in the title area.
func (p *Panel) SetTitle(w Widget) {
if p.title != nil {
p.RemoveWidget(p.title)
@ -53,6 +55,8 @@ func (p *Panel) SetTitle(w Widget) {
p.title = w
}
// SetMenu sets the Widget to display in the menu area, which is
// just below the title.
func (p *Panel) SetMenu(w Widget) {
index := 0
if p.title != nil {
@ -65,6 +69,7 @@ func (p *Panel) SetMenu(w Widget) {
p.menu = w
}
// SetContent sets the Widget to display in the content area.
func (p *Panel) SetContent(w Widget) {
index := 0
if p.title != nil {
@ -80,6 +85,8 @@ func (p *Panel) SetContent(w Widget) {
p.content = w
}
// SetStatus sets the Widget to display in the status area, which is at
// the bottom of the panel.
func (p *Panel) SetStatus(w Widget) {
index := 0
if p.title != nil {
@ -98,6 +105,7 @@ func (p *Panel) SetStatus(w Widget) {
p.status = w
}
// NewPanel creates a new Panel. A zero valued panel can be created too.
func NewPanel() *Panel {
return &Panel{}
}

View File

@ -96,7 +96,7 @@ func (t *SimpleStyledText) SetMarkup(s string) {
t.markup = markup
}
// SimpleStyledText returns the text that was set, including markup.
// Markup returns the text that was set, including markup.
func (t *SimpleStyledText) Markup() string {
return string(t.markup)
}

View File

@ -18,9 +18,9 @@ import (
"github.com/gdamore/tcell"
)
// Spacer is a Widget that occupies no visible real-estate. It is useful to add this
// to layouts when expansion space is required - its blank filler.
// Spacer is a Widget that occupies no visible real-estate. It is useful to
// add this to layouts when expansion space is required. It expands as needed
// with blank space.
type Spacer struct {
WidgetWatchers
}
@ -28,6 +28,7 @@ type Spacer struct {
// Draw is called to update the displayed content.
func (*Spacer) Draw() {}
// Size always returns 0, 0, since no size is ever *requird* to display nothing.
func (*Spacer) Size() (int, int) {
return 0, 0
}
@ -45,7 +46,8 @@ func (s *Spacer) Resize() {
s.PostEventWidgetResize(s)
}
// NewSpacer creates an empty Spacer. Its probably easier just to declare it directly.
// NewSpacer creates an empty Spacer. It's probably easier just to declare it
// directly.
func NewSpacer() *Spacer {
return &Spacer{}
}

View File

@ -36,7 +36,7 @@ type Text struct {
WidgetWatchers
}
// Draw is called to update the displayed content.
// Draw draws the Text.
func (t *Text) Draw() {
v := t.view
if v == nil {
@ -115,6 +115,7 @@ func (t *Text) Draw() {
}
}
// Size returns the width and height in character cells of the Text.
func (t *Text) Size() (int, int) {
if len(t.text) != 0 {
return t.width, t.height
@ -132,6 +133,7 @@ func (t *Text) SetAlignment(align Alignment) {
}
}
// Alignment returns the alignment of the Text.
func (t *Text) Alignment() Alignment {
return t.align
}

View File

@ -20,6 +20,11 @@ import (
"github.com/gdamore/tcell"
)
// TextArea is a pannable 2 dimensional text widget. It wraps both
// the view and the model for text in a single, convenient widget.
// Text is provided as an array of strings, each of which represents
// a single line to display. All text in the TextArea has the same
// style. An optional soft cursor is available.
type TextArea struct {
view *CellView
model *linesModel
@ -78,6 +83,7 @@ func (m *linesModel) GetCursor() (int, int, bool, bool) {
return m.x, m.y, m.cursor, !m.hide
}
// SetLines sets the content text to display.
func (ta *TextArea) SetLines(lines []string) {
m := ta.model
m.width = 0
@ -91,35 +97,46 @@ func (ta *TextArea) SetLines(lines []string) {
ta.view.SetModel(m)
}
// EnableCursor enables a soft cursor in the TextArea.
func (ta *TextArea) EnableCursor(on bool) {
ta.model.cursor = on
}
// HideCursor hides or shows the cursor in the TextArea.
// If on is true, the cursor is hidden. Note that a cursor is only
// shown if it is enabled.
func (ta *TextArea) HideCursor(on bool) {
ta.model.hide = on
}
// Draw draws the TextArea.
func (ta *TextArea) Draw() {
ta.view.Draw()
}
// HandleEvent handles any events.
func (ta *TextArea) HandleEvent(ev tcell.Event) bool {
return ta.view.HandleEvent(ev)
}
// Resize is called when the drawing context (View) changes size.
func (ta *TextArea) Resize() {
ta.view.Resize()
}
// SetView sets the drawing context.
func (ta *TextArea) SetView(view View) {
ta.view.SetView(view)
}
// SetContent is used to set the textual content, passed as a
// single string. Lines within the string are delimited by newlines.
func (ta *TextArea) SetContent(text string) {
lines := strings.Split(strings.Trim(text, "\n"), "\n")
ta.SetLines(lines)
}
// NewTextArea creates a blank TextArea.
func NewTextArea() *TextArea {
lm := &linesModel{lines: []string{}, width: 0}
ta := &TextArea{model: lm}

View File

@ -116,7 +116,7 @@ func (v *ViewPort) SetContent(x, y int, ch rune, comb []rune, s tcell.Style) {
v.v.SetContent(x-v.viewx+v.physx, y-v.viewy+v.physy, ch, comb, s)
}
// This moves the ViewPort the minimum necessary to make the given
// MakeVisible moves the ViewPort the minimum necessary to make the given
// point visible. This should be called before any content is changed with
// SetContent, since otherwise it may be possible to move the location onto
// a region whose contents have been discarded.

View File

@ -62,6 +62,7 @@ type Widget interface {
Unwatch(handler tcell.EventHandler)
}
// EventWidget is an event delivered by a specific widget.
type EventWidget interface {
Widget() Widget
tcell.Event