2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2004 Blender Foundation.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Joshua Leung
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/space_outliner/outliner_draw.c
32 #include "DNA_anim_types.h"
33 #include "DNA_armature_types.h"
34 #include "DNA_collection_types.h"
35 #include "DNA_gpencil_types.h"
36 #include "DNA_gpencil_modifier_types.h"
37 #include "DNA_lamp_types.h"
38 #include "DNA_lightprobe_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_sequence_types.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_string_utils.h"
46 #include "BLI_utildefines.h"
47 #include "BLI_mempool.h"
49 #include "BLT_translation.h"
51 #include "BKE_context.h"
52 #include "BKE_deform.h"
53 #include "BKE_fcurve.h"
54 #include "BKE_global.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_idcode.h"
57 #include "BKE_layer.h"
58 #include "BKE_library.h"
60 #include "BKE_modifier.h"
61 #include "BKE_object.h"
62 #include "BKE_report.h"
63 #include "BKE_scene.h"
65 #include "DEG_depsgraph.h"
66 #include "DEG_depsgraph_build.h"
68 #include "ED_armature.h"
69 #include "ED_keyframing.h"
70 #include "ED_object.h"
71 #include "ED_screen.h"
76 #include "GPU_immediate.h"
77 #include "GPU_state.h"
79 #include "UI_interface.h"
80 #include "UI_interface_icons.h"
81 #include "UI_resources.h"
82 #include "UI_view2d.h"
84 #include "RNA_access.h"
86 #include "outliner_intern.h"
88 /* disable - this is far too slow - campbell */
89 // #define USE_GROUP_SELECT
91 /* ****************************************************** */
92 /* Tree Size Functions */
94 static void outliner_height(SpaceOops *soops, ListBase *lb, int *h)
96 TreeElement *te = lb->first;
98 TreeStoreElem *tselem = TREESTORE(te);
99 if (TSELEM_OPEN(tselem, soops)) {
100 outliner_height(soops, &te->subtree, h);
107 #if 0 // XXX this is currently disabled until te->xend is set correctly
108 static void outliner_width(SpaceOops *soops, ListBase *lb, int *w)
110 TreeElement *te = lb->first;
112 // TreeStoreElem *tselem = TREESTORE(te);
114 // XXX fixme... te->xend is not set yet
115 if (!TSELEM_OPEN(tselem, soops)) {
119 outliner_width(soops, &te->subtree, w);
125 static void outliner_rna_width(SpaceOops *soops, ListBase *lb, int *w, int startx)
127 TreeElement *te = lb->first;
129 TreeStoreElem *tselem = TREESTORE(te);
130 // XXX fixme... (currently, we're using a fixed length of 100)!
137 if (startx + 100 > *w)
140 if (TSELEM_OPEN(tselem, soops)) {
141 outliner_rna_width(soops, &te->subtree, w, startx + UI_UNIT_X);
148 * The active object is only needed for reference.
150 static bool is_object_data_in_editmode(const ID *id, const Object *obact)
152 const short id_type = GS(id->name);
154 (obact && (obact->mode & OB_MODE_EDIT)) &&
155 (id && OB_DATA_SUPPORT_EDITMODE(id_type)) &&
156 (GS(((ID *)obact->data)->name) == id_type) &&
157 BKE_object_data_is_in_editmode(id)
161 /* ****************************************************** */
163 static void restrictbutton_recursive_ebone(bContext *C, EditBone *ebone_parent, int flag, bool set_flag)
165 Object *obedit = CTX_data_edit_object(C);
166 bArmature *arm = obedit->data;
169 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
170 if (ED_armature_ebone_is_child_recursive(ebone_parent, ebone)) {
172 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
176 ebone->flag &= ~flag;
182 static void restrictbutton_recursive_bone(Bone *bone_parent, int flag, bool set_flag)
185 for (bone = bone_parent->childbase.first; bone; bone = bone->next) {
187 bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
193 restrictbutton_recursive_bone(bone, flag, set_flag);
198 static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2))
200 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin);
203 static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
205 Bone *bone = (Bone *)poin2;
206 if (bone->flag & BONE_HIDDEN_P)
207 bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
209 if (CTX_wm_window(C)->eventstate->ctrl) {
210 restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0);
213 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
216 static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
218 Bone *bone = (Bone *)poin2;
219 if (bone->flag & BONE_UNSELECTABLE)
220 bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
222 if (CTX_wm_window(C)->eventstate->ctrl) {
223 restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0);
226 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
229 static void restrictbutton_ebone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
231 EditBone *ebone = (EditBone *)poin2;
233 if (ebone->flag & BONE_UNSELECTABLE) {
234 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
237 if (CTX_wm_window(C)->eventstate->ctrl) {
238 restrictbutton_recursive_ebone(C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0);
241 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
244 static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
246 EditBone *ebone = (EditBone *)poin2;
247 if (ebone->flag & BONE_HIDDEN_A) {
248 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
251 if (CTX_wm_window(C)->eventstate->ctrl) {
252 restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0);
255 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
258 static void restrictbutton_gp_layer_flag_cb(bContext *C, void *UNUSED(poin), void *UNUSED(poin2))
260 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
263 static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2))
267 BLI_assert(id != NULL);
269 if (id->flag & LIB_FAKEUSER) {
277 static void hidebutton_base_flag_cb(bContext *C, void *poin, void *poin2)
279 Scene *scene = CTX_data_scene(C);
280 ViewLayer *view_layer = poin;
282 bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0);
284 /* Undo button toggle, let function do it. */
285 base->flag ^= BASE_HIDDEN;
287 BKE_base_set_visible(scene, view_layer, base, extend);
289 if (!extend && (base->flag & BASE_VISIBLE)) {
290 /* Auto select solo-ed object. */
291 ED_object_base_select(base, BA_SELECT);
292 view_layer->basact = base;
295 DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
296 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
299 static void hidebutton_layer_collection_flag_cb(bContext *C, void *poin, void *poin2)
301 Scene *scene = CTX_data_scene(C);
302 ViewLayer *view_layer = poin;
303 LayerCollection *lc = poin2;
304 bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0);
306 /* Undo button toggle, let function do it. */
307 lc->runtime_flag ^= LAYER_COLLECTION_HAS_VISIBLE_OBJECTS;
309 BKE_layer_collection_set_visible(scene, view_layer, lc, extend);
311 DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
312 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
315 static void namebutton_cb(bContext *C, void *tsep, char *oldname)
317 Main *bmain = CTX_data_main(C);
318 SpaceOops *soops = CTX_wm_space_outliner(C);
319 Object *obedit = CTX_data_edit_object(C);
320 BLI_mempool *ts = soops->treestore;
321 TreeStoreElem *tselem = tsep;
324 TreeElement *te = outliner_find_tree_element(&soops->tree, tselem);
326 if (tselem->type == 0) {
327 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
329 switch (GS(tselem->id->name)) {
331 WM_event_add_notifier(C, NC_MATERIAL, NULL); break;
333 WM_event_add_notifier(C, NC_TEXTURE, NULL); break;
335 WM_event_add_notifier(C, NC_IMAGE, NULL); break;
337 WM_event_add_notifier(C, NC_SCENE, NULL); break;
340 Object *ob = (Object *)tselem->id;
341 if (ob->type == OB_MBALL) {
342 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
344 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break;
347 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break;
349 /* Check the library target exists */
350 if (te->idcode == ID_LI) {
351 Library *lib = (Library *)tselem->id;
352 char expanded[FILE_MAX];
354 BKE_library_filepath_set(bmain, lib, lib->name);
356 BLI_strncpy(expanded, lib->name, sizeof(expanded));
357 BLI_path_abs(expanded, BKE_main_blendfile_path(bmain));
358 if (!BLI_exists(expanded)) {
359 BKE_reportf(CTX_wm_reports(C), RPT_ERROR,
360 "Library path '%s' does not exist, correct this before saving", expanded);
362 else if (lib->id.tag & LIB_TAG_MISSING) {
363 BKE_reportf(CTX_wm_reports(C), RPT_INFO,
364 "Library path '%s' is now valid, please reload the library", expanded);
365 lib->id.tag &= ~LIB_TAG_MISSING;
370 switch (tselem->type) {
372 defgroup_unique_name(te->directdata, (Object *)tselem->id); // id = object
375 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
379 bArmature *arm = (bArmature *)tselem->id;
381 EditBone *ebone = te->directdata;
382 char newname[sizeof(ebone->name)];
384 /* restore bone name */
385 BLI_strncpy(newname, ebone->name, sizeof(ebone->name));
386 BLI_strncpy(ebone->name, oldname, sizeof(ebone->name));
387 ED_armature_bone_rename(bmain, obedit->data, oldname, newname);
388 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
395 ViewLayer *view_layer = CTX_data_view_layer(C);
396 Scene *scene = CTX_data_scene(C);
397 bArmature *arm = (bArmature *)tselem->id;
398 Bone *bone = te->directdata;
399 char newname[sizeof(bone->name)];
401 /* always make current object active */
402 tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
404 /* restore bone name */
405 BLI_strncpy(newname, bone->name, sizeof(bone->name));
406 BLI_strncpy(bone->name, oldname, sizeof(bone->name));
407 ED_armature_bone_rename(bmain, arm, oldname, newname);
408 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
411 case TSE_POSE_CHANNEL:
413 Scene *scene = CTX_data_scene(C);
414 ViewLayer *view_layer = CTX_data_view_layer(C);
415 Object *ob = (Object *)tselem->id;
416 bPoseChannel *pchan = te->directdata;
417 char newname[sizeof(pchan->name)];
419 /* always make current pose-bone active */
420 tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
422 BLI_assert(ob->type == OB_ARMATURE);
424 /* restore bone name */
425 BLI_strncpy(newname, pchan->name, sizeof(pchan->name));
426 BLI_strncpy(pchan->name, oldname, sizeof(pchan->name));
427 ED_armature_bone_rename(bmain, ob->data, oldname, newname);
428 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
433 Object *ob = (Object *)tselem->id; // id = object
434 bActionGroup *grp = te->directdata;
436 BLI_uniquename(&ob->pose->agroups, grp, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), '.',
437 offsetof(bActionGroup, name), sizeof(grp->name));
438 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
443 bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */
444 bGPDlayer *gpl = te->directdata;
446 /* always make layer active */
447 BKE_gpencil_layer_setactive(gpd, gpl);
449 // XXX: name needs translation stuff
450 BLI_uniquename(&gpd->layers, gpl, "GP Layer", '.',
451 offsetof(bGPDlayer, info), sizeof(gpl->info));
453 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, gpd);
458 Scene *scene = (Scene *)tselem->id;
459 ViewLayer *view_layer = te->directdata;
461 /* Restore old name. */
462 char newname[sizeof(view_layer->name)];
463 BLI_strncpy(newname, view_layer->name, sizeof(view_layer->name));
464 BLI_strncpy(view_layer->name, oldname, sizeof(view_layer->name));
466 /* Rename, preserving animation and compositing data. */
467 BKE_view_layer_rename(bmain, scene, view_layer, newname);
468 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
471 case TSE_LAYER_COLLECTION:
473 BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
474 WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
479 tselem->flag &= ~TSE_TEXTBUT;
483 static void outliner_draw_restrictbuts(
484 uiBlock *block, Scene *scene, ViewLayer *view_layer, ARegion *ar, SpaceOops *soops, ListBase *lb)
486 /* Get RNA properties (once for speed). */
487 static struct RestrictProperties {
490 PropertyRNA *object_hide_viewport, *object_hide_select, *object_hide_render;
491 PropertyRNA *collection_hide_viewport, *collection_hide_select, *collection_hide_render;
492 PropertyRNA *modifier_show_viewport, *modifier_show_render;
495 if (!props.initialized) {
496 props.object_hide_viewport = RNA_struct_type_find_property(&RNA_Object, "hide_viewport");
497 props.object_hide_select = RNA_struct_type_find_property(&RNA_Object, "hide_select");
498 props.object_hide_render = RNA_struct_type_find_property(&RNA_Object, "hide_render");
499 props.collection_hide_select = RNA_struct_type_find_property(&RNA_Collection, "hide_select");
500 props.collection_hide_viewport = RNA_struct_type_find_property(&RNA_Collection, "hide_viewport");
501 props.collection_hide_render = RNA_struct_type_find_property(&RNA_Collection, "hide_render");
502 props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport");
503 props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render");
505 props.initialized = true;
508 /* Create buttons. */
511 for (TreeElement *te = lb->first; te; te = te->next) {
512 TreeStoreElem *tselem = TREESTORE(te);
513 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
514 if (tselem->type == TSE_R_LAYER && (soops->outlinevis == SO_SCENES)) {
515 /* View layer render toggle. */
516 ViewLayer *layer = te->directdata;
518 bt = uiDefIconButBitS(
519 block, UI_BTYPE_ICON_TOGGLE_N, VIEW_LAYER_RENDER, 0, ICON_RESTRICT_RENDER_OFF,
520 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X,
521 UI_UNIT_Y, &layer->flag, 0, 0, 0, 0, TIP_("Use view layer for rendering"));
522 UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL);
523 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
524 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
526 else if (tselem->type == 0 && te->idcode == ID_OB) {
527 Object *ob = (Object *)tselem->id;
528 Base *base = BKE_view_layer_base_find(view_layer, ob);
531 bt = uiDefIconButBitS(
532 block, UI_BTYPE_ICON_TOGGLE, BASE_HIDDEN, 0, ICON_HIDE_OFF,
533 (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X,
534 UI_UNIT_Y, &base->flag, 0, 0, 0, 0,
535 TIP_("Hide object in viewport (Ctrl to isolate)"));
536 UI_but_func_set(bt, hidebutton_base_flag_cb, view_layer, base);
537 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
538 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
542 RNA_pointer_create(&ob->id, &RNA_Object, ob, &ptr);
544 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
545 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
546 &ptr, props.object_hide_viewport, -1, 0, 0, -1, -1, NULL);
547 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
549 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
550 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, UI_UNIT_Y,
551 &ptr, props.object_hide_select, -1, 0, 0, -1, -1, NULL);
552 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
554 bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
555 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
556 &ptr, props.object_hide_render, -1, 0, 0, -1, -1, NULL);
557 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
560 else if (tselem->type == TSE_MODIFIER) {
561 ModifierData *md = (ModifierData *)te->directdata;
564 RNA_pointer_create(tselem->id, &RNA_Modifier, md, &ptr);
566 bt = uiDefIconButR_prop(
567 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
568 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
569 &ptr, props.modifier_show_viewport, -1, 0, 0, -1, -1, NULL);
570 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
572 bt = uiDefIconButR_prop(
573 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
574 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
575 &ptr, props.modifier_show_render, -1, 0, 0, -1, -1, NULL);
576 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
578 else if (tselem->type == TSE_POSE_CHANNEL) {
579 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
580 Bone *bone = pchan->bone;
581 Object *ob = (Object *)tselem->id;
583 bt = uiDefIconButBitI(
584 block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_P, 0, ICON_HIDE_OFF,
585 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
586 UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0,
587 TIP_("Restrict/Allow visibility in the 3D View"));
588 UI_but_func_set(bt, restrictbutton_bone_visibility_cb, ob->data, bone);
589 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
590 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
592 bt = uiDefIconButBitI(
593 block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF,
594 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
595 UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0,
596 TIP_("Restrict/Allow selection in the 3D View"));
597 UI_but_func_set(bt, restrictbutton_bone_select_cb, ob->data, bone);
598 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
599 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
601 else if (tselem->type == TSE_EBONE) {
602 EditBone *ebone = (EditBone *)te->directdata;
604 bt = uiDefIconButBitI(
605 block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_A, 0, ICON_RESTRICT_VIEW_OFF,
606 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
607 UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0,
608 TIP_("Restrict/Allow visibility in the 3D View"));
609 UI_but_func_set(bt, restrictbutton_ebone_visibility_cb, NULL, ebone);
610 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
611 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
613 bt = uiDefIconButBitI(
614 block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF,
615 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
616 UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0,
617 TIP_("Restrict/Allow selection in the 3D View"));
618 UI_but_func_set(bt, restrictbutton_ebone_select_cb, NULL, ebone);
619 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
620 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
622 else if (tselem->type == TSE_GP_LAYER) {
623 bGPDlayer *gpl = (bGPDlayer *)te->directdata;
625 bt = uiDefIconButBitS(
626 block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_HIDE, 0, ICON_HIDE_OFF,
627 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
628 UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0,
629 TIP_("Restrict/Allow visibility in the 3D View"));
630 UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl);
631 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
632 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
634 bt = uiDefIconButBitS(
635 block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_LOCKED, 0, ICON_UNLOCKED,
636 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
637 UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0,
638 TIP_("Restrict/Allow editing of strokes and keyframes in this layer"));
639 UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl);
640 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
642 /* TODO: visibility in renders */
644 else if (outliner_is_collection_tree_element(te)) {
645 LayerCollection *lc = (tselem->type == TSE_LAYER_COLLECTION) ? te->directdata : NULL;
646 Collection *collection = outliner_collection_from_tree_element(te);
648 if ((!lc || !(lc->flag & LAYER_COLLECTION_EXCLUDE)) &&
649 !(collection->flag & COLLECTION_IS_MASTER))
651 if (lc && (lc->runtime_flag & LAYER_COLLECTION_HAS_ENABLED_OBJECTS)) {
652 bt = uiDefIconButBitS(
653 block, UI_BTYPE_ICON_TOGGLE_N, LAYER_COLLECTION_HAS_VISIBLE_OBJECTS, 0, ICON_HIDE_OFF,
654 (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X,
655 UI_UNIT_Y, &lc->runtime_flag, 0, 0, 0, 0,
656 TIP_("Hide collection in viewport (Ctrl to isolate)"));
657 UI_but_func_set(bt, hidebutton_layer_collection_flag_cb, view_layer, lc);
658 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
659 UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
662 PointerRNA collection_ptr;
663 RNA_id_pointer_create(&collection->id, &collection_ptr);
665 bt = uiDefIconButR_prop(
666 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
667 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
668 UI_UNIT_Y, &collection_ptr, props.collection_hide_viewport, -1, 0, 0, 0, 0, NULL);
669 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
671 bt = uiDefIconButR_prop(
672 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
673 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X,
674 UI_UNIT_Y, &collection_ptr, props.collection_hide_render, -1, 0, 0, 0, 0, NULL);
675 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
677 bt = uiDefIconButR_prop(
678 block, UI_BTYPE_ICON_TOGGLE, 0, 0,
679 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X,
680 UI_UNIT_Y, &collection_ptr, props.collection_hide_select, -1, 0, 0, 0, 0, NULL);
681 UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
686 if (TSELEM_OPEN(tselem, soops)) {
687 outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &te->subtree);
692 static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops, ListBase *lb)
695 for (TreeElement *te = lb->first; te; te = te->next) {
696 TreeStoreElem *tselem = TREESTORE(te);
697 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
698 if (tselem->type == 0) {
701 const char *tip = NULL;
702 int icon = ICON_NONE;
704 int but_flag = UI_BUT_DRAG_LOCK;
706 if (ID_IS_LINKED(id))
707 but_flag |= UI_BUT_DISABLED;
709 if (id->flag & LIB_FAKEUSER) {
710 icon = ICON_FILE_TICK;
711 tip = TIP_("Data-block will be retained using a fake user");
715 tip = TIP_("Data-block has no users and will be deleted");
717 bt = uiDefIconButBitS(
718 block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, icon,
719 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
720 &id->flag, 0, 0, 0, 0, tip);
721 UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
722 UI_but_flag_enable(bt, but_flag);
725 BLI_str_format_int_grouped(buf, id->us);
727 block, UI_BTYPE_BUT, 1, buf,
728 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys,
729 UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0,
730 TIP_("Number of users of this data-block"));
731 UI_but_flag_enable(bt, but_flag);
735 block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, (id->flag & LIB_FAKEUSER) ? "F" : " ",
736 (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y,
737 &id->flag, 0, 0, 0, 0,
738 TIP_("Data-block has a 'fake' user which will keep it in the file "
739 "even if nothing else uses it"));
740 UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
741 UI_but_flag_enable(bt, but_flag);
745 if (TSELEM_OPEN(tselem, soops)) {
746 outliner_draw_userbuts(block, ar, soops, &te->subtree);
751 static void outliner_draw_rnacols(ARegion *ar, int sizex)
753 View2D *v2d = &ar->v2d;
755 float miny = v2d->cur.ymin;
756 if (miny < v2d->tot.ymin) miny = v2d->tot.ymin;
758 GPU_line_width(1.0f);
760 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
761 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
762 immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
764 immBegin(GPU_PRIM_LINES, 4);
766 immVertex2f(pos, sizex, v2d->cur.ymax);
767 immVertex2f(pos, sizex, miny);
769 immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax);
770 immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny);
777 static void outliner_draw_rnabuts(uiBlock *block, ARegion *ar, SpaceOops *soops, int sizex, ListBase *lb)
782 for (TreeElement *te = lb->first; te; te = te->next) {
783 TreeStoreElem *tselem = TREESTORE(te);
784 if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
785 if (tselem->type == TSE_RNA_PROPERTY) {
787 prop = te->directdata;
789 if (!TSELEM_OPEN(tselem, soops)) {
790 if (RNA_property_type(prop) == PROP_POINTER) {
791 uiBut *but = uiDefAutoButR(
792 block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys,
793 OL_RNA_COL_SIZEX, UI_UNIT_Y - 1);
794 UI_but_flag_enable(but, UI_BUT_DISABLED);
796 else if (RNA_property_type(prop) == PROP_ENUM) {
798 block, ptr, prop, -1, NULL, ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
803 block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
808 else if (tselem->type == TSE_RNA_ARRAY_ELEM) {
810 prop = te->directdata;
813 block, ptr, prop, te->index, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX,
818 if (TSELEM_OPEN(tselem, soops)) {
819 outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree);
824 static void outliner_buttons(const bContext *C, uiBlock *block, ARegion *ar, TreeElement *te)
827 TreeStoreElem *tselem;
830 tselem = TREESTORE(te);
832 BLI_assert(tselem->flag & TSE_TEXTBUT);
833 /* If we add support to rename Sequence.
837 if (tselem->type == TSE_EBONE) len = sizeof(((EditBone *) 0)->name);
838 else if (tselem->type == TSE_MODIFIER) len = sizeof(((ModifierData *) 0)->name);
839 else if (tselem->id && GS(tselem->id->name) == ID_LI) len = sizeof(((Library *) 0)->name);
840 else len = MAX_ID_NAME - 2;
842 spx = te->xs + 1.8f * UI_UNIT_X;
843 dx = ar->v2d.cur.xmax - (spx + 3.2f * UI_UNIT_X);
846 block, UI_BTYPE_TEXT, OL_NAMEBUTTON, "", spx, te->ys, dx, UI_UNIT_Y - 1, (void *)te->name,
847 1.0, (float)len, 0, 0, "");
848 UI_but_func_rename_set(bt, namebutton_cb, tselem);
850 /* returns false if button got removed */
851 if (false == UI_but_active_only(C, ar, block, bt)) {
852 tselem->flag &= ~TSE_TEXTBUT;
854 /* bad! (notifier within draw) without this, we don't get a refresh */
855 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
859 /* ****************************************************** */
860 /* Normal Drawing... */
862 TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
864 TreeElementIcon data = {0};
867 switch (tselem->type) {
869 data.icon = ICON_ANIM_DATA; /* XXX */
872 data.icon = ICON_NLA;
875 data.icon = ICON_NLA; /* XXX */
878 data.icon = ICON_ACTION;
880 case TSE_DRIVER_BASE:
881 data.icon = ICON_DRIVER;
883 case TSE_DEFGROUP_BASE:
884 data.icon = ICON_GROUP_VERTEX;
888 data.icon = ICON_BONE_DATA;
890 case TSE_CONSTRAINT_BASE:
891 data.icon = ICON_CONSTRAINT;
893 case TSE_MODIFIER_BASE:
894 data.icon = ICON_MODIFIER;
897 data.icon = ICON_OBJECT_DATA;
899 case TSE_LINKED_PSYS:
900 data.icon = ICON_PARTICLES;
904 Object *ob = (Object *)tselem->id;
905 if (ob->type != OB_GPENCIL) {
906 ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr);
907 switch ((ModifierType)md->type) {
908 case eModifierType_Subsurf:
909 data.icon = ICON_MOD_SUBSURF;
911 case eModifierType_Armature:
912 data.icon = ICON_MOD_ARMATURE;
914 case eModifierType_Lattice:
915 data.icon = ICON_MOD_LATTICE;
917 case eModifierType_Curve:
918 data.icon = ICON_MOD_CURVE;
920 case eModifierType_Build:
921 data.icon = ICON_MOD_BUILD;
923 case eModifierType_Mirror:
924 data.icon = ICON_MOD_MIRROR;
926 case eModifierType_Decimate:
927 data.icon = ICON_MOD_DECIM;
929 case eModifierType_Wave:
930 data.icon = ICON_MOD_WAVE;
932 case eModifierType_Hook:
933 data.icon = ICON_HOOK;
935 case eModifierType_Softbody:
936 data.icon = ICON_MOD_SOFT;
938 case eModifierType_Boolean:
939 data.icon = ICON_MOD_BOOLEAN;
941 case eModifierType_ParticleSystem:
942 data.icon = ICON_MOD_PARTICLES;
944 case eModifierType_ParticleInstance:
945 data.icon = ICON_MOD_PARTICLES;
947 case eModifierType_EdgeSplit:
948 data.icon = ICON_MOD_EDGESPLIT;
950 case eModifierType_Array:
951 data.icon = ICON_MOD_ARRAY;
953 case eModifierType_UVProject:
954 case eModifierType_UVWarp: /* TODO, get own icon */
955 data.icon = ICON_MOD_UVPROJECT;
957 case eModifierType_Displace:
958 data.icon = ICON_MOD_DISPLACE;
960 case eModifierType_Shrinkwrap:
961 data.icon = ICON_MOD_SHRINKWRAP;
963 case eModifierType_Cast:
964 data.icon = ICON_MOD_CAST;
966 case eModifierType_MeshDeform:
967 case eModifierType_SurfaceDeform:
968 data.icon = ICON_MOD_MESHDEFORM;
970 case eModifierType_Bevel:
971 data.icon = ICON_MOD_BEVEL;
973 case eModifierType_Smooth:
974 case eModifierType_LaplacianSmooth:
975 case eModifierType_CorrectiveSmooth:
976 data.icon = ICON_MOD_SMOOTH;
978 case eModifierType_SimpleDeform:
979 data.icon = ICON_MOD_SIMPLEDEFORM;
981 case eModifierType_Mask:
982 data.icon = ICON_MOD_MASK;
984 case eModifierType_Cloth:
985 data.icon = ICON_MOD_CLOTH;
987 case eModifierType_Explode:
988 data.icon = ICON_MOD_EXPLODE;
990 case eModifierType_Collision:
991 case eModifierType_Surface:
992 data.icon = ICON_MOD_PHYSICS;
994 case eModifierType_Fluidsim:
995 data.icon = ICON_MOD_FLUIDSIM;
997 case eModifierType_Multires:
998 data.icon = ICON_MOD_MULTIRES;
1000 case eModifierType_Smoke:
1001 data.icon = ICON_MOD_SMOKE;
1003 case eModifierType_Solidify:
1004 data.icon = ICON_MOD_SOLIDIFY;
1006 case eModifierType_Screw:
1007 data.icon = ICON_MOD_SCREW;
1009 case eModifierType_Remesh:
1010 data.icon = ICON_MOD_REMESH;
1012 case eModifierType_WeightVGEdit:
1013 case eModifierType_WeightVGMix:
1014 case eModifierType_WeightVGProximity:
1015 data.icon = ICON_MOD_VERTEX_WEIGHT;
1017 case eModifierType_DynamicPaint:
1018 data.icon = ICON_MOD_DYNAMICPAINT;
1020 case eModifierType_Ocean:
1021 data.icon = ICON_MOD_OCEAN;
1023 case eModifierType_Warp:
1024 data.icon = ICON_MOD_WARP;
1026 case eModifierType_Skin:
1027 data.icon = ICON_MOD_SKIN;
1029 case eModifierType_Triangulate:
1030 data.icon = ICON_MOD_TRIANGULATE;
1032 case eModifierType_MeshCache:
1033 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1035 case eModifierType_MeshSequenceCache:
1036 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1038 case eModifierType_Wireframe:
1039 data.icon = ICON_MOD_WIREFRAME;
1041 case eModifierType_LaplacianDeform:
1042 data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1044 case eModifierType_DataTransfer:
1045 data.icon = ICON_MOD_DATA_TRANSFER;
1047 case eModifierType_NormalEdit:
1048 case eModifierType_WeightedNormal:
1049 data.icon = ICON_MOD_NORMALEDIT;
1052 case eModifierType_None:
1053 case eModifierType_ShapeKey:
1055 case NUM_MODIFIER_TYPES:
1056 data.icon = ICON_DOT;
1061 /* grease pencil modifiers */
1062 GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr);
1063 switch ((GpencilModifierType)md->type) {
1064 case eGpencilModifierType_Noise:
1065 data.icon = ICON_RNDCURVE;
1067 case eGpencilModifierType_Subdiv:
1068 data.icon = ICON_MOD_SUBSURF;
1070 case eGpencilModifierType_Thick:
1071 data.icon = ICON_MOD_THICKNESS;
1073 case eGpencilModifierType_Tint:
1074 data.icon = ICON_MOD_TINT;
1076 case eGpencilModifierType_Array:
1077 data.icon = ICON_MOD_ARRAY;
1079 case eGpencilModifierType_Build:
1080 data.icon = ICON_MOD_BUILD;
1082 case eGpencilModifierType_Opacity:
1083 data.icon = ICON_MOD_MASK;
1085 case eGpencilModifierType_Color:
1086 data.icon = ICON_MOD_HUE_SATURATION;
1088 case eGpencilModifierType_Lattice:
1089 data.icon = ICON_MOD_LATTICE;
1091 case eGpencilModifierType_Mirror:
1092 data.icon = ICON_MOD_MIRROR;
1094 case eGpencilModifierType_Simplify:
1095 data.icon = ICON_MOD_SIMPLIFY;
1097 case eGpencilModifierType_Smooth:
1098 data.icon = ICON_MOD_SMOOTH;
1100 case eGpencilModifierType_Hook:
1101 data.icon = ICON_HOOK;
1103 case eGpencilModifierType_Offset:
1104 data.icon = ICON_MOD_OFFSET;
1106 case eGpencilModifierType_Armature:
1107 data.icon = ICON_MOD_ARMATURE;
1112 data.icon = ICON_DOT;
1119 data.icon = ICON_ARMATURE_DATA;
1121 case TSE_POSE_CHANNEL:
1122 data.icon = ICON_BONE_DATA;
1125 data.icon = ICON_GHOST_ENABLED;
1127 case TSE_R_LAYER_BASE:
1128 data.icon = ICON_RENDERLAYERS;
1130 case TSE_SCENE_OBJECTS_BASE:
1131 data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
1134 data.icon = ICON_RENDER_RESULT;
1136 case TSE_LINKED_LAMP:
1137 data.icon = ICON_LIGHT_DATA;
1139 case TSE_LINKED_MAT:
1140 data.icon = ICON_MATERIAL_DATA;
1142 case TSE_POSEGRP_BASE:
1143 data.icon = ICON_GROUP_BONE;
1146 if (te->idcode == SEQ_TYPE_MOVIE)
1147 data.icon = ICON_SEQUENCE;
1148 else if (te->idcode == SEQ_TYPE_META)
1149 data.icon = ICON_DOT;
1150 else if (te->idcode == SEQ_TYPE_SCENE)
1151 data.icon = ICON_SCENE;
1152 else if (te->idcode == SEQ_TYPE_SOUND_RAM)
1153 data.icon = ICON_SOUND;
1154 else if (te->idcode == SEQ_TYPE_IMAGE)
1155 data.icon = ICON_IMAGE;
1157 data.icon = ICON_PARTICLES;
1160 data.icon = ICON_LIBRARY_DATA_DIRECT;
1162 case TSE_SEQUENCE_DUP:
1163 data.icon = ICON_OBJECT_DATA;
1165 case TSE_RNA_STRUCT:
1166 if (RNA_struct_is_ID(te->rnaptr.type)) {
1167 data.drag_id = (ID *)te->rnaptr.data;
1168 data.icon = RNA_struct_ui_icon(te->rnaptr.type);
1171 data.icon = RNA_struct_ui_icon(te->rnaptr.type);
1174 case TSE_LAYER_COLLECTION:
1175 case TSE_SCENE_COLLECTION_BASE:
1176 case TSE_VIEW_COLLECTION_BASE:
1178 Collection *collection = outliner_collection_from_tree_element(te);
1179 if (collection && !(collection->flag & COLLECTION_IS_MASTER)) {
1180 data.drag_id = tselem->id;
1181 data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
1184 data.icon = ICON_GROUP;
1187 /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */
1190 /* indicate whether layer is active */
1191 bGPDlayer *gpl = te->directdata;
1192 if (gpl->flag & GP_LAYER_ACTIVE) {
1193 data.icon = ICON_GREASEPENCIL;
1196 data.icon = ICON_DOT;
1201 data.icon = ICON_DOT;
1205 else if (tselem->id) {
1206 data.drag_id = tselem->id;
1207 data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
1209 if (GS(tselem->id->name) == ID_OB) {
1210 Object *ob = (Object *)tselem->id;
1213 data.icon = ICON_OUTLINER_OB_LIGHT; break;
1215 data.icon = ICON_OUTLINER_OB_MESH; break;
1217 data.icon = ICON_OUTLINER_OB_CAMERA; break;
1219 data.icon = ICON_OUTLINER_OB_CURVE; break;
1221 data.icon = ICON_OUTLINER_OB_META; break;
1223 data.icon = ICON_OUTLINER_OB_LATTICE; break;
1225 data.icon = ICON_OUTLINER_OB_ARMATURE; break;
1227 data.icon = ICON_OUTLINER_OB_FONT; break;
1229 data.icon = ICON_OUTLINER_OB_SURFACE; break;
1231 data.icon = ICON_OUTLINER_OB_SPEAKER; break;
1233 data.icon = ICON_OUTLINER_OB_LIGHTPROBE; break;
1235 if (ob->dup_group) {
1236 data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
1238 else if (ob->empty_drawtype == OB_EMPTY_IMAGE) {
1239 data.icon = ICON_OUTLINER_OB_IMAGE;
1242 data.icon = ICON_OUTLINER_OB_EMPTY;
1246 data.icon = ICON_OUTLINER_OB_GREASEPENCIL; break;
1251 /* TODO(sergey): Casting to short here just to handle ID_NLA which is
1252 * NOT inside of IDType enum.
1254 switch ((short)GS(tselem->id->name)) {
1256 data.icon = ICON_SCENE_DATA; break;
1258 data.icon = ICON_OUTLINER_DATA_MESH; break;
1260 data.icon = ICON_OUTLINER_DATA_CURVE; break;
1262 data.icon = ICON_OUTLINER_DATA_META; break;
1264 data.icon = ICON_OUTLINER_DATA_LATTICE; break;
1267 Lamp *la = (Lamp *)tselem->id;
1270 data.icon = ICON_LIGHT_POINT; break;
1272 data.icon = ICON_LIGHT_SUN; break;
1274 data.icon = ICON_LIGHT_SPOT; break;
1276 data.icon = ICON_LIGHT_AREA; break;
1278 data.icon = ICON_OUTLINER_DATA_LIGHT; break;
1283 data.icon = ICON_MATERIAL_DATA; break;
1285 data.icon = ICON_TEXTURE_DATA; break;
1287 data.icon = ICON_IMAGE_DATA; break;
1290 data.icon = ICON_OUTLINER_DATA_SPEAKER; break;
1292 data.icon = ICON_OUTLINER_DATA_ARMATURE; break;
1294 data.icon = ICON_OUTLINER_DATA_CAMERA; break;
1296 data.icon = ICON_SHAPEKEY_DATA; break;
1298 data.icon = ICON_WORLD_DATA; break;
1300 data.icon = ICON_ACTION; break;
1302 data.icon = ICON_NLA; break;
1304 data.icon = ICON_SCRIPT; break;
1306 data.icon = ICON_GROUP; break;
1308 if (tselem->id->tag & LIB_TAG_MISSING) {
1309 data.icon = ICON_LIBRARY_DATA_BROKEN;
1311 else if (((Library *)tselem->id)->parent) {
1312 data.icon = ICON_LIBRARY_DATA_INDIRECT;
1315 data.icon = ICON_LIBRARY_DATA_DIRECT;
1319 data.icon = ICON_LINE_DATA; break;
1321 data.icon = ICON_OUTLINER_DATA_GREASEPENCIL; break;
1324 LightProbe *lp = (LightProbe *)tselem->id;
1326 case LIGHTPROBE_TYPE_CUBE:
1327 data.icon = ICON_LIGHTPROBE_CUBEMAP; break;
1328 case LIGHTPROBE_TYPE_PLANAR:
1329 data.icon = ICON_LIGHTPROBE_PLANAR; break;
1330 case LIGHTPROBE_TYPE_GRID:
1331 data.icon = ICON_LIGHTPROBE_GRID; break;
1333 data.icon = ICON_LIGHTPROBE_CUBEMAP; break;
1338 data.icon = ICON_BRUSH_DATA; break;
1341 data.icon = ICON_WORKSPACE; break;
1351 static void tselem_draw_icon(
1352 uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te,
1353 float alpha, const bool is_clickable)
1355 TreeElementIcon data = tree_element_get_icon(tselem, te);
1357 if (data.icon == 0) {
1361 if (!is_clickable || x >= xmax) {
1362 /* placement of icons, copied from interface_widgets.c */
1363 float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
1367 /* restrict column clip... it has been coded by simply overdrawing,
1368 * doesn't work for buttons */
1369 UI_icon_draw_alpha(x, y, data.icon, alpha);
1373 block, UI_BTYPE_LABEL, 0, data.icon, x, y, UI_UNIT_X, UI_UNIT_Y, NULL,
1374 0.0, 0.0, 1.0, alpha,
1375 (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->name : "");
1380 * For icon-only children of a collapsed tree,
1381 * Draw small number over the icon to show how many items of this type are displayed.
1383 static void outliner_draw_iconrow_number(
1384 const uiFontStyle *fstyle,
1386 const int num_elements)
1388 float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1389 float ufac = 0.25f * UI_UNIT_X;
1390 float offset_x = (float) offsx + UI_UNIT_X * 0.35f;
1392 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1393 UI_draw_roundbox_aa(true,
1395 (float)ys - UI_UNIT_Y * 0.2f + ufac,
1396 offset_x + UI_UNIT_X - ufac,
1397 (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac,
1398 (float)UI_UNIT_Y / 2.0f - ufac,
1401 /* Now the numbers. */
1402 unsigned char text_col[4];
1404 UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
1407 uiFontStyle fstyle_small = *fstyle;
1408 fstyle_small.points *= 0.8f;
1410 /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */
1412 char number_text[4] = "+99\0";
1413 if (num_elements < 100) {
1414 BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements);
1415 num_digits = num_elements < 10 ? 1 : 2;
1417 UI_fontstyle_draw_simple(&fstyle_small,
1418 (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f),
1419 (float)ys - UI_UNIT_Y * 0.095f + ufac,
1420 number_text, text_col);
1421 UI_fontstyle_set(fstyle);
1422 GPU_blend(true); /* Roundbox and text drawing disables. */
1425 static void outliner_draw_iconrow_doit(
1426 uiBlock *block, TreeElement *te,
1427 const uiFontStyle *fstyle,
1428 int xmax, int *offsx, int ys, float alpha_fac,
1429 const eOLDrawState active,
1430 const int num_elements)
1432 TreeStoreElem *tselem = TREESTORE(te);
1434 if (active != OL_DRAWSEL_NONE) {
1435 float ufac = UI_UNIT_X / 20.0f;
1436 float color[4] = {1.0f, 1.0f, 1.0f, 0.2f};
1438 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1439 color[3] *= alpha_fac;
1441 UI_draw_roundbox_aa(true,
1442 (float) *offsx + 1.0f * ufac,
1443 (float)ys + 1.0f * ufac,
1444 (float)*offsx + UI_UNIT_X - 1.0f * ufac,
1445 (float)ys + UI_UNIT_Y - ufac,
1446 (float)UI_UNIT_Y / 2.0f - ufac,
1448 GPU_blend(true); /* Roundbox disables. */
1451 /* No inlined icon should be clickable. */
1452 tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.8f * alpha_fac, false);
1455 te->xend = (short)*offsx + UI_UNIT_X;
1457 if (num_elements > 1) {
1458 outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements);
1460 (*offsx) += UI_UNIT_X;
1464 * Return the index to use based on the TreeElement ID and object type
1466 * We use a continuum of indices until we get to the object datablocks
1467 * and we then make room for the object types.
1469 static int tree_element_id_type_to_index(TreeElement *te)
1471 TreeStoreElem *tselem = TREESTORE(te);
1473 const int id_index = tselem->type == 0 ? BKE_idcode_to_index(te->idcode) : INDEX_ID_GR;
1474 if (id_index < INDEX_ID_OB) {
1477 else if (id_index == INDEX_ID_OB) {
1478 const Object *ob = (Object *)tselem->id;
1479 return INDEX_ID_OB + ob->type;
1482 return id_index + OB_TYPE_MAX;
1486 typedef struct MergedIconRow {
1487 eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX];
1488 int num_elements[INDEX_ID_MAX + OB_TYPE_MAX];
1489 TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX];
1492 static void outliner_draw_iconrow(
1493 bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer, SpaceOops *soops,
1494 ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac, MergedIconRow *merged)
1496 eOLDrawState active;
1497 const Object *obact = OBACT(view_layer);
1499 for (TreeElement *te = lb->first; te; te = te->next) {
1500 /* exit drawing early */
1501 if ((*offsx) - UI_UNIT_X > xmax)
1504 TreeStoreElem *tselem = TREESTORE(te);
1506 /* object hierarchy always, further constrained on level */
1507 if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) {
1508 /* active blocks get white circle */
1509 if (tselem->type == 0) {
1510 if (te->idcode == ID_OB) {
1511 active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
1513 else if (is_object_data_in_editmode(tselem->id, obact)) {
1514 active = OL_DRAWSEL_NORMAL;
1517 active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false);
1521 active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
1524 if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) {
1525 outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
1528 const int index = tree_element_id_type_to_index(te);
1529 merged->num_elements[index]++;
1530 if ((merged->tree_element[index] == NULL) ||
1531 (active > merged->active[index]))
1533 merged->tree_element[index] = te;
1535 merged->active[index] = MAX2(active, merged->active[index]);
1539 /* this tree element always has same amount of branches, so don't draw */
1540 if (tselem->type != TSE_R_LAYER) {
1541 outliner_draw_iconrow(
1542 C, block, fstyle, scene, view_layer, soops,
1543 &te->subtree, level + 1, xmax, offsx, ys, alpha_fac, merged);
1548 for (int i = 0; i < INDEX_ID_MAX; i++) {
1549 const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1;
1550 /* See tree_element_id_type_to_index for the index logic. */
1552 if (i > INDEX_ID_OB) {
1553 index_base += OB_TYPE_MAX;
1555 for (int j = 0; j < num_subtypes; j++) {
1556 const int index = index_base + j;
1557 if (merged->num_elements[index] != 0) {
1558 outliner_draw_iconrow_doit(block,
1559 merged->tree_element[index],
1561 xmax, offsx, ys, alpha_fac,
1562 merged->active[index],
1563 merged->num_elements[index]);
1570 /* closed tree element */
1571 static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty)
1575 /* closed items may be displayed in row of parent, don't change their coordinate! */
1576 if ((te->flag & TE_ICONROW) == 0) {
1577 /* store coord and continue, we need coordinates for elements outside view too */
1582 for (ten = te->subtree.first; ten; ten = ten->next) {
1583 outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty);
1588 static void outliner_draw_tree_element(
1589 bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer,
1590 ARegion *ar, SpaceOops *soops, TreeElement *te, bool draw_grayed_out,
1591 int startx, int *starty, TreeElement **te_edit)
1593 TreeStoreElem *tselem;
1594 float ufac = UI_UNIT_X / 20.0f;
1596 eOLDrawState active = OL_DRAWSEL_NONE;
1598 tselem = TREESTORE(te);
1600 if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) {
1601 const float alpha_fac = ((te->flag & TE_DISABLED) || draw_grayed_out) ? 0.5f : 1.0f;
1602 const float alpha = 0.5f * alpha_fac;
1603 int xmax = ar->v2d.cur.xmax;
1605 if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) {
1609 /* icons can be ui buts, we don't want it to overlap with restrict */
1610 if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0)
1611 xmax -= OL_TOGW + UI_UNIT_X;
1615 /* colors for active/selected data */
1616 if (tselem->type == 0) {
1617 const Object *obact = OBACT(view_layer);
1618 if (te->idcode == ID_SCE) {
1619 if (tselem->id == (ID *)scene) {
1620 rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
1621 active = OL_DRAWSEL_ACTIVE;
1624 else if (te->idcode == ID_OB) {
1625 Object *ob = (Object *)tselem->id;
1626 Base *base = (Base *)te->directdata;
1627 const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
1629 if (ob == obact || is_selected) {
1630 char col[4] = {0, 0, 0, 0};
1632 /* outliner active ob: always white text, circle color now similar to view3d */
1634 active = OL_DRAWSEL_ACTIVE;
1637 UI_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col);
1641 active = OL_DRAWSEL_NORMAL;
1643 else if (is_selected) {
1644 UI_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col);
1647 rgba_float_args_set(color, (float)col[0] / 255, (float)col[1] / 255, (float)col[2] / 255, alpha);
1650 else if (is_object_data_in_editmode(tselem->id, obact)) {
1651 rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
1652 active = OL_DRAWSEL_ACTIVE;
1655 if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) {
1656 rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
1657 active = OL_DRAWSEL_ACTIVE;
1662 active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
1663 rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
1667 if (active != OL_DRAWSEL_NONE) {
1668 UI_draw_roundbox_corner_set(UI_CNR_ALL);
1669 UI_draw_roundbox_aa(
1671 (float)startx + UI_UNIT_X + 1.0f * ufac,
1672 (float)*starty + 1.0f * ufac,
1673 (float)startx + 2.0f * UI_UNIT_X - 1.0f * ufac,
1674 (float)*starty + UI_UNIT_Y - 1.0f * ufac,
1675 UI_UNIT_Y / 2.0f - 1.0f * ufac, color);
1676 GPU_blend(true); /* roundbox disables it */
1678 te->flag |= TE_ACTIVE; // for lookup in display hierarchies
1681 if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
1682 /* Scene collection in view layer can't expand/collapse. */
1684 else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) || (te->flag & TE_LAZY_CLOSED)) {
1685 /* open/close icon, only when sublevels, except for scene */
1686 int icon_x = startx;
1688 // icons a bit higher
1689 if (TSELEM_OPEN(tselem, soops)) {
1691 (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_DOWN,
1696 (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_RIGHT,
1704 if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) {
1705 tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac, true);
1706 offsx += UI_UNIT_X + 4 * ufac;
1711 if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) {
1712 if (tselem->id->tag & LIB_TAG_MISSING) {
1714 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN,
1717 else if (tselem->id->tag & LIB_TAG_INDIRECT) {
1719 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT,
1724 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT,
1727 offsx += UI_UNIT_X + 4 * ufac;
1729 else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_STATIC_OVERRIDE(tselem->id)) {
1731 (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_OVERRIDE,
1733 offsx += UI_UNIT_X + 4 * ufac;
1738 if ((tselem->flag & TSE_TEXTBUT) == 0) {
1739 unsigned char text_col[4];
1741 if (active == OL_DRAWSEL_NORMAL) {
1742 UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
1744 else if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
1745 UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.75f, text_col);
1749 UI_GetThemeColor4ubv(TH_TEXT, text_col);
1751 text_col[3] *= alpha_fac;
1753 UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_col);
1756 offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name));
1758 /* closed item, we draw the icons, not when it's a scene, or master-server list though */
1759 if (!TSELEM_OPEN(tselem, soops)) {
1760 if (te->subtree.first) {
1761 if (tselem->type == 0 && te->idcode == ID_SCE) {
1764 /* this tree element always has same amount of branches, so don't draw */
1765 else if (tselem->type != TSE_R_LAYER) {
1766 int tempx = startx + offsx;
1772 GPUVertFormat *format = immVertexFormat();
1773 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1774 unsigned char col[4];
1776 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1777 UI_GetThemeColorShade4ubv(TH_BACK, -40, col);
1778 col[3] *= alpha_fac;
1780 immUniformColor4ubv(col);
1781 immRecti(pos, tempx - 10.0f * ufac,
1782 *starty + 4.0f * ufac,
1783 tempx - 8.0f * ufac,
1784 *starty + UI_UNIT_Y - 4.0f * ufac);
1788 MergedIconRow merged = {{0}};
1789 outliner_draw_iconrow(
1790 C, block, fstyle, scene, view_layer, soops, &te->subtree, 0, xmax, &tempx,
1791 *starty, alpha_fac, &merged);
1798 /* store coord and continue, we need coordinates for elements outside view too */
1801 te->xend = startx + offsx;
1803 if (TSELEM_OPEN(tselem, soops)) {
1804 *starty -= UI_UNIT_Y;
1806 for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
1807 /* check if element needs to be drawn grayed out, but also gray out
1808 * childs of a grayed out parent (pass on draw_grayed_out to childs) */
1809 bool draw_childs_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
1810 outliner_draw_tree_element(
1811 C, block, fstyle, scene, view_layer,
1812 ar, soops, ten, draw_childs_grayed_out,
1813 startx + UI_UNIT_X, starty, te_edit);
1817 for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
1818 outliner_set_coord_tree_element(ten, startx, *starty);
1821 *starty -= UI_UNIT_Y;
1825 static void outliner_draw_hierarchy_lines_recursive(
1826 unsigned pos, SpaceOops *soops, ListBase *lb, int startx,
1827 const unsigned char col[4], bool draw_grayed_out,
1830 TreeElement *te, *te_vertical_line_last = NULL;
1833 if (BLI_listbase_is_empty(lb)) {
1837 const unsigned char grayed_alpha = col[3] / 2;
1839 /* For vertical lines between objects. */
1841 for (te = lb->first; te; te = te->next) {
1842 bool draw_childs_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
1843 TreeStoreElem *tselem = TREESTORE(te);
1845 if (draw_childs_grayed_out) {
1846 immUniformColor3ubvAlpha(col, grayed_alpha);
1849 immUniformColor4ubv(col);
1852 /* Horizontal Line? */
1853 if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) {
1854 immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - 1);
1856 /* Vertical Line? */
1857 if (te->idcode == ID_OB) {
1858 te_vertical_line_last = te;
1863 *starty -= UI_UNIT_Y;
1865 if (TSELEM_OPEN(tselem, soops))
1866 outliner_draw_hierarchy_lines_recursive(
1867 pos, soops, &te->subtree, startx + UI_UNIT_X,
1868 col, draw_childs_grayed_out, starty);
1871 if (draw_grayed_out) {
1872 immUniformColor3ubvAlpha(col, grayed_alpha);
1875 immUniformColor4ubv(col);
1878 /* Vertical line. */
1879 te = te_vertical_line_last;
1880 if ((te != NULL) && (te->parent || lb->first != lb->last)) {
1881 immRecti(pos, startx, y1 + UI_UNIT_Y, startx + 1, y2);
1885 static void outliner_draw_hierarchy_lines(SpaceOops *soops, ListBase *lb, int startx, int *starty)
1887 GPUVertFormat *format = immVertexFormat();
1888 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1889 unsigned char col[4];
1891 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1892 UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
1896 outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty);
1902 static void outliner_draw_struct_marks(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty)
1904 for (TreeElement *te = lb->first; te; te = te->next) {
1905 TreeStoreElem *tselem = TREESTORE(te);
1907 /* selection status */
1908 if (TSELEM_OPEN(tselem, soops)) {
1909 if (tselem->type == TSE_RNA_STRUCT) {
1910 GPUVertFormat *format = immVertexFormat();
1911 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
1912 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1913 immThemeColorShadeAlpha(TH_BACK, -15, -200);
1914 immRecti(pos, 0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1);
1919 *starty -= UI_UNIT_Y;
1920 if (TSELEM_OPEN(tselem, soops)) {
1921 outliner_draw_struct_marks(ar, soops, &te->subtree, starty);
1922 if (tselem->type == TSE_RNA_STRUCT) {
1923 GPUVertFormat *format = immVertexFormat();
1924 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1925 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1926 immThemeColorShadeAlpha(TH_BACK, -15, -200);
1928 immBegin(GPU_PRIM_LINES, 2);
1929 immVertex2f(pos, 0, (float)*starty + UI_UNIT_Y);
1930 immVertex2f(pos, ar->v2d.cur.xmax, (float)*starty + UI_UNIT_Y);
1939 static void outliner_draw_highlights_recursive(
1940 unsigned pos, const ARegion *ar, const SpaceOops *soops, const ListBase *lb,
1941 const float col_selection[4], const float col_highlight[4], const float col_searchmatch[4],
1942 int start_x, int *io_start_y)
1944 const bool is_searching = (
1945 SEARCHING_OUTLINER(soops) ||
1946 (soops->outlinevis == SO_DATA_API &&
1947 soops->search_string[0] != 0));
1949 for (TreeElement *te = lb->first; te; te = te->next) {
1950 const TreeStoreElem *tselem = TREESTORE(te);
1951 const int start_y = *io_start_y;
1953 /* selection status */
1954 if (tselem->flag & TSE_SELECTED) {
1955 immUniformColor4fv(col_selection);
1956 immRecti(pos, 0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1);
1960 if (tselem->flag & (TSE_DRAG_ANY | TSE_HIGHLIGHTED | TSE_SEARCHMATCH)) {
1961 const int end_x = (int)ar->v2d.cur.xmax;
1963 if (tselem->flag & TSE_DRAG_ANY) {
1964 /* drag and drop highlight */
1966 UI_GetThemeColorShade4fv(TH_BACK, -40, col);
1968 if (tselem->flag & TSE_DRAG_BEFORE) {
1969 immUniformColor4fv(col);
1970 immRecti(pos, start_x, start_y + UI_UNIT_Y - 1, end_x, start_y + UI_UNIT_Y + 1);
1972 else if (tselem->flag & TSE_DRAG_AFTER) {
1973 immUniformColor4fv(col);
1974 immRecti(pos, start_x, start_y - 1, end_x, start_y + 1);
1977 immUniformColor3fvAlpha(col, col[3] * 0.5f);
1978 immRecti(pos, start_x, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
1982 if (is_searching && (tselem->flag & TSE_SEARCHMATCH)) {
1983 /* search match highlights
1984 * we don't expand items when searching in the datablocks but we
1985 * still want to highlight any filter matches. */
1986 immUniformColor4fv(col_searchmatch);
1987 immRecti(pos, start_x, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
1989 else if (tselem->flag & TSE_HIGHLIGHTED) {
1990 /* mouse hover highlight */
1991 immUniformColor4fv(col_highlight);
1992 immRecti(pos, 0, start_y + 1, end_x, start_y + UI_UNIT_Y - 1);
1997 *io_start_y -= UI_UNIT_Y;
1998 if (TSELEM_OPEN(tselem, soops)) {
1999 outliner_draw_highlights_recursive(
2000 pos, ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch,
2001 start_x + UI_UNIT_X, io_start_y);
2006 static void outliner_draw_highlights(ARegion *ar, SpaceOops *soops, int startx, int *starty)
2008 const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f};
2009 float col_selection[4], col_searchmatch[4];
2011 UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection);
2012 col_selection[3] = 1.0f; /* no alpha */
2013 UI_GetThemeColor4fv(TH_MATCH, col_searchmatch);
2014 col_searchmatch[3] = 0.5f;
2017 GPUVertFormat *format = immVertexFormat();
2018 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2019 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2020 outliner_draw_highlights_recursive(
2021 pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch,
2027 static void outliner_draw_tree(
2028 bContext *C, uiBlock *block, Scene *scene, ViewLayer *view_layer,
2029 ARegion *ar, SpaceOops *soops, const bool has_restrict_icons,
2030 TreeElement **te_edit)
2032 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
2035 GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); // only once
2037 if (soops->outlinevis == SO_DATA_API) {
2039 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2040 outliner_draw_struct_marks(ar, soops, &soops->tree, &starty);
2043 /* draw highlights before hierarchy */
2044 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2046 outliner_draw_highlights(ar, soops, startx, &starty);
2048 /* set scissor so tree elements or lines can't overlap restriction icons */
2049 float scissor[4] = {0};
2050 if (has_restrict_icons) {
2051 int mask_x = BLI_rcti_size_x(&ar->v2d.mask) - (int)OL_TOGW + 1;
2052 CLAMP_MIN(mask_x, 0);
2054 GPU_scissor_get_f(scissor);
2055 GPU_scissor(0, 0, mask_x, ar->winy);
2058 // gray hierarchy lines
2060 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y / 2 - OL_Y_OFFSET;
2061 startx = UI_UNIT_X / 2 - 1.0f;
2062 outliner_draw_hierarchy_lines(soops, &soops->tree, startx, &starty);
2065 starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
2067 for (TreeElement *te = soops->tree.first; te; te = te->next) {
2068 outliner_draw_tree_element(
2069 C, block, fstyle, scene, view_layer,
2070 ar, soops, te, (te->flag & TE_DRAGGING) != 0,
2071 startx, &starty, te_edit);
2074 if (has_restrict_icons) {
2076 GPU_scissor(UNPACK4(scissor));
2081 static void outliner_back(ARegion *ar)
2085 ystart = (int)ar->v2d.tot.ymax;
2086 ystart = UI_UNIT_Y * (ystart / (UI_UNIT_Y)) - OL_Y_OFFSET;
2088 GPUVertFormat *format = immVertexFormat();
2089 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2091 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2092 immUniformThemeColorShade(TH_BACK, 6);
2094 const float x1 = 0.0f, x2 = ar->v2d.cur.xmax;
2095 float y1 = ystart, y2;
2096 int tot = (int)floor(ystart - ar->v2d.cur.ymin + 2 * UI_UNIT_Y) / (2 * UI_UNIT_Y);
2099 immBegin(GPU_PRIM_TRIS, 6 * tot);
2101 y1 -= 2 * UI_UNIT_Y;
2102 y2 = y1 + UI_UNIT_Y;
2103 immVertex2f(pos, x1, y1);
2104 immVertex2f(pos, x2, y1);
2105 immVertex2f(pos, x2, y2);
2107 immVertex2f(pos, x1, y1);
2108 immVertex2f(pos, x2, y2);
2109 immVertex2f(pos, x1, y2);
2116 static void outliner_draw_restrictcols(ARegion *ar)
2118 GPU_line_width(1.0f);
2120 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2121 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2122 immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
2123 immBegin(GPU_PRIM_LINES, 8);
2125 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymax);
2126 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymin);
2128 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymax);
2129 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymin);
2131 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymax);
2132 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymin);
2134 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymax);
2135 immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymin);
2141 /* ****************************************************** */
2142 /* Main Entrypoint - Draw contents of Outliner editor */
2144 void draw_outliner(const bContext *C)
2146 Main *mainvar = CTX_data_main(C);
2147 Scene *scene = CTX_data_scene(C);
2148 ViewLayer *view_layer = CTX_data_view_layer(C);
2149 ARegion *ar = CTX_wm_region(C);
2150 View2D *v2d = &ar->v2d;
2151 SpaceOops *soops = CTX_wm_space_outliner(C);
2153 int sizey = 0, sizex = 0, sizex_rna = 0;
2154 TreeElement *te_edit = NULL;
2155 bool has_restrict_icons;
2157 outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always
2159 /* get extents of data */
2160 outliner_height(soops, &soops->tree, &sizey);
2162 if (soops->outlinevis == SO_DATA_API) {
2163 /* RNA has two columns:
2164 * - column 1 is (max_width + OL_RNA_COL_SPACEX) or
2165 * (OL_RNA_COL_X), whichever is wider...
2166 * - column 2 is fixed at OL_RNA_COL_SIZEX
2168 * (*) XXX max width for now is a fixed factor of (UI_UNIT_X * (max_indention + 100))
2171 /* get actual width of column 1 */
2172 outliner_rna_width(soops, &soops->tree, &sizex_rna, 0);
2173 sizex_rna = max_ii(OL_RNA_COLX, sizex_rna + OL_RNA_COL_SPACEX);
2175 /* get width of data (for setting 'tot' rect, this is column 1 + column 2 + a bit extra) */
2176 sizex = sizex_rna + OL_RNA_COL_SIZEX + 50;
2177 has_restrict_icons = false;
2180 /* width must take into account restriction columns (if visible) so that entries will still be visible */
2181 //outliner_width(soops, &soops->tree, &sizex);
2182 // XXX should use outliner_width instead when te->xend will be set correctly...
2183 outliner_rna_width(soops, &soops->tree, &sizex, 0);
2185 /* constant offset for restriction columns */
2186 // XXX this isn't that great yet...
2187 if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0) {
2188 sizex += OL_TOGW * 3;
2191 has_restrict_icons = !(soops->flag & SO_HIDE_RESTRICTCOLS);
2194 /* adds vertical offset */
2195 sizey += OL_Y_OFFSET;
2197 /* update size of tot-rect (extents of data/viewable area) */
2198 UI_view2d_totRect_set(v2d, sizex, sizey);
2200 /* force display to pixel coords */
2201 v2d->flag |= (V2D_PIXELOFS_X | V2D_PIXELOFS_Y);
2202 /* set matrix for 2d-view controls */
2203 UI_view2d_view_ortho(v2d);
2205 /* draw outliner stuff (background, hierarchy lines and names) */
2207 block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
2209 (bContext *)C, block, scene, view_layer,
2210 ar, soops, has_restrict_icons, &te_edit);
2212 /* Default to no emboss for outliner UI. */
2213 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2215 if (soops->outlinevis == SO_DATA_API) {
2216 /* draw rna buttons */
2217 outliner_draw_rnacols(ar, sizex_rna);
2219 UI_block_emboss_set(block, UI_EMBOSS);
2220 outliner_draw_rnabuts(block, ar, soops, sizex_rna, &soops->tree);
2221 UI_block_emboss_set(block, UI_EMBOSS_NONE);
2223 else if ((soops->outlinevis == SO_ID_ORPHANS) && has_restrict_icons) {
2224 /* draw user toggle columns */
2225 outliner_draw_restrictcols(ar);
2226 outliner_draw_userbuts(block, ar, soops, &soops->tree);
2228 else if (has_restrict_icons) {
2229 /* draw restriction columns */
2230 outliner_draw_restrictcols(ar);
2232 outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &soops->tree);
2235 UI_block_emboss_set(block, UI_EMBOSS);
2237 /* draw edit buttons if nessecery */
2239 outliner_buttons(C, block, ar, te_edit);
2242 UI_block_end(C, block);
2243 UI_block_draw(C, block);