From 383f7d9bfdd47990f6a214ebfeebfd203b108c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofus=20Albert=20H=C3=B8gsbro=20Rose?= Date: Wed, 2 Oct 2024 12:55:38 +0200 Subject: [PATCH] fix: Registration of keymaps and associated errors. --- oscillode/__init__.py | 26 ++- oscillode/assets/__init__.py | 6 +- oscillode/assets/geonodes.py | 28 +-- oscillode/contracts/__init__.py | 7 +- oscillode/contracts/addon.py | 2 +- oscillode/contracts/bl.py | 176 ++++++++++++++++-- oscillode/contracts/bl_handlers.py | 3 + oscillode/contracts/bl_keymap.py | 60 ++++++ .../node_trees/maxwell_sim_nodes/node_tree.py | 2 + oscillode/operators/__init__.py | 17 +- oscillode/operators/connect_viewer.py | 30 +-- oscillode/registration.py | 94 +++++----- 12 files changed, 329 insertions(+), 122 deletions(-) create mode 100644 oscillode/contracts/bl_keymap.py diff --git a/oscillode/__init__.py b/oscillode/__init__.py index 98cbea7..bdefef1 100644 --- a/oscillode/__init__.py +++ b/oscillode/__init__.py @@ -18,8 +18,8 @@ from functools import reduce -# from . import node_trees, operators, preferences, registration -from . import assets, preferences, registration +# from . import node_trees +from . import assets, operators, preferences, registration from . import contracts as ct from .utils import logger @@ -30,8 +30,8 @@ log = logger.get(__name__) # - Load and Register Addon #################### BL_REGISTER: list[ct.BLClass] = [ - # *operators.BL_REGISTER, - # *assets.BL_REGISTER, + *operators.BL_REGISTER, + *assets.BL_REGISTER, # *node_trees.BL_REGISTER, ] @@ -39,20 +39,18 @@ BL_HANDLERS: ct.BLHandlers = reduce( lambda a, b: a + b, [ assets.BL_HANDLERS, - # *operators.BL_HANDLERS, - # *assets.BL_HANDLERS, - # *node_trees.BL_HANDLERS, + operators.BL_HANDLERS, + assets.BL_HANDLERS, + # node_trees.BL_HANDLERS, ], ct.BLHandlers(), ) -BL_HOTKEYS: list[ct.KeymapItemDef] = [ - # *operators.BL_HOTKEYS, - # *assets.BL_HOTKEYS, +BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [ + *assets.BL_KEYMAP_ITEMS, + *operators.BL_KEYMAP_ITEMS, ] -## TODO: BL_HANDLERS and BL_SOCKET_DEFS - #################### # - Registration @@ -79,7 +77,7 @@ def register() -> None: registration.register_classes(BL_REGISTER) registration.register_handlers(BL_HANDLERS) - registration.register_hotkeys(BL_HOTKEYS) + registration.register_keymaps(BL_KEYMAP_ITEMS) log.info('Finished Registration of Addon: %s', ct.addon.NAME) else: @@ -98,7 +96,7 @@ def unregister() -> None: """ log.info('Starting %s Unregister', ct.addon.NAME) - registration.unregister_hotkeys() + registration.unregister_keymaps() registration.unregister_handlers() registration.unregister_classes() diff --git a/oscillode/assets/__init__.py b/oscillode/assets/__init__.py index d4c2f1d..2032840 100644 --- a/oscillode/assets/__init__.py +++ b/oscillode/assets/__init__.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +"""Internal assets for use via the Python API and/or directly by the user as an asset library.""" + from functools import reduce import oscillode.contracts as ct @@ -32,8 +34,8 @@ BL_HANDLERS: ct.BLHandlers = reduce( ct.BLHandlers(), ) -BL_HOTKEYS: list[ct.KeymapItemDef] = [ - *geonodes.BL_HOTKEYS, +BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [ + *geonodes.BL_KEYMAP_ITEMS, ] __all__ = [ diff --git a/oscillode/assets/geonodes.py b/oscillode/assets/geonodes.py index e6fbce7..3af8047 100644 --- a/oscillode/assets/geonodes.py +++ b/oscillode/assets/geonodes.py @@ -31,17 +31,17 @@ log = logger.get(__name__) #################### # GeoNodes Paths ## Internal -GN_INTERNAL_PATH = ct.addon.PATH_ASSETS / 'internal' -GN_INTERNAL_INPUTS_PATH = GN_INTERNAL_PATH / 'input' -GN_INTERNAL_SOURCES_PATH = GN_INTERNAL_PATH / 'source' -GN_INTERNAL_STRUCTURES_PATH = GN_INTERNAL_PATH / 'structure' -GN_INTERNAL_MONITORS_PATH = GN_INTERNAL_PATH / 'monitor' -GN_INTERNAL_SIMULATIONS_PATH = GN_INTERNAL_PATH / 'simulation' +GN_INTERNAL_PATH: Path = ct.addon.PATH_ASSETS / 'internal' +GN_INTERNAL_INPUTS_PATH: Path = GN_INTERNAL_PATH / 'input' +GN_INTERNAL_SOURCES_PATH: Path = GN_INTERNAL_PATH / 'source' +GN_INTERNAL_STRUCTURES_PATH: Path = GN_INTERNAL_PATH / 'structure' +GN_INTERNAL_MONITORS_PATH: Path = GN_INTERNAL_PATH / 'monitor' +GN_INTERNAL_SIMULATIONS_PATH: Path = GN_INTERNAL_PATH / 'simulation' ## Structures -GN_STRUCTURES_PATH = ct.addon.PATH_ASSETS / 'structures' -GN_STRUCTURES_PRIMITIVES_PATH = GN_STRUCTURES_PATH / 'primitives' -GN_STRUCTURES_ARRAYS_PATH = GN_STRUCTURES_PATH / 'arrays' +GN_STRUCTURES_PATH: Path = ct.addon.PATH_ASSETS / 'structures' +GN_STRUCTURES_PRIMITIVES_PATH: Path = GN_STRUCTURES_PATH / 'primitives' +GN_STRUCTURES_ARRAYS_PATH: Path = GN_STRUCTURES_PATH / 'arrays' class GeoNodes(enum.StrEnum): @@ -473,10 +473,10 @@ class GeoNodesToStructureNode(bpy.types.Operator): ## 3. We compute it manually, to avoid the jank. node_location = get_view_location( editor_region, - [ + ( event.mouse_x - editor_region.x, event.mouse_y - editor_region.y, - ], + ), context.preferences.system.ui_scale, ) @@ -525,8 +525,8 @@ ASSET_LIB_SPECS: dict[str, Path] = { } -@bpy.app.handlers.persistent -def initialize_asset_libraries(_: bpy.types.Scene): +@bpy.app.handlers.persistent # type: ignore[misc] +def initialize_asset_libraries(_: bpy.types.Scene) -> None: """Before loading a `.blend` file, ensure that the WindowManager properties relied on by NodeAssetPanel are available. - Several asset libraries, defined under the global `ASSET_LIB_SPECS`, are added/replaced such that the name:path map is respected. @@ -586,4 +586,4 @@ BL_REGISTER = [ BL_HANDLERS: ct.BLHandlers = ct.BLHandlers(load_pre=(initialize_asset_libraries,)) -BL_HOTKEYS: list[ct.KeymapItemDef] = [] +BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [] diff --git a/oscillode/contracts/__init__.py b/oscillode/contracts/__init__.py index 58f65d4..33a9e61 100644 --- a/oscillode/contracts/__init__.py +++ b/oscillode/contracts/__init__.py @@ -22,24 +22,25 @@ from .bl import ( BLColorRGBA, BLEnumElement, BLEnumID, + BLEventType, + BLEventValue, BLIcon, BLIconSet, BLIDStruct, BLImportMethod, - BLKeymapItem, BLModifierType, BLNodeTreeInterfaceID, BLOperatorStatus, BLPropFlag, BLRegionType, BLSpaceType, - KeymapItemDef, ManagedObjName, PresetName, PropName, SocketName, ) from .bl_handlers import BLHandlers +from .bl_keymap import BLKeymapItem from .icons import Icon from .mobj_types import ManagedObjType from .node_tree_types import ( @@ -58,6 +59,8 @@ __all__ = [ 'BLColorRGBA', 'BLEnumElement', 'BLEnumID', + 'BLEventType', + 'BLEventValue', 'BLIcon', 'BLIconSet', 'BLIDStruct', diff --git a/oscillode/contracts/addon.py b/oscillode/contracts/addon.py index 951636c..4375bf2 100644 --- a/oscillode/contracts/addon.py +++ b/oscillode/contracts/addon.py @@ -45,7 +45,7 @@ if 'RUNNING_BLEXT_TESTS' in os.environ: PATH_ADDON_ROOT.parent / 'dev' / 'local' ) ## TODO: Consult init_settings else: - PATH_CACHE: Path = Path(bpy.utils.extension_path_user(__package__, create=True)) + PATH_CACHE: Path = Path(bpy.utils.extension_path_user(__package__, create=True)) # type: ignore[no-redef] #################### diff --git a/oscillode/contracts/bl.py b/oscillode/contracts/bl.py index 15c4000..94b9794 100644 --- a/oscillode/contracts/bl.py +++ b/oscillode/contracts/bl.py @@ -95,7 +95,6 @@ BLIDStruct: typ.TypeAlias = ( | bpy.types.WorkSpace | bpy.types.World ) -BLKeymapItem: typ.TypeAlias = typ.Any ## TODO: Better Type BLPropFlag: typ.TypeAlias = typ.Literal[ 'HIDDEN', 'SKIP_SAVE', @@ -112,6 +111,24 @@ BLColorRGBA = tuple[float, float, float, float] #################### # - Operators #################### +BLRegionType: typ.TypeAlias = typ.Literal[ + 'WINDOW', + 'HEADER', + 'CHANNELS', + 'TEMPORARY', + 'UI', + 'TOOLS', + 'TOOL_PROPS', + 'ASSET_SHELF', + 'ASSET_SHELF_HEADER', + 'PREVIEW', + 'HUD', + 'NAVIGATION_BAR', + 'EXECUTE', + 'FOOTER', + 'TOOL_HEADER', + 'XR', +] BLSpaceType: typ.TypeAlias = typ.Literal[ 'EMPTY', 'VIEW_3D', @@ -133,32 +150,151 @@ BLSpaceType: typ.TypeAlias = typ.Literal[ 'SPREADSHEET', 'PREFERENCES', ] -BLRegionType: typ.TypeAlias = typ.Literal[ - 'WINDOW', - 'HEADER', - 'CHANNELS', - 'TEMPORARY', - 'UI', - 'TOOLS', - 'TOOL_PROPS', - 'ASSET_SHELF', - 'ASSET_SHELF_HEADER', - 'PREVIEW', - 'HUD', - 'NAVIGATION_BAR', - 'EXECUTE', - 'FOOTER', - 'TOOL_HEADER', - 'XR', -] BLOperatorStatus: typ.TypeAlias = set[ typ.Literal['RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH', 'INTERFACE'] ] +#################### +# - Operators +#################### +## TODO: Write the rest in. +BLEventType: typ.TypeAlias = typ.Literal[ + 'NONE', + 'LEFTMOUSE', + 'MIDDLEMOUSE', + 'RIGHTMOUSE', + 'BUTTON4MOUSE', + 'BUTTON5MOUSE', + 'BUTTON6MOUSE', + 'BUTTON7MOUSE', + 'PEN', + 'ERASOR', + 'MOUSEMOVE', + 'INBETWEEN_MOUSEMOVE', + 'TRACKPADPAN', + 'TRACKPADZOOM', + 'MOUSEROTATE', + 'MOUSESMARTZOOM', + 'WHEELUPMOUSE', + 'WHEELDOWNMOUSE', + 'WHEELINMOUSE', + 'WHEELOUTMOUSE', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'ZERO', + 'ONE', + 'TWO', + 'THREE', + 'FOUR', + 'FIVE', + 'SIX', + 'SEVEN', + 'EIGHT', + 'NINE', + 'LEFT_CTRL', + 'LEFT_ALT', + 'LEFT_SHIFT', + 'RIGHT_ALT', + 'RIGHT_CTRL', + 'RIGHT_SHIFT', + 'ESC', + 'TAB', + 'RET', ## Enter + 'SPACE', + 'LINE_FEED', + 'BACK_SPACE', + 'DEL', + 'SEMI_COLON', + 'PERIOD', + 'COMMA', + 'QUOTE', + 'ACCENT_GRAVE', + 'MINUS', + 'PLUS', + 'SLASH', + 'BACK_SLASH', + 'EQUAL', + 'LEFT_BRACKET', + 'RIGHT_BRACKET', + 'LEFT_ARROW', + 'DOWN_ARROW', + 'RIGHT_ARROW', + 'UP_ARROW', + 'NUMPAD_0', + 'NUMPAD_1', + 'NUMPAD_2', + 'NUMPAD_3', + 'NUMPAD_4', + 'NUMPAD_5', + 'NUMPAD_6', + 'NUMPAD_7', + 'NUMPAD_8', + 'NUMPAD_9', + 'NUMPAD_PERIOD', + 'NUMPAD_SLASH', + 'NUMPAD_ASTERIX', + 'NUMPAD_MINUS', + 'NUMPAD_PLUS', + 'NUMPAD_ENTER', + 'F1', + 'F2', + 'F3', + 'F4', + 'F5', + 'F6', + 'F7', + 'F8', + 'F9', + 'F10', + 'F11', + 'F12', + 'PAUSE', + 'INSERT', + 'HOME', + 'PAGE_UP', + 'PAGE_DOWN', + 'END', + 'MEDIA_PLAY', + 'MEDIA_STOP', + 'MEDIA_FIRST', + 'MEDIA_LAST', +] +BLEventValue: typ.TypeAlias = typ.Literal[ + 'ANY', + 'PRESS', + 'RELEASE', + 'CLICK', + 'DOUBLE_CLICK', + 'CLICK_DRAG', + 'NOTHING', +] #################### # - Addon Types #################### -KeymapItemDef: typ.TypeAlias = typ.Any ManagedObjName = str #################### diff --git a/oscillode/contracts/bl_handlers.py b/oscillode/contracts/bl_handlers.py index 914a580..91d555c 100644 --- a/oscillode/contracts/bl_handlers.py +++ b/oscillode/contracts/bl_handlers.py @@ -58,6 +58,9 @@ class BLHandlers(pyd.BaseModel): render_post: tuple[BLHandler, ...] = () render_pre: tuple[BLHandler, ...] = () render_stats: tuple[BLHandler, ...] = () + ## TODO: Verify these type signatures. + + ## TODO: A validator to check that all handlers are decorated with bpy.app.handlers.persistent #################### # - Properties diff --git a/oscillode/contracts/bl_keymap.py b/oscillode/contracts/bl_keymap.py new file mode 100644 index 0000000..33c384f --- /dev/null +++ b/oscillode/contracts/bl_keymap.py @@ -0,0 +1,60 @@ +# oscillode +# Copyright (C) 2024 oscillode Project Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Declares types for working with `bpy.types.KeyMap`s.""" + +import bpy +import pydantic as pyd + +from .bl import BLEventType, BLEventValue, BLSpaceType +from .operator_types import OperatorType + + +class BLKeymapItem(pyd.BaseModel): + """Contains lists of handlers associated with this addon.""" + + operator: OperatorType + + event_type: BLEventType + event_value: BLEventValue + + shift: bool = False + ctrl: bool = False + alt: bool = False + key_modifier: BLEventType = 'NONE' + + space_type: BLSpaceType = 'EMPTY' + + def register(self, addon_keymap: bpy.types.KeyMap) -> bpy.types.KeymapItem: + """Registers this hotkey with an addon keymap. + + Raises: + ValueError: If the `space_type` constraint of the addon keymap does not match the `space_type` constraint of this `BLKeymapItem`. + """ + if self.space_type == addon_keymap.space_type: + addon_keymap.keymap_items.new( + self.operator, + self.event_type, + self.event_value, + shift=self.shift, + ctrl=self.ctrl, + alt=self.alt, + key_modifier=self.key_modifier, + ) + + msg = f'Addon keymap space type {addon_keymap.space_type} does not match space_type of BLKeymapItem to register: {self}' + raise ValueError(msg) + ## TODO: Check if space_type matches? diff --git a/oscillode/node_trees/maxwell_sim_nodes/node_tree.py b/oscillode/node_trees/maxwell_sim_nodes/node_tree.py index e63a5fc..b7d4629 100644 --- a/oscillode/node_trees/maxwell_sim_nodes/node_tree.py +++ b/oscillode/node_trees/maxwell_sim_nodes/node_tree.py @@ -312,6 +312,8 @@ class MaxwellSimTree(bpy.types.NodeTree): default=True, ) + viewer_node_type: ct.NodeType = ct.NodeType.Viewer + #################### # - Init Methods #################### diff --git a/oscillode/operators/__init__.py b/oscillode/operators/__init__.py index 7411c0f..48c4d61 100644 --- a/oscillode/operators/__init__.py +++ b/oscillode/operators/__init__.py @@ -16,11 +16,22 @@ """Blender operators that ship with Oscillode.""" +from functools import reduce + +from oscillode import contracts as ct + from . import connect_viewer -BL_REGISTER = [ +BL_REGISTER: list[ct.BLClass] = [ *connect_viewer.BL_REGISTER, ] -BL_HOTKEYS = [ - *connect_viewer.BL_HOTKEYS, +BL_HANDLERS: ct.BLHandlers = reduce( + lambda a, b: a + b, + [ + *connect_viewer.BL_HANDLERS, + ], + ct.BLHandlers(), +) +BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [ + *connect_viewer.BL_KEYMAP_ITEMS, ] diff --git a/oscillode/operators/connect_viewer.py b/oscillode/operators/connect_viewer.py index f745cff..ede756a 100644 --- a/oscillode/operators/connect_viewer.py +++ b/oscillode/operators/connect_viewer.py @@ -19,7 +19,6 @@ import bpy from oscillode import contracts as ct -from oscillode.node_trees.maxwell_sim_nodes.contracts import node_types from oscillode.utils import logger log = logger.get(__name__) @@ -79,16 +78,17 @@ class ConnectViewerNode(bpy.types.Operator): selected_node = context.selected_nodes[0] # Find Viewer Node... + viewer_node_type = node_tree.viewer_node_type for node in node_tree.nodes: # TODO: Support multiple viewer nodes. - if hasattr(node, 'node_type') and node.node_type is node_types.Viewer: + if hasattr(node, 'node_type') and node.node_type is viewer_node_type: viewer_node = node break # ...OR: Create Viewer Node else: # TODO: Place viewer where it doesn't overlap other nodes. - viewer_node = node_tree.nodes.new(node_types.Viewer.value) + viewer_node = node_tree.nodes.new(viewer_node_type) viewer_node.location.x = selected_node.location.x + 250 viewer_node.location.y = selected_node.location.y selected_node.select = False @@ -112,19 +112,19 @@ class ConnectViewerNode(bpy.types.Operator): #################### # - Blender Registration #################### -BL_REGISTER = [ +BL_REGISTER: list[ct.BLClass] = [ ConnectViewerNode, ] -BL_HOTKEYS = [ - { - '_': ( - ct.OperatorType.ConnectViewerNode, - 'LEFTMOUSE', - 'PRESS', - ), - 'ctrl': True, - 'shift': True, - 'alt': False, - }, +BL_HANDLERS: ct.BLHandlers = ct.BLHandlers() + +BL_KEYMAP_ITEMS: list[ct.BLKeymapItem] = [ + ct.BLKeymapItem( + ct.OperatorType.ConnectViewerNode, + event_type='LEFTMOUSE', + event_value='PRESS', + ctrl=True, + shift=True, + space_type='NODE_EDITOR', + ) ] diff --git a/oscillode/registration.py b/oscillode/registration.py index b12e4f5..7670d73 100644 --- a/oscillode/registration.py +++ b/oscillode/registration.py @@ -17,9 +17,13 @@ """Manages the registration of Blender classes, including delayed registrations that require access to Python dependencies. Attributes: - _ADDON_KEYMAP: Addon-specific keymap used to register operator hotkeys. - REG__CLASSES: Currently registered Blender classes. - _REGISTERED_HOTKEYS: Currently registered Blender keymap items. + _REGISTERED_CLASSES: Blender classes currently registered by this addon. + _REGISTERED_KEYMAPS: Addon keymaps currently registered by this addon. + Each addon keymap is constrained to a single `space_type`, which is the key. + _REGISTERED_KEYMAP_ITEMS: Addon keymap items currently registered by this addon. + Each keymap item is paired to the keymap within which it is registered. + _Each keymap is guaranteed to also be found in `_REGISTERED_KEYMAPS`._ + _REGISTERED_HANDLERS: Addon handlers currently registered by this addon. """ import bpy @@ -34,8 +38,8 @@ log = logger.get(__name__) #################### _REGISTERED_CLASSES: list[ct.BLClass] = [] -_ADDON_KEYMAP: bpy.types.KeyMap | None = None -_REGISTERED_HOTKEYS: list[ct.BLKeymapItem] = [] +_REGISTERED_KEYMAPS: dict[ct.BLSpaceType, bpy.types.KeyMap] = {} +_REGISTERED_KEYMAP_ITEMS: list[tuple[bpy.types.KeyMap, bpy.types.KeymapItem]] = [] _REGISTERED_HANDLERS: ct.BLHandlers | None = None @@ -109,59 +113,47 @@ def unregister_handlers() -> None: #################### # - Keymap Registration #################### -def register_hotkeys(hotkey_defs: list[dict]) -> None: +def register_keymaps(keymap_items: list[ct.BLKeymapItem]) -> None: """Registers a list of Blender hotkey definitions. Parameters: - hotkey_defs: List of Blender hotkey definitions to register. + bl_keymap_items: List of Blender hotkey definitions to register. """ - # Lazy-Load BL_NODE_KEYMAP - global _ADDON_KEYMAP # noqa: PLW0603 - if _ADDON_KEYMAP is None: - _ADDON_KEYMAP = bpy.context.window_manager.keyconfigs.addon.keymaps.new( - name='Node Editor', - space_type='NODE_EDITOR', - ) - log.info( - 'Registered Addon Keymap (Base for Keymap Items): %s', - str(_ADDON_KEYMAP), - ) + # Aggregate Requested Spaces of All Keymap Items + keymap_space_types: set[ct.BLSpaceType] = { + keymap_item.space_type for keymap_item in keymap_items + } - # Register Keymaps - log.info('Registering %s Keymap Items', len(hotkey_defs)) - for keymap_item_def in hotkey_defs: - keymap_item = _ADDON_KEYMAP.keymap_items.new( - *keymap_item_def['_'], - ctrl=keymap_item_def['ctrl'], - shift=keymap_item_def['shift'], - alt=keymap_item_def['alt'], + # Create Addon Keymap per Requested Space + for keymap_space_type in keymap_space_types: + log.info('Registering %s Keymap', keymap_space_type) + bl_keymap = bpy.context.window_manager.keyconfigs.addon.keymaps.new( + name=f'{ct.addon.NAME} - {keymap_space_type}', + space_type=keymap_space_type, ) - log.debug( - 'Registered Keymap Item %s with spec %s', - repr(keymap_item), - keymap_item_def, - ) - _REGISTERED_HOTKEYS.append(keymap_item) + _REGISTERED_KEYMAPS[keymap_space_type] = bl_keymap + + # Register Keymap Items in Correct Addon Keymap + for keymap_item in keymap_items: + log.info('Registering %s Keymap Item', keymap_item) + bl_keymap = _REGISTERED_KEYMAPS[keymap_item.space_type] + bl_keymap_item = keymap_item.register(bl_keymap) + + _REGISTERED_KEYMAP_ITEMS.append((bl_keymap, bl_keymap_item)) -def unregister_hotkeys() -> None: - """Unregisters all Blender hotkeys associated with the addon.""" - global _ADDON_KEYMAP # noqa: PLW0603 +def unregister_keymaps() -> None: + """Unregisters all Blender keymaps associated with the addon.""" + # Unregister Keymap Items from Correct Addon Keymap + for bl_keymap, bl_keymap_item in reversed(_REGISTERED_KEYMAP_ITEMS): + log.info('Unregistering %s BL Keymap Item', bl_keymap_item) + bl_keymap.keymap_items.remove(bl_keymap_item) - # Unregister Keymaps - log.info('Unregistering %s Keymap Items', len(_REGISTERED_HOTKEYS)) - for keymap_item in reversed(_REGISTERED_HOTKEYS): - log.debug( - 'Unregistered Keymap Item %s', - repr(keymap_item), - ) - _ADDON_KEYMAP.keymap_items.remove(keymap_item) + _REGISTERED_KEYMAP_ITEMS.clear() - # Lazy-Unload BL_NODE_KEYMAP - if _ADDON_KEYMAP is not None: - log.info( - 'Unregistered Keymap %s', - repr(_ADDON_KEYMAP), - ) - _REGISTERED_HOTKEYS.clear() - _ADDON_KEYMAP = None + # Delete Addon Keymaps + for bl_keymap in reversed(_REGISTERED_KEYMAPS.values()): + log.info('Unregistering %s Keymap', bl_keymap) + bpy.context.window_manager.keyconfigs.addon.keymaps.remove(bl_keymap) + + _REGISTERED_KEYMAPS.clear()