Fix theme access and improve UX in AnimationTree editor

This commit is contained in:
Yuri Sizov 2023-09-23 21:22:33 +02:00
parent c12d63556b
commit b351cffddf
9 changed files with 292 additions and 195 deletions

View file

@ -2014,17 +2014,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
graphn_sb_titlebar_selected->set_expand_margin(SIDE_TOP, 2 * EDSCALE);
Ref<StyleBoxEmpty> graphn_sb_slot = make_empty_stylebox(12, 0, 12, 0);
// StateMachine.
const int sm_margin_side = 10;
Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width);
smgraphsb->set_border_width_all(border_width);
smgraphsb->set_border_color(graphnode_bg);
Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.9), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width);
smgraphsbselected->set_border_width_all(2 * EDSCALE + border_width);
smgraphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9));
smgraphsbselected->set_shadow_size(8 * EDSCALE);
smgraphsbselected->set_shadow_color(shadow_color);
theme->set_stylebox("panel", "GraphElement", graphn_sb_panel);
theme->set_stylebox("panel_selected", "GraphElement", graphn_sb_panel_selected);
theme->set_stylebox("titlebar", "GraphElement", graphn_sb_titlebar);
@ -2059,8 +2048,55 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
port_icon->set_size_override(Size2(12, 12));
theme->set_icon("port", "GraphNode", port_icon);
theme->set_stylebox("state_machine_frame", "GraphNode", smgraphsb);
theme->set_stylebox("state_machine_selected_frame", "GraphNode", smgraphsbselected);
// StateMachine graph
theme->set_stylebox("panel", "GraphStateMachine", style_tree_bg);
theme->set_stylebox("error_panel", "GraphStateMachine", style_tree_bg);
theme->set_color("error_color", "GraphStateMachine", error_color);
const int sm_margin_side = 10 * EDSCALE;
Ref<StyleBoxFlat> sm_node_style = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), sm_margin_side, 24 * EDSCALE, sm_margin_side, gn_margin_bottom, corner_width);
sm_node_style->set_border_width_all(border_width);
sm_node_style->set_border_color(graphnode_bg);
Ref<StyleBoxFlat> sm_node_selected_style = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.9), sm_margin_side, 24 * EDSCALE, sm_margin_side, gn_margin_bottom, corner_width);
sm_node_selected_style->set_border_width_all(2 * EDSCALE + border_width);
sm_node_selected_style->set_border_color(accent_color * Color(1, 1, 1, 0.9));
sm_node_selected_style->set_shadow_size(8 * EDSCALE);
sm_node_selected_style->set_shadow_color(shadow_color);
Ref<StyleBoxFlat> sm_node_playing_style = sm_node_selected_style->duplicate();
sm_node_playing_style->set_border_color(warning_color);
sm_node_playing_style->set_shadow_color(warning_color * Color(1, 1, 1, 0.2));
theme->set_stylebox("node_frame", "GraphStateMachine", sm_node_style);
theme->set_stylebox("node_frame_selected", "GraphStateMachine", sm_node_selected_style);
theme->set_stylebox("node_frame_playing", "GraphStateMachine", sm_node_playing_style);
Ref<StyleBoxFlat> sm_node_start_style = sm_node_style->duplicate();
sm_node_start_style->set_border_width_all(1 * EDSCALE);
sm_node_start_style->set_border_color(success_color.lightened(0.24));
theme->set_stylebox("node_frame_start", "GraphStateMachine", sm_node_start_style);
Ref<StyleBoxFlat> sm_node_end_style = sm_node_style->duplicate();
sm_node_end_style->set_border_width_all(1 * EDSCALE);
sm_node_end_style->set_border_color(error_color);
theme->set_stylebox("node_frame_end", "GraphStateMachine", sm_node_end_style);
theme->set_font("node_title_font", "GraphStateMachine", theme->get_font(SNAME("font"), SNAME("Label")));
theme->set_font_size("node_title_font_size", "GraphStateMachine", theme->get_font_size(SNAME("font_size"), SNAME("Label")));
theme->set_color("node_title_font_color", "GraphStateMachine", font_color);
theme->set_color("transition_color", "GraphStateMachine", font_color);
theme->set_color("transition_disabled_color", "GraphStateMachine", font_color * Color(1, 1, 1, 0.2));
theme->set_color("transition_icon_color", "GraphStateMachine", Color(1, 1, 1));
theme->set_color("transition_icon_disabled_color", "GraphStateMachine", Color(1, 1, 1, 0.2));
theme->set_color("highlight_color", "GraphStateMachine", accent_color);
theme->set_color("highlight_disabled_color", "GraphStateMachine", accent_color * Color(1, 1, 1, 0.6));
theme->set_color("guideline_color", "GraphStateMachine", font_color * Color(1, 1, 1, 0.3));
theme->set_color("playback_color", "GraphStateMachine", font_color);
theme->set_color("playback_background_color", "GraphStateMachine", font_color * Color(1, 1, 1, 0.3));
// GridContainer
theme->set_constant("v_separation", "GridContainer", Math::round(widget_default_margin.y - 2 * EDSCALE));

