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