Blender V4.3
outliner_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10#include <cstring>
11
12#include "BLI_listbase.h"
13#include "BLI_utildefines.h"
14
15#include "DNA_action_types.h"
16#include "DNA_screen_types.h"
17#include "DNA_space_types.h"
18
19#include "RNA_access.hh"
20#include "RNA_prototypes.hh"
21
22#include "BKE_context.hh"
23#include "BKE_layer.hh"
24#include "BKE_object.hh"
25
26#include "ED_outliner.hh"
27#include "ED_screen.hh"
28
29#include "UI_interface.hh"
30#include "UI_view2d.hh"
31
32#include "outliner_intern.hh"
33#include "tree/tree_display.hh"
34
35namespace blender::ed::outliner {
36
37/* -------------------------------------------------------------------- */
42{
43 memset(tvc, 0, sizeof(*tvc));
44
45 /* Scene level. */
46 tvc->scene = CTX_data_scene(C);
48
49 /* Objects. */
52 if (tvc->obact != nullptr) {
53 tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact);
54
55 if ((tvc->obact->type == OB_ARMATURE) ||
56 /* This could be made into its own function. */
57 ((tvc->obact->type == OB_MESH) && tvc->obact->mode & OB_MODE_WEIGHT_PAINT))
58 {
60 }
61 }
62}
63
67 const ListBase *tree,
68 float view_co_y)
69{
70 LISTBASE_FOREACH (TreeElement *, te_iter, tree) {
71 if (view_co_y < (te_iter->ys + UI_UNIT_Y)) {
72 if (view_co_y >= te_iter->ys) {
73 /* co_y is inside this element */
74 return te_iter;
75 }
76
77 if (BLI_listbase_is_empty(&te_iter->subtree) ||
78 !TSELEM_OPEN(TREESTORE(te_iter), space_outliner))
79 {
80 /* No need for recursion. */
81 continue;
82 }
83
84 /* If the coordinate is lower than the next element, we can continue with that one and skip
85 * recursion too. */
86 const TreeElement *te_next = te_iter->next;
87 if (te_next && (view_co_y < (te_next->ys + UI_UNIT_Y))) {
88 continue;
89 }
90
91 /* co_y is lower than current element (but not lower than the next one), possibly inside
92 * children */
93 TreeElement *te_sub = outliner_find_item_at_y(space_outliner, &te_iter->subtree, view_co_y);
94 if (te_sub) {
95 return te_sub;
96 }
97 }
98 }
99
100 return nullptr;
101}
102
104 float view_co_x,
105 bool *r_is_merged_icon)
106{
107 TreeElement *child_te = static_cast<TreeElement *>(parent_te->subtree.first);
108
109 while (child_te) {
110 const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend);
111 if ((child_te->flag & TE_ICONROW) && over_element) {
112 return child_te;
113 }
114 if ((child_te->flag & TE_ICONROW_MERGED) && over_element) {
115 if (r_is_merged_icon) {
116 *r_is_merged_icon = true;
117 }
118 return child_te;
119 }
120
122 child_te, view_co_x, r_is_merged_icon);
123 if (te != child_te) {
124 return te;
125 }
126
127 child_te = child_te->next;
128 }
129
130 /* return parent if no child is hovered */
131 return (TreeElement *)parent_te;
132}
133
135 TreeElement *parent_te,
136 float view_co_x,
137 bool *r_is_merged_icon,
138 bool *r_is_over_icon)
139{
140 TreeStoreElem *parent_tselem = TREESTORE(parent_te);
141 TreeElement *te = parent_te;
142
143 /* If parent_te is opened, or it is a ViewLayer, it doesn't show children in row. */
144 if (!TSELEM_OPEN(parent_tselem, space_outliner) && parent_tselem->type != TSE_R_LAYER) {
145 te = outliner_find_item_at_x_in_row_recursive(parent_te, view_co_x, r_is_merged_icon);
146 }
147
148 if ((te != parent_te) || outliner_item_is_co_over_icon(parent_te, view_co_x)) {
149 *r_is_over_icon = true;
150 }
151
152 return te;
153}
154
156{
157 LISTBASE_FOREACH (TreeElement *, te, lb) {
158 if (te->store_elem == store_elem) {
159 return te;
160 }
161 TreeElement *tes = outliner_find_tree_element(&te->subtree, store_elem);
162 if (tes) {
163 return tes;
164 }
165 }
166 return nullptr;
167}
168
170 TreeElement *parent_te,
171 const TreeElement *child_te)
172{
173 LISTBASE_FOREACH (TreeElement *, te, lb) {
174 if (te == child_te) {
175 return parent_te;
176 }
177
178 TreeElement *find_te = outliner_find_parent_element(&te->subtree, te, child_te);
179 if (find_te) {
180 return find_te;
181 }
182 }
183 return nullptr;
184}
185
186TreeElement *outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
187{
188 LISTBASE_FOREACH (TreeElement *, te, lb) {
189 TreeStoreElem *tselem = TREESTORE(te);
190 if (tselem->type == TSE_SOME_ID) {
191 if (tselem->id == id) {
192 return te;
193 }
194 }
195
196 TreeElement *tes = outliner_find_id(space_outliner, &te->subtree, id);
197 if (tes) {
198 return tes;
199 }
200 }
201 return nullptr;
202}
203
205{
206 LISTBASE_FOREACH (TreeElement *, te, lb) {
207 if (te->directdata == pchan) {
208 return te;
209 }
210
211 TreeStoreElem *tselem = TREESTORE(te);
212 if (ELEM(tselem->type, TSE_POSE_BASE, TSE_POSE_CHANNEL)) {
213 TreeElement *tes = outliner_find_posechannel(&te->subtree, pchan);
214 if (tes) {
215 return tes;
216 }
217 }
218 }
219 return nullptr;
220}
221
223{
224 LISTBASE_FOREACH (TreeElement *, te, lb) {
225 if (te->directdata == ebone) {
226 return te;
227 }
228
229 TreeStoreElem *tselem = TREESTORE(te);
230 if (ELEM(tselem->type, TSE_SOME_ID, TSE_EBONE)) {
231 TreeElement *tes = outliner_find_editbone(&te->subtree, ebone);
232 if (tes) {
233 return tes;
234 }
235 }
236 }
237 return nullptr;
238}
239
241{
242 TreeStoreElem *tselem;
243 te = te->parent;
244
245 while (te) {
246 tselem = TREESTORE(te);
247 if ((tselem->type == TSE_SOME_ID) && (te->idcode == idcode)) {
248 return te;
249 }
250 te = te->parent;
251 }
252 return nullptr;
253}
254
256{
257 TreeElement *search_te;
258 TreeStoreElem *tselem;
259
260 search_te = outliner_search_back_te(te, idcode);
261 if (search_te) {
262 tselem = TREESTORE(search_te);
263 return tselem->id;
264 }
265 return nullptr;
266}
267
268bool outliner_tree_traverse(const SpaceOutliner *space_outliner,
269 ListBase *tree,
270 int filter_te_flag,
271 int filter_tselem_flag,
273 void *customdata)
274{
275 for (TreeElement *te = static_cast<TreeElement *>(tree->first), *te_next; te; te = te_next) {
277 /* in case te is freed in callback */
278 TreeStoreElem *tselem = TREESTORE(te);
279 ListBase subtree = te->subtree;
280 te_next = te->next;
281
282 if (filter_te_flag && (te->flag & filter_te_flag) == 0) {
283 /* skip */
284 }
285 else if (filter_tselem_flag && (tselem->flag & filter_tselem_flag) == 0) {
286 /* skip */
287 }
288 else {
289 func_retval = func(te, customdata);
290 }
291 /* Don't access te or tselem from now on! Might've been freed... */
292
293 if (func_retval == TRAVERSE_BREAK) {
294 return false;
295 }
296
297 if (func_retval == TRAVERSE_SKIP_CHILDS) {
298 /* skip */
299 }
300 else if (!outliner_tree_traverse(
301 space_outliner, &subtree, filter_te_flag, filter_tselem_flag, func, customdata))
302 {
303 return false;
304 }
305 }
306
307 return true;
308}
309
310float outliner_right_columns_width(const SpaceOutliner *space_outliner)
311{
312 int num_columns = 0;
313
314 switch (space_outliner->outlinevis) {
315 case SO_DATA_API:
316 case SO_SEQUENCE:
317 case SO_LIBRARIES:
318 return 0.0f;
322 num_columns = OL_RNA_COL_SIZEX / UI_UNIT_X;
323 break;
325 num_columns = 1;
326 break;
327 }
328 break;
329 case SO_ID_ORPHANS:
330 num_columns = 3;
331 break;
332 case SO_VIEW_LAYER:
333 if (space_outliner->show_restrict_flags & SO_RESTRICT_ENABLE) {
334 num_columns++;
335 }
336 if (space_outliner->show_restrict_flags & SO_RESTRICT_HOLDOUT) {
337 num_columns++;
338 }
339 if (space_outliner->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
340 num_columns++;
341 }
343 case SO_SCENES:
344 if (space_outliner->show_restrict_flags & SO_RESTRICT_SELECT) {
345 num_columns++;
346 }
347 if (space_outliner->show_restrict_flags & SO_RESTRICT_HIDE) {
348 num_columns++;
349 }
350 if (space_outliner->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
351 num_columns++;
352 }
353 if (space_outliner->show_restrict_flags & SO_RESTRICT_RENDER) {
354 num_columns++;
355 }
356 break;
357 }
358 return (num_columns * UI_UNIT_X + V2D_SCROLL_WIDTH);
359}
360
362{
363 LISTBASE_FOREACH (TreeElement *, te, lb) {
364 if ((TREESTORE(te)->flag & flag) == flag) {
365 return te;
366 }
367 TreeElement *active_element = outliner_find_element_with_flag(&te->subtree, flag);
368 if (active_element) {
369 return active_element;
370 }
371 }
372 return nullptr;
373}
374
376{
377 TreeStoreElem *tselem;
378
379 while (te->parent) {
380 tselem = TREESTORE(te->parent);
381
382 if (tselem->flag & TSE_CLOSED) {
383 return false;
384 }
385 te = te->parent;
386 }
387
388 return true;
389}
390
392{
393 return ((te->ys + UI_UNIT_Y) >= v2d->cur.ymin) && (te->ys <= v2d->cur.ymax);
394}
395
396bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x)
397{
398 /* Special case: count area left of Scene Collection as empty space */
399 bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ?
400 (view_co_x > te->xs + UI_UNIT_X) :
401 (view_co_x > te->xs);
402
403 return outside_left && (view_co_x < te->xend);
404}
405
406bool outliner_item_is_co_over_icon(const TreeElement *te, float view_co_x)
407{
408 return (view_co_x > (te->xs + UI_UNIT_X)) && (view_co_x < (te->xs + UI_UNIT_X * 2));
409}
410
411bool outliner_item_is_co_over_name(const TreeElement *te, float view_co_x)
412{
413 return (view_co_x > (te->xs + UI_UNIT_X * 2)) && (view_co_x < te->xend);
414}
415
417{
418 return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X);
419}
420
421void outliner_scroll_view(SpaceOutliner *space_outliner, ARegion *region, int delta_y)
422{
423 int tree_width, tree_height;
424 outliner_tree_dimensions(space_outliner, &tree_width, &tree_height);
425 int y_min = std::min(int(region->v2d.cur.ymin), -tree_height);
426
427 region->v2d.cur.ymax += delta_y;
428 region->v2d.cur.ymin += delta_y;
429
430 /* Adjust view if delta placed view outside total area */
431 int offset;
432 if (region->v2d.cur.ymax > -UI_UNIT_Y) {
433 offset = region->v2d.cur.ymax;
434 region->v2d.cur.ymax -= offset;
435 region->v2d.cur.ymin -= offset;
436 }
437 else if (region->v2d.cur.ymin < y_min) {
438 offset = y_min - region->v2d.cur.ymin;
439 region->v2d.cur.ymax += offset;
440 region->v2d.cur.ymin += offset;
441 }
442}
443
445 ARegion *region)
446{
447 /* Avoid rebuild if possible. */
448 if (space_outliner->runtime->tree_display->is_lazy_built()) {
449 ED_region_tag_redraw(region);
450 }
451 else {
453 }
454}
455
456} // namespace blender::ed::outliner
457
458using namespace blender::ed::outliner;
459
461{
462 ARegion *region = CTX_wm_region(C);
463 const Scene *scene = CTX_data_scene(C);
464 ViewLayer *view_layer = CTX_data_view_layer(C);
465 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
466 TreeElement *te;
467 Base *base = nullptr;
468 float view_mval[2];
469
470 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
471
472 te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
473 if (te) {
474 TreeStoreElem *tselem = TREESTORE(te);
475 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
476 Object *ob = (Object *)tselem->id;
477 BKE_view_layer_synced_ensure(scene, view_layer);
478 base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob);
479 }
480 }
481
482 return base;
483}
484
485bool ED_outliner_give_rna_under_cursor(bContext *C, const int mval[2], PointerRNA *r_ptr)
486{
487 ARegion *region = CTX_wm_region(C);
488 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
489
490 float view_mval[2];
491 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
492
493 TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
494 if (!te) {
495 return false;
496 }
497
498 bool success = true;
499 TreeStoreElem *tselem = TREESTORE(te);
500 switch (tselem->type) {
501 case TSE_BONE: {
502 Bone *bone = (Bone *)te->directdata;
503 *r_ptr = RNA_pointer_create(tselem->id, &RNA_Bone, bone);
504 break;
505 }
506 case TSE_POSE_CHANNEL: {
507 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
508 *r_ptr = RNA_pointer_create(tselem->id, &RNA_PoseBone, pchan);
509 break;
510 }
511 case TSE_EBONE: {
512 EditBone *bone = (EditBone *)te->directdata;
513 *r_ptr = RNA_pointer_create(tselem->id, &RNA_EditBone, bone);
514 break;
515 }
516
517 default:
518 success = false;
519 break;
520 }
521 return success;
522}
SpaceOutliner * CTX_wm_space_outliner(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
General operations, lookup, etc. for blender objects.
Object * BKE_object_pose_armature_get(Object *ob)
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
@ ID_OB
@ OB_MODE_WEIGHT_PAINT
@ OB_ARMATURE
@ OB_MESH
@ TSE_POSE_CHANNEL
@ TSE_VIEW_COLLECTION_BASE
@ TSE_EBONE
@ TSE_BONE
@ TSE_SOME_ID
@ TSE_R_LAYER
@ TSE_POSE_BASE
@ TSE_CLOSED
#define OBEDIT_FROM_OBACT(ob)
eSpaceOutliner_LibOverrideViewMode
@ SO_LIB_OVERRIDE_VIEW_HIERARCHIES
@ SO_LIB_OVERRIDE_VIEW_PROPERTIES
@ SO_RESTRICT_HIDE
@ SO_RESTRICT_RENDER
@ SO_RESTRICT_INDIRECT_ONLY
@ SO_RESTRICT_VIEWPORT
@ SO_RESTRICT_ENABLE
@ SO_RESTRICT_HOLDOUT
@ SO_RESTRICT_SELECT
@ SO_OVERRIDES_LIBRARY
@ SO_SEQUENCE
@ SO_DATA_API
@ SO_LIBRARIES
@ SO_VIEW_LAYER
@ SO_SCENES
@ SO_ID_ORPHANS
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
#define UI_UNIT_Y
#define UI_UNIT_X
#define V2D_SCROLL_WIDTH
Definition UI_view2d.hh:54
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
KDTree_3d * tree
bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x)
TreeTraversalAction(*)(TreeElement *te, void *customdata) TreeTraversalFunc
bool outliner_item_is_co_over_icon(const TreeElement *te, float view_co_x)
void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner, ARegion *region)
TreeElement * outliner_find_posechannel(ListBase *lb, const bPoseChannel *pchan)
TreeElement * outliner_find_item_at_x_in_row(const SpaceOutliner *space_outliner, TreeElement *parent_te, float view_co_x, bool *r_is_merged_icon, bool *r_is_over_icon)
TreeElement * outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
static TreeElement * outliner_find_item_at_x_in_row_recursive(const TreeElement *parent_te, float view_co_x, bool *r_is_merged_icon)
float outliner_right_columns_width(const SpaceOutliner *space_outliner)
TreeElement * outliner_search_back_te(TreeElement *te, short idcode)
TreeElement * outliner_find_item_at_y(const SpaceOutliner *space_outliner, const ListBase *tree, float view_co_y)
void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc)
bool outliner_is_element_in_view(const TreeElement *te, const View2D *v2d)
TreeElement * outliner_find_editbone(ListBase *lb, const EditBone *ebone)
void outliner_scroll_view(SpaceOutliner *space_outliner, ARegion *region, int delta_y)
bool outliner_is_element_visible(const TreeElement *te)
bool outliner_tree_traverse(const SpaceOutliner *space_outliner, ListBase *tree, int filter_te_flag, int filter_tselem_flag, TreeTraversalFunc func, void *customdata)
ID * outliner_search_back(TreeElement *te, short idcode)
TreeElement * outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem)
TreeElement * outliner_find_parent_element(ListBase *lb, TreeElement *parent_te, const TreeElement *child_te)
bool outliner_item_is_co_over_name(const TreeElement *te, float view_co_x)
bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x)
TreeElement * outliner_find_element_with_flag(const ListBase *lb, short flag)
void outliner_tree_dimensions(SpaceOutliner *space_outliner, int *r_width, int *r_height)
#define TREESTORE(a)
#define OL_RNA_COL_SIZEX
#define TSELEM_OPEN(telm, sv)
Base * ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
bool ED_outliner_give_rna_under_cursor(bContext *C, const int mval[2], PointerRNA *r_ptr)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:413
void * first
short lib_override_view_mode
SpaceOutliner_Runtime * runtime
std::unique_ptr< AbstractTreeDisplay > tree_display
float ymax
float ymin
Establish and manage Outliner trees for different display modes.
uint8_t flag
Definition wm_window.cc:138