Get changes in catalog tree to update the UI correctly

The catalog tree-view now sends appropriate notifiers and messages when
changing data. Either itself or other UIs (like the main asset browser
region) can then listen to these and update.
I try to design this in a way that the views become independent on the
editor displaying them, so the views can be reused in multiple places.
This commit is contained in:
Julian Eisel 2022-02-08 16:28:40 +01:00
parent eab2a8479a
commit 29fdd43605
12 changed files with 242 additions and 51 deletions

View File

@ -207,9 +207,7 @@ void AssetList::fetch(const bContext &C)
void AssetList::setCatalogFilterSettings(const AssetCatalogFilterSettings &settings)
{
filelist_set_asset_catalog_filter_options(
filelist_,
(AssetCatalogFilterMode)settings.filter_mode,
&settings.active_catalog_id);
filelist_, (AssetCatalogFilterMode)settings.filter_mode, &settings.active_catalog_id);
}
bool AssetList::needsRefetch() const
@ -285,7 +283,11 @@ bool AssetList::listen(const wmNotifier &notifier) const
break;
}
case NC_ASSET:
if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) {
if (ELEM(notifier.data, ND_ASSET_LIST)) {
filelist_tag_needs_filtering(filelist_);
return true;
}
if (ELEM(notifier.data, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) {
return true;
}
if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) {

View File

@ -3215,6 +3215,10 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *regi
const int xy[2]) ATTR_NONNULL(1, 2);
uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region);
/**
* Listen to \a notifier, returning true if the region should redraw.
*/
bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view, const wmNotifier *notifier);
/**
* Listen to \a notifier, returning true if the region should redraw.
*/

View File

@ -42,6 +42,7 @@ struct uiButTreeRow;
struct uiLayout;
struct wmDrag;
struct wmEvent;
struct wmNotifier;
namespace blender::ui {
@ -142,6 +143,9 @@ class AbstractTreeView : public TreeViewItemContainer {
void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
/** Listen to a notifier, returning true if a redraw is needed. */
virtual bool listen(const wmNotifier &) const;
/** Only one item can be renamed at a time. */
bool is_renaming() const;

View File

@ -101,12 +101,19 @@ void ui_block_free_views(uiBlock *block)
void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
{
ARegion *region = listener_params->region;
LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
/* TODO only grid views, should this be supported by all views? */
if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) {
if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view),
listener_params->notifier)) {
ED_region_tag_redraw(listener_params->region);
ED_region_tag_redraw(region);
}
}
else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) {
if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view),
listener_params->notifier)) {
ED_region_tag_redraw(region);
}
}
}

View File

@ -82,6 +82,12 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
bool AbstractTreeView::listen(const wmNotifier &) const
{
/* Nothing by default. */
return false;
}
bool AbstractTreeView::is_renaming() const
{
return rename_buffer_ != nullptr;
@ -819,6 +825,13 @@ class TreeViewItemAPIWrapper {
using namespace blender::ui;
bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle,
const wmNotifier *notifier)
{
const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle);
return view.listen(*notifier);
}
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
{
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);

View File

@ -26,13 +26,17 @@ struct ARegion;
struct ARegionType;
struct AssetLibrary;
struct bContext;
struct SpaceAssets;
struct PointerRNA;
struct PropertyRNA;
struct uiLayout;
struct wmMsgBus;
void asset_browser_main_region_draw(const bContext *C, ARegion *region);
void asset_browser_navigation_region_panels_register(ARegionType *art);
void asset_brower_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
SpaceAssets *assets_space);
void asset_view_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
PointerRNA *catalog_filter_owner_ptr,
PropertyRNA *catalog_filter_prop,
wmMsgBus *msg_bus);

View File

@ -74,7 +74,10 @@ static void assets_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pa
uiItemS(col);
AssetLibrary *library = ED_assetlist_library_get(&assets_space->asset_library_ref);
asset_brower_create_catalog_tree_view_in_layout(library, col, assets_space);
PropertyRNA *catalog_filter_prop = RNA_struct_find_property(&assets_space_ptr, "catalog_filter");
asset_view_create_catalog_tree_view_in_layout(
library, col, &assets_space_ptr, catalog_filter_prop, CTX_wm_message_bus(C));
}
void asset_browser_navigation_region_panels_register(ARegionType *art)

View File

