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