Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <eiseljulian@gmail.com>2018-04-16 20:09:14 +0300
committerJulian Eisel <eiseljulian@gmail.com>2018-04-16 20:09:14 +0300
commite519bfea37212cbec3668978db73bb647754225a (patch)
treef384ad5eff92cb24817306cd27e9ec6f9414a554
parentd03634b7d94c5d0aa4c8385a6f45cf3a7074dfbf (diff)
Initial working drag & drop for workspace tabs
Had to do quite some changes to get this to work, mainly because we'd want to have the customized order written to files. Also wanted to use existing wmDrag and wmDropBox code but modify it for our needs. And of course, this should not just work for a single case, code should be applicable to other areas as well. Note that file read/write is not working (yet), neither is versioning, region copying and deleting workspaces working. Code is also very WIP, much of it is placed in temporary places and such. Once the basics are done, we can add nicer feeback during drag & drop.
-rw-r--r--source/blender/blenkernel/BKE_screen.h15
-rw-r--r--source/blender/blenkernel/intern/screen.c6
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/interface/CMakeLists.txt1
-rw-r--r--source/blender/editors/interface/interface.c39
-rw-r--r--source/blender/editors/interface/interface_handlers.c127
-rw-r--r--source/blender/editors/interface/interface_init_exit.c57
-rw-r--r--source/blender/editors/interface/interface_intern.h9
-rw-r--r--source/blender/editors/interface/interface_ops.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c230
-rw-r--r--source/blender/editors/screen/workspace_edit.c1
-rw-r--r--source/blender/makesdna/DNA_screen_types.h24
-rw-r--r--source/blender/windowmanager/WM_api.h14
-rw-r--r--source/blender/windowmanager/WM_types.h27
-rw-r--r--source/blender/windowmanager/intern/wm.c38
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c60
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c23
-rw-r--r--source/blender/windowmanager/intern/wm_window.c3
18 files changed, 612 insertions, 70 deletions
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 774c1c8be12..3fee98bd60f 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -58,6 +58,9 @@ struct wmWindowManager;
struct WorkSpace;
struct GPUFXSettings;
struct wmMsgBus;
+struct uiBlock;
+struct uiButtonGroup;
+struct uiButtonGroupItemInfo;
#include "BLI_compiler_attrs.h"
@@ -237,6 +240,18 @@ typedef struct uiListType {
ExtensionRNA ext;
} uiListType;
+typedef bool (*uiButtonGroupIdentifyFunc)(struct uiButtonGroup *, void *);
+typedef void (*uiButtonGroupItemsFunc)(struct uiButtonGroup *, void *, ListBase *);
+typedef void (*uiButtonGroupItemDrawFunc)(struct uiBlock *, void *, struct uiButtonGroupItemInfo *);
+
+typedef struct uiButtonGroupType {
+ const char *idname;
+
+ uiButtonGroupIdentifyFunc identify;
+ uiButtonGroupItemsFunc items;
+ uiButtonGroupItemDrawFunc item_draw;
+} uiButtonGroupType;
+
/* header types */
typedef struct HeaderType {
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 35055a59243..cea9366b907 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -378,10 +378,16 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
}
}
+ for (uiButtonGroup *group = ar->button_groups.first; group; group = group->next) {
+ BLI_freelistN(&group->items);
+ MEM_SAFE_FREE(group->reordered_indices);
+ }
+
if (ar->manipulator_map != NULL) {
region_free_manipulatormap_callback(ar->manipulator_map);
}
+ BLI_freelistN(&ar->button_groups);
BLI_freelistN(&ar->ui_lists);
BLI_freelistN(&ar->ui_previews);
BLI_freelistN(&ar->panels_category);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index ed6a1de83c5..414f78a9021 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -74,6 +74,7 @@ struct wmDrag;
struct wmEvent;
struct wmManipulator;
struct wmMsgBus;
+struct uiButtonGroup;
typedef struct uiBut uiBut;
typedef struct uiBlock uiBlock;
@@ -512,6 +513,7 @@ void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr);
void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free);
void UI_but_drag_set_name(uiBut *but, const char *name);
void UI_but_drag_set_value(uiBut *but);
+void UI_but_drag_set_reorder(uiBut *but);
void UI_but_drag_set_image(
uiBut *but, const char *path, int icon, struct ImBuf *ima, float scale, const bool use_free);
@@ -625,6 +627,7 @@ enum {
BUT_GET_RNA_LABEL,
BUT_GET_RNAENUM_LABEL,
BUT_GET_RNA_LABEL_CONTEXT, /* Context specified in CTX_XXX_ macros are just unreachable! */
+ BUT_GET_VALUE,
BUT_GET_TIP,
BUT_GET_RNA_TIP,
BUT_GET_RNAENUM_TIP,
@@ -778,6 +781,9 @@ void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but);
void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN);
+void UI_block_button_group_begin(uiBlock *block, struct uiButtonGroup *group);
+void UI_block_button_group_end(uiBlock *block);
+
/* Autocomplete
*
* Tab complete helper functions, for use in uiButCompleteFunc callbacks.
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index c023816b52c..2302b6f9d5b 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -52,6 +52,7 @@ set(SRC
interface_eyedropper_driver.c
interface_handlers.c
interface_icons.c
+ interface_init_exit.c
interface_layout.c
interface_ops.c
interface_panel.c
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index bb33956a01d..c0c55270141 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -507,6 +507,7 @@ static int ui_but_calc_float_precision(uiBut *but, double value)
return UI_calc_float_precision(prec, value);
}
+
/* ************** LINK LINE DRAWING ************* */
/* link line drawing is not part of buttons or theme.. so we stick with it here */
@@ -3369,8 +3370,12 @@ static uiBut *ui_def_but(
BLI_addtail(&block->buttons, but);
- if (block->curlayout)
+ if (block->curlayout) {
ui_layout_add_but(block->curlayout, but);
+ }
+ if (block->current_group) {
+ but->group = block->current_group;
+ }
#ifdef WITH_PYTHON
/* if the 'UI_OT_editsource' is running, extract the source info from the button */
@@ -4239,6 +4244,12 @@ void UI_but_drag_set_value(uiBut *but)
but->dragtype = WM_DRAG_VALUE;
}
+void UI_but_drag_set_reorder(uiBut *but)
+{
+ but->dragtype = WM_DRAG_BUT_REORDER;
+ but->dragpoin = but; /* Assign the button itself, later wmDrag can access it then. */
+}
+
void UI_but_drag_set_image(uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free)
{
but->dragtype = WM_DRAG_PATH;
@@ -4658,21 +4669,23 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
int type = si->type;
char *tmp = NULL;
- if (type == BUT_GET_LABEL) {
- if (but->str) {
+ if (ELEM(type, BUT_GET_LABEL, BUT_GET_VALUE)) {
+ char *but_str = (type == BUT_GET_LABEL) ? but->str : but->drawstr;
+
+ if (but_str && but_str[0]) {
const char *str_sep;
size_t str_len;
if ((but->flag & UI_BUT_HAS_SEP_CHAR) &&
- (str_sep = strrchr(but->str, UI_SEP_CHAR)))
+ (str_sep = strrchr(but_str, UI_SEP_CHAR)))
{
- str_len = (str_sep - but->str);
+ str_len = (str_sep - but_str);
}
else {
- str_len = strlen(but->str);
+ str_len = strlen(but_str);
}
- tmp = BLI_strdupn(but->str, str_len);
+ tmp = BLI_strdupn(but_str, str_len);
}
else {
type = BUT_GET_RNA_LABEL; /* Fail-safe solution... */
@@ -4833,11 +4846,22 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
}
}
+void UI_block_button_group_begin(uiBlock *block, uiButtonGroup *group)
+{
+ BLI_assert(block->current_group == NULL);
+ block->current_group = group;
+}
+void UI_block_button_group_end(uiBlock *block)
+{
+ block->current_group = NULL;
+}
+
/* Program Init/Exit */
void UI_init(void)
{
ui_resources_init();
+ ui_init_button_group_types();
}
/* after reading userdef file */
@@ -4857,5 +4881,6 @@ void UI_exit(void)
{
ui_resources_free();
ui_but_clipboard_free();
+ ui_exit_button_group_types();
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 81f579732cf..210ce28ca33 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -1703,6 +1703,12 @@ static bool ui_but_contains_point_px_icon(uiBut *but, ARegion *ar, const wmEvent
return BLI_rcti_isect_pt(&rect, x, y);
}
+static void ui_but_drop_handler(wmDrag *drag, const wmEvent *event);
+struct uiButDrag {
+ uiBut *dragged_but;
+ uiButStore *but_store;
+ int drag_start_xy[2];
+};
static bool ui_but_drag_init(
bContext *C, uiBut *but,
uiHandleButtonData *data, const wmEvent *event)
@@ -1776,6 +1782,22 @@ static bool ui_but_drag_init(
return false;
}
}
+ /* Allow reordering buttons via drag & drop */
+ else if (but->group) {
+ ListBase *dropbox_list = WM_dropboxmap_find("User Interface", 0, 0);
+ struct uiButDrag *but_drag = MEM_mallocN(sizeof(*but_drag), "uiButDrag");
+
+ but_drag->dragged_but = but;
+ but_drag->but_store = UI_butstore_create(but->block);
+ copy_v2_v2_int(but_drag->drag_start_xy, &event->x);
+
+ UI_butstore_register(but_drag->but_store, &but_drag->dragged_but);
+ WM_event_start_drag(C, but->icon, but->dragtype, but_drag, ui_but_value_get(but),
+ WM_DRAG_FREE_DATA | WM_DRAG_NOP);
+ WM_dropbox_add_custom_drop_handler(dropbox_list, ui_but_drop_handler);
+
+ return true;
+ }
else {
wmDrag *drag = WM_event_start_drag(
C, but->icon, but->dragtype, but->dragpoin,
@@ -2220,6 +2242,98 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
/* ******************* drop event ******************** */
+enum ReinsertPosition {
+ BUT_REINSERT_BEFORE,
+ BUT_REINSERT_AFTER,
+};
+static void ui_but_find_to_reinsert_next_to(
+ const wmEvent *event, const uiBut *dragged_but, const int drag_start_xy[2],
+ uiBut **r_but, enum ReinsertPosition *r_reinsert_position)
+{
+ uiBut *but_candidate = NULL;
+
+ if (event->x < drag_start_xy[0]) {
+ for (uiBut *but_iter = dragged_but->prev; but_iter; but_iter = but_iter->prev) {
+ if ((but_iter->group == dragged_but->group) && (event->x < BLI_rctf_cent_x(&but_iter->rect))) {
+ but_candidate = but_iter;
+ }
+ }
+
+ *r_reinsert_position = BUT_REINSERT_BEFORE;
+ }
+ else {
+ for (uiBut *but_iter = dragged_but->next; but_iter; but_iter = but_iter->next) {
+ if ((but_iter->group == dragged_but->group) && (event->x > BLI_rctf_cent_x(&but_iter->rect))) {
+ but_candidate = but_iter;
+ }
+ }
+
+ *r_reinsert_position = BUT_REINSERT_AFTER;
+ }
+
+ *r_but = but_candidate;
+}
+
+static uiButtonGroupItemInfo *ui_block_button_group_find_item_from_data(
+ const uiButtonGroup *group,
+ const void *data)
+{
+ return BLI_findptr(&group->items, data, offsetof(uiButtonGroupItemInfo, data));
+}
+
+static void ui_but_drop_handler(wmDrag *drag, const wmEvent *event)
+{
+ if (drag->type == WM_DRAG_BUT_REORDER) {
+ struct uiButDrag *but_drag = drag->poin;
+ uiBut *dragged_but = but_drag->dragged_but;
+ uiBut *drop_neighbor;
+ enum ReinsertPosition reinsert_pos;
+
+ ui_but_find_to_reinsert_next_to(event, dragged_but, but_drag->drag_start_xy,
+ &drop_neighbor, &reinsert_pos);
+ if (drop_neighbor) {
+ uiButtonGroup *group = dragged_but->group;
+ uiButtonGroupItemInfo *dragged_item = ui_block_button_group_find_item_from_data(
+ group, dragged_but->custom_data);
+ uiButtonGroupItemInfo *drop_neighbor_item = ui_block_button_group_find_item_from_data(
+ group, drop_neighbor->custom_data);
+ /* New index of the dragged item is the old reorder index of the neighbor */
+ const int item_new_index = group->reordered_indices[drop_neighbor_item->position_index];
+
+ BLI_assert(!ELEM(NULL, dragged_item, drop_neighbor_item));
+
+ if (reinsert_pos == BUT_REINSERT_AFTER) {
+ /* modify the reorder index of all items in a certain range */
+ const int min = group->reordered_indices[dragged_item->position_index] + 1;
+ const int max = group->reordered_indices[drop_neighbor_item->position_index];
+
+ for (int i = 0; i < group->tot_items; i++) {
+ if (IN_RANGE_INCL(group->reordered_indices[i], min, max)) {
+ group->reordered_indices[i]--;
+ }
+ }
+ }
+ else if (reinsert_pos == BUT_REINSERT_BEFORE) {
+ const int min = group->reordered_indices[drop_neighbor_item->position_index];
+ const int max = group->reordered_indices[dragged_item->position_index] - 1;
+
+ for (int i = 0; i < group->tot_items; i++) {
+ if (IN_RANGE_INCL(group->reordered_indices[i], min, max)) {
+ group->reordered_indices[i]++;
+ }
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
+ group->reordered_indices[dragged_item->position_index] = item_new_index;
+ }
+
+ UI_butstore_free(but_drag->dragged_but->block, but_drag->but_store);
+ ED_region_tag_redraw(drag->init_region);
+ }
+}
+
/* only call if event type is EVT_DROP */
static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data)
{
@@ -3910,8 +4024,14 @@ static bool ui_but_is_mouse_over_icon_extra(const ARegion *region, uiBut *but, c
static int ui_do_but_TAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
- if ((event->type == LEFTMOUSE) &&
- ((event->val == KM_DBL_CLICK) || event->ctrl))
+ if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+ data->dragstartx = event->x;
+ data->dragstarty = event->y;
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if ((event->type == LEFTMOUSE) &&
+ ((event->val == KM_DBL_CLICK) || event->ctrl))
{
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
return WM_UI_HANDLER_BREAK;
@@ -3932,6 +4052,9 @@ static int ui_do_but_TAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
return WM_UI_HANDLER_BREAK;
}
}
+ else if (data->state == BUTTON_STATE_WAIT_DRAG) {
+ return ui_do_but_EXIT(C, but, data, event);
+ }
else if (data->state == BUTTON_STATE_TEXT_EDITING) {
ui_do_but_textedit(C, block, but, data, event);
return WM_UI_HANDLER_BREAK;
diff --git a/source/blender/editors/interface/interface_init_exit.c b/source/blender/editors/interface/interface_init_exit.c
new file mode 100644
index 00000000000..fb24553e7b0
--- /dev/null
+++ b/source/blender/editors/interface/interface_init_exit.c
@@ -0,0 +1,57 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2018 Blender Foundation.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/interface/interface_init_exit.h
+ * \ingroup edinterface
+ */
+
+#include "BLI_rect.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+
+
+void ui_init_button_group_types(void)
+{
+ WM_uibuttongrouptype_init();
+ WM_uibuttongrouptype_add(UI_BGT_sortable_id_tabs());
+}
+
+void ui_exit_button_group_types(void)
+{
+ WM_uibuttongrouptype_free();
+}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 08b0d6176aa..38d3e037162 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -331,6 +331,7 @@ struct uiBut {
/* pointer back */
uiBlock *block;
+ uiButtonGroup *group;
};
typedef struct uiButTab {
@@ -369,6 +370,7 @@ struct uiBlock {
ListBase buttons;
Panel *panel;
uiBlock *oldblock;
+ uiButtonGroup *current_group;
ListBase butstore; /* UI_butstore_* runtime function */
@@ -777,6 +779,13 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but);
void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop);
void ui_layout_list_set_labels_active(uiLayout *layout);
+/* interface_templates.c */
+struct uiButtonGroupType *UI_BGT_sortable_id_tabs(void);
+
+/* interface_init_exit.c */
+void ui_init_button_group_types(void);
+void ui_exit_button_group_types(void);
+
/* interface_align.c */
bool ui_but_can_align(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
void ui_block_align_calc(uiBlock *block, const ARegion *region);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 9e31e8729d5..43f787efc27 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1508,4 +1508,6 @@ void ED_keymap_ui(wmKeyConfig *keyconf)
eyedropper_modal_keymap(keyconf);
eyedropper_colorband_modal_keymap(keyconf);
+
+ WM_dropboxmap_find("User Interface", 0, 0);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 22c18e8d69c..f3f8a5d2ec3 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -104,6 +104,199 @@ void UI_template_fix_linking(void)
{
}
+
+typedef struct TemplateID {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ wmOperatorType *unlink_ot; // XXX tmp!
+ PointerRNA active_ptr;
+
+ ListBase *idlb;
+ short idcode;
+ short filter;
+ int prv_rows, prv_cols;
+ bool preview;
+} TemplateID;
+
+static uiButtonGroup *ui_button_group_ensure(ARegion *region, const char *name, void *custom_data)
+{
+ uiButtonGroup *group;
+
+ for (group = region->button_groups.first; group; group = group->next) {
+ if (STREQ(group->type->idname, name) && group->type->identify(group, custom_data)) {
+ return group;
+ }
+ }
+
+ group = MEM_callocN(sizeof(uiButtonGroup), __func__);
+ group->type = WM_uibuttongrouptype_find(name, false);
+ BLI_addtail(&region->button_groups, group);
+
+ return group;
+}
+
+static int ui_button_group_items_cmp(const void *a, const void *b)
+{
+ const uiButtonGroupItemInfo *item_a = a, *item_b = b;
+ return item_a->position_index > item_b->position_index ? 1 : 0;
+}
+
+static void ui_button_group_sort(
+ const uiButtonGroup *group, const ListBase *items,
+ ListBase *r_sorted_items)
+{
+ int i = 0;
+ for (uiButtonGroupItemInfo *item = items->first; item; item = item->next, i++) {
+ uiButtonGroupItemInfo *sort_item = MEM_dupallocN(item);
+
+ sort_item->position_index = group->reordered_indices[i];
+ BLI_addtail(r_sorted_items, sort_item);
+ }
+ BLI_listbase_sort(r_sorted_items, ui_button_group_items_cmp);
+}
+
+static void ui_button_group_find_new_items(
+ const uiButtonGroup *group, const ListBase *current_items, const int prev_tot_items,
+ ListBase *r_new_items)
+{
+ const int difference = group->tot_items - prev_tot_items;
+
+ if (difference == 0) {
+ return;
+ }
+
+ if (difference > 0) {
+ for (uiButtonGroupItemInfo *item = current_items->first; item; item = item->next) {
+ const bool has_item = BLI_findptr(&group->items, item->data,
+ offsetof(uiButtonGroupItemInfo, data)) != NULL;
+ if (!has_item) {
+ uiButtonGroupItemInfo *item_new = MEM_dupallocN(item);
+ BLI_addtail(r_new_items, item_new);
+ }
+ }
+ }
+}
+
+
+/* XXX split this up into generic and sortable_id_tabs functions */
+static void ui_template_sortable_id_tabs(
+ ARegion *region, uiBlock *block, TemplateID *template_ui, wmOperatorType *unlink_ot)
+{
+ uiButtonGroup *group = ui_button_group_ensure(region, "UI_BGT_sortable_id_tabs", template_ui);
+ const int old_tot_items = group->tot_items;
+ ListBase items = {NULL, NULL};
+
+ template_ui->unlink_ot = unlink_ot;
+ template_ui->active_ptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
+
+ group->type->items(group, template_ui, &items);
+
+ if (!group->reordered_indices || (group->tot_items != old_tot_items)) {
+ ListBase new_items = {NULL, NULL};
+
+ group->reordered_indices = MEM_recallocN(
+ group->reordered_indices, sizeof(*group->reordered_indices) * group->tot_items);
+ ui_button_group_find_new_items(group, &items, old_tot_items, &new_items);
+ /* Add new items at the end of the list. */
+ const int tot_new_items = ABS(group->tot_items - old_tot_items);
+ int i = 0;
+ for (uiButtonGroupItemInfo *new_item = new_items.first; new_item; new_item = new_item->next, i++) {
+ group->reordered_indices[new_item->position_index] = group->tot_items - tot_new_items + i;
+ }
+ BLI_freelistN(&new_items);
+
+ /* TODO support removing items */
+ }
+
+ ListBase sorted_items = {NULL, NULL};
+ ui_button_group_sort(group, &items, &sorted_items);
+
+ UI_block_button_group_begin(block, group);
+ for (uiButtonGroupItemInfo *item = sorted_items.first; item; item = item->next) {
+ group->type->item_draw(block, template_ui, item);
+ }
+ UI_block_button_group_end(block);
+
+ BLI_freelistN(&sorted_items);
+ BLI_freelistN(&group->items);
+ group->items = items;
+}
+
+static void ui_buttong_group_item_add(void *data, ListBase *items)
+{
+ uiButtonGroupItemInfo *item = MEM_callocN(sizeof(*item), __func__);
+ BLI_addtail(items, item);
+ item->data = data;
+ item->position_index = item->prev ? ((uiButtonGroupItemInfo *)item->prev)->position_index + 1 : 0;
+}
+
+/* TODO After file read, we need to be able to identify button groups. That's what should later happen here. */
+static bool ui_sortable_id_tabs_button_group_identify(uiButtonGroup *group, void *UNUSED(custom_data))
+{
+ if (STREQ(group->type->idname, "UI_BGT_sortable_id_tabs")) {
+ // XXX
+ return true;
+ }
+
+ return false;
+}
+
+static void ui_sortable_id_tabs_items(
+ uiButtonGroup *group, void *custom_data,
+ ListBase *r_items)
+{
+ TemplateID *template_ui = custom_data;
+
+ group->tot_items = BLI_listbase_count(template_ui->idlb);
+
+ for (ID *id = template_ui->idlb->first; id; id = id->next) {
+ ui_buttong_group_item_add(id, r_items);
+ }
+}
+
+static void template_ID_set_property_cb(bContext *C, void *arg_template, void *item);
+
+static void ui_sortable_id_tab_draw(
+ uiBlock *block, void *custom_data, uiButtonGroupItemInfo *item)
+{
+ ID *id = item->data;
+ TemplateID *template_ui = custom_data;
+
+ uiStyle *style = UI_style_get_dpi();
+ const bool is_active = template_ui->active_ptr.data == id;
+ const unsigned int but_width = UI_fontstyle_string_width(&style->widgetlabel, id->name + 2) + UI_UNIT_X +
+ (is_active ? ICON_DEFAULT_WIDTH_SCALE : 0);
+// const int but_align = (region->alignment == RGN_ALIGN_TOP) ? UI_BUT_ALIGN_DOWN : UI_BUT_ALIGN_TOP;
+
+
+ uiButTab *tab = (uiButTab *)uiDefButR_prop(
+ block, UI_BTYPE_TAB, 0, "", 0, 0, but_width, UI_UNIT_Y,
+ &template_ui->ptr, template_ui->prop, 0, 0.0f,
+ sizeof(id->name) - 2, 0.0f, 0.0f, "");
+ UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template_ui), id);
+ UI_but_drawflag_enable(&tab->but, UI_BUT_ALIGN_DOWN); /* TODO fixed alignment - should be based on region alignment */
+ UI_but_drag_set_reorder(&tab->but);
+ tab->but.custom_data = (void *)id;
+ tab->unlink_ot = template_ui->unlink_ot;
+ if (is_active) {
+ UI_but_flag_enable(&tab->but, UI_BUT_VALUE_CLEAR);
+ }
+}
+
+uiButtonGroupType *UI_BGT_sortable_id_tabs(void)
+{
+ uiButtonGroupType *group_type = MEM_callocN(sizeof(uiButtonGroupType), __func__);
+
+ group_type->idname = "UI_BGT_sortable_id_tabs"; // TODO not needed?
+ group_type->identify = ui_sortable_id_tabs_button_group_identify;
+ group_type->items = ui_sortable_id_tabs_items;
+ group_type->item_draw = ui_sortable_id_tab_draw;
+
+ return group_type;
+}
+
+
/**
* Add a block button for the search menu for templateID and templateSearch.
*/
@@ -229,17 +422,6 @@ void uiTemplateHeader(uiLayout *layout, bContext *C)
/********************** Search Callbacks *************************/
-typedef struct TemplateID {
- PointerRNA ptr;
- PropertyRNA *prop;
-
- ListBase *idlb;
- short idcode;
- short filter;
- int prv_rows, prv_cols;
- bool preview;
-} TemplateID;
-
/* Search browse menu, assign */
static void template_ID_set_property_cb(bContext *C, void *arg_template, void *item)
{
@@ -809,34 +991,14 @@ static void template_ID_tabs(
bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag,
const char *newop, const char *UNUSED(openop), const char *unlinkop)
{
- const ARegion *region = CTX_wm_region(C);
+ ARegion *region = CTX_wm_region(C);
const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop);
const int but_align = (region->alignment == RGN_ALIGN_TOP) ? UI_BUT_ALIGN_DOWN : UI_BUT_ALIGN_TOP;
+ wmOperatorType *unlink_ot = WM_operatortype_find(unlinkop, false);
uiBlock *block = uiLayoutGetBlock(layout);
- uiStyle *style = UI_style_get_dpi();
-
- for (ID *id = template->idlb->first; id; id = id->next) {
- wmOperatorType *unlink_ot = WM_operatortype_find(unlinkop, false);
- const bool is_active = active_ptr.data == id;
- const unsigned int but_width = UI_fontstyle_string_width(&style->widgetlabel, id->name + 2) + UI_UNIT_X +
- (is_active ? ICON_DEFAULT_WIDTH_SCALE : 0);
- uiButTab *tab;
-
- tab = (uiButTab *)uiDefButR_prop(
- block, UI_BTYPE_TAB, 0, "", 0, 0, but_width, UI_UNIT_Y,
- &template->ptr, template->prop, 0, 0.0f,
- sizeof(id->name) - 2, 0.0f, 0.0f, "");
- UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template), id);
- tab->but.custom_data = (void *)id;
- tab->unlink_ot = unlink_ot;
-
- if (is_active) {
- UI_but_flag_enable(&tab->but, UI_BUT_VALUE_CLEAR);
- }
- UI_but_drawflag_enable(&tab->but, but_align);
- }
+ ui_template_sortable_id_tabs(region, block, template, unlink_ot);
if (flag & UI_ID_ADD_NEW) {
const bool editable = RNA_property_editable(&template->ptr, template->prop);
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index 0c591b40b8d..5bfacf99dd5 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -369,6 +369,7 @@ static int workspace_append_activate_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "directory", directory);
if (workspace_append(C, directory, idname) != OPERATOR_CANCELLED) {
+ /* XXX broken, name may have been changed */
WorkSpace *appended_workspace = BLI_findstring(&bmain->workspaces, idname, offsetof(ID, name) + 2);
BLI_assert(appended_workspace != NULL);
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 939ff9eb075..b77aa59b860 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -196,6 +196,29 @@ typedef struct uiList { /* some list UI data need to be saved in file
uiListDyn *dyn_data;
} uiList;
+/* These two Lines with # tell makesdna this struct can be excluded. */
+#
+#
+typedef struct uiButtonGroupItemInfo {
+ struct uiButtonGroupItemInfo *next, *prev;
+
+ int position_index;
+ int pad;
+ void *data;
+} uiButtonGroupItemInfo;
+
+typedef struct uiButtonGroup {
+ struct uiButtonGroup *next, *prev;
+
+ struct uiButtonGroupType *type;
+
+ ListBase items; /* uiButtonGroupItemInfo */
+// uiButtonGroupItemInfo *items;
+ int tot_items;
+ int pad;
+ int *reordered_indices;
+} uiButtonGroup;
+
typedef struct TransformOrientation {
struct TransformOrientation *next, *prev;
char name[64]; /* MAX_NAME */
@@ -280,6 +303,7 @@ typedef struct ARegion {
ListBase ui_previews; /* uiPreview */
ListBase handlers; /* wmEventHandler */
ListBase panels_category; /* Panel categories runtime */
+ ListBase button_groups; /* uiButtonGroup */
struct wmManipulatorMap *manipulator_map; /* manipulator-map of this region */
struct wmTimer *regiontimer; /* blend in/out */
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index e226c81b192..b4ff0e2b555 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -68,6 +68,7 @@ struct ScrArea;
struct Main;
struct bToolDef;
struct ViewLayer;
+struct uiButtonGroupType;
#ifdef WITH_INPUT_NDOF
struct wmNDOFMotionData;
@@ -419,6 +420,12 @@ bool WM_uilisttype_add(struct uiListType *ult);
void WM_uilisttype_freelink(struct uiListType *ult);
void WM_uilisttype_free(void);
+/* *************** button group types ******************** */
+void WM_uibuttongrouptype_init(void);
+void WM_uibuttongrouptype_add(struct uiButtonGroupType *group_type);
+struct uiButtonGroupType *WM_uibuttongrouptype_find(const char *idname, bool quiet);
+void WM_uibuttongrouptype_free(void);
+
/* *************** menu types ******************** */
void WM_menutype_init(void);
struct MenuType *WM_menutype_find(const char *idname, bool quiet);
@@ -463,8 +470,11 @@ void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx
void WM_drag_free(struct wmDrag *drag);
void WM_drag_free_list(struct ListBase *lb);
-struct wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, int (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event),
- void (*copy)(struct wmDrag *, struct wmDropBox *));
+struct wmDropBox *WM_dropbox_add(
+ ListBase *lb, const char *idname, int (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event),
+ void (*copy)(struct wmDrag *, struct wmDropBox *));
+struct wmDropBox *WM_dropbox_add_custom_drop_handler(
+ ListBase *dropbox_list, void (*drop_handler)(struct wmDrag *, const struct wmEvent *));
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
/* Set OpenGL viewport and scissor */
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 59a3f703614..6b0f4850206 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -657,12 +657,16 @@ typedef struct wmReport {
/* *************** Drag and drop *************** */
-#define WM_DRAG_ID 0
-#define WM_DRAG_RNA 1
-#define WM_DRAG_PATH 2
-#define WM_DRAG_NAME 3
-#define WM_DRAG_VALUE 4
-#define WM_DRAG_COLOR 5
+enum wmDragType {
+ WM_DRAG_ID,
+ WM_DRAG_RNA,
+ WM_DRAG_PATH,
+ WM_DRAG_NAME,
+ WM_DRAG_VALUE,
+ WM_DRAG_COLOR,
+ /* Drag the entire button for button drag & drop reordering */
+ WM_DRAG_BUT_REORDER,
+};
typedef enum wmDragFlags {
WM_DRAG_NOP = 0,
@@ -673,8 +677,10 @@ typedef enum wmDragFlags {
typedef struct wmDrag {
struct wmDrag *next, *prev;
-
- int icon, type; /* type, see WM_DRAG defines above */
+
+ struct ARegion *init_region; /* Region in which we started dragging */
+
+ int icon, type; /* type, see wmDragType defines above */
void *poin;
char path[1024]; /* FILE_MAX */
double value;
@@ -697,7 +703,10 @@ typedef struct wmDropBox {
/* before exec, this copies drag info to wmDrop properties */
void (*copy)(struct wmDrag *, struct wmDropBox *);
-
+
+ /* XXX */
+ void (*drop_handler)(struct wmDrag *, const wmEvent *);
+
/* if poll survives, operator is called */
wmOperatorType *ot; /* not saved in file, so can be pointer */
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index d7c356f33ce..510ed7e5db7 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -285,6 +285,44 @@ void WM_uilisttype_free(void)
uilisttypes_hash = NULL;
}
+/* ************ uiButtonGroupType handling ************** */
+
+static GHash *uibuttongrouptypes_hash = NULL;
+
+void WM_uibuttongrouptype_init(void)
+{
+ uibuttongrouptypes_hash = BLI_ghash_str_new("uibuttongrouptypes_hash");
+}
+
+void WM_uibuttongrouptype_add(uiButtonGroupType *group_type)
+{
+ BLI_ghash_insert(uibuttongrouptypes_hash, (void *)group_type->idname, group_type);
+}
+
+uiButtonGroupType *WM_uibuttongrouptype_find(const char *idname, bool quiet)
+{
+ uiButtonGroupType *group_type;
+
+ if (idname[0]) {
+ group_type = BLI_ghash_lookup(uibuttongrouptypes_hash, idname);
+ if (group_type) {
+ return group_type;
+ }
+ }
+
+ if (!quiet) {
+ printf("search for unknown uibuttongrouptype%s\n", idname);
+ }
+
+ return NULL;
+}
+
+void WM_uibuttongrouptype_free(void)
+{
+ BLI_ghash_free(uibuttongrouptypes_hash, NULL, MEM_freeN);
+ uibuttongrouptypes_hash = NULL;
+}
+
/* ************ MenuType handling ************** */
static GHash *menutypes_hash = NULL;
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 05d9689565c..83484c5601b 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -119,6 +119,17 @@ wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, int (*poll)(bContext
return drop;
}
+wmDropBox *WM_dropbox_add_custom_drop_handler(
+ ListBase *dropbox_list, void (*drop_handler)(wmDrag *, const wmEvent *))
+{
+ wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox");
+
+ drop->drop_handler = drop_handler;
+ BLI_addtail(dropbox_list, drop);
+
+ return drop;
+}
+
void wm_dropbox_free(void)
{
wmDropBoxMap *dm;
@@ -150,6 +161,7 @@ wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin,
/* if multiple drags are added, they're drawn as list */
BLI_addtail(&wm->drags, drag);
+ drag->init_region = CTX_wm_region(C);
drag->flags = flags;
drag->icon = icon;
drag->type = type;
@@ -194,7 +206,7 @@ static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag,
if (handler->dropboxes) {
wmDropBox *drop = handler->dropboxes->first;
for (; drop; drop = drop->next) {
- if (drop->poll(C, drag, event))
+ if (drop->poll && drop->poll(C, drag, event))
/* XXX Doing translation here might not be ideal, but later we have no more
* access to ot (and hence op context)... */
return RNA_struct_ui_name(drop->ot->srna);
@@ -276,19 +288,44 @@ static void wm_drop_operator_draw(const char *name, int x, int y)
UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg);
}
-static const char *wm_drag_name(wmDrag *drag)
+/* XXX copied from interface_handlers.c */
+struct uiButDrag {
+ uiBut *dragged_but;
+ uiButStore *but_store;
+ int drag_start_xy[2];
+};
+static void wm_drag_name(bContext *C, wmDrag *drag, char (*r_name)[UI_MAX_NAME_STR])
{
+ char *name;
+ bool free_name = false;
+
switch (drag->type) {
case WM_DRAG_ID:
{
ID *id = drag->poin;
- return id->name + 2;
+ name = id->name + 2;
+ break;
}
case WM_DRAG_PATH:
case WM_DRAG_NAME:
- return drag->path;
+ name = drag->path;
+ break;
+ case WM_DRAG_BUT_REORDER:
+ {
+ struct uiButDrag *but_drag = drag->poin;
+ uiStringInfo but_label = {BUT_GET_VALUE, NULL};
+
+ UI_but_string_info_get(C, but_drag->dragged_but, &but_label, NULL);
+ name = but_label.strinfo;
+ free_name = true;
+ break;
+ }
+ }
+
+ BLI_strncpy(*r_name, name, sizeof(*r_name));
+ if (free_name) {
+ MEM_freeN(name);
}
- return "";
}
static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2)
@@ -312,6 +349,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
wmDrag *drag;
const int winsize_y = WM_window_pixels_y(win);
int cursorx, cursory, x, y;
+ char drag_name[UI_MAX_NAME_STR];
cursorx = win->eventstate->x;
cursory = win->eventstate->y;
@@ -325,7 +363,9 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
for (drag = wm->drags.first; drag; drag = drag->next) {
int iconsize = UI_DPI_ICON_SIZE;
int padding = 4 * UI_DPI_FAC;
-
+
+ wm_drag_name(C, drag, &drag_name);
+
/* image or icon */
if (drag->imb) {
x = cursorx - drag->sx / 2;
@@ -359,14 +399,14 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
x = cursorx + 10 * UI_DPI_FAC;
y = cursory + 1 * UI_DPI_FAC;
}
-
+
if (rect) {
- int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
+ int w = UI_fontstyle_string_width(fstyle, drag_name);
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else {
const unsigned char col[] = {255, 255, 255, 255};
- UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), col);
+ UI_fontstyle_draw_simple(fstyle, x, y, drag_name, col);
}
/* operator name with roundbox */
@@ -391,7 +431,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
}
if (rect) {
- int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
+ int w = UI_fontstyle_string_width(fstyle, drag_name);
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 1151615afc1..fb0d0dfe577 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -2241,16 +2241,27 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
wmDrag *drag;
for (drag = lb->first; drag; drag = drag->next) {
- if (drop->poll(C, drag, event)) {
- drop->copy(drag, drop);
+ if (!drop->poll || drop->poll(C, drag, event)) {
+ if (drop->copy) {
+ drop->copy(drag, drop);
+ }
- /* free the drags before calling operator */
- WM_drag_free_list(lb);
-
event->customdata = NULL;
event->custom = 0;
- WM_operator_name_call_ptr(C, drop->ot, drop->opcontext, drop->ptr);
+ if (drop->ot) {
+ /* free the drags before calling operator */
+ WM_drag_free_list(lb);
+
+ WM_operator_name_call_ptr(C, drop->ot, drop->opcontext, drop->ptr);
+ }
+ else if (drop->drop_handler) {
+ drop->drop_handler(drag, event);
+ WM_drag_free_list(lb);
+ }
+ else {
+ BLI_assert(0);
+ }
action |= WM_HANDLER_BREAK;
/* XXX fileread case */
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 35a95c39545..9d1feb7f121 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -793,6 +793,9 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
{
ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
WM_event_add_dropbox_handler(&win->handlers, lb);
+
+ lb = WM_dropboxmap_find("User Interface", 0, 0);
+ WM_event_add_dropbox_handler(&win->handlers, lb);
}
wm_window_title(wm, win);
}