UI: Input Placeholders #112104

Merged
Harley Acheson merged 32 commits from Harley/blender:placeholders into blender-v4.0-release 2023-10-11 00:47:20 +02:00
7 changed files with 79 additions and 4 deletions

View File

@ -588,6 +588,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
),
"message": (),
"heading": (),
"placeholder": ((("text_ctxt",), _ctxt_to_ctxt),),
}
context_kw_set = {}
@ -621,7 +622,8 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
for arg_pos, (arg_kw, arg) in enumerate(func.parameters.items()):
if (not arg.is_output) and (arg.type == 'STRING'):
for msgid, msgctxts in context_kw_set.items():
if arg_kw in msgctxts:
# The msgid can be missing if it is used in only some UILayout functions but not all
if arg_kw in msgctxts and msgid in func_translate_args[func_id]:
func_translate_args[func_id][msgid][1][arg_kw] = arg_pos
# The report() func of operators.
for func_id, func in bpy.types.Operator.bl_rna.functions.items():

View File

@ -933,6 +933,11 @@ void UI_but_disable(uiBut *but, const char *disabled_hint);
void UI_but_type_set_menu_from_pulldown(uiBut *but);
/**
* Set at hint that describes the expected value when empty.
*/
void UI_but_placeholder_set(uiBut *but, const char *placeholder_text) ATTR_NONNULL(1);
/**
* Special button case, only draw it when used actively, for outliner etc.
*
@ -2775,7 +2780,8 @@ void uiItemFullR(uiLayout *layout,
int value,
eUI_Item_Flag flag,
const char *name,
int icon);
int icon,
const char *placeholder = nullptr);
/**
* Use a wrapper function since re-implementing all the logic in this function would be messy.
*/

View File

@ -61,6 +61,7 @@
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_enum_types.hh"
#ifdef WITH_PYTHON
# include "BPY_extern_run.h"
@ -3477,6 +3478,10 @@ static void ui_but_free(const bContext *C, uiBut *but)
MEM_freeN(but->hold_argN);
}
if (but->placeholder) {
MEM_freeN(but->placeholder);
}
ui_but_free_type_specific(but);
if (but->active) {
@ -5906,6 +5911,36 @@ void UI_but_disable(uiBut *but, const char *disabled_hint)
but->disabled_info = disabled_hint;
}
void UI_but_placeholder_set(uiBut *but, const char *placeholder_text)
{
MEM_SAFE_FREE(but->placeholder);
but->placeholder = BLI_strdup_null(placeholder_text);
}
const char *ui_but_placeholder_get(uiBut *but)
{
const char *placeholder = (but->placeholder) ? but->placeholder : nullptr;
if (!placeholder && but->rnaprop) {
if (but->type == UI_BTYPE_SEARCH_MENU) {
StructRNA *type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
const short idcode = RNA_type_to_ID_code(type);
if (idcode != 0) {
RNA_enum_name(rna_enum_id_type_items, idcode, &placeholder);
placeholder = CTX_IFACE_(BLT_I18NCONTEXT_ID_ID, placeholder);
}
}
else if (but->type == UI_BTYPE_TEXT) {
const char *identifier = RNA_property_identifier(but->rnaprop);
if (STR_ELEM(identifier, "search_filter", "filter_text", "filter_search")) {
placeholder = CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Search");
}
}
}
return placeholder;
}
void UI_but_type_set_menu_from_pulldown(uiBut *but)
{
BLI_assert(but->type == UI_BTYPE_PULLDOWN);

View File

@ -175,6 +175,8 @@ struct uiBut {
char strdata[UI_MAX_NAME_STR] = "";
char drawstr[UI_MAX_DRAW_STR] = "";
char *placeholder = nullptr;
rctf rect = {}; /* block relative coords */
char *poin = nullptr;
@ -742,6 +744,11 @@ void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but) ATTR_NONNULL()
void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value);
uiBut *ui_but_drag_multi_edit_get(uiBut *but);
Harley marked this conversation as resolved Outdated

My earlier comment was only about UI_but_placeholder_get(). The setter makes sense in the public API, it's also part of the public Python API. If that can override the placeholder, other external UI code should be able to as well.

Convention is also to prefix internal functions with ui_, not UI_.

My earlier comment was only about `UI_but_placeholder_get()`. The setter makes sense in the public API, it's also part of the public Python API. If that can override the placeholder, other external UI code should be able to as well. Convention is also to prefix internal functions with `ui_`, not `UI_`.
/**
* Get the hint that describes the expected value when empty.
*/
const char *ui_but_placeholder_get(uiBut *but);
void ui_def_but_icon(uiBut *but, int icon, int flag);
/**
* Avoid using this where possible since it's better not to ask for an icon in the first place.

View File

@ -2055,7 +2055,8 @@ void uiItemFullR(uiLayout *layout,
int value,
eUI_Item_Flag flag,
const char *name,
int icon)
int icon,
const char *placeholder)
{
uiBlock *block = layout->root->block;
char namestr[UI_MAX_NAME_STR];
@ -2468,6 +2469,10 @@ void uiItemFullR(uiLayout *layout,
UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
}
if (but && placeholder) {
UI_but_placeholder_set(but, placeholder);
}
#ifdef UI_PROP_DECORATE
if (ui_decorate.use_prop_decorate) {
uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next :

View File

@ -2187,6 +2187,22 @@ static void widget_draw_text(const uiFontStyle *fstyle,
}
}
/* Show placeholder text if the input is empty and not being edited. */
if (!drawstr[0] && !but->editstr && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
const char *placeholder = ui_but_placeholder_get(but);
if (placeholder && placeholder[0]) {
uiFontStyleDraw_Params params{};
params.align = align;
uiFontStyle style = *fstyle;
style.shadow = 0;
uchar col[4];
copy_v4_v4_uchar(col, wcol->text);
col[3] *= 0.33f;
UI_fontstyle_draw_ex(
&style, rect, placeholder, strlen(placeholder), col, &params, nullptr, nullptr, nullptr);
}
}
/* part text right aligned */
if (drawstr_right) {
uchar col[4];

View File

@ -84,6 +84,7 @@ static void rna_uiItemR(uiLayout *layout,
const char *text_ctxt,
bool translate,
int icon,
const char *placeholder,
bool expand,
bool slider,
int toggle,
@ -109,6 +110,7 @@ static void rna_uiItemR(uiLayout *layout,
/* Get translated name (label). */
name = rna_translate_ui_text(name, text_ctxt, nullptr, prop, translate);
placeholder = rna_translate_ui_text(placeholder, text_ctxt, nullptr, prop, translate);
if (slider) {
flag |= UI_ITEM_R_SLIDER;
@ -140,7 +142,7 @@ static void rna_uiItemR(uiLayout *layout,
flag |= UI_ITEM_R_CHECKBOX_INVERT;
}
uiItemFullR(layout, ptr, prop, index, 0, flag, name, icon);
uiItemFullR(layout, ptr, prop, index, 0, flag, name, icon, placeholder);
}
static void rna_uiItemR_with_popover(uiLayout *layout,
@ -1147,6 +1149,8 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_ui_description(func, "Item. Exposes an RNA item and places it into the layout");
api_ui_item_rna_common(func);
api_ui_item_common(func);
RNA_def_string(
func, "placeholder", nullptr, 0, "", "Hint describing the expected value when empty");
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_int(func,