BEAWARE all game functionality is disabled with this commit for a while

* rework client modules
* hide main window when loading
* remake top menu functions
* rework modules autoload
* improve path resolving for otml and lua
* move core_widgets to core_lib
* fix tooltip issues
* split some styles
* add bit32 lua library
* fix assert issues
* fix compilation on linux 32 systems
* rework gcc compile options
* renable and fix some warnings
* remove unused constants
* speedup sprite cache
* move UIGame to lua (not funcional yet)
* fix a lot of issues in x11 window
* fix crash handler
* add some warnings do uiwidget
and much more...
This commit is contained in:
Eduardo Bart
2012-02-20 00:27:08 -02:00
parent 96358b317d
commit e03bf33f58
201 changed files with 1443 additions and 707 deletions

View File

@@ -4,9 +4,9 @@ Module
author: OTClient team
website: https://github.com/edubart/otclient
autoload: true
autoload-antecedence: 10
autoload-priority: 10
onLoad: |
@onLoad: |
dofile 'ext/table'
dofile 'ext/string'
dofile 'ext/os'
@@ -17,9 +17,26 @@ Module
dofile 'const'
dofile 'util'
dofile 'globals'
dofile 'dispatcher'
dofile 'effects'
dofile 'settings'
dofile 'keyboard'
dofile 'mouse'
dofile 'ui/effects'
dofile 'ui/radiogroup'
dofile 'ui/tooltip'
dofile 'widgets/uiwidget'
dofile 'widgets/uibutton'
dofile 'widgets/uilabel'
dofile 'widgets/uicheckbox'
dofile 'widgets/uicombobox'
dofile 'widgets/uispinbox'
dofile 'widgets/uiprogressbar'
dofile 'widgets/uitabbar'
dofile 'widgets/uipopupmenu'
dofile 'widgets/uiwindow'
--dofile 'widgets/uiminiwindow'
--dofile 'widgets/uiminiwindowcontainer'
dofile 'widgets/uimessagebox'

View File

@@ -1,20 +0,0 @@
function scheduleEvent(callback, delay)
local event = g_dispatcher.scheduleEvent(callback, delay)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function addEvent(callback, front)
local event = g_dispatcher.addEvent(callback, front)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function removeEvent(event)
if event then
event:cancel()
end
end

View File

@@ -1,57 +1,13 @@
rootWidget = g_ui.getRootWidget()
function importStyle(otui)
g_ui.importStyle(resolvepath(otui, 2))
end
importStyle = g_ui.importStyle
importFont = g_fonts.importFont
setDefaultFont = g_fonts.setDefaultFont
function importFont(otfont)
g_fonts.importFont(resolvepath(otfont, 2))
end
loadUI = g_ui.loadUI
function setDefaultFont(font)
g_fonts.setDefaultFont(font)
end
function displayUI(arg1, options)
local widget
local parent
if options then parent = options.parent end
function displayUI(otui, parent)
parent = parent or rootWidget
-- display otui files
if type(arg1) == 'string' then
local otuiFilePath = resolvepath(arg1, 2)
widget = g_ui.loadUI(otuiFilePath, parent)
-- display already loaded widgets
else
widget = arg1
if parent:hasChild(widget) then
widget:focus()
widget:show()
else
parent:addChild(widget)
widget:show()
end
end
-- apply display options
if widget and options then
for option,value in pairs(options) do
if option == 'locked' and value then
widget:lock()
elseif option == 'visible' then
widget:setVisible(value)
elseif option == 'x' then
widget:setX(value)
elseif option == 'y' then
widget:setY(value)
end
end
end
return widget
end
function loadUI(otui, parent)
local otuiFilePath = resolvepath(otui, 2)
return g_ui.loadUI(otuiFilePath, parent)
end
@@ -65,7 +21,7 @@ function createWidget(style, parent)
local class = _G[className]
if not class then
error('could not find widget class ' .. class)
error('could not find widget class ' .. className)
return
end
@@ -80,6 +36,28 @@ function createWidget(style, parent)
return widget
end
function scheduleEvent(callback, delay)
local event = g_dispatcher.scheduleEvent(callback, delay)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function addEvent(callback, front)
local event = g_dispatcher.addEvent(callback, front)
-- must hold a reference to the callback, otherwise it would be collected
event._callback = callback
return event
end
function removeEvent(event)
if event then
event:cancel()
event._callback = nil
end
end
function reloadModule(name)
local module = g_modules.getModule(name)
if module then

