GPv3: Drag & drop reordering & inserting in the layer tree UI

Note: Change applies to Grease Pencil 3.0 only (experimental feature).

Enables use of drag & drop to reorder grease pencil layers through the layer
tree UI, as well as inserting layers into groups. This is an intuitive and
often requested method of managing such data-structures. Visual feedback should
be improved still, and the gap between items be removed, to reduce flickering
while dragging. These are general improvements for tree views however which
should be done separately.

There is no support yet for dragging layer groups, this requires further
changes in the internal grease pencil APIs.

#109825 introduced the necessary drag & drop support for tree views, #109824
prepared the internal grease pencil API for it.

Pull Request: https://projects.blender.org/blender/blender/pulls/109826
This commit is contained in:
Julian Eisel
2023-07-11 15:05:17 +02:00
committed by Julian Eisel
parent 3757ec7ee8
commit d31a0e8393
4 changed files with 148 additions and 11 deletions

View File

@@ -21,6 +21,7 @@ set(INC
../../render
../../windowmanager
../../../../intern/ghost
../../../../extern/fmtlib/include
../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna

View File

@@ -18,10 +18,133 @@
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include <fmt/format.h>
namespace blender::ui::greasepencil {
using namespace blender::bke::greasepencil;
class LayerTreeView : public AbstractTreeView {
public:
explicit LayerTreeView(GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
void build_tree() override;
private:
void build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node);
GreasePencil &grease_pencil_;
};
class LayerNodeDropTarget : public TreeViewItemDropTarget {
TreeNode &drop_tree_node_;
public:
LayerNodeDropTarget(AbstractTreeView &view, TreeNode &drop_tree_node, DropBehavior behavior)
: TreeViewItemDropTarget(view, behavior), drop_tree_node_(drop_tree_node)
{
}
bool can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const override
{
return drag.type == WM_DRAG_GREASE_PENCIL_LAYER;
}
std::string drop_tooltip(const DragInfo &drag_info) const override
{
const wmDragGreasePencilLayer *drag_grease_pencil =
static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
Layer &drag_layer = drag_grease_pencil->layer->wrap();
std::string_view drag_name = drag_layer.name();
std::string_view drop_name = drop_tree_node_.name;
switch (drag_info.drop_location) {
case DropLocation::Into:
return fmt::format(TIP_("Move layer {} into {}"), drag_name, drop_name);
case DropLocation::Before:
return fmt::format(TIP_("Move layer {} above {}"), drag_name, drop_name);
case DropLocation::After:
return fmt::format(TIP_("Move layer {} below {}"), drag_name, drop_name);
default:
BLI_assert_unreachable();
break;
}
return "";
}
bool on_drop(struct bContext * /*C*/, const DragInfo &drag_info) const override
{
const wmDragGreasePencilLayer *drag_grease_pencil =
static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
Layer &drag_layer = drag_grease_pencil->layer->wrap();
LayerGroup &drag_parent = drag_layer.parent_group();
LayerGroup *drop_parent_group = drop_tree_node_.parent_group();
if (!drop_parent_group) {
/* Root node is not added to the tree view, so there should never be a drop target for this.
*/
BLI_assert_unreachable();
return false;
}
switch (drag_info.drop_location) {
case DropLocation::Into: {
BLI_assert_msg(drop_tree_node_.is_group(),
"Inserting should not be possible for layers, only for groups, because "
"only groups use DropBehavior::Reorder_and_Insert");
LayerGroup &drop_group = drop_tree_node_.as_group_for_write();
drag_parent.unlink_node(&drag_layer.as_node());
drop_group.add_layer(&drag_layer);
return true;
}
case DropLocation::Before:
drag_parent.unlink_node(&drag_layer.as_node());
/* Draw order is inverted, so inserting before means inserting below. */
drop_parent_group->add_layer_after(&drag_layer, &drop_tree_node_);
return true;
case DropLocation::After:
drag_parent.unlink_node(&drag_layer.as_node());
/* Draw order is inverted, so inserting after means inserting above. */
drop_parent_group->add_layer_before(&drag_layer, &drop_tree_node_);
return true;
}
return false;
}
};
class LayerViewItemDragController : public AbstractViewItemDragController {
GreasePencil &grease_pencil_;
Layer &dragged_layer_;
public:
LayerViewItemDragController(LayerTreeView &tree_view, GreasePencil &grease_pencil, Layer &layer)
: AbstractViewItemDragController(tree_view),
grease_pencil_(grease_pencil),
dragged_layer_(layer)
{
}
eWM_DragDataType get_drag_type() const override
{
return WM_DRAG_GREASE_PENCIL_LAYER;
}
void *create_drag_data() const override
{
wmDragGreasePencilLayer *drag_data = MEM_new<wmDragGreasePencilLayer>(__func__);
drag_data->layer = &dragged_layer_;
return drag_data;
}
void on_drag_start() override
{
grease_pencil_.set_active_layer(&dragged_layer_);
}
};
class LayerViewItem : public AbstractTreeViewItem {
public:
LayerViewItem(GreasePencil &grease_pencil, Layer &layer)
@@ -74,6 +197,18 @@ class LayerViewItem : public AbstractTreeViewItem {
return layer_.name();
}
std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
{
return std::make_unique<LayerViewItemDragController>(
static_cast<LayerTreeView &>(get_tree_view()), grease_pencil_, layer_);
}
std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
{
return std::make_unique<LayerNodeDropTarget>(
get_tree_view(), layer_.as_node(), DropBehavior::Reorder);
}
private:
GreasePencil &grease_pencil_;
Layer &layer_;
@@ -137,6 +272,12 @@ class LayerGroupViewItem : public AbstractTreeViewItem {
return group_.name();
}
std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
{
return std::make_unique<LayerNodeDropTarget>(
get_tree_view(), group_.as_node(), DropBehavior::ReorderAndInsert);
}
private:
GreasePencil &grease_pencil_;
LayerGroup &group_;
@@ -160,17 +301,6 @@ class LayerGroupViewItem : public AbstractTreeViewItem {
}
};
class LayerTreeView : public AbstractTreeView {
public:
explicit LayerTreeView(GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
void build_tree() override;
private:
void build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node);
GreasePencil &grease_pencil_;
};
void LayerTreeView::build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node)
{
using namespace blender::bke::greasepencil;

View File

@@ -1088,6 +1088,7 @@ typedef enum eWM_DragDataType {
WM_DRAG_COLOR,
WM_DRAG_DATASTACK,
WM_DRAG_ASSET_CATALOG,
WM_DRAG_GREASE_PENCIL_LAYER,
} eWM_DragDataType;
typedef enum eWM_DragFlags {
@@ -1146,6 +1147,10 @@ typedef struct wmDragPath {
int file_type; /* eFileSel_File_Types */
} wmDragPath;
typedef struct wmDragGreasePencilLayer {
struct GreasePencilLayer *layer;
} wmDragGreasePencilLayer;
typedef char *(*WMDropboxTooltipFunc)(struct bContext *,
struct wmDrag *,
const int xy[2],

View File

@@ -201,6 +201,7 @@ wmDrag *WM_drag_data_create(
WM_drag_add_local_ID(drag, static_cast<ID *>(poin), nullptr);
}
break;
case WM_DRAG_GREASE_PENCIL_LAYER:
case WM_DRAG_ASSET:
case WM_DRAG_ASSET_CATALOG:
/* Move ownership of poin to wmDrag. */