Keybind and mod option improvements (#27)

* Fix category widget cutoff

* Fix keybind reset-to-default

* Fix multiple keybinds using the same set of keys

* Comment out unimplemented settings

* Reposition mod options widgets
This commit is contained in:
Aussiemon 2023-04-01 00:02:34 -06:00 committed by GitHub
parent 836d8e2f88
commit b13ef309af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 93 deletions

View file

@ -150,19 +150,23 @@ local PRIMARY_BINDABLE_KEYS = {
}]] }]]
} }
local OTHER_KEYS = { local MODIFIER_KEYS = {
-- modifier keys ["left shift"] = {160, "shift", "keyboard", 161},
["left shift"] = {160, "Shift", "keyboard", 161}, ["right shift"] = {160, "shift", "keyboard", 161},
["right shift"] = {160, "Shift", "keyboard", 161}, ["shift"] = {160, "shift", "keyboard", 161},
["shift"] = {160, "Shift", "keyboard", 161}, ["left ctrl"] = {162, "ctrl", "keyboard", 163},
["left ctrl"] = {162, "Ctrl", "keyboard", 163}, ["right ctrl"] = {162, "ctrl", "keyboard", 163},
["right ctrl"] = {162, "Ctrl", "keyboard", 163}, ["ctrl"] = {162, "ctrl", "keyboard", 163},
["ctrl"] = {162, "Ctrl", "keyboard", 163}, ["left alt"] = {164, "alt", "keyboard", 165},
["left alt"] = {164, "Alt", "keyboard", 165}, ["right alt"] = {164, "alt", "keyboard", 165},
["right alt"] = {164, "Alt", "keyboard", 165}, ["alt"] = {164, "alt", "keyboard", 165},
["alt"] = {164, "Alt", "keyboard", 165}, }
-- hack for 'dmf.build_keybind_string' function
["no_button"] = {-1, ""} -- Both are treated equally in keybinds, but these global keys aren't localized
local MODIFIER_KEYS_LOC_ALIAS = {
keyboard_ctrl = "keyboard_left ctrl",
keyboard_alt = "keyboard_left alt",
keyboard_shift = "keyboard_left shift",
} }
local KEYS_INFO = {} local KEYS_INFO = {}
@ -174,7 +178,7 @@ for input_device_name, input_device_keys in pairs(PRIMARY_BINDABLE_KEYS) do
end end
end end
for key_id, key_data in pairs(OTHER_KEYS) do for key_id, key_data in pairs(MODIFIER_KEYS) do
KEYS_INFO[key_id] = key_data KEYS_INFO[key_id] = key_data
end end
@ -190,6 +194,15 @@ local CHECK_INPUT_FUNCTIONS = {
} }
} }
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 _raw_keybinds_data = {}
local _keybinds = {} local _keybinds = {}
local _pressed_key local _pressed_key
@ -239,6 +252,11 @@ local function perform_keybind_action(data, is_pressed)
end end
end end
local function is_modifier_pressed(modifier)
return (Keyboard.button(MODIFIER_KEYS[modifier][1]) + Keyboard.button(MODIFIER_KEYS[modifier][4])) > 0
end
-- ##################################################################################################################### -- #####################################################################################################################
-- ##### DMF internal functions and variables ########################################################################## -- ##### DMF internal functions and variables ##########################################################################
-- ##################################################################################################################### -- #####################################################################################################################
@ -249,9 +267,11 @@ end
-- * If several mods bound the same keys, keybind action will be performed for all of them, when keybind is pressed. -- * 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. -- * Keybind is considered released, when its primary key is released.
function dmf.check_keybinds() function dmf.check_keybinds()
local ctrl_pressed = (Keyboard.button(KEYS_INFO["ctrl"][1]) + Keyboard.button(KEYS_INFO["ctrl"][4])) > 0 local pressed_modifiers = {
local alt_pressed = (Keyboard.button(KEYS_INFO["alt"][1]) + Keyboard.button(KEYS_INFO["alt"][4])) > 0 ctrl = is_modifier_pressed("ctrl"),
local shift_pressed = (Keyboard.button(KEYS_INFO["shift"][1]) + Keyboard.button(KEYS_INFO["shift"][4])) > 0 alt = is_modifier_pressed("alt"),
shift = is_modifier_pressed("shift")
}
if not _pressed_key then if not _pressed_key then
for primary_key, keybinds_data in pairs(_keybinds) do for primary_key, keybinds_data in pairs(_keybinds) do
@ -259,26 +279,26 @@ function dmf.check_keybinds()
for _, keybind_data in ipairs(keybinds_data) do for _, keybind_data in ipairs(keybinds_data) do
local all_pressed = true local all_pressed = true
for enabler, _ in pairs(keybind_data.enablers) do
-- Check that every enabler key is pressed -- Check that every enabler key is pressed
if OTHER_KEYS[enabler] and for enabler, _ in pairs(keybind_data.enablers) do
( if MODIFIER_KEYS[enabler] then
Keyboard.button(KEYS_INFO[enabler][1]) + if not pressed_modifiers[MODIFIER_KEYS[enabler][2]] then
Keyboard.button(KEYS_INFO[enabler][4]) all_pressed = false
) <= 0 break
or not (Keyboard.button(KEYS_INFO[enabler][1]) > 0) end
then elseif KEYS_INPUT_FUNCTIONS[enabler].check_released(enabler) then
all_pressed = false all_pressed = false
break break
end end
end end
-- Check that no modifier keys are pressed that shouldn't be -- Check that no modifier keys are pressed that shouldn't be
if all_pressed and if all_pressed and (
(not keybind_data.ctrl and ctrl_pressed) and (not keybind_data.ctrl and pressed_modifiers.ctrl) or
(not keybind_data.alt and alt_pressed) and (not keybind_data.alt and pressed_modifiers.alt) or
(not keybind_data.shift and shift_pressed) (not keybind_data.shift and pressed_modifiers.shift)
)
then then
all_pressed = false all_pressed = false
end end
@ -293,9 +313,6 @@ function dmf.check_keybinds()
end end
end end
end end
if _pressed_key then
break
end
end end
end end
end end
@ -324,9 +341,9 @@ function dmf.generate_keybinds()
local keys = raw_keybind_data.keys local keys = raw_keybind_data.keys
local primary_key = keys[1] local primary_key = keys[1]
local modifier_keys = {} local enabler_keys = {}
for i = 2, #keys do for i = 2, #keys do
modifier_keys[keys[i]] = true enabler_keys[keys[i]] = true
end end
local keybind_data = { local keybind_data = {
@ -334,18 +351,18 @@ function dmf.generate_keybinds()
global = raw_keybind_data.global, global = raw_keybind_data.global,
trigger = raw_keybind_data.trigger, trigger = raw_keybind_data.trigger,
type = raw_keybind_data.type, type = raw_keybind_data.type,
enablers = modifier_keys, enablers = enabler_keys,
ctrl = modifier_keys["ctrl"] or modifier_keys["left ctrl"] or modifier_keys["right ctrl"], ctrl = enabler_keys["ctrl"] or enabler_keys["left ctrl"] or enabler_keys["right ctrl"],
alt = modifier_keys["alt"] or modifier_keys["left alt"] or modifier_keys["right alt"], alt = enabler_keys["alt"] or enabler_keys["left alt"] or enabler_keys["right alt"],
shift = modifier_keys["shift"] or modifier_keys["left shift"] or modifier_keys["right shift"], shift = enabler_keys["shift"] or enabler_keys["left shift"] or enabler_keys["right shift"],
function_name = raw_keybind_data.function_name, function_name = raw_keybind_data.function_name,
view_name = raw_keybind_data.view_name view_name = raw_keybind_data.view_name
} }
_keybinds[primary_key] = _keybinds[primary_key] or { _keybinds[primary_key] = _keybinds[primary_key] or {
check_pressed = CHECK_INPUT_FUNCTIONS[KEYS_INFO[primary_key][3]].PRESSED, check_pressed = KEYS_INPUT_FUNCTIONS[primary_key].check_pressed,
check_released = CHECK_INPUT_FUNCTIONS[KEYS_INFO[primary_key][3]].RELEASED check_released = KEYS_INPUT_FUNCTIONS[primary_key].check_released
} }
table.insert(_keybinds[primary_key], keybind_data) table.insert(_keybinds[primary_key], keybind_data)
end end
@ -394,18 +411,16 @@ end
-- Simply tells if key with key_id can be binded as primary key. -- Simply tells if key with key_id can be binded as primary key.
-- (Used for verifying keybind widgets) -- (Used for verifying keybind widgets)
function dmf.can_bind_as_primary_key(key_id) function dmf.can_bind_as_primary_key(key_id)
return KEYS_INFO[key_id] and not OTHER_KEYS[key_id] return KEYS_INFO[key_id] and not MODIFIER_KEYS[key_id]
end end
-- Builds string with readable keys' names to look like "Primary Key + Ctrl + Alt + Shift". -- Builds string with readable keys' names to look like e.g. "Ctrl+Alt+Shift+<Primary Key>".
-- (Used in keybind widget) -- (Used in keybind widget)
function dmf.build_keybind_string(keys) function dmf.build_keybind_string(keys)
local readable_key_names = {} local key_unassigned_string = Managers.localization:localize("loc_keybind_unassigned")
for _, key_id in ipairs(keys) do local keybind_result = dmf.keys_to_keybind_result(keys)
table.insert(readable_key_names, KEYS_INFO[key_id][2]) return keybind_result and InputUtils.localized_string_from_key_info(keybind_result) or key_unassigned_string
end
return table.concat(readable_key_names, " + ")
end end
@ -465,6 +480,9 @@ function dmf.keys_to_keybind_result(keys)
return nil return nil
end end
if MODIFIER_KEYS_LOC_ALIAS[global_name] then
global_name = MODIFIER_KEYS_LOC_ALIAS[global_name]
end
keybind_result.main = global_name keybind_result.main = global_name
end end
@ -474,6 +492,9 @@ function dmf.keys_to_keybind_result(keys)
local global_name = KEYS_INFO[local_name] and InputUtils.local_to_global_name(local_name, KEYS_INFO[local_name][3]) local global_name = KEYS_INFO[local_name] and InputUtils.local_to_global_name(local_name, KEYS_INFO[local_name][3])
if global_name then 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 keybind_result.enablers[#keybind_result.enablers + 1] = global_name
end end
end end

View file

@ -301,11 +301,6 @@ local allowed_keybind_types = {
view_toggle = true, view_toggle = true,
mod_toggle = true mod_toggle = true
} }
local allowed_modifier_keys = {
ctrl = true,
alt = true,
shift = true
}
local function validate_keybind_data(data) local function validate_keybind_data(data)
if data.keybind_global and type(data.keybind_global) ~= "boolean" then if data.keybind_global and type(data.keybind_global) ~= "boolean" then
dmf.throw_error("[widget \"%s\" (keybind)]: 'keybind_global' field must have 'boolean' type", data.setting_id) dmf.throw_error("[widget \"%s\" (keybind)]: 'keybind_global' field must have 'boolean' type", data.setting_id)
@ -344,13 +339,6 @@ local function validate_keybind_data(data)
if default_value[1] and not dmf.can_bind_as_primary_key(default_value[1]) then if default_value[1] and not dmf.can_bind_as_primary_key(default_value[1]) then
dmf.throw_error("[widget \"%s\" (keybind)]: 'default_value[1]' must be a valid key name", data.setting_id) dmf.throw_error("[widget \"%s\" (keybind)]: 'default_value[1]' must be a valid key name", data.setting_id)
end end
if default_value[2] and not allowed_modifier_keys[default_value[2]] or
default_value[3] and not allowed_modifier_keys[default_value[3]] or
default_value[4] and not allowed_modifier_keys[default_value[4]]
then
dmf.throw_error("[widget \"%s\" (keybind)]: 'default_value [2], [3] and [4]' can be only strings: \"ctrl\", " ..
"\"alt\" and \"shift\" (in no particular order)", data.setting_id)
end
local used_keys = {} local used_keys = {}
for _, key in ipairs(default_value) do for _, key in ipairs(default_value) do

