UI: Support drag & drop reordering of collections

This adds initial support for reordering collections from the Outliner
using drag & drop.
Although drag & drop support is limited to collections for now, this
lays most foundations for general drag & drop reordering support in the
Outliner. There are some design questions to be answered though:
* Would reordering of other data types (like objects) be a purely visual change or would it affect the order in which they are stored? (Would that make a difference for the user?)
* Should/can we allow mixing of different data types? (e.g. mixing render layers with objects)
* How could we realize this technically?

Notes:
* "Sort Alphabetically" has to be disabled to use this ("View" menu).
* Reordering only works with collections on the same hierarchy level.
* Added some visual feedback that should work quite well, it's by far not a final design though: {F493806}
* Modified collection orders are stored in .blends.
* Reordering can be undone.
* Did minor cleanups here and there.
This commit is contained in:
Julian Eisel 2017-02-22 16:02:43 +01:00
parent e003499f6f
commit 910b7dec8d
10 changed files with 657 additions and 161 deletions

View File

@ -44,7 +44,7 @@ struct Scene;
struct SceneCollection *BKE_collection_add(struct Scene *scene, struct SceneCollection *sc_parent, const char *name);
bool BKE_collection_remove(struct Scene *scene, struct SceneCollection *sc);
struct SceneCollection *BKE_collection_master(struct Scene *scene);
struct SceneCollection *BKE_collection_master(const struct Scene *scene);
void BKE_collection_master_free(struct Scene *scene);
void BKE_collection_object_add(struct Scene *scene, struct SceneCollection *sc, struct Object *object);
void BKE_collection_object_add_from(struct Scene *scene, struct Object *ob_src, struct Object *ob_dst);

View File

@ -54,7 +54,7 @@ struct Scene;
struct SceneCollection;
struct SceneLayer;
struct SceneLayer *BKE_scene_layer_render_active(struct Scene *scene);
struct SceneLayer *BKE_scene_layer_render_active(const struct Scene *scene);
struct SceneLayer *BKE_scene_layer_context_active(struct Scene *scene);
struct SceneLayer *BKE_scene_layer_add(struct Scene *scene, const char *name);
@ -84,6 +84,8 @@ struct LayerCollection *BKE_layer_collection_active(struct SceneLayer *sl);
int BKE_layer_collection_count(struct SceneLayer *sl);
int BKE_layer_collection_findindex(struct SceneLayer *sl, struct LayerCollection *lc);
void BKE_layer_collection_reinsert_after(const struct Scene *scene, struct SceneLayer *sl,
struct LayerCollection *lc_reinsert, struct LayerCollection *lc_after);
struct LayerCollection *BKE_collection_link(struct SceneLayer *sl, struct SceneCollection *sc);

View File

