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()