2023-08-16 00:20:26 +10:00
|
|
|
# SPDX-FileCopyrightText: 2010-2023 Blender Authors
|
2023-06-15 13:09:04 +10:00
|
|
|
#
|
2022-02-11 09:07:11 +11:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
__all__ = (
|
|
|
|
"draw_entry",
|
|
|
|
"draw_km",
|
|
|
|
"draw_kmi",
|
|
|
|
"draw_filtered",
|
|
|
|
"draw_hierarchy",
|
|
|
|
"draw_keymaps",
|
2018-07-03 06:27:53 +02:00
|
|
|
)
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
|
2010-04-14 07:09:01 +00:00
|
|
|
import bpy
|
2022-08-12 12:07:52 +10:00
|
|
|
from bpy.app.translations import (
|
|
|
|
contexts as i18n_contexts,
|
|
|
|
pgettext_iface as iface_,
|
|
|
|
)
|
2013-03-25 11:35:42 +00:00
|
|
|
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2025-05-01 14:50:49 +02:00
|
|
|
def _is_operator_available(idname):
|
|
|
|
module, _, operator = idname.partition(".")
|
|
|
|
|
|
|
|
# Check if the module and operator exist.
|
|
|
|
return (
|
|
|
|
module and
|
|
|
|
operator and
|
|
|
|
getattr(getattr(bpy.ops, module, None), operator, None) is not None
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
def _indented_layout(layout, level):
|
|
|
|
indentpx = 16
|
|
|
|
if level == 0:
|
|
|
|
level = 0.0001 # Tweak so that a percentage of 0 won't split by half
|
|
|
|
indent = level * indentpx / bpy.context.region.width
|
2010-09-14 16:45:24 +00:00
|
|
|
|
2018-08-28 12:38:54 +10:00
|
|
|
split = layout.split(factor=indent)
|
2013-07-08 07:25:33 +00:00
|
|
|
col = split.column()
|
|
|
|
col = split.column()
|
|
|
|
return col
|
2010-09-14 16:45:24 +00:00
|
|
|
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
def draw_entry(display_keymaps, entry, col, level=0):
|
|
|
|
idname, spaceid, regionid, children = entry
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
for km, kc in display_keymaps:
|
|
|
|
if km.name == idname and km.space_type == spaceid and km.region_type == regionid:
|
|
|
|
draw_km(display_keymaps, kc, km, children, col, level)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
'''
|
|
|
|
km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid)
|
|
|
|
if not km:
|
|
|
|
kc = defkc
|
2010-08-30 13:50:59 +00:00
|
|
|
km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
if km:
|
|
|
|
draw_km(kc, km, children, col, level)
|
|
|
|
'''
|
2010-04-14 07:09:01 +00:00
|
|
|
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
def draw_km(display_keymaps, kc, km, children, layout, level):
|
|
|
|
km = km.active()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
layout.context_pointer_set("keymap", km)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
col = _indented_layout(layout, level)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2016-02-20 00:42:07 +01:00
|
|
|
row = col.row(align=True)
|
2013-07-08 07:25:33 +00:00
|
|
|
row.prop(km, "show_expanded_children", text="", emboss=False)
|
|
|
|
row.label(text=km.name, text_ctxt=i18n_contexts.id_windowmanager)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2014-01-29 16:36:42 +01:00
|
|
|
if km.is_user_modified or km.is_modal:
|
|
|
|
subrow = row.row()
|
|
|
|
subrow.alignment = 'RIGHT'
|
|
|
|
|
|
|
|
if km.is_user_modified:
|
2019-03-02 00:21:05 +11:00
|
|
|
subrow.operator("preferences.keymap_restore", text="Restore")
|
2024-11-25 13:24:46 +11:00
|
|
|
# Add margin to space the button from the scroll-bar.
|
2024-11-18 15:57:42 +01:00
|
|
|
subrow.separator()
|
2014-01-29 16:36:42 +01:00
|
|
|
if km.is_modal:
|
|
|
|
subrow.label(text="", icon='LINKED')
|
|
|
|
del subrow
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
if km.show_expanded_children:
|
|
|
|
if children:
|
|
|
|
# Put the Parent key map's entries in a 'global' sub-category
|
|
|
|
# equal in hierarchy to the other children categories
|
|
|
|
subcol = _indented_layout(col, level + 1)
|
2016-02-20 00:42:07 +01:00
|
|
|
subrow = subcol.row(align=True)
|
2013-07-08 07:25:33 +00:00
|
|
|
subrow.prop(km, "show_expanded_items", text="", emboss=False)
|
2024-04-27 16:06:51 +10:00
|
|
|
subrow.label(
|
|
|
|
text=iface_("{:s} (Global)").format(iface_(km.name, i18n_contexts.id_windowmanager)),
|
|
|
|
translate=False,
|
|
|
|
)
|
2013-07-08 07:25:33 +00:00
|
|
|
else:
|
|
|
|
km.show_expanded_items = True
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
# Key Map items
|
|
|
|
if km.show_expanded_items:
|
2016-02-20 00:10:28 +01:00
|
|
|
kmi_level = level + 3 if children else level + 1
|
2013-07-08 07:25:33 +00:00
|
|
|
for kmi in km.keymap_items:
|
2016-02-20 00:10:28 +01:00
|
|
|
draw_kmi(display_keymaps, kc, km, kmi, col, kmi_level)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
# "Add New" at end of keymap item list
|
2016-02-20 00:10:28 +01:00
|
|
|
subcol = _indented_layout(col, kmi_level)
|
2018-08-28 12:38:54 +10:00
|
|
|
subcol = subcol.split(factor=0.2).column()
|
2019-03-02 00:21:05 +11:00
|
|
|
subcol.operator("preferences.keyitem_add", text="Add New", text_ctxt=i18n_contexts.id_windowmanager,
|
2018-10-01 10:45:50 +02:00
|
|
|
icon='ADD')
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2016-02-20 00:42:07 +01:00
|
|
|
col.separator()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
# Child key maps
|
|
|
|
if children:
|
|
|
|
for entry in children:
|
|
|
|
draw_entry(display_keymaps, entry, col, level + 1)
|
|
|
|
|
2016-02-20 00:42:07 +01:00
|
|
|
col.separator()
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
|
|
|
|
map_type = kmi.map_type
|
2025-05-01 14:50:49 +02:00
|
|
|
is_op_available = _is_operator_available(kmi.idname)
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
col = _indented_layout(layout, level)
|
|
|
|
|
|
|
|
if kmi.show_expanded:
|
|
|
|
col = col.column(align=True)
|
|
|
|
box = col.box()
|
|
|
|
else:
|
|
|
|
box = col.column()
|
|
|
|
|
2017-03-10 15:10:40 +03:00
|
|
|
split = box.split()
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
# header bar
|
2017-03-10 15:10:40 +03:00
|
|
|
row = split.row(align=True)
|
2013-07-08 07:25:33 +00:00
|
|
|
row.prop(kmi, "show_expanded", text="", emboss=False)
|
|
|
|
row.prop(kmi, "active", text="", emboss=False)
|
|
|
|
|
|
|
|
if km.is_modal:
|
2017-03-10 15:10:40 +03:00
|
|
|
row.separator()
|
2025-05-01 14:50:49 +02:00
|
|
|
row.alert = not kmi.propvalue
|
2013-07-08 07:25:33 +00:00
|
|
|
row.prop(kmi, "propvalue", text="")
|
|
|
|
else:
|
2025-05-01 14:50:49 +02:00
|
|
|
if is_op_available:
|
|
|
|
row.label(text=kmi.name)
|
|
|
|
# The default item when adding a new item is "none"
|
|
|
|
# so consider this unassigned along with an empty string.
|
|
|
|
elif kmi.idname in {"none", ""}:
|
|
|
|
row.alert = True
|
|
|
|
row.label(text="(Unassigned)")
|
|
|
|
else:
|
|
|
|
row.alert = True
|
|
|
|
row.label(text="{:s} (unavailable)".format(kmi.idname), icon='WARNING_LARGE')
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
row = split.row()
|
|
|
|
row.prop(kmi, "map_type", text="")
|
|
|
|
if map_type == 'KEYBOARD':
|
|
|
|
row.prop(kmi, "type", text="", full_event=True)
|
|
|
|
elif map_type == 'MOUSE':
|
|
|
|
row.prop(kmi, "type", text="", full_event=True)
|
|
|
|
elif map_type == 'NDOF':
|
|
|
|
row.prop(kmi, "type", text="", full_event=True)
|
|
|
|
elif map_type == 'TWEAK':
|
|
|
|
subrow = row.row()
|
|
|
|
subrow.prop(kmi, "type", text="")
|
|
|
|
subrow.prop(kmi, "value", text="")
|
|
|
|
elif map_type == 'TIMER':
|
|
|
|
row.prop(kmi, "type", text="")
|
|
|
|
else:
|
|
|
|
row.label()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
if (not kmi.is_user_defined) and kmi.is_user_modified:
|
2019-03-02 00:21:05 +11:00
|
|
|
row.operator("preferences.keyitem_restore", text="", icon='BACK').item_id = kmi.id
|
2013-07-08 07:25:33 +00:00
|
|
|
else:
|
2020-04-20 15:46:10 +02:00
|
|
|
row.operator(
|
|
|
|
"preferences.keyitem_remove",
|
|
|
|
text="",
|
|
|
|
# Abusing the tracking icon, but it works pretty well here.
|
|
|
|
icon=('TRACKING_CLEAR_BACKWARDS' if kmi.is_user_defined else 'X')
|
|
|
|
).item_id = kmi.id
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2024-11-25 13:24:46 +11:00
|
|
|
# Add margin to space the buttons from the scroll-bar.
|
2024-11-18 15:57:42 +01:00
|
|
|
row.separator(factor=0.25 if kmi.show_expanded else 1.0)
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
# Expanded, additional event settings
|
|
|
|
if kmi.show_expanded:
|
2025-03-28 13:15:55 +11:00
|
|
|
from _bpy import _wm_capabilities
|
|
|
|
capabilities = _wm_capabilities()
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
box = col.box()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2018-08-28 12:38:54 +10:00
|
|
|
split = box.split(factor=0.4)
|
2013-07-08 07:25:33 +00:00
|
|
|
sub = split.row()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2010-08-18 07:14:10 +00:00
|
|
|
if km.is_modal:
|
2025-05-01 14:50:49 +02:00
|
|
|
sub.alert = not kmi.propvalue
|
2013-07-08 07:25:33 +00:00
|
|
|
sub.prop(kmi, "propvalue", text="")
|
2010-04-14 07:09:01 +00:00
|
|
|
else:
|
2025-05-01 14:50:49 +02:00
|
|
|
subrow = sub.row()
|
|
|
|
subrow.alert = not is_op_available
|
|
|
|
subrow.prop(kmi, "idname", text="", placeholder="Operator")
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
if map_type not in {'TEXTINPUT', 'TIMER'}:
|
2025-03-28 13:26:09 +11:00
|
|
|
from sys import platform
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
sub = split.column()
|
|
|
|
subrow = sub.row(align=True)
|
|
|
|
|
|
|
|
if map_type == 'KEYBOARD':
|
|
|
|
subrow.prop(kmi, "type", text="", event=True)
|
|
|
|
subrow.prop(kmi, "value", text="")
|
2020-03-06 17:24:12 +11:00
|
|
|
subrow_repeat = subrow.row(align=True)
|
|
|
|
subrow_repeat.active = kmi.value in {'ANY', 'PRESS'}
|
|
|
|
subrow_repeat.prop(kmi, "repeat", text="Repeat")
|
2013-07-08 07:25:33 +00:00
|
|
|
elif map_type in {'MOUSE', 'NDOF'}:
|
|
|
|
subrow.prop(kmi, "type", text="")
|
|
|
|
subrow.prop(kmi, "value", text="")
|
|
|
|
|
2022-03-02 15:07:00 +11:00
|
|
|
if map_type in {'KEYBOARD', 'MOUSE'} and kmi.value == 'CLICK_DRAG':
|
|
|
|
subrow = sub.row()
|
|
|
|
subrow.prop(kmi, "direction")
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
subrow = sub.row()
|
|
|
|
subrow.scale_x = 0.75
|
2019-01-21 17:20:43 +01:00
|
|
|
subrow.prop(kmi, "any", toggle=True)
|
2025-03-28 13:26:09 +11:00
|
|
|
|
|
|
|
# Match text in `WM_key_event_string`.
|
|
|
|
match platform:
|
|
|
|
case "darwin":
|
|
|
|
oskey_label = "Cmd"
|
|
|
|
case "win32":
|
|
|
|
oskey_label = "Win"
|
|
|
|
case _:
|
|
|
|
oskey_label = "OS"
|
|
|
|
|
2021-09-16 23:20:57 +10:00
|
|
|
# Use `*_ui` properties as integers aren't practical.
|
|
|
|
subrow.prop(kmi, "shift_ui", toggle=True)
|
|
|
|
subrow.prop(kmi, "ctrl_ui", toggle=True)
|
|
|
|
subrow.prop(kmi, "alt_ui", toggle=True)
|
2025-03-28 13:26:09 +11:00
|
|
|
subrow.prop(kmi, "oskey_ui", text=oskey_label, toggle=True)
|
2021-09-16 23:20:57 +10:00
|
|
|
|
2025-03-25 23:32:41 +00:00
|
|
|
# On systems that don't support Hyper, only show if it's enabled.
|
|
|
|
# Otherwise the user may have a key binding that doesn't work and can't be changed.
|
2025-03-28 13:15:55 +11:00
|
|
|
if capabilities['KEYBOARD_HYPER_KEY'] or kmi.hyper == 1:
|
2025-03-25 23:32:41 +00:00
|
|
|
subrow.prop(kmi, "hyper_ui", text="Hyper", toggle=True)
|
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
subrow.prop(kmi, "key_modifier", text="", event=True)
|
|
|
|
|
|
|
|
# Operator properties
|
|
|
|
box.template_keymap_item_properties(kmi)
|
|
|
|
|
|
|
|
# Modal key maps attached to this operator
|
|
|
|
if not km.is_modal:
|
|
|
|
kmm = kc.keymaps.find_modal(kmi.idname)
|
|
|
|
if kmm:
|
|
|
|
draw_km(display_keymaps, kc, kmm, None, layout, level + 1)
|
|
|
|
layout.context_pointer_set("keymap", km)
|
|
|
|
|
2018-07-03 06:27:53 +02:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
_EVENT_TYPES = set()
|
|
|
|
_EVENT_TYPE_MAP = {}
|
2014-02-03 15:44:24 +01:00
|
|
|
_EVENT_TYPE_MAP_EXTRA = {}
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
def draw_filtered(display_keymaps, filter_type, filter_text, layout):
|
|
|
|
|
|
|
|
if filter_type == 'NAME':
|
|
|
|
def filter_func(kmi):
|
|
|
|
return (filter_text in kmi.idname.lower() or
|
|
|
|
filter_text in kmi.name.lower())
|
|
|
|
else:
|
|
|
|
if not _EVENT_TYPES:
|
|
|
|
enum = bpy.types.Event.bl_rna.properties["type"].enum_items
|
|
|
|
_EVENT_TYPES.update(enum.keys())
|
|
|
|
_EVENT_TYPE_MAP.update({item.name.replace(" ", "_").upper(): key
|
|
|
|
for key, item in enum.items()})
|
|
|
|
|
|
|
|
del enum
|
2014-02-03 15:44:24 +01:00
|
|
|
_EVENT_TYPE_MAP_EXTRA.update({
|
2013-07-08 07:25:33 +00:00
|
|
|
"`": 'ACCENT_GRAVE',
|
|
|
|
"*": 'NUMPAD_ASTERIX',
|
|
|
|
"/": 'NUMPAD_SLASH',
|
2017-09-21 16:34:48 +12:00
|
|
|
'+': 'NUMPAD_PLUS',
|
2020-06-23 17:54:51 +10:00
|
|
|
"-": 'NUMPAD_MINUS',
|
|
|
|
".": 'NUMPAD_PERIOD',
|
|
|
|
"'": 'QUOTE',
|
2013-07-08 07:25:33 +00:00
|
|
|
"RMB": 'RIGHTMOUSE',
|
|
|
|
"LMB": 'LEFTMOUSE',
|
|
|
|
"MMB": 'MIDDLEMOUSE',
|
2018-07-03 06:27:53 +02:00
|
|
|
})
|
2014-02-03 15:44:24 +01:00
|
|
|
_EVENT_TYPE_MAP_EXTRA.update({
|
2024-04-27 16:06:51 +10:00
|
|
|
"{:d}".format(i): "NUMPAD_{:d}".format(i) for i in range(10)
|
2018-07-03 06:27:53 +02:00
|
|
|
})
|
2013-07-08 07:25:33 +00:00
|
|
|
# done with once off init
|
|
|
|
|
|
|
|
filter_text_split = filter_text.strip()
|
|
|
|
filter_text_split = filter_text.split()
|
|
|
|
|
|
|
|
# Modifier {kmi.attribute: name} mapping
|
|
|
|
key_mod = {
|
|
|
|
"ctrl": "ctrl",
|
|
|
|
"alt": "alt",
|
|
|
|
"shift": "shift",
|
|
|
|
"oskey": "oskey",
|
2025-03-25 23:32:41 +00:00
|
|
|
"hyper": "hyper",
|
2013-07-08 07:25:33 +00:00
|
|
|
"any": "any",
|
2025-01-28 02:12:06 +01:00
|
|
|
|
|
|
|
# macOS specific modifiers names
|
|
|
|
"control": "ctrl",
|
|
|
|
"option": "alt",
|
|
|
|
"cmd": "oskey",
|
|
|
|
"command": "oskey",
|
2018-07-03 06:27:53 +02:00
|
|
|
}
|
2013-07-08 07:25:33 +00:00
|
|
|
# KeyMapItem like dict, use for comparing against
|
2014-01-07 19:27:44 +11:00
|
|
|
# attr: {states, ...}
|
2013-07-08 07:25:33 +00:00
|
|
|
kmi_test_dict = {}
|
2014-02-26 00:35:00 +11:00
|
|
|
# Special handling of 'type' using a list if sets,
|
|
|
|
# keymap items must match against all.
|
|
|
|
kmi_test_type = []
|
2013-07-08 07:25:33 +00:00
|
|
|
|
2021-06-22 10:42:32 -07:00
|
|
|
# initialize? - so if a kmi has a MOD assigned it won't show up.
|
2018-07-03 06:27:53 +02:00
|
|
|
# for kv in key_mod.values():
|
|
|
|
# kmi_test_dict[kv] = {False}
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
# altname: attr
|
|
|
|
for kk, kv in key_mod.items():
|
|
|
|
if kk in filter_text_split:
|
|
|
|
filter_text_split.remove(kk)
|
2014-01-07 19:27:44 +11:00
|
|
|
kmi_test_dict[kv] = {True}
|
2014-02-26 00:35:00 +11:00
|
|
|
|
2019-07-31 14:25:09 +02:00
|
|
|
# what's left should be the event type
|
2014-02-26 00:35:00 +11:00
|
|
|
def kmi_type_set_from_string(kmi_type):
|
|
|
|
kmi_type = kmi_type.upper()
|
2014-01-07 19:27:44 +11:00
|
|
|
kmi_type_set = set()
|
2013-07-08 07:25:33 +00:00
|
|
|
|
2014-01-07 19:27:44 +11:00
|
|
|
if kmi_type in _EVENT_TYPES:
|
|
|
|
kmi_type_set.add(kmi_type)
|
2014-02-17 14:58:14 +01:00
|
|
|
|
|
|
|
if not kmi_type_set or len(kmi_type) > 1:
|
2013-07-08 07:25:33 +00:00
|
|
|
# replacement table
|
2014-02-03 15:44:24 +01:00
|
|
|
for event_type_map in (_EVENT_TYPE_MAP, _EVENT_TYPE_MAP_EXTRA):
|
|
|
|
kmi_type_test = event_type_map.get(kmi_type)
|
|
|
|
if kmi_type_test is not None:
|
|
|
|
kmi_type_set.add(kmi_type_test)
|
|
|
|
else:
|
|
|
|
# print("Unknown Type:", kmi_type)
|
|
|
|
|
|
|
|
# Partial match
|
|
|
|
for k, v in event_type_map.items():
|
|
|
|
if (kmi_type in k) or (kmi_type in v):
|
|
|
|
kmi_type_set.add(v)
|
2014-02-26 00:35:00 +11:00
|
|
|
return kmi_type_set
|
|
|
|
|
|
|
|
for i, kmi_type in enumerate(filter_text_split):
|
|
|
|
kmi_type_set = kmi_type_set_from_string(kmi_type)
|
2014-02-03 15:44:24 +01:00
|
|
|
|
2014-02-17 14:58:14 +01:00
|
|
|
if not kmi_type_set:
|
|
|
|
return False
|
2014-02-26 00:35:00 +11:00
|
|
|
|
|
|
|
kmi_test_type.append(kmi_type_set)
|
|
|
|
# tiny optimization, sort sets so the smallest is first
|
|
|
|
# improve chances of failing early
|
|
|
|
kmi_test_type.sort(key=lambda kmi_type_set: len(kmi_type_set))
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
# main filter func, runs many times
|
|
|
|
def filter_func(kmi):
|
|
|
|
for kk, ki in kmi_test_dict.items():
|
2014-02-26 00:13:20 +11:00
|
|
|
val = getattr(kmi, kk)
|
2014-02-26 00:35:00 +11:00
|
|
|
if val not in ki:
|
|
|
|
return False
|
|
|
|
|
|
|
|
# special handling of 'type'
|
|
|
|
for ki in kmi_test_type:
|
|
|
|
val = kmi.type
|
2014-02-26 00:13:20 +11:00
|
|
|
if val == 'NONE' or val not in ki:
|
|
|
|
# exception for 'type'
|
|
|
|
# also inspect 'key_modifier' as a fallback
|
2014-02-26 00:35:00 +11:00
|
|
|
val = kmi.key_modifier
|
|
|
|
if not (val == 'NONE' or val not in ki):
|
|
|
|
continue
|
2013-07-08 07:25:33 +00:00
|
|
|
return False
|
2014-02-26 00:35:00 +11:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
return True
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
for km, kc in display_keymaps:
|
|
|
|
km = km.active()
|
|
|
|
layout.context_pointer_set("keymap", km)
|
2010-04-17 19:05:53 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
filtered_items = [kmi for kmi in km.keymap_items if filter_func(kmi)]
|
2011-01-01 07:20:34 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
if filtered_items:
|
|
|
|
col = layout.column()
|
2011-01-01 07:20:34 +00:00
|
|
|
|
2023-11-29 22:36:05 +01:00
|
|
|
row = col.row(align=True)
|
2022-11-15 11:02:22 +01:00
|
|
|
row.label(text=km.name, icon='DOT',
|
|
|
|
text_ctxt=i18n_contexts.id_windowmanager)
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
if km.is_user_modified:
|
2023-11-29 22:36:05 +01:00
|
|
|
subrow = row.row()
|
|
|
|
subrow.alignment = 'RIGHT'
|
|
|
|
subrow.operator("preferences.keymap_restore", text="Restore")
|
2024-11-25 13:24:46 +11:00
|
|
|
# Add margin to space the button from the scroll-bar.
|
2024-11-18 15:57:42 +01:00
|
|
|
subrow.separator()
|
2010-04-14 07:09:01 +00:00
|
|
|
|
2013-07-08 07:25:33 +00:00
|
|
|
for kmi in filtered_items:
|
|
|
|
draw_kmi(display_keymaps, kc, km, kmi, col, 1)
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def draw_hierarchy(display_keymaps, layout):
|
2018-11-20 11:36:44 +11:00
|
|
|
from bl_keymap_utils import keymap_hierarchy
|
|
|
|
for entry in keymap_hierarchy.generate():
|
2013-07-08 07:25:33 +00:00
|
|
|
draw_entry(display_keymaps, entry, layout)
|
|
|
|
|
|
|
|
|
|
|
|
def draw_keymaps(context, layout):
|
2018-11-20 10:56:50 +11:00
|
|
|
from bl_keymap_utils.io import keyconfig_merge
|
2013-07-08 07:25:33 +00:00
|
|
|
|
|
|
|
wm = context.window_manager
|
2018-11-20 09:15:53 +11:00
|
|
|
kc_user = wm.keyconfigs.user
|
|
|
|
kc_active = wm.keyconfigs.active
|
2013-07-08 07:25:33 +00:00
|
|
|
spref = context.space_data
|
|
|
|
|
2018-07-03 06:27:53 +02:00
|
|
|
# row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config")
|
2019-01-25 13:45:56 +11:00
|
|
|
text = bpy.path.display_name(kc_active.name, has_ext=False)
|
2013-07-08 07:25:33 +00:00
|
|
|
if not text:
|
|
|
|
text = "Blender (default)"
|
2019-01-04 21:40:16 +01:00
|
|
|
|
2019-01-16 18:42:26 +01:00
|
|
|
split = layout.split(factor=0.6)
|
2019-01-04 21:40:16 +01:00
|
|
|
|
2019-01-16 18:42:26 +01:00
|
|
|
row = split.row()
|
2019-01-04 21:40:16 +01:00
|
|
|
|
|
|
|
rowsub = row.row(align=True)
|
|
|
|
|
|
|
|
rowsub.menu("USERPREF_MT_keyconfigs", text=text)
|
|
|
|
rowsub.operator("wm.keyconfig_preset_add", text="", icon='ADD')
|
2024-03-14 22:40:04 +01:00
|
|
|
rowsub.operator("wm.keyconfig_preset_remove", text="", icon='REMOVE')
|
2013-07-08 07:25:33 +00:00
|
|
|
|
2019-01-16 18:42:26 +01:00
|
|
|
rowsub = split.row(align=True)
|
2019-03-02 00:21:05 +11:00
|
|
|
rowsub.operator("preferences.keyconfig_import", text="Import...", icon='IMPORT')
|
|
|
|
rowsub.operator("preferences.keyconfig_export", text="Export...", icon='EXPORT')
|
2019-01-16 18:42:26 +01:00
|
|
|
|
|
|
|
row = layout.row()
|
|
|
|
col = layout.column()
|
|
|
|
|
2018-07-03 06:27:53 +02:00
|
|
|
# layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
|
2019-03-02 00:21:05 +11:00
|
|
|
# row.operator("preferences.keyconfig_remove", text="", icon='X')
|
2020-03-09 16:54:38 +01:00
|
|
|
rowsub = row.split(factor=0.4, align=True)
|
2013-07-08 07:25:33 +00:00
|
|
|
# postpone drawing into rowsub, so we can set alert!
|
|
|
|
|
2018-11-20 09:15:53 +11:00
|
|
|
layout.separator()
|
2018-11-20 10:56:50 +11:00
|
|
|
display_keymaps = keyconfig_merge(kc_user, kc_user)
|
2013-07-08 07:25:33 +00:00
|
|
|
filter_type = spref.filter_type
|
|
|
|
filter_text = spref.filter_text.strip()
|
|
|
|
if filter_text:
|
|
|
|
filter_text = filter_text.lower()
|
2018-11-20 09:15:53 +11:00
|
|
|
ok = draw_filtered(display_keymaps, filter_type, filter_text, layout)
|
2013-07-08 07:25:33 +00:00
|
|
|
else:
|
2018-11-20 09:15:53 +11:00
|
|
|
draw_hierarchy(display_keymaps, layout)
|
2013-07-08 07:25:33 +00:00
|
|
|
ok = True
|
|
|
|
|
|
|
|
# go back and fill in rowsub
|
2020-03-09 16:54:38 +01:00
|
|
|
rowsubsub = rowsub.row(align=True)
|
|
|
|
rowsubsub.prop(spref, "filter_type", expand=True)
|
2013-07-08 07:25:33 +00:00
|
|
|
rowsubsub = rowsub.row(align=True)
|
|
|
|
if not ok:
|
|
|
|
rowsubsub.alert = True
|
2024-04-10 16:36:58 +02:00
|
|
|
search_placeholder = ""
|
|
|
|
if spref.filter_type == 'NAME':
|
|
|
|
search_placeholder = iface_("Search by Name")
|
|
|
|
elif spref.filter_type == 'KEY':
|
|
|
|
search_placeholder = iface_("Search by Key-Binding")
|
|
|
|
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM', placeholder=search_placeholder)
|
2018-11-20 09:15:53 +11:00
|
|
|
|
2018-11-22 07:52:34 +11:00
|
|
|
if not filter_text:
|
2021-05-21 22:19:46 +10:00
|
|
|
# When the keyconfig defines its own preferences.
|
2018-11-22 07:52:34 +11:00
|
|
|
kc_prefs = kc_active.preferences
|
|
|
|
if kc_prefs is not None:
|
|
|
|
box = col.box()
|
|
|
|
row = box.row(align=True)
|
|
|
|
|
2019-01-04 21:40:16 +01:00
|
|
|
pref = context.preferences
|
|
|
|
keymappref = pref.keymap
|
|
|
|
show_ui_keyconfig = keymappref.show_ui_keyconfig
|
2018-11-22 07:52:34 +11:00
|
|
|
row.prop(
|
2019-01-04 21:40:16 +01:00
|
|
|
keymappref,
|
2018-11-22 07:52:34 +11:00
|
|
|
"show_ui_keyconfig",
|
|
|
|
text="",
|
2019-01-04 21:40:16 +01:00
|
|
|
icon='DISCLOSURE_TRI_DOWN' if show_ui_keyconfig else 'DISCLOSURE_TRI_RIGHT',
|
2018-11-22 07:52:34 +11:00
|
|
|
emboss=False,
|
|
|
|
)
|
|
|
|
row.label(text="Preferences")
|
|
|
|
|
|
|
|
if show_ui_keyconfig:
|
|
|
|
# Defined by user preset, may contain mistakes out of our control.
|
|
|
|
try:
|
|
|
|
kc_prefs.draw(box)
|
2024-10-01 13:18:46 +10:00
|
|
|
except Exception:
|
2018-11-22 07:52:34 +11:00
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
del box
|
|
|
|
del kc_prefs
|