UI: Support layout panels in dialog popups #119519

Merged
Jacques Lucke merged 22 commits from guishe/blender:dialog-layout-panels into main 2024-03-21 10:31:15 +01:00
7 changed files with 153 additions and 45 deletions

View File

@ -712,6 +712,21 @@ int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) A
* E.g. WM might need to do this for exiting files correctly.
*/
void UI_popup_menu_retval_set(const uiBlock *block, int retval, bool enable);
/**
guishe marked this conversation as resolved Outdated

Add comment to this function.

Add comment to this function.
* Set a dummy panel in the popup `block` to support using layout panels, the panel is linked
* to the popup `region` so layout panels state can be persistent until the popup is closed.
*/
void UI_popup_dummy_panel_set(ARegion *region, uiBlock *block);
/** Toggles layout panel open state and returns the new state. */
bool UI_layout_panel_toggle_open(const bContext *C, struct LayoutPanelHeader *header);
void UI_panel_drag_collapse_handler_add(const bContext *C, const bool was_open);
LayoutPanelHeader *UI_layout_panel_header_under_mouse(const Panel &panel, const int my);
/** Apply scroll to layout panels when the main panel is used in popups. */
void UI_layout_panel_popup_scroll_apply(Panel *panel, const float dy);
void UI_draw_layout_panels_backdrop(const ARegion *region,
const Panel *panel,
const float radius,
float subpanel_backcolor[4]);
/**
* Setting the button makes the popup open from the button instead of the cursor.
*/

View File

@ -2096,6 +2096,15 @@ void UI_block_draw(const bContext *C, uiBlock *block)
UI_panel_should_show_background(region, block->panel->type),
region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE);
}
/* Shared layout panel backdrop style between redo region and popups. */
guishe marked this conversation as resolved Outdated

Comment style, same below.

Comment style, same below.
if (block->panel && ELEM(region->regiontype, RGN_TYPE_HUD, RGN_TYPE_TEMPORARY)) {
/* TODO: Add as theme color. */
guishe marked this conversation as resolved Outdated

Comment style.

Comment style.
float subpanel_backcolor[4]{0.2f, 0.3f, 0.33f, 0.05f};
const bTheme *btheme = UI_GetTheme();
const float aspect = block->panel->runtime->block->aspect;
const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f / aspect;
UI_draw_layout_panels_backdrop(region, block->panel, radius, subpanel_backcolor);
}
BLF_batch_draw_begin();
UI_icon_draw_cache_begin();

View File

