Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/modules/corelib/ui/uiminiwindowcontainer.lua +213-0 View file
@@ -0,0 +1,213 @@
1+-- @docclass
2+UIMiniWindowContainer = extends(UIWidget, "UIMiniWindowContainer")
3+
4+function UIMiniWindowContainer.create()
5+ local container = UIMiniWindowContainer.internalCreate()
6+ container.scheduledWidgets = {}
7+ container:setFocusable(false)
8+ container:setPhantom(true)
9+ return container
10+end
11+
12+-- TODO: connect to window onResize event
13+-- TODO: try to resize another widget?
14+-- TODO: try to find another panel?
15+function UIMiniWindowContainer:fitAll(noRemoveChild)
16+ if not self:isVisible() then
17+ return
18+ end
19+
20+ if not noRemoveChild then
21+ local children = self:getChildren()
22+ if #children > 0 then
23+ noRemoveChild = children[#children]
24+ else
25+ return
26+ end
27+ end
28+
29+ local sumHeight = 0
30+ local children = self:getChildren()
31+ for i=1,#children do
32+ if children[i]:isVisible() then
33+ sumHeight = sumHeight + children[i]:getHeight()
34+ end
35+ end
36+
37+ local selfHeight = self:getHeight() - (self:getPaddingTop() + self:getPaddingBottom())
38+ if sumHeight <= selfHeight then
39+ return
40+ end
41+
42+ local removeChildren = {}
43+
44+ -- try to resize noRemoveChild
45+ local maximumHeight = selfHeight - (sumHeight - noRemoveChild:getHeight())
46+ if noRemoveChild:isResizeable() and noRemoveChild:getMinimumHeight() <= maximumHeight then
47+ sumHeight = sumHeight - noRemoveChild:getHeight() + maximumHeight
48+ addEvent(function() noRemoveChild:setHeight(maximumHeight) end)
49+ end
50+
51+ -- try to remove no-save widget
52+ for i=#children,1,-1 do
53+ if sumHeight <= selfHeight then
54+ break
55+ end
56+
57+ local child = children[i]
58+ if child ~= noRemoveChild and not child.save then
59+ local childHeight = child:getHeight()
60+ sumHeight = sumHeight - childHeight
61+ table.insert(removeChildren, child)
62+ end
63+ end
64+
65+ -- try to remove save widget, not forceOpen
66+ for i=#children,1,-1 do
67+ if sumHeight <= selfHeight then
68+ break
69+ end
70+
71+ local child = children[i]
72+ if child ~= noRemoveChild and child:isVisible() and not child.forceOpen then
73+ local childHeight = child:getHeight()
74+ sumHeight = sumHeight - childHeight
75+ table.insert(removeChildren, child)
76+ end
77+ end
78+
79+ -- try to remove save widget
80+ for i=#children,1,-1 do
81+ if sumHeight <= selfHeight then
82+ break
83+ end
84+
85+ local child = children[i]
86+ if child ~= noRemoveChild and child:isVisible() then
87+ local childHeight = child:getHeight() - 50
88+ sumHeight = sumHeight - childHeight
89+ table.insert(removeChildren, child)
90+ end
91+ end
92+
93+ -- close widgets
94+ for i=1,#removeChildren do
95+ if removeChildren[i].forceOpen then
96+ removeChildren[i]:minimize(true)
97+ else
98+ removeChildren[i]:close()
99+ end
100+ end
101+end
102+
103+function UIMiniWindowContainer:onDrop(widget, mousePos)
104+ if widget.UIMiniWindowContainer then
105+ local oldParent = widget:getParent()
106+ if oldParent == self then
107+ return true
108+ end
109+
110+ if oldParent then
111+ oldParent:removeChild(widget)
112+ end
113+
114+ if widget.movedWidget then
115+ local index = self:getChildIndex(widget.movedWidget)
116+ self:insertChild(index + widget.movedIndex, widget)
117+ else
118+ self:addChild(widget)
119+ end
120+
121+ self:fitAll(widget)
122+ return true
123+ end
124+end
125+
126+function UIMiniWindowContainer:moveTo(newPanel)
127+ if not newPanel or newPanel == self then
128+ return
129+ end
130+ local children = self:getChildByIndex(1)
131+ while children do
132+ newPanel:addChild(children)
133+ children = self:getChildByIndex(1)
134+ end
135+ newPanel:fitAll()
136+end
137+
138+function UIMiniWindowContainer:swapInsert(widget, index)
139+ local oldParent = widget:getParent()
140+ local oldIndex = self:getChildIndex(widget)
141+
142+ if oldParent == self and oldIndex ~= index then
143+ local oldWidget = self:getChildByIndex(index)
144+ if oldWidget then
145+ self:removeChild(oldWidget)
146+ self:insertChild(oldIndex, oldWidget)
147+ end
148+ self:removeChild(widget)
149+ self:insertChild(index, widget)
150+ end
151+end
152+
153+function UIMiniWindowContainer:scheduleInsert(widget, index)
154+ if index - 1 > self:getChildCount() then
155+ if self.scheduledWidgets[index] then
156+ pdebug('replacing scheduled widget id ' .. widget:getId())
157+ end
158+ self.scheduledWidgets[index] = widget
159+ else
160+ local oldParent = widget:getParent()
161+ if oldParent ~= self then
162+ if oldParent then
163+ oldParent:removeChild(widget)
164+ end
165+ self:insertChild(index, widget)
166+
167+ while true do
168+ local placed = false
169+ for nIndex,nWidget in pairs(self.scheduledWidgets) do
170+ if nIndex - 1 <= self:getChildCount() then
171+ self:insertChild(nIndex, nWidget)
172+ self.scheduledWidgets[nIndex] = nil
173+ placed = true
174+ break
175+ end
176+ end
177+ if not placed then break end
178+ end
179+
180+ end
181+ end
182+end
183+
184+function UIMiniWindowContainer:order()
185+ local children = self:getChildren()
186+ for i=1,#children do
187+ if not children[i].miniLoaded then return end
188+ end
189+
190+ for i=1,#children do
191+ if children[i].miniIndex then
192+ self:swapInsert(children[i], children[i].miniIndex)
193+ elseif children[i].autoOpen then
194+ self:swapInsert(children[i], children[i].autoOpen)
195+ end
196+ end
197+end
198+
199+function UIMiniWindowContainer:saveChildren()
200+ local children = self:getChildren()
201+ local ignoreIndex = 0
202+ for i=1,#children do
203+ if children[i].save then
204+ children[i]:saveParentIndex(self:getId(), i - ignoreIndex)
205+ else
206+ ignoreIndex = ignoreIndex + 1
207+ end
208+ end
209+end
210+
211+function UIMiniWindowContainer:onGeometryChange()
212+ self:fitAll()
213+end
A RD_Client/modules/corelib/ui/uimovabletabbar.lua +505-0 View file
@@ -0,0 +1,505 @@
1+-- @docclass
2+UIMoveableTabBar = extends(UIWidget, "UIMoveableTabBar")
3+
4+-- private functions
5+local function onTabClick(tab)
6+ tab.tabBar:selectTab(tab)
7+end
8+
9+local 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
17+end
18+
19+local 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
35+end
36+
37+local 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)
55+end
56+
57+local 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)
67+end
68+
69+local 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+ if not tabBar.currentTab and #tabBar.tabs > 0 then
98+ tabBar:selectTab(tabBar.tabs[1])
99+ end
100+end
101+
102+local function hideTabs(tabBar, fromBack, toArray, width)
103+ while #tabBar.tabs > 0 and getMaxMargin(tabBar) + width > tabBar:getWidth() do
104+ local index = fromBack and #tabBar.tabs or 1
105+ local tab = tabBar.tabs[index]
106+ table.remove(tabBar.tabs, index)
107+ if fromBack then
108+ table.insert(toArray, 1, tab)
109+ else
110+ table.insert(toArray, tab)
111+ end
112+ if tabBar.currentTab == tab then
113+ if #tabBar.tabs > 0 then
114+ tabBar:selectTab(tabBar.tabs[#tabBar.tabs])
115+ else
116+ tabBar.currentTab:setChecked(false)
117+ tabBar.currentTab = nil
118+ end
119+ end
120+ tab:setVisible(false)
121+ end
122+end
123+
124+local function showPreTab(tabBar)
125+ if #tabBar.preTabs == 0 then
126+ return nil
127+ end
128+
129+ local tmpTab = tabBar.preTabs[#tabBar.preTabs]
130+ hideTabs(tabBar, true, tabBar.postTabs, tmpTab:getWidth())
131+
132+ table.remove(tabBar.preTabs, #tabBar.preTabs)
133+ table.insert(tabBar.tabs, 1, tmpTab)
134+ tmpTab:setVisible(true)
135+ return tmpTab
136+end
137+
138+local function showPostTab(tabBar)
139+ if #tabBar.postTabs == 0 then
140+ return nil
141+ end
142+
143+ local tmpTab = tabBar.postTabs[1]
144+ hideTabs(tabBar, false, tabBar.preTabs, tmpTab:getWidth())
145+
146+ table.remove(tabBar.postTabs, 1)
147+ table.insert(tabBar.tabs, tmpTab)
148+ tmpTab:setVisible(true)
149+ return tmpTab
150+end
151+
152+local function onTabMousePress(tab, mousePos, mouseButton)
153+ if mouseButton == MouseRightButton then
154+ if tab.menuCallback then tab.menuCallback(tab, mousePos, mouseButton) end
155+ return true
156+ end
157+end
158+
159+local function onTabDragEnter(tab, mousePos)
160+ tab:raise()
161+ tab.hotSpot = mousePos.x - tab:getMarginLeft()
162+ tab.tabBar.selected = tab
163+ return true
164+end
165+
166+local function onTabDragLeave(tab)
167+ updateMargins(tab.tabBar)
168+ tab.tabBar.selected = nil
169+ return true
170+end
171+
172+local function onTabDragMove(tab, mousePos, mouseMoved)
173+ if tab == tab.tabBar.selected then
174+ local xoff = mousePos.x - tab.hotSpot
175+
176+ -- update indexes
177+ updateIndexes(tab.tabBar, tab, xoff)
178+ updateIndexes(tab.tabBar, tab, xoff)
179+
180+ -- update margins
181+ updateMargins(tab.tabBar)
182+ xoff = math.max(xoff, 0)
183+ xoff = math.min(xoff, getMaxMargin(tab.tabBar, tab))
184+ tab:setMarginLeft(xoff)
185+ end
186+end
187+
188+local function tabBlink(tab, step)
189+ local step = step or 0
190+ tab:setOn(not tab:isOn())
191+
192+ removeEvent(tab.blinkEvent)
193+ if step < 4 then
194+ tab.blinkEvent = scheduleEvent(function() tabBlink(tab, step+1) end, 500)
195+ else
196+ tab:setOn(true)
197+ tab.blinkEvent = nil
198+ end
199+end
200+
201+-- public functions
202+function UIMoveableTabBar.create()
203+ local tabbar = UIMoveableTabBar.internalCreate()
204+ tabbar:setFocusable(false)
205+ tabbar.tabs = {}
206+ tabbar.selected = nil -- dragged tab
207+ tabbar.tabSpacing = 0
208+ tabbar.tabsMoveable = false
209+ tabbar.preTabs = {}
210+ tabbar.postTabs = {}
211+ tabbar.prevNavigation = nil
212+ tabbar.nextNavigation = nil
213+ tabbar.onGeometryChange = function()
214+ hideTabs(tabbar, true, tabbar.postTabs, 0)
215+ updateTabs(tabbar)
216+ end
217+ return tabbar
218+end
219+
220+function UIMoveableTabBar:onDestroy()
221+ if self.prevNavigation then
222+ self.prevNavigation:disable()
223+ end
224+
225+ if self.nextNavigation then
226+ self.nextNavigation:disable()
227+ end
228+
229+ self.nextNavigation = nil
230+ self.prevNavigation = nil
231+end
232+
233+function UIMoveableTabBar:setContentWidget(widget)
234+ self.contentWidget = widget
235+ if #self.tabs > 0 then
236+ self.contentWidget:addChild(self.tabs[1].tabPanel)
237+ end
238+end
239+
240+function UIMoveableTabBar:setTabSpacing(tabSpacing)
241+ self.tabSpacing = tabSpacing
242+ updateMargins(self)
243+end
244+
245+function UIMoveableTabBar:addTab(text, panel, menuCallback)
246+ if panel == nil then
247+ panel = g_ui.createWidget(self:getStyleName() .. 'Panel')
248+ panel:setId('tabPanel')
249+ end
250+
251+ local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self)
252+ panel.isTab = true
253+ tab.tabPanel = panel
254+ tab.tabBar = self
255+ tab:setId('tab')
256+ tab:setDraggable(self.tabsMoveable)
257+ tab:setText(text)
258+ tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight())
259+ tab.menuCallback = menuCallback or nil
260+ tab.onClick = onTabClick
261+ tab.onMousePress = onTabMousePress
262+ tab.onDragEnter = onTabDragEnter
263+ tab.onDragLeave = onTabDragLeave
264+ tab.onDragMove = onTabDragMove
265+ tab.onDestroy = function() tab.tabPanel:destroy() end
266+
267+ if #self.tabs == 0 then
268+ self:selectTab(tab)
269+ tab:setMarginLeft(0)
270+ table.insert(self.tabs, tab)
271+ else
272+ local newMargin = self.tabSpacing * #self.tabs
273+ for i = 1, #self.tabs do
274+ newMargin = newMargin + self.tabs[i]:getWidth()
275+ end
276+ tab:setMarginLeft(newMargin)
277+
278+ hideTabs(self, true, self.postTabs, tab:getWidth())
279+ table.insert(self.tabs, tab)
280+ if #self.tabs == 1 then
281+ self:selectTab(tab)
282+ end
283+ updateMargins(self)
284+ end
285+
286+ updateNavigation(self)
287+ return tab
288+end
289+
290+-- Additional function to move the tab by lua
291+function UIMoveableTabBar:moveTab(tab, units)
292+ local index = table.find(self.tabs, tab)
293+ if index == nil then return end
294+
295+ local focus = false
296+ if self.currentTab == tab then
297+ self:selectPrevTab()
298+ focus = true
299+ end
300+
301+ table.remove(self.tabs, index)
302+
303+ local newIndex = math.min(#self.tabs+1, math.max(index + units, 1))
304+ table.insert(self.tabs, newIndex, tab)
305+ if focus then self:selectTab(tab) end
306+ updateMargins(self)
307+ return newIndex
308+end
309+
310+function UIMoveableTabBar:onStyleApply(styleName, styleNode)
311+ if styleNode['movable'] then
312+ self.tabsMoveable = styleNode['movable']
313+ end
314+ if styleNode['tab-spacing'] then
315+ self:setTabSpacing(styleNode['tab-spacing'])
316+ end
317+end
318+
319+function UIMoveableTabBar:clearTabs()
320+ while #self.tabs > 0 do
321+ self:removeTab(self.tabs[#self.tabs])
322+ end
323+end
324+
325+function UIMoveableTabBar:removeTab(tab)
326+ local tabTables = {self.tabs, self.preTabs, self.postTabs}
327+ local index = nil
328+ local tabTable = nil
329+ for i = 1, #tabTables do
330+ index = table.find(tabTables[i], tab)
331+ if index ~= nil then
332+ tabTable = tabTables[i]
333+ break
334+ end
335+ end
336+
337+ if tabTable == nil then
338+ return
339+ end
340+ table.remove(tabTable, index)
341+ if self.currentTab == tab then
342+ self:selectPrevTab()
343+ if #self.tabs == 1 then
344+ self.currentTab = nil
345+ end
346+ end
347+ if tab.blinkEvent then
348+ removeEvent(tab.blinkEvent)
349+ end
350+ updateTabs(self)
351+ tab:destroy()
352+end
353+
354+function UIMoveableTabBar:getTab(text)
355+ for k,tab in pairs(self.tabs) do
356+ if tab:getText():lower() == text:lower() then
357+ return tab
358+ end
359+ end
360+ for k,tab in pairs(self.preTabs) do
361+ if tab:getText():lower() == text:lower() then
362+ return tab
363+ end
364+ end
365+ for k,tab in pairs(self.postTabs) do
366+ if tab:getText():lower() == text:lower() then
367+ return tab
368+ end
369+ end
370+end
371+
372+function UIMoveableTabBar:selectTab(tab)
373+ if self.currentTab == tab then return end
374+ if self.contentWidget then
375+ local selectedWidget = self.contentWidget:getLastChild()
376+ if selectedWidget and selectedWidget.isTab then
377+ self.contentWidget:removeChild(selectedWidget)
378+ end
379+ self.contentWidget:addChild(tab.tabPanel)
380+ tab.tabPanel:fill('parent')
381+ end
382+
383+ if self.currentTab then
384+ self.currentTab:setChecked(false)
385+ end
386+ signalcall(self.onTabChange, self, tab)
387+ self.currentTab = tab
388+ tab:setChecked(true)
389+ tab:setOn(false)
390+ tab.blinking = false
391+
392+ if tab.blinkEvent then
393+ removeEvent(tab.blinkEvent)
394+ tab.blinkEvent = nil
395+ end
396+
397+ local parent = tab:getParent()
398+ parent:focusChild(tab, MouseFocusReason)
399+ updateNavigation(self)
400+end
401+
402+function UIMoveableTabBar:selectNextTab()
403+ if self.currentTab == nil then
404+ return
405+ end
406+
407+ local index = table.find(self.tabs, self.currentTab)
408+ if index == nil then
409+ return
410+ end
411+
412+ local newIndex = index + 1
413+ if newIndex > #self.tabs then
414+ if #self.postTabs > 0 then
415+ local widget = showPostTab(self)
416+ self:selectTab(widget)
417+ else
418+ if #self.preTabs > 0 then
419+ for i = 1, #self.preTabs do
420+ showPreTab(self)
421+ end
422+ end
423+
424+ self:selectTab(self.tabs[1])
425+ end
426+ updateTabs(self)
427+ return
428+ end
429+
430+ local nextTab = self.tabs[newIndex]
431+ if not nextTab then
432+ return
433+ end
434+
435+ self:selectTab(nextTab)
436+end
437+
438+function UIMoveableTabBar:selectPrevTab()
439+ if self.currentTab == nil then
440+ return
441+ end
442+
443+ local index = table.find(self.tabs, self.currentTab)
444+ if index == nil then
445+ return
446+ end
447+
448+ local newIndex = index - 1
449+ if newIndex <= 0 then
450+ if #self.preTabs > 0 then
451+ local widget = showPreTab(self)
452+ self:selectTab(widget)
453+ else
454+ if #self.postTabs > 0 then
455+ for i = 1, #self.postTabs do
456+ showPostTab(self)
457+ end
458+ end
459+
460+ self:selectTab(self.tabs[#self.tabs])
461+ end
462+ updateTabs(self)
463+ return
464+ end
465+
466+ local prevTab = self.tabs[newIndex]
467+ if not prevTab then
468+ return
469+ end
470+
471+ self:selectTab(prevTab)
472+end
473+
474+function UIMoveableTabBar:blinkTab(tab)
475+ if tab:isChecked() then return end
476+ tab.blinking = true
477+ tabBlink(tab)
478+end
479+
480+function UIMoveableTabBar:getTabPanel(tab)
481+ return tab.tabPanel
482+end
483+
484+function UIMoveableTabBar:getCurrentTabPanel()
485+ if self.currentTab then
486+ return self.currentTab.tabPanel
487+ end
488+end
489+
490+function UIMoveableTabBar:getCurrentTab()
491+ return self.currentTab
492+end
493+
494+function UIMoveableTabBar:setNavigation(prevButton, nextButton)
495+ self.prevNavigation = prevButton
496+ self.nextNavigation = nextButton
497+
498+ if self.prevNavigation then
499+ self.prevNavigation.onClick = function() self:selectPrevTab() end
500+ end
501+ if self.nextNavigation then
502+ self.nextNavigation.onClick = function() self:selectNextTab() end
503+ end
504+ updateNavigation(self)
505+end
A RD_Client/modules/corelib/ui/uipopupmenu.lua +122-0 View file
@@ -0,0 +1,122 @@
1+-- @docclass
2+UIPopupMenu = extends(UIWidget, "UIPopupMenu")
3+
4+local currentMenu
5+
6+function UIPopupMenu.create()
7+ local menu = UIPopupMenu.internalCreate()
8+ local layout = UIVerticalLayout.create(menu)
9+ layout:setFitChildren(true)
10+ menu:setLayout(layout)
11+ menu.isGameMenu = false
12+ return menu
13+end
14+
15+function UIPopupMenu:display(pos)
16+ -- don't display if not options was added
17+ if self:getChildCount() == 0 then
18+ self:destroy()
19+ return
20+ end
21+
22+ if g_ui.isMouseGrabbed() then
23+ self:destroy()
24+ return
25+ end
26+
27+ if currentMenu then
28+ currentMenu:destroy()
29+ end
30+
31+ if pos == nil then
32+ pos = g_window.getMousePosition()
33+ end
34+
35+ rootWidget:addChild(self)
36+ self:setPosition(pos)
37+ self:grabMouse()
38+ self:focus()
39+ --self:grabKeyboard()
40+ currentMenu = self
41+end
42+
43+function UIPopupMenu:onGeometryChange(oldRect, newRect)
44+ local parent = self:getParent()
45+ if not parent then return end
46+ local ymax = parent:getY() + parent:getHeight()
47+ local xmax = parent:getX() + parent:getWidth()
48+ if newRect.y + newRect.height > ymax then
49+ local newy = ymax - newRect.height
50+ if newy > 0 and newy + newRect.height < ymax then self:setY(newy) end
51+ end
52+ if newRect.x + newRect.width > xmax then
53+ local newx = xmax - newRect.width
54+ if newx > 0 and newx + newRect.width < xmax then self:setX(newx) end
55+ end
56+ self:bindRectToParent()
57+end
58+
59+function UIPopupMenu:addOption(optionName, optionCallback, shortcut)
60+ local optionWidget = g_ui.createWidget(self:getStyleName() .. 'Button', self)
61+ optionWidget.onClick = function(widget)
62+ self:destroy()
63+ optionCallback()
64+ end
65+ optionWidget:setText(optionName)
66+ local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 15
67+
68+ if shortcut then
69+ local shortcutLabel = g_ui.createWidget(self:getStyleName() .. 'ShortcutLabel', optionWidget)
70+ shortcutLabel:setText(shortcut)
71+ width = width + shortcutLabel:getTextSize().width + shortcutLabel:getMarginLeft() + shortcutLabel:getMarginRight()
72+ end
73+
74+ self:setWidth(math.max(self:getWidth(), width))
75+end
76+
77+function UIPopupMenu:addSeparator()
78+ g_ui.createWidget(self:getStyleName() .. 'Separator', self)
79+end
80+
81+function UIPopupMenu:setGameMenu(state)
82+ self.isGameMenu = state
83+end
84+
85+function UIPopupMenu:onDestroy()
86+ if currentMenu == self then
87+ currentMenu = nil
88+ end
89+ self:ungrabMouse()
90+end
91+
92+function UIPopupMenu:onMousePress(mousePos, mouseButton)
93+ -- clicks outside menu area destroys the menu
94+ if not self:containsPoint(mousePos) then
95+ self:destroy()
96+ end
97+ return true
98+end
99+
100+function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers)
101+ if keyCode == KeyEscape then
102+ self:destroy()
103+ return true
104+ end
105+ return false
106+end
107+
108+-- close all menus when the window is resized
109+local function onRootGeometryUpdate()
110+ if currentMenu then
111+ currentMenu:destroy()
112+ end
113+end
114+
115+local function onGameEnd()
116+ if currentMenu and currentMenu.isGameMenu then
117+ currentMenu:destroy()
118+ end
119+end
120+
121+connect(rootWidget, { onGeometryChange = onRootGeometryUpdate })
122+connect(g_game, { onGameEnd = onGameEnd } )
A RD_Client/modules/corelib/ui/uipopupscrollmenu.lua +129-0 View file
@@ -0,0 +1,129 @@
1+-- @docclass
2+UIPopupScrollMenu = extends(UIWidget, "UIPopupScrollMenu")
3+
4+local currentMenu
5+
6+function UIPopupScrollMenu.create()
7+ local menu = UIPopupScrollMenu.internalCreate()
8+
9+ local scrollArea = g_ui.createWidget('UIScrollArea', menu)
10+ scrollArea:setLayout(UIVerticalLayout.create(menu))
11+ scrollArea:setId('scrollArea')
12+
13+ local scrollBar = g_ui.createWidget('VerticalScrollBar', menu)
14+ scrollBar:setId('scrollBar')
15+ scrollBar.pixelsScroll = false
16+
17+ scrollBar:addAnchor(AnchorRight, 'parent', AnchorRight)
18+ scrollBar:addAnchor(AnchorTop, 'parent', AnchorTop)
19+ scrollBar:addAnchor(AnchorBottom, 'parent', AnchorBottom)
20+
21+ scrollArea:addAnchor(AnchorLeft, 'parent', AnchorLeft)
22+ scrollArea:addAnchor(AnchorTop, 'parent', AnchorTop)
23+ scrollArea:addAnchor(AnchorBottom, 'parent', AnchorBottom)
24+ scrollArea:addAnchor(AnchorRight, 'next', AnchorLeft)
25+ scrollArea:setVerticalScrollBar(scrollBar)
26+
27+ menu.scrollArea = scrollArea
28+ menu.scrollBar = scrollBar
29+ return menu
30+end
31+
32+function UIPopupScrollMenu:setScrollbarStep(step)
33+ self.scrollBar:setStep(step)
34+end
35+
36+function UIPopupScrollMenu:display(pos)
37+ -- don't display if not options was added
38+ if self.scrollArea:getChildCount() == 0 then
39+ self:destroy()
40+ return
41+ end
42+
43+ if g_ui.isMouseGrabbed() then
44+ self:destroy()
45+ return
46+ end
47+
48+ if currentMenu then
49+ currentMenu:destroy()
50+ end
51+
52+ if pos == nil then
53+ pos = g_window.getMousePosition()
54+ end
55+
56+ rootWidget:addChild(self)
57+ self:setPosition(pos)
58+ self:grabMouse()
59+ currentMenu = self
60+end
61+
62+function UIPopupScrollMenu:onGeometryChange(oldRect, newRect)
63+ local parent = self:getParent()
64+ if not parent then return end
65+ local ymax = parent:getY() + parent:getHeight()
66+ local xmax = parent:getX() + parent:getWidth()
67+ if newRect.y + newRect.height > ymax then
68+ local newy = newRect.y - newRect.height
69+ if newy > 0 and newy + newRect.height < ymax then self:setY(newy) end
70+ end
71+ if newRect.x + newRect.width > xmax then
72+ local newx = newRect.x - newRect.width
73+ if newx > 0 and newx + newRect.width < xmax then self:setX(newx) end
74+ end
75+ self:bindRectToParent()
76+end
77+
78+function UIPopupScrollMenu:addOption(optionName, optionCallback, shortcut)
79+ local optionWidget = g_ui.createWidget(self:getStyleName() .. 'Button', self.scrollArea)
80+ optionWidget.onClick = function(widget)
81+ self:destroy()
82+ optionCallback()
83+ end
84+ optionWidget:setText(optionName)
85+ local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 15
86+
87+ if shortcut then
88+ local shortcutLabel = g_ui.createWidget(self:getStyleName() .. 'ShortcutLabel', optionWidget)
89+ shortcutLabel:setText(shortcut)
90+ width = width + shortcutLabel:getTextSize().width + shortcutLabel:getMarginLeft() + shortcutLabel:getMarginRight()
91+ end
92+
93+ self:setWidth(math.max(self:getWidth(), width))
94+end
95+
96+function UIPopupScrollMenu:addSeparator()
97+ g_ui.createWidget(self:getStyleName() .. 'Separator', self.scrollArea)
98+end
99+
100+function UIPopupScrollMenu:onDestroy()
101+ if currentMenu == self then
102+ currentMenu = nil
103+ end
104+ self:ungrabMouse()
105+end
106+
107+function UIPopupScrollMenu:onMousePress(mousePos, mouseButton)
108+ -- clicks outside menu area destroys the menu
109+ if not self:containsPoint(mousePos) then
110+ self:destroy()
111+ end
112+ return true
113+end
114+
115+function UIPopupScrollMenu:onKeyPress(keyCode, keyboardModifiers)
116+ if keyCode == KeyEscape then
117+ self:destroy()
118+ return true
119+ end
120+ return false
121+end
122+
123+-- close all menus when the window is resized
124+local function onRootGeometryUpdate()
125+ if currentMenu then
126+ currentMenu:destroy()
127+ end
128+end
129+connect(rootWidget, { onGeometryChange = onRootGeometryUpdate} )
A RD_Client/modules/corelib/ui/uiprogressbar.lua +99-0 View file
@@ -0,0 +1,99 @@
1+-- @docclass
2+UIProgressBar = extends(UIWidget, "UIProgressBar")
3+
4+function UIProgressBar.create()
5+ local progressbar = UIProgressBar.internalCreate()
6+ progressbar:setFocusable(false)
7+ progressbar:setOn(true)
8+ progressbar.min = 0
9+ progressbar.max = 100
10+ progressbar.value = 0
11+ progressbar.bgBorderLeft = 0
12+ progressbar.bgBorderRight = 0
13+ progressbar.bgBorderTop = 0
14+ progressbar.bgBorderBottom = 0
15+ return progressbar
16+end
17+
18+function UIProgressBar:setMinimum(minimum)
19+ self.minimum = minimum
20+ if self.value < minimum then
21+ self:setValue(minimum)
22+ end
23+end
24+
25+function UIProgressBar:setMaximum(maximum)
26+ self.maximum = maximum
27+ if self.value > maximum then
28+ self:setValue(maximum)
29+ end
30+end
31+
32+function UIProgressBar:setValue(value, minimum, maximum)
33+ if minimum then
34+ self:setMinimum(minimum)
35+ end
36+
37+ if maximum then
38+ self:setMaximum(maximum)
39+ end
40+
41+ self.value = math.max(math.min(value, self.maximum), self.minimum)
42+ self:updateBackground()
43+end
44+
45+function UIProgressBar:setPercent(percent)
46+ self:setValue(percent, 0, 100)
47+end
48+
49+function UIProgressBar:getPercent()
50+ return self.value
51+end
52+
53+function UIProgressBar:getPercentPixels()
54+ return (self.maximum - self.minimum) / self:getWidth()
55+end
56+
57+function UIProgressBar:getProgress()
58+ if self.minimum == self.maximum then return 1 end
59+ return (self.value - self.minimum) / (self.maximum - self.minimum)
60+end
61+
62+function UIProgressBar:updateBackground()
63+ if self:isOn() then
64+ local width = math.round(math.max((self:getProgress() * (self:getWidth() - self.bgBorderLeft - self.bgBorderRight)), 1))
65+ local height = self:getHeight() - self.bgBorderTop - self.bgBorderBottom
66+ local rect = { x = self.bgBorderLeft, y = self.bgBorderTop, width = width, height = height }
67+ self:setBackgroundRect(rect)
68+ end
69+end
70+
71+function UIProgressBar:onSetup()
72+ self:updateBackground()
73+end
74+
75+function UIProgressBar:onStyleApply(name, node)
76+ for name,value in pairs(node) do
77+ if name == 'background-border-left' then
78+ self.bgBorderLeft = tonumber(value)
79+ elseif name == 'background-border-right' then
80+ self.bgBorderRight = tonumber(value)
81+ elseif name == 'background-border-top' then
82+ self.bgBorderTop = tonumber(value)
83+ elseif name == 'background-border-bottom' then
84+ self.bgBorderBottom = tonumber(value)
85+ elseif name == 'background-border' then
86+ self.bgBorderLeft = tonumber(value)
87+ self.bgBorderRight = tonumber(value)
88+ self.bgBorderTop = tonumber(value)
89+ self.bgBorderBottom = tonumber(value)
90+ end
91+ end
92+end
93+
94+function UIProgressBar:onGeometryChange(oldRect, newRect)
95+ if not self:isOn() then
96+ self:setHeight(0)
97+ end
98+ self:updateBackground()
99+end
A RD_Client/modules/corelib/ui/uiradiogroup.lua +66-0 View file
@@ -0,0 +1,66 @@
1+-- @docclass
2+UIRadioGroup = newclass("UIRadioGroup")
3+
4+function UIRadioGroup.create()
5+ local radiogroup = UIRadioGroup.internalCreate()
6+ radiogroup.widgets = {}
7+ radiogroup.selectedWidget = nil
8+ return radiogroup
9+end
10+
11+function UIRadioGroup:destroy()
12+ for k,widget in pairs(self.widgets) do
13+ widget.onClick = nil
14+ end
15+ self.widgets = {}
16+end
17+
18+function UIRadioGroup:addWidget(widget)
19+ table.insert(self.widgets, widget)
20+ widget.onClick = function(widget) self:selectWidget(widget) end
21+end
22+
23+function UIRadioGroup:removeWidget(widget)
24+ if self.selectedWidget == widget then
25+ self:selectWidget(nil)
26+ end
27+ widget.onClick = nil
28+ table.removevalue(self.widgets, widget)
29+end
30+
31+function UIRadioGroup:selectWidget(selectedWidget, dontSignal)
32+ if selectedWidget == self.selectedWidget then return end
33+
34+ local previousSelectedWidget = self.selectedWidget
35+ self.selectedWidget = selectedWidget
36+
37+ if previousSelectedWidget then
38+ previousSelectedWidget:setChecked(false)
39+ end
40+
41+ if selectedWidget then
42+ selectedWidget:setChecked(true)
43+ end
44+
45+ if not dontSignal then
46+ signalcall(self.onSelectionChange, self, selectedWidget, previousSelectedWidget)
47+ end
48+end
49+
50+function UIRadioGroup:clearSelected()
51+ if not self.selectedWidget then return end
52+
53+ local previousSelectedWidget = self.selectedWidget
54+ self.selectedWidget:setChecked(false)
55+ self.selectedWidget = nil
56+
57+ signalcall(self.onSelectionChange, self, nil, previousSelectedWidget)
58+end
59+
60+function UIRadioGroup:getSelectedWidget()
61+ return self.selectedWidget
62+end
63+
64+function UIRadioGroup:getFirstWidget()
65+ return self.widgets[1]
66+end
A RD_Client/modules/corelib/ui/uiresizeborder.lua +132-0 View file
@@ -0,0 +1,132 @@
1+-- @docclass
2+UIResizeBorder = extends(UIWidget, "UIResizeBorder")
3+
4+function UIResizeBorder.create()
5+ local resizeborder = UIResizeBorder.internalCreate()
6+ resizeborder:setFocusable(false)
7+ resizeborder.minimum = 0
8+ resizeborder.maximum = 1000
9+ return resizeborder
10+end
11+
12+function UIResizeBorder:onSetup()
13+ if self:getWidth() > self:getHeight() then
14+ self.vertical = true
15+ else
16+ self.vertical = false
17+ end
18+end
19+
20+function UIResizeBorder:onDestroy()
21+ if self.hovering then
22+ g_mouse.popCursor(self.cursortype)
23+ end
24+end
25+
26+function UIResizeBorder:onHoverChange(hovered)
27+ if hovered then
28+ if g_mouse.isCursorChanged() or g_mouse.isPressed() then return end
29+ if self:getWidth() > self:getHeight() then
30+ self.vertical = true
31+ self.cursortype = 'vertical'
32+ else
33+ self.vertical = false
34+ self.cursortype = 'horizontal'
35+ end
36+ g_mouse.pushCursor(self.cursortype)
37+ self.hovering = true
38+ if not self:isPressed() then
39+ g_effects.fadeIn(self)
40+ end
41+ else
42+ if not self:isPressed() and self.hovering then
43+ g_mouse.popCursor(self.cursortype)
44+ g_effects.fadeOut(self)
45+ self.hovering = false
46+ end
47+ end
48+end
49+
50+function UIResizeBorder:onMouseMove(mousePos, mouseMoved)
51+ if self:isPressed() then
52+ local parent = self:getParent()
53+ local newSize = 0
54+ if self.vertical then
55+ local delta = mousePos.y - self:getY() - self:getHeight()/2
56+ newSize = math.min(math.max(parent:getHeight() + delta, self.minimum), self.maximum)
57+ parent:setHeight(newSize)
58+ else
59+ local delta = mousePos.x - self:getX() - self:getWidth()/2
60+ newSize = math.min(math.max(parent:getWidth() + delta, self.minimum), self.maximum)
61+ parent:setWidth(newSize)
62+ end
63+
64+ self:checkBoundary(newSize)
65+ return true
66+ end
67+end
68+
69+function UIResizeBorder:onMouseRelease(mousePos, mouseButton)
70+ if not self:isHovered() then
71+ g_mouse.popCursor(self.cursortype)
72+ g_effects.fadeOut(self)
73+ self.hovering = false
74+ end
75+end
76+
77+function UIResizeBorder:onStyleApply(styleName, styleNode)
78+ for name,value in pairs(styleNode) do
79+ if name == 'maximum' then
80+ self:setMaximum(tonumber(value))
81+ elseif name == 'minimum' then
82+ self:setMinimum(tonumber(value))
83+ end
84+ end
85+end
86+
87+function UIResizeBorder:onVisibilityChange(visible)
88+ if visible and self.maximum == self.minimum then
89+ self:hide()
90+ end
91+end
92+
93+function UIResizeBorder:setMaximum(maximum)
94+ self.maximum = maximum
95+ self:checkBoundary()
96+end
97+
98+function UIResizeBorder:setMinimum(minimum)
99+ self.minimum = minimum
100+ self:checkBoundary()
101+end
102+
103+function UIResizeBorder:getMaximum() return self.maximum end
104+function UIResizeBorder:getMinimum() return self.minimum end
105+
106+function UIResizeBorder:setParentSize(size)
107+ local parent = self:getParent()
108+ if self.vertical then
109+ parent:setHeight(size)
110+ else
111+ parent:setWidth(size)
112+ end
113+ self:checkBoundary(size)
114+end
115+
116+function UIResizeBorder:getParentSize()
117+ local parent = self:getParent()
118+ if self.vertical then
119+ return parent:getHeight()
120+ else
121+ return parent:getWidth()
122+ end
123+end
124+
125+function UIResizeBorder:checkBoundary(size)
126+ size = size or self:getParentSize()
127+ if self.maximum == self.minimum and size == self.maximum then
128+ self:hide()
129+ else
130+ self:show()
131+ end
132+end
A RD_Client/modules/corelib/ui/uiscrollarea.lua +190-0 View file
@@ -0,0 +1,190 @@
1+-- @docclass
2+UIScrollArea = extends(UIWidget, "UIScrollArea")
3+
4+-- public functions
5+function UIScrollArea.create()
6+ local scrollarea = UIScrollArea.internalCreate()
7+ scrollarea:setClipping(true)
8+ scrollarea.inverted = false
9+ scrollarea.alwaysScrollMaximum = false
10+ return scrollarea
11+end
12+
13+function UIScrollArea:onStyleApply(styleName, styleNode)
14+ for name,value in pairs(styleNode) do
15+ if name == 'vertical-scrollbar' then
16+ addEvent(function()
17+ local parent = self:getParent()
18+ if parent then
19+ self:setVerticalScrollBar(parent:getChildById(value))
20+ end
21+ end)
22+ elseif name == 'horizontal-scrollbar' then
23+ addEvent(function()
24+ local parent = self:getParent()
25+ if parent then
26+ self:setHorizontalScrollBar(self:getParent():getChildById(value))
27+ end
28+ end)
29+ elseif name == 'inverted-scroll' then
30+ self:setInverted(value)
31+ elseif name == 'always-scroll-maximum' then
32+ self:setAlwaysScrollMaximum(value)
33+ end
34+ end
35+end
36+
37+function UIScrollArea:updateScrollBars()
38+ local scrollWidth = math.max(self:getChildrenRect().width - self:getPaddingRect().width, 0)
39+ local scrollHeight = math.max(self:getChildrenRect().height - self:getPaddingRect().height, 0)
40+
41+ local scrollbar = self.verticalScrollBar
42+ if scrollbar then
43+ if self.inverted then
44+ scrollbar:setMinimum(-scrollHeight)
45+ scrollbar:setMaximum(0)
46+ else
47+ scrollbar:setMinimum(0)
48+ scrollbar:setMaximum(scrollHeight)
49+ end
50+ end
51+
52+ local scrollbar = self.horizontalScrollBar
53+ if scrollbar then
54+ if self.inverted then
55+ scrollbar:setMinimum(-scrollWidth)
56+ scrollbar:setMaximum(0)
57+ else
58+ scrollbar:setMinimum(0)
59+ scrollbar:setMaximum(scrollWidth)
60+ end
61+ end
62+
63+ if self.lastScrollWidth ~= scrollWidth then
64+ self:onScrollWidthChange()
65+ end
66+ if self.lastScrollHeight ~= scrollHeight then
67+ self:onScrollHeightChange()
68+ end
69+
70+ self.lastScrollWidth = scrollWidth
71+ self.lastScrollHeight = scrollHeight
72+end
73+
74+function UIScrollArea:setVerticalScrollBar(scrollbar)
75+ self.verticalScrollBar = scrollbar
76+ connect(self.verticalScrollBar, 'onValueChange', function(scrollbar, value)
77+ local virtualOffset = self:getVirtualOffset()
78+ virtualOffset.y = value
79+ self:setVirtualOffset(virtualOffset)
80+ signalcall(self.onScrollChange, self, virtualOffset)
81+ end)
82+ self:updateScrollBars()
83+end
84+
85+function UIScrollArea:setHorizontalScrollBar(scrollbar)
86+ self.horizontalScrollBar = scrollbar
87+ connect(self.horizontalScrollBar, 'onValueChange', function(scrollbar, value)
88+ local virtualOffset = self:getVirtualOffset()
89+ virtualOffset.x = value
90+ self:setVirtualOffset(virtualOffset)
91+ signalcall(self.onScrollChange, self, virtualOffset)
92+ end)
93+ self:updateScrollBars()
94+end
95+
96+function UIScrollArea:setInverted(inverted)
97+ self.inverted = inverted
98+end
99+
100+function UIScrollArea:setAlwaysScrollMaximum(value)
101+ self.alwaysScrollMaximum = value
102+end
103+
104+function UIScrollArea:onLayoutUpdate()
105+ self:updateScrollBars()
106+end
107+
108+function UIScrollArea:onMouseWheel(mousePos, mouseWheel)
109+ if self.verticalScrollBar then
110+ if not self.verticalScrollBar:isOn() then
111+ return false
112+ end
113+ if mouseWheel == MouseWheelUp then
114+ local minimum = self.verticalScrollBar:getMinimum()
115+ if self.verticalScrollBar:getValue() <= minimum then
116+ return false
117+ end
118+ self.verticalScrollBar:decrement()
119+ else
120+ local maximum = self.verticalScrollBar:getMaximum()
121+ if self.verticalScrollBar:getValue() >= maximum then
122+ return false
123+ end
124+ self.verticalScrollBar:increment()
125+ end
126+ elseif self.horizontalScrollBar then
127+ if not self.horizontalScrollBar:isOn() then
128+ return false
129+ end
130+ if mouseWheel == MouseWheelUp then
131+ local maximum = self.horizontalScrollBar:getMaximum()
132+ if self.horizontalScrollBar:getValue() >= maximum then
133+ return false
134+ end
135+ self.horizontalScrollBar:increment()
136+ else
137+ local minimum = self.horizontalScrollBar:getMinimum()
138+ if self.horizontalScrollBar:getValue() <= minimum then
139+ return false
140+ end
141+ self.horizontalScrollBar:decrement()
142+ end
143+ end
144+ return true
145+end
146+
147+function UIScrollArea:ensureChildVisible(child)
148+ if child then
149+ local paddingRect = self:getPaddingRect()
150+ if self.verticalScrollBar then
151+ local deltaY = paddingRect.y - child:getY()
152+ if deltaY > 0 then
153+ self.verticalScrollBar:decrement(deltaY)
154+ end
155+
156+ deltaY = (child:getY() + child:getHeight()) - (paddingRect.y + paddingRect.height)
157+ if deltaY > 0 then
158+ self.verticalScrollBar:increment(deltaY)
159+ end
160+ elseif self.horizontalScrollBar then
161+ local deltaX = paddingRect.x - child:getX()
162+ if deltaX > 0 then
163+ self.horizontalScrollBar:decrement(deltaX)
164+ end
165+
166+ deltaX = (child:getX() + child:getWidth()) - (paddingRect.x + paddingRect.width)
167+ if deltaX > 0 then
168+ self.horizontalScrollBar:increment(deltaX)
169+ end
170+ end
171+ end
172+end
173+
174+function UIScrollArea:onChildFocusChange(focusedChild, oldFocused, reason)
175+ if focusedChild and (reason == MouseFocusReason or reason == KeyboardFocusReason) then
176+ self:ensureChildVisible(focusedChild)
177+ end
178+end
179+
180+function UIScrollArea:onScrollWidthChange()
181+ if self.alwaysScrollMaximum and self.horizontalScrollBar then
182+ self.horizontalScrollBar:setValue(self.horizontalScrollBar:getMaximum())
183+ end
184+end
185+
186+function UIScrollArea:onScrollHeightChange()
187+ if self.alwaysScrollMaximum and self.verticalScrollBar then
188+ self.verticalScrollBar:setValue(self.verticalScrollBar:getMaximum())
189+ end
190+end
A RD_Client/modules/corelib/ui/uiscrollbar.lua +290-0 View file
@@ -0,0 +1,290 @@
1+-- @docclass
2+UIScrollBar = extends(UIWidget, "UIScrollBar")
3+
4+-- private functions
5+local function calcValues(self)
6+ local slider = self:getChildById('sliderButton')
7+ local decrementButton = self:getChildById('decrementButton')
8+ local incrementButton = self:getChildById('incrementButton')
9+
10+ local pxrange, center
11+ if self.orientation == 'vertical' then
12+ pxrange = (self:getHeight() - decrementButton:getHeight() - decrementButton:getMarginTop() - decrementButton:getMarginBottom()
13+ - incrementButton:getHeight() - incrementButton:getMarginTop() - incrementButton:getMarginBottom())
14+ center = self:getY() + math.floor(self:getHeight() / 2)
15+ else -- horizontal
16+ pxrange = (self:getWidth() - decrementButton:getWidth() - decrementButton:getMarginLeft() - decrementButton:getMarginRight()
17+ - incrementButton:getWidth() - incrementButton:getMarginLeft() - incrementButton:getMarginRight())
18+ center = self:getX() + math.floor(self:getWidth() / 2)
19+ end
20+
21+ local range = self.maximum - self.minimum + 1
22+
23+ local proportion
24+
25+ if self.pixelsScroll then
26+ proportion = pxrange/(range+pxrange)
27+ else
28+ proportion = math.min(math.max(self.step, 1), range)/range
29+ end
30+
31+ local px = math.max(proportion * pxrange, 6)
32+ if g_app.isMobile() then
33+ px = math.max(proportion * pxrange, 24)
34+ end
35+ px = px - px % 2 + 1
36+
37+ local offset = 0
38+ if range == 0 or self.value == self.minimum then
39+ if self.orientation == 'vertical' then
40+ offset = -math.floor((self:getHeight() - px) / 2) + decrementButton:getMarginRect().height
41+ else
42+ offset = -math.floor((self:getWidth() - px) / 2) + decrementButton:getMarginRect().width
43+ end
44+ elseif range > 1 and self.value == self.maximum then
45+ if self.orientation == 'vertical' then
46+ offset = math.ceil((self:getHeight() - px) / 2) - incrementButton:getMarginRect().height
47+ else
48+ offset = math.ceil((self:getWidth() - px) / 2) - incrementButton:getMarginRect().width
49+ end
50+ elseif range > 1 then
51+ offset = (((self.value - self.minimum) / (range - 1)) - 0.5) * (pxrange - px)
52+ end
53+
54+ return range, pxrange, px, offset, center
55+end
56+
57+local function updateValueDisplay(widget)
58+ if widget == nil then return end
59+
60+ if widget:getShowValue() then
61+ widget:setText(widget:getValue() .. (widget:getSymbol() or ''))
62+ end
63+end
64+
65+local function updateSlider(self)
66+ local slider = self:getChildById('sliderButton')
67+ if slider == nil then return end
68+
69+ local range, pxrange, px, offset, center = calcValues(self)
70+ if self.orientation == 'vertical' then
71+ slider:setHeight(px)
72+ slider:setMarginTop(offset)
73+ else -- horizontal
74+ slider:setWidth(px)
75+ slider:setMarginLeft(offset)
76+ end
77+ updateValueDisplay(self)
78+
79+ local status = (self.maximum ~= self.minimum)
80+
81+ self:setOn(status)
82+ for _i,child in pairs(self:getChildren()) do
83+ child:setEnabled(status)
84+ end
85+end
86+
87+local function parseSliderPos(self, slider, pos, move)
88+ local delta, hotDistance
89+ if self.orientation == 'vertical' then
90+ delta = move.y
91+ hotDistance = pos.y - slider:getY()
92+ else
93+ delta = move.x
94+ hotDistance = pos.x - slider:getX()
95+ end
96+
97+ if (delta > 0 and hotDistance + delta > self.hotDistance) or
98+ (delta < 0 and hotDistance + delta < self.hotDistance) then
99+ local range, pxrange, px, offset, center = calcValues(self)
100+ local newvalue = self.value + delta * (range / (pxrange - px))
101+ self:setValue(newvalue)
102+ end
103+end
104+
105+local function parseSliderPress(self, slider, pos, button)
106+ if self.orientation == 'vertical' then
107+ self.hotDistance = pos.y - slider:getY()
108+ else
109+ self.hotDistance = pos.x - slider:getX()
110+ end
111+end
112+
113+-- public functions
114+function UIScrollBar.create()
115+ local scrollbar = UIScrollBar.internalCreate()
116+ scrollbar:setFocusable(false)
117+ scrollbar.value = 0
118+ scrollbar.minimum = -999999
119+ scrollbar.maximum = 999999
120+ scrollbar.step = 1
121+ scrollbar.orientation = 'vertical'
122+ scrollbar.pixelsScroll = false
123+ scrollbar.showValue = false
124+ scrollbar.symbol = nil
125+ scrollbar.mouseScroll = true
126+ return scrollbar
127+end
128+
129+function UIScrollBar:onSetup()
130+ self.setupDone = true
131+ local sliderButton = self:getChildById('sliderButton')
132+ g_mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:onDecrement() end, 300)
133+ g_mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:onIncrement() end, 300)
134+ g_mouse.bindPressMove(sliderButton, function(mousePos, mouseMoved) parseSliderPos(self, sliderButton, mousePos, mouseMoved) end)
135+ g_mouse.bindPress(sliderButton, function(mousePos, mouseButton) parseSliderPress(self, sliderButton, mousePos, mouseButton) end)
136+
137+ updateSlider(self)
138+end
139+
140+function UIScrollBar:onStyleApply(styleName, styleNode)
141+ for name,value in pairs(styleNode) do
142+ if name == 'maximum' then
143+ self:setMaximum(tonumber(value))
144+ elseif name == 'minimum' then
145+ self:setMinimum(tonumber(value))
146+ elseif name == 'step' then
147+ self:setStep(tonumber(value))
148+ elseif name == 'orientation' then
149+ self:setOrientation(value)
150+ elseif name == 'value' then
151+ self:setValue(value)
152+ elseif name == 'pixels-scroll' then
153+ self.pixelsScroll = true
154+ elseif name == 'show-value' then
155+ self.showValue = true
156+ elseif name == 'symbol' then
157+ self.symbol = value
158+ elseif name == 'mouse-scroll' then
159+ self.mouseScroll = value
160+ end
161+ end
162+end
163+
164+function UIScrollBar:onDecrement()
165+ if g_keyboard.isCtrlPressed() then
166+ self:decrement(self.value)
167+ elseif g_keyboard.isShiftPressed() then
168+ self:decrement(10)
169+ else
170+ self:decrement()
171+ end
172+end
173+
174+function UIScrollBar:onIncrement()
175+ if g_keyboard.isCtrlPressed() then
176+ self:increment(self.maximum)
177+ elseif g_keyboard.isShiftPressed() then
178+ self:increment(10)
179+ else
180+ self:increment()
181+ end
182+end
183+
184+function UIScrollBar:decrement(count)
185+ count = count or self.step
186+ self:setValue(self.value - count)
187+end
188+
189+function UIScrollBar:increment(count)
190+ count = count or self.step
191+ self:setValue(self.value + count)
192+end
193+
194+function UIScrollBar:setMaximum(maximum)
195+ if maximum == self.maximum then return end
196+ self.maximum = maximum
197+ if self.minimum > maximum then
198+ self:setMinimum(maximum)
199+ end
200+ if self.value > maximum then
201+ self:setValue(maximum)
202+ else
203+ updateSlider(self)
204+ end
205+end
206+
207+function UIScrollBar:setMinimum(minimum)
208+ if minimum == self.minimum then return end
209+ self.minimum = minimum
210+ if self.maximum < minimum then
211+ self:setMaximum(minimum)
212+ end
213+ if self.value < minimum then
214+ self:setValue(minimum)
215+ else
216+ updateSlider(self)
217+ end
218+end
219+
220+function UIScrollBar:setRange(minimum, maximum)
221+ self:setMinimum(minimum)
222+ self:setMaximum(maximum)
223+end
224+
225+function UIScrollBar:setValue(value)
226+ value = math.max(math.min(value, self.maximum), self.minimum)
227+ if self.value == value then return end
228+ local delta = value - self.value
229+ self.value = value
230+ updateSlider(self)
231+ if self.setupDone then
232+ signalcall(self.onValueChange, self, math.round(value), delta)
233+ end
234+end
235+
236+function UIScrollBar:setMouseScroll(scroll)
237+ self.mouseScroll = scroll
238+end
239+
240+function UIScrollBar:setStep(step)
241+ self.step = step
242+end
243+
244+function UIScrollBar:setOrientation(orientation)
245+ self.orientation = orientation
246+end
247+
248+function UIScrollBar:setText(text)
249+ local valueLabel = self:getChildById('valueLabel')
250+ if valueLabel then
251+ valueLabel:setText(text)
252+ end
253+end
254+
255+function UIScrollBar:onGeometryChange()
256+ updateSlider(self)
257+end
258+
259+function UIScrollBar:onMouseWheel(mousePos, mouseWheel)
260+ if not self.mouseScroll or not self:isOn() or self.disableScroll then
261+ return false
262+ end
263+ if mouseWheel == MouseWheelUp then
264+ if self.orientation == 'vertical' then
265+ if self.value <= self.minimum then return false end
266+ self:decrement()
267+ else
268+ if self.value >= self.maximum then return false end
269+ self:increment()
270+ end
271+ else
272+ if self.orientation == 'vertical' then
273+ if self.value >= self.maximum then return false end
274+ self:increment()
275+ else
276+ if self.value <= self.minimum then return false end
277+ self:decrement()
278+ end
279+ end
280+ return true
281+end
282+
283+function UIScrollBar:getMaximum() return self.maximum end
284+function UIScrollBar:getMinimum() return self.minimum end
285+function UIScrollBar:getValue() return math.round(self.value) end
286+function UIScrollBar:getStep() return self.step end
287+function UIScrollBar:getOrientation() return self.orientation end
288+function UIScrollBar:getShowValue() return self.showValue end
289+function UIScrollBar:getSymbol() return self.symbol end
290+function UIScrollBar:getMouseScroll() return self.mouseScroll end
A RD_Client/modules/corelib/ui/uispinbox.lua +191-0 View file
@@ -0,0 +1,191 @@
1+-- @docclass
2+UISpinBox = extends(UITextEdit, "UISpinBox")
3+
4+function UISpinBox.create()
5+ local spinbox = UISpinBox.internalCreate()
6+ spinbox:setFocusable(false)
7+ spinbox:setValidCharacters('0123456789')
8+ spinbox.displayButtons = true
9+ spinbox.minimum = 0
10+ spinbox.maximum = 1
11+ spinbox.value = 0
12+ spinbox.step = 1
13+ spinbox.firstchange = true
14+ spinbox.mouseScroll = true
15+ spinbox:setText("1")
16+ spinbox:setValue(1)
17+ return spinbox
18+end
19+
20+function UISpinBox:onSetup()
21+ g_mouse.bindAutoPress(self:getChildById('up'), function() self:up() end, 300)
22+ g_mouse.bindAutoPress(self:getChildById('down'), function() self:down() end, 300)
23+end
24+
25+function UISpinBox:onMouseWheel(mousePos, direction)
26+ if not self.mouseScroll or self.disableScroll then
27+ return false
28+ end
29+ if direction == MouseWheelUp then
30+ self:up()
31+ elseif direction == MouseWheelDown then
32+ self:down()
33+ end
34+ return true
35+end
36+
37+function UISpinBox:onKeyPress()
38+ if self.firstchange then
39+ self.firstchange = false
40+ self:setText('')
41+ end
42+ return false
43+end
44+
45+function UISpinBox:onTextChange(text, oldText)
46+ if text:len() == 0 then
47+ self:setValue(self.minimum)
48+ return
49+ end
50+
51+ local number = tonumber(text)
52+ if not number then
53+ self:setText(number)
54+ return
55+ else
56+ if number < self.minimum then
57+ self:setText(self.minimum)
58+ return
59+ elseif number > self.maximum then
60+ self:setText(self.maximum)
61+ return
62+ end
63+ end
64+
65+ self:setValue(number)
66+end
67+
68+function UISpinBox:onValueChange(value)
69+ -- nothing to do
70+end
71+
72+function UISpinBox:onFocusChange(focused)
73+ if not focused then
74+ if self:getText():len() == 0 then
75+ self:setText(self.minimum)
76+ end
77+ end
78+end
79+
80+function UISpinBox:onStyleApply(styleName, styleNode)
81+ for name, value in pairs(styleNode) do
82+ if name == 'maximum' then
83+ self.maximum = value
84+ addEvent(function() self:setMaximum(value) end)
85+ elseif name == 'minimum' then
86+ self.minimum = value
87+ addEvent(function() self:setMinimum(value) end)
88+ elseif name == 'mouse-scroll' then
89+ addEvent(function() self:setMouseScroll(value) end)
90+ elseif name == 'buttons' then
91+ addEvent(function()
92+ if value then
93+ self:showButtons()
94+ else
95+ self:hideButtons()
96+ end
97+ end)
98+ end
99+ end
100+end
101+
102+function UISpinBox:showButtons()
103+ self:getChildById('up'):show()
104+ self:getChildById('down'):show()
105+ self.displayButtons = true
106+end
107+
108+function UISpinBox:hideButtons()
109+ self:getChildById('up'):hide()
110+ self:getChildById('down'):hide()
111+ self.displayButtons = false
112+end
113+
114+function UISpinBox:up()
115+ self:setValue(self.value + self.step)
116+end
117+
118+function UISpinBox:down()
119+ self:setValue(self.value - self.step)
120+end
121+
122+function UISpinBox:setValue(value, dontSignal)
123+ if type(value) == "string" then
124+ value = tonumber(value)
125+ end
126+ value = value or 0
127+ value = math.max(math.min(self.maximum, value), self.minimum)
128+
129+ if value == self.value then return end
130+
131+ self.value = value
132+ if self:getText():len() > 0 then
133+ self:setText(value)
134+ end
135+
136+ local upButton = self:getChildById('up')
137+ local downButton = self:getChildById('down')
138+ if upButton then
139+ upButton:setEnabled(self.maximum ~= self.minimum and self.value ~= self.maximum)
140+ end
141+ if downButton then
142+ downButton:setEnabled(self.maximum ~= self.minimum and self.value ~= self.minimum)
143+ end
144+
145+ if not dontSignal then
146+ signalcall(self.onValueChange, self, value)
147+ end
148+end
149+
150+function UISpinBox:getValue()
151+ return self.value
152+end
153+
154+function UISpinBox:setMinimum(minimum)
155+ minimum = minimum or -9223372036854775808
156+ self.minimum = minimum
157+ if self.minimum > self.maximum then
158+ self.maximum = self.minimum
159+ end
160+ if self.value < minimum then
161+ self:setValue(minimum)
162+ end
163+end
164+
165+function UISpinBox:getMinimum()
166+ return self.minimum
167+end
168+
169+function UISpinBox:setMaximum(maximum)
170+ maximum = maximum or 9223372036854775807
171+ self.maximum = maximum
172+ if self.value > maximum then
173+ self:setValue(maximum)
174+ end
175+end
176+
177+function UISpinBox:getMaximum()
178+ return self.maximum
179+end
180+
181+function UISpinBox:setStep(step)
182+ self.step = step or 1
183+end
184+
185+function UISpinBox:setMouseScroll(mouseScroll)
186+ self.mouseScroll = mouseScroll
187+end
188+
189+function UISpinBox:getMouseScroll()
190+ return self.mouseScroll
191+end
A RD_Client/modules/corelib/ui/uisplitter.lua +85-0 View file
@@ -0,0 +1,85 @@
1+-- @docclass
2+UISplitter = extends(UIWidget, "UISplitter")
3+
4+function UISplitter.create()
5+ local splitter = UISplitter.internalCreate()
6+ splitter:setFocusable(false)
7+ splitter.relativeMargin = 'bottom'
8+ return splitter
9+end
10+
11+function UISplitter:onHoverChange(hovered)
12+ -- Check if margin can be changed
13+ local margin = (self.vertical and self:getMarginBottom() or self:getMarginRight())
14+ if hovered and (self:canUpdateMargin(margin + 1) ~= margin or self:canUpdateMargin(margin - 1) ~= margin) then
15+ if g_mouse.isCursorChanged() or g_mouse.isPressed() then return end
16+ if self:getWidth() > self:getHeight() then
17+ self.vertical = true
18+ self.cursortype = 'vertical'
19+ else
20+ self.vertical = false
21+ self.cursortype = 'horizontal'
22+ end
23+ self.hovering = true
24+ g_mouse.pushCursor(self.cursortype)
25+ if not self:isPressed() then
26+ g_effects.fadeIn(self)
27+ end
28+ else
29+ if not self:isPressed() and self.hovering then
30+ g_mouse.popCursor(self.cursortype)
31+ g_effects.fadeOut(self)
32+ self.hovering = false
33+ end
34+ end
35+end
36+
37+function UISplitter:onMouseMove(mousePos, mouseMoved)
38+ if self:isPressed() then
39+ --local currentmargin, newmargin, delta
40+ if self.vertical then
41+ local delta = mousePos.y - self:getY() - self:getHeight()/2
42+ local newMargin = self:canUpdateMargin(self:getMarginBottom() - delta)
43+ local currentMargin = self:getMarginBottom()
44+ if newMargin ~= currentMargin then
45+ self.newMargin = newMargin
46+ if not self.event or self.event:isExecuted() then
47+ self.event = addEvent(function()
48+ self:setMarginBottom(self.newMargin)
49+ end)
50+ end
51+ end
52+ else
53+ local delta = mousePos.x - self:getX() - self:getWidth()/2
54+ local newMargin = self:canUpdateMargin(self:getMarginRight() - delta)
55+ local currentMargin = self:getMarginRight()
56+ if newMargin ~= currentMargin then
57+ self.newMargin = newMargin
58+ if not self.event or self.event:isExecuted() then
59+ self.event = addEvent(function()
60+ self:setMarginRight(self.newMargin)
61+ end)
62+ end
63+ end
64+ end
65+ return true
66+ end
67+end
68+
69+function UISplitter:onMouseRelease(mousePos, mouseButton)
70+ if not self:isHovered() then
71+ g_mouse.popCursor(self.cursortype)
72+ g_effects.fadeOut(self)
73+ self.hovering = false
74+ end
75+end
76+
77+function UISplitter:onStyleApply(styleName, styleNode)
78+ if styleNode['relative-margin'] then
79+ self.relativeMargin = styleNode['relative-margin']
80+ end
81+end
82+
83+function UISplitter:canUpdateMargin(newMargin)
84+ return newMargin
85+end
A RD_Client/modules/corelib/ui/uitabbar.lua +163-0 View file
@@ -0,0 +1,163 @@
1+-- @docclass
2+UITabBar = extends(UIWidget, "UITabBar")
3+
4+-- private functions
5+local function onTabClick(tab)
6+ tab.tabBar:selectTab(tab)
7+end
8+
9+local function onTabMouseRelease(tab, mousePos, mouseButton)
10+ if mouseButton == MouseRightButton and tab:containsPoint(mousePos) then
11+ signalcall(tab.tabBar.onTabLeftClick, tab.tabBar, tab)
12+ end
13+end
14+
15+-- public functions
16+function UITabBar.create()
17+ local tabbar = UITabBar.internalCreate()
18+ tabbar:setFocusable(false)
19+ tabbar.tabs = {}
20+ return tabbar
21+end
22+
23+function UITabBar:onSetup()
24+ self.buttonsPanel = self:getChildById('buttonsPanel')
25+end
26+
27+function UITabBar:setContentWidget(widget)
28+ self.contentWidget = widget
29+ if #self.tabs > 0 then
30+ self.contentWidget:addChild(self.tabs[1].tabPanel)
31+ end
32+end
33+
34+function UITabBar:addTab(text, panel, icon)
35+ if panel == nil then
36+ panel = g_ui.createWidget(self:getStyleName() .. 'Panel')
37+ panel:setId('tabPanel')
38+ end
39+
40+ local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self.buttonsPanel)
41+
42+ panel.isTab = true
43+ tab.tabPanel = panel
44+ tab.tabBar = self
45+ tab:setId('tab')
46+ tab:setText(text)
47+ tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight())
48+ tab.onClick = onTabClick
49+ tab.onMouseRelease = onTabMouseRelease
50+ tab.onDestroy = function() tab.tabPanel:destroy() end
51+
52+ table.insert(self.tabs, tab)
53+ if #self.tabs == 1 then
54+ self:selectTab(tab)
55+ end
56+
57+ local tabStyle = {}
58+ tabStyle['icon-source'] = icon
59+ tab:mergeStyle(tabStyle)
60+
61+ return tab
62+end
63+
64+function UITabBar:addButton(text, func, icon)
65+ local button = g_ui.createWidget(self:getStyleName() .. 'Button', self.buttonsPanel)
66+ button:setText(text)
67+
68+ local style = {}
69+ style['icon-source'] = icon
70+ button:mergeStyle(style)
71+
72+ button.onClick = func
73+ return button
74+end
75+
76+function UITabBar:removeTab(tab)
77+ local index = table.find(self.tabs, tab)
78+ if index == nil then return end
79+ if self.currentTab == tab then
80+ self:selectPrevTab()
81+ end
82+ table.remove(self.tabs, index)
83+ tab:destroy()
84+end
85+
86+function UITabBar:getTab(text)
87+ for k,tab in pairs(self.tabs) do
88+ if tab:getText():lower() == text:lower() then
89+ return tab
90+ end
91+ end
92+end
93+
94+function UITabBar:selectTab(tab)
95+ if self.currentTab == tab then return end
96+ if self.contentWidget then
97+ local selectedWidget = self.contentWidget:getLastChild()
98+ if selectedWidget and selectedWidget.isTab then
99+ self.contentWidget:removeChild(selectedWidget)
100+ end
101+ self.contentWidget:addChild(tab.tabPanel)
102+ tab.tabPanel:fill('parent')
103+ end
104+
105+ if self.currentTab then
106+ self.currentTab:setChecked(false)
107+ end
108+ signalcall(self.onTabChange, self, tab)
109+ self.currentTab = tab
110+ tab:setChecked(true)
111+ tab:setOn(false)
112+
113+ local parent = tab:getParent()
114+ if parent then
115+ parent:focusChild(tab, MouseFocusReason)
116+ end
117+end
118+
119+function UITabBar:selectNextTab()
120+ if self.currentTab == nil then return end
121+ local index = table.find(self.tabs, self.currentTab)
122+ if index == nil then return end
123+ local nextTab = self.tabs[index + 1] or self.tabs[1]
124+ if not nextTab then return end
125+ self:selectTab(nextTab)
126+end
127+
128+function UITabBar:selectPrevTab()
129+ if self.currentTab == nil then return end
130+ local index = table.find(self.tabs, self.currentTab)
131+ if index == nil then return end
132+ local prevTab = self.tabs[index - 1] or self.tabs[#self.tabs]
133+ if not prevTab then return end
134+ self:selectTab(prevTab)
135+end
136+
137+function UITabBar:getTabPanel(tab)
138+ return tab.tabPanel
139+end
140+
141+function UITabBar:getCurrentTabPanel()
142+ if self.currentTab then
143+ return self.currentTab.tabPanel
144+ end
145+end
146+
147+function UITabBar:getCurrentTab()
148+ return self.currentTab
149+end
150+
151+function UITabBar:getTabs()
152+ return self.tabs
153+end
154+
155+function UITabBar:getTabsPanel()
156+ return table.collect(self.tabs, function(_,tab) return tab.tabPanel end)
157+end
158+
159+function UITabBar:clearTabs()
160+ while #self.tabs > 0 do
161+ self:removeTab(self.tabs[#self.tabs])
162+ end
163+end
A RD_Client/modules/corelib/ui/uitable.lua +432-0 View file
@@ -0,0 +1,432 @@
1+-- @docclass
2+--[[
3+ TODO:
4+ * Make table headers more robust.
5+ * Get dynamic row heights working with text wrapping.
6+]]
7+
8+TABLE_SORTING_ASC = 0
9+TABLE_SORTING_DESC = 1
10+
11+UITable = extends(UIWidget, "UITable")
12+
13+-- Initialize default values
14+function UITable.create()
15+ local table = UITable.internalCreate()
16+ table.headerRow = nil
17+ table.headerColumns = {}
18+ table.dataSpace = nil
19+ table.rows = {}
20+ table.rowBaseStyle = nil
21+ table.columns = {}
22+ table.columnWidth = {}
23+ table.columBaseStyle = nil
24+ table.headerRowBaseStyle = nil
25+ table.headerColumnBaseStyle = nil
26+ table.selectedRow = nil
27+ table.defaultColumnWidth = 80
28+ table.sortColumn = -1
29+ table.sortType = TABLE_SORTING_ASC
30+ table.autoSort = false
31+
32+ return table
33+end
34+
35+-- Clear table values
36+function UITable:onDestroy()
37+ for _,row in pairs(self.rows) do
38+ row.onClick = nil
39+ end
40+ self.rows = {}
41+ self.columns = {}
42+ self.headerRow = nil
43+ self.headerColumns = {}
44+ self.columnWidth = {}
45+ self.selectedRow = nil
46+
47+ if self.dataSpace then
48+ self.dataSpace:destroyChildren()
49+ self.dataSpace = nil
50+ end
51+end
52+
53+-- Detect if a header is already defined
54+function UITable:onSetup()
55+ local header = self:getChildById('header')
56+ if header then
57+ self:setHeader(header)
58+ end
59+end
60+
61+-- Parse table related styles
62+function UITable:onStyleApply(styleName, styleNode)
63+ for name, value in pairs(styleNode) do
64+ if value ~= false then
65+ if name == 'table-data' then
66+ addEvent(function()
67+ self:setTableData(self:getParent():getChildById(value))
68+ end)
69+ elseif name == 'column-style' then
70+ addEvent(function()
71+ self:setColumnStyle(value)
72+ end)
73+ elseif name == 'row-style' then
74+ addEvent(function()
75+ self:setRowStyle(value)
76+ end)
77+ elseif name == 'header-column-style' then
78+ addEvent(function()
79+ self:setHeaderColumnStyle(value)
80+ end)
81+ elseif name == 'header-row-style' then
82+ addEvent(function()
83+ self:setHeaderRowStyle(value)
84+ end)
85+ end
86+ end
87+ end
88+end
89+
90+function UITable:setColumnWidth(width)
91+ if self:hasHeader() then return end
92+ self.columnWidth = width
93+end
94+
95+function UITable:setDefaultColumnWidth(width)
96+ self.defaultColumnWidth = width
97+end
98+
99+-- Check if the table has a header
100+function UITable:hasHeader()
101+ return self.headerRow ~= nil
102+end
103+
104+-- Clear all rows
105+function UITable:clearData()
106+ if not self.dataSpace then
107+ return
108+ end
109+ self.dataSpace:destroyChildren()
110+ self.selectedRow = nil
111+ self.columns = {}
112+ self.rows = {}
113+end
114+
115+-- Set existing child as header
116+function UITable:setHeader(headerWidget)
117+ self:removeHeader()
118+
119+ if self.dataSpace then
120+ local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
121+ self.dataSpace:applyStyle({ height = newHeight })
122+ end
123+
124+ self.headerColumns = {}
125+ self.columnWidth = {}
126+ for colId, column in pairs(headerWidget:getChildren()) do
127+ column.colId = colId
128+ column.table = self
129+ table.insert(self.columnWidth, column:getWidth())
130+ table.insert(self.headerColumns, column)
131+ end
132+
133+ self.headerRow = headerWidget
134+end
135+
136+-- Create and add header from table data
137+function UITable:addHeader(data)
138+ if not data or type(data) ~= 'table' then
139+ g_logger.error('UITable:addHeaderRow - table columns must be provided in a table')
140+ return
141+ end
142+
143+ self:removeHeader()
144+
145+ -- build header columns
146+ local columns = {}
147+ for colId, column in pairs(data) do
148+ local col = g_ui.createWidget(self.headerColumnBaseStyle)
149+ col.colId = colId
150+ col.table = self
151+ for type, value in pairs(column) do
152+ if type == 'width' then
153+ col:setWidth(value)
154+ elseif type == 'height' then
155+ col:setHeight(value)
156+ elseif type == 'text' then
157+ col:setText(value)
158+ elseif type == 'onClick' then
159+ col.onClick = value
160+ end
161+ end
162+ table.insert(columns, col)
163+ end
164+
165+ -- create a new header
166+ local headerRow = g_ui.createWidget(self.headerRowBaseStyle, self)
167+ local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
168+ self.dataSpace:applyStyle({ height = newHeight })
169+
170+ headerRow:setId('header')
171+ self.headerColumns = {}
172+ self.columnWidth = {}
173+ for _, column in pairs(columns) do
174+ headerRow:addChild(column)
175+ table.insert(self.columnWidth, column:getWidth())
176+ table.insert(self.headerColumns, column)
177+ end
178+
179+ self.headerRow = headerRow
180+ return headerRow
181+end
182+
183+-- Remove header
184+function UITable:removeHeader()
185+ if self:hasHeader() then
186+ if self.dataSpace then
187+ local newHeight = self.dataSpace:getHeight()+self.headerRow:getHeight()+self.dataSpace:getMarginTop()
188+ self.dataSpace:applyStyle({ height = newHeight })
189+ end
190+ self.headerColumns = {}
191+ self.columnWidth = {}
192+ self.headerRow:destroy()
193+ self.headerRow = nil
194+ end
195+end
196+
197+function UITable:addRow(data, height)
198+ if not self.dataSpace then
199+ g_logger.error('UITable:addRow - table data space has not been set, cannot add rows.')
200+ return
201+ end
202+ if not data or type(data) ~= 'table' then
203+ g_logger.error('UITable:addRow - table columns must be provided in a table.')
204+ return
205+ end
206+
207+ local row = g_ui.createWidget(self.rowBaseStyle)
208+ row.table = self
209+ if height then row:setHeight(height) end
210+
211+ local rowId = #self.rows + 1
212+ row.rowId = rowId
213+ row:setId('row'..rowId)
214+ row:updateBackgroundColor()
215+
216+ self.columns[rowId] = {}
217+ for colId, column in pairs(data) do
218+ local col = g_ui.createWidget(self.columBaseStyle, row)
219+ if column.width then
220+ col:setWidth(column.width)
221+ else
222+ col:setWidth(self.columnWidth[colId] or self.defaultColumnWidth)
223+ end
224+ if column.height then
225+ col:setHeight(column.height)
226+ end
227+ if column.text then
228+ col:setText(column.text)
229+ end
230+ if column.sortvalue then
231+ col.sortvalue = column.sortvalue
232+ else
233+ col.sortvalue = column.text or 0
234+ end
235+ table.insert(self.columns[rowId], col)
236+ end
237+
238+ self.dataSpace:addChild(row)
239+ table.insert(self.rows, row)
240+
241+ if self.autoSort then
242+ self:sort()
243+ end
244+
245+ return row
246+end
247+
248+-- Update row indices and background color
249+function UITable:updateRows()
250+ for rowId = 1, #self.rows do
251+ local row = self.rows[rowId]
252+ row.rowId = rowId
253+ row:setId('row'..rowId)
254+ row:updateBackgroundColor()
255+ end
256+end
257+
258+-- Removes the given row widget from the table
259+function UITable:removeRow(row)
260+ if self.selectedRow == row then
261+ self:selectRow(nil)
262+ end
263+ row.onClick = nil
264+ row.table = nil
265+ table.remove(self.columns, row.rowId)
266+ table.remove(self.rows, row.rowId)
267+ self.dataSpace:removeChild(row)
268+ self:updateRows()
269+end
270+
271+function UITable:toggleSorting(enabled)
272+ self.autoSort = enabled
273+end
274+
275+function UITable:setSorting(colId, sortType)
276+ self.headerColumns[colId]:focus()
277+
278+ if sortType then
279+ self.sortType = sortType
280+ elseif self.sortColumn == colId then
281+ if self.sortType == TABLE_SORTING_ASC then
282+ self.sortType = TABLE_SORTING_DESC
283+ else
284+ self.sortType = TABLE_SORTING_ASC
285+ end
286+ else
287+ self.sortType = TABLE_SORTING_ASC
288+ end
289+ self.sortColumn = colId
290+end
291+
292+function UITable:sort()
293+ if self.sortColumn <= 0 then
294+ return
295+ end
296+
297+ if self.sortType == TABLE_SORTING_ASC then
298+ table.sort(self.rows, function(rowA, b)
299+ return rowA:getChildByIndex(self.sortColumn).sortvalue < b:getChildByIndex(self.sortColumn).sortvalue
300+ end)
301+ else
302+ table.sort(self.rows, function(rowA, b)
303+ return rowA:getChildByIndex(self.sortColumn).sortvalue > b:getChildByIndex(self.sortColumn).sortvalue
304+ end)
305+ end
306+
307+ if self.dataSpace then
308+ for _, child in pairs(self.dataSpace:getChildren()) do
309+ self.dataSpace:removeChild(child)
310+ end
311+ end
312+
313+ self:updateRows()
314+ self.columns = {}
315+ for _, row in pairs(self.rows) do
316+ if self.dataSpace then
317+ self.dataSpace:addChild(row)
318+ end
319+
320+ self.columns[row.rowId] = {}
321+ for _, column in pairs(row:getChildren()) do
322+ table.insert(self.columns[row.rowId], column)
323+ end
324+ end
325+end
326+
327+function UITable:selectRow(selectedRow)
328+ if selectedRow == self.selectedRow then return end
329+
330+ local previousSelectedRow = self.selectedRow
331+ self.selectedRow = selectedRow
332+
333+ if previousSelectedRow then
334+ previousSelectedRow:setChecked(false)
335+ end
336+
337+ if selectedRow then
338+ selectedRow:setChecked(true)
339+ end
340+
341+ signalcall(self.onSelectionChange, self, selectedRow, previousSelectedRow)
342+end
343+
344+function UITable:setTableData(tableData)
345+ local headerHeight = 0
346+ if self.headerRow then
347+ headerHeight = self.headerRow:getHeight()
348+ end
349+
350+ self.dataSpace = tableData
351+ self.dataSpace:applyStyle({ height = self:getHeight()-headerHeight-self:getMarginTop() })
352+end
353+
354+function UITable:setRowStyle(style, dontUpdate)
355+ self.rowBaseStyle = style
356+
357+ if not dontUpdate then
358+ for _, row in pairs(self.rows) do
359+ row:setStyle(style)
360+ end
361+ end
362+end
363+
364+function UITable:setColumnStyle(style, dontUpdate)
365+ self.columBaseStyle = style
366+
367+ if not dontUpdate then
368+ for _, columns in pairs(self.columns) do
369+ for _, col in pairs(columns) do
370+ col:setStyle(style)
371+ end
372+ end
373+ end
374+end
375+
376+function UITable:setHeaderRowStyle(style)
377+ self.headerRowBaseStyle = style
378+ if self.headerRow then
379+ self.headerRow:setStyle(style)
380+ end
381+end
382+
383+function UITable:setHeaderColumnStyle(style)
384+ self.headerColumnBaseStyle = style
385+ for _, col in pairs(self.headerColumns) do
386+ col:setStyle(style)
387+ end
388+end
389+
390+
391+UITableRow = extends(UIWidget, "UITableRow")
392+
393+function UITableRow:onFocusChange(focused)
394+ if focused then
395+ if self.table then self.table:selectRow(self) end
396+ end
397+end
398+
399+function UITableRow:onStyleApply(styleName, styleNode)
400+ for name,value in pairs(styleNode) do
401+ if name == 'even-background-color' then
402+ self.evenBackgroundColor = value
403+ elseif name == 'odd-background-color' then
404+ self.oddBackgroundColor = value
405+ end
406+ end
407+end
408+
409+function UITableRow:updateBackgroundColor()
410+ self.backgroundColor = nil
411+
412+ local isEven = (self.rowId % 2 == 0)
413+ if isEven and self.evenBackgroundColor then
414+ self.backgroundColor = self.evenBackgroundColor
415+ elseif not isEven and self.oddBackgroundColor then
416+ self.backgroundColor = self.oddBackgroundColor
417+ end
418+
419+ if self.backgroundColor then
420+ self:mergeStyle({ ['background-color'] = self.backgroundColor })
421+ end
422+end
423+
424+
425+UITableHeaderColumn = extends(UIButton, "UITableHeaderColumn")
426+
427+function UITableHeaderColumn:onClick()
428+ if self.table then
429+ self.table:setSorting(self.colId)
430+ self.table:sort()
431+ end
432+end
A RD_Client/modules/corelib/ui/uitextedit.lua +78-0 View file
@@ -0,0 +1,78 @@
1+function UITextEdit:onStyleApply(styleName, styleNode)
2+ for name,value in pairs(styleNode) do
3+ if name == 'vertical-scrollbar' then
4+ addEvent(function()
5+ self:setVerticalScrollBar(self:getParent():getChildById(value))
6+ end)
7+ elseif name == 'horizontal-scrollbar' then
8+ addEvent(function()
9+ self:setHorizontalScrollBar(self:getParent():getChildById(value))
10+ end)
11+ end
12+ end
13+end
14+
15+function UITextEdit:onMouseWheel(mousePos, mouseWheel)
16+ if self.verticalScrollBar and self:isMultiline() then
17+ if mouseWheel == MouseWheelUp then
18+ self.verticalScrollBar:decrement()
19+ else
20+ self.verticalScrollBar:increment()
21+ end
22+ return true
23+ elseif self.horizontalScrollBar then
24+ if mouseWheel == MouseWheelUp then
25+ self.horizontalScrollBar:increment()
26+ else
27+ self.horizontalScrollBar:decrement()
28+ end
29+ return true
30+ end
31+end
32+
33+function UITextEdit:onTextAreaUpdate(virtualOffset, virtualSize, totalSize)
34+ self:updateScrollBars()
35+end
36+
37+function UITextEdit:setVerticalScrollBar(scrollbar)
38+ self.verticalScrollBar = scrollbar
39+ self.verticalScrollBar.onValueChange = function(scrollbar, value)
40+ local virtualOffset = self:getTextVirtualOffset()
41+ virtualOffset.y = value
42+ self:setTextVirtualOffset(virtualOffset)
43+ end
44+ self:updateScrollBars()
45+end
46+
47+function UITextEdit:setHorizontalScrollBar(scrollbar)
48+ self.horizontalScrollBar = scrollbar
49+ self.horizontalScrollBar.onValueChange = function(scrollbar, value)
50+ local virtualOffset = self:getTextVirtualOffset()
51+ virtualOffset.x = value
52+ self:setTextVirtualOffset(virtualOffset)
53+ end
54+ self:updateScrollBars()
55+end
56+
57+function UITextEdit:updateScrollBars()
58+ local scrollSize = self:getTextTotalSize()
59+ local scrollWidth = math.max(scrollSize.width - self:getTextVirtualSize().width, 0)
60+ local scrollHeight = math.max(scrollSize.height - self:getTextVirtualSize().height, 0)
61+
62+ local scrollbar = self.verticalScrollBar
63+ if scrollbar then
64+ scrollbar:setMinimum(0)
65+ scrollbar:setMaximum(scrollHeight)
66+ scrollbar:setValue(self:getTextVirtualOffset().y)
67+ end
68+
69+ local scrollbar = self.horizontalScrollBar
70+ if scrollbar then
71+ scrollbar:setMinimum(0)
72+ scrollbar:setMaximum(scrollWidth)
73+ scrollbar:setValue(self:getTextVirtualOffset().x)
74+ end
75+
76+end
77+
78+-- todo: ontext change, focus to cursor
A RD_Client/modules/corelib/ui/uiwidget.lua +21-0 View file
@@ -0,0 +1,21 @@
1+-- @docclass UIWidget
2+
3+function UIWidget:setMargin(...)
4+ local params = {...}
5+ if #params == 1 then
6+ self:setMarginTop(params[1])
7+ self:setMarginRight(params[1])
8+ self:setMarginBottom(params[1])
9+ self:setMarginLeft(params[1])
10+ elseif #params == 2 then
11+ self:setMarginTop(params[1])
12+ self:setMarginRight(params[2])
13+ self:setMarginBottom(params[1])
14+ self:setMarginLeft(params[2])
15+ elseif #params == 4 then
16+ self:setMarginTop(params[1])
17+ self:setMarginRight(params[2])
18+ self:setMarginBottom(params[3])
19+ self:setMarginLeft(params[4])
20+ end
21+end
A RD_Client/modules/corelib/ui/uiwindow.lua +46-0 View file
@@ -0,0 +1,46 @@
1+-- @docclass
2+UIWindow = extends(UIWidget, "UIWindow")
3+
4+function UIWindow.create()
5+ local window = UIWindow.internalCreate()
6+ window:setTextAlign(AlignTopCenter)
7+ window:setDraggable(true)
8+ window:setAutoFocusPolicy(AutoFocusFirst)
9+ return window
10+end
11+
12+function UIWindow:onKeyDown(keyCode, keyboardModifiers)
13+ if keyboardModifiers == KeyboardNoModifier then
14+ if keyCode == KeyEnter then
15+ signalcall(self.onEnter, self)
16+ elseif keyCode == KeyEscape then
17+ signalcall(self.onEscape, self)
18+ end
19+ end
20+end
21+
22+function UIWindow:onFocusChange(focused)
23+ if focused then self:raise() end
24+end
25+
26+function UIWindow:onDragEnter(mousePos)
27+ if self.static then
28+ return false
29+ end
30+ self:breakAnchors()
31+ self.movingReference = { x = mousePos.x - self:getX(), y = mousePos.y - self:getY() }
32+ return true
33+end
34+
35+function UIWindow:onDragLeave(droppedWidget, mousePos)
36+ -- TODO: auto detect and reconnect anchors
37+end
38+
39+function UIWindow:onDragMove(mousePos, mouseMoved)
40+ if self.static then
41+ return
42+ end
43+ local pos = { x = mousePos.x - self.movingReference.x, y = mousePos.y - self.movingReference.y }
44+ self:setPosition(pos)
45+ self:bindRectToParent()
46+end
Top