diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 6 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_button_group.c | 13 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 66 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_panel.c | 178 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 20 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_screen_types.h | 5 |
7 files changed, 191 insertions, 108 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 972c741f061..cac18d81f56 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1683,14 +1683,15 @@ struct Panel *UI_panel_begin(struct ARegion *region, struct PanelType *pt, struct Panel *panel, bool *r_open); -void UI_panel_end(const struct ARegion *region, uiBlock *block, int width, int height, bool open); +void UI_panel_header_buttons_begin(struct Panel *panel); +void UI_panel_header_buttons_end(struct Panel *panel); +void UI_panel_end(struct Panel *panel, int width, int height); bool UI_panel_is_active(const struct Panel *panel); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *panel); bool UI_panel_is_dragging(const struct Panel *panel); bool UI_panel_matches_search_filter(const struct Panel *panel); -void UI_panels_set_expansion_from_seach_filter(const struct bContext *C, struct ARegion *region); bool UI_panel_category_is_visible(const struct ARegion *region); void UI_panel_category_add(struct ARegion *region, const char *name); @@ -1922,7 +1923,6 @@ float uiLayoutGetUnitsY(uiLayout *layout); int uiLayoutGetEmboss(uiLayout *layout); bool uiLayoutGetPropSep(uiLayout *layout); bool uiLayoutGetPropDecorate(uiLayout *layout); -void uiLayoutRootSetSearchOnly(uiLayout *layout, bool search_only); /* layout specifiers */ uiLayout *uiLayoutRow(uiLayout *layout, bool align); diff --git a/source/blender/editors/interface/interface_button_group.c b/source/blender/editors/interface/interface_button_group.c index 455a3c6a69c..1f544831982 100644 --- a/source/blender/editors/interface/interface_button_group.c +++ b/source/blender/editors/interface/interface_button_group.c @@ -32,17 +32,26 @@ * Every function that adds a set of buttons must create another group, * then #ui_def_but adds buttons to the current group (the last). */ -void ui_block_new_button_group(uiBlock *block) +void ui_block_new_button_group(uiBlock *block, short flag) { + /* Don't create a new group if there is a "lock" on new groups. */ + if (!BLI_listbase_is_empty(&block->button_groups)) { + uiButtonGroup *last_button_group = block->button_groups.last; + if (last_button_group->flag & UI_BUTTON_GROUP_LOCK) { + return; + } + } + uiButtonGroup *new_group = MEM_mallocN(sizeof(uiButtonGroup), __func__); BLI_listbase_clear(&new_group->buttons); + new_group->flag = flag; BLI_addtail(&block->button_groups, new_group); } void ui_button_group_add_but(uiBlock *block, uiBut *but) { if (BLI_listbase_is_empty(&block->button_groups)) { - ui_block_new_button_group(block); + ui_block_new_button_group(block, 0); } uiButtonGroup *current_button_group = block->button_groups.last; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5a5e962e2bc..dc1f6cfce50 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -426,8 +426,17 @@ enum eBlockContentHints { typedef struct uiButtonGroup { void *next, *prev; ListBase buttons; /* #LinkData with #uiBut data field. */ + short flag; } uiButtonGroup; +/* #uiButtonGroup.flag. */ +typedef enum uiButtonGroupFlag { + /** While this flag is set, don't create new button groups for layout item calls. */ + UI_BUTTON_GROUP_LOCK = (1 << 0), + /** The buttons in this group are inside a panel header. */ + UI_BUTTON_GROUP_PANEL_HEADER = (1 << 1), +} uiButtonGroupFlag; + struct uiBlock { uiBlock *next, *prev; @@ -1023,7 +1032,7 @@ void ui_item_menutype_func(struct bContext *C, struct uiLayout *layout, void *ar void ui_item_paneltype_func(struct bContext *C, struct uiLayout *layout, void *arg_pt); /* interface_button_group.c */ -void ui_block_new_button_group(uiBlock *block); +void ui_block_new_button_group(uiBlock *block, short flag); void ui_button_group_add_but(uiBlock *block, uiBut *but); void ui_button_group_replace_but_ptr(uiBlock *block, const void *old_but_ptr, uiBut *new_but); void ui_block_free_button_groups(uiBlock *block); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 4315981afb7..8b59e85ec56 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -84,13 +84,6 @@ typedef struct uiLayoutRoot { int type; int opcontext; - /** - * If true, the root will be removed as part of the property search process. - * Necessary for cases like searching the contents of closed panels, where the - * block-level tag isn't enough, as there might be visible buttons in the header. - */ - bool search_only; - int emw, emh; int padding; @@ -1985,7 +1978,7 @@ void uiItemFullR(uiLayout *layout, #endif /* UI_PROP_DECORATE */ UI_block_layout_set_current(block, layout); - ui_block_new_button_group(block); + ui_block_new_button_group(block, 0); /* retrieve info */ const PropertyType type = RNA_property_type(prop); @@ -2723,7 +2716,7 @@ void uiItemPointerR_prop(uiLayout *layout, { const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0); - ui_block_new_button_group(uiLayoutGetBlock(layout)); + ui_block_new_button_group(uiLayoutGetBlock(layout), 0); const PropertyType type = RNA_property_type(prop); if (!ELEM(type, PROP_POINTER, PROP_STRING, PROP_ENUM)) { @@ -2829,7 +2822,7 @@ static uiBut *ui_item_menu(uiLayout *layout, uiLayout *heading_layout = ui_layout_heading_find(layout); UI_block_layout_set_current(block, layout); - ui_block_new_button_group(block); + ui_block_new_button_group(block, 0); if (!name) { name = ""; @@ -3095,7 +3088,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) uiBlock *block = layout->root->block; UI_block_layout_set_current(block, layout); - ui_block_new_button_group(block); + ui_block_new_button_group(block, 0); if (!name) { name = ""; @@ -5015,16 +5008,6 @@ int uiLayoutGetEmboss(uiLayout *layout) return layout->emboss; } -/** - * Tags the layout root as search only, meaning the search process will run, but not the rest of - * the layout process. Use in situations where part of the block's contents normally wouldn't be - * drawn, but must be searched anyway, like the contents of closed panels with headers. - */ -void uiLayoutRootSetSearchOnly(uiLayout *layout, bool search_only) -{ - layout->root->search_only = search_only; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -5045,42 +5028,6 @@ static bool block_search_panel_label_matches(const uiBlock *block, const char *s } /** - * Buttons for search only layouts (closed panel sub-panels) have still been added from the - * layout functions, but they need to be hidden. Theoretically they could be removed too. - */ -static void layout_free_and_hide_buttons(uiLayout *layout) -{ - LISTBASE_FOREACH_MUTABLE (uiItem *, item, &layout->items) { - if (item->type == ITEM_BUTTON) { - uiButtonItem *button_item = (uiButtonItem *)item; - BLI_assert(button_item->but != NULL); - button_item->but->flag |= UI_HIDDEN; - MEM_freeN(item); - } - else { - layout_free_and_hide_buttons((uiLayout *)item); - } - } - - MEM_freeN(layout); -} - -/** - * Remove layouts used only for search and hide their buttons. - * (See comment for #uiLayoutRootSetSearchOnly and in #uiLayoutRoot). - */ -static void block_search_remove_search_only_roots(uiBlock *block) -{ - LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) { - if (root->search_only) { - layout_free_and_hide_buttons(root->layout); - BLI_remlink(&block->layouts, root); - MEM_freeN(root); - } - } -} - -/** * Returns true if a button or the data / operator it represents matches the search filter. */ static bool button_matches_search_filter(uiBut *but, const char *search_filter) @@ -5198,8 +5145,6 @@ bool UI_block_apply_search_filter(uiBlock *block, const char *search_filter) true : block_search_filter_tag_buttons(block, search_filter); - block_search_remove_search_only_roots(block); - if (block->panel != NULL) { if (has_result) { ui_panel_tag_search_filter_match(block->panel); @@ -5642,9 +5587,6 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y) block->curlayout = NULL; LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) { - /* Search only roots should be removed by #UI_block_apply_search_filter. */ - BLI_assert(!root->search_only); - ui_layout_add_padding_button(root); /* NULL in advance so we don't interfere when adding button */ diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 974e2c62ab2..f50975760f1 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -193,10 +193,11 @@ static bool panel_active_animation_changed(ListBase *lb, return false; } -static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_animation) +/** + * \return True if the properties editor switch tabs since the last layout pass. + */ +static bool properties_space_needs_realign(ScrArea *area, ARegion *region) { - *r_panel_animation = NULL; - if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) { SpaceProperties *sbuts = area->spacedata.first; @@ -205,6 +206,17 @@ static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_ } } + return false; +} + +static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_animation) +{ + *r_panel_animation = NULL; + + if (properties_space_needs_realign(area, region)) { + return true; + } + /* Detect if a panel was added or removed. */ Panel *panel_animation = NULL; bool no_animation = false; @@ -678,6 +690,8 @@ Panel *UI_panel_begin( panel->type = pt; } + panel->runtime.block = block; + /* Do not allow closed panels without headers! Else user could get "disappeared" UI! */ if ((pt->flag & PNL_NO_HEADER) && (panel->flag & PNL_CLOSED)) { panel->flag &= ~PNL_CLOSED; @@ -732,6 +746,41 @@ Panel *UI_panel_begin( return panel; } +/** + * Create the panel header button group, used to mark which buttons are part of + * panel headers for later panel search handling. Should be called before adding + * buttons for the panel's header layout. + */ +void UI_panel_header_buttons_begin(Panel *panel) +{ + uiBlock *block = panel->runtime.block; + + ui_block_new_button_group(block, UI_BUTTON_GROUP_LOCK | UI_BUTTON_GROUP_PANEL_HEADER); +} + +/** + * Allow new button groups to be created after the header group. + */ +void UI_panel_header_buttons_end(Panel *panel) +{ + uiBlock *block = panel->runtime.block; + + /* There should always be the button group created in #UI_panel_header_buttons_begin. */ + BLI_assert(!BLI_listbase_is_empty(&block->button_groups)); + + uiButtonGroup *button_group = block->button_groups.last; + + /* Repurpose the first "header" button group if it is empty, in case the first button added to + * the panel doesn't add a new group (if the button is created directly rather than through an + * interface layout call). */ + if (BLI_listbase_count(&block->button_groups) == 1 && + BLI_listbase_is_empty(&button_group->buttons)) { + button_group->flag &= ~UI_BUTTON_GROUP_PANEL_HEADER; + } + + button_group->flag &= ~UI_BUTTON_GROUP_LOCK; +} + static float panel_region_offset_x_get(const ARegion *region) { if (UI_panel_category_is_visible(region)) { @@ -740,22 +789,23 @@ static float panel_region_offset_x_get(const ARegion *region) } } - return 0; + return 0.0f; } -void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, bool open) +/** + * Starting from the "block size" set in #UI_panel_end, calculate the full size + * of the panel including the subpanel headers and buttons. + */ +static void panel_calculate_size_recursive(ARegion *region, Panel *panel) { - Panel *panel = block->panel; - - /* Set panel size excluding children. */ - panel->blocksizex = width; - panel->blocksizey = height; + int width = panel->blocksizex; + int height = panel->blocksizey; - /* Compute total panel size including children. */ - LISTBASE_FOREACH (Panel *, pachild, &panel->children) { - if (pachild->runtime_flag & PANEL_ACTIVE) { - width = max_ii(width, pachild->sizex); - height += get_panel_real_size_y(pachild); + LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { + if (child_panel->runtime_flag & PANEL_ACTIVE) { + panel_calculate_size_recursive(region, child_panel); + width = max_ii(width, child_panel->sizex); + height += get_panel_real_size_y(child_panel); } } @@ -773,7 +823,7 @@ void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, if (width != 0) { panel->sizex = width; } - if (height != 0 || open) { + if (height != 0 || !(panel->flag & PNL_CLOSED)) { panel->sizey = height; } @@ -790,6 +840,14 @@ void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, } } +void UI_panel_end(Panel *panel, int width, int height) +{ + /* Store the size of the buttons layout in the panel. The actual panel size + * (including subpanels) is calculated in #UI_panels_end. */ + panel->blocksizex = width; + panel->blocksizey = height; +} + static void ui_offset_panel_block(uiBlock *block) { const uiStyle *style = UI_style_get_dpi(); @@ -840,12 +898,14 @@ bool UI_panel_matches_search_filter(const Panel *panel) /** * Expands a panel if it was tagged as having a result by property search, otherwise collapses it. */ -static void panel_set_expansion_from_seach_filter_recursive(const bContext *C, Panel *panel) +static void panel_set_expansion_from_seach_filter_recursive(const bContext *C, + Panel *panel, + const bool use_animation) { if (!(panel->type->flag & PNL_NO_HEADER)) { short start_flag = panel->flag; SET_FLAG_FROM_TEST(panel->flag, !UI_panel_matches_search_filter(panel), PNL_CLOSED); - if (start_flag != panel->flag) { + if (use_animation && start_flag != panel->flag) { panel_activate_state(C, panel, PANEL_STATE_ANIMATION); } } @@ -853,7 +913,7 @@ static void panel_set_expansion_from_seach_filter_recursive(const bContext *C, P /* If the panel is filtered (removed) we need to check that its children are too. */ LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { if (child_panel->runtime_flag & PANEL_ACTIVE) { - panel_set_expansion_from_seach_filter_recursive(C, child_panel); + panel_set_expansion_from_seach_filter_recursive(C, child_panel, use_animation); } } } @@ -862,11 +922,63 @@ static void panel_set_expansion_from_seach_filter_recursive(const bContext *C, P * Uses the panel's search filter flag to set its expansion, activating animation if it was closed * or opened. Note that this can't be set too often, or manual interaction becomes impossible. */ -void UI_panels_set_expansion_from_seach_filter(const bContext *C, ARegion *region) +static void region_panels_set_expansion_from_seach_filter(const bContext *C, + ARegion *region, + const bool use_animation) +{ + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + if (panel->runtime_flag & PANEL_ACTIVE) { + panel_set_expansion_from_seach_filter_recursive(C, panel, use_animation); + } + } + set_panels_list_data_expand_flag(C, region); +} + +/** + * Hide buttons in invisible layouts, which are created because in order to search, + * buttons must be added for all panels, even panels that will end up closed. + */ +static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel) +{ + uiBlock *block = panel->runtime.block; + BLI_assert(block != NULL); + BLI_assert(block->active); + if (parent_panel != NULL && parent_panel->flag & PNL_CLOSED) { + /* The parent panel is closed, so this panel can be completely removed. */ + UI_block_set_search_only(block, true); + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + but->flag |= UI_HIDDEN; + } + } + else if (panel->flag & PNL_CLOSED) { + /* If subpanels have no search results but the parent panel does, then the parent panel open + * and the subpanels will close. In that case there must be a way to hide the buttons in the + * panel but keep the header buttons. */ + LISTBASE_FOREACH (uiButtonGroup *, button_group, &block->button_groups) { + if (button_group->flag & UI_BUTTON_GROUP_PANEL_HEADER) { + continue; + } + LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) { + uiBut *but = link->data; + but->flag |= UI_HIDDEN; + } + } + } + + LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { + if (child_panel->runtime_flag & PANEL_ACTIVE) { + BLI_assert(child_panel->runtime.block != NULL); + panel_remove_invisible_layouts_recursive(child_panel, panel); + } + } +} + +static void region_panels_remove_invisible_layouts(ARegion *region) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PANEL_ACTIVE) { - panel_set_expansion_from_seach_filter_recursive(C, panel); + BLI_assert(panel->runtime.block != NULL); + panel_remove_invisible_layouts_recursive(panel, NULL); } } } @@ -1933,12 +2045,24 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) region_panels_set_expansion_from_list_data(C, region); - /* Update panel expansion based on property search results. */ - if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) { - /* Don't use the last update from the deactivation, or all the panels will be left closed. */ - if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) { - UI_panels_set_expansion_from_seach_filter(C, region); - set_panels_list_data_expand_flag(C, region); + if (region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) { + /* Update panel expansion based on property search results. Keep this inside the check + * for an active search filter, or all panels will be left closed when a search ends. */ + if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE) { + region_panels_set_expansion_from_seach_filter(C, region, true); + } + else if (properties_space_needs_realign(area, region)) { + region_panels_set_expansion_from_seach_filter(C, region, false); + } + + /* Clean up the extra panels and buttons created for searching. */ + region_panels_remove_invisible_layouts(region); + } + + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + if (panel->runtime_flag & PANEL_ACTIVE) { + BLI_assert(panel->runtime.block != NULL); + panel_calculate_size_recursive(region, panel); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 7b41b1df0ab..6fa9d203bba 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2610,8 +2610,7 @@ static void ed_panel_draw(const bContext *C, int w, int em, char *unique_panel_str, - const char *search_filter, - bool search_only) + const char *search_filter) { const uiStyle *style = UI_style_get_dpi(); @@ -2624,7 +2623,6 @@ static void ed_panel_draw(const bContext *C, strncat(block_name, unique_panel_str, INSTANCED_PANEL_UNIQUE_STR_LEN); } uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS); - UI_block_set_search_only(block, search_only); bool open; panel = UI_panel_begin(region, lb, block, pt, panel, &open); @@ -2635,6 +2633,7 @@ static void ed_panel_draw(const bContext *C, int xco, yco, h = 0; int headerend = w - UI_UNIT_X; + UI_panel_header_buttons_begin(panel); if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER)) { /* for preset menu */ panel->layout = UI_block_layout(block, @@ -2646,7 +2645,6 @@ static void ed_panel_draw(const bContext *C, 1, 0, style); - uiLayoutRootSetSearchOnly(panel->layout, search_only); pt->draw_header_preset(C, panel); @@ -2678,7 +2676,6 @@ static void ed_panel_draw(const bContext *C, panel->layout = UI_block_layout( block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, labelx, labely, UI_UNIT_Y, 1, 0, style); } - uiLayoutRootSetSearchOnly(panel->layout, search_only); pt->draw_header(C, panel); @@ -2690,6 +2687,7 @@ static void ed_panel_draw(const bContext *C, else { panel->labelofs = 0; } + UI_panel_header_buttons_end(panel); if (open || search_filter_active) { short panelContext; @@ -2715,7 +2713,6 @@ static void ed_panel_draw(const bContext *C, em, 0, style); - uiLayoutRootSetSearchOnly(panel->layout, search_only || !open); pt->draw(C, panel); @@ -2745,13 +2742,12 @@ static void ed_panel_draw(const bContext *C, w, em, unique_panel_str, - search_filter, - !open); + search_filter); } } } - UI_panel_end(region, block, w, h, open); + UI_panel_end(panel, w, h); } /** @@ -2921,8 +2917,7 @@ void ED_region_panels_layout_ex(const bContext *C, (pt->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, NULL, - search_filter, - false); + search_filter); } /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */ @@ -2956,8 +2951,7 @@ void ED_region_panels_layout_ex(const bContext *C, (panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, unique_panel_str, - search_filter, - false); + search_filter); } } diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index cfc9751c9a5..f0ff02d3668 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -38,6 +38,7 @@ struct Scene; struct SpaceLink; struct SpaceType; struct uiLayout; +struct uiBlock; struct wmDrawBuffer; struct wmTimer; struct wmTooltipState; @@ -143,6 +144,10 @@ typedef struct Panel_Runtime { * This avoids freeing the same pointer twice when panels are removed. */ struct PointerRNA *custom_data_ptr; + + /* Pointer to the panel's block. Useful when changes to panel #uiBlocks + * need some context from traversal of the panel "tree". */ + struct uiBlock *block; } Panel_Runtime; /** The part from uiBlock that needs saved in file. */ |