diff options
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 3 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 44 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 25 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 12 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_query.c | 18 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_utils.c | 96 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 2 |
7 files changed, 189 insertions, 11 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 58eaba480e1..020ab5f5b66 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -707,6 +707,7 @@ void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], void UI_block_end(const struct bContext *C, uiBlock *block); void UI_block_draw(const struct bContext *C, struct uiBlock *block); void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb); +void UI_blocklist_update_view_for_buttons(const struct bContext *C, const struct ListBase *lb); void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb); void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block); @@ -2654,6 +2655,8 @@ bool UI_editsource_enable_check(void); void UI_editsource_active_but_test(uiBut *but); void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but); +void UI_but_ensure_in_view(const struct bContext *C, struct ARegion *region, const uiBut *but); + /* UI_butstore_ helpers */ typedef struct uiButStore uiButStore; typedef struct uiButStoreElem uiButStoreElem; diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 32b86119753..d5316df0e0c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -131,12 +131,10 @@ static bool ui_but_is_unit_radians(const uiBut *but) /* ************* window matrix ************** */ -void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +void ui_block_to_region_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) { const int getsizex = BLI_rcti_size_x(®ion->winrct) + 1; const int getsizey = BLI_rcti_size_y(®ion->winrct) + 1; - const int sx = region->winrct.xmin; - const int sy = region->winrct.ymin; float gx = *r_x; float gy = *r_y; @@ -146,14 +144,19 @@ void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, fl gy += block->panel->ofsy; } - *r_x = ((float)sx) + - ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + + *r_x = ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + block->winmat[3][0])); - *r_y = ((float)sy) + - ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + + *r_y = ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + block->winmat[3][1])); } +void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +{ + ui_block_to_region_fl(region, block, r_x, r_y); + *r_x += region->winrct.xmin; + *r_y += region->winrct.ymin; +} + void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_y) { float fx = *r_x; @@ -165,6 +168,16 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_ *r_y = (int)(fy + 0.5f); } +void ui_block_to_region_rctf(const ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src) +{ + *rct_dst = *rct_src; + ui_block_to_region_fl(region, block, &rct_dst->xmin, &rct_dst->ymin); + ui_block_to_region_fl(region, block, &rct_dst->xmax, &rct_dst->ymax); +} + void ui_block_to_window_rctf(const ARegion *region, uiBlock *block, rctf *rct_dst, @@ -249,6 +262,14 @@ void ui_window_to_region_rcti(const ARegion *region, rcti *rect_dst, const rcti rect_dst->ymax = rct_src->ymax - region->winrct.ymin; } +void ui_window_to_region_rctf(const ARegion *region, rctf *rect_dst, const rctf *rct_src) +{ + rect_dst->xmin = rct_src->xmin - region->winrct.xmin; + rect_dst->xmax = rct_src->xmax - region->winrct.xmin; + rect_dst->ymin = rct_src->ymin - region->winrct.ymin; + rect_dst->ymax = rct_src->ymax - region->winrct.ymin; +} + void ui_region_to_window(const ARegion *region, int *r_x, int *r_y) { *r_x += region->winrct.xmin; @@ -3440,6 +3461,15 @@ void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb) } } +void UI_blocklist_update_view_for_buttons(const bContext *C, const ListBase *lb) +{ + LISTBASE_FOREACH (uiBlock *, block, lb) { + if (block->active) { + ui_but_update_view_for_active(C, block); + } + } +} + void UI_blocklist_draw(const bContext *C, const ListBase *lb) { LISTBASE_FOREACH (uiBlock *, block, lb) { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index cd975e97613..5bd7ac28664 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3519,6 +3519,11 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) ui_but_update(but); + /* Popup blocks don't support moving after creation, so don't change the view for them. */ + if (!data->searchbox) { + UI_but_ensure_in_view(C, data->region, but); + } + WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); #ifdef WITH_INPUT_IME @@ -8883,6 +8888,26 @@ void UI_context_update_anim_flag(const bContext *C) } } +/** + * In some cases we may want to update the view (#View2D) in-between layout definition and drawing. + * E.g. to make sure a button is visible while editing. + */ +void ui_but_update_view_for_active(const bContext *C, const uiBlock *block) +{ + uiBut *active_but = ui_block_active_but_get(block); + if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) { + return; + } + /* If there is a search popup attached to the button, don't change the view. The popups don't + * support updating the position to the button position nicely. */ + uiHandleButtonData *data = active_but->active; + if (data->searchbox) { + return; + } + + UI_but_ensure_in_view(C, active_but->active->region, active_but); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index efc94b89046..a3a9776a510 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -597,11 +597,19 @@ typedef struct uiSafetyRct { void ui_fontscale(short *points, float aspect); +extern void ui_block_to_region_fl(const struct ARegion *region, + uiBlock *block, + float *r_x, + float *r_y); extern void ui_block_to_window_fl(const struct ARegion *region, uiBlock *block, float *x, float *y); extern void ui_block_to_window(const struct ARegion *region, uiBlock *block, int *x, int *y); +extern void ui_block_to_region_rctf(const struct ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src); extern void ui_block_to_window_rctf(const struct ARegion *region, uiBlock *block, rctf *rct_dst, @@ -620,6 +628,9 @@ extern void ui_window_to_region(const struct ARegion *region, int *x, int *y); extern void ui_window_to_region_rcti(const struct ARegion *region, rcti *rect_dst, const rcti *rct_src); +extern void ui_window_to_region_rctf(const struct ARegion *region, + rctf *rect_dst, + const rctf *rct_src); extern void ui_region_to_window(const struct ARegion *region, int *x, int *y); extern void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r_rect); @@ -944,6 +955,7 @@ extern void ui_but_execute_end(struct bContext *C, uiBut *but, void *active_back); extern void ui_but_active_free(const struct bContext *C, uiBut *but); +extern void ui_but_update_view_for_active(const struct bContext *C, const uiBlock *block); extern int ui_but_menu_direction(uiBut *but); extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore); extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index ecb96a83355..8534c95b6fd 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -572,6 +572,17 @@ size_t ui_but_tip_len_only_first_line(const uiBut *but) /** \name Block (#uiBlock) State * \{ */ +uiBut *ui_block_active_but_get(const uiBlock *block) +{ + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active) { + return but; + } + } + + return NULL; +} + bool ui_block_is_menu(const uiBlock *block) { return (((block->flag & UI_BLOCK_LOOP) != 0) && @@ -675,10 +686,9 @@ uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, b uiBut *ui_region_find_active_but(ARegion *region) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->active) { - return but; - } + uiBut *but = ui_block_active_but_get(block); + if (but) { + return but; } } diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 7ea02226f02..aa6dff3952a 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -29,6 +29,8 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "ED_screen.h" + #include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -38,6 +40,7 @@ #include "BLT_translation.h" +#include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -48,6 +51,7 @@ #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "WM_api.h" #include "WM_types.h" @@ -774,6 +778,98 @@ bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, } /* -------------------------------------------------------------------- */ + +static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d) +{ + rctf region_rect; + ui_block_to_region_rctf(region, but->block, ®ion_rect, &but->rect); + + rctf view_rect; + UI_view2d_region_to_view_rctf(v2d, ®ion_rect, &view_rect); + + return view_rect; +} + +/** + * To get a margin (typically wanted), add the margin to \a rect directly. + * + * Based on #file_ensure_inside_viewbounds(), could probably share code. + * + * \return true if anything changed. + */ +static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect) +{ + const float rect_width = BLI_rctf_size_x(rect); + const float rect_height = BLI_rctf_size_y(rect); + + rctf *cur = &v2d->cur; + const float cur_width = BLI_rctf_size_x(cur); + const float cur_height = BLI_rctf_size_y(cur); + + bool changed = false; + + /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */ + if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) { + cur->ymin = rect->ymin; + cur->ymax = cur->ymin + cur_height; + changed = true; + } + /* Snap to upper edge. */ + else if (cur->ymax < rect->ymax) { + cur->ymax = rect->ymax; + cur->ymin = cur->ymax - cur_height; + changed = true; + } + /* Snap to left edge. Also use if rect is wider than view bounds. */ + else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) { + cur->xmin = rect->xmin; + cur->xmax = cur->xmin + cur_width; + changed = true; + } + /* Snap to right edge. */ + else if (cur->xmax < rect->xmax) { + cur->xmax = rect->xmax; + cur->xmin = cur->xmax - cur_width; + changed = true; + } + else { + BLI_assert(BLI_rctf_inside_rctf(cur, rect)); + } + + return changed; +} + +/** + * Adjust the view so the rectangle of \a but is in view, with some extra margin. + * + * It's important that this is only executed after buttons received their final #uiBut.rect. E.g. + * #UI_panels_end() modifies them, so if that is executed, this function must not be called before + * it. + * + * \param region: The region the button is placed in. Make sure this is actually the one the button + * is placed in, not just the context region. + */ +void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but) +{ + View2D *v2d = ®ion->v2d; + /* Unitialized view or region that doesn't use View2D. */ + if ((v2d->flag & V2D_IS_INIT) == 0) { + return; + } + + rctf rect = ui_but_rect_to_view(but, region, v2d); + + const int margin = UI_UNIT_X * 0.5f; + BLI_rctf_pad(&rect, margin, margin); + + const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect); + if (changed) { + UI_view2d_curRect_changed(C, v2d); + ED_region_tag_redraw_no_rebuild(region); + } +} + +/* -------------------------------------------------------------------- */ /** \name Button Store * * Modal Button Store API. diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index f4109981aae..cb81857eb83 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3018,6 +3018,8 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } + UI_blocklist_update_view_for_buttons(C, ®ion->uiblocks); + if (update_tot_size) { /* this also changes the 'cur' */ UI_view2d_totRect_set(v2d, x, y); |