First version of updated key map editor UI (in User Preferences)

Now the key maps are displayed in a hierarchical list which you can 
browse through. As well as in the main list, modal key maps are also 
available in context, for example, if you unfold out a Transform key 
map item, you'll be able to fold out and access its modal key map underneath.

More work to do, including search, better operator browsing, etc.
Still need to revise the ordering/hierarchy and clean up naming to be 
consistent too, it's a bit of an 'evolved' mess right now.

Thanks to theeth for some initial work here too.
This commit is contained in:
Matt Ebb 2009-12-16 10:13:26 +00:00
parent 0095b89a67
commit 3455261e17
6 changed files with 316 additions and 129 deletions

@ -19,6 +19,83 @@
# <pep8 compliant>
import bpy
KM_HIERARCHY = [
('Window', 'EMPTY', 'WINDOW', []), # file save, window change, exit
('Screen Editing', 'EMPTY', 'WINDOW', []), # resizing, action corners
('Screen', 'EMPTY', 'WINDOW', []), # full screen, undo, screenshot
('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region)
('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region)
('Header', 'EMPTY', 'WINDOW', []), # header stuff (per region)
('Markers', 'EMPTY', 'WINDOW', []), # markers (per region)
('Animation', 'EMPTY', 'WINDOW', []), # frame change on click, preview range (per region)
('Grease Pencil', 'EMPTY', 'WINDOW', []), # grease pencil stuff (per region)
('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation
('Animation_Channels', 'EMPTY', 'WINDOW', []),
('Buttons Generic', 'PROPERTIES', 'WINDOW', []), # align context menu
('TimeLine', 'TIMELINE', 'WINDOW', []),
('Outliner', 'OUTLINER', 'WINDOW', []),
('View3D', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform)
('Pose', 'EMPTY', 'WINDOW', []),
('Object Mode', 'EMPTY', 'WINDOW', []),
('Vertex Paint', 'EMPTY', 'WINDOW', []),
('Weight Paint', 'EMPTY', 'WINDOW', []),
('Face Mask', 'EMPTY', 'WINDOW', []),
('Sculpt', 'EMPTY', 'WINDOW', []),
('EditMesh', 'EMPTY', 'WINDOW', []),
('Curve', 'EMPTY', 'WINDOW', []),
('Armature', 'EMPTY', 'WINDOW', []),
('Metaball', 'EMPTY', 'WINDOW', []),
('Lattice', 'EMPTY', 'WINDOW', []),
('Armature_Sketch', 'EMPTY', 'WINDOW', []),
('Particle', 'EMPTY', 'WINDOW', []),
('Font', 'EMPTY', 'WINDOW', []),
('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change
('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d
('View3D Generic', 'VIEW_3D', 'WINDOW', []) # toolbar and properties
]),
('GraphEdit Keys', 'GRAPH_EDITOR', 'WINDOW', [
('GraphEdit Generic', 'GRAPH_EDITOR', 'WINDOW', [])
]),
('Image', 'IMAGE_EDITOR', 'WINDOW', [
('UVEdit', 'EMPTY', 'WINDOW', []), # image (reverse order, UVEdit before Image
('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d
('Image Generic', 'IMAGE_EDITOR', 'WINDOW', [])
]),
('Node Generic', 'NODE_EDITOR', 'WINDOW', [
('Node', 'NODE_EDITOR', 'WINDOW', [])
]),
('File', 'FILE_BROWSER', 'WINDOW', [
('FileMain', 'FILE_BROWSER', 'WINDOW', []),
('FileButtons', 'FILE_BROWSER', 'WINDOW', [])
]),
('Action_Keys', 'DOPESHEET_EDITOR', 'WINDOW', []),
('NLA Generic', 'NLA_EDITOR', 'WINDOW', [
('NLA Channels', 'NLA_EDITOR', 'WINDOW', []),
('NLA Data', 'NLA_EDITOR', 'WINDOW', [])
]),
('Script', 'SCRIPTS_WINDOW', 'WINDOW', []),
('Text', 'TEXT_EDITOR', 'WINDOW', []),
('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', []),
('Logic Generic', 'LOGIC_EDITOR', 'WINDOW', []),
('Console', 'CONSOLE', 'WINDOW', []),
('View3D Gesture Circle', 'EMPTY', 'WINDOW', []),
('Gesture Border', 'EMPTY', 'WINDOW', []),
('Standard Modal Map', 'EMPTY', 'WINDOW', []),
('Transform Modal Map', 'EMPTY', 'WINDOW', []),
('View3D Fly Modal', 'EMPTY', 'WINDOW', []),
('View3D Rotate Modal', 'EMPTY', 'WINDOW', []),
('View3D Move Modal', 'EMPTY', 'WINDOW', []),
('View3D Zoom Modal', 'EMPTY', 'WINDOW', []),
]
class USERPREF_HT_header(bpy.types.Header):
bl_space_type = 'USER_PREFERENCES'
@ -1176,28 +1253,171 @@ class USERPREF_PT_input(bpy.types.Panel):
def poll(self, context):
userpref = context.user_preferences
return (userpref.active_section == 'INPUT')
def draw_entry(self, kc, entry, col, level = 0):
idname, spaceid, regionid, children = entry
km = kc.find_keymap(idname, space_type = spaceid, region_type = regionid)
if km:
km = km.active()
self.draw_km(kc, km, children, col, level)
def draw(self, context):
layout = self.layout
def indented_layout(self, 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
split=layout.split(percentage=indent)
col = split.column()
col = split.column()
return col
def draw_km(self, kc, km, children, layout, level):
layout.set_context_pointer("keymap", km)
col = self.indented_layout(layout, level)
row = col.row()
row.prop(km, "children_expanded", text="", no_bg=True)
row.label(text=km.name)
row.label()
row.label()
if km.user_defined:
row.operator("WM_OT_keymap_restore", text="Restore")
else:
row.operator("WM_OT_keymap_edit", text="Edit")
if km.children_expanded:
if children:
# Put the Parent key map's entries in a 'global' sub-category
# equal in hierarchy to the other children categories
subcol = self.indented_layout(col, level + 1)
subrow = subcol.row()
subrow.prop(km, "items_expanded", text="", no_bg=True)
subrow.label(text="%s (Global)" % km.name)
else:
km.items_expanded = True
# Key Map items
if km.items_expanded:
for kmi in km.items:
self.draw_kmi(kc, km, kmi, col, level + 1)
# "Add New" at end of keymap item list
col = self.indented_layout(col, level+1)
subcol = col.split(percentage=0.2).column()
subcol.active = km.user_defined
subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN')
col.separator()
# Child key maps
if children:
subcol = col.column()
row = subcol.row()
for entry in children:
self.draw_entry(kc, entry, col, level + 1)
def draw_kmi(self, kc, km, kmi, layout, level):
layout.set_context_pointer("keyitem", kmi)
col = self.indented_layout(layout, level)
userpref = context.user_preferences
wm = context.manager
#input = userpref.input
#input = userpref
inputs = userpref.inputs
split = layout.split(percentage=0.25)
# General settings
col.enabled = km.user_defined
if km.user_defined:
col = col.column(align=True)
box = col.box()
else:
box = col.column()
split = box.split(percentage=0.4)
# header bar
row = split.row()
row.prop(kmi, "expanded", text="", no_bg=True)
row.prop(kmi, "active", text="", no_bg=True)
if km.modal:
row.prop(kmi, "propvalue", text="")
else:
row.label(text=kmi.name)
row = split.row()
row.prop(kmi, "map_type", text="")
if kmi.map_type == 'KEYBOARD':
row.prop(kmi, "type", text="", full_event=True)
elif kmi.map_type == 'MOUSE':
row.prop(kmi, "type", text="", full_event=True)
elif kmi.map_type == 'TWEAK':
subrow = row.row()
subrow.prop(kmi, "type", text="")
subrow.prop(kmi, "value", text="")
elif kmi.map_type == 'TIMER':
row.prop(kmi, "type", text="")
else:
row.label()
row.operator("wm.keyitem_remove", text="", icon='X')
# Expanded, additional event settings
if kmi.expanded:
box = col.box()
if kmi.map_type not in ('TEXTINPUT', 'TIMER'):
split = box.split(percentage=0.4)
sub = split.row()
if km.modal:
sub.prop(kmi, "propvalue", text="")
else:
sub.prop(kmi, "idname", text="")
sub = split.column()
subrow = sub.row(align=True)
if kmi.map_type == 'KEYBOARD':
subrow.prop(kmi, "type", text="", event=True)
subrow.prop(kmi, "value", text="")
elif kmi.map_type == 'MOUSE':
subrow.prop(kmi, "type", text="")
subrow.prop(kmi, "value", text="")
subrow = sub.row()
subrow.scale_x = 0.75
subrow.prop(kmi, "any")
subrow.prop(kmi, "shift")
subrow.prop(kmi, "ctrl")
subrow.prop(kmi, "alt")
subrow.prop(kmi, "oskey", text="Cmd")
subrow.prop(kmi, "key_modifier", text="", event=True)
# Operator properties
props = kmi.properties
if props is not None:
box.separator()
flow = box.column_flow(columns=2)
for pname in dir(props):
if not props.is_property_hidden(pname):
flow.prop(props, pname)
# Modal key maps attached to this operator
if not km.modal:
kmm = kc.find_keymap_modal(kmi.idname)
if kmm:
self.draw_km(kc, kmm, None, layout, level + 1)
def draw_input_prefs(self, inputs, layout):
# General settings
row = layout.row()
col = row.column()
sub = col.column()
sub.label(text="Configuration:")
sub.prop_object(wm, "active_keyconfig", wm, "keyconfigs", text="")
col.separator()
sub = col.column()
sub.label(text="Mouse:")
sub1 = sub.column()
@ -1243,111 +1463,34 @@ class USERPREF_PT_input(bpy.types.Panel):
sub.prop(inputs, "ndof_rotate_speed", text="Orbit Speed")
row.separator()
def draw(self, context):
layout = self.layout
userpref = context.user_preferences
wm = context.manager
inputs = userpref.inputs
split = layout.split(percentage=0.25)
# Input settings
self.draw_input_prefs(inputs, split)
# Keymap Settings
col = split.column()
# kc = wm.active_keyconfig
defkc = wm.default_keyconfig
km = wm.active_keymap
subsplit = col.split()
subsplit.prop_object(wm, "active_keymap", defkc, "keymaps", text="Map:")
if km.user_defined:
row = subsplit.row()
row.operator("WM_OT_keymap_restore", text="Restore")
row.operator("WM_OT_keymap_restore", text="Restore All").all = True
else:
row = subsplit.row()
row.operator("WM_OT_keymap_edit", text="Edit")
row.label()
sub = col.column()
subrow = sub.row()
subrow.prop_object(wm, "active_keyconfig", wm, "keyconfigs", text="Configuration:")
subrow.label()
col.separator()
for kmi in km.items:
subcol = col.column()
subcol.set_context_pointer("keyitem", kmi)
row = subcol.row()
if kmi.expanded:
row.prop(kmi, "expanded", text="", icon='TRIA_DOWN')
else:
row.prop(kmi, "expanded", text="", icon='TRIA_RIGHT')
itemrow = row.row()
itemrow.enabled = km.user_defined
if kmi.active:
itemrow.prop(kmi, "active", text="", icon='CHECKBOX_HLT')
else:
itemrow.prop(kmi, "active", text="", icon='CHECKBOX_DEHLT')
itemcol = itemrow.column()
itemcol.active = kmi.active
row = itemcol.row()
if km.modal:
row.prop(kmi, "propvalue", text="")
else:
row.prop(kmi, "idname", text="")
sub = row.row()
sub.scale_x = 0.6
sub.prop(kmi, "map_type", text="")
sub = row.row(align=True)
if kmi.map_type == 'KEYBOARD':
sub.prop(kmi, "type", text="", full_event=True)
elif kmi.map_type == 'MOUSE':
sub.prop(kmi, "type", text="", full_event=True)
elif kmi.map_type == 'TWEAK':
sub.scale_x = 0.5
sub.prop(kmi, "type", text="")
sub.prop(kmi, "value", text="")
elif kmi.map_type == 'TIMER':
sub.prop(kmi, "type", text="")
else:
sub.label()
if kmi.expanded:
if kmi.map_type not in ('TEXTINPUT', 'TIMER'):
sub = itemcol.row(align=True)
if kmi.map_type == 'KEYBOARD':
sub.prop(kmi, "type", text="", event=True)
sub.prop(kmi, "value", text="")
elif kmi.map_type == 'MOUSE':
sub.prop(kmi, "type", text="")
sub.prop(kmi, "value", text="")
else:
sub.label()
sub.label()
subrow = sub.row()
subrow.scale_x = 0.75
subrow.prop(kmi, "any")
subrow.prop(kmi, "shift")
subrow.prop(kmi, "ctrl")
subrow.prop(kmi, "alt")
subrow.prop(kmi, "oskey", text="Cmd")
sub.prop(kmi, "key_modifier", text="", event=True)
flow = itemcol.column_flow(columns=2)
props = kmi.properties
if props is not None:
for pname in dir(props):
if not props.is_property_hidden(pname):
flow.prop(props, pname)
itemcol.separator()
itemrow.operator("wm.keyitem_remove", text="", icon='ZOOMOUT')
itemrow = col.row()
itemrow.label()
itemrow.operator("wm.keyitem_add", text="", icon='ZOOMIN')
itemrow.enabled = km.user_defined
for entry in KM_HIERARCHY:
self.draw_entry(defkc, entry, col)
bpy.types.register(USERPREF_HT_header)
bpy.types.register(USERPREF_PT_tabs)
@ -1464,7 +1607,7 @@ class WM_OT_keymap_edit(bpy.types.Operator):
def execute(self, context):
wm = context.manager
km = wm.active_keymap
km = context.keymap # wm.active_keymap
km.copy_to_user()
return ('FINISHED',)
@ -1483,7 +1626,7 @@ class WM_OT_keymap_restore(bpy.types.Operator):
for km in wm.default_keyconfig.keymaps:
km.restore_to_default()
else:
km = wm.active_keymap
km = context.keymap # wm.active_keymap
km.restore_to_default()
return ('FINISHED',)
@ -1496,7 +1639,7 @@ class WM_OT_keyitem_add(bpy.types.Operator):
def execute(self, context):
wm = context.manager
km = wm.active_keymap
km = context.keymap # wm.active_keymap
if km.modal:
km.add_modal_item("", 'A', 'PRESS') # kmi
else:
@ -1512,7 +1655,7 @@ class WM_OT_keyitem_remove(bpy.types.Operator):
def execute(self, context):
wm = context.manager
kmi = context.keyitem
km = wm.active_keymap
km = context.keymap # wm.active_keymap
km.remove_item(kmi)
return ('FINISHED',)

@ -591,6 +591,7 @@ void UI_exit(void);
#define UI_ITEM_R_ICON_ONLY 16
#define UI_ITEM_R_EVENT 32
#define UI_ITEM_R_FULL_EVENT 64
#define UI_ITEM_R_NO_BG 128
uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int size, int em, struct uiStyle *style);
void uiBlockSetCurLayout(uiBlock *block, uiLayout *layout);

@ -880,7 +880,7 @@ void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, Proper
uiBut *but;
PropertyType type;
char namestr[UI_MAX_NAME_STR];
int len, w, h, slider, toggle, expand, icon_only;
int len, w, h, slider, toggle, expand, icon_only, no_bg;
uiBlockSetCurLayout(block, layout);
@ -912,10 +912,14 @@ void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, Proper
toggle= (flag & UI_ITEM_R_TOGGLE);
expand= (flag & UI_ITEM_R_EXPAND);
icon_only= (flag & UI_ITEM_R_ICON_ONLY);
no_bg= (flag & UI_ITEM_R_NO_BG);
/* get size */
ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, &w, &h);
if (no_bg)
uiBlockSetEmboss(block, UI_EMBOSSN);
/* array property */
if(index == RNA_NO_INDEX && len > 0)
ui_item_array(layout, block, name, icon, ptr, prop, len, 0, 0, w, h, expand, slider, toggle, icon_only);
@ -948,6 +952,9 @@ void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, Proper
if(toggle && but->type==OPTION)
but->type= TOG;
}
if (no_bg)
uiBlockSetEmboss(block, UI_EMBOSS);
}
void uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int flag)