View file

@ -1564,14 +1564,6 @@ void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) {
show_all_nodes->show();
}
void SceneTreeDialog::_update_theme() {
filter->set_right_icon(tree->get_editor_theme_icon(SNAME("Search")));
for (TextureRect *trect : valid_type_icons) {
trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0));
trect->set_texture(EditorNode::get_singleton()->get_class_icon(trect->get_meta("type")));
}
}
void SceneTreeDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
@ -1585,11 +1577,14 @@ void SceneTreeDialog::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
connect("confirmed", callable_mp(this, &SceneTreeDialog::_select));
_update_theme();
} break;
case NOTIFICATION_THEME_CHANGED: {
_update_theme();
filter->set_right_icon(get_editor_theme_icon(SNAME("Search")));
for (TextureRect *trect : valid_type_icons) {
trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0));
trect->set_texture(EditorNode::get_singleton()->get_class_icon(trect->get_meta("type")));
}
} break;
case NOTIFICATION_EXIT_TREE: {

View file

@ -188,7 +188,6 @@ class SceneTreeDialog : public ConfirmationDialog {
void _selected_changed();
void _filter_changed(const String &p_filter);
void _show_all_nodes_changed(bool p_button_pressed);
void _update_theme();
protected:
void _notification(int p_what);

View file

@ -263,7 +263,8 @@ void AnimationNodeBlendTreeEditor::update_graph() {
mb->get_popup()->connect("index_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected).bind(options, E), CONNECT_DEFERRED);
}
Ref<StyleBoxFlat> sb = node->get_theme_stylebox(SNAME("panel"), SNAME("GraphNode"));
// TODO: Avoid using strings, expose a method on GraphNode instead.
Ref<StyleBoxFlat> sb = node->get_theme_stylebox(SNAME("panel"));
Color c = sb->get_border_color();
Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0);
mono_color.a = 0.85;
@ -831,16 +832,10 @@ void AnimationNodeBlendTreeEditor::_update_editor_settings() {
graph->set_warped_panning(bool(EDITOR_GET("editors/panning/warped_mouse_panning")));
}
void AnimationNodeBlendTreeEditor::_update_theme() {
error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
}
void AnimationNodeBlendTreeEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_editor_settings();
_update_theme();
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
@ -848,7 +843,8 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
_update_theme();
error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
if (is_visible_in_tree()) {
update_graph();

View file

@ -127,7 +127,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
void _update_editor_settings();
void _update_theme();
EditorFileDialog *open_file = nullptr;
Ref<AnimationNode> file_loaded;

View file

@ -38,7 +38,6 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_file_dialog.h"
#include "scene/animation/animation_blend_tree.h"
@ -53,6 +52,7 @@
#include "scene/main/window.h"
#include "scene/resources/style_box_flat.h"
#include "scene/scene_string_names.h"
#include "scene/theme/theme_db.h"
bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) {
Ref<AnimationNodeStateMachine> ansm = p_node;
@ -179,8 +179,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
if (!read_only) {
if (node_rects[i].name.has_point(mb->get_position()) && state_machine->can_edit_node(node_rects[i].node_name)) { // edit name
Ref<StyleBox> line_sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit"));
// TODO: Avoid using strings, expose a method on LineEdit.
Ref<StyleBox> line_sb = name_edit->get_theme_stylebox(SNAME("normal"));
Rect2 edit_rect = node_rects[i].name;
edit_rect.position -= line_sb->get_offset();
edit_rect.size += line_sb->get_minimum_size();
@ -447,8 +447,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
if (mm.is_valid()) {
state_machine_draw->grab_focus();
String new_over_node;
int new_over_node_what = -1;
String new_hovered_node_name;
HoveredNodeArea new_hovered_node_area = HOVER_NODE_NONE;
if (tool_select->is_pressed()) {
for (int i = node_rects.size() - 1; i >= 0; i--) { // Inverse to draw order.
@ -457,20 +457,20 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
if (node_rects[i].node.has_point(mm->get_position())) {
new_over_node = node_rects[i].node_name;
new_hovered_node_name = node_rects[i].node_name;
if (node_rects[i].play.has_point(mm->get_position())) {
new_over_node_what = 0;
new_hovered_node_area = HOVER_NODE_PLAY;
} else if (node_rects[i].edit.has_point(mm->get_position())) {
new_over_node_what = 1;
new_hovered_node_area = HOVER_NODE_EDIT;
}
break;
}
}
}
if (new_over_node != over_node || new_over_node_what != over_node_what) {
over_node = new_over_node;
over_node_what = new_over_node_what;
if (new_hovered_node_name != hovered_node_name || new_hovered_node_area != hovered_node_area) {
hovered_node_name = new_hovered_node_name;
hovered_node_area = new_hovered_node_area;
state_machine_draw->queue_redraw();
}
@ -534,6 +534,23 @@ Control::CursorShape AnimationNodeStateMachineEditor::get_cursor_shape(const Poi
return cursor_shape;
}
String AnimationNodeStateMachineEditor::get_tooltip(const Point2 &p_pos) const {
if (hovered_node_name == StringName()) {
return AnimationTreeNodeEditorPlugin::get_tooltip(p_pos);
}
String tooltip_text;
if (hovered_node_area == HOVER_NODE_PLAY) {
tooltip_text = vformat(TTR("Play/Travel to %s"), hovered_node_name);
} else if (hovered_node_area == HOVER_NODE_EDIT) {
tooltip_text = vformat(TTR("Edit %s"), hovered_node_name);
} else {
tooltip_text = hovered_node_name;
}
return tooltip_text;
}
void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
if (!tree) {
@ -543,24 +560,29 @@ void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
menu->clear();
animations_menu->clear();
animations_to_add.clear();
List<StringName> classes;
classes.sort_custom<StringName::AlphCompare>();
ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
menu->add_submenu_item(TTR("Add Animation"), "animations");
List<StringName> animation_names;
if (tree->has_node(tree->get_animation_player())) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player()));
if (ap) {
List<StringName> names;
ap->get_animation_list(&names);
for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
animations_menu->add_icon_item(get_editor_theme_icon("Animation"), E->get());
animations_to_add.push_back(E->get());
}
ap->get_animation_list(&animation_names);
}
}
menu->add_submenu_item(TTR("Add Animation"), "animations");
if (animation_names.is_empty()) {
menu->set_item_disabled(menu->get_item_idx_from_text(TTR("Add Animation")), true);
} else {
for (const StringName &name : animation_names) {
animations_menu->add_icon_item(theme_cache.animation_icon, name);
animations_to_add.push_back(name);
}
}
List<StringName> classes;
ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
classes.sort_custom<StringName::AlphCompare>();
for (List<StringName>::Element *E = classes.front(); E; E = E->next()) {
String name = String(E->get()).replace_first("AnimationNode", "");
if (name == "Animation" || name == "StartState" || name == "EndState") {
@ -823,44 +845,29 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action
}
void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group) {
Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label"));
Color icon_color(1, 1, 1);
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
if (!p_enabled) {
linecolor.a *= 0.2;
icon_color.a *= 0.2;
accent.a *= 0.6;
}
const Ref<Texture2D> icons[] = {
get_editor_theme_icon(SNAME("TransitionImmediateBig")),
get_editor_theme_icon(SNAME("TransitionSyncBig")),
get_editor_theme_icon(SNAME("TransitionEndBig")),
get_editor_theme_icon(SNAME("TransitionImmediateAutoBig")),
get_editor_theme_icon(SNAME("TransitionSyncAutoBig")),
get_editor_theme_icon(SNAME("TransitionEndAutoBig"))
};
const int ICON_COUNT = sizeof(icons) / sizeof(*icons);
if (p_selected) {
state_machine_draw->draw_line(p_from, p_to, accent, 6);
}
Color line_color = p_enabled ? theme_cache.transition_color : theme_cache.transition_disabled_color;
Color icon_color = p_enabled ? theme_cache.transition_icon_color : theme_cache.transition_icon_disabled_color;
Color highlight_color = p_enabled ? theme_cache.highlight_color : theme_cache.highlight_disabled_color;
if (p_travel) {
linecolor = accent;
line_color = highlight_color;
}
state_machine_draw->draw_line(p_from, p_to, linecolor, 2);
if (p_selected) {
state_machine_draw->draw_line(p_from, p_to, highlight_color, 6, true);
}
state_machine_draw->draw_line(p_from, p_to, line_color, 2, true);
if (p_fade_ratio > 0.0) {
Color fade_linecolor = accent;
fade_linecolor.set_hsv(1.0, fade_linecolor.get_s(), fade_linecolor.get_v());
state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_linecolor, 2);
Color fade_line_color = highlight_color;
fade_line_color.set_hsv(1.0, fade_line_color.get_s(), fade_line_color.get_v());
state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_line_color, 2);
}
const int ICON_COUNT = sizeof(theme_cache.transition_icons) / sizeof(*theme_cache.transition_icons);
int icon_index = p_mode + (p_auto_advance ? ICON_COUNT / 2 : 0);
ERR_FAIL_COND(icon_index >= ICON_COUNT);
Ref<Texture2D> icon = icons[icon_index];
Ref<Texture2D> icon = theme_cache.transition_icons[icon_index];
Transform2D xf;
xf.columns[0] = (p_to - p_from).normalized();
@ -904,34 +911,12 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
return;
}
Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
Ref<StyleBoxFlat> style = get_theme_stylebox(SNAME("state_machine_frame"), SNAME("GraphNode"));
Ref<StyleBoxFlat> style_selected = get_theme_stylebox(SNAME("state_machine_selected_frame"), SNAME("GraphNode"));
Ref<Font> font = get_theme_font(SNAME("title_font"), SNAME("GraphNode"));
int font_size = get_theme_font_size(SNAME("title_font_size"), SNAME("GraphNode"));
Color font_color = get_theme_color(SNAME("title_color"), SNAME("GraphNode"));
Ref<Texture2D> play = get_editor_theme_icon(SNAME("Play"));
Ref<Texture2D> edit = get_editor_theme_icon(SNAME("Edit"));
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label"));
linecolor.a *= 0.3;
Ref<StyleBox> playing_overlay = get_theme_stylebox(SNAME("position"), SNAME("GraphNode"));
Ref<StyleBoxFlat> start_overlay = style->duplicate();
start_overlay->set_border_width_all(1 * EDSCALE);
start_overlay->set_border_color(Color::html("#80f6cf"));
Ref<StyleBoxFlat> end_overlay = style->duplicate();
end_overlay->set_border_width_all(1 * EDSCALE);
end_overlay->set_border_color(Color::html("#f26661"));
bool playing = false;
StringName current;
StringName blend_from;
Vector<StringName> travel_path;
Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
if (playback.is_valid()) {
playing = playback->is_playing();
current = playback->get_current_node();
@ -940,7 +925,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
}
if (state_machine_draw->has_focus()) {
state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false);
state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), theme_cache.highlight_color, false);
}
int sep = 3 * EDSCALE;
@ -955,29 +940,30 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE;
if (snap_x != StringName()) {
Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
state_machine_draw->draw_line(from, to, linecolor, 2);
state_machine_draw->draw_line(from, to, theme_cache.guideline_color, 2);
}
if (snap_y != StringName()) {
Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE;
state_machine_draw->draw_line(from, to, linecolor, 2);
state_machine_draw->draw_line(from, to, theme_cache.guideline_color, 2);
}
}
//pre pass nodes so we know the rectangles
for (const StringName &E : nodes) {
Ref<AnimationNode> anode = state_machine->get_node(E);
String name = E;
bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode);
Ref<StyleBox> sb = selected_nodes.has(E) ? style_selected : style;
int name_string_size = theme_cache.node_title_font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.node_title_font_size).width;
Size2 s = sb->get_minimum_size();
int strsize = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
s.width += strsize;
s.height += MAX(font->get_height(font_size), play->get_height());
s.width += sep + play->get_width();
Ref<AnimationNode> anode = state_machine->get_node(name);
bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode);
bool is_selected = selected_nodes.has(name);
Size2 s = (is_selected ? theme_cache.node_frame_selected : theme_cache.node_frame)->get_minimum_size();
s.width += name_string_size;
s.height += MAX(theme_cache.node_title_font->get_height(theme_cache.node_title_font_size), theme_cache.play_node->get_height());
s.width += sep + theme_cache.play_node->get_width();
if (needs_editor) {
s.width += sep + edit->get_width();
s.width += sep + theme_cache.edit_node->get_width();
}
Vector2 offset;
@ -1028,8 +1014,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
_connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, 0.0, false, false);
}
Ref<Texture2D> tr_reference_icon = get_editor_theme_icon(SNAME("TransitionImmediateBig"));
float tr_bidi_offset = int(tr_reference_icon->get_height() * 0.8);
// TransitionImmediateBig
float tr_bidi_offset = int(theme_cache.transition_icons[0]->get_height() * 0.8);
//draw transition lines
for (int i = 0; i < state_machine->get_transition_count(); i++) {
@ -1117,62 +1103,60 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
//draw actual nodes
for (int i = 0; i < node_rects.size(); i++) {
String name = node_rects[i].node_name;
int name_string_size = theme_cache.node_title_font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.node_title_font_size).width;
Ref<AnimationNode> anode = state_machine->get_node(name);
bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode);
Ref<StyleBox> sb = selected_nodes.has(name) ? style_selected : style;
int strsize = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
NodeRect &nr = node_rects.write[i];
bool is_selected = selected_nodes.has(name);
NodeRect &nr = node_rects.write[i];
Vector2 offset = nr.node.position;
int h = nr.node.size.height;
//prepre rect
//now scroll it to draw
state_machine_draw->draw_style_box(sb, nr.node);
Ref<StyleBox> node_frame_style = is_selected ? theme_cache.node_frame_selected : theme_cache.node_frame;
state_machine_draw->draw_style_box(node_frame_style, nr.node);
if (state_machine->start_node == name) {
state_machine_draw->draw_style_box(sb == style_selected ? style_selected : start_overlay, nr.node);
if (!is_selected && state_machine->start_node == name) {
state_machine_draw->draw_style_box(theme_cache.node_frame_start, nr.node);
}
if (state_machine->end_node == name) {
state_machine_draw->draw_style_box(sb == style_selected ? style_selected : end_overlay, nr.node);
if (!is_selected && state_machine->end_node == name) {
state_machine_draw->draw_style_box(theme_cache.node_frame_end, nr.node);
}
if (playing && (blend_from == name || current == name || travel_path.has(name))) {
state_machine_draw->draw_style_box(playing_overlay, nr.node);
state_machine_draw->draw_style_box(theme_cache.node_frame_playing, nr.node);
}
offset.x += sb->get_offset().x;
offset.x += node_frame_style->get_offset().x;
nr.play.position = offset + Vector2(0, (h - play->get_height()) / 2).floor();
nr.play.size = play->get_size();
nr.play.position = offset + Vector2(0, (h - theme_cache.play_node->get_height()) / 2).floor();
nr.play.size = theme_cache.play_node->get_size();
Ref<Texture2D> play_tex = play;
if (over_node == name && over_node_what == 0) {
state_machine_draw->draw_texture(play_tex, nr.play.position, accent);
if (hovered_node_name == name && hovered_node_area == HOVER_NODE_PLAY) {
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position, theme_cache.highlight_color);
} else {
state_machine_draw->draw_texture(play_tex, nr.play.position);
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position);
}
offset.x += sep + play->get_width();
offset.x += sep + theme_cache.play_node->get_width();
nr.name.position = offset + Vector2(0, (h - font->get_height(font_size)) / 2).floor();
nr.name.size = Vector2(strsize, font->get_height(font_size));
nr.name.position = offset + Vector2(0, (h - theme_cache.node_title_font->get_height(theme_cache.node_title_font_size)) / 2).floor();
nr.name.size = Vector2(name_string_size, theme_cache.node_title_font->get_height(theme_cache.node_title_font_size));
state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent(font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
offset.x += strsize + sep;
state_machine_draw->draw_string(theme_cache.node_title_font, nr.name.position + Vector2(0, theme_cache.node_title_font->get_ascent(theme_cache.node_title_font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.node_title_font_size, theme_cache.node_title_font_color);
offset.x += name_string_size + sep;
nr.can_edit = needs_editor;
if (needs_editor) {
nr.edit.position = offset + Vector2(0, (h - edit->get_height()) / 2).floor();
nr.edit.size = edit->get_size();
nr.edit.position = offset + Vector2(0, (h - theme_cache.edit_node->get_height()) / 2).floor();
nr.edit.size = theme_cache.edit_node->get_size();
if (over_node == name && over_node_what == 1) {
state_machine_draw->draw_texture(edit, nr.edit.position, accent);
if (hovered_node_name == name && hovered_node_area == HOVER_NODE_EDIT) {
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position, theme_cache.highlight_color);
} else {
state_machine_draw->draw_texture(edit, nr.edit.position);
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position);
}
}
}
@ -1229,7 +1213,6 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String
}
const NodeRect &nr = node_rects[idx];
if (nr.can_edit) {
return; // It is not AnimationNodeAnimation.
}
@ -1246,16 +1229,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String
}
to.y = from.y;
float c = p_ratio;
Color fg = get_theme_color(SNAME("font_color"), SNAME("Label"));
Color bg = fg;
bg.a *= 0.3;
state_machine_play_pos->draw_line(from, to, bg, 2);
to = from.lerp(to, c);
state_machine_play_pos->draw_line(from, to, fg, 2);
state_machine_play_pos->draw_line(from, to, theme_cache.playback_background_color, 2);
to = from.lerp(to, p_ratio);
state_machine_play_pos->draw_line(from, to, theme_cache.playback_color, 2);
}
void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() {
@ -1298,30 +1274,27 @@ void AnimationNodeStateMachineEditor::_update_graph() {
void AnimationNodeStateMachineEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
case NOTIFICATION_THEME_CHANGED: {
panel->add_theme_style_override("panel", theme_cache.panel_style);
error_panel->add_theme_style_override("panel", theme_cache.error_panel_style);
error_label->add_theme_color_override("font_color", theme_cache.error_color);
tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect")));
tool_create->set_icon(get_editor_theme_icon(SNAME("ToolAddNode")));
tool_connect->set_icon(get_editor_theme_icon(SNAME("ToolConnect")));
tool_select->set_icon(theme_cache.tool_icon_select);
tool_create->set_icon(theme_cache.tool_icon_create);
tool_connect->set_icon(theme_cache.tool_icon_connect);
switch_mode->clear();
switch_mode->add_icon_item(get_editor_theme_icon(SNAME("TransitionImmediate")), TTR("Immediate"));
switch_mode->add_icon_item(get_editor_theme_icon(SNAME("TransitionSync")), TTR("Sync"));
switch_mode->add_icon_item(get_editor_theme_icon(SNAME("TransitionEnd")), TTR("At End"));
switch_mode->add_icon_item(theme_cache.transition_icon_immediate, TTR("Immediate"));
switch_mode->add_icon_item(theme_cache.transition_icon_sync, TTR("Sync"));
switch_mode->add_icon_item(theme_cache.transition_icon_end, TTR("At End"));
auto_advance->set_icon(get_editor_theme_icon(SNAME("AutoPlay")));
auto_advance->set_icon(theme_cache.play_icon_auto);
tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove")));
tool_erase->set_icon(theme_cache.tool_icon_erase);
play_mode->clear();
play_mode->add_icon_item(get_editor_theme_icon(SNAME("PlayTravel")), TTR("Travel"));
play_mode->add_icon_item(get_editor_theme_icon(SNAME("Play")), TTR("Immediate"));
play_mode->add_icon_item(theme_cache.play_icon_travel, TTR("Travel"));
play_mode->add_icon_item(theme_cache.play_icon_start, TTR("Immediate"));
} break;
case NOTIFICATION_PROCESS: {
@ -1486,7 +1459,8 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
over_node = StringName();
hovered_node_name = StringName();
hovered_node_area = HOVER_NODE_NONE;
set_process(is_visible_in_tree());
} break;
}
@ -1638,6 +1612,56 @@ void AnimationNodeStateMachineEditor::_bind_methods() {
ClassDB::bind_method("_delete_selected", &AnimationNodeStateMachineEditor::_delete_selected);
ClassDB::bind_method("_delete_all", &AnimationNodeStateMachineEditor::_delete_all);
ClassDB::bind_method("_delete_tree_draw", &AnimationNodeStateMachineEditor::_delete_tree_draw);
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, panel_style, "panel", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, error_panel_style, "error_panel", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, error_color, "error_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, tool_icon_select, "ToolSelect", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, tool_icon_create, "ToolAddNode", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, tool_icon_connect, "ToolConnect", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, tool_icon_erase, "Remove", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icon_immediate, "TransitionImmediate", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icon_sync, "TransitionSync", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icon_end, "TransitionEnd", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, play_icon_start, "Play", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, play_icon_travel, "PlayTravel", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, play_icon_auto, "AutoPlay", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, animation_icon, "Animation", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, node_frame, "node_frame", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, node_frame_selected, "node_frame_selected", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, node_frame_playing, "node_frame_playing", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, node_frame_start, "node_frame_start", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, AnimationNodeStateMachineEditor, node_frame_end, "node_frame_end", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_FONT, AnimationNodeStateMachineEditor, node_title_font, "node_title_font", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_FONT_SIZE, AnimationNodeStateMachineEditor, node_title_font_size, "node_title_font_size", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, node_title_font_color, "node_title_font_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, play_node, "Play", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, edit_node, "Edit", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_color, "transition_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_disabled_color, "transition_disabled_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_icon_color, "transition_icon_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_icon_disabled_color, "transition_icon_disabled_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_color, "highlight_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_disabled_color, "highlight_disabled_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, guideline_color, "guideline_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[0], "TransitionImmediateBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[1], "TransitionSyncBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[2], "TransitionEndBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[3], "TransitionImmediateAutoBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[4], "TransitionSyncAutoBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[5], "TransitionEndAutoBig", "EditorIcons");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, playback_color, "playback_color", "GraphStateMachine");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, playback_background_color, "playback_background_color", "GraphStateMachine");
}
AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = nullptr;
@ -1803,15 +1827,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
Button *delete_all = delete_window->add_button(TTR("Delete All"), true);
delete_all->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_delete_all));
over_node_what = -1;
dragging_selected_attempt = false;
connecting = false;
selected_transition_index = -1;
last_active = false;
error_time = 0;
}
void EditorAnimationMultiTransitionEdit::add_transition(const StringName &p_from, const StringName &p_to, Ref<AnimationNodeStateMachineTransition> p_transition) {

View file

@ -78,6 +78,53 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
PanelContainer *error_panel = nullptr;
Label *error_label = nullptr;
struct ThemeCache {
Ref<StyleBox> panel_style;
Ref<StyleBox> error_panel_style;
Color error_color;
Ref<Texture2D> tool_icon_select;
Ref<Texture2D> tool_icon_create;
Ref<Texture2D> tool_icon_connect;
Ref<Texture2D> tool_icon_erase;
Ref<Texture2D> transition_icon_immediate;
Ref<Texture2D> transition_icon_sync;
Ref<Texture2D> transition_icon_end;
Ref<Texture2D> play_icon_start;
Ref<Texture2D> play_icon_travel;
Ref<Texture2D> play_icon_auto;
Ref<Texture2D> animation_icon;
Ref<StyleBox> node_frame;
Ref<StyleBox> node_frame_selected;
Ref<StyleBox> node_frame_playing;
Ref<StyleBox> node_frame_start;
Ref<StyleBox> node_frame_end;
Ref<Font> node_title_font;
int node_title_font_size = 0;
Color node_title_font_color;
Ref<Texture2D> play_node;
Ref<Texture2D> edit_node;
Color transition_color;
Color transition_disabled_color;
Color transition_icon_color;
Color transition_icon_disabled_color;
Color highlight_color;
Color highlight_disabled_color;
Color guideline_color;
Ref<Texture2D> transition_icons[6]{};
Color playback_color;
Color playback_background_color;
} theme_cache;
bool updating = false;
static AnimationNodeStateMachineEditor *singleton;
@ -176,11 +223,17 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
StringName selected_transition_from;
StringName selected_transition_to;
int selected_transition_index;
int selected_transition_index = -1;
void _add_transition(const bool p_nested_action = false);
StringName over_node;
int over_node_what = -1;
enum HoveredNodeArea {
HOVER_NODE_NONE = -1,
HOVER_NODE_PLAY = 0,
HOVER_NODE_EDIT = 1,
};
StringName hovered_node_name;
HoveredNodeArea hovered_node_area = HOVER_NODE_NONE;
String prev_name;
void _name_edited(const String &p_text);
@ -240,9 +293,13 @@ protected:
public:
static AnimationNodeStateMachineEditor *get_singleton() { return singleton; }
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
virtual void edit(const Ref<AnimationNode> &p_node) override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
virtual String get_tooltip(const Point2 &p_pos) const override;
AnimationNodeStateMachineEditor();
};

View file

@ -177,12 +177,12 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim
}
float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, 0);
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, 0);
return blend_points[p_point].position;
}
Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref<AnimationRootNode>());
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Ref<AnimationRootNode>());
return blend_points[p_point].node;
}

View file

@ -114,12 +114,12 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim
}
Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2());
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Vector2());
return blend_points[p_point].position;
}
Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_point) const {
ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref<AnimationRootNode>());
ERR_FAIL_INDEX_V(p_point, MAX_BLEND_POINTS, Ref<AnimationRootNode>());
return blend_points[p_point].node;
}