myspace / CommonLua /X /XActions.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
27.5 kB
--[[@@@
@class XAction
Defines a UI action, which can be bound to buttons or used on its own.
Always initialize it with a parent or if that is not possible, call RegisterInHost afterwards with the correct XActionsHost
]]
DefineClass.XAction = {
__parents = { "XRollover" },
properties = {
{ category = "Action", id = "ActionId", editor = "text", default = "" },
{ category = "Action", id = "ActionMode", editor = "text", default = "" },
{ category = "Action", id = "InheritedActionModes", editor = "text", default = "", read_only = true, help = "ActionModes inherited from parent if ActionMode is empty", dont_save = true },
{ category = "Action", id = "ActionSortKey", editor = "text", default = "",
buttons = { { name = "Rebuild", func = "RebuildSortKeys" } },
},
{ category = "Action", id = "ActionTranslate", editor = "bool", default = true, },
{ category = "Action", id = "ActionName", editor = "text", default = "", translate = function(obj) return obj:GetProperty("ActionTranslate") end, },
{ category = "Action", id = "ActionDescription", editor = "text", default = "", translate = function(obj) return obj:GetProperty("ActionTranslate") end, },
{ category = "Action", id = "ActionIcon", editor = "ui_image", default = "", },
{ category = "Action", id = "ActionMenubar", editor = "text", default = "", },
{ category = "Action", id = "ActionToolbar", editor = "text", default = "" },
{ category = "Action", id = "ActionToolbarSplit", editor = "bool", default = false },
{ category = "Action", id = "ActionToolbarSection", editor = "text", default = "" },
{ category = "Action", id = "ActionUIStyle", editor = "choice", default = "auto", items = {"auto", "gamepad", "keyboard"}, },
{ category = "Action", id = "ActionShortcut", editor = "shortcut", default = "" },
{ category = "Action", id = "ActionShortcut2", editor = "shortcut", default = "" },
{ category = "Action", id = "ActionGamepad", editor = "shortcut", shortcut_type = "gamepad", default = "" },
{ category = "Action", id = "ActionGamepadHold", editor = "bool", default = false },
{ category = "Action", id = "ActionBindable", editor = "bool", default = false },
{ category = "Action", id = "ActionMouseBindable", editor = "bool", default = true },
{ category = "Action", id = "ActionBindSingleKey", editor = "bool", default = false },
{ category = "Action", id = "BindingsMenuCategory", editor = "text", default = "Default" },
{ category = "Action", id = "ActionButtonTemplate", editor = "choice", default = false, items = function() return XTemplateCombo("XButton") end, },
{ category = "Action", id = "ActionToggle", editor = "bool", default = false },
{ category = "Action", id = "ActionToggled", editor = "func", params = "self, host", read_only = function(self) return not self:GetProperty("ActionToggle") end, },
{ category = "Action", id = "ActionToggledIcon", editor = "ui_image", default = "", read_only = function(self) return not self:GetProperty("ActionToggle") end, },
{ category = "Action", id = "ActionState", editor = "func", params = "self, host" },
{ category = "Action", id = "OnActionEffect", editor = "choice", default = "", items = {"", "popup", "back", "close", "mode"}, },
{ category = "Action", id = "OnActionParam", editor = "text", default = "" },
{ category = "Action", id = "OnAction", editor = "func", params = "self, host, source, ...", },
{ category = "Action", id = "OnShortcutUp", editor = "func", params = "self, host, source, ...", default = false },
{ category = "Action", id = "OnAltAction", editor = "func", params = "self, host, source, ...", default = false},
{ category = "Action", id = "IgnoreRepeated", editor = "bool", default = false },
{ category = "Action", id = "ActionContexts", editor = "string_list", default = false },
{ category = "FX", id = "FXMouseIn", editor = "text", default = "", },
{ category = "FX", id = "FXPress", editor = "text", default = "", },
{ category = "FX", id = "FXPressDisabled", editor = "text", default = "", },
-- properties of XRollover should regard ActionTranslate
{ category = "Rollover", id = "RolloverTranslate", editor = false },
{ category = "Rollover", id = "RolloverAnchor", editor = false },
{ category = "Rollover", id = "RolloverText", editor = "text", default = "", translate = function(obj) return obj:GetProperty("ActionTranslate") end, lines = 3, },
{ category = "Rollover", id = "RolloverDisabledText", editor = "text", default = "", translate = function(obj) return obj:GetProperty("ActionTranslate") end, lines = 3, },
},
-- the default values are needed when saving and loading from AccountStorage
default_ActionShortcut = false,
default_ActionShortcut2 = false,
default_ActionGamepad = false,
shortcut_up_thread = false,
host = false,
multi_mode_cache = false
}
function XAction:RegisterInHost(host, replace_matching_id)
self.host = host
if host then host:_InternalAddAction(self, replace_matching_id) end
self:BindShortcuts()
if self.OnShortcutUp and host and self.OnAction ~= XAction.OnAction then
local oldAction = self.OnAction
self.OnAction = function(self, ...)
oldAction(self, ...)
local keyOne = self.ActionShortcut and VKStrNamesInverse[self.ActionShortcut]
local keyTwo = self.ActionShortcut2 and VKStrNamesInverse[self.ActionShortcut2]
local downKey = (keyOne and terminal.IsKeyPressed(keyOne) and keyOne) or (keyTwo and terminal.IsKeyPressed(keyTwo) and keyTwo)
if IsValidThread(self.shortcut_up_thread) then
DeleteThread(self.shortcut_up_thread)
end
self.shortcut_up_thread = CreateRealTimeThread(function(self, ...)
while downKey and terminal.IsKeyPressed(downKey) and not terminal.desktop.inactive do
Sleep(16)
end
self.OnShortcutUp(self, ...)
end, self, ...)
end
end
end
function XAction:Init(parent, context, replace_matching_id)
self:RegisterInHost(GetActionsHost(parent), replace_matching_id)
end
function XAction:BindShortcuts()
self.default_ActionShortcut = self.ActionShortcut
self.default_ActionShortcut2 = self.ActionShortcut2
self.default_ActionGamepad = self.ActionGamepad
if self.ActionBindable then
local bindings = AccountStorage and AccountStorage.Shortcuts[self.ActionId]
if bindings then
self:SetActionShortcuts(bindings[1] or self.ActionShortcut, bindings[2] or self.ActionShortcut2, bindings[3] or self.ActionGamepad)
end
end
end
local function RemoveShortcut(action, name, shortcut)
shortcut = shortcut or ""
local old_shortcut = action[name]
if shortcut == old_shortcut then
-- no change
return
end
action[name] = nil
local host = action.host
if not host then return end
-- unregister old shortcut
host:CallHostParents("RemoveShortcutToAction", action, old_shortcut)
end
local function AddShortcut(action, name, shortcut)
shortcut = shortcut or ""
local old_shortcut = action[name]
if shortcut == old_shortcut then
-- no change
return
end
action[name] = shortcut
local host = action.host
if not host then return end
-- register new one
host:CallHostParents("AddShortcutToAction", action, shortcut)
end
function XAction:SetActionShortcuts(shortcut, shortcut2, shortcut_gamepad)
-- remove old shortcuts before adding any of the new ones
RemoveShortcut(self, "ActionShortcut", shortcut)
RemoveShortcut(self, "ActionShortcut2", shortcut2)
RemoveShortcut(self, "ActionGamepad", shortcut_gamepad)
-- add new shortcuts
AddShortcut(self, "ActionShortcut", shortcut)
AddShortcut(self, "ActionShortcut2", shortcut2)
AddShortcut(self, "ActionGamepad", shortcut_gamepad)
end
function XAction:SetActionMenubar(menubar)
local host = self.host
if host and self.ActionMenubar ~= menubar then
host:CallHostParents("RemoveMenubarAction", self)
end
self.ActionMenubar = menubar
if not host then return end
host:CallHostParents("AddMenubarAction", self)
end
function XAction:SetActionToolbar(toolbar)
local host = self.host
if host and self.ActionToolbar ~= toolbar then
host:CallHostParents("RemoveToolbarAction", self)
end
self.ActionToolbar = toolbar
if not host then return end
host:CallHostParents("AddToolbarAction", self)
end
function XAction:SetActionSortKey(sort_key)
self.ActionSortKey = sort_key
if not self.host then return end
self.host:InvalidateActionSortKey(self)
end
function XAction:ActionState(host)
end
function XAction:ActionToggled(host)
end
function XAction:OnAction(host, source, ...)
local effect = self.OnActionEffect
local param = self.OnActionParam
if effect == "close" and host and host.window_state ~= "destroying" then
host:Close(param ~= "" and param or nil, source, ...)
elseif effect == "mode" and host then
assert(IsKindOf(host, "XDialog"))
host:SetMode(param)
elseif effect == "back" and host then
assert(IsKindOf(host, "XDialog"))
SetBackDialogMode(host)
elseif effect == "popup" then
local actions_view = GetParentOfKind(source, "XActionsView")
if actions_view then
actions_view:PopupAction(self.ActionId, host, source)
else
XShortcutsTarget:OpenPopupMenu(self.ActionId, terminal.GetMousePos())
end
else
--print(self.ActionId, "activated")
end
end
function XAction:OnXTemplateSetProperty(prop_id, old_value)
-- toggle text properties between Ts and strings when ActionTranslate is edited
if prop_id == "ActionTranslate" then
self:UpdateLocalizedProperty("ActionName", self.ActionTranslate)
self:UpdateLocalizedProperty("RolloverText", self.ActionTranslate)
self:UpdateLocalizedProperty("RolloverDisabledText", self.ActionTranslate)
ObjModified(self)
end
if prop_id == "ActionSortKey" and self.ActionSortKey ~= "" then
local preset = GetParentTableOfKind(self, "XTemplate")
preset.RequireActionSortKeys = true
end
end
function XAction:EnabledInMode(mode)
local myMode = self.ActionMode
if myMode == "" then return true end
if mode == myMode then return true end
if not self.multi_mode_cache or self.multi_mode_cache.strsrc ~= myMode then
local modeCache = { strsrc = myMode }
for str in string.gmatch(myMode, "([%w%-_]+)") do
modeCache[str] = true
end
self.multi_mode_cache = modeCache
end
return self.multi_mode_cache[mode]
end
local function assign_sortkeys(node, sortkey)
if node:IsKindOf("XTemplateAction") and node.ActionId ~= "" then
if node.ActionSortKey ~= "" then
print("Overwriting SortKey of", node.ActionId, "to", node.ActionSortKey)
end
node:SetActionSortKey(tostring(sortkey * 10))
sortkey = sortkey + 1
end
for _, item in ipairs(node) do
sortkey = assign_sortkeys(item, sortkey)
end
return sortkey
end
function XAction:RebuildSortKeys(root, prop_id, ged, btn_param)
if ged:WaitQuestion("Rebuild SortKeys", "This will assign SortKeys to each Action in the current file, in the order\nthey appear, overwriting existing ones.\n\nContinue?", "OK", "Cancel") ~= "ok" then
return
end
assign_sortkeys(root, 100) -- start from 100 so that alphabetical order and numerical order coincide
root.RequireActionSortKeys = true
ObjModified(self)
ObjModified(root)
end
----- XActionsHost
DefineClass.XActionsHost = {
__parents = { "XContextWindow", "XHoldButton" },
properties = {
{ category = "Actions", id = "ActionsMode", editor = "text", default = "", },
{ category = "Actions", id = "Translate", editor = "bool", default = false, },
{ category = "Actions", id = "HostInParent", editor = "bool", default = false, },
},
actions = false,
shortcut_to_actions = false,
menubar_actions = false,
toolbar_actions = false,
action_hold_buttons = false,
dirty_actions_order = false,
dirty_menubars = false,
dirty_toolbars = false,
dirty_shortcuts = false,
}
function XActionsHost:Init()
self.actions = self.actions or {}
self.shortcut_to_actions = {}
self.menubar_actions = {}
self.toolbar_actions = {}
self.dirty_menubars = {}
self.dirty_toolbars = {}
self.dirty_shortcuts = {}
-- register already created actions in the host
for _, action in ipairs(self.actions) do
action:RegisterInHost(self)
end
end
function XActionsHost:ClearActions()
if self.HostInParent then
local host = GetActionsHost(self.parent)
if host then
for _, action in ipairs(host and self.actions) do
host:RemoveAction(action)
end
end
end
table.clear(self.actions)
table.clear(self.shortcut_to_actions)
table.clear(self.menubar_actions)
table.clear(self.toolbar_actions)
end
function XActionsHost:Done()
self:ClearActions()
end
function XActionsHost:ActionsUpdated()
if not self:GetThread("UpdateActionViews") then
self:CreateThread("UpdateActionViews", self.UpdateActionViews, self, self)
end
end
function XActionsHost:SetActionsMode(mode)
if self.ActionsMode ~= mode then
self.ActionsMode = mode
self:ActionsUpdated()
end
end
function XActionsHost:InvalidateActionSortKey(action)
self.dirty_actions_order = true
if action.ActionMenubar ~= "" then
self.dirty_menubars[action.ActionMenubar] = true
end
if action.ActionToolbar ~= "" then
self.dirty_toolbars[action.ActionToolbar] = true
end
if action.ActionShortcut ~= "" then
self.dirty_shortcuts[action.ActionShortcut] = true
end
if action.ActionShortcut2 ~= "" then
self.dirty_shortcuts[action.ActionShortcut2] = true
end
if action.ActionGamepad ~= "" then
self.dirty_shortcuts[action.ActionGamepad] = true
end
end
local function sort_actions(actions)
table.stable_sort(actions, function(a,b)
return a.ActionSortKey < b.ActionSortKey
end)
end
function XActionsHost:GetActions()
if self.dirty_actions_order then
sort_actions(self.actions)
self.dirty_actions_order = nil
end
return self.actions
end
function XActionsHost:GetMenubarActions(menubar)
if self.dirty_menubars[menubar] then
sort_actions(self.menubar_actions[menubar])
self.dirty_menubars[menubar] = nil
end
return self.menubar_actions[menubar]
end
function XActionsHost:GetToolbarActions(toolbar)
if self.dirty_toolbars[toolbar] then
sort_actions(self.toolbar_actions[toolbar])
self.dirty_toolbars[toolbar] = nil
end
return self.toolbar_actions[toolbar]
end
function XActionsHost:GetShortcutActions(shortcut)
if self.dirty_shortcuts[shortcut] then
sort_actions(self.shortcut_to_actions[shortcut])
self.dirty_shortcuts[shortcut] = nil
end
return self.shortcut_to_actions[shortcut]
end
function XActionsHost:CallHostParents(func, ...)
self[func](self, ...)
if self.HostInParent then
local host = GetActionsHost(self.parent)
if host then
host:CallHostParents(func, ...)
return
end
end
end
local function add_sorted(actions, action)
actions = actions or {}
local i = 1
local key = action.ActionSortKey
local skip_add
while i <= #actions and actions[i].ActionSortKey <= key do
if actions[i] == action then
-- don't duplicate actions
skip_add = true
break
end
i = i + 1
end
if not skip_add then
table.insert(actions, i, action)
end
return actions
end
function XActionsHost:AddShortcutToAction(action, shortcut)
if (shortcut or "") == "" then return end
-- insert in list based on ActionSortKey
self.shortcut_to_actions[shortcut] = add_sorted(self.shortcut_to_actions[shortcut], action)
end
function XActionsHost:RemoveShortcutToAction(action, shortcut)
if (shortcut or "") == "" then return end
local actions = self.shortcut_to_actions[shortcut]
if not actions then return end
table.remove_value(actions, action)
end
function XActionsHost:AddMenubarAction(action)
local menubar = action.ActionMenubar
if (menubar or "") == "" then return end
self.menubar_actions[menubar] = add_sorted(self.menubar_actions[menubar], action)
end
function XActionsHost:RemoveMenubarAction(action)
table.remove_entry(self.menubar_actions[action.ActionMenubar], action)
end
function XActionsHost:AddToolbarAction(action)
local toolbar = action.ActionToolbar
if (toolbar or "") == "" then return end
self.toolbar_actions[toolbar] = add_sorted(self.toolbar_actions[toolbar], action)
end
function XActionsHost:RemoveToolbarAction(action)
table.remove_entry(self.toolbar_actions[action.ActionToolbar], action)
end
function XActionsHost:_InternalAddAction(action, replace_matching_id)
local actions = self.actions
local key = action.ActionSortKey
local old_idx = replace_matching_id and self:RemoveAction(self:ActionById(action.ActionId))
-- is the new action still on a correct place at the old index?
if old_idx and (old_idx == 1 or actions[old_idx - 1].ActionSortKey <= key) and
(old_idx > #actions or actions[old_idx].ActionSortKey >= key) then
table.insert(actions, old_idx, action)
else
-- insert in list based on ActionSortKey
add_sorted(actions, action)
end
-- add action to menubar and toolbar mappings
self:AddMenubarAction(action)
self:AddToolbarAction(action)
-- add action to shortcuts mapping
self:AddShortcutToAction(action, action.ActionShortcut)
self:AddShortcutToAction(action, action.ActionShortcut2)
self:AddShortcutToAction(action, action.ActionGamepad)
if self.HostInParent then
local host = GetActionsHost(self.parent)
if host then
host:_InternalAddAction(action, replace_matching_id)
return
end
end
self:ActionsUpdated()
--assert(action.ActionName == "" or (IsT(action.ActionName) or false) == self.Translate)
end
function XActionsHost:RemoveAction(action)
if not action then return end
local actions = self.actions
local idx = table.remove_entry(self.actions, action)
-- remove action from menubar and toolbar mappings
self:RemoveMenubarAction(action)
self:RemoveToolbarAction(action)
-- remove action from shortcuts mapping
self:RemoveShortcutToAction(action, action.ActionShortcut)
self:RemoveShortcutToAction(action, action.ActionShortcut2)
self:RemoveShortcutToAction(action, action.ActionGamepad)
if self.HostInParent then
local host = GetActionsHost(self.parent)
if host then
host:RemoveAction(action)
return
end
end
self:ActionsUpdated()
return idx
end
function XActionsHost:SetHostInParent(host_in_parent)
if self.HostInParent == host_in_parent then return end
self.HostInParent = host_in_parent
local host = GetActionsHost(self.parent)
if host then
for _, action in ipairs(self.actions) do
if host_in_parent then
host:_InternalAddAction(action)
else
host:RemoveAction(action)
end
end
end
end
function XActionsHost:ActionsSanityCheck()
--[[local ids, shortcuts = {}, {}
for _, action in ipairs(self.actions) do
if action.ActionId ~= "" then
assert(not ids[action.ActionId], string.format("Conflicting action Id %s", action.ActionId))
ids[action.ActionId] = action
end
local mode = action.ActionMode
if not shortcuts[mode] then
shortcuts[mode] = {}
end
local mode_actions = shortcuts[mode]
if action.ActionShortcut ~= "" then
local other = mode_actions[action.ActionShortcut]
assert(not other, string.format("Conflicting shortcut %s (between actions %s & %s)", action.ActionShortcut, other and other.ActionId or "", action.ActionId))
mode_actions[action.ActionShortcut] = action
end
if action.ActionShortcut2 ~= "" then
local other = mode_actions[action.ActionShortcut2]
assert(not other, string.format("Conflicting shortcut %s (between actions %s & %s)", action.ActionShortcut2, other and other.ActionId or "", action.ActionId))
mode_actions[action.ActionShortcut2] = action
end
end]]
end
function XActionsHost:UpdateActionViews(win)
if Platform.developer then
self:ActionsSanityCheck()
end
for _, win in ipairs(win) do
if IsKindOf(win, "XActionsView") then
win:OnUpdateActions()
end
if not IsKindOf(win, "XActionsHost") or win.HostInParent then
self:UpdateActionViews(win)
end
end
end
function XActionsHost:ShowActionBar(bShow)
local action_bar = self:HasMember("idActionBar") and self.idActionBar
if action_bar then
action_bar:SetVisible(bShow)
end
end
function XActionsHost:FilterAction(action, action_context)
if not action_context then
return action:EnabledInMode(self.ActionsMode) and self:ActionState(action) ~= "hidden"
end
for _, context in ipairs(action.ActionContexts) do
if context == action_context and self:ActionState(action) ~= "hidden" then
return true
end
end
return false
end
function XActionsHost:ActionState(action)
local action_id = action.ActionId
if action.OnActionEffect == "popup" and action.OnAction == XAction.OnAction and
not self:HasMenubarActions(action_id) and not self:HasToolbarActions(action_id) then
return "hidden" -- hide menu entries with no children actions, that would result in an empty popup
end
return action:ActionState(self)
end
function XActionsHost:HasMenubarActions(action_id)
return next(self.menubar_actions[action_id])
end
function XActionsHost:HasToolbarActions(action_id)
return next(self.toolbar_actions[action_id])
end
function XActionsHost:OnAction(action, ctrl, ...)
local hasFx = ctrl and ctrl.FXPress
local ret = action:OnAction(self, ctrl, ...)
if #(action.FXPress or "") ~= 0 and not hasFx then PlayFX(action.FXPress, "start", action) end
if action.ActionToggle then
self:ActionsUpdated()
end
Msg("XActionActivated", self, action, ctrl, ...)
return ret
end
function XActionsHost:ActionById(id)
return table.find_value(self.actions, "ActionId", id)
end
function XActionsHost:IsActionShortcut(id, shortcut)
local action = self:ActionById(id)
if not action then return end
return action.ActionShortcut == shortcut or action.ActionShortcut2 == shortcut or action.ActionGamepad == shortcut
end
function XActionsHost:ActionByShortcut(shortcut, input, controller_id, repeated, ...)
local found
for _, action in ipairs(self:GetShortcutActions(shortcut)) do
if (not action.IgnoreRepeated or not repeated) then
if self:FilterAction(action) then
local state = action:ActionState(self)
if state ~= "disabled" and state ~= "hidden" then
found = action
break
end
end
end
end
return found
end
function XActionsHost:GamepadHoldActionByShortcut(shortcut)
local found
for _, action in ipairs(self:GetShortcutActions(shortcut)) do
if action.ActionGamepadHold and self:FilterAction(action) then
local state = action:ActionState(self)
if state ~= "disabled" and state ~= "hidden" then
found = action
break
end
end
end
return found
end
if FirstLoad then
KbdShortcutToRelation = {
["Tab"] = "next",
["Shift-Tab"] = "prev",
["Up"] = "up",
["Down"] = "down",
["Left"] = "left",
["Right"] = "right",
}
XShortcutToRelation = {
["LeftThumbLeft"] = "left", ["LeftThumbDownLeft"] = "left", ["LeftThumbUpLeft"] = "left",
["LeftThumbRight"] = "right", ["LeftThumbDownRight"] = "right", ["LeftThumbUpRight"] = "right",
["LeftThumbUp"] = "up",
["LeftThumbDown"] = "down",
["DPadLeft"] = "left",
["DPadRight"] = "right",
["DPadUp"] = "up",
["DPadDown"] = "down",
}
end
function XActionsHost:OnHoldDown(pt, button)
local action = self:GamepadHoldActionByShortcut(button)
action:OnAction(self,button)
end
function XActionsHost:OnHoldButtonTick(i, shortcut)
local action = self:GamepadHoldActionByShortcut(shortcut)
if not action then
return
end
local ctrl = self.action_hold_buttons and self.action_hold_buttons[action.ActionId]
if ctrl and ctrl:HasMember("OnHoldButtonTick") then
ctrl:OnHoldButtonTick(i)
else
XHoldButton.OnHoldButtonTick(self, i, shortcut)
end
end
function XActionsHost:OnXButtonRepeat(shortcut, controller_id,...)
if self.HostInParent then return end
if not RepeatableXButtons[shortcut] then
local found = self:GamepadHoldActionByShortcut(shortcut)
if found then
XHoldButton.OnHoldButtonRepeat(self,shortcut, controller_id)
return "break"
end
end
end
function XActionsHost:OnShortcut(shortcut, source, controller_id, ...)
if self.HostInParent then return end
local found
if source=="gamepad" then
if shortcut:starts_with("-") then
local org_shortcut = shortcut:gsub("-", "")
found = self:GamepadHoldActionByShortcut(org_shortcut)
if found then
found = XHoldButton.OnHoldButtonUp(self, org_shortcut, controller_id)
if found then
return "break"
end
end
elseif shortcut:starts_with("+") then
local org_shortcut = shortcut:gsub("+", "")
found = self:GamepadHoldActionByShortcut(org_shortcut)
if found then
XHoldButton.OnHoldButtonDown(self,org_shortcut, controller_id)
end
else
found = self:GamepadHoldActionByShortcut(shortcut)
if found then
XHoldButton.OnHoldButtonRepeat(self,shortcut, controller_id)
end
end
end
local action = not found and self:ActionByShortcut(shortcut, source, controller_id, ...)
if action then
self:OnAction(action, source, controller_id, ...)
return "break"
end
if source ~= "mouse" then
local relation = (source == "keyboard") and KbdShortcutToRelation[shortcut] or XShortcutToRelation[shortcut]
if relation then
local focus = self.desktop and self.desktop.keyboard_focus
local order = focus and focus:IsWithin(self) and focus:GetFocusOrder() or point(0, 0)
focus = self:GetRelativeFocus(order, relation)
if focus then
-- the thread prevents Tab keys to be processed in OnKbdChar of the new focus
CreateRealTimeThread(function()
if focus.window_state ~= "destroying" then
focus:SetFocus()
if source == "gamepad" and RolloverControl ~= focus then
XCreateRolloverWindow(focus, true)
end
end
end)
return "break"
end
end
end
end
function XActionsHost:OpenContextMenu(action_context, anchor_pt)
if not action_context or not anchor_pt or action_context == "" then return end
local menu = XPopupMenu:new({
ActionContextEntries = action_context,
Anchor = anchor_pt,
AnchorType = "mouse",
MaxItems = 12,
GetActionsHost = function() return self end,
popup_parent = self,
}, terminal.desktop)
menu:Open()
return menu
end
function XActionsHost:OpenPopupMenu(menubar_id, anchor_pt)
local menu = XPopupMenu:new({
MenuEntries = menubar_id,
Anchor = anchor_pt,
AnchorType = "mouse",
MaxItems = 12,
GetActionsHost = function() return self end,
popup_parent = self,
}, terminal.desktop)
menu:Open()
return menu
end
----- XActionsView
DefineClass.XActionsView = {
__parents = { "XContextWindow" },
properties = {
{ category = "General", id = "HideWithoutActions", name = "Hide without actions", editor = "bool", default = false },
}
}
function XActionsView:GetActionsHost()
return GetActionsHost(self, true)
end
function XActionsView:Open(...)
XContextWindow.Open(self, ...)
self:OnUpdateActions()
end
function XActionsView:PopupAction(action_id, host, source)
assert(false)
end
function XActionsView:OnUpdateActions()
local host = self:GetActionsHost()
if not host or self.window_state == "new" then return end
self:RebuildActions(host)
if self.HideWithoutActions then
self:SetVisible(#self > 0)
end
Msg("XWindowRecreated", self)
end
function XActionsView:RebuildActions(host)
end
----- globals
function GetActionsHost(win, final)
while win and (not win:IsKindOf("XActionsHost") or win.HostInParent and final) do
win = win.parent
if final and win and win:IsKindOf("XActionsView") then
return win:GetActionsHost()
end
end
return win
end
function EnabledInModes(givenModes, modes)
if givenModes == "" or modes == "" or modes == givenModes then return true end
for givenMode in string.gmatch(givenModes, "([%w%-_]+)") do
for mode in string.gmatch(modes, "([%w%-_]+)") do
if givenMode == mode or givenMode == "ForwardToC" or mode == "ForwardToC" then
return true
end
end
end
return false
end