WIP: UI: tree-view drag & drop reordering #109283
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,6 +24,7 @@ set(INC
|
|||
../../windowmanager
|
||||
../../../../intern/ghost
|
||||
../../../../intern/guardedalloc
|
||||
../../../../extern/fmtlib/include
|
||||
../../bmesh
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ = █
|
||||
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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue