66857701669e0e74d624f171ad28bba9f55e6669
[blender.git] / release / scripts / modules / bl_app_override / __init__.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 """
22 Module to manage overriding various parts of Blender.
23
24 Intended for use with 'app_templates', though it can be used from anywhere.
25 """
26
27
28 # TODO, how to check these aren't from add-ons.
29 # templates might need to un-register while filtering.
30 def class_filter(cls_parent, **kw):
31     whitelist = kw.pop("whitelist", None)
32     blacklist = kw.pop("blacklist", None)
33     kw_items = tuple(kw.items())
34     for cls in cls_parent.__subclasses__():
35         # same as is_registered()
36         if "bl_rna" in cls.__dict__:
37             if blacklist is not None and cls.__name__ in blacklist:
38                 continue
39             if ((whitelist is not None and cls.__name__ is whitelist) or
40                     all((getattr(cls, attr) in expect) for attr, expect in kw_items)):
41                 yield cls
42
43
44 def ui_draw_filter_register(
45     *,
46     ui_ignore_classes=None,
47     ui_ignore_operator=None,
48     ui_ignore_property=None,
49     ui_ignore_menu=None,
50     ui_ignore_label=None
51 ):
52     import bpy
53
54     UILayout = bpy.types.UILayout
55
56     if ui_ignore_classes is None:
57         ui_ignore_classes = (
58             bpy.types.Panel,
59             bpy.types.Menu,
60             bpy.types.Header,
61         )
62
63     class OperatorProperties_Fake:
64         pass
65
66     class UILayout_Fake(bpy.types.UILayout):
67         __slots__ = ()
68
69         def __getattribute__(self, attr):
70             # ensure we always pass down UILayout_Fake instances
71             if attr in {"row", "split", "column", "box", "column_flow"}:
72                 real_func = UILayout.__getattribute__(self, attr)
73
74                 def dummy_func(*args, **kw):
75                     # print("wrapped", attr)
76                     ret = real_func(*args, **kw)
77                     return UILayout_Fake(ret)
78                 return dummy_func
79
80             elif attr in {"operator", "operator_menu_enum", "operator_enum"}:
81                 if ui_ignore_operator is None:
82                     return UILayout.__getattribute__(self, attr)
83
84                 real_func = UILayout.__getattribute__(self, attr)
85
86                 def dummy_func(*args, **kw):
87                     # print("wrapped", attr)
88                     if not ui_ignore_operator(args[0]):
89                         ret = real_func(*args, **kw)
90                     else:
91                         # UILayout.__getattribute__(self, "label")()
92                         # may need to be set
93                         ret = OperatorProperties_Fake()
94                     return ret
95                 return dummy_func
96
97             elif attr in {"prop", "prop_enum"}:
98                 if ui_ignore_property is None:
99                     return UILayout.__getattribute__(self, attr)
100
101                 real_func = UILayout.__getattribute__(self, attr)
102
103                 def dummy_func(*args, **kw):
104                     # print("wrapped", attr)
105                     if not ui_ignore_property(args[0].__class__.__name__, args[1]):
106                         ret = real_func(*args, **kw)
107                     else:
108                         ret = None
109                     return ret
110                 return dummy_func
111
112             elif attr == "menu":
113                 if ui_ignore_menu is None:
114                     return UILayout.__getattribute__(self, attr)
115
116                 real_func = UILayout.__getattribute__(self, attr)
117
118                 def dummy_func(*args, **kw):
119                     # print("wrapped", attr)
120                     if not ui_ignore_menu(args[0]):
121                         ret = real_func(*args, **kw)
122                     else:
123                         ret = None
124                     return ret
125                 return dummy_func
126
127             elif attr == "label":
128                 if ui_ignore_label is None:
129                     return UILayout.__getattribute__(self, attr)
130
131                 real_func = UILayout.__getattribute__(self, attr)
132
133                 def dummy_func(*args, **kw):
134                     # print("wrapped", attr)
135                     if not ui_ignore_label(args[0] if args else kw.get("text", "")):
136                         ret = real_func(*args, **kw)
137                     else:
138                         # ret = real_func()
139                         ret = None
140                     return ret
141                 return dummy_func
142             else:
143                 return UILayout.__getattribute__(self, attr)
144             # print(self, attr)
145
146         def operator(*args, **kw):
147             return super().operator(*args, **kw)
148
149     def draw_override(func_orig, self_real, context):
150         # simple, no wrapping
151         # return func_orig(self_wrap, context)
152
153         class Wrapper(self_real.__class__):
154             __slots__ = ()
155             def __getattribute__(self, attr):
156                 if attr == "layout":
157                     return UILayout_Fake(self_real.layout)
158                 else:
159                     cls = super()
160                     try:
161                         return cls.__getattr__(self, attr)
162                     except AttributeError:
163                         # class variable
164                         try:
165                             return getattr(cls, attr)
166                         except AttributeError:
167                             # for preset bl_idname access
168                             return getattr(UILayout(self), attr)
169
170             @property
171             def layout(self):
172                 # print("wrapped")
173                 return self_real.layout
174
175         return func_orig(Wrapper(self_real), context)
176
177     ui_ignore_store = []
178
179     for cls in ui_ignore_classes:
180         for subcls in list(cls.__subclasses__()):
181             if "draw" in subcls.__dict__:  # don't want to get parents draw()
182
183                 def replace_draw():
184                     # function also serves to hold draw_old in a local name-space
185                     draw_orig = subcls.draw
186
187                     def draw(self, context):
188                         return draw_override(draw_orig, self, context)
189                     subcls.draw = draw
190
191                 ui_ignore_store.append((subcls, "draw", subcls.draw))
192
193                 replace_draw()
194
195     return ui_ignore_store
196
197
198 def ui_draw_filter_unregister(ui_ignore_store):
199     for (obj, attr, value) in ui_ignore_store:
200         setattr(obj, attr, value)