WIP: UI: tree-view drag & drop reordering #109283

Closed
Julian Eisel wants to merge 6 commits from JulianEisel/blender:temp-tree-view-drag-drop-reorder into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
22 changed files with 564 additions and 143 deletions

View File

@ -133,6 +133,11 @@ class TreeNode : public ::GreasePencilLayerTreeNode {
* \note This results in undefined behavior if the node is not a Layer.
*/
Layer &as_layer_for_write();
/**
* \returns the parent layer group or null for root nodes.
*/
LayerGroup *parent_group() const;
};
/**
@ -296,6 +301,12 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
bool is_visible() const;
bool is_locked() const;
/**
* \returns the layer as a `TreeNode`.
*/
const TreeNode &as_node() const;
TreeNode &as_node();
/**
* Adds a group at the end of this group.
*/
@ -317,14 +328,14 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
/**
* Adds a layer before \a link and returns it.
*/
Layer &add_layer_before(Layer *layer, Layer *link);
Layer &add_layer_before(StringRefNull name, Layer *link);
Layer &add_layer_before(Layer *layer, TreeNode *link);
Layer &add_layer_before(StringRefNull name, TreeNode *link);
/**
* Adds a layer after \a link and returns it.
*/
Layer &add_layer_after(Layer *layer, Layer *link);
Layer &add_layer_after(StringRefNull name, Layer *link);
Layer &add_layer_after(Layer *layer, TreeNode *link);
Layer &add_layer_after(StringRefNull name, TreeNode *link);
/**
* Returns the number of direct nodes in this group.
@ -340,7 +351,7 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
* Tries to unlink the layer from the list of nodes in this group.
* \returns true, if the layer was successfully unlinked.
*/
bool unlink_layer(Layer *link);
bool unlink_node(TreeNode *link);
/**
* Returns a `Span` of pointers to all the `TreeNode`s in this group.
@ -382,6 +393,15 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
void tag_nodes_cache_dirty() const;
};
inline const TreeNode &LayerGroup::as_node() const
{
return *reinterpret_cast<const TreeNode *>(this);
}
inline TreeNode &LayerGroup::as_node()
{
return *reinterpret_cast<TreeNode *>(this);
}
inline const TreeNode &Layer::as_node() const
{
return *reinterpret_cast<const TreeNode *>(this);

View File

@ -401,6 +401,11 @@ Layer &TreeNode::as_layer_for_write()
return *reinterpret_cast<Layer *>(this);
}
LayerGroup *TreeNode::parent_group() const
{
return &this->parent->wrap();
}
LayerMask::LayerMask()
{
this->layer_name = nullptr;
@ -738,7 +743,7 @@ Layer &LayerGroup::add_layer(Layer *layer)
return *layer;
}
Layer &LayerGroup::add_layer_before(Layer *layer, Layer *link)
Layer &LayerGroup::add_layer_before(Layer *layer, TreeNode *link)
{
BLI_assert(layer != nullptr && link != nullptr);
BLI_insertlinkbefore(&this->children,
@ -749,7 +754,7 @@ Layer &LayerGroup::add_layer_before(Layer *layer, Layer *link)
return *layer;
}
Layer &LayerGroup::add_layer_after(Layer *layer, Layer *link)
Layer &LayerGroup::add_layer_after(Layer *layer, TreeNode *link)
{
BLI_assert(layer != nullptr && link != nullptr);
BLI_insertlinkafter(&this->children,
@ -766,13 +771,13 @@ Layer &LayerGroup::add_layer(StringRefNull name)
return this->add_layer(new_layer);
}
Layer &LayerGroup::add_layer_before(StringRefNull name, Layer *link)
Layer &LayerGroup::add_layer_before(StringRefNull name, TreeNode *link)
{
Layer *new_layer = MEM_new<Layer>(__func__, name);
return this->add_layer_before(new_layer, link);
}
Layer &LayerGroup::add_layer_after(StringRefNull name, Layer *link)
Layer &LayerGroup::add_layer_after(StringRefNull name, TreeNode *link)
{
Layer *new_layer = MEM_new<Layer>(__func__, name);
return this->add_layer_after(new_layer, link);
@ -789,11 +794,11 @@ int64_t LayerGroup::num_nodes_total() const
return this->runtime->nodes_cache_.size();
}
bool LayerGroup::unlink_layer(Layer *link)
bool LayerGroup::unlink_node(TreeNode *link)
{
if (BLI_remlink_safe(&this->children, link)) {
this->tag_nodes_cache_dirty();
link->base.parent = nullptr;
link->parent = nullptr;
return true;
}
return false;
@ -1434,14 +1439,14 @@ blender::bke::greasepencil::Layer &GreasePencil::add_layer(
blender::bke::greasepencil::Layer &GreasePencil::add_layer_after(
blender::bke::greasepencil::LayerGroup &group,
blender::bke::greasepencil::Layer *layer,
blender::bke::greasepencil::TreeNode *link,
const blender::StringRefNull name)
{
using namespace blender;
VectorSet<StringRefNull> names = get_node_names(*this);
std::string unique_name(name.c_str());
unique_layer_name(names, unique_name.data());
return group.add_layer_after(unique_name, layer);
return group.add_layer_after(unique_name, link);
}
blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::StringRefNull name)
@ -1551,7 +1556,7 @@ void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
}
/* Unlink the layer from the parent group. */
layer.parent_group().unlink_layer(&layer);
layer.parent_group().unlink_node(&layer.as_node());
/* Remove drawings. */
/* TODO: In the future this should only remove drawings when the user count hits zero. */