@ -290,9 +290,10 @@ typedef struct wmKeyMap {
} wmKeyMap;
/* wmKeyMap.flag */
#define KEYMAP_MODAL 1 /* modal map, not using operatornames */
#define KEYMAP_USER 2 /* user created keymap */
#define KEYMAP_EXPANDED 4
#define KEYMAP_MODAL 1 /* modal map, not using operatornames */
#define KEYMAP_USER 2 /* user created keymap */
#define KEYMAP_EXPANDED 4
#define KEYMAP_CHILDREN_EXPANDED 8
typedef struct wmKeyConfig {
struct wmKeyConfig *next, *prev;

@ -37,7 +37,7 @@
#ifdef RNA_RUNTIME
static void rna_uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int expand, int slider, int toggle, int icon_only, int event, int full_event, int index)
static void rna_uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int expand, int slider, int toggle, int icon_only, int event, int full_event, int no_bg, int index)
{
PropertyRNA *prop= RNA_struct_find_property(ptr, propname);
int flag= 0;
@ -53,6 +53,7 @@ static void rna_uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr,
flag |= (icon_only)? UI_ITEM_R_ICON_ONLY: 0;
flag |= (event)? UI_ITEM_R_EVENT: 0;
flag |= (full_event)? UI_ITEM_R_FULL_EVENT: 0;
flag |= (no_bg)? UI_ITEM_R_NO_BG: 0;
uiItemFullR(layout, name, icon, ptr, prop, index, 0, flag);
}
@ -157,6 +158,7 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_boolean(func, "icon_only", 0, "", "Draw only icons in buttons, no text.");
RNA_def_boolean(func, "event", 0, "", "Use button to input key events.");
RNA_def_boolean(func, "full_event", 0, "", "Use button to input full events including modifiers.");
RNA_def_boolean(func, "no_bg", 0, "", "Don't draw the button itself, just the icon/text.");
RNA_def_int(func, "index", -1, -2, INT_MAX, "", "The index of this button, when set a single member of an array can be accessed, when set to -1 all array members are used.", -2, INT_MAX); /* RNA_NO_INDEX == -1 */
func= RNA_def_function(srna, "props_enum", "uiItemsEnumR");
@ -249,7 +251,7 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_property_flag(parm, PROP_REQUIRED);
parm= RNA_def_pointer(func, "data", "AnyType", "", "Pointer to put in context.");
RNA_def_property_flag(parm, PROP_REQUIRED|PROP_RNAPTR);
/* templates */
func= RNA_def_function(srna, "template_header", "uiTemplateHeader");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);

