mirror of
https://github.com/edubart/otclient.git
synced 2025-10-14 03:24:55 +02:00
Refactoring and flexibility changes
* Split game module into game and game_interface * Move core_lib to corelib * Move miniwindow to corelib * Introduce init.lua script for initializing the client, giving much more flexibility * OTClient is no longer Application derived and is much simpler
This commit is contained in:
312
modules/corelib/const.lua
Normal file
312
modules/corelib/const.lua
Normal file
@@ -0,0 +1,312 @@
|
||||
AnchorNone = 0
|
||||
AnchorTop = 1
|
||||
AnchorBottom = 2
|
||||
AnchorLeft = 3
|
||||
AnchorRight = 4
|
||||
AnchorVerticalCenter = 5
|
||||
AnchorHorizontalCenter = 6
|
||||
|
||||
LogDebug = 0
|
||||
LogInfo = 1
|
||||
LogWarning = 2
|
||||
LogError = 3
|
||||
LogFatal = 4
|
||||
|
||||
MouseFocusReason = 0
|
||||
KeyboardFocusReason = 1
|
||||
ActiveFocusReason = 2
|
||||
OtherFocusReason = 3
|
||||
|
||||
KeyboardNoModifier = 0
|
||||
KeyboardCtrlModifier = 1
|
||||
KeyboardAltModifier = 2
|
||||
KeyboardCtrlAltModifier = 3
|
||||
KeyboardShiftModifier = 4
|
||||
KeyboardCtrlShiftModifier = 5
|
||||
KeyboardAltShiftModifier = 6
|
||||
KeyboardCtrlAltShiftModifier = 7
|
||||
|
||||
MouseNoButton = 0
|
||||
MouseLeftButton = 1
|
||||
MouseRightButton = 2
|
||||
MouseMidButton = 3
|
||||
|
||||
MouseNoWheel = 0
|
||||
MouseWheelUp = 1
|
||||
MouseWheelDown = 2
|
||||
|
||||
AlignNone = 0
|
||||
AlignLeft = 1
|
||||
AlignRight = 2
|
||||
AlignTop = 4
|
||||
AlignBottom = 8
|
||||
AlignHorizontalCenter = 16
|
||||
AlignVerticalCenter = 32
|
||||
AlignTopLeft = 5
|
||||
AlignTopRight = 6
|
||||
AlignBottomLeft = 9
|
||||
AlignBottomRight = 10
|
||||
AlignLeftCenter = 33
|
||||
AlignRightCenter = 34
|
||||
AlignTopCenter = 20
|
||||
AlignBottomCenter = 24
|
||||
AlignCenter = 48
|
||||
|
||||
KeyUnknown = 0
|
||||
KeyEscape = 1
|
||||
KeyTab = 2
|
||||
KeyBackspace = 3
|
||||
KeyEnter = 5
|
||||
KeyInsert = 6
|
||||
KeyDelete = 7
|
||||
KeyPause = 8
|
||||
KeyPrintScreen = 9
|
||||
KeyHome = 10
|
||||
KeyEnd = 11
|
||||
KeyPageUp = 12
|
||||
KeyPageDown = 13
|
||||
KeyUp = 14
|
||||
KeyDown = 15
|
||||
KeyLeft = 16
|
||||
KeyRight = 17
|
||||
KeyNumLock = 18
|
||||
KeyScrollLock = 19
|
||||
KeyCapsLock = 20
|
||||
KeyCtrl = 21
|
||||
KeyShift = 22
|
||||
KeyAlt = 23
|
||||
KeyMeta = 25
|
||||
KeyMenu = 26
|
||||
KeySpace = 32 -- ' '
|
||||
KeyExclamation = 33 -- !
|
||||
KeyQuote = 34 -- "
|
||||
KeyNumberSign = 35 -- #
|
||||
KeyDollar = 36 -- $
|
||||
KeyPercent = 37 -- %
|
||||
KeyAmpersand = 38 -- &
|
||||
KeyApostrophe = 39 -- '
|
||||
KeyLeftParen = 40 -- (
|
||||
KeyRightParen = 41 -- )
|
||||
KeyAsterisk = 42 -- *
|
||||
KeyPlus = 43 -- +
|
||||
KeyComma = 44 -- ,
|
||||
KeyMinus = 45 -- -
|
||||
KeyPeriod = 46 -- .
|
||||
KeySlash = 47 -- /
|
||||
Key0 = 48 -- 0
|
||||
Key1 = 49 -- 1
|
||||
Key2 = 50 -- 2
|
||||
Key3 = 51 -- 3
|
||||
Key4 = 52 -- 4
|
||||
Key5 = 53 -- 5
|
||||
Key6 = 54 -- 6
|
||||
Key7 = 55 -- 7
|
||||
Key8 = 56 -- 8
|
||||
Key9 = 57 -- 9
|
||||
KeyColon = 58 -- :
|
||||
KeySemicolon = 59 -- ;
|
||||
KeyLess = 60 -- <
|
||||
KeyEqual = 61 -- =
|
||||
KeyGreater = 62 -- >
|
||||
KeyQuestion = 63 -- ?
|
||||
KeyAtSign = 64 -- @
|
||||
KeyA = 65 -- a
|
||||
KeyB = 66 -- b
|
||||
KeyC = 67 -- c
|
||||
KeyD = 68 -- d
|
||||
KeyE = 69 -- e
|
||||
KeyF = 70 -- f
|
||||
KeyG = 71 -- g
|
||||
KeyH = 72 -- h
|
||||
KeyI = 73 -- i
|
||||
KeyJ = 74 -- j
|
||||
KeyK = 75 -- k
|
||||
KeyL = 76 -- l
|
||||
KeyM = 77 -- m
|
||||
KeyN = 78 -- n
|
||||
KeyO = 79 -- o
|
||||
KeyP = 80 -- p
|
||||
KeyQ = 81 -- q
|
||||
KeyR = 82 -- r
|
||||
KeyS = 83 -- s
|
||||
KeyT = 84 -- t
|
||||
KeyU = 85 -- u
|
||||
KeyV = 86 -- v
|
||||
KeyW = 87 -- w
|
||||
KeyX = 88 -- x
|
||||
KeyY = 89 -- y
|
||||
KeyZ = 90 -- z
|
||||
KeyLeftBracket = 91 -- [
|
||||
KeyBackslash = 92 -- '\'
|
||||
KeyRightBracket = 93 -- ]
|
||||
KeyCaret = 94 -- ^
|
||||
KeyUnderscore = 95 -- _
|
||||
KeyGrave = 96 -- `
|
||||
KeyLeftCurly = 123 -- {
|
||||
KeyBar = 124 -- |
|
||||
KeyRightCurly = 125 -- }
|
||||
KeyTilde = 126 -- ~
|
||||
KeyF1 = 128
|
||||
KeyF2 = 129
|
||||
KeyF3 = 130
|
||||
KeyF4 = 131
|
||||
KeyF5 = 132
|
||||
KeyF6 = 134
|
||||
KeyF7 = 135
|
||||
KeyF8 = 136
|
||||
KeyF9 = 137
|
||||
KeyF10 = 138
|
||||
KeyF11 = 139
|
||||
KeyF12 = 140
|
||||
KeyNumpad0 = 141
|
||||
KeyNumpad1 = 142
|
||||
KeyNumpad2 = 143
|
||||
KeyNumpad3 = 144
|
||||
KeyNumpad4 = 145
|
||||
KeyNumpad5 = 146
|
||||
KeyNumpad6 = 147
|
||||
KeyNumpad7 = 148
|
||||
KeyNumpad8 = 149
|
||||
KeyNumpad9 = 150
|
||||
|
||||
KeyCodeDescs = {
|
||||
[KeyUnknown] = 'Unknown',
|
||||
[KeyEscape] = 'Escape',
|
||||
[KeyTab] = 'Tab',
|
||||
[KeyBackspace] = 'Backspace',
|
||||
[KeyEnter] = 'Enter',
|
||||
[KeyInsert] = 'Insert',
|
||||
[KeyDelete] = 'Delete',
|
||||
[KeyPause] = 'Pause',
|
||||
[KeyPrintScreen] = 'PrintScreen',
|
||||
[KeyHome] = 'Home',
|
||||
[KeyEnd] = 'End',
|
||||
[KeyPageUp] = 'PageUp',
|
||||
[KeyPageDown] = 'PageDown',
|
||||
[KeyUp] = 'Up',
|
||||
[KeyDown] = 'Down',
|
||||
[KeyLeft] = 'Left',
|
||||
[KeyRight] = 'Right',
|
||||
[KeyNumLock] = 'NumLock',
|
||||
[KeyScrollLock] = 'ScrollLock',
|
||||
[KeyCapsLock] = 'CapsLock',
|
||||
[KeyCtrl] = 'Ctrl',
|
||||
[KeyShift] = 'Shift',
|
||||
[KeyAlt] = 'Alt',
|
||||
[KeyMeta] = 'Meta',
|
||||
[KeyMenu] = 'Menu',
|
||||
[KeySpace] = 'Space',
|
||||
[KeyExclamation] = '!',
|
||||
[KeyQuote] = '\"',
|
||||
[KeyNumberSign] = '#',
|
||||
[KeyDollar] = '$',
|
||||
[KeyPercent] = '%',
|
||||
[KeyAmpersand] = '&',
|
||||
[KeyApostrophe] = '\'',
|
||||
[KeyLeftParen] = '(',
|
||||
[KeyRightParen] = ')',
|
||||
[KeyAsterisk] = '*',
|
||||
[KeyPlus] = 'Plus',
|
||||
[KeyComma] = ',',
|
||||
[KeyMinus] = '-',
|
||||
[KeyPeriod] = '.',
|
||||
[KeySlash] = '/',
|
||||
[Key0] = '0',
|
||||
[Key1] = '1',
|
||||
[Key2] = '2',
|
||||
[Key3] = '3',
|
||||
[Key4] = '4',
|
||||
[Key5] = '5',
|
||||
[Key6] = '6',
|
||||
[Key7] = '7',
|
||||
[Key8] = '8',
|
||||
[Key9] = '9',
|
||||
[KeyColon] = ':',
|
||||
[KeySemicolon] = ';',
|
||||
[KeyLess] = '<',
|
||||
[KeyEqual] = '=',
|
||||
[KeyGreater] = '>',
|
||||
[KeyQuestion] = '?',
|
||||
[KeyAtSign] = '@',
|
||||
[KeyA] = 'A',
|
||||
[KeyB] = 'B',
|
||||
[KeyC] = 'C',
|
||||
[KeyD] = 'D',
|
||||
[KeyE] = 'E',
|
||||
[KeyF] = 'F',
|
||||
[KeyG] = 'G',
|
||||
[KeyH] = 'H',
|
||||
[KeyI] = 'I',
|
||||
[KeyJ] = 'J',
|
||||
[KeyK] = 'K',
|
||||
[KeyL] = 'L',
|
||||
[KeyM] = 'M',
|
||||
[KeyN] = 'N',
|
||||
[KeyO] = 'O',
|
||||
[KeyP] = 'P',
|
||||
[KeyQ] = 'Q',
|
||||
[KeyR] = 'R',
|
||||
[KeyS] = 'S',
|
||||
[KeyT] = 'T',
|
||||
[KeyU] = 'U',
|
||||
[KeyV] = 'V',
|
||||
[KeyW] = 'W',
|
||||
[KeyX] = 'X',
|
||||
[KeyY] = 'Y',
|
||||
[KeyZ] = 'Z',
|
||||
[KeyLeftBracket] = '[',
|
||||
[KeyBackslash] = '\\',
|
||||
[KeyRightBracket] = ']',
|
||||
[KeyCaret] = '^',
|
||||
[KeyUnderscore] = '_',
|
||||
[KeyGrave] = '`',
|
||||
[KeyLeftCurly] = '{',
|
||||
[KeyBar] = '|',
|
||||
[KeyRightCurly] = '}',
|
||||
[KeyTilde] = '~',
|
||||
[KeyF1] = 'F1',
|
||||
[KeyF2] = 'F2',
|
||||
[KeyF3] = 'F3',
|
||||
[KeyF4] = 'F4',
|
||||
[KeyF5] = 'F5',
|
||||
[KeyF6] = 'F6',
|
||||
[KeyF7] = 'F7',
|
||||
[KeyF8] = 'F8',
|
||||
[KeyF9] = 'F9',
|
||||
[KeyF10] = 'F10',
|
||||
[KeyF11] = 'F11',
|
||||
[KeyF12] = 'F12',
|
||||
[KeyNumpad0] = 'Numpad0',
|
||||
[KeyNumpad1] = 'Numpad1',
|
||||
[KeyNumpad2] = 'Numpad2',
|
||||
[KeyNumpad3] = 'Numpad3',
|
||||
[KeyNumpad4] = 'Numpad4',
|
||||
[KeyNumpad5] = 'Numpad5',
|
||||
[KeyNumpad6] = 'Numpad6',
|
||||
[KeyNumpad7] = 'Numpad7',
|
||||
[KeyNumpad8] = 'Numpad8',
|
||||
[KeyNumpad9] = 'Numpad9'
|
||||
}
|
||||
|
||||
SpeakSay = 1
|
||||
SpeakWhisper = 2
|
||||
SpeakYell = 3
|
||||
SpeakBroadcast = 4
|
||||
SpeakPrivate = 5
|
||||
SpeakPrivateRed = 6
|
||||
SpeakPrivatePlayerToNpc = 7
|
||||
SpeakPrivateNpcToPlayer = 8
|
||||
SpeakChannelYellow = 9
|
||||
SpeakChannelWhite = 10
|
||||
SpeakChannelRed = 11
|
||||
SpeakChannelOrange = 12
|
||||
SpeakMonsterSay = 13
|
||||
SpeakMonsterYell = 14
|
||||
|
||||
|
||||
FightOffensive = 1
|
||||
FightBalanced = 2
|
||||
FightDefensive = 3
|
||||
|
||||
DontChase = 0
|
||||
ChaseOpponent = 1
|
21
modules/corelib/corelib.otmod
Normal file
21
modules/corelib/corelib.otmod
Normal file
@@ -0,0 +1,21 @@
|
||||
Module
|
||||
name: corelib
|
||||
description: Contains core lua classes, functions and constants used by other modules
|
||||
author: OTClient team
|
||||
website: www.otclient.info
|
||||
reloadable: false
|
||||
|
||||
@onLoad: |
|
||||
dofiles 'ext'
|
||||
dofiles 'math'
|
||||
|
||||
dofile 'const'
|
||||
dofile 'util'
|
||||
dofile 'globals'
|
||||
dofile 'settings'
|
||||
dofile 'keyboard'
|
||||
dofile 'mouse'
|
||||
|
||||
dofiles 'ui'
|
||||
dofiles 'widgets'
|
||||
|
6
modules/corelib/ext/os.lua
Normal file
6
modules/corelib/ext/os.lua
Normal file
@@ -0,0 +1,6 @@
|
||||
function os.execute(command)
|
||||
local f = assert(io.popen(command, 'r'))
|
||||
local data = assert(f:read('*a'))
|
||||
f:close()
|
||||
print(data)
|
||||
end
|
45
modules/corelib/ext/string.lua
Normal file
45
modules/corelib/ext/string.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
function string.split(s, delim)
|
||||
local start = 1
|
||||
local results = {}
|
||||
while true do
|
||||
local pos = string.find(s, delim, start, true)
|
||||
if not pos then
|
||||
break
|
||||
end
|
||||
table.insert(results, string.sub(s, start, pos-1))
|
||||
start = pos + string.len(delim)
|
||||
end
|
||||
table.insert(results, string.sub(s, start))
|
||||
table.removevalue(results, '')
|
||||
return results
|
||||
end
|
||||
|
||||
function string.starts(s, start)
|
||||
return string.sub(s, 1, #start) == start
|
||||
end
|
||||
|
||||
function string.trim(s)
|
||||
return string.match(s, '^%s*(.*%S)') or ''
|
||||
end
|
||||
|
||||
function string.explode(str, sep, limit)
|
||||
if(type(sep) ~= 'string' or tostring(str):len() == 0 or sep:len() == 0) then
|
||||
return {}
|
||||
end
|
||||
|
||||
local i, pos, tmp, t = 0, 1, "", {}
|
||||
for s, e in function() return string.find(str, sep, pos) end do
|
||||
tmp = str:sub(pos, s - 1):trim()
|
||||
table.insert(t, tmp)
|
||||
pos = e + 1
|
||||
|
||||
i = i + 1
|
||||
if(limit ~= nil and i == limit) then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
tmp = str:sub(pos):trim()
|
||||
table.insert(t, tmp)
|
||||
return t
|
||||
end
|
57
modules/corelib/ext/table.lua
Normal file
57
modules/corelib/ext/table.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
function table.dump(t, depth)
|
||||
if not depth then depth = 0 end
|
||||
for k,v in pairs(t) do
|
||||
str = (' '):rep(depth * 2) .. k .. ': '
|
||||
if type(v) ~= "table" then
|
||||
print(str .. tostring(v))
|
||||
else
|
||||
print(str)
|
||||
table.dump(v, depth+1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function table.copy(t)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
res[k] = v
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function table.selectivecopy(t, keys)
|
||||
local res = { }
|
||||
for i,v in ipairs(keys) do
|
||||
res[v] = t[v]
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function table.merge(t, src)
|
||||
for k,v in pairs(src) do
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function table.find(t, value)
|
||||
for k,v in pairs(t) do
|
||||
if v == value then return k end
|
||||
end
|
||||
end
|
||||
|
||||
function table.removevalue(t, value)
|
||||
for k,v in pairs(t) do
|
||||
if v == value then
|
||||
table.remove(t, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function table.compare(t, other)
|
||||
if #t ~= #other then return false end
|
||||
for k,v in pairs(t) do
|
||||
if v ~= other[k] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
87
modules/corelib/globals.lua
Normal file
87
modules/corelib/globals.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
rootWidget = g_ui.getRootWidget()
|
||||
|
||||
importStyle = g_ui.importStyle
|
||||
importFont = g_fonts.importFont
|
||||
setDefaultFont = g_fonts.setDefaultFont
|
||||
|
||||
-- G is used as a global table to save variables in memory between reloads
|
||||
G = G or {}
|
||||
|
||||
function loadUI(otui, parent)
|
||||
local otuiFilePath = resolvepath(otui, 2)
|
||||
return g_ui.loadUI(otuiFilePath, parent)
|
||||
end
|
||||
|
||||
function displayUI(otui, parent)
|
||||
parent = parent or rootWidget
|
||||
local otuiFilePath = resolvepath(otui, 2)
|
||||
return g_ui.loadUI(otuiFilePath, parent)
|
||||
end
|
||||
|
||||
function createWidget(stylename, parent)
|
||||
if type(parent) == 'string' then
|
||||
parent = rootWidget:recursiveGetChildById(parent)
|
||||
end
|
||||
local widget = g_ui.createWidgetFromStyle(stylename, parent)
|
||||
return widget
|
||||
end
|
||||
|
||||
function scheduleEvent(callback, delay)
|
||||
local event = g_eventDispatcher.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_eventDispatcher.addEvent(callback, front)
|
||||
-- must hold a reference to the callback, otherwise it would be collected
|
||||
event._callback = callback
|
||||
return event
|
||||
end
|
||||
|
||||
function cycleEvent(callback, front)
|
||||
local event = g_eventDispatcher.cycleEvent(callback, front)
|
||||
-- must hold a reference to the callback, otherwise it would be collected
|
||||
event._callback = callback
|
||||
return event
|
||||
end
|
||||
|
||||
|
||||
function periodicalEvent(eventFunc, conditionFunc, delay, autoRepeatDelay)
|
||||
delay = delay or 30
|
||||
autoRepeatDelay = autoRepeatDelay or delay
|
||||
|
||||
local func
|
||||
func = function()
|
||||
if conditionFunc and not conditionFunc() then
|
||||
func = nil
|
||||
return
|
||||
end
|
||||
eventFunc()
|
||||
scheduleEvent(func, delay)
|
||||
end
|
||||
|
||||
scheduleEvent(function()
|
||||
func()
|
||||
end, autoRepeatDelay)
|
||||
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
|
||||
module:reload()
|
||||
end
|
||||
end
|
||||
|
||||
function reloadModules()
|
||||
g_modules.reloadModules()
|
||||
end
|
146
modules/corelib/keyboard.lua
Normal file
146
modules/corelib/keyboard.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
Keyboard = {}
|
||||
|
||||
-- private functions
|
||||
function translateKeyCombo(keyCombo)
|
||||
if not keyCombo or #keyCombo == 0 then return nil end
|
||||
table.sort(keyCombo)
|
||||
local keyComboDesc = ''
|
||||
for k,v in pairs(keyCombo) do
|
||||
local keyDesc = KeyCodeDescs[v]
|
||||
if keyDesc == nil then return nil end
|
||||
keyComboDesc = keyComboDesc .. '+' .. keyDesc
|
||||
end
|
||||
keyComboDesc = keyComboDesc:sub(2)
|
||||
return keyComboDesc
|
||||
end
|
||||
|
||||
local function retranslateKeyComboDesc(keyComboDesc)
|
||||
if keyComboDesc == nil then
|
||||
error('Unable to translate key combo \'' .. keyComboDesc .. '\'')
|
||||
end
|
||||
local keyCombo = {}
|
||||
for i,currentKeyDesc in ipairs(keyComboDesc:split('+')) do
|
||||
for keyCode, keyDesc in pairs(KeyCodeDescs) do
|
||||
if keyDesc:lower() == currentKeyDesc:trim():lower() then
|
||||
table.insert(keyCombo, keyCode)
|
||||
end
|
||||
end
|
||||
end
|
||||
return translateKeyCombo(keyCombo)
|
||||
end
|
||||
|
||||
function determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||
local keyCombo = {}
|
||||
if keyCode == KeyCtrl or keyCode == KeyShift or keyCode == KeyAlt then
|
||||
table.insert(keyCombo, keyCode)
|
||||
elseif KeyCodeDescs[keyCode] ~= nil then
|
||||
if keyboardModifiers == KeyboardCtrlModifier then
|
||||
table.insert(keyCombo, KeyCtrl)
|
||||
elseif keyboardModifiers == KeyboardAltModifier then
|
||||
table.insert(keyCombo, KeyAlt)
|
||||
elseif keyboardModifiers == KeyboardCtrlAltModifier then
|
||||
table.insert(keyCombo, KeyCtrl)
|
||||
table.insert(keyCombo, KeyAlt)
|
||||
elseif keyboardModifiers == KeyboardShiftModifier then
|
||||
table.insert(keyCombo, KeyShift)
|
||||
elseif keyboardModifiers == KeyboardCtrlShiftModifier then
|
||||
table.insert(keyCombo, KeyCtrl)
|
||||
table.insert(keyCombo, KeyShift)
|
||||
elseif keyboardModifiers == KeyboardAltShiftModifier then
|
||||
table.insert(keyCombo, KeyAlt)
|
||||
table.insert(keyCombo, KeyShift)
|
||||
elseif keyboardModifiers == KeyboardCtrlAltShiftModifier then
|
||||
table.insert(keyCombo, KeyCtrl)
|
||||
table.insert(keyCombo, KeyAlt)
|
||||
table.insert(keyCombo, KeyShift)
|
||||
end
|
||||
table.insert(keyCombo, keyCode)
|
||||
end
|
||||
table.sort(keyCombo)
|
||||
return translateKeyCombo(keyCombo)
|
||||
end
|
||||
|
||||
local function onWidgetKeyDown(widget, keyCode, keyboardModifiers)
|
||||
if keyCode == KeyUnknown then return false end
|
||||
local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||
local callback = widget.boundKeyDownCombos[keyComboDesc]
|
||||
if callback then
|
||||
callback()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function onWidgetKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
|
||||
if keyCode == KeyUnknown then return false end
|
||||
local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||
local comboConf = widget.boundKeyPressCombos[keyComboDesc]
|
||||
if comboConf and (autoRepeatTicks >= comboConf.autoRepeatDelay or autoRepeatTicks == 0) and comboConf.callback then
|
||||
comboConf.callback()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function connectKeyDownEvent(widget)
|
||||
if widget.boundKeyDownCombos then return end
|
||||
connect(widget, { onKeyDown = onWidgetKeyDown })
|
||||
widget.boundKeyDownCombos = {}
|
||||
end
|
||||
|
||||
local function connectKeyPressEvent(widget)
|
||||
if widget.boundKeyPressCombos then return end
|
||||
connect(widget, { onKeyPress = onWidgetKeyPress })
|
||||
widget.boundKeyPressCombos = {}
|
||||
end
|
||||
|
||||
-- public functions
|
||||
function Keyboard.bindKeyDown(keyComboDesc, callback, widget)
|
||||
widget = widget or rootWidget
|
||||
connectKeyDownEvent(widget)
|
||||
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
|
||||
widget.boundKeyDownCombos[keyComboDesc] = callback
|
||||
end
|
||||
|
||||
function Keyboard.bindKeyPress(keyComboDesc, callback, widget, autoRepeatDelay)
|
||||
autoRepeatDelay = autoRepeatDelay or 500
|
||||
widget = widget or rootWidget
|
||||
connectKeyPressEvent(widget)
|
||||
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
|
||||
widget.boundKeyPressCombos[keyComboDesc] = { callback = callback, autoRepeatDelay = autoRepeatDelay }
|
||||
widget:setAutoRepeatDelay(math.min(autoRepeatDelay, widget:getAutoRepeatDelay()))
|
||||
end
|
||||
|
||||
function Keyboard.unbindKeyDown(keyComboDesc, widget)
|
||||
widget = widget or rootWidget
|
||||
if widget.boundKeyDownCombos == nil then return end
|
||||
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
|
||||
if keyComboDesc then
|
||||
widget.boundKeyDownCombos[keyComboDesc] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Keyboard.unbindKeyPress(keyComboDesc, widget)
|
||||
widget = widget or rootWidget
|
||||
if widget.boundKeyPressCombos == nil then return end
|
||||
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
|
||||
if keyComboDesc then
|
||||
widget.boundKeyPressCombos[keyComboDesc] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Keyboard.getModifiers()
|
||||
return g_window.getKeyboardModifiers()
|
||||
end
|
||||
|
||||
function Keyboard.isCtrlPressed()
|
||||
return bit32.band(g_window.getKeyboardModifiers(), KeyboardCtrlModifier) ~= 0
|
||||
end
|
||||
|
||||
function Keyboard.isAltPressed()
|
||||
return bit32.band(g_window.getKeyboardModifiers(), KeyboardAltModifier) ~= 0
|
||||
end
|
||||
|
||||
function Keyboard.isShiftPressed()
|
||||
return bit32.band(g_window.getKeyboardModifiers(), KeyboardShiftModifier) ~= 0
|
||||
end
|
1
modules/corelib/math/color.lua
Normal file
1
modules/corelib/math/color.lua
Normal file
@@ -0,0 +1 @@
|
||||
Color = {}
|
8
modules/corelib/math/math.lua
Normal file
8
modules/corelib/math/math.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
function math.round(num, idp)
|
||||
local mult = 10^(idp or 0)
|
||||
if num >= 0 then
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
else
|
||||
return math.ceil(num * mult - 0.5) / mult
|
||||
end
|
||||
end
|
1
modules/corelib/math/point.lua
Normal file
1
modules/corelib/math/point.lua
Normal file
@@ -0,0 +1 @@
|
||||
Point = {}
|
1
modules/corelib/math/rect.lua
Normal file
1
modules/corelib/math/rect.lua
Normal file
@@ -0,0 +1 @@
|
||||
Rect = {}
|
1
modules/corelib/math/size.lua
Normal file
1
modules/corelib/math/size.lua
Normal file
@@ -0,0 +1 @@
|
||||
Size = {}
|
53
modules/corelib/mouse.lua
Normal file
53
modules/corelib/mouse.lua
Normal file
@@ -0,0 +1,53 @@
|
||||
Mouse = {}
|
||||
|
||||
local cursorChanged = false
|
||||
|
||||
function Mouse.setTargetCursor()
|
||||
g_window.setMouseCursor('/cursors/targetcursor.png', {x=9,y=9})
|
||||
cursorChanged = true
|
||||
end
|
||||
|
||||
function Mouse.setHorizontalCursor()
|
||||
g_window.setMouseCursor('/cursors/horizontal.png', {x=9,y=4})
|
||||
cursorChanged = true
|
||||
end
|
||||
|
||||
function Mouse.setVerticalCursor()
|
||||
g_window.setMouseCursor('/cursors/vertical.png', {x=4,y=9})
|
||||
cursorChanged = true
|
||||
end
|
||||
|
||||
function Mouse.restoreCursor()
|
||||
g_window.restoreMouseCursor()
|
||||
cursorChanged = false
|
||||
end
|
||||
|
||||
function Mouse.isCursorChanged()
|
||||
return cursorChanged
|
||||
end
|
||||
|
||||
function Mouse.isPressed(button)
|
||||
if not button then button = MouseLeftButton end
|
||||
return g_window.isMouseButtonPressed(button)
|
||||
end
|
||||
|
||||
function Mouse.bindAutoPress(widget, callback)
|
||||
connect(widget, { onMousePress = function(widget, mousePos, mouseButton)
|
||||
callback()
|
||||
periodicalEvent(function()
|
||||
callback()
|
||||
end, function()
|
||||
return widget:isPressed()
|
||||
end, 30, 300)
|
||||
return true
|
||||
end })
|
||||
end
|
||||
|
||||
function Mouse.bindPressMove(widget, callback)
|
||||
connect(widget, { onMouseMove = function(widget, mousePos, mouseMoved)
|
||||
if widget:isPressed() then
|
||||
callback(mousePos, mouseMoved)
|
||||
end
|
||||
return true
|
||||
end })
|
||||
end
|
82
modules/corelib/settings.lua
Normal file
82
modules/corelib/settings.lua
Normal file
@@ -0,0 +1,82 @@
|
||||
Settings = {}
|
||||
|
||||
Settings.exists = g_configs.exists
|
||||
Settings.setNode = g_configs.setNode
|
||||
Settings.addNode = g_configs.addNode
|
||||
Settings.getNode = g_configs.getNode
|
||||
Settings.remove = g_configs.remove
|
||||
Settings.setList = g_configs.setList
|
||||
Settings.getList = g_configs.getList
|
||||
|
||||
local function convertSettingValue(value)
|
||||
if type(value) == 'table' then
|
||||
if value.x and value.width then
|
||||
return recttostring(value)
|
||||
elseif value.x then
|
||||
return pointtostring(value)
|
||||
elseif value.width then
|
||||
return sizetostring(value)
|
||||
elseif value.r then
|
||||
return colortostring(value)
|
||||
else
|
||||
return value
|
||||
end
|
||||
elseif value == nil then
|
||||
return ''
|
||||
else
|
||||
return tostring(value)
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
return true
|
||||
end
|
||||
|
||||
function Settings.get(key, default)
|
||||
if not Settings.exists(key) and default ~= nil then
|
||||
Settings.set(key, default)
|
||||
end
|
||||
return g_configs.get(key)
|
||||
end
|
||||
|
||||
function Settings.getString(key, default)
|
||||
return Settings.get(key, default)
|
||||
end
|
||||
|
||||
function Settings.getInteger(key, default)
|
||||
return tonumber(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getNumber(key, default)
|
||||
return tonumber(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getBoolean(key, default)
|
||||
return toboolean(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getPoint(key, default)
|
||||
return topoint(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getRect(key, default)
|
||||
return torect(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getSize(key, default)
|
||||
return tosize(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getColor(key, default)
|
||||
return tocolor(Settings.get(key, default))
|
||||
end
|
||||
|
||||
function Settings.getColor(key, default)
|
||||
return tocolor(Settings.get(key, default))
|
||||
end
|
36
modules/corelib/ui/effects.lua
Normal file
36
modules/corelib/ui/effects.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
Effects = {}
|
||||
|
||||
function Effects.fadeIn(widget, time, elapsed)
|
||||
if not elapsed then elapsed = 0 end
|
||||
if not time then time = 300 end
|
||||
widget:setOpacity(math.min(elapsed/time, 1))
|
||||
removeEvent(widget.fadeEvent)
|
||||
if elapsed < time then
|
||||
removeEvent(widget.fadeEvent)
|
||||
widget.fadeEvent = scheduleEvent(function()
|
||||
Effects.fadeIn(widget, time, elapsed + 30)
|
||||
end, 30)
|
||||
else
|
||||
widget.fadeEvent = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Effects.fadeOut(widget, time, elapsed)
|
||||
if not elapsed then elapsed = 0 end
|
||||
if not time then time = 300 end
|
||||
elapsed = math.max((1 - widget:getOpacity()) * time, elapsed)
|
||||
removeEvent(widget.fadeEvent)
|
||||
widget:setOpacity(math.max((time - elapsed)/time, 0))
|
||||
if elapsed < time then
|
||||
widget.fadeEvent = scheduleEvent(function()
|
||||
Effects.fadeOut(widget, time, elapsed + 30)
|
||||
end, 30)
|
||||
else
|
||||
widget.fadeEvent = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Effects.cancelFade(widget)
|
||||
removeEvent(widget.fadeEvent)
|
||||
widget.fadeEvent = nil
|
||||
end
|
44
modules/corelib/ui/radiogroup.lua
Normal file
44
modules/corelib/ui/radiogroup.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
RadioGroup = newclass()
|
||||
|
||||
function RadioGroup.create()
|
||||
local radiogroup = RadioGroup.internalCreate()
|
||||
radiogroup.widgets = {}
|
||||
return radiogroup
|
||||
end
|
||||
|
||||
function RadioGroup:destroy()
|
||||
for k,widget in pairs(self.widgets) do
|
||||
widget.onClick = nil
|
||||
end
|
||||
self.widgets = {}
|
||||
end
|
||||
|
||||
function RadioGroup:addWidget(widget)
|
||||
table.insert(self.widgets, widget)
|
||||
widget.onClick = function(widget) self:selectWidget(widget) end
|
||||
end
|
||||
|
||||
function RadioGroup:removeWidget(widget)
|
||||
if self.selectedWidget == widget then
|
||||
self:selectWidget(nil)
|
||||
end
|
||||
widget.onClick = 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
|
96
modules/corelib/ui/tooltip.lua
Normal file
96
modules/corelib/ui/tooltip.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
ToolTip = {}
|
||||
|
||||
-- private variables
|
||||
local toolTipLabel
|
||||
local currentHoveredWidget
|
||||
|
||||
-- private functions
|
||||
local function moveToolTip(tooltip)
|
||||
if not tooltip:isVisible() or tooltip:getOpacity() < 0.1 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 and not Mouse.isPressed() 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('UILabel', rootWidget)
|
||||
toolTipLabel:setId('toolTip')
|
||||
toolTipLabel:setBackgroundColor('#111111cc')
|
||||
toolTipLabel:setTextAlign(AlignCenter)
|
||||
toolTipLabel:hide()
|
||||
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()
|
||||
Effects.fadeIn(toolTipLabel, 100)
|
||||
moveToolTip(toolTipLabel)
|
||||
end
|
||||
|
||||
function ToolTip.hide()
|
||||
Effects.fadeOut(toolTipLabel, 100)
|
||||
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 })
|
176
modules/corelib/util.lua
Normal file
176
modules/corelib/util.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
function print(...)
|
||||
local msg = ""
|
||||
for i,v in ipairs({...}) do
|
||||
msg = msg .. tostring(v) .. "\t"
|
||||
end
|
||||
g_logger.log(LogInfo, msg)
|
||||
end
|
||||
|
||||
function pinfo(msg)
|
||||
g_logger.log(LogInfo, msg)
|
||||
end
|
||||
|
||||
function perror(msg)
|
||||
g_logger.log(LogError, msg)
|
||||
end
|
||||
|
||||
function pwarning(msg)
|
||||
g_logger.log(LogWarning, msg)
|
||||
end
|
||||
|
||||
function pdebug(msg)
|
||||
g_logger.log(LogDebug, msg)
|
||||
end
|
||||
|
||||
function fatal(msg)
|
||||
g_logger.log(LogFatal, msg)
|
||||
end
|
||||
|
||||
|
||||
exit = g_app.exit
|
||||
quit = g_app.exit
|
||||
|
||||
function connect(object, signalsAndSlots, pushFront)
|
||||
for signal,slot in pairs(signalsAndSlots) do
|
||||
if not object[signal] then
|
||||
local mt = getmetatable(object)
|
||||
if mt and type(object) == 'userdata' then
|
||||
object[signal] = function(...)
|
||||
return signalcall(mt[signal], ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not object[signal] then
|
||||
object[signal] = slot
|
||||
elseif type(object[signal]) == 'function' then
|
||||
object[signal] = { object[signal] }
|
||||
end
|
||||
if type(object[signal]) == 'table' then
|
||||
if pushFront then
|
||||
table.insert(object[signal], 1, slot)
|
||||
else
|
||||
table.insert(object[signal], #object[signal]+1, slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function disconnect(object, signalsAndSlots)
|
||||
for signal,slot in pairs(signalsAndSlots) do
|
||||
if not object[signal] then
|
||||
elseif type(object[signal]) == 'function' then
|
||||
if object[signal] == slot then
|
||||
object[signal] = nil
|
||||
end
|
||||
elseif type(object[signal]) == 'table' then
|
||||
for k,func in pairs(object[signal]) do
|
||||
if func == slot then
|
||||
table.remove(object[signal], k)
|
||||
|
||||
if #object[signal] == 1 then
|
||||
object[signal] = object[signal][1]
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function newclass()
|
||||
local class = {}
|
||||
function class.internalCreate()
|
||||
local instance = {}
|
||||
for k,v in pairs(class) do
|
||||
instance[k] = v
|
||||
end
|
||||
return instance
|
||||
end
|
||||
class.create = class.internalCreate
|
||||
return class
|
||||
end
|
||||
|
||||
function extends(base)
|
||||
local derived = {}
|
||||
function derived.internalCreate()
|
||||
local instance = base.create()
|
||||
for k,v in pairs(derived) do
|
||||
instance[k] = v
|
||||
end
|
||||
return instance
|
||||
end
|
||||
derived.create = derived.internalCreate
|
||||
return derived
|
||||
end
|
||||
|
||||
function newenv()
|
||||
local env = { }
|
||||
setmetatable(env, { __index = _G} )
|
||||
return env
|
||||
end
|
||||
|
||||
function getfsrcpath(depth)
|
||||
depth = depth or 2
|
||||
local info = debug.getinfo(1+depth, "Sn")
|
||||
local path
|
||||
if info.short_src then
|
||||
path = info.short_src:match("(.*)/.*")
|
||||
end
|
||||
if not path then
|
||||
path = '/'
|
||||
elseif path:sub(0, 1) ~= '/' then
|
||||
path = '/' .. path
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function resolvepath(filePath, depth)
|
||||
depth = depth or 1
|
||||
if filePath then
|
||||
if filePath:sub(0, 1) ~= '/' then
|
||||
local basepath = getfsrcpath(depth+1)
|
||||
if basepath:sub(#basepath) ~= '/' then basepath = basepath .. '/' end
|
||||
return basepath .. filePath
|
||||
else
|
||||
return filePath
|
||||
end
|
||||
else
|
||||
local basepath = getfsrcpath(depth+1)
|
||||
if basepath:sub(#basepath) ~= '/' then basepath = basepath .. '/' end
|
||||
return basepath
|
||||
end
|
||||
end
|
||||
|
||||
function toboolean(str)
|
||||
str = str:trim():lower()
|
||||
if str == '1' or str == 'true' then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local oldtonumber = tonumber
|
||||
function tonumber(v)
|
||||
if v == nil then return 0 end
|
||||
return oldtonumber(v)
|
||||
end
|
||||
|
||||
function signalcall(param, ...)
|
||||
if type(param) == 'function' then
|
||||
return param(...)
|
||||
elseif type(param) == 'table' then
|
||||
for k,v in pairs(param) do
|
||||
if v(...) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
elseif func ~= nil then
|
||||
error('attempt to call a non function value')
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function tr(s)
|
||||
return s
|
||||
end
|
7
modules/corelib/widgets/uibutton.lua
Normal file
7
modules/corelib/widgets/uibutton.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
UIButton = extends(UIWidget)
|
||||
|
||||
function UIButton.create()
|
||||
local button = UIButton.internalCreate()
|
||||
button:setFocusable(false)
|
||||
return button
|
||||
end
|
12
modules/corelib/widgets/uicheckbox.lua
Normal file
12
modules/corelib/widgets/uicheckbox.lua
Normal file
@@ -0,0 +1,12 @@
|
||||
UICheckBox = extends(UIWidget)
|
||||
|
||||
function UICheckBox.create()
|
||||
local checkbox = UICheckBox.internalCreate()
|
||||
checkbox:setFocusable(false)
|
||||
checkbox:setTextAlign(AlignLeft)
|
||||
return checkbox
|
||||
end
|
||||
|
||||
function UICheckBox:onClick()
|
||||
self:setChecked(not self:isChecked())
|
||||
end
|
70
modules/corelib/widgets/uicombobox.lua
Normal file
70
modules/corelib/widgets/uicombobox.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
UIComboBox = extends(UIWidget)
|
||||
|
||||
function UIComboBox.create()
|
||||
local combobox = UIComboBox.internalCreate()
|
||||
combobox.options = {}
|
||||
combobox.currentIndex = -1
|
||||
return combobox
|
||||
end
|
||||
|
||||
function UIComboBox:setCurrentOption(text)
|
||||
if not self.options then return end
|
||||
for i,v in ipairs(self.options) do
|
||||
if v.text == text and self.currentIndex ~= i then
|
||||
self.currentIndex = i
|
||||
self:setText(text)
|
||||
self:onOptionChange(text, v.data)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIComboBox:setCurrentIndex(index)
|
||||
if index >= 1 and index <= #self.options then
|
||||
local v = self.options[index]
|
||||
self.currentIndex = index
|
||||
self:setText(v.text)
|
||||
self:onOptionChange(v.text, v.data)
|
||||
end
|
||||
end
|
||||
|
||||
function UIComboBox:addOption(text, data)
|
||||
table.insert(self.options, { text = text, data = data })
|
||||
local index = #self.options
|
||||
if index == 1 then self:setCurrentOption(text) end
|
||||
return index
|
||||
end
|
||||
|
||||
function UIComboBox:onMousePress(mousePos, mouseButton)
|
||||
local menu = createWidget(self:getStyleName() .. 'PopupMenu')
|
||||
menu:setId(self:getId() .. 'PopupMenu')
|
||||
for i,v in ipairs(self.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.currentIndex > 1 then
|
||||
self:setCurrentIndex(self.currentIndex - 1)
|
||||
elseif direction == MouseWheelDown and self.currentIndex < #self.options then
|
||||
self:setCurrentIndex(self.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
|
9
modules/corelib/widgets/uilabel.lua
Normal file
9
modules/corelib/widgets/uilabel.lua
Normal 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
|
67
modules/corelib/widgets/uimessagebox.lua
Normal file
67
modules/corelib/widgets/uimessagebox.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
if not UIWindow then dofile 'uiwindow' end
|
||||
|
||||
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:getTextSize().width + 20))
|
||||
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
|
122
modules/corelib/widgets/uiminiwindow.lua
Normal file
122
modules/corelib/widgets/uiminiwindow.lua
Normal file
@@ -0,0 +1,122 @@
|
||||
UIMiniWindow = extends(UIWindow)
|
||||
|
||||
function UIMiniWindow.create()
|
||||
local miniwindow = UIMiniWindow.internalCreate()
|
||||
return miniwindow
|
||||
end
|
||||
|
||||
function UIMiniWindow:onSetup()
|
||||
self:getChildById('closeButton').onClick = function() signalcall(self.onClose, self) end
|
||||
self:getChildById('minimizeButton').onClick = function() signalcall(self.onMinimize, self) end
|
||||
end
|
||||
|
||||
function UIMiniWindow:onDragEnter(mousePos)
|
||||
local parent = self:getParent()
|
||||
if not parent then return false end
|
||||
|
||||
if parent:getClassName() == 'UIMiniWindowContainer' then
|
||||
local containerParent = parent:getParent()
|
||||
parent:removeChild(self)
|
||||
containerParent:addChild(self)
|
||||
end
|
||||
|
||||
local oldPos = self:getPosition()
|
||||
self.movingReference = { x = mousePos.x - oldPos.x, y = mousePos.y - oldPos.y }
|
||||
self:setPosition(oldPos)
|
||||
self.free = true
|
||||
return true
|
||||
end
|
||||
|
||||
function UIMiniWindow:onDragMove(mousePos, mouseMoved)
|
||||
local oldMousePosY = mousePos.y - mouseMoved.y
|
||||
local children = rootWidget:recursiveGetChildrenByMarginPos(mousePos)
|
||||
local overAnyWidget = false
|
||||
for i=1,#children do
|
||||
local child = children[i]
|
||||
if child:getParent():getClassName() == 'UIMiniWindowContainer' then
|
||||
overAnyWidget = true
|
||||
|
||||
local childCenterY = child:getY() + child:getHeight() / 2
|
||||
if child == self.movedWidget and mousePos.y < childCenterY and oldMousePosY < childCenterY then
|
||||
break
|
||||
end
|
||||
|
||||
if self.movedWidget then
|
||||
self.setMovedChildMargin(0)
|
||||
self.setMovedChildMargin = nil
|
||||
end
|
||||
|
||||
if mousePos.y < childCenterY then
|
||||
self.setMovedChildMargin = function(v) child:setMarginTop(v) end
|
||||
self.movedIndex = 0
|
||||
else
|
||||
self.setMovedChildMargin = function(v) child:setMarginBottom(v) end
|
||||
self.movedIndex = 1
|
||||
end
|
||||
|
||||
self.movedWidget = child
|
||||
self.setMovedChildMargin(self:getHeight())
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not overAnyWidget and self.movedWidget then
|
||||
self.setMovedChildMargin(0)
|
||||
self.movedWidget = nil
|
||||
end
|
||||
|
||||
return UIWindow.onDragMove(self, mousePos, mouseMoved)
|
||||
end
|
||||
|
||||
function UIMiniWindow:onMousePress()
|
||||
local parent = self:getParent()
|
||||
if not parent then return false end
|
||||
if parent:getClassName() ~= 'UIMiniWindowContainer' then
|
||||
self:raise()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function UIMiniWindow:onDragLeave(droppedWidget, mousePos)
|
||||
if self.movedWidget then
|
||||
self.setMovedChildMargin(0)
|
||||
self.movedWidget = nil
|
||||
self.setMovedChildMargin = nil
|
||||
self.movedIndex = nil
|
||||
end
|
||||
end
|
||||
|
||||
function UIMiniWindow:onFocusChange(focused)
|
||||
-- miniwindows only raises when its outside MiniWindowContainers
|
||||
if not focused then return end
|
||||
local parent = self:getParent()
|
||||
if parent and parent:getClassName() ~= 'UIMiniWindowContainer' then
|
||||
self:raise()
|
||||
end
|
||||
end
|
||||
|
||||
function UIMiniWindow:onClose()
|
||||
end
|
||||
|
||||
function UIMiniWindow:onMinimize()
|
||||
if self:isOn() then
|
||||
self:setOn(false)
|
||||
self:getChildById('contentsPanel'):show()
|
||||
self:getChildById('miniwindowScrollBar'):show()
|
||||
self:getChildById('bottomResizeBorder'):show()
|
||||
self:getChildById('minimizeButton'):setOn(false)
|
||||
self:setHeight(self.savedHeight)
|
||||
else
|
||||
self.savedHeight = self:getHeight()
|
||||
self:setHeight(self.minimizedHeight)
|
||||
self:setOn(true)
|
||||
self:getChildById('contentsPanel'):hide()
|
||||
self:getChildById('miniwindowScrollBar'):hide()
|
||||
self:getChildById('bottomResizeBorder'):hide()
|
||||
self:getChildById('minimizeButton'):setOn(true)
|
||||
end
|
||||
end
|
||||
|
||||
function UIMiniWindow:getClassName()
|
||||
return 'UIMiniWindow'
|
||||
end
|
32
modules/corelib/widgets/uiminiwindowcontainer.lua
Normal file
32
modules/corelib/widgets/uiminiwindowcontainer.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
UIMiniWindowContainer = extends(UIWidget)
|
||||
|
||||
function UIMiniWindowContainer.create()
|
||||
local container = UIMiniWindowContainer.internalCreate()
|
||||
container:setFocusable(false)
|
||||
container:setPhantom(true)
|
||||
return container
|
||||
end
|
||||
|
||||
function UIMiniWindowContainer:onDrop(widget, mousePos)
|
||||
if widget:getClassName() == 'UIMiniWindow' then
|
||||
local oldParent = widget:getParent()
|
||||
if oldParent == self then
|
||||
return true
|
||||
end
|
||||
|
||||
oldParent:removeChild(widget)
|
||||
|
||||
if widget.movedWidget then
|
||||
local index = self:getChildIndex(widget.movedWidget)
|
||||
self:insertChild(index + widget.movedIndex, widget)
|
||||
else
|
||||
self:addChild(widget)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function UIMiniWindowContainer:getClassName()
|
||||
return 'UIMiniWindowContainer'
|
||||
end
|
79
modules/corelib/widgets/uipopupmenu.lua
Normal file
79
modules/corelib/widgets/uipopupmenu.lua
Normal 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} )
|
35
modules/corelib/widgets/uiprogressbar.lua
Normal file
35
modules/corelib/widgets/uiprogressbar.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
UIProgressBar = extends(UIWidget)
|
||||
|
||||
function UIProgressBar.create()
|
||||
local progressbar = UIProgressBar.internalCreate()
|
||||
progressbar:setFocusable(false)
|
||||
progressbar:setPhantom(true)
|
||||
progressbar.percent = 0
|
||||
progressbar:updateBackground()
|
||||
return progressbar
|
||||
end
|
||||
|
||||
function UIProgressBar:setPercent(percent)
|
||||
self.percent = math.max(math.min(percent, 100), 0)
|
||||
self:updateBackground()
|
||||
end
|
||||
|
||||
|
||||
function UIProgressBar:getPercent()
|
||||
return self.percent
|
||||
end
|
||||
|
||||
function UIProgressBar:getPercentPixels()
|
||||
return 100 / self:getWidth()
|
||||
end
|
||||
|
||||
function UIProgressBar:updateBackground()
|
||||
local width = math.round(math.max((self.percent * self:getWidth())/100, 1))
|
||||
local height = self:getHeight()
|
||||
self:setBackgroundSize({width=width, height=height})
|
||||
end
|
||||
|
||||
|
||||
function UIProgressBar:onGeometryChange(oldRect, newRect)
|
||||
self:updateBackground()
|
||||
end
|
92
modules/corelib/widgets/uiresizeborder.lua
Normal file
92
modules/corelib/widgets/uiresizeborder.lua
Normal file
@@ -0,0 +1,92 @@
|
||||
UIResizeBorder = extends(UIWidget)
|
||||
|
||||
function UIResizeBorder.create()
|
||||
local resizeborder = UIResizeBorder.internalCreate()
|
||||
resizeborder:setFocusable(false)
|
||||
resizeborder.minimum = 0
|
||||
resizeborder.maximum = 1000
|
||||
return resizeborder
|
||||
end
|
||||
|
||||
function UIResizeBorder:onHoverChange(hovered)
|
||||
if hovered then
|
||||
if Mouse.isCursorChanged() or Mouse.isPressed() then return end
|
||||
if self:getWidth() > self:getHeight() then
|
||||
Mouse.setVerticalCursor()
|
||||
self.vertical = true
|
||||
else
|
||||
Mouse.setHorizontalCursor()
|
||||
self.vertical = false
|
||||
end
|
||||
self.hovering = true
|
||||
if not self:isPressed() then
|
||||
Effects.fadeIn(self)
|
||||
end
|
||||
else
|
||||
if not self:isPressed() and self.hovering then
|
||||
Mouse.restoreCursor()
|
||||
Effects.fadeOut(self)
|
||||
self.hovering = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIResizeBorder:onMouseMove(mousePos, mouseMoved)
|
||||
if self:isPressed() then
|
||||
if self.vertical then
|
||||
local delta = mousePos.y - self:getY() - self:getHeight()/2
|
||||
local parent = self:getParent()
|
||||
local newsize = math.min(math.max(parent:getHeight() + delta, self.minimum), self.maximum)
|
||||
if newsize ~= currentMargin then
|
||||
self.newsize = newsize
|
||||
if not self.event or self.event:isExecuted() then
|
||||
self.event = addEvent(function()
|
||||
parent:setHeight(self.newsize)
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
local delta = mousePos.x - self:getX() - self:getWidth()/2
|
||||
local parent = self:getParent()
|
||||
local newsize = math.min(math.max(parent:getWidth() + delta, self.minimum), self.maximum)
|
||||
if newsize ~= currentMargin then
|
||||
self.newsize = newsize
|
||||
if not self.event or self.event:isExecuted() then
|
||||
self.event = addEvent(function()
|
||||
parent:setWidth(self.newsize)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function UIResizeBorder:onMouseRelease(mousePos, mouseButton)
|
||||
if not self:isHovered() then
|
||||
Mouse.restoreCursor()
|
||||
Effects.fadeOut(self)
|
||||
self.hovering = false
|
||||
end
|
||||
end
|
||||
|
||||
function UIResizeBorder:onStyleApply(styleName, styleNode)
|
||||
for name,value in pairs(styleNode) do
|
||||
if name == 'maximum' then
|
||||
self:setMaximum(tonumber(value))
|
||||
elseif name == 'minimum' then
|
||||
self:setMinimum(tonumber(value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIResizeBorder:setMaximum(maximum)
|
||||
self.maximum = maximum
|
||||
end
|
||||
|
||||
function UIResizeBorder:setMinimum(minimum)
|
||||
self.minimum = minimum
|
||||
end
|
||||
|
||||
function UIResizeBorder:getMaximum() return self.maximum end
|
||||
function UIResizeBorder:getMinimum() return self.minimum end
|
100
modules/corelib/widgets/uiscrollarea.lua
Normal file
100
modules/corelib/widgets/uiscrollarea.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
UIScrollArea = extends(UIWidget)
|
||||
|
||||
-- public functions
|
||||
function UIScrollArea.create()
|
||||
local scrollarea = UIScrollArea.internalCreate()
|
||||
scrollarea:setClipping(true)
|
||||
scrollarea.inverted = false
|
||||
return scrollarea
|
||||
end
|
||||
|
||||
function UIScrollArea:onStyleApply(styleName, styleNode)
|
||||
for name,value in pairs(styleNode) do
|
||||
if name == 'vertical-scrollbar' then
|
||||
addEvent(function()
|
||||
self:setVerticalScrollBar(self:getParent():getChildById(value))
|
||||
end)
|
||||
elseif name == 'horizontal-scrollbar' then
|
||||
addEvent(function()
|
||||
self:setHorizontalScrollBar(self:getParent():getChildById(value))
|
||||
end)
|
||||
elseif name == 'inverted-scroll' then
|
||||
self:setInverted(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollArea:updateScrollBars()
|
||||
local offset = { x = 0, y = 0 }
|
||||
local scrollheight = math.max(self:getChildrenRect().height - self:getPaddingRect().height, 0)
|
||||
local scrollwidth = math.max(self:getChildrenRect().width - self:getPaddingRect().width, 0)
|
||||
|
||||
local scrollbar = self.verticalScrollBar
|
||||
if scrollbar then
|
||||
if self.inverted then
|
||||
scrollbar:setMinimum(-scrollheight)
|
||||
scrollbar:setMaximum(0)
|
||||
else
|
||||
scrollbar:setMinimum(0)
|
||||
scrollbar:setMaximum(scrollheight)
|
||||
end
|
||||
end
|
||||
|
||||
local scrollbar = self.horizontalScrollBar
|
||||
if scrollbar then
|
||||
if self.inverted then
|
||||
scrollbar:setMinimum(-scrollwidth)
|
||||
else
|
||||
scrollbar:setMaximum(scrollwidth)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollArea:setVerticalScrollBar(scrollbar)
|
||||
self.verticalScrollBar = scrollbar
|
||||
self.verticalScrollBar.onValueChange = function(scrollbar, value)
|
||||
local virtualOffset = self:getVirtualOffset()
|
||||
virtualOffset.y = value
|
||||
self:setVirtualOffset(virtualOffset)
|
||||
end
|
||||
self:updateScrollBars()
|
||||
end
|
||||
|
||||
function UIScrollArea:setHorizontalScrollBar(scrollbar)
|
||||
self.horizontalScrollBar = scrollbar
|
||||
self:updateScrollBars()
|
||||
end
|
||||
|
||||
function UIScrollArea:setInverted(inverted)
|
||||
self.inverted = inverted
|
||||
end
|
||||
|
||||
function UIScrollArea:onLayoutUpdate()
|
||||
self:updateScrollBars()
|
||||
end
|
||||
|
||||
function UIScrollArea:onMouseWheel(mousePos, mouseWheel)
|
||||
if self.verticalScrollBar then
|
||||
if mouseWheel == MouseWheelUp then
|
||||
self.verticalScrollBar:decrement()
|
||||
else
|
||||
self.verticalScrollBar:increment()
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function UIScrollArea:onChildFocusChange(focusedChild, oldFocused, reason)
|
||||
if focusedChild and (reason == MouseFocusReason or reason == KeyboardFocusReason) then
|
||||
local paddingRect = self:getPaddingRect()
|
||||
local delta = paddingRect.y - focusedChild:getY()
|
||||
if delta > 0 then
|
||||
self.verticalScrollBar:decrement(delta)
|
||||
end
|
||||
|
||||
delta = (focusedChild:getY() + focusedChild:getHeight()) - (paddingRect.y + paddingRect.height)
|
||||
if delta > 0 then
|
||||
self.verticalScrollBar:increment(delta)
|
||||
end
|
||||
end
|
||||
end
|
207
modules/corelib/widgets/uiscrollbar.lua
Normal file
207
modules/corelib/widgets/uiscrollbar.lua
Normal file
@@ -0,0 +1,207 @@
|
||||
UIScrollBar = extends(UIWidget)
|
||||
|
||||
-- private functions
|
||||
local function calcValues(self)
|
||||
local slider = self:getChildById('sliderButton')
|
||||
local decrementButton = self:getChildById('decrementButton')
|
||||
local incrementButton = self:getChildById('incrementButton')
|
||||
|
||||
local pxrange, center
|
||||
if self.orientation == 'vertical' then
|
||||
pxrange = (self:getHeight() - decrementButton:getHeight() - decrementButton:getMarginTop() - decrementButton:getMarginBottom()
|
||||
- incrementButton:getHeight() - incrementButton:getMarginTop() - incrementButton:getMarginBottom())
|
||||
center = self:getY() + math.floor(self:getHeight() / 2)
|
||||
else -- horizontal
|
||||
pxrange = (self:getWidth() - decrementButton:getWidth() - decrementButton:getMarginLeft() - decrementButton:getMarginRight()
|
||||
- incrementButton:getWidth() - incrementButton:getMarginLeft() - incrementButton:getMarginRight())
|
||||
center = self:getX() + self:getWidth() / 2
|
||||
end
|
||||
|
||||
local range = self.maximum - self.minimum + 1
|
||||
|
||||
local proportion
|
||||
|
||||
if self.pixelsScroll then
|
||||
proportion = pxrange/(range+pxrange)
|
||||
else
|
||||
proportion = math.min(math.max(self.step, 1), range)/range
|
||||
end
|
||||
|
||||
local px = math.max(proportion * pxrange, 10)
|
||||
px = px - px % 2 + 1
|
||||
|
||||
local offset = 0
|
||||
if range == 0 or self.value == self.minimum then
|
||||
if self.orientation == 'vertical' then
|
||||
offset = -math.floor((self:getHeight() - px) / 2) + decrementButton:getMarginRect().height
|
||||
else
|
||||
offset = -math.floor((self:getWidth() - px) / 2) + decrementButton:getMarginRect().width
|
||||
end
|
||||
elseif range > 1 and self.value == self.maximum then
|
||||
if self.orientation == 'vertical' then
|
||||
offset = math.ceil((self:getHeight() - px) / 2) - incrementButton:getMarginRect().height
|
||||
else
|
||||
offset = math.ceil((self:getWidth() - px) / 2) - incrementButton:getMarginRect().width
|
||||
end
|
||||
elseif range > 1 then
|
||||
offset = (((self.value - self.minimum) / (range - 1)) - 0.5) * (pxrange - px)
|
||||
end
|
||||
|
||||
return range, pxrange, px, offset, center
|
||||
end
|
||||
|
||||
local function updateSlider(self)
|
||||
local slider = self:getChildById('sliderButton')
|
||||
if slider == nil then return end
|
||||
|
||||
local range, pxrange, px, offset, center = calcValues(self)
|
||||
if self.orientation == 'vertical' then
|
||||
slider:setHeight(px)
|
||||
slider:setMarginTop(offset)
|
||||
else -- horizontal
|
||||
slider:setWidth(px)
|
||||
slider:setMarginLeft(offset)
|
||||
end
|
||||
|
||||
local status = (self.maximum ~= self.minimum)
|
||||
|
||||
self:setOn(status)
|
||||
for _i,child in pairs(self:getChildren()) do
|
||||
child:setEnabled(status)
|
||||
end
|
||||
end
|
||||
|
||||
local function parseSliderPos(self, pos)
|
||||
local point
|
||||
if self.orientation == 'vertical' then
|
||||
point = pos.y
|
||||
else
|
||||
point = pos.x
|
||||
end
|
||||
local range, pxrange, px, offset, center = calcValues(self)
|
||||
offset = math.min(math.max(point - center, -pxrange/2), pxrange/2)
|
||||
local newvalue = math.floor(((offset / (pxrange - px)) + 0.5) * (range - 1)) + self.minimum
|
||||
self:setValue(newvalue)
|
||||
end
|
||||
|
||||
|
||||
-- public functions
|
||||
function UIScrollBar.create()
|
||||
local scrollbar = UIScrollBar.internalCreate()
|
||||
scrollbar:setFocusable(false)
|
||||
scrollbar.value = 0
|
||||
scrollbar.minimum = -999999
|
||||
scrollbar.maximum = 999999
|
||||
scrollbar.step = 1
|
||||
scrollbar.orientation = 'vertical'
|
||||
scrollbar.pixelsScroll = false
|
||||
return scrollbar
|
||||
end
|
||||
|
||||
function UIScrollBar:onSetup()
|
||||
self.setupDone = true
|
||||
signalcall(self.onValueChange, self, self.value)
|
||||
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
|
||||
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
|
||||
Mouse.bindPressMove(self:getChildById('sliderButton'), function(mousePos, mouseMoved) parseSliderPos(self, mousePos) end)
|
||||
updateSlider(self)
|
||||
end
|
||||
|
||||
function UIScrollBar:onStyleApply(styleName, styleNode)
|
||||
for name,value in pairs(styleNode) do
|
||||
if name == 'maximum' then
|
||||
self:setMaximum(tonumber(value))
|
||||
elseif name == 'minimum' then
|
||||
self:setMinimum(tonumber(value))
|
||||
elseif name == 'step' then
|
||||
self:setStep(tonumber(value))
|
||||
elseif name == 'orientation' then
|
||||
self:setOrientation(value)
|
||||
elseif name == 'value' then
|
||||
self:setValue(value)
|
||||
elseif name == 'pixels-scroll' then
|
||||
self.pixelsScroll = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:decrement(count)
|
||||
count = count or self.step
|
||||
self:setValue(self.value - count)
|
||||
end
|
||||
|
||||
function UIScrollBar:increment(count)
|
||||
count = count or self.step
|
||||
self:setValue(self.value + count)
|
||||
end
|
||||
|
||||
function UIScrollBar:setMaximum(maximum)
|
||||
if maximum == self.maximum then return end
|
||||
self.maximum = maximum
|
||||
if self.value > maximum then
|
||||
self:setValue(maximum)
|
||||
else
|
||||
updateSlider(self)
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:setMinimum(minimum)
|
||||
if minimum == self.minimum then return end
|
||||
self.minimum = minimum
|
||||
if self.value < minimum then
|
||||
self:setValue(minimum)
|
||||
else
|
||||
updateSlider(self)
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:setRange(minimum, maximum)
|
||||
self:setMinimum(minimum)
|
||||
self:setMaximum(maximum)
|
||||
end
|
||||
|
||||
function UIScrollBar:setValue(value)
|
||||
value = math.max(math.min(value, self.maximum), self.minimum)
|
||||
if self.value == value then return end
|
||||
local delta = value - self.value
|
||||
self.value = value
|
||||
updateSlider(self)
|
||||
if self.setupDone then
|
||||
signalcall(self.onValueChange, self, value, delta)
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:setStep(step)
|
||||
self.step = step
|
||||
end
|
||||
|
||||
function UIScrollBar:setOrientation(orientation)
|
||||
self.orientation = orientation
|
||||
end
|
||||
|
||||
function UIScrollBar:onGeometryChange()
|
||||
updateSlider(self)
|
||||
end
|
||||
|
||||
function UIScrollBar:onMouseWheel(mousePos, mouseWheel)
|
||||
if mouseWheel == MouseWheelUp then
|
||||
if self.orientation == 'vertical' then
|
||||
self:decrement()
|
||||
else
|
||||
self:increment()
|
||||
end
|
||||
else
|
||||
if self.orientation == 'vertical' then
|
||||
self:increment()
|
||||
else
|
||||
self:decrement()
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function UIScrollBar:getMaximum() return self.maximum end
|
||||
function UIScrollBar:getMinimum() return self.minimum end
|
||||
function UIScrollBar:getValue() return self.value end
|
||||
function UIScrollBar:getStep() return self.step end
|
||||
function UIScrollBar:getOrientation() return self.orientation end
|
75
modules/corelib/widgets/uispinbox.lua
Normal file
75
modules/corelib/widgets/uispinbox.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
UISpinBox = extends(UITextEdit)
|
||||
|
||||
function UISpinBox.create()
|
||||
local spinbox = UISpinBox.internalCreate()
|
||||
spinbox:setValidCharacters('0123456789')
|
||||
spinbox.minimum = 0
|
||||
spinbox.maximum = 0
|
||||
spinbox.value = 0
|
||||
spinbox:setText("0")
|
||||
return spinbox
|
||||
end
|
||||
|
||||
function UISpinBox:onMouseWheel(mousePos, direction)
|
||||
if direction == MouseWheelUp then
|
||||
self:setValue(self.value + 1)
|
||||
elseif direction == MouseWheelDown then
|
||||
self:setValue(self.value - 1)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function UISpinBox:onTextChange(text, oldText)
|
||||
if text:len() == 0 then
|
||||
self:setValue(self.minimum)
|
||||
return
|
||||
end
|
||||
|
||||
local number = tonumber(text)
|
||||
if not number or number > self.maximum or number < self.minimum then
|
||||
self:setText(oldText)
|
||||
return
|
||||
end
|
||||
|
||||
self:setValue(number)
|
||||
end
|
||||
|
||||
function UISpinBox:onValueChange(value)
|
||||
-- nothing todo
|
||||
end
|
||||
|
||||
function UISpinBox:onStyleApply(styleName, styleNode)
|
||||
for name, value in pairs(styleNode) do
|
||||
if name == 'maximum' then
|
||||
self:setMaximum(value)
|
||||
elseif name == 'minimum' then
|
||||
self:setMinimum(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UISpinBox:setValue(value)
|
||||
value = math.max(math.min(self.maximum, value), self.minimum)
|
||||
if value == self.value then return end
|
||||
if self:getText():len() > 0 then
|
||||
self:setText(value)
|
||||
end
|
||||
self.value = value
|
||||
signalcall(self.onValueChange, self, value)
|
||||
end
|
||||
|
||||
function UISpinBox:setMinimum(minimum)
|
||||
self.minimum = minimum
|
||||
if self.value < minimum then
|
||||
self:setValue(minimum)
|
||||
end
|
||||
end
|
||||
|
||||
function UISpinBox:setMaximum(maximum)
|
||||
self.maximum = maximum
|
||||
if self.value > maximum then
|
||||
self:setValue(maximum)
|
||||
end
|
||||
end
|
||||
|
||||
function UISpinBox:getValue() return self.value end
|
81
modules/corelib/widgets/uisplitter.lua
Normal file
81
modules/corelib/widgets/uisplitter.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
UISplitter = extends(UIWidget)
|
||||
|
||||
function UISplitter.create()
|
||||
local splitter = UISplitter.internalCreate()
|
||||
splitter:setFocusable(false)
|
||||
splitter.relativeMargin = 'bottom'
|
||||
return splitter
|
||||
end
|
||||
|
||||
function UISplitter:onHoverChange(hovered)
|
||||
if hovered then
|
||||
if Mouse.isCursorChanged() or Mouse.isPressed() then return end
|
||||
if self:getWidth() > self:getHeight() then
|
||||
Mouse.setVerticalCursor()
|
||||
self.vertical = true
|
||||
else
|
||||
Mouse.setHorizontalCursor()
|
||||
self.vertical = false
|
||||
end
|
||||
self.hovering = true
|
||||
if not self:isPressed() then
|
||||
Effects.fadeIn(self)
|
||||
end
|
||||
else
|
||||
if not self:isPressed() and self.hovering then
|
||||
Mouse.restoreCursor()
|
||||
Effects.fadeOut(self)
|
||||
self.hovering = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UISplitter:onMouseMove(mousePos, mouseMoved)
|
||||
if self:isPressed() then
|
||||
--local currentmargin, newmargin, delta
|
||||
if self.vertical then
|
||||
local delta = mousePos.y - self:getY() - self:getHeight()/2
|
||||
local newMargin = self:canUpdateMargin(self:getMarginBottom() - delta)
|
||||
local currentMargin = self:getMarginBottom()
|
||||
if newMargin ~= currentMargin then
|
||||
self.newMargin = newMargin
|
||||
if not self.event or self.event:isExecuted() then
|
||||
self.event = addEvent(function()
|
||||
self:setMarginBottom(self.newMargin)
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
local delta = mousePos.x - self:getX() - self:getWidth()/2
|
||||
local newMargin = self:canUpdateMargin(self:getMarginRight() - delta)
|
||||
local currentMargin = self:getMarginRight()
|
||||
if newMargin ~= currentMargin then
|
||||
self.newMargin = newMargin
|
||||
if not self.event or self.event:isExecuted() then
|
||||
self.event = addEvent(function()
|
||||
self:setMarginRight(self.newMargin)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function UISplitter:onMouseRelease(mousePos, mouseButton)
|
||||
if not self:isHovered() then
|
||||
Mouse.restoreCursor()
|
||||
Effects.fadeOut(self)
|
||||
self.hovering = false
|
||||
end
|
||||
end
|
||||
|
||||
function UISplitter:onStyleApply(styleName, styleNode)
|
||||
if styleNode['relative-margin'] then
|
||||
self.relativeMargin = styleNode['relative-margin']
|
||||
end
|
||||
end
|
||||
|
||||
function UISplitter:canUpdateMargin(newMargin)
|
||||
return newMargin
|
||||
end
|
131
modules/corelib/widgets/uitabbar.lua
Normal file
131
modules/corelib/widgets/uitabbar.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
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())
|
||||
tab.blinkEvent = 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')
|
||||
panel:setId('tabPanel')
|
||||
end
|
||||
|
||||
local tab = createWidget(self:getStyleName() .. 'Button', self)
|
||||
panel.isTab = true
|
||||
tab.tabPanel = panel
|
||||
tab.tabBar = self
|
||||
tab:setId('tab')
|
||||
tab:setText(text)
|
||||
tab:setWidth(tab:getTextSize().width + tab:getPaddingLeft() + tab:getPaddingRight())
|
||||
tab.onClick = onTabClick
|
||||
tab.onDestroy = function() tab.tabPanel:destroy() end
|
||||
|
||||
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)
|
||||
if tab.blinkEvent then
|
||||
removeEvent(tab.blinkEvent)
|
||||
end
|
||||
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:getLastChild()
|
||||
if selectedWidget and selectedWidget.isTab 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
|
19
modules/corelib/widgets/uiwidget.lua
Normal file
19
modules/corelib/widgets/uiwidget.lua
Normal 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
|
38
modules/corelib/widgets/uiwindow.lua
Normal file
38
modules/corelib/widgets/uiwindow.lua
Normal 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
|
Reference in New Issue
Block a user