UI: Add scroll to sidebar tabs #105355

Merged
Pablo Vazquez merged 3 commits from guishe/blender:category-tab-scroll into main 2023-03-15 16:45:21 +01:00
3 changed files with 61 additions and 32 deletions
Showing only changes of commit 2675f00b3c - Show all commits

View File

@ -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, &region->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, &region->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, &region->panels_category) {
rcti *rct = &pc_dyn->rect;
rct->ymin += scroll;
rct->ymax += scroll;
}
/* Begin drawing. */
@ -1405,14 +1400,13 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->panels_category) {
const rcti *rct = &pc_dyn->rect;
const bool is_visible = rct->ymin < v2d->mask.ymax && rct->ymax > v2d->mask.ymin;
guishe marked this conversation as resolved Outdated

Couldn't this be more efficient?

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;
}
Couldn't this be more efficient? ``` 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; } ```
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;
}

Works fine!

``` 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; } ``` Works fine!
if (!is_visible) {
continue;
}
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 +1463,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 +2244,7 @@ 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)) {
if (LIKELY(pc_dyn) && (event->modifier & KM_CTRL)) {
guishe marked this conversation as resolved Outdated

Include a comment for shy checking CTRL is needed, also shouldn't this check event->modifier == KM_CTRL (so other modifier combinations are ignored).

Include a comment for shy checking CTRL is needed, also shouldn't this check `event->modifier == KM_CTRL` (so other modifier combinations are ignored).
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 +2265,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;

View File

@ -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. */
@ -196,6 +206,21 @@ static int view_pan_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
guishe marked this conversation as resolved
Review

Use ED_region_panel_category_gutter_isect_xy or add a new public utility function to area_query.c if existing functions cannot be used - with an explanation for how it differs from existing intersection checking functions.

Use `ED_region_panel_category_gutter_isect_xy` or add a new public utility function to `area_query.c` if existing functions cannot be used - with an explanation for how it differs from existing intersection checking functions.

It works, can I just assume that win->eventstate is never null?

It works, can I just assume that `win->eventstate` is never null?
Review

Yes, you can assume it's never NULL (the doc-string should be updated to note this).

Yes, you can assume it's never NULL (the doc-string should be updated to note this).
inline bool mouse_in_category_tab(const ARegion *region, const wmEvent *event)
guishe marked this conversation as resolved Outdated

I don't see a point in using inline here, this is not a performance sensitive code path, so I rather don't optimize prematurely and let the compiler figure things out.

I don't see a point in using `inline` here, this is not a performance sensitive code path, so I rather don't optimize prematurely and let the compiler figure things out.
{
if (event == nullptr) {
return false;
}
const int mvalx = event->mval[0];
if (region->runtime.category && !BLI_listbase_is_empty(&region->panels_category)) {
const PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(
region->panels_category.first);
const bool in_category_tab = (mvalx < pc_dyn->rect.xmax) && (mvalx > pc_dyn->rect.xmin);
return in_category_tab;
}
return false;
}
/* set up modal operator and relevant settings */
static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
@ -212,6 +237,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 = mouse_in_category_tab(vpd->region, event);
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 +509,10 @@ static int view_scrolldown_exec(bContext *C, wmOperator *op)
return OPERATOR_PASS_THROUGH;
}
wmWindow *win = CTX_wm_window(C);
wmEvent *event = win->event_last_handled;
guishe marked this conversation as resolved Outdated

Use win->eventstate, win->event_last_handled is intended for event handling logic and not to be used by interface logic. Updated the doc-string to note this 09ba0210d9.

Use `win->eventstate`, `win->event_last_handled` is intended for event handling logic and not to be used by interface logic. Updated the doc-string to note this 09ba0210d9a19a2713fa0f92cf5f54914789c62f.

Solved, the problem I had is that mval is not assigned in win->eventstate for 'Wheel Up/Down' events.

Solved, the problem I had is that `mval` is not assigned in `win->eventstate` for 'Wheel Up/Down' events.
vpd->do_category_scroll = mouse_in_category_tab(vpd->region, event);
/* set RNA-Props */
RNA_int_set(op->ptr, "deltax", 0);
RNA_int_set(op->ptr, "deltay", -40);
@ -529,6 +560,10 @@ static int view_scrollup_exec(bContext *C, wmOperator *op)
return OPERATOR_PASS_THROUGH;
}
wmWindow *win = CTX_wm_window(C);
wmEvent *event = win->event_last_handled;
vpd->do_category_scroll = mouse_in_category_tab(vpd->region, event);
/* set RNA-Props */
RNA_int_set(op->ptr, "deltax", 0);
RNA_int_set(op->ptr, "deltay", 40);
@ -2181,6 +2216,8 @@ static int reset_exec(bContext *C, wmOperator * /*op*/)
View2D *v2d = &region->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);

View File

@ -455,6 +455,9 @@ typedef struct ARegion {
rcti drawrct;
/** Size. */
short winx, winy;
/** Scroll value for category tab. */
guishe marked this conversation as resolved Outdated

Doc-string should state what unit this is in.

Also, is it scaled by DPI - does it properly handle changes (loading other peoples files with Hi-DPI?).

Doc-string should state what unit this is in. Also, is it scaled by DPI - does it properly handle changes (loading other peoples files with Hi-DPI?).
int category_scroll;
char _pad0[4];
/** Region is currently visible on screen. */
short visible;