diff options
-rw-r--r-- | editor/input_event_configuration_dialog.cpp | 6 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 127 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 112 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs | 57 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs | 4 | ||||
-rw-r--r-- | modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs | 49 | ||||
-rw-r--r-- | modules/mono/glue/runtime_interop.cpp | 10 | ||||
-rw-r--r-- | scene/main/window.cpp | 68 | ||||
-rw-r--r-- | scene/main/window.h | 2 |
9 files changed, 346 insertions, 89 deletions
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 48e3759e57..37c2fd5f1e 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -685,9 +685,9 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { // Key Mode Selection key_mode = memnew(OptionButton); - key_mode->add_item("Keycode (Latin equvialent)", KEYMODE_KEYCODE); - key_mode->add_item("Physical Keycode (poistion of US QWERTY keyboard)", KEYMODE_PHY_KEYCODE); - key_mode->add_item("Unicode (case-insencetive)", KEYMODE_UNICODE); + key_mode->add_item(TTR("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE); + key_mode->add_item(TTR("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE); + key_mode->add_item(TTR("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE); key_mode->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected)); key_mode->hide(); additional_options_container->add_child(key_mode); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index f4646d1d3c..a61c5403b9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -189,10 +189,15 @@ namespace Godot.Collections /// <summary> /// Resizes this <see cref="Array"/> to the given size. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="newSize">The new size of the array.</param> /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) { + ThrowIfReadOnly(); + var self = (godot_array)NativeValue; return NativeFuncs.godotsharp_array_resize(ref self, newSize); } @@ -200,8 +205,13 @@ namespace Godot.Collections /// <summary> /// Shuffles the contents of this <see cref="Array"/> into a random order. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Shuffle() { + ThrowIfReadOnly(); + var self = (godot_array)NativeValue; NativeFuncs.godotsharp_array_shuffle(ref self); } @@ -240,6 +250,9 @@ namespace Godot.Collections /// <summary> /// Returns the item at the given <paramref name="index"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the array is read-only. + /// </exception> /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value> public unsafe Variant this[int index] { @@ -250,8 +263,11 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)NativeValue; godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); godot_variant* itemPtr = &ptrw[index]; @@ -264,9 +280,14 @@ namespace Godot.Collections /// Adds an item to the end of this <see cref="Array"/>. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The <see cref="Variant"/> item to add.</param> public void Add(Variant item) { + ThrowIfReadOnly(); + godot_variant variantValue = (godot_variant)item.NativeVar; var self = (godot_array)NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); @@ -282,6 +303,9 @@ namespace Godot.Collections /// <summary> /// Erases all items from this <see cref="Array"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Clear() => Resize(0); /// <summary> @@ -303,10 +327,15 @@ namespace Godot.Collections /// or the position at the end of the array. /// Existing items will be moved to the right. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index to insert at.</param> /// <param name="item">The <see cref="Variant"/> item to insert.</param> public void Insert(int index, Variant item) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -319,9 +348,14 @@ namespace Godot.Collections /// Removes the first occurrence of the specified <paramref name="item"/> /// from this <see cref="Array"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The value to remove.</param> public bool Remove(Variant item) { + ThrowIfReadOnly(); + int index = IndexOf(item); if (index >= 0) { @@ -335,9 +369,14 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Array"/> by index. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -358,7 +397,28 @@ namespace Godot.Collections object ICollection.SyncRoot => false; - bool ICollection<Variant>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the array is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Array"/> read-only, i.e. disabled modying of the + /// array's elements. Does not apply to nested content, e.g. content of + /// nested arrays. + /// </summary> + public void MakeReadOnly() + { + if (IsReadOnly) + { + // Avoid interop call when the array is already read-only. + return; + } + + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_make_read_only(ref self); + } /// <summary> /// Copies the elements of this <see cref="Array"/> to the given @@ -472,6 +532,14 @@ namespace Godot.Collections { elem = NativeValue.DangerousSelfRef.Elements[index]; } + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Array instance is read-only."); + } + } } internal interface IGenericGodotArray @@ -592,6 +660,9 @@ namespace Godot.Collections /// <summary> /// Resizes this <see cref="Array{T}"/> to the given size. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="newSize">The new size of the array.</param> /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) @@ -602,6 +673,9 @@ namespace Godot.Collections /// <summary> /// Shuffles the contents of this <see cref="Array{T}"/> into a random order. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Shuffle() { _underlyingArray.Shuffle(); @@ -634,6 +708,9 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="index"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the array is read-only. + /// </exception> /// <value>The value at the given <paramref name="index"/>.</value> public unsafe T this[int index] { @@ -644,8 +721,11 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)_underlyingArray.NativeValue; godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); godot_variant* itemPtr = &ptrw[index]; @@ -673,10 +753,15 @@ namespace Godot.Collections /// or the position at the end of the array. /// Existing items will be moved to the right. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index to insert at.</param> /// <param name="item">The item to insert.</param> public void Insert(int index, T item) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -688,6 +773,9 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Array{T}"/> by index. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { @@ -703,16 +791,35 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count => _underlyingArray.Count; - bool ICollection<T>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the array is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => _underlyingArray.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Array{T}"/> read-only, i.e. disabled modying of the + /// array's elements. Does not apply to nested content, e.g. content of + /// nested arrays. + /// </summary> + public void MakeReadOnly() + { + _underlyingArray.MakeReadOnly(); + } /// <summary> /// Adds an item to the end of this <see cref="Array{T}"/>. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The item to add.</param> /// <returns>The new size after adding the item.</returns> public void Add(T item) { + ThrowIfReadOnly(); + using var variantValue = VariantUtils.CreateFrom(item); var self = (godot_array)_underlyingArray.NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); @@ -721,6 +828,9 @@ namespace Godot.Collections /// <summary> /// Erases all items from this <see cref="Array{T}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Clear() { _underlyingArray.Clear(); @@ -769,10 +879,15 @@ namespace Godot.Collections /// Removes the first occurrence of the specified value /// from this <see cref="Array{T}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The value to remove.</param> /// <returns>A <see langword="bool"/> indicating success or failure.</returns> public bool Remove(T item) { + ThrowIfReadOnly(); + int index = IndexOf(item); if (index >= 0) { @@ -812,5 +927,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>(); + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Array instance is read-only."); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index fa304c1b94..923b2adafd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -93,10 +93,15 @@ namespace Godot.Collections /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> /// is <see langword="true"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="dictionary">Dictionary to copy entries from.</param> /// <param name="overwrite">If duplicate keys should be copied over as well.</param> public void Merge(Dictionary dictionary, bool overwrite = false) { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; var other = (godot_dictionary)dictionary.NativeValue; NativeFuncs.godotsharp_dictionary_merge(ref self, in other, overwrite.ToGodotBool()); @@ -175,8 +180,12 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="key"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the dictionary is read-only. + /// </exception> /// <exception cref="KeyNotFoundException"> - /// An entry for <paramref name="key"/> does not exist in the dictionary. + /// The property is retrieved and an entry for <paramref name="key"/> + /// does not exist in the dictionary. /// </exception> /// <value>The value at the given <paramref name="key"/>.</value> public Variant this[Variant key] @@ -197,6 +206,8 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; NativeFuncs.godotsharp_dictionary_set_value(ref self, (godot_variant)key.NativeVar, (godot_variant)value.NativeVar); @@ -207,6 +218,9 @@ namespace Godot.Collections /// Adds an value <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <exception cref="ArgumentException"> /// An entry for <paramref name="key"/> already exists in the dictionary. /// </exception> @@ -214,6 +228,8 @@ namespace Godot.Collections /// <param name="value">The value to add.</param> public void Add(Variant key, Variant value) { + ThrowIfReadOnly(); + var variantKey = (godot_variant)key.NativeVar; var self = (godot_dictionary)NativeValue; @@ -230,8 +246,13 @@ namespace Godot.Collections /// <summary> /// Clears the dictionary, removing all entries from it. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> public void Clear() { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; NativeFuncs.godotsharp_dictionary_clear(ref self); } @@ -267,15 +288,22 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Dictionary"/> by key. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key of the element to remove.</param> public bool Remove(Variant key) { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool(); } bool ICollection<KeyValuePair<Variant, Variant>>.Remove(KeyValuePair<Variant, Variant> item) { + ThrowIfReadOnly(); + godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, @@ -311,7 +339,28 @@ namespace Godot.Collections } } - bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the dictionary is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Dictionary"/> read-only, i.e. disabled modying of the + /// dictionary's elements. Does not apply to nested content, e.g. content of + /// nested dictionaries. + /// </summary> + public void MakeReadOnly() + { + if (IsReadOnly) + { + // Avoid interop call when the dictionary is already read-only. + return; + } + + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_make_read_only(ref self); + } /// <summary> /// Gets the value for the given <paramref name="key"/> in the dictionary. @@ -397,6 +446,14 @@ namespace Godot.Collections using (str) return Marshaling.ConvertStringToManaged(str); } + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Dictionary instance is read-only."); + } + } } internal interface IGenericGodotDictionary @@ -508,6 +565,9 @@ namespace Godot.Collections /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> /// is <see langword="true"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="dictionary">Dictionary to copy entries from.</param> /// <param name="overwrite">If duplicate keys should be copied over as well.</param> public void Merge(Dictionary<TKey, TValue> dictionary, bool overwrite = false) @@ -536,6 +596,13 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="key"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the dictionary is read-only. + /// </exception> + /// <exception cref="KeyNotFoundException"> + /// The property is retrieved and an entry for <paramref name="key"/> + /// does not exist in the dictionary. + /// </exception> /// <value>The value at the given <paramref name="key"/>.</value> public TValue this[TKey key] { @@ -557,6 +624,8 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); using var variantValue = VariantUtils.CreateFrom(value); var self = (godot_dictionary)_underlyingDict.NativeValue; @@ -616,10 +685,15 @@ namespace Godot.Collections /// Adds an object <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary{TKey, TValue}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key at which to add the object.</param> /// <param name="value">The object to add.</param> public void Add(TKey key, TValue value) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; @@ -645,9 +719,14 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key of the element to remove.</param> public bool Remove(TKey key) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); @@ -683,7 +762,21 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count => _underlyingDict.Count; - bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the dictionary is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => _underlyingDict.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Dictionary{TKey, TValue}"/> read-only, i.e. disabled + /// modying of the dictionary's elements. Does not apply to nested content, + /// e.g. content of nested dictionaries. + /// </summary> + public void MakeReadOnly() + { + _underlyingDict.MakeReadOnly(); + } void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value); @@ -691,6 +784,9 @@ namespace Godot.Collections /// <summary> /// Clears the dictionary, removing all entries from it. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> public void Clear() => _underlyingDict.Clear(); bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) @@ -740,6 +836,8 @@ namespace Godot.Collections bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(item.Key); var self = (godot_dictionary)_underlyingDict.NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, @@ -789,5 +887,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotDictionary<TKey, TValue>(); + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Dictionary instance is read-only."); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index c295e500de..43e7c7eb9a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -697,6 +697,9 @@ namespace Godot.NativeInterop private uint _safeRefCount; public VariantVector _arrayVector; + + private unsafe godot_variant* _readOnly; + // There are more fields here, but we don't care as we never store this in C# public readonly int Size @@ -704,6 +707,12 @@ namespace Godot.NativeInterop [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _arrayVector.Size; } + + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _readOnly != null; + } } [StructLayout(LayoutKind.Sequential)] @@ -737,6 +746,12 @@ namespace Godot.NativeInterop get => _p != null ? _p->Size : 0; } + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _p != null && _p->IsReadOnly; + } + public unsafe void Dispose() { if (_p == null) @@ -766,35 +781,59 @@ namespace Godot.NativeInterop // A correctly constructed value needs to call the native default constructor to allocate `_p`. // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming public ref struct godot_dictionary { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal readonly unsafe godot_dictionary* GetUnsafeAddress() - => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _p)); + => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper)); - private IntPtr _p; + [FieldOffset(0)] private byte _getUnsafeAddressHelper; - public readonly bool IsAllocated + [FieldOffset(0)] private unsafe DictionaryPrivate* _p; + + [StructLayout(LayoutKind.Sequential)] + private struct DictionaryPrivate + { + private uint _safeRefCount; + + private unsafe godot_variant* _readOnly; + + // There are more fields here, but we don't care as we never store this in C# + + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _readOnly != null; + } + } + + public readonly unsafe bool IsAllocated { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _p != IntPtr.Zero; + get => _p != null; } - public void Dispose() + public readonly unsafe bool IsReadOnly { - if (_p == IntPtr.Zero) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _p != null && _p->IsReadOnly; + } + + public unsafe void Dispose() + { + if (_p == null) return; NativeFuncs.godotsharp_dictionary_destroy(ref this); - _p = IntPtr.Zero; + _p = null; } [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming internal struct movable { - private IntPtr _p; + private unsafe DictionaryPrivate* _p; public static unsafe explicit operator movable(in godot_dictionary value) => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index d116284e1c..1e23689c95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -376,6 +376,8 @@ namespace Godot.NativeInterop public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + public static partial void godotsharp_array_make_read_only(ref godot_array p_self); + public static partial void godotsharp_array_shuffle(ref godot_array p_self); public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str); @@ -416,6 +418,8 @@ namespace Godot.NativeInterop public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, in godot_variant p_key); + public static partial void godotsharp_dictionary_make_read_only(ref godot_dictionary p_self); + public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); // StringExtensions diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index cb9525b49c..df67e075ac 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -69,19 +69,6 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the strings begins - /// with the given string <paramref name="text"/>. - /// </summary> - /// <param name="instance">The string to check.</param> - /// <param name="text">The beginning string.</param> - /// <returns>If the string begins with the given string.</returns> - [Obsolete("Use string.StartsWith instead.")] - public static bool BeginsWith(this string instance, string text) - { - return instance.StartsWith(text); - } - - /// <summary> /// Returns the bigrams (pairs of consecutive letters) of this string. /// </summary> /// <param name="instance">The string that will be used.</param> @@ -618,7 +605,7 @@ namespace Godot } else { - if (instance.BeginsWith("/")) + if (instance.StartsWith('/')) { rs = instance.Substring(1); directory = "/"; @@ -1199,23 +1186,6 @@ namespace Godot } /// <summary> - /// Returns a copy of the string with characters removed from the left. - /// The <paramref name="chars"/> argument is a string specifying the set of characters - /// to be removed. - /// Note: The <paramref name="chars"/> is not a prefix. See <see cref="TrimPrefix"/> - /// method that will remove a single prefix string rather than a set of characters. - /// </summary> - /// <seealso cref="RStrip(string, string)"/> - /// <param name="instance">The string to remove characters from.</param> - /// <param name="chars">The characters to be removed.</param> - /// <returns>A copy of the string with characters removed from the left.</returns> - [Obsolete("Use string.TrimStart instead.")] - public static string LStrip(this string instance, string chars) - { - return instance.TrimStart(chars.ToCharArray()); - } - - /// <summary> /// Do a simple expression match, where '*' matches zero or more /// arbitrary characters and '?' matches any single character except '.'. /// </summary> @@ -1504,23 +1474,6 @@ namespace Godot } /// <summary> - /// Returns a copy of the string with characters removed from the right. - /// The <paramref name="chars"/> argument is a string specifying the set of characters - /// to be removed. - /// Note: The <paramref name="chars"/> is not a suffix. See <see cref="TrimSuffix"/> - /// method that will remove a single suffix string rather than a set of characters. - /// </summary> - /// <seealso cref="LStrip(string, string)"/> - /// <param name="instance">The string to remove characters from.</param> - /// <param name="chars">The characters to be removed.</param> - /// <returns>A copy of the string with characters removed from the right.</returns> - [Obsolete("Use string.TrimEnd instead.")] - public static string RStrip(this string instance, string chars) - { - return instance.TrimEnd(chars.ToCharArray()); - } - - /// <summary> /// Returns the SHA-1 hash of the string as an array of bytes. /// </summary> /// <seealso cref="Sha1Text(string)"/> diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index e7f50d2caa..d17fe3e75f 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -1012,6 +1012,10 @@ int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) { return (int32_t)p_self->resize(p_new_size); } +void godotsharp_array_make_read_only(Array *p_self) { + p_self->make_read_only(); +} + void godotsharp_array_shuffle(Array *p_self) { p_self->shuffle(); } @@ -1081,6 +1085,10 @@ bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) return p_self->erase(*p_key); } +void godotsharp_dictionary_make_read_only(Dictionary *p_self) { + p_self->make_read_only(); +} + void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) { *r_str = Variant(*p_self).operator String(); } @@ -1439,6 +1447,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_array_insert, (void *)godotsharp_array_remove_at, (void *)godotsharp_array_resize, + (void *)godotsharp_array_make_read_only, (void *)godotsharp_array_shuffle, (void *)godotsharp_array_to_string, (void *)godotsharp_dictionary_try_get_value, @@ -1454,6 +1463,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_dictionary_merge, (void *)godotsharp_dictionary_recursive_equal, (void *)godotsharp_dictionary_remove_key, + (void *)godotsharp_dictionary_make_read_only, (void *)godotsharp_dictionary_to_string, (void *)godotsharp_string_simplify_path, (void *)godotsharp_string_to_camel_case, diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 3ceb27b3e6..869d12b4df 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -663,18 +663,18 @@ void Window::set_visible(bool p_visible) { return; } - visible = p_visible; - if (!is_inside_tree()) { + visible = p_visible; return; } - if (updating_child_controls) { - _update_child_controls(); - } - ERR_FAIL_COND_MSG(get_parent() == nullptr, "Can't change visibility of main window."); + visible = p_visible; + + // Stop any queued resizing, as the window will be resized right now. + updating_child_controls = false; + Viewport *embedder_vp = _get_embedder(); if (!embedder_vp) { @@ -833,7 +833,7 @@ bool Window::is_visible() const { } void Window::_update_window_size() { - // Force window to respect size limitations of rendering server + // Force window to respect size limitations of rendering server. RenderingServer *rendering_server = RenderingServer::get_singleton(); if (rendering_server) { Size2i max_window_size = rendering_server->get_maximum_viewport_size(); @@ -1130,6 +1130,7 @@ void Window::_notification(int p_what) { case NOTIFICATION_READY: { if (wrap_controls) { + // Finish any resizing immediately so it doesn't interfere on stuff overriding _ready(). _update_child_controls(); } } break; @@ -1138,9 +1139,7 @@ void Window::_notification(int p_what) { _invalidate_theme_cache(); _update_theme_item_cache(); - if (embedder) { - embedder->_sub_window_update(this); - } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) { String tr_title = atr(title); #ifdef DEBUG_ENABLED if (window_id == DisplayServer::MAIN_WINDOW_ID) { @@ -1152,8 +1151,6 @@ void Window::_notification(int p_what) { #endif DisplayServer::get_singleton()->window_set_title(tr_title, window_id); } - - child_controls_changed(); } break; case NOTIFICATION_EXIT_TREE: { @@ -1254,8 +1251,13 @@ Vector<Vector2> Window::get_mouse_passthrough_polygon() const { void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; - if (wrap_controls) { - child_controls_changed(); + + if (!is_inside_tree()) { + return; + } + + if (updating_child_controls) { + _update_child_controls(); } else { _update_window_size(); } @@ -1282,23 +1284,23 @@ Size2 Window::_get_contents_minimum_size() const { return max; } -void Window::_update_child_controls() { - if (!updating_child_controls) { +void Window::child_controls_changed() { + if (!is_inside_tree() || !visible || updating_child_controls) { return; } - _update_window_size(); - - updating_child_controls = false; + updating_child_controls = true; + call_deferred(SNAME("_update_child_controls")); } -void Window::child_controls_changed() { - if (!is_inside_tree() || !visible || updating_child_controls) { +void Window::_update_child_controls() { + if (!updating_child_controls) { return; } - updating_child_controls = true; - call_deferred(SNAME("_update_child_controls")); + _update_window_size(); + + updating_child_controls = false; } bool Window::_can_consume_input_events() const { @@ -1647,7 +1649,24 @@ void Window::_invalidate_theme_cache() { void Window::_update_theme_item_cache() { // Request an update on the next frame to reflect theme changes. // Updating without a delay can cause a lot of lag. - child_controls_changed(); + if (!wrap_controls) { + updating_embedded_window = true; + call_deferred(SNAME("_update_embedded_window")); + } else { + child_controls_changed(); + } +} + +void Window::_update_embedded_window() { + if (!updating_embedded_window) { + return; + } + + if (embedder) { + embedder->_sub_window_update(this); + }; + + updating_embedded_window = false; } void Window::set_theme_type_variation(const StringName &p_theme_type) { @@ -2201,6 +2220,7 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed); ClassDB::bind_method(D_METHOD("_update_child_controls"), &Window::_update_child_controls); + ClassDB::bind_method(D_METHOD("_update_embedded_window"), &Window::_update_embedded_window); ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme); diff --git a/scene/main/window.h b/scene/main/window.h index 18841a4f1a..182caf5f0c 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -116,6 +116,7 @@ private: bool exclusive = false; bool wrap_controls = false; bool updating_child_controls = false; + bool updating_embedded_window = false; bool clamp_to_embedder = false; LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; @@ -123,6 +124,7 @@ private: bool auto_translate = true; void _update_child_controls(); + void _update_embedded_window(); Size2i content_scale_size; ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED; |