View File

@@ -7,3 +7,4 @@ end
function Mouse.restoreCursor()
g_window.restoreMouseCursor()
end

View File

@@ -32,7 +32,6 @@ function Settings.set(key, value)
g_configs.set(key, convertSettingValue(value))
end
function Settings.setDefault(key, value)
if Settings.exists(key) then return false end
Settings.set(key, value)

View File

@@ -0,0 +1,43 @@
RadioGroup = newclass()
function RadioGroup.create()
local radiogroup = RadioGroup.internalCreate()
radiogroup.widgets = {}
return radiogroup
end
function RadioGroup:destroy()
while #self.widgets ~= 0 do
self:removeWidget(self.widgets[1])
end
end
function RadioGroup:addWidget(widget)
table.insert(self.widgets, widget)
widget.onMousePress = function(widget) self:selectWidget(widget) end
end
function RadioGroup:removeWidget(widget)
if self.selectedWidget == widget then
self:selectWidget(nil)
end
widget.onMousePress = nil
table.removevalue(self.widgets, widget)
end
function RadioGroup:selectWidget(selectedWidget)
if selectedWidget == self.selectedWidget then return end
local previousSelectedWidget = self.selectedWidget
self.selectedWidget = selectedWidget
if previousSelectedWidget then
previousSelectedWidget:setChecked(false)
end
if selectedWidget then
selectedWidget:setChecked(true)
end
signalcall(self.onSelectionChange, self, selectedWidget, previousSelectedWidget)
end

View File

@@ -0,0 +1,94 @@
ToolTip = {}
-- private variables
local toolTipLabel
local currentHoveredWidget
-- private functions
local function moveToolTip(tooltip)
if not tooltip:isVisible() then return end
local pos = g_window.getMousePosition()
pos.y = pos.y + 1
local xdif = g_window.getSize().width - (pos.x + tooltip:getWidth())
if xdif < 2 then
pos.x = pos.x - tooltip:getWidth() - 3
else
pos.x = pos.x + 10
end
tooltip:setPosition(pos)
end
local function onWidgetHoverChange(widget, hovered)
if hovered then
if widget.tooltip then
ToolTip.display(widget.tooltip)
currentHoveredWidget = widget
end
else
if widget == currentHoveredWidget then
ToolTip:hide()
currentHoveredWidget = nil
end
end
end
local function onWidgetStyleApply(widget, styleName, styleNode)
if styleNode.tooltip then
widget.tooltip = styleNode.tooltip
end
end
-- public functions
function ToolTip.init()
connect(UIWidget, { onStyleApply = onWidgetStyleApply,
onHoverChange = onWidgetHoverChange})
addEvent(function()
toolTipLabel = createWidget('Label', rootWidget)
toolTipLabel:setId('toolTip')
toolTipLabel:setBackgroundColor('#111111bb')
toolTipLabel.onMouseMove = moveToolTip
end)
end
function ToolTip.terminate()
disconnect(UIWidget, { onStyleApply = onWidgetStyleApply,
onHoverChange = onWidgetHoverChange })
currentHoveredWidget = nil
toolTipLabel:destroy()
toolTipLabel = nil
ToolTip = nil
end
function ToolTip.display(text)
if text == nil then return end
if not toolTipLabel then return end
toolTipLabel:setText(text)
toolTipLabel:resizeToText()
toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
toolTipLabel:show()
toolTipLabel:raise()
toolTipLabel:enable()
moveToolTip(toolTipLabel)
end
function ToolTip.hide()
toolTipLabel:hide()
end
-- UIWidget extensions
function UIWidget:setTooltip(text)
self.tooltip = text
end
function UIWidget:getTooltip()
return self.tooltip
end
ToolTip.init()
connect(g_app, { onTerminate = ToolTip.terminate })

