Add Tabpane

This commit is contained in:
marigs 2015-05-25 20:55:34 +02:00
parent 9d0302382d
commit 5a0bce64fa
2 changed files with 314 additions and 0 deletions

79
example/tabs.go Normal file
View File

@ -0,0 +1,79 @@
package main
import (
"github.com/gizak/termui"
//"fmt"
//"os"
)
func main() {
err := termui.Init()
if err != nil {
panic(err)
}
defer termui.Close()
termui.UseTheme("helloworld")
header := termui.NewPar("Press q to quit, Press j or k to switch tabs")
header.Height = 1
header.Width = 50
header.HasBorder = false
header.TextBgColor = termui.ColorBlue
tab1 := termui.NewTab("pierwszy")
par2 := termui.NewPar("Press q to quit\nPress j or k to switch tabs\n")
par2.Height = 5
par2.Width = 37
par2.Y = 0
par2.Border.Label = "Keys"
par2.Border.FgColor = termui.ColorYellow
tab1.AddBlocks(par2)
tab2 := termui.NewTab("drugi")
bc := termui.NewBarChart()
data := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
bc.Border.Label = "Bar Chart"
bc.Data = data
bc.Width = 26
bc.Height = 10
bc.DataLabels = bclabels
bc.TextColor = termui.ColorGreen
bc.BarColor = termui.ColorRed
bc.NumColor = termui.ColorYellow
tab2.AddBlocks(bc)
tab3 := termui.NewTab("trzeci")
tab4 := termui.NewTab("żółw")
tab5 := termui.NewTab("four")
tab6 := termui.NewTab("five")
tabpane := termui.NewTabpane()
tabpane.Y = 1
tabpane.Width = 30
tabpane.HasBorder = true
tabpane.SetTabs(*tab1, *tab2, *tab3, *tab4, *tab5, *tab6)
termui.Render(header, tabpane)
evt := termui.EventCh()
for {
select {
case e := <- evt:
if e.Type == termui.EventKey {
switch e.Ch {
case 'q':
return
case 'j':
tabpane.SetActiveLeft()
termui.Render(header, tabpane)
case 'k':
tabpane.SetActiveRight()
termui.Render(header, tabpane)
}
}
}
}
}

235
tabpane.go Normal file
View File

