Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/interface/interface_templates.c
| Show First 20 Lines • Show All 5,639 Lines • ▼ Show 20 Lines | for (int row = 0; row < 2; row++) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name List Template | |||||
| * \{ */ | |||||
| static void uilist_draw_item_default(struct uiList *ui_list, | |||||
| struct bContext *UNUSED(C), | |||||
| struct uiLayout *layout, | |||||
| struct PointerRNA *UNUSED(dataptr), | |||||
| struct PointerRNA *itemptr, | |||||
| int icon, | |||||
| struct PointerRNA *UNUSED(active_dataptr), | |||||
| const char *UNUSED(active_propname), | |||||
| int UNUSED(index), | |||||
| int UNUSED(flt_flag)) | |||||
| { | |||||
| PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); | |||||
| /* Simplest one! */ | |||||
| switch (ui_list->layout_type) { | |||||
| case UILST_LAYOUT_GRID: | |||||
| uiItemL(layout, "", icon); | |||||
| break; | |||||
| case UILST_LAYOUT_DEFAULT: | |||||
| case UILST_LAYOUT_COMPACT: | |||||
| default: | |||||
| if (nameprop) { | |||||
| uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon); | |||||
| } | |||||
| else { | |||||
| uiItemL(layout, "", icon); | |||||
| } | |||||
| break; | |||||
| } | |||||
| } | |||||
| static void uilist_draw_filter_default(struct uiList *ui_list, | |||||
| struct bContext *UNUSED(C), | |||||
| struct uiLayout *layout) | |||||
| { | |||||
| PointerRNA listptr; | |||||
| RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); | |||||
| uiLayout *row = uiLayoutRow(layout, false); | |||||
| uiLayout *subrow = uiLayoutRow(row, true); | |||||
| uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); | |||||
| uiItemR(subrow, | |||||
| &listptr, | |||||
| "use_filter_invert", | |||||
| UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, | |||||
| "", | |||||
| ICON_ARROW_LEFTRIGHT); | |||||
| if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) { | |||||
| subrow = uiLayoutRow(row, true); | |||||
| uiItemR(subrow, | |||||
| &listptr, | |||||
| "use_filter_sort_alpha", | |||||
| UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, | |||||
| "", | |||||
| ICON_NONE); | |||||
| uiItemR(subrow, | |||||
| &listptr, | |||||
| "use_filter_sort_reverse", | |||||
| UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, | |||||
| "", | |||||
| (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC); | |||||
| } | |||||
| } | |||||
| typedef struct { | |||||
| char name[MAX_IDPROP_NAME]; | |||||
| int org_idx; | |||||
| } StringCmp; | |||||
| static int cmpstringp(const void *p1, const void *p2) | |||||
| { | |||||
| /* Case-insensitive comparison. */ | |||||
| return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name); | |||||
| } | |||||
| static void uilist_filter_items_default(struct uiList *ui_list, | |||||
| struct bContext *UNUSED(C), | |||||
| struct PointerRNA *dataptr, | |||||
| const char *propname) | |||||
| { | |||||
| uiListDyn *dyn_data = ui_list->dyn_data; | |||||
| PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); | |||||
| const char *filter_raw = ui_list->filter_byname; | |||||
| char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; | |||||
| const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; | |||||
| const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == | |||||
| UILST_FLT_SORT_ALPHA; | |||||
| const int len = RNA_property_collection_length(dataptr, prop); | |||||
| dyn_data->items_shown = dyn_data->items_len = len; | |||||
| if (len && (order_by_name || filter_raw[0])) { | |||||
| StringCmp *names = NULL; | |||||
| int order_idx = 0, i = 0; | |||||
| if (order_by_name) { | |||||
| names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp"); | |||||
| } | |||||
| if (filter_raw[0]) { | |||||
| const size_t slen = strlen(filter_raw); | |||||
| dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags"); | |||||
| dyn_data->items_shown = 0; | |||||
| /* Implicitly add heading/trailing wildcards if needed. */ | |||||
| if (slen + 3 <= sizeof(filter_buff)) { | |||||
| filter = filter_buff; | |||||
| } | |||||
| else { | |||||
| filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn"); | |||||
| } | |||||
| BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3); | |||||
| } | |||||
| RNA_PROP_BEGIN (dataptr, itemptr, prop) { | |||||
| bool do_order = false; | |||||
| char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); | |||||
| const char *name = namebuf ? namebuf : ""; | |||||
| if (filter[0]) { | |||||
| /* Case-insensitive! */ | |||||
| if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { | |||||
| dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; | |||||
| if (!filter_exclude) { | |||||
| dyn_data->items_shown++; | |||||
| do_order = order_by_name; | |||||
| } | |||||
| // printf("%s: '%s' matches '%s'\n", __func__, name, filter); | |||||
| } | |||||
| else if (filter_exclude) { | |||||
| dyn_data->items_shown++; | |||||
| do_order = order_by_name; | |||||
| } | |||||
| } | |||||
| else { | |||||
| do_order = order_by_name; | |||||
| } | |||||
| if (do_order) { | |||||
| names[order_idx].org_idx = order_idx; | |||||
| BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); | |||||
| } | |||||
| /* free name */ | |||||
| if (namebuf) { | |||||
| MEM_freeN(namebuf); | |||||
| } | |||||
| i++; | |||||
| } | |||||
| RNA_PROP_END; | |||||
| if (order_by_name) { | |||||
| int new_idx; | |||||
| /* NOTE: order_idx equals either to ui_list->items_len if no filtering done, | |||||
| * or to ui_list->items_shown if filter is enabled, | |||||
| * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. | |||||
| * This way, we only sort items we actually intend to draw! | |||||
| */ | |||||
| qsort(names, order_idx, sizeof(StringCmp), cmpstringp); | |||||
| dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx, | |||||
| "items_filter_neworder"); | |||||
| for (new_idx = 0; new_idx < order_idx; new_idx++) { | |||||
| dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; | |||||
| } | |||||
| } | |||||
| if (filter_dyn) { | |||||
| MEM_freeN(filter_dyn); | |||||
| } | |||||
| if (names) { | |||||
| MEM_freeN(names); | |||||
| } | |||||
| } | |||||
| } | |||||
| typedef struct { | |||||
| PointerRNA item; | |||||
| int org_idx; | |||||
| int flt_flag; | |||||
| } _uilist_item; | |||||
| typedef struct { | |||||
| int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ | |||||
| int start_idx; /* Index of first item to display. */ | |||||
| int end_idx; /* Index of last item to display + 1. */ | |||||
| } uiListLayoutdata; | |||||
| static void uilist_prepare(uiList *ui_list, | |||||
| int len, | |||||
| int activei, | |||||
| int rows, | |||||
| int maxrows, | |||||
| int columns, | |||||
| uiListLayoutdata *layoutdata) | |||||
| { | |||||
| uiListDyn *dyn_data = ui_list->dyn_data; | |||||
| const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD)); | |||||
| /* default rows */ | |||||
| if (rows <= 0) { | |||||
| rows = 5; | |||||
| } | |||||
| dyn_data->visual_height_min = rows; | |||||
| if (maxrows < rows) { | |||||
| maxrows = max_ii(rows, 5); | |||||
| } | |||||
| if (columns <= 0) { | |||||
| columns = 9; | |||||
| } | |||||
| int activei_row; | |||||
| if (columns > 1) { | |||||
| dyn_data->height = (int)ceil((double)len / (double)columns); | |||||
| activei_row = (int)floor((double)activei / (double)columns); | |||||
| } | |||||
| else { | |||||
| dyn_data->height = len; | |||||
| activei_row = activei; | |||||
| } | |||||
| if (!use_auto_size) { | |||||
| /* No auto-size, yet we clamp at min size! */ | |||||
| maxrows = rows = max_ii(ui_list->list_grip, rows); | |||||
| } | |||||
| else if ((rows != maxrows) && (dyn_data->height > rows)) { | |||||
| /* Expand size if needed and possible. */ | |||||
| rows = min_ii(dyn_data->height, maxrows); | |||||
| } | |||||
| /* If list length changes or list is tagged to check this, | |||||
| * and active is out of view, scroll to it. */ | |||||
| if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) { | |||||
| if (activei_row < ui_list->list_scroll) { | |||||
| ui_list->list_scroll = activei_row; | |||||
| } | |||||
| else if (activei_row >= ui_list->list_scroll + rows) { | |||||
| ui_list->list_scroll = activei_row - rows + 1; | |||||
| } | |||||
| ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; | |||||
| } | |||||
| const int max_scroll = max_ii(0, dyn_data->height - rows); | |||||
| CLAMP(ui_list->list_scroll, 0, max_scroll); | |||||
| ui_list->list_last_len = len; | |||||
| dyn_data->visual_height = rows; | |||||
| layoutdata->visual_items = rows * columns; | |||||
| layoutdata->start_idx = ui_list->list_scroll * columns; | |||||
| layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len); | |||||
| } | |||||
| static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) | |||||
| { | |||||
| uiList *ui_list = arg1; | |||||
| uiListDyn *dyn_data = ui_list->dyn_data; | |||||
| /* This way we get diff in number of additional items to show (positive) or hide (negative). */ | |||||
| const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) / | |||||
| (float)UI_UNIT_Y); | |||||
| if (diff != 0) { | |||||
| ui_list->list_grip += diff; | |||||
| dyn_data->resize_prev += diff * UI_UNIT_Y; | |||||
| ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; | |||||
| } | |||||
| /* In case uilist is in popup, we need special refreshing */ | |||||
| ED_region_tag_refresh_ui(CTX_wm_menu(C)); | |||||
| } | |||||
| static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname) | |||||
| { | |||||
| if (propname && propname[0] && itemptr && itemptr->data) { | |||||
| PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); | |||||
| if (prop && (RNA_property_type(prop) == PROP_STRING)) { | |||||
| return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL); | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) | |||||
| { | |||||
| char *dyn_tooltip = argN; | |||||
| return BLI_sprintfN("%s - %s", tip, dyn_tooltip); | |||||
| } | |||||
| void uiTemplateList(uiLayout *layout, | |||||
| bContext *C, | |||||
| const char *listtype_name, | |||||
| const char *list_id, | |||||
| PointerRNA *dataptr, | |||||
| const char *propname, | |||||
| PointerRNA *active_dataptr, | |||||
| const char *active_propname, | |||||
| const char *item_dyntip_propname, | |||||
| int rows, | |||||
| int maxrows, | |||||
| int layout_type, | |||||
| int columns, | |||||
| bool sort_reverse, | |||||
| bool sort_lock) | |||||
| { | |||||
| PropertyRNA *prop = NULL, *activeprop; | |||||
| _uilist_item *items_ptr = NULL; | |||||
| uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; | |||||
| uiBut *but; | |||||
| uiListLayoutdata layoutdata; | |||||
| char ui_list_id[UI_MAX_NAME_STR]; | |||||
| char numstr[32]; | |||||
| int rnaicon = ICON_NONE, icon = ICON_NONE; | |||||
| int i = 0, activei = 0; | |||||
| int len = 0; | |||||
| /* validate arguments */ | |||||
| /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ | |||||
| if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { | |||||
| RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", | |||||
| UI_UL_DEFAULT_CLASS_NAME); | |||||
| return; | |||||
| } | |||||
| uiBlock *block = uiLayoutGetBlock(layout); | |||||
| if (!active_dataptr->data) { | |||||
| RNA_warning("No active data"); | |||||
| return; | |||||
| } | |||||
| if (dataptr->data) { | |||||
| prop = RNA_struct_find_property(dataptr, propname); | |||||
| if (!prop) { | |||||
| RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); | |||||
| return; | |||||
| } | |||||
| } | |||||
| activeprop = RNA_struct_find_property(active_dataptr, active_propname); | |||||
| if (!activeprop) { | |||||
| RNA_warning( | |||||
| "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); | |||||
| return; | |||||
| } | |||||
| if (prop) { | |||||
| const PropertyType type = RNA_property_type(prop); | |||||
| if (type != PROP_COLLECTION) { | |||||
| RNA_warning("Expected a collection data property"); | |||||
| return; | |||||
| } | |||||
| } | |||||
| const PropertyType activetype = RNA_property_type(activeprop); | |||||
| if (activetype != PROP_INT) { | |||||
| RNA_warning("Expected an integer active data property"); | |||||
| return; | |||||
| } | |||||
| /* get icon */ | |||||
| if (dataptr->data && prop) { | |||||
| StructRNA *ptype = RNA_property_pointer_type(dataptr, prop); | |||||
| rnaicon = RNA_struct_ui_icon(ptype); | |||||
| } | |||||
| /* get active data */ | |||||
| activei = RNA_property_int_get(active_dataptr, activeprop); | |||||
| /* Find the uiList type. */ | |||||
| uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false); | |||||
| if (ui_list_type == NULL) { | |||||
| RNA_warning("List type %s not found", listtype_name); | |||||
| return; | |||||
| } | |||||
| uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : | |||||
| uilist_draw_item_default; | |||||
| uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : | |||||
| uilist_draw_filter_default; | |||||
| uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : | |||||
| uilist_filter_items_default; | |||||
| /* Find or add the uiList to the current Region. */ | |||||
| /* We tag the list id with the list type... */ | |||||
| BLI_snprintf( | |||||
| ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : ""); | |||||
| /* Allows to work in popups. */ | |||||
| ARegion *region = CTX_wm_menu(C); | |||||
| if (region == NULL) { | |||||
| region = CTX_wm_region(C); | |||||
| } | |||||
| uiList *ui_list = BLI_findstring(®ion->ui_lists, ui_list_id, offsetof(uiList, list_id)); | |||||
| if (!ui_list) { | |||||
| ui_list = MEM_callocN(sizeof(uiList), "uiList"); | |||||
| BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id)); | |||||
| BLI_addtail(®ion->ui_lists, ui_list); | |||||
| ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ | |||||
| if (sort_reverse) { | |||||
| ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE; | |||||
| } | |||||
| if (sort_lock) { | |||||
| ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK; | |||||
| } | |||||
| } | |||||
| if (!ui_list->dyn_data) { | |||||
| ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"); | |||||
| } | |||||
| uiListDyn *dyn_data = ui_list->dyn_data; | |||||
| /* Because we can't actually pass type across save&load... */ | |||||
| ui_list->type = ui_list_type; | |||||
| ui_list->layout_type = layout_type; | |||||
| /* Reset filtering data. */ | |||||
| MEM_SAFE_FREE(dyn_data->items_filter_flags); | |||||
| MEM_SAFE_FREE(dyn_data->items_filter_neworder); | |||||
| dyn_data->items_len = dyn_data->items_shown = -1; | |||||
| /* When active item changed since last draw, scroll to it. */ | |||||
| if (activei != ui_list->list_last_activei) { | |||||
| ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; | |||||
| ui_list->list_last_activei = activei; | |||||
| } | |||||
| /* Filter list items! (not for compact layout, though) */ | |||||
| if (dataptr->data && prop) { | |||||
| const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; | |||||
| const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; | |||||
| int items_shown, idx = 0; | |||||
| #if 0 | |||||
| int prev_ii = -1, prev_i; | |||||
| #endif | |||||
| if (layout_type == UILST_LAYOUT_COMPACT) { | |||||
| dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop); | |||||
| } | |||||
| else { | |||||
| // printf("%s: filtering...\n", __func__); | |||||
| filter_items(ui_list, C, dataptr, propname); | |||||
| // printf("%s: filtering done.\n", __func__); | |||||
| } | |||||
| items_shown = dyn_data->items_shown; | |||||
| if (items_shown >= 0) { | |||||
| bool activei_mapping_pending = true; | |||||
| items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__); | |||||
| // printf("%s: items shown: %d.\n", __func__, items_shown); | |||||
| RNA_PROP_BEGIN (dataptr, itemptr, prop) { | |||||
| if (!dyn_data->items_filter_flags || | |||||
| ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { | |||||
| int ii; | |||||
| if (dyn_data->items_filter_neworder) { | |||||
| ii = dyn_data->items_filter_neworder[idx++]; | |||||
| ii = order_reverse ? items_shown - ii - 1 : ii; | |||||
| } | |||||
| else { | |||||
| ii = order_reverse ? items_shown - ++idx : idx++; | |||||
| } | |||||
| // printf("%s: ii: %d\n", __func__, ii); | |||||
| items_ptr[ii].item = itemptr; | |||||
| items_ptr[ii].org_idx = i; | |||||
| items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] : | |||||
| 0; | |||||
| if (activei_mapping_pending && activei == i) { | |||||
| activei = ii; | |||||
| /* So that we do not map again activei! */ | |||||
| activei_mapping_pending = false; | |||||
| } | |||||
| #if 0 /* For now, do not alter active element, even if it will be hidden... */ | |||||
| else if (activei < i) { | |||||
| /* We do not want an active but invisible item! | |||||
| * Only exception is when all items are filtered out... | |||||
| */ | |||||
| if (prev_ii >= 0) { | |||||
| activei = prev_ii; | |||||
| RNA_property_int_set(active_dataptr, activeprop, prev_i); | |||||
| } | |||||
| else { | |||||
| activei = ii; | |||||
| RNA_property_int_set(active_dataptr, activeprop, i); | |||||
| } | |||||
| } | |||||
| prev_i = i; | |||||
| prev_ii = ii; | |||||
| #endif | |||||
| } | |||||
| i++; | |||||
| } | |||||
| RNA_PROP_END; | |||||
| if (activei_mapping_pending) { | |||||
| /* No active item found, set to 'invalid' -1 value... */ | |||||
| activei = -1; | |||||
| } | |||||
| } | |||||
| if (dyn_data->items_shown >= 0) { | |||||
| len = dyn_data->items_shown; | |||||
| } | |||||
| else { | |||||
| len = dyn_data->items_len; | |||||
| } | |||||
| } | |||||
| switch (layout_type) { | |||||
| case UILST_LAYOUT_DEFAULT: | |||||
| /* layout */ | |||||
| box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); | |||||
| glob = uiLayoutColumn(box, true); | |||||
| row = uiLayoutRow(glob, false); | |||||
| col = uiLayoutColumn(row, true); | |||||
| /* init numbers */ | |||||
| uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata); | |||||
| if (dataptr->data && prop) { | |||||
| /* create list items */ | |||||
| for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { | |||||
| PointerRNA *itemptr = &items_ptr[i].item; | |||||
| void *dyntip_data; | |||||
| const int org_i = items_ptr[i].org_idx; | |||||
| const int flt_flag = items_ptr[i].flt_flag; | |||||
| uiBlock *subblock = uiLayoutGetBlock(col); | |||||
| overlap = uiLayoutOverlap(col); | |||||
| UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); | |||||
| /* list item behind label & other buttons */ | |||||
| sub = uiLayoutRow(overlap, false); | |||||
| but = uiDefButR_prop(subblock, | |||||
| UI_BTYPE_LISTROW, | |||||
| 0, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X * 10, | |||||
| UI_UNIT_Y, | |||||
| active_dataptr, | |||||
| activeprop, | |||||
| 0, | |||||
| 0, | |||||
| org_i, | |||||
| 0, | |||||
| 0, | |||||
| TIP_("Double click to rename")); | |||||
| if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) { | |||||
| UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); | |||||
| } | |||||
| sub = uiLayoutRow(overlap, false); | |||||
| icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); | |||||
| if (icon == ICON_DOT) { | |||||
| icon = ICON_NONE; | |||||
| } | |||||
| draw_item(ui_list, | |||||
| C, | |||||
| sub, | |||||
| dataptr, | |||||
| itemptr, | |||||
| icon, | |||||
| active_dataptr, | |||||
| active_propname, | |||||
| org_i, | |||||
| flt_flag); | |||||
| /* Items should be able to set context pointers for the layout. But the list-row button | |||||
| * swallows events, so it needs the context storage too for handlers to see it. */ | |||||
| but->context = uiLayoutGetContextStore(sub); | |||||
| /* If we are "drawing" active item, set all labels as active. */ | |||||
| if (i == activei) { | |||||
| ui_layout_list_set_labels_active(sub); | |||||
| } | |||||
| UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); | |||||
| } | |||||
| } | |||||
| /* add dummy buttons to fill space */ | |||||
| for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { | |||||
| uiItemL(col, "", ICON_NONE); | |||||
| } | |||||
| /* add scrollbar */ | |||||
| if (len > layoutdata.visual_items) { | |||||
| col = uiLayoutColumn(row, false); | |||||
| uiDefButI(block, | |||||
| UI_BTYPE_SCROLL, | |||||
| 0, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| V2D_SCROLL_WIDTH, | |||||
| UI_UNIT_Y * dyn_data->visual_height, | |||||
| &ui_list->list_scroll, | |||||
| 0, | |||||
| dyn_data->height - dyn_data->visual_height, | |||||
| dyn_data->visual_height, | |||||
| 0, | |||||
| ""); | |||||
| } | |||||
| break; | |||||
| case UILST_LAYOUT_COMPACT: | |||||
| row = uiLayoutRow(layout, true); | |||||
| if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) && | |||||
| (activei < dyn_data->items_shown)) { | |||||
| PointerRNA *itemptr = &items_ptr[activei].item; | |||||
| const int org_i = items_ptr[activei].org_idx; | |||||
| icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); | |||||
| if (icon == ICON_DOT) { | |||||
| icon = ICON_NONE; | |||||
| } | |||||
| draw_item( | |||||
| ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0); | |||||
| } | |||||
| /* if list is empty, add in dummy button */ | |||||
| else { | |||||
| uiItemL(row, "", ICON_NONE); | |||||
| } | |||||
| /* next/prev button */ | |||||
| BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); | |||||
| but = uiDefIconTextButR_prop(block, | |||||
| UI_BTYPE_NUM, | |||||
| 0, | |||||
| 0, | |||||
| numstr, | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X * 5, | |||||
| UI_UNIT_Y, | |||||
| active_dataptr, | |||||
| activeprop, | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| ""); | |||||
| if (dyn_data->items_shown == 0) { | |||||
| UI_but_flag_enable(but, UI_BUT_DISABLED); | |||||
| } | |||||
| break; | |||||
| case UILST_LAYOUT_GRID: | |||||
| box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); | |||||
| glob = uiLayoutColumn(box, true); | |||||
| row = uiLayoutRow(glob, false); | |||||
| col = uiLayoutColumn(row, true); | |||||
| subrow = NULL; /* Quite gcc warning! */ | |||||
| uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata); | |||||
| if (dataptr->data && prop) { | |||||
| /* create list items */ | |||||
| for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { | |||||
| PointerRNA *itemptr = &items_ptr[i].item; | |||||
| const int org_i = items_ptr[i].org_idx; | |||||
| const int flt_flag = items_ptr[i].flt_flag; | |||||
| /* create button */ | |||||
| if (!(i % columns)) { | |||||
| subrow = uiLayoutRow(col, false); | |||||
| } | |||||
| uiBlock *subblock = uiLayoutGetBlock(subrow); | |||||
| overlap = uiLayoutOverlap(subrow); | |||||
| UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); | |||||
| /* list item behind label & other buttons */ | |||||
| sub = uiLayoutRow(overlap, false); | |||||
| but = uiDefButR_prop(subblock, | |||||
| UI_BTYPE_LISTROW, | |||||
| 0, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X * 10, | |||||
| UI_UNIT_Y, | |||||
| active_dataptr, | |||||
| activeprop, | |||||
| 0, | |||||
| 0, | |||||
| org_i, | |||||
| 0, | |||||
| 0, | |||||
| NULL); | |||||
| UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); | |||||
| sub = uiLayoutRow(overlap, false); | |||||
| icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); | |||||
| draw_item(ui_list, | |||||
| C, | |||||
| sub, | |||||
| dataptr, | |||||
| itemptr, | |||||
| icon, | |||||
| active_dataptr, | |||||
| active_propname, | |||||
| org_i, | |||||
| flt_flag); | |||||
| /* If we are "drawing" active item, set all labels as active. */ | |||||
| if (i == activei) { | |||||
| ui_layout_list_set_labels_active(sub); | |||||
| } | |||||
| UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); | |||||
| } | |||||
| } | |||||
| /* add dummy buttons to fill space */ | |||||
| for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { | |||||
| if (!(i % columns)) { | |||||
| subrow = uiLayoutRow(col, false); | |||||
| } | |||||
| uiItemL(subrow, "", ICON_NONE); | |||||
| } | |||||
| /* add scrollbar */ | |||||
| if (len > layoutdata.visual_items) { | |||||
| /* col = */ uiLayoutColumn(row, false); | |||||
| uiDefButI(block, | |||||
| UI_BTYPE_SCROLL, | |||||
| 0, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| V2D_SCROLL_WIDTH, | |||||
| UI_UNIT_Y * dyn_data->visual_height, | |||||
| &ui_list->list_scroll, | |||||
| 0, | |||||
| dyn_data->height - dyn_data->visual_height, | |||||
| dyn_data->visual_height, | |||||
| 0, | |||||
| ""); | |||||
| } | |||||
| break; | |||||
| } | |||||
| if (glob) { | |||||
| /* About #UI_BTYPE_GRIP drag-resize: | |||||
| * We can't directly use results from a grip button, since we have a | |||||
| * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). | |||||
| * Since we *never* know whether we are grip-resizing or not | |||||
| * (because there is no callback for when a button enters/leaves its "edit mode"), | |||||
| * we use the fact that grip-controlled value (dyn_data->resize) is completely handled | |||||
| * by the grip during the grab resize, so settings its value here has no effect at all. | |||||
| * | |||||
| * It is only meaningful when we are not resizing, | |||||
| * in which case this gives us the correct "init drag" value. | |||||
| * Note we cannot affect `dyn_data->resize_prev here`, | |||||
| * since this value is not controlled by the grip! | |||||
| */ | |||||
| dyn_data->resize = dyn_data->resize_prev + | |||||
| (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y; | |||||
| row = uiLayoutRow(glob, true); | |||||
| uiBlock *subblock = uiLayoutGetBlock(row); | |||||
| UI_block_emboss_set(subblock, UI_EMBOSS_NONE); | |||||
| if (ui_list->filter_flag & UILST_FLT_SHOW) { | |||||
| but = uiDefIconButBitI(subblock, | |||||
| UI_BTYPE_TOGGLE, | |||||
| UILST_FLT_SHOW, | |||||
| 0, | |||||
| ICON_DISCLOSURE_TRI_DOWN, | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X, | |||||
| UI_UNIT_Y * 0.5f, | |||||
| &(ui_list->filter_flag), | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| TIP_("Hide filtering options")); | |||||
| UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ | |||||
| but = uiDefIconButI(subblock, | |||||
| UI_BTYPE_GRIP, | |||||
| 0, | |||||
| ICON_GRIP, | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X * 10.0f, | |||||
| UI_UNIT_Y * 0.5f, | |||||
| &dyn_data->resize, | |||||
| 0.0, | |||||
| 0.0, | |||||
| 0, | |||||
| 0, | |||||
| ""); | |||||
| UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); | |||||
| UI_block_emboss_set(subblock, UI_EMBOSS); | |||||
| col = uiLayoutColumn(glob, false); | |||||
| subblock = uiLayoutGetBlock(col); | |||||
| uiDefBut(subblock, | |||||
| UI_BTYPE_SEPR, | |||||
| 0, | |||||
| "", | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X, | |||||
| UI_UNIT_Y * 0.05f, | |||||
| NULL, | |||||
| 0.0, | |||||
| 0.0, | |||||
| 0, | |||||
| 0, | |||||
| ""); | |||||
| draw_filter(ui_list, C, col); | |||||
| } | |||||
| else { | |||||
| but = uiDefIconButBitI(subblock, | |||||
| UI_BTYPE_TOGGLE, | |||||
| UILST_FLT_SHOW, | |||||
| 0, | |||||
| ICON_DISCLOSURE_TRI_RIGHT, | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X, | |||||
| UI_UNIT_Y * 0.5f, | |||||
| &(ui_list->filter_flag), | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| 0, | |||||
| TIP_("Show filtering options")); | |||||
| UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ | |||||
| but = uiDefIconButI(subblock, | |||||
| UI_BTYPE_GRIP, | |||||
| 0, | |||||
| ICON_GRIP, | |||||
| 0, | |||||
| 0, | |||||
| UI_UNIT_X * 10.0f, | |||||
| UI_UNIT_Y * 0.5f, | |||||
| &dyn_data->resize, | |||||
| 0.0, | |||||
| 0.0, | |||||
| 0, | |||||
| 0, | |||||
| ""); | |||||
| UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); | |||||
| UI_block_emboss_set(subblock, UI_EMBOSS); | |||||
| } | |||||
| } | |||||
| if (items_ptr) { | |||||
| MEM_freeN(items_ptr); | |||||
| } | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Running Jobs Template | /** \name Running Jobs Template | ||||
| * \{ */ | * \{ */ | ||||
| #define B_STOPRENDER 1 | #define B_STOPRENDER 1 | ||||
| #define B_STOPCAST 2 | #define B_STOPCAST 2 | ||||
| #define B_STOPANIM 3 | #define B_STOPANIM 3 | ||||
| #define B_STOPCOMPO 4 | #define B_STOPCOMPO 4 | ||||
| #define B_STOPSEQ 5 | #define B_STOPSEQ 5 | ||||
| ▲ Show 20 Lines • Show All 819 Lines • Show Last 20 Lines | |||||