diff options
Diffstat (limited to 'release/scripts/startup/bl_ui/space_toolsystem_common.py')
-rw-r--r-- | release/scripts/startup/bl_ui/space_toolsystem_common.py | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py new file mode 100644 index 00000000000..aff9a8d6170 --- /dev/null +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -0,0 +1,271 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> +import bpy +from bpy.types import ( + Menu, +) + +__all__ = ( + "ToolSelectPanelHelper", +) + + +class ToolSelectPanelHelper: + """ + Generic Class, can be used for any toolbar. + + - keymap_prefix: + The text prefix for each key-map for this spaces tools. + - tools_all(): + Returns all tools defined. + - tools_from_context(context): + Returns tools available in this context. + + Each tool is a triplet: + ``(tool_name, manipulator_group_idname, keymap_actions)`` + For a separator in the toolbar, use ``None``. + + Where: + ``tool_name`` + is the name to display in the interface. + ``manipulator_group_idname`` + is an optional manipulator group to activate when the tool is set. + ``keymap_actions`` + 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 item is not None: + if ToolSelectPanelHelper._tool_is_group(item): + for sub_item in item: + if sub_item is not None: + yield sub_item + else: + yield item + + @classmethod + def _tool_vars_from_def(cls, item): + text, mp_idname, actions = item + km, km_idname = (None, None) if actions is None else cls._tool_keymap[text] + return (km_idname, mp_idname) + + @staticmethod + def _tool_vars_from_active_with_index(context): + workspace = context.workspace + return ( + (workspace.tool_keymap or None, workspace.tool_manipulator_group or None), + workspace.tool_index, + ) + + @staticmethod + def _tool_vars_from_button_with_index(context): + props = context.button_operator + return ( + (props.keymap or None or None, props.manipulator_group or None), + props.index, + ) + + @classmethod + def _km_actionmouse_simple(cls, kc, text, actions): + + # standalone + def props_assign_recursive(rna_props, py_props): + for prop_id, value in py_props.items(): + if isinstance(value, dict): + props_assign_recursive(getattr(rna_props, prop_id), value) + else: + setattr(rna_props, prop_id, value) + + km_idname = cls.keymap_prefix + text + km = kc.keymaps.get(km_idname) + if km is not None: + return km, km_idname + km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW') + for op_idname, op_props_dict, kmi_kwargs in actions: + kmi = km.keymap_items.new(op_idname, **kmi_kwargs) + kmi_props = kmi.properties + if op_props_dict: + props_assign_recursive(kmi.properties, op_props_dict) + return km, km_idname + + @classmethod + def register(cls): + wm = bpy.context.window_manager + + # XXX, should we be manipulating the user-keyconfig on load? + # Perhaps this should only add when keymap items don't already exist. + # + # This needs some careful consideration. + kc = wm.keyconfigs.user + + # {tool_name: (keymap, keymap_idname, manipulator_group_idname), ...} + cls._tool_keymap = {} + + # Track which tool-group was last used for non-active groups. + # Blender stores the active tool-group index. + # + # {tool_name_first: index_in_group, ...} + cls._tool_group_active = {} + + # ignore in background mode + if kc is None: + return + + for item in ToolSelectPanelHelper._tools_flatten(cls.tools_all()): + text, mp_idname, actions = item + if actions is not None: + km, km_idname = cls._km_actionmouse_simple(kc, text, actions) + cls._tool_keymap[text] = km, km_idname + + def draw(self, context): + # XXX, this UI isn't very nice. + # We might need to create new button types for this. + # Since we probably want: + # - tool-tips that include multiple key shortcuts. + # - ability to click and hold to expose sub-tools. + + workspace = context.workspace + tool_def_active, index_active = self._tool_vars_from_active_with_index(context) + layout = self.layout + + for tool_items in self.tools_from_context(context): + if tool_items: + col = layout.column(align=True) + for item in tool_items: + if item is None: + col = layout.column(align=True) + continue + + if self._tool_is_group(item): + is_active = False + i = 0 + for i, sub_item in enumerate(item): + if sub_item is None: + continue + tool_def = self._tool_vars_from_def(sub_item) + is_active = (tool_def == tool_def_active) + if is_active: + index = i + break + del i, sub_item + + if is_active: + # not ideal, write this every time :S + self._tool_group_active[item[0][0]] = index + else: + index = self._tool_group_active.get(item[0][0], 0) + + item = item[index] + use_menu = True + else: + index = -1 + use_menu = False + + tool_def = self._tool_vars_from_def(item) + is_active = (tool_def == tool_def_active) + + if use_menu: + props = col.operator_menu_hold( + "wm.tool_set", + text=item[0], + depress=is_active, + menu="WM_MT_toolsystem_submenu", + ) + else: + props = col.operator( + "wm.tool_set", + text=item[0], + depress=is_active, + ) + + props.keymap = tool_def[0] or "" + props.manipulator_group = tool_def[1] 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: + tool_def_button, index_button = cls._tool_vars_from_button_with_index(context) + + 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] + tool_def = cls._tool_vars_from_def(item) + is_active = (tool_def == tool_def_button) + 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("Unable to find toolbar group") + return + + index = 0 + for item in item_group: + if item is None: + layout.separator() + continue + tool_def = cls._tool_vars_from_def(item) + props = layout.operator( + "wm.tool_set", + text=item[0], + ) + props.keymap = tool_def[0] or "" + props.manipulator_group = tool_def[1] 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) |