Add math/script expression evaluation in editor (#3620)

Rebase of #453
This commit is contained in:
Franco Eusébio Garcia 2016-05-01 05:33:32 -03:00 committed by Rémi Verschelde
parent ff40dcd83f
commit a735573327
5 changed files with 252 additions and 40 deletions

View File

@ -0,0 +1,43 @@
/*************************************************************************/
/* value_evaluator.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef VALUE_EVALUATOR_H
#define VALUE_EVALUATOR_H
#include "core/object.h"
class ValueEvaluator : public Object {
OBJ_TYPE(ValueEvaluator, Object);
public:
virtual double eval(const String& p_text) {
return p_text.to_double();
}
};
#endif // VALUE_EVALUATOR_H

View File

@ -154,7 +154,7 @@ void TreeItem::set_text(int p_column,String p_text) {
ERR_FAIL_INDEX( p_column, cells.size() ); ERR_FAIL_INDEX( p_column, cells.size() );
cells[p_column].text=p_text; cells[p_column].text=p_text;
if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE) { if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE || cells[p_column].mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) {
cells[p_column].min=0; cells[p_column].min=0;
cells[p_column].max=p_text.get_slice_count(","); cells[p_column].max=p_text.get_slice_count(",");
@ -704,6 +704,7 @@ void TreeItem::_bind_methods() {
BIND_CONSTANT( CELL_MODE_STRING ); BIND_CONSTANT( CELL_MODE_STRING );
BIND_CONSTANT( CELL_MODE_CHECK ); BIND_CONSTANT( CELL_MODE_CHECK );
BIND_CONSTANT( CELL_MODE_RANGE ); BIND_CONSTANT( CELL_MODE_RANGE );
BIND_CONSTANT( CELL_MODE_RANGE_EXPRESSION );
BIND_CONSTANT( CELL_MODE_ICON ); BIND_CONSTANT( CELL_MODE_ICON );
BIND_CONSTANT( CELL_MODE_CUSTOM ); BIND_CONSTANT( CELL_MODE_CUSTOM );
@ -1127,7 +1128,8 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
//font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w ); //font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w );
} break; } break;
case TreeItem::CELL_MODE_RANGE: { case TreeItem::CELL_MODE_RANGE:
case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (p_item->cells[i].text!="") { if (p_item->cells[i].text!="") {
@ -1594,7 +1596,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
} }
} break; } break;
case TreeItem::CELL_MODE_RANGE: { case TreeItem::CELL_MODE_RANGE:
case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (c.text!="") { if (c.text!="") {
@ -1794,6 +1797,13 @@ void Tree::text_editor_enter(String p_text) {
//popup_edited_item->edited_signal.call( popup_edited_item_col ); //popup_edited_item->edited_signal.call( popup_edited_item_col );
} break; } break;
case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if(evaluator)
c.val=evaluator->eval(p_text);
else
c.val=p_text.to_double();
} break;
default: { ERR_FAIL(); } default: { ERR_FAIL(); }
} }
@ -2372,7 +2382,7 @@ bool Tree::edit_selected() {
item_edited(col,s); item_edited(col,s);
return true; return true;
} else if (c.mode==TreeItem::CELL_MODE_RANGE && c.text!="") { } else if ((c.mode==TreeItem::CELL_MODE_RANGE||c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) && c.text!="") {
popup_menu->clear(); popup_menu->clear();
for (int i=0;i<c.text.get_slice_count(",");i++) { for (int i=0;i<c.text.get_slice_count(",");i++) {
@ -2389,7 +2399,7 @@ bool Tree::edit_selected() {
popup_edited_item_col=col; popup_edited_item_col=col;
return true; return true;
} else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE) { } else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
Point2i textedpos=get_global_pos() + rect.pos; Point2i textedpos=get_global_pos() + rect.pos;
text_editor->set_pos( textedpos ); text_editor->set_pos( textedpos );
@ -2398,7 +2408,7 @@ bool Tree::edit_selected() {
text_editor->set_text( c.mode==TreeItem::CELL_MODE_STRING?c.text:rtos(c.val) ); text_editor->set_text( c.mode==TreeItem::CELL_MODE_STRING?c.text:rtos(c.val) );
text_editor->select_all(); text_editor->select_all();
if (c.mode==TreeItem::CELL_MODE_RANGE) { if (c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
value_editor->set_pos(textedpos + Point2i(0,text_editor->get_size().height) ); value_editor->set_pos(textedpos + Point2i(0,text_editor->get_size().height) );
value_editor->set_size( Size2(rect.size.width,1)); value_editor->set_size( Size2(rect.size.width,1));
@ -3227,6 +3237,9 @@ bool Tree::is_folding_hidden() const {
return hide_folding; return hide_folding;
} }
void Tree::set_value_evaluator(ValueEvaluator *p_evaluator) {
evaluator = p_evaluator;
}
void Tree::_bind_methods() { void Tree::_bind_methods() {
@ -3367,6 +3380,7 @@ Tree::Tree() {
hide_folding=false; hide_folding=false;
evaluator=NULL;
} }

View File

@ -34,6 +34,7 @@
#include "scene/gui/line_edit.h" #include "scene/gui/line_edit.h"
#include "scene/gui/scroll_bar.h" #include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h" #include "scene/gui/slider.h"
#include "core/helper/value_evaluator.h"
/** /**
@author Juan Linietsky <reduzio@gmail.com> @author Juan Linietsky <reduzio@gmail.com>
@ -52,6 +53,7 @@ public:
CELL_MODE_STRING, ///< just a string CELL_MODE_STRING, ///< just a string
CELL_MODE_CHECK, ///< string + check CELL_MODE_CHECK, ///< string + check
CELL_MODE_RANGE, ///< Contains a range CELL_MODE_RANGE, ///< Contains a range
CELL_MODE_RANGE_EXPRESSION, ///< Contains a range
CELL_MODE_ICON, ///< Contains a icon, not editable CELL_MODE_ICON, ///< Contains a icon, not editable
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
}; };
@ -422,6 +424,8 @@ friend class TreeItem;
bool hide_folding; bool hide_folding;
ValueEvaluator *evaluator;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -482,7 +486,7 @@ public:
void set_hide_folding(bool p_hide); void set_hide_folding(bool p_hide);
bool is_folding_hidden() const; bool is_folding_hidden() const;
void set_value_evaluator(ValueEvaluator *p_evaluator);
Tree(); Tree();
~Tree(); ~Tree();

View File

@ -1313,7 +1313,10 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::REAL: { case Variant::REAL: {
if (hint!=PROPERTY_HINT_EXP_EASING) { if (hint!=PROPERTY_HINT_EXP_EASING) {
v=value_editor[0]->get_text().to_double(); if (evaluator)
evaluator->eval(value_editor[0]->get_text());
else
v=value_editor[0]->get_text().to_double();
emit_signal("variant_changed"); emit_signal("variant_changed");
} }
@ -1327,8 +1330,13 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::VECTOR2: { case Variant::VECTOR2: {
Vector2 vec; Vector2 vec;
vec.x=value_editor[0]->get_text().to_double(); if (evaluator) {
vec.y=value_editor[1]->get_text().to_double(); vec.x=evaluator->eval(value_editor[0]->get_text());
vec.y=evaluator->eval(value_editor[1]->get_text());
} else {
vec.x=value_editor[0]->get_text().to_double();
vec.y=value_editor[1]->get_text().to_double();
}
v=vec; v=vec;
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1336,10 +1344,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::RECT2: { case Variant::RECT2: {
Rect2 r2; Rect2 r2;
r2.pos.x=value_editor[0]->get_text().to_double(); if (evaluator) {
r2.pos.y=value_editor[1]->get_text().to_double(); r2.pos.x=evaluator->eval(value_editor[0]->get_text());
r2.size.x=value_editor[2]->get_text().to_double(); r2.pos.y=evaluator->eval(value_editor[1]->get_text());
r2.size.y=value_editor[3]->get_text().to_double(); r2.size.x=evaluator->eval(value_editor[2]->get_text());
r2.size.y=evaluator->eval(value_editor[3]->get_text());
} else {
r2.pos.x=value_editor[0]->get_text().to_double();
r2.pos.y=value_editor[1]->get_text().to_double();
r2.size.x=value_editor[2]->get_text().to_double();
r2.size.y=value_editor[3]->get_text().to_double();
}
v=r2; v=r2;
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1348,9 +1363,15 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::VECTOR3: { case Variant::VECTOR3: {
Vector3 vec; Vector3 vec;
vec.x=value_editor[0]->get_text().to_double(); if (evaluator) {
vec.y=value_editor[1]->get_text().to_double(); vec.x=evaluator->eval(value_editor[0]->get_text());
vec.z=value_editor[2]->get_text().to_double(); vec.y=evaluator->eval(value_editor[1]->get_text());
vec.z=evaluator->eval(value_editor[2]->get_text());
} else {
vec.x=value_editor[0]->get_text().to_double();
vec.y=value_editor[1]->get_text().to_double();
vec.z=value_editor[2]->get_text().to_double();
}
v=vec; v=vec;
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1358,10 +1379,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::PLANE: { case Variant::PLANE: {
Plane pl; Plane pl;
pl.normal.x=value_editor[0]->get_text().to_double(); if (evaluator) {
pl.normal.y=value_editor[1]->get_text().to_double(); pl.normal.x=evaluator->eval(value_editor[0]->get_text());
pl.normal.z=value_editor[2]->get_text().to_double(); pl.normal.y=evaluator->eval(value_editor[1]->get_text());
pl.d=value_editor[3]->get_text().to_double(); pl.normal.z=evaluator->eval(value_editor[2]->get_text());
pl.d=evaluator->eval(value_editor[3]->get_text());
} else {
pl.normal.x=value_editor[0]->get_text().to_double();
pl.normal.y=value_editor[1]->get_text().to_double();
pl.normal.z=value_editor[2]->get_text().to_double();
pl.d=value_editor[3]->get_text().to_double();
}
v=pl; v=pl;
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1369,10 +1397,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::QUAT: { case Variant::QUAT: {
Quat q; Quat q;
q.x=value_editor[0]->get_text().to_double(); if (evaluator) {
q.y=value_editor[1]->get_text().to_double(); q.x=evaluator->eval(value_editor[0]->get_text());
q.z=value_editor[2]->get_text().to_double(); q.y=evaluator->eval(value_editor[1]->get_text());
q.w=value_editor[3]->get_text().to_double(); q.z=evaluator->eval(value_editor[2]->get_text());
q.w=evaluator->eval(value_editor[3]->get_text());
} else {
q.x=value_editor[0]->get_text().to_double();
q.y=value_editor[1]->get_text().to_double();
q.z=value_editor[2]->get_text().to_double();
q.w=value_editor[3]->get_text().to_double();
}
v=q; v=q;
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1380,14 +1415,23 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::_AABB: { case Variant::_AABB: {
Vector3 pos; Vector3 pos;
pos.x=value_editor[0]->get_text().to_double();
pos.y=value_editor[1]->get_text().to_double();
pos.z=value_editor[2]->get_text().to_double();
Vector3 size; Vector3 size;
size.x=value_editor[3]->get_text().to_double();
size.y=value_editor[4]->get_text().to_double();
size.z=value_editor[5]->get_text().to_double();
if (evaluator) {
pos.x=evaluator->eval(value_editor[0]->get_text());
pos.y=evaluator->eval(value_editor[1]->get_text());
pos.z=evaluator->eval(value_editor[2]->get_text());
size.x=evaluator->eval(value_editor[3]->get_text());
size.y=evaluator->eval(value_editor[4]->get_text());
size.z=evaluator->eval(value_editor[5]->get_text());
} else {
pos.x=value_editor[0]->get_text().to_double();
pos.y=value_editor[1]->get_text().to_double();
pos.z=value_editor[2]->get_text().to_double();
size.x=value_editor[3]->get_text().to_double();
size.y=value_editor[4]->get_text().to_double();
size.z=value_editor[5]->get_text().to_double();
}
v=AABB(pos,size); v=AABB(pos,size);
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1396,8 +1440,11 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix32 m; Matrix32 m;
for(int i=0;i<6;i++) { for(int i=0;i<6;i++) {
if (evaluator) {
m.elements[i/2][i%2]=value_editor[i]->get_text().to_double(); m.elements[i/2][i%2]=evaluator->eval(value_editor[i]->get_text());
} else {
m.elements[i/2][i%2]=value_editor[i]->get_text().to_double();
}
} }
v=m; v=m;
@ -1409,7 +1456,11 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix3 m; Matrix3 m;
for(int i=0;i<9;i++) { for(int i=0;i<9;i++) {
m.elements[i/3][i%3]=value_editor[i]->get_text().to_double(); if (evaluator) {
m.elements[i/3][i%3]=evaluator->eval(value_editor[i]->get_text());
} else {
m.elements[i/3][i%3]=value_editor[i]->get_text().to_double();
}
} }
v=m; v=m;
@ -1421,13 +1472,24 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix3 basis; Matrix3 basis;
for(int i=0;i<9;i++) { for(int i=0;i<9;i++) {
basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double(); if (evaluator) {
basis.elements[i/3][i%3]=evaluator->eval(value_editor[(i/3)*4+i%3]->get_text());
} else {
basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double();
}
} }
Vector3 origin; Vector3 origin;
origin.x=value_editor[3]->get_text().to_double();
origin.y=value_editor[7]->get_text().to_double(); if (evaluator) {
origin.z=value_editor[11]->get_text().to_double(); origin.x=evaluator->eval(value_editor[3]->get_text());
origin.y=evaluator->eval(value_editor[7]->get_text());
origin.z=evaluator->eval(value_editor[11]->get_text());
} else {
origin.x=value_editor[3]->get_text().to_double();
origin.y=value_editor[7]->get_text().to_double();
origin.z=value_editor[11]->get_text().to_double();
}
v=Transform(basis,origin); v=Transform(basis,origin);
emit_signal("variant_changed"); emit_signal("variant_changed");
@ -1736,6 +1798,8 @@ CustomPropertyEditor::CustomPropertyEditor() {
add_child(menu); add_child(menu);
menu->connect("item_pressed",this,"_menu_option"); menu->connect("item_pressed",this,"_menu_option");
evaluator = NULL;
spinbox = memnew ( SpinBox ); spinbox = memnew ( SpinBox );
add_child(spinbox); add_child(spinbox);
spinbox->set_area_as_parent_rect(5); spinbox->set_area_as_parent_rect(5);
@ -1750,7 +1814,7 @@ CustomPropertyEditor::CustomPropertyEditor() {
bool PropertyEditor::_might_be_in_instance() { bool PropertyEditor::_might_be_in_instance() {
if (!obj) if (!obj)
return NULL; return false;
Node *node = obj->cast_to<Node>(); Node *node = obj->cast_to<Node>();
@ -2695,8 +2759,11 @@ void PropertyEditor::update_tree() {
} }
if (p.hint==PROPERTY_HINT_ENUM)
item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE );
else
item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE_EXPRESSION );
item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE );
if (p.hint==PROPERTY_HINT_SPRITE_FRAME) { if (p.hint==PROPERTY_HINT_SPRITE_FRAME) {
item->set_range_config(1,0,99999,1); item->set_range_config(1,0,99999,1);
@ -3390,6 +3457,9 @@ void PropertyEditor::edit(Object* p_object) {
} }
obj=p_object; obj=p_object;
evaluator->edit(p_object);
update_tree(); update_tree();
if (obj) { if (obj) {
@ -3719,6 +3789,10 @@ PropertyEditor::PropertyEditor() {
custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED); custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED);
tree->set_hide_folding(true); tree->set_hide_folding(true);
evaluator = memnew (PropertyValueEvaluator);
tree->set_value_evaluator(evaluator);
custom_editor->set_value_evaluator(evaluator);
capitalize_paths=true; capitalize_paths=true;
autoclear=false; autoclear=false;
tree->set_column_titles_visible(false); tree->set_column_titles_visible(false);
@ -3737,6 +3811,7 @@ PropertyEditor::PropertyEditor() {
PropertyEditor::~PropertyEditor() PropertyEditor::~PropertyEditor()
{ {
memdelete(evaluator);
} }
@ -3975,3 +4050,51 @@ SectionedPropertyEditor::~SectionedPropertyEditor() {
memdelete(filter); memdelete(filter);
} }
double PropertyValueEvaluator::eval(const String& p_text) {
if (!obj)
return _default_eval(p_text);
Ref<Script> script= Ref<Script>(script_language ->create_script());
script->set_source_code(_build_script(p_text));
Error err = script->reload();
if (err) {
print_line("[PropertyValueEvaluator] Error loading script for expression: " + p_text);
return _default_eval(p_text);
}
ScriptInstance *script_instance = script->instance_create(this);
Variant::CallError call_err;
script_instance->call("set_this",obj);
double result = script_instance->call("e", NULL, 0, call_err );
if (call_err.error == Variant::CallError::CALL_OK) {
return result;
}
print_line("[PropertyValueEvaluator]: Error eval! Error code: " + itos(call_err.error));
memdelete(script_instance);
return _default_eval(p_text);
}
void PropertyValueEvaluator::edit(Object *p_obj) {
obj = p_obj;
}
String PropertyValueEvaluator::_build_script(const String& p_text) {
String script_text = "tool\nvar this\nfunc set_this(p_this):\n\tthis=p_this\nfunc e():\n\treturn ";
script_text += p_text.strip_edges();
script_text += "\n";
return script_text;
}
PropertyValueEvaluator::PropertyValueEvaluator() {
script_language = ScriptServer::get_language(0); // todo: get script language from editor setting
}
PropertyValueEvaluator::~PropertyValueEvaluator() {
}

View File

@ -46,6 +46,8 @@
@author Juan Linietsky <reduzio@gmail.com> @author Juan Linietsky <reduzio@gmail.com>
*/ */
class PropertyValueEvaluator;
class CustomPropertyEditor : public Popup { class CustomPropertyEditor : public Popup {
OBJ_TYPE( CustomPropertyEditor, Popup ); OBJ_TYPE( CustomPropertyEditor, Popup );
@ -104,6 +106,8 @@ class CustomPropertyEditor : public Popup {
bool updating; bool updating;
PropertyValueEvaluator *evaluator;
void _text_edit_changed(); void _text_edit_changed();
void _file_selected(String p_file); void _file_selected(String p_file);
void _scroll_modified(double p_value); void _scroll_modified(double p_value);
@ -137,6 +141,8 @@ public:
void set_read_only(bool p_read_only) { read_only=p_read_only; } void set_read_only(bool p_read_only) { read_only=p_read_only; }
void set_value_evaluator( PropertyValueEvaluator *p_evaluator) { evaluator=p_evaluator; }
bool edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text); bool edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text);
CustomPropertyEditor(); CustomPropertyEditor();
@ -151,6 +157,8 @@ class PropertyEditor : public Control {
//Object *object; //Object *object;
LineEdit *search_box; LineEdit *search_box;
PropertyValueEvaluator *evaluator;
Object* obj; Object* obj;
Array _prop_edited_name; Array _prop_edited_name;
@ -283,4 +291,24 @@ public:
~SectionedPropertyEditor(); ~SectionedPropertyEditor();
}; };
class PropertyValueEvaluator : public ValueEvaluator {
OBJ_TYPE( PropertyValueEvaluator, ValueEvaluator );
Object *obj;
ScriptLanguage *script_language;
String _build_script(const String& p_text);
_FORCE_INLINE_ double _default_eval(const String& p_text) {
return p_text.to_double();
}
public:
void edit(Object *p_obj);
double eval(const String& p_text);
PropertyValueEvaluator();
~PropertyValueEvaluator();
};
#endif #endif