Asset Browser Space API: add activate_asset_by_id() function

Add an RNA function `activate_asset_by_id(asset_id: ID, deferred: bool)`
to the File Browser space type, which intended to be used to activate an
asset's entry as identified by its `ID *`. Calling it changes the active
asset, but only if the given ID can actually be found.

The activation can be deferred (by passing `deferred=True`) until the
next refresh operation has finished. This is necessary when an asset has
just been added, as it will be loaded by the filebrowser in a background
job.

Reviewed By: Severin

Differential Revision: https://developer.blender.org/D10549
This commit is contained in:
Sybren A. Stüvel
2021-03-05 15:00:56 +01:00
parent 0dd9a4a576
commit fe35551df2
12 changed files with 166 additions and 11 deletions

View File

@@ -1681,6 +1681,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
sfile->op = NULL;
sfile->previews_timer = NULL;
sfile->tags = 0;
sfile->runtime = NULL;
BLO_read_data_address(reader, &sfile->params);
BLO_read_data_address(reader, &sfile->asset_params);
}

View File

@@ -145,6 +145,13 @@ 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);
/* Activate the file that corresponds to the given ID.
* Pass deferred=true to wait for the next refresh before activating. */
void ED_fileselect_activate_by_id(struct SpaceFile *sfile,
struct ID *asset_id,
const bool deferred);
void ED_fileselect_window_params_get(const struct wmWindow *win,
int win_size[2],

View File

@@ -112,6 +112,21 @@ int autocomplete_file(struct bContext *C, char *str, void *arg_v);
void file_params_renamefile_activate(struct SpaceFile *sfile, struct FileSelectParams *params);
typedef void *onReloadFnData;
typedef void (*onReloadFn)(struct SpaceFile *space_data, onReloadFnData custom_data);
typedef struct SpaceFile_Runtime {
/* Called once after the file browser has reloaded. Reset to NULL after calling.
* Use file_on_reload_callback_register() to register a callback. */
onReloadFn on_reload;
onReloadFnData on_reload_custom_data;
} SpaceFile_Runtime;
/* Register an on-reload callback function. Note that there can only be one such function at a
* time; registering a new one will overwrite the previous one. */
void file_on_reload_callback_register(struct SpaceFile *sfile,
onReloadFn callback,
onReloadFnData custom_data);
/* file_panels.c */
void file_tool_props_region_panels_register(struct ARegionType *art);
void file_execute_region_panels_register(struct ARegionType *art);

View File

@@ -1990,9 +1990,7 @@ static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
filelist_entry_free(entry);
}
static FileDirEntry *filelist_file_ex(struct FileList *filelist,
const int index,
const bool use_request)
FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const bool use_request)
{
FileDirEntry *ret = NULL, *old;
FileListEntryCache *cache = &filelist->filelist_cache;
@@ -3464,7 +3462,7 @@ 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, NULL);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL);
return;
}
@@ -3476,7 +3474,10 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
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);
WM_jobs_timer(wm_job,
0.01,
NC_SPACE | ND_SPACE_FILE_LIST,
NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED);
WM_jobs_callbacks(
wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);

View File

@@ -93,6 +93,8 @@ void filelist_setdir(struct FileList *filelist, char *r_dir);
int filelist_files_ensure(struct FileList *filelist);
int filelist_needs_reading(struct FileList *filelist);
FileDirEntry *filelist_file(struct FileList *filelist, int index);
FileDirEntry *filelist_file_ex(struct FileList *filelist, int index, bool use_request);
int filelist_file_findpath(struct FileList *filelist, const char *file);
struct ID *filelist_file_get_id(const struct FileDirEntry *file);
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);

View File

@@ -454,6 +454,66 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
{
if (!ED_fileselect_is_asset_browser(sfile)) {
return NULL;
}
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file == NULL) {
return NULL;
}
return filelist_file_get_id(file);
}
static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data)
{
ID *asset_id = (ID *)custom_data;
ED_fileselect_activate_by_id(sfile, asset_id, false);
}
void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool deferred)
{
if (!ED_fileselect_is_asset_browser(sfile)) {
return;
}
/* If there are filelist operations running now ("pending" true) or soon ("force reset" true),
* there is a fair chance that the to-be-activated ID will only be present after these operations
* have completed. Defer activation until then. */
if (deferred || filelist_pending(sfile->files) || filelist_needs_force_reset(sfile->files)) {
/* This should be thread-safe, as this function is likely called from the main thread, and
* notifiers (which cause a call to the on-reload callback function) are handled on the main
* thread as well. */
file_on_reload_callback_register(sfile, on_reload_activate_by_id, asset_id);
return;
}
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
struct FileList *files = sfile->files;
const int num_files_filtered = filelist_files_ensure(files);
for (int file_index = 0; file_index < num_files_filtered; ++file_index) {
const FileDirEntry *file = filelist_file_ex(files, file_index, false);
if (filelist_file_get_id(file) != asset_id) {
filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
continue;
}
params->active_file = file_index;
filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
/* Keep looping to deselect the other files. */
}
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL);
WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL);
}
/* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA
* may also be remembered, but only conditionally. */
#define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT)

View File

