diff options
author | Julian Eisel <julian@blender.org> | 2021-09-30 17:26:56 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2021-09-30 17:39:09 +0300 |
commit | 4ee2d9df428d16f07e351f5554b951ae75804ea0 (patch) | |
tree | a10641e618b9de7aeec38248ebf3a1dfad257abc | |
parent | 42ce88f15cb77c859c6b2fed119934836235b961 (diff) |
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
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 9 | ||||
-rw-r--r-- | source/blender/editors/include/UI_tree_view.hh | 20 | ||||
-rw-r--r-- | source/blender/editors/interface/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_dropboxes.cc | 66 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_ops.c | 47 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_query.c | 10 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_view.cc | 17 | ||||
-rw-r--r-- | source/blender/editors/interface/tree_view.cc | 52 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 3 | ||||
-rw-r--r-- | source/blender/editors/space_api/spacetypes.c | 1 |
11 files changed, 223 insertions, 4 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 106f6166760..f642895f64e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -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 } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 81a614cd195..d36e688dd65 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -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(); - - /** 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 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. */ 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. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 79e08f46292..8fcc704a301 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -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 diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc new file mode 100644 index 00000000000..cb33e7f736e --- /dev/null +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -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); +} diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 95e6791b359..8b45d9faae6 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -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, diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 7b59a6f7263..1fc07bce341 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1918,6 +1918,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); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 15d1d2f2eec..2f6bda3252d 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -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); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc index 7419f21cbc6..b199ce9562e 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/interface_view.cc @@ -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. */ diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 16499065019..0ea15a2a5bb 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -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; +} diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index d791c0717be..833c9accf95 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -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); } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index b3b3eafb6e7..3da283089c5 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -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) { |