@ -42,6 +42,7 @@
#include "UI_tree_view.hh"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_types.h"
#include "asset_browser_intern.hh"
@ -54,19 +55,29 @@ namespace blender::ed::asset_browser {
class AssetCatalogTreeViewAllItem;
class AssetCatalogTreeView : public ui::AbstractTreeView {
/** The asset library this catalog tree comes from. May be null when drawing the catalog tree
* before the library was read. */
::AssetLibrary *asset_library_;
/** The asset catalog tree this tree-view represents. */
bke::AssetCatalogTree *catalog_tree_;
AssetCatalogFilterSettings &catalog_filter_;
PointerRNA catalog_filter_owner_;
PropertyRNA &catalog_filter_prop_;
/** Used to notify the parts of the UI that display the filtered assets. */
wmMsgBus *msg_bus_;
friend class AssetCatalogTreeViewItem;
friend class AssetCatalogDropController;
friend class AssetCatalogTreeViewAllItem;
public:
AssetCatalogTreeView(::AssetLibrary *library, AssetCatalogFilterSettings *catalog_filter);
AssetCatalogTreeView(::AssetLibrary *library,
const PointerRNA &catalog_filter_owner,
PropertyRNA &catalog_filter_prop,
wmMsgBus *msg_bus);
void build_tree() override;
bool listen(const wmNotifier &notifier) const override;
void activate_catalog_by_id(CatalogID catalog_id);
@ -77,6 +88,11 @@ class AssetCatalogTreeView : public ui::AbstractTreeView {
AssetCatalogTreeViewAllItem &add_all_item();
void add_unassigned_item();
bool is_active_catalog(CatalogID catalog_id) const;
AssetCatalogFilterSettings &catalog_filter_settings() const;
void notify_catalog_filter_change();
void notify_catalog_tree_change();
void notify_catalog_assignments_change();
};
/* ---------------------------------------------------------------------- */
@ -129,7 +145,7 @@ class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController
static AssetCatalog *get_drag_catalog(const wmDrag &drag, const ::AssetLibrary &asset_library);
static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint);
static bool drop_assets_into_catalog(struct bContext *C,
const AssetCatalogTreeView &tree_view,
AssetCatalogTreeView &tree_view,
const wmDrag &drag,
CatalogID catalog_id,
StringRefNull simple_name = "");
@ -181,14 +197,15 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
/* ---------------------------------------------------------------------- */
AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library,
AssetCatalogFilterSettings *catalog_filter)
const PointerRNA &catalog_filter_owner,
PropertyRNA &catalog_filter_prop,
wmMsgBus *msg_bus)
: asset_library_(library),
catalog_tree_(BKE_asset_library_get_catalog_tree(library)),
catalog_filter_(*catalog_filter)
catalog_filter_owner_(catalog_filter_owner),
catalog_filter_prop_(catalog_filter_prop),
msg_bus_(msg_bus)
{
if (!catalog_filter) {
throw "Catalog filter should never be null";
}
}
void AssetCatalogTreeView::build_tree()
@ -224,12 +241,11 @@ AssetCatalogTreeViewAllItem &AssetCatalogTreeView::add_all_item()
{
AssetCatalogTreeViewAllItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(IFACE_("All"));
item.set_on_activate_fn([this](ui::BasicTreeViewItem & /*item*/) {
catalog_filter_.filter_mode = ASSET_CATALOG_SHOW_ALL_ASSETS;
/* TODO */
// WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
catalog_filter_settings().filter_mode = ASSET_CATALOG_SHOW_ALL_ASSETS;
notify_catalog_filter_change();
});
item.set_is_active_fn(
[this]() { return catalog_filter_.filter_mode == ASSET_CATALOG_SHOW_ALL_ASSETS; });
[this]() { return catalog_filter_settings().filter_mode == ASSET_CATALOG_SHOW_ALL_ASSETS; });
return item;
}
@ -239,26 +255,64 @@ void AssetCatalogTreeView::add_unassigned_item()
IFACE_("Unassigned"), ICON_FILE_HIDDEN);
item.set_on_activate_fn([this](ui::BasicTreeViewItem & /*item*/) {
catalog_filter_.filter_mode = ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
/* TODO */
// WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
catalog_filter_settings().filter_mode = ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
notify_catalog_filter_change();
});
item.set_is_active_fn([this]() {
return catalog_filter_settings().filter_mode == ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG;
});
item.set_is_active_fn(
[this]() { return catalog_filter_.filter_mode == ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG; });
}
void AssetCatalogTreeView::activate_catalog_by_id(CatalogID catalog_id)
{
catalog_filter_.filter_mode = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
catalog_filter_.active_catalog_id = catalog_id;
/* TODO */
// WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
AssetCatalogFilterSettings &catalog_filter = catalog_filter_settings();
catalog_filter.filter_mode = ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG;
catalog_filter.active_catalog_id = catalog_id;
notify_catalog_filter_change();
}
bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
{
return (catalog_filter_.filter_mode == ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG) &&
(catalog_filter_.active_catalog_id == catalog_id);
const AssetCatalogFilterSettings &catalog_filter = catalog_filter_settings();
return (catalog_filter.filter_mode == ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG) &&
(catalog_filter.active_catalog_id == catalog_id);
}
AssetCatalogFilterSettings &AssetCatalogTreeView::catalog_filter_settings() const
{
/* Copy so we can pass a non-const pointer to this to RNA functions. */
PointerRNA catalog_filter_owner = catalog_filter_owner_;
PointerRNA catalog_filter_ptr = RNA_property_pointer_get(&catalog_filter_owner,
&catalog_filter_prop_);
BLI_assert(catalog_filter_ptr.type == &RNA_AssetCatalogFilterSettings);
return *reinterpret_cast<AssetCatalogFilterSettings *>(catalog_filter_ptr.data);
}
bool AssetCatalogTreeView::listen(const wmNotifier &notifier) const
{
switch (notifier.category) {
case NC_ASSET:
if (ELEM(notifier.data, ND_ASSET_CATALOGS, ND_ASSET_LIST_READING)) {
return true;
}
}
return false;
}
void AssetCatalogTreeView::notify_catalog_filter_change()
{
WM_msg_publish_rna(msg_bus_, &catalog_filter_owner_, &catalog_filter_prop_);
}
void AssetCatalogTreeView::notify_catalog_tree_change()
{
WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr);
}
void AssetCatalogTreeView::notify_catalog_assignments_change()
{
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
}
/* ---------------------------------------------------------------------- */
@ -445,16 +499,16 @@ bool AssetCatalogDropController::drop_asset_catalog_into_catalog(
ED_asset_catalog_move(tree_view.asset_library_, catalog_drag->drag_catalog_id, drop_catalog_id);
tree_view.activate_catalog_by_id(catalog_drag->drag_catalog_id);
WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr);
tree_view.notify_catalog_tree_change();
return true;
}
bool AssetCatalogDropController::drop_assets_into_catalog(
struct bContext *C,
const AssetCatalogTreeView &UNUSED(tree_view),
const wmDrag &drag,
CatalogID catalog_id,
StringRefNull simple_name)
bool AssetCatalogDropController::drop_assets_into_catalog(struct bContext *C,
AssetCatalogTreeView &tree_view,
const wmDrag &drag,
CatalogID catalog_id,
StringRefNull simple_name)
{
BLI_assert(drag.type == WM_DRAG_ASSET_LIST);
const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
@ -478,10 +532,10 @@ bool AssetCatalogDropController::drop_assets_into_catalog(
// filelist_tag_needs_filtering(tree_view.space_file_.files);
// file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED |
// FILE_SEL_HIGHLIGHTED);
// WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, nullptr);
}
if (did_update) {
tree_view.notify_catalog_assignments_change();
ED_undo_push(C, "Assign Asset Catalog");
}
return true;
@ -661,9 +715,11 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext
/* ---------------------------------------------------------------------- */
void asset_brower_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
SpaceAssets *assets_space)
void asset_view_create_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
uiLayout *layout,
PointerRNA *catalog_filter_owner_ptr,
PropertyRNA *catalog_filter_prop,
wmMsgBus *msg_bus)
{
uiBlock *block = uiLayoutGetBlock(layout);
@ -672,8 +728,8 @@ void asset_brower_create_catalog_tree_view_in_layout(::AssetLibrary *asset_libra
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(asset_library,
&assets_space->catalog_filter));
std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
asset_library, *catalog_filter_owner_ptr, *catalog_filter_prop, msg_bus));
ui::TreeViewBuilder builder(*block);
builder.build_tree_view(*tree_view);

