diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 5136c96cbffa..9733f49f42fc 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -415,7 +415,11 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: menu->clear(); Vector options = hint_text.split(","); for (int i = 0; i < options.size(); i++) { - menu->add_item(options[i], i); + if (options[i].find(":") != -1) { + menu->add_item(options[i].get_slicec(':', 0), options[i].get_slicec(':', 1).to_int()); + } else { + menu->add_item(options[i], i); + } } menu->set_position(get_position()); menu->popup(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 29b9865b1d38..1e677f11c42b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3758,22 +3758,82 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint = PROPERTY_HINT_NONE; } - } else if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER) { + } else { - String identifier = tokenizer->get_token_identifier(); - if (!ClassDB::is_parent_class(identifier, "Resource")) { + parenthesis++; + Node *subexpr = _parse_and_reduce_expression(p_class, true, true); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } + return; + } + parenthesis--; + if (subexpr->type != Node::TYPE_CONSTANT) { current_export = PropertyInfo(); - _set_error("Export hint not a type or resource."); + _set_error("Expected a constant expression."); } - current_export.type = Variant::OBJECT; - current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; - current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + Variant constant = static_cast(subexpr)->value; - current_export.hint_string = identifier; + if (constant.get_type() == Variant::OBJECT) { + GDScriptNativeClass *native_class = Object::cast_to(constant); - tokenizer->advance(); + if (native_class && ClassDB::is_parent_class(native_class->get_name(), "Resource")) { + current_export.type = Variant::OBJECT; + current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; + current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + + current_export.hint_string = native_class->get_name(); + + } else { + current_export = PropertyInfo(); + _set_error("Export hint not a resource type."); + } + } else if (constant.get_type() == Variant::DICTIONARY) { + // Enumeration + bool is_flags = false; + + if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { + tokenizer->advance(); + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { + is_flags = true; + tokenizer->advance(); + } else { + current_export = PropertyInfo(); + _set_error("Expected 'FLAGS' after comma."); + } + } + + current_export.type = Variant::INT; + current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; + Dictionary enum_values = constant; + + List keys; + enum_values.get_key_list(&keys); + + bool first = true; + for (List::Element *E = keys.front(); E; E = E->next()) { + if (enum_values[E->get()].get_type() == Variant::INT) { + if (!first) + current_export.hint_string += ","; + else + first = false; + + current_export.hint_string += E->get().operator String().camelcase_to_underscore(true).capitalize().xml_escape(); + if (!is_flags) { + current_export.hint_string += ":"; + current_export.hint_string += enum_values[E->get()].operator String().xml_escape(); + } + } + } + } else { + current_export = PropertyInfo(); + _set_error("Expected type for export."); + return; + } } if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f2e5919b5f6a..749ff51f57cc 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "tree.h" +#include #include "os/input.h" #include "os/keyboard.h" @@ -154,8 +155,17 @@ void TreeItem::set_text(int p_column, String p_text) { 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].max = p_text.get_slice_count(","); + Vector strings = p_text.split(","); + cells[p_column].min = INT_MAX; + cells[p_column].max = INT_MIN; + for (int i = 0; i < strings.size(); i++) { + int value = i; + if (!strings[i].get_slicec(':', 1).empty()) { + value = strings[i].get_slicec(':', 1).to_int(); + } + cells[p_column].min = MIN(cells[p_column].min, value); + cells[p_column].max = MAX(cells[p_column].max, value); + } cells[p_column].step = 0; } _changed_notify(p_column); @@ -1231,8 +1241,18 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int option = (int)p_item->cells[i].val; - String s = p_item->cells[i].text; - s = s.get_slicec(',', option); + String s = RTR("(Other)"); + Vector strings = p_item->cells[i].text.split(","); + for (int i = 0; i < strings.size(); i++) { + int value = i; + if (!strings[i].get_slicec(':', 1).empty()) { + value = strings[i].get_slicec(':', 1).to_int(); + } + if (option == value) { + s = strings[i].get_slicec(':', 0); + break; + } + } if (p_item->cells[i].suffix != String()) s += " " + p_item->cells[i].suffix; @@ -1776,7 +1796,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool for (int i = 0; i < c.text.get_slice_count(","); i++) { String s = c.text.get_slicec(',', i); - popup_menu->add_item(s, i); + popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).empty() ? i : s.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(col_width, 0)); @@ -2634,7 +2654,7 @@ bool Tree::edit_selected() { for (int i = 0; i < c.text.get_slice_count(","); i++) { String s = c.text.get_slicec(',', i); - popup_menu->add_item(s, i); + popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).empty() ? i : s.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(rect.size.width, 0));