View File

@@ -3,20 +3,15 @@ function print(...)
for i,v in ipairs(arg) do
msg = msg .. tostring(v) .. "\t"
end
Logger.log(LogInfo, msg)
g_logger.log(LogInfo, msg)
end
function fatal(msg)
Logger.log(LogFatal, msg)
g_logger.log(LogFatal, msg)
end
function setonclose(func)
g_app.onClose = func
end
function exit()
g_app.exit()
end
exit = g_app.exit
quit = g_app.exit
function connect(object, signalsAndSlots, pushFront)
for signal,slot in pairs(signalsAndSlots) do

View File

@@ -0,0 +1,7 @@
UIButton = extends(UIWidget)
function UIButton.create()
local button = UIButton.internalCreate()
button:setFocusable(false)
return button
end

View File

@@ -0,0 +1,13 @@
UICheckBox = extends(UIWidget)
function UICheckBox.create()
local checkbox = UICheckBox.internalCreate()
checkbox:setFocusable(false)
checkbox:setTextAlign(AlignLeft)
return checkbox
end
function UICheckBox:onMousePress(mousePos, mouseButton)
self:setChecked(not self:isChecked())
return true
end

View File

@@ -0,0 +1,69 @@
UIComboBox = extends(UIWidget)
function UIComboBox.create()
local combobox = UIComboBox.internalCreate()
combobox.m_options = {}
combobox.m_currentIndex = -1
return combobox
end
function UIComboBox:setCurrentOption(text)
if not self.m_options then return end
for i,v in ipairs(self.m_options) do
if v.text == text and self.m_currentIndex ~= i then
self.m_currentIndex = i
self:setText(text)
self:onOptionChange(text, v.data)
return
end
end
end
function UIComboBox:setCurrentIndex(index)
if index >= 1 and index <= #self.m_options then
local v = self.m_options[index]
self.m_currentIndex = index
self:setText(v.text)
self:onOptionChange(v.text, v.data)
end
end
function UIComboBox:addOption(text, data)
table.insert(self.m_options, { text = text, data = data })
local index = #self.m_options
if index == 1 then self:setCurrentOption(text) end
return index
end
function UIComboBox:onMousePress(mousePos, mouseButton)
local menu = createWidget(self:getStyleName() .. 'PopupMenu', self)
for i,v in ipairs(self.m_options) do
menu:addOption(v.text, function() self:setCurrentOption(v.text) end)
end
menu:setWidth(self:getWidth())
menu:display({ x = self:getX(), y = self:getY() + self:getHeight() })
connect(menu, { onDestroy = function() self:setOn(false) end })
self:setOn(true)
return true
end
function UIComboBox:onMouseWheel(mousePos, direction)
if direction == MouseWheelUp and self.m_currentIndex > 1 then
self:setCurrentIndex(self.m_currentIndex - 1)
elseif direction == MouseWheelDown and self.m_currentIndex < #self.m_options then
self:setCurrentIndex(self.m_currentIndex + 1)
end
return true
end
function UIComboBox:onStyleApply(styleName, styleNode)
if styleNode.options then
for k,option in pairs(styleNode.options) do
self:addOption(option)
end
end
end
function UIComboBox:onOptionChange(optionText, optionData)
-- nothing todo
end

View File

@@ -0,0 +1,9 @@
UILabel = extends(UIWidget)
function UILabel.create()
local label = UILabel.internalCreate()
label:setPhantom(true)
label:setFocusable(false)
label:setTextAlign(AlignLeft)
return label
end

View File

