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