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:
@@ -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, ®ion->ui_previews);
|
BLI_duplicatelist(&newar->ui_previews, ®ion->ui_previews);
|
||||||
|
BLI_listbase_clear(&newar->view_states);
|
||||||
|
BLI_duplicatelist(&newar->view_states, ®ion->view_states);
|
||||||
|
|
||||||
return newar;
|
return newar;
|
||||||
}
|
}
|
||||||
@@ -597,6 +599,7 @@ void BKE_area_region_free(SpaceType *st, ARegion *region)
|
|||||||
BLI_freelistN(®ion->ui_previews);
|
BLI_freelistN(®ion->ui_previews);
|
||||||
BLI_freelistN(®ion->panels_category);
|
BLI_freelistN(®ion->panels_category);
|
||||||
BLI_freelistN(®ion->panels_category_active);
|
BLI_freelistN(®ion->panels_category_active);
|
||||||
|
BLI_freelistN(®ion->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, ®ion->ui_previews) {
|
LISTBASE_FOREACH (uiPreview *, ui_preview, ®ion->ui_previews) {
|
||||||
BLO_write_struct(writer, uiPreview, ui_preview);
|
BLO_write_struct(writer, uiPreview, ui_preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LISTBASE_FOREACH (uiViewStateLink *, view_state, ®ion->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, ®ion->panels_category_active);
|
BLO_read_list(reader, ®ion->panels_category_active);
|
||||||
|
|
||||||
BLO_read_list(reader, ®ion->ui_lists);
|
BLO_read_list(reader, ®ion->ui_lists);
|
||||||
|
BLO_read_list(reader, ®ion->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 ®ion,
|
||||||
|
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 ®ion,
|
||||||
|
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, ®ion.view_states) {
|
||||||
|
if (stored_state->idname == idname) {
|
||||||
|
view.persistent_state_apply(stored_state->state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uiViewStateLink *ensure_view_state(ARegion ®ion, const ViewLink &link)
|
||||||
|
{
|
||||||
|
LISTBASE_FOREACH (uiViewStateLink *, stored_state, ®ion.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(®ion.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",
|
||||||
|
|||||||
Reference in New Issue
Block a user