uiLists enhacements: dragresize and better GRID layout.
authorBastien Montagne <montagne29@wanadoo.fr>
Tue, 27 Aug 2013 15:27:41 +0000 (15:27 +0000)
committerBastien Montagne <montagne29@wanadoo.fr>
Tue, 27 Aug 2013 15:27:41 +0000 (15:27 +0000)
Many thanks to Brecht for the review!

* You can now drag-resize uiLists (in default or grid layouts).
** Note about "default" size: when you drag below minimal size of the uiList, it will automatically reset to automatic sizing (i.e. size between rows and maxrows, depending on the number of items to show). This often means (e.g. in Materials list with many mat slots) that the list will grow again to maxrows!

* Grid uiLists now have a customizable number of columns (previously it was a fixed value of 9), and they will respect the rows/maxrows settings as well (i.e. show a scrollbar when needed), instead of growing indefinitly!

source/blender/blenkernel/intern/screen.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_layout.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_buttons.c
source/blender/makesdna/DNA_screen_types.h
source/blender/makesrna/intern/rna_ui_api.c
source/blenderplayer/bad_level_call_stubs/stubs.c

index fe2f52d79fd06193326fde9196fd35afba13c2cc..924cdff798d1ffb7d9825dd3990fc5026ae581b2 100644 (file)
@@ -267,6 +267,8 @@ void BKE_spacedata_draw_locks(int set)
 /* not region itself */
 void BKE_area_region_free(SpaceType *st, ARegion *ar)
 {
+       uiList *uilst;
+
        if (st) {
                ARegionType *art = BKE_regiontype_from_id(st, ar->regiontype);
                
@@ -285,6 +287,12 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
        }
 
        BLI_freelistN(&ar->panels);
+
+       for (uilst = ar->ui_lists.first; uilst; uilst = uilst->next) {
+               if (uilst->dyn_data) {
+                       MEM_freeN(uilst->dyn_data);
+               }
+       }
        BLI_freelistN(&ar->ui_lists);
 }
 
index bab015d2a81c3945c6b129d4777630bf132a8788..ff44bcc4cfefb87c5a3be8cc16f711613f260bee 100644 (file)
@@ -6127,6 +6127,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
 
        for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) {
                ui_list->type = NULL;
+               ui_list->dyn_data = NULL;
        }
 
        if (spacetype == SPACE_EMPTY) {
index 9a9cab9b46cdc8a80fcb88d58e8bd53e3ffb2ed8..5712b64ae8f5589ed054a41fa66f41d8903252eb 100644 (file)
@@ -845,7 +845,7 @@ void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
 #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
 void uiTemplateList(uiLayout *layout, struct bContext *C, const char *listtype_name, const char *list_id,
                     struct PointerRNA *dataptr, const char *propname, struct PointerRNA *active_dataptr,
-                    const char *active_propname, int rows, int maxrows, int layout_type);
+                    const char *active_propname, int rows, int maxrows, int layout_type, int columns);
 void uiTemplateNodeLink(uiLayout *layout, struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *input);
 void uiTemplateNodeView(uiLayout *layout, struct bContext *C, struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *input);
 void uiTemplateTextureUser(uiLayout *layout, struct bContext *C);
index f9ee2b67e6bd1265082df482e6cf7d718d60a5e3..c0c8367eb8c75499a2ba13810516b97511d4a629 100644 (file)
@@ -170,6 +170,7 @@ typedef struct uiHandleButtonData {
        /* coords are Window/uiBlock relative (depends on the button) */
        int draglastx, draglasty;
        int dragstartx, dragstarty;
+       int draglastvalue;
        bool dragchange, draglock;
        int dragsel;
        float dragf, dragfstart;
@@ -3406,6 +3407,87 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut
        return retval;
 }
 
