diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2017-02-22 18:02:43 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2017-02-22 19:25:00 +0300 |
commit | 910b7dec8dca25473d98c23f7c4fd8e44fafd47d (patch) | |
tree | c4dba049c8670704a7d789cf6f16f0e711dd0ec3 | |
parent | e003499f6ffed72f3a166cca4d59e855774767d2 (diff) |
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.
-rw-r--r-- | source/blender/blenkernel/BKE_collection.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_layer.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/collection.c | 33 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/layer.c | 67 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface_icons.h | 1 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_icons.c | 5 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_draw.c | 445 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_intern.h | 18 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_ops.c | 218 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tree.c | 25 |
10 files changed, 657 insertions, 161 deletions
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 4d5de723dce..644bcef6226 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -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); diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 641cc969e71..d208cee5b4b 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -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); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 100cbf2dd45..24f97c00afb 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -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) { @@ -174,9 +177,35 @@ bool BKE_collection_remove(Scene *scene, SceneCollection *sc) } /** + * 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; } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 621ac51aba3..3653a9ac651 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -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); @@ -492,6 +492,71 @@ int BKE_layer_collection_findindex(SceneLayer *sl, LayerCollection *lc) } /** + * \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 */ diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 945ac1b6db9..cee68ed361c 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -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); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index f61eb99a621..7d87527bdad 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -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); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index d15c3d67d45..48a21075608 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -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 */ diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 4685744c570..6a4d53449e9 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -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) || \ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 68a6ea85b12..d94ccde57e5 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -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); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 62ab69ebda2..11b25f7fc87 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -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); |