Blender V5.0
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
8
9#include <cstdio>
10#include <memory>
11
12#include <fmt/format.h>
13
14#include "BLI_listbase.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"
29#include "UI_resources.hh"
30#include "UI_tree_view.hh"
31
32#include "WM_api.hh"
33
34#include "ED_undo.hh"
35
37
39
40namespace {
41
42class CollectionDropTarget {
43 Collection &collection_;
44
45 public:
46 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const
47 {
48 if (drag.type != WM_DRAG_ID) {
49 return false;
50 }
51
52 const wmDragID *drag_id = static_cast<wmDragID *>(drag.ids.first);
53 if (!drag_id) {
54 return false;
55 }
56
57 /* The dragged IDs are guaranteed to be the same type, so only check the type of the first one.
58 */
59 const ID_Type id_type = GS(drag_id->id->name);
60 if (!ELEM(id_type, ID_OB, ID_GR)) {
61 *r_disabled_hint = "Can only add objects and collections to the light linking collection";
62 return false;
63 }
64
65 return true;
66 }
67
68 CollectionDropTarget(Collection &collection) : collection_(collection) {}
69
70 Collection &get_collection() const
71 {
72 return collection_;
73 }
74};
75
79class InsertCollectionDropTarget : public DropTargetInterface {
80 CollectionDropTarget collection_target_;
81
82 public:
83 InsertCollectionDropTarget(Collection &collection) : collection_target_(collection) {}
84
85 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
86 {
87 return collection_target_.can_drop(drag, r_disabled_hint);
88 }
89
90 std::string drop_tooltip(const DragInfo & /*drag*/) const override
91 {
92 return TIP_("Add to linking collection");
93 }
94
95 bool on_drop(bContext *C, const DragInfo &drag) const override
96 {
97 Main *bmain = CTX_data_main(C);
98 Scene *scene = CTX_data_scene(C);
99
100 LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
102 &collection_target_.get_collection(),
103 drag_id->id,
105 }
106
107 /* It is possible that the light linking collection is also used by the view layer.
108 * For this case send a notifier so that the UI is updated for the changes in the collection
109 * content. */
111
112 ED_undo_push(C, "Add to linking collection");
113
114 return true;
115 }
116};
117
118class ReorderCollectionDropTarget : public TreeViewItemDropTarget {
119 CollectionDropTarget collection_target_;
120 const ID &drop_id_;
121
122 public:
123 ReorderCollectionDropTarget(AbstractTreeViewItem &item,
124 Collection &collection,
125 const ID &drop_id)
127 collection_target_(collection),
128 drop_id_(drop_id)
129 {
130 }
131
132 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
133 {
134 return collection_target_.can_drop(drag, r_disabled_hint);
135 }
136
137 std::string drop_tooltip(const DragInfo &drag) const override
138 {
139 const StringRef drop_name = drop_id_.name + 2;
140
141 switch (drag.drop_location) {
143 return "Add to linking collection";
145 return fmt::format(fmt::runtime(TIP_("Add to linking collection before {}")), drop_name);
147 return fmt::format(fmt::runtime(TIP_("Add to linking collection after {}")), drop_name);
148 }
149
150 return "";
151 }
152
153 bool on_drop(bContext *C, const DragInfo &drag) const override
154 {
155 Main *bmain = CTX_data_main(C);
156 Scene *scene = CTX_data_scene(C);
157
158 Collection &collection = collection_target_.get_collection();
160
161 LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
162 if (drag_id->id == &drop_id_) {
163 continue;
164 }
165
166 BKE_light_linking_unlink_id_from_collection(bmain, &collection, drag_id->id, nullptr);
167
168 switch (drag.drop_location) {
171 bmain, &collection, drag_id->id, link_state);
172 break;
175 bmain, &collection, drag_id->id, &drop_id_, link_state);
176 break;
179 bmain, &collection, drag_id->id, &drop_id_, link_state);
180 break;
181 }
182 }
183
184 /* It is possible that the light linking collection is also used by the view layer.
185 * For this case send a notifier so that the UI is updated for the changes in the collection
186 * content. */
188
189 ED_undo_push(C, "Add to linking collection");
190
191 return true;
192 }
193};
194
195class ItemDragController : public AbstractViewItemDragController {
196 ID &id_;
197
198 public:
199 explicit ItemDragController(AbstractView &view, ID &id)
201 {
202 }
203
204 std::optional<eWM_DragDataType> get_drag_type() const override
205 {
206 return WM_DRAG_ID;
207 }
208
209 void *create_drag_data() const override
210 {
211 return static_cast<void *>(&id_);
212 }
213};
214
215class CollectionViewItem : public BasicTreeViewItem {
216 uiLayout &context_layout_;
217 Collection &collection_;
218
219 ID &id_;
220 CollectionLightLinking &collection_light_linking_;
221
222 public:
223 CollectionViewItem(uiLayout &context_layout,
224 Collection &collection,
225 ID &id,
226 CollectionLightLinking &collection_light_linking,
227 const BIFIconID icon)
228 : BasicTreeViewItem(id.name + 2, icon),
229 context_layout_(context_layout),
230 collection_(collection),
231 id_(id),
232 collection_light_linking_(collection_light_linking)
233 {
234 }
235
236 void build_row(uiLayout &row) override
237 {
238 if (is_active()) {
239 PointerRNA id_ptr = RNA_id_pointer_create(&id_);
240 PointerRNA collection_ptr = RNA_id_pointer_create(&collection_.id);
241
242 context_layout_.context_ptr_set("id", &id_ptr);
243 context_layout_.context_ptr_set("collection", &collection_ptr);
244 }
245
246 add_label(row);
247
248 uiLayout *sub = &row.row(true);
249 sub->use_property_decorate_set(false);
250
251 build_state_button(*sub);
252 }
253
254 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
255 {
256 return std::make_unique<ItemDragController>(get_tree_view(), id_);
257 }
258
259 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
260 {
261 return std::make_unique<ReorderCollectionDropTarget>(*this, collection_, id_);
262 }
263
264 private:
265 int get_state_icon() const
266 {
267 switch (collection_light_linking_.link_state) {
269 return ICON_CHECKBOX_HLT;
271 return ICON_CHECKBOX_DEHLT;
272 }
274 return ICON_NONE;
275 }
276
277 static void link_state_toggle(CollectionLightLinking &collection_light_linking)
278 {
279 switch (collection_light_linking.link_state) {
281 collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_EXCLUDE;
282 return;
284 collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_INCLUDE;
285 return;
286 }
287
289 }
290
291 void build_state_button(uiLayout &row)
292 {
293 uiBlock *block = row.block();
294 const int icon = get_state_icon();
295
296 PointerRNA collection_light_linking_ptr = RNA_pointer_create_discrete(
297 &collection_.id, &RNA_CollectionLightLinking, &collection_light_linking_);
298
299 uiBut *button = uiDefIconButR(block,
301 0,
302 icon,
303 0,
304 0,
305 UI_UNIT_X,
306 UI_UNIT_Y,
307 &collection_light_linking_ptr,
308 "link_state",
309 0,
310 0.0f,
311 0.0f,
312 std::nullopt);
313
314 UI_but_func_set(button, [&collection_light_linking = collection_light_linking_](bContext &) {
315 link_state_toggle(collection_light_linking);
316 });
317 }
318};
319
320class CollectionView : public AbstractTreeView {
321 uiLayout &context_layout_;
322 Collection &collection_;
323
324 public:
325 CollectionView(uiLayout &context_layout, Collection &collection)
326 : context_layout_(context_layout), collection_(collection)
327 {
328 }
329
330 void build_tree() override
331 {
332 LISTBASE_FOREACH (CollectionChild *, collection_child, &collection_.children) {
333 Collection *child_collection = collection_child->collection;
334 add_tree_item<CollectionViewItem>(context_layout_,
335 collection_,
336 child_collection->id,
337 collection_child->light_linking,
338 ICON_OUTLINER_COLLECTION);
339 }
340
341 LISTBASE_FOREACH (CollectionObject *, collection_object, &collection_.gobject) {
342 Object *child_object = collection_object->ob;
343 add_tree_item<CollectionViewItem>(context_layout_,
344 collection_,
345 child_object->id,
346 collection_object->light_linking,
347 ICON_OBJECT_DATA);
348 }
349 }
350
351 std::unique_ptr<DropTargetInterface> create_drop_target() override
352 {
353 return std::make_unique<InsertCollectionDropTarget>(collection_);
354 }
355};
356
357} // namespace
358
359} // namespace blender::ui::light_linking
360
362 bContext *C,
363 uiLayout *context_layout,
365 const StringRefNull propname)
366{
367 if (!ptr->data) {
368 return;
369 }
370
371 PropertyRNA *prop = RNA_struct_find_property(ptr, propname.c_str());
372 if (!prop) {
373 printf("%s: property not found: %s.%s\n",
374 __func__,
376 propname.c_str());
377 return;
378 }
379
380 if (RNA_property_type(prop) != PROP_POINTER) {
381 printf("%s: expected pointer property for %s.%s\n",
382 __func__,
384 propname.c_str());
385 return;
386 }
387
388 const PointerRNA collection_ptr = RNA_property_pointer_get(ptr, prop);
389 if (!collection_ptr.data) {
390 return;
391 }
392 if (collection_ptr.type != &RNA_Collection) {
393 printf("%s: expected collection pointer property for %s.%s\n",
394 __func__,
396 propname.c_str());
397 return;
398 }
399
400 Collection *collection = static_cast<Collection *>(collection_ptr.data);
401
402 uiBlock *block = layout->block();
403
405 *block,
406 "Light Linking Collection Tree View",
407 std::make_unique<blender::ui::light_linking::CollectionView>(*context_layout, *collection));
408 tree_view->set_context_menu_title("Light Linking");
409 tree_view->set_default_rows(5);
410
412}
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:93
#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.
int BIFIconID
Definition ED_asset.hh:28
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
static AppView * view
@ PROP_POINTER
Definition RNA_types.hh:167
#define C
Definition RandGen.cpp:29
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, ButType type, int retval, int icon, int x, int y, short width, short height, PointerRNA *ptr, blender::StringRefNull propname, int index, float min, float max, std::optional< blender::StringRef > tip)
#define UI_UNIT_X
@ WM_DRAG_ID
Definition WM_types.hh:1201
#define NC_SCENE
Definition WM_types.hh:378
#define ND_LAYER_CONTENT
Definition WM_types.hh:453
constexpr const char * c_str() const
Abstract base class for defining a customizable tree-view item.
void set_default_rows(int default_rows)
Definition tree_view.cc:141
void set_context_menu_title(const std::string &title)
static void build_tree_view(const bContext &C, AbstractTreeView &tree_view, uiLayout &layout, bool add_box=true)
#define GS(x)
#define printf(...)
void uiTemplateLightLinkingCollection(uiLayout *layout, bContext *C, uiLayout *context_layout, PointerRNA *ptr, const StringRefNull propname)
const char * name
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_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
const wmDrag & drag_data
const DropLocation drop_location
void use_property_decorate_set(bool is_sep)
uiBlock * block() const
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiLayout & row(bool align)
eWM_DragDataType type
Definition WM_types.hh:1331
ListBase ids
Definition WM_types.hh:1345
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238