+static int ui_do_but_LISTBOX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+{
+       uiList *ui_list = but->custom_data;
+       int *size = (int *)but->poin;
+       int mx, my;
+       int retval = WM_UI_HANDLER_CONTINUE;
+
+       mx = event->x;
+       my = event->y;
+       ui_window_to_block(data->region, block, &mx, &my);
+
+       if (data->state == BUTTON_STATE_NUM_EDITING) {
+               if (event->type == ESCKEY) {
+                       data->cancel = true;
+                       data->escapecancel = true;
+                       *size = (int)data->origvalue;
+                       button_activate_state(C, but, BUTTON_STATE_EXIT);
+                       ED_region_tag_redraw(data->region);
+               }
+               else if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
+                       button_activate_state(C, but, BUTTON_STATE_EXIT);
+                       ED_region_tag_redraw(data->region);
+               }
+               else if (event->type == MOUSEMOVE) {
+                       /* If we switched from dragged to auto size, suspend shrinking dragging and set dragstarty to a temp
+                        * refpoint.
+                        */
+                       if (data->draglastvalue > 0 && *size == 0) {
+                               data->draglastvalue = *size;
+                               data->draglasty = data->dragstarty;
+                               data->dragstarty = my;
+                       }
+                       else {
+                               int delta = -(my - data->dragstarty);
+                               /* Number of rows to show/hide, UI_UNIT_Y should work nice in most cases. */
+                               delta /= UI_UNIT_Y;
+
+                               /* If we are not in autosize mode, default behavior... */
+                               if (*size > 0 && delta != 0) {
+                                       /* Note: In case some items of the list would draw more than UI_UNIT_Y height, we only grow from one
+                                        *       item at a time, to avoid instability!
+                                        */
+                                       delta = delta / abs(delta);
+                                       /* We can't use ui_numedit_apply()... */
+                                       /* list template will clamp, but we do not want to reach 0 aka autosize mode!. */
+                                       *size = max_ii(*size + delta, 1);
+
+                                       /* Used to detect switch to/from autosize mode. */
+                                       data->draglastvalue = *size;
+
+                                       data->dragchange = true;
+                                       data->applied = data->applied_interactive = true;
+
+                                       ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+                                       ED_region_tag_redraw(data->region);
+                               }
+                               /* If we are leaving autosize mode (growing dragging), restore to minimal size. */
+                               else if (delta > 0) {
+                                       /* We can't use ui_numedit_apply()... */
+                                       *size = ui_list->dyn_data->visual_height_min;
+
+                                       /* Restore real dragstarty value! */
+                                       data->dragstarty = data->draglasty;
+
+                                       /* Used to detect switch to/from autosize mode. */
+                                       data->draglastvalue = *size;
+
+                                       data->dragchange = true;
+                                       data->applied = data->applied_interactive = true;
+
+                                       ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+                                       ED_region_tag_redraw(data->region);
+                               }
+                       }
+               }
+
+               retval = WM_UI_HANDLER_BREAK;
+       }
+
+       return retval;
+}
 
 static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
 {
@@ -5367,8 +5449,10 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                case NUMSLI:
                        retval = ui_do_but_SLI(C, block, but, data, event);
                        break;
-               case ROUNDBOX:
                case LISTBOX:
+                       retval = ui_do_but_LISTBOX(C, block, but, data, event);
+                       break;
+               case ROUNDBOX:
                case LABEL:
                case LISTLABEL:
                case ROW:
@@ -5622,8 +5706,6 @@ static uiBut *ui_list_find_mouse_over(ARegion *ar, int x, int y)
        uiBut *but;
        int mx, my;
 
-//     if (!win->active)
-//             return NULL;
        if (!ui_mouse_inside_region(ar, x, y))
                return NULL;
 
@@ -5768,7 +5850,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
                                WM_cursor_grab_disable(data->window, NULL);
                        }
 #else
-                       WM_cursor_grab_disable(data->window, );
+                       WM_cursor_grab_disable(data->window, NULL);
 #endif
                }
        }
