From f0aef3aacc8bb15e154bb92d9854b4fa26bf1fc5 Mon Sep 17 00:00:00 2001 From: s-fernandez-v Date: Fri, 21 Jun 2024 00:07:03 +0200 Subject: [PATCH] Updated to NoesisGUI 3.2.4 version --- Src/Noesis/Core/Src/Core/Extend.cs | 42 ++--- Src/Noesis/Core/Src/Core/NoesisGUI.cs | 2 +- Src/Noesis/Core/Src/Core/RenderDevice.cs | 21 +++ .../Core/Src/Core/TypeConverterHelper.cs | 126 +++++++++++++ Src/Noesis/Core/Src/Proxies/Color.cs | 13 ++ Src/Noesis/Core/Src/Proxies/CornerRadius.cs | 1 + Src/Noesis/Core/Src/Proxies/Inline.cs | 17 +- Src/Noesis/Core/Src/Proxies/Int32Rect.cs | 1 + .../Core/Src/Proxies/NoesisGUI_PINVOKE.cs | 10 +- Src/Noesis/Core/Src/Proxies/Point.cs | 1 + Src/Noesis/Core/Src/Proxies/Rect.cs | 1 + Src/Noesis/Core/Src/Proxies/Size.cs | 1 + Src/Noesis/Core/Src/Proxies/TextElement.cs | 17 +- Src/Noesis/Core/Src/Proxies/Thickness.cs | 1 + .../Extensions/Noesis.GUI.Extensions.csproj | 6 +- Src/Noesis/Extensions/Src/LocExtension.cs | 130 +++++++++++-- Src/Noesis/Extensions/Src/Rive.cs | 38 ++++ .../Extensions/Theme/NoesisTheme.Styles.xaml | 2 + .../Src/Interactivity/InvokeCommandAction.cs | 9 + .../Core/Src/Localization/LocExtension.cs | 177 +++++++++++++----- .../Theme/Theme/NoesisTheme.Styles.xaml | 2 + 21 files changed, 484 insertions(+), 134 deletions(-) diff --git a/Src/Noesis/Core/Src/Core/Extend.cs b/Src/Noesis/Core/Src/Core/Extend.cs index 709b7b9..8ec86a3 100644 --- a/Src/Noesis/Core/Src/Core/Extend.cs +++ b/Src/Noesis/Core/Src/Core/Extend.cs @@ -2576,19 +2576,10 @@ private static bool ConverterConvert(IntPtr cPtr, object obj = converter.Convert(val, targetType, param, CultureInfo.CurrentCulture); - if (AreCompatibleTypes(obj, targetType)) - { - HandleRef res = GetInstanceHandle(obj); - BaseComponent.AddReference(res.Handle); // released by native bindings - result = res.Handle; - return true; - } - else - { - Log.Error(string.Format("{0} Convert() expects {1} and {2} is returned", - converter.GetType().FullName, targetType.FullName, - obj != null ? obj.GetType().FullName : "null")); - } + HandleRef res = GetInstanceHandle(obj); + BaseComponent.AddReference(res.Handle); // released by native bindings + result = res.Handle; + return true; } } catch (Exception e) @@ -2622,19 +2613,10 @@ private static bool ConverterConvertBack(IntPtr cPtr, object obj = converter.ConvertBack(val, targetType, param, CultureInfo.CurrentCulture); - if (AreCompatibleTypes(obj, targetType)) - { - HandleRef res = GetInstanceHandle(obj); - BaseComponent.AddReference(res.Handle); // released by native bindings - result = res.Handle; - return true; - } - else - { - Log.Error(string.Format("{0} ConvertBack() expects {1} and {2} is returned", - converter.GetType().FullName, targetType.FullName, - obj != null ? obj.GetType().FullName : "null")); - } + HandleRef res = GetInstanceHandle(obj); + BaseComponent.AddReference(res.Handle); // released by native bindings + result = res.Handle; + return true; } } catch (Exception e) @@ -5742,11 +5724,12 @@ public static IntPtr NewCPtr(Type type) } //////////////////////////////////////////////////////////////////////////////////////////////// - private delegate void Callback_CreateInstance(IntPtr nativeType, IntPtr cPtr); + [return: MarshalAs(UnmanagedType.U1)] + private delegate bool Callback_CreateInstance(IntPtr nativeType, IntPtr cPtr); private static Callback_CreateInstance _createInstance = CreateInstance; [MonoPInvokeCallback(typeof(Callback_CreateInstance))] - private static void CreateInstance(IntPtr nativeType, IntPtr cPtr) + private static bool CreateInstance(IntPtr nativeType, IntPtr cPtr) { try { @@ -5782,10 +5765,13 @@ private static void CreateInstance(IntPtr nativeType, IntPtr cPtr) BaseComponent.AddReference(cPtr); RegisterInterfaces(instance); } + + return true; } catch (Exception e) { Error.UnhandledException(e); + return false; } } diff --git a/Src/Noesis/Core/Src/Core/NoesisGUI.cs b/Src/Noesis/Core/Src/Core/NoesisGUI.cs index c0a8926..8302db9 100644 --- a/Src/Noesis/Core/Src/Core/NoesisGUI.cs +++ b/Src/Noesis/Core/Src/Core/NoesisGUI.cs @@ -629,7 +629,7 @@ static extern IntPtr Noesis_LoadStreamXaml(HandleRef stream, static extern IntPtr Noesis_LoadXaml([MarshalAs(UnmanagedType.LPStr)] string filename); [DllImport(Library.Name)] - static extern IntPtr Noesis_ParseXaml([MarshalAs(UnmanagedType.LPStr)] string xamlText); + static extern IntPtr Noesis_ParseXaml([MarshalAs(UnmanagedType.LPWStr)] string xamlText); [DllImport(Library.Name)] static extern IntPtr Noesis_LoadXamlResource( diff --git a/Src/Noesis/Core/Src/Core/RenderDevice.cs b/Src/Noesis/Core/Src/Core/RenderDevice.cs index db07502..3e28fab 100644 --- a/Src/Noesis/Core/Src/Core/RenderDevice.cs +++ b/Src/Noesis/Core/Src/Core/RenderDevice.cs @@ -19,12 +19,33 @@ public enum TextureFormat [StructLayout(LayoutKind.Sequential)] public struct DeviceCaps { + /// Offset in pixel units from top-left corner to center of pixel [MarshalAs(UnmanagedType.R4)] public float CenterPixelOffset; + + /// + /// When this flag is enabled the device works in 'Linear' mode. All internal textures and + /// offscreens are created in 'sRGB' format. In this mode, the device also expects colors + /// (like the ones in the vertex buffer) in 'sRGB' format. It also indicates that the device + /// writes to the render target in linear space + /// [MarshalAs(UnmanagedType.U1)] public bool LinearRendering; + + /// + /// This flag is enabled to indicate that the device supports LCD subpixel rendering. Extra + /// shaders and dual source blending are needed for this feature + /// [MarshalAs(UnmanagedType.U1)] public bool SubpixelRendering; + + /// Indicates whether the device clip space depth values range from 0 to 1 + [MarshalAs(UnmanagedType.U1)] + public bool DepthRangeZeroToOne; + + /// Whether the device clip space Y values are inverted (increase from top (-1) to bottom (1)) + [MarshalAs(UnmanagedType.U1)] + public bool ClipSpaceYInverted; } /// diff --git a/Src/Noesis/Core/Src/Core/TypeConverterHelper.cs b/Src/Noesis/Core/Src/Core/TypeConverterHelper.cs index c6c3aa1..12662b8 100644 --- a/Src/Noesis/Core/Src/Core/TypeConverterHelper.cs +++ b/Src/Noesis/Core/Src/Core/TypeConverterHelper.cs @@ -5,6 +5,132 @@ namespace Noesis { + public class SizeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Size.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class PointConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Point.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class RectConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Rect.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class Int32RectConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Int32Rect.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class ThicknessConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Thickness.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class CornerRadiusConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return CornerRadius.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + + public class ColorConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string) + { + return Color.Parse((string)value); + } + + return base.ConvertFrom(context, culture, value); + } + } + public class BrushConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) diff --git a/Src/Noesis/Core/Src/Proxies/Color.cs b/Src/Noesis/Core/Src/Proxies/Color.cs index 4cd21a4..b2bc3a5 100644 --- a/Src/Noesis/Core/Src/Proxies/Color.cs +++ b/Src/Noesis/Core/Src/Proxies/Color.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(ColorConverter))] public struct Color { [MarshalAs(UnmanagedType.R4)] @@ -186,6 +187,18 @@ private static bool AreClose(float a, float b) { return !(l == r); } + internal static Color Parse(string str) { + if (Color.TryParse(str, out Color color)) { + return color; + } + throw new ArgumentException("Cannot create Color from '" + str + "'"); + } + + private static bool TryParse(string str, out Color result) { + bool ret = NoesisGUI_PINVOKE.Color_TryParse(str != null ? str : string.Empty, out result); + return ret; + } + } } diff --git a/Src/Noesis/Core/Src/Proxies/CornerRadius.cs b/Src/Noesis/Core/Src/Proxies/CornerRadius.cs index ff29e6d..718fef6 100644 --- a/Src/Noesis/Core/Src/Proxies/CornerRadius.cs +++ b/Src/Noesis/Core/Src/Proxies/CornerRadius.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(CornerRadiusConverter))] public struct CornerRadius { [MarshalAs(UnmanagedType.R4)] diff --git a/Src/Noesis/Core/Src/Proxies/Inline.cs b/Src/Noesis/Core/Src/Proxies/Inline.cs index 657e6f4..5ee0fd9 100644 --- a/Src/Noesis/Core/Src/Proxies/Inline.cs +++ b/Src/Noesis/Core/Src/Proxies/Inline.cs @@ -15,11 +15,7 @@ namespace Noesis { -public class Inline : TextElement { - internal new static Inline CreateProxy(IntPtr cPtr, bool cMemoryOwn) { - return new Inline(cPtr, cMemoryOwn); - } - +public abstract class Inline : TextElement { internal Inline(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { } @@ -27,6 +23,9 @@ internal static HandleRef getCPtr(Inline obj) { return (obj == null) ? new HandleRef(null, IntPtr.Zero) : obj.swigCPtr; } + protected Inline() { + } + public new FlowDirection FlowDirection { set { SetFlowDirection(value); @@ -36,14 +35,6 @@ internal static HandleRef getCPtr(Inline obj) { } } - public Inline() { - } - - protected override IntPtr CreateCPtr(Type type, out bool registerExtend) { - registerExtend = false; - return NoesisGUI_PINVOKE.new_Inline(); - } - private FlowDirection GetFlowDirection() { FlowDirection ret = (FlowDirection)NoesisGUI_PINVOKE.Inline_GetFlowDirection(swigCPtr); return ret; diff --git a/Src/Noesis/Core/Src/Proxies/Int32Rect.cs b/Src/Noesis/Core/Src/Proxies/Int32Rect.cs index cdbedab..edc44ad 100644 --- a/Src/Noesis/Core/Src/Proxies/Int32Rect.cs +++ b/Src/Noesis/Core/Src/Proxies/Int32Rect.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(Int32RectConverter))] public struct Int32Rect { [MarshalAs(UnmanagedType.I4)] diff --git a/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs b/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs index 2ef09c6..98f801e 100644 --- a/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs +++ b/Src/Noesis/Core/Src/Proxies/NoesisGUI_PINVOKE.cs @@ -730,6 +730,10 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern IntPtr Box_Color(ref Color jarg1); + [DllImport(Library.Name)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool Color_TryParse([MarshalAs(UnmanagedType.LPWStr)]string jarg1, out Color jarg2); + [DllImport(Library.Name)] public static extern IntPtr Matrix_GetStaticType(); @@ -5782,12 +5786,6 @@ internal class NoesisGUI_PINVOKE { [DllImport(Library.Name)] public static extern float TextElement_StrokeThickness_get(HandleRef jarg1); - [DllImport(Library.Name)] - public static extern IntPtr new_TextElement(); - - [DllImport(Library.Name)] - public static extern IntPtr new_Inline(); - [DllImport(Library.Name)] public static extern int Inline_GetFlowDirection(HandleRef jarg1); diff --git a/Src/Noesis/Core/Src/Proxies/Point.cs b/Src/Noesis/Core/Src/Proxies/Point.cs index 91cb68e..55ee67b 100644 --- a/Src/Noesis/Core/Src/Proxies/Point.cs +++ b/Src/Noesis/Core/Src/Proxies/Point.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(PointConverter))] public struct Point { [MarshalAs(UnmanagedType.R4)] diff --git a/Src/Noesis/Core/Src/Proxies/Rect.cs b/Src/Noesis/Core/Src/Proxies/Rect.cs index 0480f82..56bf393 100644 --- a/Src/Noesis/Core/Src/Proxies/Rect.cs +++ b/Src/Noesis/Core/Src/Proxies/Rect.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(RectConverter))] public struct Rect { [MarshalAs(UnmanagedType.R4)] diff --git a/Src/Noesis/Core/Src/Proxies/Size.cs b/Src/Noesis/Core/Src/Proxies/Size.cs index 2c07c70..a7af167 100644 --- a/Src/Noesis/Core/Src/Proxies/Size.cs +++ b/Src/Noesis/Core/Src/Proxies/Size.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(SizeConverter))] public struct Size { [MarshalAs(UnmanagedType.R4)] diff --git a/Src/Noesis/Core/Src/Proxies/TextElement.cs b/Src/Noesis/Core/Src/Proxies/TextElement.cs index 11594cf..974feca 100644 --- a/Src/Noesis/Core/Src/Proxies/TextElement.cs +++ b/Src/Noesis/Core/Src/Proxies/TextElement.cs @@ -15,11 +15,7 @@ namespace Noesis { -public class TextElement : FrameworkElement { - internal new static TextElement CreateProxy(IntPtr cPtr, bool cMemoryOwn) { - return new TextElement(cPtr, cMemoryOwn); - } - +public abstract class TextElement : FrameworkElement { internal TextElement(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { } @@ -27,6 +23,9 @@ internal static HandleRef getCPtr(TextElement obj) { return (obj == null) ? new HandleRef(null, IntPtr.Zero) : obj.swigCPtr; } + protected TextElement() { + } + public Typography Typography { get { return new Typography(this); } } @@ -321,14 +320,6 @@ public float StrokeThickness { } } - public TextElement() { - } - - protected override IntPtr CreateCPtr(Type type, out bool registerExtend) { - registerExtend = false; - return NoesisGUI_PINVOKE.new_TextElement(); - } - } } diff --git a/Src/Noesis/Core/Src/Proxies/Thickness.cs b/Src/Noesis/Core/Src/Proxies/Thickness.cs index 9008648..14bb80b 100644 --- a/Src/Noesis/Core/Src/Proxies/Thickness.cs +++ b/Src/Noesis/Core/Src/Proxies/Thickness.cs @@ -16,6 +16,7 @@ namespace Noesis { [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] +[System.ComponentModel.TypeConverter(typeof(ThicknessConverter))] public struct Thickness { [MarshalAs(UnmanagedType.R4)] diff --git a/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj b/Src/Noesis/Extensions/Noesis.GUI.Extensions.csproj index 51205ce..688067d 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.26 - 3.0.26.13320 - 3.0.26.13320 + 3.0.30 + 3.0.30.13778 + 3.0.30.13778 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/LocExtension.cs b/Src/Noesis/Extensions/Src/LocExtension.cs index 1aad0a6..451cc3d 100644 --- a/Src/Noesis/Extensions/Src/LocExtension.cs +++ b/Src/Noesis/Extensions/Src/LocExtension.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; using System.Windows; +using System.Windows.Data; using System.Windows.Markup; namespace NoesisGUIExtensions @@ -15,6 +18,8 @@ namespace NoesisGUIExtensions /// If used with a string or object property, and the provided resource key is not found, the /// LocExtension will return a string in the format "" where %s is replaced with the key. /// + /// A Converter can also be specified, with an optional ConverterParameter. + /// /// This example shows the full setup for a LocExtension. It utilizes the RichText attached /// property to support BBCode markup in the localized strings. The Loc.Source property references /// the "Language_en-gb.xaml" ResourceDictionary below. @@ -26,8 +31,8 @@ namespace NoesisGUIExtensions /// xmlns:noesis="clr-namespace:NoesisGUIExtensions" /// noesis:Loc.Source="Language_en-gb.xaml"> /// - /// /// + /// /// /// /// This is the contents of a "Language_en-gb.xaml" localized ResourceDictionary: @@ -39,7 +44,7 @@ namespace NoesisGUIExtensions /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" /// xmlns:sys="clr-namespace:System;assembly=mscorlib"> /// - /// [b]LOCALIZATION SAMPLE[/b] + /// [b]Localization Sample[/b] /// A [i]sound[/i] label /// /// @@ -48,6 +53,8 @@ namespace NoesisGUIExtensions public class LocExtension : MarkupExtension { private string _resourceKey; + private IValueConverter _converter; + private object _converterParameter; public LocExtension() { @@ -78,16 +85,43 @@ public string ResourceKey set { _resourceKey = value; } } + /// + /// Gets or sets a converter to use when finding a resource in the active localization ResourceDictionary. + /// + public IValueConverter Converter + { + get => this._converter; + set => this._converter = value; + } + + /// + /// Gets or sets an optional converter parameter to use when finding a resource in the active localization ResourceDictionary. + /// + public object ConverterParameter + { + get => this._converterParameter; + set => this._converterParameter = value; + } + public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget valueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (valueTarget == null) { - return null; + return this; + } + + DependencyObject target = valueTarget.TargetObject as DependencyObject; + if (target == null) + { + return this; } - DependencyObject target = (DependencyObject)valueTarget.TargetObject; - DependencyProperty targetProperty = (DependencyProperty)valueTarget.TargetProperty; + DependencyProperty targetProperty = valueTarget.TargetProperty as DependencyProperty; + if (targetProperty == null) + { + return null; + } LocMonitor monitor = (LocMonitor)target.GetValue(MonitorProperty); @@ -97,7 +131,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) target.SetValue(MonitorProperty, monitor); } - return monitor.AddDependencyProperty(targetProperty, _resourceKey); + return monitor.AddDependencyProperty(targetProperty, _resourceKey, Converter, ConverterParameter); } #region Source attached property @@ -192,42 +226,67 @@ public static void SetResources(DependencyObject dependencyObject, ResourceDicti internal class LocMonitor { - private readonly List> _monitoredDependencyProperties = - new List>(); + internal class LocProperty + { + public DependencyProperty TargetProperty { get; set; } + public string ResourceKey { get; set; } + public IValueConverter Converter { get; set; } + public object ConverterParameter { get; set; } + + public LocProperty(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, object converterParameter) + { + TargetProperty = targetProperty; + ResourceKey = resourceKey; + Converter = converter; + ConverterParameter = converterParameter; + } + } + + private readonly List _monitoredDependencyProperties = + new List(); public DependencyObject TargetObject { get; } public LocMonitor(DependencyObject targetObject) { TargetObject = targetObject; + + if (!(TargetObject is FrameworkElement)) + { + Binding binding = new Binding(); + binding.Path = new PropertyPath("(0)", new object[] { LocExtension.ResourcesProperty }); + binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(FrameworkElement), 1); + + BindingOperations.SetBinding(TargetObject, LocExtension.ResourcesProperty, binding); + } } - public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey) + public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, object converterParameter) { ResourceDictionary resourceDictionary = LocExtension.GetResources(TargetObject); for (int i = 0; i < _monitoredDependencyProperties.Count; i++) { - if (_monitoredDependencyProperties[i].Item1 == targetProperty) + if (_monitoredDependencyProperties[i].TargetProperty == targetProperty) { _monitoredDependencyProperties[i] = - new Tuple(_monitoredDependencyProperties[i].Item1, resourceKey); + new LocProperty(_monitoredDependencyProperties[i].TargetProperty, resourceKey, converter, converterParameter); - return Evaluate(targetProperty, resourceKey, resourceDictionary); + return Evaluate(targetProperty, resourceKey, converter, converterParameter, resourceDictionary); } } - _monitoredDependencyProperties.Add(new Tuple(targetProperty, resourceKey)); + _monitoredDependencyProperties.Add(new LocProperty(targetProperty, resourceKey, converter, converterParameter)); - return Evaluate(targetProperty, resourceKey, resourceDictionary); + return Evaluate(targetProperty, resourceKey, converter, converterParameter, resourceDictionary); } public void InvalidateResources(ResourceDictionary resourceDictionary) { - foreach (Tuple entry in _monitoredDependencyProperties) + foreach (LocProperty entry in _monitoredDependencyProperties) { - TargetObject.SetValue(entry.Item1, - Evaluate(entry.Item1, entry.Item2, resourceDictionary)); + TargetObject.SetValue(entry.TargetProperty, + Evaluate(entry.TargetProperty, entry.ResourceKey, entry.Converter, entry.ConverterParameter, resourceDictionary)); } } @@ -238,14 +297,45 @@ public LocMonitor Clone(DependencyObject targetObject) return clone; } - private static object Evaluate(DependencyProperty targetProperty, string resourceKey, - ResourceDictionary resourceDictionary) + private static object DynamicConvert(Type targetType, object value) + { + Type valueType = value.GetType(); + if (!targetType.IsAssignableFrom(valueType)) + { + TypeConverter converter = TypeDescriptor.GetConverter(targetType); + if (converter.CanConvertFrom(valueType)) + { + value = converter.ConvertFrom(value); + } + else + { + converter = TypeDescriptor.GetConverter(valueType); + if (converter.CanConvertTo(targetType)) + { + value = converter.ConvertTo(value, targetType); + } + } + } + + return value; + } + + private static object Evaluate(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, + object converterParameter, ResourceDictionary resourceDictionary) { if (resourceDictionary != null && resourceDictionary.Contains(resourceKey)) { object resource = resourceDictionary[resourceKey]; if (resource != null) { + if (converter != null) + { + resource = converter.Convert(resource, targetProperty.PropertyType, converterParameter, + CultureInfo.InvariantCulture); + } + + resource = DynamicConvert(targetProperty.PropertyType, resource); + return resource; } @@ -257,7 +347,7 @@ private static object Evaluate(DependencyProperty targetProperty, string resourc return $""; } - return null; + return Activator.CreateInstance(targetProperty.PropertyType); } } } \ No newline at end of file diff --git a/Src/Noesis/Extensions/Src/Rive.cs b/Src/Noesis/Extensions/Src/Rive.cs index a758f73..45400dd 100644 --- a/Src/Noesis/Extensions/Src/Rive.cs +++ b/Src/Noesis/Extensions/Src/Rive.cs @@ -63,6 +63,10 @@ public Stretch Stretch public RiveInputCollection Inputs { get; } = new RiveInputCollection(); #endregion + #region Runs property + public RiveRunCollection Runs { get; } = new RiveRunCollection(); + #endregion + protected override void OnRender(DrawingContext context) { SolidColorBrush brush = Brushes.CornflowerBlue.Clone(); @@ -122,4 +126,38 @@ protected override void Invoke(object parameter) { } } + + public class RiveRun : Animatable + { + #region RunName property + public string RunName + { + get { return (string)GetValue(RunNameProperty); } + set { SetValue(RunNameProperty, value); } + } + + public static readonly DependencyProperty RunNameProperty = DependencyProperty.Register( + "RunName", typeof(string), typeof(RiveRun), new PropertyMetadata(string.Empty)); + #endregion + + #region RunText property + public object RunText + { + get { return (object)GetValue(RunTextProperty); } + set { SetValue(RunTextProperty, value); } + } + + public static readonly DependencyProperty RunTextProperty = DependencyProperty.Register( + "RunText", typeof(string), typeof(RiveRun), new PropertyMetadata(string.Empty)); + #endregion + + protected override Freezable CreateInstanceCore() + { + return new RiveRun(); + } + } + + public class RiveRunCollection : FreezableCollection + { + } } diff --git a/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml b/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml index 32d8769..02d4299 100644 --- a/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml +++ b/Src/Noesis/Extensions/Theme/NoesisTheme.Styles.xaml @@ -1775,6 +1775,7 @@ + @@ -1903,6 +1904,7 @@ + diff --git a/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs b/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs index 0d5d53a..27d09b6 100644 --- a/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs +++ b/Src/NoesisApp/Core/Src/Interactivity/InvokeCommandAction.cs @@ -21,6 +21,15 @@ public class InvokeCommandAction : TriggerAction return (InvokeCommandAction)base.CloneCurrentValue(); } + protected override void CloneCommonCore(Freezable source) + { + base.CloneCommonCore(source); + + InvokeCommandAction action = (InvokeCommandAction)source; + CommandName = action.CommandName; + PassEventArgsToCommand = action.PassEventArgsToCommand; + } + /// /// Gets or sets the name of the command this action should invoke /// diff --git a/Src/NoesisApp/Core/Src/Localization/LocExtension.cs b/Src/NoesisApp/Core/Src/Localization/LocExtension.cs index 1ea873d..1877ed8 100644 --- a/Src/NoesisApp/Core/Src/Localization/LocExtension.cs +++ b/Src/NoesisApp/Core/Src/Localization/LocExtension.cs @@ -1,6 +1,8 @@ using Noesis; using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; namespace NoesisGUIExtensions { @@ -14,6 +16,8 @@ namespace NoesisGUIExtensions /// If used with a string or object property, and the provided resource key is not found, the /// LocExtension will return a string in the format "" where %s is replaced with the key. /// + /// A Converter can also be specified, with an optional ConverterParameter. + /// /// This example shows the full setup for a LocExtension. It utilizes the RichText attached /// property to support BBCode markup in the localized strings. The Loc.Source property references /// the "Language_en-gb.xaml" ResourceDictionary below. @@ -25,8 +29,8 @@ namespace NoesisGUIExtensions /// xmlns:noesis="clr-namespace:NoesisGUIExtensions" /// noesis:Loc.Source="Language_en-gb.xaml"> /// - /// /// + /// /// /// /// This is the contents of a "Language_en-gb.xaml" localized ResourceDictionary: @@ -38,7 +42,7 @@ namespace NoesisGUIExtensions /// xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" /// xmlns:sys="clr-namespace:System;assembly=mscorlib"> /// - /// [b]LOCALIZATION SAMPLE[/b] + /// [b]Localization Sample[/b] /// A [i]sound[/i] label /// /// @@ -47,6 +51,8 @@ namespace NoesisGUIExtensions public class LocExtension : MarkupExtension { private string _resourceKey; + private IValueConverter _converter; + private object _converterParameter; public LocExtension() { @@ -77,6 +83,24 @@ public string ResourceKey set { _resourceKey = value; } } + /// + /// Gets or sets a converter to use when finding a resource in the active localization ResourceDictionary. + /// + public IValueConverter Converter + { + get => this._converter; + set => this._converter = value; + } + + /// + /// Gets or sets an optional converter parameter to use when finding a resource in the active localization ResourceDictionary. + /// + public object ConverterParameter + { + get => this._converterParameter; + set => this._converterParameter = value; + } + public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget valueTarget = serviceProvider as IProvideValueTarget; @@ -96,7 +120,7 @@ public override object ProvideValue(IServiceProvider serviceProvider) target.SetValue(MonitorProperty, monitor); } - return monitor.AddDependencyProperty(targetProperty, _resourceKey); + return monitor.AddDependencyProperty(targetProperty, _resourceKey, Converter, ConverterParameter); } #region Source attached property @@ -182,76 +206,129 @@ public static ResourceDictionary GetResources(DependencyObject dependencyObject) ); #endregion - } - internal class LocMonitor - { - private readonly List> _monitoredDependencyProperties = - new List>(); + internal class LocMonitor + { + internal class LocProperty + { + public DependencyProperty TargetProperty { get; set; } + public string ResourceKey { get; set; } + public IValueConverter Converter { get; set; } + public object ConverterParameter { get; set; } - public DependencyObject TargetObject { get; } + public LocProperty(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, object converterParameter) + { + TargetProperty = targetProperty; + ResourceKey = resourceKey; + Converter = converter; + ConverterParameter = converterParameter; + } + } - public LocMonitor(DependencyObject targetObject) - { - TargetObject = targetObject; - } + private readonly List _monitoredDependencyProperties = + new List(); - public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey) - { - ResourceDictionary resourceDictionary = LocExtension.GetResources(TargetObject); + public DependencyObject TargetObject { get; } - for (int i = 0; i < _monitoredDependencyProperties.Count; i++) + public LocMonitor(DependencyObject targetObject) { - if (_monitoredDependencyProperties[i].Item1 == targetProperty) - { - _monitoredDependencyProperties[i] = - new Tuple(_monitoredDependencyProperties[i].Item1, resourceKey); + TargetObject = targetObject; - return Evaluate(targetProperty, resourceKey, resourceDictionary); + if (!(TargetObject is FrameworkElement)) + { + Binding binding = new Binding(ResourcesProperty, new RelativeSource(RelativeSourceMode.FindAncestor, typeof(FrameworkElement), 1)); + BindingOperations.SetBinding(TargetObject, ResourcesProperty, binding); } } - _monitoredDependencyProperties.Add(new Tuple(targetProperty, resourceKey)); + public object AddDependencyProperty(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, object converterParameter) + { + ResourceDictionary resourceDictionary = LocExtension.GetResources(TargetObject); - return Evaluate(targetProperty, resourceKey, resourceDictionary); - } + for (int i = 0; i < _monitoredDependencyProperties.Count; i++) + { + if (_monitoredDependencyProperties[i].TargetProperty == targetProperty) + { + _monitoredDependencyProperties[i] = + new LocProperty(_monitoredDependencyProperties[i].TargetProperty, resourceKey, converter, converterParameter); - public void InvalidateResources(ResourceDictionary resourceDictionary) - { - foreach (Tuple entry in _monitoredDependencyProperties) + return Evaluate(targetProperty, resourceKey, converter, converterParameter, resourceDictionary); + } + } + + _monitoredDependencyProperties.Add(new LocProperty(targetProperty, resourceKey, converter, converterParameter)); + + return Evaluate(targetProperty, resourceKey, converter, converterParameter, resourceDictionary); + } + + public void InvalidateResources(ResourceDictionary resourceDictionary) { - TargetObject.SetValue(entry.Item1, - Evaluate(entry.Item1, entry.Item2, resourceDictionary)); + foreach (LocProperty entry in _monitoredDependencyProperties) + { + TargetObject.SetValue(entry.TargetProperty, + Evaluate(entry.TargetProperty, entry.ResourceKey, entry.Converter, entry.ConverterParameter, resourceDictionary)); + } } - } - public LocMonitor Clone(DependencyObject targetObject) - { - LocMonitor clone = new LocMonitor(targetObject); - clone._monitoredDependencyProperties.AddRange(_monitoredDependencyProperties); - return clone; - } + 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) - { - if (resourceDictionary != null && resourceDictionary.Contains(resourceKey)) + private static object DynamicConvert(Type targetType, object value) { - object resource = resourceDictionary[resourceKey]; - if (resource != null) + Type valueType = value.GetType(); + if (!targetType.IsAssignableFrom(valueType)) { - return resource; + TypeConverter converter = TypeDescriptor.GetConverter(targetType); + if (converter.CanConvertFrom(valueType)) + { + value = converter.ConvertFrom(value); + } + else + { + converter = TypeDescriptor.GetConverter(valueType); + if (converter.CanConvertTo(targetType)) + { + value = converter.ConvertTo(value, targetType); + } + } } - Console.WriteLine($"[NOESIS/E] Resource key '{resourceKey}' not found in Loc Resources"); + return value; } - if (targetProperty.PropertyType == typeof(string)) + private static object Evaluate(DependencyProperty targetProperty, string resourceKey, IValueConverter converter, + object converterParameter, ResourceDictionary resourceDictionary) { - return $""; - } + if (resourceDictionary != null && resourceDictionary.Contains(resourceKey)) + { + object resource = resourceDictionary[resourceKey]; + if (resource != null) + { + if (converter != null) + { + resource = converter.Convert(resource, targetProperty.PropertyType, converterParameter, + CultureInfo.InvariantCulture); + } + + resource = DynamicConvert(targetProperty.PropertyType, resource); + + return resource; + } + + Console.WriteLine($"[NOESIS/E] Resource key '{resourceKey}' not found in Loc Resources"); + } + + if (targetProperty.PropertyType == typeof(string)) + { + return $""; + } - return null; + return null; + } } } -} \ No newline at end of file +} diff --git a/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml b/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml index 32d8769..02d4299 100644 --- a/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml +++ b/Src/NoesisApp/Theme/Theme/NoesisTheme.Styles.xaml @@ -1775,6 +1775,7 @@ + @@ -1903,6 +1904,7 @@ +