diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 4 | ||||
-rw-r--r-- | source/blender/editors/include/ED_screen.h | 4 | ||||
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 11 | ||||
-rw-r--r-- | source/blender/editors/interface/interface.c | 27 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_intern.h | 15 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 215 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_panel.c | 121 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 82 | ||||
-rw-r--r-- | source/blender/editors/space_buttons/space_buttons.c | 13 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_screen_types.h | 8 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_space_types.h | 10 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_space.c | 20 |
12 files changed, 500 insertions, 30 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 38dc63af1fe..1a9abe99012 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4924,6 +4924,9 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa BLO_read_list(reader, ®ion->ui_lists); + /* The area's search filter is runtime only, so we need to clear the active flag on read. */ + region->flag &= ~RGN_FLAG_SEARCH_FILTER_ACTIVE; + LISTBASE_FOREACH (uiList *, ui_list, ®ion->ui_lists) { ui_list->type = NULL; ui_list->dyn_data = NULL; @@ -5164,6 +5167,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sbuts->texuser = NULL; sbuts->mainbo = sbuts->mainb; sbuts->mainbuser = sbuts->mainb; + sbuts->runtime = NULL; } else if (sl->spacetype == SPACE_CONSOLE) { SpaceConsole *sconsole = (SpaceConsole *)sl; diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index d9c7128c2ee..53554e3f34c 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -80,6 +80,10 @@ void ED_region_tag_redraw_no_rebuild(struct ARegion *region); void ED_region_tag_refresh_ui(struct ARegion *region); void ED_region_tag_redraw_editor_overlays(struct ARegion *region); +void ED_region_search_filter_update(const struct ScrArea *area, struct ARegion *region); +const char *ED_area_region_search_filter_get(const struct ScrArea *area, + const struct ARegion *region); + void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *region); void ED_region_panels_ex(const struct bContext *C, struct ARegion *region, const char *contexts[]); void ED_region_panels(const struct bContext *C, struct ARegion *region); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index d5d489b1742..6a0b643fb84 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -157,6 +157,9 @@ enum { UI_BLOCK_POPOVER_ONCE = 1 << 22, /** Always show keymaps, even for non-menus. */ UI_BLOCK_SHOW_SHORTCUT_ALWAYS = 1 << 23, + /** The block is only used during the search process and will not be drawn. + * Currently just for the case of a closed panel's subpanel (and its subpanels). */ + UI_BLOCK_SEARCH_ONLY = 1 << 25, }; /** #uiPopupBlockHandle.menuretval */ @@ -671,6 +674,9 @@ enum { void UI_block_theme_style_set(uiBlock *block, char theme_style); char UI_block_emboss_get(uiBlock *block); void UI_block_emboss_set(uiBlock *block, char emboss); +bool UI_block_is_search_only(const uiBlock *block); +void UI_block_set_search_only(uiBlock *block, bool search_only); +void UI_block_set_search_filter(uiBlock *block, const char *search_filter); void UI_block_free(const struct bContext *C, uiBlock *block); void UI_blocklist_free(const struct bContext *C, struct ListBase *lb); @@ -1684,6 +1690,8 @@ void UI_panels_scale(struct ARegion *region, float new_width); 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); @@ -1862,6 +1870,8 @@ uiLayout *UI_block_layout(uiBlock *block, void UI_block_layout_set_current(uiBlock *block, uiLayout *layout); void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y); +bool UI_block_apply_search_filter(uiBlock *block); + void UI_region_message_subscribe(struct ARegion *region, struct wmMsgBus *mbus); uiBlock *uiLayoutGetBlock(uiLayout *layout); @@ -1914,6 +1924,7 @@ 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.c b/source/blender/editors/interface/interface.c index b9a001a9571..5d20846c528 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1995,8 +1995,12 @@ void UI_block_draw(const bContext *C, uiBlock *block) } } } - ui_draw_aligned_panel( - &style, block, &rect, UI_panel_category_is_visible(region), show_background); + ui_draw_aligned_panel(&style, + block, + &rect, + UI_panel_category_is_visible(region), + show_background, + region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE); } BLF_batch_draw_begin(); @@ -3541,6 +3545,25 @@ void UI_block_theme_style_set(uiBlock *block, char theme_style) block->theme_style = theme_style; } +bool UI_block_is_search_only(const uiBlock *block) +{ + return block->flag & UI_BLOCK_SEARCH_ONLY; +} + +/** + * Use when a block must be searched to give accurate results + * for the whole region but shouldn't be displayed. + */ +void UI_block_set_search_only(uiBlock *block, bool search_only) +{ + SET_FLAG_FROM_TEST(block->flag, search_only, UI_BLOCK_SEARCH_ONLY); +} + +void UI_block_set_search_filter(uiBlock *block, const char *search_filter) +{ + block->search_filter = search_filter; +} + static void ui_but_build_drawstr_float(uiBut *but, double value) { size_t slen = 0; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index edaadd880d7..d62b8a0258a 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -81,6 +81,11 @@ enum { UI_HAS_ICON = (1 << 3), UI_HIDDEN = (1 << 4), UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */ + /** + * The button matches the search filter. When property search is active, this + * is used to determine which items to keep enabled and which to disable. + */ + UI_SEARCH_FILTER_MATCHES = (1 << 12), /* warn: rest of uiBut->flag in UI_interface.h */ }; @@ -517,6 +522,12 @@ struct uiBlock { */ char display_device[64]; + /** + * Pointer to the space's property search string. + * The block doesn't allocate this or change it. + */ + const char *search_filter; + struct PieMenuData pie_data; }; @@ -805,7 +816,9 @@ extern void ui_draw_aligned_panel(const struct uiStyle *style, const uiBlock *block, const rcti *rect, const bool show_pin, - const bool show_background); + const bool show_background, + const bool region_search_filter_active); +void ui_panel_set_search_filter_match(struct Panel *panel, const bool value); /* interface_draw.c */ extern void ui_draw_dropshadow( diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c3bcaff0d0c..38f2a2fb3c0 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -97,6 +97,13 @@ 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; + ListBase button_groups; /* #uiButtonGroup. */ int emw, emh; @@ -5132,6 +5139,208 @@ 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; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Block Layout Search Filtering + * \{ */ + +/* Disabled for performance reasons, but this could be turned on in the future. */ +// #define PROPERTY_SEARCH_USE_TOOLTIPS + +static bool block_search_panel_label_matches(const uiBlock *block) +{ + if ((block->panel != NULL) && (block->panel->type != NULL)) { + if (BLI_strcasestr(block->panel->type->label, block->search_filter)) { + return true; + } + } + return false; +} + +/** + * Buttons for search only layouts (closed panel subpanels) 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); +} + +/* Prototype of function below. */ +static void layout_root_free(uiLayoutRoot *root); + +/** + * 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); + layout_root_free(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) +{ + /* Do the shorter checks first for better performance in case there is a match. */ + if (BLI_strcasestr(but->str, search_filter)) { + return true; + } + + if (but->optype != NULL) { + if (BLI_strcasestr(but->optype->name, search_filter)) { + return true; + } + } + + if (but->rnaprop != NULL) { + if (BLI_strcasestr(RNA_property_ui_name(but->rnaprop), search_filter)) { + return true; + } +#ifdef PROPERTY_SEARCH_USE_TOOLTIPS + if (BLI_strcasestr(RNA_property_description(but->rnaprop), search_filter)) { + return true; + } +#endif + + /* Search through labels of enum property items if they are in a dropdown menu. + * Unfortunately we have no #bContext here so we cannot search through RNA enums + * with dynamic entries (or "itemf" functions) which require context. */ + if (but->type == UI_BTYPE_MENU) { + PointerRNA *ptr = &but->rnapoin; + PropertyRNA *enum_prop = but->rnaprop; + + int items_len; + const EnumPropertyItem *items_array = NULL; + bool free; + RNA_property_enum_items_gettexted(NULL, ptr, enum_prop, &items_array, &items_len, &free); + + if (items_array == NULL) { + return false; + } + + for (int i = 0; i < items_len; i++) { + if (BLI_strcasestr(items_array[i].name, search_filter)) { + return true; + } + } + if (free) { + MEM_freeN((EnumPropertyItem *)items_array); + } + } + } + + return false; +} + +/** + * Test for a search result within a specific button group. + */ +static bool button_group_has_search_match(uiButtonGroup *button_group, const char *search_filter) +{ + LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) { + uiBut *but = link->data; + if (button_matches_search_filter(but, search_filter)) { + return true; + } + } + + return false; +} + +/** + * Apply the search filter, tagging all buttons with whether they match or not. + * Tag every button in the group as a result if any button in the group matches. + * + * \note It would be great to return early here if we found a match, but because + * the results may be visible we have to continue searching the entire block. + * + * \return True if the block has any search results. + */ +static bool block_search_filter_tag_buttons(uiBlock *block) +{ + bool has_result = false; + LISTBASE_FOREACH (uiLayoutRoot *, root, &block->layouts) { + LISTBASE_FOREACH (uiButtonGroup *, button_group, &root->button_groups) { + if (button_group_has_search_match(button_group, block->search_filter)) { + LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) { + uiBut *but = link->data; + but->flag |= UI_SEARCH_FILTER_MATCHES; + } + has_result = true; + } + } + } + return has_result; +} + +static void block_search_deactivate_buttons(uiBlock *block) +{ + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (!(but->flag & UI_SEARCH_FILTER_MATCHES)) { + but->flag |= UI_BUT_INACTIVE; + } + } +} + +/** + * Apply property search behavior, setting panel flags and deactivating buttons that don't match. + * + * \note Must not be run after #UI_block_layout_resolve. + */ +bool UI_block_apply_search_filter(uiBlock *block) +{ + if (!(block->search_filter && block->search_filter[0])) { + return false; + } + + const bool panel_label_matches = block_search_panel_label_matches(block); + + const bool has_result = panel_label_matches ? true : block_search_filter_tag_buttons(block); + + block_search_remove_search_only_roots(block); + + if (block->panel != NULL) { + ui_panel_set_search_filter_match(block->panel, has_result); + } + + if (!panel_label_matches) { + block_search_deactivate_buttons(block); + } + + return has_result; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -5378,8 +5587,6 @@ static void ui_layout_free(uiLayout *layout) static void layout_root_free(uiLayoutRoot *root) { - ui_layout_free(root->layout); - LISTBASE_FOREACH_MUTABLE (uiButtonGroup *, button_group, &root->button_groups) { button_group_free(button_group); } @@ -5575,10 +5782,14 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y) block->curlayout = NULL; LISTBASE_FOREACH_MUTABLE (uiLayoutRoot *, root, &block->layouts) { + /* Seach 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 */ ui_layout_end(block, root->layout, r_x, r_y); + ui_layout_free(root->layout); layout_root_free(root); } diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 6e2229ce3e3..c11e4397b4c 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -77,6 +77,7 @@ #define PNL_ANIM_ALIGN 8 #define PNL_NEW_ADDED 16 #define PNL_FIRST 32 +#define PNL_SEARCH_FILTER_MATCHES 64 /* the state of the mouse position relative to the panel */ typedef enum uiPanelMouseState { @@ -125,16 +126,32 @@ static bool panel_type_context_poll(ARegion *region, /** \name Local Functions * \{ */ -static void panel_title_color_get(bool show_background, uchar color[4]) +static void panel_title_color_get(const Panel *panel, + const bool show_background, + const bool use_search_color, + const bool region_search_filter_active, + uchar r_color[4]) { - if (show_background) { - UI_GetThemeColor4ubv(TH_TITLE, color); - } - else { + if (!show_background) { /* Use menu colors for floating panels. */ bTheme *btheme = UI_GetTheme(); const uiWidgetColors *wcol = &btheme->tui.wcol_menu_back; - copy_v4_v4_uchar(color, (const uchar *)wcol->text); + copy_v4_v4_uchar(r_color, (const uchar *)wcol->text); + return; + } + + const bool search_match = UI_panel_matches_search_filter(panel); + + if (region_search_filter_active && use_search_color && search_match) { + UI_GetThemeColor4ubv(TH_MATCH, r_color); + } + else { + UI_GetThemeColor4ubv(TH_TITLE, r_color); + if (region_search_filter_active && !search_match) { + r_color[0] *= 0.5; + r_color[1] *= 0.5; + r_color[2] *= 0.5; + } } } @@ -230,6 +247,7 @@ static Panel *UI_panel_add_instanced_ex(ARegion *region, BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname)); panel->runtime.custom_data_ptr = custom_data; + panel->runtime_flag |= PNL_NEW_ADDED; /* Add the panel's children too. Although they aren't instanced panels, we can still use this * function to create them, as UI_panel_begin does other things we don't need to do. */ @@ -786,6 +804,65 @@ static void ui_offset_panel_block(uiBlock *block) block->rect.xmin = block->rect.ymin = 0.0; } +void ui_panel_set_search_filter_match(struct Panel *panel, const bool value) +{ + SET_FLAG_FROM_TEST(panel->runtime_flag, value, PNL_SEARCH_FILTER_MATCHES); +} + +static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches) +{ + *filter_matches |= panel->runtime_flag & PNL_SEARCH_FILTER_MATCHES; + + /* If the panel has no match we need to make sure that its children are too. */ + if (!*filter_matches) { + LISTBASE_FOREACH (const Panel *, child_panel, &panel->children) { + panel_matches_search_filter_recursive(child_panel, filter_matches); + } + } +} + +/** + * Find whether a panel or any of its subpanels contain a property that matches the search filter, + * depending on the search process running in #UI_block_apply_search_filter earlier. + */ +bool UI_panel_matches_search_filter(const Panel *panel) +{ + bool search_filter_matches = false; + panel_matches_search_filter_recursive(panel, &search_filter_matches); + return search_filter_matches; +} + +static void panel_set_expansion_from_seach_filter_recursive(const bContext *C, Panel *panel) +{ + short start_flag = panel->flag; + SET_FLAG_FROM_TEST(panel->flag, !UI_panel_matches_search_filter(panel), PNL_CLOSED); + if (start_flag != panel->flag) { + panel_activate_state(C, panel, PANEL_STATE_ANIMATION); + } + + /* If the panel is filtered (removed) we need to check that its children are too. */ + LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { + if (panel->type == NULL || (panel->type->flag & PNL_NO_HEADER)) { + continue; + } + panel_set_expansion_from_seach_filter_recursive(C, child_panel); + } +} + +/** + * 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) +{ + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + if (panel->type == NULL || (panel->type->flag & PNL_NO_HEADER)) { + continue; + } + panel_set_expansion_from_seach_filter_recursive(C, panel); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -829,7 +906,8 @@ void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y) static void ui_draw_aligned_panel_header(const uiStyle *style, const uiBlock *block, const rcti *rect, - const bool show_background) + const bool show_background, + const bool region_search_filter_active) { const Panel *panel = block->panel; const bool is_subpanel = (panel->type && panel->type->parent); @@ -840,7 +918,8 @@ static void ui_draw_aligned_panel_header(const uiStyle *style, /* draw text label */ uchar col_title[4]; - panel_title_color_get(show_background, col_title); + panel_title_color_get( + panel, show_background, is_subpanel, region_search_filter_active, col_title); col_title[3] = 255; rcti hrect = *rect; @@ -862,7 +941,8 @@ void ui_draw_aligned_panel(const uiStyle *style, const uiBlock *block, const rcti *rect, const bool show_pin, - const bool show_background) + const bool show_background, + const bool region_search_filter_active) { const Panel *panel = block->panel; float color[4]; @@ -937,7 +1017,7 @@ void ui_draw_aligned_panel(const uiStyle *style, GPU_blend(GPU_BLEND_ALPHA); /* draw with background color */ - immUniformThemeColor(TH_PANEL_HEADER); + immUniformThemeColor(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER); immRectf(pos, minx, headrect.ymin, rect->xmax, y); immBegin(GPU_PRIM_LINES, 4); @@ -957,7 +1037,7 @@ void ui_draw_aligned_panel(const uiStyle *style, /* draw optional pin icon */ if (show_pin && (block->panel->flag & PNL_PIN)) { uchar col_title[4]; - panel_title_color_get(show_background, col_title); + panel_title_color_get(panel, show_background, false, region_search_filter_active, col_title); GPU_blend(GPU_BLEND_ALPHA); UI_icon_draw_ex(headrect.xmax - ((PNL_ICON * 2.2f) / block->aspect), @@ -976,7 +1056,8 @@ void ui_draw_aligned_panel(const uiStyle *style, if (is_subpanel) { titlerect.xmin += (0.7f * UI_UNIT_X) / block->aspect + 0.001f; } - ui_draw_aligned_panel_header(style, block, &titlerect, show_background); + ui_draw_aligned_panel_header( + style, block, &titlerect, show_background, region_search_filter_active); if (show_drag) { /* Make `itemrect` smaller. */ @@ -1071,7 +1152,7 @@ void ui_draw_aligned_panel(const uiStyle *style, BLI_rctf_scale(&itemrect, 0.25f); uchar col_title[4]; - panel_title_color_get(show_background, col_title); + panel_title_color_get(panel, show_background, false, region_search_filter_active, col_title); float tria_color[4]; rgb_uchar_to_float(tria_color, col_title); tria_color[3] = 1.0f; @@ -1853,19 +1934,23 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) ui_panels_size(region, r_x, r_y); } +/** + * Draw panels, selected (panels currently being dragged) on top. + */ void UI_panels_draw(const bContext *C, ARegion *region) { - /* Draw panels, selected on top. Also in reverse order, because - * UI blocks are added in reverse order and we need child panels - * to draw on top. */ + /* Draw in reverse order, because #uiBlocks are added in reverse order + * and we need child panels to draw on top. */ LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { - if (block->active && block->panel && !(block->panel->flag & PNL_SELECT)) { + if (block->active && block->panel && !(block->panel->flag & PNL_SELECT) && + !UI_block_is_search_only(block)) { UI_block_draw(C, block); } } LISTBASE_FOREACH_BACKWARD (uiBlock *, block, ®ion->uiblocks) { - if (block->active && block->panel && (block->panel->flag & PNL_SELECT)) { + if (block->active && block->panel && (block->panel->flag & PNL_SELECT) && + !UI_block_is_search_only(block)) { UI_block_draw(C, block); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e7f5f5646cb..f6c54e87410 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -492,6 +492,9 @@ void ED_region_do_layout(bContext *C, ARegion *region) UI_SetTheme(area ? area->spacetype : 0, at->regionid); at->layout(C, region); + + /* Clear temporary update flag. */ + region->flag &= ~RGN_FLAG_SEARCH_FILTER_UPDATE; } /* only exported for WM */ @@ -736,6 +739,37 @@ void ED_area_tag_refresh(ScrArea *area) /* *************************************************************** */ +/** + * Returns the search string if the space type and region type support property search. + */ +const char *ED_area_region_search_filter_get(const ScrArea *area, const ARegion *region) +{ + /* Only the properties editor has a search string for now. */ + if (area->spacetype == SPACE_PROPERTIES) { + SpaceProperties *sbuts = area->spacedata.first; + if (region->regiontype == RGN_TYPE_WINDOW) { + return sbuts->runtime->search_string; + } + } + + return NULL; +} + +/** + * Set the temporary update flag for property search. + */ +void ED_region_search_filter_update(const ScrArea *area, ARegion *region) +{ + region->flag |= RGN_FLAG_SEARCH_FILTER_UPDATE; + + const char *search_filter = ED_area_region_search_filter_get(area, region); + SET_FLAG_FROM_TEST(region->flag, + region->regiontype == RGN_TYPE_WINDOW && search_filter[0] != '\0', + RGN_FLAG_SEARCH_FILTER_ACTIVE); +} + +/* *************************************************************** */ + /* use NULL to disable it */ void ED_area_status_text(ScrArea *area, const char *str) { @@ -2558,7 +2592,9 @@ static void ed_panel_draw(const bContext *C, Panel *panel, int w, int em, - char *unique_panel_str) + char *unique_panel_str, + const char *search_filter, + bool search_only) { const uiStyle *style = UI_style_get_dpi(); @@ -2571,10 +2607,14 @@ 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_filter(block, search_filter); + UI_block_set_search_only(block, search_only); bool open; panel = UI_panel_begin(region, lb, block, pt, panel, &open); + const bool search_filter_active = search_filter != NULL && search_filter[0] != '\0'; + /* bad fixed values */ int xco, yco, h = 0; int headerend = w - UI_UNIT_X; @@ -2590,9 +2630,11 @@ static void ed_panel_draw(const bContext *C, 1, 0, style); + uiLayoutRootSetSearchOnly(panel->layout, search_only); pt->draw_header_preset(C, panel); + UI_block_apply_search_filter(block); UI_block_layout_resolve(block, &xco, &yco); UI_block_translate(block, headerend - xco, 0); panel->layout = NULL; @@ -2620,9 +2662,11 @@ 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); + UI_block_apply_search_filter(block); UI_block_layout_resolve(block, &xco, &yco); panel->labelofs = xco - labelx; panel->layout = NULL; @@ -2631,7 +2675,7 @@ static void ed_panel_draw(const bContext *C, panel->labelofs = 0; } - if (open) { + if (open || search_filter_active) { short panelContext; /* panel context can either be toolbar region or normal panels region */ @@ -2655,9 +2699,11 @@ static void ed_panel_draw(const bContext *C, em, 0, style); + uiLayoutRootSetSearchOnly(panel->layout, search_only || !open); pt->draw(C, panel); + UI_block_apply_search_filter(block); UI_block_layout_resolve(block, &xco, &yco); panel->layout = NULL; @@ -2669,13 +2715,22 @@ static void ed_panel_draw(const bContext *C, UI_block_end(C, block); /* Draw child panels. */ - if (open) { + if (open || search_filter_active) { LISTBASE_FOREACH (LinkData *, link, &pt->children) { PanelType *child_pt = link->data; Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt); if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) { - ed_panel_draw(C, region, &panel->children, child_pt, child_panel, w, em, unique_panel_str); + ed_panel_draw(C, + region, + &panel->children, + child_pt, + child_panel, + w, + em, + unique_panel_str, + search_filter, + !open); } } } @@ -2815,6 +2870,9 @@ void ED_region_panels_layout_ex(const bContext *C, /* create panels */ UI_panels_begin(C, region); + /* Get search string for property search. */ + const char *search_filter = ED_area_region_search_filter_get(area, region); + /* set view2d view matrix - UI_block_begin() stores it */ UI_view2d_view_ortho(v2d); @@ -2846,7 +2904,9 @@ void ED_region_panels_layout_ex(const bContext *C, panel, (pt->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - NULL); + NULL, + search_filter, + false); } /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */ @@ -2879,7 +2939,17 @@ void ED_region_panels_layout_ex(const bContext *C, panel, (panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - unique_panel_str); + unique_panel_str, + search_filter, + false); + } + } + + /* 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); } } diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 25f8fecbf26..59723fb1926 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -109,20 +109,31 @@ static void buttons_free(SpaceLink *sl) BLI_freelistN(&ct->users); MEM_freeN(ct); } + + MEM_SAFE_FREE(sbuts->runtime); } /* spacetype; init callback */ -static void buttons_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area)) +static void buttons_init(struct wmWindowManager *UNUSED(wm), ScrArea *area) { + SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first; + + if (sbuts->runtime == NULL) { + sbuts->runtime = MEM_mallocN(sizeof(SpaceProperties_Runtime), __func__); + sbuts->runtime->search_string[0] = '\0'; + } } static SpaceLink *buttons_duplicate(SpaceLink *sl) { + SpaceProperties *sfile_old = (SpaceProperties *)sl; SpaceProperties *sbutsn = MEM_dupallocN(sl); /* clear or remove stuff from old */ sbutsn->path = NULL; sbutsn->texuser = NULL; + sbutsn->runtime = MEM_dupallocN(sfile_old->runtime); + sbutsn->runtime->search_string[0] = '\0'; return (SpaceLink *)sbutsn; } diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 93110d148bf..c275def5414 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -687,6 +687,14 @@ enum { /** When the user sets the region is hidden, * needed for floating regions that may be hidden for other reasons. */ RGN_FLAG_HIDDEN_BY_USER = (1 << 7), + /** Property search filter is active. */ + RGN_FLAG_SEARCH_FILTER_ACTIVE = (1 << 8), + /** + * Update the expansion of the region's panels and switch contexts. Only Set + * temporarily when the search filter is updated and cleared at the end of the + * region's layout pass. so that expansion is still interactive, + */ + RGN_FLAG_SEARCH_FILTER_UPDATE = (1 << 9), }; /** #ARegion.do_draw */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 06ab01a9730..22045a97826 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -129,6 +129,13 @@ typedef enum eSpaceInfo_RptMask { /** \name Properties Editor * \{ */ +# +# +typedef struct SpaceProperties_Runtime { + /** For filtering properties displayed in the space. Length defined as UI_MAX_NAME_STR. */ + char search_string[128]; +} SpaceProperties_Runtime; + /* Properties Editor */ typedef struct SpaceProperties { SpaceLink *next, *prev; @@ -159,6 +166,9 @@ typedef struct SpaceProperties { ID *pinid; void *texuser; + + /* Doesn't necessarily need to be a pointer, but runtime structs are still written to files. */ + SpaceProperties_Runtime *runtime; } SpaceProperties; /* button defines (deprecated) */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 317759ce418..d33939724ef 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1817,6 +1817,18 @@ static void rna_SpaceProperties_context_update(Main *UNUSED(bmain), } } +static void rna_SpaceProperties_search_filter_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ScrArea *area = rna_area_from_space(ptr); + + /* Update the search filter flag for the main region with the panels. */ + ARegion *main_region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + BLI_assert(main_region != NULL); + ED_region_search_filter_update(area, main_region); +} + /* Space Console */ static void rna_ConsoleLine_body_get(PointerRNA *ptr, char *value) { @@ -4482,6 +4494,14 @@ static void rna_def_space_properties(BlenderRNA *brna) prop = RNA_def_property(srna, "use_pin_id", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SB_PIN_CONTEXT); RNA_def_property_ui_text(prop, "Pin ID", "Use the pinned context"); + + /* Property search. */ + prop = RNA_def_property(srna, "search_filter", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "runtime->search_string"); + RNA_def_property_ui_text(prop, "Display Filter", "Live search filtering string"); + RNA_def_property_flag(prop, PROP_TEXTEDIT_UPDATE); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_SpaceProperties_search_filter_update"); } static void rna_def_space_image(BlenderRNA *brna) |