diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index e2f395ec203..d34c9a67d25 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -12,34 +12,44 @@ * - Custom context menus * - Notifier listening * - Drag controllers (dragging view items) - * - Drop controllers (dropping onto/into view items) + * - Drop targets (dropping onto/into view items) */ #pragma once #include #include +#include #include "DNA_defs.h" +#include "DNA_vec_types.h" #include "BLI_span.hh" #include "BLI_string_ref.hh" +#include "UI_interface.hh" + struct bContext; struct uiBlock; struct uiLayout; struct uiViewItemHandle; +struct ViewLink; struct wmDrag; struct wmNotifier; namespace blender::ui { class AbstractViewItem; -class AbstractViewItemDropController; +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; bool is_reconstructed_ = false; /** @@ -51,9 +61,21 @@ class AbstractView { */ std::unique_ptr> rename_buffer_; + /* See #get_bounds(). */ + std::optional bounds_; + 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. + * + * \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 create_drop_target() const; + /** Listen to a notifier, returning true if a redraw is needed. */ virtual bool listen(const wmNotifier &) const; @@ -70,6 +92,11 @@ class AbstractView { void end_renaming(); Span get_rename_buffer() const; MutableSpan get_rename_buffer(); + /** + * Get the rectangle containing all the view items that are in the layout, in button space. + * Updated as part of #UI_block_end(), before that it's unset. + */ + std::optional get_bounds() const; protected: AbstractView() = default; @@ -133,13 +160,13 @@ class AbstractViewItem { */ virtual std::unique_ptr create_drag_controller() const; /** - * If an item wants to support dropping data into it, it has to return a drop controller here. - * That is an object implementing #AbstractViewItemDropController. + * If an item wants to support dropping data into it, it has to return a drop target here. + * That is an object implementing #AbstractViewItemDropTarget. * - * \note This drop controller may be requested for each event. The view doesn't keep a drop - * controller around currently. So it can not contain persistent state. + * \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 create_drop_controller() const; + virtual std::unique_ptr create_drop_target() const; /** Get the view this item is registered for using #AbstractView::register_item(). */ AbstractView &get_view() const; @@ -200,7 +227,7 @@ template ToType *AbstractViewItem::from_item_handle(uiViewItemH * \{ */ /** - * Class to enable dragging a view item. An item can return a drop controller for itself by + * Class to enable dragging a view item. An item can return a drag controller for itself by * implementing #AbstractViewItem::create_drag_controller(). */ class AbstractViewItemDragController { @@ -222,38 +249,15 @@ class AbstractViewItemDragController { /** * 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 controller for itself via a custom - * implementation of #AbstractViewItem::create_drop_controller(). + * when dragging over this item. An item can return a drop target for itself via a custom + * implementation of #AbstractViewItem::create_drop_target(). */ -class AbstractViewItemDropController { +class AbstractViewItemDropTarget : public DropTargetInterface { protected: AbstractView &view_; public: - AbstractViewItemDropController(AbstractView &view); - virtual ~AbstractViewItemDropController() = default; - - /** - * Check if the data dragged with \a drag can be dropped on the item this controller is for. - * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping - * isn't possible on this item. Shouldn't be done too aggressively, e.g. - * don't set this if the drag-type can't be dropped here; only if it can - * but there's another reason it can't be dropped. - * Can assume this is a non-null pointer. - */ - virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; - /** - * Custom text to display when dragging over a view item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractViewItem::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; - /** - * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this - * controller is for. - */ - virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; + 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. */ @@ -267,7 +271,7 @@ template ViewType &AbstractViewItemDragController::get_view() co return dynamic_cast(view_); } -template ViewType &AbstractViewItemDropController::get_view() const +template ViewType &AbstractViewItemDropTarget::get_view() const { static_assert(std::is_base_of::value, "Type must derive from and implement the ui::AbstractView interface"); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0ad39f8c05f..4759afe24a2 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -3274,18 +3274,12 @@ void UI_view_item_context_menu_build(struct bContext *C, * \return True if dragging started successfully, otherwise false. */ bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_); -bool UI_view_item_can_drop(const uiViewItemHandle *item_, - const struct wmDrag *drag, - const char **r_disabled_hint); -char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag); -/** - * Let a view item handle a drop event. - * \return True if the drop was handled by the view item. - */ -bool UI_view_item_drop_handle(struct bContext *C, - const uiViewItemHandle *item_, - const struct ListBase *drags); +/** + * \param xy: Coordinate to find a view item at, in window space. + * \param pad: Extra padding added to the bounding box of the view. + */ +uiViewHandle *UI_region_view_find_at(const struct ARegion *region, const int xy[2], int pad); /** * \param xy: Coordinate to find a view item at, in window space. */ diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 147b48b454e..eddf2e12cba 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -18,11 +18,17 @@ namespace blender::nodes::geo_eval_log { struct GeometryAttributeInfo; } +struct ARegion; +struct bContext; struct PointerRNA; struct StructRNA; struct uiBlock; +struct uiLayout; struct uiList; struct uiSearchItems; +struct uiViewHandle; +struct uiViewItemHandle; +struct wmDrag; namespace blender::ui { @@ -54,6 +60,67 @@ void attribute_search_add_items(StringRefNull str, uiSearchItems *items, bool is_first); +/** + * 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. + */ +class DropTargetInterface { + public: + DropTargetInterface() = default; + virtual ~DropTargetInterface() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the element this drop target is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this UI element. Shouldn't be done too aggressively, + * e.g. don't set this if the drag-type can't be dropped here; only if it + * can but there's another reason it can't be dropped. Can assume this is + * a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * 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; + /** + * 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; +}; + +/** + * Let a drop target handle a drop event. + * \return True if the dropping was successful. + */ +bool drop_target_apply_drop(bContext &C, + 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); + +std::unique_ptr view_drop_target(const uiViewHandle *view_handle); +std::unique_ptr view_item_drop_target(const uiViewItemHandle *item_handle); +/** + * Try to find a view item with a drop target under the mouse cursor, or if not found, a view + * with a drop target. + * \param xy: Coordinate to find a drop target at, in window space. + */ +std::unique_ptr region_views_find_drop_target_at(const ARegion *region, + const int xy[2]); + } // namespace blender::ui enum eUIListFilterResult { diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 5ecf4c59ed8..c3ca44ceb3c 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -46,6 +46,7 @@ set(SRC interface_context_path.cc interface_drag.cc interface_draw.cc + interface_drop.cc interface_dropboxes.cc interface_handlers.cc interface_icons.cc diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 6f593c74fb9..6a0c2757eb9 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -2014,6 +2014,8 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x break; } + ui_block_views_bounds_calc(block); + if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) { UI_block_bounds_set_normal(block, 0); } diff --git a/source/blender/editors/interface/interface_drop.cc b/source/blender/editors/interface/interface_drop.cc new file mode 100644 index 00000000000..2449565536b --- /dev/null +++ b/source/blender/editors/interface/interface_drop.cc @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "UI_interface.hh" + +namespace blender::ui { + +bool drop_target_apply_drop(bContext &C, + 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); + } + } + + return false; +} + +char *drop_target_tooltip(const DropTargetInterface &drop_target, const wmDrag &drag) +{ + const std::string tooltip = drop_target.drop_tooltip(drag); + return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); +} + +} // namespace blender::ui diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 60e1c0abfa1..2a9cec4ada0 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -20,6 +20,9 @@ #include "WM_api.h" #include "UI_interface.h" +#include "UI_interface.hh" + +using namespace blender::ui; /* -------------------------------------------------------------------- */ /** \name View Drag/Drop Callbacks @@ -28,28 +31,28 @@ static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); - if (!hovered_item) { + + std::unique_ptr drop_target = region_views_find_drop_target_at( + region, event->xy); + if (!drop_target) { return false; } if (drag->drop_state.free_disabled_info) { MEM_SAFE_FREE(drag->drop_state.disabled_info); } - drag->drop_state.free_disabled_info = false; - return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info); + + return drop_target->can_drop(*drag, &drag->drop_state.disabled_info); } static char *ui_view_drop_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox * /*drop*/) { const ARegion *region = CTX_wm_region(C); - const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy); - if (!hovered_item) { - return nullptr; - } + std::unique_ptr drop_target = region_views_find_drop_target_at(region, + xy); - return UI_view_item_drop_tooltip(hovered_item, drag); + return drop_target_tooltip(*drop_target, *drag); } /** \} */ diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 830cc977d49..ae6f5c9cbce 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -1452,6 +1452,7 @@ void ui_interface_tag_script_reload_queries(); /* interface_view.cc */ void ui_block_free_views(uiBlock *block); +void ui_block_views_bounds_calc(const uiBlock *block); void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params); uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, const uiViewHandle *new_view); diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 604c493c989..202597f5651 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -47,6 +47,7 @@ #include "RNA_types.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "interface_intern.hh" @@ -65,6 +66,8 @@ #include "ED_screen.h" #include "ED_text.h" +using namespace blender::ui; + /* -------------------------------------------------------------------- */ /** \name Immediate redraw helper * @@ -2351,7 +2354,7 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name UI Tree-View Drop Operator +/** \name UI View Drop Operator * \{ */ static bool ui_view_drop_poll(bContext *C) @@ -2361,9 +2364,7 @@ static bool ui_view_drop_poll(bContext *C) if (region == nullptr) { return false; } - const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy); - - return hovered_item != nullptr; + return region_views_find_drop_target_at(region, win->eventstate->xy) != nullptr; } static int ui_view_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event) @@ -2373,10 +2374,11 @@ static int ui_view_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent * } const ARegion *region = CTX_wm_region(C); - uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); + std::unique_ptr drop_target = region_views_find_drop_target_at( + region, event->xy); - if (!UI_view_item_drop_handle( - C, hovered_item, static_cast(event->customdata))) { + if (!drop_target_apply_drop( + *C, *drop_target, *static_cast(event->customdata))) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -2385,9 +2387,9 @@ static int ui_view_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent * static void UI_OT_view_drop(wmOperatorType *ot) { - ot->name = "View drop"; + ot->name = "View Drop"; ot->idname = "UI_OT_view_drop"; - ot->description = "Drag and drop items onto a data-set item"; + ot->description = "Drag and drop onto a data-set or item within the data-set"; ot->invoke = ui_view_drop_invoke; ot->poll = ui_view_drop_poll; diff --git a/source/blender/editors/interface/views/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc index 301302d818a..2968c475360 100644 --- a/source/blender/editors/interface/views/abstract_view.cc +++ b/source/blender/editors/interface/views/abstract_view.cc @@ -62,6 +62,12 @@ void AbstractView::update_from_old(uiBlock &new_block) /** \name Default implementations of virtual functions * \{ */ +std::unique_ptr AbstractView::create_drop_target() const +{ + /* There's no drop target (and hence no drop support) by default. */ + return nullptr; +} + bool AbstractView::listen(const wmNotifier & /*notifier*/) const { /* Nothing by default. */ @@ -104,6 +110,23 @@ MutableSpan AbstractView::get_rename_buffer() return *rename_buffer_; } +std::optional AbstractView::get_bounds() const +{ + return bounds_; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name General API functions + * \{ */ + +std::unique_ptr view_drop_target(const uiViewHandle *view_handle) +{ + const AbstractView &view = reinterpret_cast(*view_handle); + return view.create_drop_target(); +} + /** \} */ } // namespace blender::ui diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc index 5e59591dd96..9f7afb03908 100644 --- a/source/blender/editors/interface/views/abstract_view_item.cc +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -174,9 +174,9 @@ std::unique_ptr AbstractViewItem::create_drag_co return nullptr; } -std::unique_ptr AbstractViewItem::create_drop_controller() const +std::unique_ptr AbstractViewItem::create_drop_target() const { - /* There's no drop controller (and hence no drop support) by default. */ + /* There's no drop target (and hence no drop support) by default. */ return nullptr; } @@ -189,7 +189,7 @@ void AbstractViewItemDragController::on_drag_start() /* Do nothing by default. */ } -AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) +AbstractViewItemDropTarget::AbstractViewItemDropTarget(AbstractView &view) : view_(view) { } @@ -217,6 +217,18 @@ bool AbstractViewItem::is_active() const /** \} */ +/* ---------------------------------------------------------------------- */ +/** \name General API functions + * \{ */ + +std::unique_ptr view_item_drop_target(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast(*item_handle); + return item.create_drop_target(); +} + +/** \} */ + } // namespace blender::ui /* ---------------------------------------------------------------------- */ @@ -264,45 +276,6 @@ class ViewItemAPIWrapper { return true; } - - static bool can_drop(const AbstractViewItem &item, - const wmDrag &drag, - const char **r_disabled_hint) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return false; - } - - return drop_controller->can_drop(drag, r_disabled_hint); - } - - static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) - { - const std::unique_ptr drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return {}; - } - - return drop_controller->drop_tooltip(drag); - } - - static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) - { - std::unique_ptr drop_controller = - item.create_drop_controller(); - - const char *disabled_hint_dummy = nullptr; - LISTBASE_FOREACH (const wmDrag *, drag, &drags) { - if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { - return drop_controller->on_drop(&C, *drag); - } - } - - return false; - } }; } // namespace blender::ui @@ -348,26 +321,4 @@ bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) return ViewItemAPIWrapper::drag_start(*C, item); } -bool UI_view_item_can_drop(const uiViewItemHandle *item_, - const wmDrag *drag, - const char **r_disabled_hint) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); -} - -char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - - const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); - return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); -} - -bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) -{ - const AbstractViewItem &item = reinterpret_cast(*item_); - return ViewItemAPIWrapper::drop_handle(*C, item, *drags); -} - /** \} */ diff --git a/source/blender/editors/interface/views/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index 9b77f3132e8..62166b6a449 100644 --- a/source/blender/editors/interface/views/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -24,6 +24,7 @@ #include "BKE_screen.h" #include "BLI_listbase.h" +#include "BLI_map.hh" #include "ED_screen.h" @@ -44,6 +45,8 @@ using namespace blender::ui; struct ViewLink : public Link { std::string idname; std::unique_ptr view; + + static void views_bounds_calc(const uiBlock &block); }; template @@ -81,6 +84,51 @@ void ui_block_free_views(uiBlock *block) } } +void ViewLink::views_bounds_calc(const uiBlock &block) +{ + Map views_bounds; + + rcti minmax; + BLI_rcti_init_minmax(&minmax); + LISTBASE_FOREACH (ViewLink *, link, &block.views) { + views_bounds.add(link->view.get(), minmax); + } + + LISTBASE_FOREACH (uiBut *, but, &block.buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + uiButViewItem *view_item_but = static_cast(but); + if (!view_item_but->view_item) { + continue; + } + + /* Get the view from the button. */ + AbstractViewItem &view_item = reinterpret_cast(*view_item_but->view_item); + AbstractView &view = view_item.get_view(); + + rcti &bounds = views_bounds.lookup(&view); + rcti but_rcti{}; + BLI_rcti_rctf_copy_round(&but_rcti, &view_item_but->rect); + BLI_rcti_do_minmax_rcti(&bounds, &but_rcti); + } + + for (const auto item : views_bounds.items()) { + const rcti &bounds = item.value; + if (BLI_rcti_is_empty(&bounds)) { + continue; + } + + AbstractView &view = *item.key; + view.bounds_ = bounds; + } +} + +void ui_block_views_bounds_calc(const uiBlock *block) +{ + ViewLink::views_bounds_calc(*block); +} + void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) { ARegion *region = listener_params->region; @@ -92,6 +140,35 @@ void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l } } +/* Similar to #ui_but_find_mouse_over_ex(). */ +uiViewHandle *UI_region_view_find_at(const ARegion *region, const int xy[2], const int pad) +{ + if (!ui_region_contains_point_px(region, xy)) { + return nullptr; + } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + float mx = xy[0], my = xy[1]; + ui_window_to_block_fl(region, block, &mx, &my); + + LISTBASE_FOREACH (ViewLink *, view_link, &block->views) { + std::optional bounds = view_link->view->get_bounds(); + if (!bounds) { + continue; + } + + rcti padded_bounds = *bounds; + if (pad) { + BLI_rcti_pad(&padded_bounds, pad, pad); + } + if (BLI_rcti_isect_pt(&padded_bounds, mx, my)) { + return reinterpret_cast(view_link->view.get()); + } + } + } + + return nullptr; +} + uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2]) { uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); @@ -112,6 +189,34 @@ uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region) return item_but->view_item; } +namespace blender::ui { + +std::unique_ptr region_views_find_drop_target_at(const ARegion *region, + const int xy[2]) +{ + const uiViewItemHandle *hovered_view_item = UI_region_views_find_item_at(region, xy); + if (hovered_view_item) { + std::unique_ptr drop_target = view_item_drop_target(hovered_view_item); + if (drop_target) { + return drop_target; + } + } + + /* Get style for some sensible padding around the view items. */ + const uiStyle *style = UI_style_get_dpi(); + const uiViewHandle *hovered_view = UI_region_view_find_at(region, xy, style->buttonspacex); + if (hovered_view) { + std::unique_ptr drop_target = view_drop_target(hovered_view); + if (drop_target) { + return drop_target; + } + } + + return nullptr; +} + +} // namespace blender::ui + static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) { /* First get the idname the of the view we're looking for. */ diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 535121ab252..85f36b96f22 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -49,7 +49,7 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { SpaceFile &space_file_; friend class AssetCatalogTreeViewItem; - friend class AssetCatalogDropController; + friend class AssetCatalogDropTarget; friend class AssetCatalogTreeViewAllItem; public: @@ -90,7 +90,7 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { /** Add drag support for catalog items. */ std::unique_ptr create_drag_controller() const override; /** Add dropping support for catalog items. */ - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_target() const override; }; class AssetCatalogDragController : public ui::AbstractViewItemDragController { @@ -105,15 +105,15 @@ class AssetCatalogDragController : public ui::AbstractViewItemDragController { void on_drag_start() override; }; -class AssetCatalogDropController : public ui::AbstractViewItemDropController { +class AssetCatalogDropTarget : public ui::AbstractViewItemDropTarget { AssetCatalogTreeItem &catalog_item_; public: - AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item); + 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(struct bContext *C, const wmDrag &drag) override; + bool on_drop(struct bContext *C, const wmDrag &drag) const override; ::AssetLibrary &get_asset_library() const; @@ -146,29 +146,29 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { void build_row(uiLayout &row) override; - struct DropController : public ui::AbstractViewItemDropController { - DropController(AssetCatalogTreeView &tree_view); + struct DropTarget : public ui::AbstractViewItemDropTarget { + 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(struct bContext *C, const wmDrag &drag) override; + bool on_drop(struct bContext *C, const wmDrag &drag) const override; }; - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_target() const override; }; class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - struct DropController : public ui::AbstractViewItemDropController { - DropController(AssetCatalogTreeView &tree_view); + struct DropTarget : public ui::AbstractViewItemDropTarget { + 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(struct bContext *C, const wmDrag &drag) override; + bool on_drop(struct bContext *C, const wmDrag &drag) const override; }; - std::unique_ptr create_drop_controller() const override; + std::unique_ptr create_drop_target() const override; }; /* ---------------------------------------------------------------------- */ @@ -339,10 +339,10 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) return true; } -std::unique_ptr AssetCatalogTreeViewItem:: - create_drop_controller() const +std::unique_ptr AssetCatalogTreeViewItem::create_drop_target() + const { - return std::make_unique( + return std::make_unique( static_cast(get_tree_view()), catalog_item_); } @@ -355,13 +355,13 @@ std::unique_ptr AssetCatalogTreeViewItem:: /* ---------------------------------------------------------------------- */ -AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, - AssetCatalogTreeItem &catalog_item) - : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item) +AssetCatalogDropTarget::AssetCatalogDropTarget(AssetCatalogTreeView &tree_view, + AssetCatalogTreeItem &catalog_item) + : ui::AbstractViewItemDropTarget(tree_view), catalog_item_(catalog_item) { } -bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const +bool AssetCatalogDropTarget::can_drop(const wmDrag &drag, const char **r_disabled_hint) const { if (drag.type == WM_DRAG_ASSET_CATALOG) { const ::AssetLibrary &library = get_asset_library(); @@ -389,7 +389,7 @@ bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_dis return false; } -std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const +std::string AssetCatalogDropTarget::drop_tooltip(const wmDrag &drag) const { if (drag.type == WM_DRAG_ASSET_CATALOG) { return drop_tooltip_asset_catalog(drag); @@ -397,7 +397,7 @@ std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const return drop_tooltip_asset_list(drag); } -std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const +std::string AssetCatalogDropTarget::drop_tooltip_asset_catalog(const wmDrag &drag) const { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); const AssetCatalog *src_catalog = get_drag_catalog(drag, get_asset_library()); @@ -406,7 +406,7 @@ std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag TIP_("into") + " '" + catalog_item_.get_name() + "'"; } -std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const +std::string AssetCatalogDropTarget::drop_tooltip_asset_list(const wmDrag &drag) const { BLI_assert(drag.type == WM_DRAG_ASSET_LIST); @@ -429,7 +429,7 @@ std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &dr return basic_tip; } -bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) +bool AssetCatalogDropTarget::on_drop(struct bContext *C, const wmDrag &drag) const { if (drag.type == WM_DRAG_ASSET_CATALOG) { return drop_asset_catalog_into_catalog( @@ -442,7 +442,7 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) catalog_item_.get_simple_name()); } -bool AssetCatalogDropController::drop_asset_catalog_into_catalog( +bool AssetCatalogDropTarget::drop_asset_catalog_into_catalog( const wmDrag &drag, AssetCatalogTreeView &tree_view, const std::optional drop_catalog_id) @@ -456,11 +456,11 @@ bool AssetCatalogDropController::drop_asset_catalog_into_catalog( return true; } -bool AssetCatalogDropController::drop_assets_into_catalog(struct bContext *C, - const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name) +bool AssetCatalogDropTarget::drop_assets_into_catalog(struct bContext *C, + const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) { BLI_assert(drag.type == WM_DRAG_ASSET_LIST); const ListBase *asset_drags = WM_drag_asset_list_get(&drag); @@ -491,8 +491,8 @@ bool AssetCatalogDropController::drop_assets_into_catalog(struct bContext *C, return true; } -AssetCatalog *AssetCatalogDropController::get_drag_catalog(const wmDrag &drag, - const ::AssetLibrary &asset_library) +AssetCatalog *AssetCatalogDropTarget::get_drag_catalog(const wmDrag &drag, + const ::AssetLibrary &asset_library) { if (drag.type != WM_DRAG_ASSET_CATALOG) { return nullptr; @@ -504,8 +504,7 @@ AssetCatalog *AssetCatalogDropController::get_drag_catalog(const wmDrag &drag, return catalog_service->find_catalog(catalog_drag->drag_catalog_id); } -bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, - const char **r_disabled_hint) +bool AssetCatalogDropTarget::has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint) { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); @@ -521,8 +520,8 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, return false; } -bool AssetCatalogDropController::can_modify_catalogs(const ::AssetLibrary &library, - const char **r_disabled_hint) +bool AssetCatalogDropTarget::can_modify_catalogs(const ::AssetLibrary &library, + const char **r_disabled_hint) { if (ED_asset_catalogs_read_only(library)) { *r_disabled_hint = "Catalogs cannot be edited in this asset library"; @@ -531,7 +530,7 @@ bool AssetCatalogDropController::can_modify_catalogs(const ::AssetLibrary &libra return true; } -::AssetLibrary &AssetCatalogDropController::get_asset_library() const +::AssetLibrary &AssetCatalogDropTarget::get_asset_library() const { return *get_view().asset_library_; } @@ -580,30 +579,30 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) RNA_string_set(props, "parent_path", nullptr); } -std::unique_ptr AssetCatalogTreeViewAllItem:: - create_drop_controller() const +std::unique_ptr AssetCatalogTreeViewAllItem::create_drop_target() + const { - return std::make_unique( + return std::make_unique( static_cast(get_tree_view())); } -AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractViewItemDropController(tree_view) +AssetCatalogTreeViewAllItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view) + : ui::AbstractViewItemDropTarget(tree_view) { } -bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, - const char **r_disabled_hint) const +bool AssetCatalogTreeViewAllItem::DropTarget::can_drop(const wmDrag &drag, + const char **r_disabled_hint) const { if (drag.type != WM_DRAG_ASSET_CATALOG) { return false; } ::AssetLibrary &library = *get_view().asset_library_; - if (!AssetCatalogDropController::can_modify_catalogs(library, r_disabled_hint)) { + if (!AssetCatalogDropTarget::can_modify_catalogs(library, r_disabled_hint)) { return false; } - const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(drag, library); + const AssetCatalog *drag_catalog = AssetCatalogDropTarget::get_drag_catalog(drag, library); if (drag_catalog->path.parent() == "") { *r_disabled_hint = "Catalog is already placed at the highest level"; return false; @@ -612,21 +611,21 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, return true; } -std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDrag &drag) const +std::string AssetCatalogTreeViewAllItem::DropTarget::drop_tooltip(const wmDrag &drag) const { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); - const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( + const AssetCatalog *drag_catalog = AssetCatalogDropTarget::get_drag_catalog( drag, *get_view().asset_library_); return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " + TIP_("to the top level of the tree"); } -bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext * /*C*/, - const wmDrag &drag) +bool AssetCatalogTreeViewAllItem::DropTarget::on_drop(struct bContext * /*C*/, + const wmDrag &drag) const { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); - return AssetCatalogDropController::drop_asset_catalog_into_catalog( + return AssetCatalogDropTarget::drop_asset_catalog_into_catalog( drag, get_view(), /* No value to drop into the root level. */ @@ -635,29 +634,28 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext * /*C* /* ---------------------------------------------------------------------- */ -std::unique_ptr AssetCatalogTreeViewUnassignedItem:: - create_drop_controller() const +std::unique_ptr AssetCatalogTreeViewUnassignedItem:: + create_drop_target() const { - return std::make_unique( + return std::make_unique( static_cast(get_tree_view())); } -AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractViewItemDropController(tree_view) +AssetCatalogTreeViewUnassignedItem::DropTarget::DropTarget(AssetCatalogTreeView &tree_view) + : ui::AbstractViewItemDropTarget(tree_view) { } -bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop( - const wmDrag &drag, const char **r_disabled_hint) const +bool AssetCatalogTreeViewUnassignedItem::DropTarget::can_drop(const wmDrag &drag, + const char **r_disabled_hint) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint); + return AssetCatalogDropTarget::has_droppable_asset(drag, r_disabled_hint); } -std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( - const wmDrag &drag) const +std::string AssetCatalogTreeViewUnassignedItem::DropTarget::drop_tooltip(const wmDrag &drag) const { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -666,11 +664,11 @@ std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( TIP_("Move asset out of any catalog"); } -bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext *C, - const wmDrag &drag) +bool AssetCatalogTreeViewUnassignedItem::DropTarget::on_drop(struct bContext *C, + const wmDrag &drag) const { /* Assign to nil catalog ID. */ - return AssetCatalogDropController::drop_assets_into_catalog( + return AssetCatalogDropTarget::drop_assets_into_catalog( C, get_view(), drag, CatalogID{}); }