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
from bpy.types import WorkSpaceTool
from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData
from typing import Union
from functools import partial
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")
def add_layout_hotkey_operator(
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
add_layout_hotkey_operator = partial(tool.Blender.add_layout_hotkey_operator, tool_name="cad", module_name=__name__)
custom_icon_previews = None
@@ -24,6 +24,7 @@ import bonsai.tool as tool
from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool
from functools import partial
class CoveringTool(WorkSpaceTool):
@@ -45,9 +46,7 @@ class CoveringTool(WorkSpaceTool):
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:
args = ("covering", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="covering", module_name=__name__)
class CoveringToolUI:
@@ -27,7 +27,7 @@ import ifcopenshell.util.representation
from bonsai.bim.module.drawing.data import DecoratorData, AnnotationData
from bonsai.bim.helper import prop_with_search
from bpy.types import WorkSpaceTool
from typing import Union
from functools import partial
class LaunchAnnotationTypeManager(bpy.types.Operator):
@@ -135,23 +135,9 @@ class AnnotationTool(WorkSpaceTool):
AnnotationToolUI.draw(context, layout)
def add_layout_hotkey_operator(
layout: bpy.types.UILayout, text: str, hotkey: str, description: Union[str, None]
) -> 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
add_layout_hotkey_operator = partial(
tool.Blender.add_layout_hotkey_operator, tool_name="annotation", module_name=__name__
)
# 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.model.prop import get_ifc_class
from typing import Optional, Union
from functools import partial
def load_custom_icons():
@@ -365,43 +366,7 @@ class CableTool(BimTool):
ifc_element_type = "IfcCableSegmentType"
def add_layout_hotkey_operator(
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
add_layout_hotkey_operator = partial(tool.Blender.add_layout_hotkey_operator, tool_name="bim", module_name=__name__)
def format_ifc_camel_case(string):
@@ -23,6 +23,7 @@ import bonsai.tool as tool
from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool
import bonsai.core.spatial
from functools import partial
class SpatialTool(WorkSpaceTool):
@@ -49,9 +50,7 @@ class SpatialTool(WorkSpaceTool):
SpatialToolUI.draw(context, layout)
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
args = ("spatial", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="spatial", module_name=__name__)
class SpatialToolUI:
@@ -21,6 +21,7 @@ import os
import bpy
import bonsai.tool as tool
from bpy.types import WorkSpaceTool
from functools import partial
class StructuralTool(WorkSpaceTool):
@@ -41,9 +42,7 @@ class StructuralTool(WorkSpaceTool):
StructuralToolUI.draw(context, layout)
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
args = ("structural", layout, text, hotkey, description)
tool.Blender.add_layout_hotkey_operator(*args)
add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="structural", module_name=__name__)
# NOTES before adding new operators:
+45 -8
View File
@@ -500,18 +500,55 @@ class Blender(bonsai.core.tool.Blender):
@classmethod
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]:
"""
: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("_")
row = layout.row(align=True)
op_text = "" if ui_context == "TOOL_HEADER" else text
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)
op.hotkey = hotkey
op.description = description
row = layout if ui_context == "TOOL_HEADER" else layout.row(align=True)
module = sys.modules[module_name]
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
@classmethod