@ -44,6 +44,9 @@
#include "MEM_guardedalloc.h"
bool collection_remlink(SceneCollection *, SceneCollection *);
bool collection_insert_after(SceneCollection *, SceneCollection *, SceneCollection *);
/**
* Add a collection to a collection ListBase and syncronize all render layers
* The ListBase is NULL when the collection is to be added to the master collection
@ -95,7 +98,7 @@ static void collection_free(SceneCollection *sc)
* Unlink the collection recursively
* return true if unlinked
*/
static bool collection_remlink(SceneCollection *sc_parent, SceneCollection *sc_gone)
bool collection_remlink(SceneCollection *sc_parent, SceneCollection *sc_gone)
{
for (SceneCollection *sc = sc_parent->scene_collections.first; sc; sc = sc->next)
{
@ -173,10 +176,36 @@ bool BKE_collection_remove(Scene *scene, SceneCollection *sc)
return true;
}
/**
* Lookup the parent listbase of \a sc_insert_after and insert \a sc_insert after it.
* \param sc_after: If this is NULL, \a sc_insert will be inserted as first collection in \a parent.
*/
bool collection_insert_after(
SceneCollection *parent, SceneCollection *sc_insert, SceneCollection *sc_after)
{
if (sc_after == NULL) {
BLI_addhead(&parent->scene_collections, sc_insert);
return true;
}
for (SceneCollection *sc = parent->scene_collections.first; sc; sc = sc->next) {
if (sc == sc_after) {
BLI_insertlinkafter(&parent->scene_collections, sc_after, sc_insert);
return true;
}
if (collection_insert_after(sc, sc_insert, sc_after)) {
return true;
}
}
return false;
}
/**
* Returns the master collection
*/
SceneCollection *BKE_collection_master(Scene *scene)
SceneCollection *BKE_collection_master(const Scene *scene)
{
return scene->collection;
}

View File

@ -66,7 +66,7 @@ static void object_bases_Iterator_next(Iterator *iter, const int flag);
* Returns the SceneLayer to be used for rendering
* Most of the time BKE_scene_layer_context_active should be used instead
*/
SceneLayer *BKE_scene_layer_render_active(Scene *scene)
SceneLayer *BKE_scene_layer_render_active(const Scene *scene)
{
SceneLayer *sl = BLI_findlink(&scene->render_layers, scene->active_layer);
BLI_assert(sl);
@ -491,6 +491,71 @@ int BKE_layer_collection_findindex(SceneLayer *sl, LayerCollection *lc)
return index_from_collection(&sl->layer_collections, lc, &i);
}
/**
* \param lc_after: Can be NULL to insert \a lc_after as first collection in \a lb.
*/
static bool layer_collection_insert_after(ListBase *lb, LayerCollection *lc_insert, LayerCollection *lc_after)
{
if (lc_after == NULL) {
BLI_addhead(lb, lc_insert);
return true;
}
for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
if (lc == lc_after) {
BLI_insertlinkafter(lb, lc_after, lc_insert);
return true;
}
if (layer_collection_insert_after(&lc->layer_collections, lc_insert, lc_after)) {
return true;
}
}
return false;
}
static bool layer_collection_remlink(ListBase *lb, LayerCollection *lc)
{
for (LayerCollection *lc_iter = lb->first; lc_iter; lc_iter = lc_iter->next) {
if (lc_iter == lc) {
BLI_remlink(lb, lc);
return true;
}
if (layer_collection_remlink(&lc_iter->layer_collections, lc)) {
return true;
}
}
return false;
}
/**
* Move \a lc_reinsert so that it follows \a lc_after. After this, both \a lc_reinsert
* and \a lc_after should be stored in the same list.
*
* \param lc_after: Can be NULL to insert \a lc_after as first collection in \a sl.
*/
void BKE_layer_collection_reinsert_after(
const Scene *scene, SceneLayer *sl, LayerCollection *lc_reinsert, LayerCollection *lc_after)
{
/* XXX maybe worth having a BKE internal header file for this? */
extern bool collection_remlink(SceneCollection *, SceneCollection *);
extern bool collection_insert_after(SceneCollection *, SceneCollection *, SceneCollection *);
SceneCollection *sc_master = BKE_collection_master(scene);
layer_collection_remlink(&sl->layer_collections, lc_reinsert);
collection_remlink(sc_master, lc_reinsert->scene_collection);
layer_collection_insert_after(&sl->layer_collections, lc_reinsert, lc_after);
collection_insert_after(sc_master, lc_reinsert->scene_collection, lc_after ? lc_after->scene_collection : NULL);
BKE_scene_layer_base_flag_recalculate(sl);
BKE_scene_layer_engine_settings_collection_recalculate(sl, lc_reinsert);
}
/**
* Link a collection to a renderlayer
* The collection needs to be created separately

View File

@ -66,6 +66,7 @@ void UI_id_icon_render(
int UI_preview_render_size(enum eIconSizes size);
void UI_icon_draw(float x, float y, int icon_id);
void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha);
void UI_icon_draw_preview(float x, float y, int icon_id);
void UI_icon_draw_preview_aspect(float x, float y, int icon_id, float aspect);
void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, float alpha, int size);

View File

@ -1432,6 +1432,11 @@ void UI_icon_draw(float x, float y, int icon_id)
UI_icon_draw_aspect(x, y, icon_id, 1.0f / UI_DPI_FAC, 1.0f);
}
void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha)
{
UI_icon_draw_aspect(x, y, icon_id, 1.0f / UI_DPI_FAC, alpha);
}
void UI_icon_draw_size(float x, float y, int size, int icon_id, float alpha)
{
icon_draw_size(x, y, icon_id, 1.0f, alpha, NULL, ICON_SIZE_ICON, size, true, false);

View File

@ -663,6 +663,7 @@ static void outliner_draw_rnacols(ARegion *ar, int sizex)
float miny = v2d->cur.ymin;
if (miny < v2d->tot.ymin) miny = v2d->tot.ymin;
glLineWidth(1.0f);
UI_ThemeColorShadeAlpha(TH_BACK, -15, -200);
/* draw column separator lines */
@ -772,7 +773,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon)
/* restrict column clip... it has been coded by simply overdrawing, doesnt work for buttons */
if (arg->x >= arg->xmax) {
glEnable(GL_BLEND);
UI_icon_draw_aspect(arg->x, arg->y, icon, 1.0f / UI_DPI_FAC, arg->alpha);
UI_icon_draw_alpha(arg->x, arg->y, icon, arg->alpha);
glDisable(GL_BLEND);
}
else {
@ -835,189 +836,258 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
arg.xb = x; /* for ui buttons */
arg.yb = y;
arg.alpha = alpha;
/* placement of icons, copied from interface_widgets.c */
aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
arg.x = x = x + 4.0f * aspect;
arg.y = y = y + 0.1f * UI_UNIT_Y;
#define ICON_DRAW(_icon) UI_icon_draw_alpha(x, y, _icon, alpha)
if (tselem->type) {
switch (tselem->type) {
case TSE_ANIM_DATA:
UI_icon_draw(x, y, ICON_ANIM_DATA); break; // xxx
ICON_DRAW(ICON_ANIM_DATA); /* XXX */
break;
case TSE_NLA:
UI_icon_draw(x, y, ICON_NLA); break;
ICON_DRAW(ICON_NLA);
break;
case TSE_NLA_TRACK:
UI_icon_draw(x, y, ICON_NLA); break; // XXX
ICON_DRAW(ICON_NLA); /* XXX */
break;
case TSE_NLA_ACTION:
UI_icon_draw(x, y, ICON_ACTION); break;
ICON_DRAW(ICON_ACTION);
break;
case TSE_DRIVER_BASE:
UI_icon_draw(x, y, ICON_DRIVER); break;
ICON_DRAW(ICON_DRIVER);
break;
case TSE_DEFGROUP_BASE:
UI_icon_draw(x, y, ICON_GROUP_VERTEX); break;
ICON_DRAW(ICON_GROUP_VERTEX);
break;
case TSE_BONE:
case TSE_EBONE:
UI_icon_draw(x, y, ICON_BONE_DATA); break;
ICON_DRAW(ICON_BONE_DATA);
break;
case TSE_CONSTRAINT_BASE:
UI_icon_draw(x, y, ICON_CONSTRAINT); break;
ICON_DRAW(ICON_CONSTRAINT);
break;
case TSE_MODIFIER_BASE:
UI_icon_draw(x, y, ICON_MODIFIER); break;
ICON_DRAW(ICON_MODIFIER);
break;
case TSE_LINKED_OB:
UI_icon_draw(x, y, ICON_OBJECT_DATA); break;
ICON_DRAW(ICON_OBJECT_DATA);
break;
case TSE_LINKED_PSYS:
UI_icon_draw(x, y, ICON_PARTICLES); break;
ICON_DRAW(ICON_PARTICLES);
break;
case TSE_MODIFIER:
{
Object *ob = (Object *)tselem->id;
ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr);
switch ((ModifierType)md->type) {
case eModifierType_Subsurf:
UI_icon_draw(x, y, ICON_MOD_SUBSURF); break;
case eModifierType_Armature:
UI_icon_draw(x, y, ICON_MOD_ARMATURE); break;
case eModifierType_Lattice:
UI_icon_draw(x, y, ICON_MOD_LATTICE); break;
case eModifierType_Curve:
UI_icon_draw(x, y, ICON_MOD_CURVE); break;
case eModifierType_Build:
UI_icon_draw(x, y, ICON_MOD_BUILD); break;
case eModifierType_Mirror:
UI_icon_draw(x, y, ICON_MOD_MIRROR); break;
case eModifierType_Decimate:
UI_icon_draw(x, y, ICON_MOD_DECIM); break;
case eModifierType_Wave:
UI_icon_draw(x, y, ICON_MOD_WAVE); break;
case eModifierType_Hook:
UI_icon_draw(x, y, ICON_HOOK); break;
case eModifierType_Softbody:
UI_icon_draw(x, y, ICON_MOD_SOFT); break;
case eModifierType_Boolean:
UI_icon_draw(x, y, ICON_MOD_BOOLEAN); break;
case eModifierType_ParticleSystem:
UI_icon_draw(x, y, ICON_MOD_PARTICLES); break;
case eModifierType_Subsurf:
ICON_DRAW(ICON_MOD_SUBSURF);
break;
case eModifierType_Armature:
ICON_DRAW(ICON_MOD_ARMATURE);
break;
case eModifierType_Lattice:
ICON_DRAW(ICON_MOD_LATTICE);
break;
case eModifierType_Curve:
ICON_DRAW(ICON_MOD_CURVE);
break;
case eModifierType_Build:
ICON_DRAW(ICON_MOD_BUILD);
break;
case eModifierType_Mirror:
ICON_DRAW(ICON_MOD_MIRROR);
break;
case eModifierType_Decimate:
ICON_DRAW(ICON_MOD_DECIM);
break;
case eModifierType_Wave:
ICON_DRAW(ICON_MOD_WAVE);
break;
case eModifierType_Hook:
ICON_DRAW(ICON_HOOK);
break;
case eModifierType_Softbody:
ICON_DRAW(ICON_MOD_SOFT);
break;
case eModifierType_Boolean:
ICON_DRAW(ICON_MOD_BOOLEAN);
break;
case eModifierType_ParticleSystem:
ICON_DRAW(ICON_MOD_PARTICLES);
break;
case eModifierType_ParticleInstance:
UI_icon_draw(x, y, ICON_MOD_PARTICLES); break;
ICON_DRAW(ICON_MOD_PARTICLES);
break;
case eModifierType_EdgeSplit:
UI_icon_draw(x, y, ICON_MOD_EDGESPLIT); break;
ICON_DRAW(ICON_MOD_EDGESPLIT);
break;
case eModifierType_Array:
UI_icon_draw(x, y, ICON_MOD_ARRAY); break;
ICON_DRAW(ICON_MOD_ARRAY);
break;
case eModifierType_UVProject:
case eModifierType_UVWarp: /* TODO, get own icon */
UI_icon_draw(x, y, ICON_MOD_UVPROJECT); break;
ICON_DRAW(ICON_MOD_UVPROJECT);
break;
case eModifierType_Displace:
UI_icon_draw(x, y, ICON_MOD_DISPLACE); break;
ICON_DRAW(ICON_MOD_DISPLACE);
break;
case eModifierType_Shrinkwrap:
UI_icon_draw(x, y, ICON_MOD_SHRINKWRAP); break;
ICON_DRAW(ICON_MOD_SHRINKWRAP);
break;
case eModifierType_Cast:
UI_icon_draw(x, y, ICON_MOD_CAST); break;
ICON_DRAW(ICON_MOD_CAST);
break;
case eModifierType_MeshDeform:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break;
ICON_DRAW(ICON_MOD_MESHDEFORM);
break;
case eModifierType_Bevel:
UI_icon_draw(x, y, ICON_MOD_BEVEL); break;
ICON_DRAW(ICON_MOD_BEVEL);
break;
case eModifierType_Smooth:
case eModifierType_LaplacianSmooth:
case eModifierType_CorrectiveSmooth:
UI_icon_draw(x, y, ICON_MOD_SMOOTH); break;
ICON_DRAW(ICON_MOD_SMOOTH);
break;
case eModifierType_SimpleDeform:
UI_icon_draw(x, y, ICON_MOD_SIMPLEDEFORM); break;
ICON_DRAW(ICON_MOD_SIMPLEDEFORM);
break;
case eModifierType_Mask:
UI_icon_draw(x, y, ICON_MOD_MASK); break;
ICON_DRAW(ICON_MOD_MASK);
break;
case eModifierType_Cloth:
UI_icon_draw(x, y, ICON_MOD_CLOTH); break;
ICON_DRAW(ICON_MOD_CLOTH);
break;
case eModifierType_Explode:
UI_icon_draw(x, y, ICON_MOD_EXPLODE); break;
ICON_DRAW(ICON_MOD_EXPLODE);
break;
case eModifierType_Collision:
case eModifierType_Surface:
UI_icon_draw(x, y, ICON_MOD_PHYSICS); break;
ICON_DRAW(ICON_MOD_PHYSICS);
break;
case eModifierType_Fluidsim:
UI_icon_draw(x, y, ICON_MOD_FLUIDSIM); break;
ICON_DRAW(ICON_MOD_FLUIDSIM);
break;
case eModifierType_Multires:
UI_icon_draw(x, y, ICON_MOD_MULTIRES); break;
ICON_DRAW(ICON_MOD_MULTIRES);
break;
case eModifierType_Smoke:
UI_icon_draw(x, y, ICON_MOD_SMOKE); break;
ICON_DRAW(ICON_MOD_SMOKE);
break;
case eModifierType_Solidify:
UI_icon_draw(x, y, ICON_MOD_SOLIDIFY); break;
ICON_DRAW(ICON_MOD_SOLIDIFY);
break;
case eModifierType_Screw:
UI_icon_draw(x, y, ICON_MOD_SCREW); break;
ICON_DRAW(ICON_MOD_SCREW);
break;
case eModifierType_Remesh:
UI_icon_draw(x, y, ICON_MOD_REMESH); break;
ICON_DRAW(ICON_MOD_REMESH);
break;
case eModifierType_WeightVGEdit:
case eModifierType_WeightVGMix:
case eModifierType_WeightVGProximity:
UI_icon_draw(x, y, ICON_MOD_VERTEX_WEIGHT); break;
ICON_DRAW(ICON_MOD_VERTEX_WEIGHT);
break;
case eModifierType_DynamicPaint:
UI_icon_draw(x, y, ICON_MOD_DYNAMICPAINT); break;
ICON_DRAW(ICON_MOD_DYNAMICPAINT);
break;
case eModifierType_Ocean:
UI_icon_draw(x, y, ICON_MOD_OCEAN); break;
ICON_DRAW(ICON_MOD_OCEAN);
break;
case eModifierType_Warp:
UI_icon_draw(x, y, ICON_MOD_WARP); break;
ICON_DRAW(ICON_MOD_WARP);
break;
case eModifierType_Skin:
UI_icon_draw(x, y, ICON_MOD_SKIN); break;
ICON_DRAW(ICON_MOD_SKIN);
break;
case eModifierType_Triangulate:
UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break;
ICON_DRAW(ICON_MOD_TRIANGULATE);
break;
case eModifierType_MeshCache:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */
break;
case eModifierType_MeshSequenceCache:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */
break;
case eModifierType_Wireframe:
UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break;
ICON_DRAW(ICON_MOD_WIREFRAME);
break;
case eModifierType_LaplacianDeform:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */
break;
case eModifierType_DataTransfer:
UI_icon_draw(x, y, ICON_MOD_DATA_TRANSFER); break;
ICON_DRAW(ICON_MOD_DATA_TRANSFER);
break;
case eModifierType_NormalEdit:
UI_icon_draw(x, y, ICON_MOD_NORMALEDIT); break;
ICON_DRAW(ICON_MOD_NORMALEDIT);
break;
/* Default */
case eModifierType_None:
case eModifierType_ShapeKey:
case NUM_MODIFIER_TYPES:
UI_icon_draw(x, y, ICON_DOT); break;
ICON_DRAW(ICON_DOT);
break;
}
break;
}
case TSE_POSE_BASE:
UI_icon_draw(x, y, ICON_ARMATURE_DATA); break;
ICON_DRAW(ICON_ARMATURE_DATA);
break;
case TSE_POSE_CHANNEL:
UI_icon_draw(x, y, ICON_BONE_DATA); break;
ICON_DRAW(ICON_BONE_DATA);
break;
case TSE_PROXY:
UI_icon_draw(x, y, ICON_GHOST); break;
ICON_DRAW(ICON_GHOST);
break;
case TSE_R_LAYER_BASE:
UI_icon_draw(x, y, ICON_RENDERLAYERS); break;
ICON_DRAW(ICON_RENDERLAYERS);
break;
case TSE_R_LAYER:
UI_icon_draw(x, y, ICON_RENDERLAYERS); break;
ICON_DRAW(ICON_RENDERLAYERS);
break;
case TSE_LINKED_LAMP:
UI_icon_draw(x, y, ICON_LAMP_DATA); break;
ICON_DRAW(ICON_LAMP_DATA);
break;
case TSE_LINKED_MAT:
UI_icon_draw(x, y, ICON_MATERIAL_DATA); break;
ICON_DRAW(ICON_MATERIAL_DATA);
break;
case TSE_POSEGRP_BASE:
UI_icon_draw(x, y, ICON_GROUP_BONE); break;
ICON_DRAW(ICON_GROUP_BONE);
break;
case TSE_SEQUENCE:
if (te->idcode == SEQ_TYPE_MOVIE)
UI_icon_draw(x, y, ICON_SEQUENCE);
ICON_DRAW(ICON_SEQUENCE);
else if (te->idcode == SEQ_TYPE_META)
UI_icon_draw(x, y, ICON_DOT);
ICON_DRAW(ICON_DOT);
else if (te->idcode == SEQ_TYPE_SCENE)
UI_icon_draw(x, y, ICON_SCENE);
ICON_DRAW(ICON_SCENE);
else if (te->idcode == SEQ_TYPE_SOUND_RAM)
UI_icon_draw(x, y, ICON_SOUND);
ICON_DRAW(ICON_SOUND);
else if (te->idcode == SEQ_TYPE_IMAGE)
UI_icon_draw(x, y, ICON_IMAGE_COL);
ICON_DRAW(ICON_IMAGE_COL);
else
UI_icon_draw(x, y, ICON_PARTICLES);
ICON_DRAW(ICON_PARTICLES);
break;
case TSE_SEQ_STRIP:
UI_icon_draw(x, y, ICON_LIBRARY_DATA_DIRECT);
ICON_DRAW(ICON_LIBRARY_DATA_DIRECT);
break;
case TSE_SEQUENCE_DUP:
UI_icon_draw(x, y, ICON_OBJECT_DATA);
ICON_DRAW(ICON_OBJECT_DATA);
break;
case TSE_RNA_STRUCT:
if (RNA_struct_is_ID(te->rnaptr.type)) {
arg.id = (ID *)te->rnaptr.data;
tselem_draw_icon_uibut(&arg, RNA_struct_ui_icon(te->rnaptr.type));
}
else
UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type));
else {
int icon = RNA_struct_ui_icon(te->rnaptr.type);
ICON_DRAW(icon);
}
break;
/* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */
#if 0
@ -1026,7 +1096,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
break;
#endif
default:
UI_icon_draw(x, y, ICON_DOT); break;
ICON_DRAW(ICON_DOT);
break;
}
}
else if (tselem->id) {
@ -1131,17 +1202,18 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
}
}
}
#undef ICON_DRAW
}
static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, SceneLayer *sl, SpaceOops *soops, ListBase *lb, int level,
int xmax, int *offsx, int ys)
static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, SceneLayer *sl, SpaceOops *soops,
ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac)
{
TreeElement *te;
TreeStoreElem *tselem;
eOLDrawState active;
for (te = lb->first; te; te = te->next) {
/* exit drawing early */
if ((*offsx) - UI_UNIT_X > xmax)
break;
@ -1169,9 +1241,11 @@ static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, Sce
if (active != OL_DRAWSEL_NONE) {
float ufac = UI_UNIT_X / 20.0f;
float color[4] = {1.0f, 1.0f, 1.0f, 0.4f};
UI_draw_roundbox_corner_set(UI_CNR_ALL);
float color[4] = {1.0f, 1.0f, 1.0f, 0.4f};
color[3] *= alpha_fac;
UI_draw_roundbox(
(float) *offsx - 1.0f * ufac,
(float)ys + 1.0f * ufac,
@ -1182,7 +1256,7 @@ static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, Sce
glEnable(GL_BLEND); /* roundbox disables */
}
tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.5f);
tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.5f * alpha_fac);
te->xs = *offsx;
te->ys = ys;
te->xend = (short)*offsx + UI_UNIT_X;
@ -1193,7 +1267,7 @@ static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, Sce
/* this tree element always has same amount of branches, so don't draw */
if (tselem->type != TSE_R_LAYER)
outliner_draw_iconrow(C, block, scene, sl, soops, &te->subtree, level + 1, xmax, offsx, ys);
outliner_draw_iconrow(C, block, scene, sl, soops, &te->subtree, level + 1, xmax, offsx, ys, alpha_fac);
}
}
@ -1217,10 +1291,10 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta
static void outliner_draw_tree_element(
bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, SceneLayer *sl, ARegion *ar, SpaceOops *soops,
TreeElement *te, int startx, int *starty, TreeElement **te_edit)
bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, SceneLayer *sl,
ARegion *ar, SpaceOops *soops, TreeElement *te, bool draw_grayed_out,
int startx, int *starty, TreeElement **te_edit, TreeElement **te_floating)
{
TreeElement *ten;
TreeStoreElem *tselem;
float ufac = UI_UNIT_X / 20.0f;
int offsx = 0;
@ -1229,12 +1303,16 @@ static void outliner_draw_tree_element(
tselem = TREESTORE(te);
if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) {
const float alpha_fac = draw_grayed_out ? 0.5f : 1.0f;
const float alpha = 0.5f * alpha_fac;
int xmax = ar->v2d.cur.xmax;
float alpha = 0.5f;
if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) {
*te_edit = te;
}
if ((te->drag_data != NULL) && (*te_floating == NULL)) {
*te_floating = te;
}
/* icons can be ui buts, we don't want it to overlap with restrict */
if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0)
@ -1314,38 +1392,39 @@ static void outliner_draw_tree_element(
icon_x = startx;
else
icon_x = startx + 5 * ufac;
// icons a bit higher
if (TSELEM_OPEN(tselem, soops))
UI_icon_draw((float)icon_x, (float)*starty + 2 * ufac, ICON_DISCLOSURE_TRI_DOWN);
UI_icon_draw_alpha((float)icon_x, (float)*starty + 2 * ufac, ICON_DISCLOSURE_TRI_DOWN,
alpha_fac);
else
UI_icon_draw((float)icon_x, (float)*starty + 2 * ufac, ICON_DISCLOSURE_TRI_RIGHT);
UI_icon_draw_alpha((float)icon_x, (float)*starty + 2 * ufac, ICON_DISCLOSURE_TRI_RIGHT,
alpha_fac);
}
offsx += UI_UNIT_X;
/* datatype icon */
if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM))) {
tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, 1.0f);
tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac);
offsx += UI_UNIT_X;
}
else
offsx += 2 * ufac;
if (tselem->type == 0 && ID_IS_LINKED_DATABLOCK(tselem->id)) {
glPixelTransferf(GL_ALPHA_SCALE, 0.5f);
if (tselem->id->tag & LIB_TAG_MISSING) {
UI_icon_draw((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN);
UI_icon_draw_alpha((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN,
alpha_fac);
}
else if (tselem->id->tag & LIB_TAG_INDIRECT) {
UI_icon_draw((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT);
UI_icon_draw_alpha((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT,
alpha_fac);
}
else {
UI_icon_draw((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT);
UI_icon_draw_alpha((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT,
alpha_fac);
}
glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
offsx += UI_UNIT_X;
}
glDisable(GL_BLEND);
@ -1364,6 +1443,7 @@ static void outliner_draw_tree_element(
else {
UI_GetThemeColor4ubv(TH_TEXT, text_col);
}
text_col[3] *= alpha_fac;
UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_col);
}
@ -1376,31 +1456,33 @@ static void outliner_draw_tree_element(
if (tselem->type == 0 && te->idcode == ID_SCE) {
/* pass */
}
/* this tree element always has same amount of branches, so don't draw */
else if (tselem->type != TSE_R_LAYER) {
/* this tree element always has same amount of branches, so don't draw */
int tempx = startx + offsx;
glEnable(GL_BLEND);
/* divider */
{
VertexFormat *format = immVertexFormat();
unsigned pos = add_attrib(format, "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
unsigned int pos = add_attrib(format, "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
unsigned char col[4];
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
UI_GetThemeColorShade4ubv(TH_BACK, -40, col);
col[3] *= alpha_fac;
immUniformColor4ubv(col);
immRecti(pos, tempx - 10.0f * ufac,
*starty + 4.0f * ufac,
tempx - 8.0f * ufac,
*starty + UI_UNIT_Y - 4.0f * ufac);
*starty + 4.0f * ufac,
tempx - 8.0f * ufac,
*starty + UI_UNIT_Y - 4.0f * ufac);
immUnbindProgram();
}
glEnable(GL_BLEND);
glPixelTransferf(GL_ALPHA_SCALE, 0.5);
outliner_draw_iconrow(C, block, scene, sl, soops, &te->subtree, 0, xmax, &tempx, *starty);
glPixelTransferf(GL_ALPHA_SCALE, 1.0);
outliner_draw_iconrow(C, block, scene, sl, soops, &te->subtree, 0, xmax, &tempx,
*starty, alpha_fac);
glDisable(GL_BLEND);
}
}
@ -1410,16 +1492,20 @@ static void outliner_draw_tree_element(
te->xs = startx;
te->ys = *starty;
te->xend = startx + offsx;
if (TSELEM_OPEN(tselem, soops)) {
*starty -= UI_UNIT_Y;
for (ten = te->subtree.first; ten; ten = ten->next) {
outliner_draw_tree_element(C, block, fstyle, scene, sl, ar, soops, ten, startx + UI_UNIT_X, starty, te_edit);
for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
/* check if element needs to be drawn grayed out, but also gray out
* childs of a grayed out parent (pass on draw_grayed_out to childs) */
bool draw_childs_grayed_out = draw_grayed_out || (ten->drag_data != NULL);
outliner_draw_tree_element(C, block, fstyle, scene, sl, ar, soops, ten, draw_childs_grayed_out,
startx + UI_UNIT_X, starty, te_edit, te_floating);
}
}
else {
for (ten = te->subtree.first; ten; ten = ten->next) {
for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
outliner_set_coord_tree_element(ten, startx, *starty);
}
@ -1427,19 +1513,76 @@ static void outliner_draw_tree_element(
}
}
static void outliner_draw_hierarchy_lines_recursive(unsigned pos, SpaceOops *soops, ListBase *lb, int startx, int *starty)
/**
* Count how many visible childs (and open grandchilds, great-grandchilds, ...) \a te has.
*/
static int outliner_count_visible_childs(const SpaceOops *soops, const TreeElement *te)
{
if (BLI_listbase_is_empty(lb)) return;
TreeStoreElem *tselem = TREESTORE(te);
int current_count = 0;
if (TSELEM_OPEN(tselem, soops)) {
for (TreeElement *te_child = te->subtree.first; te_child; te_child = te_child->next) {
current_count += outliner_count_visible_childs(soops, te_child);
current_count++;
}
}
return current_count;
}
static void outliner_draw_tree_element_floating(const SpaceOops *soops, const ARegion *ar,
const TreeElement *te_floating)
{
const TreeElement *te_insert = te_floating->drag_data->insert_te;
const int line_width = 2;
unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT);
int coord_y = (te_insert ? te_insert->ys : ((int)ar->v2d.tot.ymax - OL_Y_OFFSET)) - (int)(line_width * 0.5f);
unsigned char col[4];
if (te_insert == te_floating) {
/* don't draw anything */
return;
}
if (te_insert) {
coord_y -= UI_UNIT_Y * outliner_count_visible_childs(soops, te_insert);
}
UI_GetThemeColorShade4ubv(TH_BACK, -40, col);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4ubv(col);
glLineWidth(line_width);
immBegin(PRIM_LINE_STRIP, 2);
immVertex2f(pos, 0, coord_y);
immVertex2f(pos, ar->v2d.cur.xmax, coord_y);
immEnd();
immUnbindProgram();
}
static void outliner_draw_hierarchy_lines_recursive(unsigned pos, SpaceOops *soops, ListBase *lb, int startx,
const unsigned char col[4], bool draw_grayed_out,
int *starty)
{
TreeElement *te;
TreeStoreElem *tselem;
int y1, y2;
if (BLI_listbase_is_empty(lb)) {
return;
}
y1 = y2 = *starty; /* for vertical lines between objects */
for (te = lb->first; te; te = te->next) {
bool draw_childs_grayed_out = draw_grayed_out || (te->drag_data != NULL);
y2 = *starty;
tselem = TREESTORE(te);
immUniformColor4ub(UNPACK3(col), col[3] * (draw_childs_grayed_out ? 0.5f : 1.0f));
/* horizontal line? */
if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE))
immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - 1);
@ -1447,9 +1590,12 @@ static void outliner_draw_hierarchy_lines_recursive(unsigned pos, SpaceOops *soo
*starty -= UI_UNIT_Y;
if (TSELEM_OPEN(tselem, soops))
outliner_draw_hierarchy_lines_recursive(pos, soops, &te->subtree, startx + UI_UNIT_X, starty);
outliner_draw_hierarchy_lines_recursive(pos, soops, &te->subtree, startx + UI_UNIT_X,
col, draw_childs_grayed_out, starty);
}
immUniformColor4ub(UNPACK3(col), col[3] * (draw_grayed_out ? 0.5f : 1.0f));
/* vertical line */
te = lb->last;
if (te->parent || lb->first != lb->last) {
@ -1462,12 +1608,17 @@ static void outliner_draw_hierarchy_lines_recursive(unsigned pos, SpaceOops *soo
static void outliner_draw_hierarchy_lines(SpaceOops *soops, ListBase *lb, int startx, int *starty)
{
VertexFormat *format = immVertexFormat();
unsigned pos = add_attrib(format, "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
unsigned int pos = add_attrib(format, "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
unsigned char col[4];
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
unsigned char col[3];
UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
immUniformColor3ubv(col);
outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, starty);
col[3] = 255;
glEnable(GL_BLEND);
outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty);
glDisable(GL_BLEND);
immUnbindProgram();
}
@ -1532,7 +1683,7 @@ static void outliner_draw_highlights_recursive(
}
/* mouse hover highlights */
if (tselem->flag & TSE_HIGHLIGHTED) {
if ((tselem->flag & TSE_HIGHLIGHTED) || (te->drag_data != NULL)) {
immUniformColor4fv(col_highlight);
immRecti(pos, 0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1);
}
@ -1540,8 +1691,8 @@ static void outliner_draw_highlights_recursive(
*io_start_y -= UI_UNIT_Y;
if (TSELEM_OPEN(tselem, soops)) {
outliner_draw_highlights_recursive(
pos, ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch,
start_x + UI_UNIT_X, io_start_y);
pos, ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch,
start_x + UI_UNIT_X, io_start_y);
}
}
}
@ -1561,7 +1712,7 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOops *soops, int startx,
unsigned pos = add_attrib(format, "pos", GL_INT, 2, CONVERT_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
outliner_draw_highlights_recursive(pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch,
startx, starty);
startx, starty);
immUnbindProgram();
glDisable(GL_BLEND);
}
@ -1572,6 +1723,7 @@ static void outliner_draw_tree(
TreeElement **te_edit)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
TreeElement *te_floating = NULL;
int starty, startx;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only once
@ -1607,7 +1759,11 @@ static void outliner_draw_tree(
starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET;
startx = 0;
for (TreeElement *te = soops->tree.first; te; te = te->next) {
outliner_draw_tree_element(C, block, fstyle, scene, sl, ar, soops, te, startx, &starty, te_edit);
outliner_draw_tree_element(C, block, fstyle, scene, sl, ar, soops, te, te->drag_data != NULL,
startx, &starty, te_edit, &te_floating);
}
if (te_floating) {
outliner_draw_tree_element_floating(soops, ar, te_floating);
}
if (has_restrict_icons) {
@ -1651,6 +1807,7 @@ static void outliner_back(ARegion *ar)
static void outliner_draw_restrictcols(ARegion *ar)
{
glLineWidth(1.0f);
UI_ThemeColorShadeAlpha(TH_BACK, -15, -200);
/* view */

View File

@ -37,6 +37,7 @@
/* internal exports only */
struct wmOperatorType;
struct TreeElement;
struct TreeStoreElem;
struct bContext;
struct Scene;
@ -46,6 +47,13 @@ struct Object;
struct bPoseChannel;
struct EditBone;
/**
* Callback type for reinserting elements at a different position, used to allow user customizable element order.
* Passing scene right now, may be better to allow some custom data.
*/
typedef void (*TreeElementReinsertFunc)(const struct Scene *scene, struct TreeElement *insert_element,
struct TreeElement *insert_after);
typedef struct TreeElement {
struct TreeElement *next, *prev, *parent;
ListBase subtree;
@ -58,7 +66,15 @@ typedef struct TreeElement {
const char *name;
void *directdata; // Armature Bones, Base, Sequence, Strip...
PointerRNA rnaptr; // RNA Pointer
} TreeElement;
/* callbacks */
TreeElementReinsertFunc reinsert;
struct {
/* the element after which we may insert the dragged one (NULL to insert at top) */
struct TreeElement *insert_te;
} *drag_data;
} TreeElement;
#define TREESTORE_ID_TYPE(_id) \
(ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \

View File

@ -28,17 +28,198 @@
* \ingroup spoutliner
*/
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BLI_math.h"
#include "ED_screen.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_view2d.h"
#include "WM_api.h"
#include "WM_types.h"
#include "outliner_intern.h"
enum {
OUTLINER_ITEM_DRAG_CANCEL,
OUTLINER_ITEM_DRAG_CONFIRM,
};
typedef struct OutlinerItemDrag {
TreeElement *dragged_te;
int init_mouse_xy[2];
} OutlinerItemDrag;
static int outliner_item_drag_drop_poll(bContext *C)
{
SpaceOops *soops = CTX_wm_space_outliner(C);
return ED_operator_outliner_active(C) &&
(soops->flag & SO_SKIP_SORT_ALPHA) &&
/* Only collection display mode supported for now. Others need more design work */
ELEM(soops->outlinevis, SO_COLLECTIONS);
}
static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event)
{
const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
return outliner_find_item_at_y(soops, &soops->tree, my);
}
static OutlinerItemDrag *outliner_item_drag_data_create(TreeElement *dragged_te, const int mouse_xy[2])
{
OutlinerItemDrag *drag_data = MEM_mallocN(sizeof(*drag_data), __func__);
drag_data->dragged_te = dragged_te;
copy_v2_v2_int(drag_data->init_mouse_xy, mouse_xy);
return drag_data;
}
static void outliner_item_drag_end(OutlinerItemDrag *op_drag_data)
{
MEM_SAFE_FREE(op_drag_data->dragged_te->drag_data);
MEM_freeN(op_drag_data);
}
static void outliner_item_drag_handle(OutlinerItemDrag *op_drag_data, ARegion *ar, const wmEvent *event)
{
TreeElement *dragged_te = op_drag_data->dragged_te;
const int delta_mouse_y = event->y - op_drag_data->init_mouse_xy[1];
const int cmp_coord = (int)UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
/* by default we don't change the item position */
dragged_te->drag_data->insert_te = dragged_te;
if (delta_mouse_y > 0) {
for (TreeElement *te = dragged_te->prev; te && (cmp_coord >= (te->ys + (UI_UNIT_Y * 0.5f))); te = te->prev) {
/* will be NULL if we want to insert as first element */
dragged_te->drag_data->insert_te = te->prev;
}
}
else {
for (TreeElement *te = dragged_te->next; te && (cmp_coord <= (te->ys + (UI_UNIT_Y * 0.5f))); te = te->next) {
dragged_te->drag_data->insert_te = te;
}
}
}
static bool outliner_item_drag_drop_apply(const Scene *scene, OutlinerItemDrag *op_drag_data)
{
TreeElement *dragged_te = op_drag_data->dragged_te;
TreeElement *insert_after = dragged_te->drag_data->insert_te;
if (insert_after == dragged_te) {
/* No need to do anything */
return false;
}
if (dragged_te->reinsert) {
/* Not sure yet what the best way to handle reordering elements of different types
* (and stored in different lists). For collection display mode this is enough. */
if (!insert_after || (insert_after->reinsert == dragged_te->reinsert)) {
dragged_te->reinsert(scene, dragged_te, insert_after);
}
}
return true;
}
static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
OutlinerItemDrag *op_drag_data = op->customdata;
int retval = OPERATOR_RUNNING_MODAL;
bool redraw = false;
bool skip_rebuild = true;
switch (event->type) {
case EVT_MODAL_MAP:
if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) {
outliner_item_drag_drop_apply(CTX_data_scene(C), op_drag_data);
skip_rebuild = false;
retval = OPERATOR_FINISHED;
}
else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) {
retval = OPERATOR_CANCELLED;
}
else {
BLI_assert(0);
}
WM_event_add_mousemove(C); /* update highlight */
outliner_item_drag_end(op_drag_data);
redraw = true;
break;
case MOUSEMOVE:
outliner_item_drag_handle(op_drag_data, ar, event);
redraw = true;
break;
}
if (skip_rebuild) {
soops->storeflag |= SO_TREESTORE_REDRAW; /* only needs to redraw, no rebuild */
}
if (redraw) {
ED_region_tag_redraw(ar);
}
return retval;
}
static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
TreeElement *te = outliner_item_drag_element_find(soops, ar, event);
if (!te) {
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
}
op->customdata = outliner_item_drag_data_create(te, &event->x);
te->drag_data = MEM_callocN(sizeof(*te->drag_data), __func__);
/* by default we don't change the item position */
te->drag_data->insert_te = te;
/* unset highlighted tree element, dragged one will be highlighted instead */
outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false);
soops->storeflag |= SO_TREESTORE_REDRAW; /* only needs to redraw, no rebuild */
ED_region_tag_redraw(ar);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
/**
* Notes about Outliner Item Drag 'n Drop:
* Right now only collections display mode is supported. But ideally all/most modes would support this. There are
* just some open design questions that have to be answered: do we want to allow mixing order of different data types
* (like render-layers and objects)? Would that be a purely visual change or would that have any other effect? ...
*/
static void OUTLINER_OT_item_drag_drop(wmOperatorType *ot)
{
ot->name = "Drag and Drop Item";
ot->idname = "OUTLINER_OT_item_drag_drop";
ot->description = "Change the hierarchical position of an item by repositioning it using drag and drop";
ot->invoke = outliner_item_drag_drop_invoke;
ot->modal = outliner_item_drag_drop_modal;
ot->poll = outliner_item_drag_drop_poll;
ot->flag = OPTYPE_UNDO;
}
/* ************************** registration **********************************/
void outliner_operatortypes(void)
@ -48,6 +229,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_select_border);
WM_operatortype_append(OUTLINER_OT_item_openclose);
WM_operatortype_append(OUTLINER_OT_item_rename);
WM_operatortype_append(OUTLINER_OT_item_drag_drop);
WM_operatortype_append(OUTLINER_OT_operation);
WM_operatortype_append(OUTLINER_OT_scene_operation);
WM_operatortype_append(OUTLINER_OT_object_operation);
@ -99,6 +281,36 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_collection_objects_deselect);
}
static wmKeyMap *outliner_item_drag_drop_modal_keymap(wmKeyConfig *keyconf)
{
static EnumPropertyItem modal_items[] = {
{OUTLINER_ITEM_DRAG_CANCEL, "CANCEL", 0, "Cancel", ""},
{OUTLINER_ITEM_DRAG_CONFIRM, "CONFIRM", 0, "Confirm/Drop", ""},
{0, NULL, 0, NULL, NULL}
};
const char *map_name = "Outliner Item Drap 'n Drop Modal Map";
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, map_name);
/* this function is called for each spacetype, only needs to add map once */
if (keymap && keymap->modal_items)
return NULL;
keymap = WM_modalkeymap_add(keyconf, map_name, modal_items);
/* items for modal map */
WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, OUTLINER_ITEM_DRAG_CANCEL);
WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, OUTLINER_ITEM_DRAG_CANCEL);
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM);
WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM);
WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM);
WM_modalkeymap_assign(keymap, "OUTLINER_OT_item_drag_drop");
return keymap;
}
void outliner_keymap(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_find(keyconf, "Outliner", SPACE_OUTLINER, 0);
@ -135,6 +347,8 @@ void outliner_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "OUTLINER_OT_operation", RIGHTMOUSE, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "OUTLINER_OT_item_drag_drop", EVT_TWEAK_L, KM_ANY, 0, 0);
WM_keymap_add_item(keymap, "OUTLINER_OT_show_hierarchy", HOMEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "OUTLINER_OT_show_active", PERIODKEY, KM_PRESS, 0, 0);
@ -161,5 +375,7 @@ void outliner_keymap(wmKeyConfig *keyconf)
WM_keymap_verify_item(keymap, "OUTLINER_OT_drivers_add_selected", DKEY, KM_PRESS, 0, 0);
WM_keymap_verify_item(keymap, "OUTLINER_OT_drivers_delete_selected", DKEY, KM_PRESS, KM_ALT, 0);
outliner_item_drag_drop_modal_keymap(keyconf);
}

