1-- @docclass
2UIChatTabBar = extends(UIWidget, "UIChatTabBar")
3
4-- private functions
5local function onTabClick(tab)
6 tab.tabBar:selectTab(tab)
7end
8
9local function updateMargins(tabBar)
10 if #tabBar.tabs == 0 then return end
11
12 local currentMargin = 0
13 for i = 1, #tabBar.tabs do
14 tabBar.tabs[i]:setMarginLeft(currentMargin)
15 currentMargin = currentMargin + tabBar.tabSpacing + tabBar.tabs[i]:getWidth()
16 end
17end
18
19local function updateNavigation(tabBar)
20 if tabBar.prevNavigation then
21 if #tabBar.preTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= 1 then
22 tabBar.prevNavigation:enable()
23 else
24 tabBar.prevNavigation:disable()
25 end
26 end
27
28 if tabBar.nextNavigation then
29 if #tabBar.postTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= #tabBar.tabs then
30 tabBar.nextNavigation:enable()
31 else
32 tabBar.nextNavigation:disable()
33 end
34 end
35end
36
37local function updateIndexes(tabBar, tab, xoff)
38 local tabs = tabBar.tabs
39 local currentMargin = 0
40 local prevIndex = table.find(tabs, tab)
41 local newIndex = prevIndex
42 local xmid = xoff + tab:getWidth()/2
43 for i = 1, #tabs do
44 local nextTab = tabs[i]
45 if xmid >= currentMargin + nextTab:getWidth()/2 then
46 newIndex = table.find(tabs, nextTab)
47 end
48 currentMargin = currentMargin + tabBar.tabSpacing * (i - 1) + tabBar.tabs[i]:getWidth()
49 end
50 if newIndex ~= prevIndex then
51 table.remove(tabs, table.find(tabs, tab))
52 table.insert(tabs, newIndex, tab)
53 end
54 updateNavigation(tabBar)
55end
56
57local function getMaxMargin(tabBar, tab)
58 if #tabBar.tabs == 0 then return 0 end
59
60 local maxMargin = 0
61 for i = 1, #tabBar.tabs do
62 if tabBar.tabs[i] ~= tab then
63 maxMargin = maxMargin + tabBar.tabs[i]:getWidth()
64 end
65 end
66 return maxMargin + tabBar.tabSpacing * (#tabBar.tabs - 1)
67end
68
69local function updateTabs(tabBar)
70 if #tabBar.postTabs > 0 then
71 local i = 1
72 while i <= #tabBar.postTabs do
73 local tab = tabBar.postTabs[i]
74 if getMaxMargin(tabBar) + tab:getWidth() > tabBar:getWidth() then
75 break
76 end
77
78 table.remove(tabBar.postTabs, i)
79 table.insert(tabBar.tabs, tab)
80 tab:setVisible(true)
81 end
82 end
83 if #tabBar.preTabs > 0 then
84 for i = #tabBar.preTabs, 1, -1 do
85 local tab = tabBar.preTabs[i]
86 if getMaxMargin(tabBar) + tab:getWidth() > tabBar:getWidth() then
87 break
88 end
89
90 table.remove(tabBar.preTabs, i)
91 table.insert(tabBar.tabs, 1, tab)
92 tab:setVisible(true)
93 end
94 end
95 updateNavigation(tabBar)
96 updateMargins(tabBar)
97
98 if not tabBar.currentTab and #tabBar.tabs > 0 then
99 tabBar:selectTab(tabBar.tabs[1])
100 end
101end
102
103local function hideTabs(tabBar, fromBack, toArray, width)
104 while #tabBar.tabs > 0 and getMaxMargin(tabBar) + width > tabBar:getWidth() do
105 local index = fromBack and #tabBar.tabs or 1
106 local tab = tabBar.tabs[index]
107 table.remove(tabBar.tabs, index)
108 if fromBack then
109 table.insert(toArray, 1, tab)
110 else
111 table.insert(toArray, tab)
112 end
113 if tabBar.currentTab == tab then
114 if #tabBar.tabs > 0 then
115 tabBar:selectTab(tabBar.tabs[#tabBar.tabs])
116 else
117 tabBar.currentTab:setChecked(false)
118 tabBar.currentTab = nil
119 end
120 end
121 tab:setVisible(false)
122 end
123end
124
125local function showPreTab(tabBar)
126 if #tabBar.preTabs == 0 then
127 return nil
128 end
129
130 local tmpTab = tabBar.preTabs[#tabBar.preTabs]
131 hideTabs(tabBar, true, tabBar.postTabs, tmpTab:getWidth())
132
133 table.remove(tabBar.preTabs, #tabBar.preTabs)
134 table.insert(tabBar.tabs, 1, tmpTab)
135 tmpTab:setVisible(true)
136 return tmpTab
137end
138
139local function showPostTab(tabBar)
140 if #tabBar.postTabs == 0 then
141 return nil
142 end
143
144 local tmpTab = tabBar.postTabs[1]
145 hideTabs(tabBar, false, tabBar.preTabs, tmpTab:getWidth())
146
147 table.remove(tabBar.postTabs, 1)
148 table.insert(tabBar.tabs, tmpTab)
149 tmpTab:setVisible(true)
150 return tmpTab
151end
152
153local function onTabMousePress(tab, mousePos, mouseButton)
154 if mouseButton == MouseRightButton then
155 if tab.menuCallback then tab.menuCallback(tab, mousePos, mouseButton) end
156 return true
157 end
158end
159
160local function onTabDragEnter(tab, mousePos)
161 tab:raise()
162 tab.hotSpot = mousePos.x - tab:getMarginLeft()
163 tab.tabBar.selected = tab
164 return true
165end
166
167local function onTabDragLeave(tab)
168 updateMargins(tab.tabBar)
169 tab.tabBar.selected = nil
170 return true
171end
172
173local function onTabDragMove(tab, mousePos, mouseMoved)
174 if tab == tab.tabBar.selected then
175 local xoff = mousePos.x - tab.hotSpot
176
177 -- update indexes
178 updateIndexes(tab.tabBar, tab, xoff)
179 updateIndexes(tab.tabBar, tab, xoff)
180
181 -- update margins
182 updateMargins(tab.tabBar)
183 xoff = math.max(xoff, 0)
184 xoff = math.min(xoff, getMaxMargin(tab.tabBar, tab))
185 tab:setMarginLeft(xoff)
186 end
187end
188
189local function tabBlink(tab, steps, maxSteps, interval)
190 local steps = steps or 0
191 local maxSteps = maxSteps or 4
192 local interval = interval or 500
193 tab:setOn(not tab:isOn())
194
195 removeEvent(tab.blinkEvent)
196 if steps < maxSteps then
197 tab.blinkEvent = scheduleEvent(function() tabBlink(tab, steps + 1, maxSteps, interval) end, interval)
198 else
199 tab:setOn(true)
200 tab.blinkEvent = nil
201 end
202end
203
204-- public functions
205function UIChatTabBar.create()
206 local tabbar = UIChatTabBar.internalCreate()
207 tabbar:setFocusable(false)
208 tabbar.tabs = {}
209 tabbar.selected = nil -- dragged tab
210 tabbar.tabSpacing = 0
211 tabbar.tabsMoveable = false
212 tabbar.preTabs = {}
213 tabbar.postTabs = {}
214 tabbar.prevNavigation = nil
215 tabbar.nextNavigation = nil
216 tabbar.onGeometryChange = function()
217 hideTabs(tabbar, true, tabbar.postTabs, 0)
218 updateTabs(tabbar)
219 end
220 return tabbar
221end
222
223function UIChatTabBar:onDestroy()
224 if self.prevNavigation then
225 self.prevNavigation:disable()
226 end
227
228 if self.nextNavigation then
229 self.nextNavigation:disable()
230 end
231
232 self.nextNavigation = nil
233 self.prevNavigation = nil
234end
235
236function UIChatTabBar:setContentWidget(widget)
237 self.contentWidget = widget
238 if #self.tabs > 0 then
239 self.contentWidget:addChild(self.tabs[1].tabPanel)
240 end
241end
242
243function UIChatTabBar:setTabSpacing(tabSpacing)
244 self.tabSpacing = tabSpacing
245 updateMargins(self)
246end
247
248function UIChatTabBar:addTab(text, panel, menuCallback)
249 if panel == nil then
250 panel = g_ui.createWidget(self:getStyleName() .. 'Panel')
251 panel:setId('tabPanel')
252 end
253
254 local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self)
255 panel.isTab = true
256 tab.tabPanel = panel
257 tab.tabBar = self
258 tab:setId('tab')
259 tab:setDraggable(self.tabsMoveable)
260 tab:setText(text)
261 tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight())
262 tab.menuCallback = menuCallback or nil
263 tab.onClick = onTabClick
264 tab.onMousePress = onTabMousePress
265 tab.onDragEnter = onTabDragEnter
266 tab.onDragLeave = onTabDragLeave
267 tab.onDragMove = onTabDragMove
268 tab.onDestroy = function() tab.tabPanel:destroy() end
269
270 if #self.tabs == 0 then
271 self:selectTab(tab)
272 tab:setMarginLeft(0)
273 table.insert(self.tabs, tab)
274 else
275 local newMargin = self.tabSpacing * #self.tabs
276 for i = 1, #self.tabs do
277 newMargin = newMargin + self.tabs[i]:getWidth()
278 end
279 tab:setMarginLeft(newMargin)
280
281 hideTabs(self, true, self.postTabs, tab:getWidth())
282 table.insert(self.tabs, tab)
283 if #self.tabs == 1 then
284 self:selectTab(tab)
285 end
286 updateMargins(self)
287 end
288
289 updateNavigation(self)
290 return tab
291end
292
293-- Additional function to move the tab by lua
294function UIChatTabBar:moveTab(tab, units)
295 local index = table.find(self.tabs, tab)
296 if index == nil then return end
297
298 local focus = false
299 if self.currentTab == tab then
300 self:selectPrevTab()
301 focus = true
302 end
303
304 table.remove(self.tabs, index)
305
306 local newIndex = math.min(#self.tabs+1, math.max(index + units, 1))
307 table.insert(self.tabs, newIndex, tab)
308 if focus then self:selectTab(tab) end
309 updateMargins(self)
310 return newIndex
311end
312
313function UIChatTabBar:onStyleApply(styleName, styleNode)
314 if styleNode['movable'] then
315 self.tabsMoveable = styleNode['movable']
316 end
317 if styleNode['tab-spacing'] then
318 self:setTabSpacing(styleNode['tab-spacing'])
319 end
320end
321
322function UIChatTabBar:removeTab(tab)
323 local tabTables = {self.tabs, self.preTabs, self.postTabs}
324 local index = nil
325 local tabTable = nil
326 for i = 1, #tabTables do
327 index = table.find(tabTables[i], tab)
328 if index ~= nil then
329 tabTable = tabTables[i]
330 break
331 end
332 end
333
334 if tabTable == nil then
335 return
336 end
337
338 if self.currentTab == tab then
339 self:selectPrevTab()
340 if #self.tabs == 1 then
341 self.currentTab = nil
342 end
343 end
344 table.remove(tabTable, index)
345 if tab.blinkEvent then
346 removeEvent(tab.blinkEvent)
347 end
348 tab:destroy()
349 updateTabs(self)
350end
351
352function UIChatTabBar:getTab(text)
353 for k,tab in pairs(self.tabs) do
354 if tab:getText():lower() == text:lower() then
355 return tab
356 end
357 end
358 for k,tab in pairs(self.preTabs) do
359 if tab:getText():lower() == text:lower() then
360 return tab
361 end
362 end
363 for k,tab in pairs(self.postTabs) do
364 if tab:getText():lower() == text:lower() then
365 return tab
366 end
367 end
368end
369
370function UIChatTabBar:selectTab(tab)
371 if self.currentTab == tab then return end
372 if self.contentWidget then
373 local selectedWidget = self.contentWidget:getLastChild()
374 if selectedWidget and selectedWidget.isTab then
375 self.contentWidget:removeChild(selectedWidget)
376 end
377 self.contentWidget:addChild(tab.tabPanel)
378 tab.tabPanel:fill('parent')
379 end
380
381 if self.currentTab then
382 self.currentTab:setChecked(false)
383 end
384 signalcall(self.onTabChange, self, tab)
385 self.currentTab = tab
386 tab:setChecked(true)
387 tab:setOn(false)
388 tab.blinking = false
389
390 local parent = tab:getParent()
391 parent:focusChild(tab, MouseFocusReason)
392 updateNavigation(self)
393end
394
395function UIChatTabBar:selectNextTab()
396 if self.currentTab == nil then
397 return
398 end
399
400 local index = table.find(self.tabs, self.currentTab)
401 if index == nil then
402 return
403 end
404
405 local newIndex = index + 1
406 if newIndex > #self.tabs then
407 if #self.postTabs > 0 then
408 local widget = showPostTab(self)
409 self:selectTab(widget)
410 else
411 if #self.preTabs > 0 then
412 for i = 1, #self.preTabs do
413 showPreTab(self)
414 end
415 end
416
417 self:selectTab(self.tabs[1])
418 end
419 updateTabs(self)
420 return
421 end
422
423 local nextTab = self.tabs[newIndex]
424 if not nextTab then
425 return
426 end
427
428 self:selectTab(nextTab)
429end
430
431function UIChatTabBar:selectPrevTab()
432 if self.currentTab == nil then
433 return
434 end
435
436 local index = table.find(self.tabs, self.currentTab)
437 if index == nil then
438 return
439 end
440
441 local newIndex = index - 1
442 if newIndex <= 0 then
443 if #self.preTabs > 0 then
444 local widget = showPreTab(self)
445 self:selectTab(widget)
446 else
447 if #self.postTabs > 0 then
448 for i = 1, #self.postTabs do
449 showPostTab(self)
450 end
451 end
452
453 self:selectTab(self.tabs[#self.tabs])
454 end
455 updateTabs(self)
456 return
457 end
458
459 local prevTab = self.tabs[newIndex]
460 if not prevTab then
461 return
462 end
463
464 self:selectTab(prevTab)
465end
466
467function UIChatTabBar:blinkTab(tab)
468 if tab:isChecked() then return end
469 if tab.blinking then return end
470 tab.blinking = true
471 tabBlink(tab, 0, 2, 800)
472end
473
474function UIChatTabBar:getTabPanel(tab)
475 return tab.tabPanel
476end
477
478function UIChatTabBar:getCurrentTabPanel()
479 if self.currentTab then
480 return self.currentTab.tabPanel
481 end
482end
483
484function UIChatTabBar:getCurrentTab()
485 return self.currentTab
486end
487
488function UIChatTabBar:setNavigation(prevButton, nextButton)
489 self.prevNavigation = prevButton
490 self.nextNavigation = nextButton
491
492 if self.prevNavigation then
493 self.prevNavigation.onClick = function() self:selectPrevTab() end
494 end
495 if self.nextNavigation then
496 self.nextNavigation.onClick = function() self:selectNextTab() end
497 end
498 updateNavigation(self)
499end
500