UI: Support easy dropping into/onto rows in new tree-view API
Adds an easy way to add drop support for tree-view rows. Most of the work is handled by the tree-view UI code. The tree items can simply override a few functions (`can_drop()`, `on_drop()`, `drop_tooltip()`) to implement their custom drop behavior. While dragging over a tree-view item that can be dropped into/onto, the item can show a custom and dynamic tooltip explaining what's gonna happen on drop. This isn't used yet, but will soon be for asset catalogs. See documentation here: https://wiki.blender.org/wiki/Source/Interface/Views#Further_Customizations
This commit is contained in:
parent
42ce88f15c
commit
4ee2d9df42
Notes:
blender-bot
2023-02-13 11:53:01 +01:00
Referenced by commit c4dca65228
, Asset Browser: Support dragging assets into catalogs
|
@ -2593,6 +2593,7 @@ typedef struct uiDragColorHandle {
|
|||
|
||||
void ED_operatortypes_ui(void);
|
||||
void ED_keymap_ui(struct wmKeyConfig *keyconf);
|
||||
void ED_dropboxes_ui(void);
|
||||
void ED_uilisttypes_ui(void);
|
||||
|
||||
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
|
||||
|
@ -2763,6 +2764,14 @@ void UI_interface_tag_script_reload(void);
|
|||
|
||||
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
|
||||
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
|
||||
bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag);
|
||||
bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags);
|
||||
char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item,
|
||||
const struct bContext *C,
|
||||
const struct wmDrag *drag,
|
||||
const struct wmEvent *event);
|
||||
|
||||
uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -29,11 +29,14 @@
|
|||
|
||||
#include "UI_resources.h"
|
||||
|
||||
struct bContext;
|
||||
struct PointerRNA;
|
||||
struct uiBlock;
|
||||
struct uiBut;
|
||||
struct uiButTreeRow;
|
||||
struct uiLayout;
|
||||
struct wmEvent;
|
||||
struct wmDrag;
|
||||
|
||||
namespace blender::ui {
|
||||
|
||||
|
@ -185,10 +188,19 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
|
|||
virtual void build_row(uiLayout &row) = 0;
|
||||
|
||||
virtual void on_activate();
|
||||
virtual bool on_drop(const wmDrag &drag);
|
||||
virtual bool can_drop(const wmDrag &drag) const;
|
||||
/** Custom text to display when dragging over a tree item. Should explain what happens when
|
||||
* dropping the data onto this item. Will only be used if #AbstractTreeViewItem::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 bContext &C,
|
||||
const wmDrag &drag,
|
||||
const wmEvent &event) const;
|
||||
|
||||
/** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of the
|
||||
* last redraw to this item. If sub-classes introduce more advanced state they should override
|
||||
* this and make it update their state accordingly. */
|
||||
/** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of
|
||||
* the last redraw to this item. If sub-classes introduce more advanced state they should
|
||||
* override this and make it update their state accordingly. */
|
||||
virtual void update_from_old(const AbstractTreeViewItem &old);
|
||||
/** Compare this item to \a other to check if they represent the same data. This is critical for
|
||||
* being able to recognize an item from a previous redraw, to be able to keep its state (e.g.
|
||||
|
|
|
@ -42,6 +42,7 @@ set(SRC
|
|||
interface_button_group.c
|
||||
interface_context_menu.c
|
||||
interface_draw.c
|
||||
interface_dropboxes.cc
|
||||
interface_eyedropper.c
|
||||
interface_eyedropper_color.c
|
||||
interface_eyedropper_colorband.c
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup edinterface
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
|
||||
{
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
|
||||
region, event->x, event->y);
|
||||
if (!hovered_tree_item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return UI_tree_view_item_can_drop(hovered_tree_item, drag);
|
||||
}
|
||||
|
||||
static char *ui_tree_view_drop_tooltip(bContext *C,
|
||||
wmDrag *drag,
|
||||
const wmEvent *event,
|
||||
wmDropBox *UNUSED(drop))
|
||||
{
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
|
||||
region, event->x, event->y);
|
||||
if (!hovered_tree_item) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event);
|
||||
}
|
||||
|
||||
void ED_dropboxes_ui()
|
||||
{
|
||||
ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0);
|
||||
|
||||
WM_dropbox_add(lb,
|
||||
"UI_OT_tree_view_drop",
|
||||
ui_tree_view_drop_poll,
|
||||
nullptr,
|
||||
nullptr,
|
||||
ui_tree_view_drop_tooltip);
|
||||
}
|
|
@ -1171,6 +1171,7 @@ uiBut *ui_list_row_find_mouse_over(const struct ARegion *region,
|
|||
uiBut *ui_list_row_find_from_index(const struct ARegion *region,
|
||||
const int index,
|
||||
uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
|
||||
uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y);
|
||||
|
||||
typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
|
||||
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
|
||||
|
|
|
@ -1917,6 +1917,51 @@ static void UI_OT_list_start_filter(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UI Tree-View Drop Operator
|
||||
* \{ */
|
||||
|
||||
static bool ui_tree_view_drop_poll(bContext *C)
|
||||
{
|
||||
const wmWindow *win = CTX_wm_window(C);
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
|
||||
region, win->eventstate->x, win->eventstate->y);
|
||||
|
||||
return hovered_tree_item != NULL;
|
||||
}
|
||||
|
||||
static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
||||
{
|
||||
if (event->custom != EVT_DATA_DRAGDROP) {
|
||||
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
|
||||
region, event->x, event->y);
|
||||
|
||||
if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) {
|
||||
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void UI_OT_tree_view_drop(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Tree View drop";
|
||||
ot->idname = "UI_OT_tree_view_drop";
|
||||
ot->description = "Drag and drop items onto a tree item";
|
||||
|
||||
ot->invoke = ui_tree_view_drop_invoke;
|
||||
ot->poll = ui_tree_view_drop_poll;
|
||||
|
||||
ot->flag = OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operator & Keymap Registration
|
||||
* \{ */
|
||||
|
@ -1944,6 +1989,8 @@ void ED_operatortypes_ui(void)
|
|||
|
||||
WM_operatortype_append(UI_OT_list_start_filter);
|
||||
|
||||
WM_operatortype_append(UI_OT_tree_view_drop);
|
||||
|
||||
/* external */
|
||||
WM_operatortype_append(UI_OT_eyedropper_color);
|
||||
WM_operatortype_append(UI_OT_eyedropper_colorramp);
|
||||
|
|
|
@ -463,6 +463,16 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut
|
|||
return ui_but_find(region, ui_but_is_listrow_at_index, &data);
|
||||
}
|
||||
|
||||
static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata))
|
||||
{
|
||||
return but->type == UI_BTYPE_TREEROW;
|
||||
}
|
||||
|
||||
uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int y)
|
||||
{
|
||||
return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "interface_intern.h"
|
||||
|
@ -77,6 +79,21 @@ void ui_block_free_views(uiBlock *block)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \param x, y: Coordinate to find a tree-row item at, in window space.
|
||||
*/
|
||||
uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region,
|
||||
const int x,
|
||||
const int y)
|
||||
{
|
||||
uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, x, y);
|
||||
if (!tree_row_but) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tree_row_but->tree_item;
|
||||
}
|
||||
|
||||
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
|
||||
{
|
||||
/* First get the idname the of the view we're looking for. */
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "interface_intern.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
@ -139,6 +141,24 @@ void AbstractTreeViewItem::on_activate()
|
|||
/* Do nothing by default. */
|
||||
}
|
||||
|
||||
bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/)
|
||||
{
|
||||
/* Do nothing by default. */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/,
|
||||
const wmDrag & /*drag*/,
|
||||
const wmEvent & /*event*/) const
|
||||
{
|
||||
return TIP_("Drop into/onto tree item");
|
||||
}
|
||||
|
||||
void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
|
||||
{
|
||||
is_open_ = old.is_open_;
|
||||
|
@ -327,3 +347,35 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
|
|||
const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle);
|
||||
return a.matches(b);
|
||||
}
|
||||
|
||||
bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag)
|
||||
{
|
||||
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
|
||||
return item.can_drop(*drag);
|
||||
}
|
||||
|
||||
char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_,
|
||||
const bContext *C,
|
||||
const wmDrag *drag,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
|
||||
return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Let a tree-view item handle a drop event.
|
||||
* \return True if the drop was handled by the tree-view item.
|
||||
*/
|
||||
bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags)
|
||||
{
|
||||
AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_);
|
||||
|
||||
LISTBASE_FOREACH (const wmDrag *, drag, drags) {
|
||||
if (item.can_drop(*drag)) {
|
||||
return item.on_drop(*drag);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1699,6 +1699,9 @@ static void ed_default_handlers(
|
|||
wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0);
|
||||
WM_event_add_keymap_handler(handlers, keymap);
|
||||
|
||||
ListBase *dropboxes = WM_dropboxmap_find("User Interface", 0, 0);
|
||||
WM_event_add_dropbox_handler(handlers, dropboxes);
|
||||
|
||||
/* user interface widgets */
|
||||
UI_region_handlers_add(handlers);
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ void ED_spacemacros_init(void)
|
|||
ED_operatormacros_gpencil();
|
||||
|
||||
/* Register dropboxes (can use macros). */
|
||||
ED_dropboxes_ui();
|
||||
const ListBase *spacetypes = BKE_spacetypes_list();
|
||||
LISTBASE_FOREACH (const SpaceType *, type, spacetypes) {
|
||||
if (type->dropboxes) {
|
||||
|
|
Loading…
Reference in New Issue