UI: support drawing booleans with icons as check-boxes

Previously, if a boolean happened to use an icon there was no way
to make it display as a check-box from Python scripts.

The previous logic meant we ended up having to edit the RNA.
Since booleans with icons don't work well with the split-property layout
(now used for most of the interface).
Icons were being removed from RNA then added back using awkward Python
ternary expressions in the interface scripts.

The toggle argument now has an unset state (-1).

- toggle=True: no checkbox (emboss).
- toggle=False: always use a checkbox (no icon).
- toggle=(unset/-1): depends on the icon status, default as before.

Since toggle=False was default, this isn't used in existing UI logic.
This commit is contained in:
Campbell Barton 2019-05-21 14:39:09 +10:00
parent 87fda5bc60
commit 6640bcca74
Notes: blender-bot 2023-02-13 11:57:48 +01:00
Referenced by commit 6539cf3199, Visibility panel: Use "toggle" keyword
5 changed files with 110 additions and 50 deletions

View File

@ -1714,15 +1714,28 @@ enum {
UI_ITEM_O_RETURN_PROPS = 1 << 0,
UI_ITEM_R_EXPAND = 1 << 1,
UI_ITEM_R_SLIDER = 1 << 2,
/**
* Use for booleans, causes the button to draw with an outline (emboss),
* instead of text with a checkbox.
* This is implied when toggle buttons have an icon
* unless #UI_ITEM_R_ICON_NEVER flag is set.
*/
UI_ITEM_R_TOGGLE = 1 << 3,
UI_ITEM_R_ICON_ONLY = 1 << 4,
UI_ITEM_R_EVENT = 1 << 5,
UI_ITEM_R_FULL_EVENT = 1 << 6,
UI_ITEM_R_NO_BG = 1 << 7,
UI_ITEM_R_IMMEDIATE = 1 << 8,
UI_ITEM_O_DEPRESS = 1 << 9,
UI_ITEM_R_COMPACT = 1 << 10,
UI_ITEM_R_CHECKBOX_INVERT = 1 << 11,
/**
* Don't attempt to use an icon when the icon is set to #ICON_NONE.
*
* Use for boolean's, causes the buttons to always show as a checkbox
* even when there is an icon (which would normally show the button as a toggle).
*/
UI_ITEM_R_ICON_NEVER = 1 << 4,
UI_ITEM_R_ICON_ONLY = 1 << 5,
UI_ITEM_R_EVENT = 1 << 6,
UI_ITEM_R_FULL_EVENT = 1 << 7,
UI_ITEM_R_NO_BG = 1 << 8,
UI_ITEM_R_IMMEDIATE = 1 << 9,
UI_ITEM_O_DEPRESS = 1 << 10,
UI_ITEM_R_COMPACT = 1 << 11,
UI_ITEM_R_CHECKBOX_INVERT = 1 << 12,
};
#define UI_HEADER_OFFSET ((void)0, 0.4f * UI_UNIT_X)

View File

