From c0e757a713cd8845f1e9a7e174ae7008412bab5b Mon Sep 17 00:00:00 2001 From: guishe Date: Wed, 15 Mar 2023 16:45:11 +0100 Subject: [PATCH] UI: Add scroll to sidebar tabs When the height of the editor couldn't fit the sidebar tabs, they would shrink to a size too small to read the tab label. This change matches the behaviour with the Properties Editor navigation bar, by introducing the following improvements: * Avoid truncating tab labels. * Allow scrolling when tabs don't fit. Behaviour is similar to how scrolling works in the Properties Editor navigation bar, supporting mouse wheel up/down and MMB, and switching tabs with `Ctrl+Wheel Up/Down`. Pull Request: https://projects.blender.org/blender/blender/pulls/105355 --- .../editors/interface/interface_panel.cc | 45 +++++++++---------- .../blender/editors/interface/view2d_ops.cc | 36 ++++++++++++--- source/blender/makesdna/DNA_screen_types.h | 4 ++ 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index d1702f1a9d1..7a7987540b2 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -1298,11 +1298,9 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) const int roundboxtype = is_left ? (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT) : (UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); bool is_alpha; - bool do_scaletabs = false; #ifdef USE_FLAT_INACTIVE bool is_active_prev = false; #endif - float scaletabs = 1.0f; /* Same for all tabs. */ /* Intentionally don't scale by 'px'. */ const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width); @@ -1344,7 +1342,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) BLI_assert(UI_panel_category_is_visible(region)); } - /* Calculate tab rectangle and check if we need to scale down. */ + /* Calculate tab rectangle for each panel. */ LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { rcti *rct = &pc_dyn->rect; const char *category_id = pc_dyn->idname; @@ -1360,16 +1358,13 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2); } - if (y_ofs > BLI_rcti_size_y(&v2d->mask)) { - scaletabs = float(BLI_rcti_size_y(&v2d->mask)) / float(y_ofs); - - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - rcti *rct = &pc_dyn->rect; - rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - } - - do_scaletabs = true; + const int max_scroll = max_ii(y_ofs - BLI_rcti_size_y(&v2d->mask), 0); + const int scroll = clamp_i(region->category_scroll, 0, max_scroll); + region->category_scroll = scroll; + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + rcti *rct = &pc_dyn->rect; + rct->ymin += scroll; + rct->ymax += scroll; } /* Begin drawing. */ @@ -1405,14 +1400,17 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { const rcti *rct = &pc_dyn->rect; + if (rct->ymin > v2d->mask.ymax) { + /* Scrolled outside the top of the view, check the next tab. */ + continue; + } + if (rct->ymax < v2d->mask.ymin) { + /* Scrolled past visible bounds, no need to draw other tabs. */ + break; + } const char *category_id = pc_dyn->idname; const char *category_id_draw = IFACE_(category_id); - const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2); size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX; -#if 0 - int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); -#endif - const bool is_active = STREQ(category_id, category_id_active); GPU_blend(GPU_BLEND_ALPHA); @@ -1469,11 +1467,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) /* Tab titles. */ - if (do_scaletabs) { - category_draw_len = BLF_width_to_strlen( - fontid, category_id_draw, category_draw_len, category_width, nullptr); - } - BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text); BLF_draw(fontid, category_id_draw, category_draw_len); @@ -2255,7 +2248,9 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, const char *category = UI_panel_category_active_get(region, false); if (LIKELY(category)) { PanelCategoryDyn *pc_dyn = UI_panel_category_find(region, category); - if (LIKELY(pc_dyn)) { + /* Cyclic behavior between categories + * using Ctrl+Tab (+Shift for backwards) or Ctrl+Wheel Up/Down. */ + if (LIKELY(pc_dyn) && (event->modifier & KM_CTRL)) { if (is_mousewheel) { /* We can probably get rid of this and only allow Ctrl-Tabbing. */ pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev; @@ -2276,9 +2271,9 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, UI_panel_category_active_set(region, pc_dyn->idname); ED_region_tag_redraw(region); } + return WM_UI_HANDLER_BREAK; } } - return WM_UI_HANDLER_BREAK; } return WM_UI_HANDLER_CONTINUE; diff --git a/source/blender/editors/interface/view2d_ops.cc b/source/blender/editors/interface/view2d_ops.cc index 37e5626b204..41bc73e4616 100644 --- a/source/blender/editors/interface/view2d_ops.cc +++ b/source/blender/editors/interface/view2d_ops.cc @@ -82,6 +82,9 @@ struct v2dViewPanData { /** event starting pan, for modal exit */ int invoke_event; + /** Tag if the scroll is done in the category tab. */ + bool do_category_scroll; + /** for MMB in scrollers (old feature in past, but now not that useful) */ short in_scroller; @@ -133,6 +136,8 @@ static void view_pan_init(bContext *C, wmOperator *op) vpd->facy = BLI_rctf_size_y(&vpd->v2d->cur) / winy; vpd->v2d->flag |= V2D_IS_NAVIGATING; + + vpd->do_category_scroll = false; } /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -144,14 +149,19 @@ static void view_pan_apply_ex(bContext *C, v2dViewPanData *vpd, float dx, float dx *= vpd->facx; dy *= vpd->facy; - /* only move view on an axis if change is allowed */ - if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) { - v2d->cur.xmin += dx; - v2d->cur.xmax += dx; + if (!vpd->do_category_scroll) { + /* only move view on an axis if change is allowed */ + if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) { + v2d->cur.xmin += dx; + v2d->cur.xmax += dx; + } + if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) { + v2d->cur.ymin += dy; + v2d->cur.ymax += dy; + } } - if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) { - v2d->cur.ymin += dy; - v2d->cur.ymax += dy; + else { + vpd->region->category_scroll -= dy; } /* Inform v2d about changes after this operation. */ @@ -212,6 +222,8 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) vpd->starty = vpd->lasty = event->xy[1]; vpd->invoke_event = event->type; + vpd->do_category_scroll = ED_region_panel_category_gutter_isect_xy(vpd->region, event->xy); + if (event->type == MOUSEPAN) { RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]); RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]); @@ -482,6 +494,10 @@ static int view_scrolldown_exec(bContext *C, wmOperator *op) return OPERATOR_PASS_THROUGH; } + const wmWindow *win = CTX_wm_window(C); + vpd->do_category_scroll = ED_region_panel_category_gutter_isect_xy(vpd->region, + win->eventstate->xy); + /* set RNA-Props */ RNA_int_set(op->ptr, "deltax", 0); RNA_int_set(op->ptr, "deltay", -40); @@ -529,6 +545,10 @@ static int view_scrollup_exec(bContext *C, wmOperator *op) return OPERATOR_PASS_THROUGH; } + const wmWindow *win = CTX_wm_window(C); + vpd->do_category_scroll = ED_region_panel_category_gutter_isect_xy(vpd->region, + win->eventstate->xy); + /* set RNA-Props */ RNA_int_set(op->ptr, "deltax", 0); RNA_int_set(op->ptr, "deltay", 40); @@ -2181,6 +2201,8 @@ static int reset_exec(bContext *C, wmOperator * /*op*/) View2D *v2d = ®ion->v2d; const int snap_test = ED_region_snap_size_test(region); + region->category_scroll = 0; + /* zoom 1.0 */ const int winx = float(BLI_rcti_size_x(&v2d->mask) + 1); const int winy = float(BLI_rcti_size_y(&v2d->mask) + 1); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 9872e30f0a2..fb2aef28551 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -455,6 +455,10 @@ typedef struct ARegion { rcti drawrct; /** Size. */ short winx, winy; + /* This is a Y offset on the panel tabs that represents pixels, where zero represents no scroll - + * the first category always shows first at the top. */ + int category_scroll; + char _pad0[4]; /** Region is currently visible on screen. */ short visible;