Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2020-09-09 21:00:56 +0300
committerHans Goudey <h.goudey@me.com>2020-09-09 21:01:32 +0300
commit58488c08b869a42f81c2acec5b14504a59d5b500 (patch)
tree96b8f45f5f3f70bb11fca73c04b3a029306d344a /source/blender
parent6a76eeaced199de1143a9e6c54e6a7ca29a3bc7f (diff)
Property Search: Single tab
This adds a search bar to the properties editor. The full search for every tab isn't includede in this patch, but the interaction with panels, searching behavior, internal UI, region level, and DNA changes are included here. The block-level search works by iterating over the block's buttons and checking whether they match the search. If they do, they are tagged with a flag, and the block's panel is tagged too. For every update (text edit), the panel's expansion is set to whether the panel has a result or not. There is some complications to this that you might no initially think of: 1. Closed panel's subpanels have to be searched too. This adds some complexity and special cases to the area-level panel layout code. 2. //Maybe more if I think of things to add here// There might be some methods of simplifying some of the logic, particularly around choosing whether to highlight panel headers. Also note that automatic subpanel expansion isn't working right now. I'll look into this, but I want to post all the patches first. **Future Improvements** Here are some improvements possible in the future that won't be part of this patch: 1. Use the new fuzzy search in BLI 2. Reseting panels to their expansion before the search started if you `esc` out of the text box 3. Open parent panels if their subpanels have a match but they don't. This requires adding a reference to parent panels for subpanels. Differential Revision: https://developer.blender.org/D8856
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/editors/include/ED_screen.h3
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/interface.c31
-rw-r--r--source/blender/editors/interface/interface_intern.h8
-rw-r--r--source/blender/editors/interface/interface_layout.c200
-rw-r--r--source/blender/editors/interface/interface_panel.c96
-rw-r--r--source/blender/editors/screen/area.c55
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c1
-rw-r--r--source/blender/makesdna/DNA_screen_types.h8
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_space.c20
11 files changed, 410 insertions, 24 deletions
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index d9c7128c2ee..f4cfb34831d 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -80,6 +80,9 @@ 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 bContext *C, struct ARegion *region);
+char *ED_area_search_filter_get(const struct bContext *C);
+
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 86275ce4d39..58b23d1a4e5 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_has_search_filter(const uiBlock *block);
+bool UI_block_is_search_only(const uiBlock *block);
+void UI_block_set_search_only(uiBlock *block, bool search_only);
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_panel_set_expansion_from_seach_filter(const struct bContext *C, struct Panel *panel);
bool UI_panel_category_is_visible(const struct ARegion *region);
void UI_panel_category_add(struct ARegion *region, const char *name);
@@ -1914,6 +1922,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 0bd4934dd0f..ff05da89993 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1959,8 +1959,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();
@@ -3456,6 +3460,14 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, ch
block->emboss = emboss;
block->evil_C = (void *)C; /* XXX */
+ /* Set the search filter for the properties editor. */
+ ScrArea *area = CTX_wm_area(C);
+ if ((region && region->regiontype == RGN_TYPE_WINDOW) &&
+ (area && area->spacetype == SPACE_PROPERTIES)) {
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ block->search_filter = sbuts->search_string;
+ }
+
if (scn) {
/* store display device name, don't lookup for transformations yet
* block could be used for non-color displays where looking up for transformation
@@ -3505,6 +3517,21 @@ void UI_block_theme_style_set(uiBlock *block, char theme_style)
block->theme_style = theme_style;
}
+bool UI_block_has_search_filter(const uiBlock *block)
+{
+ return block->search_filter != NULL && block->search_filter[0] != '\0';
+}
+
+bool UI_block_is_search_only(const uiBlock *block)
+{
+ return block->flag & UI_BLOCK_SEARCH_ONLY;
+}
+
+void UI_block_set_search_only(uiBlock *block, bool search_only)
+{
+ SET_FLAG_FROM_TEST(block->flag, search_only, UI_BLOCK_SEARCH_ONLY);
+}
+
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 fe0d9fda44b..ff8d5d5b55d 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -81,6 +81,8 @@ 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, later used to deactivate it. */
+ UI_SEARCH_FILTER_MATCHES = (1 << 12),
/* warn: rest of uiBut->flag in UI_interface.h */
};
@@ -525,6 +527,8 @@ struct uiBlock {
*/
char display_device[64];
+ char *search_filter;
+
struct PieMenuData pie_data;
};
@@ -813,7 +817,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 45085288264..4b767b530f0 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -86,6 +86,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 because there might be buttons in the header.
+ */
+ bool search_only;
+
int emw, emh;
int padding;
@@ -5097,6 +5104,193 @@ int uiLayoutGetEmboss(uiLayout *layout)
return layout->emboss;
}
+void uiLayoutRootSetSearchOnly(uiLayout *layout, bool search_only)
+{
+ layout->root->search_only = search_only;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Block Layout Search Filtering
+ * \{ */
+
+// #define PROPERTY_SEARCH_USE_TOOLTIPS
+#define PROPERTY_SEARCH_USE_PANEL_LABELS
+
+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);
+}
+
+/**
+ * Returns true if a button or the data / operator it represents matches the search filter.
+ *
+ * \note It's important to do the shorter checks first for performance.
+ */
+static bool button_matches_search_filter(uiBut *but, const char *search_filter)
+{
+ 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 context 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 *enum_items = NULL;
+ bool free;
+ RNA_property_enum_items(NULL, ptr, enum_prop, &enum_items, &items_len, &free);
+
+ if (enum_items != NULL) {
+ for (int i = 0; i < items_len; i++) {
+ if (BLI_strcasestr(enum_items[i].name, search_filter)) {
+ return true;
+ }
+ }
+ if (free) {
+ MEM_freeN((void *)enum_items);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Tag all buttons with whether they matched the search filter or not.
+ */
+static bool block_search_filter_tag_buttons(uiBlock *block)
+{
+ bool has_result = false;
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ /* First match regular buttons. */
+ if (!ELEM(but->type, UI_BTYPE_LABEL) &&
+ button_matches_search_filter(but, block->search_filter)) {
+ has_result = true;
+ but->flag |= UI_SEARCH_FILTER_MATCHES;
+ }
+ /* Then match their labels. */
+ if (but->label_but != NULL &&
+ button_matches_search_filter(but->label_but, block->search_filter)) {
+ has_result = true;
+ but->flag |= UI_SEARCH_FILTER_MATCHES;
+ }
+ }
+
+ /* Remove filter from labels and decorators that correspond to un-filtered buttons. */
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ if (but->flag & UI_SEARCH_FILTER_MATCHES) {
+ if (but->label_but != NULL) {
+ but->label_but->flag |= UI_SEARCH_FILTER_MATCHES;
+ }
+ if (but->decorator_but != NULL) {
+ but->decorator_but->flag |= UI_SEARCH_FILTER_MATCHES;
+ }
+ }
+ }
+
+ return has_result;
+}
+
+static bool block_search(uiBlock *block)
+{
+ /* Only continue if the block has the search filter set. */
+ if (!(block->search_filter && block->search_filter[0])) {
+ return false;
+ }
+
+ /* Also search based on panel labels. */
+ bool panel_label_matches = false;
+#ifdef PROPERTY_SEARCH_USE_PANEL_LABELS
+ if ((block->panel != NULL) && (block->panel->type != NULL)) {
+ if (BLI_strcasestr(block->panel->type->label, block->search_filter)) {
+ panel_label_matches = true;
+ }
+ }
+#endif
+
+ /* Apply search filter. */
+ bool has_result;
+ if (!panel_label_matches) {
+ has_result = block_search_filter_tag_buttons(block);
+ }
+ else {
+ has_result = true;
+ }
+
+ /* Remove search only layout roots before the next step. */
+ 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);
+ }
+ }
+
+ /* Set empty flags. */
+ if (UI_block_is_search_only(block)) {
+ /* Make sure all of the block's buttons are hidden. They might not have
+ * been hidden if a layout wasn't searched. */
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ but->flag |= UI_HIDDEN;
+ }
+ }
+
+ if (block->panel != NULL) {
+ ui_panel_set_search_filter_match(block->panel, has_result);
+ }
+
+ return panel_label_matches;
+}
+
+static void block_search_deactive_buttons(uiBlock *block)
+{
+ /* Only continue if the block has the search filter set. */
+ if (!(block->search_filter && block->search_filter[0])) {
+ return;
+ }
+
+ LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
+ if (!(but->flag & UI_SEARCH_FILTER_MATCHES)) {
+ but->flag |= UI_BUT_INACTIVE;
+ }
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -5524,6 +5718,8 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
block->curlayout = NULL;
+ const bool search_disabled = block_search(block);
+
LISTBASE_FOREACH (uiLayoutRoot *, root, &block->layouts) {
ui_layout_add_padding_button(root);
@@ -5532,6 +5728,10 @@ void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
ui_layout_free(root->layout);
}
+ if (!search_disabled) {
+ block_search_deactive_buttons(block);
+ }
+
BLI_freelistN(&block->layouts);
/* XXX silly trick, interface_templates.c doesn't get linked
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 5c4877534f6..4e33bc4f8bd 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,33 @@ 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_SEARCH_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 +248,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 +805,46 @@ 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 is filtered (removed) we need to check 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.
+ */
+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;
+}
+
+/**
+ * Uses the panel's search filter flag to set its expansion,
+ * activating animation if it was closed or opened.
+ */
+void UI_panel_set_expansion_from_seach_filter(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);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -829,7 +888,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 +900,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 +923,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 +999,8 @@ 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_SEARCH_MATCH :
+ TH_PANEL_HEADER);
immRectf(pos, minx, headrect.ymin, rect->xmax, y);
immBegin(GPU_PRIM_LINES, 4);
@@ -957,7 +1020,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 +1039,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 +1135,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;
@@ -1859,13 +1923,15 @@ void UI_panels_draw(const bContext *C, ARegion *region)
* UI blocks are added in reverse order and we need child panels
* to draw on top. */
LISTBASE_FOREACH_BACKWARD (uiBlock *, block, &region->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, &region->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 8ec58b4c75e..c145dfd0278 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,28 @@ void ED_area_tag_refresh(ScrArea *area)
/* *************************************************************** */
+/**
+ * Returns the search string if the space type supports property search.
+ */
+char *ED_area_search_filter_get(const bContext *C)
+{
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts != NULL) {
+ return sbuts->search_string;
+ }
+ return NULL;
+}
+
+void ED_region_search_filter_update(const bContext *C, ARegion *region)
+{
+ region->flag |= RGN_FLAG_SEARCH_FILTER_UPDATE;
+
+ const char *search_filter = ED_area_search_filter_get(C);
+ SET_FLAG_FROM_TEST(region->flag, 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 +2583,8 @@ static void ed_panel_draw(const bContext *C,
Panel *panel,
int w,
int em,
- char *unique_panel_str)
+ char *unique_panel_str,
+ bool search_only)
{
const uiStyle *style = UI_style_get_dpi();
@@ -2571,6 +2597,7 @@ 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);
@@ -2590,6 +2617,7 @@ static void ed_panel_draw(const bContext *C,
1,
0,
style);
+ uiLayoutRootSetSearchOnly(panel->layout, search_only);
pt->draw_header_preset(C, panel);
@@ -2620,6 +2648,7 @@ 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);
@@ -2631,7 +2660,7 @@ static void ed_panel_draw(const bContext *C,
panel->labelofs = 0;
}
- if (open) {
+ if (open || UI_block_has_search_filter(block) || search_only) {
short panelContext;
/* panel context can either be toolbar region or normal panels region */
@@ -2655,6 +2684,7 @@ static void ed_panel_draw(const bContext *C,
em,
0,
style);
+ uiLayoutRootSetSearchOnly(panel->layout, search_only || !open);
pt->draw(C, panel);
@@ -2669,13 +2699,14 @@ static void ed_panel_draw(const bContext *C,
UI_block_end(C, block);
/* Draw child panels. */
- if (open) {
+ if (open || UI_block_has_search_filter(block)) {
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, !open);
}
}
}
@@ -2813,7 +2844,8 @@ void ED_region_panels_layout_ex(const bContext *C,
panel,
(pt->flag & PNL_DRAW_BOX) ? w_box_panel : w,
em,
- NULL);
+ NULL,
+ false);
}
/* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */
@@ -2846,7 +2878,18 @@ 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,
+ false);
+ }
+ }
+
+ if (region->flag & RGN_FLAG_SEARCH_FILTER_UPDATE &&
+ region->flag & RGN_FLAG_SEARCH_FILTER_ACTIVE) {
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ if (panel->type == NULL || (panel->type->flag & PNL_NO_HEADER)) {
+ continue; /* Some panels don't have a type. */
+ }
+ UI_panel_set_expansion_from_seach_filter(C, panel);
}
}
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 20d81ff6da1..609d53ccc55 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -123,6 +123,7 @@ static SpaceLink *buttons_duplicate(SpaceLink *sl)
/* clear or remove stuff from old */
sbutsn->path = NULL;
sbutsn->texuser = NULL;
+ strcpy(sbutsn->search_string, "");
return (SpaceLink *)sbutsn;
}
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 93110d148bf..43b836ca86b 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 so that interactions are still
+ * interactive, cleared at the end of the region's layout pass.
+ */
+ 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 6fe6a5461e1..c4706cace07 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -158,6 +158,9 @@ typedef struct SpaceProperties {
int pathflag, dataicon;
ID *pinid;
+ /** For filtering properties displayed in the space. */
+ char search_string[64];
+
void *texuser;
} SpaceProperties;
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 34a84ce9265..b8615d22753 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1817,6 +1817,17 @@ static void rna_SpaceProperties_context_update(Main *UNUSED(bmain),
}
}
+static void rna_SpaceProperties_search_filter_update(struct bContext *C,
+ struct PointerRNA *UNUSED(ptr))
+{
+ ScrArea *area = CTX_wm_area(C);
+
+ /* 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(C, main_region);
+}
+
/* Space Console */
static void rna_ConsoleLine_body_get(PointerRNA *ptr, char *value)
{
@@ -4471,6 +4482,15 @@ 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, "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_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_update(
+ prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_SpaceProperties_search_filter_update");
}
static void rna_def_space_image(BlenderRNA *brna)