keymap filter by keybindings in the userpreferences.
- optional, select between name/keybinding. - when key input can't be parsed, alert red to give the user some feedback. - key shortcut parsing could be improved or swapped out for button which grabs shortcut.
This commit is contained in:
parent
2de8dfd784
commit
2de9ce7940
@ -213,14 +213,97 @@ class InputKeyMapPanel:
|
||||
self.draw_km(display_keymaps, kc, kmm, None, layout, level + 1)
|
||||
layout.context_pointer_set("keymap", km)
|
||||
|
||||
def draw_filtered(self, display_keymaps, filter_text, layout):
|
||||
_EVENT_TYPES = set()
|
||||
_EVENT_TYPE_MAP = {}
|
||||
def draw_filtered(self, 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 self._EVENT_TYPES:
|
||||
enum = bpy.types.Event.bl_rna.properties["type"].enum_items
|
||||
self._EVENT_TYPES.update(enum.keys())
|
||||
self._EVENT_TYPE_MAP.update({item.name.replace(" ", "_").upper(): key for key, item in enum.items()})
|
||||
|
||||
del enum
|
||||
self._EVENT_TYPE_MAP.update({
|
||||
"*": 'NUMPAD_ASTERIX',
|
||||
"/": 'NUMPAD_SLASH',
|
||||
"RMB": 'RIGHTMOUSE',
|
||||
"LMB": 'LEFTMOUSE',
|
||||
"MMB": 'MIDDLEMOUSE',
|
||||
})
|
||||
# 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",
|
||||
"cmd": "oskey",
|
||||
"oskey": "oskey",
|
||||
"any": "any",
|
||||
}
|
||||
# KeyMapItem like dict, use for comparing against
|
||||
# attr: state
|
||||
kmi_test_dict = {}
|
||||
|
||||
# initialize? - so if a if a kmi has a MOD assigned it wont show up.
|
||||
#~ for kv in key_mod.values():
|
||||
#~ kmi_test_dict[kv] = False
|
||||
|
||||
# altname: attr
|
||||
for kk, kv in key_mod.items():
|
||||
if kk in filter_text_split:
|
||||
filter_text_split.remove(kk)
|
||||
kmi_test_dict[kv] = True
|
||||
# whats left should be the event type
|
||||
if len(filter_text_split) > 1:
|
||||
return False
|
||||
elif filter_text_split:
|
||||
kmi_type = filter_text_split[0].upper()
|
||||
|
||||
if kmi_type not in self._EVENT_TYPES:
|
||||
# replacement table
|
||||
kmi_type_test = self._EVENT_TYPE_MAP.get(kmi_type)
|
||||
if kmi_type_test is None:
|
||||
# print("Unknown Type:", kmi_type)
|
||||
|
||||
# Partial match
|
||||
for k, v in self._EVENT_TYPE_MAP.items():
|
||||
if kmi_type in k:
|
||||
kmi_type_test = v
|
||||
break
|
||||
if kmi_type in v:
|
||||
kmi_type_test = v
|
||||
break
|
||||
|
||||
if kmi_type_test is None:
|
||||
return False
|
||||
|
||||
kmi_type = kmi_type_test
|
||||
del kmi_type_test
|
||||
|
||||
kmi_test_dict["type"] = kmi_type
|
||||
|
||||
# main filter func, runs many times
|
||||
def filter_func(kmi):
|
||||
for kk, ki in kmi_test_dict.items():
|
||||
if getattr(kmi, kk) != ki:
|
||||
return False
|
||||
return True
|
||||
|
||||
for km, kc in display_keymaps:
|
||||
km = km.active()
|
||||
layout.context_pointer_set("keymap", km)
|
||||
|
||||
filtered_items = [kmi for kmi in km.keymap_items
|
||||
if (filter_text in kmi.idname.lower() or
|
||||
filter_text in kmi.name.lower())]
|
||||
filtered_items = [kmi for kmi in km.keymap_items if filter_func(kmi)]
|
||||
|
||||
if filtered_items:
|
||||
col = layout.column()
|
||||
@ -243,6 +326,7 @@ class InputKeyMapPanel:
|
||||
col = self.indented_layout(layout, 1)
|
||||
subcol = col.split(percentage=0.2).column()
|
||||
subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN')
|
||||
return True
|
||||
|
||||
def draw_hierarchy(self, display_keymaps, layout):
|
||||
from bpy_extras import keyconfig_utils
|
||||
@ -254,6 +338,7 @@ class InputKeyMapPanel:
|
||||
|
||||
wm = context.window_manager
|
||||
kc = wm.keyconfigs.user
|
||||
spref = context.space_data
|
||||
|
||||
col = layout.column()
|
||||
sub = col.column()
|
||||
@ -264,7 +349,7 @@ class InputKeyMapPanel:
|
||||
row = subcol.row(align=True)
|
||||
|
||||
#~ row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:")
|
||||
text = bpy.path.display_name(context.window_manager.keyconfigs.active.name)
|
||||
text = bpy.path.display_name(wm.keyconfigs.active.name)
|
||||
if not text:
|
||||
text = "Blender (default)"
|
||||
row.menu("USERPREF_MT_keyconfigs", text=text)
|
||||
@ -273,17 +358,27 @@ class InputKeyMapPanel:
|
||||
|
||||
#~ layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
|
||||
#~ row.operator("wm.keyconfig_remove", text="", icon='X')
|
||||
|
||||
row.prop(context.space_data, "filter_text", icon='VIEWZOOM')
|
||||
row.separator()
|
||||
rowsub = row.split(align=True, percentage=0.33)
|
||||
# postpone drawing into rowsub, so we can set alert!
|
||||
|
||||
col.separator()
|
||||
|
||||
display_keymaps = keyconfig_utils.keyconfig_merge(kc, kc)
|
||||
if context.space_data.filter_text != "":
|
||||
filter_text = context.space_data.filter_text.lower()
|
||||
self.draw_filtered(display_keymaps, filter_text, col)
|
||||
filter_type = spref.filter_type
|
||||
filter_text = spref.filter_text
|
||||
if filter_text != "":
|
||||
filter_text = filter_text.lower()
|
||||
ok = self.draw_filtered(display_keymaps, filter_type, filter_text, col)
|
||||
else:
|
||||
self.draw_hierarchy(display_keymaps, col)
|
||||
ok = True
|
||||
|
||||
# go back and fill in rowsub
|
||||
rowsub.prop(spref, "filter_type", text="")
|
||||
rowsubsub = rowsub.row(align=True)
|
||||
if not ok:
|
||||
rowsubsub.alert = True
|
||||
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
|
||||
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
|
@ -1004,8 +1004,8 @@ typedef struct SpaceUserPref {
|
||||
ListBase regionbase; /* storage of regions for inactive spaces */
|
||||
int spacetype;
|
||||
|
||||
int pad;
|
||||
|
||||
char pad[3];
|
||||
char filter_type;
|
||||
char filter[64]; /* search term for filtering in the UI */
|
||||
} SpaceUserPref;
|
||||
|
||||
|
@ -3054,13 +3054,24 @@ static void rna_def_space_info(BlenderRNA *brna)
|
||||
|
||||
static void rna_def_space_userpref(BlenderRNA *brna)
|
||||
{
|
||||
static EnumPropertyItem filter_type_items[] = {
|
||||
{0, "NAME", 0, "Name", "Filter based on the operator name"},
|
||||
{1, "KEY", 0, "Key-Binding", "Filter based on key bindings"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "SpaceUserPreferences", "Space");
|
||||
RNA_def_struct_sdna(srna, "SpaceUserPref");
|
||||
RNA_def_struct_ui_text(srna, "Space User Preferences", "User preferences space data");
|
||||
|
||||
|
||||
prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "filter_type");
|
||||
RNA_def_property_enum_items(prop, filter_type_items);
|
||||
RNA_def_property_ui_text(prop, "Filter Type", "Filter method");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "filter_text", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "filter");
|
||||
RNA_def_property_ui_text(prop, "Filter", "Search term for filtering in the UI");
|
||||
|
Loading…
Reference in New Issue
Block a user