UI: support nested tools in toolbar

This commit is contained in:
2017-11-02 23:05:13 +11:00
parent 08141260ff
commit 1ca3e1a91d
6 changed files with 179 additions and 30 deletions

View File

@@ -68,6 +68,7 @@ _modules = [
# Generic Space Modules # Generic Space Modules
# #
# Depends on DNA_WORKSPACE_TOOL (C define). # Depends on DNA_WORKSPACE_TOOL (C define).
"space_toolsystem_common",
"space_toolsystem_toolbar", "space_toolsystem_toolbar",
"space_clip", "space_clip",

View File

@@ -18,6 +18,9 @@
# <pep8 compliant> # <pep8 compliant>
import bpy import bpy
from bpy.types import (
Menu,
)
__all__ = ( __all__ = (
"ToolSelectPanelHelper", "ToolSelectPanelHelper",
@@ -48,6 +51,19 @@ class ToolSelectPanelHelper:
an optional triple of: ``(operator_id, operator_properties, keymap_item_args)`` an optional triple of: ``(operator_id, operator_properties, keymap_item_args)``
""" """
@staticmethod
def _tool_is_group(tool):
return type(tool[0]) is not str
@staticmethod
def _tools_flatten(tools):
for item in tools:
if ToolSelectPanelHelper._tool_is_group(item):
for sub_item in item:
yield sub_item
else:
yield item
@classmethod @classmethod
def _km_actionmouse_simple(cls, kc, text, actions): def _km_actionmouse_simple(cls, kc, text, actions):
@@ -85,7 +101,7 @@ class ToolSelectPanelHelper:
if kc is None: if kc is None:
return return
for t in cls.tools_all(): for t in ToolSelectPanelHelper._tools_flatten(cls.tools_all()):
text, mp_idname, actions = t text, mp_idname, actions = t
if actions is not None: if actions is not None:
km, km_idname = cls._km_actionmouse_simple(kc, text, actions) km, km_idname = cls._km_actionmouse_simple(kc, text, actions)
@@ -101,6 +117,7 @@ class ToolSelectPanelHelper:
workspace = context.workspace workspace = context.workspace
km_idname_active = workspace.tool_keymap or None km_idname_active = workspace.tool_keymap or None
mp_idname_active = workspace.tool_manipulator_group or None mp_idname_active = workspace.tool_manipulator_group or None
index_active = workspace.tool_index
layout = self.layout layout = self.layout
for tool_items in self.tools_from_context(context): for tool_items in self.tools_from_context(context):
@@ -110,22 +127,122 @@ class ToolSelectPanelHelper:
if item is None: if item is None:
col = layout.column(align=True) col = layout.column(align=True)
continue continue
text, mp_idname, actions = item
if actions is not None: if self._tool_is_group(item):
km, km_idname = self._tool_keymap[text] index = 0
is_active = False
for i, sub_item in enumerate(item):
text, mp_idname, actions = sub_item
km, km_idname = (None, None) if actions is None else self._tool_keymap[text]
is_active = (
km_idname_active == km_idname and
mp_idname_active == mp_idname
)
if is_active:
index = i
break
del i, sub_item
item = item[index]
use_menu = True
else: else:
km = None index = -1
km_idname = None use_menu = False
props = col.operator( text, mp_idname, actions = item
"wm.tool_set", km, km_idname = (None, None) if actions is None else self._tool_keymap[text]
text=text, is_active = (
depress=( km_idname_active == km_idname and
km_idname_active == km_idname and mp_idname_active == mp_idname
mp_idname_active == mp_idname
),
) )
if use_menu:
props = col.operator_menu_hold(
"wm.tool_set",
text=text,
depress=is_active,
menu="WM_MT_toolsystem_submenu",
)
else:
props = col.operator(
"wm.tool_set",
text=text,
depress=is_active,
)
props.keymap = km_idname or "" props.keymap = km_idname or ""
props.manipulator_group = mp_idname or "" props.manipulator_group = mp_idname or ""
props.index = index
def tools_from_context(cls, context):
return (cls._tools[None], cls._tools.get(context.mode, ()))
# The purpose of this menu is to be a generic popup to select between tools
# in cases when a single tool allows to select alternative tools.
class WM_MT_toolsystem_submenu(Menu):
bl_label = ""
@staticmethod
def _tool_group_from_button(context):
# Lookup the tool definitions based on the space-type.
space_type = context.space_data.type
cls = next(
(cls for cls in ToolSelectPanelHelper.__subclasses__()
if cls.bl_space_type == space_type),
None
)
if cls is not None:
props = context.button_operator
km_idname_button = props.keymap or None
mp_idname_button = props.manipulator_group or None
index_button = props.index
for item_items in cls.tools_from_context(context):
for item_group in item_items:
if (item_group is not None) and ToolSelectPanelHelper._tool_is_group(item_group):
if index_button < len(item_group):
item = item_group[index_button]
text, mp_idname, actions = item
km, km_idname = (None, None) if actions is None else cls._tool_keymap[text]
is_active = (
km_idname_button == km_idname and
mp_idname_button == mp_idname
)
if is_active:
return cls, item_group, index_button
return None, None, -1
def draw(self, context):
layout = self.layout
cls, item_group, index_active = self._tool_group_from_button(context)
if item_group is None:
# Should never happen, just in case
layout.label(f"Unable to find toolbar group")
return
index = 0
for item in item_group:
if item is None:
layout.separator()
continue
text, mp_idname, actions = item
km, km_idname = (None, None) if actions is None else cls._tool_keymap[text]
props = layout.operator(
"wm.tool_set",
text=text,
)
props.keymap = km_idname or ""
props.manipulator_group = mp_idname or ""
props.index = index
index += 1
classes = (
WM_MT_toolsystem_submenu,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@@ -66,20 +66,25 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None: [ None: [
("Cursor", None, ("Cursor", None,
(("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),)), (("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),)),
("Select Border", None, (
("view3d.select_border", dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')), # 'Select' Group
("view3d.select_border", dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)), (
)), ("Select Border", None, (
("Select Circle", None, ( ("view3d.select_border", dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')),
("view3d.select_circle", dict(deselect=False), dict(type='ACTIONMOUSE', value='PRESS')), ("view3d.select_border", dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
("view3d.select_circle", dict(deselect=True), dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)), )),
)), ("Select Circle", None, (
("Select Lasso", None, ( ("view3d.select_circle", dict(deselect=False), dict(type='ACTIONMOUSE', value='PRESS')),
("view3d.select_lasso", ("view3d.select_circle", dict(deselect=True), dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')), )),
("view3d.select_lasso", ("Select Lasso", None, (
dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)), ("view3d.select_lasso",
)), dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')),
("view3d.select_lasso",
dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
)),
),
# End group.
], ],
'OBJECT': [ 'OBJECT': [
*_tools_transform, *_tools_transform,
@@ -122,8 +127,22 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
("mesh.polybuild_hover", dict(use_boundary=True), dict(type='MOUSEMOVE', value='ANY', any=True)), ("mesh.polybuild_hover", dict(use_boundary=True), dict(type='MOUSEMOVE', value='ANY', any=True)),
)), )),
("Knife", None, (("mesh.knife_tool", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),)), # Knife Group
("Bisect", None, (("mesh.bisect", dict(), dict(type='EVT_TWEAK_A', value='ANY')),)), (
("Knife", None, (
("mesh.knife_tool",
dict(wait_for_input=False, use_occlude_geometry=True, only_selected=False),
dict(type='ACTIONMOUSE', value='PRESS')),)),
("Knife (Selected)", None, (
("mesh.knife_tool",
dict(wait_for_input=False, use_occlude_geometry=False, only_selected=True),
dict(type='ACTIONMOUSE', value='PRESS')),)),
("Bisect", None, (
("mesh.bisect",
dict(),
dict(type='EVT_TWEAK_A', value='ANY')),)),
),
# End group.
("Extrude Cursor", None, ("Extrude Cursor", None,
(("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)), (("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)),
], ],

View File

@@ -58,7 +58,8 @@ typedef struct bToolDef {
char keymap[64]; char keymap[64];
char manipulator_group[64]; char manipulator_group[64];
int spacetype; int spacetype;
int _pad; /* index when a tool is a member of a group */
int index;
} bToolDef; } bToolDef;
/** /**

View File

@@ -156,6 +156,11 @@ static void rna_def_workspace(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Active Tool", "Currently active tool manipulator"); RNA_def_property_ui_text(prop, "Active Tool", "Currently active tool manipulator");
RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "tool_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "tool.index");
RNA_def_property_ui_text(prop, "Active Tool Index", "Tool group index");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "orientations", PROP_COLLECTION, PROP_NONE); prop = RNA_def_property(srna, "orientations", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "transform_orientations", NULL); RNA_def_property_collection_sdna(prop, NULL, "transform_orientations", NULL);
RNA_def_property_struct_type(prop, "TransformOrientation"); RNA_def_property_struct_type(prop, "TransformOrientation");

View File

@@ -1760,6 +1760,9 @@ static int wm_operator_tool_set_exec(bContext *C, wmOperator *op)
char id_manipulator_group[sizeof(workspace->tool.manipulator_group)]; char id_manipulator_group[sizeof(workspace->tool.manipulator_group)];
RNA_string_get(op->ptr, "keymap", id_keymap); RNA_string_get(op->ptr, "keymap", id_keymap);
RNA_string_get(op->ptr, "manipulator_group", id_manipulator_group); RNA_string_get(op->ptr, "manipulator_group", id_manipulator_group);
int index = RNA_int_get(op->ptr, "index");
workspace->tool.index = index;
if (workspace->tool.manipulator_group[0]) { if (workspace->tool.manipulator_group[0]) {
wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(workspace->tool.manipulator_group, false); wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(workspace->tool.manipulator_group, false);
@@ -1780,6 +1783,8 @@ static int wm_operator_tool_set_exec(bContext *C, wmOperator *op)
WM_manipulator_group_type_ensure(workspace->tool.manipulator_group); WM_manipulator_group_type_ensure(workspace->tool.manipulator_group);
} }
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }
@@ -1795,6 +1800,7 @@ static void WM_OT_tool_set(wmOperatorType *ot)
RNA_def_string(ot->srna, "keymap", NULL, KMAP_MAX_NAME, "Key Map", ""); RNA_def_string(ot->srna, "keymap", NULL, KMAP_MAX_NAME, "Key Map", "");
RNA_def_string(ot->srna, "manipulator_group", NULL, MAX_NAME, "Manipulator Group", ""); RNA_def_string(ot->srna, "manipulator_group", NULL, MAX_NAME, "Manipulator Group", "");
RNA_def_int(ot->srna, "index", 0, INT_MIN, INT_MAX, "Index", "", INT_MIN, INT_MAX);
} }
#endif /* USE_WORKSPACE_TOOL */ #endif /* USE_WORKSPACE_TOOL */