diff options
author | Severin <eiseljulian@gmail.com> | 2018-07-02 15:19:49 +0300 |
---|---|---|
committer | Severin <eiseljulian@gmail.com> | 2018-07-02 15:19:49 +0300 |
commit | 9887b38692e3bcf7ba613ca947fd76e299c58cbd (patch) | |
tree | ee03c74f7ea6180ef78f47a4f735ef43033d3acb | |
parent | 9b2a4c67656ebc40a1026dd0e5f59b40446a5a1a (diff) |
Cleanup: Refactor button-group code
* Split generic button-group code from specific application
* Move button-group API to interface.c.
* Avoid struct abuse by using wrapper structs
* Naming, comments, etc.
-rw-r--r-- | source/blender/blenkernel/BKE_screen.h | 2 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 3 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 151 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 163 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_screen_types.h | 5 |
5 files changed, 189 insertions, 135 deletions
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index b1777cb77d7..3f3eca7d34b 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -259,7 +259,7 @@ typedef struct uiListType { } uiListType; typedef bool (*uiButtonGroupIdentifyFunc)(struct uiButtonGroup *, void *); -typedef void (*uiButtonGroupItemsFunc)(struct uiButtonGroup *, void *, ListBase *); +typedef void (*uiButtonGroupItemsFunc)(void *, ListBase *); typedef void (*uiButtonGroupItemDrawFunc)(struct uiBlock *, void *, struct uiButtonGroupItemInfo *); typedef struct uiButtonGroupType { diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7fef72b7484..946c1822da3 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -810,6 +810,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); +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); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 3eb8f3d6bb5..5673bc2535c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4756,6 +4756,157 @@ 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(®ion->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); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index b890e8b38b0..b70468dd508 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -103,14 +103,10 @@ 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; @@ -118,123 +114,13 @@ typedef struct TemplateID { 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(®ion->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); - } - } - } -} - +typedef struct SortableIDTabsData { + TemplateID *template_id; -/* 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 = group->tot_items - old_tot_items; - - BLI_assert(BLI_listbase_count(&new_items) == tot_new_items); - int i = 0; - for (uiButtonGroupItemInfo *new_item = new_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(&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); + wmOperatorType *unlink_ot; + PointerRNA active_ptr; +} SortableIDTabsData; - 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)) @@ -247,29 +133,25 @@ static bool ui_sortable_id_tabs_button_group_identify(uiButtonGroup *group, void return false; } -static void ui_sortable_id_tabs_items( - uiButtonGroup *group, void *custom_data, - ListBase *r_items) +static void ui_sortable_id_tabs_items(void *custom_data, ListBase *r_items) { - TemplateID *template_ui = custom_data; - - group->tot_items = BLI_listbase_count(template_ui->idlb); + SortableIDTabsData *data = custom_data; - for (ID *id = template_ui->idlb->first; id; id = id->next) { - ui_buttong_group_item_add(id, r_items); + 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_draw( +static void ui_sortable_id_tab_item_draw( uiBlock *block, void *custom_data, uiButtonGroupItemInfo *item) { + SortableIDTabsData *data = custom_data; 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 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; @@ -277,13 +159,13 @@ static void ui_sortable_id_tab_draw( 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, + &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(template_ui), id); + 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 = template_ui->unlink_ot; + tab->unlink_ot = data->unlink_ot; if (is_active) { UI_but_flag_enable(&tab->but, UI_BUT_VALUE_CLEAR); } @@ -296,11 +178,24 @@ uiButtonGroupType *UI_BGT_sortable_id_tabs(void) 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; + 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. diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 0034b88eec8..56d1dd57b89 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -225,6 +225,11 @@ typedef struct uiButtonGroupItemInfo { void *data; } uiButtonGroupItemInfo; +/* TODO Better name for this? Alternatives: + * * uiSortedButtonGenerator + * * uiButtonGroupGenerator + * * uiStorableItems + */ typedef struct uiButtonGroup { struct uiButtonGroup *next, *prev; |