Merge pull request #43416 from pycbouh/graph-minimap

Add a minimap to the GraphEdit
This commit is contained in:
Rémi Verschelde 2020-12-17 17:34:36 +01:00 committed by GitHub
commit c83d1b2526
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 509 additions and 9 deletions

View File

@ -175,6 +175,15 @@
</methods> </methods>
<members> <members>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" /> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" />
<member name="minimap_enabled" type="bool" setter="set_minimap_enabled" getter="is_minimap_enabled" default="true">
If [code]true[/code], the minimap is visible.
</member>
<member name="minimap_opacity" type="float" setter="set_minimap_opacity" getter="get_minimap_opacity" default="0.45">
The opacity of the minimap rectangle.
</member>
<member name="minimap_size" type="Vector2" setter="set_minimap_size" getter="get_minimap_size" default="Vector2(240, 160)">
The size of the minimap rectangle. The map itself is based on the size of the grid area and is scaled to fit this rectangle.
</member>
<member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" override="true" default="true" /> <member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" override="true" default="true" />
<member name="right_disconnects" type="bool" setter="set_right_disconnects" getter="is_right_disconnects_enabled" default="false"> <member name="right_disconnects" type="bool" setter="set_right_disconnects" getter="is_right_disconnects_enabled" default="false">
If [code]true[/code], enables disconnection of existing connections in the GraphEdit by dragging the right end. If [code]true[/code], enables disconnection of existing connections in the GraphEdit by dragging the right end.

View File

