Files
PathOfBuilding/Modules/Main.lua
Openarl 8c01c85f1f Release 1.4.12
- Added shared item list
- Added options screen
- Added toasts
- Program now always updates on first run, but continues if update check fails
- Updated libcurl to 7.54.0
2017-05-19 14:50:33 +10:00

746 lines
24 KiB
Lua

-- Path of Building
--
-- Module: Main
-- Main module of program.
--
local launch = ...
local ipairs = ipairs
local t_insert = table.insert
local t_remove = table.remove
local m_floor = math.floor
local m_max = math.max
local m_min = math.min
local m_sin = math.sin
local m_cos = math.cos
local m_pi = math.pi
LoadModule("Modules/Common")
LoadModule("Modules/Data", launch)
LoadModule("Modules/ModTools")
LoadModule("Modules/ItemTools")
LoadModule("Modules/CalcTools")
LoadModule("Classes/ControlHost")
local main = common.New("ControlHost")
local classList = {
"UndoHandler",
-- Basic controls
"Control",
"LabelControl",
"SectionControl",
"ButtonControl",
"CheckBoxControl",
"EditControl",
"DropDownControl",
"ScrollBarControl",
"SliderControl",
"TextListControl",
"ListControl",
-- Misc
"PopupDialog",
-- Mode: Build list
"BuildListControl",
-- Mode: Build
"ModList",
"ModDB",
"MinionListControl",
"ImportTab",
"NotesTab",
"ConfigTab",
"TreeTab",
"PassiveTree",
"PassiveSpec",
"PassiveTreeView",
"PassiveSpecListControl",
"SkillsTab",
"SkillListControl",
"GemSelectControl",
"ItemsTab",
"ItemSlotControl",
"ItemListControl",
"ItemDBControl",
"SharedItemListControl",
"CalcsTab",
"CalcSectionControl",
"CalcBreakdownControl",
}
for _, className in pairs(classList) do
LoadModule("Classes/"..className, launch, main)
end
local tempTable1 = { }
local tempTable2 = { }
function main:Init()
self.modes = { }
self.modes["LIST"] = LoadModule("Modules/BuildList", launch, self)
self.modes["BUILD"] = LoadModule("Modules/Build", launch, self)
if launch.devMode or GetScriptPath() == GetRuntimePath() then
-- If running in dev mode or standalone mode, put user data in the script path
self.userPath = GetScriptPath().."/"
else
self.userPath = GetUserPath().."/Path of Building/"
MakeDir(self.userPath)
end
self.defaultBuildPath = self.userPath.."Builds/"
self.buildPath = self.defaultBuildPath
MakeDir(self.buildPath)
self.tree = common.New("PassiveTree")
ConPrintf("Loading item databases...")
self.uniqueDB = { list = { } }
for type, typeList in pairs(data.uniques) do
for _, raw in pairs(typeList) do
local newItem = itemLib.makeItemFromRaw("Rarity: Unique\n"..raw)
if newItem then
itemLib.normaliseQuality(newItem)
self.uniqueDB.list[newItem.name] = newItem
else
ConPrintf("Unique DB unrecognised item of type '%s':\n%s", type, raw)
end
end
end
self.rareDB = { list = { } }
for _, raw in pairs(data.rares) do
local newItem = itemLib.makeItemFromRaw(raw)
if newItem then
itemLib.normaliseQuality(newItem)
self.rareDB.list[newItem.name] = newItem
else
ConPrintf("Rare DB unrecognised item:\n%s", raw)
end
end
self.sharedItems = { }
self.anchorUpdate = common.New("Control", nil, 2, 0, 0, 0)
self.anchorUpdate.y = function()
return self.screenH - 4
end
self.controls.options = common.New("ButtonControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 0, 0, 70, 20, "Options", function()
self:OpenOptionsPopup()
end)
self.controls.about = common.New("ButtonControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 230, 0, 70, 20, "About", function()
self:OpenAboutPopup()
end)
self.controls.applyUpdate = common.New("ButtonControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 0, -28, 110, 20, "^x50E050Update Ready", function()
self:OpenUpdatePopup()
end)
self.controls.applyUpdate.shown = function()
return launch.updateAvailable and launch.updateAvailable ~= "none"
end
self.controls.checkUpdate = common.New("ButtonControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 0, -28, 120, 18, "", function()
launch:CheckForUpdate()
end)
self.controls.checkUpdate.shown = function()
return not launch.devMode and (not launch.updateAvailable or launch.updateAvailable == "none")
end
self.controls.checkUpdate.label = function()
return launch.updateCheckRunning and launch.updateProgress or "Check for Update"
end
self.controls.checkUpdate.enabled = function()
return not launch.updateCheckRunning
end
self.controls.versionLabel = common.New("LabelControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 124, -28, 0, 14, "")
self.controls.versionLabel.label = function()
return "^8Version: "..launch.versionNumber..(launch.versionBranch == "dev" and " (Dev)" or "")
end
self.controls.devMode = common.New("LabelControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 0, -28, 0, 18, "^1Dev Mode")
self.controls.devMode.shown = function()
return launch.devMode
end
self.controls.dismissToast = common.New("ButtonControl", {"BOTTOMLEFT",self.anchorUpdate,"BOTTOMLEFT"}, 0, function() return -self.mainBarHeight + self.toastHeight end, 80, 20, "Dismiss", function()
self.toastMode = "HIDING"
self.toastStart = GetTime()
end)
self.controls.dismissToast.shown = function()
return self.toastMode == "SHOWN"
end
self.mainBarHeight = 58
self.toastMessages = { }
if launch.devMode and GetTime() < 15000 then
t_insert(self.toastMessages, [[
^xFF7700Warning: ^7Developer Mode active!
The program is currently running in developer
mode, which is not intended for normal use.
If you are not expecting this, then you may have
set up the program from the source .zip instead
of using one of the installers. If that is the case,
please reinstall using one of the installers from
the "Releases" section of the GitHub page.]])
end
self.inputEvents = { }
self.popups = { }
self.tooltipLines = { }
self.accountSessionIDs = { }
self.buildSortMode = "NAME"
self.nodePowerTheme = "RED/BLUE"
self:SetMode("BUILD", false, "Unnamed build")
self:LoadSettings()
end
function main:CanExit()
local ret = self:CallMode("CanExit", "EXIT")
if ret ~= nil then
return ret
else
return true
end
end
function main:Shutdown()
self:CallMode("Shutdown")
self:SaveSettings()
end
function main:OnFrame()
self.screenW, self.screenH = GetScreenSize()
while self.newMode do
if self.mode then
self:CallMode("Shutdown")
end
self.mode = self.newMode
self.modeArgs = self.newModeArgs
self.newMode = nil
self:CallMode("Init", unpack(self.modeArgs))
end
self.viewPort = { x = 0, y = 0, width = self.screenW, height = self.screenH }
if self.popups[1] then
self.popups[1]:ProcessInput(self.inputEvents, self.viewPort)
wipeTable(self.inputEvents)
else
self:ProcessControlsInput(self.inputEvents, self.viewPort)
end
self:CallMode("OnFrame", self.inputEvents, self.viewPort)
if launch.updateErrMsg then
t_insert(self.toastMessages, string.format("Update check failed!\n%s", launch.updateErrMsg))
launch.updateErrMsg = nil
end
if launch.updateAvailable and launch.updateAvailable ~= "none" and not self.updateAvailableShown then
t_insert(self.toastMessages, "Update Available\nAn update has been downloaded and is ready\nto be applied.")
self.updateAvailableShown = true
end
-- Run toasts
if self.toastMessages[1] then
if not self.toastMode then
self.toastMode = "SHOWING"
self.toastStart = GetTime()
self.toastHeight = #self.toastMessages[1]:gsub("[^\n]","") * 16 + 20 + 40
end
if self.toastMode == "SHOWING" then
local now = GetTime()
if now >= self.toastStart + 250 then
self.toastMode = "SHOWN"
else
self.mainBarHeight = 58 + self.toastHeight * (now - self.toastStart) / 250
end
end
if self.toastMode == "SHOWN" then
self.mainBarHeight = 58 + self.toastHeight
elseif self.toastMode == "HIDING" then
local now = GetTime()
if now >= self.toastStart + 75 then
self.toastMode = nil
self.mainBarHeight = 58
t_remove(self.toastMessages, 1)
else
self.mainBarHeight = 58 + self.toastHeight * (1 - (now - self.toastStart) / 75)
end
end
if self.toastMode then
SetDrawColor(0.85, 0.85, 0.85)
DrawImage(nil, 0, self.screenH - self.mainBarHeight, 312, self.mainBarHeight)
SetDrawColor(0.1, 0.1, 0.1)
DrawImage(nil, 0, self.screenH - self.mainBarHeight + 4, 308, self.mainBarHeight - 4)
SetDrawColor(1, 1, 1)
DrawString(4, self.screenH - self.mainBarHeight + 8, "LEFT", 20, "VAR", self.toastMessages[1]:gsub("\n.*",""))
DrawString(4, self.screenH - self.mainBarHeight + 28, "LEFT", 16, "VAR", self.toastMessages[1]:gsub("^[^\n]*\n?",""))
end
end
-- Draw main controls
SetDrawColor(0.85, 0.85, 0.85)
DrawImage(nil, 0, self.screenH - 58, 312, 58)
SetDrawColor(0.1, 0.1, 0.1)
DrawImage(nil, 0, self.screenH - 54, 308, 54)
self:DrawControls(self.viewPort)
if self.popups[1] then
SetDrawLayer(10)
SetDrawColor(0, 0, 0, 0.5)
DrawImage(nil, 0, 0, self.screenW, self.screenH)
self.popups[1]:Draw(self.viewPort)
SetDrawLayer(0)
end
wipeTable(self.inputEvents)
end
function main:OnKeyDown(key, doubleClick)
t_insert(self.inputEvents, { type = "KeyDown", key = key, doubleClick = doubleClick })
end
function main:OnKeyUp(key)
t_insert(self.inputEvents, { type = "KeyUp", key = key })
end
function main:OnChar(key)
t_insert(self.inputEvents, { type = "Char", key = key })
end
function main:SetMode(newMode, ...)
self.newMode = newMode
self.newModeArgs = {...}
end
function main:CallMode(func, ...)
local modeTbl = self.modes[self.mode]
if modeTbl[func] then
return modeTbl[func](modeTbl, ...)
end
end
function main:LoadSettings()
local setXML, errMsg = common.xml.LoadXMLFile(self.userPath.."Settings.xml")
if not setXML then
return true
elseif setXML[1].elem ~= "PathOfBuilding" then
launch:ShowErrMsg("^1Error parsing 'Settings.xml': 'PathOfBuilding' root element missing")
return true
end
for _, node in ipairs(setXML[1]) do
if type(node) == "table" then
if node.elem == "Mode" then
if not node.attrib.mode or not self.modes[node.attrib.mode] then
launch:ShowErrMsg("^1Error parsing 'Settings.xml': Invalid mode attribute in 'Mode' element")
return true
end
local args = { }
for _, child in ipairs(node) do
if type(child) == "table" then
if child.elem == "Arg" then
if child.attrib.number then
t_insert(args, tonumber(child.attrib.number))
elseif child.attrib.string then
t_insert(args, child.attrib.string)
elseif child.attrib.boolean then
t_insert(args, child.attrib.boolean == "true")
end
end
end
end
self:SetMode(node.attrib.mode, unpack(args))
elseif node.elem == "Accounts" then
self.lastAccountName = node.attrib.lastAccountName
for _, child in ipairs(node) do
if child.elem == "Account" then
self.accountSessionIDs[child.attrib.accountName] = child.attrib.sessionID
end
end
elseif node.elem == "SharedItems" then
for _, child in ipairs(node) do
if child.elem == "Item" then
local item = { raw = "" }
itemLib.parseItemRaw(item)
for _, subChild in ipairs(child) do
if type(subChild) == "string" then
item.raw = subChild
itemLib.parseItemRaw(item)
end
end
itemLib.buildItemModList(item)
t_insert(self.sharedItems, item)
end
end
elseif node.elem == "Misc" then
if node.attrib.buildSortMode then
self.buildSortMode = node.attrib.buildSortMode
end
launch.proxyURL = node.attrib.proxyURL
if node.attrib.buildPath then
self.buildPath = node.attrib.buildPath
end
if node.attrib.nodePowerTheme then
self.nodePowerTheme = node.attrib.nodePowerTheme
end
end
end
end
end
function main:SaveSettings()
local setXML = { elem = "PathOfBuilding" }
local mode = { elem = "Mode", attrib = { mode = self.mode } }
for _, val in ipairs(self.modeArgs) do
local child = { elem = "Arg", attrib = { } }
if type(val) == "number" then
child.attrib.number = tostring(val)
elseif type(val) == "boolean" then
child.attrib.boolean = tostring(val)
else
child.attrib.string = tostring(val)
end
t_insert(mode, child)
end
t_insert(setXML, mode)
local accounts = { elem = "Accounts", attrib = { lastAccountName = self.lastAccountName } }
for accountName, sessionID in pairs(self.accountSessionIDs) do
t_insert(accounts, { elem = "Account", attrib = { accountName = accountName, sessionID = sessionID } })
end
t_insert(setXML, accounts)
local sharedItems = { elem = "SharedItems" }
for _, item in ipairs(self.sharedItems) do
t_insert(sharedItems, { elem = "Item", [1] = item.raw })
end
t_insert(setXML, sharedItems)
t_insert(setXML, { elem = "Misc", attrib = {
buildSortMode = self.buildSortMode,
proxyURL = launch.proxyURL,
buildPath = (self.buildPath ~= self.defaultBuildPath and self.buildPath or nil),
nodePowerTheme = self.nodePowerTheme,
} })
local res, errMsg = common.xml.SaveXMLFile(setXML, self.userPath.."Settings.xml")
if not res then
launch:ShowErrMsg("Error saving 'Settings.xml': %s", errMsg)
return true
end
end
function main:OpenOptionsPopup()
local controls = { }
controls.proxyType = common.New("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, 150, 20, 80, 18, {{val="http",label="HTTP"},{val="socks5",label="SOCKS"}})
controls.proxyLabel = common.New("LabelControl", {"RIGHT",controls.proxyType,"LEFT"}, -4, 0, 0, 16, "^7Proxy server:")
controls.proxyURL = common.New("EditControl", {"LEFT",controls.proxyType,"RIGHT"}, 4, 0, 206, 18)
if launch.proxyURL then
local scheme, url = launch.proxyURL:match("(%w+)://(.+)")
controls.proxyType:SelByValue(scheme)
controls.proxyURL:SetText(url)
end
controls.buildPath = common.New("EditControl", {"TOPLEFT",nil,"TOPLEFT"}, 150, 44, 290, 18)
controls.buildPathLabel = common.New("LabelControl", {"RIGHT",controls.buildPath,"LEFT"}, -4, 0, 0, 16, "^7Build save path:")
if self.buildPath ~= self.defaultBuildPath then
controls.buildPath:SetText(self.buildPath)
end
controls.buildPath.tooltip = "Overrides the default save location for builds.\nThe default location is: '"..self.defaultBuildPath.."'"
controls.nodePowerTheme = common.New("DropDownControl", {"TOPLEFT",nil,"TOPLEFT"}, 150, 68, 100, 18, {{val="RED/BLUE",label="Red & Blue"},{val="RED/GREEN",label="Red & Green"},{val="GREEN/BLUE",label="Green & Blue"}}, function(sel, value)
self.nodePowerTheme = value.val
end)
controls.nodePowerThemeLabel = common.New("LabelControl", {"RIGHT",controls.nodePowerTheme,"LEFT"}, -4, 0, 0, 16, "^7Node Power colours:")
controls.nodePowerTheme.tooltip = "Changes the colour scheme used for the node power display on the passive tree."
controls.nodePowerTheme:SelByValue(self.nodePowerTheme)
local initialNodePowerTheme = self.nodePowerTheme
controls.save = common.New("ButtonControl", nil, -45, 120, 80, 20, "Save", function()
if controls.proxyURL.buf:match("%w") then
launch.proxyURL = controls.proxyType.list[controls.proxyType.sel].val .. "://" .. controls.proxyURL.buf
else
launch.proxyURL = nil
end
if controls.buildPath.buf:match("%S") then
self.buildPath = controls.buildPath.buf
if not self.buildPath:match("[\\/]$") then
self.buildPath = self.buildPath .. "/"
end
else
self.buildPath = self.defaultBuildPath
end
if self.mode == "LIST" then
self.modes.LIST:BuildList()
end
main:ClosePopup()
end)
controls.cancel = common.New("ButtonControl", nil, 45, 120, 80, 20, "Cancel", function()
self.nodePowerTheme = initialNodePowerTheme
main:ClosePopup()
end)
self:OpenPopup(450, 150, "Options", controls, "save", nil, "cancel")
end
function main:OpenUpdatePopup()
local changeList = { }
for line in io.lines("changelog.txt") do
local ver, date = line:match("^VERSION%[(.+)%]%[(.+)%]$")
if ver then
if ver == launch.versionNumber then
break
end
if #changeList > 0 then
t_insert(changeList, { height = 12 })
end
t_insert(changeList, { height = 20, "^7Version "..ver.." ("..date..")" })
else
t_insert(changeList, { height = 14, "^7"..line })
end
end
local controls = { }
controls.changeLog = common.New("TextListControl", nil, 0, 20, 780, 192, nil, changeList)
controls.update = common.New("ButtonControl", nil, -45, 220, 80, 20, "Update", function()
self:ClosePopup()
local ret = self:CallMode("CanExit", "UPDATE")
if ret == nil or ret == true then
launch:ApplyUpdate(launch.updateAvailable)
end
end)
controls.cancel = common.New("ButtonControl", nil, 45, 220, 80, 20, "Cancel", function()
self:ClosePopup()
end)
controls.patreon = common.New("ButtonControl", {"BOTTOMLEFT",nil,"BOTTOMLEFT"}, 10, -10, 82, 22, "", function()
OpenURL("https://www.patreon.com/openarl")
end)
controls.patreon:SetImage("Assets/patreon_logo.png")
controls.patreon.tooltip = "Help support the development of Path of Building by pledging a monthly donation!"
self:OpenPopup(800, 250, "Update Available", controls)
end
function main:OpenAboutPopup()
local changeList = { }
for line in io.lines("changelog.txt") do
local ver, date = line:match("^VERSION%[(.+)%]%[(.+)%]$")
if ver then
if #changeList > 0 then
t_insert(changeList, { height = 10 })
end
t_insert(changeList, { height = 18, "^7Version "..ver.." ("..date..")" })
else
t_insert(changeList, { height = 12, "^7"..line })
end
end
local controls = { }
controls.close = common.New("ButtonControl", {"TOPRIGHT",nil,"TOPRIGHT"}, -10, 10, 50, 20, "Close", function()
self:ClosePopup()
end)
controls.version = common.New("LabelControl", nil, 0, 18, 0, 18, "Path of Building v"..launch.versionNumber.." by Openarl")
controls.forum = common.New("ButtonControl", nil, 0, 42, 420, 18, "Forum Thread: ^x4040FFhttps://www.pathofexile.com/forum/view-thread/1716360", function(control)
OpenURL("https://www.pathofexile.com/forum/view-thread/1716360")
end)
controls.github = common.New("ButtonControl", nil, 0, 64, 340, 18, "GitHub page: ^x4040FFhttps://github.com/Openarl/PathOfBuilding", function(control)
OpenURL("https://github.com/Openarl/PathOfBuilding")
end)
controls.patreon = common.New("ButtonControl", {"TOPLEFT",nil,"TOPLEFT"}, 10, 10, 82, 22, "", function()
OpenURL("https://www.patreon.com/openarl")
end)
controls.patreon:SetImage("Assets/patreon_logo.png")
controls.patreon.tooltip = "Help support the development of Path of Building by pledging a monthly donation!"
controls.verLabel = common.New("LabelControl", {"TOPLEFT",nil,"TOPLEFT"}, 10, 82, 0, 18, "^7Version history:")
controls.changelog = common.New("TextListControl", nil, 0, 100, 630, 290, nil, changeList)
self:OpenPopup(650, 400, "About", controls)
end
function main:DrawBackground(viewPort)
SetDrawLayer(nil, -100)
SetDrawColor(0.5, 0.5, 0.5)
DrawImage(self.tree.assets.Background1.handle, viewPort.x, viewPort.y, viewPort.width, viewPort.height, 0, 0, viewPort.width / 100, viewPort.height / 100)
SetDrawLayer(nil, 0)
end
function main:DrawArrow(x, y, size, dir)
local x1 = x - size / 2
local x2 = x + size / 2
local xMid = (x1 + x2) / 2
local y1 = y - size / 2
local y2 = y + size / 2
local yMid = (y1 + y2) / 2
if dir == "UP" then
DrawImageQuad(nil, xMid, y1, xMid, y1, x2, y2, x1, y2)
elseif dir == "RIGHT" then
DrawImageQuad(nil, x1, y1, x2, yMid, x2, yMid, x1, y2)
elseif dir == "DOWN" then
DrawImageQuad(nil, x1, y1, x2, y1, xMid, y2, xMid, y2)
elseif dir == "LEFT" then
DrawImageQuad(nil, x1, yMid, x2, y1, x2, y2, x1, yMid)
end
end
function main:DrawCheckMark(x, y, size)
size = size / 0.8
x = x - size / 2
y = y - size / 2
DrawImageQuad(nil, x + size * 0.15, y + size * 0.50, x + size * 0.30, y + size * 0.45, x + size * 0.50, y + size * 0.80, x + size * 0.40, y + size * 0.90)
DrawImageQuad(nil, x + size * 0.40, y + size * 0.90, x + size * 0.35, y + size * 0.75, x + size * 0.80, y + size * 0.10, x + size * 0.90, y + size * 0.20)
end
do
local cos45 = m_cos(m_pi / 4)
local cos35 = m_cos(m_pi * 0.195)
local sin35 = m_sin(m_pi * 0.195)
function main:WorldToScreen(x, y, z, width, height)
-- World -> camera
local cx = (x - y) * cos45
local cy = -5.33 - (y + x) * cos45 * cos35 - z * sin35
local cz = 122 + (y + x) * cos45 * sin35 - z * cos35
-- Camera -> screen
local sx = width * 0.5 + cx / cz * 1.27 * height
local sy = height * 0.5 + cy / cz * 1.27 * height
return round(sx), round(sy)
end
end
function main:RenderCircle(x, y, width, height, oX, oY, radius)
local minX = wipeTable(tempTable1)
local maxX = wipeTable(tempTable2)
local minY = height
local maxY = 0
for d = 0, 360, 0.2 do
local r = d / 180 * m_pi
local px, py = main:WorldToScreen(oX + m_sin(r) * radius, oY + m_cos(r) * radius, 0, width, height)
if py >= 0 and py < height then
px = m_min(width, m_max(0, px))
minY = m_min(minY, py)
maxY = m_max(maxY, py)
minX[py] = m_min(minX[py] or px, px)
maxX[py] = m_max(maxX[py] or px, px)
end
end
for ly = minY, maxY do
DrawImage(nil, x + minX[ly], y + ly, maxX[ly] - minX[ly] + 1, 1)
end
end
function main:RenderRing(x, y, width, height, oX, oY, radius, size)
local lastX, lastY
for d = 0, 360, 0.2 do
local r = d / 180 * m_pi
local px, py = main:WorldToScreen(oX + m_sin(r) * radius, oY + m_cos(r) * radius, 0, width, height)
if px >= -size/2 and px < width + size/2 and py >= -size/2 and py < height + size/2 and (px ~= lastX or py ~= lastY) then
DrawImage(nil, x + px - size/2, y + py, size, size)
lastX, lastY = px, py
end
end
end
function main:OpenPopup(width, height, title, controls, enterControl, defaultControl, escapeControl)
local popup = common.New("PopupDialog", width, height, title, controls, enterControl, defaultControl, escapeControl)
t_insert(self.popups, 1, popup)
return popup
end
function main:ClosePopup()
t_remove(self.popups, 1)
end
function main:OpenMessagePopup(title, msg)
local controls = { }
local numMsgLines = 0
for line in string.gmatch(msg .. "\n", "([^\n]*)\n") do
t_insert(controls, common.New("LabelControl", nil, 0, 20 + numMsgLines * 16, 0, 16, line))
numMsgLines = numMsgLines + 1
end
controls.close = common.New("ButtonControl", nil, 0, 40 + numMsgLines * 16, 80, 20, "Ok", function()
main:ClosePopup()
end)
return self:OpenPopup(m_max(DrawStringWidth(16, "VAR", msg) + 30, 190), 70 + numMsgLines * 16, title, controls, "close")
end
function main:OpenConfirmPopup(title, msg, confirmLabel, onConfirm)
local controls = { }
local numMsgLines = 0
for line in string.gmatch(msg .. "\n", "([^\n]*)\n") do
t_insert(controls, common.New("LabelControl", nil, 0, 20 + numMsgLines * 16, 0, 16, line))
numMsgLines = numMsgLines + 1
end
controls.confirm = common.New("ButtonControl", nil, -45, 40 + numMsgLines * 16, 80, 20, confirmLabel, function()
onConfirm()
main:ClosePopup()
end)
t_insert(controls, common.New("ButtonControl", nil, 45, 40 + numMsgLines * 16, 80, 20, "Cancel", function()
main:ClosePopup()
end))
return self:OpenPopup(m_max(DrawStringWidth(16, "VAR", msg) + 30, 190), 70 + numMsgLines * 16, title, controls, "confirm")
end
function main:AddTooltipLine(size, text)
for line in string.gmatch(text .. "\n", "([^\n]*)\n") do
t_insert(self.tooltipLines, { size = size, text = line })
end
end
function main:AddTooltipSeparator(size)
t_insert(self.tooltipLines, { size = size })
end
function main:DrawTooltip(x, y, w, h, viewPort, col, center)
if #self.tooltipLines == 0 then
return
end
local ttW, ttH = 0, 0
for i, data in ipairs(self.tooltipLines) do
if data.text or (self.tooltipLines[i - 1] and self.tooltipLines[i + 1] and self.tooltipLines[i + 1].text) then
ttH = ttH + data.size + 2
end
if data.text then
ttW = m_max(ttW, DrawStringWidth(data.size, "VAR", data.text))
end
end
ttW = ttW + 12
ttH = ttH + 10
local ttX = x
local ttY = y
if w and h then
ttX = ttX + w + 5
if ttX + ttW > viewPort.x + viewPort.width then
ttX = m_max(viewPort.x, x - 5 - ttW)
if ttX + ttW > x then
ttY = ttY + h
end
end
if ttY + ttH > viewPort.y + viewPort.height then
ttY = m_max(viewPort.y, y + h - ttH)
end
elseif center then
ttX = m_floor(x - ttW/2)
end
col = col or { 0.5, 0.3, 0 }
if type(col) == "string" then
SetDrawColor(col)
else
SetDrawColor(unpack(col))
end
DrawImage(nil, ttX, ttY, ttW, 3)
DrawImage(nil, ttX, ttY, 3, ttH)
DrawImage(nil, ttX, ttY + ttH - 3, ttW, 3)
DrawImage(nil, ttX + ttW - 3, ttY, 3, ttH)
SetDrawColor(0, 0, 0, 0.75)
DrawImage(nil, ttX + 3, ttY + 3, ttW - 6, ttH - 6)
SetDrawColor(1, 1, 1)
local y = ttY + 6
for i, data in ipairs(self.tooltipLines) do
if data.text then
if center then
DrawString(ttX + ttW/2, y, "CENTER_X", data.size, "VAR", data.text)
else
DrawString(ttX + 6, y, "LEFT", data.size, "VAR", data.text)
end
y = y + data.size + 2
elseif self.tooltipLines[i + 1] and self.tooltipLines[i - 1] and self.tooltipLines[i + 1].text then
if type(col) == "string" then
SetDrawColor(col)
else
SetDrawColor(unpack(col))
end
DrawImage(nil, ttX + 3, y - 1 + data.size / 2, ttW - 6, 2)
y = y + data.size + 2
end
end
self.tooltipLines = wipeTable(self.tooltipLines)
return ttW, ttH
end
return main