UI: Support persistent view state, write tree-view height to files

Adds support for saving some view state persistently and uses this to keep the
height of a tree-view, even as the region containing it is hidden, or the file
re-loaded.

Fixes #129058.

Basically the design is to have state stored in the region, so it can be saved
to files. Views types (tree-view, grid-view, etc) can decide themselves if they
have state to be preserved, and what state that is. If a view wants to preserve
state, it's stored in a list inside the region, identified by the view's idname.

Limitation is that multiple instances of the same view would share these bits of
state, in practice I don't think that's ever an issue.

More state can be added to be preserved as needed. Since different kinds of
views may require different state, I was thinking we could add ID properties to
`uiViewState` even, making it much more dynamic.

Pull Request: https://projects.blender.org/blender/blender/pulls/130292

(cherry picked from commit f0db870822)
This commit is contained in:
Julian Eisel
2024-11-18 17:19:48 +00:00
committed by Chris Clyne
parent 3482605b1e
commit e1b4610bf4
26 changed files with 208 additions and 41 deletions
@@ -374,6 +374,8 @@ ARegion *BKE_area_region_copy(const SpaceType *st, const ARegion *region)
BLI_listbase_clear(&newar->ui_previews); BLI_listbase_clear(&newar->ui_previews);
BLI_duplicatelist(&newar->ui_previews, &region->ui_previews); BLI_duplicatelist(&newar->ui_previews, &region->ui_previews);
BLI_listbase_clear(&newar->view_states);
BLI_duplicatelist(&newar->view_states, &region->view_states);
return newar; return newar;
} }
@@ -597,6 +599,7 @@ void BKE_area_region_free(SpaceType *st, ARegion *region)
BLI_freelistN(&region->ui_previews); BLI_freelistN(&region->ui_previews);
BLI_freelistN(&region->panels_category); BLI_freelistN(&region->panels_category);
BLI_freelistN(&region->panels_category_active); BLI_freelistN(&region->panels_category_active);
BLI_freelistN(&region->view_states);
} }
void BKE_screen_area_free(ScrArea *area) void BKE_screen_area_free(ScrArea *area)
@@ -1110,6 +1113,10 @@ static void write_area(BlendWriter *writer, ScrArea *area)
LISTBASE_FOREACH (uiPreview *, ui_preview, &region->ui_previews) { LISTBASE_FOREACH (uiPreview *, ui_preview, &region->ui_previews) {
BLO_write_struct(writer, uiPreview, ui_preview); BLO_write_struct(writer, uiPreview, ui_preview);
} }
LISTBASE_FOREACH (uiViewStateLink *, view_state, &region->view_states) {
BLO_write_struct(writer, uiViewStateLink, view_state);
}
} }
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
@@ -1168,6 +1175,7 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
BLO_read_list(reader, &region->panels_category_active); BLO_read_list(reader, &region->panels_category_active);
BLO_read_list(reader, &region->ui_lists); BLO_read_list(reader, &region->ui_lists);
BLO_read_list(reader, &region->view_states);
/* The area's search filter is runtime only, so we need to clear the active flag on read. */ /* The area's search filter is runtime only, so we need to clear the active flag on read. */
/* Clear runtime flags (e.g. search filter is runtime only). */ /* Clear runtime flags (e.g. search filter is runtime only). */
@@ -443,8 +443,7 @@ void region_layout(const bContext *C, ARegion *region)
0, 0,
style); style);
build_asset_view( build_asset_view(*layout, active_shelf->settings.asset_library_reference, *active_shelf, *C, *region);
*layout, active_shelf->settings.asset_library_reference, *active_shelf, *C, *region);
int layout_height; int layout_height;
UI_block_layout_resolve(block, nullptr, &layout_height); UI_block_layout_resolve(block, nullptr, &layout_height);
@@ -266,7 +266,7 @@ void build_asset_view(uiLayout &layout,
*block, "asset shelf asset view", std::move(asset_view)); *block, "asset shelf asset view", std::move(asset_view));
ui::GridViewBuilder builder(*block); ui::GridViewBuilder builder(*block);
builder.build_grid_view(*grid_view, region.v2d, layout); builder.build_grid_view(C, *grid_view, layout);
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@@ -208,7 +208,7 @@ static void catalog_selector_panel_draw(const bContext *C, Panel *panel)
"asset catalog tree view", "asset catalog tree view",
std::make_unique<AssetCatalogSelectorTree>(*library, *shelf)); std::make_unique<AssetCatalogSelectorTree>(*library, *shelf));
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
void catalog_selector_panel_register(ARegionType *region_type) void catalog_selector_panel_register(ARegionType *region_type)
@@ -25,6 +25,7 @@
#include "DNA_defs.h" #include "DNA_defs.h"
#include "DNA_vec_types.h" #include "DNA_vec_types.h"
#include "DNA_screen_types.h"
#include "BLI_span.hh" #include "BLI_span.hh"
#include "BLI_string_ref.hh" #include "BLI_string_ref.hh"
@@ -98,6 +99,20 @@ class AbstractView {
virtual bool supports_scrolling() const; virtual bool supports_scrolling() const;
virtual void scroll(ViewScrollDirection direction); virtual void scroll(ViewScrollDirection direction);
/**
* From the current view state, return certain state that will be written to files (stored in
* #ARegion.view_states) to preserve it over UI changes and file loading. The state can be
* restored using #persistent_state_apply().
*
* Return an empty value if there's no state to preserve (default implementation).
*/
virtual std::optional<uiViewState> persistent_state() const;
/**
* Restore a view state given in \a state, which was created by #persistent_state() for saving in
* files, and potentially loaded from a file.
*/
virtual void persistent_state_apply(const uiViewState &state);
/** /**
* Makes \a item valid for display in this view. Behavior is undefined for items not registered * Makes \a item valid for display in this view. Behavior is undefined for items not registered
* with this. * with this.
@@ -167,9 +167,10 @@ class GridViewBuilder {
public: public:
GridViewBuilder(uiBlock &block); GridViewBuilder(uiBlock &block);
/** Build \a grid_view into the previously provided block, clipped by \a view_bounds (view space, void build_grid_view(const bContext &C,
* typically `View2D.cur`). */ AbstractGridView &grid_view,
void build_grid_view(AbstractGridView &grid_view, const View2D &v2d, uiLayout &layout); uiLayout &layout,
std::optional<StringRef> search_string = {});
}; };
/** \} */ /** \} */
@@ -252,6 +252,8 @@ void UI_list_filter_and_sort_items(uiList *ui_list,
/** /**
* Override this for all available view types. * Override this for all available view types.
* \param idname: Used for restoring persistent state of this view, potentially written to files.
* Must not be longer than #BKE_ST_MAXNAME (including 0 terminator).
*/ */
blender::ui::AbstractGridView *UI_block_add_view( blender::ui::AbstractGridView *UI_block_add_view(
uiBlock &block, uiBlock &block,
@@ -2699,6 +2699,7 @@ void uiTemplateAssetView(uiLayout *layout,
PointerRNA *r_drag_op_properties); PointerRNA *r_drag_op_properties);
void uiTemplateLightLinkingCollection(uiLayout *layout, void uiTemplateLightLinkingCollection(uiLayout *layout,
bContext *C,
uiLayout *context_layout, uiLayout *context_layout,
PointerRNA *ptr, PointerRNA *ptr,
const char *propname); const char *propname);
@@ -2709,7 +2710,7 @@ void uiTemplateBoneCollectionTree(uiLayout *layout, bContext *C);
void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C); void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C);
#endif #endif
void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr); void uiTemplateNodeTreeInterface(uiLayout *layout, bContext *C, PointerRNA *ptr);
/** /**
* Draw all node buttons and socket default values with the same panel structure used by the node. * Draw all node buttons and socket default values with the same panel structure used by the node.
*/ */
@@ -146,6 +146,9 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
protected: protected:
virtual void build_tree() = 0; virtual void build_tree() = 0;
std::optional<uiViewState> persistent_state() const override;
void persistent_state_apply(const uiViewState &state) override;
private: private:
void foreach_view_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const final; void foreach_view_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const final;
void update_children_from_old(const AbstractView &old_view) override; void update_children_from_old(const AbstractView &old_view) override;
@@ -401,7 +404,11 @@ class TreeViewItemDropTarget : public DropTargetInterface {
class TreeViewBuilder { class TreeViewBuilder {
public: public:
static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout); static void build_tree_view(const bContext &C,
AbstractTreeView &tree_view,
uiLayout &layout,
std::optional<StringRef> search_string = {},
bool add_box = true);
private: private:
static void ensure_min_rows_items(AbstractTreeView &tree_view); static void ensure_min_rows_items(AbstractTreeView &tree_view);
@@ -2020,7 +2020,7 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x
/* Update bounds of all views in this block. If this block is a panel, this will be done later in /* Update bounds of all views in this block. If this block is a panel, this will be done later in
* #UI_panels_end(), because buttons are offset there. */ * #UI_panels_end(), because buttons are offset there. */
if (!block->panel) { if (!block->panel) {
ui_block_views_bounds_calc(block); ui_block_views_end(region, block);
} }
if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) { if (block->rect.xmin == 0.0f && block->rect.xmax == 0.0f) {
@@ -18,6 +18,7 @@
#include "DNA_listBase.h" #include "DNA_listBase.h"
#include "RNA_types.hh" #include "RNA_types.hh"
#include "UI_abstract_view.hh"
#include "UI_interface.hh" #include "UI_interface.hh"
#include "UI_resources.hh" #include "UI_resources.hh"
@@ -1538,7 +1539,10 @@ void ui_interface_tag_script_reload_queries();
/* interface_view.cc */ /* interface_view.cc */
void ui_block_free_views(uiBlock *block); void ui_block_free_views(uiBlock *block);
void ui_block_views_bounds_calc(const uiBlock *block); void ui_block_views_end(ARegion *region, const uiBlock *block);
void ui_block_view_persistent_state_restore(const ARegion &region,
const uiBlock &block,
blender::ui::AbstractView &view);
void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params); void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params);
void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block); void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block);
uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
@@ -1871,7 +1871,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
/* Update bounds for all "views" in this block. Usually this is done in #UI_block_end(), but /* Update bounds for all "views" in this block. Usually this is done in #UI_block_end(), but
* that wouldn't work because of the offset applied above. */ * that wouldn't work because of the offset applied above. */
ui_block_views_bounds_calc(block); ui_block_views_end(region, block);
} }
} }
@@ -475,5 +475,5 @@ void uiTemplateBoneCollectionTree(uiLayout *layout, bContext *C)
std::make_unique<blender::ui::bonecollections::BoneCollectionTreeView>(*armature)); std::make_unique<blender::ui::bonecollections::BoneCollectionTreeView>(*armature));
tree_view->set_default_rows(3); tree_view->set_default_rows(3);
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
@@ -412,5 +412,5 @@ void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C)
std::make_unique<blender::ui::greasepencil::LayerTreeView>(grease_pencil)); std::make_unique<blender::ui::greasepencil::LayerTreeView>(grease_pencil));
tree_view->set_default_rows(3); tree_view->set_default_rows(3);
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
@@ -355,10 +355,8 @@ class CollectionView : public AbstractTreeView {
} // namespace blender::ui::light_linking } // namespace blender::ui::light_linking
void uiTemplateLightLinkingCollection(uiLayout *layout, void uiTemplateLightLinkingCollection(
uiLayout *context_layout, uiLayout *layout, bContext *C, uiLayout *context_layout, PointerRNA *ptr, const char *propname)
PointerRNA *ptr,
const char *propname)
{ {
if (!ptr->data) { if (!ptr->data) {
return; return;
@@ -401,5 +399,5 @@ void uiTemplateLightLinkingCollection(uiLayout *layout,
std::make_unique<blender::ui::light_linking::CollectionView>(*context_layout, *collection)); std::make_unique<blender::ui::light_linking::CollectionView>(*context_layout, *collection));
tree_view->set_default_rows(3); tree_view->set_default_rows(3);
blender::ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); blender::ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
@@ -500,7 +500,7 @@ wmDragNodeTreeInterface *NodePanelDropTarget::get_drag_node_tree_declaration(
} // namespace blender::ui::nodes } // namespace blender::ui::nodes
void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr) void uiTemplateNodeTreeInterface(uiLayout *layout, bContext *C, PointerRNA *ptr)
{ {
if (!ptr->data) { if (!ptr->data) {
return; return;
@@ -519,5 +519,5 @@ void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr)
std::make_unique<blender::ui::nodes::NodeTreeInterfaceView>(nodetree, interface)); std::make_unique<blender::ui::nodes::NodeTreeInterfaceView>(nodetree, interface));
tree_view->set_default_rows(3); tree_view->set_default_rows(3);
blender::ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); blender::ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
@@ -126,6 +126,13 @@ void AbstractView::scroll(ViewScrollDirection /*direction*/)
BLI_assert_msg(false, "Unsupported for this view type"); BLI_assert_msg(false, "Unsupported for this view type");
} }
std::optional<uiViewState> AbstractView::persistent_state() const
{
return {};
}
void AbstractView::persistent_state_apply(const uiViewState & /*state*/) {}
/** \} */ /** \} */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@@ -11,6 +11,7 @@
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
#include "BKE_context.hh"
#include "BKE_icons.h" #include "BKE_icons.h"
#include "BLI_index_range.hh" #include "BLI_index_range.hh"
@@ -225,12 +226,15 @@ class BuildOnlyVisibleButtonsHelper {
void add_spacer_button(uiBlock &block, int row_count) const; void add_spacer_button(uiBlock &block, int row_count) const;
}; };
BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d, BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(
const AbstractGridView &grid_view, const View2D &v2d,
const int cols_per_row) const AbstractGridView &grid_view,
: v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row) int cols_per_row)
: grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row), v2d_(v2d)
{ {
visible_items_range_ = this->get_visible_range(); if (v2d.flag & V2D_IS_INIT && grid_view.get_item_count_filtered()) {
visible_items_range_ = this->get_visible_range();
}
} }
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
@@ -385,12 +389,16 @@ uiLayout *GridViewLayoutBuilder::current_layout() const
GridViewBuilder::GridViewBuilder(uiBlock & /*block*/) {} GridViewBuilder::GridViewBuilder(uiBlock & /*block*/) {}
void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, void GridViewBuilder::build_grid_view(const bContext &C,
const View2D &v2d, AbstractGridView &grid_view,
uiLayout &layout) uiLayout &layout,
std::optional<StringRef> search_string)
{ {
uiBlock &block = *uiLayoutGetBlock(&layout); uiBlock &block = *uiLayoutGetBlock(&layout);
const ARegion *region = CTX_wm_region(&C);
ui_block_view_persistent_state_restore(*region, block, grid_view);
grid_view.build_items(); grid_view.build_items();
grid_view.update_from_old(block); grid_view.update_from_old(block);
grid_view.change_state_delayed(); grid_view.change_state_delayed();
@@ -399,7 +407,7 @@ void GridViewBuilder::build_grid_view(AbstractGridView &grid_view,
UI_block_layout_set_current(&block, &layout); UI_block_layout_set_current(&block, &layout);
GridViewLayoutBuilder builder(layout); GridViewLayoutBuilder builder(layout);
builder.build_from_view(grid_view, v2d); builder.build_from_view(grid_view, region->v2d);
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
@@ -27,6 +27,7 @@
#include "BLI_listbase.h" #include "BLI_listbase.h"
#include "BLI_map.hh" #include "BLI_map.hh"
#include "BLI_string.h"
#include "ED_screen.hh" #include "ED_screen.hh"
@@ -56,6 +57,8 @@ static T *ui_block_add_view_impl(uiBlock &block,
StringRef idname, StringRef idname,
std::unique_ptr<AbstractView> view) std::unique_ptr<AbstractView> view)
{ {
BLI_assert(idname.size() < int64_t(sizeof(uiViewStateLink::idname)));
ViewLink *view_link = MEM_new<ViewLink>(__func__); ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link); BLI_addtail(&block.views, view_link);
@@ -126,9 +129,58 @@ void ViewLink::views_bounds_calc(const uiBlock &block)
} }
} }
void ui_block_views_bounds_calc(const uiBlock *block) void ui_block_view_persistent_state_restore(const ARegion &region,
const uiBlock &block,
ui::AbstractView &view)
{
StringRef idname = [&]() -> StringRef {
LISTBASE_FOREACH (ViewLink *, link, &block.views) {
if (link->view.get() == &view) {
return link->idname;
}
}
return "";
}();
if (idname.is_empty()) {
BLI_assert_unreachable();
return;
}
LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
if (stored_state->idname == idname) {
view.persistent_state_apply(stored_state->state);
}
}
}
static uiViewStateLink *ensure_view_state(ARegion &region, const ViewLink &link)
{
LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
if (link.idname == stored_state->idname) {
return stored_state;
}
}
uiViewStateLink *new_state = MEM_cnew<uiViewStateLink>(__func__);
link.idname.copy(new_state->idname, sizeof(new_state->idname));
BLI_addhead(&region.view_states, new_state);
return new_state;
}
void ui_block_views_end(ARegion *region, const uiBlock *block)
{ {
ViewLink::views_bounds_calc(*block); ViewLink::views_bounds_calc(*block);
if (region && region->regiontype != RGN_TYPE_TEMPORARY) {
LISTBASE_FOREACH (const ViewLink *, link, &block->views) {
/* Ensure persistent view state storage for writing to files if needed. */
if (std::optional<uiViewState> temp_state = link->view->persistent_state()) {
uiViewStateLink *state_link = ensure_view_state(*region, *link);
state_link->state = *temp_state;
}
}
}
} }
void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params) void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
@@ -121,6 +121,28 @@ void AbstractTreeView::set_default_rows(int default_rows)
custom_height_ = std::make_unique<int>(default_rows * padded_item_height()); custom_height_ = std::make_unique<int>(default_rows * padded_item_height());
} }
std::optional<uiViewState> AbstractTreeView::persistent_state() const
{
if (!custom_height_) {
return {};
}
uiViewState state{0};
if (custom_height_) {
state.custom_height = *custom_height_ * UI_INV_SCALE_FAC;
}
return state;
}
void AbstractTreeView::persistent_state_apply(const uiViewState &state)
{
if (state.custom_height) {
set_default_rows(round_fl_to_int(state.custom_height * UI_SCALE_FAC) / padded_item_height());
}
}
int AbstractTreeView::count_visible_descendants(const AbstractTreeViewItem &parent) const int AbstractTreeView::count_visible_descendants(const AbstractTreeViewItem &parent) const
{ {
if (parent.is_collapsed()) { if (parent.is_collapsed()) {
@@ -879,10 +901,19 @@ void TreeViewBuilder::ensure_min_rows_items(AbstractTreeView &tree_view)
} }
} }
void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view, uiLayout &layout) void TreeViewBuilder::build_tree_view(const bContext &C,
AbstractTreeView &tree_view,
uiLayout &layout,
std::optional<StringRef> search_string,
const bool add_box)
{ {
uiBlock &block = *uiLayoutGetBlock(&layout); uiBlock &block = *uiLayoutGetBlock(&layout);
const ARegion *region = CTX_wm_region(&C);
if (region) {
ui_block_view_persistent_state_restore(*region, block, tree_view);
}
tree_view.build_tree(); tree_view.build_tree();
tree_view.update_from_old(block); tree_view.update_from_old(block);
tree_view.change_state_delayed(); tree_view.change_state_delayed();
@@ -771,7 +771,8 @@ bool file_is_asset_visible_in_catalog_filter_settings(
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
void file_create_asset_catalog_tree_view_in_layout(asset_system::AssetLibrary *asset_library, void file_create_asset_catalog_tree_view_in_layout(const bContext *C,
asset_system::AssetLibrary *asset_library,
uiLayout *layout, uiLayout *layout,
SpaceFile *space_file, SpaceFile *space_file,
FileAssetSelectParams *params) FileAssetSelectParams *params)
@@ -786,5 +787,5 @@ void file_create_asset_catalog_tree_view_in_layout(asset_system::AssetLibrary *a
std::make_unique<ed::asset_browser::AssetCatalogTreeView>( std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
asset_library, params, *space_file)); asset_library, params, *space_file));
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
@@ -230,11 +230,11 @@ void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
/* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */ /* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */
struct FileAssetCatalogFilterSettingsHandle; struct FileAssetCatalogFilterSettingsHandle;
void file_create_asset_catalog_tree_view_in_layout( void file_create_asset_catalog_tree_view_in_layout(const bContext *C,
blender::asset_system::AssetLibrary *asset_library, blender::asset_system::AssetLibrary *asset_library,
uiLayout *layout, uiLayout *layout,
SpaceFile *space_file, SpaceFile *space_file,
FileAssetSelectParams *params); FileAssetSelectParams *params);
namespace blender::asset_system { namespace blender::asset_system {
class AssetLibrary; class AssetLibrary;
@@ -252,7 +252,8 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane
uiItemS(col); uiItemS(col);
file_create_asset_catalog_tree_view_in_layout(asset_library, col, sfile, params); file_create_asset_catalog_tree_view_in_layout(
C, asset_library, col, sfile, params);
} }
void file_tools_region_panels_register(ARegionType *art) void file_tools_region_panels_register(ARegionType *art)
@@ -320,7 +320,7 @@ void spreadsheet_data_set_panel_draw(const bContext *C, Panel *panel)
std::make_unique<GeometryDataSetTreeView>( std::make_unique<GeometryDataSetTreeView>(
spreadsheet_get_display_geometry_set(sspreadsheet, object), *C)); spreadsheet_get_display_geometry_set(sspreadsheet, object), *C));
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
} }
} // namespace blender::ed::spreadsheet } // namespace blender::ed::spreadsheet
@@ -319,6 +319,31 @@ typedef struct uiList { /* some list UI data need to be saved in file */
uiListDyn *dyn_data; uiListDyn *dyn_data;
} uiList; } uiList;
/** See #uiViewStateLink. */
typedef struct uiViewState {
/**
* User set height of the view in unscaled pixels. A value of 0 means no custom height was set
* and the default should be used.
*/
int custom_height;
char _pad[4];
} uiViewState;
/**
* Persistent storage for some state of views (#ui::AbstractView), for storage in a region. The
* view state is matched to the view using the view's idname.
*
* The actual state is stored in #uiViewState, so views can manage this conveniently without having
* to care about the idname and listbase pointers themselves.
*/
typedef struct uiViewStateLink {
struct uiViewStateLink *next, *prev;
char idname[64]; /* #BKE_ST_MAXNAME */
uiViewState state;
} uiViewStateLink;
typedef struct TransformOrientation { typedef struct TransformOrientation {
struct TransformOrientation *next, *prev; struct TransformOrientation *next, *prev;
/** MAX_NAME. */ /** MAX_NAME. */
@@ -517,6 +542,11 @@ typedef struct ARegion {
ListBase handlers; ListBase handlers;
/** Panel categories runtime. */ /** Panel categories runtime. */
ListBase panels_category; ListBase panels_category;
/**
* Permanent state storage of #ui::AbstractView instances, so hiding regions with views or
* loading files remembers the view state.
*/
ListBase view_states; /* #uiViewStateLink */
/** Gizmo-map of this region. */ /** Gizmo-map of this region. */
struct wmGizmoMap *gizmo_map; struct wmGizmoMap *gizmo_map;
@@ -2202,6 +2202,7 @@ void RNA_api_ui_layout(StructRNA *srna)
srna, "template_light_linking_collection", "uiTemplateLightLinkingCollection"); srna, "template_light_linking_collection", "uiTemplateLightLinkingCollection");
RNA_def_function_ui_description(func, RNA_def_function_ui_description(func,
"Visualization of a content of a light linking collection"); "Visualization of a content of a light linking collection");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, parm = RNA_def_pointer(func,
"context_layout", "context_layout",
"UILayout", "UILayout",
@@ -2223,6 +2224,7 @@ void RNA_api_ui_layout(StructRNA *srna)
func = RNA_def_function(srna, "template_node_tree_interface", "uiTemplateNodeTreeInterface"); func = RNA_def_function(srna, "template_node_tree_interface", "uiTemplateNodeTreeInterface");
RNA_def_function_ui_description(func, "Show a node tree interface"); RNA_def_function_ui_description(func, "Show a node tree interface");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, parm = RNA_def_pointer(func,
"interface", "interface",
"NodeTreeInterface", "NodeTreeInterface",