@ -85,6 +85,25 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1,
return style; return style;
} }
static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
if (!p_flip_y && !p_flip_x) {
return p_texture;
}
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = p_texture->get_data();
if (p_flip_y) {
img->flip_y();
}
if (p_flip_x) {
img->flip_x();
}
texture->create_from_image(img);
return texture;
}
#ifdef MODULE_SVG_ENABLED #ifdef MODULE_SVG_ENABLED
static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) { static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) {
Ref<ImageTexture> icon = memnew(ImageTexture); Ref<ImageTexture> icon = memnew(ImageTexture);
@ -1074,11 +1093,33 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons")); theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons"));
theme->set_icon("reset", "GraphEdit", theme->get_icon("ZoomReset", "EditorIcons")); theme->set_icon("reset", "GraphEdit", theme->get_icon("ZoomReset", "EditorIcons"));
theme->set_icon("snap", "GraphEdit", theme->get_icon("SnapGrid", "EditorIcons")); theme->set_icon("snap", "GraphEdit", theme->get_icon("SnapGrid", "EditorIcons"));
theme->set_icon("minimap", "GraphEdit", theme->get_icon("GridMinimap", "EditorIcons"));
theme->set_constant("bezier_len_pos", "GraphEdit", 80 * EDSCALE); theme->set_constant("bezier_len_pos", "GraphEdit", 80 * EDSCALE);
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * EDSCALE); theme->set_constant("bezier_len_neg", "GraphEdit", 160 * EDSCALE);
// GraphNode // GraphEditMinimap
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(dark_color_1, 0, 0, 0, 0));
Ref<StyleBoxFlat> style_minimap_camera;
Ref<StyleBoxFlat> style_minimap_node;
if (dark_theme) {
style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);
style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
} else {
style_minimap_camera = make_flat_stylebox(Color(0.38, 0.38, 0.38, 0.2), 0, 0, 0, 0);
style_minimap_camera->set_border_color(Color(0.38, 0.38, 0.38, 0.45));
style_minimap_node = make_flat_stylebox(Color(0, 0, 0), 0, 0, 0, 0);
}
style_minimap_camera->set_border_width_all(1);
style_minimap_node->set_corner_radius_all(1);
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
Ref<Texture2D> resizer_icon = theme->get_icon("GuiResizer", "EditorIcons");
theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.65));
// GraphNode
const float mv = dark_theme ? 0.0 : 1.0; const float mv = dark_theme ? 0.0 : 1.0;
const float mv2 = 1.0 - mv; const float mv2 = 1.0 - mv;
const int gn_margin_side = 28; const int gn_margin_side = 28;

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 2.1992188v2.6152343l-2.625 1.3125v-2.6152343zm-12 4.0644531 2.625 1.3125v2.5507811l-2.625-1.3124999zm12 0v2.5507812l-2.625 1.3124999v-2.5507811zm-8 1.4550781h4v2.640625h-4zm-4 2.560547 2.625 1.3125v2.521484l-2.625-1.3125zm12 0v2.521484l-2.625 1.3125v-2.521484zm-8 1.455078h4v2.640625h-4zm1.7014535-8.109375h2.2985465v2.734375h-4.15625s-.7487346.647119-.8746377.640625c-.1310411-.0067594-1.5097373-1.4558594-1.5097373-1.4558594l-1.459375-.7296875v-2.6152343l.068419.034223s.026411-.4573464.062111-.6760553c.0346282-.2121439.1970747-.59225724.1970747-.59225724l-1.0483078-.52372301c-.0795772-.04012218-.1668141-.06276382-.2558594-.06640625-.35427845-.01325803-.64865004.27047362-.6484375.625v12c.00021484.236623.13402736.45284.34570312.558594l3.99999998 2c.086686.043505.1823067.06624.2792969.066406h6c.09699-.000166.192611-.0229.279297-.06641l4-2c.211676-.10575.345488-.321967.345703-.55859v-12c-.000468-.46423753-.488958-.76598317-.904297-.55859375l-3.869141 1.93359375h-2.9709527s.033448.4166167.015891.625c-.029188.3464401-.1950466.625-.1950468.625z" fill="#e0e0e0"/><path d="m5 6s-2.21875-2.1616704-2.21875-3.2425057c0-1.0808352 0-2.6072392 2.21875-2.6072392s2.21875 1.526404 2.21875 2.6072392c0 1.0808353-2.21875 3.2425057-2.21875 3.2425057z" fill="#fff" fill-opacity=".68627"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -31,6 +31,7 @@
#include "graph_edit.h" #include "graph_edit.h"
#include "core/input/input.h" #include "core/input/input.h"
#include "core/math/math_funcs.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "scene/gui/box_container.h" #include "scene/gui/box_container.h"
#include "scene/gui/button.h" #include "scene/gui/button.h"
@ -44,6 +45,9 @@
#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE) #define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE)
#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE) #define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE)
#define MINIMAP_OFFSET 12
#define MINIMAP_PADDING 5
bool GraphEditFilter::has_point(const Point2 &p_point) const { bool GraphEditFilter::has_point(const Point2 &p_point) const {
return ge->_filter_input(p_point); return ge->_filter_input(p_point);
} }
@ -52,6 +56,141 @@ GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {
ge = p_edit; ge = p_edit;
} }
void GraphEditMinimap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &GraphEditMinimap::_gui_input);
}
GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
ge = p_edit;
graph_proportions = Vector2(1, 1);
graph_padding = Vector2(0, 0);
camera_position = Vector2(100, 50);
camera_size = Vector2(200, 200);
minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING);
minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
is_pressing = false;
is_resizing = false;
}
void GraphEditMinimap::update_minimap() {
Vector2 graph_offset = _get_graph_offset();
Vector2 graph_size = _get_graph_size();
camera_position = ge->get_scroll_ofs() - graph_offset;
camera_size = ge->get_size();
Vector2 render_size = _get_render_size();
float target_ratio = render_size.x / render_size.y;
float graph_ratio = graph_size.x / graph_size.y;
graph_proportions = graph_size;
graph_padding = Vector2(0, 0);
if (graph_ratio > target_ratio) {
graph_proportions.x = graph_size.x;
graph_proportions.y = graph_size.x / target_ratio;
graph_padding.y = Math::abs(graph_size.y - graph_proportions.y) / 2;
} else {
graph_proportions.x = graph_size.y * target_ratio;
graph_proportions.y = graph_size.y;
graph_padding.x = Math::abs(graph_size.x - graph_proportions.x) / 2;
}
// This centers minimap inside the minimap rectangle.
minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
}
Rect2 GraphEditMinimap::get_camera_rect() {
Vector2 camera_center = _convert_from_graph_position(camera_position + camera_size / 2) + minimap_offset;
Vector2 camera_viewport = _convert_from_graph_position(camera_size);
Vector2 camera_position = (camera_center - camera_viewport / 2);
return Rect2(camera_position, camera_viewport);
}
Vector2 GraphEditMinimap::_get_render_size() {
if (!is_inside_tree()) {
return Vector2(0, 0);
}
return get_size() - 2 * minimap_padding;
}
Vector2 GraphEditMinimap::_get_graph_offset() {
return Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
}
Vector2 GraphEditMinimap::_get_graph_size() {
Vector2 graph_size = Vector2(ge->h_scroll->get_max(), ge->v_scroll->get_max()) - Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
if (graph_size.x == 0) {
graph_size.x = 1;
}
if (graph_size.y == 0) {
graph_size.y = 1;
}
return graph_size;
}
Vector2 GraphEditMinimap::_convert_from_graph_position(const Vector2 &p_position) {
Vector2 map_position = Vector2(0, 0);
Vector2 render_size = _get_render_size();
map_position.x = p_position.x * render_size.x / graph_proportions.x;
map_position.y = p_position.y * render_size.y / graph_proportions.y;
return map_position;
}
Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position) {
Vector2 graph_position = Vector2(0, 0);
Vector2 render_size = _get_render_size();
graph_position.x = p_position.x * graph_proportions.x / render_size.x;
graph_position.y = p_position.y * graph_proportions.y / render_size.y;
return graph_position;
}
void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
Ref<InputEventMouseMotion> mm = p_ev;
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
if (mb->is_pressed()) {
is_pressing = true;
Ref<Texture2D> resizer = get_theme_icon("resizer");
Rect2 resizer_hitbox = Rect2(Point2(), resizer->get_size());
if (resizer_hitbox.has_point(mb->get_position())) {
is_resizing = true;
} else {
Vector2 click_position = _convert_to_graph_position(mb->get_position() - minimap_padding) - graph_padding;
_adjust_graph_scroll(click_position);
}
} else {
is_pressing = false;
is_resizing = false;
}
accept_event();
} else if (mm.is_valid() && is_pressing) {
if (is_resizing) {
ge->set_minimap_size(ge->get_minimap_size() - mm->get_relative());
update();
} else {
Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding;
_adjust_graph_scroll(click_position);
}
accept_event();
}
}
void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
Vector2 graph_offset = _get_graph_offset();
ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2);
}
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) { Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) { if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
return OK; return OK;
@ -64,6 +203,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
c.activity = 0; c.activity = 0;
connections.push_back(c); connections.push_back(c);
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
@ -85,6 +225,7 @@ void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const
if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) { if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
connections.erase(E); connections.erase(E);
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
return; return;
@ -118,6 +259,7 @@ void GraphEdit::_scroll_moved(double) {
awaiting_scroll_offset_update = true; awaiting_scroll_offset_update = true;
} }
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
@ -234,6 +376,7 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
GraphNode *gn = Object::cast_to<GraphNode>(p_gn); GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
ERR_FAIL_COND(!gn); ERR_FAIL_COND(!gn);
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
} }
@ -241,7 +384,8 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
void GraphEdit::add_child_notify(Node *p_child) { void GraphEdit::add_child_notify(Node *p_child) {
Control::add_child_notify(p_child); Control::add_child_notify(p_child);
top_layer->call_deferred("raise"); //top layer always on top! top_layer->call_deferred("raise"); // Top layer always on top!
GraphNode *gn = Object::cast_to<GraphNode>(p_child); GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) { if (gn) {
gn->set_scale(Vector2(zoom, zoom)); gn->set_scale(Vector2(zoom, zoom));
@ -255,9 +399,11 @@ void GraphEdit::add_child_notify(Node *p_child) {
void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::remove_child_notify(Node *p_child) {
Control::remove_child_notify(p_child); Control::remove_child_notify(p_child);
if (is_inside_tree()) { if (is_inside_tree()) {
top_layer->call_deferred("raise"); //top layer always on top! top_layer->call_deferred("raise"); // Top layer always on top!
} }
GraphNode *gn = Object::cast_to<GraphNode>(p_child); GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) { if (gn) {
gn->disconnect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved)); gn->disconnect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
@ -275,6 +421,7 @@ void GraphEdit::_notification(int p_what) {
zoom_reset->set_icon(get_theme_icon("reset")); zoom_reset->set_icon(get_theme_icon("reset"));
zoom_plus->set_icon(get_theme_icon("more")); zoom_plus->set_icon(get_theme_icon("more"));
snap_button->set_icon(get_theme_icon("snap")); snap_button->set_icon(get_theme_icon("snap"));
minimap_button->set_icon(get_theme_icon("minimap"));
} }
if (p_what == NOTIFICATION_READY) { if (p_what == NOTIFICATION_READY) {
Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 hmin = h_scroll->get_combined_minimum_size();
@ -338,6 +485,7 @@ void GraphEdit::_notification(int p_what) {
if (p_what == NOTIFICATION_RESIZED) { if (p_what == NOTIFICATION_RESIZED) {
_update_scroll(); _update_scroll();
top_layer->update(); top_layer->update();
minimap->update();
} }
} }
@ -472,6 +620,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_to = mm->get_position(); connecting_to = mm->get_position();
connecting_target = false; connecting_target = false;
top_layer->update(); top_layer->update();
minimap->update();
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom; connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom;
if (connecting_valid) { if (connecting_valid) {
@ -541,6 +690,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting = false; connecting = false;
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
} }
@ -632,12 +782,12 @@ void GraphEdit::_bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors,
} }
} }
void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color) { void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_bezier_ratio) {
//cubic bezier code //cubic bezier code
float diff = p_to.x - p_from.x; float diff = p_to.x - p_from.x;
float cp_offset; float cp_offset;
int cp_len = get_theme_constant("bezier_len_pos"); int cp_len = get_theme_constant("bezier_len_pos") * p_bezier_ratio;
int cp_neg_len = get_theme_constant("bezier_len_neg"); int cp_neg_len = get_theme_constant("bezier_len_neg") * p_bezier_ratio;
if (diff > 0) { if (diff > 0) {
cp_offset = MIN(cp_len, diff * 0.5); cp_offset = MIN(cp_len, diff * 0.5);
@ -708,7 +858,7 @@ void GraphEdit::_connections_layer_draw() {
color = color.lerp(activity_color, E->get().activity); color = color.lerp(activity_color, E->get().activity);
tocolor = tocolor.lerp(activity_color, E->get().activity); tocolor = tocolor.lerp(activity_color, E->get().activity);
} }
_draw_cos_line(connections_layer, frompos, topos, color, tocolor); _draw_cos_line(connections_layer, frompos, topos, color, tocolor, 1.0);
} }
while (to_erase.size()) { while (to_erase.size()) {
@ -747,7 +897,7 @@ void GraphEdit::_top_layer_draw() {
if (!connecting_out) { if (!connecting_out) {
SWAP(pos, topos); SWAP(pos, topos);
} }
_draw_cos_line(top_layer, pos, topos, col, col); _draw_cos_line(top_layer, pos, topos, col, col, 1.0);
} }
if (box_selecting) { if (box_selecting) {
@ -756,6 +906,114 @@ void GraphEdit::_top_layer_draw() {
} }
} }
void GraphEdit::_minimap_draw() {
if (!is_minimap_enabled()) {
return;
}
minimap->update_minimap();
// Draw the minimap background.
Rect2 minimap_rect = Rect2(Point2(), minimap->get_size());
minimap->draw_style_box(minimap->get_theme_stylebox("bg"), minimap_rect);
Vector2 graph_offset = minimap->_get_graph_offset();
Vector2 minimap_offset = minimap->minimap_offset;
// Draw comment graph nodes.
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn || !gn->is_comment()) {
continue;
}
Vector2 node_position = minimap->_convert_from_graph_position(gn->get_offset() * zoom - graph_offset) + minimap_offset;
Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
Rect2 node_rect = Rect2(node_position, node_size);
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "commentfocus" : "comment");
if (sbf.is_valid()) {
Color node_color = sbf->get_bg_color();
sb_minimap->set_bg_color(node_color);
}
minimap->draw_style_box(sb_minimap, node_rect);
}
// Draw regular graph nodes.
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn || gn->is_comment()) {
continue;
}
Vector2 node_position = minimap->_convert_from_graph_position(gn->get_offset() * zoom - graph_offset) + minimap_offset;
Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
Rect2 node_rect = Rect2(node_position, node_size);
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox("node")->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selectedframe" : "frame");
if (sbf.is_valid()) {
Color node_color = sbf->get_border_color();
sb_minimap->set_bg_color(node_color);
}
minimap->draw_style_box(sb_minimap, node_rect);
}
// Draw node connections.
Color activity_color = get_theme_color("activity");
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
NodePath fromnp(E->get().from);
Node *from = get_node(fromnp);
if (!from) {
continue;
}
GraphNode *gfrom = Object::cast_to<GraphNode>(from);
if (!gfrom) {
continue;
}
NodePath tonp(E->get().to);
Node *to = get_node(tonp);
if (!to) {
continue;
}
GraphNode *gto = Object::cast_to<GraphNode>(to);
if (!gto) {
continue;
}
Vector2 from_slot_position = gfrom->get_offset() + gfrom->get_connection_output_position(E->get().from_port);
Vector2 from_position = minimap->_convert_from_graph_position(from_slot_position * zoom - graph_offset) + minimap_offset;
Color from_color = gfrom->get_connection_output_color(E->get().from_port);
Vector2 to_slot_position = gto->get_offset() + gto->get_connection_input_position(E->get().to_port);
Vector2 to_position = minimap->_convert_from_graph_position(to_slot_position * zoom - graph_offset) + minimap_offset;
Color to_color = gto->get_connection_input_color(E->get().to_port);
if (E->get().activity > 0) {
from_color = from_color.lerp(activity_color, E->get().activity);
to_color = to_color.lerp(activity_color, E->get().activity);
}
_draw_cos_line(minimap, from_position, to_position, from_color, to_color, 0.5);
}
// Draw the "camera" viewport.
Rect2 camera_rect = minimap->get_camera_rect();
minimap->draw_style_box(minimap->get_theme_stylebox("camera"), camera_rect);
// Draw the resizer control.
Ref<Texture2D> resizer = minimap->get_theme_icon("resizer");
Color resizer_color = minimap->get_theme_color("resizer_color");
minimap->draw_texture(resizer, Point2(), resizer_color);
}
void GraphEdit::set_selected(Node *p_child) { void GraphEdit::set_selected(Node *p_child) {
for (int i = get_child_count() - 1; i >= 0; i--) { for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@ -836,6 +1094,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
} }
top_layer->update(); top_layer->update();
minimap->update();
} }
Ref<InputEventMouseButton> b = p_ev; Ref<InputEventMouseButton> b = p_ev;
@ -858,10 +1117,12 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
gn->set_selected(select); gn->set_selected(select);
} }
top_layer->update(); top_layer->update();
minimap->update();
} else { } else {
if (connecting) { if (connecting) {
connecting = false; connecting = false;
top_layer->update(); top_layer->update();
minimap->update();
} else { } else {
emit_signal("popup_request", b->get_global_position()); emit_signal("popup_request", b->get_global_position());
} }
@ -902,6 +1163,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
dragging = false; dragging = false;
top_layer->update(); top_layer->update();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
} }
@ -1012,6 +1274,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
box_selecting = false; box_selecting = false;
previus_selected.clear(); previus_selected.clear();
top_layer->update(); top_layer->update();
minimap->update();
} }
if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) { if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
@ -1079,6 +1342,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
if (Math::is_equal_approx(E->get().activity, p_activity)) { if (Math::is_equal_approx(E->get().activity, p_activity)) {
//update only if changed //update only if changed
top_layer->update(); top_layer->update();
minimap->update();
connections_layer->update(); connections_layer->update();
} }
E->get().activity = p_activity; E->get().activity = p_activity;
@ -1089,6 +1353,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
void GraphEdit::clear_connections() { void GraphEdit::clear_connections() {
connections.clear(); connections.clear();
minimap->update();
update(); update();
connections_layer->update(); connections_layer->update();
} }
@ -1112,6 +1377,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
top_layer->update(); top_layer->update();
_update_scroll(); _update_scroll();
minimap->update();
connections_layer->update(); connections_layer->update();
if (is_visible_in_tree()) { if (is_visible_in_tree()) {
@ -1229,6 +1495,47 @@ void GraphEdit::_snap_value_changed(double) {
update(); update();
} }
// _minimap_toggled
void GraphEdit::set_minimap_size(Vector2 p_size) {
minimap->set_size(p_size);
Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size.
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
minimap->set_margin(Margin::MARGIN_LEFT, -minimap_size.x - MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_TOP, -minimap_size.y - MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_RIGHT, -MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_BOTTOM, -MINIMAP_OFFSET);
minimap->update();
}
Vector2 GraphEdit::get_minimap_size() const {
return minimap->get_size();
}
void GraphEdit::set_minimap_opacity(float p_opacity) {
minimap->set_modulate(Color(1, 1, 1, p_opacity));
minimap->update();
}
float GraphEdit::get_minimap_opacity() const {
Color minimap_modulate = minimap->get_modulate();
return minimap_modulate.a;
}
void GraphEdit::set_minimap_enabled(bool p_enable) {
minimap_button->set_pressed(p_enable);
minimap->update();
}
bool GraphEdit::is_minimap_enabled() const {
return minimap_button->is_pressed();
}
void GraphEdit::_minimap_toggled() {
minimap->update();
}
HBoxContainer *GraphEdit::get_zoom_hbox() { HBoxContainer *GraphEdit::get_zoom_hbox() {
return zoom_hb; return zoom_hb;
} }
@ -1260,6 +1567,14 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap); ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap);
ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap); ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap);
ClassDB::bind_method(D_METHOD("set_minimap_size", "p_size"), &GraphEdit::set_minimap_size);
ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size);
ClassDB::bind_method(D_METHOD("set_minimap_opacity", "p_opacity"), &GraphEdit::set_minimap_opacity);
ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity);
ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled);
ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled);
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects); ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled); ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
@ -1275,6 +1590,10 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
ADD_GROUP("Minimap", "minimap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size"), "set_minimap_size", "get_minimap_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity");
ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot")));
@ -1380,6 +1699,32 @@ GraphEdit::GraphEdit() {
snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed)); snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed));
zoom_hb->add_child(snap_amount); zoom_hb->add_child(snap_amount);
minimap_button = memnew(Button);
minimap_button->set_flat(true);
minimap_button->set_toggle_mode(true);
minimap_button->set_tooltip(RTR("Enable grid minimap."));
minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled));
minimap_button->set_pressed(true);
minimap_button->set_focus_mode(FOCUS_NONE);
zoom_hb->add_child(minimap_button);
Vector2 minimap_size = Vector2(240, 160);
float minimap_opacity = 0.45;
minimap = memnew(GraphEditMinimap(this));
top_layer->add_child(minimap);
minimap->set_name("_minimap");
minimap->set_modulate(Color(1, 1, 1, minimap_opacity));
minimap->set_mouse_filter(MOUSE_FILTER_STOP);
minimap->set_custom_minimum_size(Vector2(50, 50));
minimap->set_size(minimap_size);
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
minimap->set_margin(Margin::MARGIN_LEFT, -minimap_size.x - MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_TOP, -minimap_size.y - MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_RIGHT, -MINIMAP_OFFSET);
minimap->set_margin(Margin::MARGIN_BOTTOM, -MINIMAP_OFFSET);
minimap->connect("draw", callable_mp(this, &GraphEdit::_minimap_draw));
setting_scroll_ofs = false; setting_scroll_ofs = false;
just_disconnected = false; just_disconnected = false;
set_clip_contents(true); set_clip_contents(true);

View File

@ -45,6 +45,7 @@ class GraphEditFilter : public Control {
GDCLASS(GraphEditFilter, Control); GDCLASS(GraphEditFilter, Control);
friend class GraphEdit; friend class GraphEdit;
friend class GraphEditMinimap;
GraphEdit *ge; GraphEdit *ge;
virtual bool has_point(const Point2 &p_point) const override; virtual bool has_point(const Point2 &p_point) const override;
@ -52,6 +53,45 @@ public:
GraphEditFilter(GraphEdit *p_edit); GraphEditFilter(GraphEdit *p_edit);
}; };
class GraphEditMinimap : public Control {
GDCLASS(GraphEditMinimap, Control);
friend class GraphEdit;
friend class GraphEditFilter;
GraphEdit *ge;
protected:
static void _bind_methods();
public:
GraphEditMinimap(GraphEdit *p_edit);
void update_minimap();
Rect2 get_camera_rect();
private:
Vector2 minimap_padding;
Vector2 minimap_offset;
Vector2 graph_proportions;
Vector2 graph_padding;
Vector2 camera_position;
Vector2 camera_size;
bool is_pressing;
bool is_resizing;
Vector2 _get_render_size();
Vector2 _get_graph_offset();
Vector2 _get_graph_size();
Vector2 _convert_from_graph_position(const Vector2 &p_position);
Vector2 _convert_to_graph_position(const Vector2 &p_position);
void _gui_input(const Ref<InputEvent> &p_ev);
void _adjust_graph_scroll(const Vector2 &p_offset);
};
class GraphEdit : public Control { class GraphEdit : public Control {
GDCLASS(GraphEdit, Control); GDCLASS(GraphEdit, Control);
@ -72,6 +112,8 @@ private:
Button *snap_button; Button *snap_button;
SpinBox *snap_amount; SpinBox *snap_amount;
Button *minimap_button;
void _zoom_minus(); void _zoom_minus();
void _zoom_reset(); void _zoom_reset();
void _zoom_plus(); void _zoom_plus();
@ -118,7 +160,7 @@ private:
void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const; void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const;
void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color); void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_bezier_ratio);
void _graph_node_raised(Node *p_gn); void _graph_node_raised(Node *p_gn);
void _graph_node_moved(Node *p_gn); void _graph_node_moved(Node *p_gn);
@ -129,12 +171,14 @@ private:
Control *connections_layer; Control *connections_layer;
GraphEditFilter *top_layer; GraphEditFilter *top_layer;
GraphEditMinimap *minimap;
void _top_layer_input(const Ref<InputEvent> &p_ev); void _top_layer_input(const Ref<InputEvent> &p_ev);
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos); bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos);
void _top_layer_draw(); void _top_layer_draw();
void _connections_layer_draw(); void _connections_layer_draw();
void _minimap_draw();
void _update_scroll_offset(); void _update_scroll_offset();
Array _get_connection_list() const; Array _get_connection_list() const;
@ -171,6 +215,9 @@ private:
void _snap_toggled(); void _snap_toggled();
void _snap_value_changed(double); void _snap_value_changed(double);
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 &pos);
protected: protected:
@ -196,7 +243,16 @@ public:
void set_zoom_custom(float p_zoom, const Vector2 &p_center); void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const; float get_zoom() const;
void set_minimap_size(Vector2 p_size);
Vector2 get_minimap_size() const;
void set_minimap_opacity(float p_opacity);
float get_minimap_opacity() const;
void set_minimap_enabled(bool p_enable);
bool is_minimap_enabled() const;
GraphEditFilter *get_top_layer() const { return top_layer; } GraphEditFilter *get_top_layer() const { return top_layer; }
GraphEditMinimap *get_minimap() const { return minimap; }
void get_connection_list(List<Connection> *r_connections) const; void get_connection_list(List<Connection> *r_connections) const;
void set_right_disconnects(bool p_enable); void set_right_disconnects(bool p_enable);

