Files
blender-portable-repo/extensions/blender_org/bool_tool/ui.py
T
2026-03-17 14:58:51 -06:00

271 lines
8.5 KiB
Python

import bpy
from .functions.poll import is_canvas
from .functions.list import list_canvas_cutters
#### ------------------------------ /ui/ ------------------------------ ####
def carve_menu(self, context):
layout = self.layout
layout.operator("object.carve_box", text="Box Carve").shape='BOX'
layout.operator("object.carve_box", text="Circle Carve").shape='CIRCLE'
layout.operator("object.carve_polyline", text="Polyline Carve")
def boolean_operators_menu(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
col = layout.column(align=True)
col.label(text="Auto Boolean")
col.operator("object.boolean_auto_difference", text="Difference", icon='SELECT_SUBTRACT')
col.operator("object.boolean_auto_union", text="Union", icon='SELECT_EXTEND')
col.operator("object.boolean_auto_intersect", text="Intersect", icon='SELECT_INTERSECT')
col.operator("object.boolean_auto_slice", text="Slice", icon='SELECT_DIFFERENCE')
col.separator()
col.label(text="Brush Boolean")
col.operator("object.boolean_brush_difference", text="Difference", icon='SELECT_SUBTRACT')
col.operator("object.boolean_brush_union", text="Union", icon='SELECT_EXTEND')
col.operator("object.boolean_brush_intersect", text="Intersect", icon='SELECT_INTERSECT')
col.operator("object.boolean_brush_slice", text="Slice", icon='SELECT_DIFFERENCE')
def boolean_extras_menu(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
col = layout.column(align=True)
if context.active_object:
# Canvas operators
active_object = context.active_object
if active_object.booleans.canvas == True and any(mod.name.startswith("boolean_") for mod in active_object.modifiers):
col.separator()
col.operator("object.boolean_toggle_all", text="Toggle All Cuters")
col.operator("object.boolean_apply_all", text="Apply All Cutters")
col.operator("object.boolean_remove_all", text="Remove All Cutters")
# Cutter operators
if active_object.booleans.cutter:
col.separator()
col.operator("object.boolean_toggle_cutter", text="Toggle Cutter").method='ALL'
col.operator("object.boolean_apply_cutter", text="Apply Cutter").method='ALL'
col.operator("object.boolean_remove_cutter", text="Remove Cutter").method='ALL'
#### ------------------------------ PANELS ------------------------------ ####
# Boolean Operators Panel
class VIEW3D_PT_boolean(bpy.types.Panel):
bl_label = "Boolean"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Edit"
bl_context = "objectmode"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
prefs = context.preferences.addons[__package__].preferences
return prefs.show_in_sidebar
def draw(self, context):
boolean_operators_menu(self, context)
# Properties Panel
class VIEW3D_PT_boolean_properties(bpy.types.Panel):
bl_label = "Properties"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Edit"
bl_context = "objectmode"
bl_parent_id = "VIEW3D_PT_boolean"
@classmethod
def poll(cls, context):
prefs = context.preferences.addons[__package__].preferences
if prefs.show_in_sidebar:
if context.active_object:
if is_canvas(context.active_object) or context.active_object.booleans.cutter:
return True
else:
return False
else:
return False
else:
return False
def draw(self, context):
boolean_extras_menu(self, context)
# Cutters Panel
class VIEW3D_PT_boolean_cutters(bpy.types.Panel):
bl_label = "Cutters"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Edit"
bl_context = "objectmode"
bl_parent_id = "VIEW3D_PT_boolean"
@classmethod
def poll(cls, context):
prefs = context.preferences.addons[__package__].preferences
if prefs.show_in_sidebar:
if context.active_object:
if is_canvas(context.active_object):
return True
else:
return False
else:
return False
else:
return False
def draw(self, context):
layout = self.layout
canvas = context.active_object
__, modifiers = list_canvas_cutters([canvas])
for mod in modifiers:
col = layout.column(align=True)
row = col.row(align=True)
# icon
if mod.operation == 'DIFFERENCE':
icon = 'SELECT_SUBTRACT'
elif mod.operation == 'UNION':
icon = 'SELECT_EXTEND'
elif mod.operation == 'INTERSECT':
icon = 'SELECT_INTERSECT'
row.prop(mod.object, "name", text="", icon=icon)
# Toggle
op_toggle = row.operator("object.boolean_toggle_cutter", text="", icon='HIDE_OFF' if mod.show_viewport else 'HIDE_ON')
op_toggle.method = 'SPECIFIED'
op_toggle.specified_cutter = mod.object.name
op_toggle.specified_canvas = canvas.name
# Apply
op_apply = row.operator("object.boolean_apply_cutter", text="", icon='CHECKMARK')
op_apply.method = 'SPECIFIED'
op_apply.specified_cutter = mod.object.name
op_apply.specified_canvas = canvas.name
# Remove
op_remove = row.operator("object.boolean_remove_cutter", text="", icon='X')
op_remove.method = 'SPECIFIED'
op_remove.specified_cutter = mod.object.name
op_remove.specified_canvas = canvas.name
#### ------------------------------ MENUS ------------------------------ ####
# Carve Menu
class VIEW3D_MT_carve(bpy.types.Menu):
bl_label = "Carve"
bl_idname = "VIEW3D_MT_carve"
def draw(self, context):
carve_menu(self, context)
# 3D Viewport (Object Mode) -> Object
class VIEW3D_MT_boolean(bpy.types.Menu):
bl_label = "Boolean"
bl_idname = "VIEW3D_MT_boolean"
def draw(self, context):
layout = self.layout
layout.menu("VIEW3D_MT_carve")
layout.separator()
boolean_operators_menu(self, context)
boolean_extras_menu(self, context)
# Shift-Ctrl-B Menu
class VIEW3D_MT_boolean_popup(bpy.types.Menu):
bl_label = "Boolean"
bl_idname = "VIEW3D_MT_boolean_popup"
def draw(self, context):
boolean_operators_menu(self, context)
boolean_extras_menu(self, context)
def object_mode_menu(self, context):
layout = self.layout
layout.separator()
layout.menu("VIEW3D_MT_boolean")
def edit_mode_menu(self, context):
layout = self.layout
layout.separator()
layout.menu("VIEW3D_MT_carve")
def boolean_select_menu(self, context):
layout = self.layout
active_obj = context.active_object
if active_obj:
if active_obj.booleans.canvas == True or active_obj.booleans.cutter:
layout.separator()
if active_obj.booleans.canvas == True:
layout.operator("object.boolean_select_all", text="Boolean Cutters")
if active_obj.booleans.cutter:
layout.operator("object.select_cutter_canvas", text="Boolean Canvases")
#### ------------------------------ REGISTRATION ------------------------------ ####
addon_keymaps = []
classes = [
VIEW3D_MT_carve,
VIEW3D_MT_boolean,
VIEW3D_MT_boolean_popup,
VIEW3D_PT_boolean,
VIEW3D_PT_boolean_properties,
VIEW3D_PT_boolean_cutters,
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
# MENU
bpy.types.VIEW3D_MT_object.append(object_mode_menu)
bpy.types.VIEW3D_MT_select_object.append(boolean_select_menu)
bpy.types.VIEW3D_MT_edit_mesh.append(edit_mode_menu)
# KEYMAP
addon = bpy.context.window_manager.keyconfigs.addon
km = addon.keymaps.new(name="Object Mode")
kmi = km.keymap_items.new("wm.call_menu", 'B', 'PRESS', ctrl=True, shift=True)
kmi.properties.name = "VIEW3D_MT_boolean_popup"
kmi.active = True
addon_keymaps.append((km, kmi))
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
# MENU
bpy.types.VIEW3D_MT_object.remove(object_mode_menu)
bpy.types.VIEW3D_MT_select_object.remove(boolean_select_menu)
bpy.types.VIEW3D_MT_edit_mesh.remove(edit_mode_menu)
# KEYMAP
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()