@ -594,6 +594,26 @@ static void rna_wmKeyMapItem_idname_set(PointerRNA *ptr, const char *value)
}
}
static void rna_wmKeyMapItem_name_get(PointerRNA *ptr, char *value)
{
wmKeyMapItem *kmi= ptr->data;
wmOperatorType *ot= WM_operatortype_find(kmi->idname, 1);
if (ot)
strcpy(value, ot->name);
}
static int rna_wmKeyMapItem_name_length(PointerRNA *ptr)
{
wmKeyMapItem *kmi= ptr->data;
wmOperatorType *ot= WM_operatortype_find(kmi->idname, 1);
if (ot)
return strlen(ot->name);
else
return 0;
}
#else
static void rna_def_operator(BlenderRNA *brna)
@ -904,9 +924,15 @@ static void rna_def_keyconfig(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Modal Keymap", "Indicates that a keymap is used for translate modal events for an operator.");
prop= RNA_def_property(srna, "expanded", PROP_BOOLEAN, PROP_NONE);
prop= RNA_def_property(srna, "items_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYMAP_EXPANDED);
RNA_def_property_ui_text(prop, "Expanded", "Expanded in the user interface.");
RNA_def_property_ui_text(prop, "Items Expanded", "Expanded in the user interface.");
RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1);
prop= RNA_def_property(srna, "children_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYMAP_CHILDREN_EXPANDED);
RNA_def_property_ui_text(prop, "Children Expanded", "Children expanded in the user interface.");
RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1);
RNA_api_keymap(srna);
@ -921,7 +947,12 @@ static void rna_def_keyconfig(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Identifier", "Identifier of operator to call on input event.");
RNA_def_property_string_funcs(prop, "rna_wmKeyMapItem_idname_get", "rna_wmKeyMapItem_idname_length", "rna_wmKeyMapItem_idname_set");
RNA_def_struct_name_property(srna, prop);
prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Name", "Name of operator to call on input event.");
RNA_def_property_string_funcs(prop, "rna_wmKeyMapItem_name_get", "rna_wmKeyMapItem_name_length", NULL);
prop= RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "OperatorProperties");
RNA_def_property_pointer_funcs(prop, "rna_KeyMapItem_properties_get", NULL, NULL);
@ -981,6 +1012,7 @@ static void rna_def_keyconfig(BlenderRNA *brna)
prop= RNA_def_property(srna, "expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", KMI_EXPANDED);
RNA_def_property_ui_text(prop, "Expanded", "Expanded in the user interface.");
RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1);
prop= RNA_def_property(srna, "propvalue", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "propvalue");
@ -991,6 +1023,7 @@ static void rna_def_keyconfig(BlenderRNA *brna)
prop= RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", KMI_INACTIVE);
RNA_def_property_ui_text(prop, "Active", "Activate or deactivate item.");
RNA_def_property_ui_icon(prop, ICON_CHECKBOX_DEHLT, 1);
}
void RNA_def_wm(BlenderRNA *brna)