@ -3738,6 +3738,16 @@ void ui_def_but_icon(uiBut *but, const int icon, const int flag)
}
}
/**
* Avoid using this where possible since it's better not to ask for an icon in the first place.
*/
void ui_def_but_icon_clear(uiBut *but)
{
but->icon = ICON_NONE;
but->flag &= ~UI_HAS_ICON;
but->drawflag &= ~UI_BUT_ICON_LEFT;
}
static void ui_def_but_rna__disable(uiBut *but, const char *info)
{
but->flag |= UI_BUT_DISABLED;

View File

@ -500,6 +500,7 @@ extern int ui_but_string_get_max_length(uiBut *but);
extern uiBut *ui_but_drag_multi_edit_get(uiBut *but);
void ui_def_but_icon(uiBut *but, const int icon, const int flag);
void ui_def_but_icon_clear(uiBut *but);
extern uiButExtraIconType ui_but_icon_extra_get(uiBut *but);
extern void ui_but_default_set(struct bContext *C, const bool all, const bool use_afterfunc);

View File

@ -484,7 +484,7 @@ static void ui_item_array(uiLayout *layout,
int UNUSED(h),
bool expand,
bool slider,
bool toggle,
int toggle,
bool icon_only,
bool compact,
bool show_text)
@ -691,7 +691,7 @@ static void ui_item_array(uiLayout *layout,
if (slider && but->type == UI_BTYPE_NUM) {
but->type = UI_BTYPE_NUM_SLIDER;
}
if (toggle && but->type == UI_BTYPE_CHECKBOX) {
if ((toggle == 1) && but->type == UI_BTYPE_CHECKBOX) {
but->type = UI_BTYPE_TOGGLE;
}
if ((a == 0) && (subtype == PROP_AXISANGLE)) {
@ -1889,6 +1889,10 @@ void uiItemFullR(uiLayout *layout,
const bool icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0;
/* Boolean with -1 to signify that the value depends on the presence of an icon. */
const int toggle = ((flag & UI_ITEM_R_TOGGLE) ? 1 : ((flag & UI_ITEM_R_ICON_NEVER) ? 0 : -1));
const bool no_icon = (toggle == 0);
/* set name and icon */
if (!name) {
if (!icon_only) {
@ -1903,10 +1907,6 @@ void uiItemFullR(uiLayout *layout,
flag &= ~UI_ITEM_R_CHECKBOX_INVERT;
}
if (icon == ICON_NONE) {
icon = RNA_property_ui_icon(prop);
}
if (flag & UI_ITEM_R_ICON_ONLY) {
/* pass */
}
@ -1931,6 +1931,47 @@ void uiItemFullR(uiLayout *layout,
}
}
if (no_icon == false) {
if (icon == ICON_NONE) {
icon = RNA_property_ui_icon(prop);
}
/* Menus and pie-menus don't show checkbox without this. */
if ((layout->root->type == UI_LAYOUT_MENU) ||
/* Use checkboxes only as a fallback in pie-menu's, when no icon is defined. */
((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) {
int prop_flag = RNA_property_flag(prop);
if (type == PROP_BOOLEAN) {
if ((is_array == false) || (index != RNA_NO_INDEX)) {
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
}
else if (is_array) {
icon = (RNA_property_boolean_get_index(ptr, prop, index)) ? ICON_CHECKBOX_HLT :
ICON_CHECKBOX_DEHLT;
}
else {
icon = (RNA_property_boolean_get(ptr, prop)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
}
}
else if (type == PROP_ENUM) {
if (index == RNA_ENUM_VALUE) {
int enum_value = RNA_property_enum_get(ptr, prop);
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
}
else if (prop_flag & PROP_ENUM_FLAG) {
icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
else {
icon = (enum_value == value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
}
}
}
}
#ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
if (use_prop_sep) {
if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
@ -1939,43 +1980,11 @@ void uiItemFullR(uiLayout *layout,
}
#endif
/* menus and pie-menus don't show checkbox without this */
if ((layout->root->type == UI_LAYOUT_MENU) ||
/* use checkboxes only as a fallback in pie-menu's, when no icon is defined */
((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) {
int prop_flag = RNA_property_flag(prop);
if (type == PROP_BOOLEAN && ((is_array == false) || (index != RNA_NO_INDEX))) {
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
}
else if (is_array) {
icon = (RNA_property_boolean_get_index(ptr, prop, index)) ? ICON_CHECKBOX_HLT :
ICON_CHECKBOX_DEHLT;
}
else {
icon = (RNA_property_boolean_get(ptr, prop)) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
}
else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
int enum_value = RNA_property_enum_get(ptr, prop);
if (prop_flag & PROP_ICONS_CONSECUTIVE) {
icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */
}
else if (prop_flag & PROP_ENUM_FLAG) {
icon = (enum_value & value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
else {
icon = (enum_value == value) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
}
}
}
if ((type == PROP_ENUM) && (RNA_property_flag(prop) & PROP_ENUM_FLAG)) {
flag |= UI_ITEM_R_EXPAND;
}
const bool slider = (flag & UI_ITEM_R_SLIDER) != 0;
const bool toggle = (flag & UI_ITEM_R_TOGGLE) != 0;
const bool expand = (flag & UI_ITEM_R_EXPAND) != 0;
const bool no_bg = (flag & UI_ITEM_R_NO_BG) != 0;
const bool compact = (flag & UI_ITEM_R_COMPACT) != 0;
@ -2164,7 +2173,7 @@ void uiItemFullR(uiLayout *layout,
}
}
if (toggle && but->type == UI_BTYPE_CHECKBOX) {
if ((toggle == 1) && but->type == UI_BTYPE_CHECKBOX) {
but->type = UI_BTYPE_TOGGLE;
}
@ -2183,6 +2192,18 @@ void uiItemFullR(uiLayout *layout,
}
}
/* The resulting button may have the icon set since boolean button drawing
* is being 'helpful' and adding an icon for us.
* In this case we want the ability not to have an icon.
*
* We could pass an argument not to set the icon to begin with however this is the one case
* the functionality is needed. */
if (but && no_icon) {
if ((icon == ICON_NONE) && (but->icon != ICON_NONE)) {
ui_def_but_icon_clear(but);
}
}
/* Mark non-embossed textfields inside a listbox. */
if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) &&
(but->dt & UI_EMBOSS_NONE)) {

View File

@ -95,7 +95,7 @@ static void rna_uiItemR(uiLayout *layout,
int icon,
bool expand,
bool slider,
bool toggle,
int toggle,
bool icon_only,
bool event,
bool full_event,
@ -121,7 +121,12 @@ static void rna_uiItemR(uiLayout *layout,
flag |= (slider) ? UI_ITEM_R_SLIDER : 0;
flag |= (expand) ? UI_ITEM_R_EXPAND : 0;
flag |= (toggle) ? UI_ITEM_R_TOGGLE : 0;
if (toggle == 1) {
flag |= UI_ITEM_R_TOGGLE;
}
else if (toggle == 0) {
flag |= UI_ITEM_R_ICON_NEVER;
}
flag |= (icon_only) ? UI_ITEM_R_ICON_ONLY : 0;
flag |= (event) ? UI_ITEM_R_EVENT : 0;
flag |= (full_event) ? UI_ITEM_R_FULL_EVENT : 0;
@ -773,7 +778,17 @@ void RNA_api_ui_layout(StructRNA *srna)
api_ui_item_common(func);
RNA_def_boolean(func, "expand", false, "", "Expand button to show more detail");
RNA_def_boolean(func, "slider", false, "", "Use slider widget for numeric values");
RNA_def_boolean(func, "toggle", false, "", "Use toggle widget for boolean values");
RNA_def_int(func,
"toggle",
-1,
-1,
1,
"",
"Use toggle widget for boolean values, "
"or a checkbox when disabled "
"(the default is -1 which uses toggle only when an icon is displayed)",
-1,
1);
RNA_def_boolean(func, "icon_only", false, "", "Draw only icons in buttons, no text");
RNA_def_boolean(func, "event", false, "", "Use button to input key events");
RNA_def_boolean(