View File

@ -1377,6 +1377,15 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops)
}
}
static void outliner_collections_reorder(const Scene *scene, TreeElement *insert_element, TreeElement *insert_after)
{
SceneLayer *sl = BKE_scene_layer_render_active(scene);
LayerCollection *insert_coll = insert_element->directdata;
LayerCollection *insert_after_coll = insert_after ? insert_after->directdata : NULL;
BKE_layer_collection_reinsert_after(scene, sl, insert_coll, insert_after_coll);
}
static void outliner_add_collections_recursive(SpaceOops *soops, ListBase *tree, Scene *scene,
ListBase *layer_collections, TreeElement *parent_ten)
{
@ -1385,6 +1394,7 @@ static void outliner_add_collections_recursive(SpaceOops *soops, ListBase *tree,
ten->name = collection->scene_collection->name;
ten->directdata = collection;
ten->reinsert = outliner_collections_reorder;
for (LinkData *link = collection->object_bases.first; link; link = link->next) {
outliner_add_element(soops, &ten->subtree, ((Base *)link->data)->object, NULL, 0, 0);
@ -1513,18 +1523,15 @@ static void outliner_sort(ListBase *lb)
{
TreeElement *te;
TreeStoreElem *tselem;
int totelem = 0;
te = lb->last;
if (te == NULL) return;
tselem = TREESTORE(te);
/* sorting rules; only object lists, ID lists, or deformgroups */
if ( ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || (tselem->type == 0 && te->idcode == ID_OB)) {
/* count first */
for (te = lb->first; te; te = te->next) totelem++;
if (ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || (tselem->type == 0 && te->idcode == ID_OB)) {
int totelem = BLI_listbase_count(lb);
if (totelem > 1) {
tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array");
tTreeSort *tp = tear;
@ -1863,9 +1870,7 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SceneLayer *sl, SpaceOops
ten = outliner_add_element(soops, &soops->tree, OBACT_NEW, NULL, 0, 0);
}
if ((soops->flag & SO_SKIP_SORT_ALPHA) == 0) {
outliner_sort(&soops->tree);
}
outliner_sort(&soops->tree);
outliner_filter_tree(soops, &soops->tree);
BKE_main_id_clear_newpoins(mainvar);