Reuse tool.Blender.add_layout_hotkey_operator

Now all tools are using a general method and it should be easier to maintain the consistency between the tools.

Fixes #6238.

ping @trhyder just in case
This commit is contained in:
Andrej730
2025-02-26 17:40:06 +05:00
parent 746a61a199
commit 2d0bd9782a
7 changed files with 59 additions and 96 deletions
+2 -24
View File
@@ -23,7 +23,7 @@ import bonsai.bim.module.type.prop as type_prop
import ifcopenshell.util.unit import ifcopenshell.util.unit
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData
from typing import Union from functools import partial
def load_custom_icons(): def load_custom_icons():
@@ -330,29 +330,7 @@ def add_header_apply_button(layout, text, apply_operator, cancel_operator, ui_co
row.label(text="Tools") row.label(text="Tools")
def add_layout_hotkey_operator( add_layout_hotkey_operator = partial(tool.Blender.add_layout_hotkey_operator, tool_name="cad", module_name=__name__)
layout: bpy.types.UILayout, text: str, hotkey: str, description: Union[str, None], ui_context: str = ""
) -> bpy.types.OperatorProperties:
parts = hotkey.split("_")
modifier, key = parts
op_text = "" if ui_context == "TOOL_HEADER" else text
custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id
modifier_icon, modifier_str = tool.Blender.KEY_MODIFIERS.get(modifier, ("NONE", ""))
row = layout if ui_context == "TOOL_HEADER" else layout.row(align=True)
op = row.operator("bim.cad_hotkey", text=op_text, icon_value=custom_icon)
if ui_context != "TOOL_HEADER" and len(parts) == 2:
layout = layout.row(align=True) # Create a new line for hotkey display
layout.label(text="", icon=modifier_icon)
layout.label(text="", icon=f"EVENT_{key}")
hotkey_description = f"Hotkey: {modifier_str} {key}"
description = "\n\n".join(filter(None, [hotkey_description if description else ""]))
op.hotkey = hotkey
op.description = description or hotkey_description
return op
custom_icon_previews = None custom_icon_previews = None
@@ -24,6 +24,7 @@ import bonsai.tool as tool
from bonsai.bim.helper import prop_with_search from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.model.data import AuthoringData from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
from functools import partial
class CoveringTool(WorkSpaceTool): class CoveringTool(WorkSpaceTool):
@@ -45,9 +46,7 @@ class CoveringTool(WorkSpaceTool):
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type) CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="covering", module_name=__name__)
args = ("covering", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
class CoveringToolUI: class CoveringToolUI:
@@ -27,7 +27,7 @@ import ifcopenshell.util.representation
from bonsai.bim.module.drawing.data import DecoratorData, AnnotationData from bonsai.bim.module.drawing.data import DecoratorData, AnnotationData
from bonsai.bim.helper import prop_with_search from bonsai.bim.helper import prop_with_search
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
from typing import Union from functools import partial
class LaunchAnnotationTypeManager(bpy.types.Operator): class LaunchAnnotationTypeManager(bpy.types.Operator):
@@ -135,23 +135,9 @@ class AnnotationTool(WorkSpaceTool):
AnnotationToolUI.draw(context, layout) AnnotationToolUI.draw(context, layout)
def add_layout_hotkey_operator( add_layout_hotkey_operator = partial(
layout: bpy.types.UILayout, text: str, hotkey: str, description: Union[str, None] tool.Blender.add_layout_hotkey_operator, tool_name="annotation", module_name=__name__
) -> tuple[bpy.types.OperatorProperties, bpy.types.UILayout]: )
modifiers = {
"A": "EVENT_ALT",
"S": "EVENT_SHIFT",
}
modifier, key = hotkey.split("_")
row = layout.row(align=True)
row.label(text="", icon=modifiers[modifier])
row.label(text="", icon=f"EVENT_{key}")
op = row.operator("bim.annotation_hotkey", text=text)
op.hotkey = hotkey
op.description = description
return op, row
# TODO: move to operator # TODO: move to operator
@@ -30,6 +30,7 @@ from bonsai.bim.module.model.data import AuthoringData, ItemData
from bonsai.bim.module.system.data import PortData from bonsai.bim.module.system.data import PortData
from bonsai.bim.module.model.prop import get_ifc_class from bonsai.bim.module.model.prop import get_ifc_class
from typing import Optional, Union from typing import Optional, Union
from functools import partial
def load_custom_icons(): def load_custom_icons():
@@ -365,43 +366,7 @@ class CableTool(BimTool):
ifc_element_type = "IfcCableSegmentType" ifc_element_type = "IfcCableSegmentType"
def add_layout_hotkey_operator( add_layout_hotkey_operator = partial(tool.Blender.add_layout_hotkey_operator, tool_name="bim", module_name=__name__)
layout: bpy.types.UILayout,
text: str,
hotkey: str,
description: Union[str, None],
ui_context: str = "",
*,
operator: str = "bim.hotkey",
) -> bpy.types.OperatorProperties:
"""
:param operator: Operator to display in UI. Displaying the specific operator in UI can be useful
to provide poll error messages.
"""
parts = hotkey.split("_") if hotkey else []
modifier, key = (parts + ["", ""])[:2]
op_text = "" if ui_context == "TOOL_HEADER" else text
custom_icon = custom_icon_previews.get(text.upper().replace(" ", "_"), custom_icon_previews["IFC"]).icon_id
modifier_icon, modifier_str = tool.Blender.KEY_MODIFIERS.get(modifier, ("NONE", ""))
row = layout.row(align=True)
op = row.operator(operator, text=op_text, icon_value=custom_icon)
if ui_context != "TOOL_HEADER":
row.label(text="", icon=modifier_icon)
row.label(text="", icon=f"EVENT_{key}" if key else "BLANK1")
hotkey_description = f"Hotkey: {modifier_str} {key}".strip()
description = "\n\n".join(filter(None, [description, hotkey_description]))
if operator == "bim.hotkey":
op.hotkey = hotkey
if ui_context == "TOOL_HEADER":
op.description = text + "\n" + description
else:
op.description = description
return op
def format_ifc_camel_case(string): def format_ifc_camel_case(string):
@@ -23,6 +23,7 @@ import bonsai.tool as tool
from bonsai.bim.module.model.data import AuthoringData from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
import bonsai.core.spatial import bonsai.core.spatial
from functools import partial
class SpatialTool(WorkSpaceTool): class SpatialTool(WorkSpaceTool):
@@ -49,9 +50,7 @@ class SpatialTool(WorkSpaceTool):
SpatialToolUI.draw(context, layout) SpatialToolUI.draw(context, layout)
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="spatial", module_name=__name__)
args = ("spatial", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
class SpatialToolUI: class SpatialToolUI:
@@ -21,6 +21,7 @@ import os
import bpy import bpy
import bonsai.tool as tool import bonsai.tool as tool
from bpy.types import WorkSpaceTool from bpy.types import WorkSpaceTool
from functools import partial
class StructuralTool(WorkSpaceTool): class StructuralTool(WorkSpaceTool):
@@ -41,9 +42,7 @@ class StructuralTool(WorkSpaceTool):
StructuralToolUI.draw(context, layout) StructuralToolUI.draw(context, layout)
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None: add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="structural", module_name=__name__)
args = ("structural", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
# NOTES before adding new operators: # NOTES before adding new operators:
+45 -8
View File
@@ -500,18 +500,55 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def add_layout_hotkey_operator( def add_layout_hotkey_operator(
cls, tool_name: str, layout: bpy.types.UILayout, text: str, hotkey: str, description: str cls,
layout: bpy.types.UILayout,
text: str,
hotkey: str,
description: str,
ui_context: str = "",
*,
tool_name: str,
module_name: str,
operator: Optional[str] = None,
) -> tuple[bpy.types.OperatorProperties, bpy.types.UILayout]: ) -> tuple[bpy.types.OperatorProperties, bpy.types.UILayout]:
"""
:param module_name: Provide `__name__` of the current module,
so method could pick up icon previews based on the module's `custom_icon_previews` attribute.
:param operator: Operator to display in UI. Displaying the specific operator in UI can be useful
to provide poll error messages.
"""
if tool_name == "bim":
hotkey_operator = "bim.hotkey"
else:
hotkey_operator = f"bim.{tool_name}_hotkey"
operator_to_use = operator or hotkey_operator
modifier, key = hotkey.split("_") modifier, key = hotkey.split("_")
op_text = "" if ui_context == "TOOL_HEADER" else text
row = layout.row(align=True)
modifier_icon, modifier_str = cls.KEY_MODIFIERS.get(modifier, ("NONE", "")) modifier_icon, modifier_str = cls.KEY_MODIFIERS.get(modifier, ("NONE", ""))
row.label(text="", icon=modifier_icon)
row.label(text="", icon=f"EVENT_{key}")
op = row.operator(f"bim.{tool_name}_hotkey", text=text) row = layout if ui_context == "TOOL_HEADER" else layout.row(align=True)
op.hotkey = hotkey module = sys.modules[module_name]
op.description = description icon_previews: Union[bpy.utils.previews.ImagePreviewCollection, None]
icon_previews = getattr(module, "custom_icon_previews", None)
if icon_previews:
custom_icon = icon_previews.get(text.upper().replace(" ", "_"), icon_previews["IFC"]).icon_id
op = row.operator(operator_to_use, text=op_text, icon_value=custom_icon)
else:
op = row.operator(operator_to_use, text=op_text)
if ui_context != "TOOL_HEADER":
row.label(text="", icon=modifier_icon)
row.label(text="", icon=f"EVENT_{key}")
if operator_to_use == hotkey_operator:
hotkey_description = f"Hotkey: {modifier_str} {key}".strip()
description = "\n\n".join(filter(None, [description, hotkey_description]))
op.hotkey = hotkey
if ui_context == "TOOL_HEADER":
op.description = text + "\n" + description
else:
op.description = description
return op, row return op, row
@classmethod @classmethod