mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-01 00:33:05 +08:00
Use a more sophisticated algorithm to distribute widget heights.
This is needed since there can now be an arbitrary number of widgets competing for vertical space. - Add a new MaxHeight method to the tk.Widget interface to provide hint on how to distribute the height. - Implement the algorithm in distributeHeight in cli/app.go.
This commit is contained in:
parent
db0b88f664
commit
d2936c06a1
|
@ -4,6 +4,7 @@ package cli
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
@ -259,7 +260,7 @@ func (a *app) redraw(flag redrawFlag) {
|
||||||
if hideRPrompt {
|
if hideRPrompt {
|
||||||
a.codeArea.MutateState(func(s *tk.CodeAreaState) { s.HideRPrompt = true })
|
a.codeArea.MutateState(func(s *tk.CodeAreaState) { s.HideRPrompt = true })
|
||||||
}
|
}
|
||||||
bufMain := renderApp(a.codeArea, nil /* addon */, width, height)
|
bufMain := renderApp([]tk.Widget{a.codeArea /* no addon */}, width, height)
|
||||||
if hideRPrompt {
|
if hideRPrompt {
|
||||||
a.codeArea.MutateState(func(s *tk.CodeAreaState) { s.HideRPrompt = false })
|
a.codeArea.MutateState(func(s *tk.CodeAreaState) { s.HideRPrompt = false })
|
||||||
}
|
}
|
||||||
|
@ -269,7 +270,7 @@ func (a *app) redraw(flag redrawFlag) {
|
||||||
a.TTY.UpdateBuffer(bufNotes, bufMain, flag&fullRedraw != 0)
|
a.TTY.UpdateBuffer(bufNotes, bufMain, flag&fullRedraw != 0)
|
||||||
a.TTY.ResetBuffer()
|
a.TTY.ResetBuffer()
|
||||||
} else {
|
} else {
|
||||||
bufMain := renderApp(a.codeArea, addons, width, height)
|
bufMain := renderApp(append([]tk.Widget{a.codeArea}, addons...), width, height)
|
||||||
a.TTY.UpdateBuffer(bufNotes, bufMain, flag&fullRedraw != 0)
|
a.TTY.UpdateBuffer(bufNotes, bufMain, flag&fullRedraw != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,18 +292,95 @@ func renderNotes(notes []string, width int) *term.Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders the codearea, and uses the rest of the height for the listing.
|
// Renders the codearea, and uses the rest of the height for the listing.
|
||||||
func renderApp(codeArea tk.Renderer, addons []tk.Widget, width, height int) *term.Buffer {
|
func renderApp(widgets []tk.Widget, width, height int) *term.Buffer {
|
||||||
buf := codeArea.Render(width, height)
|
heights, focus := distributeHeight(widgets, width, height)
|
||||||
for _, w := range addons {
|
var buf *term.Buffer
|
||||||
if len(buf.Lines) >= height {
|
for i, w := range widgets {
|
||||||
break
|
if heights[i] == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf2 := w.Render(width, heights[i])
|
||||||
|
if buf == nil {
|
||||||
|
buf = buf2
|
||||||
|
} else {
|
||||||
|
buf.Extend(buf2, i == focus)
|
||||||
}
|
}
|
||||||
bufListing := w.Render(width, height-len(buf.Lines))
|
|
||||||
buf.Extend(bufListing, hasFocus(w))
|
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distributes the height among all the widgets. Returns the height for each
|
||||||
|
// widget, and the index of the widget currently focused.
|
||||||
|
func distributeHeight(widgets []tk.Widget, width, height int) ([]int, int) {
|
||||||
|
var focus int
|
||||||
|
for i, w := range widgets {
|
||||||
|
if hasFocus(w) {
|
||||||
|
focus = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := len(widgets)
|
||||||
|
heights := make([]int, n)
|
||||||
|
if height <= n {
|
||||||
|
// Not enough (or just enough) height to render every widget with a
|
||||||
|
// height of 1.
|
||||||
|
remain := height
|
||||||
|
// Start from the focused widget, and extend downwards as much as
|
||||||
|
// possible.
|
||||||
|
for i := focus; i < n && remain > 0; i++ {
|
||||||
|
heights[i] = 1
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
// If there is still space remaining, start from the focused widget
|
||||||
|
// again, and extend upwards as much as possible.
|
||||||
|
for i := focus - 1; i >= 0 && remain > 0; i-- {
|
||||||
|
heights[i] = 1
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
return heights, focus
|
||||||
|
}
|
||||||
|
|
||||||
|
maxHeights := make([]int, n)
|
||||||
|
for i, w := range widgets {
|
||||||
|
maxHeights[i] = w.MaxHeight(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The algorithm below achieves the following goals:
|
||||||
|
//
|
||||||
|
// 1. If maxHeights[u] > maxHeights[v], heights[u] >= heights[v];
|
||||||
|
//
|
||||||
|
// 2. While achieving goal 1, have as many widgets u s.t. heights[u] ==
|
||||||
|
// maxHeights[u].
|
||||||
|
//
|
||||||
|
// This is done by allocating the height among the widgets following an
|
||||||
|
// non-decreasing order of maxHeights. At each step:
|
||||||
|
//
|
||||||
|
// - If it's possible to allocate maxHeights[u] to all remaining widgets,
|
||||||
|
// then allocate maxHeights[u] to widget u;
|
||||||
|
//
|
||||||
|
// - If not, allocate the remaining budget evenly - rounding down at each
|
||||||
|
// step, so the widgets with smaller maxHeights gets smaller heights.
|
||||||
|
|
||||||
|
indices := make([]int, n)
|
||||||
|
for i := range indices {
|
||||||
|
indices[i] = i
|
||||||
|
}
|
||||||
|
sort.Slice(indices, func(i, j int) bool {
|
||||||
|
return maxHeights[indices[i]] < maxHeights[indices[j]]
|
||||||
|
})
|
||||||
|
|
||||||
|
remain := height
|
||||||
|
for rank, idx := range indices {
|
||||||
|
if remain >= maxHeights[idx] {
|
||||||
|
heights[idx] = maxHeights[idx]
|
||||||
|
} else {
|
||||||
|
heights[idx] = remain / (n - rank)
|
||||||
|
}
|
||||||
|
remain -= heights[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
return heights, focus
|
||||||
|
}
|
||||||
|
|
||||||
func hasFocus(w interface{}) bool {
|
func hasFocus(w interface{}) bool {
|
||||||
if f, ok := w.(interface{ Focus() bool }); ok {
|
if f, ok := w.(interface{ Focus() bool }); ok {
|
||||||
return f.Focus()
|
return f.Focus()
|
||||||
|
|
|
@ -416,8 +416,9 @@ func TestReadCode_HidesAddonsWhenNotEnoughSpace(t *testing.T) {
|
||||||
})
|
})
|
||||||
defer f.Stop()
|
defer f.Stop()
|
||||||
|
|
||||||
f.TestTTY(t, "\n",
|
f.TestTTY(t,
|
||||||
term.DotHere, "addon1> ")
|
"addon1> \n",
|
||||||
|
term.DotHere, "addon2> ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type testAddon struct {
|
type testAddon struct {
|
||||||
|
|
|
@ -37,13 +37,21 @@ type histwalk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *histwalk) Render(width, height int) *term.Buffer {
|
func (w *histwalk) Render(width, height int) *term.Buffer {
|
||||||
cmd, _ := w.cursor.Get()
|
buf := w.render(width)
|
||||||
content := modeLine(fmt.Sprintf(" HISTORY #%d ", cmd.Seq), false)
|
|
||||||
buf := term.NewBufferBuilder(width).WriteStyled(content).Buffer()
|
|
||||||
buf.TrimToLines(0, height)
|
buf.TrimToLines(0, height)
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *histwalk) MaxHeight(width, height int) int {
|
||||||
|
return len(w.render(width).Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *histwalk) render(width int) *term.Buffer {
|
||||||
|
cmd, _ := w.cursor.Get()
|
||||||
|
content := modeLine(fmt.Sprintf(" HISTORY #%d ", cmd.Seq), false)
|
||||||
|
return term.NewBufferBuilder(width).WriteStyled(content).Buffer()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *histwalk) Handle(event term.Event) bool {
|
func (w *histwalk) Handle(event term.Event) bool {
|
||||||
handled := w.Bindings.Handle(w, event)
|
handled := w.Bindings.Handle(w, event)
|
||||||
if handled {
|
if handled {
|
||||||
|
|
|
@ -32,18 +32,26 @@ type instant struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *instant) Render(width, height int) *term.Buffer {
|
func (w *instant) Render(width, height int) *term.Buffer {
|
||||||
|
buf := w.render(width, height)
|
||||||
|
buf.TrimToLines(0, height)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *instant) MaxHeight(width, height int) int {
|
||||||
|
return len(w.render(width, height).Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *instant) render(width, height int) *term.Buffer {
|
||||||
bb := term.NewBufferBuilder(width).
|
bb := term.NewBufferBuilder(width).
|
||||||
WriteStyled(modeLine(" INSTANT ", false)).SetDotHere()
|
WriteStyled(modeLine(" INSTANT ", false)).SetDotHere()
|
||||||
if w.lastErr != nil {
|
if w.lastErr != nil {
|
||||||
bb.Newline().Write(w.lastErr.Error(), ui.FgRed)
|
bb.Newline().Write(w.lastErr.Error(), ui.FgRed)
|
||||||
}
|
}
|
||||||
buf := bb.Buffer()
|
buf := bb.Buffer()
|
||||||
if len(buf.Lines) >= height {
|
if len(buf.Lines) < height {
|
||||||
buf.TrimToLines(0, height)
|
bufTextView := w.textView.Render(width, height-len(buf.Lines))
|
||||||
return buf
|
buf.Extend(bufTextView, false)
|
||||||
}
|
}
|
||||||
bufTextView := w.textView.Render(width, height-len(buf.Lines))
|
|
||||||
buf.Extend(bufTextView, false)
|
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,10 @@ func (w *navigation) Render(width, height int) *term.Buffer {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *navigation) MaxHeight(width, height int) int {
|
||||||
|
return w.codeArea.MaxHeight(width, height) + w.colView.MaxHeight(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *navigation) Focus() bool {
|
func (w *navigation) Focus() bool {
|
||||||
return w.CopyState().Filtering
|
return w.CopyState().Filtering
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,10 +192,8 @@ func testNavigation(t *testing.T, c NavigationCursor) {
|
||||||
" ++++++++++++++ -",
|
" ++++++++++++++ -",
|
||||||
" d d2 │\n", Styles,
|
" d d2 │\n", Styles,
|
||||||
"#### ////////////// -",
|
"#### ////////////// -",
|
||||||
" f d3 \n", Styles,
|
" f d3 ", Styles,
|
||||||
" ////////////// X",
|
" ////////////// X",
|
||||||
" ", Styles,
|
|
||||||
" X",
|
|
||||||
)
|
)
|
||||||
f.TTY.TestBuffer(t, d1Buf2)
|
f.TTY.TestBuffer(t, d1Buf2)
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,20 @@ type stub struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w stub) Render(width, height int) *term.Buffer {
|
func (w stub) Render(width, height int) *term.Buffer {
|
||||||
buf := term.NewBufferBuilder(width).
|
buf := w.render(width)
|
||||||
WriteStyled(modeLine(w.Name, false)).SetDotHere().Buffer()
|
|
||||||
buf.TrimToLines(0, height)
|
buf.TrimToLines(0, height)
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w stub) MaxHeight(width, height int) int {
|
||||||
|
return len(w.render(width).Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w stub) render(width int) *term.Buffer {
|
||||||
|
return term.NewBufferBuilder(width).
|
||||||
|
WriteStyled(modeLine(w.Name, false)).SetDotHere().Buffer()
|
||||||
|
}
|
||||||
|
|
||||||
func (w stub) Handle(event term.Event) bool {
|
func (w stub) Handle(event term.Event) bool {
|
||||||
return w.Bindings.Handle(w, event)
|
return w.Bindings.Handle(w, event)
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,12 +146,20 @@ func (w *codeArea) Submit() {
|
||||||
// Render renders the code area, including the prompt and rprompt, highlighted
|
// Render renders the code area, including the prompt and rprompt, highlighted
|
||||||
// code, the cursor, and compilation errors in the code content.
|
// code, the cursor, and compilation errors in the code content.
|
||||||
func (w *codeArea) Render(width, height int) *term.Buffer {
|
func (w *codeArea) Render(width, height int) *term.Buffer {
|
||||||
|
b := w.render(width)
|
||||||
|
truncateToHeight(b, height)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *codeArea) MaxHeight(width, height int) int {
|
||||||
|
return len(w.render(width).Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *codeArea) render(width int) *term.Buffer {
|
||||||
view := getView(w)
|
view := getView(w)
|
||||||
bb := term.NewBufferBuilder(width)
|
bb := term.NewBufferBuilder(width)
|
||||||
renderView(view, bb)
|
renderView(view, bb)
|
||||||
b := bb.Buffer()
|
return bb.Buffer()
|
||||||
truncateToHeight(b, height)
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle handles KeyEvent's of non-function keys, as well as PasteSetting
|
// Handle handles KeyEvent's of non-function keys, as well as PasteSetting
|
||||||
|
|
|
@ -95,26 +95,47 @@ const colViewColGap = 1
|
||||||
// Render renders all the columns side by side, putting the dot in the focused
|
// Render renders all the columns side by side, putting the dot in the focused
|
||||||
// column.
|
// column.
|
||||||
func (w *colView) Render(width, height int) *term.Buffer {
|
func (w *colView) Render(width, height int) *term.Buffer {
|
||||||
|
cols, widths := w.prepareRender(width)
|
||||||
|
if len(cols) == 0 {
|
||||||
|
return &term.Buffer{Width: width}
|
||||||
|
}
|
||||||
|
var buf term.Buffer
|
||||||
|
for i, col := range cols {
|
||||||
|
if i > 0 {
|
||||||
|
buf.Width += colViewColGap
|
||||||
|
}
|
||||||
|
bufCol := col.Render(widths[i], height)
|
||||||
|
buf.ExtendRight(bufCol)
|
||||||
|
}
|
||||||
|
return &buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *colView) MaxHeight(width, height int) int {
|
||||||
|
cols, widths := w.prepareRender(width)
|
||||||
|
max := 0
|
||||||
|
for i, col := range cols {
|
||||||
|
colMax := col.MaxHeight(widths[i], height)
|
||||||
|
if max < colMax {
|
||||||
|
max = colMax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns widgets in and widths of columns.
|
||||||
|
func (w *colView) prepareRender(width int) ([]Widget, []int) {
|
||||||
state := w.CopyState()
|
state := w.CopyState()
|
||||||
ncols := len(state.Columns)
|
ncols := len(state.Columns)
|
||||||
if ncols == 0 {
|
if ncols == 0 {
|
||||||
// No column.
|
// No column.
|
||||||
return &term.Buffer{Width: width}
|
return nil, nil
|
||||||
}
|
}
|
||||||
if width < ncols {
|
if width < ncols {
|
||||||
// To narrow; give up by rendering nothing.
|
// To narrow; give up by rendering nothing.
|
||||||
return &term.Buffer{Width: width}
|
return nil, nil
|
||||||
}
|
}
|
||||||
colWidths := distribute(width-(ncols-1)*colViewColGap, w.Weights(ncols))
|
widths := distribute(width-(ncols-1)*colViewColGap, w.Weights(ncols))
|
||||||
var buf term.Buffer
|
return state.Columns, widths
|
||||||
for i, col := range state.Columns {
|
|
||||||
if i > 0 {
|
|
||||||
buf.Width += colViewColGap
|
|
||||||
}
|
|
||||||
bufCol := col.Render(colWidths[i], height)
|
|
||||||
buf.ExtendRight(bufCol)
|
|
||||||
}
|
|
||||||
return &buf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle handles the event first by consulting the overlay handler, and then
|
// Handle handles the event first by consulting the overlay handler, and then
|
||||||
|
|
|
@ -53,6 +53,10 @@ func (w *comboBox) Render(width, height int) *term.Buffer {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *comboBox) MaxHeight(width, height int) int {
|
||||||
|
return w.codeArea.MaxHeight(width, height) + w.listBox.MaxHeight(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle first lets the listbox handle the event, and if it is unhandled, lets
|
// Handle first lets the listbox handle the event, and if it is unhandled, lets
|
||||||
// the codearea handle it. If the codearea has handled the event and the code
|
// the codearea handle it. If the codearea has handled the event and the code
|
||||||
// content has changed, it calls OnFilter with the new content.
|
// content has changed, it calls OnFilter with the new content.
|
||||||
|
|
|
@ -12,6 +12,11 @@ func (Empty) Render(width, height int) *term.Buffer {
|
||||||
return term.NewBufferBuilder(width).Buffer()
|
return term.NewBufferBuilder(width).Buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxHeight returns 1, since this widget always occupies one line.
|
||||||
|
func (Empty) MaxHeight(width, height int) int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
// Handle always returns false.
|
// Handle always returns false.
|
||||||
func (Empty) Handle(event term.Event) bool {
|
func (Empty) Handle(event term.Event) bool {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -13,13 +13,21 @@ type Label struct {
|
||||||
// Render shows the content. If the given box is too small, the text is cropped.
|
// Render shows the content. If the given box is too small, the text is cropped.
|
||||||
func (l Label) Render(width, height int) *term.Buffer {
|
func (l Label) Render(width, height int) *term.Buffer {
|
||||||
// TODO: Optimize by stopping as soon as $height rows are written.
|
// TODO: Optimize by stopping as soon as $height rows are written.
|
||||||
bb := term.NewBufferBuilder(width)
|
b := l.render(width)
|
||||||
bb.WriteStyled(l.Content)
|
|
||||||
b := bb.Buffer()
|
|
||||||
b.TrimToLines(0, height)
|
b.TrimToLines(0, height)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxHeight returns the maximum height the Label can take when rendering within
|
||||||
|
// a bound box.
|
||||||
|
func (l Label) MaxHeight(width, height int) int {
|
||||||
|
return len(l.render(width).Lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Label) render(width int) *term.Buffer {
|
||||||
|
return term.NewBufferBuilder(width).WriteStyled(l.Content).Buffer()
|
||||||
|
}
|
||||||
|
|
||||||
// Handle always returns false.
|
// Handle always returns false.
|
||||||
func (l Label) Handle(event term.Event) bool {
|
func (l Label) Handle(event term.Event) bool {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -85,6 +85,25 @@ func (w *listBox) Render(width, height int) *term.Buffer {
|
||||||
return w.renderVertical(width, height)
|
return w.renderVertical(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *listBox) MaxHeight(width, height int) int {
|
||||||
|
s := w.CopyState()
|
||||||
|
if s.Items == nil || s.Items.Len() == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if w.Horizontal {
|
||||||
|
_, h := getHorizontalWindow(s, w.Padding, width, height)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
h := 0
|
||||||
|
for i := 0; i < s.Items.Len(); i++ {
|
||||||
|
h += s.Items.Show(i).CountLines()
|
||||||
|
if h >= height {
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
const listBoxColGap = 2
|
const listBoxColGap = 2
|
||||||
|
|
||||||
func (w *listBox) renderHorizontal(width, height int) *term.Buffer {
|
func (w *listBox) renderHorizontal(width, height int) *term.Buffer {
|
||||||
|
|
|
@ -95,8 +95,8 @@ func getVerticalWindow(state ListBoxState, height int) (first, crop int) {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines the window to show in horizontal It returns the first item
|
// Determines the window to show in horizontal. Returns the first item to show
|
||||||
// to show and the amount of height required.
|
// and the amount of height required.
|
||||||
func getHorizontalWindow(state ListBoxState, padding, width, height int) (int, int) {
|
func getHorizontalWindow(state ListBoxState, padding, width, height int) (int, int) {
|
||||||
items := state.Items
|
items := state.Items
|
||||||
n := items.Len()
|
n := items.Len()
|
||||||
|
|
|
@ -80,6 +80,10 @@ func (w *textView) Render(width, height int) *term.Buffer {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *textView) MaxHeight(width, height int) int {
|
||||||
|
return len(w.CopyState().Lines)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *textView) getStateForRender(height int) (lines []string, first int) {
|
func (w *textView) getStateForRender(height int) (lines []string, first int) {
|
||||||
w.MutateState(func(s *TextViewState) {
|
w.MutateState(func(s *TextViewState) {
|
||||||
if s.First > len(s.Lines)-height && len(s.Lines)-height >= 0 {
|
if s.First > len(s.Lines)-height && len(s.Lines)-height >= 0 {
|
||||||
|
|
|
@ -12,15 +12,24 @@ import (
|
||||||
// render itself.
|
// render itself.
|
||||||
type Widget interface {
|
type Widget interface {
|
||||||
Renderer
|
Renderer
|
||||||
|
MaxHeighter
|
||||||
Handler
|
Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderer wraps the Render method.
|
// Renderer wraps the Render method.
|
||||||
type Renderer interface {
|
type Renderer interface {
|
||||||
// Render onto a region of bound width and height.
|
// Render renders onto a region of bound width and height.
|
||||||
Render(width, height int) *term.Buffer
|
Render(width, height int) *term.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxHeighter wraps the MaxHeight method.
|
||||||
|
type MaxHeighter interface {
|
||||||
|
// MaxHeight returns the maximum height needed when rendering onto a region
|
||||||
|
// of bound width and height. The returned value may be larger than the
|
||||||
|
// height argument.
|
||||||
|
MaxHeight(width, height int) int
|
||||||
|
}
|
||||||
|
|
||||||
// Handler wraps the Handle method.
|
// Handler wraps the Handle method.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
// Try to handle a terminal event and returns whether the event has been
|
// Try to handle a terminal event and returns whether the event has been
|
||||||
|
|
Loading…
Reference in New Issue
Block a user