@@ -6423,90 +6505,125 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
 
 static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar)
 {
-       uiBut *but = ui_list_find_mouse_over(ar, event->x, event->y);
+       uiBut *but, *dragbut;
+       uiList *ui_list;
+       uiListDyn *dyn_data;
        int retval = WM_UI_HANDLER_CONTINUE;
        int type = event->type, val = event->val;
+       int mx, my;
+       bool is_over_dragbut = false;
 
-       if (but) {
-               uiList *ui_list = but->custom_data;
+       but = ui_list_find_mouse_over(ar, event->x, event->y);
+       if (!but) {
+               return retval;
+       }
 
-               if (ui_list) {
-                       
-                       /* convert pan to scrollwheel */
-                       if (type == MOUSEPAN) {
-                               ui_pan_to_scroll(event, &type, &val);
-                               
-                               /* if type still is mousepan, we call it handled, since delta-y accumulate */
-                               /* also see wm_event_system.c do_wheel_ui hack */
-                               if (type == MOUSEPAN)
-                                       retval = WM_UI_HANDLER_BREAK;
-                       }
-                       
-                       if (val == KM_PRESS) {
-                               
-                               if (ELEM(type, UPARROWKEY, DOWNARROWKEY) ||
-                                       ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt)))
-                               {
-                                       const int value_orig = RNA_property_int_get(&but->rnapoin, but->rnaprop);
-                                       int value, min, max;
+       ui_list = but->custom_data;
+       if (!ui_list || !ui_list->dyn_data) {
+               return retval;
+       }
+       dyn_data = ui_list->dyn_data;
 
-                                       /* activate up/down the list */
-                                       value = value_orig;
+       mx = event->x;
+       my = event->y;
+       ui_window_to_block(ar, but->block, &mx, &my);
 
-                                       if (ELEM(type, UPARROWKEY, WHEELUPMOUSE))
-                                               value--;
-                                       else
-                                               value++;
+       /* Find our "dragging" button. */
+       for (dragbut = but->block->buttons.first; dragbut; dragbut = dragbut->next) {
+               if (dragbut->poin == (void *)ui_list) {
+                       break;
+               }
+       }
+       if (dragbut && dragbut == ui_but_find_mouse_over(ar, event->x, event->y)) {
+               is_over_dragbut = true;
+       }
 
-                                       CLAMP(value, 0, ui_list->list_last_len - 1);
+       if (is_over_dragbut && type == LEFTMOUSE && val == KM_PRESS) {
+               uiHandleButtonData *data;
+               int *size = (int *)but->poin;
 
-                                       if (value < ui_list->list_scroll)
-                                               ui_list->list_scroll = value;
-                                       else if (value >= ui_list->list_scroll + ui_list->list_size)
-                                               ui_list->list_scroll = value - ui_list->list_size + 1;
+               ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
+               button_activate_state(C, but, BUTTON_STATE_INIT);
 
-                                       RNA_property_int_range(&but->rnapoin, but->rnaprop, &min, &max);
-                                       value = CLAMPIS(value, min, max);
+               data = but->active;
+               data->dragstarty = my;
 
-                                       if (value != value_orig) {
-                                               RNA_property_int_set(&but->rnapoin, but->rnaprop, value);
-                                               RNA_property_update(C, &but->rnapoin, but->rnaprop);
+               button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
 
-                                               ui_apply_undo(but);
-                                               ED_region_tag_redraw(ar);
-                                       }
+               /* Again, have to override values set by ui_numedit_begin, because our listbox button also has a rnapoin... */
+               *size = data->origvalue = (double)dyn_data->visual_height;
 
-                                       retval = WM_UI_HANDLER_BREAK;
+               retval = WM_UI_HANDLER_BREAK;
+       }
+       else {
+               /* convert pan to scrollwheel */
+               if (type == MOUSEPAN) {
+                       ui_pan_to_scroll(event, &type, &val);
+
+                       /* if type still is mousepan, we call it handled, since delta-y accumulate */
+                       /* also see wm_event_system.c do_wheel_ui hack */
+                       if (type == MOUSEPAN)
+                               retval = WM_UI_HANDLER_BREAK;
+               }
+
+               if (val == KM_PRESS) {
+                       if (ELEM(type, UPARROWKEY, DOWNARROWKEY) ||
+                           ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt)))
+                       {
+                               const int value_orig = RNA_property_int_get(&but->rnapoin, but->rnaprop);
+                               int value, min, max;
+
+                               /* activate up/down the list */
+                               value = value_orig;
+
+                               if (ELEM(type, UPARROWKEY, WHEELUPMOUSE))
+                                       value--;
+                               else
+                                       value++;
+
+                               CLAMP(value, 0, dyn_data->items_len - 1);
+
+                               RNA_property_int_range(&but->rnapoin, but->rnaprop, &min, &max);
+                               CLAMP(value, min, max);
+
+                               if (value != value_orig) {
+                                       RNA_property_int_set(&but->rnapoin, but->rnaprop, value);
+                                       RNA_property_update(C, &but->rnapoin, but->rnaprop);
+
+                                       ui_apply_undo(but);
+
+                                       ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+                                       ED_region_tag_redraw(ar);
                                }
-                               else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) {
-                                       /* silly replacement for proper grip */
-                                       if (ui_list->list_grip_size == 0)
-                                               ui_list->list_grip_size = ui_list->list_size;
+                               retval = WM_UI_HANDLER_BREAK;
+                       }
+                       else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) {
+                               /* We now have proper grip, but keep this anyway! */
+                               if (ui_list->list_grip == 0)
+                                       ui_list->list_grip = dyn_data->visual_height;
+                               /* list template will clamp */
+                               if (type == WHEELUPMOUSE)
+                                       ui_list->list_grip--;
+                               else
+                                       ui_list->list_grip++;
+
+                               ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+                               ED_region_tag_redraw(ar);
 
+                               retval = WM_UI_HANDLER_BREAK;
+                       }
+                       else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
+                               if (dyn_data->height > dyn_data->visual_height) {
+                                       /* list template will clamp */
                                        if (type == WHEELUPMOUSE)
-                                               ui_list->list_grip_size--;
+                                               ui_list->list_scroll--;
                                        else
-                                               ui_list->list_grip_size++;
-
-                                       ui_list->list_grip_size = MAX2(ui_list->list_grip_size, 1);
+                                               ui_list->list_scroll++;
 
                                        ED_region_tag_redraw(ar);
 
                                        retval = WM_UI_HANDLER_BREAK;
                                }
-                               else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
-                                       if (ui_list->list_last_len > ui_list->list_size) {
-                                               /* list template will clamp */
-                                               if (type == WHEELUPMOUSE)
-                                                       ui_list->list_scroll--;
-                                               else
-                                                       ui_list->list_scroll++;
-
-                                               ED_region_tag_redraw(ar);
-
-                                               retval = WM_UI_HANDLER_BREAK;
-                                       }
-                               }
                        }
                }
        }