@@ -173,6 +173,7 @@ static void file_free(SpaceLink *sl)
MEM_SAFE_FREE(sfile->params);
MEM_SAFE_FREE(sfile->asset_params);
MEM_SAFE_FREE(sfile->runtime);
if (sfile->layout) {
MEM_freeN(sfile->layout);
@@ -188,6 +189,10 @@ static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area)
if (sfile->layout) {
sfile->layout->dirty = true;
}
if (sfile->runtime == NULL) {
sfile->runtime = MEM_callocN(sizeof(*sfile->runtime), __func__);
}
}
static void file_exit(wmWindowManager *wm, ScrArea *area)
@@ -209,6 +214,7 @@ static SpaceLink *file_duplicate(SpaceLink *sl)
/* clear or remove stuff from old */
sfilen->op = NULL; /* file window doesn't own operators */
sfilen->runtime = NULL;
sfilen->previews_timer = NULL;
sfilen->smoothscroll_timer = NULL;
@@ -392,6 +398,26 @@ static void file_refresh(const bContext *C, ScrArea *area)
ED_area_tag_redraw(area);
}
void file_on_reload_callback_register(SpaceFile *sfile,
onReloadFn callback,
onReloadFnData custom_data)
{
sfile->runtime->on_reload = callback;
sfile->runtime->on_reload_custom_data = custom_data;
}
static void file_on_reload_callback_call(SpaceFile *sfile)
{
if (sfile->runtime->on_reload == NULL) {
return;
}
sfile->runtime->on_reload(sfile, sfile->runtime->on_reload_custom_data);
sfile->runtime->on_reload = NULL;
sfile->runtime->on_reload_custom_data = NULL;
}
static void file_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
@@ -419,12 +445,26 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
}
break;
}
switch (wmn->action) {
case NA_JOB_FINISHED:
file_on_reload_callback_call(sfile);
break;
}
break;
case NC_ASSET: {
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
/* 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. */
file_tag_reset_list(area, sfile);
switch (wmn->action) {
case NA_SELECTED:
case NA_ACTIVATED:
ED_area_tag_refresh(area);
break;
case NA_ADDED:
case NA_REMOVED:
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
/* 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. */
file_tag_reset_list(area, sfile);
}
break;
}
break;
}
@@ -464,8 +504,7 @@ static void file_main_region_listener(const wmRegionListenerParams *params)
}
break;
case NC_ID:
if (ELEM(wmn->action, NA_RENAME)) {
/* In case the filelist shows ID names. */
if (ELEM(wmn->action, NA_SELECTED, NA_ACTIVATED, NA_RENAME)) {
ED_region_tag_redraw(region);
}
break;

View File

@@ -66,6 +66,9 @@ typedef struct SpaceProperties_Runtime SpaceProperties_Runtime;
/* Defined in `node_intern.h`. */
typedef struct SpaceNode_Runtime SpaceNode_Runtime;
/* Defined in `file_intern.h`. */
typedef struct SpaceFile_Runtime SpaceFile_Runtime;
/* -------------------------------------------------------------------- */
/** \name SpaceLink (Base)
* \{ */
@@ -846,6 +849,8 @@ typedef struct SpaceFile {
short recentnr, bookmarknr;
short systemnr, system_bookmarknr;
SpaceFile_Runtime *runtime;
} SpaceFile;
/* SpaceFile.browse_mode (File Space Browsing Mode) */

View File

@@ -424,6 +424,7 @@ void RNA_api_window(struct StructRNA *srna);
void RNA_api_wm(struct StructRNA *srna);
void RNA_api_space_node(struct StructRNA *srna);
void RNA_api_space_text(struct StructRNA *srna);
void RNA_api_space_filebrowser(struct StructRNA *srna);
void RNA_api_region_view3d(struct StructRNA *srna);
void RNA_api_texture(struct StructRNA *srna);
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastrip);

View File

@@ -6569,6 +6569,8 @@ static void rna_def_space_filebrowser(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(
prop, NC_SPACE | ND_SPACE_FILE_PARAMS, "rna_FileBrowser_FSMenu_active_update");
RNA_api_space_filebrowser(srna);
}
static void rna_def_space_info(BlenderRNA *brna)

View File

@@ -27,6 +27,7 @@
# include "BKE_global.h"
# include "ED_fileselect.h"
# include "ED_screen.h"
# include "ED_text.h"
@@ -115,4 +116,24 @@ void RNA_api_space_text(StructRNA *srna)
RNA_def_function_output(func, parm);
}
void RNA_api_space_filebrowser(StructRNA *srna)
{
FunctionRNA *func;
PropertyRNA *parm;
func = RNA_def_function(srna, "activate_asset_by_id", "ED_fileselect_activate_by_id");
RNA_def_function_ui_description(func, "Activate the asset entry that represents the given ID");
parm = RNA_def_property(func, "id_to_activate", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(parm, "ID");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_boolean(
func,
"deferred",
0,
"",
"Whether to activate the ID immediately (false) or after the file browser refreshes (true)");
}
#endif

View File

@@ -461,6 +461,7 @@ typedef struct wmNotifier {
#define NA_SELECTED 6
#define NA_ACTIVATED 7
#define NA_PAINTING 8
#define NA_JOB_FINISHED 9
/* ************** Gesture Manager data ************** */