@@ -0,0 +1,65 @@
UIMessageBox = extends(UIWindow)
MessageBoxOk = 1
MessageBoxCancel = 2
-- messagebox cannot be created from otui files
UIMessageBox.create = nil
function UIMessageBox.display(title, message, flags)
local messagebox = UIMessageBox.internalCreate()
rootWidget:addChild(messagebox)
messagebox:setStyle('MessageBoxWindow')
messagebox:setText(title)
local messageLabel = createWidget('MessageBoxLabel', messagebox)
messageLabel:setText(message)
messageLabel:resizeToText()
messagebox:setWidth(math.max(messageLabel:getWidth() + 48, messagebox:getWidth()))
messagebox:setHeight(math.max(messageLabel:getHeight() + 64, messagebox:getHeight()))
-- setup messagebox first button
local buttonRight = createWidget('MessageBoxRightButton', messagebox)
if flags == MessageBoxOk then
buttonRight:setText('Ok')
connect(buttonRight, { onClick = function(self) self:getParent():ok() end })
connect(messagebox, { onEnter = function(self) self:ok() end })
connect(messagebox, { onEscape = function(self) self:ok() end })
elseif flags == MessageBoxCancel then
buttonRight:setText('Cancel')
connect(buttonRight, { onClick = function(self) self:getParent():cancel() end })
connect(messagebox, { onEnter = function(self) self:cancel() end })
connect(messagebox, { onEscape = function(self) self:cancel() end })
end
messagebox:lock()
return messagebox
end
function displayInfoBox(title, message)
return UIMessageBox.display(title, message, MessageBoxOk)
end
function displayErrorBox(title, message)
return UIMessageBox.display(title, message, MessageBoxOk)
end
function displayCancelBox(title, message)
return UIMessageBox.display(title, message, MessageBoxCancel)
end
function UIMessageBox:ok()
signalcall(self.onOk, self)
self.onOk = nil
self:destroy()
end
function UIMessageBox:cancel()
signalcall(self.onCancel, self)
self.onCancel = nil
self:destroy()
end

View File

@@ -0,0 +1,79 @@
UIPopupMenu = extends(UIWidget)
local currentMenu
function UIPopupMenu.create()
local menu = UIPopupMenu.internalCreate()
local layout = UIVerticalLayout.create(menu)
layout:setFitChildren(true)
menu:setLayout(layout)
return menu
end
function UIPopupMenu:display(pos)
-- don't display if not options was added
if self:getChildCount() == 0 then
self:destroy()
return
end
if currentMenu then
currentMenu:destroy()
end
rootWidget:addChild(self)
self:setPosition(pos)
self:grabMouse()
self:grabKeyboard()
currentMenu = self
end
function UIPopupMenu:onGeometryChange()
self:bindRectToParent()
end
function UIPopupMenu:addOption(optionName, optionCallback)
local optionWidget = createWidget(self:getStyleName() .. 'Button', self)
local lastOptionWidget = self:getLastChild()
optionWidget.onClick = function(self)
optionCallback()
self:getParent():destroy()
end
optionWidget:setText(optionName)
local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 6
self:setWidth(math.max(self:getWidth(), width))
end
function UIPopupMenu:addSeparator()
createWidget(self:getStyleName() .. 'Separator', self)
end
function UIPopupMenu:onDestroy()
if currentMenu == self then
currentMenu = nil
end
end
function UIPopupMenu:onMousePress(mousePos, mouseButton)
-- clicks outside menu area destroys the menu
if not self:containsPoint(mousePos) then
self:destroy()
end
return true
end
function UIPopupMenu:onKeyPress(keyCode, keyboardModifiers)
if keyCode == KeyEscape then
self:destroy()
return true
end
return false
end
-- close all menus when the window is resized
local function onRootGeometryUpdate()
if currentMenu then
currentMenu:destroy()
end
end
connect(rootWidget, { onGeometryChange = onRootGeometryUpdate} )

View File

