diff options
5 files changed, 153 insertions, 100 deletions
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index e6f40d758d1..de2217a3d3e 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -551,6 +551,7 @@ struct uiPopupBlockHandle { struct uiPopupBlockCreate popup_create_vars; /* true if we can re-create the popup using 'popup_create_vars' */ bool can_refresh; + bool refresh; struct wmTimer *scrolltimer; @@ -572,8 +573,14 @@ struct uiPopupBlockHandle { /* menu direction */ int direction; - /* previous rect for refresh */ + /* Previous values so we don't resize or reposition on refresh. */ rctf prev_block_rect; + rctf prev_butrct; + short prev_dir1, prev_dir2; + int prev_mx, prev_my; + + /* Maximum estimated size to avoid having to reposition on refresh. */ + float max_size_x, max_size_y; /* #ifdef USE_DRAG_POPUP */ bool is_grab; diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index dea09393504..4dfa9198958 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -57,6 +57,8 @@ #include "BKE_context.h" +#include "ED_screen.h" + #include "WM_api.h" #include "WM_types.h" @@ -122,7 +124,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v UI_block_region_set(block, handle->region); UI_block_layout_resolve(block, &width, &height); - UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); + UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER | UI_BLOCK_NO_WIN_CLIP); UI_block_direction_set(block, UI_DIR_DOWN | UI_DIR_CENTER_X); const int block_margin = U.widget_unit / 2; @@ -138,13 +140,32 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v UI_block_direction_set(block, UI_DIR_RIGHT); /* Store the button location for positioning the popover arrow hint. */ - { + if (!handle->refresh) { float center[2] = {BLI_rctf_cent_x(&pup->but->rect), BLI_rctf_cent_y(&pup->but->rect)}; ui_block_to_window_fl(handle->ctx_region, pup->but->block, ¢er[0], ¢er[1]); /* These variables aren't used for popovers, we could add new variables if there is a conflict. */ - block->mx = (int)center[0]; - block->my = (int)center[1]; + handle->prev_mx = block->mx = (int)center[0]; + handle->prev_my = block->my = (int)center[1]; + } + else { + block->mx = handle->prev_mx; + block->my = handle->prev_my; } + + /* Prefer popover from header to be positioned into the editor. */ + if (!slideout) { + ScrArea *sa = CTX_wm_area(C); + if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { + ARegion *ar = CTX_wm_region(C); + if (ar && ar->regiontype == RGN_TYPE_HEADER) { + UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X); + } + } + } + + /* Estimated a maximum size so we don't go offscreen for low height + * areas near the bottom of the window on refreshes. */ + handle->max_size_y = UI_UNIT_Y * 16.0f; } else { /* Not attached to a button. */ diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index cc194b41f1b..e971edb95cb 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -84,32 +84,38 @@ void ui_popup_translate(bContext *C, ARegion *ar, const int mdiff[2]) } /* position block relative to but, result is in window space */ -static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block) +static void ui_popup_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block) { - uiBut *bt; - uiSafetyRct *saferct; + uiPopupBlockHandle *handle = block->handle; + + /* Compute button position in window coordinates using the source + * button region/block, to position the popup attached to it. */ rctf butrct; - /*float aspect;*/ /*UNUSED*/ - int size_x, size_y, offset_x = 0, offset_y = 0; - short dir1 = 0, dir2 = 0; - /* transform to window coordinates, using the source button region/block */ - ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect); + if (!handle->refresh) { + ui_block_to_window_rctf(butregion, but->block, &butrct, &but->rect); + + /* widget_roundbox_set has this correction too, keep in sync */ + if (but->type != UI_BTYPE_PULLDOWN) { + if (but->drawflag & UI_BUT_ALIGN_TOP) + butrct.ymax += U.pixelsize; + if (but->drawflag & UI_BUT_ALIGN_LEFT) + butrct.xmin -= U.pixelsize; + } - /* widget_roundbox_set has this correction too, keep in sync */ - if (but->type != UI_BTYPE_PULLDOWN) { - if (but->drawflag & UI_BUT_ALIGN_TOP) - butrct.ymax += U.pixelsize; - if (but->drawflag & UI_BUT_ALIGN_LEFT) - butrct.xmin -= U.pixelsize; + handle->prev_butrct = butrct; + } + else { + /* For refreshes, keep same button position so popup doesn't move. */ + butrct = handle->prev_butrct; } - /* calc block rect */ + /* Compute block size in window space, based on buttons contained in it. */ if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) { if (block->buttons.first) { BLI_rctf_init_minmax(&block->rect); - for (bt = block->buttons.first; bt; bt = bt->next) { + for (uiBut *bt = block->buttons.first; bt; bt = bt->next) { BLI_rctf_union(&block->rect, &bt->rect); } } @@ -120,35 +126,34 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, } } - /* aspect = (float)(BLI_rcti_size_x(&block->rect) + 4);*/ /*UNUSED*/ ui_block_to_window_rctf(butregion, but->block, &block->rect, &block->rect); - //block->rect.xmin -= 2.0; block->rect.ymin -= 2.0; - //block->rect.xmax += 2.0; block->rect.ymax += 2.0; + /* Compute direction relative to button, based on available space. */ + const int size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X; /* 4 for shadow */ + const int size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y; + const int center_x = (block->direction & UI_DIR_CENTER_X) ? size_x / 2 : 0; + const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0; - size_x = BLI_rctf_size_x(&block->rect) + 0.2f * UI_UNIT_X; /* 4 for shadow */ - size_y = BLI_rctf_size_y(&block->rect) + 0.2f * UI_UNIT_Y; - /* aspect /= (float)size_x;*/ /*UNUSED*/ + short dir1 = 0, dir2 = 0; - { + if (!handle->refresh) { bool left = 0, right = 0, top = 0, down = 0; - // int offscreen; const int win_x = WM_window_pixels_x(window); const int win_y = WM_window_pixels_y(window); - // wm_window_get_size(window, &win_x, &win_y); - const int center_x = (block->direction & UI_DIR_CENTER_X) ? size_x / 2 : 0; - const int center_y = (block->direction & UI_DIR_CENTER_Y) ? size_y / 2 : 0; + /* Take into account maximum size so we don't have to flip on refresh. */ + const float max_size_x = max_ff(size_x, handle->max_size_x); + const float max_size_y = max_ff(size_y, handle->max_size_y); /* check if there's space at all */ - if (butrct.xmin - size_x + center_x > 0.0f) left = 1; - if (butrct.xmax + size_x - center_x < win_x) right = 1; - if (butrct.ymin - size_y + center_y > 0.0f) down = 1; - if (butrct.ymax + size_y - center_y < win_y) top = 1; + if (butrct.xmin - max_size_x + center_x > 0.0f) left = 1; + if (butrct.xmax + max_size_x - center_x < win_x) right = 1; + if (butrct.ymin - max_size_y + center_y > 0.0f) down = 1; + if (butrct.ymax + max_size_y - center_y < win_y) top = 1; if (top == 0 && down == 0) { - if (butrct.ymin - size_y < win_y - butrct.ymax - size_y) + if (butrct.ymin - max_size_y < win_y - butrct.ymax - max_size_y) top = 1; else down = 1; @@ -182,70 +187,57 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, if (dir2 == UI_DIR_DOWN && down == 0) dir2 = UI_DIR_UP; } - if (dir1 == UI_DIR_LEFT) { - offset_x = butrct.xmin - block->rect.xmax; - if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; - else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; - } - else if (dir1 == UI_DIR_RIGHT) { - offset_x = butrct.xmax - block->rect.xmin; - if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; - else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; - } - else if (dir1 == UI_DIR_UP) { - offset_y = butrct.ymax - block->rect.ymin; - if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; - else offset_x = butrct.xmin - block->rect.xmin - center_x; - /* changed direction? */ - if ((dir1 & block->direction) == 0) { - UI_block_order_flip(block); - } - } - else if (dir1 == UI_DIR_DOWN) { - offset_y = butrct.ymin - block->rect.ymax; - if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; - else offset_x = butrct.xmin - block->rect.xmin - center_x; - /* changed direction? */ - if ((dir1 & block->direction) == 0) { - UI_block_order_flip(block); - } - } + handle->prev_dir1 = dir1; + handle->prev_dir2 = dir2; + } + else { + /* For refreshes, keep same popup direct so popup doesn't move + * to a totally different position while editing in it. */ + dir1 = handle->prev_dir1; + dir2 = handle->prev_dir2; + } - /* Center over popovers for eg. */ - if (block->direction & UI_DIR_CENTER_X) { - offset_x += BLI_rctf_size_x(&butrct) / ((dir2 == UI_DIR_LEFT) ? 2 : - 2); - } + /* Compute offset based on direction. */ + int offset_x = 0, offset_y = 0; - /* and now we handle the exception; no space below or to top */ - if (top == 0 && down == 0) { - if (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT) { - /* align with bottom of screen */ - // offset_y = size_y; (not with menu scrolls) - } + if (dir1 == UI_DIR_LEFT) { + offset_x = butrct.xmin - block->rect.xmax; + if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; + else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; + } + else if (dir1 == UI_DIR_RIGHT) { + offset_x = butrct.xmax - block->rect.xmin; + if (dir2 == UI_DIR_UP) offset_y = butrct.ymin - block->rect.ymin - center_y - MENU_PADDING; + else offset_y = butrct.ymax - block->rect.ymax + center_y + MENU_PADDING; + } + else if (dir1 == UI_DIR_UP) { + offset_y = butrct.ymax - block->rect.ymin; + if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; + else offset_x = butrct.xmin - block->rect.xmin - center_x; + /* changed direction? */ + if ((dir1 & block->direction) == 0) { + /* TODO: still do */ + UI_block_order_flip(block); } - -#if 0 /* seems redundant and causes issues with blocks inside big regions */ - /* or no space left or right */ - if (left == 0 && right == 0) { - if (dir1 == UI_DIR_UP || dir1 == UI_DIR_DOWN) { - /* align with left size of screen */ - offset_x = -block->rect.xmin + 5; - } + } + else if (dir1 == UI_DIR_DOWN) { + offset_y = butrct.ymin - block->rect.ymax; + if (dir2 == UI_DIR_RIGHT) offset_x = butrct.xmax - block->rect.xmax + center_x; + else offset_x = butrct.xmin - block->rect.xmin - center_x; + /* changed direction? */ + if ((dir1 & block->direction) == 0) { + /* TODO: still do */ + UI_block_order_flip(block); } -#endif - -#if 0 - /* clamp to window bounds, could be made into an option if its ever annoying */ - if ( (offscreen = (block->rect.ymin + offset_y)) < 0) offset_y -= offscreen; /* bottom */ - else if ((offscreen = (block->rect.ymax + offset_y) - winy) > 0) offset_y -= offscreen; /* top */ - if ( (offscreen = (block->rect.xmin + offset_x)) < 0) offset_x -= offscreen; /* left */ - else if ((offscreen = (block->rect.xmax + offset_x) - winx) > 0) offset_x -= offscreen; /* right */ -#endif } - /* apply offset, buttons in window coords */ + /* Center over popovers for eg. */ + if (block->direction & UI_DIR_CENTER_X) { + offset_x += BLI_rctf_size_x(&butrct) / ((dir2 == UI_DIR_LEFT) ? 2 : - 2); + } - for (bt = block->buttons.first; bt; bt = bt->next) { + /* Apply offset, buttons in window coords. */ + for (uiBut *bt = block->buttons.first; bt; bt = bt->next) { ui_block_to_window_rctf(butregion, but->block, &bt->rect, &bt->rect); BLI_rctf_translate(&bt->rect, offset_x, offset_y); @@ -256,7 +248,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, BLI_rctf_translate(&block->rect, offset_x, offset_y); - /* safety calculus */ + /* Safety calculus. */ { const float midx = BLI_rctf_cent_x(&butrct); const float midy = BLI_rctf_cent_y(&butrct); @@ -286,7 +278,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, } /* keep a list of these, needed for pulldown menus */ - saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct"); + uiSafetyRct *saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct"); saferct->parent = butrct; saferct->safety = block->safety; BLI_freelistN(&block->saferct); @@ -481,7 +473,9 @@ uiBlock *ui_popup_block_refresh( uiBlock *block_old = ar->uiblocks.first; uiBlock *block; - BLI_assert(!block_old || handle->can_refresh == true); + handle->refresh = (block_old != NULL); + + BLI_assert(!handle->refresh || handle->can_refresh); #ifdef DEBUG wmEvent *event_back = window->eventstate; @@ -528,7 +522,7 @@ uiBlock *ui_popup_block_refresh( /* if this is being created from a button */ if (but) { block->aspect = but->block->aspect; - ui_block_position(window, butregion, but, block); + ui_popup_block_position(window, butregion, but, block); handle->direction = block->direction; } else { @@ -594,7 +588,7 @@ uiBlock *ui_popup_block_refresh( /* Avoid menu moving down and losing cursor focus by keeping it at * the same height. */ - if (block_old && handle->prev_block_rect.ymax > block->rect.ymax) { + if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) { float offset = handle->prev_block_rect.ymax - block->rect.ymax; ui_block_translate(block, 0, offset); block->rect.ymin = handle->prev_block_rect.ymin; diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 7fff9bd74b8..60f555a6188 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4274,7 +4274,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) /* alignment */ if ((but->drawflag & UI_BUT_ALIGN) && but->type != UI_BTYPE_PULLDOWN) { - /* ui_block_position has this correction too, keep in sync */ + /* ui_popup_block_position has this correction too, keep in sync */ if (but->drawflag & (UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_STITCH_TOP)) rect->ymax += U.pixelsize; if (but->drawflag & (UI_BUT_ALIGN_LEFT | UI_BUT_ALIGN_STITCH_LEFT)) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a5a12b509e1..ff5eb46a38e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1154,6 +1154,36 @@ static void view3d_header_region_listener( } } +static void view3d_header_region_message_subscribe( + const struct bContext *UNUSED(C), + struct WorkSpace *UNUSED(workspace), struct Scene *UNUSED(scene), + struct bScreen *UNUSED(screen), struct ScrArea *UNUSED(sa), struct ARegion *ar, + struct wmMsgBus *mbus) +{ + wmMsgParams_RNA msg_key_params = {{{0}}}; + + /* Only subscribe to types. */ + StructRNA *type_array[] = { + &RNA_View3DShading, + }; + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + + for (int i = 0; i < ARRAY_SIZE(type_array); i++) { + msg_key_params.ptr.type = type_array[i]; + WM_msg_subscribe_rna_params( + mbus, + &msg_key_params, + &msg_sub_value_region_tag_redraw, + __func__); + } +} + + /* add handlers, stuff you only do once or on area/region changes */ static void view3d_buttons_region_init(wmWindowManager *wm, ARegion *ar) { @@ -1496,6 +1526,7 @@ void ED_spacetype_view3d(void) art->listener = view3d_header_region_listener; art->init = view3d_header_region_init; art->draw = view3d_header_region_draw; + art->message_subscribe = view3d_header_region_message_subscribe; BLI_addhead(&st->regiontypes, art); BKE_spacetype_register(st); |