UI: fixes for 3D viewport popovers moving / flipping on edits.

* Ensure popover does not change direction or location.
* Open popover towards the relevant editor, like pulldown menus.
* Use a bigger maximum assumed size to deal with some corner cases.
* Do proper 3D viewport header refresh on shading mode changes.
This commit is contained in:
2018-05-06 18:08:27 +02:00
parent 16e220c959
commit c9ed78733c
5 changed files with 157 additions and 104 deletions

View File

@@ -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;

View File

@@ -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, &center[0], &center[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. */

View File

@@ -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);
}
}
/* 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);
}
/* 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 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;
}
}
#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
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;
}
/* apply offset, buttons in window coords */
/* Compute offset based on direction. */
int offset_x = 0, offset_y = 0;
for (bt = block->buttons.first; bt; bt = bt->next) {
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);
}
}
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);
}
}
/* 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);
}
/* 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;

View File

@@ -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))

View File

@@ -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);