@@ -0,0 +1,31 @@
UIProgressBar = extends(UIWidget)
function UIProgressBar.create()
local progressbar = UIProgressBar.internalCreate()
progressbar:setFocusable(false)
progressbar:setPhantom(true)
progressbar.m_percent = 0
progressbar:updateBackground()
return progressbar
end
function UIProgressBar:setPercent(percent)
self.m_percent = math.max(math.min(percent, 100), 0)
self:updateBackground()
end
function UIProgressBar:getPercent()
return self.m_percent
end
function UIProgressBar:updateBackground()
local width = math.max((self.m_percent * self:getWidth())/100, 1)
local height = self:getHeight()
self:setBackgroundSize({width=width, height=height})
end
function UIProgressBar:onGeometryChange(oldRect, newRect)
self:updateBackground()
end

View File

@@ -0,0 +1,88 @@
UISpinBox = extends(UILineEdit)
function UISpinBox.create()
local spinbox = UISpinBox.internalCreate()
spinbox:setValidCharacters('0123456789')
spinbox.m_minimum = 0
spinbox.m_maximum = 0
spinbox:setCurrentIndex(0)
spinbox:setText("0")
return spinbox
end
function UISpinBox:setCurrentIndex(index)
if index >= self.m_minimum and index <= self.m_maximum then
if self:getText():len() > 0 then
self:setText(index)
end
self.m_currentIndex = index
self:onIndexChange(index)
end
end
function UISpinBox:setMinimum(minimum)
if minimum > self.m_maximum then
print("[UISpinBox:setMinimum]: minimum value cant be greater than maximum")
return false
end
if self.m_currentIndex < minimum then
self:setCurrentIndex(minimum)
end
self.m_minimum = minimum
end
function UISpinBox:setMaximum(maximum)
if maximum < self.m_minimum then
print("[UISpinBox:setMaximum]: maximum value cant be lower than minimum")
return false
end
if self.m_currentIndex > maximum then
self:setCurrentIndex(maximum)
end
self.m_maximum = maximum
end
function UISpinBox:getCurrentIndex()
return self.m_currentIndex
end
function UISpinBox:onMouseWheel(mousePos, direction)
if direction == MouseWheelUp then
self:setCurrentIndex(self.m_currentIndex + 1)
elseif direction == MouseWheelDown then
self:setCurrentIndex(self.m_currentIndex - 1)
end
return true
end
function UISpinBox:onTextChange(text, oldText)
if text:len() == 0 then
self:setCurrentIndex(self.m_minimum)
return
end
local number = tonumber(text)
if not number or number > self.m_maximum or number < self.m_minimum then
self:setText(oldText)
return
end
self:setCurrentIndex(number)
end
function UISpinBox:onIndexChange(index)
-- nothing todo
end
function UISpinBox:onStyleApply(styleName, styleNode)
-- tonumber converts to 0 if not valid
if styleNode.maximum and tonumber(styleNode.maximum) then
self:setMaximum(tonumber(styleNode.maximum))
end
if styleNode.minimum and tonumber(styleNode.minimum) then
self:setMinimum(tonumber(styleNode.minimum))
end
end

View File

