aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gizak/termui/block.go
blob: 418738c8d4d3af37c102a90459212c2f454aa5c1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import "image"

// Hline is a horizontal line.
type Hline struct {
    X   int
    Y   int
    Len int
    Fg  Attribute
    Bg  Attribute
}

// Vline is a vertical line.
type Vline struct {
    X   int
    Y   int
    Len int
    Fg  Attribute
    Bg  Attribute
}

// Buffer draws a horizontal line.
func (l Hline) Buffer() Buffer {
    if l.Len <= 0 {
        return NewBuffer()
    }
    return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
}

// Buffer draws a vertical line.
func (l Vline) Buffer() Buffer {
    if l.Len <= 0 {
        return NewBuffer()
    }
    return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
}

// Buffer draws a box border.
func (b Block) drawBorder(buf Buffer) {
    if !b.Border {
        return
    }

    min := b.area.Min
    max := b.area.Max

    x0 := min.X
    y0 := min.Y
    x1 := max.X - 1
    y1 := max.Y - 1

    // draw lines
    if b.BorderTop {
        buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
    }
    if b.BorderBottom {
        buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
    }
    if b.BorderLeft {
        buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
    }
    if b.BorderRight {
        buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
    }

    // draw corners
    if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 {
        buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg})
    }
    if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 {
        buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg})
    }
    if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 {
        buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg})
    }
    if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 {
        buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg})
    }
}

func (b Block) drawBorderLabel(buf Buffer) {
    maxTxtW := b.area.Dx() - 2
    tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)

    for i, w := 0, 0; i < len(tx); i++ {
        buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
        w += tx[i].Width()
    }
}

// Block is a base struct for all other upper level widgets,
// consider it as css: display:block.
// Normally you do not need to create it manually.
type Block struct {
    area          image.Rectangle
    innerArea     image.Rectangle
    X             int
    Y             int
    Border        bool
    BorderFg      Attribute
    BorderBg      Attribute
    BorderLeft    bool
    BorderRight   bool
    BorderTop     bool
    BorderBottom  bool
    BorderLabel   string
    BorderLabelFg Attribute
    BorderLabelBg Attribute
    Display       bool
    Bg            Attribute
    Width         int
    Height        int
    PaddingTop    int
    PaddingBottom int
    PaddingLeft   int
    PaddingRight  int
    id            string
    Float         Align
}

// NewBlock returns a *Block which inherits styles from current theme.
func NewBlock() *Block {
    b := Block{}
    b.Display = true
    b.Border = true
    b.BorderLeft = true
    b.BorderRight = true
    b.BorderTop = true
    b.BorderBottom = true
    b.BorderBg = ThemeAttr("border.bg")
    b.BorderFg = ThemeAttr("border.fg")
    b.BorderLabelBg = ThemeAttr("label.bg")
    b.BorderLabelFg = ThemeAttr("label.fg")
    b.Bg = ThemeAttr("block.bg")
    b.Width = 2
    b.Height = 2
    b.id = GenId()
    b.Float = AlignNone
    return &b
}

func (b Block) Id() string {
    return b.id
}

// Align computes box model
func (b *Block) Align() {
    // outer
    b.area.Min.X = 0
    b.area.Min.Y = 0
    b.area.Max.X = b.Width
    b.area.Max.Y = b.Height

    // float
    b.area = AlignArea(TermRect(), b.area, b.Float)
    b.area = MoveArea(b.area, b.X, b.Y)

    // inner
    b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft
    b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop
    b.innerArea.Max.X = b.area.Max.X - b.PaddingRight
    b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom

    if b.Border {
        if b.BorderLeft {
            b.innerArea.Min.X++
        }
        if b.BorderRight {
            b.innerArea.Max.X--
        }
        if b.BorderTop {
            b.innerArea.Min.Y++
        }
        if b.BorderBottom {
            b.innerArea.Max.Y--
        }
    }
}

// InnerBounds returns the internal bounds of the block after aligning and
// calculating the padding and border, if any.
func (b *Block) InnerBounds() image.Rectangle {
    b.Align()
    return b.innerArea
}

// Buffer implements Bufferer interface.
// Draw background and border (if any).
func (b *Block) Buffer() Buffer {
    b.Align()

    buf := NewBuffer()
    buf.SetArea(b.area)
    buf.Fill(' ', ColorDefault, b.Bg)

    b.drawBorder(buf)
    b.drawBorderLabel(buf)

    return buf
}

// GetHeight implements GridBufferer.
// It returns current height of the block.
func (b Block) GetHeight() int {
    return b.Height
}

// SetX implements GridBufferer interface, which sets block's x position.
func (b *Block) SetX(x int) {
    b.X = x
}

// SetY implements GridBufferer interface, it sets y position for block.
func (b *Block) SetY(y int) {
    b.Y = y
}

// SetWidth implements GridBuffer interface, it sets block's width.
func (b *Block) SetWidth(w int) {
    b.Width = w
}

func (b Block) InnerWidth() int {
    return b.innerArea.Dx()
}

func (b Block) InnerHeight() int {
    return b.innerArea.Dy()
}

func (b Block) InnerX() int {
    return b.innerArea.Min.X
}

func (b Block) InnerY() int { return b.innerArea.Min.Y }