6 "github.com/zyedidia/tcell"
11 // A Tab holds an array of views and a splitTree to determine how the
12 // views should be arranged
14 // This contains all the views in this tab
15 // There is generally only one view per tab, but you can have
16 // multiple views with splits
18 // This is the current view for this tab
24 // NewTabFromView creates a new tab and puts the given view in the tab
25 func NewTabFromView(v *View) *Tab {
27 t.Views = append(t.Views, v)
30 t.tree = new(SplitTree)
31 t.tree.kind = VerticalSplit
32 t.tree.children = []Node{NewLeafNode(t.Views[0], t.tree)}
38 if globalSettings["infobar"].(bool) {
41 if globalSettings["keymenu"].(bool) {
50 // SetNum sets all this tab's views to have the correct tab number
51 func (t *Tab) SetNum(num int) {
53 for _, v := range t.Views {
58 // Cleanup cleans up the tree (for example if views have closed)
59 func (t *Tab) Cleanup() {
63 // Resize handles a resize event from the terminal and resizes
64 // all child views correctly
65 func (t *Tab) Resize() {
70 if globalSettings["infobar"].(bool) {
73 if globalSettings["keymenu"].(bool) {
79 for i, v := range t.Views {
82 v.term.Resize(v.Width, v.Height)
87 // CurView returns the current view
88 func CurView() *View {
89 curTab := tabs[curTab]
90 return curTab.Views[curTab.CurView]
93 // TabbarString returns the string that should be displayed in the tabbar
94 // It also returns a map containing which indicies correspond to which tab number
95 // This is useful when we know that the mouse click has occurred at an x location
96 // but need to know which tab that corresponds to to accurately change the tab
97 func TabbarString() (string, map[int]int) {
99 indicies := make(map[int]int)
100 for i, t := range tabs {
106 buf := t.Views[t.CurView].Buf
116 indicies[Count(str)-1] = i + 1
122 // TabbarHandleMouseEvent checks the given mouse event if it is clicking on the tabbar
123 // If it is it changes the current tab accordingly
124 // This function returns true if the tab is changed
125 func TabbarHandleMouseEvent(event tcell.Event) bool {
126 // There is no tabbar displayed if there are less than 2 tabs
131 switch e := event.(type) {
132 case *tcell.EventMouse:
133 button := e.Buttons()
134 // Must be a left click
135 if button == tcell.Button1 {
140 str, indicies := TabbarString()
141 if x+tabBarOffset >= len(str) {
146 for k := range indicies {
147 keys = append(keys, k)
150 for _, k := range keys {
151 if x+tabBarOffset <= k {
152 tabnum = indicies[k] - 1
164 // DisplayTabs displays the tabbar at the top of the editor if there are multiple tabs
170 str, indicies := TabbarString()
172 tabBarStyle := defStyle.Reverse(true)
173 if style, ok := colorscheme["tabbar"]; ok {
177 // Maybe there is a unicode filename?
178 fileRunes := []rune(str)
179 w, _ := screen.Size()
180 tooWide := (w < len(fileRunes))
182 // if the entire tab-bar is longer than the screen is wide,
183 // then it should be truncated appropriately to keep the
184 // active tab visible on the UI.
186 // first we have to work out where the selected tab is
187 // out of the total length of the tab bar. this is done
188 // by extracting the hit-areas from the indicies map
189 // that was constructed by `TabbarString()`
191 for offset := range indicies {
192 keys = append(keys, offset)
194 // sort them to be in ascending order so that values will
195 // correctly reflect the displayed ordering of the tabs
197 // record the offset of each tab and the previous tab so
198 // we can find the position of the tab's hit-box.
199 previousTabOffset := 0
200 currentTabOffset := 0
201 for _, k := range keys {
202 tabIndex := indicies[k] - 1
203 if tabIndex == curTab {
207 // this is +2 because there are two padding spaces that aren't accounted
208 // for in the display. please note that this is for cosmetic purposes only.
209 previousTabOffset = k + 2
211 // get the width of the hitbox of the active tab, from there calculate the offsets
212 // to the left and right of it to approximately center it on the tab bar display.
213 centeringOffset := (w - (currentTabOffset - previousTabOffset))
214 leftBuffer := previousTabOffset - (centeringOffset / 2)
215 rightBuffer := currentTabOffset + (centeringOffset / 2)
217 // check to make sure we haven't overshot the bounds of the string,
218 // if we have, then take that remainder and put it on the left side
219 overshotRight := rightBuffer - len(fileRunes)
220 if overshotRight > 0 {
221 leftBuffer = leftBuffer + overshotRight
224 overshotLeft := leftBuffer - 0
225 if overshotLeft < 0 {
227 rightBuffer = leftBuffer + (w - 1)
229 rightBuffer = leftBuffer + (w - 2)
232 if rightBuffer > len(fileRunes)-1 {
233 rightBuffer = len(fileRunes) - 1
236 // construct a new buffer of text to put the
237 // newly formatted tab bar text into.
238 var displayText []rune
240 // if the left-side of the tab bar isn't at the start
241 // of the constructed tab bar text, then show that are
242 // more tabs to the left by displaying a "+"
244 displayText = append(displayText, '+')
246 // copy the runes in from the original tab bar text string
247 // into the new display buffer
248 for x := leftBuffer; x < rightBuffer; x++ {
249 displayText = append(displayText, fileRunes[x])
251 // if there is more text to the right of the right-most
252 // column in the tab bar text, then indicate there are more
253 // tabs to the right by displaying a "+"
254 if rightBuffer < len(fileRunes)-1 {
255 displayText = append(displayText, '+')
258 // now store the offset from zero of the left-most text
259 // that is being displayed. This is to ensure that when
260 // clicking on the tab bar, the correct tab gets selected.
261 tabBarOffset = leftBuffer
263 // use the constructed buffer as the display buffer to print
265 fileRunes = displayText
270 // iterate over the width of the terminal display and for each column,
271 // write a character into the tab display area with the appropriate style.
272 for x := 0; x < w; x++ {
273 if x < len(fileRunes) {
274 screen.SetContent(x, 0, fileRunes[x], nil, tabBarStyle)
276 screen.SetContent(x, 0, ' ', nil, tabBarStyle)