]> git.lizzy.rs Git - micro.git/blob - cmd/micro/action/tab.go
Fix small tab problem
[micro.git] / cmd / micro / action / tab.go
1 package action
2
3 import (
4         "github.com/zyedidia/micro/cmd/micro/buffer"
5         "github.com/zyedidia/micro/cmd/micro/display"
6         "github.com/zyedidia/micro/cmd/micro/screen"
7         "github.com/zyedidia/micro/cmd/micro/views"
8         "github.com/zyedidia/tcell"
9 )
10
11 // The TabList is a list of tabs and a window to display the tab bar
12 // at the top of the screen
13 type TabList struct {
14         *display.TabWindow
15         List []*Tab
16 }
17
18 // NewTabList creates a TabList from a list of buffers by creating a Tab
19 // for each buffer
20 func NewTabList(bufs []*buffer.Buffer) *TabList {
21         w, h := screen.Screen.Size()
22         tl := new(TabList)
23         tl.List = make([]*Tab, len(bufs))
24         if len(bufs) > 1 {
25                 for i, b := range bufs {
26                         tl.List[i] = NewTabFromBuffer(0, 1, w, h-2, b)
27                 }
28         } else {
29                 tl.List[0] = NewTabFromBuffer(0, 0, w, h-1, bufs[0])
30         }
31         tl.TabWindow = display.NewTabWindow(w, 0)
32         tl.Names = make([]string, len(bufs))
33
34         return tl
35 }
36
37 // UpdateNames makes sure that the list of names the tab window has access to is
38 // correct
39 func (t *TabList) UpdateNames() {
40         t.Names = t.Names[:0]
41         for _, p := range t.List {
42                 t.Names = append(t.Names, p.Panes[p.active].Name())
43         }
44 }
45
46 // AddTab adds a new tab to this TabList
47 func (t *TabList) AddTab(p *Tab) {
48         t.List = append(t.List, p)
49         t.Resize()
50         t.UpdateNames()
51 }
52
53 // RemoveTab removes a tab with the given id from the TabList
54 func (t *TabList) RemoveTab(id uint64) {
55         for i, p := range t.List {
56                 if len(p.Panes) == 0 {
57                         continue
58                 }
59                 if p.Panes[0].ID() == id {
60                         copy(t.List[i:], t.List[i+1:])
61                         t.List[len(t.List)-1] = nil
62                         t.List = t.List[:len(t.List)-1]
63                         if t.Active() >= len(t.List) {
64                                 t.SetActive(len(t.List) - 1)
65                         }
66                         t.Resize()
67                         t.UpdateNames()
68                         return
69                 }
70         }
71 }
72
73 // Resize resizes all elements within the tab list
74 // One thing to note is that when there is only 1 tab
75 // the tab bar should not be drawn so resizing must take
76 // that into account
77 func (t *TabList) Resize() {
78         w, h := screen.Screen.Size()
79         InfoBar.Resize(w, h-1)
80         if len(t.List) > 1 {
81                 for _, p := range t.List {
82                         p.Y = 1
83                         p.Node.Resize(w, h-2)
84                         p.Resize()
85                 }
86         } else if len(t.List) == 1 {
87                 t.List[0].Y = 0
88                 t.List[0].Node.Resize(w, h-1)
89                 t.List[0].Resize()
90         }
91 }
92
93 // HandleEvent checks for a resize event or a mouse event on the tab bar
94 // otherwise it will forward the event to the currently active tab
95 func (t *TabList) HandleEvent(event tcell.Event) {
96         switch e := event.(type) {
97         case *tcell.EventResize:
98                 t.Resize()
99         case *tcell.EventMouse:
100                 mx, my := e.Position()
101                 switch e.Buttons() {
102                 case tcell.Button1:
103                         ind := t.GetMouseLoc(buffer.Loc{mx, my})
104                         if ind != -1 {
105                                 t.SetActive(ind)
106                         }
107                 case tcell.WheelUp:
108                         if my == t.Y {
109                                 t.Scroll(4)
110                                 return
111                         }
112                 case tcell.WheelDown:
113                         if my == t.Y {
114                                 t.Scroll(-4)
115                                 return
116                         }
117                 }
118         }
119         t.List[t.Active()].HandleEvent(event)
120 }
121
122 // Display updates the names and then displays the tab bar
123 func (t *TabList) Display() {
124         t.UpdateNames()
125         if len(t.List) > 1 {
126                 t.TabWindow.Display()
127         }
128 }
129
130 // Tabs is the global tab list
131 var Tabs *TabList
132
133 func InitTabs(bufs []*buffer.Buffer) {
134         Tabs = NewTabList(bufs)
135 }
136
137 func MainTab() *Tab {
138         return Tabs.List[Tabs.Active()]
139 }
140
141 // A Tab represents a single tab
142 // It consists of a list of edit panes (the open buffers),
143 // a split tree (stored as just the root node), and a uiwindow
144 // to display the UI elements like the borders between splits
145 type Tab struct {
146         *views.Node
147         *display.UIWindow
148         Panes  []Pane
149         active int
150
151         resizing *views.Node // node currently being resized
152 }
153
154 // NewTabFromBuffer creates a new tab from the given buffer
155 func NewTabFromBuffer(x, y, width, height int, b *buffer.Buffer) *Tab {
156         t := new(Tab)
157         t.Node = views.NewRoot(x, y, width, height)
158         t.UIWindow = display.NewUIWindow(t.Node)
159
160         e := NewBufEditPane(x, y, width, height, b)
161         e.splitID = t.ID()
162
163         t.Panes = append(t.Panes, e)
164         return t
165 }
166
167 // HandleEvent takes a tcell event and usually dispatches it to the current
168 // active pane. However if the event is a resize or a mouse event where the user
169 // is interacting with the UI (resizing splits) then the event is consumed here
170 // If the event is a mouse event in a pane, that pane will become active and get
171 // the event
172 func (t *Tab) HandleEvent(event tcell.Event) {
173         switch e := event.(type) {
174         case *tcell.EventMouse:
175                 mx, my := e.Position()
176                 switch e.Buttons() {
177                 case tcell.Button1:
178                         resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
179                         if t.resizing != nil {
180                                 var size int
181                                 if t.resizing.Kind == views.STVert {
182                                         size = mx - t.resizing.X
183                                 } else {
184                                         size = my - t.resizing.Y + 1
185                                 }
186                                 t.resizing.ResizeSplit(size)
187                                 t.Resize()
188                                 return
189                         }
190
191                         if resizeID != 0 {
192                                 t.resizing = t.GetNode(uint64(resizeID))
193                                 return
194                         }
195
196                         for i, p := range t.Panes {
197                                 v := p.GetView()
198                                 inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
199                                 if inpane {
200                                         t.SetActive(i)
201                                         return
202                                 }
203                         }
204                 case tcell.ButtonNone:
205                         t.resizing = nil
206                 default:
207                         for _, p := range t.Panes {
208                                 v := p.GetView()
209                                 inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
210                                 if inpane {
211                                         p.HandleEvent(event)
212                                         return
213                                 }
214                         }
215                 }
216
217         }
218         t.Panes[t.active].HandleEvent(event)
219 }
220
221 // SetActive changes the currently active pane to the specified index
222 func (t *Tab) SetActive(i int) {
223         t.active = i
224         for j, p := range t.Panes {
225                 if j == i {
226                         p.SetActive(true)
227                 } else {
228                         p.SetActive(false)
229                 }
230         }
231 }
232
233 // GetPane returns the pane with the given split index
234 func (t *Tab) GetPane(splitid uint64) int {
235         for i, p := range t.Panes {
236                 if p.ID() == splitid {
237                         return i
238                 }
239         }
240         return 0
241 }
242
243 // Remove pane removes the pane with the given index
244 func (t *Tab) RemovePane(i int) {
245         copy(t.Panes[i:], t.Panes[i+1:])
246         t.Panes[len(t.Panes)-1] = nil
247         t.Panes = t.Panes[:len(t.Panes)-1]
248 }
249
250 // Resize resizes all panes according to their corresponding split nodes
251 func (t *Tab) Resize() {
252         for _, p := range t.Panes {
253                 n := t.GetNode(p.ID())
254                 pv := p.GetView()
255                 offset := 0
256                 if n.X != 0 {
257                         offset = 1
258                 }
259                 pv.X, pv.Y = n.X+offset, n.Y
260                 p.SetView(pv)
261                 p.Resize(n.W-offset, n.H)
262         }
263 }
264
265 // CurPane returns the currently active pane
266 func (t *Tab) CurPane() Pane {
267         return t.Panes[t.active]
268 }