From 41b5c92f3a2dc14b03b3577fefdf24f047a40c95 Mon Sep 17 00:00:00 2001 From: s-fernandez-v Date: Thu, 8 Feb 2024 18:17:16 +0100 Subject: [PATCH] Updated to NoesisGUI 3.2.3 version --- Src/Noesis/Core/Src/Core/Events.cs | 22 +- Src/Noesis/Core/Src/Core/Extend.cs | 311 +++++++++++------- Src/Noesis/Core/Src/Core/ExtendProps.cs | 18 +- Src/Noesis/Core/Src/Core/RenderDevice.cs | 30 +- Src/Noesis/Core/Src/Core/View.cs | 26 +- Src/Noesis/Core/Src/Proxies/Clock.cs | 10 +- Src/Noesis/Core/Src/Proxies/CommandBinding.cs | 34 +- Src/Noesis/Core/Src/Proxies/Control.cs | 68 ++++ .../Src/Proxies/DependencyObjectExtend.cs | 12 +- .../Src/Proxies/ItemContainerGenerator.cs | 16 +- Src/Noesis/Core/Src/Proxies/Matrix.cs | 2 +- Src/Noesis/Core/Src/Proxies/Matrix3D.cs | 2 +- .../Core/Src/Proxies/NoesisGUI_PINVOKE.cs | 79 +++++ Src/Noesis/Core/Src/Proxies/Popup.cs | 14 + Src/Noesis/Core/Src/Proxies/RiveControl.cs | 23 ++ Src/Noesis/Core/Src/Proxies/RiveRun.cs | 77 +++++ .../Core/Src/Proxies/RiveRunCollection.cs | 41 +++ Src/Noesis/Core/Src/Proxies/TextBlock.cs | 7 + Src/Noesis/Core/Src/Proxies/Timeline.cs | 8 + .../Core/Src/Proxies/VisualStateGroup.cs | 14 + .../Core/Src/Proxies/VisualTreeHelper.cs | 11 + .../Extensions/Noesis.GUI.Extensions.csproj | 6 +- Src/Noesis/Extensions/Src/Element.cs | 93 ++++++ .../Extensions/Src/InteractivityBehaviors.cs | 113 +++++++ .../Extensions/Src/InteractivityTriggers.cs | 2 +- Src/Noesis/Extensions/Src/LocExtension.cs | 24 +- Src/Noesis/Extensions/Src/RichText.cs | 2 +- Src/Noesis/Extensions/Src/Text.cs | 16 +- .../Extensions/Theme/NoesisTheme.Styles.xaml | 22 ++ .../Src/Interactivity/DataEventTrigger.cs | 14 +- .../Src/Interactivity/EventTriggerBase.cs | 60 +++- .../Core/Src/Interactivity/GamepadTrigger.cs | 2 +- .../Src/Interactivity/InvokeCommandAction.cs | 107 +++++- .../Core/Src/Interactivity/KeyTrigger.cs | 2 +- .../Interactivity/LineDecorationBehavior.cs | 223 +++++++++++++ .../TriggerBase.Interactivity.cs | 3 + .../Core/Src/Localization/LocExtension.cs | 24 +- .../Core/Src/Localization/RichText.cs | 2 +- .../Theme/Theme/NoesisTheme.Styles.xaml | 22 ++ THIRD_PARTY.txt | 74 +++-- 40 files changed, 1396 insertions(+), 240 deletions(-) create mode 100644 Src/Noesis/Core/Src/Proxies/RiveRun.cs create mode 100644 Src/Noesis/Core/Src/Proxies/RiveRunCollection.cs create mode 100644 Src/NoesisApp/Core/Src/Interactivity/LineDecorationBehavior.cs diff --git a/Src/Noesis/Core/Src/Core/Events.cs b/Src/Noesis/Core/Src/Core/Events.cs index 7a4a96b..27012aa 100644 --- a/Src/Noesis/Core/Src/Core/Events.cs +++ b/Src/Noesis/Core/Src/Core/Events.cs @@ -429,20 +429,14 @@ internal static void Clear() } _elements.Clear(); - DependencyObject._Destroyed.Clear(); - Clock._Completed.Clear(); - CommandBinding._PreviewCanExecute.Clear(); - CommandBinding._PreviewExecuted.Clear(); - CommandBinding._CanExecute.Clear(); - CommandBinding._Executed.Clear(); - ItemContainerGenerator._ItemsChanged.Clear(); - ItemContainerGenerator._StatusChanged.Clear(); - Popup._Closed.Clear(); - Popup._Opened.Clear(); - Timeline._Completed.Clear(); - VisualStateGroup._CurrentStateChanging.Clear(); - VisualStateGroup._CurrentStateChanged.Clear(); - View._Rendering.Clear(); + DependencyObject.ResetEvents(); + Clock.ResetEvents(); + CommandBinding.ResetEvents(); + ItemContainerGenerator.ResetEvents(); + Popup.ResetEvents(); + Timeline.ResetEvents(); + VisualStateGroup.ResetEvents(); + View.ResetEvents(); } private static void OnElementDestroyed(IntPtr d) diff --git a/Src/Noesis/Core/Src/Core/Extend.cs b/Src/Noesis/Core/Src/Core/Extend.cs index aa9ec2c..709b7b9 100644 --- a/Src/Noesis/Core/Src/Core/Extend.cs +++ b/Src/Noesis/Core/Src/Core/Extend.cs @@ -88,7 +88,7 @@ public static void Shutdown() // clear errors generated releasing all C# proxies, throwing during // assembly unload will close Unity without notifying } - + Noesis_ClearExtendTypes(); Noesis_EnableExtend(false); ClearTables(); @@ -331,20 +331,20 @@ public enum NativeTypeKind public class NativeTypeInfo { public NativeTypeKind Kind { get; private set; } - public System.Type Type { get; private set; } + public Type Type { get; private set; } - public NativeTypeInfo(NativeTypeKind kind, System.Type type) + public NativeTypeInfo(NativeTypeKind kind, Type type) { Kind = kind; Type = type; } } - public class NativeTypeComponentInfo: NativeTypeInfo + public class NativeTypeComponentInfo : NativeTypeInfo { public Func Creator { get; private set; } - public NativeTypeComponentInfo(NativeTypeKind kind, System.Type type, Func creator) + public NativeTypeComponentInfo(NativeTypeKind kind, Type type, Func creator) : base(kind, type) { Creator = creator; @@ -362,33 +362,33 @@ public NativeTypeEnumInfo(NativeTypeKind kind, Type type) : base(kind, type) } } - public class NativeTypeExtendedInfo: NativeTypeInfo, INativeTypeExtended + public class NativeTypeExtendedInfo : NativeTypeInfo, INativeTypeExtended { public Func Creator { get; private set; } - public NativeTypeExtendedInfo(NativeTypeKind kind, System.Type type, Func creator) + public NativeTypeExtendedInfo(NativeTypeKind kind, Type type, Func creator) : base(kind, type) { Creator = creator; } } - public class NativeTypePropsInfo: NativeTypeExtendedInfo + public class NativeTypePropsInfo : NativeTypeExtendedInfo { public List Properties { get; private set; } - public NativeTypePropsInfo(NativeTypeKind kind, System.Type type, Func creator) + public NativeTypePropsInfo(NativeTypeKind kind, Type type, Func creator) : base(kind, type, creator) { Properties = new List(); } } - public class NativeTypeIndexerInfo: NativeTypePropsInfo + public class NativeTypeIndexerInfo : NativeTypePropsInfo { public IndexerAccessor Indexer { get; private set; } - public NativeTypeIndexerInfo(NativeTypeKind kind, System.Type type, Func creator, IndexerAccessor indexer) + public NativeTypeIndexerInfo(NativeTypeKind kind, Type type, Func creator, IndexerAccessor indexer) : base(kind, type, creator) { Indexer = indexer; @@ -1172,59 +1172,6 @@ public static void UnregisterNativeTypes() Init(); } - //////////////////////////////////////////////////////////////////////////////////////////////// - private static PropertyInfo[] GetPublicProperties(Type type) - { -#if NETFX_CORE - return type.GetTypeInfo().DeclaredProperties.Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic).ToArray(); -#else - return type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); -#endif - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - private static EventInfo[] GetPublicEvents(Type type) - { -#if NETFX_CORE - return type.GetTypeInfo().DeclaredProperties.Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic).ToArray(); -#else - return type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); -#endif - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - private static PropertyInfo FindIndexer(Type type, Type paramType) - { -#if NETFX_CORE - var props = type.GetRuntimeProperties().Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic); -#else - var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); -#endif - - foreach (var p in props) - { - ParameterInfo[] indexParams = p.GetIndexParameters(); - if (indexParams.Length == 1 && indexParams[0].ParameterType.Equals(paramType)) - { - return p; - } - } - - return null; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - private static PropertyInfo FindListIndexer(Type type) - { - return FindIndexer(type, typeof(int)); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - private static PropertyInfo FindDictIndexer(Type type) - { - return FindIndexer(type, typeof(string)); - } - //////////////////////////////////////////////////////////////////////////////////////////////// [StructLayout(LayoutKind.Sequential)] private struct ExtendTypeData @@ -1271,6 +1218,21 @@ private struct ExtendPropertyData public int readOnly; } + [ThreadStatic] + static object[] _objArgs1 = null; + + [ThreadStatic] + static Type[] _typeArgs0 = null; + + [ThreadStatic] + static Type[] _typeArgs1 = null; + + [ThreadStatic] + static Type[] _typeArgs2 = null; + + [ThreadStatic] + static Type[] _typeArgs3 = null; + //////////////////////////////////////////////////////////////////////////////////////////////// public static IntPtr RegisterNativeType(Type type) { @@ -1289,6 +1251,12 @@ public static IntPtr RegisterNativeType(Type type, bool registerDP) PropertyInfo[] props; NativeTypeInfo info; + if (_objArgs1 == null) _objArgs1 = new object[1]; + if (_typeArgs0 == null) _typeArgs0 = new Type[0]; + if (_typeArgs1 == null) _typeArgs1 = new Type[1]; + if (_typeArgs2 == null) _typeArgs2 = new Type[2]; + if (_typeArgs3 == null) _typeArgs3 = new Type[3]; + lock (_managedTypes) { if (_managedTypes.TryGetValue(type, out nativeType)) @@ -1351,8 +1319,8 @@ public static IntPtr RegisterNativeType(Type type, bool registerDP) System.Reflection.MethodInfo extend = FindExtendMethod(type); if (extend != null) { - object[] typeName = { TypeFullName(type) }; - nativeType = (IntPtr)extend.Invoke(null, typeName); + _objArgs1[0] = TypeFullName(type); + nativeType = (IntPtr)extend.Invoke(null, _objArgs1); } else { @@ -1435,13 +1403,66 @@ public static IntPtr RegisterNativeType(Type type, bool registerDP) // FullName returned by mono AOT implementation is a bit different than non-AOT implementation. // They must be the same because those names are stored in the serialization file - private static string TypeFullName(System.Type type) + private static string TypeFullName(Type type) { return type.FullName.Replace("Culture=,", "Culture=neutral,"); } //////////////////////////////////////////////////////////////////////////////////////////////// - private static Func TypeCreator(System.Type type) + private static PropertyInfo[] GetPublicProperties(Type type) + { + #if NETFX_CORE + return type.GetTypeInfo().DeclaredProperties.Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic).ToArray(); + #else + return type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); + #endif + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + private static EventInfo[] GetPublicEvents(Type type) + { + #if NETFX_CORE + return type.GetTypeInfo().DeclaredEvents.Where(e => e.AddMethod.IsPublic && !e.AddMethod.IsStatic).ToArray(); + #else + return type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); + #endif + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + #if NETFX_CORE + private static bool IsIndexParameter(PropertyInfo p, Type type) + { + ParameterInfo[] pi = p.GetIndexParameters(); + return pi.Length == 1 && pi[0].ParameterType.Equals(type); + } + #endif + + //////////////////////////////////////////////////////////////////////////////////////////////// + private static PropertyInfo FindListIndexer(Type type) + { + #if NETFX_CORE + return type.GetTypeInfo().GetDeclaredProperties("Item") + .Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic && IsIndexParameter(typeof(int)).FirstOrDefault(); + #else + _typeArgs1[0] = typeof(int); + return type.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, _typeArgs1, null); + #endif + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + private static PropertyInfo FindDictIndexer(Type type) + { + #if NETFX_CORE + return type.GetTypeInfo().GetDeclaredProperties("Item") + .Where(p => p.GetMethod.IsPublic && !p.GetMethod.IsStatic && IsIndexParameter(typeof(string)).FirstOrDefault(); + #else + _typeArgs1[0] = typeof(string); + return type.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance, null, null, _typeArgs1, null); + #endif + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + private static Func TypeCreator(Type type) { #if !ENABLE_IL2CPP && !UNITY_IOS if (!type.GetTypeInfo().IsValueType && Platform.ID != PlatformID.iPhone) @@ -1471,7 +1492,7 @@ private static Func TypeCreator(System.Type type) } //////////////////////////////////////////////////////////////////////////////////////////////// - private static NativeTypeInfo CreateNativeTypeInfo(System.Type type, IndexerAccessor indexer, + private static NativeTypeInfo CreateNativeTypeInfo(Type type, IndexerAccessor indexer, PropertyInfo[] props) { if (indexer != null) @@ -1495,6 +1516,7 @@ private static bool IsOverride(MethodInfo m) } //////////////////////////////////////////////////////////////////////////////////////////////// + #if NETFX_CORE private static bool ParametersMatch(MethodInfo m, Type[] types) { ParameterInfo[] parameters = m.GetParameters(); @@ -1508,16 +1530,16 @@ private static bool ParametersMatch(MethodInfo m, Type[] types) return true; } + #endif //////////////////////////////////////////////////////////////////////////////////////////////// public static MethodInfo FindMethod(Type type, string name, Type[] types) { #if NETFX_CORE - return type.GetRuntimeMethods() + return type.GetTypeInfo().GetRuntimeMethods() .Where(m => m.Name == name && !m.IsStatic && ParametersMatch(m, types)).FirstOrDefault(); #else - return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(m => m.Name == name && ParametersMatch(m, types)).FirstOrDefault(); + return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); #endif } @@ -1525,16 +1547,15 @@ public static MethodInfo FindMethod(Type type, string name, Type[] types) public static PropertyInfo FindProperty(Type type, string name) { #if NETFX_CORE - return type.GetRuntimeProperties() + return type.GetTypeInfo().GetRuntimeProperties() .Where(p => p.Name == name && !p.GetMethod.IsStatic).FirstOrDefault; #else - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(p => p.Name == name).FirstOrDefault(); + return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); #endif } //////////////////////////////////////////////////////////////////////////////////////////////// - private static ExtendTypeData CreateNativeTypeData(System.Type type, IntPtr nativeType) + private static ExtendTypeData CreateNativeTypeData(Type type, IntPtr nativeType) { ExtendTypeData typeData = new ExtendTypeData(); typeData.type = nativeType.ToInt64(); @@ -1570,10 +1591,11 @@ private static ExtendTypeData CreateNativeTypeData(System.Type type, IntPtr nati // overrides ExtendTypeOverrides overrides = ExtendTypeOverrides.None; - MethodInfo toStringMethod = FindMethod(type, "ToString", new Type[] { }); + MethodInfo toStringMethod = FindMethod(type, "ToString", _typeArgs0); if (IsOverride(toStringMethod)) overrides |= ExtendTypeOverrides.Object_ToString; - MethodInfo equalsMethod = FindMethod(type, "Equals", new Type[] { typeof(object) }); + _typeArgs1[0] = typeof(object); + MethodInfo equalsMethod = FindMethod(type, "Equals", _typeArgs1); if (IsOverride(equalsMethod)) overrides |= ExtendTypeOverrides.Object_Equals; if (typeof(Visual).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) @@ -1581,52 +1603,60 @@ private static ExtendTypeData CreateNativeTypeData(System.Type type, IntPtr nati PropertyInfo childrenCountProp = FindProperty(type, "VisualChildrenCount"); if (IsOverride(childrenCountProp?.GetMethod)) overrides |= ExtendTypeOverrides.Visual_GetChildrenCount; - MethodInfo getChildMethod = FindMethod(type, "GetVisualChild", new Type[] { typeof(int) }); + _typeArgs1[0] = typeof(int); + MethodInfo getChildMethod = FindMethod(type, "GetVisualChild", _typeArgs1); if (IsOverride(getChildMethod)) overrides |= ExtendTypeOverrides.Visual_GetChild; } if (typeof(UIElement).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo renderMethod = FindMethod(type, "OnRender", new Type[] { typeof(DrawingContext) }); + _typeArgs1[0] = typeof(DrawingContext); + MethodInfo renderMethod = FindMethod(type, "OnRender", _typeArgs1); if (IsOverride(renderMethod)) overrides |= ExtendTypeOverrides.UIElement_OnRender; } if (typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo eventMethod = FindMethod(type, "ConnectEvent", new Type[] { typeof(object), typeof(string), typeof(string) }); + _typeArgs3[0] = typeof(object); _typeArgs3[1] = typeof(string); _typeArgs3[2] = typeof(string); + MethodInfo eventMethod = FindMethod(type, "ConnectEvent", _typeArgs3); if (IsOverride(eventMethod)) overrides |= ExtendTypeOverrides.FrameworkElement_ConnectEvent; - MethodInfo fieldMethod = FindMethod(type, "ConnectField", new Type[] { typeof(object), typeof(string) }); + _typeArgs2[0] = typeof(object); _typeArgs2[1] = typeof(string); + MethodInfo fieldMethod = FindMethod(type, "ConnectField", _typeArgs2); if (IsOverride(fieldMethod)) overrides |= ExtendTypeOverrides.FrameworkElement_ConnectField; - MethodInfo measureMethod = FindMethod(type, "MeasureOverride", new Type[] { typeof(Size) }); + _typeArgs1[0] = typeof(Size); + MethodInfo measureMethod = FindMethod(type, "MeasureOverride", _typeArgs1); if (IsOverride(measureMethod)) overrides |= ExtendTypeOverrides.FrameworkElement_Measure; - MethodInfo arrangeMethod = FindMethod(type, "ArrangeOverride", new Type[] { typeof(Size) }); + MethodInfo arrangeMethod = FindMethod(type, "ArrangeOverride", _typeArgs1); if (IsOverride(arrangeMethod)) overrides |= ExtendTypeOverrides.FrameworkElement_Arrange; - MethodInfo templateMethod = FindMethod(type, "OnApplyTemplate", new Type[] { }); + MethodInfo templateMethod = FindMethod(type, "OnApplyTemplate", _typeArgs0); if (IsOverride(templateMethod)) overrides |= ExtendTypeOverrides.FrameworkElement_ApplyTemplate; } if (typeof(ItemsControl).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getContainerMethod = FindMethod(type, "GetContainerForItemOverride", new Type[] { }); + MethodInfo getContainerMethod = FindMethod(type, "GetContainerForItemOverride", _typeArgs0); if (IsOverride(getContainerMethod)) overrides |= ExtendTypeOverrides.ItemsControl_GetContainer; - MethodInfo isContainerMethod = FindMethod(type, "IsItemItsOwnContainerOverride", new Type[] { typeof(object) }); + _typeArgs1[0] = typeof(object); + MethodInfo isContainerMethod = FindMethod(type, "IsItemItsOwnContainerOverride", _typeArgs1); if (IsOverride(isContainerMethod)) overrides |= ExtendTypeOverrides.ItemsControl_IsContainer; } if (typeof(Adorner).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getTransformMethod = FindMethod(type, "GetDesiredTransform", new Type[] { typeof(Matrix4) }); + _typeArgs1[0] = typeof(Matrix4); + MethodInfo getTransformMethod = FindMethod(type, "GetDesiredTransform", _typeArgs1); if (IsOverride(getTransformMethod)) overrides |= ExtendTypeOverrides.Adorner_GetTransform; } if (typeof(Freezable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo cloneMethod = FindMethod(type, "CloneCommonCore", new Type[] { typeof(Freezable) }); + _typeArgs1[0] = typeof(Freezable); + MethodInfo cloneMethod = FindMethod(type, "CloneCommonCore", _typeArgs1); if (IsOverride(cloneMethod)) overrides |= ExtendTypeOverrides.Freezable_Clone; } @@ -1634,55 +1664,64 @@ private static ExtendTypeData CreateNativeTypeData(System.Type type, IntPtr nati { if (typeof(Int16Animation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(short), typeof(short), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(short); _typeArgs3[1] = typeof(short); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(Int16Animation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(Int32Animation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(int), typeof(int), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(int); _typeArgs3[1] = typeof(int); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(Int32Animation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(Int64Animation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(long), typeof(long), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(long); _typeArgs3[1] = typeof(long); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(Int64Animation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(DoubleAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(float), typeof(float), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(float); _typeArgs3[1] = typeof(float); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(DoubleAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(ColorAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(Color), typeof(Color), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(Color); _typeArgs3[1] = typeof(Color); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(ColorAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(PointAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(Point), typeof(Point), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(Point); _typeArgs3[1] = typeof(Point); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(PointAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(RectAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(Rect), typeof(Rect), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(Rect); _typeArgs3[1] = typeof(Rect); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(RectAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(SizeAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(Size), typeof(Size), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(Size); _typeArgs3[1] = typeof(Size); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(SizeAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } if (typeof(ThicknessAnimation).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", new Type[] { typeof(Thickness), typeof(Thickness), typeof(AnimationClock) }); + _typeArgs3[0] = typeof(Thickness); _typeArgs3[1] = typeof(Thickness); _typeArgs3[2] = typeof(AnimationClock); + MethodInfo getValueMethod = FindMethod(type, "GetCurrentValueCore", _typeArgs3); if (IsOverride(getValueMethod) && getValueMethod.DeclaringType != typeof(ThicknessAnimation)) overrides |= ExtendTypeOverrides.Animation_GetValueCore; } @@ -1718,7 +1757,7 @@ private static bool IsDependencyProperty(Type type, PropertyInfo prop) } //////////////////////////////////////////////////////////////////////////////////////////////// - private static IntPtr CreateNativePropsData(System.Type type, PropertyInfo[] props, + private static IntPtr CreateNativePropsData(Type type, PropertyInfo[] props, NativeTypeInfo info, out int numProps) { EventInfo[] events = GetPublicEvents(type); @@ -1731,11 +1770,11 @@ private static IntPtr CreateNativePropsData(System.Type type, PropertyInfo[] pro if (propsLen > 0) { -#if ENABLE_IL2CPP || UNITY_IOS + #if ENABLE_IL2CPP || UNITY_IOS bool usePropertyInfo = true; -#else + #else bool usePropertyInfo = type.GetTypeInfo().IsValueType || Platform.ID == PlatformID.iPhone; -#endif + #endif NativeTypePropsInfo propsInfo = (NativeTypePropsInfo)info; for (int i = 0; i < propsLen; ++i) @@ -1785,7 +1824,7 @@ private static IntPtr CreateNativePropsData(System.Type type, PropertyInfo[] pro } //////////////////////////////////////////////////////////////////////////////////////////////// - private static IntPtr CreateNativeEnumsData(System.Type type, out int numEnums) + private static IntPtr CreateNativeEnumsData(Type type, out int numEnums) { var names = Enum.GetNames(type); var values = Enum.GetValues(type); @@ -1810,13 +1849,15 @@ private static IntPtr CreateNativeEnumsData(System.Type type, out int numEnums) } //////////////////////////////////////////////////////////////////////////////////////////////// - public static IntPtr EnsureNativeType(System.Type type) + public static IntPtr EnsureNativeType(Type type) { return EnsureNativeType(type, true); } - public static IntPtr EnsureNativeType(System.Type type, bool registerDP) + public static IntPtr EnsureNativeType(Type type, bool registerDP) { + if (type == null) type = typeof(BaseComponent); + IntPtr nativeType = TryGetNativeType(type); if (nativeType == IntPtr.Zero) { @@ -1850,15 +1891,15 @@ private static void RegisterDependencyProperties(Type type) private static bool HasDependencyProperties(Type type) { -#if NETFX_CORE + #if NETFX_CORE var fields = type.GetTypeInfo().DeclaredFields.Where(p => p.IsStatic); if (fields.Any()) { -#else + #else var fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (fields.Length > 0) { -#endif + #endif foreach (FieldInfo field in fields) { if (field.FieldType == typeof(DependencyProperty)) @@ -1893,12 +1934,12 @@ private static void RunClassConstructor(Type type) private static HashSet _constructedTypes = new HashSet(); //////////////////////////////////////////////////////////////////////////////////////////////// - private static System.Reflection.MethodInfo FindExtendMethod(System.Type type) + private static MethodInfo FindExtendMethod(Type type) { - System.Type baseType = type; + Type baseType = type; while (baseType != null) { - System.Reflection.MethodInfo extend = GetExtendMethod(baseType); + MethodInfo extend = GetExtendMethod(baseType); if (extend != null) { return extend; @@ -1911,13 +1952,14 @@ private static System.Reflection.MethodInfo FindExtendMethod(System.Type type) } //////////////////////////////////////////////////////////////////////////////////////////////// - private static System.Reflection.MethodInfo GetExtendMethod(System.Type type) + private static MethodInfo GetExtendMethod(Type type) { -#if NETFX_CORE - System.Reflection.MethodInfo extend = type.GetTypeInfo().GetDeclaredMethods("Extend").Where(m => !m.IsPublic && m.IsStatic).FirstOrDefault(); -#else - System.Reflection.MethodInfo extend = type.GetMethod("Extend", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static); -#endif + #if NETFX_CORE + MethodInfo extend = type.GetTypeInfo().GetDeclaredMethods("Extend") + .Where(m => !m.IsPublic && m.IsStatic).FirstOrDefault(); + #else + MethodInfo extend = type.GetMethod("Extend", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static); + #endif if (extend != null && extend.GetParameters().Length == 1) { return extend; @@ -1943,6 +1985,19 @@ private static void FreeString(IntPtr strPtr) } } + //////////////////////////////////////////////////////////////////////////////////////////////// + private static int NextPowerOf2(int x) + { + if (x < 0) return 0; + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } + //////////////////////////////////////////////////////////////////////////////////////////////// public static string StringFromNativeUtf8(IntPtr nativeUtf8) { @@ -1959,12 +2014,15 @@ public static string StringFromNativeUtf8(IntPtr nativeUtf8) // https://github.com/dotnet/corefx/issues/9605 int len = 0; while (Marshal.ReadByte(nativeUtf8, len) != 0) len++; - byte[] buffer = new byte[len]; - Marshal.Copy(nativeUtf8, buffer, 0, len); - return System.Text.Encoding.UTF8.GetString(buffer, 0, len); + if (_byteBuffer == null || len > _byteBuffer.Length) _byteBuffer = new byte[NextPowerOf2(Math.Max(2048, len))]; + Marshal.Copy(nativeUtf8, _byteBuffer, 0, len); + return System.Text.Encoding.UTF8.GetString(_byteBuffer, 0, len); #endif } + [ThreadStatic] + private static byte[] _byteBuffer = null; + //////////////////////////////////////////////////////////////////////////////////////////////// private delegate void Callback_RegisterType(string typeName); private static Callback_RegisterType _registerType = RegisterType; @@ -3180,9 +3238,10 @@ private static uint StreamRead(IntPtr cPtr, IntPtr buffer, uint bufferSize) var stream = (System.IO.Stream)GetExtendInstance(cPtr); if (stream != null) { - byte[] bytes = new byte[bufferSize]; - int readBytes = stream.Read(bytes, 0, (int)bufferSize); - System.Runtime.InteropServices.Marshal.Copy(bytes, 0, buffer, (int)bufferSize); + int len = (int)bufferSize; + if (_byteBuffer == null || len > _byteBuffer.Length) _byteBuffer = new byte[NextPowerOf2(Math.Max(2048, len))]; + int readBytes = stream.Read(_byteBuffer, 0, len); + System.Runtime.InteropServices.Marshal.Copy(_byteBuffer, 0, buffer, len); return (uint)readBytes; } } @@ -5134,7 +5193,7 @@ private static IntPtr GetPropertyValue_Type(IntPtr nativeType, int propertyIndex { Type type = GetPropertyValue(GetProperty(nativeType, propertyIndex), GetExtendInstance(cPtr)); - return GetNativeType(type); + return type != null ? GetNativeType(type) : IntPtr.Zero; } catch (Exception e) { @@ -5606,7 +5665,7 @@ private static void SetPropertyValue_Type(IntPtr nativeType, int propertyIndex, { try { - NativeTypeInfo info = GetNativeTypeInfo(cPtr); + NativeTypeInfo info = GetNativeTypeInfo(val); SetPropertyValue(GetProperty(nativeType, propertyIndex), GetExtendInstance(cPtr), info.Type); } @@ -5664,7 +5723,7 @@ public static IntPtr GetCPtr(BaseComponent instance, Type extendType) } //////////////////////////////////////////////////////////////////////////////////////////////// - public static IntPtr NewCPtr(System.Type type) + public static IntPtr NewCPtr(Type type) { // Ensure native type is registered IntPtr nativeType = EnsureNativeType(type); diff --git a/Src/Noesis/Core/Src/Core/ExtendProps.cs b/Src/Noesis/Core/Src/Core/ExtendProps.cs index 552fad3..11c24d5 100644 --- a/Src/Noesis/Core/Src/Core/ExtendProps.cs +++ b/Src/Noesis/Core/Src/Core/ExtendProps.cs @@ -776,6 +776,15 @@ private static Dictionary AddPropertyFunctions() #endregion + private static bool IsCollectionType(Type type) + { + if (typeof(System.Collections.IList).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) return true; + if (typeof(System.Collections.IDictionary).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) return true; + if (FindListIndexer(type) != null) return true; + if (FindDictIndexer(type) != null) return true; + return false; + } + private static ExtendPropertyData AddProperty(NativeTypePropsInfo info, PropertyInfo p, bool usePropertyInfo) { AddPropertyDelegate addPropFunction; @@ -784,8 +793,6 @@ private static ExtendPropertyData AddProperty(NativeTypePropsInfo info, Property return addPropFunction(info, p, usePropertyInfo); } - IntPtr propertyType = EnsureNativeType(p.PropertyType); - if (p.PropertyType.GetTypeInfo().IsEnum) { if (IsSignedEnum(p.PropertyType)) @@ -803,12 +810,19 @@ private static ExtendPropertyData AddProperty(NativeTypePropsInfo info, Property true); } + IntPtr propertyType = EnsureNativeType(p.PropertyType); return CreatePropertyData(p, NativePropertyType.Enum, propertyType); } else { AddPropertyAccessor(info, p, true); + IntPtr propertyType = TryGetNativeType(p.PropertyType); + if (propertyType == IntPtr.Zero) + { + propertyType = IsCollectionType(p.PropertyType) ? + EnsureNativeType(p.PropertyType) : BaseComponent.GetStaticType(); + } return CreatePropertyData(p, NativePropertyType.BaseComponent, propertyType); } } diff --git a/Src/Noesis/Core/Src/Core/RenderDevice.cs b/Src/Noesis/Core/Src/Core/RenderDevice.cs index 092dd9b..db07502 100644 --- a/Src/Noesis/Core/Src/Core/RenderDevice.cs +++ b/Src/Noesis/Core/Src/Core/RenderDevice.cs @@ -210,14 +210,16 @@ public enum Enum // -------------------------------------------------------------------------------- // Attr Interpolation Semantic // -------------------------------------------------------------------------------- - Pos = 1, // linear Position (xy) - Color = 2, // nointerpolation sRGB Color (rgba) - Tex0 = 4, // linear TexCoord0 (uv) - Tex1 = 8, // linear TexCoord1 (uv) - Coverage = 16, // linear Coverage (alpha) - Rect = 32, // nointerpolation Rect (x0, y0, x1, y1) - Tile = 64, // nointerpolation Rect (x, y, width, height) - ImagePos = 128, // linear Position (xy) - Scale(zw) + Pos, // linear Position (xy) + Color, // nointerpolation sRGB Color (rgba) + Tex0, // linear TexCoord0 (uv) + Tex1, // linear TexCoord1 (uv) + Coverage, // linear Coverage (alpha) + Rect, // nointerpolation Rect (x0, y0, x1, y1) + Tile, // nointerpolation Rect (x, y, width, height) + ImagePos, // linear Position (xy) - Scale(zw) + + Count } /// @@ -284,15 +286,19 @@ public static int SizeForFormat(Shader.Vertex.Format.Enum format) return map[(int)format]; } - /// Table for getting the attribute bitmask corresponding to each vertex format - public static Shader.Vertex.Format.Attr.Enum AttributesForFormat(Shader.Vertex.Format.Enum format) + /// + /// Table for getting the attribute bitmask corresponding to each vertex format. + /// For example, to determine if Pos attribute is used by the format you would check: + /// bitmask & (1 << Shader.Vertex.Format.Attr.Enum.Pos) + /// + public static int AttributesForFormat(Shader.Vertex.Format.Enum format) { int[] map = new int[(int)Shader.Vertex.Format.Enum.Count] { 1, 3, 5, 37, 101, 19, 21, 53, 117, 11, 13, 45, 109, 15, 43, 167 }; - return (Shader.Vertex.Format.Attr.Enum)map[(int)format]; + return map[(int)format]; } /// @@ -301,7 +307,7 @@ public static Shader.Vertex.Format.Attr.Enum AttributesForFormat(Shader.Vertex.F /// public static Shader.Vertex.Format.Attr.Type.Enum TypeForAttr(Shader.Vertex.Format.Attr.Enum attr) { - int[] map = new int[] + int[] map = new int[(int)Shader.Vertex.Format.Attr.Enum.Count] { 1, 3, 1, 1, 0, 4, 2, 2 }; diff --git a/Src/Noesis/Core/Src/Core/View.cs b/Src/Noesis/Core/Src/Core/View.cs index 985c5ad..63d7aa3 100644 --- a/Src/Noesis/Core/Src/Core/View.cs +++ b/Src/Noesis/Core/Src/Core/View.cs @@ -152,6 +152,17 @@ public void SetProjectionMatrix(Matrix4 projection) Noesis_View_SetProjectionMatrix(CPtr, ref projection); } + /// + /// The projection matrix set to the view is used for determining the visual impact of nodes + /// in the offscreen phase. The stereo matrices used for rendering in VR are slightly + /// different. To account for this difference, it is recommended to apply a scale using this + /// function. For non-VR this must be 1. For VR, we recommend a value between 2 and 3. + /// + public void SetStereoOffscreenScaleFactor(float factor) + { + Noesis_View_SetStereoOffscreenScaleFactor(CPtr, factor); + } + /// /// Indicates if touch input events are emulated by the mouse /// @@ -435,7 +446,17 @@ private static void RaiseRendering(IntPtr cPtr, IntPtr sender) } } - internal static Dictionary _Rendering = + internal static void ResetEvents() + { + foreach (var kv in _Rendering) + { + IntPtr cPtr = new IntPtr(kv.Key); + Noesis_View_UnbindRenderingEvent(new HandleRef(null, cPtr), _raiseRendering); + } + _Rendering.Clear(); + } + + private static Dictionary _Rendering = new Dictionary(); #endregion @@ -619,6 +640,9 @@ protected override IntPtr CreateCPtr(Type type, out bool registerExtend) [DllImport(Library.Name)] static extern void Noesis_View_SetProjectionMatrix(HandleRef view, ref Matrix4 projection); + [DllImport(Library.Name)] + static extern void Noesis_View_SetStereoOffscreenScaleFactor(HandleRef view, float factor); + [DllImport(Library.Name)] static extern void Noesis_View_SetEmulateTouch(HandleRef view, bool emulate); diff --git a/Src/Noesis/Core/Src/Proxies/Clock.cs b/Src/Noesis/Core/Src/Proxies/Clock.cs index cc855ba..8475912 100644 --- a/Src/Noesis/Core/Src/Proxies/Clock.cs +++ b/Src/Noesis/Core/Src/Proxies/Clock.cs @@ -84,7 +84,15 @@ private static void RaiseCompleted(IntPtr cPtr, IntPtr sender, IntPtr e) { } } - internal static Dictionary _Completed = + internal static void ResetEvents() { + foreach (var kv in _Completed) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_Clock_Completed(_raiseCompleted, cPtr); + } + _Completed.Clear(); + } + + private static Dictionary _Completed = new Dictionary(); #endregion diff --git a/Src/Noesis/Core/Src/Proxies/CommandBinding.cs b/Src/Noesis/Core/Src/Proxies/CommandBinding.cs index 4ff855c..be172ed 100644 --- a/Src/Noesis/Core/Src/Proxies/CommandBinding.cs +++ b/Src/Noesis/Core/Src/Proxies/CommandBinding.cs @@ -83,7 +83,7 @@ private static void RaisePreviewCanExecute(IntPtr cPtr, IntPtr sender, IntPtr e) } } - internal static Dictionary _PreviewCanExecute = + private static Dictionary _PreviewCanExecute = new Dictionary(); #endregion @@ -139,7 +139,7 @@ private static void RaiseCanExecute(IntPtr cPtr, IntPtr sender, IntPtr e) { } } - internal static Dictionary _CanExecute = + private static Dictionary _CanExecute = new Dictionary(); #endregion @@ -195,7 +195,7 @@ private static void RaisePreviewExecuted(IntPtr cPtr, IntPtr sender, IntPtr e) { } } - internal static Dictionary _PreviewExecuted = + private static Dictionary _PreviewExecuted = new Dictionary(); #endregion @@ -251,10 +251,36 @@ private static void RaiseExecuted(IntPtr cPtr, IntPtr sender, IntPtr e) { } } - internal static Dictionary _Executed = + private static Dictionary _Executed = new Dictionary(); #endregion + internal static void ResetEvents() { + foreach (var kv in _PreviewCanExecute) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_CommandBinding_PreviewCanExecute(_raisePreviewCanExecute, cPtr); + } + _PreviewCanExecute.Clear(); + + foreach (var kv in _CanExecute) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_CommandBinding_CanExecute(_raiseCanExecute, cPtr); + } + _CanExecute.Clear(); + + foreach (var kv in _PreviewExecuted) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_CommandBinding_PreviewExecuted(_raisePreviewExecuted, cPtr); + } + _PreviewExecuted.Clear(); + + foreach (var kv in _Executed) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_CommandBinding_Executed(_raiseExecuted, cPtr); + } + _Executed.Clear(); + } + #endregion public ICommand Command { diff --git a/Src/Noesis/Core/Src/Proxies/Control.cs b/Src/Noesis/Core/Src/Proxies/Control.cs index 516900a..6735f7e 100644 --- a/Src/Noesis/Core/Src/Proxies/Control.cs +++ b/Src/Noesis/Core/Src/Proxies/Control.cs @@ -180,6 +180,34 @@ public static DependencyProperty IsFocusEngagementEnabledProperty { } } + public static DependencyProperty XYFocusLeftProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusLeftProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public static DependencyProperty XYFocusRightProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusRightProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public static DependencyProperty XYFocusUpProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusUpProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public static DependencyProperty XYFocusDownProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusDownProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + public static RoutedEvent MouseDoubleClickEvent { get { IntPtr cPtr = NoesisGUI_PINVOKE.Control_MouseDoubleClickEvent_get(); @@ -378,6 +406,46 @@ public bool IsFocusEngagementEnabled { } } + public UIElement XYFocusLeft { + set { + NoesisGUI_PINVOKE.Control_XYFocusLeft_set(swigCPtr, UIElement.getCPtr(value)); + } + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusLeft_get(swigCPtr); + return (UIElement)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public UIElement XYFocusRight { + set { + NoesisGUI_PINVOKE.Control_XYFocusRight_set(swigCPtr, UIElement.getCPtr(value)); + } + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusRight_get(swigCPtr); + return (UIElement)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public UIElement XYFocusUp { + set { + NoesisGUI_PINVOKE.Control_XYFocusUp_set(swigCPtr, UIElement.getCPtr(value)); + } + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusUp_get(swigCPtr); + return (UIElement)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public UIElement XYFocusDown { + set { + NoesisGUI_PINVOKE.Control_XYFocusDown_set(swigCPtr, UIElement.getCPtr(value)); + } + get { + IntPtr cPtr = NoesisGUI_PINVOKE.Control_XYFocusDown_get(swigCPtr); + return (UIElement)Noesis.Extend.GetProxy(cPtr, false); + } + } + internal new static IntPtr Extend(string typeName) { return NoesisGUI_PINVOKE.Extend_Control(Marshal.StringToHGlobalAnsi(typeName)); } diff --git a/Src/Noesis/Core/Src/Proxies/DependencyObjectExtend.cs b/Src/Noesis/Core/Src/Proxies/DependencyObjectExtend.cs index 545b738..f29d835 100644 --- a/Src/Noesis/Core/Src/Proxies/DependencyObjectExtend.cs +++ b/Src/Noesis/Core/Src/Proxies/DependencyObjectExtend.cs @@ -970,7 +970,17 @@ private static void RaiseDestroyed(IntPtr cPtr) } } - internal static Dictionary _Destroyed = + internal static void ResetEvents() + { + foreach (var kv in _Destroyed) + { + IntPtr cPtr = new IntPtr(kv.Key); + Noesis_Dependency_Destroyed_Unbind(_raiseDestroyed, new HandleRef(null, cPtr)); + } + _Destroyed.Clear(); + } + + private static Dictionary _Destroyed = new Dictionary(); #endregion diff --git a/Src/Noesis/Core/Src/Proxies/ItemContainerGenerator.cs b/Src/Noesis/Core/Src/Proxies/ItemContainerGenerator.cs index 6ba4140..9db1a30 100644 --- a/Src/Noesis/Core/Src/Proxies/ItemContainerGenerator.cs +++ b/Src/Noesis/Core/Src/Proxies/ItemContainerGenerator.cs @@ -140,10 +140,24 @@ private static void RaiseStatusChanged(IntPtr cPtr, IntPtr sender, IntPtr e) { } } - internal static Dictionary _StatusChanged = + private static Dictionary _StatusChanged = new Dictionary(); #endregion + internal static void ResetEvents() { + foreach (var kv in _ItemsChanged) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_ItemContainerGenerator_ItemsChanged(_raiseItemsChanged, cPtr); + } + _ItemsChanged.Clear(); + + foreach (var kv in _StatusChanged) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_ItemContainerGenerator_StatusChanged(_raiseStatusChanged, cPtr); + } + _StatusChanged.Clear(); + } + #endregion ItemContainerGenerator IItemContainerGenerator.GetItemContainerGeneratorForPanel(Panel panel) { diff --git a/Src/Noesis/Core/Src/Proxies/Matrix.cs b/Src/Noesis/Core/Src/Proxies/Matrix.cs index 995c1ae..07e13d9 100644 --- a/Src/Noesis/Core/Src/Proxies/Matrix.cs +++ b/Src/Noesis/Core/Src/Proxies/Matrix.cs @@ -212,7 +212,7 @@ public float Determinant { } public bool HasInverse { - get { return Math.Abs(Determinant) < 0.0001f; } + get { return Math.Abs(Determinant) >= 0.0001f; } } public void Invert() { diff --git a/Src/Noesis/Core/Src/Proxies/Matrix3D.cs b/Src/Noesis/Core/Src/Proxies/Matrix3D.cs index 6ff32bb..4de7b33 100644 --- a/Src/Noesis/Core/Src/Proxies/Matrix3D.cs +++ b/Src/Noesis/Core/Src/Proxies/Matrix3D.cs @@ -238,7 +238,7 @@ public float Determinant { } public bool HasInverse { - get { return Math.Abs(Determinant) < 0.0001f; } + get { return Math.Abs(Determinant) >= 0.0001f; } } public void Invert() { diff --git a/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs b/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs index 7ee139a..2ef09c6 100644 --- a/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs +++ b/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs @@ -3571,6 +3571,10 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern void VisualTreeHelper_HitTest3DHelper(HandleRef jarg1, ref Point3D jarg2, ref Point3D jarg3, ref HitTest3DResult jarg4); + [DllImport(Library.Name)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool VisualTreeHelper_IntersectPlaneHelper(HandleRef jarg1, ref Point3D jarg2, ref Point3D jarg3, ref Point3D jarg4); + [DllImport(Library.Name)] public static extern IntPtr VisualCollection_Create(HandleRef jarg1); @@ -6098,6 +6102,18 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern IntPtr Control_IsFocusEngagementEnabledProperty_get(); + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusLeftProperty_get(); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusRightProperty_get(); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusUpProperty_get(); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusDownProperty_get(); + [DllImport(Library.Name)] public static extern IntPtr Control_MouseDoubleClickEvent_get(); @@ -6209,6 +6225,30 @@ internal class NoesisGUI_PINVOKE { [return: MarshalAs(UnmanagedType.U1)] public static extern bool Control_IsFocusEngagementEnabled_get(HandleRef jarg1); + [DllImport(Library.Name)] + public static extern void Control_XYFocusLeft_set(HandleRef jarg1, HandleRef jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusLeft_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern void Control_XYFocusRight_set(HandleRef jarg1, HandleRef jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusRight_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern void Control_XYFocusUp_set(HandleRef jarg1, HandleRef jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusUp_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern void Control_XYFocusDown_set(HandleRef jarg1, HandleRef jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr Control_XYFocusDown_get(HandleRef jarg1); + [DllImport(Library.Name)] public static extern IntPtr new_ContentControl(); @@ -10605,6 +10645,9 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern int TextBlock_TextWrapping_get(HandleRef jarg1); + [DllImport(Library.Name)] + public static extern IntPtr TextBlock_FormattedText_get(HandleRef jarg1); + [DllImport(Library.Name)] public static extern IntPtr TextBlock_ToStringHelper(HandleRef jarg1); @@ -13586,6 +13629,30 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern IntPtr new_RiveInputCollection(); + [DllImport(Library.Name)] + public static extern IntPtr new_RiveRun(); + + [DllImport(Library.Name)] + public static extern IntPtr RiveRun_RunNameProperty_get(); + + [DllImport(Library.Name)] + public static extern IntPtr RiveRun_RunTextProperty_get(); + + [DllImport(Library.Name)] + public static extern void RiveRun_RunName_set(HandleRef jarg1, [MarshalAs(UnmanagedType.LPWStr)]string jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr RiveRun_RunName_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern void RiveRun_RunText_set(HandleRef jarg1, [MarshalAs(UnmanagedType.LPWStr)]string jarg2); + + [DllImport(Library.Name)] + public static extern IntPtr RiveRun_RunText_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern IntPtr new_RiveRunCollection(); + [DllImport(Library.Name)] public static extern IntPtr new_RiveControl(); @@ -13601,6 +13668,15 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern IntPtr RiveControl_GetSourceInput(HandleRef jarg1, uint jarg2, out RiveSourceInputType jarg3); + [DllImport(Library.Name)] + public static extern void RiveControl_SetRunText(HandleRef jarg1, [MarshalAs(UnmanagedType.LPWStr)]string jarg2, [MarshalAs(UnmanagedType.LPWStr)]string jarg3); + + [DllImport(Library.Name)] + public static extern uint RiveControl_GetSourceRunCount(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern IntPtr RiveControl_GetSourceRunName(HandleRef jarg1, uint jarg2); + [DllImport(Library.Name)] public static extern IntPtr RiveControl_SourceProperty_get(); @@ -13627,6 +13703,9 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern IntPtr RiveControl_Inputs_get(HandleRef jarg1); + + [DllImport(Library.Name)] + public static extern IntPtr RiveControl_Runs_get(HandleRef jarg1); } } diff --git a/Src/Noesis/Core/Src/Proxies/Popup.cs b/Src/Noesis/Core/Src/Proxies/Popup.cs index 994261f..a8def4f 100644 --- a/Src/Noesis/Core/Src/Proxies/Popup.cs +++ b/Src/Noesis/Core/Src/Proxies/Popup.cs @@ -141,6 +141,20 @@ private static void RaiseOpened(IntPtr cPtr, IntPtr sender, IntPtr e) { new Dictionary(); #endregion + internal static new void ResetEvents() { + foreach (var kv in _Closed) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_Popup_Closed(_raiseClosed, cPtr); + } + _Closed.Clear(); + + foreach (var kv in _Opened) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_Popup_Opened(_raiseOpened, cPtr); + } + _Opened.Clear(); + } + #endregion public Popup() { diff --git a/Src/Noesis/Core/Src/Proxies/RiveControl.cs b/Src/Noesis/Core/Src/Proxies/RiveControl.cs index 0eba8a9..8e2eb20 100644 --- a/Src/Noesis/Core/Src/Proxies/RiveControl.cs +++ b/Src/Noesis/Core/Src/Proxies/RiveControl.cs @@ -54,6 +54,21 @@ public string GetSourceInput(uint index, out RiveSourceInputType type) { return str; } + public void SetRunText(string name, string text) { + NoesisGUI_PINVOKE.RiveControl_SetRunText(swigCPtr, name != null ? name : string.Empty, text != null ? text : string.Empty); + } + + public uint GetSourceRunCount() { + uint ret = NoesisGUI_PINVOKE.RiveControl_GetSourceRunCount(swigCPtr); + return ret; + } + + public string GetSourceRunName(uint index) { + IntPtr strPtr = NoesisGUI_PINVOKE.RiveControl_GetSourceRunName(swigCPtr, index); + string str = Noesis.Extend.StringFromNativeUtf8(strPtr); + return str; + } + public static DependencyProperty SourceProperty { get { IntPtr cPtr = NoesisGUI_PINVOKE.RiveControl_SourceProperty_get(); @@ -111,6 +126,14 @@ public RiveInputCollection Inputs { } } + public RiveRunCollection Runs { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.RiveControl_Runs_get(swigCPtr); + RiveRunCollection ret = (cPtr == IntPtr.Zero) ? null : new RiveRunCollection(cPtr, false); + return ret; + } + } + } } diff --git a/Src/Noesis/Core/Src/Proxies/RiveRun.cs b/Src/Noesis/Core/Src/Proxies/RiveRun.cs new file mode 100644 index 0000000..5f9a874 --- /dev/null +++ b/Src/Noesis/Core/Src/Proxies/RiveRun.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (http://www.swig.org). +// Version 3.0.10 +// +// Do not make changes to this file unless you know what you are doing--modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + + +using System; +using System.Runtime.InteropServices; + +namespace Noesis +{ + +public class RiveRun : Animatable { + internal new static RiveRun CreateProxy(IntPtr cPtr, bool cMemoryOwn) { + return new RiveRun(cPtr, cMemoryOwn); + } + + internal RiveRun(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { + } + + internal static HandleRef getCPtr(RiveRun obj) { + return (obj == null) ? new HandleRef(null, IntPtr.Zero) : obj.swigCPtr; + } + + public RiveRun() { + } + + protected override IntPtr CreateCPtr(Type type, out bool registerExtend) { + registerExtend = false; + return NoesisGUI_PINVOKE.new_RiveRun(); + } + + public static DependencyProperty RunNameProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.RiveRun_RunNameProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public static DependencyProperty RunTextProperty { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.RiveRun_RunTextProperty_get(); + return (DependencyProperty)Noesis.Extend.GetProxy(cPtr, false); + } + } + + public string RunName { + set { + NoesisGUI_PINVOKE.RiveRun_RunName_set(swigCPtr, value != null ? value : string.Empty); + } + get { + IntPtr strPtr = NoesisGUI_PINVOKE.RiveRun_RunName_get(swigCPtr); + string str = Noesis.Extend.StringFromNativeUtf8(strPtr); + return str; + } + } + + public string RunText { + set { + NoesisGUI_PINVOKE.RiveRun_RunText_set(swigCPtr, value != null ? value : string.Empty); + } + get { + IntPtr strPtr = NoesisGUI_PINVOKE.RiveRun_RunText_get(swigCPtr); + string str = Noesis.Extend.StringFromNativeUtf8(strPtr); + return str; + } + } + +} + +} + diff --git a/Src/Noesis/Core/Src/Proxies/RiveRunCollection.cs b/Src/Noesis/Core/Src/Proxies/RiveRunCollection.cs new file mode 100644 index 0000000..0a601ca --- /dev/null +++ b/Src/Noesis/Core/Src/Proxies/RiveRunCollection.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (http://www.swig.org). +// Version 3.0.10 +// +// Do not make changes to this file unless you know what you are doing--modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + + +using System; +using System.Runtime.InteropServices; + +namespace Noesis +{ + +public class RiveRunCollection : UICollection { + internal new static RiveRunCollection CreateProxy(IntPtr cPtr, bool cMemoryOwn) { + return new RiveRunCollection(cPtr, cMemoryOwn); + } + + internal RiveRunCollection(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { + } + + internal static HandleRef getCPtr(RiveRunCollection obj) { + return (obj == null) ? new HandleRef(null, IntPtr.Zero) : obj.swigCPtr; + } + + public RiveRunCollection() { + } + + protected override IntPtr CreateCPtr(Type type, out bool registerExtend) { + registerExtend = false; + return NoesisGUI_PINVOKE.new_RiveRunCollection(); + } + +} + +} + diff --git a/Src/Noesis/Core/Src/Proxies/TextBlock.cs b/Src/Noesis/Core/Src/Proxies/TextBlock.cs index af660e2..f48e958 100644 --- a/Src/Noesis/Core/Src/Proxies/TextBlock.cs +++ b/Src/Noesis/Core/Src/Proxies/TextBlock.cs @@ -372,6 +372,13 @@ public TextWrapping TextWrapping { } } + public FormattedText FormattedText { + get { + IntPtr cPtr = NoesisGUI_PINVOKE.TextBlock_FormattedText_get(swigCPtr); + return (FormattedText)Noesis.Extend.GetProxy(cPtr, false); + } + } + private string ToStringHelper() { IntPtr strPtr = NoesisGUI_PINVOKE.TextBlock_ToStringHelper(swigCPtr); string str = Noesis.Extend.StringFromNativeUtf8(strPtr); diff --git a/Src/Noesis/Core/Src/Proxies/Timeline.cs b/Src/Noesis/Core/Src/Proxies/Timeline.cs index bb2fa56..7a3d4f4 100644 --- a/Src/Noesis/Core/Src/Proxies/Timeline.cs +++ b/Src/Noesis/Core/Src/Proxies/Timeline.cs @@ -88,6 +88,14 @@ private static void RaiseCompleted(IntPtr cPtr, IntPtr sender, IntPtr e) { new Dictionary(); #endregion + internal static new void ResetEvents() { + foreach (var kv in _Completed) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_Timeline_Completed(_raiseCompleted, cPtr); + } + _Completed.Clear(); + } + #endregion public static int GetDesiredFrameRate(DependencyObject timeline) { diff --git a/Src/Noesis/Core/Src/Proxies/VisualStateGroup.cs b/Src/Noesis/Core/Src/Proxies/VisualStateGroup.cs index 5e228fe..cacca64 100644 --- a/Src/Noesis/Core/Src/Proxies/VisualStateGroup.cs +++ b/Src/Noesis/Core/Src/Proxies/VisualStateGroup.cs @@ -141,6 +141,20 @@ private static void RaiseCurrentStateChanged(IntPtr cPtr, IntPtr sender, IntPtr new Dictionary(); #endregion + internal static new void ResetEvents() { + foreach (var kv in _CurrentStateChanging) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_VisualStateGroup_CurrentStateChanging(_raiseCurrentStateChanging, cPtr); + } + _CurrentStateChanging.Clear(); + + foreach (var kv in _CurrentStateChanged) { + IntPtr cPtr = new IntPtr(kv.Key); + NoesisGUI_PINVOKE.UnbindEvent_VisualStateGroup_CurrentStateChanged(_raiseCurrentStateChanged, cPtr); + } + _CurrentStateChanged.Clear(); + } + #endregion public VisualStateGroup() { diff --git a/Src/Noesis/Core/Src/Proxies/VisualTreeHelper.cs b/Src/Noesis/Core/Src/Proxies/VisualTreeHelper.cs index 606cbbc..8a0b680 100644 --- a/Src/Noesis/Core/Src/Proxies/VisualTreeHelper.cs +++ b/Src/Noesis/Core/Src/Proxies/VisualTreeHelper.cs @@ -137,6 +137,12 @@ private static void HitTest3DCallbackHelper(Visual reference, Point3D point, Vec private static extern void VisualTreeHelper_HitTest3DCallback(HandleRef reference, ref Point3D point, ref Vector3D direction, Callback_HitTestFilter filter, Callback_HitTestResult result); #endregion + public static bool IntersectPlane(Visual reference, Point3D point, Vector3D direction, out Point3D outPoint) { + if (reference == null) throw new ArgumentNullException("reference"); + outPoint = new Point3D(); + return IntersectPlaneHelper(reference, point, new Point3D(direction.X, direction.Y, direction.Z), ref outPoint); + } + public static Rect GetContentBounds(Visual visual) { if (visual == null) throw new ArgumentNullException("visual"); { @@ -241,6 +247,11 @@ private static void HitTest3DHelper(Visual reference, Point3D point, Point3D dir NoesisGUI_PINVOKE.VisualTreeHelper_HitTest3DHelper(Visual.getCPtr(reference), ref point, ref direction, ref result); } + private static bool IntersectPlaneHelper(Visual reference, Point3D point, Point3D direction, ref Point3D outPoint) { + bool ret = NoesisGUI_PINVOKE.VisualTreeHelper_IntersectPlaneHelper(Visual.getCPtr(reference), ref point, ref direction, ref outPoint); + return ret; + } + } } diff --git a/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj b/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj index 1b07547..51205ce 100644 --- a/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj +++ b/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj @@ -2,9 +2,9 @@ net45;netcoreapp3.1;net5.0-windows - 3.0.23 - 3.0.23.12788 - 3.0.23.12788 + 3.0.26 + 3.0.26.13320 + 3.0.26.13320 Noesis Technologies Extends Blend with new attached properties and types that expose features included in NoesisGUI. Copyright (c) 2013 Noesis Technologies S.L. diff --git a/Src/Noesis/Extensions/Src/Element.cs b/Src/Noesis/Extensions/Src/Element.cs index f0c497e..3489246 100644 --- a/Src/Noesis/Extensions/Src/Element.cs +++ b/Src/Noesis/Extensions/Src/Element.cs @@ -125,6 +125,99 @@ public static bool GetIsFocusEngaged(Control control) #endregion + /// + /// Adds support for XY Focus Navigation. It overrides default directional keyboard + /// navigation by indicating the target element of the focus when user presses a direction + /// on a keyboard or a controller. + /// + /// Usage: + /// + /// + /// + /// + /// + /// + #region XY Focus Navigation attached properties + + public static UIElement GetXYFocusLeft(DependencyObject obj) + { + return (UIElement)obj.GetValue(XYFocusLeftProperty); + } + + public static void SetXYFocusLeft(DependencyObject obj, UIElement value) + { + obj.SetValue(XYFocusLeftProperty, value); + } + + public static readonly DependencyProperty XYFocusLeftProperty = DependencyProperty.RegisterAttached( + "XYFocusLeft", typeof(UIElement), typeof(Element), + new PropertyMetadata(null)); + + public static UIElement GetXYFocusRight(DependencyObject obj) + { + return (UIElement)obj.GetValue(XYFocusRightProperty); + } + + public static void SetXYFocusRight(DependencyObject obj, UIElement value) + { + obj.SetValue(XYFocusRightProperty, value); + } + + public static readonly DependencyProperty XYFocusRightProperty = DependencyProperty.RegisterAttached( + "XYFocusRight", typeof(UIElement), typeof(Element), + new PropertyMetadata(null)); + + public static UIElement GetXYFocusUp(DependencyObject obj) + { + return (UIElement)obj.GetValue(XYFocusUpProperty); + } + + public static void SetXYFocusUp(DependencyObject obj, UIElement value) + { + obj.SetValue(XYFocusUpProperty, value); + } + + public static readonly DependencyProperty XYFocusUpProperty = DependencyProperty.RegisterAttached( + "XYFocusUp", typeof(UIElement), typeof(Element), + new PropertyMetadata(null)); + + public static UIElement GetXYFocusDown(DependencyObject obj) + { + return (UIElement)obj.GetValue(XYFocusDownProperty); + } + + public static void SetXYFocusDown(DependencyObject obj, UIElement value) + { + obj.SetValue(XYFocusDownProperty, value); + } + + public static readonly DependencyProperty XYFocusDownProperty = DependencyProperty.RegisterAttached( + "XYFocusDown", typeof(UIElement), typeof(Element), + new PropertyMetadata(null)); + + #endregion + /// /// Determines whether antialiasing geometry is generated for this element. This property is /// inherited down the visual tree. diff --git a/Src/Noesis/Extensions/Src/InteractivityBehaviors.cs b/Src/Noesis/Extensions/Src/InteractivityBehaviors.cs index a9f8008..477042c 100644 --- a/Src/Noesis/Extensions/Src/InteractivityBehaviors.cs +++ b/Src/Noesis/Extensions/Src/InteractivityBehaviors.cs @@ -320,4 +320,117 @@ public override GeneralTransform GetDesiredTransform(GeneralTransform transform) #endregion } #endregion + + #region Line Decoration Behavior + /// + /// Renders a line decoration on the associated TextBlock. + /// + /// Usage: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public class LineDecorationBehavior : Behavior + { + #region Pen property + public Pen Pen + { + get { return (Pen)GetValue(PenProperty); } + set { SetValue(PenProperty, value); } + } + + public static readonly DependencyProperty PenProperty = DependencyProperty.Register( + "Pen", typeof(Pen), typeof(LineDecorationBehavior), new PropertyMetadata(null)); + #endregion + + #region Offset property + public float Offset + { + get { return (float)GetValue(OffsetProperty); } + set { SetValue(OffsetProperty, value); } + } + + public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register( + "Offset", typeof(float), typeof(LineDecorationBehavior), new PropertyMetadata(0.0f)); + #endregion + + #region Progress property + public float Progress + { + get { return (float)GetValue(ProgressProperty); } + set { SetValue(ProgressProperty, value); } + } + + public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register( + "Progress", typeof(float), typeof(LineDecorationBehavior), new PropertyMetadata(1.0f)); + #endregion + + #region Behavior methods + protected override void OnAttached() + { + TextBlock target = AssociatedObject; + if (target != null) + { + target.Loaded += OnTargetLoaded; + target.Unloaded += OnTargetUnloaded; + } + } + + protected override void OnDetaching() + { + TextBlock target = AssociatedObject; + if (target != null) + { + target.Loaded -= OnTargetLoaded; + target.Unloaded -= OnTargetUnloaded; + } + } + + private void OnTargetLoaded(object sender, RoutedEventArgs e) + { + TextBlock target = AssociatedObject; + if (target != null) + { + _lineDecoration = new TextDecoration() + { + Location = TextDecorationLocation.Baseline, + PenOffsetUnit = TextDecorationUnit.Pixel + }; + + BindingOperations.SetBinding(_lineDecoration, TextDecoration.PenProperty, + new Binding("Pen") { Source = this }); + BindingOperations.SetBinding(_lineDecoration, TextDecoration.PenOffsetProperty, + new Binding("Offset") { Source = this }); + + target.TextDecorations.Add(_lineDecoration); + } + } + + private void OnTargetUnloaded(object sender, RoutedEventArgs e) + { + TextBlock target = AssociatedObject; + if (target != null) + { + target.TextDecorations.Remove(_lineDecoration); + } + } + + private TextDecoration _lineDecoration; + #endregion + } + #endregion } diff --git a/Src/Noesis/Extensions/Src/InteractivityTriggers.cs b/Src/Noesis/Extensions/Src/InteractivityTriggers.cs index f362d80..d44eeed 100644 --- a/Src/Noesis/Extensions/Src/InteractivityTriggers.cs +++ b/Src/Noesis/Extensions/Src/InteractivityTriggers.cs @@ -140,7 +140,7 @@ private void OnButtonPress(object sender, KeyEventArgs e) const int GamepadLeft = 175; if (Button == (GamepadButton)((int)e.Key - GamepadLeft)) { - InvokeActions(0); + InvokeActions(e); } } diff --git a/Src/Noesis/Extensions/Src/LocExtension.cs b/Src/Noesis/Extensions/Src/LocExtension.cs index fc06f33..1aad0a6 100644 --- a/Src/Noesis/Extensions/Src/LocExtension.cs +++ b/Src/Noesis/Extensions/Src/LocExtension.cs @@ -156,6 +156,11 @@ private static void ResourcesChangedCallback(DependencyObject d, DependencyPrope if (monitor != null) { + if (monitor.TargetObject != d) + { + monitor = monitor.Clone(d); + d.SetValue(MonitorProperty, monitor); + } monitor.InvalidateResources((ResourceDictionary)e.NewValue); } } @@ -187,19 +192,19 @@ public static void SetResources(DependencyObject dependencyObject, ResourceDicti internal class LocMonitor { - private readonly DependencyObject _targetObject; - private readonly List> _monitoredDependencyProperties = new List>(); + public DependencyObject TargetObject { get; } + public LocMonitor(DependencyObject targetObject) { - _targetObject = targetObject; + TargetObject = targetObject; } public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey) { - ResourceDictionary resourceDictionary = LocExtension.GetResources(_targetObject); + ResourceDictionary resourceDictionary = LocExtension.GetResources(TargetObject); for (int i = 0; i < _monitoredDependencyProperties.Count; i++) { @@ -221,11 +226,18 @@ public void InvalidateResources(ResourceDictionary resourceDictionary) { foreach (Tuple entry in _monitoredDependencyProperties) { - _targetObject.SetValue(entry.Item1, + TargetObject.SetValue(entry.Item1, Evaluate(entry.Item1, entry.Item2, resourceDictionary)); } } + public LocMonitor Clone(DependencyObject targetObject) + { + LocMonitor clone = new LocMonitor(targetObject); + clone._monitoredDependencyProperties.AddRange(_monitoredDependencyProperties); + return clone; + } + private static object Evaluate(DependencyProperty targetProperty, string resourceKey, ResourceDictionary resourceDictionary) { @@ -248,4 +260,4 @@ private static object Evaluate(DependencyProperty targetProperty, string resourc return null; } } -} +} \ No newline at end of file diff --git a/Src/Noesis/Extensions/Src/RichText.cs b/Src/Noesis/Extensions/Src/RichText.cs index d920b7f..dd67247 100644 --- a/Src/Noesis/Extensions/Src/RichText.cs +++ b/Src/Noesis/Extensions/Src/RichText.cs @@ -329,7 +329,7 @@ static void TryCreateInlineForTag(string tagName, string content, case "img": { Image image = new Image(); - image.Source = new BitmapImage(new Uri(content)); + image.Source = new BitmapImage(new Uri(content, UriKind.RelativeOrAbsolute)); inlineCollection.Add(new InlineUIContainer(image)); foreach (Parameter element in parameters) { diff --git a/Src/Noesis/Extensions/Src/Text.cs b/Src/Noesis/Extensions/Src/Text.cs index 3f41174..da98730 100644 --- a/Src/Noesis/Extensions/Src/Text.cs +++ b/Src/Noesis/Extensions/Src/Text.cs @@ -33,12 +33,12 @@ public static class Text DependencyProperty.RegisterAttached("CharacterSpacing", typeof(int), typeof(Text), new FrameworkPropertyMetadata((int)0)); - public static void SetCharacterSpacing(UIElement element, int value) + public static void SetCharacterSpacing(DependencyObject element, int value) { element.SetValue(CharacterSpacingProperty, value); } - public static int GetCharacterSpacing(UIElement element) + public static int GetCharacterSpacing(DependencyObject element) { return (int)element.GetValue(CharacterSpacingProperty); } @@ -64,12 +64,12 @@ public static int GetCharacterSpacing(UIElement element) DependencyProperty.RegisterAttached("Stroke", typeof(System.Windows.Media.Brush), typeof(Text), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); - public static void SetStroke(UIElement element, System.Windows.Media.Brush value) + public static void SetStroke(DependencyObject element, System.Windows.Media.Brush value) { element.SetValue(StrokeProperty, value); } - public static System.Windows.Media.Brush GetStroke(UIElement element) + public static System.Windows.Media.Brush GetStroke(DependencyObject element) { return (System.Windows.Media.Brush)element.GetValue(StrokeProperty); } @@ -85,12 +85,12 @@ public static System.Windows.Media.Brush GetStroke(UIElement element) DependencyProperty.RegisterAttached("StrokeThickness", typeof(double), typeof(Text), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.Inherits)); - public static void SetStrokeThickness(UIElement element, double value) + public static void SetStrokeThickness(DependencyObject element, double value) { element.SetValue(StrokeThicknessProperty, value); } - public static double GetStrokeThickness(UIElement element) + public static double GetStrokeThickness(DependencyObject element) { return (double)element.GetValue(StrokeThicknessProperty); } @@ -183,12 +183,12 @@ public static uint GetPasswordLength(PasswordBox element) DependencyProperty.RegisterAttached("ShowLastCharacterDuration", typeof(int), typeof(Text), new PropertyMetadata(0)); - public static int GetShowLastCharacterDuration(DependencyObject obj) + public static int GetShowLastCharacterDuration(PasswordBox obj) { return (int)obj.GetValue(ShowLastCharacterDurationProperty); } - public static void SetShowLastCharacterDuration(DependencyObject obj, int value) + public static void SetShowLastCharacterDuration(PasswordBox obj, int value) { obj.SetValue(ShowLastCharacterDurationProperty, value); } diff --git a/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml b/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml index 933bf9e..32d8769 100644 --- a/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml +++ b/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml @@ -571,6 +571,28 @@ + + + + + + + + + + + + + + + + + diff --git a/Src/NoesisApp/Core/Src/Interactivity/DataEventTrigger.cs b/Src/NoesisApp/Core/Src/Interactivity/DataEventTrigger.cs index 4a21d45..c7be808 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/DataEventTrigger.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/DataEventTrigger.cs @@ -71,15 +71,13 @@ private void UpdateHandler() this.currentTarget = this.Source; - if (this.currentTarget != null && !string.IsNullOrEmpty(this.EventName)) + string eventName = this.EventName; + if (this.currentTarget != null && !string.IsNullOrEmpty(eventName)) { - Type targetType = this.currentTarget.GetType(); - this.currentEvent = targetType.GetEvent(this.EventName); + this.currentEvent = targetType.GetEvent(eventName); if (this.currentEvent != null) { - - MethodInfo handlerMethod = this.GetType().GetMethod("OnEvent", BindingFlags.NonPublic | BindingFlags.Instance); this.currentDelegate = this.GetDelegate(this.currentEvent, this.OnMethod); this.currentEvent.AddEventHandler(this.currentTarget, this.currentDelegate); } @@ -90,8 +88,7 @@ private Delegate GetDelegate(EventInfo eventInfo, Action action) { if (typeof(System.EventHandler).IsAssignableFrom(eventInfo.EventHandlerType)) { - MethodInfo method = this.GetType().GetMethod("OnEvent", BindingFlags.NonPublic | BindingFlags.Instance); - return Delegate.CreateDelegate(eventInfo.EventHandlerType, this, method); + return Delegate.CreateDelegate(eventInfo.EventHandlerType, this, OnEventMethod); } Type handlerType = eventInfo.EventHandlerType; @@ -114,6 +111,9 @@ private void OnEvent(object sender, System.EventArgs e) this.InvokeActions(e); } + public static readonly MethodInfo OnEventMethod = typeof(DataEventTrigger).GetMethod( + "OnEvent", BindingFlags.Instance | BindingFlags.NonPublic); + private EventInfo currentEvent; private Delegate currentDelegate; private object currentTarget; diff --git a/Src/NoesisApp/Core/Src/Interactivity/EventTriggerBase.cs b/Src/NoesisApp/Core/Src/Interactivity/EventTriggerBase.cs index bed8243..7c319d3 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/EventTriggerBase.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/EventTriggerBase.cs @@ -1,5 +1,6 @@ using Noesis; using System; +using System.Collections.Generic; using System.Reflection; namespace NoesisApp @@ -11,6 +12,8 @@ public abstract class EventTriggerBase : TriggerBase { protected EventTriggerBase(Type sourceType) : base(typeof(DependencyObject)) { + if (_events == null) _events = new Dictionary(); + _sourceType = sourceType; _source = IntPtr.Zero; _event = null; @@ -160,27 +163,44 @@ private void RegisterEvent(object source, string eventName) if (source != null && !string.IsNullOrEmpty(eventName)) { Type type = source.GetType(); - EventInfo ev = type.GetEvent(eventName); - if (ev == null) + EventInfo ev; + EventKey key = new EventKey { Type = type, Name = eventName }; + + if (!_events.TryGetValue(key, out ev)) { - if (SourceObject != null) + ev = type.GetEvent(eventName); + + if (ev == null) { - throw new ArgumentException(string.Format( - "EventTrigger cannot find event '{0}' in SourceObject '{1}'", - eventName, type)); + _events.Add(key, null); + + if (SourceObject != null) + { + throw new ArgumentException(string.Format( + "EventTrigger cannot find event '{0}' in SourceObject '{1}'", + eventName, type)); + } } - } - else if (!IsValidEvent(ev)) - { - if (SourceObject != null) + else if (!IsValidEvent(ev)) + { + ev = null; + _events.Add(key, null); + + if (SourceObject != null) + { + throw new ArgumentException(string.Format( + "SourceObject event '{0}' is not valid for EventTrigger", + eventName)); + } + } + else { - throw new ArgumentException(string.Format( - "SourceObject event '{0}' is not valid for EventTrigger", - eventName)); + _events.Add(key, ev); } } - else + + if (ev != null) { _event = ev; _handler = Delegate.CreateDelegate(ev.EventHandlerType, this, OnEventMethod); @@ -189,6 +209,18 @@ private void RegisterEvent(object source, string eventName) } } + [ThreadStatic] + private static Dictionary _events = null; + + private struct EventKey : IEquatable + { + public Type Type; + public string Name; + + public bool Equals(EventKey other) { return Type.Equals(other.Type) && Name == other.Name; } + public override int GetHashCode() { return Type.GetHashCode() ^ Name.GetHashCode(); } + } + private void UnregisterEvent(object source) { if (_event != null && _handler != null) diff --git a/Src/NoesisApp/Core/Src/Interactivity/GamepadTrigger.cs b/Src/NoesisApp/Core/Src/Interactivity/GamepadTrigger.cs index 8bb840f..72ca602 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/GamepadTrigger.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/GamepadTrigger.cs @@ -117,7 +117,7 @@ private void OnButtonPress(object sender, KeyEventArgs e) { if (Button == (GamepadButton)(e.OriginalKey - Key.GamepadLeft)) { - InvokeActions(0); + InvokeActions(e); } } diff --git a/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs b/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs index 6725081..0d5d53a 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs @@ -1,5 +1,6 @@ using Noesis; using System; +using System.Globalization; using System.Reflection; using System.Windows.Input; @@ -55,15 +56,98 @@ public object CommandParameter "CommandParameter", typeof(object), typeof(InvokeCommandAction), new PropertyMetadata(null)); + /// + /// Gets or sets the property path used to extract a value from the EventArgs to pass to the + /// Command as a parameter. If the CommandParameter propert is set, this property is ignored + /// + public string EventArgsParameterPath + { + get { return (string)GetValue(EventArgsParameterPathProperty); } + set { SetValue(EventArgsParameterPathProperty, value); } + } + + public static readonly DependencyProperty EventArgsParameterPathProperty = DependencyProperty.Register( + "EventArgsParameterPath", typeof(string), typeof(InvokeCommandAction), + new PropertyMetadata(null)); + + /// + /// Gets or sets the IValueConverter that is used to convert the EventArgs passed to the + /// Command as a parameter. If the CommandParameter or EventArgsParameterPath properties are + /// set, this property is ignored + /// + public Noesis.IValueConverter EventArgsConverter + { + get { return (Noesis.IValueConverter)GetValue(EventArgsConverterProperty); } + set { SetValue(EventArgsConverterProperty, value); } + } + + public static readonly DependencyProperty EventArgsConverterProperty = DependencyProperty.Register( + "EventArgsConverter", typeof(Noesis.IValueConverter), typeof(InvokeCommandAction), + new PropertyMetadata(null)); + + /// + /// Gets or sets the parameter that is passed to the EventArgsConverter + /// + public object EventArgsConverterParameter + { + get { return GetValue(EventArgsConverterParameterProperty); } + set { SetValue(EventArgsConverterParameterProperty, value); } + } + + public static readonly DependencyProperty EventArgsConverterParameterProperty = DependencyProperty.Register( + "EventArgsConverterParameter", typeof(object), typeof(InvokeCommandAction), + new PropertyMetadata(null)); + + /// + /// Specifies whether the EventArgs of the event that triggered this action should be passed to + /// the Command as a parameter. If the Command, EventArgsParameterPath, or EventArgsConverter + /// properties are set, this property is ignored + /// + public bool PassEventArgsToCommand + { + get; set; + } + protected override void Invoke(object parameter) { if (AssociatedObject != null) { ICommand command = ResolveCommand(); - object commandParameter = CommandParameter; - if (command != null && command.CanExecute(commandParameter)) + if (command != null) { - command.Execute(commandParameter); + object commandParam = CommandParameter; + + // If no CommandParameter has been provided, let's check the EventArgsParameterPath + if (commandParam == null && parameter != null) + { + string path = EventArgsParameterPath; + if (!string.IsNullOrEmpty(path)) + { + commandParam = ResolvePath(path, parameter); + } + } + + // Next let's see if an event args converter has been supplied + if (commandParam == null && parameter != null) + { + Noesis.IValueConverter converter = EventArgsConverter; + if (converter != null) + { + commandParam = converter.Convert(parameter, typeof(object), + EventArgsConverterParameter, CultureInfo.CurrentCulture); + } + } + + // Last resort, let see if they want to force the event args to be passed as a parameter + if (commandParam == null && PassEventArgsToCommand) + { + commandParam = parameter; + } + + if (command.CanExecute(commandParam)) + { + command.Execute(commandParam); + } } } } @@ -96,6 +180,21 @@ private ICommand ResolveCommand() return null; } - string _commandName = string.Empty; + private object ResolvePath(string path, object parameter) + { + object commandParameter; + object propertyValue = parameter; + string[] parts = path.Split('.'); + foreach (string part in parts) + { + PropertyInfo propInfo = propertyValue.GetType().GetProperty(part); + propertyValue = propInfo.GetValue(propertyValue, null); + } + + commandParameter = propertyValue; + return commandParameter; + } + + private string _commandName = string.Empty; } } diff --git a/Src/NoesisApp/Core/Src/Interactivity/KeyTrigger.cs b/Src/NoesisApp/Core/Src/Interactivity/KeyTrigger.cs index 71983a9..241f0a3 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/KeyTrigger.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/KeyTrigger.cs @@ -111,7 +111,7 @@ private void OnKeyPress(object sender, KeyEventArgs e) UIElement source = (UIElement)GetProxy(_source); if (Key == e.Key && Modifiers == source.Keyboard.Modifiers) { - InvokeActions(0); + InvokeActions(e); } } diff --git a/Src/NoesisApp/Core/Src/Interactivity/LineDecorationBehavior.cs b/Src/NoesisApp/Core/Src/Interactivity/LineDecorationBehavior.cs new file mode 100644 index 0000000..f9c71fe --- /dev/null +++ b/Src/NoesisApp/Core/Src/Interactivity/LineDecorationBehavior.cs @@ -0,0 +1,223 @@ +using Noesis; +using NoesisApp; +using System.Collections.Generic; + +namespace NoesisGUIExtensions +{ + /// + /// Renders a line decoration on the associated TextBlock. + /// + /// Usage: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public class LineDecorationBehavior : Behavior + { + #region Pen property + public Pen Pen + { + get { return (Pen)GetValue(PenProperty); } + set { SetValue(PenProperty, value); } + } + + public static readonly DependencyProperty PenProperty = DependencyProperty.Register( + "Pen", typeof(Pen), typeof(LineDecorationBehavior), new PropertyMetadata(null)); + #endregion + + #region Offset property + public float Offset + { + get { return (float)GetValue(OffsetProperty); } + set { SetValue(OffsetProperty, value); } + } + + public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register( + "Offset", typeof(float), typeof(LineDecorationBehavior), new PropertyMetadata(0.0f)); + #endregion + + #region Progress property + public float Progress + { + get { return (float)GetValue(ProgressProperty); } + set { SetValue(ProgressProperty, value); } + } + + public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register( + "Progress", typeof(float), typeof(LineDecorationBehavior), new PropertyMetadata(1.0f)); + #endregion + + #region Behavior methods + protected override void OnAttached() + { + TextBlock target = AssociatedObject; + if (target != null) + { + target.Loaded += OnTargetLoaded; + target.Unloaded += OnTargetUnloaded; + target.LayoutUpdated += OnTargetUpdated; + } + } + + protected override void OnDetaching() + { + TextBlock target = AssociatedObject; + if (target != null) + { + target.Loaded -= OnTargetLoaded; + target.Unloaded -= OnTargetUnloaded; + target.LayoutUpdated -= OnTargetUpdated; + } + } + + private void OnTargetLoaded(object sender, RoutedEventArgs e) + { + TextBlock target = AssociatedObject; + if (target != null) + { + AdornerLayer adorners = AdornerLayer.GetAdornerLayer(target); + _adorner = new LineAdorner(target, this); + adorners.Add(_adorner); + } + } + + private void OnTargetUnloaded(object sender, RoutedEventArgs e) + { + TextBlock target = AssociatedObject; + if (target != null) + { + AdornerLayer adorners = AdornerLayer.GetAdornerLayer(target); + adorners.Remove(_adorner); + _adorner = null; + } + } + + private void OnTargetUpdated(object sender, EventArgs e) + { + TextBlock target = AssociatedObject; + if (_adorner != null && target != null && target.IsVisible && target.Opacity > 0.0f) + { + Matrix4 mtx = target.TransformToVisual((Visual)VisualTreeHelper.GetRoot(target)); + if (_adorner.TransformToTarget != mtx) + { + _adorner.TransformToTarget = mtx; + + AdornerLayer adorners = AdornerLayer.GetAdornerLayer(target); + adorners.Update(target); + } + } + } + + private LineAdorner _adorner; + #endregion + + #region Adorner + private class LineAdorner : Adorner + { + public static readonly DependencyProperty PenProperty = DependencyProperty.Register( + "Pen", typeof(Pen), typeof(LineAdorner), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register( + "Offset", typeof(float), typeof(LineAdorner), + new FrameworkPropertyMetadata(0.0f, FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register( + "Progress", typeof(float), typeof(LineAdorner), + new FrameworkPropertyMetadata(1.0f, FrameworkPropertyMetadataOptions.AffectsRender)); + + public LineAdorner(TextBlock adornedElement, LineDecorationBehavior owner) : + base(adornedElement) + { + BindingOperations.SetBinding(this, PenProperty, + new Binding(LineDecorationBehavior.PenProperty, owner)); + + BindingOperations.SetBinding(this, OffsetProperty, + new Binding(LineDecorationBehavior.OffsetProperty, owner)); + + BindingOperations.SetBinding(this, ProgressProperty, + new Binding(LineDecorationBehavior.ProgressProperty, owner)); + } + + private Matrix4 _mtx = Matrix4.Identity; + public Matrix4 TransformToTarget + { + get => _mtx; + set { _mtx = value; } + } + + private struct LineData + { + public float x0; + public float x1; + public float y; + }; + +#if UNITY_5_3_OR_NEWER + internal +#endif + protected override void OnRender(DrawingContext context) + { + // Check if we are going to render anything + float progress = (float)GetValue(ProgressProperty); + if (progress <= 0.0f) return; + Pen pen = (Pen)GetValue(PenProperty); + if (pen == null || pen.Brush == null || pen.Thickness == 0.0f) return; + + // Get line render data and calculate the total length in pixels + List lineData = new List(); + + TextBlock tb = (TextBlock)AdornedElement; + FormattedText text = tb.FormattedText; + + float totalLength = 0.0f; + uint glyphIndex = 0; + uint numLines = text.NumLines; + for (uint i = 0; i < numLines; ++i) + { + FormattedText.LineInfo line = text.GetLineInfo(i); + + LineData l = new LineData(); + text.GetGlyphPosition(glyphIndex, false, ref l.x0, ref l.y); + text.GetGlyphPosition(glyphIndex + line.NumGlyphs - 1, true, ref l.x1, ref l.y); + totalLength += l.x1 - l.x0; + glyphIndex += line.NumGlyphs; + lineData.Add(l); + } + + // Render the lines until we reach the specified length progress + float offset = (float)GetValue(OffsetProperty); + float endLength = totalLength * progress; + float length = 0.0f; + for (int i = 0; i < numLines; ++i) + { + if (length >= endLength) break; + + LineData l = lineData[i]; + float x0 = l.x0; + float x1 = System.Math.Min(l.x1, l.x0 + endLength - length); + float y = l.y + offset; + + context.DrawLine(pen, new Point(x0, y), new Point(x1, y)); + + length += x1 - x0; + } + } + } + #endregion + } +} diff --git a/Src/NoesisApp/Core/Src/Interactivity/TriggerBase.Interactivity.cs b/Src/NoesisApp/Core/Src/Interactivity/TriggerBase.Interactivity.cs index ad8e6dc..8d82d00 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/TriggerBase.Interactivity.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/TriggerBase.Interactivity.cs @@ -66,6 +66,8 @@ protected override void OnDetaching() /// protected void InvokeActions(object parameter) { + TriggerBase thisTrigger = this; + if (PreviewInvoke != null) { PreviewInvokeEventArgs previewInvokeEventArgs = new PreviewInvokeEventArgs(); @@ -79,6 +81,7 @@ protected void InvokeActions(object parameter) foreach (TriggerAction triggerAction in Actions) { + if (base.AssociatedObject == null) break; triggerAction.CallInvoke(parameter); } } diff --git a/Src/NoesisApp/Core/Src/Localization/LocExtension.cs b/Src/NoesisApp/Core/Src/Localization/LocExtension.cs index e7e9c3a..1ea873d 100644 --- a/Src/NoesisApp/Core/Src/Localization/LocExtension.cs +++ b/Src/NoesisApp/Core/Src/Localization/LocExtension.cs @@ -155,6 +155,11 @@ private static void ResourcesChangedCallback(DependencyObject d, DependencyPrope if (monitor != null) { + if (monitor.TargetObject != d) + { + monitor = monitor.Clone(d); + d.SetValue(MonitorProperty, monitor); + } monitor.InvalidateResources((ResourceDictionary)e.NewValue); } } @@ -181,19 +186,19 @@ public static ResourceDictionary GetResources(DependencyObject dependencyObject) internal class LocMonitor { - private readonly DependencyObject _targetObject; - private readonly List> _monitoredDependencyProperties = new List>(); + public DependencyObject TargetObject { get; } + public LocMonitor(DependencyObject targetObject) { - _targetObject = targetObject; + TargetObject = targetObject; } public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey) { - ResourceDictionary resourceDictionary = LocExtension.GetResources(_targetObject); + ResourceDictionary resourceDictionary = LocExtension.GetResources(TargetObject); for (int i = 0; i < _monitoredDependencyProperties.Count; i++) { @@ -215,11 +220,18 @@ public void InvalidateResources(ResourceDictionary resourceDictionary) { foreach (Tuple entry in _monitoredDependencyProperties) { - _targetObject.SetValue(entry.Item1, + TargetObject.SetValue(entry.Item1, Evaluate(entry.Item1, entry.Item2, resourceDictionary)); } } + public LocMonitor Clone(DependencyObject targetObject) + { + LocMonitor clone = new LocMonitor(targetObject); + clone._monitoredDependencyProperties.AddRange(_monitoredDependencyProperties); + return clone; + } + private static object Evaluate(DependencyProperty targetProperty, string resourceKey, ResourceDictionary resourceDictionary) { @@ -242,4 +254,4 @@ private static object Evaluate(DependencyProperty targetProperty, string resourc return null; } } -} +} \ No newline at end of file diff --git a/Src/NoesisApp/Core/Src/Localization/RichText.cs b/Src/NoesisApp/Core/Src/Localization/RichText.cs index e09b995..238f1bf 100644 --- a/Src/NoesisApp/Core/Src/Localization/RichText.cs +++ b/Src/NoesisApp/Core/Src/Localization/RichText.cs @@ -323,7 +323,7 @@ static void TryCreateInlineForTag(string tagName, string content, case "img": { Image image = new Image(); - image.Source = new BitmapImage(new Uri(content)); + image.Source = new BitmapImage(new Uri(content, UriKind.RelativeOrAbsolute)); inlineCollection.Add(new InlineUIContainer(image)); foreach (Parameter element in parameters) { diff --git a/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml b/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml index 933bf9e..32d8769 100644 --- a/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml +++ b/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml @@ -571,6 +571,28 @@ + + + + + + + + + + + + + + + + + diff --git a/THIRD_PARTY.txt b/THIRD_PARTY.txt index 35f1335..b003264 100644 --- a/THIRD_PARTY.txt +++ b/THIRD_PARTY.txt @@ -8,7 +8,7 @@ Libraries used in Noesis Core * LibTess * HarfBuzz * Expat -* Grisu2 +* Ryu Libaries used in Samples and App Framework (C++) ================================================ @@ -17,6 +17,7 @@ Libaries used in Samples and App Framework (C++) * MiniAudio * FastLZ * glslang & SPIRV-Cross +* RenderDoc Libaries used in Samples and App Framework (C#) ================================================ @@ -228,32 +229,33 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------------------- -Grisu2 / Public Domain -http://florian.loitsch.com/publications/dtoa-pldi2010.pdf +Ryu / BSL-1.0 license +https://github.com/ulfjack/ryu ----------------------------------------------------------------------------------- -Copyright (c) 2009 Florian Loitsch +Boost Software License - Version 1.0 - August 17th, 2003 -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: +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. -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. +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------------------- glslang / BSD 3-Clause "New" or "Revised" License, and Khronos License, and Apache @@ -1530,4 +1532,30 @@ 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 MATERIALS OR THE USE OR OTHER DEALINGS -IN THE MATERIALS. \ No newline at end of file +IN THE MATERIALS. + +----------------------------------------------------------------------------------- +RenderDoc / MIT License +https://renderdoc.org/ +----------------------------------------------------------------------------------- + +The MIT License (MIT) + +Copyright (c) 2015-2023 Baldur Karlsson +Copyright (c) 2014 Crytek +Copyright (c) 1998-2018 Third party code and tools + +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. \ No newline at end of file