index 7ff7c279dcdb9fd3994edf8c43b0313ef124fd8c..ce9b0db6b14c891bf32190626a430d31eccd4abd 100644 (file)
@@ -2432,6 +2432,15 @@ uiLayout *uiLayoutListBox(uiLayout *layout, uiList *ui_list, PointerRNA *ptr, Pr
        but->rnapoin = *actptr;
        but->rnaprop = actprop;
 
+       /* Resizing data. */
+       /* Note: we can't use usual "num button" value handling, as it only tries rnapoin when it is non-NULL... :/
+        *       So just setting but->poin, not but->pointype.
+        */
+       but->poin = (void *)&ui_list->list_grip;
+       but->hardmin = but->softmin = 0.0f;
+       but->hardmax = but->softmax = 1000.0f; /* Should be more than enough! */
+       but->a1 = 0.0f;
+
        /* only for the undo string */
        if (but->flag & UI_BUT_UNDO) {
                but->tip = RNA_property_description(actprop);
index 451c38ded2dadcfc653361193f373372c223c8cd..5ffb47c4df0a9c37cf810145a2d765d830e44259 100644 (file)
@@ -2497,30 +2497,89 @@ static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UN
        }
 }
 
+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 prepare_list(uiList *ui_list, int len, int activei, int rows, int maxrows, int columns,
+                         uiListLayoutdata *layoutdata)
+{
+       uiListDyn *dyn_data = ui_list->dyn_data;
+       int activei_row, max_scroll;
+
+       /* default rows */
+       if (rows == 0)
+               rows = 5;
+       dyn_data->visual_height_min = rows;
+       if (maxrows == 0)
+               maxrows = 5;
+       if (columns == 0)
+               columns = 9;
+       if (ui_list->list_grip >= rows)
+               maxrows = rows = ui_list->list_grip;
+       else
+               ui_list->list_grip = 0;  /* Reset to auto-size mode. */
+
+       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;
+       }
+
+       /* Expand size if needed and possible. */
+       if ((ui_list->list_grip == 0) && (rows != maxrows) && (dyn_data->height > rows)) {
+               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;
+       }
+
+       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);
+}
+
 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, int rows, int maxrows, int layout_type)
