work: save startup because I'm sick of the arnoldesque materials
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to **Maya Config Pro** are listed here. Versions match `blender_manifest.toml`.
|
||||
|
||||
## [1.8.0] — 2026-04-22
|
||||
|
||||
### WIP — Animation shelf (3D View)
|
||||
- 3D View **tool header** row + **Sidebar (N) › Animation** tab (shared operator row with Scene shelf); add-on preference and `SpaceView3D.show_region_tool_header` wiring; import/pref id fixes. **Removed** the earlier **POST_PIXEL** GPU/BLF viewport strip. Still in progress / verify per layout and Blender 5.0.
|
||||
|
||||
### From recent commits
|
||||
- **NLA:** right-click in FA keymap aligned with Blender (tracks/editor/generic).
|
||||
- **FA hotkeys:** `Home` zoom respects camera-view context; **auto_loader** so FA add-on key layer does not double-load with the default/Industry keyconfig.
|
||||
- **Input:** `Ctrl+Comma` → preferences (FA layer).
|
||||
- **Keymaps:** stop shipping local copies of Blender’s **official** keysets; key switcher maps to upstream; smaller repo footprint.
|
||||
|
||||
## [1.7.1] — 2026-04-12
|
||||
|
||||
### Fixed
|
||||
- **Legacy add-on install:** Added `bl_info` in `__init__.py` so ZIP installs that land under `scripts/addons` (Install from Disk / drag-drop) register correctly instead of empty `Modules Installed ()` with no entry to enable.
|
||||
- **FA hotkeys preset:** Restored **Node Generic** + **Node Editor** keymaps (shader/node editors: selection, links, `NODE_MT_view_pie`, etc.). Completed **View2D** (shift+MMB pan, trackpad pan, horizontal wheel). Added **`` ` ``** view framing pies for **Dopesheet** and **Graph Editor**. **Outliner:** `.` and numpad `.` for *Show Active* (replaced disabled numpad entry and non-standard `F` binding).
|
||||
|
||||
### Added
|
||||
- **GitHub Actions** `release.yml`: build versioned ZIP from the repo, verify layout, attach to a draft GitHub Release. Release **title** and **ZIP filename** use the version in `blender_manifest.toml` (set the same value as your git tag, e.g. `v1.7.1` ↔ `1.7.1`).
|
||||
- **`INSTALL.txt`** and README instructions for **Get Extensions** vs legacy add-ons paths.
|
||||
|
||||
---
|
||||
|
||||
## [1.7.0] — Extension port (summary)
|
||||
|
||||
First **Blender Extension** release (`blender_manifest.toml`): preferences, **deploy keymap presets** / optional **startup.blend**, **activate keymap** presets with **view zoom axis** (horizontal for FA). **ProAni** / shelf / sidebar / animation panels; **5.1** UI refactors and toolbar drawing fixes. **Deploy** operators and preferences wiring. **Marking menu** safer invoke; **D** key → scene-level affect-only-origins toggle; pie menu / FA keymap ID updates toward current Blender menus; FA **view zoom** / axis-related preferences.
|
||||
|
||||
---
|
||||
|
||||
## Earlier history (legacy MCP → port)
|
||||
|
||||
Condensed from git history:
|
||||
|
||||
| Theme | Changes |
|
||||
|--------|---------|
|
||||
| **Port** | Import MCP 1.7 codebase; extension layout; README; `.gitignore`; remove obsolete scripts |
|
||||
| **UI** | Pro panel, shelves, camera tools, animation panel; shelf/header drawing; Blender **5.1** refactors |
|
||||
| **Deploy** | `deploy_ops`, `utils/deploy`, preferences UI for keymap + startup deploy |
|
||||
| **Ops / prefs** | Marking menu guards; special tools / pie IDs; zoom-axis handling for FA vs default keymaps |
|
||||
|
||||
Prior feature-level history (pre-extension numbering) remains in **README → Update History**.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A Maya-like configuration, shortcuts, and UI elements for Blender.
|
||||
|
||||
**Version:** 1.7.0 (Extension Port)
|
||||
**Version:** 1.8.0 (Extension Port)
|
||||
**Original Author:** Jesse Doyle | Form Affinity
|
||||
**License:** GPL-3.0-or-later
|
||||
|
||||
@@ -254,6 +254,12 @@ For older Blender versions (pre-4.2) or if you prefer the original mod approach:
|
||||
|
||||
## Update History
|
||||
|
||||
### Version 1.8.0
|
||||
- WIP: 3D View animation shelf (tool header + N-panel); NLA/FA keymap and loader fixes. See `CHANGELOG.md`.
|
||||
|
||||
### Version 1.7.1
|
||||
- See **[CHANGELOG.md](CHANGELOG.md)** for full notes. Highlights: `bl_info` for legacy ZIP installs, GitHub release workflow + install docs, FA hotkeys (node editor, View2D, framing pies, Outliner show active).
|
||||
|
||||
### Version 1.6 / Extension 1.7.0
|
||||
- **Extension Format:** Complete port to modern Blender Extension platform
|
||||
- **Multi-Version Support:** Compatible with Blender 4.2, 4.5 LTS, and 5.0+
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Maya Config Pro - Blender Extension
|
||||
A Maya-like configuration, shortcuts, and UI elements for Blender.
|
||||
|
||||
Version: 1.7.0
|
||||
Version: 1.8.0
|
||||
Author: Form Affinity (Jesse Doyle)
|
||||
"""
|
||||
|
||||
@@ -12,7 +12,7 @@ Author: Form Affinity (Jesse Doyle)
|
||||
bl_info = {
|
||||
"name": "Maya Config Pro",
|
||||
"author": "Form Affinity (Jesse Doyle)",
|
||||
"version": (1, 7, 0),
|
||||
"version": (1, 8, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Maya Config",
|
||||
"description": "Maya-like configuration, shortcuts, UI panels, and keymap presets",
|
||||
@@ -28,7 +28,6 @@ from .utils import compat
|
||||
# not only blender_manifest.toml id — otherwise AddonPreferences.draw never appears.
|
||||
_ADDON_MODULE = __package__ or "form_affinity_maya_config_pro"
|
||||
|
||||
# Import all submodules
|
||||
from .panels import (
|
||||
panel_pro,
|
||||
panel_sidebar,
|
||||
@@ -46,12 +45,34 @@ from .ops import (
|
||||
object_select,
|
||||
mesh_select_mode,
|
||||
delete_ops,
|
||||
view_frame,
|
||||
)
|
||||
|
||||
from .keyconfigs import fa_hotkeys
|
||||
from .utils import deploy as deploy_util
|
||||
from .ops import deploy_ops
|
||||
|
||||
|
||||
def _mcp_on_auto_load_keymap(self, context):
|
||||
try:
|
||||
from .keyconfigs import fa_hotkeys
|
||||
|
||||
fa_hotkeys.sync_addon_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Maya Config Pro: keymap sync: {e}")
|
||||
|
||||
|
||||
def _mcp_on_viewport_anim_shelf(self, context):
|
||||
if not self.viewport_anim_shelf:
|
||||
return
|
||||
try:
|
||||
from .panels import panel_animation
|
||||
|
||||
panel_animation.view3d_set_show_region_tool_header(True)
|
||||
except Exception as e:
|
||||
print(f"Warning: Maya Config Pro: tool header: {e}")
|
||||
|
||||
|
||||
# Addon preferences
|
||||
class MCP_AddonPreferences(bpy.types.AddonPreferences):
|
||||
"""Maya Config Pro preferences"""
|
||||
@@ -59,8 +80,24 @@ class MCP_AddonPreferences(bpy.types.AddonPreferences):
|
||||
|
||||
auto_load_keymap: bpy.props.BoolProperty(
|
||||
name="Auto-load FA Keymap",
|
||||
description="Automatically load the Maya-like keymap when Blender starts",
|
||||
default=True
|
||||
description=(
|
||||
"When the keyconfig is FA Hotkeys, register MCP's addon keymap layer (Maya-style). "
|
||||
"Switches to Blender/Industry remove that layer; no restart required"
|
||||
),
|
||||
default=True,
|
||||
update=_mcp_on_auto_load_keymap,
|
||||
)
|
||||
|
||||
viewport_anim_shelf: bpy.props.BoolProperty(
|
||||
name="3D View: Animation shelf (tool header + sidebar)",
|
||||
description=(
|
||||
"Show the Animation shelf row in the 3D View tool header (enables the tool "
|
||||
"header strip) and in Sidebar (N) under the Animation tab. If you still do "
|
||||
"not see the tool-header row, use the Animation sidebar tab; the strip can be "
|
||||
"off in some layouts until View shows the tool header."
|
||||
),
|
||||
default=True,
|
||||
update=_mcp_on_viewport_anim_shelf,
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -100,13 +137,14 @@ class MCP_AddonPreferences(bpy.types.AddonPreferences):
|
||||
|
||||
col.separator()
|
||||
col.label(
|
||||
text="Copies Blender's keymaps (and MCP fa_hotkeys) into your Scripts folder, "
|
||||
"and optional startup.blend into your config. Needed for Pro panel keymap buttons.",
|
||||
text="Installs MCP fa_hotkeys into your keyconfig folder; default/Industry use "
|
||||
"this Blender's built-in keymaps. Optional startup.blend deploys to your config.",
|
||||
icon="INFO",
|
||||
)
|
||||
|
||||
layout.separator()
|
||||
layout.prop(self, "auto_load_keymap")
|
||||
layout.prop(self, "viewport_anim_shelf")
|
||||
|
||||
|
||||
# List of all classes in this module
|
||||
@@ -130,40 +168,32 @@ submodules = [
|
||||
object_select,
|
||||
mesh_select_mode,
|
||||
delete_ops,
|
||||
view_frame,
|
||||
fa_hotkeys,
|
||||
]
|
||||
|
||||
def register():
|
||||
"""Register all classes and submodules"""
|
||||
# Register main classes
|
||||
fa_hotkeys.set_addon_id(_ADDON_MODULE)
|
||||
|
||||
for cls in classes:
|
||||
compat.safe_register_class(cls)
|
||||
|
||||
# Register all submodules
|
||||
for module in submodules:
|
||||
try:
|
||||
module.register()
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to register {module.__name__}: {e}")
|
||||
|
||||
# Register keymaps if auto-load is enabled
|
||||
prefs = bpy.context.preferences.addons.get(_ADDON_MODULE)
|
||||
if prefs and prefs.preferences.auto_load_keymap:
|
||||
try:
|
||||
fa_hotkeys.register_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to register keymaps: {e}")
|
||||
try:
|
||||
fa_hotkeys.sync_addon_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to sync keymaps: {e}")
|
||||
|
||||
|
||||
def unregister():
|
||||
"""Unregister all classes and submodules"""
|
||||
# Unregister keymaps first
|
||||
try:
|
||||
fa_hotkeys.unregister_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to unregister keymaps: {e}")
|
||||
|
||||
# Unregister all submodules (in reverse order)
|
||||
# Unregister all submodules (in reverse order; fa_hotkeys clears addon keymaps + listeners)
|
||||
for module in reversed(submodules):
|
||||
try:
|
||||
module.unregister()
|
||||
|
||||
@@ -3,7 +3,7 @@ schema_version = "1.0.0"
|
||||
id = "form_affinity_maya_config_pro"
|
||||
name = "Maya Config Pro"
|
||||
tagline = "Maya-like configuration, shortcuts, and UI elements for Blender"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
type = "add-on"
|
||||
|
||||
maintainer = "Form Affinity"
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
Maya Config Pro - Keymap Configuration
|
||||
Maya-like keymap preset for Blender.
|
||||
|
||||
This module loads and registers the FA Hotkeys keymap configuration.
|
||||
This module loads and registers the FA Hotkeys keymap configuration into
|
||||
`wm.keyconfigs.addon` only when the user keyconfig preset is the FA one and
|
||||
"Auto-load FA Keymap" is enabled. Switching to Blender/Industry unregisters
|
||||
that layer without restarting.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
@@ -11,27 +14,59 @@ from ..utils import compat
|
||||
# Import the keyconfig data
|
||||
from . import fa_hotkeys_data
|
||||
|
||||
# Addon keymaps
|
||||
addon_keymaps = []
|
||||
# Addon keymaps: (keymap, keymap_item) and created keymaps to remove for clean sync
|
||||
addon_keymaps: list = []
|
||||
addon_keymap_objects: list = []
|
||||
|
||||
# Set from __init__.register to match this add-on's module id
|
||||
_addon_id: str = "form_affinity_maya_config_pro"
|
||||
|
||||
# Message bus: react to keyconfig changes from Preferences
|
||||
_msgbus_owner = object()
|
||||
|
||||
|
||||
def set_addon_id(addon_id: str) -> None:
|
||||
global _addon_id
|
||||
_addon_id = addon_id
|
||||
|
||||
|
||||
def _get_prefs():
|
||||
a = bpy.context.preferences.addons.get(_addon_id)
|
||||
return a.preferences if a else None
|
||||
|
||||
|
||||
def is_fa_keyconfig_active() -> bool:
|
||||
"""True when the active user keyconfig is the `fa_hotkeys` preset (file stem)."""
|
||||
try:
|
||||
s = str(bpy.context.preferences.keymap.active_keyconfig or "").lower()
|
||||
if s == "fa_hotkeys":
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
kc = bpy.context.window_manager.keyconfigs.active
|
||||
if kc and kc.name and kc.name.lower() == "fa_hotkeys":
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def register_keymaps():
|
||||
"""Register FA Hotkeys as addon keymaps"""
|
||||
"""Register FA Hotkeys as addon keymaps (call unregister_keymaps first for a clean set)."""
|
||||
global addon_keymap_objects
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.addon
|
||||
|
||||
if not kc:
|
||||
return
|
||||
|
||||
# Try to load the keyconfig data
|
||||
try:
|
||||
# The keyconfig_data is defined in fa_hotkeys_data module
|
||||
if hasattr(fa_hotkeys_data, 'keyconfig_data'):
|
||||
if hasattr(fa_hotkeys_data, "keyconfig_data"):
|
||||
for km_name, km_args, km_content in fa_hotkeys_data.keyconfig_data:
|
||||
# Create or get keymap
|
||||
km = kc.keymaps.new(name=km_name, **km_args)
|
||||
addon_keymap_objects.append(km)
|
||||
|
||||
# Add keymap items
|
||||
if "items" in km_content:
|
||||
for item in km_content["items"]:
|
||||
if len(item) >= 2:
|
||||
@@ -39,28 +74,25 @@ def register_keymaps():
|
||||
keymap_item = item[1]
|
||||
props = item[2] if len(item) > 2 else None
|
||||
|
||||
# Create keymap item
|
||||
kmi = km.keymap_items.new(
|
||||
idname,
|
||||
type=keymap_item.get("type", 'A'),
|
||||
value=keymap_item.get("value", 'PRESS'),
|
||||
type=keymap_item.get("type", "A"),
|
||||
value=keymap_item.get("value", "PRESS"),
|
||||
any=keymap_item.get("any", False),
|
||||
shift=keymap_item.get("shift", 0),
|
||||
ctrl=keymap_item.get("ctrl", 0),
|
||||
alt=keymap_item.get("alt", 0),
|
||||
oskey=keymap_item.get("oskey", False),
|
||||
key_modifier=keymap_item.get("key_modifier", 'NONE'),
|
||||
key_modifier=keymap_item.get("key_modifier", "NONE"),
|
||||
repeat=keymap_item.get("repeat", False),
|
||||
head=keymap_item.get("head", False)
|
||||
head=keymap_item.get("head", False),
|
||||
)
|
||||
|
||||
# Set properties if provided
|
||||
if props and "properties" in props:
|
||||
for prop_name, prop_value in props["properties"]:
|
||||
if hasattr(kmi.properties, prop_name):
|
||||
setattr(kmi.properties, prop_name, prop_value)
|
||||
|
||||
# Handle active state
|
||||
if props and "active" in props:
|
||||
kmi.active = props["active"]
|
||||
|
||||
@@ -70,15 +102,15 @@ def register_keymaps():
|
||||
|
||||
|
||||
def unregister_keymaps():
|
||||
"""Unregister FA Hotkeys addon keymaps"""
|
||||
"""Remove all MCP addon keymap items and the keymap objects we created."""
|
||||
global addon_keymap_objects
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.addon
|
||||
|
||||
if not kc:
|
||||
return
|
||||
|
||||
# Remove all registered keymap items
|
||||
for km, kmi in addon_keymaps:
|
||||
for km, kmi in list(addon_keymaps):
|
||||
try:
|
||||
km.keymap_items.remove(kmi)
|
||||
except Exception:
|
||||
@@ -86,23 +118,85 @@ def unregister_keymaps():
|
||||
|
||||
addon_keymaps.clear()
|
||||
|
||||
for km in reversed(addon_keymap_objects):
|
||||
try:
|
||||
kc.keymaps.remove(km)
|
||||
except Exception:
|
||||
pass
|
||||
addon_keymap_objects.clear()
|
||||
|
||||
|
||||
def sync_addon_keymaps() -> None:
|
||||
"""
|
||||
If "Auto-load FA Keymap" is on and the active preset is `fa_hotkeys`, register
|
||||
the addon keymap layer; otherwise remove it. Safe to call often (switcher, prefs, msgbus).
|
||||
"""
|
||||
prefs = _get_prefs()
|
||||
if not prefs or not getattr(prefs, "auto_load_keymap", False):
|
||||
unregister_keymaps()
|
||||
return
|
||||
if is_fa_keyconfig_active():
|
||||
unregister_keymaps()
|
||||
register_keymaps()
|
||||
else:
|
||||
unregister_keymaps()
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def _mcp_load_post_sync(_a, _b) -> None:
|
||||
try:
|
||||
sync_addon_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Maya Config Pro: keymap sync after load: {e}")
|
||||
|
||||
|
||||
def _msgbus_keyconfig(_):
|
||||
try:
|
||||
sync_addon_keymaps()
|
||||
except Exception as e:
|
||||
print(f"Warning: Maya Config Pro: keymap sync: {e}")
|
||||
|
||||
|
||||
def register_keymap_listeners() -> None:
|
||||
if _mcp_load_post_sync not in bpy.app.handlers.load_post:
|
||||
bpy.app.handlers.load_post.append(_mcp_load_post_sync)
|
||||
try:
|
||||
rna_t = type(bpy.context.preferences.keymap)
|
||||
bpy.msgbus.subscribe_rna(
|
||||
key=(rna_t, "active_keyconfig"),
|
||||
owner=_msgbus_owner,
|
||||
args=(),
|
||||
notify=_msgbus_keyconfig,
|
||||
options={"PERSISTENT"},
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Warning: Maya Config Pro: keyconfig msgbus: {e}")
|
||||
|
||||
|
||||
def unregister_keymap_listeners() -> None:
|
||||
try:
|
||||
bpy.msgbus.clear_by_owner(_msgbus_owner)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
while _mcp_load_post_sync in bpy.app.handlers.load_post:
|
||||
bpy.app.handlers.load_post.remove(_mcp_load_post_sync)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# Classes to register (none needed for keyconfig)
|
||||
_classes = []
|
||||
|
||||
|
||||
def register():
|
||||
"""Register keymap module"""
|
||||
for cls in _classes:
|
||||
compat.safe_register_class(cls)
|
||||
|
||||
# Don't auto-register keymaps here - let preferences control this
|
||||
# register_keymaps()
|
||||
register_keymap_listeners()
|
||||
|
||||
|
||||
def unregister():
|
||||
"""Unregister keymap module"""
|
||||
unregister_keymap_listeners()
|
||||
unregister_keymaps()
|
||||
|
||||
for cls in reversed(_classes):
|
||||
compat.safe_unregister_class(cls)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
keyconfig_version = (5, 0, 120)
|
||||
keyconfig_version = (5, 0, 121)
|
||||
keyconfig_data = \
|
||||
[("3D View",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
@@ -107,15 +107,7 @@ keyconfig_data = \
|
||||
],
|
||||
},
|
||||
),
|
||||
("view3d.view_center_camera", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_center_lock", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS', "repeat": True},
|
||||
{"properties":
|
||||
[("center", False),
|
||||
],
|
||||
},
|
||||
),
|
||||
("mcp.view_frame_home", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS', "ctrl": True, "repeat": True},
|
||||
{"properties":
|
||||
@@ -1534,6 +1526,211 @@ keyconfig_data = \
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Generic",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("wm.context_toggle",
|
||||
{"type": 'N', "value": 'PRESS'},
|
||||
{"properties": [('data_path', 'space_data.show_region_ui')]}),
|
||||
("nla.tweakmode_enter",
|
||||
{"type": 'TAB', "value": 'PRESS'},
|
||||
{"properties": [('use_upper_stack_evaluation', True)]}),
|
||||
("nla.tweakmode_exit",
|
||||
{"type": 'TAB', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.tweakmode_enter",
|
||||
{"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('isolate_action', True)]}),
|
||||
("nla.tweakmode_exit",
|
||||
{"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('isolate_action', True)]}),
|
||||
("anim.channels_select_filter",
|
||||
{"type": 'F', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Tracks",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("nla.channels_click",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.channels_click",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('extend', True)]}),
|
||||
("nla.tracks_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('above_selected', False)]}),
|
||||
("nla.tracks_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
{"properties": [('above_selected', True)]}),
|
||||
("nla.tracks_delete",
|
||||
{"type": 'X', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.tracks_delete",
|
||||
{"type": 'DEL', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_channel_context_menu')]}),
|
||||
("wm.call_menu",
|
||||
{"type": 'APP', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_channel_context_menu')]}),
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Editor",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("nla.click_select",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('deselect_all', True)]}),
|
||||
("nla.click_select",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('extend', True)]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
|
||||
{"properties": [('mode', 'CHECK')]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True},
|
||||
{"properties": [('mode', 'CHECK'), ('extend', True)]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFT_BRACKET', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'LEFT')]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'RIGHT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'PRESS'},
|
||||
{"properties": [('action', 'SELECT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'PRESS', "alt": True},
|
||||
{"properties": [('action', 'DESELECT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'I', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [('action', 'INVERT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'DOUBLE_CLICK'},
|
||||
{"properties": [('action', 'DESELECT')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'B', "value": 'PRESS'},
|
||||
{"properties": [('axis_range', False)]}),
|
||||
("nla.select_box",
|
||||
{"type": 'B', "value": 'PRESS', "alt": True},
|
||||
{"properties": [('axis_range', True)]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
|
||||
{"properties": [('tweak', True), ('mode', 'SET')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
|
||||
{"properties": [('tweak', True), ('mode', 'ADD')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
|
||||
{"properties": [('tweak', True), ('mode', 'SUB')]}),
|
||||
("nla.previewrange_set",
|
||||
{"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
None),
|
||||
("nla.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_all",
|
||||
{"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_selected",
|
||||
{"type": 'NUMPAD_PERIOD', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_frame",
|
||||
{"type": 'NUMPAD_0', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu_pie",
|
||||
{"type": 'ACCENT_GRAVE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_view_pie')]}),
|
||||
("nla.actionclip_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.transition_add",
|
||||
{"type": 'T', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.soundclip_add",
|
||||
{"type": 'K', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.meta_add",
|
||||
{"type": 'G', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
("nla.meta_remove",
|
||||
{"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
None),
|
||||
("nla.duplicate_linked_move",
|
||||
{"type": 'D', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.duplicate_move",
|
||||
{"type": 'D', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("nla.make_single_user",
|
||||
{"type": 'U', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.delete",
|
||||
{"type": 'X', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.delete",
|
||||
{"type": 'DEL', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.split",
|
||||
{"type": 'Y', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.mute_toggle",
|
||||
{"type": 'H', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.swap",
|
||||
{"type": 'F', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("nla.move_up",
|
||||
{"type": 'PAGE_UP', "value": 'PRESS', "repeat": True},
|
||||
None),
|
||||
("nla.move_down",
|
||||
{"type": 'PAGE_DOWN', "value": 'PRESS', "repeat": True},
|
||||
None),
|
||||
("nla.apply_scale",
|
||||
{"type": 'A', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
("nla.clear_scale",
|
||||
{"type": 'S', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("wm.call_menu_pie",
|
||||
{"type": 'S', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('name', 'NLA_MT_snap_pie')]}),
|
||||
("nla.fmodifier_add",
|
||||
{"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
None),
|
||||
("transform.transform",
|
||||
{"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TRANSLATION')]}),
|
||||
("transform.transform",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
|
||||
{"properties": [('mode', 'TRANSLATION')]}),
|
||||
("transform.transform",
|
||||
{"type": 'E', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TIME_EXTEND')]}),
|
||||
("transform.transform",
|
||||
{"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TIME_SCALE')]}),
|
||||
("marker.add",
|
||||
{"type": 'M', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_context_menu')]}),
|
||||
("wm.call_menu",
|
||||
{"type": 'APP', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_context_menu')]}),
|
||||
("anim.change_frame",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('seq_solo_preview', True)]}),
|
||||
],
|
||||
},
|
||||
),
|
||||
|
||||
("Frames",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
@@ -4409,6 +4606,7 @@ keyconfig_data = \
|
||||
("render.view_cancel", {"type": 'ESC', "value": 'PRESS', "repeat": True}, None),
|
||||
("render.view_show", {"type": 'F11', "value": 'PRESS', "repeat": True}, None),
|
||||
("render.play_rendered_anim", {"type": 'F11', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||
("screen.userpref_show", {"type": 'COMMA', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||
],
|
||||
},
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
|
||||
import bpy
|
||||
|
||||
from ..keyconfigs import fa_hotkeys
|
||||
from ..utils import compat
|
||||
from ..utils import deploy as deploy_util
|
||||
|
||||
@@ -14,8 +15,7 @@ class MCP_OT_DeployKeymapPresets(bpy.types.Operator):
|
||||
bl_idname = "mcp.deploy_keymap_presets"
|
||||
bl_label = "Deploy Keymap Presets"
|
||||
bl_description = (
|
||||
"Copy Blender default keymaps (and MCP fa_hotkeys) into your user scripts folder "
|
||||
"so keymap switching buttons work"
|
||||
"Install the MCP fa_hotkeys keymap into your user keyconfig folder so FA Hotkeys works"
|
||||
)
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
|
||||
@@ -23,6 +23,10 @@ class MCP_OT_DeployKeymapPresets(bpy.types.Operator):
|
||||
ok, msg = deploy_util.deploy_keymap_presets()
|
||||
if ok:
|
||||
self.report({"INFO"}, msg.replace("\n", " - "))
|
||||
try:
|
||||
fa_hotkeys.sync_addon_keymaps()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
self.report({"ERROR"}, msg)
|
||||
return {"FINISHED"} if ok else {"CANCELLED"}
|
||||
@@ -62,27 +66,30 @@ class MCP_OT_ActivateKeymapPreset(bpy.types.Operator):
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
if not deploy_util.is_keyconfig_deployed():
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"Keymap presets are not deployed. "
|
||||
"Edit → Preferences → Add-ons → Maya Config Pro → Deploy keymap presets.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
names = {
|
||||
"BLENDER": "Blender.py",
|
||||
"FA_HOTKEYS": "fa_hotkeys.py",
|
||||
"INDUSTRY": "Industry_Compatible.py",
|
||||
}
|
||||
filename = names[self.preset]
|
||||
filepath = os.path.join(deploy_util.user_keyconfig_dir(), filename)
|
||||
if not os.path.isfile(filepath):
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Missing {filename}. Open Preferences and run Deploy keymap presets again.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
if self.preset == "FA_HOTKEYS":
|
||||
if not deploy_util.is_keyconfig_deployed():
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"Keymap is not installed. "
|
||||
"Edit → Preferences → Add-ons → Maya Config Pro → Deploy keymap presets.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
filepath = os.path.join(deploy_util.user_keyconfig_dir(), filename)
|
||||
else:
|
||||
filepath = deploy_util.official_keyconfig_filepath(filename)
|
||||
if not filepath:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Could not find {filename} in this Blender install's keyconfig folder.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
bpy.ops.preferences.keyconfig_activate(filepath=filepath)
|
||||
@@ -97,6 +104,11 @@ class MCP_OT_ActivateKeymapPreset(bpy.types.Operator):
|
||||
elif self.preset in {"BLENDER", "INDUSTRY"}:
|
||||
inputs.view_zoom_axis = "VERTICAL"
|
||||
|
||||
try:
|
||||
fa_hotkeys.sync_addon_keymaps()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Home key: frame camera bounds in camera view, frame all in perspective/ortho.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from ..utils import compat
|
||||
|
||||
|
||||
def _in_camera_view(context) -> bool:
|
||||
r = getattr(context, "region_data", None)
|
||||
if r is None or type(r).__name__ != "RegionView3D":
|
||||
return False
|
||||
vp = getattr(r, "view_perspective", None)
|
||||
if vp is not None:
|
||||
return str(vp) == "CAMERA"
|
||||
return False
|
||||
|
||||
|
||||
class MCP_OT_ViewFrameHome(bpy.types.Operator):
|
||||
bl_idname = "mcp.view_frame_home"
|
||||
bl_label = "Frame View (FA Home)"
|
||||
bl_description = "In camera view: frame camera bounds. Otherwise: frame all"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(getattr(getattr(context, "area", None), "type", None) == "VIEW_3D")
|
||||
|
||||
def execute(self, context):
|
||||
if _in_camera_view(context):
|
||||
return bpy.ops.view3d.view_center_camera()
|
||||
return bpy.ops.view3d.view_all(center=False)
|
||||
|
||||
|
||||
_classes = (MCP_OT_ViewFrameHome,)
|
||||
|
||||
|
||||
def register():
|
||||
for c in _classes:
|
||||
compat.safe_register_class(c)
|
||||
|
||||
|
||||
def unregister():
|
||||
for c in reversed(_classes):
|
||||
compat.safe_unregister_class(c)
|
||||
@@ -1,90 +1,32 @@
|
||||
"""
|
||||
Maya Config Pro - Panels Package
|
||||
UI panels for the extension.
|
||||
"""
|
||||
"""Maya Config Pro - panels package."""
|
||||
|
||||
import sys
|
||||
import importlib.util
|
||||
import os
|
||||
from . import panel_pro
|
||||
from . import panel_sidebar
|
||||
from . import panel_shelf
|
||||
from . import panel_animation
|
||||
|
||||
# Module cache
|
||||
_module_cache = {}
|
||||
|
||||
|
||||
def _load_module(name):
|
||||
"""Load a module by executing its file"""
|
||||
if name in _module_cache:
|
||||
return _module_cache[name]
|
||||
|
||||
# Get the file path
|
||||
current_dir = os.path.dirname(__file__)
|
||||
file_path = os.path.join(current_dir, f"{name}.py")
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
raise ImportError(f"Module file not found: {file_path}")
|
||||
|
||||
# Load the module
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f"{__name__}.{name}",
|
||||
file_path
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
|
||||
# Add to sys.modules temporarily
|
||||
sys.modules[f"{__name__}.{name}"] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
_module_cache[name] = module
|
||||
return module
|
||||
__all__ = (
|
||||
"panel_pro",
|
||||
"panel_sidebar",
|
||||
"panel_shelf",
|
||||
"panel_animation",
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
"""Register all panels"""
|
||||
# Register shelf header first (replaces PROPERTIES panels)
|
||||
try:
|
||||
shelf_header = _load_module("shelf_header")
|
||||
shelf_header.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register shelf header: {e}")
|
||||
|
||||
# Register other panels
|
||||
try:
|
||||
panel_pro = _load_module("panel_pro")
|
||||
panel_pro.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register panel_pro: {e}")
|
||||
|
||||
try:
|
||||
panel_sidebar = _load_module("panel_sidebar")
|
||||
panel_sidebar.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register panel_sidebar: {e}")
|
||||
|
||||
try:
|
||||
panel_shelf = _load_module("panel_shelf")
|
||||
panel_shelf.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register panel_shelf (operators only): {e}")
|
||||
|
||||
try:
|
||||
panel_animation = _load_module("panel_animation")
|
||||
panel_animation.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register panel_animation (operators only): {e}")
|
||||
|
||||
try:
|
||||
viewport_shelf = _load_module("viewport_shelf")
|
||||
viewport_shelf.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register viewport_shelf: {e}")
|
||||
"""Register panels (if this package is ``register()``'d standalone). Viewport
|
||||
anim shelf is registered from the add-on root only, not here (avoid double register).
|
||||
"""
|
||||
for mod in (panel_pro, panel_sidebar, panel_shelf, panel_animation):
|
||||
try:
|
||||
mod.register()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to register {mod.__name__}: {e}")
|
||||
|
||||
|
||||
def unregister():
|
||||
"""Unregister all panels"""
|
||||
# Unregister in reverse order
|
||||
for name in ["viewport_shelf", "panel_animation", "panel_shelf", "panel_sidebar", "panel_pro", "shelf_header"]:
|
||||
for mod in (panel_animation, panel_shelf, panel_sidebar, panel_pro):
|
||||
try:
|
||||
if name in _module_cache:
|
||||
_module_cache[name].unregister()
|
||||
mod.unregister()
|
||||
except Exception as e:
|
||||
print(f"MCP: Failed to unregister {name}: {e}")
|
||||
print(f"MCP: Failed to unregister {mod.__name__}: {e}")
|
||||
|
||||
@@ -7,6 +7,67 @@ import bpy
|
||||
from ..utils import compat
|
||||
|
||||
|
||||
def _addon_id_from_name() -> str:
|
||||
n = __name__
|
||||
if ".panels." in n:
|
||||
return n.split(".panels.", 1)[0]
|
||||
return n.rsplit(".", 2)[0] if n.count(".") >= 2 else n.split(".", 1)[0]
|
||||
|
||||
|
||||
def view3d_set_show_region_tool_header(show: bool) -> None:
|
||||
"""Set ``SpaceView3D.show_region_tool_header`` on every 3D view (Blender 5+)."""
|
||||
for screen in bpy.data.screens:
|
||||
for area in screen.areas:
|
||||
if area.type != "VIEW_3D":
|
||||
continue
|
||||
# Use active space; iterating ``area.spaces`` is unreliable in some builds.
|
||||
sp = getattr(area.spaces, "active", None) or (
|
||||
area.spaces[0] if len(area.spaces) else None
|
||||
)
|
||||
if not sp or sp.type != "VIEW_3D":
|
||||
continue
|
||||
if hasattr(sp, "show_region_tool_header"):
|
||||
sp.show_region_tool_header = show
|
||||
|
||||
|
||||
def draw_animation_shelf_row(
|
||||
layout: bpy.types.UILayout, scale_x: float = 2.0, scale_y: float = 1.0
|
||||
) -> None:
|
||||
"""Same operators as the Scene properties row; larger scale for 3D tool header."""
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = scale_y
|
||||
row.scale_x = scale_x
|
||||
|
||||
row.operator("wm.link", text="", icon="LINKED")
|
||||
row.operator("wm.append", text="", icon="APPEND_BLEND")
|
||||
row.operator("mcp.pose_mode_ani", text="", icon="POSE_HLT")
|
||||
row.operator("object.armature_add", text="", icon="OUTLINER_OB_ARMATURE")
|
||||
row.operator("mcp.drop_to_floor_ani", text="", icon="TRIA_DOWN")
|
||||
|
||||
row.label(text="", icon="DOT")
|
||||
row.operator("mcp.graph_editor_ani", text="", icon="GRAPH")
|
||||
row.operator("mcp.dope_sheet_ani", text="", icon="ACTION")
|
||||
row.operator("mcp.drivers_ani", text="", icon="DRIVER")
|
||||
row.operator("anim.keyframe_insert_menu", text="", icon="DECORATE_ANIMATE")
|
||||
|
||||
|
||||
def _anim_shelf_pref_enabled(context) -> bool:
|
||||
try:
|
||||
p = context.preferences.addons[_addon_id_from_name()].preferences
|
||||
if hasattr(p, "viewport_anim_shelf"):
|
||||
return bool(p.viewport_anim_shelf)
|
||||
except (KeyError, TypeError, AttributeError, ReferenceError):
|
||||
try:
|
||||
for k in list(context.preferences.addons.keys()):
|
||||
pr = getattr(context.preferences.addons[k], "preferences", None)
|
||||
if pr and pr.__class__.__name__ == "MCP_AddonPreferences":
|
||||
if hasattr(pr, "viewport_anim_shelf"):
|
||||
return bool(pr.viewport_anim_shelf)
|
||||
except (AttributeError, TypeError, ReferenceError, KeyError):
|
||||
pass
|
||||
return True
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Animation Shelf Panel
|
||||
# ------------------------------------------------------------------------
|
||||
@@ -22,24 +83,52 @@ class MCP_PT_AnimationShelf(bpy.types.Panel):
|
||||
draw_type = "animation"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = context.object
|
||||
draw_animation_shelf_row(self.layout, scale_x=2.0, scale_y=1.0)
|
||||
|
||||
row = layout.row()
|
||||
row.scale_y = 1
|
||||
row.scale_x = 2
|
||||
|
||||
row.operator('wm.link', text="", icon="LINKED")
|
||||
row.operator('wm.append', text="", icon="APPEND_BLEND")
|
||||
row.operator('mcp.pose_mode_ani', text="", icon="POSE_HLT")
|
||||
row.operator('object.armature_add', text="", icon="OUTLINER_OB_ARMATURE")
|
||||
row.operator('mcp.drop_to_floor_ani', text="", icon='TRIA_DOWN')
|
||||
# ------------------------------------------------------------------------
|
||||
# 3D View — Tool header (native Layout, not POST_PIXEL)
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
row.label(text="", icon='DOT')
|
||||
row.operator('mcp.graph_editor_ani', text="", icon="GRAPH")
|
||||
row.operator('mcp.dope_sheet_ani', text="", icon="ACTION")
|
||||
row.operator('mcp.drivers_ani', text="", icon="DRIVER")
|
||||
row.operator('anim.keyframe_insert_menu', text="", icon="DECORATE_ANIMATE")
|
||||
|
||||
class MCP_PT_AnimToolHeader(bpy.types.Panel):
|
||||
"""Larger icon row in the 3D View tool header (below the main top bar)."""
|
||||
bl_idname = "MCP_PT_AnimToolHeader"
|
||||
bl_label = "Animation"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOL_HEADER"
|
||||
bl_order = 20
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not _anim_shelf_pref_enabled(context):
|
||||
return False
|
||||
if context.space_data is None or context.space_data.type != "VIEW_3D":
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
draw_animation_shelf_row(self.layout, scale_x=2.4, scale_y=1.35)
|
||||
|
||||
|
||||
class MCP_PT_AnimNPanel(bpy.types.Panel):
|
||||
"""Same shelf in the 3D View sidebar (N) so it is always available."""
|
||||
bl_idname = "MCP_PT_AnimNPanel"
|
||||
bl_label = "Animation shelf"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Animation"
|
||||
bl_order = 2
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not _anim_shelf_pref_enabled(context):
|
||||
return False
|
||||
if context.space_data is None or context.space_data.type != "VIEW_3D":
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
draw_animation_shelf_row(self.layout, scale_x=1.0, scale_y=1.1)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
@@ -180,6 +269,8 @@ class MCP_OT_DropToFloorAni(bpy.types.Operator):
|
||||
_classes = [
|
||||
# Panel
|
||||
MCP_PT_AnimationShelf,
|
||||
MCP_PT_AnimToolHeader,
|
||||
MCP_PT_AnimNPanel,
|
||||
# Animation Operators
|
||||
MCP_OT_PropertiesEditorAni,
|
||||
MCP_OT_LinkRigAni,
|
||||
@@ -191,11 +282,35 @@ _classes = [
|
||||
]
|
||||
|
||||
|
||||
_th_once = False
|
||||
|
||||
|
||||
def _deferred_show_tool_header():
|
||||
global _th_once
|
||||
if _th_once or bpy.app.background:
|
||||
return None
|
||||
_th_once = True
|
||||
if not _anim_shelf_pref_enabled(bpy.context):
|
||||
return None
|
||||
try:
|
||||
view3d_set_show_region_tool_header(True)
|
||||
except (ReferenceError, TypeError, AttributeError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def register():
|
||||
for cls in _classes:
|
||||
compat.safe_register_class(cls)
|
||||
if not bpy.app.background:
|
||||
try:
|
||||
bpy.app.timers.register(_deferred_show_tool_header, first_interval=0.15)
|
||||
except (RuntimeError, TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
|
||||
def unregister():
|
||||
global _th_once
|
||||
_th_once = False
|
||||
for cls in reversed(_classes):
|
||||
compat.safe_unregister_class(cls)
|
||||
|
||||
-384
@@ -1,384 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import os
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
DIRNAME, FILENAME = os.path.split(__file__)
|
||||
IDNAME = os.path.splitext(FILENAME)[0]
|
||||
|
||||
|
||||
def update_fn(_self, _context):
|
||||
load()
|
||||
|
||||
|
||||
class Prefs(bpy.types.KeyConfigPreferences):
|
||||
bl_idname = IDNAME
|
||||
|
||||
select_mouse: EnumProperty(
|
||||
name="Select Mouse",
|
||||
items=(
|
||||
('LEFT', "Left",
|
||||
"Use left mouse button for selection. "
|
||||
"The standard behavior that works well for mouse, trackpad and tablet devices"),
|
||||
('RIGHT', "Right",
|
||||
"Use right mouse button for selection, and left mouse button for actions. "
|
||||
"This works well primarily for keyboard and mouse devices"),
|
||||
),
|
||||
description=(
|
||||
"Mouse button used for selection"
|
||||
),
|
||||
update=update_fn,
|
||||
)
|
||||
spacebar_action: EnumProperty(
|
||||
name="Spacebar Action",
|
||||
items=(
|
||||
('PLAY', "Play",
|
||||
"Toggle animation playback "
|
||||
"('Shift-Space' for Tools)",
|
||||
1),
|
||||
('TOOL', "Tools",
|
||||
"Open the popup tool-bar\n"
|
||||
"When 'Space' is held and used as a modifier:\n"
|
||||
"\u2022 Pressing the tools binding key switches to it immediately.\n"
|
||||
"\u2022 Dragging the cursor over a tool and releasing activates it (like a pie menu).\n"
|
||||
"For Play use 'Shift-Space'",
|
||||
0),
|
||||
('SEARCH', "Search",
|
||||
"Open the operator search popup",
|
||||
2),
|
||||
),
|
||||
description=(
|
||||
"Action when 'Space' is pressed"
|
||||
),
|
||||
default='PLAY',
|
||||
update=update_fn,
|
||||
)
|
||||
tool_key_mode: EnumProperty(
|
||||
name="Tool Keys",
|
||||
description=(
|
||||
"The method of keys to activate tools such as move, rotate & scale (G, R, S)"
|
||||
),
|
||||
items=(
|
||||
('IMMEDIATE', "Immediate",
|
||||
"Activate actions immediately"),
|
||||
('TOOL', "Active Tool",
|
||||
"Activate the tool for editors that support tools"),
|
||||
),
|
||||
default='IMMEDIATE',
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
rmb_action: EnumProperty(
|
||||
name="Right Mouse Select Action",
|
||||
items=(
|
||||
('TWEAK', "Select & Tweak",
|
||||
"Right mouse always tweaks"),
|
||||
('FALLBACK_TOOL', "Selection Tool",
|
||||
"Right mouse uses the selection tool"),
|
||||
),
|
||||
description=(
|
||||
"Default action for the right mouse button"
|
||||
),
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
# Experimental: only show with developer extras, see: #107785.
|
||||
use_region_toggle_pie: BoolProperty(
|
||||
name="Region Toggle Pie",
|
||||
description=(
|
||||
"N-key opens a pie menu to toggle regions"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
use_alt_click_leader: BoolProperty(
|
||||
name="Alt Click Tool Prompt",
|
||||
description=(
|
||||
"Tapping Alt (without pressing any other keys) shows a prompt in the status-bar, "
|
||||
"prompting a second keystroke to activate the tool"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
# NOTE: expose `use_alt_tool` and `use_alt_cursor` as two options in the UI
|
||||
# as the tool-tips and titles are different enough depending on RMB/LMB select.
|
||||
use_alt_tool: BoolProperty(
|
||||
name="Alt Tool Access",
|
||||
description=(
|
||||
"Hold Alt to use the active tool when the gizmo would normally be required\n"
|
||||
"Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
use_alt_cursor: BoolProperty(
|
||||
name="Alt Cursor Access",
|
||||
description=(
|
||||
"Hold Alt-LMB to place the Cursor (instead of LMB), allows tools to activate on press instead of drag.\n"
|
||||
"Incompatible with the input preference \"Emulate 3 Button Mouse\" when the \"Alt\" key is used"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
# end note.
|
||||
|
||||
use_select_all_toggle: BoolProperty(
|
||||
name="Select All Toggles",
|
||||
description=(
|
||||
"Causes select-all ('A' key) to de-select in the case a selection exists"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
gizmo_action: EnumProperty(
|
||||
name="Activate Gizmo",
|
||||
items=(
|
||||
('PRESS', "Press", "Press causes immediate activation, preventing click being passed to the tool"),
|
||||
('DRAG', "Drag", "Drag allows click events to pass through to the tool, adding a small delay"),
|
||||
),
|
||||
description="Activation event for gizmos that support drag motion",
|
||||
default='DRAG',
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
# 3D View
|
||||
use_v3d_tab_menu: BoolProperty(
|
||||
name="Tab for Pie Menu",
|
||||
description=(
|
||||
"Causes tab to open pie menu (swaps 'Tab' / 'Ctrl-Tab')"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
use_v3d_shade_ex_pie: BoolProperty(
|
||||
name="Extra Shading Pie Menu Items",
|
||||
description=(
|
||||
"Show additional options in the shading menu ('Z')"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
v3d_tilde_action: EnumProperty(
|
||||
name="Tilde Action",
|
||||
items=(
|
||||
('VIEW', "Navigate",
|
||||
"View operations (useful for keyboards without a numpad)",
|
||||
0),
|
||||
('GIZMO', "Gizmos",
|
||||
"Control transform gizmos",
|
||||
1),
|
||||
),
|
||||
description=(
|
||||
"Action when 'Tilde' is pressed"
|
||||
),
|
||||
default='VIEW',
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
v3d_mmb_action: EnumProperty(
|
||||
name="MMB Action",
|
||||
items=(
|
||||
('ORBIT', "Orbit",
|
||||
"",
|
||||
0),
|
||||
('PAN', "Pan",
|
||||
"",
|
||||
1),
|
||||
),
|
||||
description=(
|
||||
"The action when Middle-Mouse dragging in the viewport. "
|
||||
"Shift-Middle-Mouse is used for the other action. "
|
||||
"This applies to trackpad as well"
|
||||
),
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
v3d_alt_mmb_drag_action: EnumProperty(
|
||||
name="Alt-MMB Drag Action",
|
||||
items=(
|
||||
('RELATIVE', "Relative",
|
||||
"Set the view axis where each mouse direction maps to an axis relative to the current orientation",
|
||||
0),
|
||||
('ABSOLUTE', "Absolute",
|
||||
"Set the view axis where each mouse direction always maps to the same axis",
|
||||
1),
|
||||
),
|
||||
description=(
|
||||
"Action when Alt-MMB dragging in the 3D viewport"
|
||||
),
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
# Developer note, this is an experimental option.
|
||||
use_pie_click_drag: BoolProperty(
|
||||
name="Pie Menu on Drag",
|
||||
description=(
|
||||
"Activate some pie menus on drag,\n"
|
||||
"allowing the tapping the same key to have a secondary action.\n"
|
||||
"\n"
|
||||
"\u2022 Tapping Tab in the 3D view toggles edit-mode, drag for mode menu.\n"
|
||||
"\u2022 Tapping Z in the 3D view toggles wireframe, drag for draw modes.\n"
|
||||
"\u2022 Tapping Tilde in the 3D view for first person navigation, drag for view axes"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
use_file_single_click: BoolProperty(
|
||||
name="Open Folders on Single Click",
|
||||
description=(
|
||||
"Navigate into folders by clicking on them once instead of twice"
|
||||
),
|
||||
default=False,
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
use_alt_navigation: BoolProperty(
|
||||
name="Transform Navigation with Alt",
|
||||
description=(
|
||||
"During transformations, use Alt to navigate in the 3D View. "
|
||||
"Note that if disabled, hotkeys for Proportional Editing, "
|
||||
"Automatic Constraints, and Auto IK Chain Length will require holding Alt"
|
||||
),
|
||||
default=True,
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
def draw(self, layout):
|
||||
from bpy import context
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
prefs = context.preferences
|
||||
|
||||
show_developer_ui = prefs.view.show_developer_ui
|
||||
is_select_left = (self.select_mouse == 'LEFT')
|
||||
use_mouse_emulate_3_button = (
|
||||
prefs.inputs.use_mouse_emulate_3_button and
|
||||
prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
|
||||
)
|
||||
|
||||
# General settings.
|
||||
col = layout.column()
|
||||
col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True)
|
||||
col.row().prop(self, "spacebar_action", text="Spacebar Action", expand=True)
|
||||
|
||||
if is_select_left:
|
||||
col.row().prop(self, "gizmo_action", text="Activate Gizmo Event", expand=True)
|
||||
else:
|
||||
col.row().prop(self, "rmb_action", text="Right Mouse Select Action", expand=True)
|
||||
|
||||
col.row().prop(self, "tool_key_mode", expand=True)
|
||||
|
||||
# Check-box sub-layout.
|
||||
col = layout.column()
|
||||
sub = col.column(align=True)
|
||||
row = sub.row()
|
||||
row.prop(self, "use_alt_click_leader")
|
||||
|
||||
rowsub = row.row()
|
||||
if is_select_left:
|
||||
rowsub.prop(self, "use_alt_tool")
|
||||
else:
|
||||
rowsub.prop(self, "use_alt_cursor")
|
||||
rowsub.active = not use_mouse_emulate_3_button
|
||||
|
||||
row = sub.row()
|
||||
row.prop(self, "use_select_all_toggle")
|
||||
|
||||
if show_developer_ui:
|
||||
row = sub.row()
|
||||
row.prop(self, "use_region_toggle_pie")
|
||||
|
||||
# 3DView settings.
|
||||
col = layout.column()
|
||||
col.label(text="3D View")
|
||||
col.row().prop(self, "v3d_tilde_action", text="Grave Accent / Tilde Action", expand=True)
|
||||
col.row().prop(self, "v3d_mmb_action", text="Middle Mouse Action", expand=True)
|
||||
col.row().prop(self, "v3d_alt_mmb_drag_action", text="Alt Middle Mouse Drag Action", expand=True)
|
||||
|
||||
# Check-boxes sub-layout.
|
||||
col = layout.column()
|
||||
sub = col.column(align=True)
|
||||
sub.prop(self, "use_v3d_tab_menu")
|
||||
sub.prop(self, "use_pie_click_drag")
|
||||
sub.prop(self, "use_v3d_shade_ex_pie")
|
||||
sub.prop(self, "use_alt_navigation")
|
||||
|
||||
# File Browser settings.
|
||||
col = layout.column()
|
||||
col.label(text="File Browser")
|
||||
col.row().prop(self, "use_file_single_click")
|
||||
|
||||
|
||||
blender_default = bpy.utils.execfile(os.path.join(DIRNAME, "keymap_data", "blender_default.py"))
|
||||
|
||||
|
||||
def load():
|
||||
from sys import platform
|
||||
from bpy import context
|
||||
from bl_keymap_utils.io import keyconfig_init_from_data
|
||||
|
||||
prefs = context.preferences
|
||||
kc = context.window_manager.keyconfigs.new(IDNAME)
|
||||
kc_prefs = kc.preferences
|
||||
|
||||
show_developer_ui = prefs.view.show_developer_ui
|
||||
is_select_left = (kc_prefs.select_mouse == 'LEFT')
|
||||
use_mouse_emulate_3_button = (
|
||||
prefs.inputs.use_mouse_emulate_3_button and
|
||||
prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
|
||||
)
|
||||
|
||||
keyconfig_data = blender_default.generate_keymaps(
|
||||
blender_default.Params(
|
||||
select_mouse=kc_prefs.select_mouse,
|
||||
use_mouse_emulate_3_button=use_mouse_emulate_3_button,
|
||||
spacebar_action=kc_prefs.spacebar_action,
|
||||
use_key_activate_tools=(kc_prefs.tool_key_mode == 'TOOL'),
|
||||
use_region_toggle_pie=(show_developer_ui and kc_prefs.use_region_toggle_pie),
|
||||
v3d_tilde_action=kc_prefs.v3d_tilde_action,
|
||||
use_v3d_mmb_pan=(kc_prefs.v3d_mmb_action == 'PAN'),
|
||||
v3d_alt_mmb_drag_action=kc_prefs.v3d_alt_mmb_drag_action,
|
||||
use_select_all_toggle=kc_prefs.use_select_all_toggle,
|
||||
use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu,
|
||||
use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie,
|
||||
use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'),
|
||||
use_fallback_tool=True,
|
||||
use_fallback_tool_select_handled=(
|
||||
# LMB doesn't need additional selection fallback key-map items.
|
||||
False if is_select_left else
|
||||
# RMB is select and RMB must trigger the fallback tool.
|
||||
# Otherwise LMB activates the fallback tool and RMB always tweak-selects.
|
||||
(kc_prefs.rmb_action != 'FALLBACK_TOOL')
|
||||
),
|
||||
use_alt_tool_or_cursor=(
|
||||
(not use_mouse_emulate_3_button) and
|
||||
(kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor)
|
||||
),
|
||||
use_alt_click_leader=kc_prefs.use_alt_click_leader,
|
||||
use_pie_click_drag=kc_prefs.use_pie_click_drag,
|
||||
use_file_single_click=kc_prefs.use_file_single_click,
|
||||
use_alt_navigation=kc_prefs.use_alt_navigation,
|
||||
),
|
||||
)
|
||||
|
||||
if platform == "darwin":
|
||||
from bl_keymap_utils.platform_helpers import keyconfig_data_oskey_from_ctrl_for_macos
|
||||
keyconfig_data = keyconfig_data_oskey_from_ctrl_for_macos(keyconfig_data)
|
||||
|
||||
keyconfig_init_from_data(kc, keyconfig_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bpy.utils.register_class(Prefs)
|
||||
load()
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2018-2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Notes on this key-map:
|
||||
#
|
||||
# This uses Blender's key-map, running with `legacy=True`.
|
||||
#
|
||||
# The intention of this key-map is to match Blender 2.7x which had many more key-map items available.
|
||||
#
|
||||
# There are some differences with the original Blender 2.7x key-map.
|
||||
# There is no intention to change these are they are not considered significant
|
||||
# enough to make a 1:1 match with the previous Blender version.
|
||||
#
|
||||
# These include:
|
||||
#
|
||||
# 3D View
|
||||
# =======
|
||||
#
|
||||
# - Border Render (`Shift-B` -> `Ctrl-B`)
|
||||
# Both `Shift-B` and `Ctrl-B` were used.
|
||||
#
|
||||
# Time Line/Animation Views
|
||||
# =========================
|
||||
#
|
||||
# - Start Frame/End Frame (`S/E` -> `Ctrl-Home/Ctrl-End`)
|
||||
#
|
||||
|
||||
import os
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
DIRNAME, FILENAME = os.path.split(__file__)
|
||||
IDNAME = os.path.splitext(FILENAME)[0]
|
||||
|
||||
|
||||
def update_fn(_self, _context):
|
||||
load()
|
||||
|
||||
|
||||
class Prefs(bpy.types.KeyConfigPreferences):
|
||||
bl_idname = IDNAME
|
||||
|
||||
select_mouse: EnumProperty(
|
||||
name="Select Mouse",
|
||||
items=(
|
||||
('LEFT', "Left",
|
||||
"Use left mouse button for selection. "
|
||||
"The standard behavior that works well for mouse, trackpad and tablet devices"),
|
||||
('RIGHT', "Right",
|
||||
"Use right mouse button for selection, and left mouse button for actions. "
|
||||
"This works well primarily for keyboard and mouse devices"),
|
||||
),
|
||||
description=(
|
||||
"Mouse button used for selection"
|
||||
),
|
||||
default='RIGHT',
|
||||
update=update_fn,
|
||||
)
|
||||
|
||||
def draw(self, layout):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.row().prop(self, "select_mouse", text="Select with Mouse Button", expand=True)
|
||||
|
||||
|
||||
blender_default = bpy.utils.execfile(os.path.join(DIRNAME, "keymap_data", "blender_default.py"))
|
||||
|
||||
|
||||
def load():
|
||||
from sys import platform
|
||||
from bpy import context
|
||||
from bl_keymap_utils.io import keyconfig_init_from_data
|
||||
|
||||
prefs = context.preferences
|
||||
kc = context.window_manager.keyconfigs.new(IDNAME)
|
||||
kc_prefs = kc.preferences
|
||||
|
||||
keyconfig_data = blender_default.generate_keymaps(
|
||||
blender_default.Params(
|
||||
select_mouse=kc_prefs.select_mouse,
|
||||
use_mouse_emulate_3_button=(
|
||||
prefs.inputs.use_mouse_emulate_3_button and
|
||||
prefs.inputs.mouse_emulate_3_button_modifier == 'ALT'
|
||||
),
|
||||
spacebar_action='SEARCH',
|
||||
use_select_all_toggle=True,
|
||||
use_gizmo_drag=False,
|
||||
legacy=True,
|
||||
),
|
||||
)
|
||||
|
||||
if platform == "darwin":
|
||||
from bl_keymap_utils.platform_helpers import keyconfig_data_oskey_from_ctrl_for_macos
|
||||
keyconfig_data = keyconfig_data_oskey_from_ctrl_for_macos(keyconfig_data)
|
||||
|
||||
keyconfig_init_from_data(kc, keyconfig_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bpy.utils.register_class(Prefs)
|
||||
load()
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2019-2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import os
|
||||
import bpy
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Keymap
|
||||
|
||||
DIRNAME, FILENAME = os.path.split(__file__)
|
||||
IDNAME = os.path.splitext(FILENAME)[0]
|
||||
|
||||
|
||||
def update_fn(_self, _context):
|
||||
load()
|
||||
|
||||
|
||||
industry_compatible = bpy.utils.execfile(os.path.join(DIRNAME, "keymap_data", "industry_compatible_data.py"))
|
||||
|
||||
|
||||
def load():
|
||||
from sys import platform
|
||||
from bl_keymap_utils.io import keyconfig_init_from_data
|
||||
|
||||
prefs = bpy.context.preferences
|
||||
|
||||
kc = bpy.context.window_manager.keyconfigs.new(IDNAME)
|
||||
params = industry_compatible.Params(use_mouse_emulate_3_button=prefs.inputs.use_mouse_emulate_3_button)
|
||||
keyconfig_data = industry_compatible.generate_keymaps(params)
|
||||
|
||||
if platform == "darwin":
|
||||
from bl_keymap_utils.platform_helpers import keyconfig_data_oskey_from_ctrl_for_macos
|
||||
keyconfig_data = keyconfig_data_oskey_from_ctrl_for_macos(keyconfig_data)
|
||||
|
||||
keyconfig_init_from_data(kc, keyconfig_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
load()
|
||||
+208
-10
@@ -1,4 +1,4 @@
|
||||
keyconfig_version = (5, 0, 120)
|
||||
keyconfig_version = (5, 0, 121)
|
||||
keyconfig_data = \
|
||||
[("3D View",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
@@ -107,15 +107,7 @@ keyconfig_data = \
|
||||
],
|
||||
},
|
||||
),
|
||||
("view3d.view_center_camera", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_center_lock", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS', "repeat": True},
|
||||
{"properties":
|
||||
[("center", False),
|
||||
],
|
||||
},
|
||||
),
|
||||
("mcp.view_frame_home", {"type": 'HOME', "value": 'PRESS', "repeat": True}, None),
|
||||
("view3d.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS', "ctrl": True, "repeat": True},
|
||||
{"properties":
|
||||
@@ -1534,6 +1526,211 @@ keyconfig_data = \
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Generic",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("wm.context_toggle",
|
||||
{"type": 'N', "value": 'PRESS'},
|
||||
{"properties": [('data_path', 'space_data.show_region_ui')]}),
|
||||
("nla.tweakmode_enter",
|
||||
{"type": 'TAB', "value": 'PRESS'},
|
||||
{"properties": [('use_upper_stack_evaluation', True)]}),
|
||||
("nla.tweakmode_exit",
|
||||
{"type": 'TAB', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.tweakmode_enter",
|
||||
{"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('isolate_action', True)]}),
|
||||
("nla.tweakmode_exit",
|
||||
{"type": 'TAB', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('isolate_action', True)]}),
|
||||
("anim.channels_select_filter",
|
||||
{"type": 'F', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Tracks",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("nla.channels_click",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.channels_click",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('extend', True)]}),
|
||||
("nla.tracks_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('above_selected', False)]}),
|
||||
("nla.tracks_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
{"properties": [('above_selected', True)]}),
|
||||
("nla.tracks_delete",
|
||||
{"type": 'X', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.tracks_delete",
|
||||
{"type": 'DEL', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_channel_context_menu')]}),
|
||||
("wm.call_menu",
|
||||
{"type": 'APP', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_channel_context_menu')]}),
|
||||
],
|
||||
},
|
||||
),
|
||||
("NLA Editor",
|
||||
{"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
[("nla.click_select",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('deselect_all', True)]}),
|
||||
("nla.click_select",
|
||||
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('extend', True)]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
|
||||
{"properties": [('mode', 'CHECK')]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True},
|
||||
{"properties": [('mode', 'CHECK'), ('extend', True)]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'LEFT_BRACKET', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'LEFT')]}),
|
||||
("nla.select_leftright",
|
||||
{"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'RIGHT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'PRESS'},
|
||||
{"properties": [('action', 'SELECT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'PRESS', "alt": True},
|
||||
{"properties": [('action', 'DESELECT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'I', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [('action', 'INVERT')]}),
|
||||
("nla.select_all",
|
||||
{"type": 'A', "value": 'DOUBLE_CLICK'},
|
||||
{"properties": [('action', 'DESELECT')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'B', "value": 'PRESS'},
|
||||
{"properties": [('axis_range', False)]}),
|
||||
("nla.select_box",
|
||||
{"type": 'B', "value": 'PRESS', "alt": True},
|
||||
{"properties": [('axis_range', True)]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
|
||||
{"properties": [('tweak', True), ('mode', 'SET')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
|
||||
{"properties": [('tweak', True), ('mode', 'ADD')]}),
|
||||
("nla.select_box",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True},
|
||||
{"properties": [('tweak', True), ('mode', 'SUB')]}),
|
||||
("nla.previewrange_set",
|
||||
{"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
None),
|
||||
("nla.view_all",
|
||||
{"type": 'HOME', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_all",
|
||||
{"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_selected",
|
||||
{"type": 'NUMPAD_PERIOD', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.view_frame",
|
||||
{"type": 'NUMPAD_0', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu_pie",
|
||||
{"type": 'ACCENT_GRAVE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_view_pie')]}),
|
||||
("nla.actionclip_add",
|
||||
{"type": 'A', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.transition_add",
|
||||
{"type": 'T', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.soundclip_add",
|
||||
{"type": 'K', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.meta_add",
|
||||
{"type": 'G', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
("nla.meta_remove",
|
||||
{"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True},
|
||||
None),
|
||||
("nla.duplicate_linked_move",
|
||||
{"type": 'D', "value": 'PRESS', "shift": True},
|
||||
None),
|
||||
("nla.duplicate_move",
|
||||
{"type": 'D', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("nla.make_single_user",
|
||||
{"type": 'U', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.delete",
|
||||
{"type": 'X', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.delete",
|
||||
{"type": 'DEL', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.split",
|
||||
{"type": 'Y', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.mute_toggle",
|
||||
{"type": 'H', "value": 'PRESS'},
|
||||
None),
|
||||
("nla.swap",
|
||||
{"type": 'F', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("nla.move_up",
|
||||
{"type": 'PAGE_UP', "value": 'PRESS', "repeat": True},
|
||||
None),
|
||||
("nla.move_down",
|
||||
{"type": 'PAGE_DOWN', "value": 'PRESS', "repeat": True},
|
||||
None),
|
||||
("nla.apply_scale",
|
||||
{"type": 'A', "value": 'PRESS', "ctrl": True},
|
||||
None),
|
||||
("nla.clear_scale",
|
||||
{"type": 'S', "value": 'PRESS', "alt": True},
|
||||
None),
|
||||
("wm.call_menu_pie",
|
||||
{"type": 'S', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('name', 'NLA_MT_snap_pie')]}),
|
||||
("nla.fmodifier_add",
|
||||
{"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True},
|
||||
None),
|
||||
("transform.transform",
|
||||
{"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TRANSLATION')]}),
|
||||
("transform.transform",
|
||||
{"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
|
||||
{"properties": [('mode', 'TRANSLATION')]}),
|
||||
("transform.transform",
|
||||
{"type": 'E', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TIME_EXTEND')]}),
|
||||
("transform.transform",
|
||||
{"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [('mode', 'TIME_SCALE')]}),
|
||||
("marker.add",
|
||||
{"type": 'M', "value": 'PRESS'},
|
||||
None),
|
||||
("wm.call_menu",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_context_menu')]}),
|
||||
("wm.call_menu",
|
||||
{"type": 'APP', "value": 'PRESS'},
|
||||
{"properties": [('name', 'NLA_MT_context_menu')]}),
|
||||
("anim.change_frame",
|
||||
{"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [('seq_solo_preview', True)]}),
|
||||
],
|
||||
},
|
||||
),
|
||||
|
||||
("Frames",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items":
|
||||
@@ -4409,6 +4606,7 @@ keyconfig_data = \
|
||||
("render.view_cancel", {"type": 'ESC', "value": 'PRESS', "repeat": True}, None),
|
||||
("render.view_show", {"type": 'F11', "value": 'PRESS', "repeat": True}, None),
|
||||
("render.play_rendered_anim", {"type": 'F11', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||
("screen.userpref_show", {"type": 'COMMA', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||
],
|
||||
},
|
||||
),
|
||||
|
||||
@@ -49,13 +49,20 @@ def blender_bundled_keyconfig_dir() -> str | None:
|
||||
|
||||
|
||||
def is_keyconfig_deployed() -> bool:
|
||||
kc = user_keyconfig_dir()
|
||||
if not os.path.isdir(os.path.join(kc, "keymap_data")):
|
||||
return False
|
||||
for name in ("Blender.py", "Industry_Compatible.py", "fa_hotkeys.py"):
|
||||
if not os.path.isfile(os.path.join(kc, name)):
|
||||
return False
|
||||
return True
|
||||
"""True once MCP has copied fa_hotkeys.py into the user keyconfig folder."""
|
||||
return os.path.isfile(os.path.join(user_keyconfig_dir(), "fa_hotkeys.py"))
|
||||
|
||||
|
||||
def official_keyconfig_filepath(name: str) -> str | None:
|
||||
"""
|
||||
Path to a stock keyconfig module shipped with the running Blender (Blender.py, etc.).
|
||||
Not used for fa_hotkeys — that file lives in the user keyconfig after deploy.
|
||||
"""
|
||||
d = blender_bundled_keyconfig_dir()
|
||||
if not d:
|
||||
return None
|
||||
path = os.path.join(d, name)
|
||||
return path if os.path.isfile(path) else None
|
||||
|
||||
|
||||
def is_startup_bundle_available() -> bool:
|
||||
@@ -64,30 +71,22 @@ def is_startup_bundle_available() -> bool:
|
||||
|
||||
def deploy_keymap_presets() -> tuple[bool, str]:
|
||||
"""
|
||||
Copy Blender's default keyconfig tree from LOCAL, then overlay FA hotkeys from this add-on.
|
||||
Install only the MCP fa_hotkeys keyconfig into the user keyconfig folder.
|
||||
Stock Blender/Industry keymaps are activated from the running install, not copied here.
|
||||
"""
|
||||
src_install = blender_bundled_keyconfig_dir()
|
||||
if not src_install:
|
||||
return False, "Could not find Blender's bundled keyconfig folder (full installation required)."
|
||||
|
||||
dst = user_keyconfig_dir()
|
||||
os.makedirs(dst, exist_ok=True)
|
||||
|
||||
try:
|
||||
shutil.copytree(src_install, dst, dirs_exist_ok=True)
|
||||
except OSError as e:
|
||||
return False, f"Could not copy keyconfig presets: {e}"
|
||||
|
||||
portable_fa = os.path.join(bundled_keyconfig_dir(), "fa_hotkeys.py")
|
||||
if os.path.isfile(portable_fa):
|
||||
try:
|
||||
shutil.copy2(portable_fa, os.path.join(dst, "fa_hotkeys.py"))
|
||||
except OSError as e:
|
||||
return False, f"Could not install fa_hotkeys.py: {e}"
|
||||
else:
|
||||
if not os.path.isfile(portable_fa):
|
||||
return False, "Add-on is missing portable/scripts/presets/keyconfig/fa_hotkeys.py."
|
||||
|
||||
return True, f"Keymap presets deployed to:\n{dst}"
|
||||
try:
|
||||
shutil.copy2(portable_fa, os.path.join(dst, "fa_hotkeys.py"))
|
||||
except OSError as e:
|
||||
return False, f"Could not install fa_hotkeys.py: {e}"
|
||||
|
||||
return True, f"fa_hotkeys keymap installed to:\n{dst}"
|
||||
|
||||
|
||||
def deploy_startup_blend() -> tuple[bool, str]:
|
||||
|
||||
Reference in New Issue
Block a user