Improve and fix the GraphNode port hotzones

Co-authored-by: Ansraer <jacky2611@gmail.com>
This commit is contained in:
Hendrik Brucker 2022-05-30 15:48:58 +02:00
parent 1f690f197a
commit 771cb1261a
7 changed files with 106 additions and 48 deletions

View File

@ -376,11 +376,11 @@
</theme_item>
<theme_item name="bezier_len_pos" data_type="constant" type="int" default="80">
</theme_item>
<theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="24">
The horizontal range within which a port can be grabbed (on both sides).
<theme_item name="port_hotzone_inner_extent" data_type="constant" type="int" default="22">
The horizontal range within which a port can be grabbed (inner side).
</theme_item>
<theme_item name="port_grab_distance_vertical" data_type="constant" type="int" default="26">
The vertical range within which a port can be grabbed (on both sides).
<theme_item name="port_hotzone_outer_extent" data_type="constant" type="int" default="26">
The horizontal range within which a port can be grabbed (outer side).
</theme_item>
<theme_item name="layout" data_type="icon" type="Texture2D">
</theme_item>

View File

@ -43,6 +43,13 @@
Returns the number of enabled input slots (connections) to the GraphNode.
</description>
</method>
<method name="get_connection_input_height">
<return type="int" />
<argument index="0" name="idx" type="int" />
<description>
Returns the height of the input connection [code]idx[/code].
</description>
</method>
<method name="get_connection_input_position">
<return type="Vector2" />
<argument index="0" name="idx" type="int" />
@ -70,6 +77,13 @@
Returns the number of enabled output slots (connections) of the GraphNode.
</description>
</method>
<method name="get_connection_output_height">
<return type="int" />
<argument index="0" name="idx" type="int" />
<description>
Returns the height of the output connection [code]idx[/code].
</description>
</method>
<method name="get_connection_output_position">
<return type="Vector2" />
<argument index="0" name="idx" type="int" />

View File

