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/modules/bpy_extras/__init__.py1
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py63
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/assets.py74
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py196
-rw-r--r--source/blender/editors/interface/interface_style.c8
-rw-r--r--source/blender/editors/space_file/file_draw.c186
-rw-r--r--source/blender/editors/space_file/file_intern.h3
-rw-r--r--source/blender/editors/space_file/file_ops.c28
-rw-r--r--source/blender/editors/space_file/file_utils.c17
-rw-r--r--source/blender/editors/space_file/space_file.c87
-rw-r--r--source/blender/makesrna/intern/rna_screen.c2
-rw-r--r--source/blender/makesrna/intern/rna_space.c62
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c5
14 files changed, 677 insertions, 56 deletions
diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py
index 1caef074d43..cb990b014a1 100644
--- a/release/scripts/modules/bpy_extras/__init__.py
+++ b/release/scripts/modules/bpy_extras/__init__.py
@@ -24,6 +24,7 @@ Utility modules associated with the bpy module.
__all__ = (
"anim_utils",
+ "asset_utils",
"object_utils",
"io_utils",
"image_utils",
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
new file mode 100644
index 00000000000..db982e119d4
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -0,0 +1,63 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+Helpers for asset management tasks.
+"""
+
+import bpy
+from bpy.types import (
+ Context,
+)
+
+__all__ = (
+ "SpaceAssetInfo",
+)
+
+class SpaceAssetInfo:
+ @classmethod
+ def is_asset_browser(cls, space_data: bpy.types.Space):
+ return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
+
+ @classmethod
+ def is_asset_browser_poll(cls, context: Context):
+ return cls.is_asset_browser(context.space_data)
+
+ @classmethod
+ def get_active_asset(cls, context: Context):
+ if hasattr(context, "active_file"):
+ active_file = context.active_file
+ return active_file.asset_data if active_file else None
+
+class AssetBrowserPanel:
+ bl_space_type = 'FILE_BROWSER'
+
+ @classmethod
+ def poll(cls, context):
+ return SpaceAssetInfo.is_asset_browser_poll(context)
+
+class AssetMetaDataPanel:
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+
+ @classmethod
+ def poll(cls, context):
+ active_file = context.active_file
+ return SpaceAssetInfo.is_asset_browser_poll(context) and active_file and active_file.asset_data
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 71b2de41d9e..e91d3b3ce60 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -27,6 +27,7 @@ if "bpy" in locals():
_modules = [
"add_mesh_torus",
"anim",
+ "assets",
"clip",
"console",
"constraint",
diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py
new file mode 100644
index 00000000000..c317df78aa5
--- /dev/null
+++ b/release/scripts/startup/bl_operators/assets.py
@@ -0,0 +1,74 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+
+from bpy_extras.asset_utils import (
+ SpaceAssetInfo,
+)
+
+class ASSET_OT_tag_add(bpy.types.Operator):
+ """Add a new keyword tag to the active asset"""
+
+ bl_idname = "asset.tag_add"
+ bl_label = "Add Asset Tag"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context)
+
+ def execute(self, context):
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ active_asset.tags.new("Unnamed Tag")
+
+ return {'FINISHED'}
+
+
+class ASSET_OT_tag_remove(bpy.types.Operator):
+ """Remove an existing keyword tag from the active asset"""
+
+ bl_idname = "asset.tag_remove"
+ bl_label = "Remove Asset Tag"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ if not SpaceAssetInfo.is_asset_browser_poll(context):
+ return False
+
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ if not active_asset:
+ return False
+
+ return active_asset.active_tag in range(len(active_asset.tags))
+
+ def execute(self, context):
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ tag = active_asset.tags[active_asset.active_tag]
+
+ active_asset.tags.remove(tag)
+ active_asset.active_tag -= 1
+
+ return {'FINISHED'}
+
+
+classes = (
+ ASSET_OT_tag_add,
+ ASSET_OT_tag_remove,
+)
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index a9bb2e79762..527a5bc623e 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -17,27 +17,79 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
+
+import bpy
+
from bpy.types import Header, Panel, Menu, UIList
+from bpy_extras import (
+ asset_utils,
+)
+
class FILEBROWSER_HT_header(Header):
bl_space_type = 'FILE_BROWSER'
+ def draw_asset_browser_buttons(self, context):
+ layout = self.layout
+
+ space_data = context.space_data
+ params = space_data.params
+
+ row = layout.row(align=True)
+ row.prop(params, "asset_library", text="")
+ # External libraries don't auto-refresh, add refresh button.
+ if params.asset_library != 'LOCAL':
+ row.operator("file.refresh", text="", icon="FILE_REFRESH")
+
+ layout.separator_spacer()
+
+ # Uses prop_with_popover() as popover() only adds the triangle icon in headers.
+ layout.prop_with_popover(
+ params,
+ "display_type",
+ panel="FILEBROWSER_PT_display",
+ text="",
+ icon_only=True,
+ )
+ layout.prop_with_popover(
+ params,
+ "display_type",
+ panel="FILEBROWSER_PT_filter",
+ text="",
+ icon='FILTER',
+ icon_only=True,
+ )
+
+ layout.prop(params, "filter_search", text="", icon='VIEWZOOM')
+
+ layout.operator(
+ "screen.region_toggle",
+ text="",
+ icon='PREFERENCES',
+ depress=is_option_region_visible(context, space_data)
+ ).region_type = 'TOOL_PROPS'
+
def draw(self, context):
+ from bpy_extras.asset_utils import SpaceAssetInfo
+
layout = self.layout
- st = context.space_data
+ space_data = context.space_data
- if st.active_operator is None:
+ if space_data.active_operator is None:
layout.template_header()
FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout)
- # can be None when save/reload with a file selector open
-
- layout.separator_spacer()
+ if SpaceAssetInfo.is_asset_browser(space_data):
+ layout.separator()
+ self.draw_asset_browser_buttons(context)
+ else:
+ layout.separator_spacer()
- layout.template_running_jobs()
+ if not context.screen.show_statusbar:
+ layout.template_running_jobs()
class FILEBROWSER_PT_display(Panel):
@@ -144,6 +196,9 @@ class FILEBROWSER_PT_filter(Panel):
row.label(icon='BLANK1') # Indentation
sub = row.column(align=True)
+
+ sub.prop(params, "use_filter_asset_only")
+
filter_id = params.filter_id
for identifier in dir(filter_id):
if identifier.startswith("category_"):
@@ -160,6 +215,11 @@ def panel_poll_is_upper_region(region):
return region.alignment in {'LEFT', 'RIGHT'}
+def panel_poll_is_asset_browsing(context):
+ from bpy_extras.asset_utils import SpaceAssetInfo
+ return SpaceAssetInfo.is_asset_browser_poll(context)
+
+
class FILEBROWSER_UL_dir(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
direntry = item
@@ -187,7 +247,7 @@ class FILEBROWSER_PT_bookmarks_volumes(Panel):
@classmethod
def poll(cls, context):
- return panel_poll_is_upper_region(context.region)
+ return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -207,7 +267,7 @@ class FILEBROWSER_PT_bookmarks_system(Panel):
@classmethod
def poll(cls, context):
- return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region)
+ return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -241,7 +301,7 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel):
@classmethod
def poll(cls, context):
- return panel_poll_is_upper_region(context.region)
+ return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -278,7 +338,7 @@ class FILEBROWSER_PT_bookmarks_recents(Panel):
@classmethod
def poll(cls, context):
- return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region)
+ return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -302,7 +362,7 @@ class FILEBROWSER_PT_advanced_filter(Panel):
@classmethod
def poll(cls, context):
# only useful in append/link (library) context currently...
- return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region)
+ return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -314,12 +374,26 @@ class FILEBROWSER_PT_advanced_filter(Panel):
if params.use_filter_blendid:
layout.separator()
col = layout.column(align=True)
+
+ col.prop(params, "use_filter_asset_only")
+
filter_id = params.filter_id
for identifier in dir(filter_id):
if identifier.startswith("filter_"):
col.prop(filter_id, identifier, toggle=True)
+def is_option_region_visible(context, space):
+ if not space.active_operator:
+ return False
+
+ for region in context.area.regions:
+ if region.type == 'TOOL_PROPS' and region.width <= 1:
+ return False
+
+ return True
+
+
class FILEBROWSER_PT_directory_path(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'UI'
@@ -334,16 +408,6 @@ class FILEBROWSER_PT_directory_path(Panel):
return True
- def is_option_region_visible(self, context, space):
- if not space.active_operator:
- return False
-
- for region in context.area.regions:
- if region.type == 'TOOL_PROPS' and region.width <= 1:
- return False
-
- return True
-
def draw(self, context):
layout = self.layout
space = context.space_data
@@ -388,7 +452,7 @@ class FILEBROWSER_PT_directory_path(Panel):
"screen.region_toggle",
text="",
icon='PREFERENCES',
- depress=self.is_option_region_visible(context, space)
+ depress=is_option_region_visible(context, space)
).region_type = 'TOOL_PROPS'
@@ -482,6 +546,89 @@ class FILEBROWSER_MT_context_menu(Menu):
layout.prop_menu_enum(params, "sort_method")
+class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel):
+ bl_label = "Asset Navigation"
+ bl_region_type = 'TOOLS'
+ bl_options = {'HIDE_HEADER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ space_file = context.space_data
+
+ col = layout.column()
+
+ col.scale_x = 1.3
+ col.scale_y = 1.3
+ col.prop(space_file.params, "asset_category", expand=True)
+
+
+class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Asset Metadata"
+ bl_options = {'HIDE_HEADER'}
+
+ def draw(self, context):
+ layout = self.layout
+ active_file = context.active_file
+ active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ layout.use_property_split = True
+
+ if not active_file or not active_asset:
+ layout.label(text="No asset selected.")
+ return
+
+ box = layout.box()
+ box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0)
+ if bpy.ops.ed.lib_id_load_custom_preview.poll():
+ box.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="Load Custom")
+ layout.prop(active_file, "name")
+
+
+class ASSETBROWSER_PT_metadata_details(asset_utils.AssetBrowserPanel, Panel):
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Details"
+ bl_parent_id = "ASSETBROWSER_PT_metadata"
+
+ def draw(self, context):
+ layout = self.layout
+ active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ layout.use_property_split = True
+
+ if active_asset:
+ layout.prop(active_asset, "description")
+
+
+class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
+ bl_label = "Tags"
+
+ def draw(self, context):
+ layout = self.layout
+ asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ row = layout.row()
+ row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags",
+ asset_data, "active_tag", rows=4)
+
+ col = row.column(align=True)
+ col.operator("asset.tag_add", icon='ADD', text="")
+ col.operator("asset.tag_remove", icon='REMOVE', text="")
+
+
+class ASSETBROWSER_UL_metadata_tags(UIList):
+ def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
+ tag = item
+
+ row = layout.row(align=True)
+ # Non-editable entries would show grayed-out, which is bad in this specific case, so switch to mere label.
+ if tag.is_property_readonly("name"):
+ row.label(text=tag.name, icon_value=icon)
+ else:
+ row.prop(tag, "name", text="", emboss=False, icon_value=icon)
+
+
classes = (
FILEBROWSER_HT_header,
FILEBROWSER_PT_display,
@@ -498,6 +645,11 @@ classes = (
FILEBROWSER_MT_view,
FILEBROWSER_MT_select,
FILEBROWSER_MT_context_menu,
+ ASSETBROWSER_PT_navigation_bar,
+ ASSETBROWSER_PT_metadata,
+ ASSETBROWSER_PT_metadata_details,
+ ASSETBROWSER_PT_metadata_tags,
+ ASSETBROWSER_UL_metadata_tags,
)
if __name__ == "__main__": # only for live edit.
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index c3d528ad5c5..a37fb0dfde1 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -206,8 +206,12 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
BLF_disable(fs->uifont_id, font_flag);
- *r_xofs = xofs;
- *r_yofs = yofs;
+ if (r_xofs) {
+ *r_xofs = xofs;
+ }
+ if (r_yofs) {
+ *r_yofs = yofs;
+ }
}
void UI_fontstyle_draw(const uiFontStyle *fs,
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index e3bdda7c480..f2f7f9d82f9 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -25,6 +25,7 @@
#include <math.h>
#include <string.h>
+#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
#include "BLI_math.h"
@@ -134,6 +135,7 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
}
static void file_draw_icon(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -157,8 +159,29 @@ static void file_draw_icon(uiBlock *block,
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
if (drag) {
- /* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_path(but, BLI_strdup(path), true);
+ /* TODO duplicated from file_draw_preview(). */
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ ImBuf *preview_image = filelist_file_getimage(file);
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(but,
+ file->name,
+ BLI_strdup(blend_path),
+ file->blentype,
+ icon,
+ preview_image,
+ UI_DPI_FAC);
+ }
+ }
+ else {
+ /* path is no more static, cannot give it directly to but... */
+ UI_but_drag_set_path(but, BLI_strdup(path), true);
+ }
}
}
@@ -200,6 +223,65 @@ static void file_draw_string(int sx,
});
}
+/**
+ * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on
+ * completion.
+ */
+static void file_draw_string_multiline(int sx,
+ int sy,
+ const char *string,
+ int wrap_width,
+ int line_height,
+ const uchar text_col[4],
+ int *r_sx,
+ int *r_sy)
+{
+ rcti rect;
+
+ if (string[0] == '\0' || wrap_width < 1) {
+ return;
+ }
+
+ const uiStyle *style = UI_style_get();
+ int font_id = style->widgetlabel.uifont_id;
+ int len = strlen(string);
+
+ rctf textbox;
+ BLF_wordwrap(font_id, wrap_width);
+ BLF_enable(font_id, BLF_WORD_WRAP);
+ BLF_boundbox(font_id, string, len, &textbox);
+ BLF_disable(font_id, BLF_WORD_WRAP);
+
+ /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
+ * (for buttons it works) */
+ rect.xmin = sx;
+ rect.xmax = sx + wrap_width;
+ /* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will
+ * actually start drawing at (ymax - line-height). */
+ rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height;
+ rect.ymax = sy;
+
+ struct ResultBLF result;
+ UI_fontstyle_draw_ex(&style->widget,
+ &rect,
+ string,
+ text_col,
+ &(struct uiFontStyleDraw_Params){
+ .align = UI_STYLE_TEXT_LEFT,
+ .word_wrap = true,
+ },
+ len,
+ NULL,
+ NULL,
+ &result);
+ if (r_sx) {
+ *r_sx = result.width;
+ }
+ if (r_sy) {
+ *r_sy = rect.ymin + line_height;
+ }
+}
+
void file_calc_previews(const bContext *C, ARegion *region)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -210,6 +292,7 @@ void file_calc_previews(const bContext *C, ARegion *region)
}
static void file_draw_preview(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -218,7 +301,6 @@ static void file_draw_preview(uiBlock *block,
const int icon,
FileLayout *layout,
const bool is_icon,
- const int typeflags,
const bool drag,
const bool dimmed,
const bool is_link)
@@ -232,7 +314,7 @@ static void file_draw_preview(uiBlock *block,
float scale;
int ex, ey;
bool show_outline = !is_icon &&
- (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ (file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
BLI_assert(imb != NULL);
@@ -273,14 +355,14 @@ static void file_draw_preview(uiBlock *block,
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (is_icon) {
- if (typeflags & FILE_TYPE_DIR) {
+ if (file->typeflag & FILE_TYPE_DIR) {
UI_GetThemeColor4fv(TH_ICON_FOLDER, col);
}
else {
UI_GetThemeColor4fv(TH_TEXT, col);
}
}
- else if (typeflags & FILE_TYPE_FTFONT) {
+ else if (file->typeflag & FILE_TYPE_FTFONT) {
UI_GetThemeColor4fv(TH_TEXT, col);
}
@@ -288,7 +370,7 @@ static void file_draw_preview(uiBlock *block,
col[3] *= 0.3f;
}
- if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) {
+ if (!is_icon && file->typeflag & FILE_TYPE_BLENDERLIB) {
/* Datablock preview images use premultiplied alpha. */
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
@@ -324,7 +406,7 @@ static void file_draw_preview(uiBlock *block,
icon_color[2] = 255;
}
icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
- icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.75f));
+ icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f));
UI_icon_draw_ex(
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
@@ -346,13 +428,13 @@ static void file_draw_preview(uiBlock *block,
/* Link to folder or non-previewed file. */
uchar icon_color[4];
UI_GetThemeColor4ubv(TH_BACK, icon_color);
- icon_x = xco + ((typeflags & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
- icon_y = yco + ((typeflags & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
+ icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
+ icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
UI_icon_draw_ex(
icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false);
}
}
- else if (icon && !is_icon && !(typeflags & FILE_TYPE_FTFONT)) {
+ else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) {
/* Smaller, fainter icon at bottom-left for preview image thumbnail, but not for fonts. */
float icon_x, icon_y;
const uchar dark[4] = {0, 0, 0, 255};
@@ -385,8 +467,22 @@ static void file_draw_preview(uiBlock *block,
/* dragregion */
if (drag) {
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
/* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(
+ but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale);
+ }
+ }
+ else {
+ UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ }
}
GPU_blend(GPU_BLEND_NONE);
@@ -821,6 +917,7 @@ void file_draw_list(const bContext *C, ARegion *region)
}
file_draw_preview(block,
+ file,
path,
sx,
sy,
@@ -829,13 +926,13 @@ void file_draw_list(const bContext *C, ARegion *region)
icon,
layout,
is_icon,
- file->typeflag,
do_drag,
is_hidden,
is_link);
}
else {
file_draw_icon(block,
+ file,
path,
sx,
sy - layout->tile_border_y,
@@ -906,3 +1003,66 @@ void file_draw_list(const bContext *C, ARegion *region)
layout->curr_size = params->thumbnail_size;
}
+
+static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region)
+{
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+
+ char library_ui_path[PATH_MAX];
+ file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
+
+ uchar text_col[4];
+ uchar text_alert_col[4];
+ UI_GetThemeColor4ubv(TH_TEXT, text_col);
+ UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col);
+
+ const View2D *v2d = &region->v2d;
+ const int pad = sfile->layout->tile_border_x;
+ const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad);
+ const int line_height = sfile->layout->textheight;
+ int sx = v2d->tot.xmin + pad;
+ /* For some reason no padding needed. */
+ int sy = v2d->tot.ymax;
+
+ {
+ const char *message = TIP_("Library not found");
+ const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path);
+ char *draw_string = alloca(draw_string_str_len);
+ BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path);
+ file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy);
+ }
+
+ /* Next line, but separate it a bit further. */
+ sy -= line_height;
+
+ {
+ UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
+
+ const char *suggestion = TIP_(
+ "Set up the library or edit libraries in the Preferences, File Paths section.");
+ file_draw_string_multiline(
+ sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL);
+ }
+}
+
+/**
+ * Draw a string hint if the file list is invalid.
+ * \return true if the list is invalid and a hint was drawn.
+ */
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
+{
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ /* Only for asset browser. */
+ if (!ED_fileselect_is_asset_browser(sfile)) {
+ return false;
+ }
+ /* Check if the library exists. */
+ if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
+ return false;
+ }
+
+ file_draw_invalid_library_hint(sfile, region);
+
+ return true;
+}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index a0e02681e0e..56fb588776e 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -38,6 +38,7 @@ struct View2D;
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region);
void file_draw_check_ex(bContext *C, struct ScrArea *area);
void file_draw_check(bContext *C);
@@ -117,3 +118,5 @@ void file_execute_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
+
+void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 8af84f65ced..be4577bcba7 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -40,6 +40,7 @@
# include "BLI_winstuff.h"
#endif
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
@@ -2695,6 +2696,29 @@ static bool file_delete_poll(bContext *C)
return poll;
}
+static bool file_delete_single(const FileSelectParams *params,
+ FileDirEntry *file,
+ const char **r_error_message)
+{
+ if (file->typeflag & FILE_TYPE_ASSET) {
+ ID *id = filelist_file_get_id(file);
+ if (!id) {
+ *r_error_message = "File is not a local data-block asset.";
+ return false;
+ }
+ ED_asset_clear_id(id);
+ }
+ else {
+ char str[FILE_MAX];
+ BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
+ if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int file_delete_exec(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -2708,9 +2732,7 @@ static int file_delete_exec(bContext *C, wmOperator *op)
for (int i = 0; i < numfiles; i++) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
FileDirEntry *file = filelist_file(sfile->files, i);
- char str[FILE_MAX];
- BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
- if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) {
+ if (!file_delete_single(params, file, &error_message)) {
report_error = true;
}
}
diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c
index 452f2f704cf..9d85996c559 100644
--- a/source/blender/editors/space_file/file_utils.c
+++ b/source/blender/editors/space_file/file_utils.c
@@ -18,12 +18,14 @@
* \ingroup spfile
*/
+#include "BLI_fileops.h"
#include "BLI_listbase.h"
+#include "BLI_path_util.h"
#include "BLI_rect.h"
-
-#include "BLO_readfile.h"
+#include "BLI_string.h"
#include "BKE_context.h"
+#include "BLO_readfile.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
@@ -44,3 +46,14 @@ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int fil
ymax - layout->tile_h - layout->tile_border_y,
ymax);
}
+
+/**
+ * If \a path leads to a .blend, remove the trailing slash (if needed).
+ */
+void file_path_to_ui_path(const char *path, char *r_path, int max_size)
+{
+ char tmp_path[PATH_MAX];
+ BLI_strncpy(tmp_path, path, sizeof(tmp_path));
+ BLI_path_slash_rstrip(tmp_path);
+ BLI_strncpy(r_path, BLO_has_bfile_extension(tmp_path) ? tmp_path : path, max_size);
+}
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index f1d72387791..46ae52fd4cf 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -57,6 +57,23 @@
#include "filelist.h"
#include "fsmenu.h"
+static ARegion *file_ui_region_ensure(ScrArea *area, ARegion *region_prev)
+{
+ ARegion *region;
+
+ if ((region = BKE_area_find_region_type(area, RGN_TYPE_UI)) != NULL) {
+ return region;
+ }
+
+ region = MEM_callocN(sizeof(ARegion), "execute region for file");
+ BLI_insertlinkafter(&area->regionbase, region_prev, region);
+ region->regiontype = RGN_TYPE_UI;
+ region->alignment = RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_DYNAMIC_SIZE;
+
+ return region;
+}
+
static ARegion *file_execute_region_ensure(ScrArea *area, ARegion *region_prev)
{
ARegion *region;
@@ -223,15 +240,30 @@ static void file_ensure_valid_region_state(bContext *C,
SpaceFile *sfile,
FileSelectParams *params)
{
- ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
- ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
- ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_tools = BKE_area_find_region_type(area, RGN_TYPE_TOOLS);
bool needs_init = false; /* To avoid multiple ED_area_init() calls. */
+ BLI_assert(region_tools);
+
+ if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) {
+ ARegion *region_execute = file_execute_region_ensure(area, region_tools);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
+
+ /* Hide specific regions by default. */
+ region_props->flag |= RGN_FLAG_HIDDEN;
+ region_execute->flag |= RGN_FLAG_HIDDEN;
+
+ ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
+ if (region_ui) {
+ ED_region_remove(C, area, region_ui);
+ needs_init = true;
+ }
+ }
/* If there's an file-operation, ensure we have the option and execute region */
- if (sfile->op && (region_props == NULL)) {
- region_execute = file_execute_region_ensure(area, region_ui);
- region_props = file_tool_props_region_ensure(area, region_execute);
+ else if (sfile->op) {
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ ARegion *region_execute = file_execute_region_ensure(area, region_ui);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
if (params->flag & FILE_HIDE_TOOL_PROPS) {
region_props->flag |= RGN_FLAG_HIDDEN;
@@ -243,12 +275,19 @@ static void file_ensure_valid_region_state(bContext *C,
needs_init = true;
}
/* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */
- else if ((sfile->op == NULL) && (region_props != NULL)) {
- BLI_assert(region_execute != NULL);
+ else {
+ ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
+ ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ UNUSED_VARS(region_ui);
- ED_region_remove(C, area, region_props);
- ED_region_remove(C, area, region_execute);
- needs_init = true;
+ if (region_props) {
+ BLI_assert(region_execute);
+
+ ED_region_remove(C, area, region_props);
+ ED_region_remove(C, area, region_execute);
+ needs_init = true;
+ }
}
if (needs_init) {
@@ -530,7 +569,9 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
file_highlight_set(sfile, region, event->x, event->y);
}
- file_draw_list(C, region);
+ if (!file_draw_hint_if_invalid(sfile, region)) {
+ file_draw_list(C, region);
+ }
/* reset view matrix */
UI_view2d_view_restore(C);
@@ -714,6 +755,25 @@ static void file_dropboxes(void)
WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy);
}
+static int file_space_subtype_get(ScrArea *area)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ return sfile->browse_mode;
+}
+
+static void file_space_subtype_set(ScrArea *area, int value)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ sfile->browse_mode = value;
+}
+
+static void file_space_subtype_item_extend(bContext *UNUSED(C),
+ EnumPropertyItem **item,
+ int *totitem)
+{
+ RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
+}
+
const char *file_context_dir[] = {"active_file", "active_id", NULL};
static int /*eContextResult*/ file_context(const bContext *C,
@@ -779,6 +839,9 @@ void ED_spacetype_file(void)
st->operatortypes = file_operatortypes;
st->keymap = file_keymap;
st->dropboxes = file_dropboxes;
+ st->space_subtype_item_extend = file_space_subtype_item_extend;
+ st->space_subtype_get = file_space_subtype_get;
+ st->space_subtype_set = file_space_subtype_set;
st->context = file_context;
st->id_remap = file_id_remap;
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index e300fb1b31b..784172b3ac9 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -270,6 +270,8 @@ static void rna_Area_ui_type_update(bContext *C, PointerRNA *ptr)
st->space_subtype_set(area, area->butspacetype_subtype);
}
area->butspacetype_subtype = 0;
+
+ ED_area_tag_refresh(area);
}
static void rna_View2D_region_to_view(struct View2D *v2d, float x, float y, float result[2])
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 4b39858026c..59012ce4528 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -2574,6 +2574,18 @@ static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf(
return item;
}
+static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
+{
+ FileSelectParams *params = ptr->data;
+ params->filter_id = value;
+}
+
+static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr)
+{
+ FileSelectParams *params = ptr->data;
+ return params->filter_id;
+}
+
static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value)
{
const FileDirEntry *entry = ptr->data;
@@ -6207,6 +6219,47 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ /* XXX copied from rna_def_fileselect_idfilter. */
+ 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"},
+ {FILTER_ID_OB | FILTER_ID_GR,
+ "OBJECTS_AND_COLLECTIONS",
+ ICON_GROUP,
+ "Objects & Collections",
+ "Show objects and collections"},
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME
+ /* XXX avoid warning */
+ // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO
+ ,
+ "GEOMETRY",
+ ICON_MESH_DATA,
+ "Geometry",
+ "Show meshes, curves, lattice, armatures and metaballs data"},
+ {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
+ "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,
+ "IMAGES_AND_SOUNDS",
+ 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 | FILTER_ID_WS,
+ "ENVIRONMENTS",
+ 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,
+ "MISC",
+ ICON_GREASEPENCIL,
+ "Miscellaneous",
+ "Show other data types"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "FileAssetSelectParams", "FileSelectParams");
RNA_def_struct_ui_text(
srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode");
@@ -6219,6 +6272,15 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
"rna_FileAssetSelectParams_asset_library_itemf");
RNA_def_property_ui_text(prop, "Asset Library", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
+ prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, asset_category_items);
+ RNA_def_property_enum_funcs(prop,
+ "rna_FileAssetSelectParams_asset_category_get",
+ "rna_FileAssetSelectParams_asset_category_set",
+ NULL);
+ RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
static void rna_def_filemenu_entry(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 2175cddcd7f..7a285df235a 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -5993,8 +5993,9 @@ static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna)
RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, 0, "rna_userdef_update");
- prop = RNA_def_property(srna, "path", PROP_STRING, PROP_FILEPATH);
- RNA_def_property_ui_text(prop, "Path", "Path to a .blend file to use as an asset library");
+ prop = RNA_def_property(srna, "path", PROP_STRING, PROP_DIRPATH);
+ RNA_def_property_ui_text(
+ prop, "Path", "Path to a directory with .blend files to use as an asset library");
RNA_def_property_update(prop, 0, "rna_userdef_update");
}