View File

@ -77,6 +77,17 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl
return style; return style;
} }
static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
style->set_bg_color(p_color);
style->set_default_margin(MARGIN_LEFT, p_margin_left * scale);
style->set_default_margin(MARGIN_RIGHT, p_margin_right * scale);
style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * scale);
style->set_default_margin(MARGIN_TOP, p_margin_top * scale);
return style;
}
static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_botton) { static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_botton) {
p_sbox->set_expand_margin_size(MARGIN_LEFT, p_left * scale); p_sbox->set_expand_margin_size(MARGIN_LEFT, p_left * scale);
p_sbox->set_expand_margin_size(MARGIN_TOP, p_top * scale); p_sbox->set_expand_margin_size(MARGIN_TOP, p_top * scale);
@ -97,6 +108,25 @@ static Ref<Texture2D> make_icon(T p_src) {
return texture; return texture;
} }
static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
if (!p_flip_y && !p_flip_x) {
return p_texture;
}
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = p_texture->get_data();
if (p_flip_y) {
img->flip_y();
}
if (p_flip_x) {
img->flip_x();
}
texture->create_from_image(img);
return texture;
}
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) { static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1) {
Ref<StyleBox> style(memnew(StyleBoxEmpty)); Ref<StyleBox> style(memnew(StyleBoxEmpty));
@ -854,6 +884,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png)); theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png));
theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png)); theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png)); theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5)); theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05)); theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2)); theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
@ -867,6 +898,19 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * 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);
style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
style_minimap_camera->set_border_width_all(1);
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
style_minimap_node->set_corner_radius_all(2);
theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
Ref<Texture2D> resizer_icon = make_icon(window_resizer_png);
theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85));
// Theme // Theme
default_icon = make_icon(error_icon_png); default_icon = make_icon(error_icon_png);

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

View File

@ -166,6 +166,10 @@ static const unsigned char icon_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
}; };
static const unsigned char icon_grid_minimap_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned char icon_parent_folder_png[] = { static const unsigned char icon_parent_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
}; };