View file

@ -38,16 +38,16 @@ dmf_mod_data.options = {
keybind_type = "function_call", keybind_type = "function_call",
function_name = "toggle_developer_console" function_name = "toggle_developer_console"
}, },
{ -- {
setting_id = "show_network_debug_info", -- setting_id = "show_network_debug_info",
type = "checkbox", -- type = "checkbox",
default_value = false -- default_value = false
}, -- },
{ -- {
setting_id = "log_ui_renderers_info", -- setting_id = "log_ui_renderers_info",
type = "checkbox", -- type = "checkbox",
default_value = false -- default_value = false
} -- }
} }
}, },
{ {

View file

@ -676,7 +676,7 @@ DMFOptionsView.present_category_widgets = function (self, category)
local scrollbar_widget_id = "settings_scrollbar" local scrollbar_widget_id = "settings_scrollbar"
local grid_scenegraph_id = "settings_grid_background" local grid_scenegraph_id = "settings_grid_background"
local grid_pivot_scenegraph_id = "settings_grid_content_pivot" local grid_pivot_scenegraph_id = "settings_grid_content_pivot"
local grid_spacing = _view_settings.grid_spacing local grid_spacing = _view_settings.settings_grid_spacing
self._settings_content_grid = self:_setup_grid(self._settings_content_widgets, self._settings_alignment_list, grid_scenegraph_id, grid_spacing, false) self._settings_content_grid = self:_setup_grid(self._settings_content_widgets, self._settings_alignment_list, grid_scenegraph_id, grid_spacing, false)
self:_setup_content_grid_scrollbar(self._settings_content_grid, scrollbar_widget_id, grid_scenegraph_id, grid_pivot_scenegraph_id) self:_setup_content_grid_scrollbar(self._settings_content_grid, scrollbar_widget_id, grid_scenegraph_id, grid_pivot_scenegraph_id)
@ -745,7 +745,7 @@ DMFOptionsView._setup_category_config = function (self, config)
local scrollbar_widget_id = "scrollbar" local scrollbar_widget_id = "scrollbar"
local grid_scenegraph_id = "background" local grid_scenegraph_id = "background"
local grid_pivot_scenegraph_id = "grid_content_pivot" local grid_pivot_scenegraph_id = "grid_content_pivot"
local grid_spacing = _view_settings.grid_spacing local grid_spacing = _view_settings.category_grid_spacing
self._category_content_grid = self:_setup_grid(self._category_content_widgets, self._category_alignment_list, grid_scenegraph_id, grid_spacing, true) self._category_content_grid = self:_setup_grid(self._category_content_widgets, self._category_alignment_list, grid_scenegraph_id, grid_spacing, true)
self:_setup_content_grid_scrollbar(self._category_content_grid, scrollbar_widget_id, grid_scenegraph_id, grid_pivot_scenegraph_id) self:_setup_content_grid_scrollbar(self._category_content_grid, scrollbar_widget_id, grid_scenegraph_id, grid_pivot_scenegraph_id)

View file

@ -62,9 +62,10 @@ local scenegraph_definition = {
grid_width, grid_width,
grid_height grid_height
}, },
-- Move the categories up and left to compensate for removed icons
position = { position = {
180, 140,
240, 190,
1 1
} }
}, },