@ -55,6 +55,7 @@
#include "ED_undo.hh"
#include "UI_interface.hh"
#include "UI_interface_c.hh"
#include "UI_string_search.hh"
#include "BLF_api.hh"
@ -10124,6 +10125,8 @@ static void ui_menu_scroll_apply_offset_y(ARegion *region, uiBlock *block, float
/* remember scroll offset for refreshes */
block->handle->scrolloffset += dy;
/* Apply popup scroll delta to layout panels too. */
UI_layout_panel_popup_scroll_apply(block->panel, dy);
/* apply scroll offset */
LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
@ -11428,6 +11431,25 @@ static int ui_handle_menus_recursive(bContext *C,
C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false);
}
}
else if (event->val == KM_PRESS && event->type == LEFTMOUSE) {
LISTBASE_FOREACH (uiBlock *, block, &menu->region->uiblocks) {
if (block->panel) {
int mx = event->xy[0];
int my = event->xy[1];
ui_window_to_block(menu->region, block, &mx, &my);
if (!IN_RANGE(float(mx), block->rect.xmin, block->rect.xmax)) {
break;
}
LayoutPanelHeader *header = UI_layout_panel_header_under_mouse(*block->panel, my);
if (header) {
ED_region_tag_redraw(menu->region);
ED_region_tag_refresh_ui(menu->region);
UI_panel_drag_collapse_handler_add(C, !UI_layout_panel_toggle_open(C, header));
retval = WM_UI_HANDLER_BREAK;
}
}
}
}
/* now handle events for our own menu */
if (retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {

View File

@ -38,6 +38,7 @@
#include "ED_screen.hh"
#include "UI_interface.hh"
#include "UI_interface_c.hh"
#include "UI_interface_icons.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
@ -1162,6 +1163,36 @@ static int layout_panel_y_offset()
return UI_style_get_dpi()->panelspace;
}
void UI_draw_layout_panels_backdrop(const ARegion *region,
const Panel *panel,
const float radius,
float subpanel_backcolor[4])
{
/* Draw backdrops for layout panels. */
for (const LayoutPanelBody &body : panel->runtime->layout_panels.bodies) {
rctf panel_blockspace = panel->runtime->block->rect;
panel_blockspace.ymax = panel->runtime->block->rect.ymax + body.end_y;
panel_blockspace.ymin = panel->runtime->block->rect.ymax + body.start_y;
BLI_rctf_translate(&panel_blockspace, 0, -layout_panel_y_offset());
/* If the layout panel is at the end of the root panel, it's bottom corners are rounded. */
const bool is_main_panel_end = panel_blockspace.ymin - panel->runtime->block->rect.ymin < 10;
if (is_main_panel_end) {
panel_blockspace.ymin = panel->runtime->block->rect.ymin;
UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
}
else {
UI_draw_roundbox_corner_set(UI_CNR_NONE);
}
rcti panel_pixelspace = ui_to_pixelrect(region, panel->runtime->block, &panel_blockspace);
rctf panel_pixelspacef;
BLI_rctf_rcti_copy(&panel_pixelspacef, &panel_pixelspace);
UI_draw_roundbox_4fv(&panel_pixelspacef, true, radius, subpanel_backcolor);
}
}
static void panel_draw_aligned_backdrop(const ARegion *region,
const Panel *panel,
const rcti *rect,
@ -1200,31 +1231,9 @@ static void panel_draw_aligned_backdrop(const ARegion *region,
box_rect.ymax = rect->ymax;
UI_draw_roundbox_4fv(&box_rect, true, radius, panel_backcolor);
/* Draw backdrops for layout panels. */
for (const LayoutPanelBody &body : panel->runtime->layout_panels.bodies) {
float subpanel_backcolor[4];
UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, subpanel_backcolor);
rctf panel_blockspace = panel->runtime->block->rect;
panel_blockspace.ymax = panel->runtime->block->rect.ymax + body.end_y;
panel_blockspace.ymin = panel->runtime->block->rect.ymax + body.start_y;
BLI_rctf_translate(&panel_blockspace, 0, -layout_panel_y_offset());
/* If the layout panel is at the end of the root panel, it's bottom corners are rounded. */
const bool is_main_panel_end = panel_blockspace.ymin - panel->runtime->block->rect.ymin < 10;
if (is_main_panel_end) {
panel_blockspace.ymin = panel->runtime->block->rect.ymin;
UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
}
else {
UI_draw_roundbox_corner_set(UI_CNR_NONE);
}
rcti panel_pixelspace = ui_to_pixelrect(region, panel->runtime->block, &panel_blockspace);
rctf panel_pixelspacef;
BLI_rctf_rcti_copy(&panel_pixelspacef, &panel_pixelspace);
UI_draw_roundbox_4fv(&panel_pixelspacef, true, radius, subpanel_backcolor);
}
float subpanel_backcolor[4];
UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, subpanel_backcolor);
UI_draw_layout_panels_backdrop(region, panel, radius, subpanel_backcolor);
}
/* Panel header backdrops for non sub-panels. */
@ -1931,7 +1940,7 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
/** \name Region Level Panel Interaction
* \{ */
static LayoutPanelHeader *get_layout_panel_header_under_mouse(const Panel &panel, const int my)
LayoutPanelHeader *UI_layout_panel_header_under_mouse(const Panel &panel, const int my)
{
for (LayoutPanelHeader &header : panel.runtime->layout_panels.headers) {
if (IN_RANGE(float(my - panel.runtime->block->rect.ymax + layout_panel_y_offset()),
@ -1956,7 +1965,7 @@ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block,
if (IN_RANGE(float(my), block->rect.ymax, block->rect.ymax + PNL_HEADER)) {
return PANEL_MOUSE_INSIDE_HEADER;
}
if (get_layout_panel_header_under_mouse(*panel, my) != nullptr) {
if (UI_layout_panel_header_under_mouse(*panel, my) != nullptr) {
return PANEL_MOUSE_INSIDE_LAYOUT_PANEL_HEADER;
}
@ -1984,8 +1993,10 @@ static void ui_panel_drag_collapse(const bContext *C,
const uiPanelDragCollapseHandle *dragcol_data,
const int xy_dst[2])
{
ARegion *region = CTX_wm_region(C);
ARegion *region = CTX_wm_menu(C);
if (!region) {
region = CTX_wm_region(C);
}
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
float xy_a_block[2] = {float(dragcol_data->xy_init[0]), float(dragcol_data->xy_init[1])};
float xy_b_block[2] = {float(xy_dst[0]), float(xy_dst[1])};
@ -2015,6 +2026,7 @@ static void ui_panel_drag_collapse(const bContext *C,
&header.open_owner_ptr,
RNA_struct_find_property(&header.open_owner_ptr, header.open_prop_name.c_str()));
ED_region_tag_redraw(region);
ED_region_tag_refresh_ui(region);
}
}
@ -2080,7 +2092,7 @@ static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, voi
return retval;
}
static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
void UI_panel_drag_collapse_handler_add(const bContext *C, const bool was_open)
{
wmWindow *win = CTX_wm_window(C);
const wmEvent *event = win->eventstate;
@ -2097,32 +2109,32 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was
eWM_EventHandlerFlag(0));
}
bool UI_layout_panel_toggle_open(const bContext *C, LayoutPanelHeader *header)
{
const bool is_open = RNA_boolean_get(&header->open_owner_ptr, header->open_prop_name.c_str());
RNA_boolean_set(&header->open_owner_ptr, header->open_prop_name.c_str(), !is_open);
RNA_property_update(
const_cast<bContext *>(C),
&header->open_owner_ptr,
RNA_struct_find_property(&header->open_owner_ptr, header->open_prop_name.c_str()));
return !is_open;
}
static void ui_handle_layout_panel_header(
const bContext *C, const uiBlock *block, const int /*mx*/, const int my, const int event_type)
{
Panel *panel = block->panel;
BLI_assert(panel->type != nullptr);
LayoutPanelHeader *header = get_layout_panel_header_under_mouse(*panel, my);
LayoutPanelHeader *header = UI_layout_panel_header_under_mouse(*panel, my);
if (header == nullptr) {
return;
}
const bool is_open = RNA_boolean_get(&header->open_owner_ptr, header->open_prop_name.c_str());
if (is_open) {
RNA_boolean_set(&header->open_owner_ptr, header->open_prop_name.c_str(), false);
}
else {
RNA_boolean_set(&header->open_owner_ptr, header->open_prop_name.c_str(), true);
}
RNA_property_update(
const_cast<bContext *>(C),
&header->open_owner_ptr,
RNA_struct_find_property(&header->open_owner_ptr, header->open_prop_name.c_str()));
const bool new_state = UI_layout_panel_toggle_open(C, header);
ED_region_tag_redraw(CTX_wm_region(C));
if (event_type == LEFTMOUSE) {
ui_panel_drag_collapse_handler_add(C, is_open);
UI_panel_drag_collapse_handler_add(C, !new_state);
}
}
@ -2188,7 +2200,7 @@ static void ui_handle_panel_header(const bContext *C,
SET_FLAG_FROM_TEST(panel->flag, !UI_panel_is_closed(panel), PNL_CLOSED);
if (event_type == LEFTMOUSE) {
ui_panel_drag_collapse_handler_add(C, UI_panel_is_closed(panel));
UI_panel_drag_collapse_handler_add(C, UI_panel_is_closed(panel));
}
/* Set panel custom data (modifier) active when expanding sub-panels, but not top-level

View File

@ -576,6 +576,38 @@ static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
}
}
void UI_layout_panel_popup_scroll_apply(Panel *panel, const float dy)
{
if (!panel || dy == 0.0f) {
return;
}
for (LayoutPanelBody &body : panel->runtime->layout_panels.bodies) {
body.start_y += dy;
body.end_y += dy;
}
for (LayoutPanelHeader &headcer : panel->runtime->layout_panels.headers) {
headcer.start_y += dy;
headcer.end_y += dy;
}
}
void UI_popup_dummy_panel_set(ARegion *region, uiBlock *block)
{
Panel *&panel = region->runtime.popup_block_panel;
if (!panel) {
/* Dummy popup panel type. */
static PanelType panel_type = []() {
PanelType type{};
type.flag = PANEL_TYPE_NO_HEADER;
return type;
}();
panel = BKE_panel_new(&panel_type);
}
panel->runtime->layout_panels.clear();
block->panel = panel;
panel->runtime->block = block;
guishe marked this conversation as resolved Outdated

Use make format (even better, configure clang format to run on every save)

Use `make format` (even better, configure [clang format](https://developer.blender.org/docs/handbook/tooling/clangformat/) to run on every save)
}
uiBlock *ui_popup_block_refresh(bContext *C,
uiPopupBlockHandle *handle,
ARegion *butregion,
@ -753,7 +785,14 @@ uiBlock *ui_popup_block_refresh(bContext *C,
region->winrct.ymax = block->rect.ymax + UI_POPUP_MENU_TOP;
UI_block_translate(block, -region->winrct.xmin, -region->winrct.ymin);
/* Popups can change size, fix scroll offset if a panel was closed. */
float ymin = FLT_MAX;
LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
ymin = min_ff(ymin, bt->rect.ymin);
}
handle->scrolloffset = std::clamp<float>(
handle->scrolloffset, 0.0f, std::max<float>(block->rect.ymin - ymin, 0.0f));
/* apply scroll offset */
if (handle->scrolloffset != 0.0f) {
LISTBASE_FOREACH (uiBut *, bt, &block->buttons) {
@ -762,6 +801,8 @@ uiBlock *ui_popup_block_refresh(bContext *C,
}
}
}
/* Apply popup scroll offset to layout panels. */
UI_layout_panel_popup_scroll_apply(block->panel, handle->scrolloffset);
if (block_old) {
block->oldblock = block_old;
@ -875,6 +916,10 @@ void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
handle->popup_create_vars.arg_free(handle->popup_create_vars.arg);
}
if (handle->region->runtime.popup_block_panel) {
BKE_panel_free(handle->region->runtime.popup_block_panel);
}
guishe marked this conversation as resolved
Review

I get a crash when closing the splash screen here.
This code might have to come before ui_popup_block_remove above.

I get a crash when closing the splash screen here. This code might have to come before `ui_popup_block_remove` above.
ui_popup_block_remove(C, handle);
MEM_freeN(handle);

View File

@ -454,6 +454,9 @@ typedef struct ARegion_Runtime {
/** Maps #uiBlock::name to uiBlock for faster lookups. */
struct GHash *block_name_map;
/* Dummy panel used in popups so they can support layout panels. */
Panel *popup_block_panel;
} ARegion_Runtime;
typedef struct ARegion {

View File

@ -1391,6 +1391,7 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *region, void *arg_op)
BLI_assert(op->type->flag & OPTYPE_REGISTER);
UI_block_func_handle_set(block, wm_block_redo_cb, arg_op);
UI_popup_dummy_panel_set(region, block);
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, 0, style);
@ -1476,6 +1477,7 @@ 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_POPUP);
UI_popup_dummy_panel_set(region, block);
if (data->mouse_move_quit) {
UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT);