Add Tabpane
This commit is contained in:
		
							parent
							
								
									9d0302382d
								
							
						
					
					
						commit
						5a0bce64fa
					
				
							
								
								
									
										79
									
								
								example/tabs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								example/tabs.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										235
									
								
								tabpane.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user