myspace / CommonLua /X /XDialog.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
14.7 kB
if FirstLoad then
Dialogs = {}
end
function OpenDialog(template, parent, context, reason, id, prop_preset)
id = id or template
local dialog = GetDialog(id)
if dialog then
if context then
dialog:SetContext(context)
end
local mode = ResolveValue(context, "Mode")
if mode ~= nil then
dialog:SetMode(mode)
end
else
assert(Dialogs[id] == nil)
dialog = XTemplateSpawn(template, parent, context)
assert(IsKindOf(dialog, "XDialog"))
if not dialog then
return
end
dialog.XTemplate = XTemplates[template] and template or nil
Dialogs[id] = dialog
Dialogs[dialog] = id
if not parent or parent.window_state == "open" then
if prop_preset then
dialog:CopyProperties(prop_preset, prop_preset:GatherTemplateProperties())
end
dialog:Open()
end
end
if IsKindOf(dialog, "XDialog") then
dialog:AddOpenReason(reason)
end
return dialog
end
function CloseDialog(id, result, reason)
local dialog = GetDialog(id)
if dialog then
if IsKindOf(dialog, "XDialog") then
dialog:RemoveOpenReason(reason, result)
else
dialog:Close(result)
end
return dialog
end
end
function ListDialogs()
local dlgs = {}
for k,v in pairs(Dialogs) do
if type(k)=="string" then
dlgs[#dlgs + 1] = k
end
end
table.sort(dlgs)
return dlgs
end
function ListAllDialogs()
return PresetsCombo("XTemplate", nil, "InGameInterface")()
end
function RemoveOpenReason(reason, result)
for dialog in pairs(Dialogs) do
if IsKindOf(dialog, "XDialog") then
dialog:RemoveOpenReason(reason, result)
end
end
end
function GetDialog(id_or_win)
if type(id_or_win) == "table" and IsKindOf(id_or_win, "XWindow") then
return GetParentOfKind(id_or_win, "XDialog")
end
return Dialogs[id_or_win]
end
function WaitDialog(...)
local dialog = OpenDialog(...)
if dialog then
return dialog:Wait()
end
end
function EnsureDialog(dlg)
local dialog = GetDialog(dlg)
if not dialog then
local t = GetInGameInterface()
if not t then
ShowInGameInterface(true)
t = GetInGameInterface()
end
dialog = OpenDialog(dlg, t)
end
return dialog
end
function GetDialogMode(id_or_win)
local dlg = GetDialog(id_or_win)
return dlg and dlg.Mode
end
function GetDialogModeParam(id_or_win)
local dlg = GetDialog(id_or_win)
return dlg and dlg.mode_param
end
function SetDialogMode(id_or_win, mode, mode_param)
local dlg = GetDialog(id_or_win)
if dlg then
dlg:SetMode(mode, mode_param)
end
end
function SetBackDialogMode(id_or_win)
local dlg = GetDialog(id_or_win)
if dlg then
local mode = table.remove(dlg.mode_log)
if mode then
dlg:SetMode(mode[1], mode[2])
table.remove(dlg.mode_log)
end
end
end
function GetDialogContext(id_or_win)
local dlg = GetDialog(id_or_win)
return dlg and dlg.context
end
function _GetUIPath(win, parent)
win = win or (parent or terminal.desktop):GetMouseTarget(terminal:GetMousePos())
parent = parent or false
local wins = {}
local w = win
local f
while w and w ~= parent do
local name = Dialogs[w] or rawget(w, "XTemplate") or w.class
local id = rawget(w, "Id")
if id and name ~= id and id ~= "" then
name = name .. "(" .. id .. ")"
end
local z = w.ZOrder ~= 1 and w.ZOrder
if z then
name = name .. "[" .. z .. "]"
end
if not w:IsVisible() then
name = name .. "|X|"
end
if not f and w:IsFocused() then
f = w
name = name .. "*"
end
table.insert(wins, 1, name)
w = w.parent or false
end
return table.concat(wins, " - ")
end
function _PrintDialogs(print_func, indent, except)
except = except or {}
indent = indent or ""
print_func = print_func or print
local texts = {}
for name, dlg in pairs(Dialogs) do
if type(name) == "string" and type(dlg) == "table" and not table.find(except,name) then
texts[#texts + 1] = _GetUIPath(dlg, terminal.desktop)
if dlg:IsKindOf("BaseLoadingScreen") then
texts[#texts + 1] = "\t" .. ValueToLuaCode(dlg:GetOpenReasons())
end
end
end
table.sort(texts)
local indents = {}
for i=1,#texts do
local txt = texts[i]
for j=i+1,#texts do
local t = texts[j]
local b, e = string.find(t, txt, 1, true)
if b then
indents[j] = (indents[j] or 0) + 1
texts[j] = string.sub(t, e+1)
end
end
end
for i=1,#texts do
local txt = texts[i]
if indents[i] then
txt = string.rep("\t", indents[i]) .. txt
end
print_func(indent, txt)
end
end
function OnMsg.BugReportStart(print_func)
print_func("Screen size: ", UIL.GetScreenSize())
if next(Dialogs) ~= nil then
print_func("Opened dialogs: (notation: (n) = Id, [n] = ZOrder, |X| = Invisible, * = Focused)")
_PrintDialogs(print_func, "\t")
print_func("")
end
end
function CloseAllDialogs(except, force_loading_screens)
if Platform.xbox then
AsyncOpCancel(ActiveVirtualKeyboard)
end
CloseAllMessagesAndQuestions()
local dialogs = ListDialogs()
for i = 1, #dialogs do
local dialog = dialogs[i]
if except and except[dialog] then
print("Skipping dialog " .. dialog)
elseif not force_loading_screens and (IsKindOf(GetDialog(dialog), "BaseLoadingScreen") or IsKindOf(GetDialog(dialog), "BaseSavingScreen")) then
-- should be closed by the code that opened them
else
CloseDialog(dialog)
end
end
end
function CloseDialogs(id, ...)
if not id then return end
CloseDialog(id)
return CloseDialogs(...)
end
----- XDialog
DefineClass.XDialog = {
__parents = { "XActionsHost" },
properties = {
{ category = "General", id = "InitialMode", editor = "text", default = "" },
{ category = "General", id = "Mode", editor = "text", default = "", read_only = true, },
{ category = "General", id = "InternalModes", name = "Internal modes", editor = "text", default = "", help = "A list of internal modes. When present, any mode outside of the list will be propagated to the parent dialog."},
{ category = "General", id = "gamestate", Name = "Game state", editor = "text", default = "" },
{ category = "General", id = "FocusOnOpen", editor = "choice", default = "self", items = {"", "self", "child"}, },
{ category = "Visual", id = "HideInScreenshots", editor = "bool", default = false },
},
XTemplate = false,
Translate = true,
IdNode = true,
open_reasons = false,
result = false,
close_controller_id = false, -- which controller was used to close the dialog
mode_log = false,
mode_param = false,
}
function XDialog:Init(parent, context)
self.InitialMode = ResolveValue(context, "Mode") or self.InitialMode
self.mode_log = ResolveValue(context, "mode_log") or {}
end
function XDialog:Close(reason, source, controller_id, ...)
if source == "gamepad" then
self.close_controller_id = controller_id
end
XActionsHost.Close(self, reason, source, controller_id, ...)
end
function XDialog:Done(result)
Msg("DialogClose", self, result)
local id = Dialogs[self]
Dialogs[self] = nil
if id and Dialogs[id] == self then
Dialogs[id] = nil
if self.gamestate ~= "" then
ChangeGameState(self.gamestate, false)
end
end
self.result = result
Msg(self)
end
function XDialog:Open(...)
if not self.HostInParent then
self:ResolveRelativeFocusOrder()
end
self:SetFocus_OnOpen(self.FocusOnOpen)
XActionsHost.Open(self, ...)
Msg("DialogOpen", self, self.InitialMode)
if self.InitialMode ~= "" then
self:SetMode(self.InitialMode)
end
if self.gamestate ~= "" then
ChangeGameState(self.gamestate, true)
end
end
function XDialog:SetFocus_OnOpen(focus)
focus = focus or self.FocusOnOpen
if focus == "self" then
self:SetFocus()
elseif focus == "child" then
local child = self:GetRelativeFocus(point(1, 1), "nearest")
if child then
child:SetFocus()
end
end
end
function XDialog:Wait()
assert(IsAsyncCode())
if self.window_state == "open" then
assert(not self:GetThreadName(), "The window thread will be deleted before self:Wait() returns")
WaitMsg(self)
end
return self.result, self, self.close_controller_id
end
function XDialog:AddOpenReason(reason)
self.open_reasons = self.open_reasons or {}
self.open_reasons[reason or true] = true
end
function XDialog:RemoveOpenReason(reason, result)
local open_reasons = self.open_reasons
reason = reason or true
if open_reasons and open_reasons[reason] then
open_reasons[reason] = nil
if next(open_reasons) == nil and self.window_state ~= "destroying" then
self:Close(result)
return true
end
end
end
function XDialog:GetOpenReasons()
return self.open_reasons or empty_table
end
local function callOnModeChange(win, mode, dialog)
if win == dialog or not IsKindOf(win, "XDialog") then
for _, win in ipairs(win or empty_table) do
callOnModeChange(win, mode, dialog)
end
end
win:OnDialogModeChange(mode, dialog)
end
function MatchDialogMode(mode, list)
if not list or list == "" then return end
if mode == "" then
return list:starts_with(",") -- the first mode in the list can be ""
end
if mode == list then
return true
end
if not list:find(mode, 1, true) then
return
end
for m in list:gmatch("([%w%-_]+)") do
if m == mode then
return true
end
end
end
function XDialog:GetModes(list)
list = list or self.InternalModes or ""
local arr = { list:starts_with(",") and "" or nil }
for m in list:gmatch("([%w%-_]+)") do
arr[#arr + 1] = m
end
return arr
end
function XDialog:SetMode(mode, mode_param)
if not MatchDialogMode(mode, self.InternalModes) then
local dlg = GetParentOfKind(self.parent, "XDialog")
if dlg then
dlg:SetMode(mode, mode_param)
return
end
end
self.mode_log[#self.mode_log + 1] = { self.Mode, self.mode_param }
local old_mode = self.Mode
self.Mode = mode
self.mode_param = mode_param
Msg("DialogSetMode", self, mode, mode_param, old_mode)
self:CallOnModeChange()
end
function XDialog:CallOnModeChange()
callOnModeChange(self, self.Mode, self)
end
----- XLayer
DefineClass.XLayer = {
__parents = { "XDialog" },
FocusOnOpen = "",
}
----- XOpenLayer
DefineClass.XOpenLayer = {
__parents = { "XWindow" },
properties = {
{ category = "General", id = "Layer", editor = "combo", default = "", items = function() return XTemplateCombo("XLayer") end, },
{ category = "General", id = "LayerId", editor = "text", default = "", },
{ category = "General", id = "Mode", editor = "text", default = false, },
},
Dock = "ignore",
visible = false,
dialog = false,
xtemplate = false,
}
function XOpenLayer:Open()
if self.Layer ~= "" then
local context = self:GetContext()
if self.Mode then
context = SubContext(context, { Mode = self.Mode })
end
local id = self.LayerId ~= "" and self.LayerId or nil
self.dialog = OpenDialog(self.Layer, nil, context, self, id, self.xtemplate)
end
end
function XOpenLayer:Done()
if self.dialog then
CreateRealTimeThread(self.dialog.RemoveOpenReason, self.dialog, self)
end
end
----- XContentTemplate
DefineClass.XContentTemplate = {
__parents = { "XActionsHost" },
properties = {
{ category = "Template", id = "RespawnOnContext", name = "Respawn on context update", editor = "bool", default = true, },
{ category = "Template", id = "RespawnOnDialogMode", name = "Respawn on mode change", editor = "bool", default = true, },
{ category = "Template", id = "RespawnExpression", name = "Respawn on expression change", editor = "expression", default = empty_func, params = "self, context",
dont_save = function(self) return self.RespawnOnContext end },
},
IdNode = true,
HostInParent = true,
xtemplate = false,
respawn_value = false,
}
function XContentTemplate:Init(parent, context, xtemplate)
self.xtemplate = xtemplate
self.respawn_value = self:RespawnExpression(context)
end
function XContentTemplate:OnContextUpdate(context, ...)
if self.RespawnOnContext then
if self.window_state == "open" then
self:RespawnContent()
end
else
local respawn_value = self:RespawnExpression(context)
if rawget(self, "respawn_value") ~= respawn_value then
self.respawn_value = respawn_value
if self.window_state == "open" then
self:RespawnContent()
end
end
end
end
function XContentTemplate:OnDialogModeChange(mode, dialog)
if self.RespawnOnDialogMode then
self:RespawnContent()
end
end
function XContentTemplate:RespawnContent()
local xtemplate = self.xtemplate
if xtemplate then
local desktop = self.desktop
local focus = desktop.keyboard_focus
local focus_order = focus and focus:IsWithin(self) and focus:GetFocusOrder()
local gamepad_rollover = RolloverControl and RolloverControl == focus and RolloverGamepad
local mouse_rollover = RolloverControl and RolloverControl == desktop.last_mouse_target and RolloverControl:IsWithin(self)
self:DeleteChildren()
self:ClearActions()
xtemplate:EvalChildren(self, self.context)
for _, win in ipairs(self) do
win:Open()
end
self:InvalidateMeasure()
self:InvalidateLayout()
local host = GetActionsHost(self, true)
if not host or host == self then
self:ResolveRelativeFocusOrder()
elseif host and not host:GetThread("resolve_focus") then
host:CreateThread("resolve_focus", host.ResolveRelativeFocusOrder, host)
end
self:DeleteThread("rollover")
self:CreateThread("rollover", function(self, focus_order, gamepad_rollover, mouse_rollover)
local focus = self:GetRelativeFocus(focus_order, "nearest")
if focus then
focus:SetFocus()
end
if focus and gamepad_rollover then
XCreateRolloverWindow(focus, true, true)
elseif mouse_rollover then
local win = XGetRolloverControl()
if win and win:IsWithin(self) then
XCreateRolloverWindow(win, false, true)
end
end
end, self, focus_order, gamepad_rollover, mouse_rollover)
Msg("XWindowRecreated", self)
end
end
----- XContentTemplateScrollArea
DefineClass.XContentTemplateScrollArea = {
__parents = { "XScrollArea", "XContentTemplate" },
}
----- XContentTemplateList
DefineClass.XContentTemplateList = {
__parents = { "XList", "XContentTemplate" },
MouseScroll = false,
properties = {
{ category = "General", id = "KeepSelectionOnRespawn", editor = "bool", default = false },
},
}
function XContentTemplateList:OnShortcut(shortcut, source, ...)
if XList.OnShortcut(self, shortcut, source, ...) == "break" then
return "break"
end
return XActionsHost.OnShortcut(self, shortcut, source, ...)
end
function XContentTemplateList:Open(...)
self:GenerateItemHashTable()
XContentTemplate.Open(self, ...)
self:CreateThread("SetInitialSelection", self.SetInitialSelection, self)
end
function XContentTemplateList:RespawnContent()
local last_selection
if self.KeepSelectionOnRespawn and next(self.selection) then
last_selection = self.selection[1]
end
XContentTemplate.RespawnContent(self)
self:GenerateItemHashTable()
self:DeleteThread("SetInitialSelection")
self:CreateThread("SetInitialSelection", self.SetInitialSelection, self, last_selection)
end