Node Editor: Link Drag Search Menu
This commit adds a search menu when links are dragged above empty space. When releasing the drag, a menu displays all compatible sockets with the source link. The "main" sockets (usually the first) are weighted above other sockets in the search, so they appear first when you type the name of the node. A few special operators for creating a reroute or a group input node are also added to the search. Translation is started after choosing a node so it can be placed quickly, since users would likely adjust the position after anyway. A small "+" is displayed next to the cursor to give a hint about this. Further improvements are possible after this first iteration: - Support custom node trees. - Better drawing of items in the search menu. - Potential tweaks to filtering of items, depending on user feedback. Thanks to Juanfran Matheu for developing an initial patch. Differential Revision: https://developer.blender.org/D8286
This commit is contained in:
@@ -23,6 +23,52 @@
|
||||
|
||||
namespace blender::nodes::decl {
|
||||
|
||||
/**
|
||||
* \note This function only deals with declarations, not the field status of existing nodes. If the
|
||||
* field status of existing nodes was stored on the sockets, an improvement would be to check the
|
||||
* existing socket's current status instead of the declaration.
|
||||
*/
|
||||
static bool field_types_are_compatible(const SocketDeclaration &input,
|
||||
const SocketDeclaration &output)
|
||||
{
|
||||
if (output.output_field_dependency().field_type() == OutputSocketFieldType::FieldSource) {
|
||||
if (input.input_field_type() == InputSocketFieldType::None) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sockets_can_connect(const SocketDeclaration &socket_decl,
|
||||
const bNodeSocket &other_socket)
|
||||
{
|
||||
/* Input sockets cannot connect to input sockets, outputs cannot connect to outputs. */
|
||||
if (socket_decl.in_out() == other_socket.in_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other_socket.declaration) {
|
||||
if (socket_decl.in_out() == SOCK_IN) {
|
||||
if (!field_types_are_compatible(socket_decl, *other_socket.declaration)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!field_types_are_compatible(*other_socket.declaration, socket_decl)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool basic_types_can_connect(const SocketDeclaration &UNUSED(socket_decl),
|
||||
const bNodeSocket &other_socket)
|
||||
{
|
||||
return ELEM(other_socket.type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
|
||||
}
|
||||
|
||||
static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subtype)
|
||||
{
|
||||
const char *idname = nodeStaticSocketType(socket.type, new_subtype);
|
||||
@@ -35,10 +81,10 @@ static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subty
|
||||
/** \name #Float
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Float::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value;
|
||||
value.min = soft_min_value_;
|
||||
@@ -68,10 +114,19 @@ bool Float::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Float::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
return basic_types_can_connect(*this, socket);
|
||||
}
|
||||
|
||||
bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
|
||||
{
|
||||
if (socket.type != SOCK_FLOAT) {
|
||||
return this->build(ntree, node, (eNodeSocketInOut)socket.in_out);
|
||||
BLI_assert(socket.in_out == in_out_);
|
||||
return this->build(ntree, node);
|
||||
}
|
||||
if (socket.typeinfo->subtype != subtype_) {
|
||||
modify_subtype_except_for_storage(socket, subtype_);
|
||||
@@ -90,10 +145,10 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &
|
||||
/** \name #Int
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Int::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value;
|
||||
value.min = soft_min_value_;
|
||||
@@ -123,10 +178,19 @@ bool Int::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Int::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
return basic_types_can_connect(*this, socket);
|
||||
}
|
||||
|
||||
bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
|
||||
{
|
||||
if (socket.type != SOCK_INT) {
|
||||
return this->build(ntree, node, (eNodeSocketInOut)socket.in_out);
|
||||
BLI_assert(socket.in_out == in_out_);
|
||||
return this->build(ntree, node);
|
||||
}
|
||||
if (socket.typeinfo->subtype != subtype_) {
|
||||
modify_subtype_except_for_storage(socket, subtype_);
|
||||
@@ -145,10 +209,10 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so
|
||||
/** \name #Vector
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value;
|
||||
copy_v3_v3(value.value, default_value_);
|
||||
@@ -171,10 +235,19 @@ bool Vector::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Vector::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
return basic_types_can_connect(*this, socket);
|
||||
}
|
||||
|
||||
bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
|
||||
{
|
||||
if (socket.type != SOCK_VECTOR) {
|
||||
return this->build(ntree, node, (eNodeSocketInOut)socket.in_out);
|
||||
BLI_assert(socket.in_out == in_out_);
|
||||
return this->build(ntree, node);
|
||||
}
|
||||
if (socket.typeinfo->subtype != subtype_) {
|
||||
modify_subtype_except_for_storage(socket, subtype_);
|
||||
@@ -192,10 +265,10 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket
|
||||
/** \name #Bool
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
bNodeSocketValueBoolean &value = *(bNodeSocketValueBoolean *)socket.default_value;
|
||||
value.value = default_value_;
|
||||
@@ -213,16 +286,24 @@ bool Bool::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bool::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
return basic_types_can_connect(*this, socket);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #Color
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Color::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
bNodeSocketValueRGBA &value = *(bNodeSocketValueRGBA *)socket.default_value;
|
||||
copy_v4_v4(value.value, default_value_);
|
||||
@@ -245,16 +326,24 @@ bool Color::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Color::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
return basic_types_can_connect(*this, socket);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #String
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &String::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddStaticSocket(
|
||||
&ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str());
|
||||
STRNCPY(((bNodeSocketValueString *)socket.default_value)->value, default_value_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
return socket;
|
||||
@@ -271,18 +360,21 @@ bool String::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
return sockets_can_connect(*this, socket) && socket.type == SOCK_STRING;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #IDSocketDeclaration
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree,
|
||||
bNode &node,
|
||||
eNodeSocketInOut in_out) const
|
||||
bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddSocket(
|
||||
&ntree, &node, in_out, idname_, identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, idname_, identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
return socket;
|
||||
}
|
||||
@@ -298,12 +390,18 @@ bool IDSocketDeclaration::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IDSocketDeclaration::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
return sockets_can_connect(*this, socket) && STREQ(socket.idname, idname_);
|
||||
}
|
||||
|
||||
bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree,
|
||||
bNode &node,
|
||||
bNodeSocket &socket) const
|
||||
{
|
||||
if (StringRef(socket.idname) != idname_) {
|
||||
return this->build(ntree, node, (eNodeSocketInOut)socket.in_out);
|
||||
BLI_assert(socket.in_out == in_out_);
|
||||
return this->build(ntree, node);
|
||||
}
|
||||
this->set_common_flags(socket);
|
||||
return socket;
|
||||
@@ -315,10 +413,10 @@ bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree,
|
||||
/** \name #Geometry
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddSocket(
|
||||
&ntree, &node, in_out, "NodeSocketGeometry", identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, "NodeSocketGeometry", identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
return socket;
|
||||
}
|
||||
@@ -334,6 +432,11 @@ bool Geometry::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Geometry::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
return sockets_can_connect(*this, socket) && socket.type == SOCK_GEOMETRY;
|
||||
}
|
||||
|
||||
Span<GeometryComponentType> Geometry::supported_types() const
|
||||
{
|
||||
return supported_types_;
|
||||
@@ -380,10 +483,10 @@ GeometryBuilder &GeometryBuilder::only_instances(bool value)
|
||||
/** \name #Shader
|
||||
* \{ */
|
||||
|
||||
bNodeSocket &Shader::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const
|
||||
bNodeSocket &Shader::build(bNodeTree &ntree, bNode &node) const
|
||||
{
|
||||
bNodeSocket &socket = *nodeAddSocket(
|
||||
&ntree, &node, in_out, "NodeSocketShader", identifier_.c_str(), name_.c_str());
|
||||
&ntree, &node, in_out_, "NodeSocketShader", identifier_.c_str(), name_.c_str());
|
||||
this->set_common_flags(socket);
|
||||
return socket;
|
||||
}
|
||||
@@ -399,6 +502,18 @@ bool Shader::matches(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Shader::can_connect(const bNodeSocket &socket) const
|
||||
{
|
||||
if (!sockets_can_connect(*this, socket)) {
|
||||
return false;
|
||||
}
|
||||
/* Basic types can convert to shaders, but not the other way around. */
|
||||
if (in_out_ == SOCK_IN) {
|
||||
return ELEM(socket.type, SOCK_VECTOR, SOCK_RGBA, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN);
|
||||
}
|
||||
return socket.type == SOCK_SHADER;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::nodes::decl
|
||||
|
||||
Reference in New Issue
Block a user