View file

@ -5,9 +5,13 @@ local dmf_options_view_settings = {
shading_environment = "content/shading_environments/ui/system_menu", shading_environment = "content/shading_environments/ui/system_menu",
grid_size = { grid_size = {
500, 500,
800 820 -- Increased to compensate for upshifted category grid
}, },
grid_spacing = { category_grid_spacing = {
0,
3
},
settings_grid_spacing = {
0, 0,
10 10
}, },

View file

@ -249,7 +249,9 @@ _type_template_map["dropdown"] = create_dropdown_template
-- ######### Keybind ######### -- ######### Keybind #########
-- ########################### -- ###########################
local set_new_keybind = function (self, keybind_data) local set_keybind = function (self, keybind_data, keys)
keybind_data.keys = keys
local mod = get_mod(keybind_data.mod_name) local mod = get_mod(keybind_data.mod_name)
dmf.add_mod_keybind( dmf.add_mod_keybind(
mod, mod,
@ -286,25 +288,30 @@ local create_keybind_template = function (self, params)
mod_name = params.mod_name, mod_name = params.mod_name,
setting_id = params.setting_id, setting_id = params.setting_id,
keys = dmf.keys_to_keybind_result(params.keys), keys = dmf.keys_to_keybind_result(params.keys),
default_value = dmf.keys_to_keybind_result(params.default_value) or {},
on_activated = function (new_value, old_value) on_activated = function (new_value, old_value)
for i = 1, #_cancel_keys do
local cancel_key = _cancel_keys[i]
if cancel_key == new_value.main then
-- Prevent unbinding the mod options menu -- Prevent unbinding the mod options menu
if params.setting_id ~= "open_dmf_options" then if params.setting_id ~= "open_dmf_options" then
-- Unbind the keybind -- Unbind the keybind if the new value is empty
params.keys = {} if not (new_value and new_value.main) then
set_new_keybind(self, params) set_keybind(self, params, {})
return true
end end
-- Unbind the keybind if the new value matches a cancel key
for i = 1, #_cancel_keys do
local cancel_key = _cancel_keys[i]
if cancel_key == new_value.main then
set_keybind(self, params, {})
return true return true
end end
end end
end
-- Don't modify the keybind if the new value is a reserved key
for i = 1, #_reserved_keys do for i = 1, #_reserved_keys do
local reserved_key = _reserved_keys[i] local reserved_key = _reserved_keys[i]
if reserved_key == new_value.main then if reserved_key == new_value.main then
@ -312,16 +319,16 @@ local create_keybind_template = function (self, params)
end end
end end
-- Get the new keybind -- Get the keys of the new value
local keys = dmf.keybind_result_to_keys(new_value) local keys = dmf.keybind_result_to_keys(new_value)
-- Bind the new key and prevent unbinding the mod options menu -- 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 if keys and #keys > 0 or params.setting_id ~= "open_dmf_options" then
params.keys = keys set_keybind(self, params, keys)
set_new_keybind(self, params) return true
end end
return true return false
end, end,
get_function = function (template) get_function = function (template)