UI: Snapping for Sidebars

This PR adds snapping to the sizes of various Sidebars, showing only
the category tabs. While in this state you can click a tab to expand
out that panel, click again to hide it.

Pull Request: https://projects.blender.org/blender/blender/pulls/136575
This commit is contained in:
Harley Acheson
2025-06-12 20:38:35 +02:00
committed by Harley Acheson
parent 187d27b2ec
commit 7c110f265e
11 changed files with 83 additions and 1 deletions

View File

@@ -685,6 +685,7 @@ void ED_region_generic_tools_region_message_subscribe(
* Callback for #ARegionType.snap_size
*/
int ED_region_generic_tools_region_snap_size(const ARegion *region, int size, int axis);
int ED_region_generic_panel_region_snap_size(const ARegion *region, int size, int axis);
/* `area_query.cc` */

View File

@@ -313,6 +313,9 @@ enum {
#define UI_PANEL_CATEGORY_MARGIN_WIDTH (U.widget_unit * 1.0f)
/* Minimum width for a panel showing only category tabs. */
#define UI_PANEL_CATEGORY_MIN_WIDTH 26.0f
/* Both these margins should be ignored if the panel doesn't show a background (check
* #UI_panel_should_show_background()). */
#define UI_PANEL_MARGIN_X (U.widget_unit * 0.4f)

View File

@@ -1293,6 +1293,11 @@ void ui_draw_aligned_panel(const ARegion *region,
{
const Panel *panel = block->panel;
if (panel->sizex < 0 || panel->sizey < 0) {
/* Nothing to draw. */
return;
}
/* Add 0.001f to prevent flicker from float inaccuracy. */
const rcti header_rect = {
rect->xmin,
@@ -1478,6 +1483,10 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
immUnbindProgram();
/* If the area is too small to show panels, then don't show any tabs as active. */
const bool too_narrow = BLI_rcti_size_x(&region->winrct) <=
int(UI_PANEL_CATEGORY_MIN_WIDTH * UI_SCALE_FAC / aspect);
LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, &region->runtime->panels_category) {
const rcti *rct = &pc_dyn->rect;
if (rct->ymin > v2d->mask.ymax) {
@@ -1491,7 +1500,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
const char *category_id = pc_dyn->idname;
const char *category_id_draw = IFACE_(category_id);
size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
const bool is_active = STREQ(category_id, category_id_active);
const bool is_active = !too_narrow && STREQ(category_id, category_id_active);
GPU_blend(GPU_BLEND_ALPHA);
@@ -2517,6 +2526,25 @@ static int ui_handle_panel_category_cycling(const wmEvent *event,
return WM_UI_HANDLER_CONTINUE;
}
static void ui_panel_region_width_set(ARegion *region, const float aspect, int unscaled_size)
{
const float size_new = unscaled_size / aspect;
if (region->alignment & RGN_ALIGN_RIGHT) {
region->winrct.xmin = region->winrct.xmax - (size_new * UI_SCALE_FAC);
}
else {
region->winrct.xmax = region->winrct.xmin + (size_new * UI_SCALE_FAC);
}
region->winx = size_new * UI_SCALE_FAC;
region->sizex = size_new;
region->v2d.winx = region->winx;
region->v2d.cur.xmin = 0;
region->v2d.cur.xmax = size_new * UI_SCALE_FAC;
region->v2d.mask.xmin = 0;
region->v2d.mask.xmax = size_new * UI_SCALE_FAC;
UI_view2d_curRect_validate(&region->v2d);
}
int ui_handler_panel_region(bContext *C,
const wmEvent *event,
ARegion *region,
@@ -2544,7 +2572,26 @@ int ui_handler_panel_region(bContext *C,
if (event->type == LEFTMOUSE) {
PanelCategoryDyn *pc_dyn = panel_categories_find_mouse_over(region, event);
if (pc_dyn) {
const bool already_active = STREQ(pc_dyn->idname,
UI_panel_category_active_get(region, false));
UI_panel_category_active_set(region, pc_dyn->idname);
const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
(BLI_rcti_size_y(&region->v2d.mask) + 1);
const bool too_narrow = BLI_rcti_size_x(&region->winrct) <=
int(std::ceil(UI_PANEL_CATEGORY_MIN_WIDTH * UI_SCALE_FAC /
aspect));
if (too_narrow) {
/* Enlarge region. */
ui_panel_region_width_set(region, aspect, 250.0f);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
}
else if (already_active) {
/* Minimize region. */
ui_panel_region_width_set(region, aspect, UI_PANEL_CATEGORY_MIN_WIDTH);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
}
ED_region_tag_redraw(region);
/* Reset scroll to the top (#38348). */

View File

@@ -3376,6 +3376,16 @@ void ED_region_panels_draw(const bContext *C, ARegion *region)
mask.xmax -= round_fl_to_int(UI_view2d_scale_get_x(&region->v2d) *
UI_PANEL_CATEGORY_MARGIN_WIDTH);
}
/* Hide scrollbars below a threshold. */
const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
(BLI_rcti_size_y(&region->v2d.mask) + 1);
int min_width = UI_panel_category_is_visible(region) ? 60.0f * UI_SCALE_FAC / aspect :
40.0f * UI_SCALE_FAC / aspect;
if (BLI_rcti_size_x(&region->winrct) <= min_width) {
v2d->scroll &= ~(V2D_SCROLL_HORIZONTAL | V2D_SCROLL_VERTICAL);
}
UI_view2d_scrollers_draw(v2d, use_mask ? &mask : nullptr);
}

View File

@@ -66,4 +66,19 @@ int ED_region_generic_tools_region_snap_size(const ARegion *region, int size, in
return size;
}
int ED_region_generic_panel_region_snap_size(const ARegion *region, int size, int axis)
{
if (axis == 0) {
if (!UI_panel_category_is_visible(region)) {
return size;
}
/* Using Y axis avoids slight feedback loop when adjusting X. */
const float aspect = BLI_rctf_size_y(&region->v2d.cur) /
(BLI_rcti_size_y(&region->v2d.mask) + 1);
return int(UI_PANEL_CATEGORY_MIN_WIDTH / aspect);
}
return size;
}
/** \} */

View File

@@ -1275,6 +1275,7 @@ void ED_spacetype_clip()
art->keymapflag = ED_KEYMAP_FRAMES | ED_KEYMAP_UI;
art->poll = clip_properties_region_poll;
art->init = clip_properties_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->draw = clip_properties_region_draw;
art->listener = clip_properties_region_listener;
BLI_addhead(&st->regiontypes, art);

View File

@@ -1249,6 +1249,7 @@ void ED_spacetype_image()
art->listener = image_buttons_region_listener;
art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui;
art->init = image_buttons_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->layout = image_buttons_region_layout;
art->draw = image_buttons_region_draw;
BLI_addhead(&st->regiontypes, art);

View File

@@ -2143,6 +2143,7 @@ void ED_spacetype_node()
art->listener = node_region_listener;
art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui;
art->init = node_buttons_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->draw = node_buttons_region_draw;
BLI_addhead(&st->regiontypes, art);

View File

@@ -1161,6 +1161,7 @@ void ED_spacetype_sequencer()
art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui;
art->listener = sequencer_buttons_region_listener;
art->init = sequencer_buttons_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->draw = sequencer_buttons_region_draw;
BLI_addhead(&st->regiontypes, art);

View File

@@ -460,6 +460,7 @@ void ED_spacetype_text()
art->keymapflag = ED_KEYMAP_UI;
art->init = text_properties_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->draw = text_properties_region_draw;
BLI_addhead(&st->regiontypes, art);

View File

@@ -1632,6 +1632,7 @@ void ED_spacetype_view3d()
art->listener = view3d_buttons_region_listener;
art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui;
art->init = view3d_buttons_region_init;
art->snap_size = ED_region_generic_panel_region_snap_size;
art->layout = view3d_buttons_region_layout;
art->draw = ED_region_panels_draw;
BLI_addhead(&st->regiontypes, art);