diff options
36 files changed, 2285 insertions, 838 deletions
diff --git a/core/color.cpp b/core/color.cpp index dd8b13c047..78b11a84df 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -250,6 +250,14 @@ Color Color::html(const String &p_color) { return Color(); if (color[0] == '#') color = color.substr(1, color.length() - 1); + if (color.length() == 3 || color.length() == 4) { + String exp_color; + for (int i = 0; i < color.length(); i++) { + exp_color += color[i]; + exp_color += color[i]; + } + color = exp_color; + } bool alpha = false; diff --git a/core/hash_map.h b/core/hash_map.h index 37391a4c83..e100d7a904 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -37,39 +37,6 @@ #include "os/memory.h" #include "ustring.h" -struct HashMapHasherDefault { - static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } - static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } - static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } - - static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); } - static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); } - static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_djb2_one_float(p_double); } - static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return p_int; } - static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return (uint32_t)p_int; } - static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return p_int; } - static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return (uint32_t)p_int; } - static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } - static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } - static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } - //static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); } -}; - -template <typename T> -struct HashMapComparatorDefault { - static bool compare(const T &p_lhs, const T &p_rhs) { - return p_lhs == p_rhs; - } - - bool compare(const float &p_lhs, const float &p_rhs) { - return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); - } - - bool compare(const double &p_lhs, const double &p_rhs) { - return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); - } -}; - /** * @class HashMap * @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/hashfuncs.h b/core/hashfuncs.h index 56d40f1dd7..2880cc451e 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -33,6 +33,7 @@ #include "math_defs.h" #include "math_funcs.h" #include "typedefs.h" +#include "ustring.h" /** * Hashing functions @@ -128,4 +129,37 @@ static inline uint64_t make_uint64_t(T p_in) { return _u._u64; } +struct HashMapHasherDefault { + static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } + static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); } + static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); } + + static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); } + static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); } + static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_djb2_one_float(p_double); } + static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; } + static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; } + static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; } + //static _FORCE_INLINE_ uint32_t hash(const void* p_ptr) { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); } +}; + +template <typename T> +struct HashMapComparatorDefault { + static bool compare(const T &p_lhs, const T &p_rhs) { + return p_lhs == p_rhs; + } + + bool compare(const float &p_lhs, const float &p_rhs) { + return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); + } + + bool compare(const double &p_lhs, const double &p_rhs) { + return (p_lhs == p_rhs) || (Math::is_nan(p_lhs) && Math::is_nan(p_rhs)); + } +}; + #endif diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 7132b6573e..2c587762e8 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -577,7 +577,7 @@ real_t CameraMatrix::get_fov() const { if ((matrix[8] == 0) && (matrix[9] == 0)) { return Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0; } else { - // our frustum is asymetrical need to calculate the left planes angle seperately.. + // our frustum is asymmetrical need to calculate the left planes angle separately.. Plane left_plane = Plane(matrix[3] + matrix[0], matrix[7] + matrix[4], matrix[11] + matrix[8], diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h new file mode 100644 index 0000000000..66a1e348a1 --- /dev/null +++ b/core/oa_hash_map.h @@ -0,0 +1,593 @@ +/*************************************************************************/ +/* oa_hash_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 OA_HASH_MAP_H +#define OA_HASH_MAP_H + +#include "hashfuncs.h" +#include "math_funcs.h" +#include "os/copymem.h" +#include "os/memory.h" + +// uncomment this to disable intial local storage. +#define OA_HASH_MAP_INITIAL_LOCAL_STORAGE + +/** + * This class implements a hash map datastructure that uses open addressing with + * local probing. + * + * It can give huge performance improvements over a chained HashMap because of + * the increased data locality. + * + * Because of that locality property it's important to not use "large" value + * types as the "TData" type. If TData values are too big it can cause more + * cache misses then chaining. If larger values are needed then storing those + * in a separate array and using pointers or indices to reference them is the + * better solution. + * + * This hash map also implements real-time incremental rehashing. + * + */ +template <class TKey, class TData, + uint16_t INITIAL_NUM_ELEMENTS = 64, + class Hasher = HashMapHasherDefault, + class Comparator = HashMapComparatorDefault<TKey> > +class OAHashMap { + +private: +#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + TData local_data[INITIAL_NUM_ELEMENTS]; + TKey local_keys[INITIAL_NUM_ELEMENTS]; + uint32_t local_hashes[INITIAL_NUM_ELEMENTS]; + uint8_t local_flags[INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0)]; +#endif + + struct { + TData *data; + TKey *keys; + uint32_t *hashes; + + // This is actually an array of bits, 4 bit pairs per octet. + // | ba ba ba ba | ba ba ba ba | .... + // + // if a is set it means that there is an element present. + // if b is set it means that an element was deleted. This is needed for + // the local probing to work without relocating any succeeding and + // colliding entries. + uint8_t *flags; + + uint32_t capacity; + } table, old_table; + + bool is_rehashing; + uint32_t rehash_position; + uint32_t rehash_amount; + + uint32_t elements; + + /* Methods */ + + // returns true if the value already existed, false if it's a new entry + bool _raw_set_with_hash(uint32_t p_hash, const TKey &p_key, const TData &p_data) { + for (int i = 0; i < table.capacity; i++) { + + int pos = (p_hash + i) % table.capacity; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset)); + bool is_deleted_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1)); + + if (is_filled_flag) { + if (table.hashes[pos] == p_hash && Comparator::compare(table.keys[pos], p_key)) { + table.data[pos] = p_data; + return true; + } + continue; + } + + table.keys[pos] = p_key; + table.data[pos] = p_data; + table.hashes[pos] = p_hash; + + table.flags[flags_pos] |= (1 << (2 * flags_pos_offset)); + table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset + 1)); + + return false; + } + return false; + } + +public: + _FORCE_INLINE_ uint32_t get_capacity() const { return table.capacity; } + _FORCE_INLINE_ uint32_t get_num_elements() const { return elements; } + + void set(const TKey &p_key, const TData &p_data) { + + uint32_t hash = Hasher::hash(p_key); + + // We don't progress the rehashing if the table just got resized + // to keep the cost of this function low. + if (is_rehashing) { + + // rehash progress + + for (int i = 0; i <= rehash_amount && rehash_position < old_table.capacity; rehash_position++) { + + int flags_pos = rehash_position / 4; + int flags_pos_offset = rehash_position % 4; + + bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + + if (is_filled_flag) { + _raw_set_with_hash(old_table.hashes[rehash_position], old_table.keys[rehash_position], old_table.data[rehash_position]); + + old_table.keys[rehash_position].~TKey(); + old_table.data[rehash_position].~TData(); + + memnew_placement(&old_table.keys[rehash_position], TKey); + memnew_placement(&old_table.data[rehash_position], TData); + + old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); + old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); + } + } + + if (rehash_position >= old_table.capacity) { + + // wohooo, we can get rid of the old table. + is_rehashing = false; + +#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + if (old_table.data == local_data) { + // Everything is local, so no cleanup :P + } else +#endif + { + memdelete_arr(old_table.data); + memdelete_arr(old_table.keys); + memdelete_arr(old_table.hashes); + memdelete_arr(old_table.flags); + } + } + } + + // Table is almost full, resize and start rehashing process. + if (elements >= table.capacity * 0.7) { + + old_table.capacity = table.capacity; + old_table.data = table.data; + old_table.flags = table.flags; + old_table.hashes = table.hashes; + old_table.keys = table.keys; + + table.capacity = old_table.capacity * 2; + + table.data = memnew_arr(TData, table.capacity); + table.flags = memnew_arr(uint8_t, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0)); + table.hashes = memnew_arr(uint32_t, table.capacity); + table.keys = memnew_arr(TKey, table.capacity); + + zeromem(table.flags, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0)); + + is_rehashing = true; + rehash_position = 0; + rehash_amount = (elements * 2) / (table.capacity * 0.7 - old_table.capacity); + } + + if (!_raw_set_with_hash(hash, p_key, p_data)) + elements++; + } + + /** + * returns true if the value was found, false otherwise. + * + * if r_data is not NULL then the value will be written to the object + * it points to. + */ + bool lookup(const TKey &p_key, TData *r_data) { + + uint32_t hash = Hasher::hash(p_key); + + bool check_old_table = is_rehashing; + bool check_new_table = true; + + // search for the key and return the value associated with it + // + // if we're rehashing we need to check both the old and the + // current table. If we find a value in the old table we still + // need to continue searching in the new table as it might have + // been added after + + TData *value = NULL; + + for (int i = 0; i < table.capacity; i++) { + + if (!check_new_table && !check_old_table) { + + break; + } + + // if we're rehashing check the old table + if (check_old_table && i < old_table.capacity) { + + int pos = (hash + i) % old_table.capacity; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + + if (is_filled_flag) { + // found our entry? + if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) { + value = &old_table.data[pos]; + check_old_table = false; + } + } else if (!is_deleted_flag) { + + // we hit an empty field here, we don't + // need to further check this old table + // because we know it's not in here. + + check_old_table = false; + } + } + + if (check_new_table) { + + int pos = (hash + i) % table.capacity; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + + if (is_filled_flag) { + // found our entry? + if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) { + if (r_data != NULL) + *r_data = table.data[pos]; + return true; + } + continue; + } else if (is_deleted_flag) { + continue; + } else if (value != NULL) { + + // We found a value in the old table + if (r_data != NULL) + *r_data = *value; + return true; + } else { + check_new_table = false; + } + } + } + + if (value != NULL) { + if (r_data != NULL) + *r_data = *value; + return true; + } + return false; + } + + _FORCE_INLINE_ bool has(const TKey &p_key) { + return lookup(p_key, NULL); + } + + void remove(const TKey &p_key) { + uint32_t hash = Hasher::hash(p_key); + + bool check_old_table = is_rehashing; + bool check_new_table = true; + + for (int i = 0; i < table.capacity; i++) { + + if (!check_new_table && !check_old_table) { + return; + } + + // if we're rehashing check the old table + if (check_old_table && i < old_table.capacity) { + + int pos = (hash + i) % old_table.capacity; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + + if (is_filled_flag) { + // found our entry? + if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) { + old_table.keys[pos].~TKey(); + old_table.data[pos].~TData(); + + memnew_placement(&old_table.keys[pos], TKey); + memnew_placement(&old_table.data[pos], TData); + + old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); + old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); + + elements--; + return; + } + } else if (!is_deleted_flag) { + + // we hit an empty field here, we don't + // need to further check this old table + // because we know it's not in here. + + check_old_table = false; + } + } + + if (check_new_table) { + + int pos = (hash + i) % table.capacity; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0; + + if (is_filled_flag) { + // found our entry? + if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) { + table.keys[pos].~TKey(); + table.data[pos].~TData(); + + memnew_placement(&table.keys[pos], TKey); + memnew_placement(&table.data[pos], TData); + + table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset)); + table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1)); + + // don't return here, this value might still be in the old table + // if it was already relocated. + + elements--; + return; + } + continue; + } else if (is_deleted_flag) { + continue; + } else { + check_new_table = false; + } + } + } + } + + struct Iterator { + bool valid; + + uint32_t hash; + + const TKey *key; + const TData *data; + + private: + friend class OAHashMap; + bool was_from_old_table; + }; + + Iterator iter() const { + Iterator it; + + it.valid = false; + it.was_from_old_table = false; + + bool check_old_table = is_rehashing; + + for (int i = 0; i < table.capacity; i++) { + + // if we're rehashing check the old table first + if (check_old_table && i < old_table.capacity) { + + int pos = i; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + + if (is_filled_flag) { + it.valid = true; + it.hash = old_table.hashes[pos]; + it.data = &old_table.data[pos]; + it.key = &old_table.keys[pos]; + + it.was_from_old_table = true; + + return it; + } + } + + { + + int pos = i; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + + if (is_filled_flag) { + it.valid = true; + it.hash = table.hashes[pos]; + it.data = &table.data[pos]; + it.key = &table.keys[pos]; + + return it; + } + } + } + + return it; + } + + Iterator next_iter(const Iterator &p_iter) const { + if (!p_iter.valid) { + return p_iter; + } + + Iterator it; + + it.valid = false; + it.was_from_old_table = false; + + bool check_old_table = is_rehashing; + + // we use this to skip the first check or not + bool was_from_old_table = p_iter.was_from_old_table; + + int prev_index = (p_iter.data - (p_iter.was_from_old_table ? old_table.data : table.data)); + + if (!was_from_old_table) { + prev_index++; + } + + for (int i = prev_index; i < table.capacity; i++) { + + // if we're rehashing check the old table first + if (check_old_table && i < old_table.capacity && !was_from_old_table) { + + int pos = i; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + + if (is_filled_flag) { + it.valid = true; + it.hash = old_table.hashes[pos]; + it.data = &old_table.data[pos]; + it.key = &old_table.keys[pos]; + + it.was_from_old_table = true; + + return it; + } + } + + was_from_old_table = false; + + { + int pos = i; + + int flags_pos = pos / 4; + int flags_pos_offset = pos % 4; + + bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0; + + if (is_filled_flag) { + it.valid = true; + it.hash = table.hashes[pos]; + it.data = &table.data[pos]; + it.key = &table.keys[pos]; + + return it; + } + } + } + + return it; + } + + OAHashMap(uint32_t p_initial_capacity = INITIAL_NUM_ELEMENTS) { + +#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + + if (p_initial_capacity <= INITIAL_NUM_ELEMENTS) { + table.data = local_data; + table.keys = local_keys; + table.hashes = local_hashes; + table.flags = local_flags; + + zeromem(table.flags, INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0)); + + table.capacity = INITIAL_NUM_ELEMENTS; + elements = 0; + } else +#endif + { + table.data = memnew_arr(TData, p_initial_capacity); + table.keys = memnew_arr(TKey, p_initial_capacity); + table.hashes = memnew_arr(uint32_t, p_initial_capacity); + table.flags = memnew_arr(uint8_t, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0)); + + zeromem(table.flags, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0)); + + table.capacity = p_initial_capacity; + elements = 0; + } + + is_rehashing = false; + rehash_position = 0; + } + + ~OAHashMap() { +#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + if (table.capacity <= INITIAL_NUM_ELEMENTS) { + return; // Everything is local, so no cleanup :P + } +#endif + if (is_rehashing) { + +#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE + if (old_table.data == local_data) { + // Everything is local, so no cleanup :P + } else +#endif + { + memdelete_arr(old_table.data); + memdelete_arr(old_table.keys); + memdelete_arr(old_table.hashes); + memdelete_arr(old_table.flags); + } + } + + memdelete_arr(table.data); + memdelete_arr(table.keys); + memdelete_arr(table.hashes); + memdelete_arr(table.flags); + } +}; + +#endif diff --git a/core/os/file_access.h b/core/os/file_access.h index 151c41c263..63692cb290 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -140,7 +140,7 @@ public: virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType - virtual Error _chmod(const String &p_path, int p_mod) {} + virtual Error _chmod(const String &p_path, int p_mod) { return FAILED; } static FileAccess *create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files. static FileAccess *create_for_path(const String &p_path); diff --git a/core/variant.cpp b/core/variant.cpp index 10d86152ee..52bdd4e22d 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -903,9 +903,6 @@ bool Variant::is_one() const { void Variant::reference(const Variant &p_variant) { - if (this == &p_variant) - return; - clear(); type = p_variant.type; @@ -924,17 +921,14 @@ void Variant::reference(const Variant &p_variant) { case INT: { _data._int = p_variant._data._int; - } break; case REAL: { _data._real = p_variant._data._real; - } break; case STRING: { memnew_placement(_data._mem, String(*reinterpret_cast<const String *>(p_variant._data._mem))); - } break; // math types @@ -942,33 +936,24 @@ void Variant::reference(const Variant &p_variant) { case VECTOR2: { memnew_placement(_data._mem, Vector2(*reinterpret_cast<const Vector2 *>(p_variant._data._mem))); - } break; case RECT2: { memnew_placement(_data._mem, Rect2(*reinterpret_cast<const Rect2 *>(p_variant._data._mem))); - } break; case TRANSFORM2D: { _data._transform2d = memnew(Transform2D(*p_variant._data._transform2d)); - } break; case VECTOR3: { memnew_placement(_data._mem, Vector3(*reinterpret_cast<const Vector3 *>(p_variant._data._mem))); - } break; case PLANE: { memnew_placement(_data._mem, Plane(*reinterpret_cast<const Plane *>(p_variant._data._mem))); - } break; - /* - case QUAT: { - - } break;*/ case RECT3: { _data._rect3 = memnew(Rect3(*p_variant._data._rect3)); @@ -986,7 +971,6 @@ void Variant::reference(const Variant &p_variant) { case TRANSFORM: { _data._transform = memnew(Transform(*p_variant._data._transform)); - } break; // misc types @@ -1058,6 +1042,7 @@ void Variant::reference(const Variant &p_variant) { default: {} } } + void Variant::zero() { switch (type) { case NIL: break; @@ -1073,6 +1058,7 @@ void Variant::zero() { default: this->clear(); break; } } + void Variant::clear() { switch (type) { @@ -1092,12 +1078,10 @@ void Variant::clear() { case TRANSFORM2D: { memdelete(_data._transform2d); - } break; case RECT3: { memdelete(_data._rect3); - } break; case BASIS: { @@ -1106,14 +1090,12 @@ void Variant::clear() { case TRANSFORM: { memdelete(_data._transform); - } break; // misc types case NODE_PATH: { reinterpret_cast<NodePath *>(_data._mem)->~NodePath(); - } break; case OBJECT: { @@ -1127,48 +1109,39 @@ void Variant::clear() { case DICTIONARY: { reinterpret_cast<Dictionary *>(_data._mem)->~Dictionary(); - } break; case ARRAY: { reinterpret_cast<Array *>(_data._mem)->~Array(); - } break; // arrays case POOL_BYTE_ARRAY: { reinterpret_cast<PoolVector<uint8_t> *>(_data._mem)->~PoolVector<uint8_t>(); - } break; case POOL_INT_ARRAY: { reinterpret_cast<PoolVector<int> *>(_data._mem)->~PoolVector<int>(); - } break; case POOL_REAL_ARRAY: { reinterpret_cast<PoolVector<real_t> *>(_data._mem)->~PoolVector<real_t>(); - } break; case POOL_STRING_ARRAY: { reinterpret_cast<PoolVector<String> *>(_data._mem)->~PoolVector<String>(); - } break; case POOL_VECTOR2_ARRAY: { reinterpret_cast<PoolVector<Vector2> *>(_data._mem)->~PoolVector<Vector2>(); - } break; case POOL_VECTOR3_ARRAY: { reinterpret_cast<PoolVector<Vector3> *>(_data._mem)->~PoolVector<Vector3>(); - } break; case POOL_COLOR_ARRAY: { reinterpret_cast<PoolVector<Color> *>(_data._mem)->~PoolVector<Color>(); - } break; default: {} /* not needed */ } @@ -2496,7 +2469,135 @@ Variant::Variant(const Vector<Color> &p_array) { void Variant::operator=(const Variant &p_variant) { - reference(p_variant); + if (this == &p_variant) + return; + + if (type != p_variant.type) { + reference(p_variant); + return; + } + + switch (p_variant.type) { + case NIL: { + + // none + } break; + + // atomic types + case BOOL: { + + _data._bool = p_variant._data._bool; + } break; + case INT: { + + _data._int = p_variant._data._int; + } break; + case REAL: { + + _data._real = p_variant._data._real; + } break; + case STRING: { + + *reinterpret_cast<String *>(_data._mem) = *reinterpret_cast<const String *>(p_variant._data._mem); + } break; + + // math types + + case VECTOR2: { + + *reinterpret_cast<Vector2 *>(_data._mem) = *reinterpret_cast<const Vector2 *>(p_variant._data._mem); + } break; + case RECT2: { + + *reinterpret_cast<Rect2 *>(_data._mem) = *reinterpret_cast<const Rect2 *>(p_variant._data._mem); + } break; + case TRANSFORM2D: { + + *_data._transform2d = *(p_variant._data._transform2d); + } break; + case VECTOR3: { + + *reinterpret_cast<Vector3 *>(_data._mem) = *reinterpret_cast<const Vector3 *>(p_variant._data._mem); + } break; + case PLANE: { + + *reinterpret_cast<Plane *>(_data._mem) = *reinterpret_cast<const Plane *>(p_variant._data._mem); + } break; + + case RECT3: { + + *_data._rect3 = *(p_variant._data._rect3); + } break; + case QUAT: { + + *reinterpret_cast<Quat *>(_data._mem) = *reinterpret_cast<const Quat *>(p_variant._data._mem); + } break; + case BASIS: { + + *_data._basis = *(p_variant._data._basis); + } break; + case TRANSFORM: { + + *_data._transform = *(p_variant._data._transform); + } break; + + // misc types + case COLOR: { + + *reinterpret_cast<Color *>(_data._mem) = *reinterpret_cast<const Color *>(p_variant._data._mem); + } break; + case _RID: { + + *reinterpret_cast<RID *>(_data._mem) = *reinterpret_cast<const RID *>(p_variant._data._mem); + } break; + case OBJECT: { + + *reinterpret_cast<ObjData *>(_data._mem) = p_variant._get_obj(); + } break; + case NODE_PATH: { + + *reinterpret_cast<NodePath *>(_data._mem) = *reinterpret_cast<const NodePath *>(p_variant._data._mem); + } break; + case DICTIONARY: { + + *reinterpret_cast<Dictionary *>(_data._mem) = *reinterpret_cast<const Dictionary *>(p_variant._data._mem); + } break; + case ARRAY: { + + *reinterpret_cast<Array *>(_data._mem) = *reinterpret_cast<const Array *>(p_variant._data._mem); + } break; + + // arrays + case POOL_BYTE_ARRAY: { + + *reinterpret_cast<PoolVector<uint8_t> *>(_data._mem) = *reinterpret_cast<const PoolVector<uint8_t> *>(p_variant._data._mem); + } break; + case POOL_INT_ARRAY: { + + *reinterpret_cast<PoolVector<int> *>(_data._mem) = *reinterpret_cast<const PoolVector<int> *>(p_variant._data._mem); + } break; + case POOL_REAL_ARRAY: { + + *reinterpret_cast<PoolVector<real_t> *>(_data._mem) = *reinterpret_cast<const PoolVector<real_t> *>(p_variant._data._mem); + } break; + case POOL_STRING_ARRAY: { + + *reinterpret_cast<PoolVector<String> *>(_data._mem) = *reinterpret_cast<const PoolVector<String> *>(p_variant._data._mem); + } break; + case POOL_VECTOR2_ARRAY: { + + *reinterpret_cast<PoolVector<Vector2> *>(_data._mem) = *reinterpret_cast<const PoolVector<Vector2> *>(p_variant._data._mem); + } break; + case POOL_VECTOR3_ARRAY: { + + *reinterpret_cast<PoolVector<Vector3> *>(_data._mem) = *reinterpret_cast<const PoolVector<Vector3> *>(p_variant._data._mem); + } break; + case POOL_COLOR_ARRAY: { + + *reinterpret_cast<PoolVector<Color> *>(_data._mem) = *reinterpret_cast<const PoolVector<Color> *>(p_variant._data._mem); + } break; + default: {} + } } Variant::Variant(const IP_Address &p_address) { diff --git a/core/variant.h b/core/variant.h index edc86dedd4..5ea540a63f 100644 --- a/core/variant.h +++ b/core/variant.h @@ -390,7 +390,7 @@ public: uint32_t hash() const; bool hash_compare(const Variant &p_variant) const; - bool booleanize(bool &valid) const; + bool booleanize() const; void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index d67466556d..5d47936679 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -143,56 +143,13 @@ Variant::operator bool() const { - bool b; - return booleanize(b); + return booleanize(); } -bool Variant::booleanize(bool &r_valid) const { - - r_valid = true; - switch (type) { - case NIL: - return false; - case BOOL: - return _data._bool; - case INT: - return _data._int; - case REAL: - return _data._real; - case STRING: - return (*reinterpret_cast<const String *>(_data._mem)) != ""; - case VECTOR2: - case RECT2: - case TRANSFORM2D: - case VECTOR3: - case PLANE: - case RECT3: - case QUAT: - case BASIS: - case TRANSFORM: - case COLOR: - case _RID: - return (*reinterpret_cast<const RID *>(_data._mem)).is_valid(); - case OBJECT: - return _get_obj().obj; - case NODE_PATH: - return (*reinterpret_cast<const NodePath *>(_data._mem)) != NodePath(); - case DICTIONARY: - case ARRAY: - case POOL_BYTE_ARRAY: - case POOL_INT_ARRAY: - case POOL_REAL_ARRAY: - case POOL_STRING_ARRAY: - case POOL_VECTOR2_ARRAY: - case POOL_VECTOR3_ARRAY: - case POOL_COLOR_ARRAY: - r_valid = false; - return false; - default: { - } - } - - return false; +// We consider all unitialized or empty types to be false based on the type's +// zeroiness. +bool Variant::booleanize() const { + return !is_zero(); } #define _RETURN(m_what) \ @@ -403,12 +360,6 @@ bool Variant::booleanize(bool &r_valid) const { _RETURN(sum); \ } -#define DEFAULT_OP_FAIL(m_name) \ - case m_name: { \ - r_valid = false; \ - return; \ - } - void Variant::evaluate(const Operator &p_op, const Variant &p_a, const Variant &p_b, Variant &r_ret, bool &r_valid) { @@ -421,11 +372,12 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, if (p_b.type == NIL) _RETURN(true); if (p_b.type == OBJECT) _RETURN(p_b._get_obj().obj == NULL); - _RETURN(false); + _RETURN_FAIL; } CASE_TYPE(math, OP_EQUAL, BOOL) { - if (p_b.type != BOOL) _RETURN(false); + if (p_b.type != BOOL) + _RETURN_FAIL; _RETURN(p_a._data._bool == p_b._data._bool); } @@ -434,11 +386,12 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN((p_a._get_obj().obj == p_b._get_obj().obj)); if (p_b.type == NIL) _RETURN(p_a._get_obj().obj == NULL); + _RETURN_FAIL; } CASE_TYPE(math, OP_EQUAL, DICTIONARY) { if (p_b.type != DICTIONARY) - _RETURN(false); + _RETURN_FAIL; const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); @@ -448,7 +401,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, CASE_TYPE(math, OP_EQUAL, ARRAY) { if (p_b.type != ARRAY) - _RETURN(false); + _RETURN_FAIL; const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); @@ -495,11 +448,12 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, if (p_b.type == NIL) _RETURN(false); if (p_b.type == OBJECT) _RETURN(p_b._get_obj().obj != NULL); - _RETURN(true); + _RETURN_FAIL; } CASE_TYPE(math, OP_NOT_EQUAL, BOOL) { - if (p_b.type != BOOL) _RETURN(true); + if (p_b.type != BOOL) + _RETURN_FAIL; _RETURN(p_a._data._bool != p_b._data._bool); } @@ -508,11 +462,12 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN((p_a._get_obj().obj != p_b._get_obj().obj)); if (p_b.type == NIL) _RETURN(p_a._get_obj().obj != NULL); + _RETURN_FAIL; } CASE_TYPE(math, OP_NOT_EQUAL, DICTIONARY) { if (p_b.type != DICTIONARY) - _RETURN(true); + _RETURN_FAIL; const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); @@ -522,7 +477,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, CASE_TYPE(math, OP_NOT_EQUAL, ARRAY) { if (p_b.type != ARRAY) - _RETURN(true); + _RETURN_FAIL; const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); @@ -580,13 +535,14 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, } CASE_TYPE(math, OP_LESS, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj < p_b._get_obj().obj)); + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN((p_a._get_obj().obj < p_b._get_obj().obj)); } CASE_TYPE(math, OP_LESS, ARRAY) { if (p_b.type != ARRAY) - _RETURN(false); + _RETURN_FAIL; const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); @@ -633,8 +589,9 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_LESS_EQUAL, p_a.type) { CASE_TYPE(math, OP_LESS_EQUAL, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj <= p_b._get_obj().obj)); + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN((p_a._get_obj().obj <= p_b._get_obj().obj)); } DEFAULT_OP_NUM(math, OP_LESS_EQUAL, INT, <=, _int); @@ -682,13 +639,14 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, } CASE_TYPE(math, OP_GREATER, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj > p_b._get_obj().obj)); + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN((p_a._get_obj().obj > p_b._get_obj().obj)); } CASE_TYPE(math, OP_GREATER, ARRAY) { if (p_b.type != ARRAY) - _RETURN(false); + _RETURN_FAIL; const Array *arr_a = reinterpret_cast<const Array *>(p_a._data._mem); const Array *arr_b = reinterpret_cast<const Array *>(p_b._data._mem); @@ -735,8 +693,9 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_GREATER_EQUAL, p_a.type) { CASE_TYPE(math, OP_GREATER_EQUAL, OBJECT) { - if (p_b.type == OBJECT) - _RETURN((p_a._get_obj().obj >= p_b._get_obj().obj)); + if (p_b.type != OBJECT) + _RETURN_FAIL; + _RETURN((p_a._get_obj().obj >= p_b._get_obj().obj)); } DEFAULT_OP_NUM(math, OP_GREATER_EQUAL, INT, >=, _int); @@ -771,10 +730,9 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_ADD, p_a.type) { CASE_TYPE(math, OP_ADD, ARRAY) { - if (p_a.type != p_b.type) { - r_valid = false; - return; - } + if (p_a.type != p_b.type) + _RETURN_FAIL; + const Array &array_a = *reinterpret_cast<const Array *>(p_a._data._mem); const Array &array_b = *reinterpret_cast<const Array *>(p_b._data._mem); Array sum; @@ -853,65 +811,54 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_MULTIPLY, p_a.type) { CASE_TYPE(math, OP_MULTIPLY, TRANSFORM2D) { - if (p_b.type == TRANSFORM2D) { - _RETURN(*p_a._data._transform2d * *p_b._data._transform2d); - }; - if (p_b.type == VECTOR2) { - _RETURN(p_a._data._transform2d->xform(*(const Vector2 *)p_b._data._mem)); - }; - r_valid = false; - return; + switch (p_b.type) { + case TRANSFORM2D: { + _RETURN(*p_a._data._transform2d * *p_b._data._transform2d); + } + case VECTOR2: { + _RETURN(p_a._data._transform2d->xform(*(const Vector2 *)p_b._data._mem)); + } + default: _RETURN_FAIL; + } } CASE_TYPE(math, OP_MULTIPLY, QUAT) { switch (p_b.type) { case VECTOR3: { - _RETURN(reinterpret_cast<const Quat *>(p_a._data._mem)->xform(*(const Vector3 *)p_b._data._mem)); - } break; + } case QUAT: { - _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) * *reinterpret_cast<const Quat *>(p_b._data._mem)); - } break; + } case REAL: { _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) * p_b._data._real); - } break; - default: {} - }; - r_valid = false; - return; + } + default: _RETURN_FAIL; + } } CASE_TYPE(math, OP_MULTIPLY, BASIS) { switch (p_b.type) { case VECTOR3: { - _RETURN(p_a._data._basis->xform(*(const Vector3 *)p_b._data._mem)); - }; + } case BASIS: { - _RETURN(*p_a._data._basis * *p_b._data._basis); - }; - default: {} - }; - r_valid = false; - return; + } + default: _RETURN_FAIL; + } } CASE_TYPE(math, OP_MULTIPLY, TRANSFORM) { switch (p_b.type) { case VECTOR3: { - _RETURN(p_a._data._transform->xform(*(const Vector3 *)p_b._data._mem)); - }; + } case TRANSFORM: { - _RETURN(*p_a._data._transform * *p_b._data._transform); - }; - default: {} - }; - r_valid = false; - return; + } + default: _RETURN_FAIL; + } } DEFAULT_OP_NUM_VEC(math, OP_MULTIPLY, INT, *, _int); @@ -943,18 +890,15 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_DIVIDE, p_a.type) { CASE_TYPE(math, OP_DIVIDE, QUAT) { - if (p_b.type != REAL) { - r_valid = false; - return; - } + if (p_b.type != REAL) + _RETURN_FAIL; #ifdef DEBUG_ENABLED if (p_b._data._real == 0) { r_valid = false; _RETURN("Division By Zero"); } #endif - _RETURN( - *reinterpret_cast<const Quat *>(p_a._data._mem) / p_b._data._real); + _RETURN(*reinterpret_cast<const Quat *>(p_a._data._mem) / p_b._data._real); } DEFAULT_OP_NUM_DIV(math, OP_DIVIDE, INT, _int); @@ -1054,9 +998,8 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_MODULE, p_a.type) { CASE_TYPE(math, OP_MODULE, INT) { - if (p_b.type != INT) { + if (p_b.type != INT) _RETURN_FAIL; - } #ifdef DEBUG_ENABLED if (p_b._data._int == 0) { r_valid = false; @@ -1067,15 +1010,13 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, } CASE_TYPE(math, OP_MODULE, STRING) { - const String *format = - reinterpret_cast<const String *>(p_a._data._mem); + const String *format = reinterpret_cast<const String *>(p_a._data._mem); String result; bool error; if (p_b.type == ARRAY) { // e.g. "frog %s %d" % ["fish", 12] - const Array *args = - reinterpret_cast<const Array *>(p_b._data._mem); + const Array *args = reinterpret_cast<const Array *>(p_b._data._mem); result = format->sprintf(*args, &error); } else { // e.g. "frog %d" % 12 @@ -1127,6 +1068,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN_FAIL; _RETURN(p_a._data._int << p_b._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_LEFT) _RETURN_FAIL; } @@ -1137,6 +1079,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN_FAIL; _RETURN(p_a._data._int >> p_b._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_SHIFT_RIGHT) _RETURN_FAIL; } @@ -1147,6 +1090,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN_FAIL; _RETURN(p_a._data._int & p_b._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_AND) _RETURN_FAIL; } @@ -1157,6 +1101,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN_FAIL; _RETURN(p_a._data._int | p_b._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_OR) _RETURN_FAIL; } @@ -1167,6 +1112,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, _RETURN_FAIL; _RETURN(p_a._data._int ^ p_b._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_XOR) _RETURN_FAIL; } @@ -1175,18 +1121,15 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, CASE_TYPE(math, OP_BIT_NEGATE, INT) { _RETURN(~p_a._data._int); } + CASE_TYPE_ALL_BUT_INT(math, OP_BIT_NEGATE) _RETURN_FAIL; } SWITCH_OP(math, OP_AND, p_a.type) { CASE_TYPE_ALL(math, OP_AND) { - bool l = p_a.booleanize(r_valid); - if (!r_valid) - return; - bool r = p_b.booleanize(r_valid); - if (!r_valid) - return; + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); _RETURN(l && r); } @@ -1194,12 +1137,8 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_OR, p_a.type) { CASE_TYPE_ALL(math, OP_OR) { - bool l = p_a.booleanize(r_valid); - if (!r_valid) - return; - bool r = p_b.booleanize(r_valid); - if (!r_valid) - return; + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); _RETURN(l || r); } @@ -1207,12 +1146,8 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_XOR, p_a.type) { CASE_TYPE_ALL(math, OP_XOR) { - bool l = p_a.booleanize(r_valid); - if (!r_valid) - return; - bool r = p_b.booleanize(r_valid); - if (!r_valid) - return; + bool l = p_a.booleanize(); + bool r = p_b.booleanize(); _RETURN((l || r) && !(l && r)); } @@ -1220,9 +1155,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, SWITCH_OP(math, OP_NOT, p_a.type) { CASE_TYPE_ALL(math, OP_NOT) { - bool l = p_a.booleanize(r_valid); - if (!r_valid) - return; + bool l = p_a.booleanize(); _RETURN(!l); } } @@ -2676,7 +2609,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const { return true; } break; case VECTOR2: { - int64_t to = reinterpret_cast<const Vector3 *>(_data._mem)->y; + int64_t to = reinterpret_cast<const Vector2 *>(_data._mem)->y; int64_t idx = r_iter; idx++; diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index d806b825ba..345c7b1f65 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -137,6 +137,9 @@ void editor_register_fonts(Ref<Theme> p_theme) { p_theme->set_font("doc", "EditorFonts", df_doc); p_theme->set_font("doc_title", "EditorFonts", df_title); + MAKE_DEFAULT_FONT(df_rulers, int(EDITOR_DEF("canvas_item_editor/rulers", 8)) * EDSCALE); + p_theme->set_font("rulers", "EditorFonts", df_rulers); + Ref<DynamicFont> df_code; df_code.instance(); df_code->set_size(int(EditorSettings::get_singleton()->get("interface/source_font_size")) * EDSCALE); diff --git a/editor/icons/icon_snap.svg b/editor/icons/icon_snap.svg index b1f36cfe43..0195f7cfc6 100644 --- a/editor/icons/icon_snap.svg +++ b/editor/icons/icon_snap.svg @@ -1,10 +1,93 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path d="m3 1036.4v3h-3v2h3v4h-3v2h3v3h2v-3-2-4h4 2 3v-2h-3v-3h-2v3h-4v-3h-2z" fill="#f3f3f3"/> -<path d="m11 1043.4a4 4 0 0 0 -4 4h2a2 2 0 0 1 2 -2 2 2 0 0 1 2 2h2a4 4 0 0 0 -4 -4z" fill="#ff8484"/> -<rect x="7" y="1047.4" width="2" height="2" fill="#ff8484"/> -<rect x="13" y="1047.4" width="2" height="2" fill="#ff8484"/> -<rect x="7" y="1049.4" width="2" height="2" fill="#fff"/> -<rect x="13" y="1049.4" width="2" height="2" fill="#fff"/> -</g> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg848" + sodipodi:docname="icon_snap.svg" + inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + <metadata + id="metadata854"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs852" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2558" + inkscape:window-height="1422" + id="namedview850" + showgrid="true" + inkscape:pagecheckerboard="true" + inkscape:zoom="26.317631" + inkscape:cx="-14.078109" + inkscape:cy="-1.3201296" + inkscape:window-x="1366" + inkscape:window-y="16" + inkscape:window-maximized="1" + inkscape:current-layer="svg848"> + <inkscape:grid + type="xygrid" + id="grid862" /> + </sodipodi:namedview> + <path + style="fill:#ff8484;stroke-width:1.48039925" + inkscape:connector-curvature="0" + id="path836" + d="m 3.9501321,14.20668 a 5.9215969,5.9215969 0 0 0 8.0890509,-2.167455 l -2.5641252,-1.4804 A 2.9607985,2.9607985 0 0 1 5.4305313,11.642553 2.9607985,2.9607985 0 0 1 4.3468039,7.5980268 L 1.7826772,6.1176278 A 5.9215969,5.9215969 0 0 0 3.9501321,14.20668 Z" /> + <rect + style="fill:#ff8484;stroke-width:1.48039925" + id="rect838" + height="2.9607985" + width="2.9607985" + y="-4.4066463" + x="-16.44585" + transform="rotate(-150)" /> + <rect + style="fill:#ff8484;stroke-width:1.48039925" + id="rect840" + height="2.9607985" + width="2.9607985" + y="-4.4066463" + x="-7.5634551" + transform="rotate(-150)" /> + <rect + style="fill:#ffffff;stroke-width:1.48039925" + id="rect842" + height="2.9607985" + width="2.9607985" + y="-1.4458472" + x="-16.44585" + transform="rotate(-150)" /> + <rect + style="fill:#ffffff;stroke-width:1.48039925" + id="rect844" + height="2.9607985" + width="2.9607985" + y="-1.4458472" + x="-7.5634551" + transform="rotate(-150)" /> </svg> diff --git a/editor/icons/icon_snap_grid.svg b/editor/icons/icon_snap_grid.svg new file mode 100644 index 0000000000..b479496327 --- /dev/null +++ b/editor/icons/icon_snap_grid.svg @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg913" + sodipodi:docname="icon_snap_grid.svg" + inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + <metadata + id="metadata919"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs917" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2558" + inkscape:window-height="1422" + id="namedview915" + showgrid="false" + inkscape:pagecheckerboard="true" + inkscape:zoom="52.635261" + inkscape:cx="6.4374987" + inkscape:cy="5.7074131" + inkscape:window-x="1366" + inkscape:window-y="16" + inkscape:window-maximized="1" + inkscape:current-layer="svg913" /> + <g + transform="translate(0 -1036.4)" + id="g911"> + <path + d="m3 1036.4v3h-3v2h3v4h-3v2h3v3h2v-3-2-4h4 2 3v-2h-3v-3h-2v3h-4v-3h-2z" + fill="#f3f3f3" + id="path899" /> + <path + d="m11 1043.4a4 4 0 0 0 -4 4h2a2 2 0 0 1 2 -2 2 2 0 0 1 2 2h2a4 4 0 0 0 -4 -4z" + fill="#ff8484" + id="path901" /> + <rect + x="7" + y="1047.4" + width="2" + height="2" + fill="#ff8484" + id="rect903" /> + <rect + x="13" + y="1047.4" + width="2" + height="2" + fill="#ff8484" + id="rect905" /> + <rect + x="7" + y="1049.4" + width="2" + height="2" + fill="#fff" + id="rect907" /> + <rect + x="13" + y="1049.4" + width="2" + height="2" + fill="#fff" + id="rect909" /> + </g> +</svg> diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index cc519c1c4b..b448ab8920 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -120,13 +120,13 @@ String ResourceImporterScene::get_preset_name(int p_idx) const { switch (p_idx) { case PRESET_SINGLE_SCENE: return TTR("Import as Single Scene"); - case PRESET_SEPERATE_ANIMATIONS: return TTR("Import with Seperate Animations"); + case PRESET_SEPARATE_ANIMATIONS: return TTR("Import with Separate Animations"); case PRESET_SEPARATE_MATERIALS: return TTR("Import with Separate Materials"); case PRESET_SEPARATE_MESHES: return TTR("Import with Separate Objects"); case PRESET_SEPARATE_MESHES_AND_MATERIALS: return TTR("Import with Separate Objects+Materials"); - case PRESET_SEPARATE_MESHES_AND_ANIMATIONS: return TTR("Import with Seperate Objects+Animations"); - case PRESET_SEPERATE_MATERIALS_AND_ANIMATIONS: return TTR("Import with Seperate Materials+Animations"); - case PRESET_SEPERATE_MESHES_MATERIALS_AND_ANIMATIONS: return TTR("Import with Seperate Objects+Materials+Animations"); + case PRESET_SEPARATE_MESHES_AND_ANIMATIONS: return TTR("Import with Separate Objects+Animations"); + case PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS: return TTR("Import with Separate Materials+Animations"); + case PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS: return TTR("Import with Separate Objects+Materials+Animations"); case PRESET_MULTIPLE_SCENES: return TTR("Import as Multiple Scenes"); case PRESET_MULTIPLE_SCENES_AND_MATERIALS: return TTR("Import as Multiple Scenes+Materials"); } @@ -954,10 +954,10 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in script_ext_hint += "*." + E->get(); } - bool materials_out = p_preset == PRESET_SEPARATE_MATERIALS || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS || p_preset == PRESET_SEPERATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPERATE_MESHES_MATERIALS_AND_ANIMATIONS; - bool meshes_out = p_preset == PRESET_SEPARATE_MESHES || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || PRESET_SEPARATE_MESHES_AND_ANIMATIONS || PRESET_SEPERATE_MESHES_MATERIALS_AND_ANIMATIONS; + bool materials_out = p_preset == PRESET_SEPARATE_MATERIALS || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS || p_preset == PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS; + bool meshes_out = p_preset == PRESET_SEPARATE_MESHES || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || PRESET_SEPARATE_MESHES_AND_ANIMATIONS || PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS; bool scenes_out = p_preset == PRESET_MULTIPLE_SCENES || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS; - bool animations_out = p_preset == PRESET_SEPERATE_ANIMATIONS || PRESET_SEPARATE_MESHES_AND_ANIMATIONS || p_preset == PRESET_SEPERATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPERATE_MESHES_MATERIALS_AND_ANIMATIONS; + bool animations_out = p_preset == PRESET_SEPARATE_ANIMATIONS || PRESET_SEPARATE_MESHES_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS; r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/custom_script", PROPERTY_HINT_FILE, script_ext_hint), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "nodes/storage", PROPERTY_HINT_ENUM, "Single Scene,Instanced Sub-Scenes"), scenes_out ? 1 : 0)); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 652977b98a..c3a66aa8b8 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -85,14 +85,14 @@ class ResourceImporterScene : public ResourceImporter { enum Presets { PRESET_SEPARATE_MATERIALS, PRESET_SEPARATE_MESHES, - PRESET_SEPERATE_ANIMATIONS, + PRESET_SEPARATE_ANIMATIONS, PRESET_SINGLE_SCENE, PRESET_SEPARATE_MESHES_AND_MATERIALS, PRESET_SEPARATE_MESHES_AND_ANIMATIONS, - PRESET_SEPERATE_MATERIALS_AND_ANIMATIONS, - PRESET_SEPERATE_MESHES_MATERIALS_AND_ANIMATIONS, + PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS, + PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS, PRESET_MULTIPLE_SCENES, PRESET_MULTIPLE_SCENES_AND_MATERIALS, diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index b28da54c6d..bf1769e672 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -53,6 +53,8 @@ #define MIN_ZOOM 0.01 #define MAX_ZOOM 100 +#define RULER_WIDTH 15 * EDSCALE + class SnapDialog : public ConfirmationDialog { GDCLASS(SnapDialog, ConfirmationDialog); @@ -109,13 +111,13 @@ public: label->set_h_size_flags(SIZE_EXPAND_FILL); grid_step_x = memnew(SpinBox); - grid_step_x->set_min(-SPIN_BOX_GRID_RANGE); + grid_step_x->set_min(0.01); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_suffix("px"); child_container->add_child(grid_step_x); grid_step_y = memnew(SpinBox); - grid_step_y->set_min(-SPIN_BOX_GRID_RANGE); + grid_step_y->set_min(0.01); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_suffix("px"); child_container->add_child(grid_step_y); @@ -149,7 +151,7 @@ public: child_container->add_child(rotation_step); } - void set_fields(const Point2 p_grid_offset, const Size2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) { + void set_fields(const Point2 p_grid_offset, const Point2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) { grid_offset_x->set_value(p_grid_offset.x); grid_offset_y->set_value(p_grid_offset.y); grid_step_x->set_value(p_grid_step.x); @@ -158,11 +160,9 @@ public: rotation_step->set_value(p_rotation_step * (180 / Math_PI)); } - void get_fields(Point2 &p_grid_offset, Size2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) { - p_grid_offset.x = grid_offset_x->get_value(); - p_grid_offset.y = grid_offset_y->get_value(); - p_grid_step.x = grid_step_x->get_value(); - p_grid_step.y = grid_step_y->get_value(); + void get_fields(Point2 &p_grid_offset, Point2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) { + p_grid_offset = Point2(grid_offset_x->get_value(), grid_offset_y->get_value()); + p_grid_step = Point2(grid_step_x->get_value(), grid_step_y->get_value()); p_rotation_offset = rotation_offset->get_value() / (180 / Math_PI); p_rotation_step = rotation_step->get_value() / (180 / Math_PI); } @@ -222,6 +222,135 @@ void CanvasItemEditor::_edit_set_pivot(const Vector2 &mouse_pos) { undo_redo->commit_action(); } +void CanvasItemEditor::_snap_if_closer(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation, float p_radius) { + float radius = p_radius / zoom; + float dist; + + Transform2D rot_trans = Transform2D(rotation, Point2()); + p_value = rot_trans.inverse().xform(p_value); + p_target_snap = rot_trans.inverse().xform(p_target_snap); + r_current_snap = rot_trans.inverse().xform(r_current_snap); + + dist = Math::abs(p_value.x - p_target_snap.x); + if (p_radius < 0 || dist < radius && (!r_snapped[0] || dist < Math::abs(r_current_snap.x - p_value.x))) { + r_current_snap.x = p_target_snap.x; + r_snapped[0] = true; + } + + dist = Math::abs(p_value.y - p_target_snap.y); + if (p_radius < 0 || dist < radius && (!r_snapped[1] || dist < Math::abs(r_current_snap.y - p_value.y))) { + r_current_snap.y = p_target_snap.y; + r_snapped[1] = true; + } + + r_current_snap = rot_trans.xform(r_current_snap); +} + +void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap) { + const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_current); + if (canvas_item && p_current != p_to_snap) { + Transform2D ci_transform = canvas_item->get_global_transform_with_canvas(); + Transform2D to_snap_transform = p_to_snap->get_global_transform_with_canvas(); + if (ci_transform.get_rotation() == to_snap_transform.get_rotation()) { + Point2 begin = ci_transform.xform(canvas_item->get_item_rect().get_position()); + Point2 end = ci_transform.xform(canvas_item->get_item_rect().get_position() + canvas_item->get_item_rect().get_size()); + + _snap_if_closer(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation()); + _snap_if_closer(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation()); + } + } + for (int i = 0; i < p_current->get_child_count(); i++) { + _snap_other_nodes(p_value, r_current_snap, r_snapped, p_current->get_child(i), p_to_snap); + } +} + +Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const CanvasItem *p_canvas_item, unsigned int p_forced_modes) { + Point2 dist[2]; + bool snapped[2] = { false, false }; + + // Smart snap using the canvas position + Vector2 output = p_target; + real_t rotation = 0.0; + + if (p_canvas_item) { + Point2 begin; + Point2 end; + rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation(); + + if ((snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { + // Parent sides and center + bool can_snap = false; + if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { + begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0))); + end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1))); + can_snap = true; + } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) { + begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->get_item_rect().get_position()); + end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->get_item_rect().get_position() + parent_ci->get_item_rect().get_size()); + can_snap = true; + } + + if (can_snap) { + _snap_if_closer(p_target, begin, output, snapped, rotation); + _snap_if_closer(p_target, (begin + end) / 2.0, output, snapped, rotation); + _snap_if_closer(p_target, end, output, snapped, rotation); + } + } + + // Self anchors (for sides) + if ((snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { + if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { + begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP)))); + end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM)))); + _snap_if_closer(p_target, begin, output, snapped, rotation); + _snap_if_closer(p_target, end, output, snapped, rotation); + } + } + + // Self sides (for anchors) + if ((snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { + begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->get_item_rect().get_position()); + end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->get_item_rect().get_position() + p_canvas_item->get_item_rect().get_size()); + _snap_if_closer(p_target, begin, output, snapped, rotation); + _snap_if_closer(p_target, end, output, snapped, rotation); + } + + // Other nodes sides + if ((snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { + _snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item); + } + } + + if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && rotation == 0.0) { + // Grid + Point2 offset = grid_offset; + if (snap_relative) { + List<Node *> &selection = editor_selection->get_selected_node_list(); + if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0])) { + offset = Object::cast_to<Node2D>(selection[0])->get_global_position(); + } else { + offset = _find_topleftmost_point(); + } + } + Point2 grid_output; + grid_output.x = Math::stepify(p_target.x - offset.x, grid_step.x * Math::pow(2.0, grid_step_multiplier)) + offset.x; + grid_output.y = Math::stepify(p_target.y - offset.y, grid_step.y * Math::pow(2.0, grid_step_multiplier)) + offset.y; + _snap_if_closer(p_target, grid_output, output, snapped, 0.0, -1.0); + } + + if (((snap_pixel && (p_modes & SNAP_PIXEL)) || (p_forced_modes & SNAP_PIXEL)) && rotation == 0.0) { + // Pixel + output = output.snapped(Size2(1, 1)); + } + + return output; +} + +float CanvasItemEditor::snap_angle(float p_target, float p_start) const { + float offset = snap_relative ? p_start : p_target; + return (snap_rotation && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; +} + void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { Ref<InputEventKey> k = p_ev; @@ -232,22 +361,34 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { if (k->get_control()) return; - if (k->is_pressed() && !k->is_echo() && k->get_scancode() == KEY_V && drag == DRAG_NONE && can_move_pivot) { - - if (k->get_shift()) { + if (k->is_pressed() && !k->is_echo()) { + if (drag_pivot_shortcut.is_valid() && drag_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) { //move drag pivot drag = DRAG_PIVOT; - } else if (!Input::get_singleton()->is_mouse_button_pressed(0)) { - - List<Node *> &selection = editor_selection->get_selected_node_list(); - Vector2 mouse_pos = viewport->get_local_mouse_pos(); - if (selection.size() && viewport->get_rect().has_point(mouse_pos)) { - //just in case, make it work if over viewport - mouse_pos = transform.affine_inverse().xform(mouse_pos); - mouse_pos = snap_point(mouse_pos); - - _edit_set_pivot(mouse_pos); + } else if (set_pivot_shortcut.is_valid() && set_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) { + if (!Input::get_singleton()->is_mouse_button_pressed(0)) { + List<Node *> &selection = editor_selection->get_selected_node_list(); + Vector2 mouse_pos = viewport->get_local_mouse_pos(); + if (selection.size() && viewport->get_rect().has_point(mouse_pos)) { + //just in case, make it work if over viewport + mouse_pos = transform.affine_inverse().xform(mouse_pos); + mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item()); + + _edit_set_pivot(mouse_pos); + } } + } else if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { + // Multiply the grid size + grid_step_multiplier = MIN(grid_step_multiplier + 1, 12); + viewport_base->update(); + viewport->update(); + } else if ((snap_grid || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) { + // Divide the grid size + Point2 new_grid_step = grid_step * Math::pow(2.0, grid_step_multiplier - 1); + if (new_grid_step.x >= 1.0 && new_grid_step.y >= 1.0) + grid_step_multiplier--; + viewport_base->update(); + viewport->update(); } } } @@ -272,38 +413,25 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) { return memnew(CanvasItemEditorSelectedItem); } -inline float _snap_scalar(float p_offset, float p_step, bool p_snap_relative, float p_target, float p_start) { - float offset = p_snap_relative ? p_start : p_offset; - return p_step != 0 ? Math::stepify(p_target - offset, p_step) + offset : p_target; -} - -Vector2 CanvasItemEditor::snap_point(Vector2 p_target, Vector2 p_start) const { - if (snap_grid) { - p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_relative, p_target.x, p_start.x); - p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_relative, p_target.y, p_start.y); - } - if (snap_pixel) - p_target = p_target.snapped(Size2(1, 1)); - - return p_target; -} - -float CanvasItemEditor::snap_angle(float p_target, float p_start) const { - return snap_rotation ? _snap_scalar(snap_rotation_offset, snap_rotation_step, snap_relative, p_target, p_start) : p_target; -} - Dictionary CanvasItemEditor::get_state() const { Dictionary state; state["zoom"] = zoom; state["ofs"] = Point2(h_scroll->get_value(), v_scroll->get_value()); //state["ofs"]=-transform.get_origin(); - state["snap_offset"] = snap_offset; - state["snap_step"] = snap_step; + state["grid_offset"] = grid_offset; + state["grid_step"] = grid_step; state["snap_rotation_offset"] = snap_rotation_offset; state["snap_rotation_step"] = snap_rotation_step; + state["snap_active"] = snap_active; + state["snap_node_parent"] = snap_node_parent; + state["snap_node_anchors"] = snap_node_anchors; + state["snap_node_sides"] = snap_node_sides; + state["snap_other_nodes"] = snap_other_nodes; state["snap_grid"] = snap_grid; - state["snap_show_grid"] = snap_show_grid; + state["show_grid"] = show_grid; + state["show_rulers"] = show_rulers; + state["show_helpers"] = show_helpers; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; state["snap_pixel"] = snap_pixel; @@ -324,12 +452,12 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { v_scroll->set_value(ofs.y); } - if (state.has("snap_step")) { - snap_step = state["snap_step"]; + if (state.has("grid_offset")) { + grid_offset = state["grid_offset"]; } - if (state.has("snap_offset")) { - snap_offset = state["snap_offset"]; + if (state.has("grid_step")) { + grid_step = state["grid_step"]; } if (state.has("snap_rotation_step")) { @@ -340,40 +468,81 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { snap_rotation_offset = state["snap_rotation_offset"]; } + if (state.has("snap_active")) { + snap_active = state["snap_active"]; + snap_button->set_pressed(snap_active); + } + + if (state.has("snap_node_parent")) { + snap_node_parent = state["snap_node_parent"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); + smartsnap_config_popup->set_item_checked(idx, snap_node_parent); + } + + if (state.has("snap_node_anchors")) { + snap_node_anchors = state["snap_node_anchors"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->set_item_checked(idx, snap_node_anchors); + } + + if (state.has("snap_node_sides")) { + snap_node_sides = state["snap_node_sides"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES); + smartsnap_config_popup->set_item_checked(idx, snap_node_sides); + } + + if (state.has("snap_other_nodes")) { + snap_other_nodes = state["snap_other_nodes"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES); + smartsnap_config_popup->set_item_checked(idx, snap_other_nodes); + } + if (state.has("snap_grid")) { snap_grid = state["snap_grid"]; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE); - edit_menu->get_popup()->set_item_checked(idx, snap_grid); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); + snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); + } + + if (state.has("show_grid")) { + show_grid = state["show_grid"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); + view_menu->get_popup()->set_item_checked(idx, show_grid); + } + + if (state.has("show_rulers")) { + show_rulers = state["show_rulers"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); + view_menu->get_popup()->set_item_checked(idx, show_rulers); } - if (state.has("snap_show_grid")) { - snap_show_grid = state["snap_show_grid"]; - int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID); - edit_menu->get_popup()->set_item_checked(idx, snap_show_grid); + if (state.has("show_helpers")) { + show_helpers = state["show_helpers"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS); + view_menu->get_popup()->set_item_checked(idx, show_helpers); } if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); - edit_menu->get_popup()->set_item_checked(idx, snap_rotation); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); + snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation); } if (state.has("snap_relative")) { snap_relative = state["snap_relative"]; - int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE); - edit_menu->get_popup()->set_item_checked(idx, snap_relative); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE); + snap_config_menu->get_popup()->set_item_checked(idx, snap_relative); } if (state.has("snap_pixel")) { snap_pixel = state["snap_pixel"]; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); - edit_menu->get_popup()->set_item_checked(idx, snap_pixel); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); + snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel); } if (state.has("skeleton_show_bones")) { skeleton_show_bones = state["skeleton_show_bones"]; - int idx = skeleton_menu->get_item_index(SKELETON_SHOW_BONES); - skeleton_menu->set_item_checked(idx, skeleton_show_bones); + int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); + skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); } viewport->update(); @@ -495,6 +664,7 @@ void CanvasItemEditor::_select_click_on_empty_area(Point2 p_click_pos, bool p_ap if (!p_append) { editor_selection->clear(); viewport->update(); + viewport_base->update(); }; if (p_box_selection) { @@ -534,6 +704,7 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po } viewport->update(); + viewport_base->update(); return still_selected; } @@ -566,7 +737,7 @@ void CanvasItemEditor::_key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE Vector2 drag = p_dir; if (p_snap) - drag *= snap_step; + drag *= grid_step * Math::pow(2.0, grid_step_multiplier); undo_redo->add_undo_method(canvas_item, "edit_set_state", canvas_item->edit_get_state()); @@ -649,7 +820,7 @@ int CanvasItemEditor::get_item_count() { return ic; } -CanvasItem *CanvasItemEditor::get_single_item() { +CanvasItem *CanvasItemEditor::_get_single_item() { Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -674,7 +845,7 @@ CanvasItem *CanvasItemEditor::get_single_item() { CanvasItemEditor::DragType CanvasItemEditor::_get_resize_handle_drag_type(const Point2 &p_click, Vector2 &r_point) { // Returns a drag type if a resize handle is clicked - CanvasItem *canvas_item = get_single_item(); + CanvasItem *canvas_item = _get_single_item(); ERR_FAIL_COND_V(!canvas_item, DRAG_NONE); @@ -738,35 +909,7 @@ CanvasItemEditor::DragType CanvasItemEditor::_get_resize_handle_drag_type(const return DRAG_NONE; } -float CanvasItemEditor::_anchor_snap(float p_anchor, bool *p_snapped, float p_opposite_anchor) { - bool snapped = false; - float dist, dist_min = 0.0; - float radius = 0.05 / zoom; - float basic_anchors[3] = { 0.0, 0.5, 1.0 }; - for (int i = 0; i < 3; i++) { - dist = fabs(p_anchor - basic_anchors[i]); - if (dist < radius) { - if (!snapped || dist <= dist_min) { - p_anchor = basic_anchors[i]; - dist_min = dist; - snapped = true; - } - } - } - dist = fabs(p_anchor - p_opposite_anchor); - if (p_opposite_anchor >= 0 && dist < radius) { - if (!snapped || dist <= dist_min) { - p_anchor = p_opposite_anchor; - dist_min = dist; - snapped = true; - } - } - if (p_snapped) - *p_snapped = snapped; - return p_anchor; -} - -Vector2 CanvasItemEditor::_anchor_to_position(Control *p_control, Vector2 anchor) { +Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2 anchor) { ERR_FAIL_COND_V(!p_control, Vector2()); Transform2D parent_transform = p_control->get_transform().affine_inverse(); @@ -775,7 +918,7 @@ Vector2 CanvasItemEditor::_anchor_to_position(Control *p_control, Vector2 anchor return parent_transform.xform(Vector2(parent_size.x * anchor.x, parent_size.y * anchor.y)); } -Vector2 CanvasItemEditor::_position_to_anchor(Control *p_control, Vector2 position) { +Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 position) { ERR_FAIL_COND_V(!p_control, Vector2()); Size2 parent_size = p_control->get_parent_area_size(); @@ -784,7 +927,7 @@ Vector2 CanvasItemEditor::_position_to_anchor(Control *p_control, Vector2 positi CanvasItemEditor::DragType CanvasItemEditor::_get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point) { // Returns a drag type if an anchor handle is clicked - CanvasItem *canvas_item = get_single_item(); + CanvasItem *canvas_item = _get_single_item(); ERR_FAIL_COND_V(!canvas_item, DRAG_NONE); Control *control = Object::cast_to<Control>(canvas_item); @@ -920,28 +1063,12 @@ void CanvasItemEditor::_append_canvas_item(CanvasItem *p_item) { } void CanvasItemEditor::_snap_changed() { - ((SnapDialog *)snap_dialog)->get_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step); + ((SnapDialog *)snap_dialog)->get_fields(grid_offset, grid_step, snap_rotation_offset, snap_rotation_step); + grid_step_multiplier = 0; + viewport_base->update(); viewport->update(); } -void CanvasItemEditor::_dialog_value_changed(double) { - - if (updating_value_dialog) - return; - - switch (last_option) { - - case ZOOM_SET: { - - zoom = dialog_val->get_value() / 100.0; - _update_scroll(0); - viewport->update(); - - } break; - default: {} - } -} - void CanvasItemEditor::_selection_result_pressed(int p_result) { if (selection_results.size() <= p_result) @@ -962,7 +1089,7 @@ void CanvasItemEditor::_selection_menu_hide() { void CanvasItemEditor::_list_select(const Ref<InputEventMouseButton> &b) { - Point2 click = b->get_position(); + Point2 click = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); Node *scene = editor->get_edited_scene(); if (!scene) @@ -1070,7 +1197,7 @@ void CanvasItemEditor::_update_cursor() { viewport->set_default_cursor_shape(c); } -void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { +void CanvasItemEditor::_viewport_base_gui_input(const Ref<InputEvent> &p_event) { { EditorNode *en = editor; @@ -1103,7 +1230,7 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { float prev_zoom = zoom; zoom = zoom * (1 - (0.05 * b->get_factor())); { - Point2 ofs = b->get_position(); + Point2 ofs = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); ofs = ofs / prev_zoom - ofs / zoom; h_scroll->set_value(h_scroll->get_value() + ofs.x); v_scroll->set_value(v_scroll->get_value() + ofs.y); @@ -1127,7 +1254,7 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { float prev_zoom = zoom; zoom = zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95); { - Point2 ofs = b->get_position(); + Point2 ofs = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); ofs = ofs / prev_zoom - ofs / zoom; h_scroll->set_value(h_scroll->get_value() + ofs.x); v_scroll->set_value(v_scroll->get_value() + ofs.y); @@ -1216,9 +1343,9 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) { if (b->is_pressed()) { // Set the pivot point - Point2 mouse_pos = b->get_position(); + Point2 mouse_pos = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); mouse_pos = transform.affine_inverse().xform(mouse_pos); - mouse_pos = snap_point(mouse_pos); + mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item()); _edit_set_pivot(mouse_pos); } return; @@ -1339,8 +1466,8 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { E->get().to }; - Vector2 p = Geometry::get_closest_point_to_segment_2d(b->get_position(), s); - float d = p.distance_to(b->get_position()); + Vector2 p = Geometry::get_closest_point_to_segment_2d(viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()), s); + float d = p.distance_to(viewport_scrollable->get_transform().affine_inverse().xform(b->get_position())); if (d < bone_width && d < closest_dist) { Cbone = E; closest_dist = d; @@ -1397,12 +1524,12 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { } // Single selected item - CanvasItem *canvas_item = get_single_item(); + CanvasItem *canvas_item = _get_single_item(); if (canvas_item) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); ERR_FAIL_COND(!se); - Point2 click = b->get_position(); + Point2 click = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); // Rotation if ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) { @@ -1440,7 +1567,8 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { } // Drag anchor handles - if (Object::cast_to<Control>(canvas_item)) { + Control *control = Object::cast_to<Control>(canvas_item); + if (control && show_helpers && !Object::cast_to<Container>(control->get_parent())) { drag = _get_anchor_handle_drag_type(click, drag_point_from); if (drag != DRAG_NONE) { drag_from = transform.affine_inverse().xform(click); @@ -1453,7 +1581,7 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { } // Multiple selected items - Point2 click = b->get_position(); + Point2 click = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); if ((b->get_alt() || tool == TOOL_MOVE) && get_item_count()) { // Drag the nodes @@ -1513,12 +1641,12 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { // Mouse motion event _update_cursor(); - if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) - viewport->call_deferred("grab_focus"); + if (!viewport_base->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) + viewport_base->call_deferred("grab_focus"); if (box_selecting) { // Update box selection - box_selecting_to = transform.affine_inverse().xform(m->get_position()); + box_selecting_to = transform.affine_inverse().xform(viewport_scrollable->get_transform().affine_inverse().xform(m->get_position())); viewport->update(); return; } @@ -1564,7 +1692,7 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { } Vector2 dfrom = drag_from; - Vector2 dto = transform.affine_inverse().xform(m->get_position()); + Vector2 dto = transform.affine_inverse().xform(viewport_scrollable->get_transform().affine_inverse().xform(m->get_position())); if (canvas_item->has_meta("_edit_lock_")) continue; @@ -1598,60 +1726,74 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { continue; } + bool uniform = m->get_shift(); + bool symmetric = m->get_alt(); + + Vector2 drag_vector = + canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - + canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom); + + switch (drag) { + case DRAG_ALL: + case DRAG_NODE_2D: + dto -= drag_from - drag_point_from; + if (uniform) { + if (ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) { + dto.y = drag_point_from.y; + } else { + dto.x = drag_point_from.x; + } + } + break; + } + Control *control = Object::cast_to<Control>(canvas_item); if (control) { // Drag and snap the anchor - Vector2 anchor = _position_to_anchor(control, canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto - drag_from + drag_point_from)); + Transform2D c_trans_rev = canvas_item->get_global_transform_with_canvas().affine_inverse(); + + Vector2 anchor = c_trans_rev.xform(dto - drag_from + drag_point_from); + anchor = _position_to_anchor(control, anchor); + + Vector2 anchor_snapped = c_trans_rev.xform(snap_point(dto - drag_from + drag_point_from, SNAP_GRID | SNAP_OTHER_NODES, _get_single_item(), SNAP_NODE_PARENT | SNAP_NODE_SIDES)); + anchor_snapped = _position_to_anchor(control, anchor_snapped).snapped(Vector2(0.00001, 0.00001)); + + bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x); switch (drag) { case DRAG_ANCHOR_TOP_LEFT: - control->set_anchor(MARGIN_LEFT, _anchor_snap(anchor.x, NULL, control->get_anchor(MARGIN_RIGHT)), false, false); - control->set_anchor(MARGIN_TOP, _anchor_snap(anchor.y, NULL, control->get_anchor(MARGIN_BOTTOM)), false, false); + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y); continue; break; case DRAG_ANCHOR_TOP_RIGHT: - control->set_anchor(MARGIN_RIGHT, _anchor_snap(anchor.x, NULL, control->get_anchor(MARGIN_LEFT)), false, false); - control->set_anchor(MARGIN_TOP, _anchor_snap(anchor.y, NULL, control->get_anchor(MARGIN_BOTTOM)), false, false); + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y); continue; break; case DRAG_ANCHOR_BOTTOM_RIGHT: - control->set_anchor(MARGIN_RIGHT, _anchor_snap(anchor.x, NULL, control->get_anchor(MARGIN_LEFT)), false, false); - control->set_anchor(MARGIN_BOTTOM, _anchor_snap(anchor.y, NULL, control->get_anchor(MARGIN_TOP)), false, false); - continue; + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y); break; case DRAG_ANCHOR_BOTTOM_LEFT: - control->set_anchor(MARGIN_LEFT, _anchor_snap(anchor.x, NULL, control->get_anchor(MARGIN_RIGHT)), false, false); - control->set_anchor(MARGIN_BOTTOM, _anchor_snap(anchor.y, NULL, control->get_anchor(MARGIN_TOP)), false, false); + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y); continue; break; case DRAG_ANCHOR_ALL: - control->set_anchor(MARGIN_LEFT, _anchor_snap(anchor.x)); - control->set_anchor(MARGIN_RIGHT, _anchor_snap(anchor.x)); - control->set_anchor(MARGIN_TOP, _anchor_snap(anchor.y)); - control->set_anchor(MARGIN_BOTTOM, _anchor_snap(anchor.y)); + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x); + if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y); + if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y); continue; break; } } - bool uniform = m->get_shift(); - bool symmetric = m->get_alt(); - - if (drag == DRAG_ALL || drag == DRAG_NODE_2D) - dto -= drag_from - drag_point_from; - - if (uniform && (drag == DRAG_ALL || drag == DRAG_NODE_2D)) { - if (ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) { - dto.y = drag_point_from.y; - } else { - dto.x = drag_point_from.x; - } - } - dfrom = drag_point_from; - dto = snap_point(dto, drag_point_from); + dto = snap_point(dto, SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, _get_single_item()); - Vector2 drag_vector = + drag_vector = canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom); @@ -1901,56 +2043,148 @@ void CanvasItemEditor::_viewport_gui_input(const Ref<InputEvent> &p_event) { } } +void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string, Margin p_side) { + Color color = Color(0.8, 0.8, 0.8, 0.5); + Ref<Font> font = get_font("font", "Label"); + Size2 text_size = font->get_string_size(p_string); + switch (p_side) { + case MARGIN_LEFT: + p_position += Vector2(-text_size.x - 5, text_size.y / 2); + break; + case MARGIN_TOP: + p_position += Vector2(-text_size.x / 2, -5); + break; + case MARGIN_RIGHT: + p_position += Vector2(5, text_size.y / 2); + break; + case MARGIN_BOTTOM: + p_position += Vector2(-text_size.x / 2, text_size.y + 5); + break; + } + viewport->draw_string(font, p_position, p_string, color); +} + +void CanvasItemEditor::_draw_margin_at_position(int p_value, Point2 p_position, Margin p_side) { + String str = vformat("%d px", p_value); + if (p_value != 0) { + _draw_text_at_position(p_position, str, p_side); + } +} + void CanvasItemEditor::_draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side) { + String str = vformat("%.1f %%", p_value * 100.0); if (p_value != 0) { - Color color = Color(0.8, 0.8, 0.8, 0.5); - Ref<Font> font = get_font("font", "Label"); - String str = vformat("%.1f %%", p_value * 100.0); - Size2 text_size = font->get_string_size(str); - switch (p_side) { - case MARGIN_LEFT: - p_position += Vector2(-text_size.x - 5, text_size.y / 2); - break; - case MARGIN_TOP: - p_position += Vector2(-text_size.x / 2, -5); - break; - case MARGIN_RIGHT: - p_position += Vector2(5, text_size.y / 2); - break; - case MARGIN_BOTTOM: - p_position += Vector2(-text_size.x / 2, text_size.y + 5); - break; - } - viewport->draw_string(font, p_position, str, color); + _draw_text_at_position(p_position, str, p_side); } } -void CanvasItemEditor::_viewport_draw() { +void CanvasItemEditor::_draw_rulers() { + Color graduation_color = Color(0.5, 0.5, 0.5, 1); + Color bg_color = get_color("dark_color_2", "Editor"); + Color font_color = Color(0.8, 0.8, 0.8, 1); + Ref<Font> font = get_font("rulers", "EditorFonts"); + + // The rule transform + Transform2D ruler_transform; + if (show_grid || snap_grid) { + ruler_transform = Transform2D(); + if (snap_relative && get_item_count() > 0) { + ruler_transform.translate(_find_topleftmost_point()); + ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier)); + } else { + ruler_transform.translate(grid_offset); + ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier)); + } + while ((transform * ruler_transform).get_scale().x < 50 || (transform * ruler_transform).get_scale().y < 50) { - // TODO fetch the viewport? + ruler_transform.scale_basis(Point2(2, 2)); + } + } else { + float basic_rule = 100; + for (int i = 0; basic_rule * zoom > 100; i++) { + basic_rule /= (i % 2) ? 5.0 : 2.0; + } + for (int i = 0; basic_rule * zoom < 100; i++) { + basic_rule *= (i % 2) ? 2.0 : 5.0; + } + ruler_transform = Transform2D(); + ruler_transform.scale(Size2(basic_rule, basic_rule)); + } - Ref<Texture> pivot = get_icon("EditorPivot", "EditorIcons"); - _update_scrollbars(); - RID ci = viewport->get_canvas_item(); + // Subdivisions + int major_subdivision = 2; + Transform2D major_subdivide = Transform2D(); + major_subdivide.scale(Size2(1.0 / major_subdivision, 1.0 / major_subdivision)); + + int minor_subdivision = 5; + Transform2D minor_subdivide = Transform2D(); + minor_subdivide.scale(Size2(1.0 / minor_subdivision, 1.0 / minor_subdivision)); + + // First and last graduations to draw (in the ruler space) + Point2 first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Point2()); + Point2 last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(viewport->get_size()); + + // Draw top ruler + viewport_base->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color); + for (int i = Math::ceil(first.x); i < last.x; i++) { + Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)); + if (i % (major_subdivision * minor_subdivision) == 0) { + viewport_base->draw_line(Point2(position.x + RULER_WIDTH, 0), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x; + viewport_base->draw_string(font, Point2(position.x + RULER_WIDTH + 2, font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + } else { + if (i % minor_subdivision == 0) { + viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.33), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + } else { + viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.66), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + } + } + } + + // Draw left ruler + viewport_base->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color); + for (int i = Math::ceil(first.y); i < last.y; i++) { + Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)); + if (i % (major_subdivision * minor_subdivision) == 0) { + viewport_base->draw_line(Point2(0, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; + viewport_base->draw_string(font, Point2(2, position.y + RULER_WIDTH + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + } else { + if (i % minor_subdivision == 0) { + viewport_base->draw_line(Point2(RULER_WIDTH * 0.33, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + } else { + viewport_base->draw_line(Point2(RULER_WIDTH * 0.66, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + } + } + } + viewport_base->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color); +} + +void CanvasItemEditor::_draw_focus() { + if (viewport_base->has_focus()) { + get_stylebox("Focus", "EditorStyles")->draw(viewport_base->get_canvas_item(), Rect2(Point2(), viewport_base->get_size())); + } +} - if (snap_show_grid) { +void CanvasItemEditor::_draw_grid() { + if (show_grid) { //Draw the grid Size2 s = viewport->get_size(); int last_cell = 0; Transform2D xform = transform.affine_inverse(); - Vector2 grid_offset; - if (snap_relative && snap_grid && get_item_count() > 0) { + Vector2 real_grid_offset; + if (snap_relative && get_item_count() > 0) { Vector2 topleft = _find_topleftmost_point(); - grid_offset.x = fmod(topleft.x, snap_step.x); - grid_offset.y = fmod(topleft.y, snap_step.y); + real_grid_offset.x = fmod(topleft.x, grid_step.x * Math::pow(2.0, grid_step_multiplier)); + real_grid_offset.y = fmod(topleft.y, grid_step.y * Math::pow(2.0, grid_step_multiplier)); } else { - grid_offset = snap_offset; + real_grid_offset = grid_offset; } - if (snap_step.x != 0) { + if (grid_step.x != 0) { for (int i = 0; i < s.width; i++) { - int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i, 0)).x - grid_offset.x) / snap_step.x)); + int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i, 0)).x - real_grid_offset.x) / (grid_step.x * Math::pow(2.0, grid_step_multiplier)))); if (i == 0) last_cell = cell; if (last_cell != cell) @@ -1959,9 +2193,9 @@ void CanvasItemEditor::_viewport_draw() { } } - if (snap_step.y != 0) { + if (grid_step.y != 0) { for (int i = 0; i < s.height; i++) { - int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0, i)).y - grid_offset.y) / snap_step.y)); + int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0, i)).y - real_grid_offset.y) / (grid_step.y * Math::pow(2.0, grid_step_multiplier)))); if (i == 0) last_cell = cell; if (last_cell != cell) @@ -1970,21 +2204,15 @@ void CanvasItemEditor::_viewport_draw() { } } } +} - if (viewport->has_focus()) { - Size2 size = viewport->get_size(); - get_stylebox("Focus", "EditorStyles")->draw(ci, Rect2(Point2(), size)); - } - - Ref<Texture> lock = get_icon("Lock", "EditorIcons"); - Ref<Texture> group = get_icon("Group", "EditorIcons"); - - bool single = get_single_item() != NULL; - - Map<Node *, Object *> &selection = editor_selection->get_selection(); - +void CanvasItemEditor::_draw_selection() { bool pivot_found = false; + Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons"); + bool single = _get_single_item() != NULL; + RID ci = viewport->get_canvas_item(); + Map<Node *, Object *> &selection = editor_selection->get_selection(); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); @@ -2036,10 +2264,10 @@ void CanvasItemEditor::_viewport_draw() { if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks - if (Object::cast_to<Node2D>(canvas_item)) { - - if (Object::cast_to<Node2D>(canvas_item)->edit_has_pivot()) { - viewport->draw_texture(pivot, xform.get_origin() + (-pivot->get_size() / 2).floor()); + Node2D *node2d = Object::cast_to<Node2D>(canvas_item); + if (node2d) { + if (node2d->edit_has_pivot()) { + viewport->draw_texture(pivot_icon, xform.get_origin() + (-pivot_icon->get_size() / 2).floor()); can_move_pivot = true; pivot_found = true; } @@ -2049,12 +2277,15 @@ void CanvasItemEditor::_viewport_draw() { if (control) { Vector2 pivot_ofs = control->get_pivot_offset(); if (pivot_ofs != Vector2()) { - viewport->draw_texture(pivot, xform.xform(pivot_ofs) + (-pivot->get_size() / 2).floor()); + viewport->draw_texture(pivot_icon, xform.xform(pivot_ofs) + (-pivot_icon->get_size() / 2).floor()); } can_move_pivot = true; pivot_found = true; - if (tool == TOOL_SELECT) { + if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { + // Draw the helpers + Color color_base = Color(0.8, 0.8, 0.8, 0.5); + float anchors_values[4]; anchors_values[0] = control->get_anchor(MARGIN_LEFT); anchors_values[1] = control->get_anchor(MARGIN_TOP); @@ -2069,6 +2300,7 @@ void CanvasItemEditor::_viewport_draw() { anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i])); } + Map<Node *, Object *> &selection = editor_selection->get_selection(); // Get which anchor is dragged int dragged_anchor = -1; switch (drag) { @@ -2091,7 +2323,6 @@ void CanvasItemEditor::_viewport_draw() { // Draw the 4 lines when dragged bool snapped; Color color_snapped = Color(0.64, 0.93, 0.67, 0.5); - Color color_base = Color(0.8, 0.8, 0.8, 0.5); Vector2 corners_pos[4]; for (int i = 0; i < 4; i++) { @@ -2104,7 +2335,7 @@ void CanvasItemEditor::_viewport_draw() { float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val); line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val); - _anchor_snap(anchors_values[i], &snapped); + snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0; viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1); } @@ -2136,6 +2367,83 @@ void CanvasItemEditor::_viewport_draw() { for (int i = 0; i < 4; i++) { anchor_handle->draw_rect(ci, anchor_rects[i]); } + + // Draw the margin values and the node width/height when dragging control side + float ratio = 0.33; + Transform2D parent_transform = xform * control->get_transform().affine_inverse(); + float node_pos_in_parent[4]; + + node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * control->get_parent_area_size().width + control->get_margin(MARGIN_LEFT); + node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * control->get_parent_area_size().height + control->get_margin(MARGIN_TOP); + node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * control->get_parent_area_size().width + control->get_margin(MARGIN_RIGHT); + node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * control->get_parent_area_size().height + control->get_margin(MARGIN_BOTTOM); + + switch (drag) { + case DRAG_LEFT: + case DRAG_TOP_LEFT: + case DRAG_BOTTOM_LEFT: + _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + case DRAG_ALL: + Point2 start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); + Point2 end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); + _draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + } + switch (drag) { + case DRAG_RIGHT: + case DRAG_TOP_RIGHT: + case DRAG_BOTTOM_RIGHT: + _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + case DRAG_ALL: + Point2 start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); + Point2 end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); + _draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + } + switch (drag) { + case DRAG_TOP: + case DRAG_TOP_LEFT: + case DRAG_TOP_RIGHT: + _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); + case DRAG_ALL: + Point2 start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); + Point2 end = start - Vector2(0, control->get_margin(MARGIN_TOP)); + _draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + } + switch (drag) { + case DRAG_BOTTOM: + case DRAG_BOTTOM_LEFT: + case DRAG_BOTTOM_RIGHT: + _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); + case DRAG_ALL: + Point2 start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); + Point2 end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); + _draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + } + + switch (drag) { + //Draw the ghost rect if the node if rotated/scaled + case DRAG_LEFT: + case DRAG_TOP_LEFT: + case DRAG_TOP: + case DRAG_TOP_RIGHT: + case DRAG_RIGHT: + case DRAG_BOTTOM_RIGHT: + case DRAG_BOTTOM: + case DRAG_BOTTOM_LEFT: + case DRAG_ALL: + if (control->get_rotation() != 0.0 || control->get_scale() != Vector2(1, 1)) { + Rect2 rect = Rect2(Vector2(node_pos_in_parent[0], node_pos_in_parent[1]), control->get_size()); + viewport->draw_rect(parent_transform.xform(rect), color_base, false); + } + break; + } } } @@ -2158,33 +2466,32 @@ void CanvasItemEditor::_viewport_draw() { } } } - - //DRAW_EMPTY_RECT( Rect2( current_window->get_scroll()-Point2(1,1), get_size()+Size2(2,2)), Color(0.8,0.8,1.0,0.8) ); - //E->get().last_rect = rect; } - pivot_button->set_disabled(!pivot_found); - VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D()); - - Color x_axis_color(1.0, 0.4, 0.4, 0.6); - Color y_axis_color(0.4, 1.0, 0.4, 0.6); - Color area_axis_color(0.4, 0.4, 1.0, 0.4); - Color rotate_color(0.4, 0.7, 1.0, 0.8); - - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(h_scroll->get_min(), 0) + transform.get_origin(), Point2(h_scroll->get_max(), 0) + transform.get_origin(), x_axis_color); - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(0, v_scroll->get_min()) + transform.get_origin(), Point2(0, v_scroll->get_max()) + transform.get_origin(), y_axis_color); if (box_selecting) { - Point2 bsfrom = transform.xform(drag_from); Point2 bsto = transform.xform(box_selecting_to); VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(bsfrom, bsto - bsfrom), Color(0.7, 0.7, 1.0, 0.3)); } + Color rotate_color(0.4, 0.7, 1.0, 0.8); if (drag == DRAG_ROTATE) { VisualServer::get_singleton()->canvas_item_add_line(ci, transform.xform(display_rotate_from), transform.xform(display_rotate_to), rotate_color); } +} + +void CanvasItemEditor::_draw_axis() { + RID ci = viewport->get_canvas_item(); + + Color x_axis_color(1.0, 0.4, 0.4, 0.6); + Color y_axis_color(0.4, 1.0, 0.4, 0.6); + Color area_axis_color(0.4, 0.4, 1.0, 0.4); + + Point2 origin = transform.get_origin(); + VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(0, origin.y), Point2(viewport->get_size().x, origin.y), x_axis_color); + VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(origin.x, 0), Point2(origin.x, viewport->get_size().y), y_axis_color); Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); @@ -2196,34 +2503,12 @@ void CanvasItemEditor::_viewport_draw() { }; for (int i = 0; i < 4; i++) { - VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color); } +} - for (List<LockList>::Element *E = lock_list.front(); E; E = E->next()) { - - Vector2 ofs = transform.xform(E->get().pos); - if (E->get().lock) { - - lock->draw(ci, ofs); - ofs.x += lock->get_width(); - } - if (E->get().group) { - - group->draw(ci, ofs); - } - } - - { - - EditorNode *en = editor; - EditorPluginList *over_plugin_list = en->get_editor_plugins_over(); - - if (!over_plugin_list->empty()) { - - over_plugin_list->forward_draw_over_canvas(transform, viewport); - } - } +void CanvasItemEditor::_draw_bones() { + RID ci = viewport->get_canvas_item(); if (skeleton_show_bones) { int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); @@ -2291,6 +2576,125 @@ void CanvasItemEditor::_viewport_draw() { } } +void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_xform) { + RID viewport_ci = viewport->get_canvas_item(); + + Transform2D transform_ci = p_xform; + CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); + if (ci) + transform_ci = transform_ci * ci->get_transform(); + + for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + _draw_locks_and_groups(p_node->get_child(i), transform_ci); + } + + if (ci) { + Ref<Texture> lock = get_icon("Lock", "EditorIcons"); + if (p_node->has_meta("_edit_lock_")) { + lock->draw(viewport_ci, transform_ci.xform(Point2(0, 0))); + } + + Ref<Texture> group = get_icon("Group", "EditorIcons"); + if (ci->has_meta("_edit_group_")) { + Vector2 ofs = transform_ci.xform(Point2(0, 0)); + if (ci->has_meta("_edit_lock_")) + ofs = Point2(ofs.x + lock->get_size().x, ofs.y); + group->draw(viewport_ci, ofs); + } + } +} + +void CanvasItemEditor::_build_bones_list(Node *p_node) { + for (int i = 0; i < p_node->get_child_count(); i++) { + _build_bones_list(p_node->get_child(i)); + } + + CanvasItem *c = Object::cast_to<CanvasItem>(p_node); + if (c && c->is_visible_in_tree()) { + if (c->has_meta("_edit_bone_")) { + + ObjectID id = c->get_instance_id(); + if (!bone_list.has(id)) { + BoneList bone; + bone.bone = id; + bone_list[id] = bone; + } + + bone_list[id].last_pass = bone_last_frame; + } + } +} + +void CanvasItemEditor::_get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform) { + for (int i = 0; i < p_node->get_child_count(); i++) { + _get_encompassing_rect(p_node->get_child(i), r_rect, p_xform); + } + + CanvasItem *c = Object::cast_to<CanvasItem>(p_node); + if (c && c->is_visible_in_tree()) { + Rect2 rect = c->get_item_rect(); + Transform2D xform = p_xform * c->get_transform(); + r_rect.expand_to(xform.xform(rect.position)); + r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); + r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); + r_rect.expand_to(xform.xform(rect.position + rect.size)); + } +} + +void CanvasItemEditor::_draw_viewport_base() { + if (show_rulers) + _draw_rulers(); + _draw_focus(); +} + +void CanvasItemEditor::_draw_viewport() { + + // hide/show buttons depending on the selection + bool all_locked = true; + bool all_group = true; + List<Node *> &selection = editor_selection->get_selected_node_list(); + if (selection.empty()) { + all_locked = false; + all_group = false; + } else { + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to<Control>(E->get()) && !Object::cast_to<Control>(E->get())->has_meta("_edit_lock_")) { + all_locked = false; + break; + } + } + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to<Control>(E->get()) && !Object::cast_to<Control>(E->get())->has_meta("_edit_group_")) { + all_group = false; + break; + } + } + } + + lock_button->set_visible(!all_locked); + lock_button->set_disabled(selection.empty()); + unlock_button->set_visible(all_locked); + group_button->set_visible(!all_group); + group_button->set_disabled(selection.empty()); + ungroup_button->set_visible(all_group); + + _update_scrollbars(); + + _draw_grid(); + _draw_selection(); + _draw_axis(); + _draw_locks_and_groups(editor->get_edited_scene(), transform); + + RID ci = viewport->get_canvas_item(); + VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D()); + + EditorPluginList *over_plugin_list = editor->get_editor_plugins_over(); + if (!over_plugin_list->empty()) { + over_plugin_list->forward_draw_over_canvas(transform, viewport); + } + _draw_focus(); +} + void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_FIXED_PROCESS) { @@ -2341,6 +2745,7 @@ void CanvasItemEditor::_notification(int p_what) { if (pivot != se->prev_pivot || anchors[MARGIN_LEFT] != se->prev_anchors[MARGIN_LEFT] || anchors[MARGIN_RIGHT] != se->prev_anchors[MARGIN_RIGHT] || anchors[MARGIN_TOP] != se->prev_anchors[MARGIN_TOP] || anchors[MARGIN_BOTTOM] != se->prev_anchors[MARGIN_BOTTOM]) { viewport->update(); + viewport_base->update(); se->prev_pivot = pivot; se->prev_anchors[MARGIN_LEFT] = anchors[MARGIN_LEFT]; se->prev_anchors[MARGIN_RIGHT] = anchors[MARGIN_RIGHT]; @@ -2389,6 +2794,9 @@ void CanvasItemEditor::_notification(int p_what) { list_select_button->set_icon(get_icon("ListSelect", "EditorIcons")); move_button->set_icon(get_icon("ToolMove", "EditorIcons")); rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); + snap_button->set_icon(get_icon("Snap", "EditorIcons")); + snap_config_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + skeleton_menu->set_icon(get_icon("Bone", "EditorIcons")); pan_button->set_icon(get_icon("ToolPan", "EditorIcons")); pivot_button->set_icon(get_icon("EditPivot", "EditorIcons")); select_handle = get_icon("EditorHandle", "EditorIcons"); @@ -2399,6 +2807,10 @@ void CanvasItemEditor::_notification(int p_what) { ungroup_button->set_icon(get_icon("Ungroup", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); + zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); + zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); + zoom_plus->set_icon(get_icon("ZoomMore", "EditorIcons")); + anchor_menu->set_icon(get_icon("Anchor", "EditorIcons")); PopupMenu *p = anchor_menu->get_popup(); @@ -2433,6 +2845,9 @@ void CanvasItemEditor::_notification(int p_what) { list_select_button->set_icon(get_icon("ListSelect", "EditorIcons")); move_button->set_icon(get_icon("ToolMove", "EditorIcons")); rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); + snap_button->set_icon(get_icon("Snap", "EditorIcons")); + snap_config_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + skeleton_menu->set_icon(get_icon("Bone", "EditorIcons")); pan_button->set_icon(get_icon("ToolPan", "EditorIcons")); pivot_button->set_icon(get_icon("EditPivot", "EditorIcons")); select_handle = get_icon("EditorHandle", "EditorIcons"); @@ -2474,66 +2889,23 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { drag = DRAG_NONE; + // Clear the selection editor_selection->clear(); //_clear_canvas_items(); editor_selection->add_node(p_canvas_item); //_add_canvas_item(p_canvas_item); viewport->update(); -} - -void CanvasItemEditor::_find_canvas_items_span(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform) { - - if (!p_node) - return; - - CanvasItem *c = Object::cast_to<CanvasItem>(p_node); - - for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - - //CanvasItem *r=NULL; - - if (c && !c->is_set_as_toplevel()) - _find_canvas_items_span(p_node->get_child(i), r_rect, p_xform * c->get_transform()); - else - _find_canvas_items_span(p_node->get_child(i), r_rect, Transform2D()); - } - - if (c && c->is_visible_in_tree()) { - - Rect2 rect = c->get_item_rect(); - Transform2D xform = p_xform * c->get_transform(); - - LockList lock; - lock.lock = c->has_meta("_edit_lock_"); - lock.group = c->has_meta("_edit_group_"); - - if (lock.group || lock.lock) { - lock.pos = xform.xform(rect.position); - lock_list.push_back(lock); - } - - if (c->has_meta("_edit_bone_")) { - - ObjectID id = c->get_instance_id(); - if (!bone_list.has(id)) { - BoneList bone; - bone.bone = id; - bone_list[id] = bone; - } - - bone_list[id].last_pass = bone_last_frame; - } - - r_rect.expand_to(xform.xform(rect.position)); - r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); - r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); - r_rect.expand_to(xform.xform(rect.position + rect.size)); - } + viewport_base->update(); } void CanvasItemEditor::_update_scrollbars() { updating_scroll = true; + if (show_rulers) + viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH)); + else + viewport_scrollable->set_begin(Point2()); + Size2 size = viewport->get_size(); Size2 hmin = h_scroll->get_minimum_size(); Size2 vmin = v_scroll->get_minimum_size(); @@ -2550,11 +2922,12 @@ void CanvasItemEditor::_update_scrollbars() { Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); - lock_list.clear(); bone_last_frame++; - if (editor->get_edited_scene()) - _find_canvas_items_span(editor->get_edited_scene(), canvas_item_rect, Transform2D()); + if (editor->get_edited_scene()) { + _build_bones_list(editor->get_edited_scene()); + _get_encompassing_rect(editor->get_edited_scene(), canvas_item_rect, Transform2D()); + } List<Map<ObjectID, BoneList>::Element *> bone_to_erase; @@ -2577,7 +2950,6 @@ void CanvasItemEditor::_update_scrollbars() { Point2 ofs; if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) { - v_scroll->hide(); ofs.y = canvas_item_rect.position.y; } else { @@ -2638,6 +3010,7 @@ void CanvasItemEditor::_update_scroll(float) { editor->get_scene_root()->set_global_canvas_transform(transform); viewport->update(); + viewport_base->update(); } void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { @@ -2684,88 +3057,117 @@ void CanvasItemEditor::_set_full_rect() { undo_redo->commit_action(); } +void CanvasItemEditor::_zoom_minus() { + if (zoom < MIN_ZOOM) + return; + zoom /= 2.0; + + _update_scroll(0); + viewport->update(); + viewport_base->update(); +} + +void CanvasItemEditor::_zoom_reset() { + zoom = 1; + _update_scroll(0); + viewport->update(); + viewport_base->update(); +} + +void CanvasItemEditor::_zoom_plus() { + if (zoom > MAX_ZOOM) + return; + + zoom *= 2.0; + _update_scroll(0); + viewport->update(); + viewport_base->update(); +} + +void CanvasItemEditor::_toggle_snap(bool p_status) { + snap_active = p_status; + viewport->update(); + viewport_base->update(); +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); switch (p_op) { - case SNAP_USE: { - snap_grid = !snap_grid; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE); - edit_menu->get_popup()->set_item_checked(idx, snap_grid); + case SHOW_GRID: { + show_grid = !show_grid; + int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); + view_menu->get_popup()->set_item_checked(idx, show_grid); viewport->update(); + viewport_base->update(); } break; - case SNAP_SHOW_GRID: { - snap_show_grid = !snap_show_grid; - int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID); - edit_menu->get_popup()->set_item_checked(idx, snap_show_grid); - viewport->update(); + case SNAP_USE_NODE_PARENT: { + snap_node_parent = !snap_node_parent; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); + smartsnap_config_popup->set_item_checked(idx, snap_node_parent); + } break; + case SNAP_USE_NODE_ANCHORS: { + snap_node_anchors = !snap_node_anchors; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->set_item_checked(idx, snap_node_anchors); + } break; + case SNAP_USE_NODE_SIDES: { + snap_node_sides = !snap_node_sides; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES); + smartsnap_config_popup->set_item_checked(idx, snap_node_sides); + } break; + case SNAP_USE_OTHER_NODES: { + snap_other_nodes = !snap_other_nodes; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES); + smartsnap_config_popup->set_item_checked(idx, snap_other_nodes); + } break; + case SNAP_USE_GRID: { + snap_grid = !snap_grid; + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); + snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); } break; case SNAP_USE_ROTATION: { snap_rotation = !snap_rotation; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); - edit_menu->get_popup()->set_item_checked(idx, snap_rotation); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); + snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation); } break; case SNAP_RELATIVE: { snap_relative = !snap_relative; - int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE); - edit_menu->get_popup()->set_item_checked(idx, snap_relative); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE); + snap_config_menu->get_popup()->set_item_checked(idx, snap_relative); viewport->update(); + viewport_base->update(); } break; case SNAP_USE_PIXEL: { snap_pixel = !snap_pixel; - int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); - edit_menu->get_popup()->set_item_checked(idx, snap_pixel); + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); + snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel); } break; case SNAP_CONFIGURE: { - ((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step); + ((SnapDialog *)snap_dialog)->set_fields(grid_offset, grid_step, snap_rotation_offset, snap_rotation_step); snap_dialog->popup_centered(Size2(220, 160)); } break; case SKELETON_SHOW_BONES: { skeleton_show_bones = !skeleton_show_bones; - int idx = skeleton_menu->get_item_index(SKELETON_SHOW_BONES); - skeleton_menu->set_item_checked(idx, skeleton_show_bones); + int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); + skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); viewport->update(); } break; - case ZOOM_IN: { - if (zoom > MAX_ZOOM) - return; - zoom = zoom * (1.0 / 0.5); - _update_scroll(0); + case SHOW_HELPERS: { + show_helpers = !show_helpers; + int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS); + view_menu->get_popup()->set_item_checked(idx, show_helpers); viewport->update(); - return; } break; - case ZOOM_OUT: { - if (zoom < MIN_ZOOM) - return; - - zoom = zoom * 0.5; - _update_scroll(0); - viewport->update(); - return; - - } break; - case ZOOM_RESET: { - - zoom = 1; - _update_scroll(0); + case SHOW_RULERS: { + show_rulers = !show_rulers; + int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); + view_menu->get_popup()->set_item_checked(idx, show_rulers); viewport->update(); - return; - + viewport_base->update(); } break; - case ZOOM_SET: { - updating_value_dialog = true; - - dialog_label->set_text(TTR("Zoom (%):")); - dialog_val->set_min(0.1); - dialog_val->set_step(0.1); - dialog_val->set_max(800); - dialog_val->set_value(zoom * 100); - value_dialog->popup_centered(Size2(200, 85)); - updating_value_dialog = false; - - } break; case LOCK_SELECTED: { List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -3112,7 +3514,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { n2d->set_meta("_edit_bone_", true); if (!skeleton_show_bones) - skeleton_menu->activate_item(skeleton_menu->get_item_index(SKELETON_SHOW_BONES)); + skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } viewport->update(); @@ -3131,7 +3533,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { n2d->set_meta("_edit_bone_", Variant()); if (!skeleton_show_bones) - skeleton_menu->activate_item(skeleton_menu->get_item_index(SKELETON_SHOW_BONES)); + skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } viewport->update(); @@ -3151,7 +3553,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { canvas_item->set_meta("_edit_ik_", true); if (!skeleton_show_bones) - skeleton_menu->activate_item(skeleton_menu->get_item_index(SKELETON_SHOW_BONES)); + skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } viewport->update(); @@ -3171,7 +3573,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { n2d->set_meta("_edit_ik_", Variant()); if (!skeleton_show_bones) - skeleton_menu->activate_item(skeleton_menu->get_item_index(SKELETON_SHOW_BONES)); + skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } viewport->update(); @@ -3234,15 +3636,19 @@ void CanvasItemEditor::_focus_selection(int p_op) { void CanvasItemEditor::_bind_methods() { + ClassDB::bind_method("_zoom_minus", &CanvasItemEditor::_zoom_minus); + ClassDB::bind_method("_zoom_reset", &CanvasItemEditor::_zoom_reset); + ClassDB::bind_method("_zoom_plus", &CanvasItemEditor::_zoom_plus); + ClassDB::bind_method("_toggle_snap", &CanvasItemEditor::_toggle_snap); ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll); ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback); - ClassDB::bind_method("_dialog_value_changed", &CanvasItemEditor::_dialog_value_changed); ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data); ClassDB::bind_method("_tool_select", &CanvasItemEditor::_tool_select); ClassDB::bind_method("_keying_changed", &CanvasItemEditor::_keying_changed); ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input); - ClassDB::bind_method("_viewport_draw", &CanvasItemEditor::_viewport_draw); - ClassDB::bind_method("_viewport_gui_input", &CanvasItemEditor::_viewport_gui_input); + ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); + ClassDB::bind_method("_draw_viewport_base", &CanvasItemEditor::_draw_viewport_base); + ClassDB::bind_method("_viewport_base_gui_input", &CanvasItemEditor::_viewport_base_gui_input); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); @@ -3284,64 +3690,92 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->set_area_as_parent_rect(); bottom_split = memnew(VSplitContainer); - bottom_split->set_v_size_flags(SIZE_EXPAND_FILL); add_child(bottom_split); + bottom_split->set_v_size_flags(SIZE_EXPAND_FILL); palette_split = memnew(HSplitContainer); - palette_split->set_v_size_flags(SIZE_EXPAND_FILL); bottom_split->add_child(palette_split); + palette_split->set_v_size_flags(SIZE_EXPAND_FILL); - Control *vp_base = memnew(Control); - vp_base->set_v_size_flags(SIZE_EXPAND_FILL); - palette_split->add_child(vp_base); - - ViewportContainer *vp = memnew(ViewportContainer); - vp->set_stretch(true); - vp_base->add_child(vp); - vp->set_area_as_parent_rect(); - vp->add_child(p_editor->get_scene_root()); + viewport_base = memnew(Control); + palette_split->add_child(viewport_base); + viewport_base->connect("draw", this, "_draw_viewport_base"); + viewport_base->connect("gui_input", this, "_viewport_base_gui_input"); + viewport_base->set_focus_mode(FOCUS_ALL); + viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); + viewport_base->set_h_size_flags(SIZE_EXPAND_FILL); + + viewport_scrollable = memnew(Control); + viewport_base->add_child(viewport_scrollable); + viewport_scrollable->set_mouse_filter(MOUSE_FILTER_PASS); + viewport_scrollable->set_draw_behind_parent(true); + viewport_scrollable->set_area_as_parent_rect(); + viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH)); + + ViewportContainer *scene_tree = memnew(ViewportContainer); + viewport_scrollable->add_child(scene_tree); + scene_tree->set_stretch(true); + scene_tree->set_area_as_parent_rect(); + scene_tree->add_child(p_editor->get_scene_root()); viewport = memnew(CanvasItemEditorViewport(p_editor, this)); - vp_base->add_child(viewport); + viewport_scrollable->add_child(viewport); + viewport->set_mouse_filter(MOUSE_FILTER_PASS); viewport->set_area_as_parent_rect(); viewport->set_clip_contents(true); + viewport->connect("draw", this, "_draw_viewport"); h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - viewport->add_child(h_scroll); - viewport->add_child(v_scroll); - viewport->connect("draw", this, "_viewport_draw"); - viewport->connect("gui_input", this, "_viewport_gui_input"); - h_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); - v_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); - h_scroll->hide(); + + v_scroll = memnew(VScrollBar); + viewport->add_child(v_scroll); + v_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); v_scroll->hide(); + + HBoxContainer *zoom_hb = memnew(HBoxContainer); + viewport->add_child(zoom_hb); + zoom_hb->set_begin(Point2(5, 5)); + + zoom_minus = memnew(ToolButton); + zoom_hb->add_child(zoom_minus); + zoom_minus->connect("pressed", this, "_zoom_minus"); + zoom_minus->set_focus_mode(FOCUS_NONE); + + zoom_reset = memnew(ToolButton); + zoom_hb->add_child(zoom_reset); + zoom_reset->connect("pressed", this, "_zoom_reset"); + zoom_reset->set_focus_mode(FOCUS_NONE); + + zoom_plus = memnew(ToolButton); + zoom_hb->add_child(zoom_plus); + zoom_plus->connect("pressed", this, "_zoom_plus"); + zoom_plus->set_focus_mode(FOCUS_NONE); + updating_scroll = false; - viewport->set_focus_mode(FOCUS_ALL); handle_len = 10; first_update = true; select_button = memnew(ToolButton); - select_button->set_toggle_mode(true); hb->add_child(select_button); + select_button->set_toggle_mode(true); select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_SELECT)); select_button->set_pressed(true); select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q)); select_button->set_tooltip(TTR("Select Mode") + " $sc\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate") + "\n" + TTR("Alt+Drag: Move") + "\n" + TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).") + "\n" + TTR("Alt+RMB: Depth list selection")); move_button = memnew(ToolButton); - move_button->set_toggle_mode(true); hb->add_child(move_button); + move_button->set_toggle_mode(true); move_button->connect("pressed", this, "_tool_select", make_binds(TOOL_MOVE)); move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W)); move_button->set_tooltip(TTR("Move Mode")); rotate_button = memnew(ToolButton); - rotate_button->set_toggle_mode(true); hb->add_child(rotate_button); + rotate_button->set_toggle_mode(true); rotate_button->connect("pressed", this, "_tool_select", make_binds(TOOL_ROTATE)); rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E)); rotate_button->set_tooltip(TTR("Rotate Mode")); @@ -3349,25 +3783,60 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(memnew(VSeparator)); list_select_button = memnew(ToolButton); - list_select_button->set_toggle_mode(true); hb->add_child(list_select_button); + list_select_button->set_toggle_mode(true); list_select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_LIST_SELECT)); list_select_button->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); pivot_button = memnew(ToolButton); - pivot_button->set_toggle_mode(true); hb->add_child(pivot_button); + pivot_button->set_toggle_mode(true); pivot_button->connect("pressed", this, "_tool_select", make_binds(TOOL_EDIT_PIVOT)); pivot_button->set_tooltip(TTR("Click to change object's rotation pivot.")); pan_button = memnew(ToolButton); - pan_button->set_toggle_mode(true); hb->add_child(pan_button); + pan_button->set_toggle_mode(true); pan_button->connect("pressed", this, "_tool_select", make_binds(TOOL_PAN)); pan_button->set_tooltip(TTR("Pan Mode")); hb->add_child(memnew(VSeparator)); + snap_button = memnew(ToolButton); + hb->add_child(snap_button); + snap_button->set_toggle_mode(true); + snap_button->connect("toggled", this, "_toggle_snap"); + snap_button->set_tooltip(TTR("Toggles snapping")); + snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_S)); + + snap_config_menu = memnew(MenuButton); + hb->add_child(snap_config_menu); + snap_config_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + snap_config_menu->set_h_size_flags(SIZE_SHRINK_END); + snap_config_menu->set_tooltip(TTR("Snapping options")); + + PopupMenu *p = snap_config_menu->get_popup(); + p->connect("id_pressed", this, "_popup_callback"); + p->set_hide_on_checkable_item_selection(false); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to grid")), SNAP_USE_GRID); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap...")), SNAP_CONFIGURE); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL); + p->add_submenu_item(TTR("Smart snapping"), "SmartSnapping"); + + smartsnap_config_popup = memnew(PopupMenu); + p->add_child(smartsnap_config_popup); + smartsnap_config_popup->set_name("SmartSnapping"); + smartsnap_config_popup->connect("id_pressed", this, "_popup_callback"); + smartsnap_config_popup->set_hide_on_checkable_item_selection(false); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to parent")), SNAP_USE_NODE_PARENT); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to node anchor")), SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to node sides")), SNAP_USE_NODE_SIDES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to other nodes")), SNAP_USE_OTHER_NODES); + + hb->add_child(memnew(VSeparator)); + lock_button = memnew(ToolButton); hb->add_child(lock_button); @@ -3391,35 +3860,23 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(memnew(VSeparator)); - edit_menu = memnew(MenuButton); - edit_menu->set_text(TTR("Edit")); - hb->add_child(edit_menu); - edit_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + skeleton_menu = memnew(MenuButton); + hb->add_child(skeleton_menu); - PopupMenu *p; - p = edit_menu->get_popup(); + p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap")), SNAP_USE); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid")), SNAP_SHOW_GRID); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap..")), SNAP_CONFIGURE); + p->connect("id_pressed", this, "_popup_callback"); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES); p->add_separator(); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES); p->add_separator(); - p->add_submenu_item(TTR("Skeleton.."), "skeleton"); - skeleton_menu = memnew(PopupMenu); - p->add_child(skeleton_menu); - skeleton_menu->set_name("skeleton"); - skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); - skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES); - skeleton_menu->add_separator(); - skeleton_menu->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES); - skeleton_menu->add_separator(); - skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN); - skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN); - skeleton_menu->set_hide_on_checkable_item_selection(false); - skeleton_menu->connect("id_pressed", this, "_popup_callback"); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN); + p->set_hide_on_checkable_item_selection(false); + p->connect("id_pressed", this, "_popup_callback"); + + hb->add_child(memnew(VSeparator)); view_menu = memnew(MenuButton); view_menu->set_text(TTR("View")); @@ -3427,11 +3884,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { view_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); p = view_menu->get_popup(); - - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_in", TTR("Zoom In")), ZOOM_IN); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_out", TTR("Zoom Out")), ZOOM_OUT); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset")), ZOOM_RESET); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_set", TTR("Zoom Set..")), ZOOM_SET); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid"), KEY_G), SHOW_GRID); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show helpers"), KEY_H), SHOW_HELPERS); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show rulers"), KEY_R), SHOW_RULERS); p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); @@ -3502,23 +3957,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_dialog->connect("confirmed", this, "_snap_changed"); add_child(snap_dialog); - value_dialog = memnew(AcceptDialog); - value_dialog->set_title(TTR("Set a Value")); - value_dialog->get_ok()->set_text(TTR("Close")); - add_child(value_dialog); - - Label *l = memnew(Label); - l->set_text(TTR("Snap (Pixels):")); - l->set_position(Point2(5, 5)); - value_dialog->add_child(l); - dialog_label = l; - - dialog_val = memnew(SpinBox); - dialog_val->set_anchor(MARGIN_RIGHT, ANCHOR_END); - dialog_val->set_begin(Point2(15, 25)); - dialog_val->set_end(Point2(-10, 25)); - value_dialog->add_child(dialog_val); - dialog_val->connect("value_changed", this, "_dialog_value_changed"); select_sb = Ref<StyleBoxTexture>(memnew(StyleBoxTexture)); selection_menu = memnew(PopupMenu); @@ -3527,22 +3965,35 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { selection_menu->connect("id_pressed", this, "_selection_result_pressed"); selection_menu->connect("popup_hide", this, "_selection_menu_hide"); + drag_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/drag_pivot", TTR("Drag pivot from mouse position"), KEY_MASK_SHIFT | KEY_V); + set_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/set_pivot", TTR("Set pivot at mouse position"), KEY_V); + + multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY); + divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE); + key_pos = true; key_rot = true; key_scale = false; + show_grid = false; + show_helpers = false; + show_rulers = false; zoom = 1; - snap_offset = Vector2(0, 0); - snap_step = Vector2(10, 10); + grid_offset = Point2(); + grid_step = Point2(10, 10); + grid_step_multiplier = 0; snap_rotation_offset = 0; snap_rotation_step = 15 / (180 / Math_PI); - snap_grid = false; - snap_show_grid = false; + snap_active = false; + snap_node_parent = true; + snap_node_anchors = true; + snap_node_sides = true; + snap_other_nodes = true; + snap_grid = true; snap_rotation = false; snap_pixel = false; skeleton_show_bones = true; - skeleton_menu->set_item_checked(skeleton_menu->get_item_index(SKELETON_SHOW_BONES), true); - updating_value_dialog = false; + skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true); box_selecting = false; //zoom=0.5; singleton = this; @@ -3573,7 +4024,7 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) { canvas_item_editor->show(); canvas_item_editor->set_fixed_process(true); VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport_rid(), false); - canvas_item_editor->viewport->grab_focus(); + canvas_item_editor->viewport_base->grab_focus(); } else { @@ -3742,7 +4193,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & target_pos -= texture_size / 2; } // there's nothing to be used as source position so snapping will work as absolute if enabled - target_pos = canvas->snap_point(target_pos, Vector2()); + target_pos = canvas->snap_point(target_pos); editor_data->get_undo_redo().add_do_method(child, "set_position", target_pos); } @@ -3776,22 +4227,13 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name); editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); - Point2 pos; - Node2D *parent_node2d = Object::cast_to<Node2D>(parent); - if (parent_node2d) { - pos = parent_node2d->get_global_position(); - } else { - Control *parent_control = Object::cast_to<Control>(parent); - if (parent_control) { - pos = parent_control->get_global_position(); - } + CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent); + if (parent_ci) { + Vector2 target_pos = canvas->get_canvas_transform().affine_inverse().xform(p_point); + target_pos = canvas->snap_point(target_pos); + target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos); + editor_data->get_undo_redo().add_do_method(instanced_scene, "set_position", target_pos); } - Transform2D trans = canvas->get_canvas_transform(); - Vector2 target_pos = (p_point - trans.get_origin()) / trans.get_scale().x - pos; - // in relative snapping it may be useful for the user to take the original node position into account - Vector2 start_pos = Object::cast_to<Node2D>(instanced_scene) ? Object::cast_to<Node2D>(instanced_scene)->get_position() : target_pos; - target_pos = canvas->snap_point(target_pos, start_pos); - editor_data->get_undo_redo().add_do_method(instanced_scene, "set_position", target_pos); return true; } @@ -3978,41 +4420,39 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte editor_data = editor->get_scene_tree_dock()->get_editor_data(); canvas = p_canvas; preview_node = memnew(Node2D); + accept = memnew(AcceptDialog); editor->get_gui_base()->add_child(accept); selector = memnew(AcceptDialog); + editor->get_gui_base()->add_child(selector); selector->set_title(TTR("Change default type")); + selector->connect("confirmed", this, "_on_change_type"); VBoxContainer *vbc = memnew(VBoxContainer); + selector->add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); vbc->set_v_size_flags(SIZE_EXPAND_FILL); vbc->set_custom_minimum_size(Size2(200, 260) * EDSCALE); selector_label = memnew(Label); + vbc->add_child(selector_label); selector_label->set_align(Label::ALIGN_CENTER); selector_label->set_valign(Label::VALIGN_BOTTOM); selector_label->set_custom_minimum_size(Size2(0, 30) * EDSCALE); - vbc->add_child(selector_label); - - button_group.instance(); btn_group = memnew(VBoxContainer); + vbc->add_child(btn_group); btn_group->set_h_size_flags(0); + button_group.instance(); for (int i = 0; i < types.size(); i++) { CheckBox *check = memnew(CheckBox); + btn_group->add_child(check); check->set_text(types[i]); check->connect("button_down", this, "_on_select_type", varray(check)); - btn_group->add_child(check); check->set_button_group(button_group); } - vbc->add_child(btn_group); - - selector->connect("confirmed", this, "_on_change_type"); - - selector->add_child(vbc); - editor->get_gui_base()->add_child(selector); label = memnew(Label); label->add_color_override("font_color_shadow", Color(0, 0, 0, 1)); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index f61bfc9ebb..af834507e0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -70,7 +70,6 @@ class CanvasItemEditor : public VBoxContainer { EditorNode *editor; enum Tool { - TOOL_SELECT, TOOL_LIST_SELECT, TOOL_MOVE, @@ -82,15 +81,18 @@ class CanvasItemEditor : public VBoxContainer { enum MenuOption { SNAP_USE, - SNAP_SHOW_GRID, + SNAP_USE_NODE_PARENT, + SNAP_USE_NODE_ANCHORS, + SNAP_USE_NODE_SIDES, + SNAP_USE_OTHER_NODES, + SNAP_USE_GRID, SNAP_USE_ROTATION, SNAP_RELATIVE, SNAP_CONFIGURE, SNAP_USE_PIXEL, - ZOOM_IN, - ZOOM_OUT, - ZOOM_RESET, - ZOOM_SET, + SHOW_GRID, + SHOW_HELPERS, + SHOW_RULERS, LOCK_SELECTED, UNLOCK_SELECTED, GROUP_SELECTED, @@ -163,6 +165,8 @@ class CanvasItemEditor : public VBoxContainer { Tool tool; bool first_update; Control *viewport; + Control *viewport_base; + Control *viewport_scrollable; bool can_move_pivot; @@ -170,14 +174,28 @@ class CanvasItemEditor : public VBoxContainer { VScrollBar *v_scroll; HBoxContainer *hb; + ToolButton *zoom_minus; + ToolButton *zoom_reset; + ToolButton *zoom_plus; + Transform2D transform; + bool show_grid; + bool show_rulers; + bool show_helpers; float zoom; - Vector2 snap_offset; - Vector2 snap_step; + + Point2 grid_offset; + Point2 grid_step; + int grid_step_multiplier; + float snap_rotation_step; float snap_rotation_offset; + bool snap_active; + bool snap_node_parent; + bool snap_node_anchors; + bool snap_node_sides; + bool snap_other_nodes; bool snap_grid; - bool snap_show_grid; bool snap_rotation; bool snap_relative; bool snap_pixel; @@ -204,18 +222,6 @@ class CanvasItemEditor : public VBoxContainer { Vector<_SelectResult> selection_results; - struct LockList { - Point2 pos; - bool lock; - bool group; - LockList() { - lock = false; - group = false; - } - }; - - List<LockList> lock_list; - struct BoneList { Transform2D xform; @@ -255,6 +261,10 @@ class CanvasItemEditor : public VBoxContainer { ToolButton *move_button; ToolButton *rotate_button; + ToolButton *snap_button; + MenuButton *snap_config_menu; + PopupMenu *smartsnap_config_popup; + ToolButton *pivot_button; ToolButton *pan_button; @@ -264,8 +274,7 @@ class CanvasItemEditor : public VBoxContainer { ToolButton *group_button; ToolButton *ungroup_button; - MenuButton *edit_menu; - PopupMenu *skeleton_menu; + MenuButton *skeleton_menu; MenuButton *view_menu; HBoxContainer *animation_hb; MenuButton *animation_menu; @@ -278,6 +287,9 @@ class CanvasItemEditor : public VBoxContainer { PopupMenu *selection_menu; + Control *top_ruler; + Control *left_ruler; + //PopupMenu *popup; DragType drag; Point2 drag_from; @@ -290,6 +302,11 @@ class CanvasItemEditor : public VBoxContainer { Ref<Texture> select_handle; Ref<Texture> anchor_handle; + Ref<ShortCut> drag_pivot_shortcut; + Ref<ShortCut> set_pivot_shortcut; + Ref<ShortCut> multiply_grid_step_shortcut; + Ref<ShortCut> divide_grid_step_shortcut; + int handle_len; bool _is_part_of_subscene(CanvasItem *p_item); void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, Vector<_SelectResult> &r_items, int limit = 0); @@ -300,10 +317,6 @@ class CanvasItemEditor : public VBoxContainer { ConfirmationDialog *snap_dialog; - AcceptDialog *value_dialog; - Label *dialog_label; - SpinBox *dialog_val; - CanvasItem *ref_item; void _edit_set_pivot(const Vector2 &mouse_pos); @@ -317,9 +330,8 @@ class CanvasItemEditor : public VBoxContainer { void _prepare_drag(const Point2 &p_click_pos); DragType _get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point); - float _anchor_snap(float p_anchor, bool *p_snapped = NULL, float p_opposite_anchor = -1); - Vector2 _anchor_to_position(Control *p_control, Vector2 anchor); - Vector2 _position_to_anchor(Control *p_control, Vector2 position); + Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor); + Vector2 _position_to_anchor(const Control *p_control, Vector2 position); void _popup_callback(int p_op); bool updating_scroll; @@ -330,7 +342,6 @@ class CanvasItemEditor : public VBoxContainer { void incend(float &beg, float &end, float inc, float minsize, bool p_symmetric); void _append_canvas_item(CanvasItem *p_item); - void _dialog_value_changed(double); void _snap_changed(); void _selection_result_pressed(int); void _selection_menu_hide(); @@ -339,26 +350,49 @@ class CanvasItemEditor : public VBoxContainer { Point2 _find_topleftmost_point(); - void _find_canvas_items_span(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform); + void _build_bones_list(Node *p_node); + + void _get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform); Object *_get_editor_data(Object *p_what); - CanvasItem *get_single_item(); + CanvasItem *_get_single_item(); int get_item_count(); void _keying_changed(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); + void _draw_text_at_position(Point2 p_position, String p_string, Margin p_side); + void _draw_margin_at_position(int p_value, Point2 p_position, Margin p_side); void _draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side); - void _viewport_gui_input(const Ref<InputEvent> &p_event); - void _viewport_draw(); + void _draw_rulers(); + void _draw_focus(); + void _draw_grid(); + void _draw_selection(); + void _draw_axis(); + void _draw_bones(); + void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform); + + void _draw_viewport(); + + void _viewport_base_gui_input(const Ref<InputEvent> &p_event); + void _draw_viewport_base(); void _focus_selection(int p_op); + void _snap_if_closer(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation = 0.0, float p_radius = 10.0); + void _snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap); + void _set_anchors_preset(Control::LayoutPreset p_preset); void _set_full_rect(); + void _zoom_minus(); + void _zoom_reset(); + void _zoom_plus(); + + void _toggle_snap(bool p_status); + HSplitContainer *palette_split; VSplitContainer *bottom_split; @@ -402,7 +436,18 @@ protected: static CanvasItemEditor *singleton; public: - Vector2 snap_point(Vector2 p_target, Vector2 p_start = Vector2(0, 0)) const; + enum SnapMode { + SNAP_GRID = 1 << 0, + SNAP_PIXEL = 1 << 1, + SNAP_NODE_PARENT = 1 << 2, + SNAP_NODE_ANCHORS = 1 << 3, + SNAP_NODE_SIDES = 1 << 4, + SNAP_OTHER_NODES = 1 << 5, + + SNAP_DEFAULT = 0x03, + }; + + Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, const CanvasItem *p_canvas_item = NULL, unsigned int p_forced_modes = 0); float snap_angle(float p_target, float p_start = 0) const; Transform2D get_canvas_transform() const { return transform; } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 3917c700f0..8c4e1b8f27 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -52,7 +52,7 @@ void Polygon2DEditor::_notification(int p_what) { uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons")); b_snap_grid->set_icon(get_icon("Grid", "EditorIcons")); - b_snap_enable->set_icon(get_icon("Snap", "EditorIcons")); + b_snap_enable->set_icon(get_icon("SnapGrid", "EditorIcons")); uv_icon_zoom->set_texture(get_icon("Zoom", "EditorIcons")); get_tree()->connect("node_removed", this, "_node_removed"); diff --git a/main/main.cpp b/main/main.cpp index bb601198db..04375666a9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -248,7 +248,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph performance = memnew(Performance); globals->add_singleton(ProjectSettings::Singleton("Performance", performance)); - GLOBAL_DEF("debug/settings/backtrace/message", String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues")); + GLOBAL_DEF("debug/settings/crash_handler/message", String("Please include this when reporting the bug on https://github.com/godotengine/godot/issues")); MAIN_PRINT("Main: Parse CMDLine"); diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index 645db2826e..d9b20254a8 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -37,6 +37,7 @@ #include "test_image.h" #include "test_io.h" #include "test_math.h" +#include "test_oa_hash_map.h" #include "test_ordered_hash_map.h" #include "test_physics.h" #include "test_physics_2d.h" @@ -56,6 +57,7 @@ const char **tests_get_names() { "io", "shaderlang", "physics", + "oa_hash_map", NULL }; @@ -89,6 +91,11 @@ MainLoop *test_main(String p_test, const List<String> &p_args) { return TestRender::test(); } + if (p_test == "oa_hash_map") { + + return TestOAHashMap::test(); + } + #ifndef _3D_DISABLED if (p_test == "gui") { diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp new file mode 100644 index 0000000000..302c259262 --- /dev/null +++ b/main/tests/test_oa_hash_map.cpp @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* test_oa_hash_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "test_oa_hash_map.h" + +#include "core/os/os.h" + +#include "core/oa_hash_map.h" + +namespace TestOAHashMap { + +MainLoop *test() { + + OS::get_singleton()->print("\n\n\nHello from test\n"); + + // test element tracking. + { + OAHashMap<int, int> map; + + map.set(42, 1337); + map.set(1337, 21); + map.set(42, 11880); + + int value; + map.lookup(42, &value); + + OS::get_singleton()->print("capacity %d\n", map.get_capacity()); + OS::get_singleton()->print("elements %d\n", map.get_num_elements()); + + OS::get_singleton()->print("map[42] = %d\n", value); + } + + // rehashing and deletion + { + OAHashMap<int, int> map; + + for (int i = 0; i < 500; i++) { + map.set(i, i * 2); + } + + for (int i = 0; i < 500; i += 2) { + map.remove(i); + } + + uint32_t num_elems = 0; + for (int i = 0; i < 500; i++) { + int tmp; + if (map.lookup(i, &tmp)) + num_elems++; + } + + OS::get_singleton()->print("elements %d == %d.\n", map.get_num_elements(), num_elems); + } + + // iteration + { + OAHashMap<String, int> map; + + map.set("Hello", 1); + map.set("World", 2); + map.set("Godot rocks", 42); + + for (OAHashMap<String, int>::Iterator it = map.iter(); it.valid; it = map.next_iter(it)) { + OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.data); + } + } + + return NULL; +} +} // namespace TestOAHashMap diff --git a/main/tests/test_oa_hash_map.h b/main/tests/test_oa_hash_map.h new file mode 100644 index 0000000000..92b4bc5e5a --- /dev/null +++ b/main/tests/test_oa_hash_map.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* test_oa_hash_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 TEST_OA_HASH_MAP_H +#define TEST_OA_HASH_MAP_H + +#include "os/main_loop.h" + +namespace TestOAHashMap { + +MainLoop *test(); +} +#endif // TEST_OA_HASH_MAP_H diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 8cc8b0bec8..93a9bac11c 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -40,8 +40,8 @@ const String init_symbol = "godot_gdnative_init"; const String terminate_symbol = "godot_gdnative_terminate"; -#define GDAPI_FUNC(name, ret_type, ...) .name = name, -#define GDAPI_FUNC_VOID(name, ...) .name = name, +#define GDAPI_FUNC(name, ret_type, ...) name, +#define GDAPI_FUNC_VOID(name, ...) name, const godot_gdnative_api_struct api_struct = { GODOT_GDNATIVE_API_FUNCTIONS diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp index 1b2aae607f..9ba4166c1d 100644 --- a/modules/gdnative/gdnative/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -480,10 +480,9 @@ godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const g return self->hash_compare(*other); } -godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self, godot_bool *r_valid) { +godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self) { const Variant *self = (const Variant *)p_self; - bool &valid = *r_valid; - return self->booleanize(valid); + return self->booleanize(); } void GDAPI godot_variant_destroy(godot_variant *p_self) { diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 04dca6f56d..1c5e91d733 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -237,12 +237,12 @@ godot_variant GDAPI godot_method_bind_call(godot_method_bind *p_method_bind, god struct godot_gdnative_api_struct; // Forward declaration typedef struct { - const struct godot_gdnative_api_struct *api_struct; godot_bool in_editor; uint64_t core_api_hash; uint64_t editor_api_hash; uint64_t no_api_hash; godot_object *gd_native_library; // pointer to GDNativeLibrary that is being initialized + const struct godot_gdnative_api_struct *api_struct; } godot_gdnative_init_options; typedef struct { diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h index 969506585d..7b804c1eaf 100644 --- a/modules/gdnative/include/gdnative/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -190,7 +190,7 @@ godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other); -godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self, godot_bool *r_valid); +godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self); void GDAPI godot_variant_destroy(godot_variant *p_self); diff --git a/modules/gdnative/include/gdnative_api_struct.h b/modules/gdnative/include/gdnative_api_struct.h index cfebeb08cc..c345e27227 100644 --- a/modules/gdnative/include/gdnative_api_struct.h +++ b/modules/gdnative/include/gdnative_api_struct.h @@ -534,7 +534,7 @@ extern "C" { GDAPI_FUNC(godot_variant_operator_equal, godot_bool, const godot_variant *p_self, const godot_variant *p_other) \ GDAPI_FUNC(godot_variant_operator_less, godot_bool, const godot_variant *p_self, const godot_variant *p_other) \ GDAPI_FUNC(godot_variant_hash_compare, godot_bool, const godot_variant *p_self, const godot_variant *p_other) \ - GDAPI_FUNC(godot_variant_booleanize, godot_bool, const godot_variant *p_self, godot_bool *r_valid) \ + GDAPI_FUNC(godot_variant_booleanize, godot_bool, const godot_variant *p_self) \ GDAPI_FUNC_VOID(godot_variant_destroy, godot_variant *p_self) \ GDAPI_FUNC_VOID(godot_string_new, godot_string *r_dest) \ GDAPI_FUNC_VOID(godot_string_new_copy, godot_string *r_dest, const godot_string *p_src) \ diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index ddee7b2521..df7b16c96e 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -271,6 +271,8 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (ScriptDebugger::get_singleton()) GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line); +#define GD_ERR_BREAK(m_cond) ERR_BREAK(m_cond) + #define CHECK_SPACE(m_space) \ ERR_BREAK((ip + m_space) > _code_size) @@ -281,6 +283,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a break; #else +#define GD_ERR_BREAK(m_cond) #define CHECK_SPACE(m_space) #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ @@ -302,9 +305,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #endif bool exit_ok = false; +#ifdef DEBUG_ENABLED while (ip < _code_size) { - int last_opcode = _code_ptr[ip]; +#else + while (true) { +#endif + switch (_code_ptr[ip]) { case OPCODE_OPERATOR: { @@ -313,21 +320,21 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; Variant::Operator op = (Variant::Operator)_code_ptr[ip + 1]; - ERR_BREAK(op >= Variant::OP_MAX); + GD_ERR_BREAK(op >= Variant::OP_MAX); GET_VARIANT_PTR(a, 2); GET_VARIANT_PTR(b, 3); GET_VARIANT_PTR(dst, 4); #ifdef DEBUG_ENABLED + Variant ret; Variant::evaluate(op, *a, *b, ret, valid); #else Variant::evaluate(op, *a, *b, *dst, valid); #endif - - if (!valid) { #ifdef DEBUG_ENABLED + if (!valid) { if (ret.get_type() == Variant::STRING) { //return a string when invalid with the error @@ -336,13 +343,10 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } else { err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'."; } -#endif break; } -#ifdef DEBUG_ENABLED *dst = ret; #endif - ip += 5; continue; } @@ -355,7 +359,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(dst, 3); #ifdef DEBUG_ENABLED - if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) { err_text = "Left operand of 'is' is not an instance of anything."; @@ -367,7 +370,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a break; } #endif - Object *obj_A = *a; Object *obj_B = *b; @@ -399,12 +401,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GDNativeClass *nc = Object::cast_to<GDNativeClass>(obj_B); +#ifdef DEBUG_ENABLED if (!nc) { err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; break; } - +#endif extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); } @@ -423,6 +426,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; dst->set(*index, *value, &valid); +#ifdef DEBUG_ENABLED if (!valid) { String v = index->operator String(); if (v != "") { @@ -433,7 +437,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "')."; break; } - +#endif ip += 4; continue; } @@ -453,6 +457,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a *dst = src->get(*index, &valid); #endif +#ifdef DEBUG_ENABLED if (!valid) { String v = index->operator String(); if (v != "") { @@ -463,7 +468,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "')."; break; } -#ifdef DEBUG_ENABLED *dst = ret; #endif ip += 4; @@ -478,18 +482,19 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a int indexname = _code_ptr[ip + 2]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; bool valid; dst->set_named(*index, *value, &valid); +#ifdef DEBUG_ENABLED if (!valid) { String err_type; err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "')."; break; } - +#endif ip += 4; continue; } @@ -502,7 +507,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a int indexname = _code_ptr[ip + 2]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; bool valid; @@ -513,7 +518,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #else *dst = src->get_named(*index, &valid); #endif - +#ifdef DEBUG_ENABLED if (!valid) { if (src->has_method(*index)) { err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' ?"; @@ -522,7 +527,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } break; } -#ifdef DEBUG_ENABLED *dst = ret; #endif ip += 4; @@ -532,7 +536,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; GET_VARIANT_PTR(src, 2); @@ -554,7 +558,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a CHECK_SPACE(3); int indexname = _code_ptr[ip + 1]; - ERR_BREAK(indexname < 0 || indexname >= _global_names_count); + GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; GET_VARIANT_PTR(dst, 2); bool ok = ClassDB::get_property(p_instance->owner, *index, *dst); @@ -615,11 +619,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a Variant::CallError err; *dst = Variant::construct(t, (const Variant **)argptrs, argc, err); +#ifdef DEBUG_ENABLED if (err.error != Variant::CallError::CALL_OK) { err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs); break; } +#endif ip += 4 + argc; //construct a basic type @@ -677,10 +683,10 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(base, 2); int nameg = _code_ptr[ip + 3]; - ERR_BREAK(nameg < 0 || nameg >= _global_names_count); + GD_ERR_BREAK(nameg < 0 || nameg >= _global_names_count); const StringName *methodname = &_global_names_ptr[nameg]; - ERR_BREAK(argc < 0); + GD_ERR_BREAK(argc < 0); ip += 4; CHECK_SPACE(argc + 1); Variant **argptrs = call_args; @@ -711,7 +717,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (GDScriptLanguage::get_singleton()->profiling) { function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; } -#endif if (err.error != Variant::CallError::CALL_OK) { @@ -742,6 +747,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs); break; } +#endif //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack); ip += argc + 1; @@ -753,7 +759,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GDFunctions::Function func = GDFunctions::Function(_code_ptr[ip + 1]); int argc = _code_ptr[ip + 2]; - ERR_BREAK(argc < 0); + GD_ERR_BREAK(argc < 0); ip += 3; CHECK_SPACE(argc + 1); @@ -770,6 +776,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GDFunctions::call(func, (const Variant **)argptrs, argc, *dst, err); +#ifdef DEBUG_ENABLED if (err.error != Variant::CallError::CALL_OK) { String methodstr = GDFunctions::get_func_name(func); @@ -781,6 +788,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } break; } +#endif ip += argc + 1; continue; } @@ -792,8 +800,8 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a CHECK_SPACE(2); int self_fun = _code_ptr[ip + 1]; -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (self_fun < 0 || self_fun >= _global_names_count) { err_text = "compiler bug, function name not found"; @@ -898,10 +906,11 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a retvalue = gdfs; if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) { + //do the oneshot connect GET_VARIANT_PTR(argobj, 1); GET_VARIANT_PTR(argname, 2); - //do the oneshot connect +#ifdef DEBUG_ENABLED if (argobj->get_type() != Variant::OBJECT) { err_text = "First argument of yield() not of type object."; break; @@ -910,6 +919,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a err_text = "Second argument of yield() not a string (for signal name)."; break; } +#endif Object *obj = argobj->operator Object *(); String signal = argname->operator String(); #ifdef DEBUG_ENABLED @@ -932,10 +942,12 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a #endif Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); +#ifdef DEBUG_ENABLED if (err != OK) { err_text = "Error connecting to signal: " + signal + " during yield()."; break; } +#endif } exit_ok = true; @@ -944,10 +956,12 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a case OPCODE_YIELD_RESUME: { CHECK_SPACE(2); +#ifdef DEBUG_ENABLED if (!p_state) { err_text = ("Invalid Resume (bug?)"); break; } +#endif GET_VARIANT_PTR(result, 1); *result = p_state->result; ip += 2; @@ -958,7 +972,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a CHECK_SPACE(2); int to = _code_ptr[ip + 1]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; continue; } @@ -968,18 +982,11 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(test, 1); - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { + bool result = test->booleanize(); - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } -#endif if (result) { int to = _code_ptr[ip + 2]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; continue; } @@ -992,18 +999,11 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(test, 1); - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { + bool result = test->booleanize(); - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } -#endif if (!result) { int to = _code_ptr[ip + 2]; - ERR_BREAK(to < 0 || to > _code_size); + GD_ERR_BREAK(to < 0 || to > _code_size); ip = to; continue; } @@ -1033,23 +1033,26 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; if (!container->iter_init(*counter, valid)) { +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "'."; break; } +#endif int jumpto = _code_ptr[ip + 3]; - ERR_BREAK(jumpto < 0 || jumpto > _code_size); + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; continue; } GET_VARIANT_PTR(iterator, 4); *iterator = container->iter_get(*counter, valid); +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "'."; break; } - +#endif ip += 5; //skip regular iterate which is always next continue; } @@ -1062,23 +1065,26 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a bool valid; if (!container->iter_next(*counter, valid)) { +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?)."; break; } +#endif int jumpto = _code_ptr[ip + 3]; - ERR_BREAK(jumpto < 0 || jumpto > _code_size); + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; continue; } GET_VARIANT_PTR(iterator, 4); *iterator = container->iter_get(*counter, valid); +#ifdef DEBUG_ENABLED if (!valid) { err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?)."; break; } - +#endif ip += 5; //loop again continue; } @@ -1087,14 +1093,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(test, 1); #ifdef DEBUG_ENABLED - bool valid; - bool result = test->booleanize(valid); - - if (!valid) { - - err_text = "cannot evaluate conditional expression of type: " + Variant::get_type_name(test->get_type()); - break; - } + bool result = test->booleanize(); if (!result) { @@ -1103,7 +1102,6 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } #endif - ip += 2; continue; } @@ -1157,6 +1155,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } } +#ifdef DEBUG_ENABLED if (exit_ok) break; //error @@ -1182,6 +1181,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT); } +#endif break; } diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 67d2a6e369..f103035b27 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -172,14 +172,14 @@ static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent if (!is_canvas_focused()) { focus_canvas(); } - mask |= 1 << ev->get_button_index(); - } else if (mask & (1 << ev->get_button_index())) { - mask &= ~(1 << ev->get_button_index()); + mask |= ev->get_button_index(); + } else if (mask & ev->get_button_index()) { + mask &= ~ev->get_button_index(); } else { // release event, but press was outside the canvas, so ignore return false; } - ev->set_button_mask(mask >> 1); + ev->set_button_mask(mask); _input->parse_input_event(ev); // prevent selection dragging @@ -200,7 +200,7 @@ static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *m Ref<InputEventMouseMotion> ev; ev.instance(); dom2godot_mod(mouse_event, ev); - ev->set_button_mask(input_mask >> 1); + ev->set_button_mask(input_mask); ev->set_position(pos); ev->set_global_position(ev->get_position()); @@ -227,7 +227,7 @@ static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel Ref<InputEventMouseButton> ev; ev.instance(); - ev->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev->set_button_mask(_input->get_mouse_button_mask()); ev->set_position(_input->get_mouse_position()); ev->set_global_position(ev->get_position()); @@ -291,7 +291,7 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent * Ref<InputEventMouseButton> ev_mouse; ev_mouse.instance(); - ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev_mouse->set_button_mask(_input->get_mouse_button_mask()); dom2godot_mod(touch_event, ev_mouse); const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; @@ -334,7 +334,7 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t Ref<InputEventMouseMotion> ev_mouse; ev_mouse.instance(); dom2godot_mod(touch_event, ev_mouse); - ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev_mouse->set_button_mask(_input->get_mouse_button_mask()); const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 9239573734..2ed88db309 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -43,6 +43,7 @@ #include <dlfcn.h> #include <execinfo.h> #include <signal.h> +#include <stdlib.h> #include <mach-o/dyld.h> #include <mach-o/getsect.h> @@ -77,7 +78,7 @@ static void handle_crash(int sig) { void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); String _execpath = OS::get_singleton()->get_executable_path(); - String msg = GLOBAL_GET("debug/settings/backtrace/message"); + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp index c9385f36d6..2f5ee7956e 100644 --- a/platform/windows/crash_handler_win.cpp +++ b/platform/windows/crash_handler_win.cpp @@ -116,7 +116,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { DWORD cbNeeded; std::vector<HMODULE> module_handles(1); - if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler()) { + if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) { return EXCEPTION_CONTINUE_SEARCH; } @@ -159,7 +159,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { IMAGE_NT_HEADERS *h = ImageNtHeader(base); DWORD image_type = h->FileHeader.Machine; int n = 0; - String msg = GLOBAL_GET("debug/settings/backtrace/message"); + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp index 4450cb3670..cff2cbad42 100644 --- a/platform/windows/godot_win.cpp +++ b/platform/windows/godot_win.cpp @@ -156,32 +156,36 @@ int widechar_main(int argc, wchar_t **argv) { return os.get_exit_code(); }; -int main(int _argc, char **_argv) { -// _argc and _argv are ignored -// we are going to use the WideChar version of them instead +int _main() { + LPWSTR *wc_argv; + int argc; + int result; -#ifdef CRASH_HANDLER_EXCEPTION - __try { -#endif - LPWSTR *wc_argv; - int argc; - int result; + wc_argv = CommandLineToArgvW(GetCommandLineW(), &argc); + + if (NULL == wc_argv) { + wprintf(L"CommandLineToArgvW failed\n"); + return 0; + } - wc_argv = CommandLineToArgvW(GetCommandLineW(), &argc); + result = widechar_main(argc, wc_argv); - if (NULL == wc_argv) { - wprintf(L"CommandLineToArgvW failed\n"); - return 0; - } + LocalFree(wc_argv); + return result; +} - result = widechar_main(argc, wc_argv); +int main(int _argc, char **_argv) { +// _argc and _argv are ignored +// we are going to use the WideChar version of them instead - LocalFree(wc_argv); - return result; #ifdef CRASH_HANDLER_EXCEPTION + __try { + return _main(); } __except (CrashHandlerException(GetExceptionInformation())) { return 1; } +#else + return _main(); #endif } diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index c926b7799d..3c54d5cbc2 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -39,6 +39,7 @@ #include <dlfcn.h> #include <execinfo.h> #include <signal.h> +#include <stdlib.h> static void handle_crash(int sig) { if (OS::get_singleton() == NULL) @@ -47,7 +48,7 @@ static void handle_crash(int sig) { void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); String _execpath = OS::get_singleton()->get_executable_path(); - String msg = GLOBAL_GET("debug/settings/backtrace/message"); + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 5b00aab2ef..4477e9ecf9 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -283,7 +283,7 @@ void GraphEdit::_notification(int p_what) { zoom_minus->set_icon(get_icon("minus")); zoom_reset->set_icon(get_icon("reset")); zoom_plus->set_icon(get_icon("more")); - snap_button->set_icon(get_icon("snap")); + snap_button->set_icon(get_icon("SnapGrid")); //zoom_icon->set_texture( get_icon("Zoom", "EditorIcons")); } if (p_what == NOTIFICATION_DRAW) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 1272e5a946..2fbd5ebdd3 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -879,7 +879,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("minus", "GraphEdit", make_icon(icon_zoom_less_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("snap", "GraphEdit", make_icon(icon_snap_png)); + theme->set_icon("SnapGrid", "GraphEdit", make_icon(icon_snap_grid_png)); 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_major", "GraphEdit", Color(1, 1, 1, 0.2)); diff --git a/scene/resources/default_theme/icon_snap.png b/scene/resources/default_theme/icon_snap_grid.png Binary files differindex 93194d34e7..44db9bdfdc 100644 --- a/scene/resources/default_theme/icon_snap.png +++ b/scene/resources/default_theme/icon_snap_grid.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index bed4bdb760..6643b4ccc1 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -182,8 +182,8 @@ static const unsigned char icon_reload_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, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x1, 0x59, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x31, 0x4b, 0x3, 0x31, 0x18, 0x86, 0x9f, 0x5c, 0xe, 0xec, 0x20, 0x76, 0x70, 0x39, 0xd0, 0xe3, 0xba, 0x74, 0x51, 0x41, 0xdd, 0xfc, 0x7, 0xba, 0xb8, 0x8, 0x52, 0xec, 0x2e, 0x4e, 0x3a, 0x88, 0x3f, 0xa3, 0xa3, 0x53, 0x7, 0x47, 0x67, 0x41, 0x17, 0xdd, 0x9c, 0x5c, 0x3b, 0xd8, 0x82, 0x64, 0x68, 0xca, 0xd5, 0x55, 0x11, 0x3c, 0x1a, 0x87, 0x24, 0x2e, 0xd7, 0x72, 0x9e, 0x9e, 0xab, 0xbe, 0xd3, 0xc7, 0x1b, 0x9e, 0x37, 0xf9, 0xbe, 0x7c, 0xf0, 0xd7, 0x12, 0x3f, 0x99, 0x4a, 0xa9, 0xb9, 0x30, 0xc, 0x8f, 0x84, 0x10, 0x7, 0xc0, 0x5a, 0x6e, 0x3f, 0x3a, 0xe7, 0x2e, 0xad, 0xb5, 0xdd, 0x66, 0xb3, 0xf9, 0x51, 0x19, 0x90, 0xa6, 0xe9, 0x92, 0xb5, 0xf6, 0x6, 0x58, 0xaf, 0xb8, 0xb4, 0x27, 0xa5, 0xdc, 0x8d, 0xe3, 0xf8, 0x19, 0x20, 0x28, 0xdf, 0x5c, 0x80, 0x53, 0x60, 0xdf, 0x18, 0x53, 0x37, 0xc6, 0xd4, 0xbd, 0xf7, 0x7b, 0xc0, 0x13, 0xb0, 0x61, 0xad, 0xbd, 0x56, 0x4a, 0xcd, 0x1, 0x84, 0xc5, 0x80, 0x30, 0xc, 0x8f, 0xa6, 0xb0, 0x94, 0x72, 0x33, 0x8e, 0xe3, 0x97, 0xc2, 0xf1, 0xd5, 0x68, 0x34, 0xba, 0xf7, 0xde, 0xf7, 0x80, 0xd, 0x29, 0xe5, 0x21, 0x70, 0xfe, 0xe5, 0x5, 0x42, 0x88, 0x76, 0x5e, 0x9e, 0x96, 0x60, 0x0, 0x92, 0x24, 0x79, 0x75, 0xce, 0x9d, 0x1, 0x4, 0x41, 0xd0, 0xfe, 0xd6, 0x2, 0xb0, 0x2, 0x60, 0x8c, 0xb9, 0xab, 0xe8, 0x1f, 0x6b, 0xed, 0x6d, 0x5e, 0xae, 0xce, 0x2, 0xb4, 0xd6, 0x17, 0x5a, 0x6b, 0x7, 0xcc, 0x3, 0xd4, 0x6a, 0xb5, 0x37, 0xad, 0xf5, 0xc5, 0x4f, 0x1, 0x41, 0x10, 0x7c, 0x19, 0x7c, 0x90, 0x3, 0xc7, 0xc0, 0xa0, 0xe0, 0xf, 0x72, 0xef, 0x9b, 0x84, 0x10, 0xdb, 0x79, 0xd9, 0x9f, 0x5, 0x44, 0x51, 0x94, 0x9, 0x21, 0x5a, 0x40, 0x6, 0x64, 0x42, 0x88, 0x56, 0x14, 0x45, 0x59, 0x19, 0x1e, 0x8f, 0xc7, 0x8b, 0x52, 0xca, 0xe, 0x80, 0x73, 0xee, 0x12, 0xa, 0xbf, 0x90, 0x24, 0x49, 0x7f, 0x38, 0x1c, 0x1e, 0x3, 0x34, 0x1a, 0x8d, 0x7e, 0x11, 0x54, 0x4a, 0x2d, 0x48, 0x29, 0x77, 0xac, 0xb5, 0x1d, 0xef, 0xfd, 0x32, 0xd0, 0x9b, 0x4c, 0x26, 0x5d, 0xa8, 0xd8, 0xc4, 0xb2, 0xf2, 0xf9, 0x4c, 0x55, 0xbd, 0x48, 0xbf, 0xe8, 0x1d, 0x78, 0x70, 0xce, 0x9d, 0x64, 0x59, 0xb6, 0x35, 0x85, 0xff, 0x87, 0x3e, 0x1, 0x53, 0x7, 0x87, 0x11, 0xd3, 0x3a, 0x9b, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; -static const unsigned char icon_snap_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, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xc2, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xad, 0x90, 0xbd, 0xa, 0xc2, 0x30, 0x14, 0x85, 0xbf, 0x5b, 0x5c, 0x23, 0xe8, 0x6c, 0x9f, 0xc1, 0xb7, 0xd0, 0x47, 0xd1, 0x47, 0x70, 0x48, 0xa1, 0x43, 0x57, 0xe9, 0xd3, 0x88, 0x93, 0xef, 0xe0, 0xea, 0x5c, 0x9d, 0x1d, 0xb2, 0x96, 0xc6, 0xa1, 0x9, 0xd4, 0xd8, 0xd8, 0x4a, 0xfd, 0x20, 0x10, 0xee, 0xcf, 0xe1, 0xdc, 0x3, 0x1d, 0x8c, 0x31, 0xd6, 0x18, 0x63, 0x9, 0x88, 0xd5, 0x1, 0x92, 0xbe, 0xe2, 0x2f, 0x4c, 0x16, 0x90, 0x98, 0xb5, 0x21, 0x94, 0x52, 0xf2, 0x17, 0x7, 0x6f, 0x7c, 0xb, 0x2b, 0xc6, 0x64, 0x7, 0xb3, 0xa1, 0x1, 0x5b, 0x14, 0x29, 0x50, 0x2, 0x1b, 0x0, 0x44, 0x2e, 0x34, 0xcd, 0x41, 0xb2, 0xec, 0x6, 0x20, 0xdd, 0x61, 0x6f, 0xdf, 0x7, 0xe4, 0x96, 0xaf, 0xc0, 0x32, 0xd0, 0x7d, 0x2, 0x6b, 0xd1, 0xba, 0x4a, 0xfc, 0xdd, 0x91, 0xdb, 0x4b, 0xb7, 0x7c, 0x2, 0x56, 0xd4, 0x75, 0xa, 0x9c, 0x81, 0x5, 0x70, 0x84, 0xe1, 0xc, 0x5a, 0xdb, 0x75, 0xbd, 0x17, 0xad, 0x1f, 0x92, 0xe7, 0x77, 0x60, 0xe7, 0x7a, 0x5b, 0x80, 0x44, 0x29, 0x25, 0xfe, 0xf5, 0x8, 0x28, 0x0, 0xb7, 0xd8, 0x46, 0xa0, 0x75, 0xe5, 0xbe, 0xf3, 0x31, 0xe, 0x7e, 0x23, 0xcc, 0xc2, 0x3a, 0xc2, 0xb9, 0x58, 0xfd, 0x83, 0xc9, 0x2, 0x63, 0x78, 0x1, 0x4a, 0x50, 0x70, 0x86, 0xcc, 0x86, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +static const unsigned char icon_snap_grid_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, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 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, 0x0, 0xc2, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xad, 0x90, 0xbd, 0xa, 0xc2, 0x30, 0x14, 0x85, 0xbf, 0x5b, 0x5c, 0x23, 0xe8, 0x6c, 0x9f, 0xc1, 0xb7, 0xd0, 0x47, 0xd1, 0x47, 0x70, 0x48, 0xa1, 0x43, 0x57, 0xe9, 0xd3, 0x88, 0x93, 0xef, 0xe0, 0xea, 0x5c, 0x9d, 0x1d, 0xb2, 0x96, 0xc6, 0xa1, 0x9, 0xd4, 0xd8, 0xd8, 0x4a, 0xfd, 0x20, 0x10, 0xee, 0xcf, 0xe1, 0xdc, 0x3, 0x1d, 0x8c, 0x31, 0xd6, 0x18, 0x63, 0x9, 0x88, 0xd5, 0x1, 0x92, 0xbe, 0xe2, 0x2f, 0x4c, 0x16, 0x90, 0x98, 0xb5, 0x21, 0x94, 0x52, 0xf2, 0x17, 0x7, 0x6f, 0x7c, 0xb, 0x2b, 0xc6, 0x64, 0x7, 0xb3, 0xa1, 0x1, 0x5b, 0x14, 0x29, 0x50, 0x2, 0x1b, 0x0, 0x44, 0x2e, 0x34, 0xcd, 0x41, 0xb2, 0xec, 0x6, 0x20, 0xdd, 0x61, 0x6f, 0xdf, 0x7, 0xe4, 0x96, 0xaf, 0xc0, 0x32, 0xd0, 0x7d, 0x2, 0x6b, 0xd1, 0xba, 0x4a, 0xfc, 0xdd, 0x91, 0xdb, 0x4b, 0xb7, 0x7c, 0x2, 0x56, 0xd4, 0x75, 0xa, 0x9c, 0x81, 0x5, 0x70, 0x84, 0xe1, 0xc, 0x5a, 0xdb, 0x75, 0xbd, 0x17, 0xad, 0x1f, 0x92, 0xe7, 0x77, 0x60, 0xe7, 0x7a, 0x5b, 0x80, 0x44, 0x29, 0x25, 0xfe, 0xf5, 0x8, 0x28, 0x0, 0xb7, 0xd8, 0x46, 0xa0, 0x75, 0xe5, 0xbe, 0xf3, 0x31, 0xe, 0x7e, 0x23, 0xcc, 0xc2, 0x3a, 0xc2, 0xb9, 0x58, 0xfd, 0x83, 0xc9, 0x2, 0x63, 0x78, 0x1, 0x4a, 0x50, 0x70, 0x86, 0xcc, 0x86, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_stop_png[] = { |