| 
									
										
										
										
											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__ = ( | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  |     "ToolDef", | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |     "ToolSelectPanelHelper", | 
					
						
							|  |  |  |     "activate_by_name", | 
					
						
							|  |  |  |     "activate_by_name_or_cycle", | 
					
						
							|  |  |  |     "description_from_name", | 
					
						
							|  |  |  |     "keymap_from_name", | 
					
						
							|  |  |  |     "keymap_from_context", | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-26 15:18:47 +02:00
										 |  |  | # Support reloading icons. | 
					
						
							|  |  |  | if "_icon_cache" in locals(): | 
					
						
							|  |  |  |     release = bpy.app.icons.release | 
					
						
							|  |  |  |     for icon_value in _icon_cache.values(): | 
					
						
							| 
									
										
										
										
											2018-04-30 13:55:15 +02:00
										 |  |  |         if icon_value != 0: | 
					
						
							|  |  |  |             release(icon_value) | 
					
						
							| 
									
										
										
										
											2018-04-26 15:18:47 +02:00
										 |  |  |     del release | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  | # (filename -> icon_value) map | 
					
						
							|  |  |  | _icon_cache = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  | def _keymap_fn_from_seq(keymap_data): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def keymap_fn(km): | 
					
						
							| 
									
										
										
										
											2018-11-15 13:36:23 +11:00
										 |  |  |         if keymap_fn.keymap_data: | 
					
						
							|  |  |  |             from bpy_extras.keyconfig_utils import keymap_items_from_data | 
					
						
							|  |  |  |             keymap_items_from_data(km, keymap_fn.keymap_data) | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  |     keymap_fn.keymap_data = keymap_data | 
					
						
							|  |  |  |     return keymap_fn | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  | def _item_is_fn(item): | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |     return (not (type(item) is ToolDef) and callable(item)) | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  | from collections import namedtuple | 
					
						
							|  |  |  | ToolDef = namedtuple( | 
					
						
							|  |  |  |     "ToolDef", | 
					
						
							|  |  |  |     ( | 
					
						
							|  |  |  |         # The name to display in the interface. | 
					
						
							|  |  |  |         "text", | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |         # Description (for tooltip), when not set, use the description of 'operator'. | 
					
						
							|  |  |  |         "description", | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon. | 
					
						
							|  |  |  |         "icon", | 
					
						
							| 
									
										
										
										
											2018-05-18 07:58:37 +02:00
										 |  |  |         # An optional cursor to use when this tool is active. | 
					
						
							|  |  |  |         "cursor", | 
					
						
							| 
									
										
										
										
											2018-07-15 19:49:07 +02:00
										 |  |  |         # An optional gizmo group to activate when the tool is set or None for no gizmo. | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         "widget", | 
					
						
							|  |  |  |         # Optional keymap for tool, either: | 
					
						
							|  |  |  |         # - A function that populates a keymaps passed in as an argument. | 
					
						
							|  |  |  |         # - A tuple filled with triple's of: | 
					
						
							|  |  |  |         #   ``(operator_id, operator_properties, keymap_item_args)``. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # Warning: currently 'from_dict' this is a list of one item, | 
					
						
							|  |  |  |         # so internally we can swap the keymap function for the keymap it's self. | 
					
						
							|  |  |  |         # This isn't very nice and may change, tool definitions shouldn't care about this. | 
					
						
							|  |  |  |         "keymap", | 
					
						
							|  |  |  |         # Optional data-block assosiated with this tool. | 
					
						
							|  |  |  |         # (Typically brush name, usage depends on mode, we could use for non-brush ID's in other modes). | 
					
						
							|  |  |  |         "data_block", | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |         # Optional primary operator (for introspection only). | 
					
						
							|  |  |  |         "operator", | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         # Optional draw settings (operator options, toolsettings). | 
					
						
							|  |  |  |         "draw_settings", | 
					
						
							| 
									
										
										
										
											2018-10-25 21:03:41 +11:00
										 |  |  |         # Optional draw cursor. | 
					
						
							|  |  |  |         "draw_cursor", | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |     ) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | del namedtuple | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  | def from_dict(kw_args): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Use so each tool can avoid defining all members of the named tuple. | 
					
						
							|  |  |  |     Also convert the keymap from a tuple into a function | 
					
						
							|  |  |  |     (since keymap is a callback). | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     kw = { | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |         "description": None, | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         "icon": None, | 
					
						
							| 
									
										
										
										
											2018-05-18 07:58:37 +02:00
										 |  |  |         "cursor": None, | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         "widget": None, | 
					
						
							|  |  |  |         "keymap": None, | 
					
						
							|  |  |  |         "data_block": None, | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |         "operator": None, | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         "draw_settings": None, | 
					
						
							| 
									
										
										
										
											2018-10-25 21:03:41 +11:00
										 |  |  |         "draw_cursor": None, | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     kw.update(kw_args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     keymap = kw["keymap"] | 
					
						
							|  |  |  |     if kw["keymap"] is None: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     elif type(keymap) is tuple: | 
					
						
							|  |  |  |         keymap = [_keymap_fn_from_seq(keymap)] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         keymap = [keymap] | 
					
						
							|  |  |  |     kw["keymap"] = keymap | 
					
						
							|  |  |  |     return ToolDef(**kw) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  | def from_fn(fn): | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |     Use as decorator so we can define functions. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     return ToolDef.from_dict(fn()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:57:37 +11:00
										 |  |  | def with_args(**kw): | 
					
						
							| 
									
										
										
										
											2018-11-05 06:57:01 +11:00
										 |  |  |     def from_fn(fn): | 
					
						
							| 
									
										
										
										
											2018-11-05 11:57:37 +11:00
										 |  |  |         return ToolDef.from_dict(fn(**kw)) | 
					
						
							| 
									
										
										
										
											2018-11-05 06:57:01 +11:00
										 |  |  |     return from_fn | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from_fn.with_args = with_args | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  | ToolDef.from_dict = from_dict | 
					
						
							|  |  |  | ToolDef.from_fn = from_fn | 
					
						
							| 
									
										
										
										
											2018-11-05 06:57:01 +11:00
										 |  |  | del from_dict, from_fn, with_args | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 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(): | 
					
						
							| 
									
										
										
										
											2018-04-26 14:43:32 +02:00
										 |  |  |       Returns (context_mode, tools) tuple pair for all tools defined. | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |     - tools_from_context(context, mode=None): | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |       Returns tools available in this context. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  |     Each tool is a 'ToolDef' or None for a separator in the toolbar, use ``None``. | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:13:13 +02:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tool_class_from_space_type(space_type): | 
					
						
							|  |  |  |         return next( | 
					
						
							|  |  |  |             (cls for cls in ToolSelectPanelHelper.__subclasses__() | 
					
						
							|  |  |  |              if cls.bl_space_type == space_type), | 
					
						
							|  |  |  |             None | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _icon_value_from_icon_handle(icon_name): | 
					
						
							|  |  |  |         import os | 
					
						
							|  |  |  |         if icon_name is not None: | 
					
						
							|  |  |  |             assert(type(icon_name) is str) | 
					
						
							|  |  |  |             icon_value = _icon_cache.get(icon_name) | 
					
						
							|  |  |  |             if icon_value is None: | 
					
						
							| 
									
										
										
										
											2018-04-25 14:05:48 +02:00
										 |  |  |                 dirname = bpy.utils.resource_path('LOCAL') | 
					
						
							| 
									
										
										
										
											2018-04-27 17:10:28 +03:00
										 |  |  |                 if not os.path.exists(dirname): | 
					
						
							| 
									
										
										
										
											2018-04-24 10:45:57 +02:00
										 |  |  |                     # TODO(campbell): use a better way of finding datafiles. | 
					
						
							| 
									
										
										
										
											2018-04-25 14:05:48 +02:00
										 |  |  |                     dirname = bpy.utils.resource_path('SYSTEM') | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |                 filename = os.path.join(dirname, "datafiles", "icons", icon_name + ".dat") | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     icon_value = bpy.app.icons.new_triangles_from_file(filename) | 
					
						
							|  |  |  |                 except Exception as ex: | 
					
						
							| 
									
										
										
										
											2018-04-27 15:34:54 +03:00
										 |  |  |                     if not os.path.exists(filename): | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |                         print("Missing icons:", filename, ex) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         print("Corrupt icon:", filename, ex) | 
					
						
							| 
									
										
										
										
											2018-04-30 13:55:15 +02:00
										 |  |  |                     # Use none as a fallback (avoids layout issues). | 
					
						
							|  |  |  |                     if icon_name != "none": | 
					
						
							|  |  |  |                         icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle("none") | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         icon_value = 0 | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |                 _icon_cache[icon_name] = icon_value | 
					
						
							|  |  |  |             return icon_value | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tools_flatten(tools): | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Flattens, skips None and calls generators. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |         for item in tools: | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |             if item is None: | 
					
						
							|  |  |  |                 yield None | 
					
						
							|  |  |  |             elif type(item) is tuple: | 
					
						
							|  |  |  |                 for sub_item in item: | 
					
						
							|  |  |  |                     if sub_item is None: | 
					
						
							|  |  |  |                         yield None | 
					
						
							|  |  |  |                     elif _item_is_fn(sub_item): | 
					
						
							|  |  |  |                         yield from sub_item(context) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         yield sub_item | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if _item_is_fn(item): | 
					
						
							|  |  |  |                     yield from item(context) | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |                     yield item | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tools_flatten_with_tool_index(tools): | 
					
						
							|  |  |  |         for item in tools: | 
					
						
							|  |  |  |             if item is None: | 
					
						
							|  |  |  |                 yield None, -1 | 
					
						
							|  |  |  |             elif type(item) is tuple: | 
					
						
							|  |  |  |                 i = 0 | 
					
						
							|  |  |  |                 for sub_item in item: | 
					
						
							|  |  |  |                     if sub_item is None: | 
					
						
							|  |  |  |                         yield None | 
					
						
							|  |  |  |                     elif _item_is_fn(sub_item): | 
					
						
							|  |  |  |                         for item_dyn in sub_item(context): | 
					
						
							|  |  |  |                             yield item_dyn, i | 
					
						
							|  |  |  |                             i += 1 | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |                         yield sub_item, i | 
					
						
							|  |  |  |                         i += 1 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if _item_is_fn(item): | 
					
						
							|  |  |  |                     for item_dyn in item(context): | 
					
						
							|  |  |  |                         yield item_dyn, -1 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     yield item, -1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |     def _tool_get_active(context, space_type, mode, with_icon=False): | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Return the active Python tool definition and icon name. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |         cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |         if cls is not None: | 
					
						
							| 
									
										
										
										
											2018-05-22 14:00:44 +02:00
										 |  |  |             tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode) | 
					
						
							|  |  |  |             tool_active_text = getattr(tool_active, "name", None) | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |             for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)): | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |                 if item is not None: | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |                     if item.text == tool_active_text: | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |                         if with_icon: | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |                             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |                         else: | 
					
						
							|  |  |  |                             icon_value = 0 | 
					
						
							| 
									
										
										
										
											2018-05-22 14:00:44 +02:00
										 |  |  |                         return (item, tool_active, icon_value) | 
					
						
							|  |  |  |         return None, None, 0 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-05-16 18:59:43 +02:00
										 |  |  |     def _tool_get_by_name(context, space_type, text): | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Return the active Python tool definition and index (if in sub-group, else -1). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:59:43 +02:00
										 |  |  |         cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  |         if cls is not None: | 
					
						
							|  |  |  |             for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)): | 
					
						
							|  |  |  |                 if item is not None: | 
					
						
							|  |  |  |                     if item.text == text: | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |                         return (cls, item, index) | 
					
						
							|  |  |  |         return None, None, -1 | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |     def _tool_active_from_context(context, space_type, mode=None, create=False): | 
					
						
							|  |  |  |         if space_type == 'VIEW_3D': | 
					
						
							|  |  |  |             if mode is None: | 
					
						
							| 
									
										
										
										
											2018-05-17 20:28:14 +02:00
										 |  |  |                 mode = context.mode | 
					
						
							| 
									
										
										
										
											2018-08-28 13:41:47 +10:00
										 |  |  |             tool = context.workspace.tools.from_space_view3d_mode(mode, create=create) | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |             if tool is not None: | 
					
						
							| 
									
										
										
										
											2018-08-02 16:24:22 +10:00
										 |  |  |                 tool.refresh_from_context() | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |                 return tool | 
					
						
							|  |  |  |         elif space_type == 'IMAGE_EDITOR': | 
					
						
							|  |  |  |             space_data = context.space_data | 
					
						
							|  |  |  |             if mode is None: | 
					
						
							| 
									
										
										
										
											2018-08-17 13:09:59 +02:00
										 |  |  |                 if space_data is None: | 
					
						
							|  |  |  |                     mode = 'VIEW' | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     mode = space_data.mode | 
					
						
							| 
									
										
										
										
											2018-08-28 13:41:47 +10:00
										 |  |  |             tool = context.workspace.tools.from_space_image_mode(mode, create=create) | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |             if tool is not None: | 
					
						
							| 
									
										
										
										
											2018-08-02 16:24:22 +10:00
										 |  |  |                 tool.refresh_from_context() | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |                 return tool | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2017-11-03 16:30:51 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |     def _tool_text_from_button(context): | 
					
						
							|  |  |  |         return context.button_operator.name | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |     @classmethod | 
					
						
							| 
									
										
										
										
											2018-10-03 15:48:37 +10:00
										 |  |  |     def _km_action_simple(cls, kc, context_descr, text, keymap_fn): | 
					
						
							|  |  |  |         km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {text:s}" | 
					
						
							| 
									
										
										
										
											2017-11-05 01:38:51 +11:00
										 |  |  |         km = kc.keymaps.get(km_idname) | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |         if km is None: | 
					
						
							| 
									
										
										
										
											2018-11-13 21:01:32 +01:00
										 |  |  |             km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW', tool=True) | 
					
						
							| 
									
										
										
										
											2018-04-30 12:14:46 +02:00
										 |  |  |             keymap_fn[0](km) | 
					
						
							|  |  |  |         keymap_fn[0] = km | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 15:48:37 +10:00
										 |  |  |     # Special internal function, gives use items that contain keymaps. | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def _tools_flatten_with_keymap(tools): | 
					
						
							|  |  |  |         for item_parent in tools: | 
					
						
							|  |  |  |             if item_parent is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             for item in item_parent if (type(item_parent) is tuple) else (item_parent,): | 
					
						
							|  |  |  |                 # skip None or generator function | 
					
						
							|  |  |  |                 if item is None or _item_is_fn(item): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if item.keymap is not None: | 
					
						
							|  |  |  |                     yield item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def register(cls): | 
					
						
							|  |  |  |         wm = bpy.context.window_manager | 
					
						
							| 
									
										
										
										
											2018-10-03 15:48:37 +10:00
										 |  |  |         # Write into defaults, users may modify in preferences. | 
					
						
							|  |  |  |         kc = wm.keyconfigs.default | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-26 14:43:32 +02:00
										 |  |  |         for context_mode, tools in cls.tools_all(): | 
					
						
							| 
									
										
										
										
											2018-10-03 15:48:37 +10:00
										 |  |  |             if context_mode is None: | 
					
						
							|  |  |  |                 context_descr = "All" | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 context_descr = context_mode.replace("_", " ").title() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for item in cls._tools_flatten_with_keymap(tools): | 
					
						
							|  |  |  |                 keymap_data = item.keymap | 
					
						
							|  |  |  |                 if callable(keymap_data[0]): | 
					
						
							| 
									
										
										
										
											2018-10-18 16:46:43 +11:00
										 |  |  |                     cls._km_action_simple(kc, context_descr, item.text, keymap_data) | 
					
						
							| 
									
										
										
										
											2018-10-03 15:48:37 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def keymap_ui_hierarchy(cls, context_mode): | 
					
						
							|  |  |  |         # See: bpy_extras.keyconfig_utils | 
					
						
							|  |  |  |         for context_mode_test, tools in cls.tools_all(): | 
					
						
							|  |  |  |             if context_mode_test == context_mode: | 
					
						
							|  |  |  |                 for item in cls._tools_flatten_with_keymap(tools): | 
					
						
							|  |  |  |                     km = item.keymap[0] | 
					
						
							|  |  |  |                     # print((km.name, cls.bl_space_type, 'WINDOW', [])) | 
					
						
							|  |  |  |                     yield (km.name, cls.bl_space_type, 'WINDOW', []) | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |     # ------------------------------------------------------------------------- | 
					
						
							|  |  |  |     # Layout Generators | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # Meaning of recieved values: | 
					
						
							|  |  |  |     # - Bool: True for a separator, otherwise False for regular tools. | 
					
						
							|  |  |  |     # - None: Signal to finish (complete any final operations, e.g. add padding). | 
					
						
							| 
									
										
										
										
											2017-10-21 16:19:48 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |     def _layout_generator_single_column(layout, scale_y): | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         col = layout.column(align=True) | 
					
						
							|  |  |  |         col.scale_y = scale_y | 
					
						
							|  |  |  |         is_sep = False | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             if is_sep is True: | 
					
						
							|  |  |  |                 col = layout.column(align=True) | 
					
						
							|  |  |  |                 col.scale_y = scale_y | 
					
						
							|  |  |  |             elif is_sep is None: | 
					
						
							|  |  |  |                 yield None | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             is_sep = yield col | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |     def _layout_generator_multi_columns(layout, column_count, scale_y): | 
					
						
							|  |  |  |         scale_x = scale_y * 1.1 | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         column_last = column_count - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         col = layout.column(align=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         row = col.row(align=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         row.scale_x = scale_x | 
					
						
							|  |  |  |         row.scale_y = scale_y | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         is_sep = False | 
					
						
							|  |  |  |         column_index = 0 | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             if is_sep is True: | 
					
						
							|  |  |  |                 if column_index != column_last: | 
					
						
							| 
									
										
										
										
											2018-08-28 12:34:51 +10:00
										 |  |  |                     row.label(text="") | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |                 col = layout.column(align=True) | 
					
						
							|  |  |  |                 row = col.row(align=True) | 
					
						
							|  |  |  |                 row.scale_x = scale_x | 
					
						
							|  |  |  |                 row.scale_y = scale_y | 
					
						
							|  |  |  |                 column_index = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             is_sep = yield row | 
					
						
							|  |  |  |             if is_sep is None: | 
					
						
							|  |  |  |                 if column_index == column_last: | 
					
						
							| 
									
										
										
										
											2018-08-28 12:34:51 +10:00
										 |  |  |                     row.label(text="") | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |                     yield None | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if column_index == column_count: | 
					
						
							|  |  |  |                 column_index = 0 | 
					
						
							|  |  |  |                 row = col.row(align=True) | 
					
						
							|  |  |  |                 row.scale_x = scale_x | 
					
						
							|  |  |  |                 row.scale_y = scale_y | 
					
						
							|  |  |  |             column_index += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |     def _layout_generator_detect_from_region(layout, region, scale_y): | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Choose an appropriate layout for the toolbar. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # Currently this just checks the width, | 
					
						
							|  |  |  |         # we could have different layouts as preferences too. | 
					
						
							| 
									
										
										
										
											2018-04-28 20:48:47 +02:00
										 |  |  |         system = bpy.context.user_preferences.system | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         view2d = region.view2d | 
					
						
							| 
									
										
										
										
											2018-04-28 20:48:47 +02:00
										 |  |  |         view2d_scale = ( | 
					
						
							| 
									
										
										
										
											2018-04-24 16:33:38 +02:00
										 |  |  |             view2d.region_to_view(1.0, 0.0)[0] - | 
					
						
							|  |  |  |             view2d.region_to_view(0.0, 0.0)[0] | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2018-04-28 20:48:47 +02:00
										 |  |  |         width_scale = region.width * view2d_scale / system.ui_scale | 
					
						
							| 
									
										
										
										
											2018-04-24 16:33:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 18:20:48 +02:00
										 |  |  |         if width_scale > 120.0: | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  |             show_text = True | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |             column_count = 1 | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             show_text = False | 
					
						
							| 
									
										
										
										
											2018-04-27 18:20:48 +02:00
										 |  |  |             # 2 column layout, disabled | 
					
						
							|  |  |  |             if width_scale > 80.0: | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  |                 column_count = 2 | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |                 column_count = 1 | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         if column_count == 1: | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |             ui_gen = ToolSelectPanelHelper._layout_generator_single_column(layout, scale_y=scale_y) | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |             ui_gen = ToolSelectPanelHelper._layout_generator_multi_columns(layout, column_count=column_count, scale_y=scale_y) | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return ui_gen, show_text | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |     @classmethod | 
					
						
							| 
									
										
										
										
											2018-06-10 16:42:19 +02:00
										 |  |  |     def draw_cls(cls, layout, context, detect_layout=True, scale_y=1.75): | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |         # Use a classmethod so it can be called outside of a panel context. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         # 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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |         space_type = context.space_data.type | 
					
						
							|  |  |  |         tool_active_text = getattr( | 
					
						
							|  |  |  |             ToolSelectPanelHelper._tool_active_from_context(context, space_type), | 
					
						
							|  |  |  |             "name", None, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-18 22:27:59 +02:00
										 |  |  |         if detect_layout: | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |             ui_gen, show_text = cls._layout_generator_detect_from_region(layout, context.region, scale_y) | 
					
						
							| 
									
										
										
										
											2018-05-18 22:27:59 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-06-06 08:31:31 +02:00
										 |  |  |             ui_gen = ToolSelectPanelHelper._layout_generator_single_column(layout, scale_y) | 
					
						
							| 
									
										
										
										
											2018-05-18 22:27:59 +02:00
										 |  |  |             show_text = True | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Start iteration | 
					
						
							|  |  |  |         ui_gen.send(None) | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |         for item in cls.tools_from_context(context): | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |             if item is None: | 
					
						
							|  |  |  |                 ui_gen.send(True) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if type(item) is tuple: | 
					
						
							|  |  |  |                 is_active = False | 
					
						
							|  |  |  |                 i = 0 | 
					
						
							|  |  |  |                 for i, sub_item in enumerate(item): | 
					
						
							|  |  |  |                     if sub_item is None: | 
					
						
							|  |  |  |                         continue | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |                     is_active = (sub_item.text == tool_active_text) | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  |                     if is_active: | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |                         index = i | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 del i, sub_item | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |                 if is_active: | 
					
						
							|  |  |  |                     # not ideal, write this every time :S | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |                     cls._tool_group_active[item[0].text] = index | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |                     index = cls._tool_group_active.get(item[0].text, 0) | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |                 item = item[index] | 
					
						
							|  |  |  |                 use_menu = True | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 index = -1 | 
					
						
							|  |  |  |                 use_menu = False | 
					
						
							| 
									
										
										
										
											2018-04-29 14:30:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |             is_active = (item.text == tool_active_text) | 
					
						
							| 
									
										
										
										
											2018-05-18 07:24:17 +02:00
										 |  |  |             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) | 
					
						
							| 
									
										
										
										
											2018-04-29 12:26:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |             sub = ui_gen.send(False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if use_menu: | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                 sub.operator_menu_hold( | 
					
						
							|  |  |  |                     "wm.tool_set_by_name", | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |                     text=item.text if show_text else "", | 
					
						
							|  |  |  |                     depress=is_active, | 
					
						
							|  |  |  |                     menu="WM_MT_toolsystem_submenu", | 
					
						
							|  |  |  |                     icon_value=icon_value, | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                 ).name = item.text | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                 sub.operator( | 
					
						
							|  |  |  |                     "wm.tool_set_by_name", | 
					
						
							| 
									
										
										
										
											2018-05-13 08:32:47 +02:00
										 |  |  |                     text=item.text if show_text else "", | 
					
						
							|  |  |  |                     depress=is_active, | 
					
						
							|  |  |  |                     icon_value=icon_value, | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                 ).name = item.text | 
					
						
							| 
									
										
										
										
											2018-04-28 10:51:05 +02:00
										 |  |  |         # Signal to finish any remaining layout edits. | 
					
						
							|  |  |  |         ui_gen.send(None) | 
					
						
							| 
									
										
										
										
											2018-04-27 17:55:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-18 21:16:57 +02:00
										 |  |  |     def draw(self, context): | 
					
						
							|  |  |  |         self.draw_cls(self.layout, context) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 11:13:22 +11:00
										 |  |  |     @staticmethod | 
					
						
							|  |  |  |     def tool_active_from_context(context): | 
					
						
							|  |  |  |         # BAD DESIGN WARNING: last used tool | 
					
						
							|  |  |  |         workspace = context.workspace | 
					
						
							|  |  |  |         space_type = workspace.tools_space_type | 
					
						
							|  |  |  |         mode = workspace.tools_mode | 
					
						
							|  |  |  |         return ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 10:49:20 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2018-08-30 08:55:34 +10:00
										 |  |  |     def draw_active_tool_header( | 
					
						
							|  |  |  |             context, layout, | 
					
						
							|  |  |  |             *, | 
					
						
							|  |  |  |             show_tool_name=False, | 
					
						
							|  |  |  |     ): | 
					
						
							| 
									
										
										
										
											2018-05-16 18:41:11 +02:00
										 |  |  |         # BAD DESIGN WARNING: last used tool | 
					
						
							|  |  |  |         workspace = context.workspace | 
					
						
							|  |  |  |         space_type = workspace.tools_space_type | 
					
						
							|  |  |  |         mode = workspace.tools_mode | 
					
						
							| 
									
										
										
										
											2018-05-22 14:00:44 +02:00
										 |  |  |         item, tool, icon_value = ToolSelectPanelHelper._tool_get_active(context, space_type, mode, with_icon=True) | 
					
						
							| 
									
										
										
										
											2018-04-27 10:49:20 +02:00
										 |  |  |         if item is None: | 
					
						
							| 
									
										
										
										
											2018-08-30 08:55:34 +10:00
										 |  |  |             return None | 
					
						
							|  |  |  |         # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. | 
					
						
							|  |  |  |         # Add some spacing since the icon is currently assuming regular small icon size. | 
					
						
							|  |  |  |         layout.label(text="    " + item.text if show_tool_name else " ", icon_value=icon_value) | 
					
						
							| 
									
										
										
										
											2018-04-27 14:13:16 +02:00
										 |  |  |         draw_settings = item.draw_settings | 
					
						
							|  |  |  |         if draw_settings is not None: | 
					
						
							| 
									
										
										
										
											2018-05-22 14:00:44 +02:00
										 |  |  |             draw_settings(context, layout, tool) | 
					
						
							| 
									
										
										
										
											2018-08-29 23:58:44 +10:00
										 |  |  |         return tool | 
					
						
							| 
									
										
										
										
											2018-04-27 14:13:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 10:49:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | # 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. | 
					
						
							| 
									
										
										
										
											2018-05-13 08:13:13 +02:00
										 |  |  |         cls = ToolSelectPanelHelper._tool_class_from_space_type(context.space_data.type) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |         if cls is not None: | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |             button_text = ToolSelectPanelHelper._tool_text_from_button(context) | 
					
						
							| 
									
										
										
										
											2018-04-29 16:24:40 +02:00
										 |  |  |             for item_group in cls.tools_from_context(context): | 
					
						
							|  |  |  |                 if type(item_group) is tuple: | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                     for sub_item in item_group: | 
					
						
							|  |  |  |                         if sub_item.text == button_text: | 
					
						
							|  |  |  |                             return cls, item_group | 
					
						
							|  |  |  |         return None, None | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def draw(self, context): | 
					
						
							|  |  |  |         layout = self.layout | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |         layout.scale_y = 2.0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |         _cls, item_group = self._tool_group_from_button(context) | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |         if item_group is None: | 
					
						
							|  |  |  |             # Should never happen, just in case | 
					
						
							| 
									
										
										
										
											2018-08-28 12:34:51 +10:00
										 |  |  |             layout.label(text="Unable to find toolbar group") | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for item in item_group: | 
					
						
							|  |  |  |             if item is None: | 
					
						
							|  |  |  |                 layout.separator() | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2018-05-18 07:24:17 +02:00
										 |  |  |             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) | 
					
						
							| 
									
										
										
										
											2018-05-16 18:59:43 +02:00
										 |  |  |             layout.operator( | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |                 "wm.tool_set_by_name", | 
					
						
							| 
									
										
										
										
											2018-04-27 13:23:29 +02:00
										 |  |  |                 text=item.text, | 
					
						
							| 
									
										
										
										
											2018-04-24 09:19:28 +02:00
										 |  |  |                 icon_value=icon_value, | 
					
						
							| 
									
										
										
										
											2018-05-13 10:01:56 +02:00
										 |  |  |             ).name = item.text | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  | def _activate_by_item(context, space_type, item, index): | 
					
						
							|  |  |  |     tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True) | 
					
						
							|  |  |  |     tool.setup( | 
					
						
							|  |  |  |         name=item.text, | 
					
						
							|  |  |  |         keymap=item.keymap[0].name if item.keymap is not None else "", | 
					
						
							|  |  |  |         cursor=item.cursor or 'DEFAULT', | 
					
						
							| 
									
										
										
										
											2018-07-14 23:49:00 +02:00
										 |  |  |         gizmo_group=item.widget or "", | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |         data_block=item.data_block or "", | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |         operator=item.operator or "", | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |         index=index, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 21:03:41 +11:00
										 |  |  |     WindowManager = bpy.types.WindowManager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     handle_map = _activate_by_item._cursor_draw_handle | 
					
						
							|  |  |  |     handle = handle_map.pop(space_type, None) | 
					
						
							|  |  |  |     if (handle is not None): | 
					
						
							|  |  |  |         WindowManager.draw_cursor_remove(handle) | 
					
						
							|  |  |  |     if item.draw_cursor is not None: | 
					
						
							|  |  |  |         def handle_fn(context, item, tool, xy): | 
					
						
							|  |  |  |             item.draw_cursor(context, tool, xy) | 
					
						
							|  |  |  |         handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type) | 
					
						
							|  |  |  |         handle_map[space_type] = handle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _activate_by_item._cursor_draw_handle = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 18:59:43 +02:00
										 |  |  | def activate_by_name(context, space_type, text): | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |     _cls, item, index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text) | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |     if item is None: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     _activate_by_item(context, space_type, item, index) | 
					
						
							|  |  |  |     return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def activate_by_name_or_cycle(context, space_type, text, offset=1): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Only cycle when the active tool is activated again. | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |     cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text) | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |     if item is None: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type) | 
					
						
							|  |  |  |     text_active = getattr(tool_active, "name", None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     text_current = "" | 
					
						
							|  |  |  |     for item_group in cls.tools_from_context(context): | 
					
						
							|  |  |  |         if type(item_group) is tuple: | 
					
						
							|  |  |  |             index_current = cls._tool_group_active.get(item_group[0].text, 0) | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |             for sub_item in item_group: | 
					
						
							| 
									
										
										
										
											2018-05-30 22:15:10 +02:00
										 |  |  |                 if sub_item.text == text: | 
					
						
							|  |  |  |                     text_current = item_group[index_current].text | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             if text_current: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if text_current == "": | 
					
						
							|  |  |  |         return activate_by_name(context, space_type, text) | 
					
						
							|  |  |  |     if text_active != text_current: | 
					
						
							|  |  |  |         return activate_by_name(context, space_type, text_current) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     index_found = (tool_active.index + offset) % len(item_group) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cls._tool_group_active[item_group[0].text] = index_found | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item_found = item_group[index_found] | 
					
						
							|  |  |  |     _activate_by_item(context, space_type, item_found, index_found) | 
					
						
							|  |  |  |     return True | 
					
						
							| 
									
										
										
										
											2018-05-13 08:59:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  | def description_from_name(context, space_type, text, *, use_operator=True): | 
					
						
							|  |  |  |     # Used directly for tooltips. | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |     _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text) | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |     if item is None: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Custom description. | 
					
						
							|  |  |  |     description = item.description | 
					
						
							|  |  |  |     if description is not None: | 
					
						
							|  |  |  |         return description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Extract from the operator. | 
					
						
							|  |  |  |     if use_operator: | 
					
						
							|  |  |  |         operator = item.operator | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if operator is None: | 
					
						
							|  |  |  |             if item.keymap is not None: | 
					
						
							|  |  |  |                 operator = item.keymap[0].keymap_items[0].idname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if operator is not None: | 
					
						
							|  |  |  |             import _bpy | 
					
						
							| 
									
										
										
										
											2018-09-13 18:21:16 +10:00
										 |  |  |             return _bpy.ops.get_rna_type(operator).description | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |     return "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def keymap_from_name(context, space_type, text): | 
					
						
							|  |  |  |     # Used directly for tooltips. | 
					
						
							| 
									
										
										
										
											2018-10-25 12:03:34 +11:00
										 |  |  |     _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text) | 
					
						
							| 
									
										
										
										
											2018-08-31 14:37:10 +10:00
										 |  |  |     if item is None: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     keymap = item.keymap | 
					
						
							|  |  |  |     # List container of one. | 
					
						
							|  |  |  |     if keymap: | 
					
						
							|  |  |  |         return keymap[0] | 
					
						
							|  |  |  |     return "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  | def keymap_from_context(context, space_type): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Keymap for popup toolbar, currently generated each time. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2018-07-09 17:36:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-13 09:18:46 +02:00
										 |  |  |     def modifier_keywords_from_item(kmi): | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |         kw = {} | 
					
						
							|  |  |  |         for (attr, default) in ( | 
					
						
							|  |  |  |                 ("any", False), | 
					
						
							|  |  |  |                 ("shift", False), | 
					
						
							|  |  |  |                 ("ctrl", False), | 
					
						
							|  |  |  |                 ("alt", False), | 
					
						
							|  |  |  |                 ("oskey", False), | 
					
						
							|  |  |  |                 ("key_modifier", 'NONE'), | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             val = getattr(kmi, attr) | 
					
						
							|  |  |  |             if val != default: | 
					
						
							|  |  |  |                 kw[attr] = val | 
					
						
							|  |  |  |         return kw | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def dict_as_tuple(d): | 
					
						
							|  |  |  |         return tuple((k, v) for (k, v) in sorted(d.items())) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |     use_simple_keymap = False | 
					
						
							| 
									
										
										
										
											2018-07-09 17:36:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 12:44:49 +11:00
										 |  |  |     # Press the toolbar popup key again to set the default tool, | 
					
						
							|  |  |  |     # this is useful because the cursor tool is useful as a way | 
					
						
							|  |  |  |     # to 'drop' currently active tools (it's basically a 'none' tool). | 
					
						
							|  |  |  |     # so this allows us to quickly go back to a state that allows | 
					
						
							|  |  |  |     # a shortcut based workflow (before the tool system was added). | 
					
						
							|  |  |  |     use_tap_reset = True | 
					
						
							|  |  |  |     # TODO: support other tools for modes which don't use the cursor. | 
					
						
							|  |  |  |     tap_reset_tool = "Cursor" | 
					
						
							|  |  |  |     # Check the tool is available in the current context. | 
					
						
							|  |  |  |     if ToolSelectPanelHelper._tool_get_by_name(context, space_type, tap_reset_tool)[1] is None: | 
					
						
							|  |  |  |         use_tap_reset = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-15 17:12:33 +11:00
										 |  |  |     from bl_operators.wm import use_toolbar_release_hack | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-27 12:22:20 +11:00
										 |  |  |     # Pie-menu style release to activate. | 
					
						
							|  |  |  |     use_release_confirm = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |     # Generate items when no keys are mapped. | 
					
						
							|  |  |  |     use_auto_keymap = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  |     # Temporary, only create so we can pass 'properties' to find_item_from_operator. | 
					
						
							|  |  |  |     use_hack_properties = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |     km_name = "Toolbar Popup" | 
					
						
							|  |  |  |     wm = context.window_manager | 
					
						
							|  |  |  |     keyconf = wm.keyconfigs.active | 
					
						
							|  |  |  |     keymap = keyconf.keymaps.get(km_name) | 
					
						
							|  |  |  |     if keymap is None: | 
					
						
							| 
									
										
										
										
											2018-11-13 21:01:32 +01:00
										 |  |  |         keymap = keyconf.keymaps.new(km_name, space_type='EMPTY', region_type='TEMPORARY', tool=True) | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |     for kmi in keymap.keymap_items: | 
					
						
							|  |  |  |         keymap.keymap_items.remove(kmi) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |     kmi_unique_args = set() | 
					
						
							| 
									
										
										
										
											2018-07-09 17:36:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |     cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |     items_all = [ | 
					
						
							|  |  |  |         # 0: tool | 
					
						
							|  |  |  |         # 1: keymap item (direct access) | 
					
						
							|  |  |  |         # 2: keymap item (newly calculated for toolbar) | 
					
						
							|  |  |  |         [item, None, None] | 
					
						
							|  |  |  |         for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context)) | 
					
						
							|  |  |  |         if item is not None | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  |     if use_hack_properties: | 
					
						
							|  |  |  |         kmi_hack = keymap.keymap_items.new("wm.tool_set_by_name", 'A', 'PRESS') | 
					
						
							|  |  |  |         kmi_hack_properties = kmi_hack.properties | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:12:53 +10:00
										 |  |  |         kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'A', 'PRESS') | 
					
						
							|  |  |  |         kmi_hack_brush_select_properties = kmi_hack_brush_select.properties | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 12:44:49 +11:00
										 |  |  |     if use_release_confirm or use_tap_reset: | 
					
						
							| 
									
										
										
										
											2018-10-27 12:22:20 +11:00
										 |  |  |         kmi_toolbar = wm.keyconfigs.find_item_from_operator(idname="wm.toolbar")[1] | 
					
						
							|  |  |  |         kmi_toolbar_type = None if not kmi_toolbar else kmi_toolbar.type | 
					
						
							| 
									
										
										
										
											2018-11-12 12:44:49 +11:00
										 |  |  |         if use_tap_reset and kmi_toolbar_type is not None: | 
					
						
							|  |  |  |             kmi_toolbar_args = { | 
					
						
							|  |  |  |                 "type": kmi_toolbar_type, | 
					
						
							|  |  |  |                 **modifier_keywords_from_item(kmi_toolbar), | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             use_tap_reset = False | 
					
						
							| 
									
										
										
										
											2018-10-27 12:22:20 +11:00
										 |  |  |         del kmi_toolbar | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-12 12:44:49 +11:00
										 |  |  |     if use_tap_reset: | 
					
						
							|  |  |  |         kmi_found = None | 
					
						
							|  |  |  |         if use_hack_properties: | 
					
						
							|  |  |  |             # First check for direct assignment, if this tool already has a key, no need to add a new one. | 
					
						
							|  |  |  |             kmi_hack_properties.name = tap_reset_tool | 
					
						
							|  |  |  |             kmi_found = wm.keyconfigs.find_item_from_operator( | 
					
						
							|  |  |  |                 idname="wm.tool_set_by_name", | 
					
						
							|  |  |  |                 context='INVOKE_REGION_WIN', | 
					
						
							|  |  |  |                 # properties={"name": item.text}, | 
					
						
							|  |  |  |                 properties=kmi_hack_properties, | 
					
						
							|  |  |  |             )[1] | 
					
						
							|  |  |  |             if kmi_found: | 
					
						
							|  |  |  |                 use_tap_reset = False | 
					
						
							|  |  |  |         del kmi_found | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if use_tap_reset: | 
					
						
							|  |  |  |         kmi_toolbar_tuple = dict_as_tuple(kmi_toolbar_args) | 
					
						
							|  |  |  |         if kmi_toolbar_tuple not in kmi_unique_args: | 
					
						
							|  |  |  |             kmi = keymap.keymap_items.new( | 
					
						
							|  |  |  |                 "wm.tool_set_by_name", | 
					
						
							| 
									
										
										
										
											2018-11-15 17:12:33 +11:00
										 |  |  |                 value='PRESS' if use_toolbar_release_hack else 'DOUBLE_CLICK', | 
					
						
							| 
									
										
										
										
											2018-11-12 12:44:49 +11:00
										 |  |  |                 **kmi_toolbar_args, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             kmi.properties.name = tap_reset_tool | 
					
						
							|  |  |  |         kmi_unique_args.add(kmi_toolbar_tuple) | 
					
						
							|  |  |  |         del kmi_toolbar_tuple | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |     if use_simple_keymap: | 
					
						
							|  |  |  |         # Simply assign a key from A-Z. | 
					
						
							|  |  |  |         for i, (item, _, _) in enumerate(items_all): | 
					
						
							|  |  |  |             key = chr(ord('A') + i) | 
					
						
							|  |  |  |             kmi = keymap.keymap_items.new("wm.tool_set_by_name", key, 'PRESS') | 
					
						
							|  |  |  |             kmi.properties.name = item.text | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for item_container in items_all: | 
					
						
							|  |  |  |             item = item_container[0] | 
					
						
							| 
									
										
										
										
											2018-05-21 13:06:59 +02:00
										 |  |  |             # Only check the first item in the tools key-map (a little arbitrary). | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if use_hack_properties: | 
					
						
							|  |  |  |                 # First check for direct assignment. | 
					
						
							|  |  |  |                 kmi_hack_properties.name = item.text | 
					
						
							|  |  |  |                 kmi_found = wm.keyconfigs.find_item_from_operator( | 
					
						
							|  |  |  |                     idname="wm.tool_set_by_name", | 
					
						
							|  |  |  |                     context='INVOKE_REGION_WIN', | 
					
						
							|  |  |  |                     # properties={"name": item.text}, | 
					
						
							|  |  |  |                     properties=kmi_hack_properties, | 
					
						
							|  |  |  |                 )[1] | 
					
						
							| 
									
										
										
										
											2018-09-03 23:12:53 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if kmi_found is None: | 
					
						
							|  |  |  |                     if item.data_block: | 
					
						
							|  |  |  |                         # PAINT_OT_brush_select | 
					
						
							| 
									
										
										
										
											2018-11-07 10:54:14 +11:00
										 |  |  |                         mode = context.active_object.mode | 
					
						
							| 
									
										
										
										
											2018-11-07 11:38:10 +11:00
										 |  |  |                         # See: BKE_paint_get_tool_prop_id_from_paintmode | 
					
						
							| 
									
										
										
										
											2018-11-07 10:54:14 +11:00
										 |  |  |                         attr = { | 
					
						
							|  |  |  |                             'SCULPT': "sculpt_tool", | 
					
						
							| 
									
										
										
										
											2018-11-07 11:38:10 +11:00
										 |  |  |                             'VERTEX_PAINT': "vertex_tool", | 
					
						
							|  |  |  |                             'WEIGHT_PAINT': "weight_tool", | 
					
						
							|  |  |  |                             'TEXTURE_PAINT': "image_tool", | 
					
						
							|  |  |  |                             'GPENCIL_PAINT': "gpencil_tool", | 
					
						
							| 
									
										
										
										
											2018-11-14 17:40:12 +11:00
										 |  |  |                         }.get(mode, None) | 
					
						
							| 
									
										
										
										
											2018-11-07 10:54:14 +11:00
										 |  |  |                         if attr is not None: | 
					
						
							|  |  |  |                             setattr(kmi_hack_brush_select_properties, attr, item.data_block) | 
					
						
							|  |  |  |                             kmi_found = wm.keyconfigs.find_item_from_operator( | 
					
						
							|  |  |  |                                 idname="paint.brush_select", | 
					
						
							|  |  |  |                                 context='INVOKE_REGION_WIN', | 
					
						
							|  |  |  |                                 properties=kmi_hack_brush_select_properties, | 
					
						
							|  |  |  |                             )[1] | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             print("Unsupported mode:", mode) | 
					
						
							|  |  |  |                         del mode, attr | 
					
						
							| 
									
										
										
										
											2018-09-03 23:12:53 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 kmi_found = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if kmi_found is not None: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             elif item.operator is not None: | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |                 kmi_found = wm.keyconfigs.find_item_from_operator( | 
					
						
							|  |  |  |                     idname=item.operator, | 
					
						
							| 
									
										
										
										
											2018-09-03 10:19:30 +10:00
										 |  |  |                     context='INVOKE_REGION_WIN', | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |                 )[1] | 
					
						
							|  |  |  |             elif item.keymap is not None: | 
					
						
							|  |  |  |                 kmi_first = item.keymap[0].keymap_items[0] | 
					
						
							|  |  |  |                 kmi_found = wm.keyconfigs.find_item_from_operator( | 
					
						
							|  |  |  |                     idname=kmi_first.idname, | 
					
						
							|  |  |  |                     # properties=kmi_first.properties,  # prevents matches, don't use. | 
					
						
							| 
									
										
										
										
											2018-09-03 10:19:30 +10:00
										 |  |  |                     context='INVOKE_REGION_WIN', | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  |                 )[1] | 
					
						
							|  |  |  |                 del kmi_first | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 kmi_found = None | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |             item_container[1] = kmi_found | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # More complex multi-pass test. | 
					
						
							|  |  |  |         for item_container in items_all: | 
					
						
							|  |  |  |             item, kmi_found = item_container[:2] | 
					
						
							|  |  |  |             if kmi_found is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             kmi_found_type = kmi_found.type | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Only for single keys. | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  |             if ( | 
					
						
							|  |  |  |                     (len(kmi_found_type) == 1) or | 
					
						
							|  |  |  |                     # When a tool is being activated instead of running an operator, just copy the shortcut. | 
					
						
							|  |  |  |                     (kmi_found.idname in {"wm.tool_set_by_name", "WM_OT_tool_set_by_name"}) | 
					
						
							|  |  |  |             ): | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |                 kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)} | 
					
						
							|  |  |  |                 kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args) | 
					
						
							|  |  |  |                 kmi.properties.name = item.text | 
					
						
							|  |  |  |                 item_container[2] = kmi | 
					
						
							|  |  |  |                 if use_auto_keymap: | 
					
						
							|  |  |  |                     kmi_unique_args.add(dict_as_tuple(kmi_args)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Test for key_modifier, where alpha key is used as a 'key_modifier' | 
					
						
							|  |  |  |         # (grease pencil holding 'D' for example). | 
					
						
							|  |  |  |         for item_container in items_all: | 
					
						
							|  |  |  |             item, kmi_found, kmi_exist = item_container | 
					
						
							|  |  |  |             if kmi_found is None or kmi_exist: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2018-07-03 18:33:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |             kmi_found_type = kmi_found.type | 
					
						
							|  |  |  |             if kmi_found_type in { | 
					
						
							|  |  |  |                     'LEFTMOUSE', | 
					
						
							|  |  |  |                     'RIGHTMOUSE', | 
					
						
							|  |  |  |                     'MIDDLEMOUSE', | 
					
						
							|  |  |  |                     'BUTTON4MOUSE', | 
					
						
							|  |  |  |                     'BUTTON5MOUSE', | 
					
						
							|  |  |  |                     'BUTTON6MOUSE', | 
					
						
							|  |  |  |                     'BUTTON7MOUSE', | 
					
						
							|  |  |  |             }: | 
					
						
							|  |  |  |                 kmi_found_type = kmi_found.key_modifier | 
					
						
							|  |  |  |                 # excludes 'NONE' | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |                 if len(kmi_found_type) == 1: | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |                     kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)} | 
					
						
							|  |  |  |                     del kmi_args["key_modifier"] | 
					
						
							|  |  |  |                     kmi_tuple = dict_as_tuple(kmi_args) | 
					
						
							|  |  |  |                     if kmi_tuple in kmi_unique_args: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args) | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |                     kmi.properties.name = item.text | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |                     item_container[2] = kmi | 
					
						
							|  |  |  |                     if use_auto_keymap: | 
					
						
							|  |  |  |                         kmi_unique_args.add(kmi_tuple) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if use_auto_keymap: | 
					
						
							|  |  |  |             # Map all unmapped keys to numbers, | 
					
						
							|  |  |  |             # while this is a bit strange it means users will not confuse regular key bindings to ordered bindings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Free events (last used first). | 
					
						
							|  |  |  |             kmi_type_auto = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO') | 
					
						
							|  |  |  |             # Map both numbers and num-pad. | 
					
						
							|  |  |  |             kmi_type_dupe = { | 
					
						
							|  |  |  |                 'ONE': 'NUMPAD_1', | 
					
						
							|  |  |  |                 'TWO': 'NUMPAD_2', | 
					
						
							|  |  |  |                 'THREE': 'NUMPAD_3', | 
					
						
							|  |  |  |                 'FOUR': 'NUMPAD_4', | 
					
						
							|  |  |  |                 'FIVE': 'NUMPAD_5', | 
					
						
							|  |  |  |                 'SIX': 'NUMPAD_6', | 
					
						
							|  |  |  |                 'SEVEN': 'NUMPAD_7', | 
					
						
							|  |  |  |                 'EIGHT': 'NUMPAD_8', | 
					
						
							|  |  |  |                 'NINE': 'NUMPAD_9', | 
					
						
							|  |  |  |                 'ZERO': 'NUMPAD_0', | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def iter_free_events(): | 
					
						
							|  |  |  |                 for mod in ({}, {"shift": True}, {"ctrl": True}, {"alt": True}): | 
					
						
							|  |  |  |                     for e in kmi_type_auto: | 
					
						
							|  |  |  |                         yield (e, mod) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             iter_events = iter(iter_free_events()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for item_container in items_all: | 
					
						
							|  |  |  |                 item, kmi_found, kmi_exist = item_container | 
					
						
							|  |  |  |                 if kmi_exist: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 kmi_args = None | 
					
						
							|  |  |  |                 while True: | 
					
						
							|  |  |  |                     key, mod = next(iter_events, (None, None)) | 
					
						
							|  |  |  |                     if key is None: | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     kmi_args = {"type": key, **mod} | 
					
						
							|  |  |  |                     kmi_tuple = dict_as_tuple(kmi_args) | 
					
						
							|  |  |  |                     if kmi_tuple in kmi_unique_args: | 
					
						
							|  |  |  |                         kmi_args = None | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2018-05-21 11:54:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 15:47:19 +10:00
										 |  |  |                 if kmi_args is not None: | 
					
						
							|  |  |  |                     kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args) | 
					
						
							|  |  |  |                     kmi.properties.name = item.text | 
					
						
							|  |  |  |                     item_container[2] = kmi | 
					
						
							|  |  |  |                     if use_auto_keymap: | 
					
						
							|  |  |  |                         kmi_unique_args.add(kmi_tuple) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     key = kmi_type_dupe.get(kmi_args["type"]) | 
					
						
							|  |  |  |                     if key is not None: | 
					
						
							|  |  |  |                         kmi_args["type"] = key | 
					
						
							|  |  |  |                         kmi_tuple = dict_as_tuple(kmi_args) | 
					
						
							|  |  |  |                         if not kmi_tuple in kmi_unique_args: | 
					
						
							|  |  |  |                             kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args) | 
					
						
							|  |  |  |                             kmi.properties.name = item.text | 
					
						
							|  |  |  |                             if use_auto_keymap: | 
					
						
							|  |  |  |                                 kmi_unique_args.add(kmi_tuple) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 11:06:34 +10:00
										 |  |  |     if use_hack_properties: | 
					
						
							|  |  |  |         keymap.keymap_items.remove(kmi_hack) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-27 12:22:20 +11:00
										 |  |  |     if use_release_confirm: | 
					
						
							|  |  |  |         kmi = keymap.keymap_items.new( | 
					
						
							|  |  |  |             "ui.button_execute", | 
					
						
							|  |  |  |             type=kmi_toolbar_type, | 
					
						
							|  |  |  |             value='RELEASE', | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         kmi.properties.skip_depressed = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-15 17:12:33 +11:00
										 |  |  |         if use_toolbar_release_hack: | 
					
						
							|  |  |  |             # ... or pass through to let the toolbar know we're released. | 
					
						
							|  |  |  |             # Let the operator know we're released. | 
					
						
							|  |  |  |             kmi = keymap.keymap_items.new( | 
					
						
							|  |  |  |                 "wm.tool_set_by_name", | 
					
						
							|  |  |  |                 type=kmi_toolbar_type, | 
					
						
							|  |  |  |                 value='RELEASE', | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 10:43:15 +02:00
										 |  |  |     wm.keyconfigs.update() | 
					
						
							|  |  |  |     return keymap | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 16:35:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 23:05:13 +11:00
										 |  |  | 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) |