]> git.lizzy.rs Git - micro.git/blob - cmd/micro/tab.go
be1f364f981d955142c4270b65c249ace9e1fd2e
[micro.git] / cmd / micro / tab.go
1 package main
2
3 import (
4         "sort"
5         "path/filepath"
6
7         "github.com/zyedidia/tcell"
8 )
9
10 var tabBarOffset int
11
12 type Tab struct {
13         // This contains all the views in this tab
14         // There is generally only one view per tab, but you can have
15         // multiple views with splits
16         views []*View
17         // This is the current view for this tab
18         CurView int
19
20         tree *SplitTree
21 }
22
23 // NewTabFromView creates a new tab and puts the given view in the tab
24 func NewTabFromView(v *View) *Tab {
25         t := new(Tab)
26         t.views = append(t.views, v)
27         t.views[0].Num = 0
28
29         t.tree = new(SplitTree)
30         t.tree.kind = VerticalSplit
31         t.tree.children = []Node{NewLeafNode(t.views[0], t.tree)}
32
33         w, h := screen.Size()
34         t.tree.width = w
35         t.tree.height = h
36
37         if globalSettings["infobar"].(bool) {
38                 t.tree.height--
39         }
40
41         t.Resize()
42
43         return t
44 }
45
46 // SetNum sets all this tab's views to have the correct tab number
47 func (t *Tab) SetNum(num int) {
48         t.tree.tabNum = num
49         for _, v := range t.views {
50                 v.TabNum = num
51         }
52 }
53
54 func (t *Tab) Cleanup() {
55         t.tree.Cleanup()
56 }
57
58 func (t *Tab) Resize() {
59         w, h := screen.Size()
60         t.tree.width = w
61         t.tree.height = h
62
63         if globalSettings["infobar"].(bool) {
64                 t.tree.height--
65         }
66
67         t.tree.ResizeSplits()
68
69         for i, v := range t.views {
70                 v.Num = i
71         }
72 }
73
74 // CurView returns the current view
75 func CurView() *View {
76         curTab := tabs[curTab]
77         return curTab.views[curTab.CurView]
78 }
79
80 // TabbarString returns the string that should be displayed in the tabbar
81 // It also returns a map containing which indicies correspond to which tab number
82 // This is useful when we know that the mouse click has occurred at an x location
83 // but need to know which tab that corresponds to to accurately change the tab
84 func TabbarString() (string, map[int]int) {
85         str := ""
86         indicies := make(map[int]int)
87         for i, t := range tabs {
88                 if i == curTab {
89                         str += "["
90                 } else {
91                         str += " "
92                 }
93                 //To address issue 556.2
94                 _, name := filepath.Split(t.views[t.CurView].Buf.GetName())
95                 str += name
96                 if i == curTab {
97                         str += "]"
98                 } else {
99                         str += " "
100                 }
101                 indicies[len(str)-1] = i + 1
102                 str += " "
103         }
104         return str, indicies
105 }
106
107 // TabbarHandleMouseEvent checks the given mouse event if it is clicking on the tabbar
108 // If it is it changes the current tab accordingly
109 // This function returns true if the tab is changed
110 func TabbarHandleMouseEvent(event tcell.Event) bool {
111         // There is no tabbar displayed if there are less than 2 tabs
112         if len(tabs) <= 1 {
113                 return false
114         }
115
116         switch e := event.(type) {
117         case *tcell.EventMouse:
118                 button := e.Buttons()
119                 // Must be a left click
120                 if button == tcell.Button1 {
121                         x, y := e.Position()
122                         if y != 0 {
123                                 return false
124                         }
125                         str, indicies := TabbarString()
126                         if x+tabBarOffset >= len(str) {
127                                 return false
128                         }
129                         var tabnum int
130                         var keys []int
131                         for k := range indicies {
132                                 keys = append(keys, k)
133                         }
134                         sort.Ints(keys)
135                         for _, k := range keys {
136                                 if x+tabBarOffset <= k {
137                                         tabnum = indicies[k] - 1
138                                         break
139                                 }
140                         }
141                         curTab = tabnum
142                         return true
143                 }
144         }
145
146         return false
147 }
148
149 // DisplayTabs displays the tabbar at the top of the editor if there are multiple tabs
150 func DisplayTabs() {
151         if len(tabs) <= 1 {
152                 return
153         }
154
155         str, indicies := TabbarString()
156
157         tabBarStyle := defStyle.Reverse(true)
158         if style, ok := colorscheme["tabbar"]; ok {
159                 tabBarStyle = style
160         }
161
162         // Maybe there is a unicode filename?
163         fileRunes := []rune(str)
164         w, _ := screen.Size()
165         tooWide := (w < len(fileRunes))
166
167         // if the entire tab-bar is longer than the screen is wide,
168         // then it should be truncated appropriately to keep the
169         // active tab visible on the UI.
170         if tooWide == true {
171                 // first we have to work out where the selected tab is
172                 // out of the total length of the tab bar. this is done
173                 // by extracting the hit-areas from the indicies map
174                 // that was constructed by `TabbarString()`
175                 var keys []int
176                 for offset := range indicies {
177                         keys = append(keys, offset)
178                 }
179                 // sort them to be in ascending order so that values will
180                 // correctly reflect the displayed ordering of the tabs
181                 sort.Ints(keys)
182                 // record the offset of each tab and the previous tab so
183                 // we can find the position of the tab's hit-box.
184                 previousTabOffset := 0
185                 currentTabOffset := 0
186                 for _, k := range keys {
187                         tabIndex := indicies[k] - 1
188                         if tabIndex == curTab {
189                                 currentTabOffset = k
190                                 break
191                         }
192                         // this is +2 because there are two padding spaces that aren't accounted
193                         // for in the display. please note that this is for cosmetic purposes only.
194                         previousTabOffset = k + 2
195                 }
196                 // get the width of the hitbox of the active tab, from there calculate the offsets
197                 // to the left and right of it to approximately center it on the tab bar display.
198                 centeringOffset := (w - (currentTabOffset - previousTabOffset))
199                 leftBuffer := previousTabOffset - (centeringOffset / 2)
200                 rightBuffer := currentTabOffset + (centeringOffset / 2)
201
202                 // check to make sure we haven't overshot the bounds of the string,
203                 // if we have, then take that remainder and put it on the left side
204                 overshotRight := rightBuffer - len(fileRunes)
205                 if overshotRight > 0 {
206                         leftBuffer = leftBuffer + overshotRight
207                 }
208
209                 overshotLeft := leftBuffer - 0
210                 if overshotLeft < 0 {
211                         leftBuffer = 0
212                         rightBuffer = leftBuffer + (w - 1)
213                 } else {
214                         rightBuffer = leftBuffer + (w - 2)
215                 }
216
217                 if rightBuffer > len(fileRunes)-1 {
218                         rightBuffer = len(fileRunes) - 1
219                 }
220
221                 // construct a new buffer of text to put the
222                 // newly formatted tab bar text into.
223                 var displayText []rune
224
225                 // if the left-side of the tab bar isn't at the start
226                 // of the constructed tab bar text, then show that are
227                 // more tabs to the left by displaying a "+"
228                 if leftBuffer != 0 {
229                         displayText = append(displayText, '+')
230                 }
231                 // copy the runes in from the original tab bar text string
232                 // into the new display buffer
233                 for x := leftBuffer; x < rightBuffer; x++ {
234                         displayText = append(displayText, fileRunes[x])
235                 }
236                 // if there is more text to the right of the right-most
237                 // column in the tab bar text, then indicate there are more
238                 // tabs to the right by displaying a "+"
239                 if rightBuffer < len(fileRunes)-1 {
240                         displayText = append(displayText, '+')
241                 }
242
243                 // now store the offset from zero of the left-most text
244                 // that is being displayed. This is to ensure that when
245                 // clicking on the tab bar, the correct tab gets selected.
246                 tabBarOffset = leftBuffer
247
248                 // use the constructed buffer as the display buffer to print
249                 // onscreen.
250                 fileRunes = displayText
251         } else {
252                 tabBarOffset = 0
253         }
254
255         // iterate over the width of the terminal display and for each column,
256         // write a character into the tab display area with the appropriate style.
257         for x := 0; x < w; x++ {
258                 if x < len(fileRunes) {
259                         screen.SetContent(x, 0, fileRunes[x], nil, tabBarStyle)
260                 } else {
261                         screen.SetContent(x, 0, ' ', nil, tabBarStyle)
262                 }
263         }
264 }