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:
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/interface/interface_button_group.c13
-rw-r--r--source/blender/editors/interface/interface_intern.h11
-rw-r--r--source/blender/editors/interface/interface_layout.c66
-rw-r--r--source/blender/editors/interface/interface_panel.c178
-rw-r--r--source/blender/editors/screen/area.c20
-rw-r--r--source/blender/makesdna/DNA_screen_types.h5
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, &region->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, &region->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, &region->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. */