View File

@ -36,7 +36,7 @@ static int grease_pencil_layer_add_exec(bContext *C, wmOperator *op)
if (grease_pencil.has_active_layer()) {
LayerGroup &active_group = grease_pencil.get_active_layer()->parent_group();
Layer &new_layer = grease_pencil.add_layer_after(
active_group, grease_pencil.get_active_layer_for_write(), new_layer_name);
active_group, &grease_pencil.get_active_layer_for_write()->as_node(), new_layer_name);
grease_pencil.set_active_layer(&new_layer);
grease_pencil.insert_blank_frame(new_layer, scene->r.cfra, 0, BEZT_KEYTYPE_KEYFRAME);
}
@ -133,19 +133,19 @@ static int grease_pencil_layer_reorder_exec(bContext *C, wmOperator *op)
}
Layer *active_layer = grease_pencil.get_active_layer_for_write();
active_layer->parent_group().unlink_layer(active_layer);
active_layer->parent_group().unlink_node(&active_layer->as_node());
switch (reorder_location) {
case LAYER_REORDER_ABOVE: {
/* Note: The layers are stored from bottom to top, so inserting above (visually), means
* inserting the link after the target. */
target_layer->parent_group().add_layer_after(active_layer, target_layer);
target_layer->parent_group().add_layer_after(active_layer, &target_layer->as_node());
break;
}
case LAYER_REORDER_BELOW: {
/* Note: The layers are stored from bottom to top, so inserting below (visually), means
* inserting the link before the target. */
target_layer->parent_group().add_layer_before(active_layer, target_layer);
target_layer->parent_group().add_layer_before(active_layer, &target_layer->as_node());
break;
}
default:

View File

@ -1,4 +1,4 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
@ -45,14 +45,8 @@ struct wmNotifier;
namespace blender::ui {
class AbstractViewItem;
class AbstractViewItemDropTarget;
class AbstractViewItemDragController;
/**
* The view drop target can share logic with the view item drop target for now, so just an alias.
*/
using AbstractViewDropTarget = AbstractViewItemDropTarget;
class AbstractView {
friend class AbstractViewItem;
friend struct ::ViewLink;
@ -70,17 +64,21 @@ class AbstractView {
/* See #get_bounds(). */
std::optional<rcti> bounds_;
protected:
const ARegion *region_ = nullptr;
const uiBlock *block_ = nullptr;
public:
virtual ~AbstractView() = default;
/**
* If a view wants to support dropping data into it, it has to return a drop target here.
* That is an object implementing #AbstractViewDropTarget.
* That is an object implementing #DropTargetInterface.
*
* \note This drop target may be requested for each event. The view doesn't keep the drop target
* around currently. So it cannot contain persistent state.
*/
virtual std::unique_ptr<AbstractViewDropTarget> create_drop_target();
virtual std::unique_ptr<DropTargetInterface> create_drop_target();
/** Listen to a notifier, returning true if a redraw is needed. */
virtual bool listen(const wmNotifier &) const;
@ -188,7 +186,7 @@ class AbstractViewItem {
* \note This drop target may be requested for each event. The view doesn't keep a drop target
* around currently. So it can not contain persistent state.
*/
virtual std::unique_ptr<AbstractViewItemDropTarget> create_drop_target();
virtual std::unique_ptr<DropTargetInterface> create_item_drop_target();
/** Return the result of #is_filtered_visible(), but ensure the result is cached so it's only
* queried once per redraw. */
@ -197,6 +195,8 @@ class AbstractViewItem {
/** Get the view this item is registered for using #AbstractView::register_item(). */
AbstractView &get_view() const;
uiButViewItem *view_item_button() const;
/** Disable the interacting with this item, meaning the buttons drawn will be disabled and there
* will be no mouse hover feedback for the view row. */
void disable_interaction();
@ -286,23 +286,6 @@ class AbstractViewItemDragController {
template<class ViewType> inline ViewType &get_view() const;
};
/**
* Class to define the behavior when dropping something onto/into a view item, plus the behavior
* when dragging over this item. An item can return a drop target for itself via a custom
* implementation of #AbstractViewItem::create_drop_target().
*/
class AbstractViewItemDropTarget : public DropTargetInterface {
protected:
AbstractView &view_;
public:
AbstractViewItemDropTarget(AbstractView &view);
/** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
* exception if the view is not of the requested type. */
template<class ViewType> inline ViewType &get_view() const;
};
template<class ViewType> ViewType &AbstractViewItemDragController::get_view() const
{
static_assert(std::is_base_of<AbstractView, ViewType>::value,
@ -310,13 +293,6 @@ template<class ViewType> ViewType &AbstractViewItemDragController::get_view() co
return dynamic_cast<ViewType &>(view_);
}
template<class ViewType> ViewType &AbstractViewItemDropTarget::get_view() const
{
static_assert(std::is_base_of<AbstractView, ViewType>::value,
"Type must derive from and implement the ui::AbstractView interface");
return dynamic_cast<ViewType &>(view_);
}
/** \} */
} // namespace blender::ui

View File

@ -27,6 +27,7 @@ struct View2D;
namespace blender::ui {
class AbstractGridView;
class AbstractGridViewItemDropTarget;
/* ---------------------------------------------------------------------- */
/** \name Grid-View Item Type
@ -63,6 +64,9 @@ class AbstractGridViewItem : public AbstractViewItem {
*/
virtual std::optional<bool> should_be_active() const;
virtual std::unique_ptr<DropTargetInterface> create_item_drop_target() final;
virtual std::unique_ptr<AbstractGridViewItemDropTarget> create_drop_target();
/**
* Activates this item, deactivates other items, and calls the
* #AbstractGridViewItem::on_activate() function.
@ -156,6 +160,31 @@ class AbstractGridView : public AbstractView {
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Drag & Drop
* \{ */
/**
* Class to define the behavior when dropping something onto/into a view item, plus the behavior
* when dragging over this item. An item can return a drop target for itself via a custom
* implementation of #AbstractGridViewItem::create_drop_target().
*/
class AbstractGridViewItemDropTarget : public DropTargetInterface {
protected:
AbstractGridView &view_;
public:
AbstractGridViewItemDropTarget(AbstractGridView &view);
std::optional<DropLocation> determine_drop_location(const wmEvent &event) const;
/** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
* exception if the view is not of the requested type. */
template<class ViewType> inline ViewType &get_view() const;
};
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Grid-View Builder
*
@ -230,4 +259,11 @@ template<class ItemT, typename... Args> inline ItemT &AbstractGridView::add_item
return dynamic_cast<ItemT &>(add_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
}
template<class ViewType> ViewType &AbstractGridViewItemDropTarget::get_view() const
{
static_assert(std::is_base_of<AbstractGridView, ViewType>::value,
"Type must derive from and implement the ui::AbstractGridView interface");
return dynamic_cast<ViewType &>(view_);
}
} // namespace blender::ui

View File

@ -33,6 +33,7 @@ struct uiSearchItems;
struct uiViewHandle;
struct uiViewItemHandle;
struct wmDrag;
struct wmEvent;
void UI_but_func_set(uiBut *but, std::function<void(bContext &)> func);
void UI_but_func_pushed_state_set(uiBut *but, std::function<bool(const uiBut &)> func);
@ -41,6 +42,7 @@ namespace blender::ui {
class AbstractGridView;
class AbstractTreeView;
class DropTargetInterface;
/**
* An item in a breadcrumb-like context. Currently this struct is very simple, but more
@ -67,15 +69,44 @@ void attribute_search_add_items(StringRefNull str,
uiSearchItems *items,
bool is_first);
enum DropLocation {
DROP_INTO,
DROP_BEFORE,
DROP_AFTER,
};
struct DragInfo {
const wmDrag &drag_data;
const wmEvent &event;
const DropLocation drop_location;
DragInfo(const wmDrag &drag, const wmEvent &event, DropLocation drop_location);
};
/**
* Drop targets sometimes want to support multiple behaviors (e.g. inserting into vs. inserting
* before/after). This can be used to toggle that, #DropTargetInterface::determine_drop_location()
* can then use this to return the wanted drop location (before, into or after).
*/
enum class DropBehavior {
Reorder,
Insert,
Reorder_and_Insert,
};
/**
* This provides a common interface for UI elements that want to support dragging & dropping
* entities into/onto them. With it, the element can determine if the dragged entity can be dropped
* onto itself, provide feedback while dragging and run custom code for the dropping.
*
* Note that this is just an interface. A #wmDropBox is needed to request instances of it from a UI
* element and call its functions. For example the drop box using "UI_OT_view_drop" implements
* dropping for views and view items via this interface. To support other kinds of UI elements,
* similar drop boxes would be necessary.
* By default the drop target behaves so that data can be dragged into or onto it.
* #determine_drop_location() can be overridden to change that.
*
* Note that this is just an interface (not in the strict sense of a Java/C# interface though). A
* #wmDropBox is needed to request instances of it from a UI element and call its functions. For
* example the drop box using "UI_OT_view_drop" implements dropping for views and view items via
* this interface. To support other kinds of UI elements, similar drop boxes would be necessary.
*
*/
class DropTargetInterface {
public:
@ -91,32 +122,41 @@ class DropTargetInterface {
* a non-null pointer.
*/
virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
virtual std::optional<DropLocation> determine_drop_location(const wmEvent &event) const;
/**
* Custom text to display when dragging over the element using this drop target. Should
* explain what happens when dropping the data onto this UI element. Will only be used if
* #DropTargetInterface::can_drop() returns true, so the implementing override doesn't have
* to check that again. The returned value must be a translated string.
*/
virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
virtual std::string drop_tooltip(const DragInfo &drag) const = 0;
/**
* Execute the logic to apply a drop of the data dragged with \a drag onto/into the UI element
* this drop target is for.
*/
virtual bool on_drop(bContext *C, const wmDrag &drag) const = 0;
virtual bool on_drop(bContext *C, const DragInfo &drag) const = 0;
};
bool drop_target_can_drop(const DropTargetInterface &drop_target,
const wmDrag &drag,
const char **r_disabled_hint);
/**
* Let a drop target handle a drop event.
* \return True if the dropping was successful.
*/
bool drop_target_apply_drop(bContext &C,
const wmEvent &event,
const DropTargetInterface &drop_target,
const ListBase &drags);
/**
* Call #DropTargetInterface::drop_tooltip() and return the result as newly allocated C string
* (unless the result is empty, returns null then). Needs freeing with MEM_freeN().
*/
char *drop_target_tooltip(const DropTargetInterface &drop_target, const wmDrag &drag);
char *drop_target_tooltip(const DropTargetInterface &drop_target,
const wmDrag &drag,
const wmEvent &event);
std::unique_ptr<DropTargetInterface> view_drop_target(uiViewHandle *view_handle);
std::unique_ptr<DropTargetInterface> view_item_drop_target(uiViewItemHandle *item_handle);

View File

@ -32,6 +32,7 @@ namespace blender::ui {
class AbstractTreeView;
class AbstractTreeViewItem;
class AbstractTreeViewItemDropTarget;
/* ---------------------------------------------------------------------- */
/** \name Tree-View Item Container
@ -114,6 +115,7 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
friend class AbstractTreeViewItem;
friend class TreeViewBuilder;
friend class AbstractTreeViewItemDropTarget;
public:
virtual ~AbstractTreeView() = default;
@ -122,6 +124,11 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
/**
* \param xy: The mouse coordinates in window space.
*/
AbstractTreeViewItem *find_hovered(const int xy[2]);
/** Visual feature: Define a number of item rows the view will always show at minimum. If there
* are fewer items, empty dummy items will be added. These contribute to the view bounds, so the
* drop target of the view includes them, but they are not interactive (e.g. no mouse-hover
@ -182,6 +189,9 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain
virtual void build_row(uiLayout &row) = 0;
virtual std::unique_ptr<DropTargetInterface> create_item_drop_target() final;
virtual std::unique_ptr<AbstractTreeViewItemDropTarget> create_drop_target();
AbstractTreeView &get_tree_view() const;
void begin_renaming();
@ -192,6 +202,7 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain
* can't be sure about the item state.
*/
bool is_collapsed() const;
bool is_collapsible() const;
protected:
/**
@ -247,12 +258,9 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain
* Note that this does a linear lookup in the old block, so isn't too great performance-wise.
*/
bool is_hovered() const;
bool is_collapsible() const;
void ensure_parents_uncollapsed();
uiButViewItem *view_item_button() const;
private:
static void tree_row_click_fn(struct bContext *, void *, void *);
static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *);
@ -317,6 +325,37 @@ class BasicTreeViewItem : public AbstractTreeViewItem {
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Drag & Drop
* \{ */
/**
* Class to define the behavior when dropping something onto/into a view item, plus the behavior
* when dragging over this item. An item can return a drop target for itself via a custom
* implementation of #AbstractTreeViewItem::create_drop_target().
*
* By default the drop target only supports dropping into/onto itself. To support
* inserting/reordering behavior, where dropping before or after the drop-target is supported, pass
* a different #DropBehavior to the constructor.
*/
class AbstractTreeViewItemDropTarget : public DropTargetInterface {
protected:
AbstractTreeView &view_;
const DropBehavior behavior_;
public:
AbstractTreeViewItemDropTarget(AbstractTreeView &view,
DropBehavior behavior = DropBehavior::Insert);
std::optional<DropLocation> determine_drop_location(const wmEvent &event) const;
/** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
* exception if the view is not of the requested type. */
template<class ViewType> inline ViewType &get_view() const;
};
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Tree-View Builder
* \{ */
@ -343,4 +382,11 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args)
add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
}
template<class ViewType> ViewType &AbstractTreeViewItemDropTarget::get_view() const
{
static_assert(std::is_base_of<AbstractTreeView, ViewType>::value,
"Type must derive from and implement the ui::AbstractTreeView interface");
return dynamic_cast<ViewType &>(view_);
}
} // namespace blender::ui

View File

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

View File

@ -10,24 +10,64 @@
namespace blender::ui {
DragInfo::DragInfo(const wmDrag &drag, const wmEvent &event, const DropLocation drop_location)
: drag_data(drag), event(event), drop_location(drop_location)
{
}
std::optional<DropLocation> DropTargetInterface::determine_drop_location(
const wmEvent & /*event*/) const
{
return DROP_INTO;
}
bool drop_target_can_drop(const DropTargetInterface &drop_target,
const wmDrag &drag,
const char **r_disabled_hint)
{
return drop_target.can_drop(drag, r_disabled_hint);
}
bool drop_target_apply_drop(bContext &C,
const wmEvent &event,
const DropTargetInterface &drop_target,
const ListBase &drags)
{
const char *disabled_hint_dummy = nullptr;
LISTBASE_FOREACH (const wmDrag *, drag, &drags) {
if (drop_target.can_drop(*drag, &disabled_hint_dummy)) {
return drop_target.on_drop(&C, *drag);
if (!drop_target.can_drop(*drag, &disabled_hint_dummy)) {
return false;
}
std::optional<DropLocation> drop_location = drop_target.determine_drop_location(event);
if (!drop_location) {
return false;
}
const DragInfo drag_info{*drag, event, *drop_location};
return drop_target.on_drop(&C, drag_info);
}
return false;
}
char *drop_target_tooltip(const DropTargetInterface &drop_target, const wmDrag &drag)
char *drop_target_tooltip(const DropTargetInterface &drop_target,
const wmDrag &drag,
const wmEvent &event)
{
const std::string tooltip = drop_target.drop_tooltip(drag);
const char *disabled_hint_dummy = nullptr;
if (!drop_target.can_drop(drag, &disabled_hint_dummy)) {
return nullptr;
}
std::optional<DropLocation> drop_location = drop_target.determine_drop_location(event);
if (!drop_location) {
return nullptr;
}
const DragInfo drag_info{drag, event, *drop_location};
const std::string tooltip = drop_target.drop_tooltip(drag_info);
return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str());
}

View File

@ -45,15 +45,16 @@ static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
}
drag->drop_state.free_disabled_info = false;
return drop_target->can_drop(*drag, &drag->drop_state.disabled_info);
return drop_target_can_drop(*drop_target, *drag, &drag->drop_state.disabled_info);
}
static char *ui_view_drop_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox * /*drop*/)
{
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
std::unique_ptr<DropTargetInterface> drop_target = region_views_find_drop_target_at(region, xy);
return drop_target_tooltip(*drop_target, *drag);
return drop_target_tooltip(*drop_target, *drag, *win->eventstate);
}
/** \} */

View File

@ -2404,15 +2404,17 @@ static int ui_view_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
const ARegion *region = CTX_wm_region(C);
ARegion *region = CTX_wm_region(C);
std::unique_ptr<DropTargetInterface> drop_target = region_views_find_drop_target_at(region,
event->xy);
if (!drop_target_apply_drop(*C, *drop_target, *static_cast<const ListBase *>(event->customdata)))
if (!drop_target_apply_drop(
*C, *event, *drop_target, *static_cast<const ListBase *>(event->customdata)))
{
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}

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 AbstractTreeViewItemDropTarget {
TreeNode &drop_tree_node_;
public:
LayerNodeDropTarget(AbstractTreeView &view, TreeNode &drop_tree_node, DropBehavior behavior)
: AbstractTreeViewItemDropTarget(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 DROP_INTO:
return fmt::format(TIP_("Move layer {} into {}"), drag_name, drop_name);
case DROP_BEFORE:
return fmt::format(TIP_("Move layer {} before {}"), drag_name, drop_name);
case DROP_AFTER:
return fmt::format(TIP_("Move layer {} after {}"), 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 DROP_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 DROP_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 DROP_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<AbstractTreeViewItemDropTarget> 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<AbstractTreeViewItemDropTarget> create_drop_target() override
{
return std::make_unique<LayerNodeDropTarget>(
get_tree_view(), group_.as_node(), DropBehavior::Reorder_and_Insert);
}
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

@ -35,12 +35,9 @@ namespace blender::ui::light_linking {
namespace {
class CollectionDropTarget : public AbstractViewItemDropTarget {
class CollectionDropTarget : public DropTargetInterface {
public:
CollectionDropTarget(AbstractView &view, Collection &collection)
: AbstractViewItemDropTarget(view), collection_(collection)
{
}
CollectionDropTarget(Collection &collection) : collection_(collection) {}
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
{
@ -64,17 +61,17 @@ class CollectionDropTarget : public AbstractViewItemDropTarget {
return true;
}
std::string drop_tooltip(const wmDrag & /*drag*/) const override
std::string drop_tooltip(const DragInfo & /*drag*/) const override
{
return TIP_("Add to light linking collection");
}
bool on_drop(struct bContext *C, const wmDrag &drag) const override
bool on_drop(struct bContext *C, const DragInfo &drag) const override
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
LISTBASE_FOREACH (wmDragID *, drag_id, &drag.ids) {
LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
BKE_light_linking_add_receiver_to_collection(
bmain, &collection_, drag_id->id, COLLECTION_LIGHT_LINKING_STATE_INCLUDE);
}
@ -217,9 +214,9 @@ class CollectionView : public AbstractTreeView {
}
}
std::unique_ptr<AbstractViewDropTarget> create_drop_target() override
std::unique_ptr<DropTargetInterface> create_drop_target() override
{
return std::make_unique<CollectionDropTarget>(*this, collection_);
return std::make_unique<CollectionDropTarget>(collection_);
}
private:

View File

@ -66,7 +66,7 @@ void AbstractView::update_from_old(uiBlock &new_block)
/** \name Default implementations of virtual functions
* \{ */
std::unique_ptr<AbstractViewDropTarget> AbstractView::create_drop_target()
std::unique_ptr<DropTargetInterface> AbstractView::create_drop_target()
{
/* There's no drop target (and hence no drop support) by default. */
return nullptr;

View File

@ -197,7 +197,7 @@ std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_co
return nullptr;
}
std::unique_ptr<AbstractViewItemDropTarget> AbstractViewItem::create_drop_target()
std::unique_ptr<DropTargetInterface> AbstractViewItem::create_item_drop_target()
{
/* There's no drop target (and hence no drop support) by default. */
return nullptr;
@ -210,8 +210,6 @@ void AbstractViewItemDragController::on_drag_start()
/* Do nothing by default. */
}
AbstractViewItemDropTarget::AbstractViewItemDropTarget(AbstractView &view) : view_(view) {}
/** \} */
/* ---------------------------------------------------------------------- */
@ -227,6 +225,11 @@ AbstractView &AbstractViewItem::get_view() const
return *view_;
}
uiButViewItem *AbstractViewItem::view_item_button() const
{
return view_item_but_;
}
void AbstractViewItem::disable_activatable()
{
is_activatable_ = false;
@ -258,7 +261,7 @@ bool AbstractViewItem::is_active() const
std::unique_ptr<DropTargetInterface> view_item_drop_target(uiViewItemHandle *item_handle)
{
AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle);
return item.create_drop_target();
return item.create_item_drop_target();
}
/** \} */

View File

@ -226,6 +226,29 @@ AbstractGridView &AbstractGridViewItem::get_view() const
/* ---------------------------------------------------------------------- */
std::unique_ptr<DropTargetInterface> AbstractGridViewItem::create_item_drop_target()
{
return create_drop_target();
}
std::unique_ptr<AbstractGridViewItemDropTarget> AbstractGridViewItem::create_drop_target()
{
return nullptr;
}
AbstractGridViewItemDropTarget::AbstractGridViewItemDropTarget(AbstractGridView &view)
: view_(view)
{
}
std::optional<DropLocation> AbstractGridViewItemDropTarget::determine_drop_location(
const wmEvent & /*event*/) const
{
return DROP_INTO;
}
/* ---------------------------------------------------------------------- */
/**
* Helper for only adding layout items for grid items that are actually in view. 3 main functions:
* - #is_item_visible(): Query if an item of a given index is visible in the view (others should be

View File

@ -23,6 +23,7 @@
#include "DNA_screen_types.h"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BLI_listbase.h"
@ -49,10 +50,14 @@ struct ViewLink : public Link {
std::unique_ptr<AbstractView> view;
static void views_bounds_calc(const uiBlock &block);
template<class T>
static T *block_add_view_impl(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractView> view);
};
template<class T>
static T *ui_block_add_view_impl(uiBlock &block,
T *ViewLink::block_add_view_impl(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractView> view)
{
@ -61,6 +66,8 @@ static T *ui_block_add_view_impl(uiBlock &block,
view_link->view = std::move(view);
view_link->idname = idname;
view_link->view->block_ = &block;
view_link->view->region_ = CTX_wm_region(static_cast<bContext *>(block.evil_C));
return dynamic_cast<T *>(view_link->view.get());
}
@ -69,14 +76,14 @@ AbstractGridView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractGridView> grid_view)
{
return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view));
return ViewLink::block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view));
}
AbstractTreeView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractTreeView> tree_view)
{
return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
return ViewLink::block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
}
void ui_block_free_views(uiBlock *block)

View File

@ -78,6 +78,30 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
AbstractTreeViewItem *AbstractTreeView::find_hovered(const int xy[2])
{
AbstractTreeViewItem *hovered_item = nullptr;
foreach_item_recursive(
[&, this](const AbstractTreeViewItem &item) {
if (hovered_item) {
return;
}
uiButViewItem *but = item.view_item_button();
if (!but) {
return;
}
rctf win_rect;
ui_block_to_window_rctf(region_, block_, &win_rect, &but->rect);
if (BLI_rctf_isect_y(&win_rect, xy[1])) {
hovered_item = reinterpret_cast<AbstractTreeViewItem *>(but->view_item);
}
},
IterOptions::SkipCollapsed | IterOptions::SkipFiltered);
return hovered_item;
}
void AbstractTreeView::set_min_rows(int min_rows)
{
min_rows_ = min_rows;
@ -229,6 +253,59 @@ void AbstractTreeView::change_state_delayed()
/* ---------------------------------------------------------------------- */
AbstractTreeViewItemDropTarget::AbstractTreeViewItemDropTarget(AbstractTreeView &view,
DropBehavior behavior)
: view_(view), behavior_(behavior)
{
}
std::optional<DropLocation> AbstractTreeViewItemDropTarget::determine_drop_location(
const wmEvent &event) const
{
if (behavior_ == DropBehavior::Insert) {
return DROP_INTO;
}
const AbstractTreeViewItem *hovered_item = view_.find_hovered(event.xy);
if (!hovered_item) {
return std::nullopt;
}
uiButViewItem *hovered_but = hovered_item->view_item_button();
rctf but_win_rect;
ui_block_to_window_rctf(view_.region_, view_.block_, &but_win_rect, &hovered_but->rect);
const float item_height = BLI_rctf_size_y(&but_win_rect);
BLI_assert(ELEM(behavior_, DropBehavior::Reorder, DropBehavior::Reorder_and_Insert));
const int segment_count =
(behavior_ == DropBehavior::Reorder) ?
/* Divide into upper (insert before) and lower (insert after) half. */
2 :
/* Upper (insert before), middle (insert into) and lower (insert after) third. */
3;
const float segment_height = item_height / segment_count;
if (event.xy[1] - but_win_rect.ymin > (item_height - segment_height)) {
return DROP_BEFORE;
}
if (event.xy[1] - but_win_rect.ymin <= segment_height) {
if (behavior_ == DropBehavior::Reorder_and_Insert && hovered_item->is_collapsible() &&
!hovered_item->is_collapsed())
{
/* Special case: Dropping at the lower 3rd of an uncollapsed item should insert into it, not
* after. */
return DROP_INTO;
}
return DROP_AFTER;
}
BLI_assert(behavior_ == DropBehavior::Reorder_and_Insert);
return DROP_INTO;
}
/* ---------------------------------------------------------------------- */
void AbstractTreeViewItem::tree_row_click_fn(bContext * /*C*/, void *but_arg1, void * /*arg2*/)
{
uiButViewItem *item_but = (uiButViewItem *)but_arg1;
@ -388,6 +465,16 @@ bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) con
return label_ == other.label_;
}
std::unique_ptr<DropTargetInterface> AbstractTreeViewItem::create_item_drop_target()
{
return create_drop_target();
}
std::unique_ptr<AbstractTreeViewItemDropTarget> AbstractTreeViewItem::create_drop_target()
{
return nullptr;
}
AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
{
return dynamic_cast<AbstractTreeView &>(get_view());
@ -499,11 +586,6 @@ bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const
return true;
}
uiButViewItem *AbstractTreeViewItem::view_item_button() const
{
return view_item_but_;
}
void AbstractTreeViewItem::change_state_delayed()
{
const std::optional<bool> should_be_active = this->should_be_active();

View File

@ -93,7 +93,7 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
/** Add drag support for catalog items. */
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
/** Add dropping support for catalog items. */
std::unique_ptr<ui::AbstractViewItemDropTarget> create_drop_target() override;
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> create_drop_target() override;
};
class AssetCatalogDragController : public ui::AbstractViewItemDragController {
@ -108,15 +108,15 @@ class AssetCatalogDragController : public ui::AbstractViewItemDragController {
void on_drag_start() override;
};
class AssetCatalogDropTarget : public ui::AbstractViewItemDropTarget {
class AssetCatalogDropTarget : public ui::AbstractTreeViewItemDropTarget {
AssetCatalogTreeItem &catalog_item_;
public:
AssetCatalogDropTarget(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const wmDrag &drag) const override;
bool on_drop(bContext *C, const wmDrag &drag) const override;
std::string drop_tooltip(const ui::DragInfo &drag_info) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override;
::AssetLibrary &get_asset_library() const;
@ -149,29 +149,29 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
void build_row(uiLayout &row) override;
struct DropTarget : public ui::AbstractViewItemDropTarget {
struct DropTarget : public ui::AbstractTreeViewItemDropTarget {
DropTarget(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const wmDrag &drag) const override;
bool on_drop(bContext *C, const wmDrag &drag) const override;
std::string drop_tooltip(const ui::DragInfo &drag_info) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override;
};
std::unique_ptr<ui::AbstractViewItemDropTarget> create_drop_target() override;
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> create_drop_target() override;
};
class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
struct DropTarget : public ui::AbstractViewItemDropTarget {
struct DropTarget : public ui::AbstractTreeViewItemDropTarget {
DropTarget(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const wmDrag &drag) const override;
bool on_drop(bContext *C, const wmDrag &drag) const override;
std::string drop_tooltip(const ui::DragInfo &drag_info) const override;
bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override;
};
std::unique_ptr<ui::AbstractViewItemDropTarget> create_drop_target() override;
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> create_drop_target() override;
};
/* ---------------------------------------------------------------------- */
@ -342,7 +342,7 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
return true;
}
std::unique_ptr<ui::AbstractViewItemDropTarget> AssetCatalogTreeViewItem::create_drop_target()
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> AssetCatalogTreeViewItem::create_drop_target()
{
return std::make_unique<AssetCatalogDropTarget>(
static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
@ -359,7 +359,7 @@ std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem::
AssetCatalogDropTarget::AssetCatalogDropTarget(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
: ui::AbstractViewItemDropTarget(tree_view), catalog_item_(catalog_item)
: ui::AbstractTreeViewItemDropTarget(tree_view), catalog_item_(catalog_item)
{
}
@ -385,18 +385,19 @@ bool AssetCatalogDropTarget::can_drop(const wmDrag &drag, const char **r_disable
}
return true;
}
if (drag.type == WM_DRAG_ASSET_LIST) {
return has_droppable_asset(drag, r_disabled_hint);
if (drag.type == WM_DRAG_ASSET_LIST && has_droppable_asset(drag, r_disabled_hint)) {
return true;
}
return false;
}
std::string AssetCatalogDropTarget::drop_tooltip(const wmDrag &drag) const
std::string AssetCatalogDropTarget::drop_tooltip(const ui::DragInfo &drag_info) const
{
if (drag.type == WM_DRAG_ASSET_CATALOG) {
return drop_tooltip_asset_catalog(drag);
if (drag_info.drag_data.type == WM_DRAG_ASSET_CATALOG) {
return drop_tooltip_asset_catalog(drag_info.drag_data);
}
return drop_tooltip_asset_list(drag);
return drop_tooltip_asset_list(drag_info.drag_data);
}
std::string AssetCatalogDropTarget::drop_tooltip_asset_catalog(const wmDrag &drag) const
@ -432,15 +433,15 @@ std::string AssetCatalogDropTarget::drop_tooltip_asset_list(const wmDrag &drag)
return basic_tip;
}
bool AssetCatalogDropTarget::on_drop(bContext *C, const wmDrag &drag) const
bool AssetCatalogDropTarget::on_drop(bContext *C, const ui::DragInfo &drag) const
{
if (drag.type == WM_DRAG_ASSET_CATALOG) {
if (drag.drag_data.type == WM_DRAG_ASSET_CATALOG) {
return drop_asset_catalog_into_catalog(
drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
drag.drag_data, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
}
return drop_assets_into_catalog(C,
get_view<AssetCatalogTreeView>(),
drag,
drag.drag_data,
catalog_item_.get_catalog_id(),
catalog_item_.get_simple_name());
}
@ -582,14 +583,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
RNA_string_set(props, "parent_path", nullptr);
}
std::unique_ptr<ui::AbstractViewItemDropTarget> AssetCatalogTreeViewAllItem::create_drop_target()
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> AssetCatalogTreeViewAllItem::
create_drop_target()
{
return std::make_unique<AssetCatalogTreeViewAllItem::DropTarget>(
static_cast<AssetCatalogTreeView &>(get_tree_view()));
}
AssetCatalogTreeViewAllItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view)
: ui::AbstractViewItemDropTarget(tree_view)
: ui::AbstractTreeViewItemDropTarget(tree_view)
{
}
@ -613,21 +615,23 @@ bool AssetCatalogTreeViewAllItem::DropTarget::can_drop(const wmDrag &drag,
return true;
}
std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(const wmDrag &drag) const
std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(
const ui::DragInfo &drag_info) const
{
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
BLI_assert(drag_info.drag_data.type == WM_DRAG_ASSET_CATALOG);
const AssetCatalog *drag_catalog = AssetCatalogDropTarget::get_drag_catalog(
drag, *get_view<AssetCatalogTreeView>().asset_library_);
drag_info.drag_data, *get_view<AssetCatalogTreeView>().asset_library_);
return fmt::format(TIP_("Move catalog {} to the top level of the tree"),
std::string_view(drag_catalog->path.name()));
}
bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(bContext * /*C*/, const wmDrag &drag) const
bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(bContext * /*C*/,
const ui::DragInfo &drag) const
{
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
BLI_assert(drag.drag_data.type == WM_DRAG_ASSET_CATALOG);
return AssetCatalogDropTarget::drop_asset_catalog_into_catalog(
drag,
drag.drag_data,
get_view<AssetCatalogTreeView>(),
/* No value to drop into the root level. */
std::nullopt);
@ -635,7 +639,7 @@ bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(bContext * /*C*/, const wm
/* ---------------------------------------------------------------------- */
std::unique_ptr<ui::AbstractViewItemDropTarget> AssetCatalogTreeViewUnassignedItem::
std::unique_ptr<ui::AbstractTreeViewItemDropTarget> AssetCatalogTreeViewUnassignedItem::
create_drop_target()
{
return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropTarget>(
@ -643,7 +647,7 @@ std::unique_ptr<ui::AbstractViewItemDropTarget> AssetCatalogTreeViewUnassignedIt
}
AssetCatalogTreeViewUnassignedItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view)
: ui::AbstractViewItemDropTarget(tree_view)
: ui::AbstractTreeViewItemDropTarget(tree_view)
{
}
@ -656,20 +660,22 @@ bool AssetCatalogTreeViewUnassignedItem::DropTarget::can_drop(const wmDrag &drag
return AssetCatalogDropTarget::has_droppable_asset(drag, r_disabled_hint);
}
std::string AssetCatalogTreeViewUnassignedItem::DropTarget::drop_tooltip(const wmDrag &drag) const
std::string AssetCatalogTreeViewUnassignedItem::DropTarget::drop_tooltip(
const ui::DragInfo &drag_info) const
{
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
const ListBase *asset_drags = WM_drag_asset_list_get(&drag_info.drag_data);
const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
return is_multiple_assets ? TIP_("Move assets out of any catalog") :
TIP_("Move asset out of any catalog");
}
bool AssetCatalogTreeViewUnassignedItem::DropTarget::on_drop(bContext *C, const wmDrag &drag) const
bool AssetCatalogTreeViewUnassignedItem::DropTarget::on_drop(bContext *C,
const ui::DragInfo &drag) const
{
/* Assign to nil catalog ID. */
return AssetCatalogDropTarget::drop_assets_into_catalog(
C, get_view<AssetCatalogTreeView>(), drag, CatalogID{});
C, get_view<AssetCatalogTreeView>(), drag.drag_data, CatalogID{});
}
} // namespace blender::ed::asset_browser

View File

@ -459,7 +459,7 @@ typedef struct GreasePencil {
blender::StringRefNull name);
blender::bke::greasepencil::Layer &add_layer(blender::StringRefNull name);
blender::bke::greasepencil::Layer &add_layer_after(blender::bke::greasepencil::LayerGroup &group,
blender::bke::greasepencil::Layer *layer,
blender::bke::greasepencil::TreeNode *link,
blender::StringRefNull name);
blender::bke::greasepencil::LayerGroup &add_layer_group(

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. */