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:
Diffstat (limited to 'source/blender/editors/interface')
-rw-r--r--source/blender/editors/interface/interface.c27
-rw-r--r--source/blender/editors/interface/interface_intern.h15
-rw-r--r--source/blender/editors/interface/interface_layout.c215
-rw-r--r--source/blender/editors/interface/interface_panel.c121
4 files changed, 355 insertions, 23 deletions
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, &region->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, &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);
}
}