]> git.lizzy.rs Git - micro.git/blob - cmd/micro/action/tab.go
Almost done terminal emulator
[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 []*Tab
14 }
15
16 func NewTabList(bufs []*buffer.Buffer) *TabList {
17         w, h := screen.Screen.Size()
18         tl := new(TabList)
19         tl.List = make([]*Tab, len(bufs))
20         if len(bufs) > 1 {
21                 for i, b := range bufs {
22                         tl.List[i] = NewTabFromBuffer(0, 1, w, h-2, b)
23                 }
24         } else {
25                 tl.List[0] = NewTabFromBuffer(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].Name())
37         }
38 }
39
40 func (t *TabList) AddTab(p *Tab) {
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].ID() == 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() *Tab {
122         return Tabs.List[Tabs.Active()]
123 }
124
125 // A Tab 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 Tab struct {
130         *views.Node
131         *display.UIWindow
132         Panes  []Pane
133         active int
134
135         resizing *views.Node // node currently being resized
136 }
137
138 func NewTabFromBuffer(x, y, width, height int, b *buffer.Buffer) *Tab {
139         t := new(Tab)
140         t.Node = views.NewRoot(x, y, width, height)
141         t.UIWindow = display.NewUIWindow(t.Node)
142
143         e := NewBufEditPane(x, y, width, height, b)
144         e.splitID = t.ID()
145
146         t.Panes = append(t.Panes, e)
147         return t
148 }
149
150 // HandleEvent takes a tcell event and usually dispatches it to the current
151 // active pane. However if the event is a resize or a mouse event where the user
152 // is interacting with the UI (resizing splits) then the event is consumed here
153 // If the event is a mouse event in a pane, that pane will become active and get
154 // the event
155 func (t *Tab) HandleEvent(event tcell.Event) {
156         switch e := event.(type) {
157         case *tcell.EventMouse:
158                 mx, my := e.Position()
159                 switch e.Buttons() {
160                 case tcell.Button1:
161                         resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
162                         if t.resizing != nil {
163                                 var size int
164                                 if t.resizing.Kind == views.STVert {
165                                         size = mx - t.resizing.X
166                                 } else {
167                                         size = my - t.resizing.Y + 1
168                                 }
169                                 t.resizing.ResizeSplit(size)
170                                 t.Resize()
171                                 return
172                         }
173
174                         if resizeID != 0 {
175                                 t.resizing = t.GetNode(uint64(resizeID))
176                                 return
177                         }
178
179                         for i, p := range t.Panes {
180                                 v := p.GetView()
181                                 inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
182                                 if inpane {
183                                         t.active = i
184                                         p.SetActive(true)
185                                 } else {
186                                         p.SetActive(false)
187                                 }
188                         }
189                 case tcell.ButtonNone:
190                         t.resizing = nil
191                 default:
192                         for _, p := range t.Panes {
193                                 v := p.GetView()
194                                 inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
195                                 if inpane {
196                                         p.HandleEvent(event)
197                                         return
198                                 }
199                         }
200                 }
201
202         }
203         t.Panes[t.active].HandleEvent(event)
204 }
205
206 // SetActive changes the currently active pane to the specified index
207 func (t *Tab) SetActive(i int) {
208         t.active = i
209         for j, p := range t.Panes {
210                 if j == i {
211                         p.SetActive(true)
212                 } else {
213                         p.SetActive(false)
214                 }
215         }
216 }
217
218 // GetPane returns the pane with the given split index
219 func (t *Tab) GetPane(splitid uint64) int {
220         for i, p := range t.Panes {
221                 if p.ID() == splitid {
222                         return i
223                 }
224         }
225         return 0
226 }
227
228 // Remove pane removes the pane with the given index
229 func (t *Tab) RemovePane(i int) {
230         copy(t.Panes[i:], t.Panes[i+1:])
231         t.Panes[len(t.Panes)-1] = nil
232         t.Panes = t.Panes[:len(t.Panes)-1]
233 }
234
235 // Resize resizes all panes according to their corresponding split nodes
236 func (t *Tab) Resize() {
237         for _, p := range t.Panes {
238                 n := t.GetNode(p.ID())
239                 pv := p.GetView()
240                 offset := 0
241                 if n.X != 0 {
242                         offset = 1
243                 }
244                 pv.X, pv.Y = n.X+offset, n.Y
245                 p.SetView(pv)
246                 p.Resize(n.W-offset, n.H)
247         }
248 }
249
250 // CurPane returns the currently active pane
251 func (t *Tab) CurPane() Pane {
252         return t.Panes[t.active]
253 }