2023-08-16 00:20:26 +10:00
|
|
|
# SPDX-FileCopyrightText: 2017-2023 Blender Authors
|
2023-06-15 13:09:04 +10:00
|
|
|
#
|
2022-02-11 09:07:11 +11:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2017-03-25 09:29:51 +11:00
|
|
|
|
|
|
|
"""
|
|
|
|
Module to manage overriding various parts of Blender.
|
|
|
|
|
|
|
|
Intended for use with 'app_templates', though it can be used from anywhere.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# TODO, how to check these aren't from add-ons.
|
|
|
|
# templates might need to un-register while filtering.
|
|
|
|
def class_filter(cls_parent, **kw):
|
|
|
|
whitelist = kw.pop("whitelist", None)
|
|
|
|
blacklist = kw.pop("blacklist", None)
|
|
|
|
kw_items = tuple(kw.items())
|
|
|
|
for cls in cls_parent.__subclasses__():
|
|
|
|
# same as is_registered()
|
|
|
|
if "bl_rna" in cls.__dict__:
|
|
|
|
if blacklist is not None and cls.__name__ in blacklist:
|
|
|
|
continue
|
|
|
|
if ((whitelist is not None and cls.__name__ is whitelist) or
|
|
|
|
all((getattr(cls, attr) in expect) for attr, expect in kw_items)):
|
|
|
|
yield cls
|
|
|
|
|
|
|
|
|
|
|
|
def ui_draw_filter_register(
|
|
|
|
*,
|
|
|
|
ui_ignore_classes=None,
|
|
|
|
ui_ignore_operator=None,
|
|
|
|
ui_ignore_property=None,
|
|
|
|
ui_ignore_menu=None,
|
2017-03-27 21:33:51 +11:00
|
|
|
ui_ignore_label=None
|
2017-03-25 09:29:51 +11:00
|
|
|
):
|
|
|
|
import bpy
|
|
|
|
|
|
|
|
UILayout = bpy.types.UILayout
|
|
|
|
|
|
|
|
if ui_ignore_classes is None:
|
|
|
|
ui_ignore_classes = (
|
|
|
|
bpy.types.Panel,
|
|
|
|
bpy.types.Menu,
|
|
|
|
bpy.types.Header,
|
|
|
|
)
|
|
|
|
|
|
|
|
class OperatorProperties_Fake:
|
|
|
|
pass
|
|
|
|
|
|
|
|
class UILayout_Fake(bpy.types.UILayout):
|
|
|
|
__slots__ = ()
|
|
|
|
|
|
|
|
def __getattribute__(self, attr):
|
|
|
|
# ensure we always pass down UILayout_Fake instances
|
|
|
|
if attr in {"row", "split", "column", "box", "column_flow"}:
|
|
|
|
real_func = UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
def dummy_func(*args, **kw):
|
|
|
|
# print("wrapped", attr)
|
|
|
|
ret = real_func(*args, **kw)
|
|
|
|
return UILayout_Fake(ret)
|
|
|
|
return dummy_func
|
|
|
|
|
2017-12-08 12:48:12 +11:00
|
|
|
elif attr in {"operator", "operator_menu_enum", "operator_enum", "operator_menu_hold"}:
|
2017-03-25 09:29:51 +11:00
|
|
|
if ui_ignore_operator is None:
|
|
|
|
return UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
real_func = UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
def dummy_func(*args, **kw):
|
|
|
|
# print("wrapped", attr)
|
2017-12-08 12:48:12 +11:00
|
|
|
ui_test = ui_ignore_operator(args[0])
|
|
|
|
if ui_test is False:
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = real_func(*args, **kw)
|
|
|
|
else:
|
2017-12-08 12:48:12 +11:00
|
|
|
if ui_test is None:
|
2019-09-16 04:06:21 +10:00
|
|
|
UILayout.__getattribute__(self, "label")(text="")
|
2017-12-08 12:48:12 +11:00
|
|
|
else:
|
2022-09-14 16:18:59 +10:00
|
|
|
assert ui_test is True
|
2017-03-25 09:29:51 +11:00
|
|
|
# may need to be set
|
|
|
|
ret = OperatorProperties_Fake()
|
|
|
|
return ret
|
|
|
|
return dummy_func
|
|
|
|
|
|
|
|
elif attr in {"prop", "prop_enum"}:
|
|
|
|
if ui_ignore_property is None:
|
|
|
|
return UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
real_func = UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
def dummy_func(*args, **kw):
|
|
|
|
# print("wrapped", attr)
|
2017-12-08 12:48:12 +11:00
|
|
|
ui_test = ui_ignore_property(args[0].__class__.__name__, args[1])
|
|
|
|
if ui_test is False:
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = real_func(*args, **kw)
|
|
|
|
else:
|
2017-12-08 12:48:12 +11:00
|
|
|
if ui_test is None:
|
2019-09-16 04:06:21 +10:00
|
|
|
UILayout.__getattribute__(self, "label")(text="")
|
2017-12-08 12:48:12 +11:00
|
|
|
else:
|
2022-09-14 16:18:59 +10:00
|
|
|
assert ui_test is True
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = None
|
|
|
|
return ret
|
|
|
|
return dummy_func
|
|
|
|
|
|
|
|
elif attr == "menu":
|
|
|
|
if ui_ignore_menu is None:
|
|
|
|
return UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
real_func = UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
def dummy_func(*args, **kw):
|
|
|
|
# print("wrapped", attr)
|
2017-12-08 12:48:12 +11:00
|
|
|
ui_test = ui_ignore_menu(args[0])
|
|
|
|
if ui_test is False:
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = real_func(*args, **kw)
|
|
|
|
else:
|
2017-12-08 12:48:12 +11:00
|
|
|
if ui_test is None:
|
2019-09-16 04:06:21 +10:00
|
|
|
UILayout.__getattribute__(self, "label")(text="")
|
2017-12-08 12:48:12 +11:00
|
|
|
else:
|
2022-09-14 16:18:59 +10:00
|
|
|
assert ui_test is True
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = None
|
|
|
|
return ret
|
|
|
|
return dummy_func
|
|
|
|
|
|
|
|
elif attr == "label":
|
|
|
|
if ui_ignore_label is None:
|
|
|
|
return UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
real_func = UILayout.__getattribute__(self, attr)
|
|
|
|
|
|
|
|
def dummy_func(*args, **kw):
|
|
|
|
# print("wrapped", attr)
|
2017-12-08 12:48:12 +11:00
|
|
|
ui_test = ui_ignore_label(args[0] if args else kw.get("text", ""))
|
|
|
|
if ui_test is False:
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = real_func(*args, **kw)
|
|
|
|
else:
|
2017-12-08 12:48:12 +11:00
|
|
|
if ui_test is None:
|
2019-09-16 04:06:21 +10:00
|
|
|
real_func(text="")
|
2017-12-08 12:48:12 +11:00
|
|
|
else:
|
2022-09-14 16:18:59 +10:00
|
|
|
assert ui_test is True
|
2017-03-25 09:29:51 +11:00
|
|
|
ret = None
|
|
|
|
return ret
|
|
|
|
return dummy_func
|
|
|
|
else:
|
|
|
|
return UILayout.__getattribute__(self, attr)
|
|
|
|
# print(self, attr)
|
|
|
|
|
|
|
|
def operator(*args, **kw):
|
|
|
|
return super().operator(*args, **kw)
|
|
|
|
|
|
|
|
def draw_override(func_orig, self_real, context):
|
2017-03-29 14:29:18 +11:00
|
|
|
cls_real = self_real.__class__
|
|
|
|
if cls_real is super:
|
|
|
|
# simple, no wrapping
|
|
|
|
return func_orig(self_real, context)
|
2017-03-25 09:29:51 +11:00
|
|
|
|
2017-03-29 14:29:18 +11:00
|
|
|
class Wrapper(cls_real):
|
2017-03-25 09:29:51 +11:00
|
|
|
__slots__ = ()
|
2018-07-03 06:27:53 +02:00
|
|
|
|
2017-03-25 09:29:51 +11:00
|
|
|
def __getattribute__(self, attr):
|
|
|
|
if attr == "layout":
|
|
|
|
return UILayout_Fake(self_real.layout)
|
|
|
|
else:
|
|
|
|
cls = super()
|
|
|
|
try:
|
|
|
|
return cls.__getattr__(self, attr)
|
|
|
|
except AttributeError:
|
|
|
|
# class variable
|
|
|
|
try:
|
|
|
|
return getattr(cls, attr)
|
|
|
|
except AttributeError:
|
|
|
|
# for preset bl_idname access
|
|
|
|
return getattr(UILayout(self), attr)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def layout(self):
|
|
|
|
# print("wrapped")
|
|
|
|
return self_real.layout
|
|
|
|
|
|
|
|
return func_orig(Wrapper(self_real), context)
|
|
|
|
|
|
|
|
ui_ignore_store = []
|
|
|
|
|
|
|
|
for cls in ui_ignore_classes:
|
|
|
|
for subcls in list(cls.__subclasses__()):
|
|
|
|
if "draw" in subcls.__dict__: # don't want to get parents draw()
|
|
|
|
|
|
|
|
def replace_draw():
|
|
|
|
# function also serves to hold draw_old in a local name-space
|
|
|
|
draw_orig = subcls.draw
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
return draw_override(draw_orig, self, context)
|
|
|
|
subcls.draw = draw
|
|
|
|
|
|
|
|
ui_ignore_store.append((subcls, "draw", subcls.draw))
|
|
|
|
|
|
|
|
replace_draw()
|
|
|
|
|
|
|
|
return ui_ignore_store
|
|
|
|
|
|
|
|
|
|
|
|
def ui_draw_filter_unregister(ui_ignore_store):
|
|
|
|
for (obj, attr, value) in ui_ignore_store:
|
|
|
|
setattr(obj, attr, value)
|