2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * The Original Code is Copyright (C) 2004 Blender Foundation.
17 * All rights reserved.
20 /** \file \ingroup spoutliner
23 #include "DNA_anim_types.h"
24 #include "DNA_armature_types.h"
25 #include "DNA_collection_types.h"
26 #include "DNA_gpencil_types.h"
27 #include "DNA_gpencil_modifier_types.h"
28 #include "DNA_lamp_types.h"
29 #include "DNA_lightprobe_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_scene_types.h"
32 #include "DNA_sequence_types.h"
35 #include "BLI_blenlib.h"
36 #include "BLI_string_utils.h"
37 #include "BLI_utildefines.h"
38 #include "BLI_mempool.h"
40 #include "BLT_translation.h"
42 #include "BKE_context.h"
43 #include "BKE_deform.h"
44 #include "BKE_fcurve.h"
45 #include "BKE_gpencil.h"
46 #include "BKE_idcode.h"
47 #include "BKE_layer.h"
48 #include "BKE_library.h"
50 #include "BKE_modifier.h"
51 #include "BKE_object.h"
52 #include "BKE_report.h"
53 #include "BKE_scene.h"
55 #include "DEG_depsgraph.h"
56 #include "DEG_depsgraph_build.h"
58 #include "ED_armature.h"
59 #include "ED_keyframing.h"
60 #include "ED_object.h"
61 #include "ED_screen.h"
66 #include "GPU_immediate.h"
67 #include "GPU_state.h"
69 #include "UI_interface.h"
70 #include "UI_interface_icons.h"
71 #include "UI_resources.h"
72 #include "UI_view2d.h"
74 #include "RNA_access.h"
76 #include "outliner_intern.h"
78 /* disable - this is far too slow - campbell */
79 // #define USE_GROUP_SELECT
81 /* ****************************************************** */
82 /* Tree Size Functions */
84 static void outliner_height(SpaceOops *soops, ListBase *lb, int *h)
86 TreeElement *te = lb->first;
88 TreeStoreElem *tselem = TREESTORE(te);
89 if (TSELEM_OPEN(tselem, soops)) {
90 outliner_height(soops, &te->subtree, h);
97 #if 0 // XXX this is currently disabled until te->xend is set correctly
98 static void outliner_width(SpaceOops *soops, ListBase *lb, int *w)
100 TreeElement *te = lb->first;
102 // TreeStoreElem *tselem = TREESTORE(te);
104 // XXX fixme... te->xend is not set yet
105 if (!TSELEM_OPEN(tselem, soops)) {
109 outliner_width(soops, &te->subtree, w);
115 static void outliner_rna_width(SpaceOops *soops, ListBase *lb, int *w, int startx)
117 TreeElement *te = lb->first;
119 TreeStoreElem *tselem = TREESTORE(te);
120 // XXX fixme... (currently, we're using a fixed length of 100)!
127 if (startx + 100 > *w)
130 if (TSELEM_OPEN(tselem, soops)) {
131 outliner_rna_width(soops, &te->subtree, w, startx + UI_UNIT_X);
138 * The active object is only needed for reference.
140 static bool is_object_data_in_editmode(const ID *id, const Object *obact)
142 const short id_type = GS(id->name);
144 (obact && (obact->mode & OB_MODE_EDIT)) &&
145 (id && OB_DATA_SUPPORT_EDITMODE(id_type)) &&
146 (GS(((ID *)obact->data)->name) == id_type) &&
147 BKE_object_data_is_in_editmode(id)
151 /* ****************************************************** */
153 static void restrictbutton_recursive_ebone(bContext *C, EditBone *ebone_parent, int flag, bool set_flag)
155 Object *obedit = CTX_data_edit_object(C);
156 bArmature *arm = obedit->data;
159 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
160 if (ED_armature_ebone_is_child_recursive(ebone_parent, ebone)) {
162 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
166 ebone->flag &= ~flag;
172 static void restrictbutton_recursive_bone(Bone *bone_parent, int flag, bool set_flag)
175 for (bone = bone_parent->childbase.first; bone; bone = bone->next) {
177 bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
183 restrictbutton_recursive_bone(bone, flag, set_flag);
188 static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2))
190 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin);
193 static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
195 Bone *bone = (Bone *)poin2;
196 if (bone->flag & BONE_HIDDEN_P)
197 bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
199 if (CTX_wm_window(C)->eventstate->ctrl) {
200 restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0);
203 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
206 static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
208 Bone *bone = (Bone *)poin2;
209 if (bone->flag & BONE_UNSELECTABLE)
210 bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
212 if (CTX_wm_window(C)->eventstate->ctrl) {
213 restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0);
216 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
219 static void restrictbutton_ebone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
221 EditBone *ebone = (EditBone *)poin2;
223 if (ebone->flag & BONE_UNSELECTABLE) {
224 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
227 if (CTX_wm_window(C)->eventstate->ctrl) {
228 restrictbutton_recursive_ebone(C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0);
231 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
234 static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
236 EditBone *ebone = (EditBone *)poin2;
237 if (ebone->flag & BONE_HIDDEN_A) {
238 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
241 if (CTX_wm_window(C)->eventstate->ctrl) {
242 restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0);
245 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
248 static void restrictbutton_gp_layer_flag_cb(bContext *C, void *UNUSED(poin), void *UNUSED(poin2))
250 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
253 static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2))
257 BLI_assert(id != NULL);
259 if (id->flag & LIB_FAKEUSER) {
267 static void hidebutton_base_flag_cb(bContext *C, void *poin, void *poin2)
269 Main *bmain = CTX_data_main(C);
270 Scene *scene = CTX_data_scene(C);
271 ViewLayer *view_layer = poin;
273 Object *ob = base->object;
274 bool freeze = (CTX_wm_window(C)->eventstate->alt != 0);
275 bool changed_restrict_view = false;
278 ob->restrictflag |= OB_RESTRICT_VIEW;
279 changed_restrict_view = true;
281 else if (ob->restrictflag & OB_RESTRICT_VIEW) {
282 ob->restrictflag &= ~OB_RESTRICT_VIEW;
283 base->flag &= ~BASE_HIDDEN;
284 changed_restrict_view = true;
287 base->flag ^= BASE_HIDDEN;
290 if (changed_restrict_view) {
291 BKE_main_collection_sync_remap(bmain);
292 DEG_id_tag_update(&ob->id, LIB_TAG_COPIED_ON_WRITE);
293 DEG_relations_tag_update(bmain);
294 WM_main_add_notifier(NC_OBJECT | ND_DRAW, &ob->id);
297 BKE_layer_collection_sync(scene, view_layer);
298 DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
299 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
303 static void hidebutton_layer_collection_flag_cb(bContext *C, void *poin, void *poin2)
305 wmWindow *win = CTX_wm_window(C);
306 Scene *scene = CTX_data_scene(C);
307 ViewLayer *view_layer = poin;
308 LayerCollection *lc = poin2;
309 Collection *collection = lc->collection;
310 bool do_disable = (win->eventstate->alt != 0);
311 bool do_isolate = (win->eventstate->ctrl != 0) && !do_disable;
312 bool extend = (win->eventstate->shift != 0);
313 bool depsgraph_changed = false;
316 collection->flag |= COLLECTION_RESTRICT_VIEW;
317 depsgraph_changed = true;
319 else if (do_isolate) {
320 depsgraph_changed |= BKE_layer_collection_isolate(scene, view_layer, lc, extend);
323 bool make_visible = ((lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0) ||
324 ((collection->flag & COLLECTION_RESTRICT_VIEW) != 0);
325 depsgraph_changed |= BKE_layer_collection_set_visible(view_layer, lc, make_visible, extend);
328 BKE_layer_collection_sync(scene, view_layer);
329 DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
331 if (depsgraph_changed) {
332 DEG_relations_tag_update(CTX_data_main(C));
334 WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
337 static void namebutton_cb(bContext *C, void *tsep, char *oldname)
339 Main *bmain = CTX_data_main(C);
340 SpaceOops *soops = CTX_wm_space_outliner(C);
341 Object *obedit = CTX_data_edit_object(C);
342 BLI_mempool *ts = soops->treestore;
343 TreeStoreElem *tselem = tsep;
346 TreeElement *te = outliner_find_tree_element(&soops->tree, tselem);
348 if (tselem->type == 0) {
349 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
351 switch (GS(tselem->id->name)) {
353 WM_event_add_notifier(C, NC_MATERIAL, NULL); break;
355 WM_event_add_notifier(C, NC_TEXTURE, NULL); break;
357 WM_event_add_notifier(C, NC_IMAGE, NULL); break;
359 WM_event_add_notifier(C, NC_SCENE, NULL); break;
362 Object *ob = (Object *)tselem->id;
363 if (ob->type == OB_MBALL) {
364 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
366 DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
367 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break;
370 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break;
372 /* Check the library target exists */
373 if (te->idcode == ID_LI) {
374 Library *lib = (Library *)tselem->id;
375 char expanded[FILE_MAX];
377 BKE_library_filepath_set(bmain, lib, lib->name);
379 BLI_strncpy(expanded, lib->name, sizeof(expanded));
380 BLI_path_abs(expanded, BKE_main_blendfile_path(bmain));
381 if (!BLI_exists(expanded)) {
382 BKE_reportf(CTX_wm_reports(C), RPT_ERROR,
383 "Library path '%s' does not exist, correct this before saving", expanded);
385 else if (lib->id.tag & LIB_TAG_MISSING) {
386 BKE_reportf(CTX_wm_reports(C), RPT_INFO,
387 "Library path '%s' is now valid, please reload the library", expanded);
388 lib->id.tag &= ~LIB_TAG_MISSING;
393 switch (tselem->type) {
395 defgroup_unique_name(te->directdata, (Object *)tselem->id); // id = object
398 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
402 bArmature *arm = (bArmature *)tselem->id;
404 EditBone *ebone = te->directdata;
405 char newname[sizeof(ebone->name)];
407 /* restore bone name */
408 BLI_strncpy(newname, ebone->name, sizeof(ebone->name));
409 BLI_strncpy(ebone->name, oldname, sizeof(ebone->name));
410 ED_armature_bone_rename(bmain, obedit->data, oldname, newname);
411 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
418 ViewLayer *view_layer = CTX_data_view_layer(C);
419 Scene *scene = CTX_data_scene(C);
420 bArmature *arm = (bArmature *)tselem->id;
421 Bone *bone = te->directdata;
422 char newname[sizeof(bone->name)];
424 /* always make current object active */
425 tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
427 /* restore bone name */
428 BLI_strncpy(newname, bone->name, sizeof(bone->name));
429 BLI_strncpy(bone->name, oldname, sizeof(bone->name));
430 ED_armature_bone_rename(bmain, arm, oldname, newname);
431 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
434 case TSE_POSE_CHANNEL:
436 Scene *scene = CTX_data_scene(C);
437 ViewLayer *view_layer = CTX_data_view_layer(C);
438 Object *ob = (Object *)tselem->id;
439 bPoseChannel *pchan = te->directdata;
440 char newname[sizeof(pchan->name)];
442 /* always make current pose-bone active */
443 tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
445 BLI_assert(ob->type == OB_ARMATURE);
447 /* restore bone name */
448 BLI_strncpy(newname, pchan->name, sizeof(pchan->name));
449 BLI_strncpy(pchan->name, oldname, sizeof(pchan->name));
450 ED_armature_bone_rename(bmain, ob->data, oldname, newname);
451 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
456 Object *ob = (Object *)tselem->id; // id = object
457 bActionGroup *grp = te->directdata;
459 BLI_uniquename(&ob->pose->agroups, grp, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), '.',
460 offsetof(bActionGroup, name), sizeof(grp->name));
461 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
466 bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */
467 bGPDlayer *gpl = te->directdata;
469 /* always make layer active */
470 BKE_gpencil_layer_setactive(gpd, gpl);
472 // XXX: name needs translation stuff
473 BLI_uniquename(&gpd->layers, gpl, "GP Layer", '.',
474 offsetof(bGPDlayer, info), sizeof(gpl->info));
476 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, gpd);
481 Scene *scene = (Scene *)tselem->id;
482 ViewLayer *view_layer = te->directdata;
484 /* Restore old name. */
485 char newname[sizeof(view_layer->name)];
486 BLI_strncpy(newname, view_layer->name, sizeof(view_layer->name));
487 BLI_strncpy(view_layer->name, oldname, sizeof(view_layer->name));
489 /* Rename, preserving animation and compositing data. */
490 BKE_view_layer_rename(bmain, scene, view_layer, newname);
491 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
494 case TSE_LAYER_COLLECTION:
496 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
497 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
502 tselem->flag &= ~TSE_TEXTBUT;
506 static void outliner_draw_restrictbuts(
507 uiBlock *block, Scene *scene, ViewLayer *view_layer, ARegion *ar, SpaceOops *soops, ListBase *lb)
509 /* Get RNA properties (once for speed). */
510 static struct RestrictProperties {
513 PropertyRNA *object_hide_viewport, *object_hide_select, *object_hide_render;
514 PropertyRNA *collection_hide_viewport, *collection_hide_select, *collection_hide_render;
515 PropertyRNA *modifier_show_viewport, *modifier_show_render;
518 if (!props.initialized) {
519 props.object_hide_viewport = RNA_struct_type_find_property(&RNA_Object, "hide_viewport");
520 props.object_hide_select = RNA_struct_type_find_property(&RNA_Object, "hide_select");
521 props.object_hide_render = RNA_struct_type_find_property(&RNA_Object, "hide_render");
522 props.collection_hide_select = RNA_struct_type_find_property(&RNA_Collection, "hide_select");
523 props.collection_hide_viewport = RNA_struct_type_find_property(&RNA_Collection, "hide_viewport");
524 props.collection_hide_render = RNA_struct_type_find_property(&RNA_Collection, "hide_render");
525 props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport");
526 props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render");
528 props.initialized = true;
531 /* Create buttons. */
534 for (TreeElement *te = lb->first; te; te = te->next) {
535 TreeStoreElem *tselem = TREESTORE(te);
536 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
537 if (tselem->type == TSE_R_LAYER && (soops->outlinevis == SO_SCENES)) {
538 /* View layer render toggle. */
539 ViewLayer *layer = te->directdata;
541 bt = uiDefIconButBitS(
542 block, UI_BTYPE_ICON_TOGGLE_N, VIEW_LAYER_RENDER, 0, ICON_RESTRICT_RENDER_OFF,
543 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X,
544 UI_UNIT_Y, &layer->flag, 0, 0, 0, 0, TIP_("Use view layer for rendering"));
545 UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL);
546 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
547 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
549 else if (tselem->type == 0 && te->idcode == ID_OB) {
551 Object *ob = (Object *)tselem->id;
552 RNA_pointer_create(&ob->id, &RNA_Object, ob, &ptr);
553 Base *base = BKE_view_layer_base_find(view_layer, ob);
556 int icon = ICON_RESTRICT_VIEW_ON;
557 if ((ob->restrictflag & OB_RESTRICT_VIEW) == 0) {
558 icon = (base->flag & BASE_HIDDEN) != 0 ?
563 block, UI_BTYPE_ICON_TOGGLE, 0, icon,
564 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
566 TIP_("Hide object in viewport (Alt to disable for all viewports)"));
567 UI_but_func_set(bt, hidebutton_base_flag_cb, view_layer, base);
568 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
571 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
572 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
573 &ptr, props.object_hide_viewport, -1, 0, 0, -1, -1, NULL);
574 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
577 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
578 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, UI_UNIT_Y,
579 &ptr, props.object_hide_select, -1, 0, 0, -1, -1, NULL);
580 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
582 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
583 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
584 &ptr, props.object_hide_render, -1, 0, 0, -1, -1, NULL);
585 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
588 else if (tselem->type == TSE_MODIFIER) {
589 ModifierData *md = (ModifierData *)te->directdata;
592 RNA_pointer_create(tselem->id, &RNA_Modifier, md, &ptr);
594 bt = uiDefIconButR_prop(
595 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
596 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
597 &ptr, props.modifier_show_viewport, -1, 0, 0, -1, -1, NULL);
598 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
600 bt = uiDefIconButR_prop(
601 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
602 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
603 &ptr, props.modifier_show_render, -1, 0, 0, -1, -1, NULL);
604 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
606 else if (tselem->type == TSE_POSE_CHANNEL) {
607 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
608 Bone *bone = pchan->bone;
609 Object *ob = (Object *)tselem->id;
611 bt = uiDefIconButBitI(
612 block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_P, 0, ICON_HIDE_OFF,
613 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
614 UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0,
615 TIP_("Restrict/Allow visibility in the 3D View"));
616 UI_but_func_set(bt, restrictbutton_bone_visibility_cb, ob->data, bone);
617 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
618 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
620 bt = uiDefIconButBitI(
621 block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF,
622 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
623 UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0,
624 TIP_("Restrict/Allow selection in the 3D View"));
625 UI_but_func_set(bt, restrictbutton_bone_select_cb, ob->data, bone);
626 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
627 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
629 else if (tselem->type == TSE_EBONE) {
630 EditBone *ebone = (EditBone *)te->directdata;
632 bt = uiDefIconButBitI(
633 block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_A, 0, ICON_RESTRICT_VIEW_OFF,
634 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
635 UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0,
636 TIP_("Restrict/Allow visibility in the 3D View"));
637 UI_but_func_set(bt, restrictbutton_ebone_visibility_cb, NULL, ebone);
638 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
639 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
641 bt = uiDefIconButBitI(
642 block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF,
643 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
644 UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0,
645 TIP_("Restrict/Allow selection in the 3D View"));
646 UI_but_func_set(bt, restrictbutton_ebone_select_cb, NULL, ebone);
647 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
648 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
650 else if (tselem->type == TSE_GP_LAYER) {
651 bGPDlayer *gpl = (bGPDlayer *)te->directdata;
653 bt = uiDefIconButBitS(
654 block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_HIDE, 0, ICON_HIDE_OFF,
655 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
656 UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0,
657 TIP_("Restrict/Allow visibility in the 3D View"));
658 UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl);
659 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
660 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
662 bt = uiDefIconButBitS(
663 block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_LOCKED, 0, ICON_UNLOCKED,
664 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
665 UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0,
666 TIP_("Restrict/Allow editing of strokes and keyframes in this layer"));
667 UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl);
668 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
670 /* TODO: visibility in renders */
672 else if (outliner_is_collection_tree_element(te)) {
673 LayerCollection *lc = (tselem->type == TSE_LAYER_COLLECTION) ? te->directdata : NULL;
674 Collection *collection = outliner_collection_from_tree_element(te);
676 if ((!lc || !(lc->flag & LAYER_COLLECTION_EXCLUDE)) &&
677 !(collection->flag & COLLECTION_IS_MASTER))
679 PointerRNA collection_ptr;
680 RNA_id_pointer_create(&collection->id, &collection_ptr);
683 int icon = ICON_RESTRICT_VIEW_ON;
684 if ((collection->flag & COLLECTION_RESTRICT_VIEW) == 0) {
685 icon = (lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0 ?
690 block, UI_BTYPE_TOGGLE, 0, icon,
691 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
693 TIP_("Hide collection in viewport\n"
694 "* Alt to disable for all viewports\n"
695 "* Ctrl to isolate visibility\n"
696 "* Shift to hide inside objects and collections"));
697 UI_but_func_set(bt, hidebutton_layer_collection_flag_cb, view_layer, lc);
698 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
701 bt = uiDefIconButR_prop(
702 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
703 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
704 UI_UNIT_Y, &collection_ptr, props.collection_hide_viewport, -1, 0, 0, 0, 0, NULL);
705 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
708 bt = uiDefIconButR_prop(
709 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
710 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X,
711 UI_UNIT_Y, &collection_ptr, props.collection_hide_render, -1, 0, 0, 0, 0, NULL);
712 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
714 bt = uiDefIconButR_prop(
715 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
716 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
717 UI_UNIT_Y, &collection_ptr, props.collection_hide_select, -1, 0, 0, 0, 0, NULL);
718 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
723 if (TSELEM_OPEN(tselem, soops)) {
724 outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &te->subtree);
729 static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops, ListBase *lb)
732 for (TreeElement *te = lb->first; te; te = te->next) {
733 TreeStoreElem *tselem = TREESTORE(te);
734 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
735 if (tselem->type == 0) {
738 const char *tip = NULL;
739 int icon = ICON_NONE;
741 int but_flag = UI_BUT_DRAG_LOCK;
743 if (ID_IS_LINKED(id))
744 but_flag |= UI_BUT_DISABLED;
746 if (id->flag & LIB_FAKEUSER) {
747 icon = ICON_FILE_TICK;
748 tip = TIP_("Data-block will be retained using a fake user");
752 tip = TIP_("Data-block has no users and will be deleted");
754 bt = uiDefIconButBitS(
755 block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, icon,
756 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
757 &id->flag, 0, 0, 0, 0, tip);
758 UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
759 UI_but_flag_enable(bt, but_flag);
762 BLI_str_format_int_grouped(buf, id->us);
764 block, UI_BTYPE_BUT, 1, buf,
765 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys,
766 UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0,
767 TIP_("Number of users of this data-block"));
768 UI_but_flag_enable(bt, but_flag);
772 block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, (id->flag & LIB_FAKEUSER) ? "F" : " ",
773 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
774 &id->flag, 0, 0, 0, 0,
775 TIP_("Data-block has a 'fake' user which will keep it in the file "
776 "even if nothing else uses it"));
777 UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
778 UI_but_flag_enable(bt, but_flag);
782 if (TSELEM_OPEN(tselem, soops)) {
783 outliner_draw_userbuts(block, ar, soops, &te->subtree);
788 static void outliner_draw_rnacols(ARegion *ar, int sizex)
790 View2D *v2d = &ar->v2d;
792 float miny = v2d->cur.ymin;
793 if (miny < v2d->tot.ymin) miny = v2d->tot.ymin;
795 GPU_line_width(1.0f);
797 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
798 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
799 immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
801 immBegin(GPU_PRIM_LINES, 4);
803 immVertex2f(pos, sizex, v2d->cur.ymax);
804 immVertex2f(pos, sizex, miny);
806 immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax);
807 immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny);
814 static void outliner_draw_rnabuts(uiBlock *block, ARegion *ar, SpaceOops *soops, int sizex, ListBase *lb)
819 for (TreeElement *te = lb->first; te; te = te->next) {
820 TreeStoreElem *tselem = TREESTORE(te);
821 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
822 if (tselem->type == TSE_RNA_PROPERTY) {
824 prop = te->directdata;
826 if (!TSELEM_OPEN(tselem, soops)) {
827 if (RNA_property_type(prop) == PROP_POINTER) {
828 uiBut *but = uiDefAutoButR(
829 block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys,
830 OL_RNA_COL_SIZEX, UI_UNIT_Y - 1);
831 UI_but_flag_enable(but, UI_BUT_DISABLED);
833 else if (RNA_property_type(prop) == PROP_ENUM) {
835 block, ptr, prop, -1, NULL, ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
840 block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
845 else if (tselem->type == TSE_RNA_ARRAY_ELEM) {
847 prop = te->directdata;
850 block, ptr, prop, te->index, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
855 if (TSELEM_OPEN(tselem, soops)) {
856 outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree);
861 static void outliner_buttons(const bContext *C, uiBlock *block, ARegion *ar, TreeElement *te)
864 TreeStoreElem *tselem;
867 tselem = TREESTORE(te);
869 BLI_assert(tselem->flag & TSE_TEXTBUT);
870 /* If we add support to rename Sequence.
874 if (tselem->type == TSE_EBONE) len = sizeof(((EditBone *) 0)->name);
875 else if (tselem->type == TSE_MODIFIER) len = sizeof(((ModifierData *) 0)->name);
876 else if (tselem->id && GS(tselem->id->name) == ID_LI) len = sizeof(((Library *) 0)->name);
877 else len = MAX_ID_NAME - 2;
879 spx = te->xs + 1.8f * UI_UNIT_X;
880 dx = ar->v2d.cur.xmax - (spx + 3.2f * UI_UNIT_X);
883 block, UI_BTYPE_TEXT, OL_NAMEBUTTON, "", spx, te->ys, dx, UI_UNIT_Y - 1, (void *)te->name,
884 1.0, (float)len, 0, 0, "");
885 UI_but_func_rename_set(bt, namebutton_cb, tselem);
887 /* returns false if button got removed */
888 if (false == UI_but_active_only(C, ar, block, bt)) {
889 tselem->flag &= ~TSE_TEXTBUT;
891 /* bad! (notifier within draw) without this, we don't get a refresh */
892 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
896 /* ****************************************************** */
897 /* Normal Drawing... */
899 TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
901 TreeElementIcon data = {0};
904 switch (tselem->type) {
906 data.icon = ICON_ANIM_DATA; /* XXX */
909 data.icon = ICON_NLA;
912 data.icon = ICON_NLA; /* XXX */
915 data.icon = ICON_ACTION;
917 case TSE_DRIVER_BASE:
918 data.icon = ICON_DRIVER;
920 case TSE_DEFGROUP_BASE:
921 data.icon = ICON_GROUP_VERTEX;
925 data.icon = ICON_BONE_DATA;
927 case TSE_CONSTRAINT_BASE:
928 data.icon = ICON_CONSTRAINT;
930 case TSE_MODIFIER_BASE:
931 data.icon = ICON_MODIFIER_DATA;
934 data.icon = ICON_OBJECT_DATA;
936 case TSE_LINKED_PSYS:
937 data.icon = ICON_PARTICLES;
941 Object *ob = (Object *)tselem->id;
942 if (ob->type != OB_GPENCIL) {
943 ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr);
944 switch ((ModifierType)md->type) {
945 case eModifierType_Subsurf:
946 data.icon = ICON_MOD_SUBSURF;
948 case eModifierType_Armature:
949 data.icon = ICON_MOD_ARMATURE;
951 case eModifierType_Lattice:
952 data.icon = ICON_MOD_LATTICE;
954 case eModifierType_Curve:
955 data.icon = ICON_MOD_CURVE;
957 case eModifierType_Build:
958 data.icon = ICON_MOD_BUILD;
960 case eModifierType_Mirror:
961 data.icon = ICON_MOD_MIRROR;
963 case eModifierType_Decimate:
964 data.icon = ICON_MOD_DECIM;
966 case eModifierType_Wave:
967 data.icon = ICON_MOD_WAVE;
969 case eModifierType_Hook:
970 data.icon = ICON_HOOK;
972 case eModifierType_Softbody:
973 data.icon = ICON_MOD_SOFT;
975 case eModifierType_Boolean:
976 data.icon = ICON_MOD_BOOLEAN;
978 case eModifierType_ParticleSystem:
979 data.icon = ICON_MOD_PARTICLES;
981 case eModifierType_ParticleInstance:
982 data.icon = ICON_MOD_PARTICLES;
984 case eModifierType_EdgeSplit:
985 data.icon = ICON_MOD_EDGESPLIT;
987 case eModifierType_Array:
988 data.icon = ICON_MOD_ARRAY;
990 case eModifierType_UVProject:
991 case eModifierType_UVWarp: /* TODO, get own icon */
992 data.icon = ICON_MOD_UVPROJECT;
994 case eModifierType_Displace:
995 data.icon = ICON_MOD_DISPLACE;
997 case eModifierType_Shrinkwrap:
998 data.icon = ICON_MOD_SHRINKWRAP;
1000 case eModifierType_Cast:
1001 data.icon = ICON_MOD_CAST;
1003 case eModifierType_MeshDeform:
1004 case eModifierType_SurfaceDeform:
1005 data.icon = ICON_MOD_MESHDEFORM;
1007 case eModifierType_Bevel:
1008 data.icon = ICON_MOD_BEVEL;
1010 case eModifierType_Smooth:
1011 case eModifierType_LaplacianSmooth:
1012 case eModifierType_CorrectiveSmooth:
1013 data.icon = ICON_MOD_SMOOTH;
1015 case eModifierType_SimpleDeform:
1016 data.icon = ICON_MOD_SIMPLEDEFORM;
1018 case eModifierType_Mask:
1019 data.icon = ICON_MOD_MASK;
1021 case eModifierType_Cloth:
1022 data.icon = ICON_MOD_CLOTH;
1024 case eModifierType_Explode:
1025 data.icon = ICON_MOD_EXPLODE;
1027 case eModifierType_Collision:
1028 case eModifierType_Surface:
1029 data.icon = ICON_MOD_PHYSICS;
1031 case eModifierType_Fluidsim:
1032 data.icon = ICON_MOD_FLUIDSIM;
1034 case eModifierType_Multires:
1035 data.icon = ICON_MOD_MULTIRES;
1037 case eModifierType_Smoke:
1038 data.icon = ICON_MOD_SMOKE;
1040 case eModifierType_Solidify:
1041 data.icon = ICON_MOD_SOLIDIFY;
1043 case eModifierType_Screw:
1044 data.icon = ICON_MOD_SCREW;
1046 case eModifierType_Remesh:
1047 data.icon = ICON_MOD_REMESH;
1049 case eModifierType_WeightVGEdit:
1050 case eModifierType_WeightVGMix:
1051 case eModifierType_WeightVGProximity:
1052 data.icon = ICON_MOD_VERTEX_WEIGHT;
1054 case eModifierType_DynamicPaint:
1055 data.icon = ICON_MOD_DYNAMICPAINT;
1057 case eModifierType_Ocean:
1058 data.icon = ICON_MOD_OCEAN;
1060 case eModifierType_Warp:
1061 data.icon = ICON_MOD_WARP;
1063 case eModifierType_Skin:
1064 data.icon = ICON_MOD_SKIN;
1066 case eModifierType_Triangulate:
1067 data.icon = ICON_MOD_TRIANGULATE;
1069 case eModifierType_MeshCache:
1070 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1072 case eModifierType_MeshSequenceCache:
1073 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1075 case eModifierType_Wireframe:
1076 data.icon = ICON_MOD_WIREFRAME;
1078 case eModifierType_LaplacianDeform:
1079 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1081 case eModifierType_DataTransfer:
1082 data.icon = ICON_MOD_DATA_TRANSFER;
1084 case eModifierType_NormalEdit:
1085 case eModifierType_WeightedNormal:
1086 data.icon = ICON_MOD_NORMALEDIT;
1089 case eModifierType_None:
1090 case eModifierType_ShapeKey:
1092 case NUM_MODIFIER_TYPES:
1093 data.icon = ICON_DOT;
1098 /* grease pencil modifiers */
1099 GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr);
1100 switch ((GpencilModifierType)md->type) {
1101 case eGpencilModifierType_Noise:
1102 data.icon = ICON_RNDCURVE;
1104 case eGpencilModifierType_Subdiv:
1105 data.icon = ICON_MOD_SUBSURF;
1107 case eGpencilModifierType_Thick:
1108 data.icon = ICON_MOD_THICKNESS;
1110 case eGpencilModifierType_Tint:
1111 data.icon = ICON_MOD_TINT;
1113 case eGpencilModifierType_Array:
1114 data.icon = ICON_MOD_ARRAY;
1116 case eGpencilModifierType_Build:
1117 data.icon = ICON_MOD_BUILD;
1119 case eGpencilModifierType_Opacity:
1120 data.icon = ICON_MOD_MASK;
1122 case eGpencilModifierType_Color:
1123 data.icon = ICON_MOD_HUE_SATURATION;
1125 case eGpencilModifierType_Lattice:
1126 data.icon = ICON_MOD_LATTICE;
1128 case eGpencilModifierType_Mirror:
1129 data.icon = ICON_MOD_MIRROR;
1131 case eGpencilModifierType_Simplify:
1132 data.icon = ICON_MOD_SIMPLIFY;
1134 case eGpencilModifierType_Smooth:
1135 data.icon = ICON_MOD_SMOOTH;
1137 case eGpencilModifierType_Hook:
1138 data.icon = ICON_HOOK;
1140 case eGpencilModifierType_Offset:
1141 data.icon = ICON_MOD_OFFSET;
1143 case eGpencilModifierType_Armature:
1144 data.icon = ICON_MOD_ARMATURE;
1149 data.icon = ICON_DOT;
1156 data.icon = ICON_ARMATURE_DATA;
1158 case TSE_POSE_CHANNEL:
1159 data.icon = ICON_BONE_DATA;
1162 data.icon = ICON_GHOST_ENABLED;
1164 case TSE_R_LAYER_BASE:
1165 data.icon = ICON_RENDERLAYERS;
1167 case TSE_SCENE_OBJECTS_BASE:
1168 data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
1171 data.icon = ICON_RENDER_RESULT;
1173 case TSE_LINKED_LAMP:
1174 data.icon = ICON_LIGHT_DATA;
1176 case TSE_LINKED_MAT:
1177 data.icon = ICON_MATERIAL_DATA;
1179 case TSE_POSEGRP_BASE:
1180 data.icon = ICON_GROUP_BONE;
1183 if (te->idcode == SEQ_TYPE_MOVIE)
1184 data.icon = ICON_SEQUENCE;
1185 else if (te->idcode == SEQ_TYPE_META)
1186 data.icon = ICON_DOT;
1187 else if (te->idcode == SEQ_TYPE_SCENE)
1188 data.icon = ICON_SCENE;
1189 else if (te->idcode == SEQ_TYPE_SOUND_RAM)
1190 data.icon = ICON_SOUND;
1191 else if (te->idcode == SEQ_TYPE_IMAGE)
1192 data.icon = ICON_IMAGE;
1194 data.icon = ICON_PARTICLES;
1197 data.icon = ICON_LIBRARY_DATA_DIRECT;
1199 case TSE_SEQUENCE_DUP:
1200 data.icon = ICON_OBJECT_DATA;
1202 case TSE_RNA_STRUCT:
1203 if (RNA_struct_is_ID(te->rnaptr.type)) {
1204 data.drag_id = (ID *)te->rnaptr.data;
1205 data.icon = RNA_struct_ui_icon(te->rnaptr.type);
1208 data.icon = RNA_struct_ui_icon(te->rnaptr.type);
1211 case TSE_LAYER_COLLECTION:
1212 case TSE_SCENE_COLLECTION_BASE:
1213 case TSE_VIEW_COLLECTION_BASE:
1215 Collection *collection = outliner_collection_from_tree_element(te);
1216 if (collection && !(collection->flag & COLLECTION_IS_MASTER)) {
1217 data.drag_id = tselem->id;
1218 data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
1221 data.icon = ICON_GROUP;
1224 /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */
1227 /* indicate whether layer is active */
1228 bGPDlayer *gpl = te->directdata;
1229 if (gpl->flag & GP_LAYER_ACTIVE) {
1230 data.icon = ICON_GREASEPENCIL;
1233 data.icon = ICON_DOT;
1238 data.icon = ICON_DOT;
1242 else if (tselem->id) {
1243 data.drag_id = tselem->id;
1244 data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
1246 if (GS(tselem->id->name) == ID_OB) {
1247 Object *ob = (Object *)tselem->id;
1250 data.icon = ICON_OUTLINER_OB_LIGHT; break;
1252 data.icon = ICON_OUTLINER_OB_MESH; break;
1254 data.icon = ICON_OUTLINER_OB_CAMERA; break;
1256 data.icon = ICON_OUTLINER_OB_CURVE; break;
1258 data.icon = ICON_OUTLINER_OB_META; break;
1260 data.icon = ICON_OUTLINER_OB_LATTICE; break;
1262 data.icon = ICON_OUTLINER_OB_ARMATURE; break;
1264 data.icon = ICON_OUTLINER_OB_FONT; break;
1266 data.icon = ICON_OUTLINER_OB_SURFACE; break;
1268 data.icon = ICON_OUTLINER_OB_SPEAKER; break;
1270 data.icon = ICON_OUTLINER_OB_LIGHTPROBE; break;
1272 if (ob->dup_group) {
1273 data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
1275 else if (ob->empty_drawtype == OB_EMPTY_IMAGE) {
1276 data.icon = ICON_OUTLINER_OB_IMAGE;
1279 data.icon = ICON_OUTLINER_OB_EMPTY;
1283 data.icon = ICON_OUTLINER_OB_GREASEPENCIL; break;
1288 /* TODO(sergey): Casting to short here just to handle ID_NLA which is
1289 * NOT inside of IDType enum.
1291 switch ((short)GS(tselem->id->name)) {
1293 data.icon = ICON_SCENE_DATA; break;
1295 data.icon = ICON_OUTLINER_DATA_MESH; break;
1297 data.icon = ICON_OUTLINER_DATA_CURVE; break;
1299 data.icon = ICON_OUTLINER_DATA_META; break;
1301 data.icon = ICON_OUTLINER_DATA_LATTICE; break;
1304 Lamp *la = (Lamp *)tselem->id;
1307 data.icon = ICON_LIGHT_POINT; break;
1309 data.icon = ICON_LIGHT_SUN; break;
1311 data.icon = ICON_LIGHT_SPOT; break;
1313 data.icon = ICON_LIGHT_AREA; break;
1315 data.icon = ICON_OUTLINER_DATA_LIGHT; break;
1320 data.icon = ICON_MATERIAL_DATA; break;
1322 data.icon = ICON_TEXTURE_DATA; break;
1324 data.icon = ICON_IMAGE_DATA; break;
1327 data.icon = ICON_OUTLINER_DATA_SPEAKER; break;
1329 data.icon = ICON_OUTLINER_DATA_ARMATURE; break;
1331 data.icon = ICON_OUTLINER_DATA_CAMERA; break;
1333 data.icon = ICON_SHAPEKEY_DATA; break;
1335 data.icon = ICON_WORLD_DATA; break;
1337 data.icon = ICON_ACTION; break;
1339 data.icon = ICON_NLA; break;
1341 data.icon = ICON_SCRIPT; break;
1343 data.icon = ICON_GROUP; break;
1345 if (tselem->id->tag & LIB_TAG_MISSING) {
1346 data.icon = ICON_LIBRARY_DATA_BROKEN;
1348 else if (((Library *)tselem->id)->parent) {
1349 data.icon = ICON_LIBRARY_DATA_INDIRECT;
1352 data.icon = ICON_LIBRARY_DATA_DIRECT;
1356 data.icon = ICON_LINE_DATA; break;
1358 data.icon = ICON_OUTLINER_DATA_GREASEPENCIL; break;
1361 LightProbe *lp = (LightProbe *)tselem->id;
1363 case LIGHTPROBE_TYPE_CUBE:
1364 data.icon = ICON_LIGHTPROBE_CUBEMAP; break;
1365 case LIGHTPROBE_TYPE_PLANAR:
1366 data.icon = ICON_LIGHTPROBE_PLANAR; break;
1367 case LIGHTPROBE_TYPE_GRID:
1368 data.icon = ICON_LIGHTPROBE_GRID; break;
1370 data.icon = ICON_LIGHTPROBE_CUBEMAP; break;
1375 data.icon = ICON_BRUSH_DATA; break;
1378 data.icon = ICON_WORKSPACE; break;
1380 data.icon = ICON_MOD_MASK; break;
1382 data.icon = ICON_SEQUENCE; break;
1392 static void tselem_draw_icon(
1393 uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te,
1394 float alpha, const bool is_clickable)
1396 TreeElementIcon data = tree_element_get_icon(tselem, te);
1398 if (data.icon == 0) {
1402 if (!is_clickable || x >= xmax) {
1403 /* placement of icons, copied from interface_widgets.c */
1404 float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
1408 /* restrict column clip... it has been coded by simply overdrawing,
1409 * doesn't work for buttons */
1410 UI_icon_draw_alpha(x, y, data.icon, alpha);
1414 block, UI_BTYPE_LABEL, 0, data.icon, x, y, UI_UNIT_X, UI_UNIT_Y, NULL,
1415 0.0, 0.0, 1.0, alpha,
1416 (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->name : "");
1421 * For icon-only children of a collapsed tree,
1422 * Draw small number over the icon to show how many items of this type are displayed.
1424 static void outliner_draw_iconrow_number(
1425 const uiFontStyle *fstyle,
1427 const int num_elements)
1429 float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1430 float ufac = 0.25f * UI_UNIT_X;
1431 float offset_x = (float) offsx + UI_UNIT_X * 0.35f;
1433 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1434 UI_draw_roundbox_aa(true,
1436 (float)ys - UI_UNIT_Y * 0.2f + ufac,
1437 offset_x + UI_UNIT_X - ufac,
1438 (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac,
1439 (float)UI_UNIT_Y / 2.0f - ufac,
1442 /* Now the numbers. */
1443 unsigned char text_col[4];
1445 UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
1448 uiFontStyle fstyle_small = *fstyle;
1449 fstyle_small.points *= 0.8f;
1451 /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */
1453 char number_text[4] = "+99\0";
1454 if (num_elements < 100) {
1455 BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements);
1456 num_digits = num_elements < 10 ? 1 : 2;
1458 UI_fontstyle_draw_simple(&fstyle_small,
1459 (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f),
1460 (float)ys - UI_UNIT_Y * 0.095f + ufac,
1461 number_text, text_col);
1462 UI_fontstyle_set(fstyle);
1463 GPU_blend(true); /* Roundbox and text drawing disables. */
1466 static void outliner_draw_iconrow_doit(
1467 uiBlock *block, TreeElement *te,
1468 const uiFontStyle *fstyle,
1469 int xmax, int *offsx, int ys, float alpha_fac,
1470 const eOLDrawState active,
1471 const int num_elements)
1473 TreeStoreElem *tselem = TREESTORE(te);
1475 if (active != OL_DRAWSEL_NONE) {
1476 float ufac = UI_UNIT_X / 20.0f;
1477 float color[4] = {1.0f, 1.0f, 1.0f, 0.2f};
1479 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1480 color[3] *= alpha_fac;
1482 UI_draw_roundbox_aa(true,
1483 (float) *offsx + 1.0f * ufac,
1484 (float)ys + 1.0f * ufac,
1485 (float)*offsx + UI_UNIT_X - 1.0f * ufac,
1486 (float)ys + UI_UNIT_Y - ufac,
1487 (float)UI_UNIT_Y / 2.0f - ufac,
1489 GPU_blend(true); /* Roundbox disables. */
1492 /* No inlined icon should be clickable. */
1493 tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.8f * alpha_fac, false);
1496 te->xend = (short)*offsx + UI_UNIT_X;
1498 if (num_elements > 1) {
1499 outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements);
1501 (*offsx) += UI_UNIT_X;
1505 * Return the index to use based on the TreeElement ID and object type
1507 * We use a continuum of indices until we get to the object datablocks
1508 * and we then make room for the object types.
1510 static int tree_element_id_type_to_index(TreeElement *te)
1512 TreeStoreElem *tselem = TREESTORE(te);
1514 const int id_index = tselem->type == 0 ? BKE_idcode_to_index(te->idcode) : INDEX_ID_GR;
1515 if (id_index < INDEX_ID_OB) {
1518 else if (id_index == INDEX_ID_OB) {
1519 const Object *ob = (Object *)tselem->id;
1520 return INDEX_ID_OB + ob->type;
1523 return id_index + OB_TYPE_MAX;
1527 typedef struct MergedIconRow {
1528 eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX];
1529 int num_elements[INDEX_ID_MAX + OB_TYPE_MAX];
1530 TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX];
1533 static void outliner_draw_iconrow(
1534 bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer, SpaceOops *soops,
1535 ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac, MergedIconRow *merged)
1537 eOLDrawState active;
1538 const Object *obact = OBACT(view_layer);
1540 for (TreeElement *te = lb->first; te; te = te->next) {
1541 /* exit drawing early */
1542 if ((*offsx) - UI_UNIT_X > xmax)
1545 TreeStoreElem *tselem = TREESTORE(te);
1547 /* object hierarchy always, further constrained on level */
1548 if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) {
1549 /* active blocks get white circle */
1550 if (tselem->type == 0) {
1551 if (te->idcode == ID_OB) {
1552 active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
1554 else if (is_object_data_in_editmode(tselem->id, obact)) {
1555 active = OL_DRAWSEL_NORMAL;
1558 active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false);
1562 active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
1565 if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER)) {
1566 outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
1569 const int index = tree_element_id_type_to_index(te);
1570 merged->num_elements[index]++;
1571 if ((merged->tree_element[index] == NULL) ||
1572 (active > merged->active[index]))
1574 merged->tree_element[index] = te;
1576 merged->active[index] = MAX2(active, merged->active[index]);
1580 /* this tree element always has same amount of branches, so don't draw */
1581 if (tselem->type != TSE_R_LAYER) {
1582 outliner_draw_iconrow(
1583 C, block, fstyle, scene, view_layer, soops,
1584 &te->subtree, level + 1, xmax, offsx, ys, alpha_fac, merged);
1589 for (int i = 0; i < INDEX_ID_MAX; i++) {
1590 const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1;
1591 /* See tree_element_id_type_to_index for the index logic. */
1593 if (i > INDEX_ID_OB) {
1594 index_base += OB_TYPE_MAX;
1596 for (int j = 0; j < num_subtypes; j++) {
1597 const int index = index_base + j;
1598 if (merged->num_elements[index] != 0) {
1599 outliner_draw_iconrow_doit(block,
1600 merged->tree_element[index],
1602 xmax, offsx, ys, alpha_fac,
1603 merged->active[index],
1604 merged->num_elements[index]);
1611 /* closed tree element */
1612 static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty)
1616 /* closed items may be displayed in row of parent, don't change their coordinate! */
1617 if ((te->flag & TE_ICONROW) == 0) {
1618 /* store coord and continue, we need coordinates for elements outside view too */
1623 for (ten = te->subtree.first; ten; ten = ten->next) {
1624 outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty);
1629 static void outliner_draw_tree_element(
1630 bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer,
1631 ARegion *ar, SpaceOops *soops, TreeElement *te, bool draw_grayed_out,
1632 int startx, int *starty, TreeElement **te_edit)
1634 TreeStoreElem *tselem;
1635 float ufac = UI_UNIT_X / 20.0f;
1637 eOLDrawState active = OL_DRAWSEL_NONE;
1639 tselem = TREESTORE(te);
1641 if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) {
1642 const float alpha_fac = ((te->flag & TE_DISABLED) || draw_grayed_out) ? 0.5f : 1.0f;
1643 const float alpha = 0.5f * alpha_fac;
1644 int xmax = ar->v2d.cur.xmax;
1646 if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) {
1650 /* icons can be ui buts, we don't want it to overlap with restrict */
1651 if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0)
1652 xmax -= OL_TOGW + UI_UNIT_X;
1656 /* colors for active/selected data */
1657 if (tselem->type == 0) {
1658 const Object *obact = OBACT(view_layer);
1659 if (te->idcode == ID_SCE) {
1660 if (tselem->id == (ID *)scene) {
1661 rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
1662 active = OL_DRAWSEL_ACTIVE;
1665 else if (te->idcode == ID_OB) {
1666 Object *ob = (Object *)tselem->id;
1667 Base *base = (Base *)te->directdata;
1668 const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
1670 if (ob == obact || is_selected) {
1671 uchar col[4] = {0, 0, 0, 0};
1673 /* outliner active ob: always white text, circle color now similar to view3d */
1675 active = OL_DRAWSEL_ACTIVE;
1678 UI_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col);
1682 active = OL_DRAWSEL_NORMAL;
1684 else if (is_selected) {
1685 UI_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col);
1688 rgba_float_args_set(color, (float)col[0] / 255, (float)col[1] / 255, (float)col[2] / 255, alpha);
1691 else if (is_object_data_in_editmode(tselem->id, obact)) {
1692 rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
1693 active = OL_DRAWSEL_ACTIVE;
1696 if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) {
1697 rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
1698 active = OL_DRAWSEL_ACTIVE;
1703 active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
1704 rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
1708 if (active != OL_DRAWSEL_NONE) {
1709 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1710 UI_draw_roundbox_aa(
1712 (float)startx + UI_UNIT_X + 1.0f * ufac,
1713 (float)*starty + 1.0f * ufac,
1714 (float)startx + 2.0f * UI_UNIT_X - 1.0f * ufac,
1715 (float)*starty + UI_UNIT_Y - 1.0f * ufac,
1716 UI_UNIT_Y / 2.0f - 1.0f * ufac, color);
1717 GPU_blend(true); /* roundbox disables it */
1719 te->flag |= TE_ACTIVE; // for lookup in display hierarchies
1722 if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
1723 /* Scene collection in view layer can't expand/collapse. */
1725 else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) || (te->flag & TE_LAZY_CLOSED)) {
1726 /* open/close icon, only when sublevels, except for scene */
1727 int icon_x = startx;
1729 // icons a bit higher
1730 if (TSELEM_OPEN(tselem, soops)) {
1732 (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_DOWN,
1737 (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_RIGHT,
1745 if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) {
1746 tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac, true);
1747 offsx += UI_UNIT_X + 4 * ufac;
1752 if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) {
1753 if (tselem->id->tag & LIB_TAG_MISSING) {
1755 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN,
1758 else if (tselem->id->tag & LIB_TAG_INDIRECT) {
1760 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT,
1765 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT,
1768 offsx += UI_UNIT_X + 4 * ufac;
1770 else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_STATIC_OVERRIDE(tselem->id)) {
1772 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_OVERRIDE,
1774 offsx += UI_UNIT_X + 4 * ufac;
1779 if ((tselem->flag & TSE_TEXTBUT) == 0) {
1780 unsigned char text_col[4];
1782 if (active == OL_DRAWSEL_NORMAL) {
1783 UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
1785 else if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
1786 UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.75f, text_col);
1790 UI_GetThemeColor4ubv(TH_TEXT, text_col);
1792 text_col[3] *= alpha_fac;
1794 UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_col);
1797 offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name));
1799 /* closed item, we draw the icons, not when it's a scene, or master-server list though */
1800 if (!TSELEM_OPEN(tselem, soops)) {
1801 if (te->subtree.first) {
1802 if (tselem->type == 0 && te->idcode == ID_SCE) {
1805 /* this tree element always has same amount of branches, so don't draw */
1806 else if (tselem->type != TSE_R_LAYER) {
1807 int tempx = startx + offsx;
1813 GPUVertFormat *format = immVertexFormat();
1814 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1815 unsigned char col[4];
1817 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1818 UI_GetThemeColorShade4ubv(TH_BACK, -40, col);
1819 col[3] *= alpha_fac;
1821 immUniformColor4ubv(col);
1822 immRecti(pos, tempx - 10.0f * ufac,
1823 *starty + 4.0f * ufac,
1824 tempx - 8.0f * ufac,
1825 *starty + UI_UNIT_Y - 4.0f * ufac);
1829 MergedIconRow merged = {{0}};
1830 outliner_draw_iconrow(
1831 C, block, fstyle, scene, view_layer, soops, &te->subtree, 0, xmax, &tempx,
1832 *starty, alpha_fac, &merged);
1839 /* store coord and continue, we need coordinates for elements outside view too */
1842 te->xend = startx + offsx;
1844 if (TSELEM_OPEN(tselem, soops)) {
1845 *starty -= UI_UNIT_Y;
1847 for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
1848 /* check if element needs to be drawn grayed out, but also gray out
1849 * childs of a grayed out parent (pass on draw_grayed_out to childs) */
1850 bool draw_childs_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
1851 outliner_draw_tree_element(
1852 C, block, fstyle, scene, view_layer,
1853 ar, soops, ten, draw_childs_grayed_out,
1854 startx + UI_UNIT_X, starty, te_edit);
1858 for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
1859 outliner_set_coord_tree_element(ten, startx, *starty);
1862 *starty -= UI_UNIT_Y;
1866 static void outliner_draw_hierarchy_lines_recursive(
1867 unsigned pos, SpaceOops *soops, ListBase *lb, int startx,
1868 const unsigned char col[4], bool draw_grayed_out,
1871 TreeElement *te, *te_vertical_line_last = NULL;
1874 if (BLI_listbase_is_empty(lb)) {
1878 const unsigned char grayed_alpha = col[3] / 2;
1880 /* For vertical lines between objects. */
1882 for (te = lb->first; te; te = te->next) {
1883 bool draw_childs_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
1884 TreeStoreElem *tselem = TREESTORE(te);
1886 if (draw_childs_grayed_out) {
1887 immUniformColor3ubvAlpha(col, grayed_alpha);
1890 immUniformColor4ubv(col);
1893 /* Horizontal Line? */
1894 if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) {
1895 immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - 1);
1897 /* Vertical Line? */
1898 if (te->idcode == ID_OB) {
1899 te_vertical_line_last = te;
1904 *starty -= UI_UNIT_Y;
1906 if (TSELEM_OPEN(tselem, soops))
1907 outliner_draw_hierarchy_lines_recursive(
1908 pos, soops, &te->subtree, startx + UI_UNIT_X,
1909 col, draw_childs_grayed_out, starty);
1912 if (draw_grayed_out) {
1913 immUniformColor3ubvAlpha(col, grayed_alpha);
1916 immUniformColor4ubv(col);
1919 /* Vertical line. */
1920 te = te_vertical_line_last;
1921 if ((te != NULL) && (te->parent || lb->first != lb->last)) {
1922 immRecti(pos, startx, y1 + UI_UNIT_Y, startx + 1, y2);
1926 static void outliner_draw_hierarchy_lines(SpaceOops *soops, ListBase *lb, int startx, int *starty)
1928 GPUVertFormat *format = immVertexFormat();
1929 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1930 unsigned char col[4];
1932 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1933 UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
1937 outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty);
1943 static void outliner_draw_struct_marks(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty)
1945 for (TreeElement *te = lb->first; te; te = te->next) {
1946 TreeStoreElem *tselem = TREESTORE(te);
1948 /* selection status */
1949 if (TSELEM_OPEN(tselem, soops)) {
1950 if (tselem->type == TSE_RNA_STRUCT) {
1951 GPUVertFormat *format = immVertexFormat();
1952 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1953 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1954 immThemeColorShadeAlpha(TH_BACK, -15, -200);
1955 immRecti(pos, 0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1);
1960 *starty -= UI_UNIT_Y;
1961 if (TSELEM_OPEN(tselem, soops)) {
1962 outliner_draw_struct_marks(ar, soops, &te->subtree, starty);
1963 if (tselem->type == TSE_RNA_STRUCT) {
1964 GPUVertFormat *format = immVertexFormat();
1965 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1966 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1967 immThemeColorShadeAlpha(TH_BACK, -15, -200);
1969 immBegin(GPU_PRIM_LINES, 2);
1970 immVertex2f(pos, 0, (float)*starty + UI_UNIT_Y);
1971 immVertex2f(pos, ar->v2d.cur.xmax, (float)*starty + UI_UNIT_Y);
1980 static void outliner_draw_highlights_recursive(
1981 unsigned pos, const ARegion *ar, const SpaceOops *soops, const ListBase *lb,
1982 const float col_selection[4], const float col_highlight[4], const float col_searchmatch[4],
1983 int start_x, int *io_start_y)
1985 const bool is_searching = (
1986 SEARCHING_OUTLINER(soops) ||
1987 (soops->outlinevis == SO_DATA_API &&
1988 soops->search_string[0] != 0));
1990 for (TreeElement *te = lb->first; te; te = te->next) {
1991 const TreeStoreElem *tselem = TREESTORE(te);
1992 const int start_y = *io_start_y;
1994 /* selection status */
1995 if (tselem->flag & TSE_SELECTED) {
1996 immUniformColor4fv(col_selection);
1997 immRecti(pos, 0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1);
2001 if (tselem->flag & (TSE_DRAG_ANY | TSE_HIGHLIGHTED | TSE_SEARCHMATCH)) {
2002 const int end_x = (int)ar->v2d.cur.xmax;
2004 if (tselem->flag & TSE_DRAG_ANY) {
2005 /* drag and drop highlight */
2007 UI_GetThemeColorShade4fv(TH_BACK, -40, col);
2009 if (tselem->flag & TSE_DRAG_BEFORE) {
2010 immUniformColor4fv(col);
2011 immRecti(pos, start_x, start_y + UI_UNIT_Y - 1, end_x, start_y + UI_UNIT_Y + 1);
2013 else if (tselem->flag & TSE_DRAG_AFTER) {
2014 immUniformColor4fv(col);
2015 immRecti(pos, start_x, start_y - 1, end_x, start_y + 1);
2018 immUniformColor3fvAlpha(col, col[3] * 0.5f);
2019 immRecti(pos, start_x, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
2023 if (is_searching && (tselem->flag & TSE_SEARCHMATCH)) {
2024 /* search match highlights
2025 * we don't expand items when searching in the datablocks but we
2026 * still want to highlight any filter matches. */
2027 immUniformColor4fv(col_searchmatch);
2028 immRecti(pos, start_x, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
2030 else if (tselem->flag & TSE_HIGHLIGHTED) {
2031 /* mouse hover highlight */
2032 immUniformColor4fv(col_highlight);
2033 immRecti(pos, 0, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
2038 *io_start_y -= UI_UNIT_Y;
2039 if (TSELEM_OPEN(tselem, soops)) {
2040 outliner_draw_highlights_recursive(
2041 pos, ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch,
2042 start_x + UI_UNIT_X, io_start_y);
2047 static void outliner_draw_highlights(ARegion *ar, SpaceOops *soops, int startx, int *starty)
2049 const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f};
2050 float col_selection[4], col_searchmatch[4];
2052 UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection);
2053 col_selection[3] = 1.0f; /* no alpha */
2054 UI_GetThemeColor4fv(TH_MATCH, col_searchmatch);
2055 col_searchmatch[3] = 0.5f;
2058 GPUVertFormat *format = immVertexFormat();
2059 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2060 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2061 outliner_draw_highlights_recursive(
2062 pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch,
2068 static void outliner_draw_tree(
2069 bContext *C, uiBlock *block, Scene *scene, ViewLayer *view_layer,
2070 ARegion *ar, SpaceOops *soops, const bool has_restrict_icons,
2071 TreeElement **te_edit)
2073 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
2076 GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // only once
2078 if (soops->outlinevis == SO_DATA_API) {
2080 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2081 outliner_draw_struct_marks(ar, soops, &soops->tree, &starty);
2084 /* draw highlights before hierarchy */
2085 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2087 outliner_draw_highlights(ar, soops, startx, &starty);
2089 /* set scissor so tree elements or lines can't overlap restriction icons */
2090 float scissor[4] = {0};
2091 if (has_restrict_icons) {
2092 int mask_x = BLI_rcti_size_x(&ar->v2d.mask) - (int)OL_TOGW + 1;
2093 CLAMP_MIN(mask_x, 0);
2095 GPU_scissor_get_f(scissor);
2096 GPU_scissor(0, 0, mask_x, ar->winy);
2099 // gray hierarchy lines
2101 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y / 2 - OL_Y_OFFSET;
2102 startx = UI_UNIT_X / 2 - 1.0f;
2103 outliner_draw_hierarchy_lines(soops, &soops->tree, startx, &starty);
2106 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2108 for (TreeElement *te = soops->tree.first; te; te = te->next) {
2109 outliner_draw_tree_element(
2110 C, block, fstyle, scene, view_layer,
2111 ar, soops, te, (te->flag & TE_DRAGGING) != 0,
2112 startx, &starty, te_edit);
2115 if (has_restrict_icons) {
2117 GPU_scissor(UNPACK4(scissor));
2122 static void outliner_back(ARegion *ar)
2126 ystart = (int)ar->v2d.tot.ymax;
2127 ystart = UI_UNIT_Y * (ystart / (UI_UNIT_Y)) - OL_Y_OFFSET;
2129 GPUVertFormat *format = immVertexFormat();
2130 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2132 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2133 immUniformThemeColorShade(TH_BACK, 6);
2135 const float x1 = 0.0f, x2 = ar->v2d.cur.xmax;
2136 float y1 = ystart, y2;
2137 int tot = (int)floor(ystart - ar->v2d.cur.ymin + 2 * UI_UNIT_Y) / (2 * UI_UNIT_Y);
2140 immBegin(GPU_PRIM_TRIS, 6 * tot);
2142 y1 -= 2 * UI_UNIT_Y;
2143 y2 = y1 + UI_UNIT_Y;
2144 immVertex2f(pos, x1, y1);
2145 immVertex2f(pos, x2, y1);
2146 immVertex2f(pos, x2, y2);
2148 immVertex2f(pos, x1, y1);
2149 immVertex2f(pos, x2, y2);
2150 immVertex2f(pos, x1, y2);
2157 static void outliner_draw_restrictcols(ARegion *ar)
2159 GPU_line_width(1.0f);
2161 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2162 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2163 immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
2164 immBegin(GPU_PRIM_LINES, 6);
2166 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymax);
2167 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymin);
2169 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymax);
2170 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymin);
2172 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymax);
2173 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymin);
2179 /* ****************************************************** */
2180 /* Main Entrypoint - Draw contents of Outliner editor */
2182 void draw_outliner(const bContext *C)
2184 Main *mainvar = CTX_data_main(C);
2185 Scene *scene = CTX_data_scene(C);
2186 ViewLayer *view_layer = CTX_data_view_layer(C);
2187 ARegion *ar = CTX_wm_region(C);
2188 View2D *v2d = &ar->v2d;
2189 SpaceOops *soops = CTX_wm_space_outliner(C);
2191 int sizey = 0, sizex = 0, sizex_rna = 0;
2192 TreeElement *te_edit = NULL;
2193 bool has_restrict_icons;
2195 outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always
2197 /* get extents of data */
2198 outliner_height(soops, &soops->tree, &sizey);
2200 /* extend size to allow for horizontal scrollbar */
2201 sizey += V2D_SCROLL_HEIGHT;
2203 if (soops->outlinevis == SO_DATA_API) {
2204 /* RNA has two columns:
2205 * - column 1 is (max_width + OL_RNA_COL_SPACEX) or
2206 * (OL_RNA_COL_X), whichever is wider...
2207 * - column 2 is fixed at OL_RNA_COL_SIZEX
2209 * (*) XXX max width for now is a fixed factor of (UI_UNIT_X * (max_indention + 100))
2212 /* get actual width of column 1 */
2213 outliner_rna_width(soops, &soops->tree, &sizex_rna, 0);
2214 sizex_rna = max_ii(OL_RNA_COLX, sizex_rna + OL_RNA_COL_SPACEX);
2216 /* get width of data (for setting 'tot' rect, this is column 1 + column 2 + a bit extra) */
2217 sizex = sizex_rna + OL_RNA_COL_SIZEX + 50;
2218 has_restrict_icons = false;
2221 /* width must take into account restriction columns (if visible)
2222 * so that entries will still be visible */
2223 //outliner_width(soops, &soops->tree, &sizex);
2224 // XXX should use outliner_width instead when te->xend will be set correctly...
2225 outliner_rna_width(soops, &soops->tree, &sizex, 0);
2227 /* constant offset for restriction columns */
2228 // XXX this isn't that great yet...
2229 if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0) {
2230 sizex += OL_TOGW * 3;
2233 has_restrict_icons = !(soops->flag & SO_HIDE_RESTRICTCOLS);
2236 /* adds vertical offset */
2237 sizey += OL_Y_OFFSET;
2239 /* update size of tot-rect (extents of data/viewable area) */
2240 UI_view2d_totRect_set(v2d, sizex, sizey);
2242 /* force display to pixel coords */
2243 v2d->flag |= (V2D_PIXELOFS_X | V2D_PIXELOFS_Y);
2244 /* set matrix for 2d-view controls */
2245 UI_view2d_view_ortho(v2d);
2247 /* draw outliner stuff (background, hierarchy lines and names) */
2249 block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
2251 (bContext *)C, block, scene, view_layer,
2252 ar, soops, has_restrict_icons, &te_edit);
2254 /* Default to no emboss for outliner UI. */
2255 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2257 if (soops->outlinevis == SO_DATA_API) {
2258 /* draw rna buttons */
2259 outliner_draw_rnacols(ar, sizex_rna);
2261 UI_block_emboss_set(block, UI_EMBOSS);
2262 outliner_draw_rnabuts(block, ar, soops, sizex_rna, &soops->tree);
2263 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2265 else if ((soops->outlinevis == SO_ID_ORPHANS) && has_restrict_icons) {
2266 /* draw user toggle columns */
2267 outliner_draw_restrictcols(ar);
2268 outliner_draw_userbuts(block, ar, soops, &soops->tree);
2270 else if (has_restrict_icons) {
2271 /* draw restriction columns */
2272 outliner_draw_restrictcols(ar);
2274 outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &soops->tree);
2277 UI_block_emboss_set(block, UI_EMBOSS);
2279 /* draw edit buttons if nessecery */
2281 outliner_buttons(C, block, ar, te_edit);
2284 UI_block_end(C, block);
2285 UI_block_draw(C, block);