+                    const char *active_propname, int rows, int maxrows, int layout_type, int columns)
 {
        uiListType *ui_list_type;
        uiList *ui_list = NULL;
+       uiListDyn *dyn_data;
        ARegion *ar;
        uiListDrawItemFunc draw_item;
 
        PropertyRNA *prop = NULL, *activeprop;
        PropertyType type, activetype;
        StructRNA *ptype;
-       uiLayout *box, *row, *col, *sub, *overlap;
+       uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
        uiBlock *block, *subblock;
        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;
-       int items;
        int found;
-       int min, max;
 
        /* validate arguments */
        /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
@@ -2592,54 +2651,39 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
        ui_list = BLI_findstring(&ar->ui_lists, ui_list_id, offsetof(uiList, list_id));
 
        if (!ui_list) {
-               ui_list = MEM_callocN(sizeof(uiList), __func__);
+               ui_list = MEM_callocN(sizeof(uiList), AT);
                BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id));
                BLI_addtail(&ar->ui_lists, ui_list);
        }
 
+       if (!ui_list->dyn_data) {
+               ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), AT);
+       }
+       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;
 
+       if (dataptr->data && prop)
+               dyn_data->items_len = dyn_data->items_shown = len = RNA_property_collection_length(dataptr, prop);
+
        switch (layout_type) {
                case UILST_LAYOUT_DEFAULT:
-                       /* default rows */
-                       if (rows == 0)
-                               rows = 5;
-                       if (maxrows == 0)
-                               maxrows = 5;
-                       if (ui_list->list_grip_size != 0)
-                               rows = ui_list->list_grip_size;
-
                        /* layout */
                        box = uiLayoutListBox(layout, ui_list, dataptr, prop, active_dataptr, activeprop);
-                       row = uiLayoutRow(box, FALSE);
+                       glob = uiLayoutColumn(box, TRUE);
+                       row = uiLayoutRow(glob, FALSE);
                        col = uiLayoutColumn(row, TRUE);
 
                        /* init numbers */
-                       RNA_property_int_range(active_dataptr, activeprop, &min, &max);
-
-                       if (prop)
-                               len = RNA_property_collection_length(dataptr, prop);
-                       items = CLAMPIS(len, rows, MAX2(rows, maxrows));
-
-                       /* if list length changes and active is out of view, scroll to it */
-                       if ((ui_list->list_last_len != len) &&
-                           (activei < ui_list->list_scroll || activei >= ui_list->list_scroll + items))
-                       {
-                               ui_list->list_scroll = activei;
-                       }
-
-                       ui_list->list_scroll = min_ii(ui_list->list_scroll, len - items);
-                       ui_list->list_scroll = max_ii(ui_list->list_scroll, 0);
-                       ui_list->list_size = items;
-                       ui_list->list_last_len = len;
+                       prepare_list(ui_list, len, activei, rows, maxrows, 1, &layoutdata);
 
                        if (dataptr->data && prop) {
                                /* create list items */
                                RNA_PROP_BEGIN (dataptr, itemptr, prop)
                                {
-                                       if (i >= ui_list->list_scroll && i < ui_list->list_scroll + items) {
+                                       if (i >= layoutdata.start_idx && i < layoutdata.end_idx) {
                                                subblock = uiLayoutGetBlock(col);
                                                overlap = uiLayoutOverlap(col);
 
@@ -2672,17 +2716,16 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                        }
 
                        /* add dummy buttons to fill space */
-                       while (i < ui_list->list_scroll + items) {
-                               if (i >= ui_list->list_scroll)
-                                       uiItemL(col, "", ICON_NONE);
-                               i++;
+                       for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
+                               uiItemL(col, "", ICON_NONE);
                        }
 
                        /* add scrollbar */
-                       if (len > items) {
+                       if (len > layoutdata.visual_items) {
                                col = uiLayoutColumn(row, FALSE);
-                               uiDefButI(block, SCROLL, 0, "", 0, 0, UI_UNIT_X * 0.75, UI_UNIT_Y * items, &ui_list->list_scroll,
-                                         0, len - items, items, 0, "");
+                               uiDefButI(block, SCROLL, 0, "", 0, 0, UI_UNIT_X * 0.75, 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:
@@ -2719,19 +2762,24 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                        break;
                case UILST_LAYOUT_GRID:
                        box = uiLayoutListBox(layout, ui_list, dataptr, prop, active_dataptr, activeprop);
-                       col = uiLayoutColumn(box, TRUE);
-                       row = uiLayoutRow(col, FALSE);
+                       glob = uiLayoutColumn(box, TRUE);
+                       row = uiLayoutRow(glob, FALSE);
+                       col = uiLayoutColumn(row, TRUE);
+                       subrow = NULL;  /* Quite gcc warning! */
+
+                       prepare_list(ui_list, len, activei, rows, maxrows, columns, &layoutdata);
 
                        if (dataptr->data && prop) {
                                /* create list items */
                                RNA_PROP_BEGIN (dataptr, itemptr, prop)
                                {
+                                       if (i >= layoutdata.start_idx && i < layoutdata.end_idx) {
                                        /* create button */
-                                       if (!(i % 9))
-                                               row = uiLayoutRow(col, FALSE);
+                                       if (!(i % columns))
+                                               subrow = uiLayoutRow(col, FALSE);
 
-                                       subblock = uiLayoutGetBlock(row);
-                                       overlap = uiLayoutOverlap(row);
+                                       subblock = uiLayoutGetBlock(subrow);
+                                       overlap = uiLayoutOverlap(subrow);
 
                                        uiBlockSetFlag(subblock, UI_BLOCK_LIST_ITEM);
 
@@ -2753,13 +2801,41 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                                        }
 
                                        uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM);
-
+                                       }
                                        i++;
                                }
                                RNA_PROP_END;
                        }
+
+                       /* 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, SCROLL, 0, "", 0, 0, UI_UNIT_X * 0.75, 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) {
+               row = uiLayoutRow(glob, TRUE);
+               subblock = uiLayoutGetBlock(row);
+               uiBlockSetEmboss(subblock, UI_EMBOSSN);
+
+               but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.4f, ui_list,
+                                  0.0, 0.0, 0, -1, "");
+               uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+               uiBlockSetEmboss(subblock, UI_EMBOSS);
+       }
 }
 
 /************************* Operator Search Template **************************/
index a011657ce51f952e99a284de592a5f21081f6416..d00d1e61b0d3db0f5538cc6682ac560724849f93 100644 (file)
@@ -1628,12 +1628,14 @@ static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C
        active_index = RNA_int_get(ptr, "active_input_index");
        /* using different collection properties if multilayer format is enabled */
        if (multilayer) {
-               uiTemplateList(col, C, "UI_UL_list", "file_output_node", ptr, "layer_slots", ptr, "active_input_index", 0, 0, 0);
+               uiTemplateList(col, C, "UI_UL_list", "file_output_node", ptr, "layer_slots", ptr, "active_input_index",
+                              0, 0, 0, 0);
                RNA_property_collection_lookup_int(ptr, RNA_struct_find_property(ptr, "layer_slots"),
                                                   active_index, &active_input_ptr);
        }
        else {
-               uiTemplateList(col, C, "UI_UL_list", "file_output_node", ptr, "file_slots", ptr, "active_input_index", 0, 0, 0);
+               uiTemplateList(col, C, "UI_UL_list", "file_output_node", ptr, "file_slots", ptr, "active_input_index",
+                              0, 0, 0, 0);
                RNA_property_collection_lookup_int(ptr, RNA_struct_find_property(ptr, "file_slots"),
                                                   active_index, &active_input_ptr);
        }
index f95e895bef2d5eadb7c50037c808ab584fe06b99..53b373e728e553c48896e846d200dc4168135a72 100644 (file)
@@ -145,13 +145,15 @@ static void node_tree_interface_panel(const bContext *C, Panel *pa)
        split = uiLayoutRow(row, TRUE);
        col = uiLayoutColumn(split, TRUE);
        uiItemL(col, IFACE_("Inputs:"), ICON_NONE);
-       uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "inputs", &ptr, "inputs", &ptr, "active_input", 0, 0, 0);
+       uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "inputs", &ptr, "inputs", &ptr, "active_input",
+                      0, 0, 0, 0);
        opptr = uiItemFullO(col, "NODE_OT_tree_socket_add", "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
        RNA_enum_set(&opptr, "in_out", SOCK_IN);
        
        col = uiLayoutColumn(split, TRUE);
        uiItemL(col, IFACE_("Outputs:"), ICON_NONE);
-       uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "outputs", &ptr, "outputs", &ptr, "active_output", 0, 0, 0);
+       uiTemplateList(col, (bContext *)C, "NODE_UL_interface_sockets", "outputs", &ptr, "outputs", &ptr, "active_output",
+                      0, 0, 0, 0);
        opptr = uiItemFullO(col, "NODE_OT_tree_socket_add", "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS);
        RNA_enum_set(&opptr, "in_out", SOCK_OUT);
        
index 9d840ad13e8fa3f8dfdf930aa58efa69ebbe3802..c15deaeb137673da69367c28cf0b2a9b32a7c477 100644 (file)
@@ -112,22 +112,36 @@ typedef struct Panel {            /* the part from uiBlock that needs saved in file */
        void *activedata;                       /* runtime for panel manipulation */
 } Panel;
 
-typedef struct uiList {                                /* some list UI data need to be saved in file */
+/* uiList dynamic data... */
+/* These two Lines with # tell makesdna this struct can be excluded. */
+#
+#
+typedef struct uiListDyn {
+       int height;                   /* Number of rows needed to draw all elements. */
+       int visual_height;            /* Actual visual height of the list (in rows). */
+       int visual_height_min;        /* Minimal visual height of the list (in rows). */
+
+       int items_len;                /* Number of items in collection. */
+       int items_shown;              /* Number of items actually visible after filtering. */
+} uiListDyn;
+
+typedef struct uiList {           /* some list UI data need to be saved in file */
        struct uiList *next, *prev;
 
-       struct uiListType *type;                /* runtime */
-       void *padp;
+       struct uiListType *type;      /* runtime */
 
-       char list_id[64];                               /* defined as UI_MAX_NAME_STR */
+       char list_id[64];             /* defined as UI_MAX_NAME_STR */
 
-       int layout_type;                                /* How items are layedout in the list */
-       int padi;
+       int layout_type;              /* How items are layedout in the list */
+       int flag;
 
        int list_scroll;
-       int list_size;
+       int list_grip;
        int list_last_len;
-       int list_grip_size;
-/*     char list_search[64]; */
+       int padi1;
+
+       /* Dynamic data (runtime). */
+       uiListDyn *dyn_data;
 } uiList;
 
 typedef struct ScrArea {
@@ -232,13 +246,18 @@ typedef struct ARegion {
 #define PNL_DEFAULT_CLOSED             1
 #define PNL_NO_HEADER                  2
 
-/* uilist layout_type */
+/* uiList layout_type */
 enum {
        UILST_LAYOUT_DEFAULT          = 0,
        UILST_LAYOUT_COMPACT          = 1,
        UILST_LAYOUT_GRID             = 2,
 };
 
+/* uiList flag */
+enum {
+       UILST_SCROLL_TO_ACTIVE_ITEM   = 1 << 0,          /* Scroll list to make active item visible. */
+};
+
 /* regiontype, first two are the default set */
 /* Do NOT change order, append on end. Types are hardcoded needed */
 enum {
index 7b90a78811fb5dc322fb1138d91dc9e8e7d0b334..d79a40aa6152e20c3449b6e2b5902d36bc5e0f3a 100644 (file)
@@ -796,9 +796,10 @@ void RNA_api_ui_layout(StructRNA *srna)
        parm = RNA_def_string(func, "active_propname", "", 0, "",
                              "Identifier of the integer property in active_data, index of the active item");
        RNA_def_property_flag(parm, PROP_REQUIRED);
-       RNA_def_int(func, "rows", 5, 0, INT_MAX, "", "Number of rows to display", 0, INT_MAX);
-       RNA_def_int(func, "maxrows", 5, 0, INT_MAX, "", "Maximum number of rows to display", 0, INT_MAX);
+       RNA_def_int(func, "rows", 5, 0, INT_MAX, "", "Default and minimum number of rows to display", 0, INT_MAX);
+       RNA_def_int(func, "maxrows", 5, 0, INT_MAX, "", "Default maximum number of rows to display", 0, INT_MAX);
        RNA_def_enum(func, "type", uilist_layout_type_items, UILST_LAYOUT_DEFAULT, "Type", "Type of layout to use");
+       RNA_def_int(func, "columns", 9, 0, INT_MAX, "", "Number of items to display per row, for GRID layout", 0, INT_MAX);
 
        func = RNA_def_function(srna, "template_running_jobs", "uiTemplateRunningJobs");
        RNA_def_function_flag(func, FUNC_USE_CONTEXT);
index 21502492c0544a07190946bdd1567a65b99d2980..65d2437a4bb2dfd952ded3be36e2f3ee5418dea0 100644 (file)
@@ -458,7 +458,7 @@ void uiTemplateLayers(struct uiLayout *layout, struct PointerRNA *ptr, char *pro
 void uiTemplateImageLayers(struct uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser) {STUB_ASSERT(0);}
 void uiTemplateList(struct uiLayout *layout, struct bContext *C, const char *listtype_name, const char *list_id,
                     PointerRNA *dataptr, const char *propname, PointerRNA *active_dataptr,
-                    const char *active_propname, int rows, int maxrows, int layout_type) {STUB_ASSERT(0);}
+                    const char *active_propname, int rows, int maxrows, int layout_type, int columns) {STUB_ASSERT(0);}
 void uiTemplateRunningJobs(struct uiLayout *layout, struct bContext *C) {STUB_ASSERT(0);}
 void uiTemplateOperatorSearch(struct uiLayout *layout) {STUB_ASSERT(0);}
 void uiTemplateHeader3D(struct uiLayout *layout, struct bContext *C) {STUB_ASSERT(0);}