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