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:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/CMakeLists.txt1
-rw-r--r--source/blender/editors/interface/interface.c189
-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.c132
-rw-r--r--source/blender/editors/screen/screen_ops.c1
-rw-r--r--source/blender/editors/screen/workspace_edit.c1
10 files changed, 484 insertions, 44 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index e5218e287d4..9204f9b62f5 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -75,6 +75,7 @@ struct wmDrag;
struct wmEvent;
struct wmManipulator;
struct wmMsgBus;
+struct uiButtonGroup;
struct wmKeyMap;
typedef struct uiBut uiBut;
@@ -551,6 +552,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);
@@ -664,6 +666,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,
@@ -807,6 +810,12 @@ void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but);
void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN);
+struct uiButtonGroup *UI_button_group_ensure(struct ARegion *region, const char *name, void *custom_data);
+void UI_button_group_add_sorted_items(struct uiButtonGroup *group, uiBlock *block, void *custom_data);
+void UI_button_group_item_add(void *data, ListBase *items);
+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 ee18f956cac..869c91f69eb 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -54,6 +54,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 3b2a2e9d3ed..4199a7c0fdd 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -3296,8 +3296,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 */
@@ -4155,6 +4159,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;
@@ -4574,21 +4584,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... */
@@ -4749,11 +4761,173 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
}
}
+
+/* -------------------------------------------------------------------- */
+/**
+ * \name Button Group
+ * \brief Generate buttons with custom, storable order.
+ *
+ * Button groups have the following key characteristics:
+ * * Sortable: Contained buttons can be sorted.
+ * * Generates: They in fact do not contain buttons, but manage info on how to
+ * create and sort buttons on runtime.
+ * * Storable: The mapping for custom sorting is written into files, alongside
+ * an identifier for this specific group. That way the group can be
+ * identified and the custom sorting re-applied after reading files.
+ *
+ * Maybe button group is a bit of a misleading term, since it doesn't actually
+ * store the buttons itself.
+ * Via a callback a button-group gathers information from which another
+ * callback can create the buttons then. In-between those two callbacks the
+ * items are sorted in a customizable way.
+ *
+ * \{ */
+
+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(*group), __func__);
+ group->type = WM_uibuttongrouptype_find(name, false);
+ BLI_addtail(&region->button_groups, group);
+
+ return group;
+}
+
+typedef struct SortedButtonGroupItemInfo {
+ struct SortedButtonGroupItemInfo *next, *prev;
+
+ uiButtonGroupItemInfo *item;
+ int new_position_index;
+} SortedButtonGroupItemInfo;
+
+static int ui_button_group_items_cmp(const void *a, const void *b)
+{
+ const SortedButtonGroupItemInfo *item_a = a, *item_b = b;
+ return item_a->new_position_index > item_b->new_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++) {
+ SortedButtonGroupItemInfo *sort_item = MEM_callocN(sizeof(*sort_item), __func__);
+
+ sort_item->item = item;
+ sort_item->new_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_added_items(
+ const uiButtonGroup *group, const ListBase *items,
+ const int prev_tot_items,
+ ListBase *r_added_items)
+{
+ const int difference = group->tot_items - prev_tot_items;
+
+ if (difference == 0) {
+ return;
+ }
+
+ if (difference > 0) {
+ for (uiButtonGroupItemInfo *item = 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_added_items, item_new);
+ }
+ }
+ }
+}
+
+static void ui_button_group_handle_added_or_removed_items(
+ uiButtonGroup *group, const ListBase *new_items,
+ int old_tot_items)
+{
+ ListBase added_items = {NULL, NULL};
+
+ group->reordered_indices = MEM_recallocN(
+ group->reordered_indices, sizeof(*group->reordered_indices) * group->tot_items);
+ ui_button_group_find_added_items(group, new_items, old_tot_items, &added_items);
+
+ /* Add new items at the end of the list. */
+ const int tot_new_items = group->tot_items - old_tot_items;
+
+ BLI_assert(BLI_listbase_count(&added_items) == tot_new_items);
+ int i = 0;
+ for (uiButtonGroupItemInfo *new_item = added_items.first; new_item; new_item = new_item->next, i++) {
+ for (int j = group->tot_items - tot_new_items + i; j > new_item->position_index; j--) {
+ group->reordered_indices[j] = group->reordered_indices[j - 1];
+ }
+ group->reordered_indices[new_item->position_index] = group->tot_items - tot_new_items + i;
+ }
+ BLI_freelistN(&added_items);
+
+ /* TODO support removing items */
+}
+
+void UI_button_group_add_sorted_items(uiButtonGroup *group, uiBlock *block, void *custom_data)
+{
+ const int old_tot_items = group->tot_items;
+ ListBase new_items = {NULL, NULL};
+ ListBase sorted_items = {NULL, NULL};
+
+ group->type->items(custom_data, &new_items);
+ group->tot_items = BLI_listbase_count(&new_items);
+
+ if (!group->reordered_indices || (group->tot_items != old_tot_items)) {
+ ui_button_group_handle_added_or_removed_items(group, &new_items, old_tot_items);
+ }
+
+ ui_button_group_sort(group, &new_items, &sorted_items);
+
+ UI_block_button_group_begin(block, group);
+ for (SortedButtonGroupItemInfo *sorted_item = sorted_items.first; sorted_item; sorted_item = sorted_item->next) {
+ group->type->item_draw(block, custom_data, sorted_item->item);
+ }
+ UI_block_button_group_end(block);
+
+ BLI_freelistN(&sorted_items);
+ BLI_freelistN(&group->items);
+ group->items = new_items;
+}
+
+void UI_button_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;
+}
+
+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 */
@@ -4773,4 +4947,5 @@ 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 ac7ed3d5106..fc0550c37b9 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -1713,6 +1713,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)
@@ -1782,6 +1788,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,
@@ -2007,6 +2029,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)
{
@@ -3756,8 +3870,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;
@@ -3778,6 +3898,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 04e9e2b18b4..23a7718ead0 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -317,6 +317,7 @@ struct uiBut {
/* pointer back */
uiBlock *block;
+ uiButtonGroup *group;
};
typedef struct uiButTab {
@@ -355,6 +356,7 @@ struct uiBlock {
ListBase buttons;
Panel *panel;
uiBlock *oldblock;
+ uiButtonGroup *current_group;
ListBase butstore; /* UI_butstore_* runtime function */
@@ -792,6 +794,13 @@ void ui_layout_list_set_labels_active(uiLayout *layout);
/* menu callback */
void ui_item_paneltype_func(struct bContext *C, struct uiLayout *layout, void *arg_pt);
+/* 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 3457d2e2eeb..8c8fe181387 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1399,4 +1399,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 fbfa64cbdbd..3e28b5ee20c 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -103,6 +103,100 @@ void UI_template_fix_linking(void)
{
}
+typedef struct TemplateID {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ ListBase *idlb;
+ short idcode;
+ short filter;
+ int prv_rows, prv_cols;
+ bool preview;
+} TemplateID;
+
+typedef struct SortableIDTabsData {
+ TemplateID *template_id;
+
+ wmOperatorType *unlink_ot;
+ PointerRNA active_ptr;
+} SortableIDTabsData;
+
+
+/* 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(void *custom_data, ListBase *r_items)
+{
+ SortableIDTabsData *data = custom_data;
+
+ for (ID *id = data->template_id->idlb->first; id; id = id->next) {
+ UI_button_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_item_draw(
+ uiBlock *block, void *custom_data, uiButtonGroupItemInfo *item)
+{
+ SortableIDTabsData *data = custom_data;
+ ID *id = item->data;
+
+ uiStyle *style = UI_style_get_dpi();
+ const bool is_active = data->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,
+ &data->template_id->ptr, data->template_id->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(data->template_id), 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 = data->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_item_draw;
+
+ return group_type;
+}
+
+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);
+ SortableIDTabsData data;
+
+ data.template_id = template_ui;
+ data.unlink_ot = unlink_ot;
+ data.active_ptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
+
+ UI_button_group_add_sorted_items(group, block, &data);
+}
+
+
/**
* Add a block button for the search menu for templateID and templateSearch.
*/
@@ -228,17 +322,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)
{
@@ -826,36 +909,15 @@ 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);
const int but_height = UI_UNIT_Y * 1.1;
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 * 1.1,
- &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/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index e21f328ddf9..d8588d7014d 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -1135,6 +1135,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
layout_new = ED_workspace_layout_add(bmain, workspace, newwin, BKE_workspace_layout_name_get(layout_old));
newsc = BKE_workspace_layout_screen_get(layout_new);
WM_window_set_active_layout(newwin, workspace, layout_new);
+ ED_screen_global_areas_create(newwin);
/* copy area to new screen */
ED_area_data_copy((ScrArea *)newsc->areabase.first, sa, true);
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index 9d329355500..df05cc07352 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -370,6 +370,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);