UI: Asset Shelf (Experimental Feature) #104831
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
return allow_asset_drag_ ? std::make_unique<AssetDragController>(get_view(), asset_) : nullptr;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ class TreeViewItemContainer {
|
|||
enum class IterOptions {
|
||||
None = 0,
|
||||
SkipCollapsed = 1 << 0,
|
||||
SkipFiltered = 1 << 1,
|
||||
|
||||
/* Keep ENUM_OPERATORS() below updated! */
|
||||
};
|
||||
|
|
|
@ -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
|
||||
* \{ */
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
This is crashing on a simple test (default sculpt brush made into an asset).