@@ -0,0 +1,124 @@
UITabBar = extends(UIWidget)
-- private functions
local function onTabClick(tab)
tab.tabBar:selectTab(tab)
end
local function tabBlink(tab)
if not tab.blinking then return end
tab:setOn(not tab:isOn())
scheduleEvent(function() tabBlink(tab) end, 500)
end
-- public functions
function UITabBar.create()
local tabbar = UITabBar.internalCreate()
tabbar:setFocusable(false)
tabbar.tabs = {}
return tabbar
end
function UITabBar:setContentWidget(widget)
self.contentWidget = widget
if #self.tabs > 0 then
self.contentWidget:addChild(self.tabs[1].tabPanel)
end
end
function UITabBar:addTab(text, panel)
if panel == nil then
panel = createWidget(self:getStyleName() .. 'Panel')
end
local tab = createWidget(self:getStyleName() .. 'Button', self)
tab.tabPanel = panel
tab.tabBar = self
tab:setText(text)
tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight())
connect(tab, { onClick = onTabClick })
table.insert(self.tabs, tab)
if #self.tabs == 1 then
self:selectTab(tab)
end
return tab
end
function UITabBar:removeTab(tab)
local index = table.find(self.tabs, tab)
if index == nil then return end
if self.currentTab == tab then
self:selectPrevTab()
end
table.remove(self.tabs, index)
tab:destroy()
end
function UITabBar:getTab(text)
for k,tab in pairs(self.tabs) do
if tab:getText():lower() == text:lower() then
return tab
end
end
end
function UITabBar:selectTab(tab)
if self.currentTab == tab then return end
if self.contentWidget then
local selectedWidget = self.contentWidget:getFirstChild()
if selectedWidget then
self.contentWidget:removeChild(selectedWidget)
end
self.contentWidget:addChild(tab.tabPanel)
tab.tabPanel:fill('parent')
end
if self.currentTab then
self.currentTab:setChecked(false)
end
signalcall(self.onTabChange, self, tab)
self.currentTab = tab
tab:setChecked(true)
tab:setOn(false)
tab.blinking = false
end
function UITabBar:selectNextTab()
if self.currentTab == nil then return end
local index = table.find(self.tabs, self.currentTab)
if index == nil then return end
local nextTab = self.tabs[index + 1] or self.tabs[1]
if not nextTab then return end
self:selectTab(nextTab)
end
function UITabBar:selectPrevTab()
if self.currentTab == nil then return end
local index = table.find(self.tabs, self.currentTab)
if index == nil then return end
local prevTab = self.tabs[index - 1] or self.tabs[#self.tabs]
if not prevTab then return end
self:selectTab(prevTab)
end
function UITabBar:blinkTab(tab)
if tab:isChecked() or tab.blinking then return end
tab.blinking = true
tabBlink(tab)
end
function UITabBar:getTabPanel(tab)
return tab.tabPanel
end
function UITabBar:getCurrentTabPanel()
if self.currentTab then
return self.currentTab.tabPanel
end
end
function UITabBar:getCurrentTab()
return self.currentTab
end

View File

@@ -0,0 +1,19 @@
function UIWidget:setMargin(...)
local params = {...}
if #params == 1 then
self:setMarginTop(params[1])
self:setMarginRight(params[1])
self:setMarginBottom(params[1])
self:setMarginLeft(params[1])
elseif #params == 2 then
self:setMarginTop(params[1])
self:setMarginRight(params[2])
self:setMarginBottom(params[1])
self:setMarginLeft(params[2])
elseif #params == 4 then
self:setMarginTop(params[1])
self:setMarginRight(params[2])
self:setMarginBottom(params[3])
self:setMarginLeft(params[4])
end
end

View File

@@ -0,0 +1,38 @@
UIWindow = extends(UIWidget)
function UIWindow.create()
local window = UIWindow.internalCreate()
window:setTextAlign(AlignTopCenter)
window:setDragable(true)
return window
end
function UIWindow:onKeyDown(keyCode, keyboardModifiers)
if keyboardModifiers == KeyboardNoModifier then
if keyCode == KeyEnter then
signalcall(self.onEnter, self)
elseif keyCode == KeyEscape then
signalcall(self.onEscape, self)
end
end
end
function UIWindow:onFocusChange(focused)
if focused then self:raise() end
end
function UIWindow:onDragEnter(mousePos)
self:breakAnchors()
self.movingReference = { x = mousePos.x - self:getX(), y = mousePos.y - self:getY() }
return true
end
function UIWindow:onDragLeave(droppedWidget, mousePos)
-- TODO: auto detect and reconnect anchors
end
function UIWindow:onDragMove(mousePos, mouseMoved)
local pos = { x = mousePos.x - self.movingReference.x, y = mousePos.y - self.movingReference.y }
self:setPosition(pos)
self:bindRectToParent()
end