UI: Operator Props Dialog Changes #117528

Merged
Harley Acheson merged 9 commits from Harley/blender:OpPropertyDialog into main 2024-01-26 20:52:39 +01:00
3 changed files with 187 additions and 31 deletions
Showing only changes of commit cc04e22abf - Show all commits

View File

@ -218,6 +218,20 @@ static int rna_Operator_props_popup(bContext *C, wmOperator *op, wmEvent *event)
return WM_operator_props_popup(C, op, event);
}
static int rna_Operator_props_dialog_popup(bContext *C,
wmOperator *op,
const int width,
const char *title,
const char *confirm_text,
const bool cancel_default,
const char *text_ctxt,
const bool translate)
{
title = RNA_translate_ui_text(title, text_ctxt, nullptr, nullptr, translate);
confirm_text = RNA_translate_ui_text(confirm_text, text_ctxt, nullptr, nullptr, translate);
return WM_operator_props_dialog_popup(C, op, width);
Harley marked this conversation as resolved Outdated

Misses nullptr checks in WM_operator_props_dialog_popup string params

Misses `nullptr` checks in `WM_operator_props_dialog_popup` string params
}
static int keymap_item_modifier_flag_from_args(bool any, int shift, int ctrl, int alt, int oskey)
{
int modifier = 0;
@ -859,13 +873,35 @@ void RNA_api_wm(StructRNA *srna)
rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN);
/* invoked dialog opens popup with OK button, does not auto-exec operator. */
func = RNA_def_function(srna, "invoke_props_dialog", "WM_operator_props_dialog_popup");
func = RNA_def_function(srna, "invoke_props_dialog", "rna_Operator_props_dialog_popup");
RNA_def_function_ui_description(
func,
"Operator dialog (non-autoexec popup) invoke "
"(show operator properties and only execute it on click on OK button)");
rna_generic_op_invoke(func, WM_GEN_INVOKE_SIZE | WM_GEN_INVOKE_RETURN);
parm = RNA_def_property(func, "title", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(parm, "Title", "Optional text to show as title of the title.");
Harley marked this conversation as resolved

title of the tile -> title of the popup

title of the tile -> title of the popup
parm = RNA_def_property(func, "confirm_text", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(
parm,
"Confirm Text",
"Optional text to show instead to the default \"OK\" confirmation button text.");
parm = RNA_def_property(func, "cancel_default", PROP_BOOLEAN, PROP_NONE);
Harley marked this conversation as resolved

I'm not sure it makes sense to have cancel_default without also being able to change the text of the cancel button. Or if we want to have popups that do this at all actually.

Can we leave this out of the Python API until it's more clear if this is something we actually want to recommend people use? I don't mind having it in C++.

I'm not sure it makes sense to have `cancel_default` without also being able to change the text of the cancel button. Or if we want to have popups that do this at all actually. Can we leave this out of the Python API until it's more clear if this is something we actually want to recommend people use? I don't mind having it in C++.
RNA_def_property_ui_text(parm,
"Cancel Default",
"Whether the cancel button should be the default button instead of the "
"confirmation button.");
parm = RNA_def_string(func,
"text_ctxt",
nullptr,
0,
"",
"Override automatic translation context of the given text");
RNA_def_property_clear_flag(parm, PROP_NEVER_NULL);
RNA_def_boolean(
func, "translate", true, "", "Translate the given text, when UI translation is enabled");
Harley marked this conversation as resolved Outdated

To deduplicate code, I suggest to add a api_ui_item_common_text_translation with these two properties, that is then also called by api_ui_item_common_text.

To deduplicate code, I suggest to add a `api_ui_item_common_text_translation` with these two properties, that is then also called by `api_ui_item_common_text`.
/* invoke enum */
func = RNA_def_function(srna, "invoke_search_popup", "rna_Operator_enum_search_invoke");
RNA_def_function_ui_description(

View File

@ -708,7 +708,14 @@ int WM_operator_props_popup_confirm(bContext *C, wmOperator *op, const wmEvent *
*/
int WM_operator_props_popup_call(bContext *C, wmOperator *op, const wmEvent *event);
int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *event);
int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width);
int WM_operator_props_dialog_popup(bContext *C,
wmOperator *op,
int width,
std::string title = "",
std::string confirm_text = "",
bool cancel_default = false);
int WM_operator_redo_popup(bContext *C, wmOperator *op);
int WM_operator_ui_popup(bContext *C, wmOperator *op, int width);

View File

@ -1642,8 +1642,17 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *region, void *arg_op)
struct wmOpPopUp {
wmOperator *op;
int width;
int height;
int free_op;
std::string title;
std::string message;
std::string message2;
std::string confirm_text;
int icon;
wmConfirmSize size;
wmConfirmPosition position;
bool cancel_default;
bool mouse_move_quit;
bool include_properties;
};
/* Only invoked by OK button in popups created with wm_block_dialog_create() */
@ -1655,7 +1664,7 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
* In this case, wm_operator_ui_popup_cancel won't run. */
wmOpPopUp *data = static_cast<wmOpPopUp *>(arg1);
op = data->op;
MEM_freeN(data);
MEM_delete(data);
}
uiBlock *block = static_cast<uiBlock *>(arg2);
@ -1672,6 +1681,29 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
WM_operator_call_ex(C, op, true);
}
/* Only invoked by Cancel button in popups created with wm_block_dialog_create() */
static void dialog_cancel_cb(bContext *C, void *arg1, void *arg2)
{
wmOpPopUp *data = static_cast<wmOpPopUp *>(arg1);
Harley marked this conversation as resolved

this few lines (1687-1700) duplicates wm_operator_ui_popup_cancel

this few lines (1687-1700) duplicates `wm_operator_ui_popup_cancel`
wmOperator *op = data->op;
if (op) {
if (op->type->cancel) {
op->type->cancel(C, op);
}
if (data->free_op) {
WM_operator_free(op);
}
}
MEM_delete(data);
uiBlock *block = static_cast<uiBlock *>(arg2);
UI_popup_menu_retval_set(block, UI_RETURN_CANCEL, true);
wmWindow *win = CTX_wm_window(C);
UI_popup_block_close(C, win, block);
}
/* Dialogs are popups that require user verification (click OK) before exec */
static uiBlock *wm_block_dialog_create(bContext *C, ARegion *region, void *user_data)
{
@ -1681,17 +1713,25 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *region, void *user_
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
UI_block_flag_disable(block, UI_BLOCK_LOOP);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_REGULAR);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
if (data->mouse_move_quit) {
UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT);
}
/* Intentionally don't use #UI_BLOCK_MOVEMOUSE_QUIT, some dialogs have many items
* where quitting by accident is very annoying. */
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_NUMSELECT);
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, 0, 0, style);
uiTemplateOperatorPropertyButs(
C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
uiItemL_ex(layout, data->title.c_str(), ICON_NONE, true, false);
uiItemS_ex(layout, 0.3f);
if (data->include_properties) {
uiTemplateOperatorPropertyButs(C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, 0);
}
uiItemS_ex(layout, 0.6f);
/* clear so the OK button is left alone */
UI_block_func_set(block, nullptr, nullptr, nullptr);
@ -1700,16 +1740,84 @@ static uiBlock *wm_block_dialog_create(bContext *C, ARegion *region, void *user_
{
uiLayout *col = uiLayoutColumn(layout, false);
uiBlock *col_block = uiLayoutGetBlock(col);
/* Create OK button, the callback of which will execute op */
uiBut *but = uiDefBut(
col_block, UI_BTYPE_BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
UI_but_func_set(but, dialog_exec_cb, data, col_block);
uiBut *confirm_but;
uiBut *cancel_but;
col = uiLayoutSplit(col, 0.0f, true);
uiLayoutSetScaleY(col, 1.2f);
#ifdef _WIN32
const bool windows_layout = true;
#else
const bool windows_layout = false;
#endif
if (windows_layout) {
confirm_but = uiDefBut(col_block,
UI_BTYPE_BUT,
0,
data->confirm_text.c_str(),
0,
-30,
0,
UI_UNIT_Y,
nullptr,
0,
0,
0,
0,
"");
uiLayoutColumn(col, false);
}
cancel_but = uiDefBut(col_block,
UI_BTYPE_BUT,
0,
IFACE_("Cancel"),
0,
-30,
0,
UI_UNIT_Y,
nullptr,
0,
0,
0,
0,
"");
if (!windows_layout) {
uiLayoutColumn(col, false);
confirm_but = uiDefBut(col_block,
UI_BTYPE_BUT,
0,
data->confirm_text.c_str(),
0,
-30,
0,
UI_UNIT_Y,
nullptr,
0,
0,
0,
0,
"");
}
UI_but_func_set(confirm_but, dialog_exec_cb, data, col_block);
UI_but_func_set(cancel_but, dialog_cancel_cb, data, col_block);
UI_but_flag_enable((data->cancel_default) ? cancel_but : confirm_but, UI_BUT_ACTIVE_DEFAULT);
}
/* center around the mouse */
UI_block_bounds_set_popup(
block, 6 * UI_SCALE_FAC, blender::int2{data->width / -2, data->height / 2});
if (data->position == WM_WARNING_POSITION_MOUSE) {
int bounds_offset[2];
bounds_offset[0] = uiLayoutGetWidth(layout) * -0.66f;
bounds_offset[1] = UI_UNIT_Y * 2;
UI_block_bounds_set_popup(block, 10 * UI_SCALE_FAC, bounds_offset);
}
else if (data->position == WM_WARNING_POSITION_CENTER) {
UI_block_bounds_set_centered(block, 10 * UI_SCALE_FAC);
}
return block;
}
@ -1726,7 +1834,7 @@ static uiBlock *wm_operator_ui_create(bContext *C, ARegion *region, void *user_d
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_REGULAR);
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, 0, 0, style);
/* since ui is defined the auto-layout args are not used */
uiTemplateOperatorPropertyButs(C, layout, op, UI_BUT_LABEL_ALIGN_COLUMN, 0);
@ -1753,7 +1861,7 @@ static void wm_operator_ui_popup_cancel(bContext *C, void *user_data)
}
}
MEM_freeN(data);
MEM_delete(data);
}
static void wm_operator_ui_popup_ok(bContext *C, void *arg, int retval)
@ -1765,17 +1873,14 @@ static void wm_operator_ui_popup_ok(bContext *C, void *arg, int retval)
WM_operator_call_ex(C, op, true);
}
MEM_freeN(data);
MEM_delete(data);
}
int WM_operator_ui_popup(bContext *C, wmOperator *op, int width)
{
wmOpPopUp *data = static_cast<wmOpPopUp *>(
MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup"));
wmOpPopUp *data = MEM_new<wmOpPopUp>(__func__);
data->op = op;
data->width = width * UI_SCALE_FAC;
/* Actual used height depends on the content. */
data->height = 0;
data->free_op = true; /* if this runs and gets registered we may want not to free it */
UI_popup_block_ex(C, wm_operator_ui_create, nullptr, wm_operator_ui_popup_cancel, data, op);
return OPERATOR_RUNNING_MODAL;
@ -1839,16 +1944,24 @@ int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent * /*event
return wm_operator_props_popup_ex(C, op, false, true);
}
int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width)
int WM_operator_props_dialog_popup(bContext *C,
wmOperator *op,
int width,
std::string title,
std::string confirm_text,
bool cancel_default)
{
wmOpPopUp *data = static_cast<wmOpPopUp *>(
MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup"));
wmOpPopUp *data = MEM_new<wmOpPopUp>(__func__);
data->op = op;
data->width = width * UI_SCALE_FAC;
/* Actual height depends on the content. */
data->height = 0;
data->free_op = true; /* if this runs and gets registered we may want not to free it */
data->title = (title.empty()) ? WM_operatortype_description(C, op->type, op->ptr) : title;
data->confirm_text = (confirm_text.empty()) ? WM_operatortype_name(op->type, op->ptr) :
confirm_text;
data->cancel_default = cancel_default;
data->mouse_move_quit = false;
data->include_properties = true;
data->position = WM_WARNING_POSITION_MOUSE;
/* op is not executed until popup OK but is clicked */
UI_popup_block_ex(