Re-design of submodules used in blender.git
This commit implements described in the #104573. The goal is to fix the confusion of the submodule hashes change, which are not ideal for any of the supported git-module configuration (they are either always visible causing confusion, or silently staged and committed, also causing confusion). This commit replaces submodules with a checkout of addons and addons_contrib, covered by the .gitignore, and locale and developer tools are moved to the main repository. This also changes the paths: - /release/scripts are moved to the /scripts - /source/tools are moved to the /tools - /release/datafiles/locale is moved to /locale This is done to avoid conflicts when using bisect, and also allow buildbot to automatically "recover" wgen building older or newer branches/patches. Running `make update` will initialize the local checkout to the changed repository configuration. Another aspect of the change is that the make update will support Github style of remote organization (origin remote pointing to thy fork, upstream remote pointing to the upstream blender/blender.git). Pull Request #104755
This commit is contained in:
committed by
Sergey Sharybin
parent
3e721195b0
commit
03806d0b67
7
scripts/modules/bl_keymap_utils/__init__.py
Normal file
7
scripts/modules/bl_keymap_utils/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
__all__ = (
|
||||
"io",
|
||||
"keymap_from_toolbar",
|
||||
"keymap_hierarchy",
|
||||
)
|
||||
306
scripts/modules/bl_keymap_utils/io.py
Normal file
306
scripts/modules/bl_keymap_utils/io.py
Normal file
@@ -0,0 +1,306 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Export Functions
|
||||
|
||||
__all__ = (
|
||||
"_init_properties_from_data", # Shared with gizmo default property initialization.
|
||||
"keyconfig_export_as_data",
|
||||
"keyconfig_import_from_data",
|
||||
"keyconfig_init_from_data",
|
||||
"keyconfig_merge",
|
||||
"keymap_init_from_data",
|
||||
)
|
||||
|
||||
|
||||
def indent(levels):
|
||||
return levels * " "
|
||||
|
||||
|
||||
def round_float_32(f):
|
||||
from struct import pack, unpack
|
||||
return unpack("f", pack("f", f))[0]
|
||||
|
||||
|
||||
def repr_f32(f):
|
||||
f_round = round_float_32(f)
|
||||
f_str = repr(f)
|
||||
f_str_frac = f_str.partition(".")[2]
|
||||
if not f_str_frac:
|
||||
return f_str
|
||||
for i in range(1, len(f_str_frac)):
|
||||
f_test = round(f, i)
|
||||
f_test_round = round_float_32(f_test)
|
||||
if f_test_round == f_round:
|
||||
return "%.*f" % (i, f_test)
|
||||
return f_str
|
||||
|
||||
|
||||
def kmi_args_as_data(kmi):
|
||||
s = [
|
||||
f"\"type\": '{kmi.type}'",
|
||||
f"\"value\": '{kmi.value}'"
|
||||
]
|
||||
|
||||
if kmi.any:
|
||||
s.append("\"any\": True")
|
||||
else:
|
||||
for attr in ("shift", "ctrl", "alt", "oskey"):
|
||||
if mod := getattr(kmi, attr):
|
||||
s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True"))
|
||||
if (mod := kmi.key_modifier) and (mod != 'NONE'):
|
||||
s.append(f"\"key_modifier\": '{mod:s}'")
|
||||
if (direction := kmi.direction) and (direction != 'ANY'):
|
||||
s.append(f"\"direction\": '{direction:s}'")
|
||||
|
||||
if kmi.repeat:
|
||||
if (
|
||||
(kmi.map_type == 'KEYBOARD' and kmi.value in {'PRESS', 'ANY'}) or
|
||||
(kmi.map_type == 'TEXTINPUT')
|
||||
):
|
||||
s.append("\"repeat\": True")
|
||||
|
||||
return "{" + ", ".join(s) + "}"
|
||||
|
||||
|
||||
def _kmi_properties_to_lines_recursive(level, properties, lines):
|
||||
from bpy.types import OperatorProperties
|
||||
|
||||
def string_value(value):
|
||||
if isinstance(value, (str, bool, int, set)):
|
||||
return repr(value)
|
||||
elif isinstance(value, float):
|
||||
return repr_f32(value)
|
||||
elif getattr(value, '__len__', False):
|
||||
return repr(tuple(value))
|
||||
raise Exception(f"Export key configuration: can't write {value!r}")
|
||||
|
||||
for pname in properties.bl_rna.properties.keys():
|
||||
if pname != "rna_type":
|
||||
value = getattr(properties, pname)
|
||||
if isinstance(value, OperatorProperties):
|
||||
lines_test = []
|
||||
_kmi_properties_to_lines_recursive(level + 2, value, lines_test)
|
||||
if lines_test:
|
||||
lines.append(f"(")
|
||||
lines.append(f"\"{pname}\",\n")
|
||||
lines.append(f"{indent(level + 3)}" "[")
|
||||
lines.extend(lines_test)
|
||||
lines.append("],\n")
|
||||
lines.append(f"{indent(level + 3)}" "),\n" f"{indent(level + 2)}")
|
||||
del lines_test
|
||||
elif properties.is_property_set(pname):
|
||||
value = string_value(value)
|
||||
lines.append((f"(\"{pname}\", {value:s}),\n" f"{indent(level + 2)}"))
|
||||
|
||||
|
||||
def _kmi_properties_to_lines(level, kmi_props, lines):
|
||||
if kmi_props is None:
|
||||
return
|
||||
|
||||
lines_test = [f"\"properties\":\n" f"{indent(level + 1)}" "["]
|
||||
_kmi_properties_to_lines_recursive(level, kmi_props, lines_test)
|
||||
if len(lines_test) > 1:
|
||||
lines_test.append("],\n")
|
||||
lines.extend(lines_test)
|
||||
|
||||
|
||||
def _kmi_attrs_or_none(level, kmi):
|
||||
lines = []
|
||||
_kmi_properties_to_lines(level + 1, kmi.properties, lines)
|
||||
if kmi.active is False:
|
||||
lines.append(f"{indent(level)}\"active\":" "False,\n")
|
||||
if not lines:
|
||||
return None
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
|
||||
# Alternate format
|
||||
|
||||
# Generate a list of keymaps to export:
|
||||
#
|
||||
# First add all user_modified keymaps (found in keyconfigs.user.keymaps list),
|
||||
# then add all remaining keymaps from the currently active custom keyconfig.
|
||||
#
|
||||
# Sort the resulting list according to top context name,
|
||||
# while this isn't essential, it makes comparing keymaps simpler.
|
||||
#
|
||||
# This will create a final list of keymaps that can be used as a "diff" against
|
||||
# the default blender keyconfig, recreating the current setup from a fresh blender
|
||||
# without needing to export keymaps which haven't been edited.
|
||||
|
||||
class FakeKeyConfig:
|
||||
keymaps = []
|
||||
edited_kc = FakeKeyConfig()
|
||||
for km in wm.keyconfigs.user.keymaps:
|
||||
if all_keymaps or km.is_user_modified:
|
||||
edited_kc.keymaps.append(km)
|
||||
# merge edited keymaps with non-default keyconfig, if it exists
|
||||
if kc != wm.keyconfigs.default:
|
||||
export_keymaps = keyconfig_merge(edited_kc, kc)
|
||||
else:
|
||||
export_keymaps = keyconfig_merge(edited_kc, edited_kc)
|
||||
|
||||
# Sort the keymap list by top context name before exporting,
|
||||
# not essential, just convenient to order them predictably.
|
||||
export_keymaps.sort(key=lambda k: k[0].name)
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as fh:
|
||||
fw = fh.write
|
||||
|
||||
# Use the file version since it includes the sub-version
|
||||
# which we can bump multiple times between releases.
|
||||
from bpy.app import version_file
|
||||
fw(f"keyconfig_version = {version_file!r}\n")
|
||||
del version_file
|
||||
|
||||
fw("keyconfig_data = \\\n[")
|
||||
|
||||
for km, _kc_x in export_keymaps:
|
||||
km = km.active()
|
||||
fw("(")
|
||||
fw(f"\"{km.name:s}\",\n")
|
||||
fw(f"{indent(2)}" "{")
|
||||
fw(f"\"space_type\": '{km.space_type:s}'")
|
||||
fw(f", \"region_type\": '{km.region_type:s}'")
|
||||
# We can detect from the kind of items.
|
||||
if km.is_modal:
|
||||
fw(", \"modal\": True")
|
||||
fw("},\n")
|
||||
fw(f"{indent(2)}" "{")
|
||||
is_modal = km.is_modal
|
||||
fw(f"\"items\":\n")
|
||||
fw(f"{indent(3)}[")
|
||||
for kmi in km.keymap_items:
|
||||
if is_modal:
|
||||
kmi_id = kmi.propvalue
|
||||
else:
|
||||
kmi_id = kmi.idname
|
||||
fw(f"(")
|
||||
kmi_args = kmi_args_as_data(kmi)
|
||||
kmi_data = _kmi_attrs_or_none(4, kmi)
|
||||
fw(f"\"{kmi_id:s}\"")
|
||||
if kmi_data is None:
|
||||
fw(f", ")
|
||||
else:
|
||||
fw(",\n" f"{indent(5)}")
|
||||
|
||||
fw(kmi_args)
|
||||
if kmi_data is None:
|
||||
fw(", None),\n")
|
||||
else:
|
||||
fw(",\n")
|
||||
fw(f"{indent(5)}" "{")
|
||||
fw(kmi_data)
|
||||
fw(f"{indent(6)}")
|
||||
fw("},\n" f"{indent(5)}")
|
||||
fw("),\n")
|
||||
fw(f"{indent(4)}")
|
||||
fw("],\n" f"{indent(3)}")
|
||||
fw("},\n" f"{indent(2)}")
|
||||
fw("),\n" f"{indent(1)}")
|
||||
|
||||
fw("]\n")
|
||||
fw("\n\n")
|
||||
fw("if __name__ == \"__main__\":\n")
|
||||
|
||||
# We could remove this in the future, as loading new key-maps in older Blender versions
|
||||
# makes less and less sense as Blender changes.
|
||||
fw(" # Only add keywords that are supported.\n")
|
||||
fw(" from bpy.app import version as blender_version\n")
|
||||
fw(" keywords = {}\n")
|
||||
fw(" if blender_version >= (2, 92, 0):\n")
|
||||
fw(" keywords[\"keyconfig_version\"] = keyconfig_version\n")
|
||||
|
||||
fw(" import os\n")
|
||||
fw(" from bl_keymap_utils.io import keyconfig_import_from_data\n")
|
||||
fw(" keyconfig_import_from_data(\n")
|
||||
fw(" os.path.splitext(os.path.basename(__file__))[0],\n")
|
||||
fw(" keyconfig_data,\n")
|
||||
fw(" **keywords,\n")
|
||||
fw(" )\n")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Import Functions
|
||||
#
|
||||
# NOTE: unlike export, this runs on startup.
|
||||
# Take care making changes that could impact performance.
|
||||
|
||||
def _init_properties_from_data(base_props, base_value):
|
||||
assert type(base_value) is list
|
||||
for attr, value in base_value:
|
||||
if type(value) is list:
|
||||
base_props.property_unset(attr)
|
||||
props = getattr(base_props, attr)
|
||||
_init_properties_from_data(props, value)
|
||||
else:
|
||||
try:
|
||||
setattr(base_props, attr, value)
|
||||
except AttributeError:
|
||||
print(f"Warning: property '{attr}' not found in item '{base_props.__class__.__name__}'")
|
||||
except Exception as ex:
|
||||
print(f"Warning: {ex!r}")
|
||||
|
||||
|
||||
def keymap_init_from_data(km, km_items, is_modal=False):
|
||||
new_fn = getattr(km.keymap_items, "new_modal" if is_modal else "new")
|
||||
for (kmi_idname, kmi_args, kmi_data) in km_items:
|
||||
kmi = new_fn(kmi_idname, **kmi_args)
|
||||
if kmi_data is not None:
|
||||
if not kmi_data.get("active", True):
|
||||
kmi.active = False
|
||||
kmi_props_data = kmi_data.get("properties", None)
|
||||
if kmi_props_data is not None:
|
||||
kmi_props = kmi.properties
|
||||
assert type(kmi_props_data) is list
|
||||
_init_properties_from_data(kmi_props, kmi_props_data)
|
||||
|
||||
|
||||
def keyconfig_init_from_data(kc, keyconfig_data):
|
||||
# Load data in the format defined above.
|
||||
#
|
||||
# Runs at load time, keep this fast!
|
||||
for (km_name, km_args, km_content) in keyconfig_data:
|
||||
km = kc.keymaps.new(km_name, **km_args)
|
||||
km_items = km_content["items"]
|
||||
# Check here instead of inside 'keymap_init_from_data'
|
||||
# because we want to allow both tuple & list types in that case.
|
||||
#
|
||||
# For full keymaps, ensure these are always lists to allow for extending them
|
||||
# in a generic way that doesn't have to check for the type each time.
|
||||
assert type(km_items) is list
|
||||
keymap_init_from_data(km, km_items, is_modal=km_args.get("modal", False))
|
||||
|
||||
|
||||
def keyconfig_import_from_data(name, keyconfig_data, *, keyconfig_version=(0, 0, 0)):
|
||||
# Load data in the format defined above.
|
||||
#
|
||||
# Runs at load time, keep this fast!
|
||||
|
||||
import bpy
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.new(name)
|
||||
if keyconfig_version is not None:
|
||||
from .versioning import keyconfig_update
|
||||
keyconfig_data = keyconfig_update(keyconfig_data, keyconfig_version)
|
||||
keyconfig_init_from_data(kc, keyconfig_data)
|
||||
return kc
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Utility Functions
|
||||
|
||||
def keyconfig_merge(kc1, kc2):
|
||||
""" note: kc1 takes priority over kc2
|
||||
"""
|
||||
kc1_names = {km.name for km in kc1.keymaps}
|
||||
merged_keymaps = [(km, kc1) for km in kc1.keymaps]
|
||||
if kc1 != kc2:
|
||||
merged_keymaps.extend(
|
||||
(km, kc2)
|
||||
for km in kc2.keymaps
|
||||
if km.name not in kc1_names
|
||||
)
|
||||
return merged_keymaps
|
||||
436
scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
Normal file
436
scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
Normal file
@@ -0,0 +1,436 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Dynamically create a keymap which is used by the popup toolbar
|
||||
# for accelerator key access.
|
||||
|
||||
__all__ = (
|
||||
"generate",
|
||||
)
|
||||
|
||||
|
||||
def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
||||
"""
|
||||
Keymap for popup toolbar, currently generated each time.
|
||||
"""
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
|
||||
def modifier_keywords_from_item(kmi):
|
||||
kw = {}
|
||||
for (attr, default) in (
|
||||
("any", False),
|
||||
("shift", False),
|
||||
("ctrl", False),
|
||||
("alt", False),
|
||||
("oskey", False),
|
||||
("key_modifier", 'NONE'),
|
||||
):
|
||||
val = getattr(kmi, attr)
|
||||
if val != default:
|
||||
kw[attr] = val
|
||||
return kw
|
||||
|
||||
def dict_as_tuple(d):
|
||||
return tuple((k, v) for (k, v) in sorted(d.items()))
|
||||
|
||||
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
||||
|
||||
items_all = [
|
||||
# 0: tool
|
||||
# 1: keymap item (direct access)
|
||||
# 2: keymap item (newly calculated for toolbar)
|
||||
[item, None, None]
|
||||
for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context))
|
||||
if item is not None
|
||||
]
|
||||
items_all_id = {item_container[0].idname for item_container in items_all}
|
||||
|
||||
# Press the toolbar popup key again to set the default tool,
|
||||
# this is useful because the select box tool is useful as a way
|
||||
# to 'drop' currently active tools (it's basically a 'none' tool).
|
||||
# so this allows us to quickly go back to a state that allows
|
||||
# a shortcut based workflow (before the tool system was added).
|
||||
use_tap_reset = use_reset
|
||||
# TODO: support other tools for modes which don't use this tool.
|
||||
tap_reset_tool = "builtin.cursor"
|
||||
# Check the tool is available in the current context.
|
||||
if tap_reset_tool not in items_all_id:
|
||||
use_tap_reset = False
|
||||
|
||||
# Pie-menu style release to activate.
|
||||
use_release_confirm = use_reset
|
||||
|
||||
# Generate items when no keys are mapped.
|
||||
use_auto_keymap_alpha = False # Map manually in the default key-map.
|
||||
use_auto_keymap_num = use_fallback_keys
|
||||
|
||||
# Temporary, only create so we can pass 'properties' to find_item_from_operator.
|
||||
use_hack_properties = True
|
||||
|
||||
km_name_default = "Toolbar Popup"
|
||||
km_name = km_name_default + " <temp>"
|
||||
wm = context.window_manager
|
||||
keyconf_user = wm.keyconfigs.user
|
||||
keyconf_active = wm.keyconfigs.active
|
||||
|
||||
keymap = keyconf_active.keymaps.get(km_name)
|
||||
if keymap is None:
|
||||
keymap = keyconf_active.keymaps.new(km_name, space_type='EMPTY', region_type='TEMPORARY')
|
||||
for kmi in keymap.keymap_items:
|
||||
keymap.keymap_items.remove(kmi)
|
||||
|
||||
keymap_src = keyconf_user.keymaps.get(km_name_default)
|
||||
if keymap_src is not None:
|
||||
for kmi_src in keymap_src.keymap_items:
|
||||
# Skip tools that aren't currently shown.
|
||||
if (
|
||||
(kmi_src.idname == "wm.tool_set_by_id") and
|
||||
(kmi_src.properties.name not in items_all_id)
|
||||
):
|
||||
continue
|
||||
keymap.keymap_items.new_from_item(kmi_src)
|
||||
del keymap_src
|
||||
del items_all_id
|
||||
|
||||
kmi_unique_args = set()
|
||||
|
||||
def kmi_unique_or_pass(kmi_args):
|
||||
kmi_unique_len = len(kmi_unique_args)
|
||||
kmi_unique_args.add(dict_as_tuple(kmi_args))
|
||||
return kmi_unique_len != len(kmi_unique_args)
|
||||
|
||||
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
||||
|
||||
if use_hack_properties:
|
||||
kmi_hack = keymap.keymap_items.new("wm.tool_set_by_id", 'NONE', 'PRESS')
|
||||
kmi_hack_properties = kmi_hack.properties
|
||||
kmi_hack.active = False
|
||||
|
||||
kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS')
|
||||
kmi_hack_brush_select_properties = kmi_hack_brush_select.properties
|
||||
kmi_hack_brush_select.active = False
|
||||
|
||||
if use_release_confirm or use_tap_reset:
|
||||
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
|
||||
idname="wm.toolbar",
|
||||
)[1]
|
||||
kmi_toolbar_type = None if not kmi_toolbar else kmi_toolbar.type
|
||||
if use_tap_reset and kmi_toolbar_type is not None:
|
||||
kmi_toolbar_args_type_only = {"type": kmi_toolbar_type}
|
||||
kmi_toolbar_args = {**kmi_toolbar_args_type_only, **modifier_keywords_from_item(kmi_toolbar)}
|
||||
else:
|
||||
use_tap_reset = False
|
||||
del kmi_toolbar
|
||||
|
||||
if use_tap_reset:
|
||||
kmi_found = None
|
||||
if use_hack_properties:
|
||||
# First check for direct assignment, if this tool already has a key, no need to add a new one.
|
||||
kmi_hack_properties.name = tap_reset_tool
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname="wm.tool_set_by_id",
|
||||
context='INVOKE_REGION_WIN',
|
||||
# properties={"name": item.idname},
|
||||
properties=kmi_hack_properties,
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
if kmi_found:
|
||||
use_tap_reset = False
|
||||
del kmi_found
|
||||
|
||||
if use_tap_reset:
|
||||
use_tap_reset = kmi_unique_or_pass(kmi_toolbar_args)
|
||||
|
||||
if use_tap_reset:
|
||||
items_all[:] = [
|
||||
item_container
|
||||
for item_container in items_all
|
||||
if item_container[0].idname != tap_reset_tool
|
||||
]
|
||||
|
||||
# -----------------------
|
||||
# Begin Keymap Generation
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Direct Tool Assignment & Brushes
|
||||
|
||||
for item_container in items_all:
|
||||
item = item_container[0]
|
||||
# Only check the first item in the tools key-map (a little arbitrary).
|
||||
if use_hack_properties:
|
||||
# First check for direct assignment.
|
||||
kmi_hack_properties.name = item.idname
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname="wm.tool_set_by_id",
|
||||
context='INVOKE_REGION_WIN',
|
||||
# properties={"name": item.idname},
|
||||
properties=kmi_hack_properties,
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
|
||||
if kmi_found is None:
|
||||
if item.data_block:
|
||||
# PAINT_OT_brush_select
|
||||
mode = context.active_object.mode
|
||||
# See: BKE_paint_get_tool_prop_id_from_paintmode
|
||||
if space_type == 'IMAGE_EDITOR':
|
||||
if context.space_data.mode == 'PAINT':
|
||||
attr = "image_tool"
|
||||
else:
|
||||
attr = None
|
||||
elif space_type == 'VIEW_3D':
|
||||
attr = {
|
||||
'SCULPT': "sculpt_tool",
|
||||
'VERTEX_PAINT': "vertex_tool",
|
||||
'WEIGHT_PAINT': "weight_tool",
|
||||
'TEXTURE_PAINT': "image_tool",
|
||||
'PAINT_GPENCIL': "gpencil_tool",
|
||||
'VERTEX_GPENCIL': "gpencil_vertex_tool",
|
||||
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
|
||||
'WEIGHT_GPENCIL': "gpencil_weight_tool",
|
||||
'SCULPT_CURVES': "curves_sculpt_tool",
|
||||
}.get(mode, None)
|
||||
else:
|
||||
attr = None
|
||||
|
||||
if attr is not None:
|
||||
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname="paint.brush_select",
|
||||
context='INVOKE_REGION_WIN',
|
||||
properties=kmi_hack_brush_select_properties,
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
|
||||
# Doesn't use brushes
|
||||
pass
|
||||
else:
|
||||
print("Unsupported mode:", mode)
|
||||
del mode, attr
|
||||
|
||||
else:
|
||||
kmi_found = None
|
||||
|
||||
if kmi_found is not None:
|
||||
pass
|
||||
elif item.operator is not None:
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname=item.operator,
|
||||
context='INVOKE_REGION_WIN',
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
elif item.keymap is not None:
|
||||
km = keyconf_user.keymaps.get(item.keymap[0])
|
||||
if km is None:
|
||||
print("Keymap", repr(item.keymap[0]), "not found for tool", item.idname)
|
||||
kmi_found = None
|
||||
else:
|
||||
kmi_first = km.keymap_items
|
||||
kmi_first = kmi_first[0] if kmi_first else None
|
||||
if kmi_first is not None:
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname=kmi_first.idname,
|
||||
# properties=kmi_first.properties, # prevents matches, don't use.
|
||||
context='INVOKE_REGION_WIN',
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
if kmi_found is None:
|
||||
# We need non-keyboard events so keys with 'key_modifier' key is found.
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname=kmi_first.idname,
|
||||
# properties=kmi_first.properties, # prevents matches, don't use.
|
||||
context='INVOKE_REGION_WIN',
|
||||
exclude={'KEYBOARD'},
|
||||
)[1]
|
||||
if kmi_found is not None:
|
||||
if kmi_found.key_modifier == 'NONE':
|
||||
kmi_found = None
|
||||
else:
|
||||
kmi_found = None
|
||||
del kmi_first
|
||||
del km
|
||||
else:
|
||||
kmi_found = None
|
||||
item_container[1] = kmi_found
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Single Key Access
|
||||
|
||||
# More complex multi-pass test.
|
||||
for item_container in items_all:
|
||||
item, kmi_found = item_container[:2]
|
||||
if kmi_found is None:
|
||||
continue
|
||||
kmi_found_type = kmi_found.type
|
||||
|
||||
# Only for single keys.
|
||||
if (
|
||||
(len(kmi_found_type) == 1) or
|
||||
# When a tool is being activated instead of running an operator, just copy the shortcut.
|
||||
(kmi_found.idname in {"wm.tool_set_by_id", "WM_OT_tool_set_by_id"})
|
||||
):
|
||||
kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)}
|
||||
if kmi_unique_or_pass(kmi_args):
|
||||
kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
|
||||
kmi.properties.name = item.idname
|
||||
item_container[2] = kmi
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Single Key Modifier
|
||||
#
|
||||
#
|
||||
# Test for key_modifier, where alpha key is used as a 'key_modifier'
|
||||
# (grease pencil holding 'D' for example).
|
||||
|
||||
for item_container in items_all:
|
||||
item, kmi_found, kmi_exist = item_container
|
||||
if kmi_found is None or kmi_exist:
|
||||
continue
|
||||
|
||||
kmi_found_type = kmi_found.type
|
||||
if kmi_found_type in {
|
||||
'LEFTMOUSE',
|
||||
'RIGHTMOUSE',
|
||||
'MIDDLEMOUSE',
|
||||
'BUTTON4MOUSE',
|
||||
'BUTTON5MOUSE',
|
||||
'BUTTON6MOUSE',
|
||||
'BUTTON7MOUSE',
|
||||
}:
|
||||
kmi_found_type = kmi_found.key_modifier
|
||||
# excludes 'NONE'
|
||||
if len(kmi_found_type) == 1:
|
||||
kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)}
|
||||
del kmi_args["key_modifier"]
|
||||
if kmi_unique_or_pass(kmi_args):
|
||||
kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
|
||||
kmi.properties.name = item.idname
|
||||
item_container[2] = kmi
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Assign A-Z to Keys
|
||||
#
|
||||
# When the keys are free.
|
||||
|
||||
if use_auto_keymap_alpha:
|
||||
# Map all unmapped keys to numbers,
|
||||
# while this is a bit strange it means users will not confuse regular key bindings to ordered bindings.
|
||||
|
||||
# First map A-Z.
|
||||
kmi_type_alpha_char = [chr(i) for i in range(65, 91)]
|
||||
kmi_type_alpha_args = {c: {"type": c} for c in kmi_type_alpha_char}
|
||||
kmi_type_alpha_args_tuple = {c: dict_as_tuple(kmi_type_alpha_args[c]) for c in kmi_type_alpha_char}
|
||||
for item_container in items_all:
|
||||
item, kmi_found, kmi_exist = item_container
|
||||
if kmi_exist:
|
||||
continue
|
||||
kmi_type = item.label[0].upper()
|
||||
kmi_tuple = kmi_type_alpha_args_tuple.get(kmi_type)
|
||||
if kmi_tuple and kmi_tuple not in kmi_unique_args:
|
||||
kmi_unique_args.add(kmi_tuple)
|
||||
kmi = keymap.keymap_items.new(
|
||||
idname="wm.tool_set_by_id",
|
||||
value='PRESS',
|
||||
**kmi_type_alpha_args[kmi_type],
|
||||
)
|
||||
kmi.properties.name = item.idname
|
||||
item_container[2] = kmi
|
||||
del kmi_type_alpha_char, kmi_type_alpha_args, kmi_type_alpha_args_tuple
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Assign Numbers to Keys
|
||||
|
||||
if use_auto_keymap_num:
|
||||
# Free events (last used first).
|
||||
kmi_type_auto = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO')
|
||||
# Map both numbers and num-pad.
|
||||
kmi_type_dupe = {
|
||||
'ONE': 'NUMPAD_1',
|
||||
'TWO': 'NUMPAD_2',
|
||||
'THREE': 'NUMPAD_3',
|
||||
'FOUR': 'NUMPAD_4',
|
||||
'FIVE': 'NUMPAD_5',
|
||||
'SIX': 'NUMPAD_6',
|
||||
'SEVEN': 'NUMPAD_7',
|
||||
'EIGHT': 'NUMPAD_8',
|
||||
'NINE': 'NUMPAD_9',
|
||||
'ZERO': 'NUMPAD_0',
|
||||
}
|
||||
|
||||
def iter_free_events():
|
||||
for mod in ({}, {"shift": True}, {"ctrl": True}, {"alt": True}):
|
||||
for e in kmi_type_auto:
|
||||
yield (e, mod)
|
||||
|
||||
iter_events = iter(iter_free_events())
|
||||
|
||||
for item_container in items_all:
|
||||
item, kmi_found, kmi_exist = item_container
|
||||
if kmi_exist:
|
||||
continue
|
||||
kmi_args = None
|
||||
while True:
|
||||
key, mod = next(iter_events, (None, None))
|
||||
if key is None:
|
||||
break
|
||||
kmi_args = {"type": key, **mod}
|
||||
kmi_tuple = dict_as_tuple(kmi_args)
|
||||
if kmi_tuple in kmi_unique_args:
|
||||
kmi_args = None
|
||||
else:
|
||||
break
|
||||
|
||||
if kmi_args is not None:
|
||||
kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
|
||||
kmi.properties.name = item.idname
|
||||
item_container[2] = kmi
|
||||
kmi_unique_args.add(kmi_tuple)
|
||||
|
||||
key = kmi_type_dupe.get(kmi_args["type"])
|
||||
if key is not None:
|
||||
kmi_args["type"] = key
|
||||
kmi_tuple = dict_as_tuple(kmi_args)
|
||||
if kmi_tuple not in kmi_unique_args:
|
||||
kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
|
||||
kmi.properties.name = item.idname
|
||||
kmi_unique_args.add(kmi_tuple)
|
||||
|
||||
# ---------------------
|
||||
# End Keymap Generation
|
||||
|
||||
if use_hack_properties:
|
||||
keymap.keymap_items.remove(kmi_hack)
|
||||
keymap.keymap_items.remove(kmi_hack_brush_select)
|
||||
|
||||
# Keep last so we can try add a key without any modifiers
|
||||
# in the case this toolbar was activated with modifiers.
|
||||
if use_tap_reset:
|
||||
if len(kmi_toolbar_args_type_only) == len(kmi_toolbar_args):
|
||||
kmi_toolbar_args_available = kmi_toolbar_args
|
||||
else:
|
||||
# We have modifiers, see if we have a free key w/o modifiers.
|
||||
kmi_toolbar_tuple = dict_as_tuple(kmi_toolbar_args_type_only)
|
||||
if kmi_toolbar_tuple not in kmi_unique_args:
|
||||
kmi_toolbar_args_available = kmi_toolbar_args_type_only
|
||||
kmi_unique_args.add(kmi_toolbar_tuple)
|
||||
else:
|
||||
kmi_toolbar_args_available = kmi_toolbar_args
|
||||
del kmi_toolbar_tuple
|
||||
|
||||
kmi = keymap.keymap_items.new(
|
||||
"wm.tool_set_by_id",
|
||||
value='DOUBLE_CLICK',
|
||||
**kmi_toolbar_args_available,
|
||||
)
|
||||
kmi.properties.name = tap_reset_tool
|
||||
|
||||
if use_release_confirm and (kmi_toolbar_type is not None):
|
||||
kmi = keymap.keymap_items.new(
|
||||
"ui.button_execute",
|
||||
type=kmi_toolbar_type,
|
||||
value='RELEASE',
|
||||
any=True,
|
||||
)
|
||||
kmi.properties.skip_depressed = True
|
||||
|
||||
wm.keyconfigs.update()
|
||||
return keymap
|
||||
229
scripts/modules/bl_keymap_utils/keymap_hierarchy.py
Normal file
229
scripts/modules/bl_keymap_utils/keymap_hierarchy.py
Normal file
@@ -0,0 +1,229 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
__all__ = (
|
||||
"generate",
|
||||
)
|
||||
|
||||
|
||||
def _km_expand_from_toolsystem(space_type, context_mode):
|
||||
def _fn():
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
for cls in ToolSelectPanelHelper.__subclasses__():
|
||||
if cls.bl_space_type == space_type:
|
||||
return cls.keymap_ui_hierarchy(context_mode)
|
||||
raise Exception("keymap not found")
|
||||
return _fn
|
||||
|
||||
|
||||
def _km_hierarchy_iter_recursive(items):
|
||||
for sub in items:
|
||||
if callable(sub):
|
||||
yield from sub()
|
||||
else:
|
||||
yield (*sub[:3], list(_km_hierarchy_iter_recursive(sub[3])))
|
||||
|
||||
|
||||
def generate():
|
||||
return list(_km_hierarchy_iter_recursive(_km_hierarchy))
|
||||
|
||||
|
||||
# bpy.type.KeyMap: (km.name, km.space_type, km.region_type, [...])
|
||||
|
||||
# ('Script', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
|
||||
# Access via 'km_hierarchy'.
|
||||
_km_hierarchy = [
|
||||
('Window', 'EMPTY', 'WINDOW', []), # file save, window change, exit
|
||||
('Screen', 'EMPTY', 'WINDOW', [ # full screen, undo, screenshot
|
||||
('Screen Editing', 'EMPTY', 'WINDOW', []), # re-sizing, action corners
|
||||
('Region Context Menu', 'EMPTY', 'WINDOW', []), # header/footer/navigation_bar stuff (per region)
|
||||
]),
|
||||
|
||||
('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region)
|
||||
('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation
|
||||
|
||||
('User Interface', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform)
|
||||
('Object Mode', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'OBJECT'),
|
||||
]),
|
||||
('Mesh', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_MESH'),
|
||||
]),
|
||||
('Curve', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_CURVE'),
|
||||
]),
|
||||
('Curves', 'EMPTY', 'WINDOW', []),
|
||||
('Armature', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_ARMATURE'),
|
||||
]),
|
||||
('Metaball', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_METABALL'),
|
||||
]),
|
||||
('Lattice', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_LATTICE'),
|
||||
]),
|
||||
('Font', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'EDIT_TEXT'),
|
||||
]),
|
||||
|
||||
('Pose', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'POSE'),
|
||||
]),
|
||||
|
||||
('Vertex Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_VERTEX'),
|
||||
]),
|
||||
('Weight Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_WEIGHT'),
|
||||
]),
|
||||
('Paint Vertex Selection (Weight, Vertex)', 'EMPTY', 'WINDOW', []),
|
||||
('Paint Face Mask (Weight, Vertex, Texture)', 'EMPTY', 'WINDOW', []),
|
||||
# image and view3d
|
||||
('Image Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PAINT_TEXTURE'),
|
||||
]),
|
||||
('Sculpt', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'SCULPT'),
|
||||
]),
|
||||
|
||||
('Sculpt Curves', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'CURVES_SCULPT'),
|
||||
]),
|
||||
|
||||
('Particle', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PARTICLE'),
|
||||
]),
|
||||
|
||||
('Knife Tool Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Custom Normals Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Bevel Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Paint Stroke Modal', 'EMPTY', 'WINDOW', []),
|
||||
('Sculpt Expand Modal', 'EMPTY', 'WINDOW', []),
|
||||
('Paint Curve', 'EMPTY', 'WINDOW', []),
|
||||
('Curve Pen Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change
|
||||
|
||||
('View3D Placement Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Walk Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Fly Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Rotate Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Move Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Zoom Modal', 'EMPTY', 'WINDOW', []),
|
||||
('View3D Dolly Modal', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
# toolbar and properties
|
||||
('3D View Generic', 'VIEW_3D', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('VIEW_3D', None),
|
||||
]),
|
||||
]),
|
||||
|
||||
('Graph Editor', 'GRAPH_EDITOR', 'WINDOW', [
|
||||
('Graph Editor Generic', 'GRAPH_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
('Dopesheet', 'DOPESHEET_EDITOR', 'WINDOW', [
|
||||
('Dopesheet Generic', 'DOPESHEET_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
('NLA Editor', 'NLA_EDITOR', 'WINDOW', [
|
||||
('NLA Channels', 'NLA_EDITOR', 'WINDOW', []),
|
||||
('NLA Generic', 'NLA_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
('Timeline', 'TIMELINE', 'WINDOW', []),
|
||||
|
||||
('Image', 'IMAGE_EDITOR', 'WINDOW', [
|
||||
# Image (reverse order, UVEdit before Image).
|
||||
('UV Editor', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('IMAGE_EDITOR', 'UV'),
|
||||
]),
|
||||
('UV Sculpt', 'EMPTY', 'WINDOW', []),
|
||||
# Image and view3d.
|
||||
('Image Paint', 'EMPTY', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('IMAGE_EDITOR', 'PAINT'),
|
||||
]),
|
||||
('Image View', 'IMAGE_EDITOR', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('IMAGE_EDITOR', 'VIEW'),
|
||||
]),
|
||||
('Image Generic', 'IMAGE_EDITOR', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('IMAGE_EDITOR', None),
|
||||
]),
|
||||
]),
|
||||
|
||||
('Outliner', 'OUTLINER', 'WINDOW', []),
|
||||
|
||||
('Node Editor', 'NODE_EDITOR', 'WINDOW', [
|
||||
('Node Generic', 'NODE_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', [
|
||||
('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('SEQUENCE_EDITOR', 'SEQUENCER'),
|
||||
]),
|
||||
('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', [
|
||||
_km_expand_from_toolsystem('SEQUENCE_EDITOR', 'PREVIEW'),
|
||||
]),
|
||||
]),
|
||||
|
||||
('File Browser', 'FILE_BROWSER', 'WINDOW', [
|
||||
('File Browser Main', 'FILE_BROWSER', 'WINDOW', []),
|
||||
('File Browser Buttons', 'FILE_BROWSER', 'WINDOW', []),
|
||||
]),
|
||||
|
||||
('Info', 'INFO', 'WINDOW', []),
|
||||
|
||||
('Property Editor', 'PROPERTIES', 'WINDOW', []), # align context menu
|
||||
|
||||
('Text', 'TEXT_EDITOR', 'WINDOW', [
|
||||
('Text Generic', 'TEXT_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
('Console', 'CONSOLE', 'WINDOW', []),
|
||||
('Clip', 'CLIP_EDITOR', 'WINDOW', [
|
||||
('Clip Editor', 'CLIP_EDITOR', 'WINDOW', []),
|
||||
('Clip Graph Editor', 'CLIP_EDITOR', 'WINDOW', []),
|
||||
('Clip Dopesheet Editor', 'CLIP_EDITOR', 'WINDOW', []),
|
||||
]),
|
||||
|
||||
('Grease Pencil', 'EMPTY', 'WINDOW', [ # grease pencil stuff (per region)
|
||||
('Grease Pencil Stroke Curve Edit Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Edit Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Paint (Draw brush)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Paint (Fill)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Paint (Erase)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Paint (Tint)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Paint Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Smooth)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Thickness)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Strength)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Grab)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Push)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Twist)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Pinch)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Randomize)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Sculpt (Clone)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Weight Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Weight (Draw)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex Mode', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex (Draw)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex (Blur)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex (Average)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex (Smear)', 'EMPTY', 'WINDOW', []),
|
||||
('Grease Pencil Stroke Vertex (Replace)', 'EMPTY', 'WINDOW', []),
|
||||
]),
|
||||
('Mask Editing', 'EMPTY', 'WINDOW', []),
|
||||
('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region)
|
||||
('Markers', 'EMPTY', 'WINDOW', []), # markers (per region)
|
||||
('Animation', 'EMPTY', 'WINDOW', []), # frame change on click, preview range (per region)
|
||||
('Animation Channels', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
('View3D Gesture Circle', 'EMPTY', 'WINDOW', []),
|
||||
('Gesture Straight Line', 'EMPTY', 'WINDOW', []),
|
||||
('Gesture Zoom Border', 'EMPTY', 'WINDOW', []),
|
||||
('Gesture Box', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
('Standard Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Transform Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Eyedropper Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Eyedropper ColorRamp PointSampling Map', 'EMPTY', 'WINDOW', []),
|
||||
]
|
||||
48
scripts/modules/bl_keymap_utils/platform_helpers.py
Normal file
48
scripts/modules/bl_keymap_utils/platform_helpers.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
def keyconfig_data_oskey_from_ctrl(keyconfig_data_src, *, filter_fn=None):
|
||||
keyconfig_data_dst = []
|
||||
for km_name, km_parms, km_items_data_src in keyconfig_data_src:
|
||||
km_items_data_dst = km_items_data_src.copy()
|
||||
items_dst = []
|
||||
km_items_data_dst["items"] = items_dst
|
||||
for item_src in km_items_data_src["items"]:
|
||||
item_op, item_event, item_prop = item_src
|
||||
if "ctrl" in item_event:
|
||||
if filter_fn is None or filter_fn(item_event):
|
||||
item_event = item_event.copy()
|
||||
item_event["oskey"] = item_event["ctrl"]
|
||||
del item_event["ctrl"]
|
||||
items_dst.append((item_op, item_event, item_prop))
|
||||
items_dst.append(item_src)
|
||||
keyconfig_data_dst.append((km_name, km_parms, km_items_data_dst))
|
||||
return keyconfig_data_dst
|
||||
|
||||
|
||||
def keyconfig_data_oskey_from_ctrl_for_macos(keyconfig_data_src):
|
||||
"""Use for apple since Cmd is typically used in-place of Ctrl."""
|
||||
def filter_fn(item_event):
|
||||
if item_event.get("ctrl"):
|
||||
event_type = item_event["type"]
|
||||
# Ctrl-{Key}
|
||||
if (event_type in {
|
||||
'H',
|
||||
'M',
|
||||
'SPACE',
|
||||
'W',
|
||||
'ACCENT_GRAVE',
|
||||
'PERIOD',
|
||||
'TAB',
|
||||
}):
|
||||
if (not item_event.get("alt")) and (not item_event.get("shift")):
|
||||
return False
|
||||
# Ctrl-Alt-{Key}
|
||||
if (event_type in {
|
||||
'Q',
|
||||
}):
|
||||
if item_event.get("alt") and (not item_event.get("shift")):
|
||||
return False
|
||||
return True
|
||||
|
||||
return keyconfig_data_oskey_from_ctrl(keyconfig_data_src, filter_fn=filter_fn)
|
||||
63
scripts/modules/bl_keymap_utils/versioning.py
Normal file
63
scripts/modules/bl_keymap_utils/versioning.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Update Blender version this key-map was written in:
|
||||
#
|
||||
# When the version is `(0, 0, 0)`, the key-map being loaded didn't contain any versioning information.
|
||||
# This will older than `(2, 92, 0)`.
|
||||
|
||||
def keyconfig_update(keyconfig_data, keyconfig_version):
|
||||
from bpy.app import version_file as blender_version
|
||||
if keyconfig_version >= blender_version:
|
||||
return keyconfig_data
|
||||
|
||||
# Version the key-map.
|
||||
import copy
|
||||
# Only copy once.
|
||||
has_copy = False
|
||||
|
||||
# Default repeat to false.
|
||||
if keyconfig_version <= (2, 92, 0):
|
||||
if not has_copy:
|
||||
keyconfig_data = copy.deepcopy(keyconfig_data)
|
||||
has_copy = True
|
||||
|
||||
for _km_name, _km_parms, km_items_data in keyconfig_data:
|
||||
for (_item_op, item_event, _item_prop) in km_items_data["items"]:
|
||||
if item_event.get("value") == 'PRESS':
|
||||
# Unfortunately we don't know the 'map_type' at this point.
|
||||
# Setting repeat true on other kinds of events is harmless.
|
||||
item_event["repeat"] = True
|
||||
|
||||
if keyconfig_version <= (3, 2, 5):
|
||||
if not has_copy:
|
||||
keyconfig_data = copy.deepcopy(keyconfig_data)
|
||||
has_copy = True
|
||||
|
||||
for _km_name, _km_parms, km_items_data in keyconfig_data:
|
||||
for (_item_op, item_event, _item_prop) in km_items_data["items"]:
|
||||
if ty_new := {
|
||||
'EVT_TWEAK_L': 'LEFTMOUSE',
|
||||
'EVT_TWEAK_M': 'MIDDLEMOUSE',
|
||||
'EVT_TWEAK_R': 'RIGHTMOUSE',
|
||||
}.get(item_event.get("type")):
|
||||
item_event["type"] = ty_new
|
||||
if (value := item_event["value"]) != 'ANY':
|
||||
item_event["direction"] = value
|
||||
item_event["value"] = 'CLICK_DRAG'
|
||||
|
||||
if keyconfig_version <= (3, 2, 6):
|
||||
if not has_copy:
|
||||
keyconfig_data = copy.deepcopy(keyconfig_data)
|
||||
has_copy = True
|
||||
|
||||
for _km_name, _km_parms, km_items_data in keyconfig_data:
|
||||
for (_item_op, item_event, _item_prop) in km_items_data["items"]:
|
||||
if ty_new := {
|
||||
'NDOF_BUTTON_ESC': 'ESC',
|
||||
'NDOF_BUTTON_ALT': 'LEFT_ALT',
|
||||
'NDOF_BUTTON_SHIFT': 'LEFT_SHIFT',
|
||||
'NDOF_BUTTON_CTRL': 'LEFT_CTRL',
|
||||
}.get(item_event.get("type")):
|
||||
item_event["type"] = ty_new
|
||||
|
||||
return keyconfig_data
|
||||
Reference in New Issue
Block a user