@ -0,0 +1,235 @@
package termui
import (
"unicode/utf8"
)
type Tab struct {
Label string
RuneLen int
Blocks []Bufferer
}
func NewTab(label string) *Tab {
return &Tab {
Label: label,
RuneLen: utf8.RuneCount([]byte(label)) }
}
func (tab *Tab) AddBlocks(rs ...Bufferer) {
for _, r := range rs {
tab.Blocks = append(tab.Blocks, r)
}
}
func (tab *Tab) Buffer() []Point {
points := []Point{}
for blockNum := 0; blockNum < len(tab.Blocks); blockNum++ {
b := &tab.Blocks[blockNum]
blockPoints := (*b).Buffer()
points = append(points, blockPoints...)
}
return points
}
type Tabpane struct {
Block
Tabs []Tab
activeTabIndex int
posTabText []int
offTabText int
}
func NewTabpane() *Tabpane {
tp := Tabpane {
Block: *NewBlock(),
activeTabIndex: 0,
offTabText: 0}
return &tp
}
func (tp *Tabpane) SetTabs(tabs ...Tab) {
tp.Tabs = make([]Tab, len(tabs))
tp.posTabText = make([]int, len(tabs)+1)
off := 0
for i :=0; i < len(tp.Tabs); i++ {
tp.Tabs[i] = tabs[i]
tp.posTabText[i] = off
off += tp.Tabs[i].RuneLen+1 //+1 for space between tabs
}
tp.posTabText[len(tabs)] = off-1 //total length of Tab's text
}
func (tp *Tabpane) SetActiveLeft() {
if tp.activeTabIndex == 0 {
return
}
tp.activeTabIndex -= 1
if tp.posTabText[tp.activeTabIndex] < tp.offTabText {
tp.offTabText = tp.posTabText[tp.activeTabIndex]
}
}
func (tp *Tabpane) SetActiveRight() {
if tp.activeTabIndex == len(tp.Tabs)-1 {
return
}
tp.activeTabIndex += 1
endOffset := tp.posTabText[tp.activeTabIndex] + tp.Tabs[tp.activeTabIndex].RuneLen
if endOffset + tp.offTabText > tp.innerWidth {
tp.offTabText = endOffset - tp.innerWidth
}
}
// Checks if left and right tabs are fully visible
// if only left tabs are not visible return -1
// if only right tabs are not visible return 1
// if both return 0
// use only if fitsWidth() returns false
func (tp *Tabpane) checkAlignment() int {
ret := 0
if tp.offTabText > 0 {
ret = -1
}
if tp.offTabText + tp.innerWidth < tp.posTabText[len(tp.Tabs)] {
ret += 1
}
return ret
}
// Checks if all tabs fits innerWidth of Tabpane
func (tp *Tabpane) fitsWidth() bool {
return tp.innerWidth >= tp.posTabText[len(tp.Tabs)]
}
func (tp *Tabpane) align() {
if !tp.fitsWidth() && !tp.HasBorder {
tp.innerWidth -= 2
tp.innerX += 1
}
}
// Adds the point only if it is visible in Tabpane.
// Point can be invisible if concatenation of Tab's texts is widther then
// innerWidth of Tabpane
func (tp *Tabpane) addPoint(ptab []Point, charOffset *int, oftX *int, points ...Point) []Point {
if *charOffset < tp.offTabText || tp.offTabText + tp.innerWidth < *charOffset {
*charOffset++
return ptab
}
for _, p := range(points) {
p.X = *oftX
ptab = append(ptab, p)
}
*oftX++
*charOffset++
return ptab
}
// Draws the point and redraws upper and lower border points (if it has one)
func (tp *Tabpane) drawPointWithBorder(p Point, ch rune, chbord rune, chdown rune, chup rune) []Point {
var addp []Point
p.Ch = ch
if tp.HasBorder {
p.Ch = chdown
p.Y = tp.innerY-1
addp = append(addp, p)
p.Ch = chup
p.Y = tp.innerY+1
addp = append(addp, p)
p.Ch = chbord
}
p.Y = tp.innerY
return append(addp, p)
}
func (tp *Tabpane) Buffer() []Point {
if tp.HasBorder {
tp.Height = 3
} else {
tp.Height = 1
}
if tp.Width > tp.posTabText[len(tp.Tabs)]+2 {
tp.Width = tp.posTabText[len(tp.Tabs)]+2
}
ps := tp.Block.Buffer()
tp.align()
if tp.innerHeight <= 0 || tp.innerWidth <= 0 {
return nil
}
oftX := tp.innerX
charOffset := 0
pt := Point{Bg: tp.Border.BgColor, Fg: tp.Border.FgColor}
for i, tab := range tp.Tabs {
if i != 0 {
pt.X = oftX
pt.Y = tp.innerY
addp := tp.drawPointWithBorder(pt, ' ', VERTICAL_LINE, HORIZONTAL_DOWN, HORIZONTAL_UP)
ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
}
if i == tp.activeTabIndex {
pt.Bg = theme.TabActiveBg
}
rs := str2runes(tab.Label)
for k := 0; k < len(rs); k++ {
addp := make([]Point, 0, 2)
if i == tp.activeTabIndex && tp.HasBorder {
pt.Ch = ' '
pt.Y = tp.innerY + 1
pt.Bg = tp.Border.BgColor
addp = append(addp, pt)
pt.Bg = theme.TabActiveBg
}
pt.Y = tp.innerY
pt.Ch = rs[k]
addp = append(addp, pt)
ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
}
pt.Bg = tp.Border.BgColor
if !tp.fitsWidth() {
all := tp.checkAlignment()
pt.X = tp.innerX-1
pt.Ch = '*'
if tp.HasBorder {
pt.Ch = VERTICAL_LINE
}
ps = append(ps, pt)
if all <= 0 {
addp := tp.drawPointWithBorder(pt, '<', '«', HORIZONTAL_LINE, HORIZONTAL_LINE)
ps = append(ps, addp...)
}
pt.X = tp.innerX + tp.innerWidth
pt.Ch = '*'
if tp.HasBorder {
pt.Ch = VERTICAL_LINE
}
ps = append(ps, pt)
if all >= 0 {
addp := tp.drawPointWithBorder(pt, '>', '»', HORIZONTAL_LINE, HORIZONTAL_LINE)
ps = append(ps, addp...)
}
}
//draw tab content below the Tabpane
if i == tp.activeTabIndex {
blockPoints := tab.Buffer()
for i := 0; i < len(blockPoints); i++ {
blockPoints[i].Y += tp.Height + tp.Y
}
ps = append(ps, blockPoints...)
}
}
return ps
}