Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface/interface_region_popup.c')
-rw-r--r--source/blender/editors/interface/interface_region_popup.c264
1 files changed, 156 insertions, 108 deletions
diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c
index 656c59055af..0ac4d4d28ec 100644
--- a/source/blender/editors/interface/interface_region_popup.c
+++ b/source/blender/editors/interface/interface_region_popup.c
@@ -48,7 +48,6 @@
#include "WM_api.h"
#include "WM_types.h"
-#include "wm_subwindow.h"
#include "UI_interface.h"
@@ -64,13 +63,13 @@
/**
* Translate any popup regions (so we can drag them).
*/
-void ui_popup_translate(bContext *C, ARegion *ar, const int mdiff[2])
+void ui_popup_translate(ARegion *ar, const int mdiff[2])
{
uiBlock *block;
BLI_rcti_translate(&ar->winrct, UNPACK2(mdiff));
- ED_region_update_rect(C, ar);
+ ED_region_update_rect(ar);
ED_region_tag_redraw(ar);
@@ -85,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);
}
}
@@ -121,34 +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_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 > 0.0f) left = 1;
- if (butrct.xmax + size_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,65 +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;
- else offset_x = butrct.xmin - block->rect.xmin;
- /* 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;
- else offset_x = butrct.xmin - block->rect.xmin;
- /* 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;
+ }
- /* 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)
- }
- }
+ /* Compute offset based on direction. */
+ int offset_x = 0, offset_y = 0;
-#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;
- }
+ 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);
}
-#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);
@@ -251,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);
@@ -281,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);
@@ -295,7 +292,7 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but,
/** \name Menu Block Creation
* \{ */
-static void ui_block_region_draw(const bContext *C, ARegion *ar)
+static void ui_block_region_refresh(const bContext *C, ARegion *ar)
{
ScrArea *ctx_area = CTX_wm_area(C);
ARegion *ctx_region = CTX_wm_region(C);
@@ -309,9 +306,11 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
ar->do_draw &= ~RGN_DRAW_REFRESH_UI;
for (block = ar->uiblocks.first; block; block = block_next) {
block_next = block->next;
- if (block->handle->can_refresh) {
- handle_ctx_area = block->handle->ctx_area;
- handle_ctx_region = block->handle->ctx_region;
+ uiPopupBlockHandle *handle = block->handle;
+
+ if (handle->can_refresh) {
+ handle_ctx_area = handle->ctx_area;
+ handle_ctx_region = handle->ctx_region;
if (handle_ctx_area) {
CTX_wm_area_set((bContext *)C, handle_ctx_area);
@@ -319,13 +318,21 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
if (handle_ctx_region) {
CTX_wm_region_set((bContext *)C, handle_ctx_region);
}
- ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+
+ uiBut *but = handle->popup_create_vars.but;
+ ARegion *butregion = handle->popup_create_vars.butregion;
+ ui_popup_block_refresh((bContext *)C, handle, butregion, but);
}
}
}
CTX_wm_area_set((bContext *)C, ctx_area);
CTX_wm_region_set((bContext *)C, ctx_region);
+}
+
+static void ui_block_region_draw(const bContext *C, ARegion *ar)
+{
+ uiBlock *block;
for (block = ar->uiblocks.first; block; block = block->next)
UI_block_draw(C, block);
@@ -335,7 +342,7 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
* Use to refresh centered popups on screen resizing (for splash).
*/
static void ui_block_region_popup_window_listener(
- bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
+ bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn, const Scene *UNUSED(scene))
{
switch (wmn->category) {
case NC_WINDOW:
@@ -455,8 +462,6 @@ uiBlock *ui_popup_block_refresh(
bContext *C, uiPopupBlockHandle *handle,
ARegion *butregion, uiBut *but)
{
- BLI_assert(handle->can_refresh == true);
-
const int margin = UI_POPUP_MARGIN;
wmWindow *window = CTX_wm_window(C);
ARegion *ar = handle->region;
@@ -468,6 +473,10 @@ uiBlock *ui_popup_block_refresh(
uiBlock *block_old = ar->uiblocks.first;
uiBlock *block;
+ handle->refresh = (block_old != NULL);
+
+ BLI_assert(!handle->refresh || handle->can_refresh);
+
#ifdef DEBUG
wmEvent *event_back = window->eventstate;
#endif
@@ -513,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 {
@@ -551,7 +560,7 @@ uiBlock *ui_popup_block_refresh(
block->pie_data.pie_center_spawned[0] += x_offset;
block->pie_data.pie_center_spawned[1] += y_offset;
- ui_block_translate(block, x_offset, y_offset);
+ UI_block_translate(block, x_offset, y_offset);
if (U.pie_initial_timeout > 0)
block->pie_data.flags |= UI_PIE_INITIAL_DIRECTION;
@@ -576,6 +585,17 @@ uiBlock *ui_popup_block_refresh(
else {
/* clip block with window boundary */
ui_popup_block_clip(window, block);
+
+ /* Avoid menu moving down and losing cursor focus by keeping it at
+ * the same height. */
+ 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;
+ }
+
+ handle->prev_block_rect = block->rect;
+
/* the block and buttons were positioned in window space as in 2.4x, now
* these menu blocks are regions so we bring it back to region space.
* additionally we add some padding for the menu shadow or rounded menus */
@@ -584,7 +604,15 @@ uiBlock *ui_popup_block_refresh(
ar->winrct.ymin = block->rect.ymin - margin;
ar->winrct.ymax = block->rect.ymax + UI_POPUP_MENU_TOP;
- ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
+ UI_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin);
+
+ /* apply scroll offset */
+ if (handle->scrolloffset != 0.0f) {
+ for (uiBut *bt = block->buttons.first; bt; bt = bt->next) {
+ bt->rect.ymin += handle->scrolloffset;
+ bt->rect.ymax += handle->scrolloffset;
+ }
+ }
}
if (block_old) {
@@ -597,17 +625,15 @@ uiBlock *ui_popup_block_refresh(
ui_popup_block_scrolltest(block);
/* adds subwindow */
- ED_region_init(C, ar);
+ ED_region_init(ar);
/* get winmat now that we actually have the subwindow */
- wmSubWindowSet(window, ar->swinid);
-
- wm_subwindow_matrix_get(window, ar->swinid, block->winmat);
+ wmGetProjectionMatrix(block->winmat, &ar->winrct);
/* notify change and redraw */
ED_region_tag_redraw(ar);
- ED_region_update_rect(C, ar);
+ ED_region_update_rect(ar);
#ifdef DEBUG
window->eventstate = event_back;
@@ -646,10 +672,12 @@ uiPopupBlockHandle *ui_popup_block_create(
handle->popup_create_vars.create_func = create_func;
handle->popup_create_vars.handle_create_func = handle_create_func;
handle->popup_create_vars.arg = arg;
+ handle->popup_create_vars.but = but;
handle->popup_create_vars.butregion = but ? butregion : NULL;
copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x);
- /* caller may free vars used to create this popup, in that case this variable should be disabled. */
- handle->can_refresh = true;
+
+ /* don't allow by default, only if popup type explicitly supports it */
+ handle->can_refresh = false;
/* create area region */
ar = ui_region_temp_add(CTX_wm_screen(C));
@@ -657,6 +685,7 @@ uiPopupBlockHandle *ui_popup_block_create(
memset(&type, 0, sizeof(ARegionType));
type.draw = ui_block_region_draw;
+ type.layout = ui_block_region_refresh;
type.regionid = RGN_TYPE_TEMPORARY;
ar->type = &type;
@@ -666,7 +695,7 @@ uiPopupBlockHandle *ui_popup_block_create(
handle = block->handle;
/* keep centered on window resizing */
- if ((block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) && handle->can_refresh) {
+ if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) {
type.listener = ui_block_region_popup_window_listener;
}
@@ -675,6 +704,25 @@ uiPopupBlockHandle *ui_popup_block_create(
void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
{
+ /* If this popup is created from a popover which does NOT have keep-open flag set,
+ * then close the popover too. We could extend this to other popup types too. */
+ ARegion *ar = handle->popup_create_vars.butregion;
+ if (ar != NULL) {
+ for (uiBlock *block = ar->uiblocks.first; block; block = block->next) {
+ if (block->handle &&
+ (block->flag & UI_BLOCK_POPOVER) &&
+ (block->flag & UI_BLOCK_KEEP_OPEN) == 0)
+ {
+ uiPopupBlockHandle *menu = block->handle;
+ menu->menuretval = UI_RETURN_OK;
+ }
+ }
+ }
+
+ if (handle->popup_create_vars.free_func) {
+ handle->popup_create_vars.free_func(handle, handle->popup_create_vars.arg);
+ }
+
ui_popup_block_remove(C, handle);
MEM_freeN(handle);