* Buttons are now created first, and after that the layout is computed. This means the layout engine now works at button level, and makes it easier to write templates. Otherwise you had to store all info and create the buttons later. * Added interface_templates.c as a separate file to put templates in. These can contain regular buttons, and can be put in a Free layout, which means you can specify manual coordinates, but still get nested correct inside other layouts. * API was changed to allow better nesting. Previously items were added in the last added layout specifier, i.e. one level up in the layout hierarchy. This doesn't work well in always, so now when creating things like rows or columns it always returns a layout which you have to add the items in. All py scripts were updated to follow this. * Computing the layout now goes in two passes, first estimating the required width/height of all nested layouts, and then in the second pass using the results of that to decide on the actual locations. * Enum and array buttons now follow the direction of the layout, i.e. they are vertical or horizontal depending if they are in a column or row. * Color properties now get a color picker, and only get the additional RGB sliders with Expand=True. * File/directory string properties now get a button next to them for opening the file browse, though this is not implemented yet. * Layout items can now be aligned, set align=True when creating a column, row, etc. * Buttons now get a minimum width of one icon (avoids squashing icon buttons). * Moved some more space variables into Style.
1709 lines
39 KiB
C
1709 lines
39 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Contributor(s): Blender Foundation 2009.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_ID.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_string.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_idprop.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_screen.h"
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_resources.h"
|
|
#include "UI_view2d.h"
|
|
|
|
#include "BIF_gl.h"
|
|
|
|
#include "ED_util.h"
|
|
#include "ED_types.h"
|
|
#include "ED_screen.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "interface_intern.h"
|
|
|
|
/************************ Structs and Defines *************************/
|
|
|
|
#define RNA_NO_INDEX -1
|
|
#define RNA_ENUM_VALUE -2
|
|
|
|
#define EM_SEPR_X 6
|
|
#define EM_SEPR_Y 6
|
|
|
|
/* uiLayoutCommon */
|
|
|
|
typedef struct uiLayoutCommon {
|
|
int type;
|
|
int opcontext;
|
|
|
|
int emw, emh;
|
|
|
|
uiMenuHandleFunc handlefunc;
|
|
void *argv;
|
|
|
|
uiStyle *style;
|
|
uiBlock *block;
|
|
} uiLayoutCommon;
|
|
|
|
/* Item */
|
|
|
|
typedef enum uiItemType {
|
|
ITEM_BUTTON,
|
|
|
|
ITEM_LAYOUT_ROW,
|
|
ITEM_LAYOUT_COLUMN,
|
|
ITEM_LAYOUT_COLUMN_FLOW,
|
|
ITEM_LAYOUT_ROW_FLOW,
|
|
ITEM_LAYOUT_BOX,
|
|
ITEM_LAYOUT_FREE,
|
|
ITEM_LAYOUT_SPLIT,
|
|
|
|
ITEM_LAYOUT_ROOT
|
|
#if 0
|
|
TEMPLATE_COLUMN_FLOW,
|
|
TEMPLATE_SPLIT,
|
|
TEMPLATE_BOX,
|
|
|
|
TEMPLATE_HEADER,
|
|
TEMPLATE_HEADER_ID
|
|
#endif
|
|
} uiItemType;
|
|
|
|
typedef struct uiItem {
|
|
void *next, *prev;
|
|
uiItemType type;
|
|
} uiItem;
|
|
|
|
typedef struct uiButtonItem {
|
|
uiItem item;
|
|
uiBut *but;
|
|
} uiButtonItem;
|
|
|
|
struct uiLayout {
|
|
uiItem item;
|
|
|
|
uiLayoutCommon *common;
|
|
ListBase items;
|
|
|
|
int x, y, w, h;
|
|
int space;
|
|
int align;
|
|
};
|
|
|
|
typedef struct uiLayoutItemFlow {
|
|
uiLayout litem;
|
|
int number;
|
|
int totcol;
|
|
} uiLayoutItemFlow;
|
|
|
|
typedef struct uiLayoutItemSplt {
|
|
uiLayout litem;
|
|
int number;
|
|
int lr;
|
|
} uiLayoutItemSplt;
|
|
|
|
typedef struct uiLayoutItemBx {
|
|
uiLayout litem;
|
|
} uiLayoutItemBx;
|
|
|
|
typedef struct uiLayoutItemRoot {
|
|
uiLayout litem;
|
|
} uiLayoutItemRoot;
|
|
|
|
/************************** Item ***************************/
|
|
|
|
static char *ui_item_name_add_colon(char *name, char namestr[UI_MAX_NAME_STR])
|
|
{
|
|
int len= strlen(name);
|
|
|
|
if(len != 0 && len+1 < UI_MAX_NAME_STR) {
|
|
BLI_strncpy(namestr, name, UI_MAX_NAME_STR);
|
|
namestr[len]= ':';
|
|
namestr[len+1]= '\0';
|
|
return namestr;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
#define UI_FIT_EXPAND 1
|
|
|
|
static int ui_item_fit(int item, int pos, int all, int available, int spacing, int last, int flag)
|
|
{
|
|
/* available == 0 is unlimited */
|
|
|
|
if(available != 0 && all > available-spacing) {
|
|
/* contents is bigger than available space */
|
|
if(last)
|
|
return available-pos;
|
|
else
|
|
return (item*(available-spacing))/all;
|
|
}
|
|
else {
|
|
/* contents is smaller or equal to available space */
|
|
if(available != 0 && (flag & UI_FIT_EXPAND)) {
|
|
if(last)
|
|
return available-pos;
|
|
else
|
|
return (item*(available-spacing))/all;
|
|
}
|
|
else
|
|
return item;
|
|
}
|
|
}
|
|
|
|
/* variable button size in which direction? */
|
|
#define UI_ITEM_VARY_X 1
|
|
#define UI_ITEM_VARY_Y 2
|
|
|
|
static int ui_layout_vary_direction(uiLayout *layout)
|
|
{
|
|
return (layout->common->type == UI_LAYOUT_HEADER)? UI_ITEM_VARY_X: UI_ITEM_VARY_Y;
|
|
}
|
|
|
|
/* estimated size of text + icon */
|
|
static int ui_text_icon_width(uiLayout *layout, char *name, int icon)
|
|
{
|
|
int variable = ui_layout_vary_direction(layout) == UI_ITEM_VARY_X;
|
|
|
|
if(icon && strcmp(name, "") == 0)
|
|
return UI_UNIT_X; /* icon only */
|
|
else if(icon)
|
|
return (variable)? UI_GetStringWidth(name) + UI_UNIT_X: 10*UI_UNIT_X; /* icon + text */
|
|
else
|
|
return (variable)? UI_GetStringWidth(name) + UI_UNIT_X: 10*UI_UNIT_X; /* text only */
|
|
}
|
|
|
|
static void ui_item_size(uiItem *item, int *r_w, int *r_h)
|
|
{
|
|
if(item->type == ITEM_BUTTON) {
|
|
uiButtonItem *bitem= (uiButtonItem*)item;
|
|
|
|
if(r_w) *r_w= bitem->but->x2 - bitem->but->x1;
|
|
if(r_h) *r_h= bitem->but->y2 - bitem->but->y1;
|
|
}
|
|
else {
|
|
uiLayout *litem= (uiLayout*)item;
|
|
|
|
if(r_w) *r_w= litem->w;
|
|
if(r_h) *r_h= litem->h;
|
|
}
|
|
}
|
|
|
|
static void ui_item_offset(uiItem *item, int *r_x, int *r_y)
|
|
{
|
|
if(item->type == ITEM_BUTTON) {
|
|
uiButtonItem *bitem= (uiButtonItem*)item;
|
|
|
|
if(r_x) *r_x= bitem->but->x1;
|
|
if(r_y) *r_y= bitem->but->y1;
|
|
}
|
|
else {
|
|
if(r_x) *r_x= 0;
|
|
if(r_y) *r_y= 0;
|
|
}
|
|
}
|
|
|
|
static void ui_item_position(uiItem *item, int x, int y, int w, int h)
|
|
{
|
|
if(item->type == ITEM_BUTTON) {
|
|
uiButtonItem *bitem= (uiButtonItem*)item;
|
|
|
|
bitem->but->x1= x;
|
|
bitem->but->y1= y;
|
|
bitem->but->x2= x+w;
|
|
bitem->but->y2= y+h;
|
|
}
|
|
else {
|
|
uiLayout *litem= (uiLayout*)item;
|
|
|
|
litem->x= x;
|
|
litem->y= y+h;
|
|
litem->w= w;
|
|
litem->h= h;
|
|
}
|
|
}
|
|
|
|
/******************** Special RNA Items *********************/
|
|
|
|
static int ui_layout_local_dir(uiLayout *layout)
|
|
{
|
|
switch(layout->item.type) {
|
|
case ITEM_LAYOUT_ROW:
|
|
case ITEM_LAYOUT_ROOT:
|
|
return UI_LAYOUT_HORIZONTAL;
|
|
case ITEM_LAYOUT_COLUMN:
|
|
case ITEM_LAYOUT_COLUMN_FLOW:
|
|
case ITEM_LAYOUT_SPLIT:
|
|
case ITEM_LAYOUT_FREE:
|
|
case ITEM_LAYOUT_BOX:
|
|
default:
|
|
return UI_LAYOUT_VERTICAL;
|
|
}
|
|
}
|
|
|
|
static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, int align)
|
|
{
|
|
uiLayout *sub;
|
|
|
|
if(ui_layout_local_dir(test) == UI_LAYOUT_HORIZONTAL)
|
|
sub= uiLayoutRow(layout, align);
|
|
else
|
|
sub= uiLayoutColumn(layout, align);
|
|
|
|
sub->space= 0;
|
|
return sub;
|
|
}
|
|
|
|
/* create buttons for an item with an RNA array */
|
|
static void ui_item_array(uiLayout *layout, uiBlock *block, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int h, int expand)
|
|
{
|
|
uiStyle *style= layout->common->style;
|
|
PropertyType type;
|
|
PropertySubType subtype;
|
|
uiLayout *sub;
|
|
int a;
|
|
|
|
/* retrieve type and subtype */
|
|
type= RNA_property_type(prop);
|
|
subtype= RNA_property_subtype(prop);
|
|
|
|
sub= ui_item_local_sublayout(layout, layout, 0);
|
|
uiBlockSetCurLayout(block, sub);
|
|
|
|
/* create label */
|
|
if(strcmp(name, "") != 0)
|
|
uiDefBut(block, LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
|
|
/* create buttons */
|
|
if(type == PROP_BOOLEAN && len == 20) {
|
|
/* special check for layer layout */
|
|
int butw, buth, unit;
|
|
|
|
uiBlockSetCurLayout(block, uiLayoutFree(layout, 0));
|
|
|
|
unit= UI_UNIT_X*0.75;
|
|
butw= unit;
|
|
buth= unit;
|
|
|
|
uiBlockBeginAlign(block);
|
|
for(a=0; a<5; a++)
|
|
uiDefAutoButR(block, ptr, prop, a, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
|
|
for(a=0; a<5; a++)
|
|
uiDefAutoButR(block, ptr, prop, a+10, "", ICON_BLANK1, x + butw*a, y, butw, buth);
|
|
uiBlockEndAlign(block);
|
|
|
|
x += 5*butw + style->buttonspacex;
|
|
|
|
uiBlockBeginAlign(block);
|
|
for(a=0; a<5; a++)
|
|
uiDefAutoButR(block, ptr, prop, a+5, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
|
|
for(a=0; a<5; a++)
|
|
uiDefAutoButR(block, ptr, prop, a+15, "", ICON_BLANK1, x + butw*a, y, butw, buth);
|
|
uiBlockEndAlign(block);
|
|
}
|
|
else if(subtype == PROP_MATRIX) {
|
|
/* matrix layout */
|
|
int row, col;
|
|
|
|
uiBlockSetCurLayout(block, uiLayoutFree(layout, 1));
|
|
|
|
len= ceil(sqrt(len));
|
|
|
|
h /= len;
|
|
w /= len;
|
|
|
|
// XXX test
|
|
for(a=0; a<len; a++) {
|
|
col= a%len;
|
|
row= a/len;
|
|
|
|
uiDefAutoButR(block, ptr, prop, a, "", 0, x + w*col, y+(row-a-1)*UI_UNIT_Y, w, UI_UNIT_Y);
|
|
}
|
|
}
|
|
else if(len <= 4 && ELEM3(subtype, PROP_ROTATION, PROP_VECTOR, PROP_COLOR)) {
|
|
uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, sub, 1));
|
|
|
|
if(subtype == PROP_COLOR)
|
|
uiDefAutoButR(block, ptr, prop, -1, "", 0, 0, 0, w, UI_UNIT_Y);
|
|
|
|
if(subtype != PROP_COLOR || expand) {
|
|
/* layout for known array subtypes */
|
|
static char vectoritem[4]= {'X', 'Y', 'Z', 'W'};
|
|
static char quatitem[4]= {'W', 'X', 'Y', 'Z'};
|
|
static char coloritem[4]= {'R', 'G', 'B', 'A'};
|
|
char str[3];
|
|
|
|
for(a=0; a<len; a++) {
|
|
if(len == 4 && subtype == PROP_ROTATION)
|
|
str[0]= quatitem[a];
|
|
else if(subtype == PROP_VECTOR || subtype == PROP_ROTATION)
|
|
str[0]= vectoritem[a];
|
|
else
|
|
str[0]= coloritem[a];
|
|
|
|
if(type == PROP_BOOLEAN) {
|
|
str[1]= '\0';
|
|
}
|
|
else {
|
|
str[1]= ':';
|
|
str[2]= '\0';
|
|
}
|
|
|
|
uiDefAutoButR(block, ptr, prop, a, str, 0, 0, 0, w, UI_UNIT_Y);
|
|
}
|
|
}
|
|
else if(subtype == PROP_COLOR && len == 4)
|
|
uiDefAutoButR(block, ptr, prop, 3, "A:", 0, 0, 0, w, UI_UNIT_Y);
|
|
}
|
|
else {
|
|
/* default array layout */
|
|
uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, sub, 1));
|
|
|
|
for(a=0; a<len; a++)
|
|
uiDefAutoButR(block, ptr, prop, a, "", 0, 0, 0, w, UI_UNIT_Y);
|
|
}
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
}
|
|
|
|
static void ui_item_enum_row(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int x, int y, int w, int h)
|
|
{
|
|
const EnumPropertyItem *item;
|
|
int a, totitem, itemw;
|
|
const char *propname;
|
|
|
|
propname= RNA_property_identifier(prop);
|
|
RNA_property_enum_items(ptr, prop, &item, &totitem);
|
|
|
|
uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, layout, 1));
|
|
for(a=0; a<totitem; a++) {
|
|
itemw= ui_text_icon_width(block->curlayout, (char*)item[a].name, 0);
|
|
uiDefButR(block, ROW, 0, NULL, 0, 0, itemw, h, ptr, propname, -1, 0, item[a].value, -1, -1, NULL);
|
|
}
|
|
uiBlockSetCurLayout(block, layout);
|
|
}
|
|
|
|
/* create label + button for RNA property */
|
|
static void ui_item_with_label(uiLayout *layout, uiBlock *block, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int x, int y, int w, int h)
|
|
{
|
|
uiLayout *sub;
|
|
PropertySubType subtype;
|
|
|
|
sub= uiLayoutRow(layout, 0);
|
|
uiBlockSetCurLayout(block, sub);
|
|
|
|
if(strcmp(name, "") != 0) {
|
|
w= w/2;
|
|
uiDefBut(block, LABEL, 0, name, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
|
|
}
|
|
|
|
subtype= RNA_property_subtype(prop);
|
|
|
|
if(subtype == PROP_FILEPATH || subtype == PROP_DIRPATH) {
|
|
uiBlockSetCurLayout(block, uiLayoutRow(sub, 1));
|
|
uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w-UI_UNIT_X, h);
|
|
uiDefIconBut(block, BUT, 0, ICON_FILESEL, x, y, UI_UNIT_X, h, NULL, 0.0f, 0.0f, 0.0f, 0.0f, "DUMMY file select button"); /* XXX */
|
|
}
|
|
else
|
|
uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w, h);
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
}
|
|
|
|
/********************* Button Items *************************/
|
|
|
|
/* disabled item */
|
|
static void ui_item_disabled(uiLayout *layout, char *name)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
uiBut *but;
|
|
int w;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
if(!name)
|
|
name= "";
|
|
|
|
w= ui_text_icon_width(layout, name, 0);
|
|
|
|
but= uiDefBut(block, LABEL, 0, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
but->flag |= UI_BUT_DISABLED;
|
|
but->lock = 1;
|
|
but->lockstr = "";
|
|
}
|
|
|
|
/* operator items */
|
|
void uiItemFullO(uiLayout *layout, char *name, int icon, char *idname, IDProperty *properties, int context)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
wmOperatorType *ot= WM_operatortype_find(idname);
|
|
uiBut *but;
|
|
int w;
|
|
|
|
if(!ot) {
|
|
ui_item_disabled(layout, idname);
|
|
return;
|
|
}
|
|
|
|
if(!name)
|
|
name= ot->name;
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
/* create button */
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
w= ui_text_icon_width(layout, name, icon);
|
|
|
|
if(icon && strcmp(name, "") != 0)
|
|
but= uiDefIconTextButO(block, BUT, ot->idname, context, icon, (char*)name, 0, 0, w, UI_UNIT_Y, NULL);
|
|
else if(icon)
|
|
but= uiDefIconButO(block, BUT, ot->idname, context, icon, 0, 0, w, UI_UNIT_Y, NULL);
|
|
else
|
|
but= uiDefButO(block, BUT, ot->idname, context, (char*)name, 0, 0, w, UI_UNIT_Y, NULL);
|
|
|
|
/* assign properties */
|
|
if(properties) {
|
|
PointerRNA *opptr= uiButGetOperatorPtrRNA(but);
|
|
opptr->data= properties;
|
|
}
|
|
}
|
|
|
|
static char *ui_menu_enumpropname(char *opname, char *propname, int retval)
|
|
{
|
|
wmOperatorType *ot= WM_operatortype_find(opname);
|
|
PointerRNA ptr;
|
|
PropertyRNA *prop;
|
|
|
|
if(!ot || !ot->srna)
|
|
return "";
|
|
|
|
RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
|
|
prop= RNA_struct_find_property(&ptr, propname);
|
|
|
|
if(prop) {
|
|
const EnumPropertyItem *item;
|
|
int totitem, i;
|
|
|
|
RNA_property_enum_items(&ptr, prop, &item, &totitem);
|
|
|
|
for (i=0; i<totitem; i++) {
|
|
if(item[i].value==retval)
|
|
return (char*)item[i].name;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void uiItemEnumO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
|
|
{
|
|
PointerRNA ptr;
|
|
|
|
WM_operator_properties_create(&ptr, opname);
|
|
RNA_enum_set(&ptr, propname, value);
|
|
|
|
if(!name)
|
|
name= ui_menu_enumpropname(opname, propname, value);
|
|
|
|
uiItemFullO(layout, name, icon, opname, ptr.data, layout->common->opcontext);
|
|
}
|
|
|
|
void uiItemsEnumO(uiLayout *layout, char *opname, char *propname)
|
|
{
|
|
wmOperatorType *ot= WM_operatortype_find(opname);
|
|
PointerRNA ptr;
|
|
PropertyRNA *prop;
|
|
|
|
if(!ot || !ot->srna) {
|
|
ui_item_disabled(layout, opname);
|
|
return;
|
|
}
|
|
|
|
RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
|
|
prop= RNA_struct_find_property(&ptr, propname);
|
|
|
|
if(prop && RNA_property_type(prop) == PROP_ENUM) {
|
|
const EnumPropertyItem *item;
|
|
int totitem, i;
|
|
|
|
RNA_property_enum_items(&ptr, prop, &item, &totitem);
|
|
|
|
for(i=0; i<totitem; i++)
|
|
uiItemEnumO(layout, NULL, 0, opname, propname, item[i].value);
|
|
}
|
|
}
|
|
|
|
void uiItemBooleanO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
|
|
{
|
|
PointerRNA ptr;
|
|
|
|
WM_operator_properties_create(&ptr, opname);
|
|
RNA_boolean_set(&ptr, propname, value);
|
|
|
|
uiItemFullO(layout, name, icon, opname, ptr.data, layout->common->opcontext);
|
|
}
|
|
|
|
void uiItemIntO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
|
|
{
|
|
PointerRNA ptr;
|
|
|
|
WM_operator_properties_create(&ptr, opname);
|
|
RNA_int_set(&ptr, propname, value);
|
|
|
|
uiItemFullO(layout, name, icon, opname, ptr.data, layout->common->opcontext);
|
|
}
|
|
|
|
void uiItemFloatO(uiLayout *layout, char *name, int icon, char *opname, char *propname, float value)
|
|
{
|
|
PointerRNA ptr;
|
|
|
|
WM_operator_properties_create(&ptr, opname);
|
|
RNA_float_set(&ptr, propname, value);
|
|
|
|
uiItemFullO(layout, name, icon, opname, ptr.data, layout->common->opcontext);
|
|
}
|
|
|
|
void uiItemStringO(uiLayout *layout, char *name, int icon, char *opname, char *propname, char *value)
|
|
{
|
|
PointerRNA ptr;
|
|
|
|
WM_operator_properties_create(&ptr, opname);
|
|
RNA_string_set(&ptr, propname, value);
|
|
|
|
uiItemFullO(layout, name, icon, opname, ptr.data, layout->common->opcontext);
|
|
}
|
|
|
|
void uiItemO(uiLayout *layout, char *name, int icon, char *opname)
|
|
{
|
|
uiItemFullO(layout, name, icon, opname, NULL, layout->common->opcontext);
|
|
}
|
|
|
|
/* RNA property items */
|
|
|
|
static void ui_item_rna_size(uiLayout *layout, char *name, int icon, PropertyRNA *prop, int index, int *r_w, int *r_h)
|
|
{
|
|
PropertyType type;
|
|
PropertySubType subtype;
|
|
int len, w, h;
|
|
|
|
w= ui_text_icon_width(layout, name, icon);
|
|
h= UI_UNIT_Y;
|
|
|
|
/* arbitrary extended width by type */
|
|
type= RNA_property_type(prop);
|
|
subtype= RNA_property_subtype(prop);
|
|
len= RNA_property_array_length(prop);
|
|
|
|
if(type == PROP_STRING)
|
|
w += 10*UI_UNIT_X;
|
|
|
|
/* increase height for arrays */
|
|
if(index == RNA_NO_INDEX && len > 0) {
|
|
if(strcmp(name, "") == 0 && icon == 0)
|
|
h= 0;
|
|
|
|
if(type == PROP_BOOLEAN && len == 20)
|
|
h += 2*UI_UNIT_Y;
|
|
else if(subtype == PROP_MATRIX)
|
|
h += ceil(sqrt(len))*UI_UNIT_Y;
|
|
else
|
|
h += len*UI_UNIT_Y;
|
|
}
|
|
else if(ui_layout_vary_direction(layout) == UI_ITEM_VARY_X) {
|
|
if(type == PROP_BOOLEAN && strcmp(name, "") != 0)
|
|
w += UI_UNIT_X;
|
|
}
|
|
|
|
*r_w= w;
|
|
*r_h= h;
|
|
}
|
|
|
|
void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int value, int expand)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
PropertyType type;
|
|
char namestr[UI_MAX_NAME_STR];
|
|
int len, w, h;
|
|
|
|
if(!ptr->data || !prop)
|
|
return;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
/* retrieve info */
|
|
type= RNA_property_type(prop);
|
|
len= RNA_property_array_length(prop);
|
|
|
|
/* set name and icon */
|
|
if(!name)
|
|
name= (char*)RNA_property_ui_name(prop);
|
|
|
|
if(ELEM5(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_ENUM, PROP_POINTER))
|
|
name= ui_item_name_add_colon(name, namestr);
|
|
if(type == PROP_BOOLEAN && len)
|
|
name= ui_item_name_add_colon(name, namestr);
|
|
|
|
if(layout->common->type == UI_LAYOUT_MENU) {
|
|
if(type == PROP_BOOLEAN)
|
|
icon= (RNA_property_boolean_get(ptr, prop))? ICON_CHECKBOX_HLT: ICON_CHECKBOX_DEHLT;
|
|
else if(type == PROP_ENUM && index == RNA_ENUM_VALUE)
|
|
icon= (RNA_property_enum_get(ptr, prop) == value)? ICON_CHECKBOX_HLT: ICON_CHECKBOX_DEHLT;
|
|
}
|
|
|
|
/* get size */
|
|
ui_item_rna_size(layout, name, icon, prop, index, &w, &h);
|
|
|
|
/* array property */
|
|
if(index == RNA_NO_INDEX && len > 0)
|
|
ui_item_array(layout, block, name, icon, ptr, prop, len, 0, 0, w, h, expand);
|
|
/* enum item */
|
|
else if(type == PROP_ENUM && index == RNA_ENUM_VALUE) {
|
|
char *identifier= (char*)RNA_property_identifier(prop);
|
|
|
|
if(icon && strcmp(name, "") != 0)
|
|
uiDefIconTextButR(block, ROW, 0, icon, name, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
|
|
else if(icon)
|
|
uiDefIconButR(block, ROW, 0, icon, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
|
|
else
|
|
uiDefButR(block, ROW, 0, name, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
|
|
}
|
|
/* expanded enum */
|
|
else if(type == PROP_ENUM && expand)
|
|
ui_item_enum_row(layout, block, ptr, prop, 0, 0, w, h);
|
|
/* property with separate label */
|
|
else if(type == PROP_ENUM || type == PROP_STRING || type == PROP_POINTER)
|
|
ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h);
|
|
/* single button */
|
|
else
|
|
uiDefAutoButR(block, ptr, prop, index, (char*)name, icon, 0, 0, w, h);
|
|
}
|
|
|
|
void uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int expand)
|
|
{
|
|
PropertyRNA *prop;
|
|
|
|
if(!ptr->data || !propname)
|
|
return;
|
|
|
|
prop= RNA_struct_find_property(ptr, propname);
|
|
|
|
if(!prop) {
|
|
ui_item_disabled(layout, propname);
|
|
printf("uiItemR: property not found: %s\n", propname);
|
|
return;
|
|
}
|
|
|
|
uiItemFullR(layout, name, icon, ptr, prop, RNA_NO_INDEX, 0, expand);
|
|
}
|
|
|
|
void uiItemEnumR(uiLayout *layout, char *name, int icon, struct PointerRNA *ptr, char *propname, int value)
|
|
{
|
|
PropertyRNA *prop;
|
|
|
|
if(!ptr->data || !propname)
|
|
return;
|
|
|
|
prop= RNA_struct_find_property(ptr, propname);
|
|
|
|
if(!prop) {
|
|
ui_item_disabled(layout, propname);
|
|
printf("uiItemEnumR: property not found: %s\n", propname);
|
|
return;
|
|
}
|
|
|
|
uiItemFullR(layout, name, icon, ptr, prop, RNA_ENUM_VALUE, value, 0);
|
|
}
|
|
|
|
void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, char *propname)
|
|
{
|
|
PropertyRNA *prop;
|
|
|
|
prop= RNA_struct_find_property(ptr, propname);
|
|
|
|
if(!prop) {
|
|
ui_item_disabled(layout, propname);
|
|
return;
|
|
}
|
|
|
|
if(RNA_property_type(prop) == PROP_ENUM) {
|
|
const EnumPropertyItem *item;
|
|
int totitem, i;
|
|
|
|
RNA_property_enum_items(ptr, prop, &item, &totitem);
|
|
|
|
for(i=0; i<totitem; i++)
|
|
uiItemEnumR(layout, (char*)item[i].name, 0, ptr, propname, item[i].value);
|
|
}
|
|
}
|
|
|
|
/* menu item */
|
|
static void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
|
|
{
|
|
MenuType *mt= (MenuType*)arg_mt;
|
|
Menu menu = {0};
|
|
|
|
menu.type= mt;
|
|
menu.layout= layout;
|
|
mt->draw(C, &menu);
|
|
}
|
|
|
|
static void ui_item_menu(uiLayout *layout, char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
uiBut *but;
|
|
int w, h;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
if(layout->common->type == UI_LAYOUT_HEADER)
|
|
uiBlockSetEmboss(block, UI_EMBOSSP);
|
|
|
|
if(!name)
|
|
name= "";
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
w= ui_text_icon_width(layout, name, icon);
|
|
h= UI_UNIT_Y;
|
|
|
|
if(layout->common->type == UI_LAYOUT_HEADER) /* ugly .. */
|
|
w -= 3;
|
|
|
|
if(icon)
|
|
but= uiDefIconTextMenuBut(block, func, arg, icon, (char*)name, 0, 0, w, h, "");
|
|
else
|
|
but= uiDefMenuBut(block, func, arg, (char*)name, 0, 0, w, h, "");
|
|
|
|
if(argN) { /* ugly .. */
|
|
but->poin= (char*)but;
|
|
but->func_argN= argN;
|
|
}
|
|
|
|
if(layout->common->type == UI_LAYOUT_HEADER)
|
|
uiBlockSetEmboss(block, UI_EMBOSS);
|
|
}
|
|
|
|
void uiItemM(uiLayout *layout, bContext *C, char *name, int icon, char *menuname)
|
|
{
|
|
ARegion *ar= CTX_wm_region(C);
|
|
MenuType *mt;
|
|
|
|
if(!menuname)
|
|
return;
|
|
|
|
for(mt=ar->type->menutypes.first; mt; mt=mt->next) {
|
|
if(strcmp(menuname, mt->idname) == 0) {
|
|
if(!name)
|
|
name= mt->label;
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
ui_item_menu(layout, name, icon, ui_item_menutype_func, mt, NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* label item */
|
|
void uiItemL(uiLayout *layout, char *name, int icon)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
uiBut *but;
|
|
int w;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
if(!name)
|
|
name= "";
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
w= ui_text_icon_width(layout, name, icon);
|
|
|
|
if(icon && strcmp(name, "") != 0)
|
|
but= uiDefIconTextBut(block, LABEL, 0, icon, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
else if(icon)
|
|
but= uiDefIconBut(block, LABEL, 0, icon, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
else
|
|
but= uiDefBut(block, LABEL, 0, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
}
|
|
|
|
/* value item */
|
|
void uiItemV(uiLayout *layout, char *name, int icon, int argval)
|
|
{
|
|
/* label */
|
|
uiBlock *block= layout->common->block;
|
|
float *retvalue= (block->handle)? &block->handle->retvalue: NULL;
|
|
int w;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
|
|
if(!name)
|
|
name= "";
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
w= ui_text_icon_width(layout, name, icon);
|
|
|
|
if(icon && strcmp(name, "") != 0)
|
|
uiDefIconTextButF(block, BUTM, 0, icon, (char*)name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
|
|
else if(icon)
|
|
uiDefIconButF(block, BUTM, 0, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
|
|
else
|
|
uiDefButF(block, BUTM, 0, (char*)name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
|
|
}
|
|
|
|
/* separator item */
|
|
void uiItemS(uiLayout *layout)
|
|
{
|
|
uiBlock *block= layout->common->block;
|
|
|
|
uiBlockSetCurLayout(block, layout);
|
|
uiDefBut(block, SEPR, 0, "", 0, 0, EM_SEPR_X, EM_SEPR_Y, NULL, 0.0, 0.0, 0, 0, "");
|
|
}
|
|
|
|
/* level items */
|
|
void uiItemMenuF(uiLayout *layout, char *name, int icon, uiMenuCreateFunc func)
|
|
{
|
|
if(!func)
|
|
return;
|
|
|
|
ui_item_menu(layout, name, icon, func, NULL, NULL);
|
|
}
|
|
|
|
typedef struct MenuItemLevel {
|
|
int opcontext;
|
|
char *opname;
|
|
char *propname;
|
|
PointerRNA rnapoin;
|
|
} MenuItemLevel;
|
|
|
|
static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
|
|
{
|
|
MenuItemLevel *lvl= (MenuItemLevel*)(((uiBut*)arg)->func_argN);
|
|
|
|
uiLayoutContext(layout, WM_OP_EXEC_REGION_WIN);
|
|
uiItemsEnumO(layout, lvl->opname, lvl->propname);
|
|
}
|
|
|
|
void uiItemMenuEnumO(uiLayout *layout, char *name, int icon, char *opname, char *propname)
|
|
{
|
|
wmOperatorType *ot= WM_operatortype_find(opname);
|
|
MenuItemLevel *lvl;
|
|
|
|
if(!ot || !ot->srna) {
|
|
ui_item_disabled(layout, opname);
|
|
return;
|
|
}
|
|
|
|
if(!name)
|
|
name= ot->name;
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
lvl= MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
|
|
lvl->opname= opname;
|
|
lvl->propname= propname;
|
|
lvl->opcontext= layout->common->opcontext;
|
|
|
|
ui_item_menu(layout, name, icon, menu_item_enum_opname_menu, NULL, lvl);
|
|
}
|
|
|
|
static void menu_item_enum_rna_menu(bContext *C, uiLayout *layout, void *arg)
|
|
{
|
|
MenuItemLevel *lvl= (MenuItemLevel*)(((uiBut*)arg)->func_argN);
|
|
|
|
uiLayoutContext(layout, lvl->opcontext);
|
|
uiItemsEnumR(layout, &lvl->rnapoin, lvl->propname);
|
|
}
|
|
|
|
void uiItemMenuEnumR(uiLayout *layout, char *name, int icon, struct PointerRNA *ptr, char *propname)
|
|
{
|
|
MenuItemLevel *lvl;
|
|
PropertyRNA *prop;
|
|
|
|
prop= RNA_struct_find_property(ptr, propname);
|
|
if(!prop) {
|
|
ui_item_disabled(layout, propname);
|
|
return;
|
|
}
|
|
|
|
if(!name)
|
|
name= (char*)RNA_property_ui_name(prop);
|
|
if(layout->common->type == UI_LAYOUT_MENU && !icon)
|
|
icon= ICON_BLANK1;
|
|
|
|
lvl= MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
|
|
lvl->rnapoin= *ptr;
|
|
lvl->propname= propname;
|
|
lvl->opcontext= layout->common->opcontext;
|
|
|
|
ui_item_menu(layout, name, icon, menu_item_enum_rna_menu, NULL, lvl);
|
|
}
|
|
|
|
/**************************** Layout Items ***************************/
|
|
|
|
/* single-row layout */
|
|
static void ui_litem_estimate_row(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int itemw, itemh;
|
|
|
|
litem->w= 0;
|
|
litem->h= 0;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
litem->w += itemw;
|
|
litem->h= MAX2(itemh, litem->h);
|
|
|
|
if(item->next)
|
|
litem->w += litem->space;
|
|
}
|
|
}
|
|
|
|
static void ui_litem_layout_row(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int neww, itemw, itemh, x, y, w, tot= 0, totw= 0, extra=0, available=0;
|
|
|
|
x= litem->x;
|
|
y= litem->y;
|
|
w= litem->w;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
totw += itemw;
|
|
tot++;
|
|
}
|
|
|
|
if(totw == 0)
|
|
return;
|
|
|
|
/* two step to enforce minimum button with .. could be better */
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
itemw= ui_item_fit(itemw, x-litem->x, totw, w, (tot-1)*litem->space, !item->next, UI_FIT_EXPAND);
|
|
x += itemw;
|
|
|
|
if(itemw < UI_UNIT_X)
|
|
extra += UI_UNIT_X - itemw;
|
|
else
|
|
available += itemw - UI_UNIT_X;
|
|
|
|
if(item->next)
|
|
x += litem->space;
|
|
}
|
|
|
|
x= litem->x;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
neww= ui_item_fit(itemw, x-litem->x, totw, w, (tot-1)*litem->space, !item->next, UI_FIT_EXPAND);
|
|
if(neww < UI_UNIT_X) {
|
|
if(item->next)
|
|
itemw= UI_UNIT_X;
|
|
else
|
|
itemw= litem->w - (x-litem->x);
|
|
}
|
|
else
|
|
itemw= ui_item_fit(itemw, x-litem->x, totw, w-extra, (tot-1)*litem->space, !item->next, UI_FIT_EXPAND);
|
|
|
|
ui_item_position(item, x, y-itemh, itemw, itemh);
|
|
x += itemw;
|
|
|
|
if(item->next)
|
|
x += litem->space;
|
|
}
|
|
|
|
litem->w= x - litem->x;
|
|
litem->h= litem->y - y;
|
|
litem->x= x;
|
|
litem->y= y;
|
|
}
|
|
|
|
/* single-column layout */
|
|
static void ui_litem_estimate_column(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int itemw, itemh;
|
|
|
|
litem->w= 0;
|
|
litem->h= 0;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
litem->w= MAX2(litem->w, itemw);
|
|
litem->h += itemh;
|
|
|
|
if(item->next)
|
|
litem->h += litem->space;
|
|
}
|
|
}
|
|
|
|
static void ui_litem_layout_column(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int itemh, x, y;
|
|
|
|
x= litem->x;
|
|
y= litem->y;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, NULL, &itemh);
|
|
|
|
y -= itemh;
|
|
ui_item_position(item, x, y, litem->w, itemh);
|
|
|
|
if(item->next)
|
|
y -= litem->space;
|
|
}
|
|
|
|
litem->h= litem->y - y;
|
|
litem->x= x;
|
|
litem->y= y;
|
|
}
|
|
|
|
/* root layout */
|
|
static void ui_litem_estimate_root(uiLayout *litem)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
static void ui_litem_layout_root(uiLayout *litem)
|
|
{
|
|
if(litem->common->type == UI_LAYOUT_HEADER)
|
|
ui_litem_layout_row(litem);
|
|
else
|
|
ui_litem_layout_column(litem);
|
|
}
|
|
|
|
/* box layout */
|
|
static void ui_litem_estimate_box(uiLayout *litem)
|
|
{
|
|
uiStyle *style= litem->common->style;
|
|
|
|
ui_litem_estimate_column(litem);
|
|
litem->w += 2*style->boxspace;
|
|
litem->h += 2*style->boxspace;
|
|
}
|
|
|
|
static void ui_litem_layout_box(uiLayout *litem)
|
|
{
|
|
uiStyle *style= litem->common->style;
|
|
int w, h;
|
|
|
|
w= litem->w;
|
|
h= litem->h;
|
|
|
|
litem->x += style->boxspace;
|
|
litem->y -= style->boxspace;
|
|
|
|
if(w != 0) litem->w -= 2*style->boxspace;
|
|
if(h != 0) litem->h -= 2*style->boxspace;
|
|
|
|
ui_litem_layout_column(litem);
|
|
|
|
litem->x -= style->boxspace;
|
|
litem->y -= style->boxspace;
|
|
|
|
if(w != 0) litem->w += 2*style->boxspace;
|
|
if(h != 0) litem->h += 2*style->boxspace;
|
|
|
|
/* roundbox around the sublayout */
|
|
uiDefBut(litem->common->block, ROUNDBOX, 0, "", litem->x, litem->y, litem->w, litem->h, NULL, 7.0, 0.0, 3, 20, "");
|
|
}
|
|
|
|
/* multi-column layout, automatically flowing to the next */
|
|
static void ui_litem_estimate_column_flow(uiLayout *litem)
|
|
{
|
|
uiStyle *style= litem->common->style;
|
|
uiLayoutItemFlow *flow= (uiLayoutItemFlow*)litem;
|
|
uiItem *item;
|
|
int col, x, y, emh, emy, miny, itemw, itemh, maxw=0;
|
|
int toth, totitem;
|
|
|
|
/* compute max needed width and total height */
|
|
toth= 0;
|
|
totitem= 0;
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
maxw= MAX2(maxw, itemw);
|
|
toth += itemh;
|
|
totitem++;
|
|
}
|
|
|
|
if(flow->number <= 0) {
|
|
/* auto compute number of columns, not very good */
|
|
if(maxw == 0) {
|
|
flow->totcol= 1;
|
|
return;
|
|
}
|
|
|
|
flow->totcol= MAX2(litem->common->emw/maxw, 1);
|
|
flow->totcol= MIN2(flow->totcol, totitem);
|
|
}
|
|
else
|
|
flow->totcol= flow->number;
|
|
|
|
/* compute sizes */
|
|
x= 0;
|
|
y= 0;
|
|
emy= 0;
|
|
miny= 0;
|
|
|
|
maxw= 0;
|
|
emh= toth/flow->totcol;
|
|
|
|
/* create column per column */
|
|
col= 0;
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
y -= itemh + style->buttonspacey;
|
|
miny= MIN2(miny, y);
|
|
emy -= itemh;
|
|
maxw= MAX2(itemw, maxw);
|
|
|
|
/* decide to go to next one */
|
|
if(col < flow->totcol-1 && emy <= -emh) {
|
|
x += maxw + litem->space;
|
|
maxw= 0;
|
|
y= 0;
|
|
col++;
|
|
}
|
|
}
|
|
|
|
litem->h= litem->y - miny;
|
|
}
|
|
|
|
static void ui_litem_layout_column_flow(uiLayout *litem)
|
|
{
|
|
uiStyle *style= litem->common->style;
|
|
uiLayoutItemFlow *flow= (uiLayoutItemFlow*)litem;
|
|
uiItem *item;
|
|
int col, x, y, w, emh, emy, miny, itemw, itemh;
|
|
int toth, totitem;
|
|
|
|
/* compute max needed width and total height */
|
|
toth= 0;
|
|
totitem= 0;
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, &itemw, &itemh);
|
|
toth += itemh;
|
|
totitem++;
|
|
}
|
|
|
|
/* compute sizes */
|
|
x= litem->x;
|
|
y= litem->y;
|
|
emy= 0;
|
|
miny= 0;
|
|
|
|
w= litem->w;
|
|
emh= toth/flow->totcol;
|
|
|
|
/* create column per column */
|
|
col= 0;
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, NULL, &itemh);
|
|
itemw= ui_item_fit(1, x-litem->x, flow->totcol, w, (flow->totcol-1)*style->columnspace, col == flow->totcol-1, UI_FIT_EXPAND);
|
|
|
|
y -= itemh;
|
|
emy -= itemh;
|
|
ui_item_position(item, x, y, itemw, itemh);
|
|
y -= style->buttonspacey;
|
|
miny= MIN2(miny, y);
|
|
|
|
/* decide to go to next one */
|
|
if(col < flow->totcol-1 && emy <= -emh) {
|
|
x += itemw + style->columnspace;
|
|
y= litem->y;
|
|
col++;
|
|
}
|
|
}
|
|
|
|
litem->h= litem->y - miny;
|
|
litem->x= x;
|
|
litem->y= miny;
|
|
}
|
|
|
|
/* free layout */
|
|
static void ui_litem_estimate_free(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int itemx, itemy, itemw, itemh, minx, miny;
|
|
|
|
minx= 1e6;
|
|
miny= 1e6;
|
|
litem->w= 0;
|
|
litem->h= 0;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_offset(item, &itemx, &itemy);
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
minx= MIN2(minx, itemx);
|
|
miny= MIN2(miny, itemy);
|
|
|
|
litem->w= MAX2(litem->w, itemx+itemw);
|
|
litem->h= MAX2(litem->h, itemy+itemh);
|
|
}
|
|
|
|
litem->w -= minx;
|
|
litem->h -= miny;
|
|
}
|
|
|
|
static void ui_litem_layout_free(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
float scalex=1.0f, scaley=1.0f;
|
|
int x, y, newx, newy, itemx, itemy, itemh, itemw, minx, miny, totw, toth;
|
|
|
|
minx= 1e6;
|
|
miny= 1e6;
|
|
totw= 0;
|
|
toth= 0;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_offset(item, &itemx, &itemy);
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
minx= MIN2(minx, itemx);
|
|
miny= MIN2(miny, itemy);
|
|
|
|
totw= MAX2(totw, itemx+itemw);
|
|
toth= MAX2(toth, itemy+itemh);
|
|
}
|
|
|
|
totw -= minx;
|
|
toth -= miny;
|
|
|
|
if(litem->w && totw > litem->w)
|
|
scalex= (float)litem->w/(float)totw;
|
|
if(litem->h && toth > litem->h)
|
|
scaley= (float)litem->h/(float)toth;
|
|
|
|
x= litem->x;
|
|
y= litem->y - scalex*toth;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_offset(item, &itemx, &itemy);
|
|
ui_item_size(item, &itemw, &itemh);
|
|
|
|
if(scalex != 1.0f) {
|
|
newx= itemx*scalex;
|
|
itemw= (itemx + itemw)*scalex - newx;
|
|
itemx= newx;
|
|
}
|
|
|
|
if(scaley != 1.0f) {
|
|
newy= itemy*scaley;
|
|
itemh= (itemy + itemh)*scaley - newy;
|
|
itemy= newy;
|
|
}
|
|
|
|
ui_item_position(item, x+itemx-minx, y+itemy-miny, itemw, itemh);
|
|
}
|
|
|
|
litem->w= scalex*totw;
|
|
litem->h= litem->y - y;
|
|
litem->x= x + litem->w;
|
|
litem->y= y;
|
|
}
|
|
|
|
/* split layout */
|
|
static void ui_litem_estimate_split(uiLayout *litem)
|
|
{
|
|
ui_litem_estimate_row(litem);
|
|
}
|
|
|
|
static void ui_litem_layout_split(uiLayout *litem)
|
|
{
|
|
uiItem *item;
|
|
int itemh, x, y, w, tot=0, colw=0;
|
|
|
|
x= litem->x;
|
|
y= litem->y;
|
|
w= litem->w;
|
|
|
|
for(item=litem->items.first; item; item=item->next)
|
|
tot++;
|
|
|
|
if(tot == 0)
|
|
return;
|
|
|
|
colw= (litem->w - (tot-1)*litem->space)/tot;
|
|
colw= MAX2(colw, 0);
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
ui_item_size(item, NULL, &itemh);
|
|
|
|
ui_item_position(item, x, y-itemh, colw, itemh);
|
|
x += colw;
|
|
|
|
if(item->next)
|
|
x += litem->space;
|
|
}
|
|
|
|
litem->w= x - litem->x;
|
|
litem->h= litem->y - y;
|
|
litem->x= x;
|
|
litem->y= y;
|
|
}
|
|
|
|
/* layout create functions */
|
|
uiLayout *uiLayoutRow(uiLayout *layout, int align)
|
|
{
|
|
uiLayout *litem;
|
|
|
|
litem= MEM_callocN(sizeof(uiLayout), "uiLayoutRow");
|
|
litem->item.type= ITEM_LAYOUT_ROW;
|
|
litem->common= layout->common;
|
|
litem->align= align;
|
|
litem->space= (align)? 0: layout->common->style->buttonspacex;
|
|
BLI_addtail(&layout->items, litem);
|
|
|
|
return litem;
|
|
}
|
|
|
|
uiLayout *uiLayoutColumn(uiLayout *layout, int align)
|
|
{
|
|
uiLayout *litem;
|
|
|
|
litem= MEM_callocN(sizeof(uiLayout), "uiLayoutColumn");
|
|
litem->item.type= ITEM_LAYOUT_COLUMN;
|
|
litem->common= layout->common;
|
|
litem->align= align;
|
|
litem->space= (litem->align)? 0: layout->common->style->buttonspacey;
|
|
BLI_addtail(&layout->items, litem);
|
|
|
|
return litem;
|
|
}
|
|
|
|
uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align)
|
|
{
|
|
uiLayoutItemFlow *flow;
|
|
|
|
flow= MEM_callocN(sizeof(uiLayoutItemFlow), "uiLayoutItemFlow");
|
|
flow->litem.item.type= ITEM_LAYOUT_COLUMN_FLOW;
|
|
flow->litem.common= layout->common;
|
|
flow->litem.align= align;
|
|
flow->litem.space= (flow->litem.align)? 0: layout->common->style->columnspace;
|
|
flow->number= number;
|
|
BLI_addtail(&layout->items, flow);
|
|
|
|
return &flow->litem;
|
|
}
|
|
|
|
uiLayout *uiLayoutBox(uiLayout *layout)
|
|
{
|
|
uiLayoutItemBx *box;
|
|
|
|
box= MEM_callocN(sizeof(uiLayoutItemBx), "uiLayoutItemBx");
|
|
box->litem.item.type= ITEM_LAYOUT_BOX;
|
|
box->litem.common= layout->common;
|
|
box->litem.space= layout->common->style->columnspace;
|
|
BLI_addtail(&layout->items, box);
|
|
|
|
return &box->litem;
|
|
}
|
|
|
|
uiLayout *uiLayoutFree(uiLayout *layout, int align)
|
|
{
|
|
uiLayout *litem;
|
|
|
|
litem= MEM_callocN(sizeof(uiLayout), "uiLayoutFree");
|
|
litem->item.type= ITEM_LAYOUT_FREE;
|
|
litem->common= layout->common;
|
|
litem->align= align;
|
|
BLI_addtail(&layout->items, litem);
|
|
|
|
return litem;
|
|
}
|
|
|
|
uiLayout *uiLayoutSplit(uiLayout *layout)
|
|
{
|
|
uiLayout *litem;
|
|
|
|
litem= uiLayoutRow(layout, 0);
|
|
litem->item.type = ITEM_LAYOUT_SPLIT;
|
|
litem->common= layout->common;
|
|
litem->space= layout->common->style->columnspace;
|
|
|
|
return litem;
|
|
}
|
|
|
|
/********************** Layout *******************/
|
|
|
|
static void ui_item_estimate(uiItem *item)
|
|
{
|
|
uiItem *subitem;
|
|
|
|
if(item->type != ITEM_BUTTON) {
|
|
uiLayout *litem= (uiLayout*)item;
|
|
|
|
for(subitem=litem->items.first; subitem; subitem=subitem->next)
|
|
ui_item_estimate(subitem);
|
|
|
|
switch(litem->item.type) {
|
|
case ITEM_LAYOUT_COLUMN:
|
|
ui_litem_estimate_column(litem);
|
|
break;
|
|
case ITEM_LAYOUT_COLUMN_FLOW:
|
|
ui_litem_estimate_column_flow(litem);
|
|
break;
|
|
case ITEM_LAYOUT_ROW:
|
|
ui_litem_estimate_row(litem);
|
|
break;
|
|
case ITEM_LAYOUT_BOX:
|
|
ui_litem_estimate_box(litem);
|
|
break;
|
|
case ITEM_LAYOUT_ROOT:
|
|
ui_litem_estimate_root(litem);
|
|
break;
|
|
case ITEM_LAYOUT_FREE:
|
|
ui_litem_estimate_free(litem);
|
|
break;
|
|
case ITEM_LAYOUT_SPLIT:
|
|
ui_litem_estimate_split(litem);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ui_item_align(uiLayout *litem, int nr)
|
|
{
|
|
uiItem *item;
|
|
|
|
for(item=litem->items.first; item; item=item->next) {
|
|
if(item->type == ITEM_BUTTON)
|
|
((uiButtonItem*)item)->but->alignnr= nr;
|
|
else
|
|
ui_item_align((uiLayout*)item, nr);
|
|
}
|
|
}
|
|
|
|
static void ui_item_layout(uiItem *item, int align)
|
|
{
|
|
uiItem *subitem;
|
|
|
|
if(item->type != ITEM_BUTTON) {
|
|
uiLayout *litem= (uiLayout*)item;
|
|
|
|
if(litem->align && !align)
|
|
ui_item_align(litem, ++litem->common->block->alignnr);
|
|
|
|
switch(litem->item.type) {
|
|
case ITEM_LAYOUT_COLUMN:
|
|
ui_litem_layout_column(litem);
|
|
break;
|
|
case ITEM_LAYOUT_COLUMN_FLOW:
|
|
ui_litem_layout_column_flow(litem);
|
|
break;
|
|
case ITEM_LAYOUT_ROW:
|
|
ui_litem_layout_row(litem);
|
|
break;
|
|
case ITEM_LAYOUT_BOX:
|
|
ui_litem_layout_box(litem);
|
|
break;
|
|
case ITEM_LAYOUT_ROOT:
|
|
ui_litem_layout_root(litem);
|
|
break;
|
|
case ITEM_LAYOUT_FREE:
|
|
ui_litem_layout_free(litem);
|
|
break;
|
|
case ITEM_LAYOUT_SPLIT:
|
|
ui_litem_layout_split(litem);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for(subitem=litem->items.first; subitem; subitem=subitem->next)
|
|
ui_item_layout(subitem, litem->align || align);
|
|
}
|
|
}
|
|
|
|
static void ui_layout_items(const bContext *C, uiBlock *block, uiLayout *layout)
|
|
{
|
|
ui_item_estimate(&layout->item);
|
|
ui_item_layout(&layout->item, 0);
|
|
}
|
|
|
|
static void ui_layout_end(const bContext *C, uiBlock *block, uiLayout *layout, int *x, int *y)
|
|
{
|
|
if(layout->common->handlefunc)
|
|
uiBlockSetButmFunc(block, layout->common->handlefunc, layout->common->argv);
|
|
|
|
ui_layout_items(C, block, layout);
|
|
|
|
if(x) *x= layout->x;
|
|
if(y) *y= layout->y;
|
|
}
|
|
|
|
static void ui_layout_free(uiLayout *layout)
|
|
{
|
|
uiItem *item, *next;
|
|
|
|
for(item=layout->items.first; item; item=next) {
|
|
next= item->next;
|
|
|
|
if(item->type == ITEM_BUTTON)
|
|
MEM_freeN(item);
|
|
else
|
|
ui_layout_free((uiLayout*)item);
|
|
}
|
|
|
|
MEM_freeN(layout);
|
|
}
|
|
|
|
uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int size, int em, uiStyle *style)
|
|
{
|
|
uiLayout *layout;
|
|
uiLayoutCommon *common;
|
|
|
|
if(!block->layout) {
|
|
common= MEM_callocN(sizeof(uiLayoutCommon), "uiLayoutCommon");
|
|
common->type= type;
|
|
common->style= style;
|
|
common->block= block;
|
|
common->opcontext= WM_OP_INVOKE_REGION_WIN;
|
|
|
|
layout= MEM_callocN(sizeof(uiLayout), "uiLayout");
|
|
layout->item.type= ITEM_LAYOUT_ROOT;
|
|
|
|
layout->x= x;
|
|
layout->y= y;
|
|
layout->common= common;
|
|
layout->space= style->templatespace;
|
|
|
|
if(type == UI_LAYOUT_MENU)
|
|
layout->space= 0;
|
|
|
|
if(dir == UI_LAYOUT_HORIZONTAL) {
|
|
layout->h= size;
|
|
layout->common->emh= em*UI_UNIT_Y;
|
|
}
|
|
else {
|
|
layout->w= size;
|
|
layout->common->emw= em*UI_UNIT_X;
|
|
}
|
|
|
|
block->curlayout= layout;
|
|
block->layout= layout;
|
|
}
|
|
|
|
return block->layout;
|
|
}
|
|
|
|
uiBlock *uiLayoutBlock(uiLayout *layout)
|
|
{
|
|
return layout->common->block;
|
|
}
|
|
|
|
void uiBlockSetCurLayout(uiBlock *block, uiLayout *layout)
|
|
{
|
|
block->curlayout= layout;
|
|
}
|
|
|
|
void ui_layout_add_but(uiLayout *layout, uiBut *but)
|
|
{
|
|
uiButtonItem *bitem;
|
|
|
|
bitem= MEM_callocN(sizeof(uiButtonItem), "uiButtonItem");
|
|
bitem->item.type= ITEM_BUTTON;
|
|
bitem->but= but;
|
|
BLI_addtail(&layout->items, bitem);
|
|
}
|
|
|
|
void uiLayoutContext(uiLayout *layout, int opcontext)
|
|
{
|
|
layout->common->opcontext= opcontext;
|
|
}
|
|
|
|
void uiLayoutFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
|
|
{
|
|
layout->common->handlefunc= handlefunc;
|
|
layout->common->argv= argv;
|
|
}
|
|
|
|
void uiBlockLayoutResolve(const bContext *C, uiBlock *block, int *x, int *y)
|
|
{
|
|
uiLayout *layout= block->layout;
|
|
|
|
if(layout) {
|
|
/* NULL in advance so we don't interfere when adding button */
|
|
block->layout= NULL;
|
|
block->curlayout= NULL;
|
|
|
|
ui_layout_end(C, block, layout, x, y);
|
|
MEM_freeN(layout->common);
|
|
ui_layout_free(layout);
|
|
}
|
|
else {
|
|
if(x) *x= 0;
|
|
if(y) *y= 0;
|
|
}
|
|
|
|
/* XXX silly trick, interface_templates.c doesn't get linked
|
|
* because it's not used by other files in this module? */
|
|
{
|
|
void ui_template_fix_linking();
|
|
ui_template_fix_linking();
|
|
}
|
|
}
|
|
|
|
float uiBlockAspect(uiBlock *block)
|
|
{
|
|
return block->aspect; /* temporary */
|
|
}
|
|
|