View File

@ -33,10 +33,14 @@
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "WM_message.h"
#include "asset_browser_intern.hh"
#include "asset_view.hh"
@ -116,6 +120,29 @@ static void asset_browser_main_region_listener(const wmRegionListenerParams *UNU
{
}
/* FIXME: See comment above #WM_msg_publish_rna_prop(). */
extern "C" {
static void asset_browser_main_region_message_subscribe(
const wmRegionMessageSubscribeParams *params)
{
struct wmMsgBus *mbus = params->message_bus;
bScreen *screen = params->screen;
SpaceAssets *assets_space = reinterpret_cast<SpaceAssets *>(params->area->spacedata.first);
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
msg_sub_value_region_tag_redraw.owner = params->region;
msg_sub_value_region_tag_redraw.user_data = params->region;
msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_prop(mbus,
&screen->id,
assets_space,
SpaceAssets,
catalog_filter,
&msg_sub_value_region_tag_redraw);
}
}
/* ---------------------------------------------------------------------- */
/* Header Region */
@ -171,6 +198,7 @@ void ED_spacetype_assets(void)
art->init = asset_browser_main_region_init;
art->draw = asset_browser_main_region_draw;
art->listener = asset_browser_main_region_listener;
art->message_subscribe = asset_browser_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
BLI_addhead(&st->regiontypes, art);

View File

@ -71,6 +71,7 @@ extern StructRNA RNA_ArrayGpencilModifier;
extern StructRNA RNA_ArrayModifier;
extern StructRNA RNA_Attribute;
extern StructRNA RNA_AttributeGroup;
extern StructRNA RNA_AssetCatalogFilterSettings;
extern StructRNA RNA_AssetHandle;
extern StructRNA RNA_AssetLibraryReference;
extern StructRNA RNA_AssetMetaData;

View File

@ -3277,6 +3277,17 @@ static void rna_SpaceAssets_asset_library_set(PointerRNA *ptr, int value)
asset_space->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
static void rna_AssetCatalogFilterSettings_active_catalog_id_get(PointerRNA *ptr, char *value)
{
const AssetCatalogFilterSettings *settings = ptr->data;
BLI_uuid_format(value, settings->active_catalog_id);
}
static int rna_AssetCatalogFilterSettings_active_catalog_id_length(PointerRNA *UNUSED(ptr))
{
return UUID_STRING_LEN - 1;
}
#else
static const EnumPropertyItem dt_uv_items[] = {
@ -7987,21 +7998,77 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
}
static void rna_def_asset_catalog_filter_settings(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem asset_catalog_filter_mode[] = {
{ASSET_CATALOG_SHOW_ALL_ASSETS,
"SHOW_ALL_ASSETS",
ICON_NONE,
"All Assets",
"Show all assets, regardless of catalogs"},
{ASSET_CATALOG_SHOW_ASSETS_FROM_CATALOG,
"SHOW_ASSETS_FROM_CATALOG",
ICON_NONE,
"From Catalog",
"Show assets assigned to a specific catalog"},
{ASSET_CATALOG_SHOW_ASSETS_WITHOUT_CATALOG,
"SHOW_ASSETS_WITHOUT_CATALOG",
ICON_NONE,
"Assets Without Catalog",
"Show any asset that doesn't have a recognized asset catalog assigned to it"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "AssetCatalogFilterSettings", NULL);
RNA_def_struct_ui_text(
srna,
"Asset Catalog Filter Settings",
"Options to determine how catalogs should affect which assets are visible");
prop = RNA_def_property(srna, "filter_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, asset_catalog_filter_mode);
RNA_def_property_ui_text(prop,
"Asset Catalog Filter Mode",
"Determine how filtering based on asset catalogs should be done");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
prop = RNA_def_property(srna, "active_catalog_id", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
"rna_AssetCatalogFilterSettings_active_catalog_id_get",
"rna_AssetCatalogFilterSettings_active_catalog_id_length",
NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog to show assets from");
}
static void rna_def_space_assets(BlenderRNA *brna)
{
PropertyRNA *prop;
StructRNA *srna;
rna_def_asset_catalog_filter_settings(brna);
srna = RNA_def_struct(brna, "SpaceAssets", "Space");
RNA_def_struct_ui_text(srna, "Space Asset Browser", "Asset browser space data");
// rna_def_space_generic_show_region_toggles(
// srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_CHANNELS) | (1 << RGN_TYPE_FOOTER));
// rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_NAV_BAR));
prop = rna_def_asset_library_reference_common(
srna, "rna_SpaceAssets_asset_library_get", "rna_SpaceAssets_asset_library_set");
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
prop = RNA_def_property(srna, "catalog_filter", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "AssetCatalogFilterSettings");
RNA_def_property_ui_text(prop,
"Asset Catalog Filter",
"Parameters to set up rules for filtering assets based on the catalogs "
"they are assigned to");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
}
void RNA_def_space(BlenderRNA *brna)

View File

@ -461,9 +461,11 @@ typedef struct wmNotifier {
#define ND_SPACE_SPREADSHEET (22 << 16)
/* NC_ASSET */
/* Denote that something in the contents of an AssetList may have changed. Triggers re-filtering of
* items. */
#define ND_ASSET_LIST (1 << 16)
/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of
* assets is done. */
#define ND_ASSET_LIST (1 << 16)
#define ND_ASSET_LIST_PREVIEW (2 << 16)
#define ND_ASSET_LIST_READING (3 << 16)
/* Catalog data changed, requiring a redraw of catalog UIs. Note that this doesn't denote a