| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | # ##### 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 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | from bpy.types import ( | 
					
						
							|  |  |  |     Menu, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | __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)`` | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tool_is_group(tool): | 
					
						
							|  |  |  |         return type(tool[0]) is not str | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tools_flatten(tools): | 
					
						
							|  |  |  |         for item in tools: | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |             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, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |     @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 | 
					
						
							| 
									
										
										
										
											2017-11-05 01:38:51 +11:00
										 |  |  |         km = kc.keymaps.get(km_idname) | 
					
						
							|  |  |  |         if km is not None: | 
					
						
							|  |  |  |             return km, km_idname | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |         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 = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |         # Track which tool-group was last used for non-active groups. | 
					
						
							|  |  |  |         # Blender stores the active tool-group index. | 
					
						
							|  |  |  |         # | 
					
						
							| 
									
										
										
										
											2017-11-02 23:30:01 +11:00
										 |  |  |         # {tool_name_first: index_in_group, ...} | 
					
						
							|  |  |  |         cls._tool_group_active = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-26 22:04:48 +11:00
										 |  |  |         # ignore in background mode | 
					
						
							|  |  |  |         if kc is None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |         for item in ToolSelectPanelHelper._tools_flatten(cls.tools_all()): | 
					
						
							|  |  |  |             text, mp_idname, actions = item | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |             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 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |         tool_def_active, index_active = self._tool_vars_from_active_with_index(context) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |         layout = self.layout | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for tool_items in self.tools_from_context(context): | 
					
						
							|  |  |  |             if tool_items: | 
					
						
							| 
									
										
										
										
											2017-11-02 15:52:16 +11:00
										 |  |  |                 col = layout.column(align=True) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |                 for item in tool_items: | 
					
						
							|  |  |  |                     if item is None: | 
					
						
							| 
									
										
										
										
											2017-11-02 15:52:16 +11:00
										 |  |  |                         col = layout.column(align=True) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |                         continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                     if self._tool_is_group(item): | 
					
						
							|  |  |  |                         is_active = False | 
					
						
							| 
									
										
										
										
											2017-11-02 23:30:01 +11:00
										 |  |  |                         i = 0 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                         for i, sub_item in enumerate(item): | 
					
						
							|  |  |  |                             if sub_item is None: | 
					
						
							| 
									
										
										
										
											2017-11-02 23:30:01 +11:00
										 |  |  |                                 continue | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                             tool_def = self._tool_vars_from_def(sub_item) | 
					
						
							|  |  |  |                             is_active = (tool_def == tool_def_active) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                             if is_active: | 
					
						
							|  |  |  |                                 index = i | 
					
						
							|  |  |  |                                 break | 
					
						
							|  |  |  |                         del i, sub_item | 
					
						
							| 
									
										
										
										
											2017-11-02 23:30:01 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |                         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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                         item = item[index] | 
					
						
							|  |  |  |                         use_menu = True | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                         index = -1 | 
					
						
							|  |  |  |                         use_menu = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                     tool_def = self._tool_vars_from_def(item) | 
					
						
							|  |  |  |                     is_active = (tool_def == tool_def_active) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                     if use_menu: | 
					
						
							|  |  |  |                         props = col.operator_menu_hold( | 
					
						
							|  |  |  |                             "wm.tool_set", | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                             text=item[0], | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                             depress=is_active, | 
					
						
							|  |  |  |                             menu="WM_MT_toolsystem_submenu", | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         props = col.operator( | 
					
						
							|  |  |  |                             "wm.tool_set", | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                             text=item[0], | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                             depress=is_active, | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                     props.keymap = tool_def[0] or "" | 
					
						
							|  |  |  |                     props.manipulator_group = tool_def[1] or "" | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                     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: | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |             tool_def_button, index_button = cls._tool_vars_from_button_with_index(context) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |             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] | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                             tool_def = cls._tool_vars_from_def(item) | 
					
						
							|  |  |  |                             is_active = (tool_def == tool_def_button) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |                             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 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |             layout.label("Unable to find toolbar group") | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         index = 0 | 
					
						
							|  |  |  |         for item in item_group: | 
					
						
							|  |  |  |             if item is None: | 
					
						
							|  |  |  |                 layout.separator() | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |             tool_def = cls._tool_vars_from_def(item) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |             props = layout.operator( | 
					
						
							|  |  |  |                 "wm.tool_set", | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                 text=item[0], | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |             props.keymap = tool_def[0] or "" | 
					
						
							|  |  |  |             props.manipulator_group = tool_def[1] or "" | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |             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) |