1// Copyright 2016 Zack Guo <zack.y.guo@gmail.com>. All rights reserved. 2// Use of this source code is governed by a MIT license that can 3// be found in the LICENSE file. 4 5package termui 6 7import "image" 8 9// Hline is a horizontal line. 10type Hline struct { 11 X int 12 Y int 13 Len int 14 Fg Attribute 15 Bg Attribute 16} 17 18// Vline is a vertical line. 19type Vline struct { 20 X int 21 Y int 22 Len int 23 Fg Attribute 24 Bg Attribute 25} 26 27// Buffer draws a horizontal line. 28func (l Hline) Buffer() Buffer { 29 if l.Len <= 0 { 30 return NewBuffer() 31 } 32 return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg) 33} 34 35// Buffer draws a vertical line. 36func (l Vline) Buffer() Buffer { 37 if l.Len <= 0 { 38 return NewBuffer() 39 } 40 return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg) 41} 42 43// Buffer draws a box border. 44func (b Block) drawBorder(buf Buffer) { 45 if !b.Border { 46 return 47 } 48 49 min := b.area.Min 50 max := b.area.Max 51 52 x0 := min.X 53 y0 := min.Y 54 x1 := max.X - 1 55 y1 := max.Y - 1 56 57 // draw lines 58 if b.BorderTop { 59 buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) 60 } 61 if b.BorderBottom { 62 buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer()) 63 } 64 if b.BorderLeft { 65 buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) 66 } 67 if b.BorderRight { 68 buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer()) 69 } 70 71 // draw corners 72 if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 { 73 buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg}) 74 } 75 if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 { 76 buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg}) 77 } 78 if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 { 79 buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg}) 80 } 81 if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 { 82 buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg}) 83 } 84} 85 86func (b Block) drawBorderLabel(buf Buffer) { 87 maxTxtW := b.area.Dx() - 2 88 tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW) 89 90 for i, w := 0, 0; i < len(tx); i++ { 91 buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i]) 92 w += tx[i].Width() 93 } 94} 95 96// Block is a base struct for all other upper level widgets, 97// consider it as css: display:block. 98// Normally you do not need to create it manually. 99type Block struct { 100 area image.Rectangle 101 innerArea image.Rectangle 102 X int 103 Y int 104 Border bool 105 BorderFg Attribute 106 BorderBg Attribute 107 BorderLeft bool 108 BorderRight bool 109 BorderTop bool 110 BorderBottom bool 111 BorderLabel string 112 BorderLabelFg Attribute 113 BorderLabelBg Attribute 114 Display bool 115 Bg Attribute 116 Width int 117 Height int 118 PaddingTop int 119 PaddingBottom int 120 PaddingLeft int 121 PaddingRight int 122 id string 123 Float Align 124} 125 126// NewBlock returns a *Block which inherits styles from current theme. 127func NewBlock() *Block { 128 b := Block{} 129 b.Display = true 130 b.Border = true 131 b.BorderLeft = true 132 b.BorderRight = true 133 b.BorderTop = true 134 b.BorderBottom = true 135 b.BorderBg = ThemeAttr("border.bg") 136 b.BorderFg = ThemeAttr("border.fg") 137 b.BorderLabelBg = ThemeAttr("label.bg") 138 b.BorderLabelFg = ThemeAttr("label.fg") 139 b.Bg = ThemeAttr("block.bg") 140 b.Width = 2 141 b.Height = 2 142 b.id = GenId() 143 b.Float = AlignNone 144 return &b 145} 146 147func (b Block) Id() string { 148 return b.id 149} 150 151// Align computes box model 152func (b *Block) Align() { 153 // outer 154 b.area.Min.X = 0 155 b.area.Min.Y = 0 156 b.area.Max.X = b.Width 157 b.area.Max.Y = b.Height 158 159 // float 160 b.area = AlignArea(TermRect(), b.area, b.Float) 161 b.area = MoveArea(b.area, b.X, b.Y) 162 163 // inner 164 b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft 165 b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop 166 b.innerArea.Max.X = b.area.Max.X - b.PaddingRight 167 b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom 168 169 if b.Border { 170 if b.BorderLeft { 171 b.innerArea.Min.X++ 172 } 173 if b.BorderRight { 174 b.innerArea.Max.X-- 175 } 176 if b.BorderTop { 177 b.innerArea.Min.Y++ 178 } 179 if b.BorderBottom { 180 b.innerArea.Max.Y-- 181 } 182 } 183} 184 185// InnerBounds returns the internal bounds of the block after aligning and 186// calculating the padding and border, if any. 187func (b *Block) InnerBounds() image.Rectangle { 188 b.Align() 189 return b.innerArea 190} 191 192// Buffer implements Bufferer interface. 193// Draw background and border (if any). 194func (b *Block) Buffer() Buffer { 195 b.Align() 196 197 buf := NewBuffer() 198 buf.SetArea(b.area) 199 buf.Fill(' ', ColorDefault, b.Bg) 200 201 b.drawBorder(buf) 202 b.drawBorderLabel(buf) 203 204 return buf 205} 206 207// GetHeight implements GridBufferer. 208// It returns current height of the block. 209func (b Block) GetHeight() int { 210 return b.Height 211} 212 213// SetX implements GridBufferer interface, which sets block's x position. 214func (b *Block) SetX(x int) { 215 b.X = x 216} 217 218// SetY implements GridBufferer interface, it sets y position for block. 219func (b *Block) SetY(y int) { 220 b.Y = y 221} 222 223// SetWidth implements GridBuffer interface, it sets block's width. 224func (b *Block) SetWidth(w int) { 225 b.Width = w 226} 227 228func (b Block) InnerWidth() int { 229 return b.innerArea.Dx() 230} 231 232func (b Block) InnerHeight() int { 233 return b.innerArea.Dy() 234} 235 236func (b Block) InnerX() int { 237 return b.innerArea.Min.X 238} 239 240func (b Block) InnerY() int { return b.innerArea.Min.Y } 241