UI: Operator Props Dialog Changes

Changes to WM_operator_props_dialog_popup to allow configuration,
including via python, and with a look consistent with new confirms.

Pull Request: https://projects.blender.org/blender/blender/pulls/117528
This commit is contained in:
Harley Acheson
2024-01-26 20:52:31 +01:00
committed by Harley Acheson
parent ac61dad14f
commit 0d6aec1c21
5 changed files with 172 additions and 40 deletions

View File

@@ -240,6 +240,8 @@ bool rna_AnimaData_override_apply(struct Main *bmain,
void rna_def_animviz_common(struct StructRNA *srna);
void rna_def_motionpath_common(struct StructRNA *srna);
void api_ui_item_common_translation(FunctionRNA *func);
/**
* Settings for curved bbone settings.
*/

View File

@@ -973,21 +973,26 @@ static void api_ui_item_common_heading(FunctionRNA *func)
func, "translate", true, "", "Translate the given heading, when UI translation is enabled");
}
void api_ui_item_common_translation(FunctionRNA *func)
{
PropertyRNA *prop = RNA_def_string(func,
"text_ctxt",
nullptr,
0,
"",
"Override automatic translation context of the given text");
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL);
RNA_def_boolean(
func, "translate", true, "", "Translate the given text, when UI translation is enabled");
}
static void api_ui_item_common_text(FunctionRNA *func)
{
PropertyRNA *prop;
prop = RNA_def_string(func, "text", nullptr, 0, "", "Override automatic text of the item");
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL);
prop = RNA_def_string(func,
"text_ctxt",
nullptr,
0,
"",
"Override automatic translation context of the given text");
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL);
RNA_def_boolean(
func, "translate", true, "", "Translate the given text, when UI translation is enabled");
api_ui_item_common_translation(func);
}
static void api_ui_item_common(FunctionRNA *func)

View File

@@ -218,6 +218,19 @@ 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 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, title, confirm_text);
}
static int keymap_item_modifier_flag_from_args(bool any, int shift, int ctrl, int alt, int oskey)
{
int modifier = 0;
@@ -859,13 +872,22 @@ 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 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");
api_ui_item_common_translation(func);
/* 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,13 @@ 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,
const char *title = nullptr,
const char *confirm_text = nullptr);
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,18 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
WM_operator_call_ex(C, op, true);
}
static void wm_operator_ui_popup_cancel(bContext *C, void *user_data);
/* Only invoked by Cancel button in popups created with wm_block_dialog_create() */
static void dialog_cancel_cb(bContext *C, void *arg1, void *arg2)
{
wm_operator_ui_popup_cancel(C, arg1);
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 +1702,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 +1729,83 @@ 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 +1822,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 +1849,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 +1861,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 +1932,20 @@ 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, const char *title, const char *confirm_text)
{
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 == nullptr) ? WM_operatortype_description(C, op->type, op->ptr) : title;
data->confirm_text = (confirm_text == nullptr) ? WM_operatortype_name(op->type, op->ptr) :
confirm_text;
data->cancel_default = false;
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(