UI: Asset Shelf (Experimental Feature) #104831

Closed
Julian Eisel wants to merge 399 commits from asset-shelf into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 104 additions and 4 deletions
Showing only changes of commit 8848a78111 - Show all commits

View File

@ -454,9 +454,14 @@ int ED_asset_shelf_context(const bContext *C,
namespace blender::ed::asset::shelf {
static PointerRNA active_shelf_ptr_from_context(const bContext *C)
{
return CTX_data_pointer_get_type(C, "asset_shelf", &RNA_AssetShelf);
}
AssetShelf *active_shelf_from_context(const bContext *C)
{
PointerRNA shelf_settings_ptr = CTX_data_pointer_get_type(C, "asset_shelf", &RNA_AssetShelf);
PointerRNA shelf_settings_ptr = active_shelf_ptr_from_context(C);
return static_cast<AssetShelf *>(shelf_settings_ptr.data);
}
@ -551,13 +556,16 @@ static void asset_shelf_footer_draw(const bContext *C, Header *header)
uiItemS(layout);
AssetShelf *shelf = shelf::active_shelf_from_context(C);
PointerRNA shelf_ptr = shelf::active_shelf_ptr_from_context(C);
AssetShelf *shelf = static_cast<AssetShelf *>(shelf_ptr.data);
if (shelf) {
add_catalog_toggle_buttons(shelf->settings, *layout);
}
uiItemSpacer(layout);
uiItemR(layout, &shelf_ptr, "search_filter", 0, "", ICON_VIEWZOOM);
uiItemS(layout);
uiItemPopoverPanel(layout, C, "ASSETSHELF_PT_display", "", ICON_IMGDISPLAY);
}

View File

@ -10,6 +10,8 @@
#include "BKE_screen.h"
#include "BLI_fnmatch.h"
#include "DNA_asset_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@ -36,6 +38,9 @@ namespace blender::ed::asset::shelf {
class AssetView : public ui::AbstractGridView {
const AssetLibraryReference library_ref_;
const AssetShelf &shelf_;
/** Copy of the filter string from #AssetShelfSettings, with extra '*' added to the beginning and
* end of the string, for `fnmatch()` to work. */
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
std::optional<asset_system::AssetCatalogFilter> catalog_filter_ = std::nullopt;
/* XXX Temporary: Only for #asset_poll__() callback. Should use traits instead. */
bContext &evil_C_;
@ -64,6 +69,7 @@ class AssetViewItem : public ui::PreviewGridItem {
void disable_asset_drag();
void build_grid_tile(uiLayout &layout) const override;
void build_context_menu(bContext &C, uiLayout &column) const override;
bool is_filtered_visible() const override;
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
};
@ -83,6 +89,10 @@ AssetView::AssetView(const AssetLibraryReference &library_ref,
bContext &evil_C)
: library_ref_(library_ref), shelf_(shelf), evil_C_(evil_C)
{
if (shelf.settings.search_string[0]) {
BLI_strncpy_ensure_pad(
search_string, shelf.settings.search_string, '*', sizeof(search_string));
}
}
void AssetView::build_items()
@ -188,6 +198,17 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
shelf_type.draw_context_menu(&C, &shelf_type, &asset_, &column);
}
bool AssetViewItem::is_filtered_visible() const
{
const AssetView &asset_view = dynamic_cast<AssetView &>(get_view());
if (asset_view.search_string[0] == '\0') {
return true;
}
const char *asset_name = ED_asset_handle_get_name(&asset_);
return fnmatch(asset_view.search_string, asset_name, FNM_CASEFOLD) == 0;
}
std::unique_ptr<ui::AbstractViewItemDragController> AssetViewItem::create_drag_controller() const
JulianEisel marked this conversation as resolved Outdated

This is crashing on a simple test (default sculpt brush made into an asset).

  • Default blend file (w/ experimental asset features enabled).
  • Enter sculpt mode.
  • Make default sculpt brush into an asset (from the properties toolbar).
  • RMB in the asset shelf.
  • Crash.
This is crashing on a simple test (default sculpt brush made into an asset). - Default blend file (w/ experimental asset features enabled). - Enter sculpt mode. - Make default sculpt brush into an asset (from the properties toolbar). - RMB in the asset shelf. - Crash.
{
return allow_asset_drag_ ? std::make_unique<AssetDragController>(get_view(), asset_) : nullptr;

View File

@ -134,6 +134,9 @@ class AbstractViewItem {
bool is_active_ = false;
bool is_renaming_ = false;
/** Cache filtered state here to avoid having to re-query. */
mutable std::optional<bool> is_filtered_visible_;
public:
virtual ~AbstractViewItem() = default;
@ -216,6 +219,16 @@ class AbstractViewItem {
*/
virtual void update_from_old(const AbstractViewItem &old);
/**
* \note Do not call this directly to avoid constantly rechecking the filter state. Instead use
* #is_filtered_visible_cached() for querying.
*/
virtual bool is_filtered_visible() const;
/** Return the result of #is_filtered_visible(), but ensure the result is cached so it's only
* queried once per redraw. */
bool is_filtered_visible_cached() const;
/**
* Add a text button for renaming the item to \a block. This must be used for the built-in
* renaming to work. This button is meant to appear temporarily. It is removed when renaming is

View File

@ -96,6 +96,8 @@ class AbstractGridView : public AbstractView {
protected:
Vector<std::unique_ptr<AbstractGridViewItem>> items_;
/** Store this to avoid recomputing. */
mutable std::optional<int> item_count_filtered_;
/** <identifier, item> map to lookup items by identifier, used for efficient lookups in
* #update_from_old(). */
Map<StringRef, AbstractGridViewItem *> item_map_;
@ -107,6 +109,7 @@ class AbstractGridView : public AbstractView {
using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>;
void foreach_item(ItemIterFn iter_fn) const;
void foreach_filtered_item(ItemIterFn iter_fn) const;
/**
* Convenience wrapper constructing the item by forwarding given arguments to the constructor of
@ -124,6 +127,7 @@ class AbstractGridView : public AbstractView {
template<class ItemT, typename... Args> inline ItemT &add_item(Args &&...args);
const GridViewStyle &get_style() const;
int get_item_count() const;
int get_item_count_filtered() const;
void set_tile_size(int tile_width, int tile_height);

View File

@ -64,6 +64,7 @@ class TreeViewItemContainer {
enum class IterOptions {
None = 0,
SkipCollapsed = 1 << 0,
SkipFiltered = 1 << 1,
/* Keep ENUM_OPERATORS() below updated! */
};

View File

@ -164,6 +164,27 @@ void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Filtering
* \{ */
bool AbstractViewItem::is_filtered_visible() const
{
return true;
}
bool AbstractViewItem::is_filtered_visible_cached() const
{
if (is_filtered_visible_.has_value()) {
return *is_filtered_visible_;
}
is_filtered_visible_ = is_filtered_visible();
return *is_filtered_visible_;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Drag 'n Drop
* \{ */

View File

@ -42,6 +42,15 @@ void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
}
}
void AbstractGridView::foreach_filtered_item(ItemIterFn iter_fn) const
{
for (const auto &item_ptr : items_) {
if (item_ptr->is_filtered_visible_cached()) {
iter_fn(*item_ptr);
}
}
}
AbstractGridViewItem *AbstractGridView::find_matching_item(
const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const
{
@ -84,6 +93,20 @@ int AbstractGridView::get_item_count() const
return items_.size();
}
int AbstractGridView::get_item_count_filtered() const
{
if (item_count_filtered_) {
return *item_count_filtered_;
}
int i = 0;
foreach_filtered_item([&i](const auto &) { i++; });
BLI_assert(i <= get_item_count());
item_count_filtered_ = i;
return i;
}
void AbstractGridView::set_tile_size(int tile_width, int tile_height)
{
style_.tile_width = tile_width;
@ -274,7 +297,7 @@ void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) c
void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const
{
const int last_item_idx = grid_view_.get_item_count() - 1;
const int last_item_idx = grid_view_.get_item_count_filtered() - 1;
const int last_visible_idx = visible_items_range_.last();
if (last_item_idx > last_visible_idx) {
@ -359,7 +382,7 @@ void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
int item_idx = 0;
uiLayout *row = nullptr;
grid_view.foreach_item([&](AbstractGridViewItem &item) {
grid_view.foreach_filtered_item([&](AbstractGridViewItem &item) {
/* Skip if item isn't visible. */
if (!build_visible_helper.is_item_visible(item_idx)) {
item_idx++;

View File

@ -763,6 +763,9 @@ typedef struct AssetShelfSettings {
/** If not set (null or empty string), all assets will be displayed ("All" catalog behavior). */
const char *active_catalog_path;
/** For filtering assets displayed in the asset view. */
char search_string[64];
short display_flag; /* #AssetShelfSettings_DisplayFlag */
char _pad1[6];

View File

@ -2143,6 +2143,12 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
"Show Names",
"Show the asset name together with the preview. Otherwise only the preview will be visible");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_SHELF, NULL);
prop = RNA_def_property(srna, "search_filter", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "settings.search_string");
RNA_def_property_ui_text(prop, "Display Filter", "Filter assets by name");
RNA_def_property_flag(prop, PROP_TEXTEDIT_UPDATE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_ASSET_SHELF, NULL);
}
void RNA_def_ui(BlenderRNA *brna)