diff options
author | Julian Eisel <julian@blender.org> | 2021-07-13 19:07:57 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2021-07-15 17:12:36 +0300 |
commit | 8925d3b7bf989e1db364d989fbd0c6cdb9a2acdb (patch) | |
tree | aaf7c43ee885ce4be384002cc59dcfadd8689541 /source/blender/editors | |
parent | 6bd42baaebac3255082eb46884bdd3e27361c135 (diff) |
UI/Assets: Initial Asset View UI template
The asset view UI template is a mini-version of the Asset Browser that
can be placed in regular layouts, regions or popups. At this point it's
made specifically for placement in vertical layouts, it can be made more
flexible in the future.
Generally the way this is implemented will likely change a lot still as
the asset system evolves.
The Pose Library add-on will use the asset view to display pose
libraries in the 3D View sidebar.
References:
* https://developer.blender.org/T86139
* https://code.blender.org/2021/06/asset-browser-project-update/#what-are-we-building
* https://code.blender.org/2021/05/pose-library-v2-0/#use-from-3d-viewport
Notes:
* Important limitation: Due to the early & WIP implementation of the
asset list, all asset views showing the same library will show the
same assets. That is despite the ID type filter option the template
provides. The first asset view created will determine what's visible.
Of course this should be made to work eventually.
* The template supports passing an activate and a drag operator name.
The former is called when an asset is clicked on (e.g. to apply the
asset) the latter when dragging (e.g. to .blend a pose asset). If no
drag operator is set, regular asset drag & drop will be executed.
* The template returns the properties for both operators (see example
below).
* The argument list for using the template is quite long, but we can't
avoid that currently. The UI list design requires that we pass a
number of RNA or custom properties to work with, that for the Pose
Libraries should be registered at the Pose Library add-on level, not
in core Blender.
* Idea is that Python scripts or add-ons that want to use the asset view
can register custom properties, to hold data like the list of assets,
and the active asset index. Maybe that will change in future and we
can manage these internally.
As an example, the pose library add-on uses it like this:
```
activate_op_props, drag_op_props = layout.template_asset_view(
"pose_assets",
workspace,
"active_asset_library",
wm,
"pose_assets",
workspace,
"active_pose_asset_index",
filter_id_types={"filter_action"},
activate_operator="poselib.apply_pose_asset",
drag_operator="poselib.blend_pose_asset",
)
drag_op_props.release_confirm = True
drag_op_props.flipped = wm.poselib_flipped
activate_op_props.flipped = wm.poselib_flipped
```
Diffstat (limited to 'source/blender/editors')
6 files changed, 302 insertions, 0 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 020ca1f8f55..a25aac5803c 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -35,6 +35,7 @@ extern "C" { /* Struct Declarations */ struct ARegion; +struct AssetFilterSettings; struct AutoComplete; struct EnumPropertyItem; struct FileSelectParams; @@ -2287,6 +2288,20 @@ 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, + struct PointerRNA *r_activate_op_properties, + const char *drag_opname, + struct PointerRNA *r_drag_op_properties); struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list, const char *opname, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 29f868eebb0..39dd6143eb9 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_list.cc interface_template_search_menu.c interface_template_search_operator.c diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index f3085c6b79b..a07f924e65b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1241,6 +1241,9 @@ void UI_OT_eyedropper_driver(struct wmOperatorType *ot); /* interface_eyedropper_gpencil_color.c */ void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot); +/* interface_template_asset_view.cc */ +struct uiListType *UI_UL_asset_view(void); + /** * For use with #ui_rna_collection_search_update_fn. */ 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..2860abb32a1 --- /dev/null +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -0,0 +1,272 @@ +/* + * 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 "WM_types.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) +{ + ID *id = asset_handle->file_data->id; + if (id != NULL) { + UI_but_drag_set_id(but, id); + return; + } + + 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, + FILE_ASSET_IMPORT_APPEND, + 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? */ + const float size_x = (96.0f / 20.0f) * UI_UNIT_X; + const 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->dyn_data->custom_drag_optype) { + 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; + const wmNotifier *notifier = params->notifier; + + switch (notifier->category) { + case NC_ID: { + if (ELEM(notifier->action, NA_RENAME)) { + ED_assetlist_storage_tag_main_data_dirty(); + } + break; + } + } + + if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) { + ED_region_tag_redraw(params->region); + } +} + +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; +} + +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, + PointerRNA *r_activate_op_properties, + const char *drag_opname, + PointerRNA *r_drag_op_properties) +{ + 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); + AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value( + RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); + + uiLayout *row = uiLayoutRow(col, true); + uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); + if (asset_library.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + } + + ED_assetlist_storage_fetch(&asset_library, filter_settings, C); + ED_assetlist_ensure_previews_job(&asset_library, C); + const int tot_items = ED_assetlist_size(&asset_library); + + 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, + tot_items, + 0, + UILST_LAYOUT_BIG_PREVIEW_GRID, + 0, + UI_TEMPLATE_LIST_NO_GRIP, + list_data); + if (!list) { + /* List creation failed. */ + MEM_freeN(list_data); + return; + } + + if (activate_opname) { + PointerRNA *ptr = UI_list_custom_activate_operator_set( + list, activate_opname, r_activate_op_properties != NULL); + if (r_activate_op_properties && ptr) { + *r_activate_op_properties = *ptr; + } + } + if (drag_opname) { + PointerRNA *ptr = UI_list_custom_drag_operator_set( + list, drag_opname, r_drag_op_properties != NULL); + if (r_drag_op_properties && ptr) { + *r_drag_op_properties = *ptr; + } + } +} diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 90980b3c963..334fc7ff057 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -20,8 +20,13 @@ * TODO: The UI list template implementation should be moved here. */ +#include "WM_api.h" + #include "UI_interface.h" +#include "interface_intern.h" + void ED_uilisttypes_ui(void) { + WM_uilisttype_add(UI_UL_asset_view()); } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index cb81857eb83..c351ade9954 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, ®ion->ui_lists) { + if (list->type && list->type->listener) { + list->type->listener(list, params); + } + } } /* only exported for WM */ |