Rewrite keybinds module to function more like vanilla keybinds (#33)

* Rewrite keybinding module to work like vanilla keybinds

* Treat left and right modifier keys separately

* Fix minor crash bug

* Don't close views if actively watching for keys
This commit is contained in:
Aussiemon 2023-04-12 15:07:04 -06:00 committed by GitHub
parent c617fc69b0
commit 124002bb1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 350 additions and 374 deletions

View file

@ -2,164 +2,16 @@ local dmf = get_mod("DMF")
local InputUtils = require("scripts/managers/input/input_utils")
local PRIMARY_BINDABLE_KEYS = {
["keyboard"] = {
[8] = {"Backspace", "backspace"},
[9] = {"Tab", "tab"},
[13] = {"Enter", "enter"},
[20] = {"Caps Lock", "caps lock"},
[32] = {"Space", "space"},
[33] = {"Page Up", "page up"},
[34] = {"Page Down", "page down"},
[35] = {"End", "end"},
[36] = {"Home", "home"},
[37] = {"Left", "left"},
[38] = {"Up", "up"},
[39] = {"Right", "right"},
[40] = {"Down", "down"},
[45] = {"Insert", "insert"},
[46] = {"Delete", "delete"},
[48] = {"0", "0"},
[49] = {"1", "1"},
[50] = {"2", "2"},
[51] = {"3", "3"},
[52] = {"4", "4"},
[53] = {"5", "5"},
[54] = {"6", "6"},
[55] = {"7", "7"},
[56] = {"8", "8"},
[57] = {"9", "9"},
[65] = {"A", "a"},
[66] = {"B", "b"},
[67] = {"C", "c"},
[68] = {"D", "d"},
[69] = {"E", "e"},
[70] = {"F", "f"},
[71] = {"G", "g"},
[72] = {"H", "h"},
[73] = {"I", "i"},
[74] = {"J", "j"},
[75] = {"K", "k"},
[76] = {"L", "l"},
[77] = {"M", "m"},
[78] = {"N", "n"},
[79] = {"O", "o"},
[80] = {"P", "p"},
[81] = {"Q", "q"},
[82] = {"R", "r"},
[83] = {"S", "s"},
[84] = {"T", "t"},
[85] = {"U", "u"},
[86] = {"V", "v"},
[87] = {"W", "w"},
[88] = {"X", "x"},
[89] = {"Y", "y"},
[90] = {"Z", "z"},
[91] = {"Win", "win"},
[92] = {"RWin", "right win"},
[96] = {"Num 0", "numpad 0"},
[97] = {"Num 1", "numpad 1"},
[98] = {"Num 2", "numpad 2"},
[99] = {"Num 3", "numpad 3"},
[100] = {"Num 4", "numpad 4"},
[101] = {"Num 5", "numpad 5"},
[102] = {"Num 6", "numpad 6"},
[103] = {"Num 7", "numpad 7"},
[104] = {"Num 8", "numpad 8"},
[105] = {"Num 9", "numpad 9"},
[106] = {"Num *", "numpad *"},
[107] = {"Num +", "numpad +"},
[109] = {"Num -", "numpad -"},
[110] = {"Num .", "numpad ."},
[111] = {"Num /", "numpad /"},
[112] = {"F1", "f1"},
[113] = {"F2", "f2"},
[114] = {"F3", "f3"},
[115] = {"F4", "f4"},
[116] = {"F5", "f5"},
[117] = {"F6", "f6"},
[118] = {"F7", "f7"},
[119] = {"F8", "f8"},
[120] = {"F9", "f9"},
[121] = {"F10", "f10"},
[122] = {"F11", "f11"},
[123] = {"F12", "f12"},
[144] = {"Num Lock", "num lock"},
[145] = {"Scroll Lock", "scroll lock"},
[166] = {"Browser Back", "browser back"},
[167] = {"Browser Forward", "browser forward"},
[168] = {"Browser Refresh", "browser refresh"},
[169] = {"Browser Stop", "browser stop"},
[170] = {"Browser Search", "browser search"},
[171] = {"Browser Favorites", "browser favorites"},
[172] = {"Browser Home", "browser home"},
[173] = {"Volume Mute", "volume mute"},
[174] = {"Volume Down", "volume down"},
[175] = {"Volume Up", "volume up"},
[176] = {"Next Track", "next track"},
[177] = {"Previous Track", "previous track"},
[178] = {"Stop", "stop"},
[179] = {"Play/Pause", "play pause"},
[180] = {"Mail", "mail"},
[181] = {"Media", "media"},
[182] = {"Start Application 1", "start app 1"},
[183] = {"Start Application 2", "start app 2"},
[186] = {";", ";"},
[187] = {"=", "="},
[188] = {",", ","},
[189] = {"-", "-"},
[190] = {".", "."},
[191] = {"/", "/"},
[192] = {"`", "`"},
[219] = {"[", "["},
[220] = {"\\", "\\"},
[221] = {"]", "]"},
[222] = {"'", "'"},
--?[226] = {"\", "oem_102 (> <)"},
[256] = {"Num Enter", "numpad enter"}
},
["mouse"] = {
[0] = {"Mouse Left", "mouse left"},
[1] = {"Mouse Right", "mouse right"},
[2] = {"Mouse Middle", "mouse middle"},
[3] = {"Mouse Extra 1", "mouse extra 1"},
[4] = {"Mouse Extra 2", "mouse extra 2"},
[10] = {"Mouse Wheel Up", "mouse wheel up"},
[11] = {"Mouse Wheel Down", "mouse wheel down"},
[12] = {"Mouse Wheel Left", "mouse wheel left"},
[13] = {"Mouse Wheel Right", "mouse wheel right"}
},
--[[ -- will work on this if it will be needed
["gamepad"] = {
[0] = {"", "d_up"},
[1] = {"", "d_down"},
[2] = {"", "d_left"},
[3] = {"", "d_right"},
[4] = {"", "start"},
[5] = {"", "back"},
[6] = {"", "left_thumb"},
[7] = {"", "right_thumb"},
[8] = {"", "left_shoulder"},
[9] = {"", "right_shoulder"},
[10] = {"", "left_trigger"},
[11] = {"", "right_trigger"},
[12] = {"", "a"},
[13] = {"", "b"},
[14] = {"", "x"},
[15] = {"", "y"},
}]]
}
local MODIFIER_KEYS = {
["left shift"] = {160, "shift", "keyboard", 161},
["right shift"] = {160, "shift", "keyboard", 161},
["shift"] = {160, "shift", "keyboard", 161},
["left ctrl"] = {162, "ctrl", "keyboard", 163},
["right ctrl"] = {162, "ctrl", "keyboard", 163},
["ctrl"] = {162, "ctrl", "keyboard", 163},
["left alt"] = {164, "alt", "keyboard", 165},
["right alt"] = {164, "alt", "keyboard", 165},
["alt"] = {164, "alt", "keyboard", 165},
["keyboard_left shift"] = {160, "shift", "keyboard", 161},
["keyboard_right shift"] = {160, "shift", "keyboard", 161},
["keyboard_shift"] = {160, "shift", "keyboard", 161},
["keyboard_left ctrl"] = {162, "ctrl", "keyboard", 163},
["keyboard_right ctrl"] = {162, "ctrl", "keyboard", 163},
["keyboard_ctrl"] = {162, "ctrl", "keyboard", 163},
["keyboard_left alt"] = {164, "alt", "keyboard", 165},
["keyboard_right alt"] = {164, "alt", "keyboard", 165},
["keyboard_alt"] = {164, "alt", "keyboard", 165},
}
-- Both are treated equally in keybinds, but these global keys aren't localized
@ -169,43 +21,8 @@ local MODIFIER_KEYS_LOC_ALIAS = {
keyboard_shift = "keyboard_left shift",
}
local KEYS_INFO = {}
-- Populate KEYS_INFO: Index, readable name, device name
for input_device_name, input_device_keys in pairs(PRIMARY_BINDABLE_KEYS) do
for key_index, key_info in pairs(input_device_keys) do
KEYS_INFO[key_info[2]] = {key_index, key_info[1], input_device_name}
end
end
for key_id, key_data in pairs(MODIFIER_KEYS) do
KEYS_INFO[key_id] = key_data
end
-- Can't use 'Device.released' because it will break keybinds if button is released when game window is not active.
local CHECK_INPUT_FUNCTIONS = {
keyboard = {
PRESSED = function(key_id) return Keyboard.pressed(KEYS_INFO[key_id][1]) end,
RELEASED = function(key_id) return Keyboard.button(KEYS_INFO[key_id][1]) == 0 end
},
mouse = {
PRESSED = function(key_id) return Mouse.pressed(KEYS_INFO[key_id][1]) end,
RELEASED = function(key_id) return Mouse.button(KEYS_INFO[key_id][1]) == 0 end
}
}
local KEYS_INPUT_FUNCTIONS = {}
for key_id, key_data in pairs(KEYS_INFO) do
KEYS_INPUT_FUNCTIONS[key_id] = {
check_pressed = CHECK_INPUT_FUNCTIONS[key_data[3]].PRESSED,
check_released = CHECK_INPUT_FUNCTIONS[key_data[3]].RELEASED
}
end
local _raw_keybinds_data = {}
local _keybinds = {}
local _pressed_key
local ERRORS = {
PREFIX = {
@ -216,12 +33,17 @@ local ERRORS = {
}
}
local SUPPORTED_DEVICES = {
"keyboard",
"mouse",
}
-- #####################################################################################################################
-- ##### Local functions ###############################################################################################
-- #####################################################################################################################
-- @TODO: Link this input service to the player's input service and find some way to see if it's blocked
local function is_dmf_input_service_active()
-- @TODO: Implement check for active DMF input service
return true
end
@ -253,118 +75,260 @@ local function perform_keybind_action(data, is_pressed)
end
-- #####################################################################################################################
-- ##### Input service functions #######################################################################################
-- #####################################################################################################################
local function is_modifier_pressed(modifier)
return (Keyboard.button(MODIFIER_KEYS[modifier][1]) + Keyboard.button(MODIFIER_KEYS[modifier][4])) > 0
end
local function default_boolean()
return false
end
local function default_vector3()
return Vector3(0, 0, 0)
end
local function default_float()
return 0
end
local function boolean_combine(value_one, value_two)
return value_one or value_two
end
local function vector3_combine(value_one, value_two)
if Vector3.length(value_two) < Vector3.length(value_one) then
return value_one
else
return value_two
end
end
local function float_combine(value_one, value_two)
return math.max(value_one, value_two)
end
local ACTION_TYPES = {
pressed = {
device_func = "pressed",
type = "boolean",
default_device_func = default_boolean,
combine_func = boolean_combine
},
held = {
device_func = "held",
type = "boolean",
default_device_func = default_boolean,
combine_func = boolean_combine
},
released = {
device_func = "released",
type = "boolean",
default_device_func = default_boolean,
combine_func = boolean_combine
},
axis = {
device_func = "axis",
type = "vector3",
default_device_func = default_vector3,
combine_func = vector3_combine
},
button = {
device_func = "button",
type = "float",
default_device_func = default_float,
combine_func = float_combine
}
}
local function _enabler_func(cb, action_type, enablers)
for _, enabler in ipairs(enablers) do
if not enabler.device:held(enabler.index) then
return ACTION_TYPES[action_type].default_device_func()
end
end
return cb()
end
local function _disabler_func(cb, action_type, disablers)
for _, disabler in ipairs(disablers) do
if disabler.device:held(disabler.index) then
return ACTION_TYPES[action_type].default_device_func()
end
end
return cb()
end
local function get_corresponding_device(key_id)
for _, device_type in pairs(SUPPORTED_DEVICES) do
local device = Managers.input:_find_active_device(device_type)
if device then
local index = device:button_index(key_id)
if index then
return {
device = device,
index = index,
key_id = key_id,
}
end
index = device:axis_index(key_id)
if index then
return {
device = device,
index = index,
key_id = key_id,
}
end
end
end
end
local function get_key_info(keybind)
local main = keybind.main
local e = keybind.enablers
local d = keybind.disablers
local info = get_corresponding_device(main)
if info then
if #e > 0 then
local enablers = {}
for _, key in ipairs(e) do
local enabler = get_corresponding_device(key)
if enabler then
enablers[#enablers + 1] = enabler
end
end
info.enablers = enablers
end
if #d > 0 then
local disablers = {}
for _, key in ipairs(d) do
local disabler = get_corresponding_device(key)
if disabler then
disablers[#disablers + 1] = disabler
end
end
info.disablers = disablers
end
end
return info
end
local function create_eval_func(keybind)
local info = get_key_info(keybind)
if info then
local eval_func
if keybind.trigger == "held" then
eval_func = callback(info.device, keybind.trigger, info.index)
else
local function func(device, trigger, index)
return device[trigger](index)
end
eval_func = callback(func, info.device:raw_device(), keybind.trigger, info.index)
end
if info.enablers then
eval_func = callback(_enabler_func, eval_func, keybind.trigger, info.enablers)
end
if info.disablers then
eval_func = callback(_disabler_func, eval_func, keybind.trigger, info.disablers)
end
return eval_func
end
end
-- #####################################################################################################################
-- ##### DMF internal functions and variables ##########################################################################
-- #####################################################################################################################
-- Checks for pressed and released keybinds, performs keybind actions.
-- * Checks for both right and left key modifiers (ctrl, alt, shift).
-- * If some keybind is pressed, won't check for other keybinds until this keybind is released.
-- * If several mods bound the same keys, keybind action will be performed for all of them, when keybind is pressed.
-- * Keybind is considered released, when its primary key is released.
-- * Right and left key modifiers (ctrl, alt, shift) are checked separately.
-- * If several mods bound the same keys, keybind action will be performed for all of them when pressed.
-- * Keybind is considered released when it was previously pressed and is no longer.
function dmf.check_keybinds()
local pressed_modifiers = {
ctrl = is_modifier_pressed("ctrl"),
alt = is_modifier_pressed("alt"),
shift = is_modifier_pressed("shift")
}
if not _pressed_key then
for primary_key, keybinds_data in pairs(_keybinds) do
if keybinds_data.check_pressed(primary_key) then
for _, keybind_data in ipairs(keybinds_data) do
-- For every keybind
for _, keybind_data in ipairs(_keybinds) do
local all_pressed = true
-- If the keybind is pressed
if keybind_data.eval_func() then
-- Check that every enabler key is pressed
for enabler, _ in pairs(keybind_data.enablers) do
if MODIFIER_KEYS[enabler] then
if not pressed_modifiers[MODIFIER_KEYS[enabler][2]] then
all_pressed = false
break
end
elseif KEYS_INPUT_FUNCTIONS[enabler].check_released(enabler) then
all_pressed = false
break
end
end
-- Peform the keybind action once
if (not keybind_data.pressed) and perform_keybind_action(keybind_data, true) then
-- Check that no modifier keys are pressed that shouldn't be
if all_pressed and (
(not keybind_data.ctrl and pressed_modifiers.ctrl) or
(not keybind_data.alt and pressed_modifiers.alt) or
(not keybind_data.shift and pressed_modifiers.shift)
)
then
all_pressed = false
end
-- Peform the keybind action if everything validates
if all_pressed then
if perform_keybind_action(keybind_data, true) then
-- Queue the release action if applicable
if keybind_data.trigger == "held" then
keybind_data.release_action = true
end
_pressed_key = primary_key
end
end
end
end
end
-- Prevent a repeat action
keybind_data.pressed = true
end
if _pressed_key then
if _keybinds[_pressed_key].check_released(_pressed_key) then
for _, keybind_data in ipairs(_keybinds[_pressed_key]) do
-- If the keybind was previously but is no longer pressed
elseif keybind_data.pressed then
-- Reactivate the keybind
keybind_data.pressed = nil
-- Play the release action if applicable
if keybind_data.release_action then
perform_keybind_action(keybind_data, false)
keybind_data.release_action = nil
end
end
_pressed_key = nil
end
end
end
-- Converts managable (raw) table of keybinds data to the table designed for the function checking for pressed and
-- released keybinds. After initial call requires to be called every time some keybind is added/removed.
-- Converts manageable (raw) table of keybinds data to a table of callbacks for checking pressed and
-- released keybinds. After initial call it must be called every time some keybind is added/removed.
function dmf.generate_keybinds()
_keybinds = {}
for mod, mod_keybinds in pairs(_raw_keybinds_data) do
for _, raw_keybind_data in pairs(mod_keybinds) do
local keys = raw_keybind_data.keys
local primary_key = keys[1]
local enabler_keys = {}
for i = 2, #keys do
enabler_keys[keys[i]] = true
end
local keybind_data = {
mod = mod,
global = raw_keybind_data.global,
trigger = raw_keybind_data.trigger,
type = raw_keybind_data.type,
enablers = enabler_keys,
ctrl = enabler_keys["ctrl"] or enabler_keys["left ctrl"] or enabler_keys["right ctrl"],
alt = enabler_keys["alt"] or enabler_keys["left alt"] or enabler_keys["right alt"],
shift = enabler_keys["shift"] or enabler_keys["left shift"] or enabler_keys["right shift"],
eval_func = create_eval_func(raw_keybind_data),
function_name = raw_keybind_data.function_name,
view_name = raw_keybind_data.view_name
}
_keybinds[primary_key] = _keybinds[primary_key] or {
check_pressed = KEYS_INPUT_FUNCTIONS[primary_key].check_pressed,
check_released = KEYS_INPUT_FUNCTIONS[primary_key].check_released
}
table.insert(_keybinds[primary_key], keybind_data)
if keybind_data.eval_func then
table.insert(_keybinds, keybind_data)
end
end
end
end
@ -372,7 +336,7 @@ end
-- Adds/removes keybinds.
function dmf.add_mod_keybind(mod, setting_id, raw_keybind_data)
if #raw_keybind_data.keys > 0 then
if raw_keybind_data.main then
_raw_keybinds_data[mod] = _raw_keybinds_data[mod] or {}
_raw_keybinds_data[mod][setting_id] = raw_keybind_data
elseif _raw_keybinds_data[mod] and _raw_keybinds_data[mod][setting_id] then
@ -388,53 +352,32 @@ end
-- Creates DMF input service. It is required to know when non-global keybinds can be triggered.
-- (Called every time a level is loaded, or on mods reload)
function dmf.create_keybinds_input_service()
-- @TODO: Link this input service to the player's input service and find some way to see if it's blocked
--[[
-- To create the DMF input service in Darktide
local input_manager = Managers.input
local service_type = "DMF"
input_manager:add_setting(service_type, aliases, raw_key_table, filter_table, default_devices)
input_manager:get_input_service(service_type)
--]]
function dmf.create_keybinds_input_service()
-- -- To create the DMF input service in Darktide
-- local input_manager = Managers.input
-- local service_type = "DMF"
-- input_manager:add_setting(service_type, aliases, raw_key_table, filter_table, default_devices)
-- input_manager:get_input_service(service_type)
end
-- Converts key_index to readable key_id, which is used by DMF to identify keys.
-- (Used for capturing keybinds)
function dmf.get_key_id(device, key_index)
local key_info = PRIMARY_BINDABLE_KEYS[device][key_index]
return key_info and key_info[2]
end
-- Simply tells if key with key_id can be binded as primary key.
-- (Used for verifying keybind widgets)
-- @TODO: So far it seems like any key can be a primary keybind, but is this actually true?
function dmf.can_bind_as_primary_key(key_id)
return KEYS_INFO[key_id] and not MODIFIER_KEYS[key_id]
return true
end
-- Builds string with readable keys' names to look like e.g. "Ctrl+Alt+Shift+<Primary Key>".
-- (Used in keybind widget)
function dmf.build_keybind_string(keys)
local key_unassigned_string = Managers.localization:localize("loc_keybind_unassigned")
local keybind_result = dmf.keys_to_keybind_result(keys)
return keybind_result and InputUtils.localized_string_from_key_info(keybind_result) or key_unassigned_string
end
-- Translate key watch result to mod options keybind
-- (Used in keybind widget)
function dmf.keybind_result_to_keys(keybind_result)
-- Translate keywatch result to array of local key names
-- (Used in keybind widget for compatibility with legacy settings)
function dmf.keywatch_result_to_local_keys(keywatch_result)
local keys = {}
-- Get the local name of the main key
if keybind_result.main then
if keywatch_result.main then
local global_name = keybind_result.main
local device_type = InputUtils.key_device_type(global_name)
local local_name = InputUtils.local_key_name(global_name, device_type)
local global_name = keywatch_result.main
local local_name = InputUtils.local_key_name(global_name, InputUtils.key_device_type(global_name))
-- Check for a missing or unbindable primary key name
if not local_name or not dmf.can_bind_as_primary_key(local_name) then
@ -445,12 +388,9 @@ function dmf.keybind_result_to_keys(keybind_result)
end
-- Add the enablers keys as additional keys
if keybind_result.enablers then
for _, global_name in ipairs(keybind_result.enablers) do
local device_type = InputUtils.key_device_type(global_name)
local local_name = InputUtils.local_key_name(global_name, device_type)
if keywatch_result.enablers then
for _, global_name in ipairs(keywatch_result.enablers) do
local local_name = InputUtils.local_key_name(global_name, InputUtils.key_device_type(global_name))
keys[#keys + 1] = local_name
end
end
@ -459,21 +399,29 @@ function dmf.keybind_result_to_keys(keybind_result)
end
-- Translate mod options keybind to key watch result
-- (Used in keybind widget)
function dmf.keys_to_keybind_result(keys)
local keybind_result = {
-- Translate array of local key names to keywatch result
-- (Used in keybind widget for compatibility with legacy settings)
function dmf.local_keys_to_keywatch_result(keys)
local keywatch_result = {
enablers = {},
disablers = {}
}
if not keys or #keys == 0 then
if type(keys) ~= "table" or #keys == 0 then
return nil
end
if keys[1] then
local local_name = keys[1]
local global_name = KEYS_INFO[local_name] and InputUtils.local_to_global_name(local_name, KEYS_INFO[local_name][3])
local global_name
-- Check all supported devices for the global name
for _, device_type in ipairs(SUPPORTED_DEVICES) do
global_name = InputUtils.local_to_global_name(local_name, device_type)
if get_corresponding_device(global_name) then
break
end
end
-- End early if our main key doesn't exist, and return an empty result
if not global_name then
@ -483,23 +431,31 @@ function dmf.keys_to_keybind_result(keys)
if MODIFIER_KEYS_LOC_ALIAS[global_name] then
global_name = MODIFIER_KEYS_LOC_ALIAS[global_name]
end
keybind_result.main = global_name
keywatch_result.main = global_name
end
-- Add all remaining keys to the enablers list
for i = 2, #keys do
local local_name = keys[i]
local global_name = KEYS_INFO[local_name] and InputUtils.local_to_global_name(local_name, KEYS_INFO[local_name][3])
local global_name
-- Check all supported devices for the global name
for _, device_type in ipairs(SUPPORTED_DEVICES) do
global_name = InputUtils.local_to_global_name(local_name,device_type)
if get_corresponding_device(global_name) then
break
end
end
if global_name then
if MODIFIER_KEYS_LOC_ALIAS[global_name] then
global_name = MODIFIER_KEYS_LOC_ALIAS[global_name]
end
keybind_result.enablers[#keybind_result.enablers + 1] = global_name
keywatch_result.enablers[#keywatch_result.enablers + 1] = global_name
end
end
return keybind_result
return keywatch_result
end
-- #####################################################################################################################

View file

@ -534,6 +534,8 @@ local function initialize_default_settings_and_keybinds(mod, initialized_widgets
mod:set(data.setting_id, data.default_value)
end
if data.type == "keybind" then
local keywatch_result = dmf.local_keys_to_keywatch_result(mod:get(data.setting_id))
if keywatch_result and keywatch_result.main then
dmf.add_mod_keybind(
mod,
data.setting_id,
@ -541,7 +543,9 @@ local function initialize_default_settings_and_keybinds(mod, initialized_widgets
global = data.keybind_global,
trigger = data.keybind_trigger,
type = data.keybind_type,
keys = mod:get(data.setting_id),
main = keywatch_result.main,
enablers = keywatch_result.enablers,
disablers = keywatch_result.disablers,
function_name = data.function_name,
view_name = data.view_name
}
@ -549,6 +553,7 @@ local function initialize_default_settings_and_keybinds(mod, initialized_widgets
end
end
end
end
-- #####################################################################################################################
-- ##### DMF internal functions and variables ##########################################################################

View file

@ -87,7 +87,7 @@ function dmf.safe_call_dofile(mod, error_prefix_data, file_path)
show_error(mod, error_prefix_data, "file path should be a string.")
return false
end
return dmf.safe_call(mod, error_prefix_data, dofile, mod, file_path)
return dmf.safe_call(mod, error_prefix_data, dofile, file_path)
end

View file

@ -7,6 +7,8 @@ local _custom_views_data = {}
local _ingame_ui
local _loaded_views = {}
local _key_watch = false
local ERRORS = {
THROWABLE = {
-- inject_view:
@ -312,13 +314,13 @@ end
-- Track the creation of the view loader
dmf:hook_safe(ViewLoader, "init", function()
dmf:hook_safe(CLASS.ViewLoader, "init", function()
_custom_view_persistent_data.loader_initialized = true
end)
-- Track the loading of views, set the loader flag if class selection is reached
dmf:hook_safe(UIManager, "load_view", function(self, view_name)
dmf:hook_safe(CLASS.UIManager, "load_view", function(self, view_name)
if view_name == "class_selection_view" then
_custom_view_persistent_data.loader_initialized = true
end
@ -326,13 +328,13 @@ dmf:hook_safe(UIManager, "load_view", function(self, view_name)
end)
-- Track the unloading of views
dmf:hook_safe(UIManager, "unload_view", function(self, view_name)
dmf:hook_safe(CLASS.UIManager, "unload_view", function(self, view_name)
_loaded_views[view_name] = nil
end)
-- Store the view handler for later use and inject views
dmf:hook_safe(UIViewHandler, "init", function(self)
dmf:hook_safe(CLASS.UIViewHandler, "init", function(self)
_ingame_ui = self
for view_name, _ in pairs(_custom_views_data) do
if not dmf.safe_call_nrc(self, {ERRORS.PREFIX.ingameui_hook_injection, view_name}, inject_view, view_name) then
@ -341,6 +343,16 @@ dmf:hook_safe(UIViewHandler, "init", function(self)
end
end)
-- Track the start of key watches
dmf:hook_safe(CLASS.InputManager, "start_key_watch", function(self)
_key_watch = true
end)
-- Track the end of key watches
dmf:hook_safe(CLASS.InputManager, "stop_key_watch", function(self)
_key_watch = false
end)
-- #####################################################################################################################
-- ##### DMF internal functions and variables ##########################################################################
-- #####################################################################################################################
@ -368,8 +380,8 @@ function dmf.keybind_toggle_view(mod, view_name, keybind_transition_data, can_pe
-- If the view is open, this is a toggle close
if Managers.ui:view_active(view_name) then
-- Don't close the view if it's already closing
if not Managers.ui:is_view_closing(view_name) then
-- Don't close the view if it's already closing or we have an active key watch
if not Managers.ui:is_view_closing(view_name) and not _key_watch then
local force_close = true
Managers.ui:close_view(view_name, force_close)
end

View file

@ -228,7 +228,7 @@ DMFOptionsView._restart_popup_info = function (self)
close_on_pressed = true,
callback = callback(function ()
self._popup_id = nil
local view_name = "options_view"
local view_name = "dmf_options_view"
self._require_restart = false
Managers.ui:close_view(view_name)

View file

@ -6,11 +6,13 @@ local _type_template_map = {}
local _devices = {
"keyboard",
"mouse"
"mouse",
}
local _cancel_keys = {
"keyboard_esc"
}
local _reserved_keys = {}
local ERRORS = {
@ -249,8 +251,8 @@ _type_template_map["dropdown"] = create_dropdown_template
-- ######### Keybind #########
-- ###########################
local set_keybind = function (self, keybind_data, keys)
keybind_data.keys = keys
local set_keybind = function (self, keybind_data, keywatch_result)
keybind_data.keys = keywatch_result
local mod = get_mod(keybind_data.mod_name)
dmf.add_mod_keybind(
@ -260,18 +262,19 @@ local set_keybind = function (self, keybind_data, keys)
global = keybind_data.keybind_global,
trigger = keybind_data.keybind_trigger,
type = keybind_data.keybind_type,
keys = keybind_data.keys,
main = keywatch_result.main,
enablers = keywatch_result.enablers,
disablers = keywatch_result.disablers,
function_name = keybind_data.function_name,
view_name = keybind_data.view_name,
}
)
mod:set(keybind_data.setting_id, keybind_data.keys, true)
mod:set(keybind_data.setting_id, dmf.keywatch_result_to_local_keys(keywatch_result), true)
end
-- Create keybind template
local create_keybind_template = function (self, params)
local template = {
widget_type = "keybind",
service_type = "Ingame",
@ -287,8 +290,8 @@ local create_keybind_template = function (self, params)
indentation_level = params.depth,
mod_name = params.mod_name,
setting_id = params.setting_id,
keys = dmf.keys_to_keybind_result(params.keys),
default_value = dmf.keys_to_keybind_result(params.default_value) or {},
keys = dmf.local_keys_to_keywatch_result(params.keys),
default_value = dmf.local_keys_to_keywatch_result(params.default_value) or {},
on_activated = function (new_value, old_value)
@ -320,11 +323,11 @@ local create_keybind_template = function (self, params)
end
-- Get the keys of the new value
local keys = dmf.keybind_result_to_keys(new_value)
local keys = dmf.keywatch_result_to_local_keys(new_value)
-- Set the new keybind unless it would unbind the mod options menu
if keys and #keys > 0 or params.setting_id ~= "open_dmf_options" then
set_keybind(self, params, keys)
set_keybind(self, params, new_value)
return true
end
@ -332,10 +335,10 @@ local create_keybind_template = function (self, params)
end,
get_function = function (template)
local keys = get_mod(template.mod_name):get(template.setting_id)
local keybind_result = dmf.keys_to_keybind_result(keys)
local saved_keys = get_mod(template.mod_name):get(template.setting_id)
local keywatch_result = dmf.local_keys_to_keywatch_result(saved_keys)
return keybind_result
return keywatch_result
end,
}