UI: Add tab filter popover to vertical tabs

This adds unobtrusive tab button for selecting displayed tabs to the
tabs region. The idea is, that this way, the filter is much more
discoverable than it being hidden in options popover. The button is
grayed out, when all tabs are visible.

To only draw arrow, is achieved by setting `UILayout.emboss` to `NONE`
or `NONE_OR_STATUS`

Pull Request: https://projects.blender.org/blender/blender/pulls/135132
This commit is contained in:
Richard Antalik
2025-10-09 04:51:18 +02:00
committed by Richard Antalik
parent 43654059ba
commit a82f3a0fe0
3 changed files with 97 additions and 71 deletions

View File

@@ -10,32 +10,36 @@ from bpy.app.translations import (
)
tab_list = [
("show_properties_tool", "Tool", 'TOOL_SETTINGS'),
("show_properties_render", "Render", 'SCENE'),
("show_properties_output", "Output", 'OUTPUT'),
("show_properties_view_layer", "View Layer", 'RENDERLAYERS'),
("show_properties_scene", "Scene", 'SCENE_DATA'),
("show_properties_world", "World", 'WORLD'),
("show_properties_collection", "Collection", 'OUTLINER_COLLECTION'),
("show_properties_object", "Object", 'OBJECT_DATA'),
("show_properties_modifiers", "Modifiers", 'MODIFIER'),
("show_properties_effects", "Effects", 'SHADERFX'),
("show_properties_particles", "Particles", 'PARTICLES'),
("show_properties_physics", "Physics", 'PHYSICS'),
("show_properties_constraints", "Constraints", 'CONSTRAINT'),
("show_properties_data", "Data", 'MESH_DATA'),
("show_properties_bone", "Bone", 'BONE_DATA'),
("show_properties_bone_constraints", "Bone Constraints", 'CONSTRAINT_BONE'),
("show_properties_material", "Material", 'MATERIAL'),
("show_properties_texture", "Texture", 'TEXTURE'),
("show_properties_strip", "Strip", 'SEQ_SEQUENCER'),
("show_properties_strip_modifier", "Strip Modifiers", 'SEQ_STRIP_MODIFIER'),
]
class PROPERTIES_HT_header(Header):
bl_space_type = 'PROPERTIES'
@staticmethod
def _search_poll(space):
return (space.show_properties_tool or
space.show_properties_render or
space.show_properties_output or
space.show_properties_view_layer or
space.show_properties_scene or
space.show_properties_world or
space.show_properties_collection or
space.show_properties_object or
space.show_properties_modifiers or
space.show_properties_effects or
space.show_properties_particles or
space.show_properties_physics or
space.show_properties_constraints or
space.show_properties_data or
space.show_properties_bone or
space.show_properties_bone_constraints or
space.show_properties_material or
space.show_properties_texture or
space.show_properties_strip or
space.show_properties_strip_modifier
)
return any(getattr(space, tab_info[0]) for tab_info in tab_list)
def draw(self, context):
layout = self.layout
@@ -64,6 +68,10 @@ class PROPERTIES_HT_header(Header):
layout.popover(panel="PROPERTIES_PT_options", text="")
def has_hidden_tabs(space):
return not all(getattr(space, tab_info[0]) for tab_info in tab_list)
class PROPERTIES_PT_navigation_bar(Panel):
bl_space_type = 'PROPERTIES'
bl_region_type = 'NAVIGATION_BAR'
@@ -85,6 +93,17 @@ class PROPERTIES_PT_navigation_bar(Panel):
else:
layout.prop_tabs_enum(view, "context", icon_only=True)
# Scale sub layout to make the popover button smaller and use separator to
# offset it, such that it is centered.
sub = layout.row(align=True)
sub.alignment = 'CENTER'
sub.emboss = 'NONE'
sub.scale_x = 0.8
sub.scale_y = 0.8
sub.separator(factor=0.7)
sub.popover(panel="PROPERTIES_PT_visibility", text="")
sub.active = has_hidden_tabs(view)
class PROPERTIES_PT_options(Panel):
"""Show options for the properties editor"""
@@ -101,40 +120,6 @@ class PROPERTIES_PT_options(Panel):
col.label(text="Sync with Outliner")
col.row().prop(space, "outliner_sync", expand=True)
layout.separator()
layout.use_property_decorate = False
visible_tabs = [
("show_properties_tool", "Tool", 'TOOL_SETTINGS'),
("show_properties_render", "Render", 'SCENE'),
("show_properties_output", "Output", 'OUTPUT'),
("show_properties_view_layer", "View Layer", 'RENDERLAYERS'),
("show_properties_scene", "Scene", 'SCENE_DATA'),
("show_properties_world", "World", 'WORLD'),
("show_properties_collection", "Collection", 'GROUP'),
("show_properties_object", "Object", 'OBJECT_DATA'),
("show_properties_modifiers", "Modifiers", 'MODIFIER'),
("show_properties_effects", "Effects", 'SHADERFX'),
("show_properties_particles", "Particles", 'PARTICLES'),
("show_properties_physics", "Physics", 'PHYSICS'),
("show_properties_constraints", "Constraints", 'CONSTRAINT'),
("show_properties_data", "Data", 'MESH_DATA'),
("show_properties_bone", "Bone", 'BONE_DATA'),
("show_properties_bone_constraints", "Bone Constraints", 'CONSTRAINT_BONE'),
("show_properties_material", "Material", 'MATERIAL'),
("show_properties_texture", "Texture", 'TEXTURE'),
("show_properties_strip", "Strip", 'SEQ_SEQUENCER'),
("show_properties_strip_modifier", "Strip Modifiers", 'SEQ_STRIP_MODIFIER')
]
col = layout.column(align=True)
col.label(text="Visible Tabs")
for prop, name, icon in visible_tabs:
row = col.row(align=True)
row.label(text=iface_(name), icon=icon)
row.prop(space, prop, text="")
class PropertiesAnimationMixin:
"""Mix-in class for Animation panels.
@@ -188,10 +173,30 @@ class PropertiesAnimationMixin:
anim.draw_action_and_slot_selector_for_id(layout, animated_id)
class PROPERTIES_PT_visibility(Panel):
"""Choose visibility of tabs in the properties editor"""
bl_space_type = 'PROPERTIES'
bl_region_type = 'HEADER'
bl_label = "Visibility"
def draw(self, context):
space = context.space_data
layout = self.layout
layout.use_property_decorate = False
col = layout.column(align=True)
col.label(text="Visible Tabs")
for prop, name, icon in tab_list:
row = col.row(align=True)
row.label(text=iface_(name), icon=icon)
row.prop(space, prop, text="")
classes = (
PROPERTIES_HT_header,
PROPERTIES_PT_navigation_bar,
PROPERTIES_PT_options,
PROPERTIES_PT_visibility,
)
if __name__ == "__main__": # only for live edit.

View File

@@ -3049,6 +3049,12 @@ void uiLayout::popover(const bContext *C,
uiBut *but = ui_item_menu(
layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true);
but->type = ButType::Popover;
/* Override button size when there is no icon or label. */
if (layout->root()->type == blender::ui::LayoutType::VerticalBar && !icon && name.is_empty()) {
but->rect.xmax = but->rect.xmin + UI_UNIT_X;
}
if (!ok) {
but->flag |= UI_BUT_DISABLED;
}

View File

@@ -124,6 +124,8 @@ struct uiWidgetStateInfo {
int but_flag;
/** Copy of #uiBut.drawflag (possibly with overrides for drawing). */
int but_drawflag;
/** Copy of #uiBut.emboss. */
blender::ui::EmbossType emboss;
/** Show that holding the button opens a menu. */
bool has_hold_action : 1;
@@ -3709,7 +3711,7 @@ static void widget_numbut(uiBut *but,
static void widget_menubut(uiWidgetColors *wcol,
rcti *rect,
const uiWidgetStateInfo * /*state*/,
const uiWidgetStateInfo *state,
int roundboxalign,
const float zoom)
{
@@ -3724,6 +3726,12 @@ static void widget_menubut(uiWidgetColors *wcol,
/* copy size and center to 2nd tria */
wtb.tria2 = wtb.tria1;
if (ELEM(state->emboss, blender::ui::EmbossType::NoneOrStatus, blender::ui::EmbossType::None)) {
wtb.draw_inner = false;
wtb.draw_outline = false;
wtb.draw_emboss = false;
}
widgetbase_draw(&wtb, wcol);
/* text space, arrows are about 0.6 height of button */
@@ -5019,6 +5027,22 @@ static int widget_roundbox_set(uiBut *but, rcti *rect)
return roundbox;
}
static uiWidgetType *popover_widget_type(uiBut *but, rcti *rect)
{
/* We could use a flag for this, but for now just check size,
* add up/down arrows if there is room. */
if ((but->str.empty() && but->icon && (BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect) + 2)) ||
/* disable for brushes also */
(but->flag & UI_BUT_ICON_PREVIEW))
{
/* No arrows. */
return widget_type(UI_WTYPE_MENU_ICON_RADIO);
}
/* With menu arrows. */
return widget_type(UI_WTYPE_MENU_RADIO);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -5068,6 +5092,9 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but,
case ButType::PreviewTile:
wt = widget_type(UI_WTYPE_PREVIEW_TILE);
break;
case ButType::Popover:
wt = popover_widget_type(but, rect);
break;
case ButType::NodeSocket:
wt = widget_type(UI_WTYPE_NODESOCKET);
break;
@@ -5184,21 +5211,8 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but,
wt = widget_type(UI_WTYPE_MENU_NODE_LINK);
}
else {
/* with menu arrows */
/* We could use a flag for this, but for now just check size,
* add up/down arrows if there is room. */
if ((but->str.empty() && but->icon &&
(BLI_rcti_size_x(rect) < BLI_rcti_size_y(rect) + 2)) ||
/* disable for brushes also */
(but->flag & UI_BUT_ICON_PREVIEW))
{
/* no arrows */
wt = widget_type(UI_WTYPE_MENU_ICON_RADIO);
}
else {
wt = widget_type(UI_WTYPE_MENU_RADIO);
}
/* Popover button. */
wt = popover_widget_type(but, rect);
}
break;
@@ -5322,6 +5336,7 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but,
uiWidgetStateInfo state = {0};
state.but_flag = but->flag;
state.but_drawflag = but->drawflag;
state.emboss = but->emboss;
/* Override selected flag for drawing. */
if (but->flag & UI_SELECT_DRAW) {