WIP: UI: Refactor of Confirmations and Op Dialogs #117493

Closed
Harley Acheson wants to merge 1 commits from Harley/blender:DialogRefactor into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 137 additions and 96 deletions

View File

@ -929,12 +929,12 @@ enum wmConfirmPosition {
WM_WARNING_POSITION_CENTER,
};
struct wmConfirmDetails {
char title[1024];
char message[1024];
char message2[1024];
char confirm_button[256];
char cancel_button[256];
struct wmOpPopUp {
std::string title;
std::string message;
std::string message2;
std::string confirm_button;
std::string cancel_button;
int icon;
wmConfirmSize size;
wmConfirmPosition position;
@ -942,6 +942,11 @@ struct wmConfirmDetails {
bool cancel_default;
bool mouse_move_quit;
bool red_alert;
int width;
int height;
bool include_properties;
wmOperator *op = nullptr;
bool free_op;
};
/**
@ -1074,7 +1079,7 @@ struct wmOperatorType {
/**
* If using WM_operator_confirm the following can override all parts of the dialog.
*/
void (*confirm)(bContext *C, wmOperator *, wmConfirmDetails *details);
void (*confirm)(bContext *C, wmOperator *, wmOpPopUp *details);
/** RNA for properties */
StructRNA *srna;

View File

@ -3582,10 +3582,10 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
static void wm_clear_recent_files_confirm(bContext * /*C*/,
wmOperator * /*op*/,
wmConfirmDetails *confirm)
wmOpPopUp *confirm)
{
STRNCPY(confirm->message, IFACE_("Remove all items from the recent files list"));
STRNCPY(confirm->confirm_button, IFACE_("Remove All"));
confirm->message = IFACE_("Remove all items from the recent files list");
confirm->confirm_button = IFACE_("Remove All");
confirm->position = WM_WARNING_POSITION_CENTER;
confirm->size = WM_WARNING_SIZE_LARGE;
confirm->cancel_default = true;

View File

@ -1186,112 +1186,142 @@ int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/
return OPERATOR_INTERFACE;
}
static void wm_operator_block_cancel(bContext *C, void *arg_op, void *arg_block)
static void wm_operator_block_cancel(bContext *C, void *arg_data, void *arg_block)
{
wmOperator *op = static_cast<wmOperator *>(arg_op);
wmOpPopUp *data = static_cast<wmOpPopUp *>(arg_data);
uiBlock *block = static_cast<uiBlock *>(arg_block);
UI_popup_block_close(C, CTX_wm_window(C), block);
WM_redraw_windows(C);
wmOperator *op = data->op;
if (op) {
if (op->type->cancel) {
op->type->cancel(C, op);
}
WM_operator_free(op);
}
}
static void wm_operator_block_confirm(bContext *C, void *arg_op, void *arg_block)
{
wmOperator *op = static_cast<wmOperator *>(arg_op);
uiBlock *block = static_cast<uiBlock *>(arg_block);
if (data->free_op) {
WM_operator_free(op);
}
}
MEM_delete(data);
UI_popup_block_close(C, CTX_wm_window(C), block);
WM_redraw_windows(C);
}
static void wm_operator_block_confirm(bContext *C, void *arg_data, void *arg_block)
{
wmOpPopUp *data = static_cast<wmOpPopUp *>(arg_data);
uiBlock *block = static_cast<uiBlock *>(arg_block);
wmOperator *op = data->op;
UI_popup_block_close(C, CTX_wm_window(C), block);
WM_redraw_windows(C);
if (op) {
WM_operator_call_ex(C, op, true);
}
MEM_delete(data);
}
static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_op)
static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_data)
{
wmOperator *op = static_cast<wmOperator *>(arg_op);
wmOpPopUp *confirm = static_cast<wmOpPopUp *>(arg_data);
wmOperator *op = confirm->op;
wmConfirmDetails confirm = {{0}};
STRNCPY(confirm.title, WM_operatortype_description(C, op->type, op->ptr).c_str());
STRNCPY(confirm.confirm_button, WM_operatortype_name(op->type, op->ptr).c_str());
STRNCPY(confirm.cancel_button, IFACE_("Cancel"));
confirm.icon = ALERT_ICON_WARNING;
confirm.size = WM_WARNING_SIZE_SMALL;
confirm.position = WM_WARNING_POSITION_MOUSE;
confirm.confirm_default = true;
confirm.cancel_default = false;
confirm.mouse_move_quit = false;
confirm.red_alert = false;
confirm->title = WM_operatortype_description(C, op->type, op->ptr);
confirm->confirm_button = WM_operatortype_name(op->type, op->ptr);
confirm->cancel_button = IFACE_("Cancel");
confirm->size = WM_WARNING_SIZE_SMALL;
confirm->position = WM_WARNING_POSITION_MOUSE;
confirm->confirm_default = true;
confirm->cancel_default = false;
confirm->mouse_move_quit = false;
confirm->red_alert = false;
/* uiBlock.flag */
int block_flags = UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT;
if (op->type->confirm) {
op->type->confirm(C, op, &confirm);
op->type->confirm(C, confirm->op, confirm);
}
if (confirm.mouse_move_quit) {
if (confirm->mouse_move_quit) {
block_flags |= UI_BLOCK_MOVEMOUSE_QUIT;
}
if (confirm.icon < ALERT_ICON_WARNING || confirm.icon >= ALERT_ICON_MAX) {
confirm.icon = ALERT_ICON_QUESTION;
if (confirm->icon >= ALERT_ICON_MAX) {
confirm->icon = ALERT_ICON_QUESTION;
}
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
UI_block_flag_enable(block, block_flags);
UI_block_flag_disable(block, UI_BLOCK_LOOP);
const uiStyle *style = UI_style_get_dpi();
int text_width = std::max(
120 * UI_SCALE_FAC,
BLF_width(style->widget.uifont_id, confirm.title, ARRAY_SIZE(confirm.title)));
if (confirm.message[0]) {
text_width = std::max(
text_width,
int(BLF_width(style->widget.uifont_id, confirm.message, ARRAY_SIZE(confirm.message))));
BLF_width(style->widget.uifont_id, confirm->title.c_str(), confirm->title.length()));
if (confirm->message[0]) {
text_width = std::max(text_width,
int(BLF_width(style->widget.uifont_id,
confirm->message.c_str(),
confirm->message.length())));
}
if (confirm.message2[0]) {
text_width = std::max(
text_width,
int(BLF_width(style->widget.uifont_id, confirm.message2, ARRAY_SIZE(confirm.message2))));
if (confirm->message2[0]) {
text_width = std::max(text_width,
int(BLF_width(style->widget.uifont_id,
confirm->message2.c_str(),
confirm->message2.length())));
}
const bool small = confirm.size == WM_WARNING_SIZE_SMALL;
const bool small = confirm->size == WM_WARNING_SIZE_SMALL;
const int padding = (small ? 7 : 14) * UI_SCALE_FAC;
const short icon_size = (small ? (confirm.message[0] ? 48 : 32) : 64) * UI_SCALE_FAC;
const int dialog_width = icon_size + text_width + (style->columnspace * 2.5);
const short icon_size = (small ? (confirm->message.empty() ? 23 : 48) : 64) * UI_SCALE_FAC;
int dialog_width = icon_size + text_width + (style->columnspace * 2.5);
if (confirm->width > 0) {
dialog_width = std::max(dialog_width, confirm->width);
}
const float split_factor = (float)icon_size / (float)(dialog_width - style->columnspace);
uiLayout *block_layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, dialog_width, UI_UNIT_Y, 0, style);
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, dialog_width, confirm->height, 0, style);
/* Split layout to put alert icon on left side. */
uiLayout *split_block = uiLayoutSplit(block_layout, split_factor, false);
/* Alert icon on the left. */
uiLayout *layout = uiLayoutRow(split_block, true);
/* Using 'align_left' with 'row' avoids stretching the icon along the width of column. */
uiLayoutSetAlignment(layout, UI_LAYOUT_ALIGN_LEFT);
uiDefButAlert(block, confirm.icon, 0, 0, icon_size, icon_size);
uiLayout *layout;
if (confirm->icon != -1) {
/* Split layout to put alert icon on left side. */
uiLayout *split_block = uiLayoutSplit(block_layout, split_factor, false);
/* Alert icon on the left. */
layout = uiLayoutRow(split_block, true);
/* Using 'align_left' with 'row' avoids stretching the icon along the width of column. */
uiLayoutSetAlignment(layout, UI_LAYOUT_ALIGN_LEFT);
uiDefButAlert(block, confirm->icon, 0, 0, icon_size, icon_size);
/* The rest of the content on the right. */
layout = uiLayoutColumn(split_block, true);
layout = uiLayoutColumn(split_block, true);
}
else {
layout = block_layout;
}
if (confirm.title[0]) {
if (!confirm.message[0]) {
if (confirm->title[0]) {
if (!confirm->message[0]) {
uiItemS(layout);
}
uiItemL_ex(layout, confirm.title, ICON_NONE, true, false);
uiItemL_ex(layout, confirm->title.c_str(), ICON_NONE, true, false);
}
if (confirm.message[0]) {
uiItemL(layout, confirm.message, ICON_NONE);
if (!confirm->message.empty()) {
uiItemL(layout, confirm->message.c_str(), ICON_NONE);
}
if (confirm.message2[0]) {
uiItemL(layout, confirm.message2, ICON_NONE);
if (!confirm->message2.empty()) {
uiItemL(layout, confirm->message2.c_str(), ICON_NONE);
}
if (confirm->include_properties) {
uiItemS_ex(layout, 1.0f);
uiTemplateOperatorPropertyButs(
C, layout, op, UI_BUT_LABEL_ALIGN_COLUMN, UI_TEMPLATE_OP_PROPS_SHOW_EMPTY);
}
uiItemS_ex(layout, small ? 0.5f : 4.0f);
@ -1315,7 +1345,7 @@ static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_
UI_BTYPE_BUT,
0,
0,
confirm.confirm_button,
confirm->confirm_button.c_str(),
0,
0,
0,
@ -1333,7 +1363,7 @@ static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_
UI_BTYPE_BUT,
0,
0,
confirm.cancel_button,
confirm->cancel_button.c_str(),
0,
0,
0,
@ -1351,7 +1381,7 @@ static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_
UI_BTYPE_BUT,
0,
0,
confirm.confirm_button,
confirm->confirm_button.c_str(),
0,
0,
0,
@ -1365,30 +1395,30 @@ static uiBlock *wm_block_confirm_create(bContext *C, ARegion *region, void *arg_
}
UI_block_func_set(block, nullptr, nullptr, nullptr);
UI_but_func_set(confirm_but, wm_operator_block_confirm, op, block);
UI_but_func_set(cancel_but, wm_operator_block_cancel, op, block);
UI_but_func_set(confirm_but, wm_operator_block_confirm, confirm, block);
UI_but_func_set(cancel_but, wm_operator_block_cancel, confirm, block);
UI_but_drawflag_disable(confirm_but, UI_BUT_TEXT_LEFT);
UI_but_drawflag_disable(cancel_but, UI_BUT_TEXT_LEFT);
if (confirm.red_alert) {
if (confirm->red_alert) {
UI_but_flag_enable(confirm_but, UI_BUT_REDALERT);
}
else {
if (confirm.cancel_default) {
if (confirm->cancel_default) {
UI_but_flag_enable(cancel_but, UI_BUT_ACTIVE_DEFAULT);
}
else if (confirm.confirm_default) {
else if (confirm->confirm_default) {
UI_but_flag_enable(confirm_but, UI_BUT_ACTIVE_DEFAULT);
}
}
if (confirm.position == WM_WARNING_POSITION_MOUSE) {
if (confirm->position == WM_WARNING_POSITION_MOUSE) {
int bounds_offset[2];
bounds_offset[0] = uiLayoutGetWidth(layout) * (windows_layout ? -0.33f : -0.66f);
bounds_offset[1] = UI_UNIT_Y * (confirm.message[0] ? 3.1 : 2.5);
bounds_offset[1] = UI_UNIT_Y * (confirm->message.empty() ? 2.5 : 3.1);
UI_block_bounds_set_popup(block, padding, bounds_offset);
}
else if (confirm.position == WM_WARNING_POSITION_CENTER) {
else if (confirm->position == WM_WARNING_POSITION_CENTER) {
UI_block_bounds_set_centered(block, padding);
}
@ -1420,10 +1450,24 @@ int WM_operator_confirm_message_ex(bContext *C,
return OPERATOR_INTERFACE;
}
static void wm_operator_ui_popup_cancel(bContext *C, void *user_data);
static void wm_operator_ui_popup_ok(bContext *C, void *arg, int retval);
int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
{
if (op->type->confirm) {
UI_popup_block_invoke(C, wm_block_confirm_create, op, nullptr);
wmOpPopUp *data = MEM_new<wmOpPopUp>(__func__);
data->op = op;
data->free_op = true; /* if this runs and gets registered we may want not to free it */
data->include_properties = false;
data->icon = ALERT_ICON_WARNING;
UI_popup_block_ex(C,
wm_block_confirm_create,
wm_operator_ui_popup_ok,
wm_operator_ui_popup_cancel,
data,
op);
return OPERATOR_RUNNING_MODAL;
}
@ -1649,13 +1693,6 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *region, void *arg_op)
return block;
}
struct wmOpPopUp {
wmOperator *op;
int width;
int height;
int free_op;
};
/* Only invoked by OK button in popups created with wm_block_dialog_create() */
static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
{
@ -1665,7 +1702,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);
@ -1763,7 +1800,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)
@ -1775,13 +1812,12 @@ 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. */
@ -1851,18 +1887,18 @@ int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent * /*event
int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width)
{
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->include_properties = true;
data->icon = -1;
/* op is not executed until popup OK but is clicked */
UI_popup_block_ex(
C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op);
C, wm_block_confirm_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op);
return OPERATOR_RUNNING_MODAL;
}