Blender V4.3
interface_template_light_linking.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "UI_interface.hh"
10
11#include <cstdio>
12#include <memory>
13
14#include <fmt/format.h>
15
16#include "BLT_translation.hh"
17
19#include "DNA_object_types.h"
20
21#include "BKE_context.hh"
22#include "BKE_light_linking.h"
23
24#include "RNA_access.hh"
25#include "RNA_prototypes.hh"
26
27#include "UI_interface.hh"
28#include "UI_resources.hh"
29#include "UI_tree_view.hh"
30
31#include "WM_api.hh"
32
33#include "ED_undo.hh"
34
36
37namespace {
38
39class CollectionDropTarget {
40 Collection &collection_;
41
42 public:
43 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const
44 {
45 if (drag.type != WM_DRAG_ID) {
46 return false;
47 }
48
49 const wmDragID *drag_id = static_cast<wmDragID *>(drag.ids.first);
50 if (!drag_id) {
51 return false;
52 }
53
54 /* The dragged IDs are guaranteed to be the same type, so only check the type of the first one.
55 */
56 const ID_Type id_type = GS(drag_id->id->name);
57 if (!ELEM(id_type, ID_OB, ID_GR)) {
58 *r_disabled_hint = "Can only add objects and collections to the light linking collection";
59 return false;
60 }
61
62 return true;
63 }
64
65 CollectionDropTarget(Collection &collection) : collection_(collection) {}
66
67 Collection &get_collection() const
68 {
69 return collection_;
70 }
71};
72
76class InsertCollectionDropTarget : public DropTargetInterface {
77 CollectionDropTarget collection_target_;
78
79 public:
80 InsertCollectionDropTarget(Collection &collection) : collection_target_(collection) {}
81
82 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
83 {
84 return collection_target_.can_drop(drag, r_disabled_hint);
85 }
86
87 std::string drop_tooltip(const DragInfo & /*drag*/) const override
88 {
89 return TIP_("Add to linking collection");
90 }
91
92 bool on_drop(bContext *C, const DragInfo &drag) const override
93 {
94 Main *bmain = CTX_data_main(C);
95 Scene *scene = CTX_data_scene(C);
96
97 LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
99 &collection_target_.get_collection(),
100 drag_id->id,
102 }
103
104 /* It is possible that the light linking collection is also used by the view layer.
105 * For this case send a notifier so that the UI is updated for the changes in the collection
106 * content. */
108
109 ED_undo_push(C, "Add to linking collection");
110
111 return true;
112 }
113};
114
115class ReorderCollectionDropTarget : public TreeViewItemDropTarget {
116 CollectionDropTarget collection_target_;
117 const ID &drop_id_;
118
119 public:
120 ReorderCollectionDropTarget(AbstractTreeViewItem &item,
121 Collection &collection,
122 const ID &drop_id)
124 collection_target_(collection),
125 drop_id_(drop_id)
126 {
127 }
128
129 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
130 {
131 return collection_target_.can_drop(drag, r_disabled_hint);
132 }
133
134 std::string drop_tooltip(const DragInfo &drag) const override
135 {
136 const StringRef drop_name = drop_id_.name + 2;
137
138 switch (drag.drop_location) {
140 return "Add to linking collection";
142 return fmt::format(TIP_("Add to linking collection before {}"), drop_name);
144 return fmt::format(TIP_("Add to linking collection after {}"), drop_name);
145 }
146
147 return "";
148 }
149
150 bool on_drop(bContext *C, const DragInfo &drag) const override
151 {
152 Main *bmain = CTX_data_main(C);
153 Scene *scene = CTX_data_scene(C);
154
155 Collection &collection = collection_target_.get_collection();
157
158 LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
159 if (drag_id->id == &drop_id_) {
160 continue;
161 }
162
163 BKE_light_linking_unlink_id_from_collection(bmain, &collection, drag_id->id, nullptr);
164
165 switch (drag.drop_location) {
168 bmain, &collection, drag_id->id, link_state);
169 break;
172 bmain, &collection, drag_id->id, &drop_id_, link_state);
173 break;
176 bmain, &collection, drag_id->id, &drop_id_, link_state);
177 break;
178 }
179 }
180
181 /* It is possible that the light linking collection is also used by the view layer.
182 * For this case send a notifier so that the UI is updated for the changes in the collection
183 * content. */
185
186 ED_undo_push(C, "Add to linking collection");
187
188 return true;
189 }
190};
191
192class ItemDragController : public AbstractViewItemDragController {
193 ID &id_;
194
195 public:
196 explicit ItemDragController(AbstractView &view, ID &id)
197 : AbstractViewItemDragController(view), id_(id)
198 {
199 }
200
201 eWM_DragDataType get_drag_type() const override
202 {
203 return WM_DRAG_ID;
204 }
205
206 void *create_drag_data() const override
207 {
208 return static_cast<void *>(&id_);
209 }
210};
211
212class CollectionViewItem : public BasicTreeViewItem {
213 uiLayout &context_layout_;
214 Collection &collection_;
215
216 ID &id_;
217 CollectionLightLinking &collection_light_linking_;
218
219 public:
220 CollectionViewItem(uiLayout &context_layout,
221 Collection &collection,
222 ID &id,
223 CollectionLightLinking &collection_light_linking,
224 const BIFIconID icon)
225 : BasicTreeViewItem(id.name + 2, icon),
226 context_layout_(context_layout),
227 collection_(collection),
228 id_(id),
229 collection_light_linking_(collection_light_linking)
230 {
231 }
232
233 void build_row(uiLayout &row) override
234 {
235 if (is_active()) {
236 PointerRNA id_ptr = RNA_id_pointer_create(&id_);
237 PointerRNA collection_ptr = RNA_id_pointer_create(&collection_.id);
238
239 uiLayoutSetContextPointer(&context_layout_, "id", &id_ptr);
240 uiLayoutSetContextPointer(&context_layout_, "collection", &collection_ptr);
241 }
242
243 add_label(row);
244
245 uiLayout *sub = uiLayoutRow(&row, true);
246 uiLayoutSetPropDecorate(sub, false);
247
248 build_state_button(*sub);
249 }
250
251 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
252 {
253 return std::make_unique<ItemDragController>(get_tree_view(), id_);
254 }
255
256 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
257 {
258 return std::make_unique<ReorderCollectionDropTarget>(*this, collection_, id_);
259 }
260
261 private:
262 int get_state_icon() const
263 {
264 switch (collection_light_linking_.link_state) {
266 return ICON_CHECKBOX_HLT;
268 return ICON_CHECKBOX_DEHLT;
269 }
271 return ICON_NONE;
272 }
273
274 static void link_state_toggle(CollectionLightLinking &collection_light_linking)
275 {
276 switch (collection_light_linking.link_state) {
278 collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_EXCLUDE;
279 return;
281 collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_INCLUDE;
282 return;
283 }
284
286 }
287
288 void build_state_button(uiLayout &row)
289 {
290 uiBlock *block = uiLayoutGetBlock(&row);
291 const int icon = get_state_icon();
292
293 PointerRNA collection_light_linking_ptr = RNA_pointer_create(
294 &collection_.id, &RNA_CollectionLightLinking, &collection_light_linking_);
295
296 uiBut *button = uiDefIconButR(block,
298 0,
299 icon,
300 0,
301 0,
302 UI_UNIT_X,
303 UI_UNIT_Y,
304 &collection_light_linking_ptr,
305 "link_state",
306 0,
307 0.0f,
308 0.0f,
309 nullptr);
310
311 UI_but_func_set(button, [&collection_light_linking = collection_light_linking_](bContext &) {
312 link_state_toggle(collection_light_linking);
313 });
314 }
315};
316
317class CollectionView : public AbstractTreeView {
318 uiLayout &context_layout_;
319 Collection &collection_;
320
321 public:
322 CollectionView(uiLayout &context_layout, Collection &collection)
323 : context_layout_(context_layout), collection_(collection)
324 {
325 }
326
327 void build_tree() override
328 {
329 LISTBASE_FOREACH (CollectionChild *, collection_child, &collection_.children) {
330 Collection *child_collection = collection_child->collection;
331 add_tree_item<CollectionViewItem>(context_layout_,
332 collection_,
333 child_collection->id,
334 collection_child->light_linking,
335 ICON_OUTLINER_COLLECTION);
336 }
337
338 LISTBASE_FOREACH (CollectionObject *, collection_object, &collection_.gobject) {
339 Object *child_object = collection_object->ob;
340 add_tree_item<CollectionViewItem>(context_layout_,
341 collection_,
342 child_object->id,
343 collection_object->light_linking,
344 ICON_OBJECT_DATA);
345 }
346 }
347
348 std::unique_ptr<DropTargetInterface> create_drop_target() override
349 {
350 return std::make_unique<InsertCollectionDropTarget>(collection_);
351 }
352};
353
354} // namespace
355
356} // namespace blender::ui::light_linking
357
359 uiLayout *context_layout,
361 const char *propname)
362{
363 if (!ptr->data) {
364 return;
365 }
366
367 PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
368 if (!prop) {
369 printf(
370 "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname);
371 return;
372 }
373
374 if (RNA_property_type(prop) != PROP_POINTER) {
375 printf("%s: expected pointer property for %s.%s\n",
376 __func__,
378 propname);
379 return;
380 }
381
382 const PointerRNA collection_ptr = RNA_property_pointer_get(ptr, prop);
383 if (!collection_ptr.data) {
384 return;
385 }
386 if (collection_ptr.type != &RNA_Collection) {
387 printf("%s: expected collection pointer property for %s.%s\n",
388 __func__,
390 propname);
391 return;
392 }
393
394 Collection *collection = static_cast<Collection *>(collection_ptr.data);
395
396 uiBlock *block = uiLayoutGetBlock(layout);
397
399 *block,
400 "Light Linking Collection Tree View",
401 std::make_unique<blender::ui::light_linking::CollectionView>(*context_layout, *collection));
402 tree_view->set_context_menu_title("Light Linking");
403 tree_view->set_default_rows(3);
404
406}
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
bool BKE_light_linking_unlink_id_from_collection(struct Main *bmain, struct Collection *collection, struct ID *id, struct ReportList *reports)
void BKE_light_linking_add_receiver_to_collection_after(struct Main *bmain, struct Collection *collection, struct ID *receiver, const struct ID *after, const eCollectionLightLinkingState link_state)
void BKE_light_linking_add_receiver_to_collection_before(struct Main *bmain, struct Collection *collection, struct ID *receiver, const struct ID *before, const eCollectionLightLinkingState link_state)
void BKE_light_linking_add_receiver_to_collection(struct Main *bmain, struct Collection *collection, struct ID *receiver, const eCollectionLightLinkingState link_state)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
#define TIP_(msgid)
ID_Type
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
eCollectionLightLinkingState
@ COLLECTION_LIGHT_LINKING_STATE_EXCLUDE
@ COLLECTION_LIGHT_LINKING_STATE_INCLUDE
Object is a sort of wrapper for general info.
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
@ PROP_POINTER
Definition RNA_types.hh:70
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
blender::ui::AbstractGridView * UI_block_add_view(uiBlock &block, blender::StringRef idname, std::unique_ptr< blender::ui::AbstractGridView > grid_view)
#define UI_UNIT_Y
uiBut * uiDefIconButR(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, PointerRNA *ptr, const char *propname, int index, float min, float max, const char *tip)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define UI_UNIT_X
@ UI_BTYPE_BUT
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
int BIFIconID
#define NC_SCENE
Definition WM_types.hh:345
#define ND_LAYER_CONTENT
Definition WM_types.hh:420
eWM_DragDataType
Definition WM_types.hh:1152
@ WM_DRAG_ID
Definition WM_types.hh:1153
Abstract base class for defining a customizable tree-view item.
void set_default_rows(int default_rows)
Definition tree_view.cc:129
void set_context_menu_title(const std::string &title)
static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, bool add_box=true)
Definition tree_view.cc:918
#define printf
void uiTemplateLightLinkingCollection(uiLayout *layout, uiLayout *context_layout, PointerRNA *ptr, const char *propname)
#define GS(x)
Definition iris.cc:202
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_struct_identifier(const StructRNA *type)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * first
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
const wmDrag & drag_data
const DropLocation drop_location
eWM_DragDataType type
Definition WM_types.hh:1282
ListBase ids
Definition WM_types.hh:1294
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126