UI: support nested tools in toolbar
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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')),)),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user