@ -426,8 +426,8 @@ void GraphEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal"));
port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical"));
port_hotzone_inner_extent = get_theme_constant("port_hotzone_inner_extent");
port_hotzone_outer_extent = get_theme_constant("port_hotzone_outer_extent");
zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
@ -544,8 +544,7 @@ void GraphEdit::_set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashM
}
bool GraphEdit::_filter_input(const Point2 &p_point) {
Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@ -553,14 +552,18 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
continue;
}
for (int j = 0; j < gn->get_connection_output_count(); j++) {
if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
@ -572,8 +575,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
connecting_valid = false;
click_pos = mb->get_position() / zoom;
@ -585,6 +587,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
if (is_in_output_hotzone(gn, j, click_pos, port_size)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
@ -629,6 +634,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
if (is_in_input_hotzone(gn, j, click_pos, port_size)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
@ -682,11 +691,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0;
if (connecting_valid) {
Ref<Texture2D> port = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
Vector2 mpos = mm->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
continue;
@ -695,6 +702,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (!connecting_out) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
int type = gn->get_connection_output_type(j);
if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
@ -707,6 +717,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
} else {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
int type = gn->get_connection_input_type(j);
if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
@ -754,19 +767,24 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) {
if (p_control->is_set_as_top_level() || !p_control->is_visible()) {
bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos, const Vector2 &p_offset) {
if (p_control->is_set_as_top_level() || !p_control->is_visible() || !p_control->is_inside_tree()) {
return false;
}
if (!p_control->has_point(pos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) {
//test children
Rect2 control_rect = p_control->get_rect();
control_rect.size *= zoom;
control_rect.position *= zoom;
control_rect.position += p_offset;
if (!control_rect.has_point(mpos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) {
// Test children.
for (int i = 0; i < p_control->get_child_count(); i++) {
Control *subchild = Object::cast_to<Control>(p_control->get_child(i));
if (!subchild) {
Control *child_rect = Object::cast_to<Control>(p_control->get_child(i));
if (!child_rect) {
continue;
}
if (_check_clickable_control(subchild, pos - subchild->get_position())) {
if (_check_clickable_control(child_rect, mpos, control_rect.position)) {
return true;
}
}
@ -798,7 +816,13 @@ bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index,
}
bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) {
Rect2 hotzone = Rect2(
pos.x - (p_left ? port_hotzone_outer_extent : port_hotzone_inner_extent),
pos.y - p_port_size.height / 2.0,
port_hotzone_inner_extent + port_hotzone_outer_extent,
p_port_size.height);
if (!hotzone.has_point(p_mouse_pos)) {
return false;
}
@ -807,23 +831,17 @@ bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_po
if (!child) {
continue;
}
Rect2 rect = child->get_rect();
// To prevent intersections with other nodes.
rect.position *= zoom;
rect.size *= zoom;
if (rect.has_point(p_mouse_pos)) {
//check sub-controls
Vector2 subpos = p_mouse_pos - rect.position;
Rect2 child_rect = child->get_rect();
child_rect.size *= zoom;
if (child_rect.has_point(p_mouse_pos * zoom)) {
for (int j = 0; j < child->get_child_count(); j++) {
Control *subchild = Object::cast_to<Control>(child->get_child(j));
if (!subchild) {
continue;
}
if (_check_clickable_control(subchild, subpos - subchild->get_position())) {
if (_check_clickable_control(subchild, p_mouse_pos * zoom, child_rect.position)) {
return false;
}
}

View File

@ -124,8 +124,8 @@ private:
HScrollBar *h_scroll = nullptr;
VScrollBar *v_scroll = nullptr;
float port_grab_distance_horizontal = 0.0;
float port_grab_distance_vertical = 0.0;
float port_hotzone_inner_extent = 0.0;
float port_hotzone_outer_extent = 0.0;
Ref<ViewPanner> panner;
bool warped_panning = true;
@ -250,7 +250,7 @@ private:
friend class GraphEditMinimap;
void _minimap_toggled();
bool _check_clickable_control(Control *p_control, const Vector2 &pos);
bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset);
bool arranging_graph = false;

View File

@ -832,6 +832,7 @@ void GraphNode::_connpos_update() {
cc.pos = Point2i(edgeofs, y + h / 2);
cc.type = slot_info[idx].type_left;
cc.color = slot_info[idx].color_left;
cc.height = size.height;
conn_input_cache.push_back(cc);
}
if (slot_info[idx].enable_right) {
@ -839,6 +840,7 @@ void GraphNode::_connpos_update() {
cc.pos = Point2i(get_size().width - edgeofs, y + h / 2);
cc.type = slot_info[idx].type_right;
cc.color = slot_info[idx].color_right;
cc.height = size.height;
conn_output_cache.push_back(cc);
}
}
@ -859,12 +861,13 @@ int GraphNode::get_connection_input_count() {
return conn_input_cache.size();
}
int GraphNode::get_connection_output_count() {
int GraphNode::get_connection_input_height(int p_idx) {
if (connpos_dirty) {
_connpos_update();
}
return conn_output_cache.size();
ERR_FAIL_INDEX_V(p_idx, conn_input_cache.size(), 0);
return conn_input_cache[p_idx].height;
}
Vector2 GraphNode::get_connection_input_position(int p_idx) {
@ -897,6 +900,23 @@ Color GraphNode::get_connection_input_color(int p_idx) {
return conn_input_cache[p_idx].color;
}
int GraphNode::get_connection_output_count() {
if (connpos_dirty) {
_connpos_update();
}
return conn_output_cache.size();
}
int GraphNode::get_connection_output_height(int p_idx) {
if (connpos_dirty) {
_connpos_update();
}
ERR_FAIL_INDEX_V(p_idx, conn_output_cache.size(), 0);
return conn_output_cache[p_idx].height;
}
Vector2 GraphNode::get_connection_output_position(int p_idx) {
if (connpos_dirty) {
_connpos_update();
@ -1066,16 +1086,18 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selected", "selected"), &GraphNode::set_selected);
ClassDB::bind_method(D_METHOD("is_selected"), &GraphNode::is_selected);
ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count);
ClassDB::bind_method(D_METHOD("get_connection_input_count"), &GraphNode::get_connection_input_count);
ClassDB::bind_method(D_METHOD("get_connection_output_position", "idx"), &GraphNode::get_connection_output_position);
ClassDB::bind_method(D_METHOD("get_connection_output_type", "idx"), &GraphNode::get_connection_output_type);
ClassDB::bind_method(D_METHOD("get_connection_output_color", "idx"), &GraphNode::get_connection_output_color);
ClassDB::bind_method(D_METHOD("get_connection_input_height", "idx"), &GraphNode::get_connection_input_height);
ClassDB::bind_method(D_METHOD("get_connection_input_position", "idx"), &GraphNode::get_connection_input_position);
ClassDB::bind_method(D_METHOD("get_connection_input_type", "idx"), &GraphNode::get_connection_input_type);
ClassDB::bind_method(D_METHOD("get_connection_input_color", "idx"), &GraphNode::get_connection_input_color);
ClassDB::bind_method(D_METHOD("get_connection_output_count"), &GraphNode::get_connection_output_count);
ClassDB::bind_method(D_METHOD("get_connection_output_height", "idx"), &GraphNode::get_connection_output_height);
ClassDB::bind_method(D_METHOD("get_connection_output_position", "idx"), &GraphNode::get_connection_output_position);
ClassDB::bind_method(D_METHOD("get_connection_output_type", "idx"), &GraphNode::get_connection_output_type);
ClassDB::bind_method(D_METHOD("get_connection_output_color", "idx"), &GraphNode::get_connection_output_color);
ClassDB::bind_method(D_METHOD("set_show_close_button", "show"), &GraphNode::set_show_close_button);
ClassDB::bind_method(D_METHOD("is_close_button_visible"), &GraphNode::is_close_button_visible);

View File

@ -81,6 +81,7 @@ private:
Vector2 pos;
int type = 0;
Color color;
int height;
};
Vector<ConnCache> conn_input_cache;
@ -167,10 +168,13 @@ public:
bool is_close_button_visible() const;
int get_connection_input_count();
int get_connection_output_count();
int get_connection_input_height(int p_idx);
Vector2 get_connection_input_position(int p_idx);
int get_connection_input_type(int p_idx);
Color get_connection_input_color(int p_idx);
int get_connection_output_count();
int get_connection_output_height(int p_idx);
Vector2 get_connection_output_position(int p_idx);
int get_connection_output_type(int p_idx);
Color get_connection_output_color(int p_idx);

View File

@ -1009,8 +1009,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Visual Node Ports
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale);
theme->set_constant("port_hotzone_inner_extent", "GraphEdit", 22 * scale);
theme->set_constant("port_hotzone_outer_extent", "GraphEdit", 26 * scale);
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0);