Nodes: support filepath filter for import nodes
Currently, when selecting a file path using the file browser opened from the node socket, there is no filter active making it harder than necessary to find the correct file. This patch adds the proper filter. Something similar is done when using e.g. the gltf import from the menu. Supporting this requires changes in a bunch of places: * `StringPropertyRNA` now has a callback that returns the file path pattern. This has to be a callback, because the same property is used on all file path sockets, but the valid extension depends on the node. * The string socket declaration also has the optional path pattern. This can be set in the node declarations. Pull Request: https://projects.blender.org/blender/blender/pulls/134931
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
#include "ED_undo.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
@@ -393,6 +394,9 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
const bool is_output_path = (RNA_property_flag(prop) & PROP_PATH_OUTPUT) != 0;
|
||||
RNA_property_boolean_set(op->ptr, prop_check_existing, is_output_path);
|
||||
}
|
||||
if (std::optional<std::string> filter = RNA_property_string_path_filter(C, &ptr, prop)) {
|
||||
RNA_string_set(op->ptr, "filter_glob", filter->c_str());
|
||||
}
|
||||
|
||||
WM_event_add_fileselect(C, op);
|
||||
|
||||
@@ -423,6 +427,11 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot)
|
||||
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
|
||||
FILE_DEFAULTDISPLAY,
|
||||
FILE_SORT_DEFAULT);
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_string(ot->srna, "filter_glob", nullptr, 0, "Glob Filter", "Custom filter");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
void BUTTONS_OT_directory_browse(wmOperatorType *ot)
|
||||
|
||||
@@ -496,6 +496,14 @@ void RNA_property_string_search(
|
||||
const char *edit_text,
|
||||
blender::FunctionRef<void(StringPropertySearchVisitParams)> visit_fn);
|
||||
|
||||
/**
|
||||
* For filepath properties, get a glob pattern to filter possible files.
|
||||
* For example: *.csv
|
||||
*/
|
||||
std::optional<std::string> RNA_property_string_path_filter(const bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop);
|
||||
|
||||
/**
|
||||
* \return the length without `\0` terminator.
|
||||
*/
|
||||
|
||||
@@ -508,6 +508,7 @@ void RNA_def_property_string_funcs(PropertyRNA *prop,
|
||||
void RNA_def_property_string_search_func(PropertyRNA *prop,
|
||||
const char *search,
|
||||
eStringPropertySearchFlag search_flag);
|
||||
void RNA_def_property_string_filepath_filter_func(PropertyRNA *prop, const char *filter);
|
||||
void RNA_def_property_pointer_funcs(
|
||||
PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll);
|
||||
void RNA_def_property_collection_funcs(PropertyRNA *prop,
|
||||
|
||||
@@ -676,6 +676,14 @@ using StringPropertySearchFunc =
|
||||
const char *edit_text,
|
||||
blender::FunctionRef<void(StringPropertySearchVisitParams)> visit_fn);
|
||||
|
||||
/**
|
||||
* Returns an optional glob pattern (e.g. "*.png") that can be passed to the file browser to filter
|
||||
* valid files for this property.
|
||||
*/
|
||||
using StringPropertyPathFilterFunc = std::optional<std::string> (*)(const bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop);
|
||||
|
||||
using EnumPropertyGetFunc = int (*)(PointerRNA *ptr, PropertyRNA *prop);
|
||||
using EnumPropertySetFunc = void (*)(PointerRNA *ptr, PropertyRNA *prop, int value);
|
||||
/* same as PropEnumItemFunc */
|
||||
|
||||
@@ -4504,7 +4504,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
|
||||
case PROP_STRING: {
|
||||
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
||||
fprintf(f,
|
||||
"\t%s, %s, %s, %s, %s, %s, %s, (eStringPropertySearchFlag)%d, %d, ",
|
||||
"\t%s, %s, %s, %s, %s, %s, %s, (eStringPropertySearchFlag)%d, %s, %d, ",
|
||||
rna_function_string(sprop->get),
|
||||
rna_function_string(sprop->length),
|
||||
rna_function_string(sprop->set),
|
||||
@@ -4513,6 +4513,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
|
||||
rna_function_string(sprop->set_ex),
|
||||
rna_function_string(sprop->search),
|
||||
int(sprop->search_flag),
|
||||
rna_function_string(sprop->path_filter),
|
||||
sprop->maxlength);
|
||||
rna_print_c_string(f, sprop->defaultvalue);
|
||||
fprintf(f, "\n");
|
||||
|
||||
@@ -3917,6 +3917,18 @@ void RNA_property_string_search(
|
||||
sprop->search(C, ptr, prop, edit_text, visit_fn);
|
||||
}
|
||||
|
||||
std::optional<std::string> RNA_property_string_path_filter(const bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop)
|
||||
{
|
||||
BLI_assert(prop->type == PROP_STRING);
|
||||
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
||||
if (!sprop->path_filter) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return sprop->path_filter(C, ptr, prop);
|
||||
}
|
||||
|
||||
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
||||
|
||||
@@ -3536,6 +3536,28 @@ void RNA_def_property_string_search_func(PropertyRNA *prop,
|
||||
}
|
||||
}
|
||||
|
||||
void RNA_def_property_string_filepath_filter_func(PropertyRNA *prop, const char *filter)
|
||||
{
|
||||
StructRNA *srna = DefRNA.laststruct;
|
||||
|
||||
if (!DefRNA.preprocess) {
|
||||
CLOG_ERROR(&LOG, "only during preprocessing.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (prop->type) {
|
||||
case PROP_STRING: {
|
||||
StringPropertyRNA *sprop = (StringPropertyRNA *)prop;
|
||||
sprop->path_filter = (StringPropertyPathFilterFunc)filter;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CLOG_ERROR(&LOG, "\"%s.%s\", type is not string.", srna->identifier, prop->identifier);
|
||||
DefRNA.error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
|
||||
StringPropertyGetFunc getfunc,
|
||||
StringPropertyLengthFunc lengthfunc,
|
||||
|
||||
@@ -492,6 +492,12 @@ struct StringPropertyRNA {
|
||||
StringPropertySearchFunc search;
|
||||
eStringPropertySearchFlag search_flag;
|
||||
|
||||
/**
|
||||
* Used for strings which are #PROP_FILEPATH to have a default filter when opening a file
|
||||
* browser.
|
||||
*/
|
||||
StringPropertyPathFilterFunc path_filter;
|
||||
|
||||
int maxlength; /* includes string terminator! */
|
||||
|
||||
const char *defaultvalue;
|
||||
|
||||
@@ -603,6 +603,20 @@ const EnumPropertyItem *RNA_node_socket_menu_itemf(bContext * /*C*/,
|
||||
return RNA_node_enum_definition_itemf(*data->enum_items, r_free);
|
||||
}
|
||||
|
||||
std::optional<std::string> rna_NodeSocketString_filepath_filter(const bContext * /*C*/,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/)
|
||||
{
|
||||
bNodeSocket *socket = static_cast<bNodeSocket *>(ptr->data);
|
||||
BLI_assert(socket->type == SOCK_STRING);
|
||||
if (const auto *decl = dynamic_cast<const blender::nodes::decl::String *>(
|
||||
socket->runtime->declaration))
|
||||
{
|
||||
return decl->path_filter;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_node_socket(BlenderRNA *brna)
|
||||
@@ -1325,6 +1339,10 @@ static void rna_def_node_socket_string(BlenderRNA *brna,
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
|
||||
if (subtype == PROP_FILEPATH) {
|
||||
RNA_def_property_string_filepath_filter_func(prop, "rna_NodeSocketString_filepath_filter");
|
||||
}
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ class String : public SocketDeclaration {
|
||||
|
||||
std::string default_value;
|
||||
PropertySubType subtype = PROP_NONE;
|
||||
std::optional<std::string> path_filter;
|
||||
|
||||
friend StringBuilder;
|
||||
|
||||
@@ -211,6 +212,7 @@ class StringBuilder : public SocketDeclarationBuilder<String> {
|
||||
public:
|
||||
StringBuilder &default_value(const std::string value);
|
||||
StringBuilder &subtype(PropertySubType subtype);
|
||||
StringBuilder &path_filter(std::optional<std::string> filter);
|
||||
};
|
||||
|
||||
class MenuBuilder;
|
||||
|
||||
@@ -17,6 +17,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::String>("Path")
|
||||
.subtype(PROP_FILEPATH)
|
||||
.path_filter("*.csv")
|
||||
.hide_label()
|
||||
.description("Path to a CSV file");
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::String>("Path")
|
||||
.subtype(PROP_FILEPATH)
|
||||
.path_filter("*.obj")
|
||||
.hide_label()
|
||||
.description("Path to a OBJ file");
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::String>("Path")
|
||||
.subtype(PROP_FILEPATH)
|
||||
.path_filter("*.ply")
|
||||
.hide_label()
|
||||
.description("Path to a PLY file");
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::String>("Path")
|
||||
.subtype(PROP_FILEPATH)
|
||||
.path_filter("*.stl")
|
||||
.hide_label()
|
||||
.description("Path to a STL file");
|
||||
|
||||
|
||||
@@ -537,6 +537,13 @@ bNodeSocket &String::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket
|
||||
return socket;
|
||||
}
|
||||
|
||||
StringBuilder &StringBuilder::path_filter(std::optional<std::string> filter)
|
||||
{
|
||||
BLI_assert(decl_->subtype == PROP_FILEPATH);
|
||||
decl_->path_filter = std::move(filter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user