123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- package door
- import (
- "bytes"
- "log"
- "strings"
- )
- type BorderStyle int
- const (
- NONE BorderStyle = iota
- SINGLE
- DOUBLE
- DOUBLE_SINGLE
- SINGLE_DOUBLE
- )
- type Panel struct {
- X int
- Y int
- Width int
- Style BorderStyle
- BorderColor []byte
- Lines []*Line
- Title string
- TitleColor []byte
- TitleOffset int
- output *bytes.Buffer
- }
- /*
- func (p *Panel) Range(withBorder bool) (sx, sy, ex, ey int) {
- sx = p.X
- sy = p.Y
- ex = p.X + p.Width
- ey = p.Y + len(p.Lines)
- if p.Style > 0 {
- if withBorder {
- ex += 2
- ey += 2
- } else {
- sx += 1
- sy += 1
- }
- }
- return
- }
- */
- func (p *Panel) Length() int {
- return len(p.Lines)
- }
- func (p *Panel) RightBottomPos() (rx, by int) {
- rx = p.X + p.Width
- by = p.Y + len(p.Lines)
- if p.HasBorder() {
- rx += 1
- by += 1
- }
- return
- }
- func (p *Panel) HasBorder() bool {
- return int(p.Style) > 0
- }
- /*
- Is X,Y within the Panel?
- */
- func (p *Panel) Within(x, y int) (bool, int, int) {
- if x < p.X || y < p.Y {
- return false, 0, 0
- }
- mx, my := p.RightBottomPos()
- if x > mx || y > my {
- return false, 0, 0
- }
- // Ok, it must be within:
- return true, x - p.X, y - p.Y
- }
- /*
- // Panel Clicked?
- // If onBorder == true, returns true, 0 for border click.
- // Otherwise true, line clicked on (starting with 1)/
- func (p *Panel) Clicked(m Mouse, onBorder bool) (bool, int) {
- var sx, sy, ex, ey = p.Range(onBorder)
- var mx = int(m.X)
- var my = int(m.Y)
- if (mx >= sx) && (mx <= ex) {
- if (my >= sy) && (my <= ey) {
- if onBorder {
- if my == sy || my == ey {
- return true, 0
- }
- return true, my - sy
- } else {
- return true, (my - sy) + 1
- }
- }
- }
- return false, 0
- }
- */
- // Clear out the panel
- func (p *Panel) Clear() []byte {
- var style int = int(p.Style)
- if p.output == nil {
- p.output = &bytes.Buffer{}
- }
- p.output.Reset()
- var row int = p.Y
- var blank []byte
- if p.HasBorder() {
- blank = bytes.Repeat([]byte{' '}, p.Width+2)
- p.output.Write(Goto(p.X, row))
- p.output.Write(blank)
- row++
- } else {
- blank = bytes.Repeat([]byte{' '}, p.Width)
- }
- for _ = range p.Lines {
- p.output.Write(Goto(p.X, row))
- p.output.Write(blank)
- row++
- }
- if style > 0 {
- p.output.Write(Goto(p.X, row))
- p.output.Write(blank)
- }
- return p.output.Bytes()
- }
- // Output the panel
- func (p *Panel) Output() []byte {
- var style int = int(p.Style)
- var box_style *BoxStyle
- if p.output == nil {
- p.output = &bytes.Buffer{}
- }
- p.output.Reset()
- if p.HasBorder() {
- box_style = &BOXES[style-1]
- }
- var row int = p.Y
- if p.HasBorder() {
- // Top line / border
- // p.output.Write(Goto(p.X, row))
- GotoW(p.X, row, p.output)
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Top_Left)
- if p.Title != "" {
- if p.TitleOffset+len(p.Title) > p.Width {
- log.Panicf("Panel (not wide enough) Width %d : Title size %d + offset %d = %d\n",
- p.Width, len(p.Title), p.TitleOffset, p.TitleOffset+len(p.Title))
- }
- p.output.WriteString(strings.Repeat(box_style.Top, p.TitleOffset))
- p.output.Write(p.TitleColor)
- p.output.WriteString(p.Title)
- p.output.Write(p.BorderColor)
- }
- p.output.WriteString(strings.Repeat(box_style.Top, p.Width-(p.TitleOffset+len(p.Title))) + box_style.Top_Right)
- row++
- }
- for _, line := range p.Lines {
- // p.output.Write(Goto(p.X, row))
- GotoW(p.X, row, p.output)
- line.LineLength(line.Text)
- var joined bool = false
- if p.HasBorder() {
- top := box_style.Top
- // This only works in non-unicode mode
- if bytes.Compare(line.Text.Bytes()[0:len(top)], []byte(top)) == 0 {
- // Yes, this line needs to be joined...
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Middle_Left)
- p.output.Write(line.Output())
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Middle_Right)
- joined = true
- }
- }
- if !joined {
- if p.HasBorder() {
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Side)
- }
- p.output.Write(line.Output())
- if style > 0 {
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Side)
- }
- }
- row++
- }
- if p.HasBorder() {
- // Bottom / border
- // p.output.Write(Goto(p.X, row))
- GotoW(p.X, row, p.output)
- p.output.Write(p.BorderColor)
- p.output.WriteString(box_style.Bottom_Left)
- p.output.WriteString(strings.Repeat(box_style.Top, p.Width) + box_style.Bottom_Right)
- }
- return p.output.Bytes()
- }
- // Output anything that has updated
- func (p *Panel) Update() []byte {
- if p.output == nil {
- p.output = &bytes.Buffer{}
- }
- p.output.Reset()
- var style int = int(p.Style)
- var row, col int
- row = p.Y
- col = p.X
- if style > 0 {
- row++
- col++
- }
- for idx := range p.Lines {
- if p.Lines[idx].Update() {
- // Yes, line was updated
- p.output.Write(Goto(col, row))
- p.output.Write(p.Lines[idx].Output())
- }
- row++
- }
- return p.output.Bytes()
- }
- // Output the updated line
- func (p *Panel) UpdateLine(index int) []byte {
- if p.output == nil {
- p.output = &bytes.Buffer{}
- }
- p.output.Reset()
- // var style int = int(p.Style)
- p.Lines[index].Update()
- var row, col int
- row = p.Y + index
- col = p.X
- if p.HasBorder() {
- row++
- col++
- }
- var line *Line = p.Lines[index]
- p.output.Write(Goto(col, row))
- p.output.Write(line.Output())
- return p.output.Bytes()
- }
- // Position Cursor at the "end" of the panel
- func (p *Panel) GotoEnd() []byte {
- rx, by := p.RightBottomPos()
- return Goto(rx+1, by)
- }
- // Is the top line of this style a single line?
- func Single(bs BorderStyle) bool {
- switch bs {
- case SINGLE, SINGLE_DOUBLE:
- return true
- default:
- return false
- }
- }
- // Create a spacer line that will be connected maybe to the sides.
- func (p *Panel) Spacer() *Line {
- var l *Line = &Line{Text: &bytes.Buffer{}}
- var pos int
- if Single(p.Style) {
- pos = 0
- } else {
- pos = 1
- }
- l.Text.WriteString(strings.Repeat(BOXES[pos].Top, p.Width))
- return l
- }
- func (p *Panel) Center() {
- p.CenterX()
- p.CenterY()
- }
- func (p *Panel) CenterX() {
- p.X = (Width - p.Width) / 2
- }
- func (p *Panel) CenterY() {
- p.Y = (Height - len(p.Lines)) / 2
- }
|