Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/__init__.py23
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py24
-rw-r--r--source/blender/blenkernel/BKE_asset.h3
-rw-r--r--source/blender/blenkernel/BKE_context.h2
-rw-r--r--source/blender/blenkernel/BKE_screen.h6
-rw-r--r--source/blender/blenkernel/intern/asset.cc5
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc22
-rw-r--r--source/blender/blenkernel/intern/cryptomatte_test.cc2
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc4
-rw-r--r--source/blender/blenkernel/intern/screen.c3
-rw-r--r--source/blender/blenkernel/intern/workspace.c10
-rw-r--r--source/blender/blenloader/intern/versioning_290.c10
-rw-r--r--source/blender/editors/asset/CMakeLists.txt2
-rw-r--r--source/blender/editors/asset/asset_edit.cc152
-rw-r--r--source/blender/editors/asset/asset_list.cc563
-rw-r--r--source/blender/editors/include/ED_asset.h50
-rw-r--r--source/blender/editors/include/ED_fileselect.h10
-rw-r--r--source/blender/editors/include/UI_interface.h33
-rw-r--r--source/blender/editors/include/UI_interface_icons.h5
-rw-r--r--source/blender/editors/interface/CMakeLists.txt1
-rw-r--r--source/blender/editors/interface/interface_handlers.c181
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_intern.h18
-rw-r--r--source/blender/editors/interface/interface_query.c11
-rw-r--r--source/blender/editors/interface/interface_region_search.c8
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc246
-rw-r--r--source/blender/editors/interface/interface_templates.c844
-rw-r--r--source/blender/editors/interface/interface_widgets.c75
-rw-r--r--source/blender/editors/screen/area.c6
-rw-r--r--source/blender/editors/screen/screen_context.c11
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_file/file_draw.c5
-rw-r--r--source/blender/editors/space_file/file_ops.c8
-rw-r--r--source/blender/editors/space_file/filelist.c23
-rw-r--r--source/blender/editors/space_file/filelist.h9
-rw-r--r--source/blender/editors/space_file/filesel.c31
-rw-r--r--source/blender/editors/space_file/space_file.c42
-rw-r--r--source/blender/editors/space_node/drawnode.c10
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/undo/ed_undo.c3
-rw-r--r--source/blender/editors/util/ed_util.c3
-rw-r--r--source/blender/makesdna/DNA_asset_defaults.h7
-rw-r--r--source/blender/makesdna/DNA_asset_types.h55
-rw-r--r--source/blender/makesdna/DNA_screen_types.h10
-rw-r--r--source/blender/makesdna/DNA_space_types.h20
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h5
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesrna/RNA_access.h4
-rw-r--r--source/blender/makesrna/RNA_enum_types.h16
-rw-r--r--source/blender/makesrna/intern/makesrna.c2
-rw-r--r--source/blender/makesrna/intern/rna_ID.c91
-rw-r--r--source/blender/makesrna/intern/rna_asset.c150
-rw-r--r--source/blender/makesrna/intern/rna_internal.h11
-rw-r--r--source/blender/makesrna/intern/rna_palette.c5
-rw-r--r--source/blender/makesrna/intern/rna_space.c230
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c120
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc2
-rw-r--r--source/blender/windowmanager/WM_types.h6
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c2
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
62 files changed, 2691 insertions, 544 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index ef705f8fe37..c282b3f281d 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -117,13 +117,19 @@ def register():
for cls in mod.classes:
register_class(cls)
- # space_userprefs.py
from bpy.props import (
+ CollectionProperty,
EnumProperty,
+ IntProperty,
StringProperty,
)
- from bpy.types import WindowManager
+ from bpy.types import (
+ AssetHandle,
+ WindowManager,
+ WorkSpace,
+ )
+ # space_userprefs.py
def addon_filter_items(_self, _context):
import addon_utils
@@ -165,6 +171,19 @@ def register():
)
# done...
+ # space_view3d.py
+ WorkSpace.active_pose_asset_index = IntProperty(
+ name="Active Pose Asset",
+ # TODO explain which list the index belongs to, or how it can be used to get the pose.
+ description="Per workspace index of the active pose asset"
+ )
+ # Register for window-manager. This is a global property that shouldn't be
+ # written to files.
+ WindowManager.pose_assets = CollectionProperty(
+ type=AssetHandle
+ )
+ # done...
+
def unregister():
from bpy.utils import unregister_class
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index e68f006ccc1..5b974dd9b3f 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -7000,6 +7000,29 @@ class VIEW3D_PT_context_properties(Panel):
rna_prop_ui.draw(self.layout, context, member, object, False)
+class VIEW3D_PT_asset_testing(Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_label = "Asset Testing"
+ bl_category = "Assets"
+
+ def draw(self, context):
+ layout = self.layout
+
+ wm = context.window_manager
+ workspace = context.workspace
+
+ layout.template_asset_view(
+ "pose_assets",
+ workspace,
+ "active_asset_library",
+ wm,
+ "pose_assets",
+ workspace,
+ "active_pose_asset_index"
+ )
+
+
# Grease Pencil Object - Multiframe falloff tools
class VIEW3D_PT_gpencil_multi_frame(Panel):
bl_space_type = 'VIEW_3D'
@@ -7713,6 +7736,7 @@ classes = (
VIEW3D_PT_transform_orientations,
VIEW3D_PT_overlay_gpencil_options,
VIEW3D_PT_context_properties,
+ VIEW3D_PT_asset_testing,
VIEW3D_PT_paint_vertex_context_menu,
VIEW3D_PT_paint_texture_context_menu,
VIEW3D_PT_paint_weight_context_menu,
diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h
index d1f543b1f38..50eb2859279 100644
--- a/source/blender/blenkernel/BKE_asset.h
+++ b/source/blender/blenkernel/BKE_asset.h
@@ -26,6 +26,7 @@
extern "C" {
#endif
+struct AssetLibraryReference;
struct BlendDataReader;
struct BlendWriter;
struct ID;
@@ -45,6 +46,8 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *
const char *name);
void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag);
+void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref);
+
struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
const struct ID *owner_id);
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 3d30188e517..3bb4ad60007 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -340,6 +340,8 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
+const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C);
+
bool CTX_wm_interface_locked(const bContext *C);
/* Gets pointer to the dependency graph.
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 085851ba5e6..2e6493a2fcb 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -332,6 +332,9 @@ typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list,
struct PointerRNA *,
const char *propname);
+/* Listen to notifiers. Only for lists defined in C. */
+typedef void (*uiListListener)(struct uiList *ui_list, wmRegionListenerParams *params);
+
typedef struct uiListType {
struct uiListType *next, *prev;
@@ -341,6 +344,9 @@ typedef struct uiListType {
uiListDrawFilterFunc draw_filter;
uiListFilterItemsFunc filter_items;
+ /* For lists defined in C only. */
+ uiListListener listener;
+
/* RNA integration */
ExtensionRNA rna_ext;
} uiListType;
diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc
index b5a7f5e37a6..f74018b20c5 100644
--- a/source/blender/blenkernel/intern/asset.cc
+++ b/source/blender/blenkernel/intern/asset.cc
@@ -110,6 +110,11 @@ void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag)
BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
}
+void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref)
+{
+ memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref));
+}
+
/* Queries -------------------------------------------- */
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index cbf7a4483c0..741db29d49b 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1398,6 +1398,11 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list)
return ctx_data_collection_get(C, "editable_gpencil_strokes", list);
}
+const AssetLibraryReference *CTX_wm_asset_library(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "asset_library");
+}
+
Depsgraph *CTX_data_depsgraph_pointer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index 99f0342e223..895f06da5fe 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -233,22 +233,30 @@ bool BKE_cryptomatte_find_name(const CryptomatteSession *session,
char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
{
- DynStr *matte_id = BLI_dynstr_new();
+ std::stringstream ss;
+ ss.precision(9);
+
bool first = true;
LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) {
if (!first) {
- BLI_dynstr_append(matte_id, ",");
+ ss << ',';
}
- if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) {
- BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name));
+ blender::StringRef entry_name(entry->name, BLI_strnlen(entry->name, sizeof(entry->name)));
+ if (!entry_name.is_empty()) {
+ ss << entry_name;
}
else {
- BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash);
+ ss << '<' << std::scientific << entry->encoded_hash << '>';
}
first = false;
}
- char *result = BLI_dynstr_get_cstring(matte_id);
- BLI_dynstr_free(matte_id);
+
+ /* Convert result to C string. */
+ const std::string result_string = ss.str();
+ const char *c_str = result_string.c_str();
+ size_t result_len = result_string.size() + 1;
+ char *result = static_cast<char *>(MEM_mallocN(sizeof(char) * result_len, __func__));
+ memcpy(result, c_str, result_len);
return result;
}
diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc
index 85ab7e43d22..d8067bf951f 100644
--- a/source/blender/blenkernel/intern/cryptomatte_test.cc
+++ b/source/blender/blenkernel/intern/cryptomatte_test.cc
@@ -21,6 +21,8 @@
#include "BKE_cryptomatte.hh"
#include "BKE_image.h"
+#include "DNA_node_types.h"
+
#include "RE_pipeline.h"
#include "MEM_guardedalloc.h"
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index f0f46717744..27756e3cbed 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -663,6 +663,7 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
default:
+ BLI_assert(false);
break;
}
break;
@@ -676,6 +677,7 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
case ATTR_DOMAIN_EDGE:
return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
default:
+ BLI_assert(false);
break;
}
break;
@@ -702,11 +704,13 @@ ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attr
case ATTR_DOMAIN_FACE:
return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
default:
+ BLI_assert(false);
break;
}
break;
}
default:
+ BLI_assert(false);
break;
}
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 9617ef68a7d..89451d70c74 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -683,6 +683,9 @@ void BKE_area_region_free(SpaceType *st, ARegion *region)
if (dyn_data->items_filter_neworder) {
MEM_freeN(dyn_data->items_filter_neworder);
}
+ if (dyn_data->customdata) {
+ MEM_freeN(dyn_data->customdata);
+ }
MEM_freeN(dyn_data);
}
if (uilst->properties) {
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index be67b2370e3..b371a36e0cf 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -29,6 +29,7 @@
#include "BLT_translation.h"
+#include "BKE_asset.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
@@ -53,6 +54,13 @@
/* -------------------------------------------------------------------- */
+static void workspace_init_data(ID *id)
+{
+ WorkSpace *workspace = (WorkSpace *)id;
+
+ BKE_asset_library_reference_init_default(&workspace->active_asset_library);
+}
+
static void workspace_free_data(ID *id)
{
WorkSpace *workspace = (WorkSpace *)id;
@@ -180,7 +188,7 @@ IDTypeInfo IDType_ID_WS = {
.translation_context = BLT_I18NCONTEXT_ID_WORKSPACE,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
- .init_data = NULL,
+ .init_data = workspace_init_data,
.copy_data = NULL,
.free_data = workspace_free_data,
.make_local = NULL,
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index c2f8b7b4ebe..0cd18d27587 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -53,6 +53,7 @@
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_asset.h"
#include "BKE_attribute.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
@@ -1964,4 +1965,13 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ {
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "WorkSpace", "AssetLibraryReference", "active_asset_library")) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
+ BKE_asset_library_reference_init_default(&workspace->active_asset_library);
+ }
+ }
+ }
}
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
index 8c5f91561b7..e49b38ca01a 100644
--- a/source/blender/editors/asset/CMakeLists.txt
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -19,6 +19,7 @@ set(INC
../include
../../blenkernel
../../blenlib
+ ../../blenloader
../../makesdna
../../makesrna
../../windowmanager
@@ -30,6 +31,7 @@ set(INC_SYS
set(SRC
asset_edit.cc
+ asset_list.cc
asset_ops.cc
)
diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc
index d20de4141cb..fb16344b37c 100644
--- a/source/blender/editors/asset/asset_edit.cc
+++ b/source/blender/editors/asset/asset_edit.cc
@@ -18,11 +18,23 @@
* \ingroup edasset
*/
+#include <memory>
+#include <string>
+
#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_report.h"
+
+#include "BLI_utility_mixins.hh"
+
+#include "BLO_readfile.h"
#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
#include "UI_interface_icons.h"
@@ -30,6 +42,8 @@
#include "ED_asset.h"
+using namespace blender;
+
bool ED_asset_mark_id(const bContext *C, ID *id)
{
if (id->asset_data) {
@@ -45,6 +59,9 @@ bool ED_asset_mark_id(const bContext *C, ID *id)
UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true);
+ /* Important for asset storage to update properly! */
+ ED_assetlist_storage_tag_main_data_dirty();
+
return true;
}
@@ -57,6 +74,9 @@ bool ED_asset_clear_id(ID *id)
/* Don't clear fake user here, there's no guarantee that it was actually set by
* #ED_asset_mark_id(), it might have been something/someone else. */
+ /* Important for asset storage to update properly! */
+ ED_assetlist_storage_tag_main_data_dirty();
+
return true;
}
@@ -65,3 +85,135 @@ bool ED_asset_can_make_single_from_context(const bContext *C)
/* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */
return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr;
}
+
+/* TODO better place? */
+/* TODO What about the setter and the itemf? */
+#include "BKE_preferences.h"
+#include "DNA_asset_types.h"
+#include "DNA_userdef_types.h"
+int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library)
+{
+ /* Simple case: Predefined repo, just set the value. */
+ if (library->type < ASSET_LIBRARY_CUSTOM) {
+ return library->type;
+ }
+
+ /* Note that the path isn't checked for validity here. If an invalid library path is used, the
+ * Asset Browser can give a nice hint on what's wrong. */
+ const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
+ &U, library->custom_library_index);
+ if (user_library) {
+ return ASSET_LIBRARY_CUSTOM + library->custom_library_index;
+ }
+
+ BLI_assert(0);
+ return ASSET_LIBRARY_LOCAL;
+}
+
+AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
+{
+ AssetLibraryReference library;
+
+ /* Simple case: Predefined repo, just set the value. */
+ if (value < ASSET_LIBRARY_CUSTOM) {
+ library.type = value;
+ library.custom_library_index = -1;
+ BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL));
+ return library;
+ }
+
+ const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
+ &U, value - ASSET_LIBRARY_CUSTOM);
+
+ /* Note that the path isn't checked for validity here. If an invalid library path is used, the
+ * Asset Browser can give a nice hint on what's wrong. */
+ const bool is_valid = (user_library->name[0] && user_library->path[0]);
+ if (!user_library) {
+ library.type = ASSET_LIBRARY_LOCAL;
+ library.custom_library_index = -1;
+ }
+ else if (user_library && is_valid) {
+ library.custom_library_index = value - ASSET_LIBRARY_CUSTOM;
+ library.type = ASSET_LIBRARY_CUSTOM;
+ }
+ return library;
+}
+
+class AssetTemporaryIDConsumer : NonCopyable, NonMovable {
+ const AssetHandle &handle_;
+ TempLibraryContext *temp_lib_context_ = nullptr;
+
+ public:
+ AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle)
+ {
+ }
+ ~AssetTemporaryIDConsumer()
+ {
+ if (temp_lib_context_) {
+ BLO_library_temp_free(temp_lib_context_);
+ }
+ }
+
+ ID *get_local_id()
+ {
+ return ED_assetlist_asset_local_id_get(&handle_);
+ }
+
+ ID *import_id(const AssetLibraryReference &asset_library,
+ ID_Type id_type,
+ Main &bmain,
+ ReportList &reports)
+ {
+ std::string asset_path = ED_assetlist_asset_filepath_get(asset_library, handle_);
+ if (asset_path.empty()) {
+ return nullptr;
+ }
+
+ char blend_file_path[FILE_MAX_LIBEXTRA];
+ char *group = NULL;
+ char *asset_name = NULL;
+ BLO_library_path_explode(asset_path.c_str(), blend_file_path, &group, &asset_name);
+
+ temp_lib_context_ = BLO_library_temp_load_id(
+ &bmain, blend_file_path, id_type, asset_name, &reports);
+
+ if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) {
+ BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path);
+ return nullptr;
+ }
+
+ BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type);
+ return temp_lib_context_->temp_id;
+ }
+};
+
+AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle)
+{
+ if (!handle) {
+ return nullptr;
+ }
+ return reinterpret_cast<AssetTempIDConsumer *>(
+ OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle));
+}
+
+void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
+{
+ OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer);
+}
+
+ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
+ const AssetLibraryReference *asset_library,
+ ID_Type id_type,
+ Main *bmain,
+ ReportList *reports)
+{
+ if (!(consumer_ && asset_library && bmain && reports)) {
+ return nullptr;
+ }
+ AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_);
+
+ if (ID *local_id = consumer->get_local_id()) {
+ return local_id;
+ }
+ return consumer->import_id(*asset_library, id_type, *bmain, *reports);
+}
diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc
new file mode 100644
index 00000000000..c418ef919e7
--- /dev/null
+++ b/source/blender/editors/asset/asset_list.cc
@@ -0,0 +1,563 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup edasset
+ *
+ * Abstractions to manage runtime asset lists with a global cache for multiple UI elements to
+ * access.
+ * Internally this uses the #FileList API and structures from `filelist.c`. This is just because it
+ * contains most necessary logic already and there's not much time for a more long-term solution.
+ */
+
+#include <optional>
+#include <string>
+
+#include "BKE_asset.h"
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "BLI_function_ref.hh"
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_path_util.h"
+#include "BLI_utility_mixins.hh"
+
+#include "DNA_asset_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_preferences.h"
+
+#include "ED_asset.h"
+#include "ED_fileselect.h"
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* XXX uses private header of file-space. */
+#include "../space_file/filelist.h"
+
+using namespace blender;
+
+/**
+ * Wrapper to add logic to the AssetLibraryReference DNA struct.
+ */
+class AssetLibraryReferenceWrapper {
+ const AssetLibraryReference &reference_;
+
+ public:
+ /* Intentionally not `explicit`, allow implicit conversion for convienience. Might have to be
+ * NOLINT */
+ AssetLibraryReferenceWrapper(const AssetLibraryReference &reference);
+ ~AssetLibraryReferenceWrapper() = default;
+
+ friend bool operator==(const AssetLibraryReferenceWrapper &a,
+ const AssetLibraryReferenceWrapper &b);
+ uint64_t hash() const;
+};
+
+AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference)
+ : reference_(reference)
+{
+}
+
+bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b)
+{
+ return (a.reference_.type == b.reference_.type) && (a.reference_.type == ASSET_LIBRARY_CUSTOM) ?
+ (a.reference_.custom_library_index == b.reference_.custom_library_index) :
+ true;
+}
+
+uint64_t AssetLibraryReferenceWrapper::hash() const
+{
+ uint64_t hash1 = DefaultHash<decltype(reference_.type)>{}(reference_.type);
+ if (reference_.type != ASSET_LIBRARY_CUSTOM) {
+ return hash1;
+ }
+
+ uint64_t hash2 = DefaultHash<decltype(reference_.custom_library_index)>{}(
+ reference_.custom_library_index);
+ return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Asset list API
+ *
+ * Internally re-uses #FileList from the File Browser. It does all the heavy lifting already.
+ * \{ */
+
+/**
+ * RAII wrapper for `FileList`
+ */
+class FileListWrapper : NonCopyable {
+ static void filelist_free_fn(FileList *list)
+ {
+ filelist_free(list);
+ MEM_freeN(list);
+ }
+
+ std::unique_ptr<FileList, decltype(&filelist_free_fn)> file_list_;
+
+ public:
+ explicit FileListWrapper(eFileSelectType filesel_type)
+ : file_list_(filelist_new(filesel_type), filelist_free_fn)
+ {
+ }
+ FileListWrapper(FileListWrapper &&other) = default;
+ FileListWrapper &operator=(FileListWrapper &&other) = default;
+ ~FileListWrapper()
+ {
+ /* Destructs the owned pointer. */
+ file_list_ = nullptr;
+ }
+
+ operator FileList *() const
+ {
+ return file_list_.get();
+ }
+};
+
+class PreviewTimer : NonCopyable {
+ /* Non-owning! The Window-Manager registers and owns this. */
+ wmTimer *timer_ = nullptr;
+
+ public:
+ void ensureRunning(const bContext *C)
+ {
+ if (!timer_) {
+ timer_ = WM_event_add_timer_notifier(
+ CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01);
+ }
+ }
+
+ void stop(const bContext *C)
+ {
+ if (timer_) {
+ WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_);
+ timer_ = nullptr;
+ }
+ }
+};
+
+class AssetList : NonCopyable {
+ FileListWrapper filelist_;
+ AssetLibraryReference library_ref_;
+ PreviewTimer previews_timer_;
+
+ public:
+ AssetList() = delete;
+ AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref);
+ AssetList(AssetList &&other) = default;
+ ~AssetList() = default;
+
+ void setup(const AssetFilterSettings *filter_settings = nullptr);
+ void fetch(const bContext &C);
+ void ensurePreviewsJob(bContext *C);
+
+ bool needsRefetch() const;
+ void iterate(AssetListIterFn fn) const;
+ bool listen(const wmNotifier &notifier) const;
+ void tagMainDataDirty() const;
+ void remapID(ID *id_old, ID *id_new) const;
+ StringRef filepath() const;
+};
+
+AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref)
+ : filelist_(filesel_type), library_ref_(asset_library_ref)
+{
+}
+
+void AssetList::setup(const AssetFilterSettings *filter_settings)
+{
+ FileList *files = filelist_;
+
+ /* TODO there should only be one (FileSelectAssetLibraryUID vs. AssetLibraryReference). */
+ FileSelectAssetLibraryUID file_asset_lib_ref;
+ file_asset_lib_ref.type = library_ref_.type;
+ file_asset_lib_ref.custom_library_index = library_ref_.custom_library_index;
+
+ bUserAssetLibrary *user_library = nullptr;
+
+ /* Ensure valid repository, or fall-back to local one. */
+ if (library_ref_.type == ASSET_LIBRARY_CUSTOM) {
+ BLI_assert(library_ref_.custom_library_index >= 0);
+
+ user_library = BKE_preferences_asset_library_find_from_index(
+ &U, library_ref_.custom_library_index);
+ }
+
+ /* Relevant bits from file_refresh(). */
+ /* TODO pass options properly. */
+ filelist_setrecursion(files, 1);
+ filelist_setsorting(files, FILE_SORT_ALPHA, false);
+ filelist_setlibrary(files, &file_asset_lib_ref);
+ /* TODO different filtering settings require the list to be reread. That's a no-go for when we
+ * want to allow showing the same asset library with different filter settings (as in,
+ * different ID types). The filelist needs to be made smarter somehow, maybe goes together with
+ * the plan to separate the view (preview caching, filtering, etc. ) from the data. */
+ filelist_setfilter_options(
+ files,
+ filter_settings != nullptr,
+ true,
+ true, /* Just always hide parent, prefer to not add an extra user option for this. */
+ FILE_TYPE_BLENDERLIB,
+ filter_settings ? filter_settings->id_types : FILTER_ID_ALL,
+ true,
+ "",
+ "");
+
+ char path[FILE_MAXDIR] = "";
+ if (user_library) {
+ BLI_strncpy(path, user_library->path, sizeof(path));
+ filelist_setdir(files, path);
+ }
+ else {
+ filelist_setdir(files, path);
+ }
+}
+
+void AssetList::fetch(const bContext &C)
+{
+ FileList *files = filelist_;
+
+ if (filelist_needs_force_reset(files)) {
+ filelist_readjob_stop(files, CTX_wm_manager(&C));
+ filelist_clear(files);
+ }
+
+ if (filelist_needs_reading(files)) {
+ if (!filelist_pending(files)) {
+ filelist_readjob_start(files, NC_ASSET | ND_ASSET_LIST_READING, &C);
+ }
+ }
+ filelist_sort(files);
+ filelist_filter(files);
+}
+
+bool AssetList::needsRefetch() const
+{
+ return filelist_needs_force_reset(filelist_);
+}
+
+void AssetList::iterate(AssetListIterFn fn) const
+{
+ FileList *files = filelist_;
+ int numfiles = filelist_files_ensure(files);
+
+ for (int i = 0; i < numfiles; i++) {
+ FileDirEntry *file = filelist_file(files, i);
+ if (!fn(*file)) {
+ break;
+ }
+ }
+}
+
+void AssetList::ensurePreviewsJob(bContext *C)
+{
+ FileList *files = filelist_;
+ int numfiles = filelist_files_ensure(files);
+
+ filelist_cache_previews_set(files, true);
+ filelist_file_cache_slidingwindow_set(files, 256);
+ /* TODO fetch all previews for now. */
+ filelist_file_cache_block(files, numfiles / 2);
+ filelist_cache_previews_update(files);
+
+ {
+ const bool previews_running = filelist_cache_previews_running(files);
+ if (previews_running) {
+ previews_timer_.ensureRunning(C);
+ }
+ else {
+ /* Preview is not running, no need to keep generating update events! */
+ previews_timer_.stop(C);
+ }
+ }
+}
+
+/**
+ * \return True if the asset-list needs a UI redraw.
+ */
+bool AssetList::listen(const wmNotifier &notifier) const
+{
+ switch (notifier.category) {
+ case NC_ASSET:
+ if (ELEM(notifier.data, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) {
+ return true;
+ }
+ if (ELEM(notifier.action, NA_ADDED, NA_REMOVED)) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+void AssetList::tagMainDataDirty() const
+{
+ if (filelist_needs_reset_on_main_changes(filelist_)) {
+ /* Full refresh of the file list if local asset data was changed. Refreshing this view
+ * is cheap and users expect this to be updated immediately. */
+ filelist_tag_force_reset(filelist_);
+ }
+}
+
+void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const
+{
+ /* Trigger full refetch of the file list if main data was changed, don't even attempt remap
+ * pointers. We could give file list types a id-remap callback, but it's probably not worth it.
+ * Refreshing local file lists is relatively cheap. */
+ tagMainDataDirty();
+}
+
+StringRef AssetList::filepath() const
+{
+ return filelist_dir(filelist_);
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Runtime asset list cache
+ * \{ */
+
+/**
+ * Class managing a global asset list map, each entry being a list for a specific asset library.
+ */
+class AssetListStorage {
+ using AssetListMap = Map<AssetLibraryReferenceWrapper, AssetList>;
+
+ public:
+ /* Purely static class, can't instantiate this. */
+ AssetListStorage() = delete;
+
+ static void fetch_library(const AssetLibraryReference &library_reference,
+ const bContext &C,
+ const AssetFilterSettings *filter_settings = nullptr);
+ static void destruct();
+ static AssetList *lookup_list(const AssetLibraryReference &library_ref);
+ static void tagMainDataDirty();
+ static void remapID(ID *id_new, ID *id_old);
+
+ private:
+ static std::optional<eFileSelectType> asset_library_reference_to_fileselect_type(
+ const AssetLibraryReference &library_reference);
+
+ using is_new_t = bool;
+ static std::tuple<AssetList &, is_new_t> ensure_list_storage(
+ const AssetLibraryReference &library_reference, eFileSelectType filesel_type);
+
+ static AssetListMap &global_storage();
+};
+
+void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference,
+ const bContext &C,
+ const AssetFilterSettings *filter_settings)
+{
+ std::optional filesel_type = asset_library_reference_to_fileselect_type(library_reference);
+ if (!filesel_type) {
+ return;
+ }
+
+ std::tuple list_create_info = ensure_list_storage(library_reference, *filesel_type);
+ AssetList &list = std::get<0>(list_create_info);
+ const bool is_new = std::get<1>(list_create_info);
+ if (is_new || list.needsRefetch()) {
+ list.setup(filter_settings);
+ list.fetch(C);
+ }
+}
+
+void AssetListStorage::destruct()
+{
+ global_storage().~AssetListMap();
+}
+
+AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref)
+{
+ return global_storage().lookup_ptr(library_ref);
+}
+
+void AssetListStorage::tagMainDataDirty()
+{
+ for (AssetList &list : global_storage().values()) {
+ list.tagMainDataDirty();
+ }
+}
+
+void AssetListStorage::remapID(ID *id_new, ID *id_old)
+{
+ for (AssetList &list : global_storage().values()) {
+ list.remapID(id_new, id_old);
+ }
+}
+
+std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_fileselect_type(
+ const AssetLibraryReference &library_reference)
+{
+ switch (library_reference.type) {
+ case ASSET_LIBRARY_CUSTOM:
+ return FILE_LOADLIB;
+ case ASSET_LIBRARY_LOCAL:
+ return FILE_MAIN_ASSET;
+ }
+
+ return std::nullopt;
+}
+
+std::tuple<AssetList &, AssetListStorage::is_new_t> AssetListStorage::ensure_list_storage(
+ const AssetLibraryReference &library_reference, eFileSelectType filesel_type)
+{
+ AssetListMap &storage = global_storage();
+
+ if (AssetList *list = storage.lookup_ptr(library_reference)) {
+ return {*list, false};
+ }
+ storage.add(library_reference, AssetList(filesel_type, library_reference));
+ return {storage.lookup(library_reference), true};
+}
+
+/**
+ * Wrapper for Construct on First Use idiom, to avoid the Static Initialization Fiasco.
+ */
+AssetListStorage::AssetListMap &AssetListStorage::global_storage()
+{
+ static AssetListMap global_storage_;
+ return global_storage_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
+
+/**
+ * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done,
+ * and may return earlier.
+ */
+void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference,
+ const AssetFilterSettings *filter_settings,
+ const bContext *C)
+{
+ AssetListStorage::fetch_library(*library_reference, *C, filter_settings);
+}
+
+void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C)
+{
+
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ list->ensurePreviewsJob(C);
+ }
+}
+
+/* TODO expose AssetList with an iterator? */
+void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ list->iterate(fn);
+ }
+}
+
+std::string ED_assetlist_asset_filepath_get(const AssetLibraryReference &library_reference,
+ const AssetHandle &asset_handle)
+{
+ if (asset_handle.file_data->id || !asset_handle.file_data->asset_data) {
+ return nullptr;
+ }
+ const char *library_path = ED_assetlist_library_path(&library_reference);
+ if (!library_path) {
+ return nullptr;
+ }
+ const char *asset_relpath = asset_handle.file_data->relpath;
+
+ char path[FILE_MAX_LIBEXTRA];
+ BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath);
+
+ return path;
+}
+
+ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle)
+{
+ return asset_handle->file_data->asset_data ? asset_handle->file_data->id : nullptr;
+}
+
+ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
+{
+ ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data);
+ if (imbuf) {
+ return imbuf;
+ }
+
+ return filelist_geticon_image_ex(asset_handle->file_data);
+}
+
+const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ return list->filepath().data();
+ }
+ return nullptr;
+}
+
+/**
+ * \return True if the region needs a UI redraw.
+ */
+bool ED_assetlist_listen(const AssetLibraryReference *library_reference,
+ const wmNotifier *notifier)
+{
+ AssetList *list = AssetListStorage::lookup_list(*library_reference);
+ if (list) {
+ return list->listen(*notifier);
+ }
+ return false;
+}
+
+/**
+ * Tag all asset lists in the storage that show main data as needing an update (refetch).
+ *
+ * This only tags the data. If the asset list is visible on screen, the space is still responsible
+ * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list
+ * needs a redraw for a given notifier.
+ */
+void ED_assetlist_storage_tag_main_data_dirty()
+{
+ AssetListStorage::tagMainDataDirty();
+}
+
+/**
+ * Remapping of ID pointers within the asset lists. Typically called when an ID is deleted to clear
+ * all references to it (\a id_new is null then).
+ */
+void ED_assetlist_storage_id_remap(ID *id_old, ID *id_new)
+{
+ AssetListStorage::remapID(id_old, id_new);
+}
+
+/**
+ * Can't wait for static deallocation to run. There's nested data allocated with our guarded
+ * allocator, it will complain about unfreed memory on exit.
+ */
+void ED_assetlist_storage_exit()
+{
+ AssetListStorage::destruct();
+}
+
+/** \} */
diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h
index dd505167fe5..0fe07f72af5 100644
--- a/source/blender/editors/include/ED_asset.h
+++ b/source/blender/editors/include/ED_asset.h
@@ -20,17 +20,67 @@
#pragma once
+#include "DNA_ID_enums.h"
+#include "DNA_asset_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+struct AssetFilterSettings;
+struct AssetLibraryReference;
+struct bContext;
+struct Main;
+struct ReportList;
+struct wmNotifier;
+
+typedef struct AssetTempIDConsumer AssetTempIDConsumer;
+
bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
bool ED_asset_clear_id(struct ID *id);
bool ED_asset_can_make_single_from_context(const struct bContext *C);
+int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library);
+struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
+
+AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle);
+void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer);
+struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer,
+ const AssetLibraryReference *asset_library,
+ ID_Type id_type,
+ struct Main *bmain,
+ struct ReportList *reports);
+
+void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference,
+ const struct AssetFilterSettings *filter_settings,
+ const struct bContext *C);
+void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference,
+ struct bContext *C);
+void ED_assetlist_storage_tag_main_data_dirty(void);
+void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new);
+void ED_assetlist_storage_exit(void);
+
+ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle);
+struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle);
+const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference);
+
+bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference,
+ const struct wmNotifier *notifier);
+
void ED_operatortypes_asset(void);
#ifdef __cplusplus
}
#endif
+
+/* TODO move to C++ asset-list header? */
+#ifdef __cplusplus
+std::string ED_assetlist_asset_filepath_get(const AssetLibraryReference &library_reference,
+ const AssetHandle &asset_handle);
+
+# include "BLI_function_ref.hh"
+/* Can return false to stop iterating. */
+using AssetListIterFn = blender::FunctionRef<bool(FileDirEntry &)>;
+void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn);
+#endif
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 983ae94b637..db61c1b082d 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -136,13 +136,9 @@ void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y);
void ED_operatormacros_file(void);
-void ED_fileselect_clear(struct wmWindowManager *wm,
- struct Scene *owner_scene,
- struct SpaceFile *sfile);
+void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile);
-void ED_fileselect_exit(struct wmWindowManager *wm,
- struct Scene *owner_scene,
- struct SpaceFile *sfile);
+void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile);
@@ -166,7 +162,7 @@ int ED_file_icon(const struct FileDirEntry *file);
void ED_file_read_bookmarks(void);
-void ED_file_change_dir_ex(struct bContext *C, struct bScreen *screen, struct ScrArea *area);
+void ED_file_change_dir_ex(struct bContext *C, struct ScrArea *area);
void ED_file_change_dir(struct bContext *C);
void ED_file_path_button(struct bScreen *screen,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index dfe0898a85b..21ff7532999 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -34,6 +34,7 @@ extern "C" {
/* Struct Declarations */
struct ARegion;
+struct AssetFilterSettings;
struct AutoComplete;
struct EnumPropertyItem;
struct FileSelectParams;
@@ -375,6 +376,9 @@ typedef enum {
/** Buttons with value >= #UI_BTYPE_SEARCH_MENU don't get undo pushes. */
UI_BTYPE_SEARCH_MENU = 41 << 9,
UI_BTYPE_EXTRA = 42 << 9,
+ /** A preview image (#PreviewImage), with text under it. Typically bigger than normal buttons and
+ * laid out in a grid, e.g. like the File Browser in thumbnail display mode. */
+ UI_BTYPE_PREVIEW_TILE = 43 << 9,
UI_BTYPE_HOTKEY_EVENT = 46 << 9,
/** Non-interactive image, used for splash screen */
UI_BTYPE_IMAGE = 47 << 9,
@@ -2159,6 +2163,22 @@ void uiTemplateList(uiLayout *layout,
int columns,
bool sort_reverse,
bool sort_lock);
+struct uiList *uiTemplateList_ex(uiLayout *layout,
+ struct bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ struct PointerRNA *dataptr,
+ const char *propname,
+ struct PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ bool sort_reverse,
+ bool sort_lock,
+ void *customdata);
void uiTemplateNodeLink(uiLayout *layout,
struct bContext *C,
struct bNodeTree *ntree,
@@ -2204,6 +2224,18 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows);
void uiTemplateFileSelectPath(uiLayout *layout,
struct bContext *C,
struct FileSelectParams *params);
+void uiTemplateAssetView(struct uiLayout *layout,
+ struct bContext *C,
+ const char *list_id,
+ struct PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ struct PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ struct PointerRNA *active_dataptr,
+ const char *active_propname,
+ const struct AssetFilterSettings *filter_settings,
+ const char *activate_opname,
+ const char *drag_opname);
/* items */
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
@@ -2457,6 +2489,7 @@ typedef struct uiDragColorHandle {
void ED_operatortypes_ui(void);
void ED_keymap_ui(struct wmKeyConfig *keyconf);
+void ED_uilisttypes_ui(void);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
bool UI_drop_color_poll(struct bContext *C,
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index 266a538b6c3..37cf7229ffb 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -105,7 +105,10 @@ int UI_iconfile_get_index(const char *filename);
struct PreviewImage *UI_icon_to_preview(int icon_id);
-int UI_icon_from_rnaptr(struct bContext *C, struct PointerRNA *ptr, int rnaicon, const bool big);
+int UI_icon_from_rnaptr(const struct bContext *C,
+ struct PointerRNA *ptr,
+ int rnaicon,
+ const bool big);
int UI_icon_from_idcode(const int idcode);
int UI_icon_from_library(const struct ID *id);
int UI_icon_from_object_mode(const int mode);
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 421019bebb8..f41b977b9bb 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -66,6 +66,7 @@ set(SRC
interface_region_tooltip.c
interface_regions.c
interface_style.c
+ interface_template_asset_view.cc
interface_template_search_menu.c
interface_template_search_operator.c
interface_templates.c
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index a5a5a69728e..01fd9b5a82c 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -714,15 +714,24 @@ static uiAfterFunc *ui_afterfunc_new(void)
* For executing operators after the button is pressed.
* (some non operator buttons need to trigger operators), see: T37795.
*
+ * \param context_but: A button from which to get the context from (`uiBut.context`) for the
+ * operator execution.
+ *
* \note Can only call while handling buttons.
*/
-PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+static PointerRNA *ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot,
+ int opcontext,
+ bool create_props,
+ const uiBut *context_but)
{
PointerRNA *ptr = NULL;
uiAfterFunc *after = ui_afterfunc_new();
after->optype = ot;
after->opcontext = opcontext;
+ if (context_but && context_but->context) {
+ after->context = CTX_store_copy(context_but->context);
+ }
if (create_props) {
ptr = MEM_callocN(sizeof(PointerRNA), __func__);
@@ -733,6 +742,11 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext,
return ptr;
}
+PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+{
+ return ui_handle_afterfunc_add_operator_ex(ot, opcontext, create_props, NULL);
+}
+
static void popup_check(bContext *C, wmOperator *op)
{
if (op && op->type->check) {
@@ -1057,6 +1071,43 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
data->applied = true;
}
+/**
+ * \returns true if the operator was executed, otherwise false.
+ */
+static bool ui_list_invoke_item_operator(bContext *C,
+ const ARegion *region,
+ const wmEvent *event,
+ const char *name)
+{
+ wmOperatorType *drag_ot = WM_operatortype_find(name, false);
+ const uiBut *hovered_but = ui_but_find_mouse_over(region, event);
+
+ if (!ui_but_context_poll_operator(C, drag_ot, hovered_but)) {
+ return false;
+ }
+
+ /* Allow the context to be set from the hovered button, so the list item draw callback can set
+ * context for the operators. */
+ ui_handle_afterfunc_add_operator_ex(drag_ot, WM_OP_INVOKE_DEFAULT, false, hovered_but);
+ return true;
+}
+
+static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ wmWindow *window = CTX_wm_window(C);
+
+ uiBut *listbox = ui_list_find_mouse_over(data->region, window->eventstate);
+ if (listbox) {
+ uiList *list = listbox->custom_data;
+ if (list && list->custom_activate_opname) {
+ ui_list_invoke_item_operator(
+ C, data->region, window->eventstate, list->custom_activate_opname);
+ }
+ }
+
+ ui_apply_but_ROW(C, block, but, data);
+}
+
static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
{
if (!data->str) {
@@ -1525,7 +1576,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
*/
if (drag_info->is_xy_lock_init == false) {
/* first store the buttons original coords */
- uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true);
+ uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL);
if (but) {
if (but->flag & UI_BUT_DRAG_LOCK) {
@@ -1596,7 +1647,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
uiBut *but = ui_but_find_mouse_over_ex(
- region, drag_info->xy_init[0], drag_info->xy_init[1], true);
+ region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL);
if (but) {
ui_apply_but_undo(but);
@@ -2158,9 +2209,11 @@ static void ui_apply_but(
ui_apply_but_TOG(C, but, data);
break;
case UI_BTYPE_ROW:
- case UI_BTYPE_LISTROW:
ui_apply_but_ROW(C, block, but, data);
break;
+ case UI_BTYPE_LISTROW:
+ ui_apply_but_LISTROW(C, block, but, data);
+ break;
case UI_BTYPE_TAB:
ui_apply_but_TAB(C, but, data);
break;
@@ -4165,7 +4218,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
uiButtonActivateType activate_type)
{
ARegion *region = CTX_wm_region(C);
- uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true);
+ uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL);
if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
/* exit listrow */
@@ -4659,6 +4712,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) {
ret = WM_UI_HANDLER_CONTINUE;
}
+ /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event
+ * will be sent for the list to work with. */
+ const uiBut *listbox = ui_list_find_mouse_over(data->region, event);
+ if (listbox) {
+ const uiList *ui_list = listbox->custom_data;
+ if (ui_list && ui_list->custom_drag_opname) {
+ ret = WM_UI_HANDLER_CONTINUE;
+ }
+ }
button_activate_state(C, but, BUTTON_STATE_EXIT);
return ret;
}
@@ -7683,6 +7745,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_IMAGE:
case UI_BTYPE_PROGRESS_BAR:
case UI_BTYPE_NODE_SOCKET:
+ case UI_BTYPE_PREVIEW_TILE:
retval = ui_do_but_EXIT(C, but, data, event);
break;
case UI_BTYPE_HISTOGRAM:
@@ -9017,6 +9080,109 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
return retval;
}
+static bool ui_but_is_listrow(const uiBut *but)
+{
+ return but->type == UI_BTYPE_LISTROW;
+}
+
+/**
+ * Activate the underlying list-row button, so the row is highlighted.
+ * Early exits if \a activate_dragging is true, but the custom drag operator fails to execute.
+ * Gives the wanted behavior where the item is activated on a tweak event when the custom drag
+ * operator is executed.
+ */
+static int ui_list_activate_hovered_row(bContext *C,
+ ARegion *region,
+ const uiList *ui_list,
+ const wmEvent *event,
+ bool activate_dragging)
+{
+ const bool do_drag = activate_dragging && ui_list->custom_drag_opname;
+
+ if (do_drag) {
+ if (!ui_list_invoke_item_operator(C, region, event, ui_list->custom_drag_opname)) {
+ return WM_UI_HANDLER_CONTINUE;
+ }
+ }
+
+ const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+ uiBut *listrow = ui_but_find_mouse_over_ex(
+ region, mouse_xy[0], mouse_xy[1], false, ui_but_is_listrow);
+ if (listrow) {
+ const char *custom_activate_opname = ui_list->custom_activate_opname;
+
+ /* Hacky: Ensure the custom activate operator is not called when the custom drag operator was.
+ * Only one should run! */
+ if (activate_dragging && do_drag) {
+ ((uiList *)ui_list)->custom_activate_opname = NULL;
+ }
+
+ /* Simulate click on listrow button itself (which may be overlapped by another button). Also
+ * calls the custom activate operator (ui_list->custom_activate_opname). */
+ UI_but_execute(C, region, listrow);
+
+ ((uiList *)ui_list)->custom_activate_opname = custom_activate_opname;
+ }
+
+ return WM_UI_HANDLER_BREAK;
+}
+
+static bool ui_list_is_hovering_draggable_but(bContext *C,
+ const uiList *list,
+ const ARegion *region,
+ const wmEvent *event)
+{
+ /* On a tweak event, uses the coordinates from where tweaking was started. */
+ const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+ const uiBut *hovered_but = ui_but_find_mouse_over_ex(
+ region, mouse_xy[0], mouse_xy[1], false, NULL);
+
+ if (list->custom_drag_opname) {
+ wmOperatorType *drag_ot = WM_operatortype_find(list->custom_drag_opname, false);
+ if (ui_but_context_poll_operator(C, drag_ot, hovered_but)) {
+ return true;
+ }
+ }
+
+ return (hovered_but && hovered_but->dragpoin);
+}
+
+static int ui_list_handle_click_drag(bContext *C,
+ const uiList *ui_list,
+ ARegion *region,
+ const wmEvent *event)
+{
+ if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ return WM_HANDLER_CONTINUE;
+ }
+
+ int retval = WM_HANDLER_CONTINUE;
+
+ const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event);
+ bool activate = false;
+ bool activate_dragging = false;
+
+ if (event->type == EVT_TWEAK_L) {
+ if (is_draggable) {
+ activate_dragging = true;
+ activate = true;
+ }
+ }
+ /* KM_CLICK is only sent after an uncaught release event, so the forground button gets all
+ * regular events (including mouse presses to start dragging) and this part only kicks in if it
+ * hasn't handled the release event. Note that if there's no overlaid button, the row selects
+ * on the press event already via regular UI_BTYPE_LISTROW handling. */
+ else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) {
+ activate = true;
+ }
+
+ if (activate) {
+ retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging);
+ }
+
+ return retval;
+}
+
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
{
int retval = WM_UI_HANDLER_CONTINUE;
@@ -9050,7 +9216,10 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
}
- if (val == KM_PRESS) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ retval = ui_list_handle_click_drag(C, ui_list, region, event);
+ }
+ else if (val == KM_PRESS) {
if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
!IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl &&
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c16c3d2c49a..39bac71e0c1 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -2205,7 +2205,7 @@ int UI_icon_from_library(const ID *id)
return ICON_NONE;
}
-int UI_icon_from_rnaptr(bContext *C, PointerRNA *ptr, int rnaicon, const bool big)
+int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, const bool big)
{
ID *id = NULL;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 4c96512b4f3..7a2c33fb569 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1024,8 +1024,18 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle,
int state,
uiMenuItemSeparatorType separator_type,
int *r_xmax);
-void ui_draw_preview_item(
- const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state);
+void ui_draw_preview_item(const struct uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ int state,
+ eFontStyle_Align text_align);
+void ui_draw_preview_item_stateless(const struct uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ const uchar text_col[4],
+ eFontStyle_Align text_align);
#define UI_TEXT_MARGIN_X 0.4f
#define UI_POPUP_MARGIN (UI_DPI_FAC * 12)
@@ -1110,10 +1120,12 @@ bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, in
uiBut *ui_list_find_mouse_over(struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
+typedef bool (*uiButFindPoll)(const uiBut *but);
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
const int x,
const int y,
- const bool labeledit) ATTR_WARN_UNUSED_RESULT;
+ const bool labeledit,
+ uiButFindPoll find_poll) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_mouse_over(const struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_rect_over(const struct ARegion *region,
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index aa10d092f5e..aaed1b779a1 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -265,10 +265,8 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
}
/* x and y are only used in case event is NULL... */
-uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
- const int x,
- const int y,
- const bool labeledit)
+uiBut *ui_but_find_mouse_over_ex(
+ const ARegion *region, const int x, const int y, const bool labeledit, uiButFindPoll find_poll)
{
uiBut *butover = NULL;
@@ -280,6 +278,9 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
ui_window_to_block_fl(region, block, &mx, &my);
LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
+ if (find_poll && find_poll(but) == false) {
+ continue;
+ }
if (ui_but_is_interactive(but, labeledit)) {
if (but->pie_dir != UI_RADIAL_NONE) {
if (ui_but_isect_pie_seg(block, but)) {
@@ -308,7 +309,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event)
{
- return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0);
+ return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL);
}
uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 12044863b8c..14239e586e5 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -599,8 +599,12 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
ui_searchbox_butrect(&rect, data, a);
/* widget itself */
- ui_draw_preview_item(
- &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state);
+ ui_draw_preview_item(&data->fstyle,
+ &rect,
+ data->items.names[a],
+ data->items.icons[a],
+ state,
+ UI_STYLE_TEXT_LEFT);
}
/* indicate more */
diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
new file mode 100644
index 00000000000..2295f21b101
--- /dev/null
+++ b/source/blender/editors/interface/interface_template_asset_view.cc
@@ -0,0 +1,246 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_screen.h"
+
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "BLO_readfile.h"
+
+#include "ED_asset.h"
+#include "ED_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+
+#include "interface_intern.h"
+
+struct AssetViewListData {
+ AssetLibraryReference asset_library;
+ bScreen *screen;
+};
+
+static void asset_view_item_but_drag_set(uiBut *but,
+ AssetViewListData *list_data,
+ AssetHandle *asset_handle)
+{
+ if (ID *id = asset_handle->file_data->id) {
+ UI_but_drag_set_id(but, id);
+ }
+ else {
+ const blender::StringRef asset_list_path = ED_assetlist_library_path(
+ &list_data->asset_library);
+ char blend_path[FILE_MAX_LIBEXTRA];
+
+ char path[FILE_MAX_LIBEXTRA];
+ BLI_join_dirfile(path, sizeof(path), asset_list_path.data(), asset_handle->file_data->relpath);
+ if (BLO_library_path_explode(path, blend_path, nullptr, nullptr)) {
+ ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle);
+ UI_but_drag_set_asset(but,
+ asset_handle->file_data->name,
+ BLI_strdup(blend_path),
+ asset_handle->file_data->blentype,
+ asset_handle->file_data->preview_icon_id,
+ imbuf,
+ 1.0f);
+ }
+ }
+}
+
+static void asset_view_draw_item(uiList *ui_list,
+ bContext *UNUSED(C),
+ uiLayout *layout,
+ PointerRNA *UNUSED(dataptr),
+ PointerRNA *itemptr,
+ int UNUSED(icon),
+ PointerRNA *UNUSED(active_dataptr),
+ const char *UNUSED(active_propname),
+ int UNUSED(index),
+ int UNUSED(flt_flag))
+{
+ AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
+
+ BLI_assert(RNA_struct_is_a(itemptr->type, &RNA_AssetHandle));
+ AssetHandle *asset_handle = (AssetHandle *)itemptr->data;
+
+ uiLayoutSetContextPointer(layout, "asset_handle", itemptr);
+
+ uiBlock *block = uiLayoutGetBlock(layout);
+ /* TODO ED_fileselect_init_layout(). Share somehow? */
+ float size_x = (96.0f / 20.0f) * UI_UNIT_X;
+ float size_y = (96.0f / 20.0f) * UI_UNIT_Y;
+ uiBut *but = uiDefIconTextBut(block,
+ UI_BTYPE_PREVIEW_TILE,
+ 0,
+ asset_handle->file_data->preview_icon_id,
+ asset_handle->file_data->name,
+ 0,
+ 0,
+ size_x,
+ size_y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ ui_def_but_icon(but,
+ asset_handle->file_data->preview_icon_id,
+ /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
+ UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+ if (!ui_list->custom_drag_opname) {
+ asset_view_item_but_drag_set(but, list_data, asset_handle);
+ }
+}
+
+static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params)
+{
+ AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata;
+
+ if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) {
+ ED_region_tag_redraw(params->region);
+ }
+}
+
+static uiListType *UI_UL_asset_view()
+{
+ uiListType *list_type = (uiListType *)MEM_callocN(sizeof(*list_type), __func__);
+
+ BLI_strncpy(list_type->idname, "UI_UL_asset_view", sizeof(list_type->idname));
+ list_type->draw_item = asset_view_draw_item;
+ list_type->listener = asset_view_listener;
+
+ return list_type;
+}
+
+void ED_uilisttypes_ui()
+{
+ WM_uilisttype_add(UI_UL_asset_view());
+}
+
+static void asset_view_template_refresh_asset_collection(
+ const AssetLibraryReference &asset_library,
+ PointerRNA &assets_dataptr,
+ const char *assets_propname)
+{
+ PropertyRNA *assets_prop = RNA_struct_find_property(&assets_dataptr, assets_propname);
+ if (!assets_prop) {
+ RNA_warning("Asset collection not found");
+ return;
+ }
+ if (!RNA_struct_is_a(RNA_property_pointer_type(&assets_dataptr, assets_prop),
+ &RNA_AssetHandle)) {
+ RNA_warning("Expected a collection property for AssetHandle items");
+ return;
+ }
+
+ RNA_property_collection_clear(&assets_dataptr, assets_prop);
+
+ ED_assetlist_iterate(&asset_library, [&](FileDirEntry &file) {
+ PointerRNA itemptr, fileptr;
+ RNA_property_collection_add(&assets_dataptr, assets_prop, &itemptr);
+
+ RNA_pointer_create(nullptr, &RNA_FileSelectEntry, &file, &fileptr);
+ RNA_pointer_set(&itemptr, "file_data", fileptr);
+
+ /* Copy name from file to asset-handle name ID-property. */
+ char name[MAX_NAME];
+ PropertyRNA *file_name_prop = RNA_struct_name_property(fileptr.type);
+ RNA_property_string_get(&fileptr, file_name_prop, name);
+ PropertyRNA *asset_name_prop = RNA_struct_name_property(&RNA_AssetHandle);
+ RNA_property_string_set(&itemptr, asset_name_prop, name);
+
+ return true;
+ });
+}
+
+void uiTemplateAssetView(uiLayout *layout,
+ bContext *C,
+ const char *list_id,
+ PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const AssetFilterSettings *filter_settings,
+ const char *activate_opname,
+ const char *drag_opname)
+{
+ if (!list_id || !list_id[0]) {
+ RNA_warning("Asset view needs a valid identifier");
+ return;
+ }
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+
+ PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr,
+ asset_library_propname);
+ uiItemFullR(col, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0);
+
+ AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value(
+ RNA_property_enum_get(asset_library_dataptr, asset_library_prop));
+ ED_assetlist_storage_fetch(&asset_library, filter_settings, C);
+ ED_assetlist_ensure_previews_job(&asset_library, C);
+
+ asset_view_template_refresh_asset_collection(asset_library, *assets_dataptr, assets_propname);
+
+ AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data),
+ "AssetViewListData");
+ list_data->asset_library = asset_library;
+ list_data->screen = CTX_wm_screen(C);
+
+ /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading
+ * (of previews) of the items? */
+ uiList *list = uiTemplateList_ex(col,
+ C,
+ "UI_UL_asset_view",
+ list_id,
+ assets_dataptr,
+ assets_propname,
+ active_dataptr,
+ active_propname,
+ nullptr,
+ 0,
+ 0,
+ UILST_LAYOUT_BIG_PREVIEW_GRID,
+ 0,
+ false,
+ false,
+ list_data);
+
+ list->custom_activate_opname = activate_opname;
+ list->custom_drag_opname = drag_opname;
+
+ if (!list) {
+ /* List creation failed. */
+ MEM_freeN(list_data);
+ }
+}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 2d1663a3ecd..c6ca097f295 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5827,36 +5827,271 @@ static void uilist_filter_items_default(struct uiList *ui_list,
}
}
+/**
+ * The validated data that was passed to #uiTemplateList (typically through Python).
+ * Populated through #ui_template_list_data_retrieve().
+ */
+typedef struct {
+ PointerRNA dataptr;
+ PropertyRNA *prop;
+ PointerRNA active_dataptr;
+ PropertyRNA *activeprop;
+ const char *item_dyntip_propname;
+
+ /* Index as stored in the input property. I.e. the index before sorting. */
+ int active_item_idx;
+} TemplateListInputData;
+
+/**
+ * Internal wrapper for a single item in the list (well, actually stored as a vector).
+ */
typedef struct {
PointerRNA item;
int org_idx;
int flt_flag;
} _uilist_item;
+/**
+ * Container for the item vector and additional info.
+ */
+typedef struct {
+ _uilist_item *item_vec;
+ /* Index of the active item following visual order. I.e. unlike
+ * TemplateListInputData.active_item_idx, this is the index after sorting. */
+ int active_item_idx;
+ int tot_items;
+} TemplateListItems;
+
+typedef struct {
+ uiListDrawItemFunc draw_item;
+ uiListDrawFilterFunc draw_filter;
+
+ int rows;
+ int maxrows;
+ int columns;
+} TemplateListLayoutDrawData;
+
typedef struct {
int visual_items; /* Visual number of items (i.e. number of items we have room to display). */
int start_idx; /* Index of first item to display. */
int end_idx; /* Index of last item to display + 1. */
-} uiListLayoutdata;
+} TemplateListVisualInfo;
+
+/**
+ * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return
+ * it in \a r_list_type.
+ *
+ * \return false if the input data isn't valid. Will also raise an RNA warning in that case.
+ */
+static bool ui_template_list_data_retrieve(const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ TemplateListInputData *r_input_data,
+ uiListType **r_list_type)
+{
+ memset(r_input_data, 0, sizeof(*r_input_data));
+
+ /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
+ if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) {
+ RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
+ UI_UL_DEFAULT_CLASS_NAME);
+ return false;
+ }
+
+ if (!active_dataptr->data) {
+ RNA_warning("No active data");
+ return false;
+ }
+
+ r_input_data->dataptr = *dataptr;
+ if (dataptr->data) {
+ r_input_data->prop = RNA_struct_find_property(dataptr, propname);
+ if (!r_input_data->prop) {
+ RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname);
+ return false;
+ }
+ }
+
+ r_input_data->active_dataptr = *active_dataptr;
+ r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname);
+ if (!r_input_data->activeprop) {
+ RNA_warning(
+ "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname);
+ return false;
+ }
+
+ if (r_input_data->prop) {
+ const PropertyType type = RNA_property_type(r_input_data->prop);
+ if (type != PROP_COLLECTION) {
+ RNA_warning("Expected a collection data property");
+ return false;
+ }
+ }
+
+ const PropertyType activetype = RNA_property_type(r_input_data->activeprop);
+ if (activetype != PROP_INT) {
+ RNA_warning("Expected an integer active data property");
+ return false;
+ }
+
+ /* Find the uiList type. */
+ if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) {
+ RNA_warning("List type %s not found", listtype_name);
+ return false;
+ }
+
+ r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr,
+ r_input_data->activeprop);
+ r_input_data->item_dyntip_propname = item_dyntip_propname;
+
+ return true;
+}
+
+static void ui_template_list_collect_items(PointerRNA *list_ptr,
+ PropertyRNA *list_prop,
+ uiListDyn *dyn_data,
+ int filter_exclude,
+ bool order_reverse,
+ int activei,
+ TemplateListItems *r_items)
+{
+ int i = 0;
+ int reorder_i = 0;
+ bool activei_mapping_pending = true;
+
+ RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) {
+ if (!dyn_data->items_filter_flags ||
+ ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
+ int new_order_idx;
+ if (dyn_data->items_filter_neworder) {
+ new_order_idx = dyn_data->items_filter_neworder[reorder_i++];
+ new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx;
+ }
+ else {
+ new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++;
+ }
+ // printf("%s: ii: %d\n", __func__, ii);
+ r_items->item_vec[new_order_idx].item = itemptr;
+ r_items->item_vec[new_order_idx].org_idx = i;
+ r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ?
+ dyn_data->items_filter_flags[i] :
+ 0;
+
+ if (activei_mapping_pending && activei == i) {
+ activei = new_order_idx;
+ /* So that we do not map again activei! */
+ activei_mapping_pending = false;
+ }
+#if 0 /* For now, do not alter active element, even if it will be hidden... */
+ else if (activei < i) {
+ /* We do not want an active but invisible item!
+ * Only exception is when all items are filtered out...
+ */
+ if (prev_order_idx >= 0) {
+ activei = prev_order_idx;
+ RNA_property_int_set(active_dataptr, activeprop, prev_i);
+ }
+ else {
+ activei = new_order_idx;
+ RNA_property_int_set(active_dataptr, activeprop, i);
+ }
+ }
+ prev_i = i;
+ prev_ii = new_order_idx;
+#endif
+ }
+ i++;
+ }
+ RNA_PROP_END;
+
+ /* If mapping is still pending, no active item was found. Mark as invalid (-1) */
+ r_items->active_item_idx = activei_mapping_pending ? -1 : activei;
+}
+
+/**
+ * Create the UI-list representation of the list items, sorted and filtered if needed.
+ */
+static void ui_template_list_collect_display_items(bContext *C,
+ uiList *ui_list,
+ TemplateListInputData *input_data,
+ const uiListFilterItemsFunc filter_items_fn,
+ TemplateListItems *r_items)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ memset(r_items, 0, sizeof(*r_items));
+
+ /* Filter list items! (not for compact layout, though) */
+ if (input_data->dataptr.data && input_data->prop) {
+ const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+ const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
+ int items_shown;
+#if 0
+ int prev_ii = -1, prev_i;
+#endif
+
+ if (ui_list->layout_type == UILST_LAYOUT_COMPACT) {
+ dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(
+ &input_data->dataptr, input_data->prop);
+ }
+ else {
+ // printf("%s: filtering...\n", __func__);
+ filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop));
+ // printf("%s: filtering done.\n", __func__);
+ }
+
+ items_shown = dyn_data->items_shown;
+ if (items_shown >= 0) {
+ r_items->item_vec = MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__);
+ // printf("%s: items shown: %d.\n", __func__, items_shown);
+
+ ui_template_list_collect_items(&input_data->dataptr,
+ input_data->prop,
+ dyn_data,
+ filter_exclude,
+ order_reverse,
+ input_data->active_item_idx,
+ r_items);
+ }
+ if (dyn_data->items_shown >= 0) {
+ r_items->tot_items = dyn_data->items_shown;
+ }
+ else {
+ r_items->tot_items = dyn_data->items_len;
+ }
+ }
+}
+
+static void ui_template_list_free_items(TemplateListItems *items)
+{
+ if (items->item_vec) {
+ MEM_freeN(items->item_vec);
+ }
+}
static void uilist_prepare(uiList *ui_list,
- int len,
- int activei,
- int rows,
- int maxrows,
- int columns,
- uiListLayoutdata *layoutdata)
+ const TemplateListItems *items,
+ const TemplateListLayoutDrawData *layout_data,
+ TemplateListVisualInfo *r_visual_info)
{
uiListDyn *dyn_data = ui_list->dyn_data;
- const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD));
+ const bool use_auto_size = (ui_list->list_grip <
+ (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD));
+
+ int actual_rows = layout_data->rows;
+ int actual_maxrows = layout_data->maxrows;
+ int columns = layout_data->columns;
/* default rows */
- if (rows <= 0) {
- rows = 5;
+ if (actual_rows <= 0) {
+ actual_rows = 5;
}
- dyn_data->visual_height_min = rows;
- if (maxrows < rows) {
- maxrows = max_ii(rows, 5);
+ dyn_data->visual_height_min = actual_rows;
+ if (actual_maxrows < actual_rows) {
+ actual_maxrows = max_ii(actual_rows, 5);
}
if (columns <= 0) {
columns = 9;
@@ -5864,42 +6099,44 @@ static void uilist_prepare(uiList *ui_list,
int activei_row;
if (columns > 1) {
- dyn_data->height = (int)ceil((double)len / (double)columns);
- activei_row = (int)floor((double)activei / (double)columns);
+ dyn_data->height = (int)ceil((double)items->tot_items / (double)columns);
+ activei_row = (int)floor((double)items->active_item_idx / (double)columns);
}
else {
- dyn_data->height = len;
- activei_row = activei;
+ dyn_data->height = items->tot_items;
+ activei_row = items->active_item_idx;
}
if (!use_auto_size) {
/* No auto-size, yet we clamp at min size! */
- maxrows = rows = max_ii(ui_list->list_grip, rows);
+ actual_rows = max_ii(ui_list->list_grip, actual_rows);
}
- else if ((rows != maxrows) && (dyn_data->height > rows)) {
+ else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) {
/* Expand size if needed and possible. */
- rows = min_ii(dyn_data->height, maxrows);
+ actual_rows = min_ii(dyn_data->height, actual_maxrows);
}
/* If list length changes or list is tagged to check this,
* and active is out of view, scroll to it .*/
- if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) {
+ if ((ui_list->list_last_len != items->tot_items) ||
+ (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) {
if (activei_row < ui_list->list_scroll) {
ui_list->list_scroll = activei_row;
}
- else if (activei_row >= ui_list->list_scroll + rows) {
- ui_list->list_scroll = activei_row - rows + 1;
+ else if (activei_row >= ui_list->list_scroll + actual_rows) {
+ ui_list->list_scroll = activei_row - actual_rows + 1;
}
ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM;
}
- const int max_scroll = max_ii(0, dyn_data->height - rows);
+ const int max_scroll = max_ii(0, dyn_data->height - actual_rows);
CLAMP(ui_list->list_scroll, 0, max_scroll);
- ui_list->list_last_len = len;
- dyn_data->visual_height = rows;
- layoutdata->visual_items = rows * columns;
- layoutdata->start_idx = ui_list->list_scroll * columns;
- layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len);
+ ui_list->list_last_len = items->tot_items;
+ dyn_data->visual_height = actual_rows;
+ r_visual_info->visual_items = actual_rows * columns;
+ r_visual_info->start_idx = ui_list->list_scroll * columns;
+ r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns,
+ items->tot_items);
}
static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2))
@@ -5939,112 +6176,28 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha
return BLI_sprintfN("%s - %s", tip, dyn_tooltip);
}
-void uiTemplateList(uiLayout *layout,
- bContext *C,
- const char *listtype_name,
- const char *list_id,
- PointerRNA *dataptr,
- const char *propname,
- PointerRNA *active_dataptr,
- const char *active_propname,
- const char *item_dyntip_propname,
- int rows,
- int maxrows,
- int layout_type,
- int columns,
- bool sort_reverse,
- bool sort_lock)
+/**
+ * \note Note that \a layout_type may be NULL.
+ */
+static uiList *ui_list_ensure(bContext *C,
+ uiListType *ui_list_type,
+ const char *list_id,
+ int layout_type,
+ bool sort_reverse,
+ bool sort_lock)
{
- PropertyRNA *prop = NULL, *activeprop;
- _uilist_item *items_ptr = NULL;
- uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
- uiBut *but;
-
- uiListLayoutdata layoutdata;
- char ui_list_id[UI_MAX_NAME_STR];
- char numstr[32];
- int rnaicon = ICON_NONE, icon = ICON_NONE;
- int i = 0, activei = 0;
- int len = 0;
-
- /* validate arguments */
- /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
- if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) {
- RNA_warning("template_list using default '%s' UIList class must provide a custom list_id",
- UI_UL_DEFAULT_CLASS_NAME);
- return;
- }
-
- uiBlock *block = uiLayoutGetBlock(layout);
-
- if (!active_dataptr->data) {
- RNA_warning("No active data");
- return;
- }
-
- if (dataptr->data) {
- prop = RNA_struct_find_property(dataptr, propname);
- if (!prop) {
- RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname);
- return;
- }
- }
-
- activeprop = RNA_struct_find_property(active_dataptr, active_propname);
- if (!activeprop) {
- RNA_warning(
- "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname);
- return;
- }
-
- if (prop) {
- const PropertyType type = RNA_property_type(prop);
- if (type != PROP_COLLECTION) {
- RNA_warning("Expected a collection data property");
- return;
- }
- }
-
- const PropertyType activetype = RNA_property_type(activeprop);
- if (activetype != PROP_INT) {
- RNA_warning("Expected an integer active data property");
- return;
- }
-
- /* get icon */
- if (dataptr->data && prop) {
- StructRNA *ptype = RNA_property_pointer_type(dataptr, prop);
- rnaicon = RNA_struct_ui_icon(ptype);
- }
-
- /* get active data */
- activei = RNA_property_int_get(active_dataptr, activeprop);
-
- /* Find the uiList type. */
- uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false);
-
- if (ui_list_type == NULL) {
- RNA_warning("List type %s not found", listtype_name);
- return;
+ /* Allows to work in popups. */
+ ARegion *region = CTX_wm_menu(C);
+ if (region == NULL) {
+ region = CTX_wm_region(C);
}
- uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
- uilist_draw_item_default;
- uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
- uilist_draw_filter_default;
- uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
- uilist_filter_items_default;
-
+ char ui_list_id[UI_MAX_NAME_STR];
/* Find or add the uiList to the current Region. */
/* We tag the list id with the list type... */
BLI_snprintf(
ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : "");
- /* Allows to work in popups. */
- ARegion *region = CTX_wm_menu(C);
- if (region == NULL) {
- region = CTX_wm_region(C);
- }
uiList *ui_list = BLI_findstring(&region->ui_lists, ui_list_id, offsetof(uiList, list_id));
if (!ui_list) {
@@ -6074,110 +6227,54 @@ void uiTemplateList(uiLayout *layout,
MEM_SAFE_FREE(dyn_data->items_filter_neworder);
dyn_data->items_len = dyn_data->items_shown = -1;
- /* When active item changed since last draw, scroll to it. */
- if (activei != ui_list->list_last_activei) {
- ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
- ui_list->list_last_activei = activei;
- }
+ return ui_list;
+}
- /* Filter list items! (not for compact layout, though) */
- if (dataptr->data && prop) {
- const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
- const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0;
- int items_shown, idx = 0;
-#if 0
- int prev_ii = -1, prev_i;
-#endif
+static void ui_template_list_layout_draw(bContext *C,
+ uiList *ui_list,
+ uiLayout *layout,
+ TemplateListInputData *input_data,
+ TemplateListItems *items,
+ const TemplateListLayoutDrawData *layout_data)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ const char *active_propname = RNA_property_identifier(input_data->activeprop);
- if (layout_type == UILST_LAYOUT_COMPACT) {
- dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop);
- }
- else {
- // printf("%s: filtering...\n", __func__);
- filter_items(ui_list, C, dataptr, propname);
- // printf("%s: filtering done.\n", __func__);
- }
+ uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
+ char numstr[32];
+ int rnaicon = ICON_NONE, icon = ICON_NONE;
+ uiBut *but;
- items_shown = dyn_data->items_shown;
- if (items_shown >= 0) {
- bool activei_mapping_pending = true;
- items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__);
- // printf("%s: items shown: %d.\n", __func__, items_shown);
- RNA_PROP_BEGIN (dataptr, itemptr, prop) {
- if (!dyn_data->items_filter_flags ||
- ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) {
- int ii;
- if (dyn_data->items_filter_neworder) {
- ii = dyn_data->items_filter_neworder[idx++];
- ii = order_reverse ? items_shown - ii - 1 : ii;
- }
- else {
- ii = order_reverse ? items_shown - ++idx : idx++;
- }
- // printf("%s: ii: %d\n", __func__, ii);
- items_ptr[ii].item = itemptr;
- items_ptr[ii].org_idx = i;
- items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] :
- 0;
-
- if (activei_mapping_pending && activei == i) {
- activei = ii;
- /* So that we do not map again activei! */
- activei_mapping_pending = false;
- }
-#if 0 /* For now, do not alter active element, even if it will be hidden... */
- else if (activei < i) {
- /* We do not want an active but invisible item!
- * Only exception is when all items are filtered out...
- */
- if (prev_ii >= 0) {
- activei = prev_ii;
- RNA_property_int_set(active_dataptr, activeprop, prev_i);
- }
- else {
- activei = ii;
- RNA_property_int_set(active_dataptr, activeprop, i);
- }
- }
- prev_i = i;
- prev_ii = ii;
-#endif
- }
- i++;
- }
- RNA_PROP_END;
+ uiBlock *block = uiLayoutGetBlock(layout);
- if (activei_mapping_pending) {
- /* No active item found, set to 'invalid' -1 value... */
- activei = -1;
- }
- }
- if (dyn_data->items_shown >= 0) {
- len = dyn_data->items_shown;
- }
- else {
- len = dyn_data->items_len;
- }
+ /* get icon */
+ if (input_data->dataptr.data && input_data->prop) {
+ StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop);
+ rnaicon = RNA_struct_ui_icon(ptype);
}
- switch (layout_type) {
- case UILST_LAYOUT_DEFAULT:
+ TemplateListVisualInfo visual_info;
+ switch (ui_list->layout_type) {
+ case UILST_LAYOUT_DEFAULT: {
/* layout */
- box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
glob = uiLayoutColumn(box, true);
row = uiLayoutRow(glob, false);
col = uiLayoutColumn(row, true);
+ TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
+ adjusted_layout_data.columns = 1;
/* init numbers */
- uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata);
+ uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
- if (dataptr->data && prop) {
+ int i = 0;
+ if (input_data->dataptr.data && input_data->prop) {
/* create list items */
- for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
- PointerRNA *itemptr = &items_ptr[i].item;
+ for (i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
void *dyntip_data;
- const int org_i = items_ptr[i].org_idx;
- const int flt_flag = items_ptr[i].flt_flag;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
uiBlock *subblock = uiLayoutGetBlock(col);
overlap = uiLayoutOverlap(col);
@@ -6185,7 +6282,7 @@ void uiTemplateList(uiLayout *layout,
UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
/* list item behind label & other buttons */
- sub = uiLayoutRow(overlap, false);
+ uiLayoutRow(overlap, false);
but = uiDefButR_prop(subblock,
UI_BTYPE_LISTROW,
@@ -6195,15 +6292,16 @@ void uiTemplateList(uiLayout *layout,
0,
UI_UNIT_X * 10,
UI_UNIT_Y,
- active_dataptr,
- activeprop,
+ &input_data->active_dataptr,
+ input_data->activeprop,
0,
0,
org_i,
0,
0,
TIP_("Double click to rename"));
- if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) {
+ if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr,
+ input_data->item_dyntip_propname))) {
UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data);
}
@@ -6213,23 +6311,23 @@ void uiTemplateList(uiLayout *layout,
if (icon == ICON_DOT) {
icon = ICON_NONE;
}
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- active_dataptr,
- active_propname,
- org_i,
- flt_flag);
+ layout_data->draw_item(ui_list,
+ C,
+ sub,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
/* Items should be able to set context pointers for the layout. But the list-row button
* swallows events, so it needs the context storage too for handlers to see it. */
but->context = uiLayoutGetContextStore(sub);
/* If we are "drawing" active item, set all labels as active. */
- if (i == activei) {
+ if (i == items->active_item_idx) {
ui_layout_list_set_labels_active(sub);
}
@@ -6238,13 +6336,13 @@ void uiTemplateList(uiLayout *layout,
}
/* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
+ for (; i < visual_info.start_idx + visual_info.visual_items; i++) {
uiItemL(col, "", ICON_NONE);
}
/* add scrollbar */
- if (len > layoutdata.visual_items) {
- col = uiLayoutColumn(row, false);
+ if (items->tot_items > visual_info.visual_items) {
+ uiLayoutColumn(row, false);
uiDefButI(block,
UI_BTYPE_SCROLL,
0,
@@ -6260,21 +6358,29 @@ void uiTemplateList(uiLayout *layout,
0,
"");
}
- break;
+ } break;
case UILST_LAYOUT_COMPACT:
row = uiLayoutRow(layout, true);
- if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) &&
- (activei < dyn_data->items_shown)) {
- PointerRNA *itemptr = &items_ptr[activei].item;
- const int org_i = items_ptr[activei].org_idx;
+ if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) &&
+ (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) {
+ PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item;
+ const int org_i = items->item_vec[items->active_item_idx].org_idx;
icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
if (icon == ICON_DOT) {
icon = ICON_NONE;
}
- draw_item(
- ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0);
+ layout_data->draw_item(ui_list,
+ C,
+ row,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ 0);
}
/* if list is empty, add in dummy button */
else {
@@ -6292,8 +6398,8 @@ void uiTemplateList(uiLayout *layout,
0,
UI_UNIT_X * 5,
UI_UNIT_Y,
- active_dataptr,
- activeprop,
+ &input_data->active_dataptr,
+ input_data->activeprop,
0,
0,
0,
@@ -6304,24 +6410,25 @@ void uiTemplateList(uiLayout *layout,
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
break;
- case UILST_LAYOUT_GRID:
- box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop);
+ case UILST_LAYOUT_GRID: {
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
glob = uiLayoutColumn(box, true);
row = uiLayoutRow(glob, false);
col = uiLayoutColumn(row, true);
subrow = NULL; /* Quite gcc warning! */
- uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata);
+ uilist_prepare(ui_list, items, layout_data, &visual_info);
- if (dataptr->data && prop) {
+ int i = 0;
+ if (input_data->dataptr.data && input_data->prop) {
/* create list items */
- for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
- PointerRNA *itemptr = &items_ptr[i].item;
- const int org_i = items_ptr[i].org_idx;
- const int flt_flag = items_ptr[i].flt_flag;
+ for (i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
/* create button */
- if (!(i % columns)) {
+ if (!(i % layout_data->columns)) {
subrow = uiLayoutRow(col, false);
}
@@ -6331,7 +6438,7 @@ void uiTemplateList(uiLayout *layout,
UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
/* list item behind label & other buttons */
- sub = uiLayoutRow(overlap, false);
+ uiLayoutRow(overlap, false);
but = uiDefButR_prop(subblock,
UI_BTYPE_LISTROW,
@@ -6341,8 +6448,8 @@ void uiTemplateList(uiLayout *layout,
0,
UI_UNIT_X * 10,
UI_UNIT_Y,
- active_dataptr,
- activeprop,
+ &input_data->active_dataptr,
+ input_data->activeprop,
0,
0,
org_i,
@@ -6354,19 +6461,19 @@ void uiTemplateList(uiLayout *layout,
sub = uiLayoutRow(overlap, false);
icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
- draw_item(ui_list,
- C,
- sub,
- dataptr,
- itemptr,
- icon,
- active_dataptr,
- active_propname,
- org_i,
- flt_flag);
+ layout_data->draw_item(ui_list,
+ C,
+ sub,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
/* If we are "drawing" active item, set all labels as active. */
- if (i == activei) {
+ if (i == items->active_item_idx) {
ui_layout_list_set_labels_active(sub);
}
@@ -6375,15 +6482,15 @@ void uiTemplateList(uiLayout *layout,
}
/* add dummy buttons to fill space */
- for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) {
- if (!(i % columns)) {
+ for (; i < visual_info.start_idx + visual_info.visual_items; i++) {
+ if (!(i % layout_data->columns)) {
subrow = uiLayoutRow(col, false);
}
uiItemL(subrow, "", ICON_NONE);
}
/* add scrollbar */
- if (len > layoutdata.visual_items) {
+ if (items->tot_items > visual_info.visual_items) {
/* col = */ uiLayoutColumn(row, false);
uiDefButI(block,
UI_BTYPE_SCROLL,
@@ -6401,6 +6508,101 @@ void uiTemplateList(uiLayout *layout,
"");
}
break;
+ }
+ case UILST_LAYOUT_BIG_PREVIEW_GRID:
+ box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
+ /* For grip button. */
+ glob = uiLayoutColumn(box, true);
+ /* For scrollbar. */
+ row = uiLayoutRow(glob, false);
+
+ /* TODO ED_fileselect_init_layout(). Share somehow? */
+ float size_x = (96.0f / 20.0f) * UI_UNIT_X;
+ float size_y = (96.0f / 20.0f) * UI_UNIT_Y;
+
+ const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1);
+ uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true);
+
+ TemplateListLayoutDrawData adjusted_layout_data = *layout_data;
+ adjusted_layout_data.columns = cols_per_row;
+ uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info);
+
+ if (input_data->dataptr.data && input_data->prop) {
+ /* create list items */
+ for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) {
+ PointerRNA *itemptr = &items->item_vec[i].item;
+ const int org_i = items->item_vec[i].org_idx;
+ const int flt_flag = items->item_vec[i].flt_flag;
+
+ overlap = uiLayoutOverlap(grid);
+ col = uiLayoutColumn(overlap, false);
+
+ uiBlock *subblock = uiLayoutGetBlock(col);
+ UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM);
+
+ but = uiDefButR_prop(subblock,
+ UI_BTYPE_LISTROW,
+ 0,
+ "",
+ 0,
+ 0,
+ size_x,
+ size_y,
+ &input_data->active_dataptr,
+ input_data->activeprop,
+ 0,
+ 0,
+ org_i,
+ 0,
+ 0,
+ NULL);
+ UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP);
+
+ col = uiLayoutColumn(overlap, false);
+
+ icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false);
+ layout_data->draw_item(ui_list,
+ C,
+ col,
+ &input_data->dataptr,
+ itemptr,
+ icon,
+ &input_data->active_dataptr,
+ active_propname,
+ org_i,
+ flt_flag);
+
+ /* Items should be able to set context pointers for the layout. But the list-row button
+ * swallows events, so it needs the context storage too for handlers to see it. */
+ but->context = uiLayoutGetContextStore(col);
+
+ /* If we are "drawing" active item, set all labels as active. */
+ if (i == items->active_item_idx) {
+ ui_layout_list_set_labels_active(col);
+ }
+
+ UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM);
+ }
+ }
+
+ if (items->tot_items > visual_info.visual_items) {
+ /* col = */ uiLayoutColumn(row, false);
+ uiDefButI(block,
+ UI_BTYPE_SCROLL,
+ 0,
+ "",
+ 0,
+ 0,
+ V2D_SCROLL_WIDTH,
+ size_y * dyn_data->visual_height,
+ &ui_list->list_scroll,
+ 0,
+ dyn_data->height - dyn_data->visual_height,
+ dyn_data->visual_height,
+ 0,
+ "");
+ }
+ break;
}
if (glob) {
@@ -6477,7 +6679,7 @@ void uiTemplateList(uiLayout *layout,
0,
"");
- draw_filter(ui_list, C, col);
+ layout_data->draw_filter(ui_list, C, col);
}
else {
but = uiDefIconButBitI(subblock,
@@ -6516,10 +6718,108 @@ void uiTemplateList(uiLayout *layout,
UI_block_emboss_set(subblock, UI_EMBOSS);
}
}
+}
+
+uiList *uiTemplateList_ex(uiLayout *layout,
+ bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ bool sort_reverse,
+ bool sort_lock,
+ void *customdata)
+{
+ TemplateListInputData input_data = {0};
+ uiListType *ui_list_type;
+ if (!ui_template_list_data_retrieve(listtype_name,
+ list_id,
+ dataptr,
+ propname,
+ active_dataptr,
+ active_propname,
+ item_dyntip_propname,
+ &input_data,
+ &ui_list_type)) {
+ return NULL;
+ }
+
+ uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item :
+ uilist_draw_item_default;
+ uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter :
+ uilist_draw_filter_default;
+ uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items :
+ uilist_filter_items_default;
+
+ uiList *ui_list = ui_list_ensure(C, ui_list_type, list_id, layout_type, sort_reverse, sort_lock);
+ uiListDyn *dyn_data = ui_list->dyn_data;
+
+ MEM_SAFE_FREE(dyn_data->customdata);
+ dyn_data->customdata = customdata;
- if (items_ptr) {
- MEM_freeN(items_ptr);
+ /* When active item changed since last draw, scroll to it. */
+ if (input_data.active_item_idx != ui_list->list_last_activei) {
+ ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+ ui_list->list_last_activei = input_data.active_item_idx;
}
+
+ TemplateListItems items;
+ ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items);
+
+ TemplateListLayoutDrawData layout_data = {
+ .draw_item = draw_item,
+ .draw_filter = draw_filter,
+ .rows = rows,
+ .maxrows = maxrows,
+ .columns = columns,
+ };
+
+ ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data);
+
+ ui_template_list_free_items(&items);
+
+ return ui_list;
+}
+
+void uiTemplateList(uiLayout *layout,
+ bContext *C,
+ const char *listtype_name,
+ const char *list_id,
+ PointerRNA *dataptr,
+ const char *propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ const char *item_dyntip_propname,
+ int rows,
+ int maxrows,
+ int layout_type,
+ int columns,
+ bool sort_reverse,
+ bool sort_lock)
+{
+ uiTemplateList_ex(layout,
+ C,
+ listtype_name,
+ list_id,
+ dataptr,
+ propname,
+ active_dataptr,
+ active_propname,
+ item_dyntip_propname,
+ rows,
+ maxrows,
+ layout_type,
+ columns,
+ sort_reverse,
+ sort_lock,
+ NULL);
}
/** \} */
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0c6be7b1196..3104503de66 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1,4 +1,4 @@
-/*
+/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -105,6 +105,7 @@ typedef enum {
/* specials */
UI_WTYPE_ICON,
UI_WTYPE_ICON_LABEL,
+ UI_WTYPE_PREVIEW_TILE,
UI_WTYPE_SWATCH,
UI_WTYPE_RGB_PICKER,
UI_WTYPE_UNITVEC,
@@ -3942,6 +3943,14 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun
widgetbase_draw(&wtb, wcol);
}
+static void widget_preview_tile(
+ uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
+{
+ const uiStyle *style = UI_style_get();
+ ui_draw_preview_item_stateless(
+ &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER);
+}
+
static void widget_menuiconbut(uiWidgetColors *wcol,
rcti *rect,
int UNUSED(state),
@@ -4407,6 +4416,13 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_icon_has_anim;
break;
+ case UI_WTYPE_PREVIEW_TILE:
+ wt.draw = NULL;
+ /* Drawn via the `custom` callback. */
+ wt.text = NULL;
+ wt.custom = widget_preview_tile;
+ break;
+
case UI_WTYPE_SWATCH:
wt.custom = widget_swatch;
break;
@@ -4698,6 +4714,10 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
wt = widget_type(UI_WTYPE_BOX);
break;
+ case UI_BTYPE_PREVIEW_TILE:
+ wt = widget_type(UI_WTYPE_PREVIEW_TILE);
+ break;
+
case UI_BTYPE_EXTRA:
widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect);
break;
@@ -4846,13 +4866,15 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
wt->draw(&wt->wcol, rect, state, roundboxalign);
}
- if (use_alpha_blend) {
- GPU_blend(GPU_BLEND_ALPHA);
- }
+ if (wt->text) {
+ if (use_alpha_blend) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ }
- wt->text(fstyle, &wt->wcol, but, rect);
- if (use_alpha_blend) {
- GPU_blend(GPU_BLEND_NONE);
+ wt->text(fstyle, &wt->wcol, but, rect);
+ if (use_alpha_blend) {
+ GPU_blend(GPU_BLEND_NONE);
+ }
}
}
@@ -5374,17 +5396,20 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
}
}
-void ui_draw_preview_item(
- const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
+/**
+ * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on
+ * state. It just draws the preview and text directly.
+ */
+void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ const uchar text_col[4],
+ eFontStyle_Align text_align)
{
rcti trect = *rect;
const float text_size = UI_UNIT_Y;
float font_dims[2] = {0.0f, 0.0f};
- uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
-
- /* drawing button background */
- wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
- wt->draw(&wt->wcol, rect, 0, 0);
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
@@ -5396,8 +5421,6 @@ void ui_draw_preview_item(
fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
/* text rect */
- trect.xmin += 0;
- trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2;
trect.ymin += U.widget_unit / 2;
trect.ymax = trect.ymin + font_dims[1];
if (trect.xmax > rect->xmax - PREVIEW_PAD) {
@@ -5416,11 +5439,27 @@ void ui_draw_preview_item(
UI_fontstyle_draw(fstyle,
&trect,
drawstr,
- wt->wcol.text,
+ text_col,
&(struct uiFontStyleDraw_Params){
- .align = UI_STYLE_TEXT_CENTER,
+ .align = text_align,
});
}
}
+void ui_draw_preview_item(const uiFontStyle *fstyle,
+ rcti *rect,
+ const char *name,
+ int iconid,
+ int state,
+ eFontStyle_Align text_align)
+{
+ uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
+
+ /* drawing button background */
+ wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED);
+ wt->draw(&wt->wcol, rect, 0, 0);
+
+ ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align);
+}
+
/** \} */
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index bd2b1c4c553..fa340cb075d 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -161,6 +161,12 @@ void ED_region_do_listen(wmRegionListenerParams *params)
if (region->type && region->type->listener) {
region->type->listener(params);
}
+
+ LISTBASE_FOREACH (uiList *, list, &region->ui_lists) {
+ if (list->type && list->type->listener) {
+ list->type->listener(list, params);
+ }
+ }
}
/* only exported for WM */
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 627a67358f2..fb1641025c8 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -36,6 +36,7 @@
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
@@ -111,6 +112,7 @@ const char *screen_context_dir[] = {
"selected_editable_fcurves",
"active_editable_fcurve",
"selected_editable_keyframes",
+ "asset_library",
NULL,
};
@@ -1024,6 +1026,14 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C,
return CTX_RESULT_NO_DATA;
}
+static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataResult *result)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ CTX_data_pointer_set(
+ result, &workspace->id, &RNA_AssetLibraryReference, &workspace->active_asset_library);
+ return CTX_RESULT_OK;
+}
+
/* Registry of context callback functions. */
typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result);
@@ -1098,6 +1108,7 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves);
register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve);
register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes);
+ register_context_function("asset_library", screen_ctx_asset_library);
}
/* Entry point for the screen context. */
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index adb824b8934..b3b3eafb6e7 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -129,6 +129,8 @@ void ED_spacetypes_init(void)
ED_screen_user_menu_register();
+ ED_uilisttypes_ui();
+
/* Gizmo types. */
ED_gizmotypes_button_2d();
ED_gizmotypes_dial_3d();
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index c1dcf2e56d3..08ea139a6d4 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -48,6 +48,7 @@
#include "IMB_imbuf_types.h"
+#include "DNA_asset_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
@@ -531,7 +532,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
}
/* to make sure we show what is on disk */
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
}
ED_region_tag_redraw(region);
@@ -1065,7 +1066,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
return false;
}
/* Check if the library exists. */
- if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) ||
filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
return false;
}
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 856bd5b1bc3..c3649edac22 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1846,7 +1846,7 @@ static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
struct FSMenu *fsmenu = ED_fsmenu_get();
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
/* refresh system directory menu */
fsmenu_refresh_system_category(fsmenu);
@@ -2323,7 +2323,7 @@ static int file_directory_new_exec(bContext *C, wmOperator *op)
sfile->scroll_offset = 0;
/* reload dir to make sure we're seeing what's in the directory */
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
if (do_diropen) {
BLI_strncpy(params->dir, path, sizeof(params->dir));
@@ -2574,7 +2574,7 @@ static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
if (params) {
params->flag ^= FILE_HIDE_DOT;
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
@@ -2834,7 +2834,7 @@ static int file_delete_exec(bContext *C, wmOperator *op)
}
}
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 4c9f80bfa64..76bfb34c809 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1051,7 +1051,7 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li
if (library_a->type != library_b->type) {
return false;
}
- if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library_a->type == ASSET_LIBRARY_CUSTOM) {
/* Don't only check the index, also check that it's valid. */
bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index(
&U, library_a->custom_library_index);
@@ -1154,7 +1154,7 @@ ImBuf *filelist_file_getimage(const FileDirEntry *file)
return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
}
-static ImBuf *filelist_geticon_image_ex(FileDirEntry *file)
+ImBuf *filelist_geticon_image_ex(const FileDirEntry *file)
{
ImBuf *ibuf = NULL;
@@ -3443,7 +3443,7 @@ static void filelist_readjob_free(void *flrjv)
MEM_freeN(flrj);
}
-void filelist_readjob_start(FileList *filelist, const bContext *C)
+void filelist_readjob_start(FileList *filelist, int space_notifier, const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmJob *wm_job;
@@ -3475,22 +3475,19 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
filelist_readjob_endjob(flrj);
filelist_readjob_free(flrj);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL);
+ WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, NULL);
return;
}
/* setup job */
wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
- CTX_data_scene(C),
+ filelist,
"Listing Dirs...",
WM_JOB_PROGRESS,
WM_JOB_TYPE_FILESEL_READDIR);
WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free);
- WM_jobs_timer(wm_job,
- 0.01,
- NC_SPACE | ND_SPACE_FILE_LIST,
- NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED);
+ WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED);
WM_jobs_callbacks(
wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
@@ -3498,12 +3495,12 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
-void filelist_readjob_stop(wmWindowManager *wm, Scene *owner_scene)
+void filelist_readjob_stop(FileList *filelist, wmWindowManager *wm)
{
- WM_jobs_kill_type(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
+ WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_READDIR);
}
-int filelist_readjob_running(wmWindowManager *wm, Scene *owner_scene)
+int filelist_readjob_running(FileList *filelist, wmWindowManager *wm)
{
- return WM_jobs_test(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
+ return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_READDIR);
}
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 9eb70dd8437..403222f826f 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -77,6 +77,7 @@ void filelist_init_icons(void);
void filelist_free_icons(void);
struct ImBuf *filelist_getimage(struct FileList *filelist, const int index);
struct ImBuf *filelist_file_getimage(const FileDirEntry *file);
+struct ImBuf *filelist_geticon_image_ex(const FileDirEntry *file);
struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index);
int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
@@ -140,9 +141,11 @@ struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group);
void filelist_freelib(struct FileList *filelist);
-void filelist_readjob_start(struct FileList *filelist, const struct bContext *C);
-void filelist_readjob_stop(struct wmWindowManager *wm, struct Scene *owner_scene);
-int filelist_readjob_running(struct wmWindowManager *wm, struct Scene *owner_scene);
+void filelist_readjob_start(struct FileList *filelist,
+ int space_notifier,
+ const struct bContext *C);
+void filelist_readjob_stop(struct FileList *filelist, struct wmWindowManager *wm);
+int filelist_readjob_running(struct FileList *filelist, struct wmWindowManager *wm);
bool filelist_cache_previews_update(struct FileList *filelist);
void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 038b9c11bca..84632b010c7 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -39,6 +39,7 @@
# include <unistd.h>
#endif
+#include "DNA_asset_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
@@ -118,7 +119,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params),
"FileAssetSelectParams");
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
- asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
+ asset_params->asset_library.type = ASSET_LIBRARY_LOCAL;
asset_params->asset_library.custom_library_index = -1;
}
@@ -419,26 +420,26 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
bUserAssetLibrary *user_library = NULL;
/* Ensure valid repository, or fall-back to local one. */
- if (library->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library->type == ASSET_LIBRARY_CUSTOM) {
BLI_assert(library->custom_library_index >= 0);
user_library = BKE_preferences_asset_library_find_from_index(&U,
library->custom_library_index);
if (!user_library) {
- library->type = FILE_ASSET_LIBRARY_LOCAL;
+ library->type = ASSET_LIBRARY_LOCAL;
}
}
switch (library->type) {
- case FILE_ASSET_LIBRARY_LOCAL:
+ case ASSET_LIBRARY_LOCAL:
base_params->dir[0] = '\0';
break;
- case FILE_ASSET_LIBRARY_CUSTOM:
+ case ASSET_LIBRARY_CUSTOM:
BLI_assert(user_library);
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
break;
}
- base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+ base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
}
void fileselect_refresh_params(SpaceFile *sfile)
@@ -1046,7 +1047,7 @@ FileLayout *ED_fileselect_get_layout(struct SpaceFile *sfile, ARegion *region)
* Support updating the directory even when this isn't the active space
* needed so RNA properties update function isn't context sensitive, see T70255.
*/
-void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
+void ED_file_change_dir_ex(bContext *C, ScrArea *area)
{
/* May happen when manipulating non-active spaces. */
if (UNLIKELY(area->spacetype != SPACE_FILE)) {
@@ -1056,10 +1057,7 @@ void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
wmWindowManager *wm = CTX_wm_manager(C);
- Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
- if (LIKELY(scene != NULL)) {
- ED_fileselect_clear(wm, scene, sfile);
- }
+ ED_fileselect_clear(wm, sfile);
/* Clear search string, it is very rare to want to keep that filter while changing dir,
* and usually very annoying to keep it actually! */
@@ -1084,9 +1082,8 @@ void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
void ED_file_change_dir(bContext *C)
{
- bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
- ED_file_change_dir_ex(C, screen, area);
+ ED_file_change_dir_ex(C, area);
}
int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matched_file)
@@ -1182,11 +1179,11 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
return match;
}
-void ED_fileselect_clear(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfile)
+void ED_fileselect_clear(wmWindowManager *wm, SpaceFile *sfile)
{
/* only NULL in rare cases - T29734. */
if (sfile->files) {
- filelist_readjob_stop(wm, owner_scene);
+ filelist_readjob_stop(sfile->files, wm);
filelist_freelib(sfile->files);
filelist_clear(sfile->files);
}
@@ -1196,7 +1193,7 @@ void ED_fileselect_clear(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfi
WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
-void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfile)
+void ED_fileselect_exit(wmWindowManager *wm, SpaceFile *sfile)
{
if (!sfile) {
return;
@@ -1223,7 +1220,7 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil
folder_history_list_free(sfile);
if (sfile->files) {
- ED_fileselect_clear(wm, owner_scene, sfile);
+ ED_fileselect_clear(wm, sfile);
filelist_free(sfile->files);
MEM_freeN(sfile->files);
sfile->files = NULL;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 993b1d9b69c..b64fa0cdc37 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -206,7 +206,7 @@ static void file_exit(wmWindowManager *wm, ScrArea *area)
sfile->previews_timer = NULL;
}
- ED_fileselect_exit(wm, NULL, sfile);
+ ED_fileselect_exit(wm, sfile);
}
static SpaceLink *file_duplicate(SpaceLink *sl)
@@ -360,13 +360,13 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
if (filelist_needs_force_reset(sfile->files)) {
- filelist_readjob_stop(wm, CTX_data_scene(C));
+ filelist_readjob_stop(sfile->files, wm);
filelist_clear(sfile->files);
}
if (filelist_needs_reading(sfile->files)) {
if (!filelist_pending(sfile->files)) {
- filelist_readjob_start(sfile->files, C);
+ filelist_readjob_start(sfile->files, NC_SPACE | ND_SPACE_FILE_LIST, C);
}
}
@@ -847,7 +847,13 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C),
}
}
-static const char *file_context_dir[] = {"active_file", "id", NULL};
+static const char *file_context_dir[] = {
+ "active_file",
+ "asset_handle",
+ "asset_library",
+ "id",
+ NULL,
+};
static int /*eContextResult*/ file_context(const bContext *C,
const char *member,
@@ -878,6 +884,34 @@ static int /*eContextResult*/ file_context(const bContext *C,
CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
return CTX_RESULT_OK;
}
+ /* TODO this kind of handle is very weak. We need something proper that doesn't depend on file
+ * data. */
+ if (CTX_data_equals(member, "asset_handle")) {
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
+ if (file == NULL || !file->asset_data) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ BLI_STATIC_ASSERT(sizeof(AssetHandle) == sizeof(FileDirEntry *),
+ "Expected AssetHandle to match FileDirEntry");
+
+ CTX_data_pointer_set(result, &screen->id, &RNA_AssetHandle, file);
+ return CTX_RESULT_OK;
+ }
+ if (CTX_data_equals(member, "asset_library")) {
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+
+ BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, type) ==
+ offsetof(AssetLibraryReference, type),
+ "Expected FileSelectAssetLibraryUID to match AssetLibraryReference");
+ BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, custom_library_index) ==
+ offsetof(AssetLibraryReference, custom_library_index),
+ "Expected FileSelectAssetLibraryUID to match AssetLibraryReference");
+
+ CTX_data_pointer_set(
+ result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library);
+ return CTX_RESULT_OK;
+ }
if (CTX_data_equals(member, "id")) {
const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file == NULL) {
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index ac6eab69864..44da4b4ab11 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -3445,7 +3445,15 @@ static void std_node_socket_draw(
case SOCK_RGBA: {
uiLayout *row = uiLayoutSplit(layout, 0.4f, false);
uiItemL(row, text, 0);
- uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0);
+
+ const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
+ if (node_tree->type == NTREE_GEOMETRY) {
+ node_geometry_add_attribute_search_button(node_tree, node, ptr, row);
+ }
+ else {
+ uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0);
+ }
+
break;
}
case SOCK_STRING: {
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index c0952cbaa42..ff37496148f 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -38,9 +38,11 @@ struct bContext;
struct bNode;
struct bNodeLink;
struct bNodeSocket;
+struct uiBut;
struct wmGizmoGroupType;
struct wmKeyConfig;
struct wmWindow;
+struct uiBlock;
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index b69f62a2875..572932ab584 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -50,6 +50,7 @@
#include "BLO_blend_validate.h"
+#include "ED_asset.h"
#include "ED_gpencil.h"
#include "ED_object.h"
#include "ED_outliner.h"
@@ -268,6 +269,8 @@ static void ed_undo_step_post(bContext *C,
WM_toolsystem_refresh_active(C);
WM_toolsystem_refresh_screen_all(bmain);
+ ED_assetlist_storage_tag_main_data_dirty();
+
if (CLOG_CHECK(&LOG, 1)) {
BKE_undosys_print(wm->undo_stack);
}
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index da94eef4917..631e58ad448 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -49,6 +49,7 @@
#include "DEG_depsgraph.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_object.h"
@@ -172,6 +173,8 @@ void ED_editors_init(bContext *C)
ED_space_image_paint_update(bmain, wm, scene);
}
+ ED_assetlist_storage_tag_main_data_dirty();
+
SWAP(int, reports->flag, reports_flag_prev);
wm->op_undo_depth--;
}
diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h
index ff00ba79cf0..ce01563f619 100644
--- a/source/blender/makesdna/DNA_asset_defaults.h
+++ b/source/blender/makesdna/DNA_asset_defaults.h
@@ -32,6 +32,13 @@
0 \
}
+#define _DNA_DEFAULT_AssetLibraryReference \
+ { \
+ .type = ASSET_LIBRARY_LOCAL, \
+ /* Not needed really (should be ignored for #ASSET_LIBRARY_LOCAL), but helps debugging. */ \
+ .custom_library_index = -1, \
+ }
+
/** \} */
/* clang-format on */
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index 697d25653f8..bd677f838a0 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -20,6 +20,7 @@
#pragma once
+#include "DNA_defs.h"
#include "DNA_listBase.h"
#ifdef __cplusplus
@@ -36,6 +37,14 @@ typedef struct AssetTag {
char name[64]; /* MAX_NAME */
} AssetTag;
+typedef struct AssetFilterSettings {
+ /** Tags to match against. These are newly allocated, and compared against the
+ * #AssetMetaData.tags.
+ * TODO not used and doesn't do anything yet. */
+ ListBase tags; /* AssetTag */
+ uint64_t id_types; /* rna_enum_id_type_filter_items */
+} AssetFilterSettings;
+
/**
* \brief The meta-data of an asset.
* By creating and giving this for a data-block (#ID.asset_data), the data-block becomes an asset.
@@ -62,6 +71,52 @@ typedef struct AssetMetaData {
char _pad[4];
} AssetMetaData;
+typedef enum eAssetLibraryType {
+ /* For the future. Display assets bundled with Blender by default. */
+ // ASSET_LIBRARY_BUNDLED = 0,
+ /** Display assets from the current session (current "Main"). */
+ ASSET_LIBRARY_LOCAL = 1,
+ /* For the future. Display assets for the current project. */
+ // ASSET_LIBRARY_PROJECT = 2,
+
+ /** Display assets from custom asset libraries, as defined in the preferences
+ * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname
+ * then.
+ * In RNA, we add the index of the custom library to this to identify it by index. So keep
+ * this last! */
+ ASSET_LIBRARY_CUSTOM = 100,
+} eAssetLibraryType;
+
+/* TODO copy of FileSelectAssetLibraryUID */
+/**
+ * Information to identify a asset library. May be either one of the predefined types (current
+ * 'Main', builtin library, project library), or a custom type as defined in the Preferences.
+ *
+ * If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the
+ * custom library. Otherwise it is not used.
+ */
+typedef struct AssetLibraryReference {
+ short type; /* eAssetLibraryType */
+ char _pad1[2];
+ /**
+ * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the
+ * #bUserAssetLibrary within #UserDef.asset_libraries.
+ * Should be ignored otherwise (but better set to -1 then, for sanity and debugging).
+ */
+ int custom_library_index;
+} AssetLibraryReference;
+
+/**
+ * Not part of the core design, we should try to get rid of it. Only needed to wrap FileDirEntry
+ * into a type with PropertyGroup as base, so we can have an RNA collection of #AssetHandle's to
+ * pass to the UI.
+ */
+#
+#
+typedef struct AssetHandle {
+ struct FileDirEntry *file_data;
+} AssetHandle;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 8d3ac3a7814..f98b30f906c 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -269,6 +269,9 @@ typedef struct uiListDyn {
int resize;
int resize_prev;
+ /* Allocated custom data. Free'ed together with the uiList (and when re-assigning). */
+ void *customdata;
+
/* Filtering data. */
/** Items_len length. */
int *items_filter_flags;
@@ -300,6 +303,12 @@ typedef struct uiList { /* some list UI data need to be saved in file */
int filter_flag;
int filter_sort_flag;
+ /** Operator executed when activating an item. */
+ const char *custom_activate_opname;
+ /** Operator executed when dragging an item (item gets activated too, without running
+ * custom_activate_opname above). */
+ const char *custom_drag_opname;
+
/* Custom sub-classes properties. */
IDProperty *properties;
@@ -583,6 +592,7 @@ enum {
UILST_LAYOUT_DEFAULT = 0,
UILST_LAYOUT_COMPACT = 1,
UILST_LAYOUT_GRID = 2,
+ UILST_LAYOUT_BIG_PREVIEW_GRID = 3,
};
/** #uiList.flag */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index c170e711756..1cb8bf2a9ea 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -696,14 +696,14 @@ typedef enum eSpaceSeq_OverlayType {
* Information to identify a asset library. May be either one of the predefined types (current
* 'Main', builtin library, project library), or a custom type as defined in the Preferences.
*
- * If the type is set to #FILE_ASSET_LIBRARY_CUSTOM, idname must have the name to identify the
+ * If the type is set to #ASSET_LIBRARY_CUSTOM, idname must have the name to identify the
* custom library. Otherwise idname is not used.
*/
typedef struct FileSelectAssetLibraryUID {
short type; /* eFileAssetLibrary_Type */
char _pad[2];
/**
- * If showing a custom asset library (#FILE_ASSET_LIBRARY_CUSTOM), this is the index of the
+ * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the
* #bUserAssetLibrary within #UserDef.asset_libraries.
* Should be ignored otherwise (but better set to -1 then, for sanity and debugging).
*/
@@ -868,22 +868,6 @@ typedef enum eFileBrowse_Mode {
FILE_BROWSE_MODE_ASSETS = 1,
} eFileBrowse_Mode;
-typedef enum eFileAssetLibrary_Type {
- /* For the future. Display assets bundled with Blender by default. */
- // FILE_ASSET_LIBRARY_BUNDLED = 0,
- /** Display assets from the current session (current "Main"). */
- FILE_ASSET_LIBRARY_LOCAL = 1,
- /* For the future. Display assets for the current project. */
- // FILE_ASSET_LIBRARY_PROJECT = 2,
-
- /** Display assets from custom asset libraries, as defined in the preferences
- * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname
- * then.
- * In RNA, we add the index of the custom library to this to identify it by index. So keep
- * this last! */
- FILE_ASSET_LIBRARY_CUSTOM = 100,
-} eFileAssetLibrary_Type;
-
/* FileSelectParams.display */
enum eFileDisplayType {
/** Internal (not exposed to users): Keep whatever display type was used during the last File
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index c5c2351c718..e49146ad429 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -23,6 +23,7 @@
#pragma once
#include "DNA_ID.h"
+#include "DNA_asset_types.h"
#ifdef __cplusplus
extern "C" {
@@ -135,6 +136,10 @@ typedef struct WorkSpace {
/** Info text from modal operators (runtime). */
char *status_text;
+
+ /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The
+ * Asset Browser has its own and doesn't use this. */
+ AssetLibraryReference active_asset_library;
} WorkSpace;
/**
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 95272fb7804..94d23ee15c5 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -152,6 +152,7 @@
/* DNA_asset_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(AssetMetaData);
+SDNA_DEFAULT_DECL_STRUCT(AssetLibraryReference);
/* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(bArmature);
@@ -346,6 +347,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* DNA_asset_defaults.h */
SDNA_DEFAULT_DECL(AssetMetaData),
+ SDNA_DEFAULT_DECL(AssetLibraryReference),
/* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL(bArmature),
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index ba67cedfdbe..dfad3dce4a6 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -71,6 +71,9 @@ extern StructRNA RNA_ArrayGpencilModifier;
extern StructRNA RNA_ArrayModifier;
extern StructRNA RNA_Attribute;
extern StructRNA RNA_AttributeGroup;
+extern StructRNA RNA_AssetFilterSettings;
+extern StructRNA RNA_AssetHandle;
+extern StructRNA RNA_AssetLibraryReference;
extern StructRNA RNA_AssetMetaData;
extern StructRNA RNA_AssetTag;
extern StructRNA RNA_BackgroundImage;
@@ -472,6 +475,7 @@ extern StructRNA RNA_Paint;
extern StructRNA RNA_PaintCurve;
extern StructRNA RNA_PaintToolSlot;
extern StructRNA RNA_Palette;
+extern StructRNA RNA_PaletteColors;
extern StructRNA RNA_PaletteColor;
extern StructRNA RNA_Panel;
extern StructRNA RNA_Particle;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index b0895609e9a..341ea9ba9df 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -243,6 +243,22 @@ extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bo
extern const EnumPropertyItem rna_enum_collection_color_items[];
+/**
+ * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64
+ * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this
+ * itself.
+ *
+ * Meant to be used with #RNA_def_property_boolean_sdna() which supports 64 bit flags as well.
+ */
+struct IDFilterEnumPropertyItem {
+ const uint64_t flag;
+ const char *identifier;
+ const int icon;
+ const char *name;
+ const char *description;
+};
+extern const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[];
+
/* API calls */
int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo);
int rna_node_tree_idname_to_enum(const char *idname);
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b20d9218316..e8d79bac9b1 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4290,7 +4290,6 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_animviz.c", NULL, RNA_def_animviz},
{"rna_armature.c", "rna_armature_api.c", RNA_def_armature},
{"rna_attribute.c", NULL, RNA_def_attribute},
- {"rna_asset.c", NULL, RNA_def_asset},
{"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
{"rna_cachefile.c", NULL, RNA_def_cachefile},
@@ -4345,6 +4344,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_simulation.c", NULL, RNA_def_simulation},
#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
+ {"rna_asset.c", NULL, RNA_def_asset}, /* After spaces! */
{"rna_speaker.c", NULL, RNA_def_speaker},
{"rna_test.c", NULL, RNA_def_test},
{"rna_text.c", "rna_text_api.c", RNA_def_text},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 657af98f5fe..f750ea8f665 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -126,6 +126,97 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items
{0, NULL, 0, NULL, NULL},
};
+/**
+ * \note Uses #IDFilterEnumPropertyItem, not EnumPropertyItem to support 64 bit items.
+ */
+const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
+ /* Datablocks */
+ {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"},
+ {FILTER_ID_AR,
+ "filter_armature",
+ ICON_ARMATURE_DATA,
+ "Armatures",
+ "Show Armature data-blocks"},
+ {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"},
+ {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"},
+ {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"},
+ {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
+ {FILTER_ID_GD,
+ "filter_grease_pencil",
+ ICON_GREASEPENCIL,
+ "Grease Pencil",
+ "Show Grease pencil data-blocks"},
+ {FILTER_ID_GR,
+ "filter_group",
+ ICON_OUTLINER_COLLECTION,
+ "Collections",
+ "Show Collection data-blocks"},
+ {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"},
+ {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"},
+ {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"},
+ {FILTER_ID_LP,
+ "filter_light_probe",
+ ICON_OUTLINER_DATA_LIGHTPROBE,
+ "Light Probes",
+ "Show Light Probe data-blocks"},
+ {FILTER_ID_LS,
+ "filter_linestyle",
+ ICON_LINE_DATA,
+ "Freestyle Linestyles",
+ "Show Freestyle's Line Style data-blocks"},
+ {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"},
+ {FILTER_ID_MA,
+ "filter_material",
+ ICON_MATERIAL_DATA,
+ "Materials",
+ "Show Material data-blocks"},
+ {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"},
+ {FILTER_ID_MC,
+ "filter_movie_clip",
+ ICON_TRACKER_DATA,
+ "Movie Clips",
+ "Show Movie Clip data-blocks"},
+ {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"},
+ {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"},
+ {FILTER_ID_NT, "filter_node_tree", ICON_NODETREE, "Node Trees", "Show Node Tree data-blocks"},
+ {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"},
+ {FILTER_ID_PA,
+ "filter_particle_settings",
+ ICON_PARTICLE_DATA,
+ "Particles Settings",
+ "Show Particle Settings data-blocks"},
+ {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"},
+ {FILTER_ID_PC,
+ "filter_paint_curve",
+ ICON_CURVE_BEZCURVE,
+ "Paint Curves",
+ "Show Paint Curve data-blocks"},
+ {FILTER_ID_PT,
+ "filter_pointcloud",
+ ICON_POINTCLOUD_DATA,
+ "Point Clouds",
+ "Show/hide Point Cloud data-blocks"},
+ {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
+ {FILTER_ID_SIM,
+ "filter_simulation",
+ ICON_PHYSICS,
+ "Simulations",
+ "Show Simulation data-blocks"}, /* TODO: Use correct icon. */
+ {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
+ {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
+ {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
+ {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"},
+ {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"},
+ {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"},
+ {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"},
+ {FILTER_ID_WS,
+ "filter_work_space",
+ ICON_WORKSPACE,
+ "Workspaces",
+ "Show workspace data-blocks"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index 1af53e95cc9..397c033d494 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -35,6 +35,8 @@
# include "BLI_listbase.h"
+# include "ED_asset.h"
+
# include "RNA_access.h"
static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data,
@@ -129,6 +131,87 @@ static void rna_AssetMetaData_active_tag_range(
*max = *softmax = MAX2(asset_data->tot_tags - 1, 0);
}
+static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr)
+{
+ AssetHandle *asset_handle = ptr->data;
+ return rna_pointer_inherit_refine(ptr, &RNA_AssetHandle, asset_handle->file_data);
+}
+
+static void rna_AssetHandle_file_data_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *UNUSED(reports))
+{
+ AssetHandle *asset_handle = ptr->data;
+ asset_handle->file_data = value.data;
+}
+
+int rna_asset_library_reference_get(const AssetLibraryReference *library)
+{
+ return ED_asset_library_reference_to_enum_value(library);
+}
+
+void rna_asset_library_reference_set(AssetLibraryReference *library, int value)
+{
+ *library = ED_asset_library_reference_from_enum_value(value);
+}
+
+const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ const EnumPropertyItem predefined_items[] = {
+ /* For the future. */
+ // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
+ {ASSET_LIBRARY_LOCAL,
+ "LOCAL",
+ ICON_BLENDER,
+ "Current File",
+ "Show the assets currently available in this Blender session"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+
+ /* Add separator if needed. */
+ if (!BLI_listbase_is_empty(&U.asset_libraries)) {
+ const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL};
+ RNA_enum_item_add(&item, &totitem, &sepr);
+ }
+
+ int i = 0;
+ for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library;
+ user_library = user_library->next, i++) {
+ /* Note that the path itself isn't checked for validity here. If an invalid library path is
+ * used, the Asset Browser can give a nice hint on what's wrong. */
+ const bool is_valid = (user_library->name[0] && user_library->path[0]);
+ if (!is_valid) {
+ continue;
+ }
+
+ /* Use library path as description, it's a nice hint for users. */
+ EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i,
+ user_library->name,
+ ICON_NONE,
+ user_library->name,
+ user_library->path};
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ }
+
+ if (totitem) {
+ const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL};
+ RNA_enum_item_add(&item, &totitem, &sepr);
+ }
+
+ /* Add predefined items. */
+ RNA_enum_items_add(&item, &totitem, predefined_items);
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+ return item;
+}
+
#else
static void rna_def_asset_tag(BlenderRNA *brna)
@@ -181,6 +264,33 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
}
+static void rna_def_asset_filter_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "AssetFilterSettings", NULL);
+ RNA_def_struct_ui_text(srna, "Asset Filter Settings", "");
+
+ prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "AssetTag");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Tags",
+ "Custom tags (name tokens) for the asset, used for filtering and "
+ "general asset management");
+ RNA_def_property_srna(prop, "AssetTags");
+
+ for (int i = 0; rna_enum_id_type_filter_items[i].identifier; i++) {
+ prop = RNA_def_property(
+ srna, rna_enum_id_type_filter_items[i].identifier, PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "id_types", rna_enum_id_type_filter_items[i].flag);
+ RNA_def_property_ui_text(
+ prop, rna_enum_id_type_filter_items[i].name, rna_enum_id_type_filter_items[i].description);
+ RNA_def_property_ui_icon(prop, rna_enum_id_type_filter_items[i].icon, 0);
+ }
+}
+
static void rna_def_asset_data(BlenderRNA *brna)
{
StructRNA *srna;
@@ -215,12 +325,52 @@ static void rna_def_asset_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing");
}
+static void rna_def_asset_handle(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "AssetHandle", "PropertyGroup");
+ RNA_def_struct_ui_text(srna, "Asset Handle", "Reference to some asset");
+
+ prop = RNA_def_property(srna, "file_data", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_struct_type(prop, "FileSelectEntry");
+ RNA_def_property_pointer_funcs(
+ prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", NULL, NULL);
+ RNA_def_property_ui_text(prop, "File Entry", "File data used to refer to the asset");
+}
+
+static void rna_def_asset_library_reference(BlenderRNA *brna)
+{
+ StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL);
+ RNA_def_struct_ui_text(
+ srna, "Asset Library Reference", "Identifier to refere to the asset library");
+}
+
+/**
+ * \note the UI text and updating has to be set by the caller.
+ */
+PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
+ const char *get,
+ const char *set)
+{
+ PropertyRNA *prop = RNA_def_property(srna, "active_asset_library", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, DummyRNA_NULL_items);
+ RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf");
+
+ return prop;
+}
+
void RNA_def_asset(BlenderRNA *brna)
{
RNA_define_animate_sdna(false);
rna_def_asset_tag(brna);
+ rna_def_asset_filter_settings(brna);
rna_def_asset_data(brna);
+ rna_def_asset_library_reference(brna);
+ rna_def_asset_handle(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 95972dd444f..eb52944e769 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -30,6 +30,7 @@
#define RNA_MAGIC ((int)~0)
+struct AssetLibraryReference;
struct FreestyleSettings;
struct ID;
struct IDOverrideLibrary;
@@ -266,6 +267,16 @@ void rna_def_mtex_common(struct BlenderRNA *brna,
void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna);
void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene);
+PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
+ const char *get,
+ const char *set);
+int rna_asset_library_reference_get(const struct AssetLibraryReference *library);
+void rna_asset_library_reference_set(struct AssetLibraryReference *library, int value);
+const EnumPropertyItem *rna_asset_library_reference_itemf(struct bContext *C,
+ struct PointerRNA *ptr,
+ struct PropertyRNA *prop,
+ bool *r_free);
+
void rna_def_actionbone_group_common(struct StructRNA *srna,
int update_flag,
const char *update_cb);
diff --git a/source/blender/makesrna/intern/rna_palette.c b/source/blender/makesrna/intern/rna_palette.c
index 67d6daf5a13..5a7d9cf6e27 100644
--- a/source/blender/makesrna/intern/rna_palette.c
+++ b/source/blender/makesrna/intern/rna_palette.c
@@ -128,6 +128,11 @@ static void rna_def_palettecolors(BlenderRNA *brna, PropertyRNA *cprop)
prop, "rna_Palette_active_color_get", "rna_Palette_active_color_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Active Palette Color", "");
+
+ /* XXX just for testing. */
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "active_color");
+ RNA_def_property_ui_text(prop, "Active Palette Color Index", "");
}
static void rna_def_palettecolor(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index f9b1816e1ba..cf22ff95e74 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -504,6 +504,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = {
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
+# include "DNA_asset_types.h"
# include "DNA_scene_types.h"
# include "DNA_screen_types.h"
# include "DNA_userdef_types.h"
@@ -2542,6 +2543,8 @@ static PointerRNA rna_FileSelectParams_filter_id_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_FileSelectIDFilter, ptr->data);
}
+/* TODO use rna_def_asset_library_reference_common() */
+
static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
{
FileAssetSelectParams *params = ptr->data;
@@ -2549,7 +2552,7 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
BLI_assert(ptr->type == &RNA_FileAssetSelectParams);
/* Simple case: Predefined repo, just set the value. */
- if (params->asset_library.type < FILE_ASSET_LIBRARY_CUSTOM) {
+ if (params->asset_library.type < ASSET_LIBRARY_CUSTOM) {
return params->asset_library.type;
}
@@ -2558,11 +2561,11 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
&U, params->asset_library.custom_library_index);
if (user_library) {
- return FILE_ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index;
+ return ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index;
}
BLI_assert(0);
- return FILE_ASSET_LIBRARY_LOCAL;
+ return ASSET_LIBRARY_LOCAL;
}
static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value)
@@ -2570,26 +2573,26 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val
FileAssetSelectParams *params = ptr->data;
/* Simple case: Predefined repo, just set the value. */
- if (value < FILE_ASSET_LIBRARY_CUSTOM) {
+ if (value < ASSET_LIBRARY_CUSTOM) {
params->asset_library.type = value;
params->asset_library.custom_library_index = -1;
- BLI_assert(ELEM(value, FILE_ASSET_LIBRARY_LOCAL));
+ BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL));
return;
}
const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
- &U, value - FILE_ASSET_LIBRARY_CUSTOM);
+ &U, value - ASSET_LIBRARY_CUSTOM);
/* Note that the path isn't checked for validity here. If an invalid library path is used, the
* Asset Browser can give a nice hint on what's wrong. */
const bool is_valid = (user_library->name[0] && user_library->path[0]);
if (!user_library) {
- params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
+ params->asset_library.type = ASSET_LIBRARY_LOCAL;
params->asset_library.custom_library_index = -1;
}
else if (user_library && is_valid) {
- params->asset_library.custom_library_index = value - FILE_ASSET_LIBRARY_CUSTOM;
- params->asset_library.type = FILE_ASSET_LIBRARY_CUSTOM;
+ params->asset_library.custom_library_index = value - ASSET_LIBRARY_CUSTOM;
+ params->asset_library.type = ASSET_LIBRARY_CUSTOM;
}
}
@@ -2598,8 +2601,8 @@ static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf(
{
const EnumPropertyItem predefined_items[] = {
/* For the future. */
- // {FILE_ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
- {FILE_ASSET_LIBRARY_LOCAL,
+ // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
+ {ASSET_LIBRARY_LOCAL,
"LOCAL",
ICON_BLENDER,
"Current File",
@@ -2627,7 +2630,7 @@ static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf(
}
/* Use library path as description, it's a nice hint for users. */
- EnumPropertyItem tmp = {FILE_ASSET_LIBRARY_CUSTOM + i,
+ EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i,
user_library->name,
ICON_NONE,
user_library->name,
@@ -2955,7 +2958,7 @@ static void rna_FileBrowser_FSMenu_active_range(PointerRNA *UNUSED(ptr),
static void rna_FileBrowser_FSMenu_active_update(struct bContext *C, PointerRNA *ptr)
{
ScrArea *area = rna_area_from_space(ptr);
- ED_file_change_dir_ex(C, (bScreen *)ptr->owner_id, area);
+ ED_file_change_dir_ex(C, area);
}
static int rna_FileBrowser_FSMenuSystem_active_get(PointerRNA *ptr)
@@ -3107,6 +3110,45 @@ static const EnumPropertyItem dt_uv_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[] = {
+ /* Categories */
+ {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"},
+ {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"},
+ {FILTER_ID_OB | FILTER_ID_GR,
+ "category_object",
+ ICON_OUTLINER_COLLECTION,
+ "Objects & Collections",
+ "Show objects and collections"},
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA |
+ FILTER_ID_PT | FILTER_ID_VO,
+ "category_geometry",
+ ICON_NODETREE,
+ "Geometry",
+ "Show meshes, curves, lattice, armatures and metaballs data"},
+ {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
+ "category_shading",
+ ICON_MATERIAL_DATA,
+ "Shading",
+ "Show materials, nodetrees, textures and Freestyle's linestyles"},
+ {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
+ "category_image",
+ ICON_IMAGE_DATA,
+ "Images & Sounds",
+ "Show images, movie clips, sounds and masks"},
+ {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
+ "category_environment",
+ ICON_WORLD_DATA,
+ "Environment",
+ "Show worlds, lights, cameras and speakers"},
+ {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
+ FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
+ "category_misc",
+ ICON_GREASEPENCIL,
+ "Miscellaneous",
+ "Show other data types"},
+ {0, NULL, 0, NULL, NULL},
+};
+
static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int region_type_mask)
{
PropertyRNA *prop;
@@ -4788,6 +4830,7 @@ static void rna_def_space_view3d(BlenderRNA *brna)
}
/* Nested Structs */
+
prop = RNA_def_property(srna, "shading", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "View3DShading");
@@ -6031,142 +6074,6 @@ static void rna_def_space_console(BlenderRNA *brna)
/* Filter for datablock types in link/append. */
static void rna_def_fileselect_idfilter(BlenderRNA *brna)
{
- struct IDFilterBoolean {
- /* 64 bit, so we can't use bitflag enum. */
- const uint64_t flag;
- const char *identifier;
- const int icon;
- const char *name;
- const char *description;
- };
-
- static const struct IDFilterBoolean booleans[] = {
- /* Datablocks */
- {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"},
- {FILTER_ID_AR,
- "filter_armature",
- ICON_ARMATURE_DATA,
- "Armatures",
- "Show Armature data-blocks"},
- {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"},
- {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"},
- {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"},
- {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"},
- {FILTER_ID_GD,
- "filter_grease_pencil",
- ICON_GREASEPENCIL,
- "Grease Pencil",
- "Show Grease pencil data-blocks"},
- {FILTER_ID_GR,
- "filter_group",
- ICON_OUTLINER_COLLECTION,
- "Collections",
- "Show Collection data-blocks"},
- {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"},
- {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"},
- {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"},
- {FILTER_ID_LP,
- "filter_light_probe",
- ICON_OUTLINER_DATA_LIGHTPROBE,
- "Light Probes",
- "Show Light Probe data-blocks"},
- {FILTER_ID_LS,
- "filter_linestyle",
- ICON_LINE_DATA,
- "Freestyle Linestyles",
- "Show Freestyle's Line Style data-blocks"},
- {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"},
- {FILTER_ID_MA,
- "filter_material",
- ICON_MATERIAL_DATA,
- "Materials",
- "Show Material data-blocks"},
- {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"},
- {FILTER_ID_MC,
- "filter_movie_clip",
- ICON_TRACKER_DATA,
- "Movie Clips",
- "Show Movie Clip data-blocks"},
- {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"},
- {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"},
- {FILTER_ID_NT,
- "filter_node_tree",
- ICON_NODETREE,
- "Node Trees",
- "Show Node Tree data-blocks"},
- {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"},
- {FILTER_ID_PA,
- "filter_particle_settings",
- ICON_PARTICLE_DATA,
- "Particles Settings",
- "Show Particle Settings data-blocks"},
- {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"},
- {FILTER_ID_PC,
- "filter_paint_curve",
- ICON_CURVE_BEZCURVE,
- "Paint Curves",
- "Show Paint Curve data-blocks"},
- {FILTER_ID_PT,
- "filter_pointcloud",
- ICON_POINTCLOUD_DATA,
- "Point Clouds",
- "Show/hide Point Cloud data-blocks"},
- {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
- {FILTER_ID_SIM,
- "filter_simulation",
- ICON_PHYSICS,
- "Simulations",
- "Show Simulation data-blocks"}, /* TODO: Use correct icon. */
- {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
- {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
- {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
- {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"},
- {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"},
- {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"},
- {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"},
- {FILTER_ID_WS,
- "filter_work_space",
- ICON_WORKSPACE,
- "Workspaces",
- "Show workspace data-blocks"},
-
- /* Categories */
- {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"},
- {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"},
- {FILTER_ID_OB | FILTER_ID_GR,
- "category_object",
- ICON_OUTLINER_COLLECTION,
- "Objects & Collections",
- "Show objects and collections"},
- {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA |
- FILTER_ID_PT | FILTER_ID_VO,
- "category_geometry",
- ICON_NODETREE,
- "Geometry",
- "Show meshes, curves, lattice, armatures and metaballs data"},
- {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
- "category_shading",
- ICON_MATERIAL_DATA,
- "Shading",
- "Show materials, nodetrees, textures and Freestyle's linestyles"},
- {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
- "category_image",
- ICON_IMAGE_DATA,
- "Images & Sounds",
- "Show images, movie clips, sounds and masks"},
- {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO,
- "category_environment",
- ICON_WORLD_DATA,
- "Environment",
- "Show worlds, lights, cameras and speakers"},
- {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT |
- FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS,
- "category_misc",
- ICON_GREASEPENCIL,
- "Miscellaneous",
- "Show other data types"},
-
- {0, NULL, 0, NULL, NULL}};
StructRNA *srna = RNA_def_struct(brna, "FileSelectIDFilter", NULL);
RNA_def_struct_sdna(srna, "FileSelectParams");
@@ -6174,12 +6081,23 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "File Select ID Filter", "Which ID types to show/hide, when browsing a library");
- for (int i = 0; booleans[i].identifier; i++) {
- PropertyRNA *prop = RNA_def_property(srna, booleans[i].identifier, PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "filter_id", booleans[i].flag);
- RNA_def_property_ui_text(prop, booleans[i].name, booleans[i].description);
- RNA_def_property_ui_icon(prop, booleans[i].icon, 0);
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ const struct IDFilterEnumPropertyItem *individual_ids_and_categories[] = {
+ rna_enum_id_type_filter_items,
+ rna_enum_space_file_id_filter_categories,
+ NULL,
+ };
+ for (uint i = 0; individual_ids_and_categories[i]; i++) {
+ for (int j = 0; individual_ids_and_categories[i][j].identifier; j++) {
+ PropertyRNA *prop = RNA_def_property(
+ srna, individual_ids_and_categories[i][j].identifier, PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "filter_id", individual_ids_and_categories[i][j].flag);
+ RNA_def_property_ui_text(prop,
+ individual_ids_and_categories[i][j].name,
+ individual_ids_and_categories[i][j].description);
+ RNA_def_property_ui_icon(prop, individual_ids_and_categories[i][j].icon, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ }
}
}
@@ -6435,7 +6353,7 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- /* XXX copied from rna_def_fileselect_idfilter. */
+ /* XXX copied from rna_enum_id_type_filter_items. */
static const EnumPropertyItem asset_category_items[] = {
{FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"},
{FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"},
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index f128719db19..7ce80c9640b 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -50,6 +50,8 @@ const EnumPropertyItem rna_enum_icon_items[] = {
#ifdef RNA_RUNTIME
+# include "DNA_asset_types.h"
+
const char *rna_translate_ui_text(
const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate)
{
@@ -568,6 +570,65 @@ static void rna_uiTemplateEventFromKeymapItem(
uiTemplateEventFromKeymapItem(layout, name, kmi, true);
}
+static void rna_uiTemplateAssetView(uiLayout *layout,
+ bContext *C,
+ const char *list_id,
+ PointerRNA *asset_library_dataptr,
+ const char *asset_library_propname,
+ PointerRNA *assets_dataptr,
+ const char *assets_propname,
+ PointerRNA *active_dataptr,
+ const char *active_propname,
+ int filter_id_types,
+ const char *activate_opname,
+ const char *drag_opname)
+{
+ AssetFilterSettings filter_settings = {
+ .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL,
+ };
+ uiTemplateAssetView(layout,
+ C,
+ list_id,
+ asset_library_dataptr,
+ asset_library_propname,
+ assets_dataptr,
+ assets_propname,
+ active_dataptr,
+ active_propname,
+ &filter_settings,
+ activate_opname,
+ drag_opname);
+}
+
+/**
+ * XXX Remove filter items that require more than 32 bits for storage. RNA enums don't support
+ * that currently.
+ */
+static const EnumPropertyItem *rna_uiTemplateAssetView_filter_id_types_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ EnumPropertyItem *items = NULL;
+ int totitem = 0;
+
+ for (int i = 0; rna_enum_id_type_filter_items[i].identifier; i++) {
+ if (rna_enum_id_type_filter_items[i].flag > (1ULL << 31)) {
+ continue;
+ }
+
+ EnumPropertyItem tmp = {0, "", 0, "", ""};
+ tmp.value = rna_enum_id_type_filter_items[i].flag;
+ tmp.identifier = rna_enum_id_type_filter_items[i].identifier;
+ tmp.icon = rna_enum_id_type_filter_items[i].icon;
+ tmp.name = rna_enum_id_type_filter_items[i].name;
+ tmp.description = rna_enum_id_type_filter_items[i].description;
+ RNA_enum_item_add(&items, &totitem, &tmp);
+ }
+ RNA_enum_item_end(&items, &totitem);
+
+ *r_free = true;
+ return items;
+}
+
static uiLayout *rna_uiLayoutRowWithHeading(
uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate)
{
@@ -1677,6 +1738,65 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_property_ui_text(parm, "Item", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
api_ui_item_common_text(func);
+
+ func = RNA_def_function(srna, "template_asset_view", "rna_uiTemplateAssetView");
+ RNA_def_function_ui_description(func, "Item. A scrollable list of assets in a grid view");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ parm = RNA_def_string(func,
+ "list_id",
+ NULL,
+ 0,
+ "",
+ "Identifier of this asset view. Necessary to tell apart different asset "
+ "views and to idenify an asset view read from a .blend");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "asset_library_dataptr",
+ "AnyType",
+ "",
+ "Data from which to take the active asset library property");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func, "asset_library_propname", NULL, 0, "", "Identifier of the asset library property");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "assets_dataptr", "AnyType", "", "Data from which to take the asset list property");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func, "assets_propname", NULL, 0, "", "Identifier of the asset list property");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "active_dataptr",
+ "AnyType",
+ "",
+ "Data from which to take the integer property, index of the active item");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ parm = RNA_def_string(
+ func,
+ "active_propname",
+ NULL,
+ 0,
+ "",
+ "Identifier of the integer property in active_data, index of the active item");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_property(func, "filter_id_types", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(parm, DummyRNA_NULL_items);
+ RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf");
+ RNA_def_property_flag(parm, PROP_ENUM_FLAG);
+ RNA_def_string(func,
+ "activate_operator",
+ NULL,
+ 0,
+ "",
+ "Name of a custom operator to invoke when activating an item");
+ RNA_def_string(func,
+ "drag_operator",
+ NULL,
+ 0,
+ "",
+ "Name of a custom operator to invoke when starting to drag an item. Never "
+ "invoked together with the `active_operator` (if set), it's either the drag or "
+ "the activate one");
}
#endif
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index 6b52a38c2da..b053bb0ff62 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -107,6 +107,18 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace)
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, workspace);
}
+static int rna_WorkSpace_active_asset_library_get(PointerRNA *ptr)
+{
+ const WorkSpace *workspace = ptr->data;
+ return rna_asset_library_reference_get(&workspace->active_asset_library);
+}
+
+static void rna_WorkSpace_active_asset_library_set(PointerRNA *ptr, int value)
+{
+ WorkSpace *workspace = ptr->data;
+ rna_asset_library_reference_set(&workspace->active_asset_library, value);
+}
+
static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace,
const bToolKey *tkey,
bool create)
@@ -407,6 +419,14 @@ static void rna_def_workspace(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use UI Tags", "Filter the UI by tags");
RNA_def_property_update(prop, 0, "rna_window_update_all");
+ prop = rna_def_asset_library_reference_common(
+ srna, "rna_WorkSpace_active_asset_library_get", "rna_WorkSpace_active_asset_library_set");
+ RNA_def_property_ui_text(prop,
+ "Asset Library",
+ "Active asset library to show in the UI, not used by the Asset Browser "
+ "(which has its own active asset library)");
+ RNA_def_property_update(prop, NC_ASSET | ND_ASSET_LIST_READING, NULL);
+
RNA_api_workspace(srna);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index e9228a2942b..0c970f0d2e2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -255,6 +255,8 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
+ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
+
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d54925272de..536b74d525c 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -434,6 +434,12 @@ typedef struct wmNotifier {
#define ND_SPACE_FILE_PREVIEW (21 << 16)
#define ND_SPACE_SPREADSHEET (22 << 16)
+/* NC_ASSET */
+/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of
+ * assets is done. */
+#define ND_ASSET_LIST_PREVIEW (1 << 16)
+#define ND_ASSET_LIST_READING (2 << 16)
+
/* subtype, 256 entries too */
#define NOTE_SUBTYPE 0x0000FF00
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 791aeeb39cd..8d19d72b5ca 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -61,6 +61,7 @@
#include "BLT_translation.h"
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_info.h"
#include "ED_screen.h"
@@ -326,6 +327,7 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
}
}
}
+ ED_assetlist_storage_id_remap(old_id, new_id);
wmWindowManager *wm = bmain->wm.first;
if (wm && wm->message_bus) {
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 56fd51ac6fd..23941867caa 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -109,6 +109,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_gpencil.h"
#include "ED_keyframes_edit.h"
#include "ED_keyframing.h"
@@ -566,6 +567,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
RE_engines_exit();
ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */
+ ED_assetlist_storage_exit();
if (wm) {
/* Before BKE_blender_free! - since the ListBases get freed there. */