]> O.S.I.I.S - jp/crow.git/commitdiff
wip
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Sun, 17 Jul 2022 18:26:28 +0000 (20:26 +0200)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Sun, 17 Jul 2022 18:26:28 +0000 (20:26 +0200)
46 files changed:
Backends/CairoBackend/src/Context.cs
Backends/CairoBackend/src/EglBackend.cs
Backends/CairoBackend/src/ImageBackend.cs
Backends/CairoBackend/src/ImageSurface.cs
Backends/CairoBackend/src/Surface.cs
Backends/VkvgBackend/Crow.VkvgBackend.csproj
Backends/VkvgBackend/src/DefaultBackend.cs
Crow.sln
Crow/Default.style
Crow/Icons/expandable.svg
Crow/Icons/radiobutton.svg
Crow/Templates/ColorPicker2.template
Crow/src/Fill/BmpPicture.cs
Crow/src/Fill/SvgPicture.cs
Crow/src/Interface.cs
Crow/src/LayoutingQueueItem.cs
Crow/src/Widgets/HueSelector.cs
Crow/src/Widgets/Image.cs
Crow/src/Widgets/Widget.cs
Directory.Build.props
Drawing2D/src/ISurface.cs
Samples/AnimTest/AnimTest.csproj [new file with mode: 0644]
Samples/AnimTest/Animation/AngleAnimation.cs [new file with mode: 0644]
Samples/AnimTest/Animation/Animation.cs [new file with mode: 0644]
Samples/AnimTest/Animation/AnimationTemplate.cs [new file with mode: 0644]
Samples/AnimTest/Animation/BezierPath.cs [new file with mode: 0644]
Samples/AnimTest/Animation/FloatAnimation.cs [new file with mode: 0644]
Samples/AnimTest/Animation/FloatAnimation2.cs [new file with mode: 0644]
Samples/AnimTest/Animation/MathHelper.cs [new file with mode: 0644]
Samples/AnimTest/Animation/Path.cs [new file with mode: 0644]
Samples/AnimTest/Animation/PathAnimation.cs [new file with mode: 0644]
Samples/AnimTest/Animation/ShakeAnimation.cs [new file with mode: 0644]
Samples/AnimTest/MiscUtil/ExpressionUtil.cs [new file with mode: 0644]
Samples/AnimTest/MiscUtil/NullOp.cs [new file with mode: 0644]
Samples/AnimTest/MiscUtil/Operator.cs [new file with mode: 0644]
Samples/AnimTest/main.cs [new file with mode: 0644]
Samples/AnimTest/ui/anim.crow [new file with mode: 0644]
Samples/AnimTest/ui/background.crow [new file with mode: 0644]
Samples/AnimTest/ui/background.jpg [new file with mode: 0644]
Samples/BasicTests/BasicTests.cs
Samples/HelloWorld/main.cs
Samples/HelloWorld/ui/helloworld.crow
Samples/ShowCase/ShowCase.cs
Samples/ShowCase/ui/showcase.crow
Samples/common/ui/Interfaces/Experimental/svg.crow [new file with mode: 0644]
Samples/common/ui/Interfaces/TemplatedContainer/buttons.crow [new file with mode: 0644]

index c15ecaa318a485d1fb900dc1b2a42938f10d6eac..f9af1bf169029f5840c32eac1028b907da7549dc 100644 (file)
@@ -661,7 +661,8 @@ namespace Crow.CairoBackend
                public void RestoreTransformations()
                        => NativeMethods.cairo_set_matrix (handle, ref savedMat);
                public void SetSource(IPattern pat) => NativeMethods.cairo_set_source (handle, (pat as Pattern).handle);
-               public void SetSource(ISurface surf, double x = 0, double y = 0)
-                       => NativeMethods.cairo_set_source_surface (handle, (surf as Surface).handle, x, y);
+               public async void SetSource(ISurface surf, double x = 0, double y = 0) {
+                       NativeMethods.cairo_set_source_surface (handle, surf.Handle, x, y);
+               }
        }
 }
index 909c3cd9fc679e53dd8439489d7d43c2a9d6ca4d..073793e75ed599ebbb405f93da53bf8a33284b0a 100644 (file)
@@ -7,11 +7,13 @@ namespace Crow.CairoBackend
 {
        public class EglBackend : CairoBackendBase {
                EGLDevice device;
+               GLSurface winSurf;
                /// <summary>
                /// Create a new generic backend bound to the application surface
                /// </summary>
                /// <param name="width">backend surface width</param>
                /// <param name="height">backend surface height</param>
+               /// <param name="nativeWindoPointer"></param>
                public EglBackend (int width, int height, IntPtr nativeWindoPointer)
                : base (width, height, nativeWindoPointer) {
                        if (nativeWindoPointer == IntPtr.Zero) {
@@ -32,7 +34,9 @@ namespace Crow.CairoBackend
                        Glfw3.SwapInterval (0);
 
                        device = new EGLDevice (Glfw3.GetEGLDisplay (), Glfw3.GetEGLContext (hWin));
-                       surf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height);
+                       //surf = new GLTextureSurface (device, width, height);
+                       surf = new ImageSurface (Format.ARGB32, width, height);
+                       winSurf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height);
                }
                /// <summary>
                /// Create a new offscreen backend, used in perfTests
@@ -61,9 +65,20 @@ namespace Crow.CairoBackend
                }
                public override void FlushUIFrame(IContext ctx)
                {
-                       base.FlushUIFrame (ctx);
 
-                       (surf as GLSurface).SwapBuffers ();
+                       base.FlushUIFrame (ctx);
+                       surf.Flush();
+                       using (Context gr = new Context (winSurf)) {
+                               gr.SetSource (surf);
+                               gr.Paint();
+                       }
+                       winSurf.SwapBuffers ();
+               }
+               public override void ResizeMainSurface(int width, int height)
+               {
+                       base.ResizeMainSurface(width, height);
+                       winSurf?.Dispose();
+                       winSurf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height);
                }
        }
 }
index 96899dbde87cd2db6d35b14158702a79b307b9ad..a90d275afa59583e9c23b7f945150b8f31796ff6 100644 (file)
@@ -79,6 +79,15 @@ namespace Crow.CairoBackend
 
                        base.FlushUIFrame (ctx);
                }
+               public override void ResizeMainSurface(int width, int height)
+               {
+                       if (surf is ImageSurface) {
+                               surf?.Dispose();
+                               surf = new ImageSurface (Format.ARGB32, width, height);
+                       } else {
+                               base.ResizeMainSurface(width, height);
+                       }
+               }
        }
 }
 
index 2cfec61c2692ce177109941808105deb4f0402c1..8870366adccf75e1c5a3eb057b84158dd625e5db 100644 (file)
@@ -96,5 +96,9 @@ namespace Crow.CairoBackend {
                public int Stride {
                        get { return NativeMethods.cairo_image_surface_get_stride (handle); }
                }
+               public override void Resize(int width, int height)
+               {
+                       
+               }
        }
 }
index 80435b5e90da4297d9b514b6183f022d9fdeee8c..1f44d3ab65ac5408ce3ee012b891229d2fbf1c0a 100644 (file)
@@ -41,6 +41,8 @@ namespace Crow.CairoBackend {
        public class Surface : ISurface
        {
                internal IntPtr handle = IntPtr.Zero;
+               public IntPtr Handle => handle;
+
 
                protected Surface()
                {
index 13386d36228151c2290c07b8b8ceccff6a2b346c..b6fbb00400f5b644d2ce75e843dc4c3ef6e1bbf9 100644 (file)
@@ -15,7 +15,7 @@
 
   <ItemGroup>
     <PackageReference Include="vke" Version="0.2.0-beta" />
-    <PackageReference Include="vkvg.net" Version="0.5.0-beta" />
+    <PackageReference Include="vkvg.net" Version="0.6.0-beta" />
     <ProjectReference Include="..\..\Drawing2D\Drawing2D.csproj" />
   </ItemGroup>
 
index 8521d503c5512ee2d29ea651332d72827e0789e6..eeed4d0a34dbad2891be0c31d5d21fd781a1531e 100644 (file)
@@ -32,7 +32,7 @@ namespace Crow.VkvgBackend
                protected Fence drawFence;
                Surface surf;
                Device vkvgDev;
-               SampleCount samples = SampleCount.Sample_1;
+               SampleCount samples = SampleCount.Sample_8;
                bool vsync = false;
 
                bool tryGetPhy (vke.PhysicalDeviceCollection physicalDevices, VkPhysicalDeviceType phyType, out PhysicalDevice phy, bool swapchainSupport) {
@@ -189,6 +189,7 @@ namespace Crow.VkvgBackend
                        new SvgHandle (vkvgDev, System.Text.Encoding.Unicode.GetBytes (svgFragment));
                bool disposeContextOnFlush;
                IRegion clipping;
+               volatile bool resizeMainSurface = false;
                protected void clear(IContext ctx) {
                        for (int i = 0; i < clipping.NumRectangles; i++)
                                ctx.Rectangle (clipping.GetRectangle (i));
@@ -200,6 +201,11 @@ namespace Crow.VkvgBackend
                }
                public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping)
                {
+                       if (resizeMainSurface) {
+                               createMainSurface (swapChain.Width, swapChain.Height);
+                               clipping.UnionRectangle (new Rectangle(new Size((int)swapChain.Width, (int)swapChain.Height)));
+                               resizeMainSurface = false;
+                       }
                        this.clipping = clipping;
                        IContext ctx = existingContext;
                        if (ctx == null) {
@@ -216,23 +222,24 @@ namespace Crow.VkvgBackend
                {
                        if (disposeContextOnFlush)
                                ctx.Dispose ();
-                       clipping = null;
-
-                       dev.WaitIdle();
 
                        int idx = swapChain.GetNextImage ();
                        if (idx < 0) {
-                               createMainSurface (swapChain.Width, swapChain.Height);
+                               resizeMainSurface = true;
                                return;
                        }
 
+                       clipping = null;
+
+                       //dev.WaitIdle();
+
                        drawFence.Wait ();
                        drawFence.Reset ();
 
                        graphicQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence);
                        (graphicQueue as PresentQueue).Present (swapChain, drawComplete[idx]);
 
-                       dev.WaitIdle();
+                       //dev.WaitIdle();
                }
                public override void ResizeMainSurface (int width, int height) {
                        //resize is done on swapchain image aquisition failure
index 3f256efd2bfa86c32e4eb57ac570885d8b82ec6a..df9d9f8c122bb3d242472cac7963b70f868f23a2 100644 (file)
--- a/Crow.sln
+++ b/Crow.sln
@@ -31,6 +31,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.VkvgBackend", "Backend
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.SkiaBackend", "Backends\SkiaBackend\Crow.SkiaBackend.csproj", "{ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicTests", "Samples\BasicTests\BasicTests.csproj", "{1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimTest", "Samples\AnimTest\AnimTest.csproj", "{17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -65,6 +69,14 @@ Global
                {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Release|Any CPU.Build.0 = Release|Any CPU
+               {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -76,6 +88,8 @@ Global
                {E06441A9-0CFD-45BB-9478-99D28CEB327F} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
                {34976828-80CF-4AC5-8C81-F66F635DC5FC} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
                {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175} = {451F5727-2A2E-4361-A41B-089429ADE8F9}
+               {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
+               {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {00D4E149-7131-49F4-BAAD-559AA961A78E}
index 066eb21bac610cf1c78056b6abea2086a5a814e2..6e14f802e223f7c4b4cd0c1af27b0a56edb6d5c3 100644 (file)
@@ -346,6 +346,6 @@ MessageBox, Popper, Slider, Spinner, TextInheritBox, NumericControl {
        Margin="0";
        BubbleEvents="MouseWheel|Keyboard";
 }
-TemplatedControl, GenericStack {
+TemplatedControl, GenericStack, Image {
        CacheEnabled="true";
 }
index e2bffbc540064bda5845186041e5c56193cb74bd..a689ee98572b7b9eaf7cdafcedaba20086418779 100644 (file)
@@ -1,4 +1,4 @@
-<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
+<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
  <g id="False">
   <rect height="9" width="9" y="0.5" x="0.5" stroke="#000" fill="#fff" stroke-width="1"/>-->
   <line stroke-linecap="undefined" stroke-linejoin="undefined" x1="5" y1="2" x2="5" y2="8" stroke="#000" fill="none"/>
index b102b3e18cc807b8eac36d0b6f11061d3856bd00..49a0c3289abb94f43f1b9643c926c50bc519eb9f 100644 (file)
@@ -6,37 +6,19 @@
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    id="svg2"
-   width="64"
-   height="64"
    viewBox="0 0 64 64"
    version="1.1">
-  <metadata
-     id="metadata12">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <defs
-     id="defs10" />
   <g
      id="True">
     <circle
        r="30"
        cy="32"
        cx="32"
-       id="path4166"
        style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" />
     <circle
        r="22"
        cy="32"
        cx="32"
-       id="path4166-1"
        style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.99999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" />
   </g>
   <circle
index b43555177cbda2e63cc7fa0b6b1e50c4c7be8135..34fe51fd2211742c3bec317da31bef86d609db8d 100644 (file)
@@ -1,44 +1,2 @@
 <?xml version="1.0"?>
-<Border Background="{./Background}"    Foreground="{./Foreground}"
-               CornerRadius="{./CornerRadius}" BorderWidth="1">
-               <HorizontalStack Spacing="0">
-                       <VerticalStack Margin="5">
-                               <SaturationValueSelector S="{²./S}" V="{²./V}" Focusable="true" Name="colorSelector" Margin="0"
-                                       Foreground="{../hueSelector.HueColor}"
-                                       Width="128" Height="128"/>
-                               <HueSelector Hue="{²./H}" Focusable="true" Name="hueSelector" Margin="0" Width="128" Height="20"/>
-                       </VerticalStack>
-                       <VerticalStack Margin="5" Spacing="1">
-                               <Widget Width="34" Height="21" Background="{./SelectedColor}"/>
-                               <Label Focusable="true" Selectable="true" Text="{./SelectedColor}" />                           
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="R" Width="Fit"/>
-                                       <Spinner Style="ColorSpinner" Value="{²./R}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="G" Width="Fit"/>
-                                       <Spinner Style="ColorSpinner" Value="{²./G}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="B" Width="Fit"/>
-                                       <Spinner Style="ColorSpinner" Value="{²./B}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="A" Width="Fit"/>
-                                       <Spinner Style="ColorSpinner" Value="{²./A}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="H" Width="Fit"/>
-                                       <Spinner Style="HSVSpinner" Value="{²./H}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="S" Width="Fit"/>
-                                       <Spinner Style="HSVSpinner" Value="{²./S}" Width="46"  />
-                               </HorizontalStack>
-                               <HorizontalStack Height="Fit">
-                                       <Label Text="V" Width="Fit"/>
-                                       <Spinner Style="HSVSpinner" Value="{²./V}" Width="46"  />
-                               </HorizontalStack>
-                       </VerticalStack>
-               </HorizontalStack>
-</Border>
+<HueSelector Hue="{²./H}" Focusable="true" Name="hueSelector" Margin="0" Width="300" Height="50"/>
index 813e8dec23c540c2502c4a2f4eaa38fc2a81b841..7cf8983cad141dffbaf7c708f3def831c449e5b5 100644 (file)
@@ -95,7 +95,7 @@ namespace Crow
                                        gr.Scale (widthRatio, heightRatio);
                                        gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2);
 
-                                       using (ISurface imgSurf = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) {
+                                       using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) {
                                                gr.SetSource (imgSurf, 0,0);
                                                gr.Paint ();
                                        }
index e6e54aff23049619efacb0692b55bc8dddc892e4..e2c73bbd73e6b96bc5270f544ea6cec2865b3aef 100644 (file)
@@ -69,7 +69,6 @@ namespace Crow
                                else
                                        widthRatio = heightRatio;
                        }
-
                        using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) {
                                using (IContext gr = iFace.Backend.CreateContext (tmp)) {
                                        gr.Translate (bounds.Left, bounds.Top);
index 1505ed120461a424c49a33aa3ad5f18955e4d735..507bed690ddfaede5cf093fd6a6acd391ef83c20 100644 (file)
@@ -160,39 +160,42 @@ namespace Crow
                /// backends are search where the main crow assembly is.
                /// </summary>
                public static string BackendsDirectory = null;
+               protected static Type getBackendType (IEnumerable<Type> backendTypes) {
+                       if (backendTypes == null)
+                               return null;
+                       switch (PreferedBackendType) {
+                               case BackendType.Default:
+                                       return backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend");
+                               case BackendType.Egl:
+                                       return backendTypes.FirstOrDefault(be => be.Name == "EglBackend");
+                               case BackendType.Vulkan:
+                                       return backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend");
+                               case BackendType.Gl:
+                                       return backendTypes.FirstOrDefault(be => be.Name == "GlBackend");
+                               default:
+                                       return backendTypes.FirstOrDefault();
+                       }
+               }
                protected static bool tryFindBackendType (out Type backendType) {
                        backendType = default;
-                       //IEnumerable<AssemblyName> backendAssemblies = Assembly.GetEntryAssembly ().GetReferencedAssemblies().Where (ra=>backends.Contains (ra.Name));
+                       //search loaded assemblies
                        System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly());
                        foreach (Assembly a in ldCtx.Assemblies.Where (asb => backends.Contains (asb.GetName ().Name))) {
-                               IEnumerable<Type> backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract);
-                               if (backendTypes != null) {
-                                       backendType = backendTypes.FirstOrDefault();
+                               backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract));
+                               if (backendType != null)
                                        return true;
-                               }
                        }
+
                        string bp =
                                (!string.IsNullOrEmpty(BackendsDirectory) && Directory.Exists(BackendsDirectory)) ?
                                        BackendsDirectory :     Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location);
+                       //search backend directory
                        foreach (string b in backends) {
                                string bPath = Path.Combine (bp,$"Crow.{b}.dll");
                                if (File.Exists (bPath)) {
                                        Assembly a = ldCtx.LoadFromAssemblyPath (bPath);
-                                       IEnumerable<Type> backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract);
-                                       if (backendTypes != null) {
-                                               if (PreferedBackendType == BackendType.Default)
-                                                       backendType = backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend");
-                                               else if (PreferedBackendType == BackendType.Egl)
-                                                       backendType = backendTypes.FirstOrDefault(be => be.Name == "EglBackend");
-                                               else if (PreferedBackendType == BackendType.Vulkan)
-                                                       backendType = backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend");
-                                               else if (PreferedBackendType == BackendType.Gl)
-                                                       backendType = backendTypes.FirstOrDefault(be => be.Name == "GlBackend");
-
-                                               if (backendType == null)
-                                                       backendType = backendTypes.FirstOrDefault();
-                                               return backendType != null;
-                                       }
+                                       backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract));
+                                       return backendType != null;
                                }
                        }
 
@@ -433,9 +436,7 @@ namespace Crow
                        Glfw3.SetCursorPosCallback (hWin, HandleCursorPosDelegate);
                        Glfw3.SetScrollCallback (hWin, HandleScrollDelegate);
                        Glfw3.SetCharCallback (hWin, HandleCharDelegate);
-#if !VKVG//resize is processed on context.Render failing
                        Glfw3.SetWindowSizeCallback (hWin, HandleWindowSizeDelegate);
-#endif
                        Glfw3.SetWindowRefreshCallback (hWin, HandleWindowRefreshDelegate);
                }
 
@@ -451,7 +452,7 @@ namespace Crow
                /// <summary>
                /// search for graphic object type in crow assembly, if not found,
                /// search for type independently of namespace in all the loaded assemblies
-               /// </summary>
+               /// </summary>  
                /// <remarks>
                /// </remarks>
                /// <returns>the corresponding type object</returns>
@@ -675,7 +676,13 @@ namespace Crow
                        Terminate ();
                }
                public virtual void Terminate () {}
-               public virtual void UpdateFrame () {}
+               int cpt;
+               public virtual void UpdateFrame () {
+                       if (cpt++ > 1000) {
+                               PerformanceMeasure.Notify ();
+                               cpt=0;
+                       }
+               }
                public virtual void Quit () => Glfw3.SetWindowShouldClose (hWin, 1);
 
                public bool Shift => Glfw3.GetKey(hWin, Key.LeftShift) == InputAction.Press ||
@@ -1176,8 +1183,6 @@ namespace Crow
 
                                Monitor.Exit (UpdateMutex);
                        }
-
-                       PerformanceMeasure.Notify ();
                }
                /// <summary>Layouting loop, this is the first step of the udpate and process registered
                /// Layouting queue items. Failing LQI's are requeued in this cycle until MaxTry is reached which
@@ -1610,6 +1615,7 @@ namespace Crow
                        DbgLogger.StartEvent (DbgEvtType.MouseMove);
 
                        try {
+                               Monitor.Enter (UpdateMutex);
 
                                int deltaX = x - MousePosition.X;
                                int deltaY = y - MousePosition.Y;
@@ -1723,6 +1729,7 @@ namespace Crow
                                HoverOrDropTarget = null;
                                return false;
                        } finally {
+                               Monitor.Exit (UpdateMutex);
                                DbgLogger.EndEvent (DbgEvtType.MouseMove);
                        }
                }
@@ -1736,6 +1743,7 @@ namespace Crow
                        DbgLogger.StartEvent (DbgEvtType.MouseDown);
 
                        try {
+                               Monitor.Enter (UpdateMutex);
                                doubleClickTriggered = (lastMouseDown.ElapsedMilliseconds < DOUBLECLICK_TRESHOLD);
                                lastMouseDown.Restart ();
 
@@ -1750,6 +1758,7 @@ namespace Crow
                                return true;
 
                        } finally {
+                               Monitor.Exit (UpdateMutex);
                                DbgLogger.EndEvent (DbgEvtType.MouseDown);
                        }
                }
@@ -1763,6 +1772,7 @@ namespace Crow
                        DbgLogger.StartEvent (DbgEvtType.MouseUp);
 
                        try {
+                               Monitor.Enter (UpdateMutex);
                                mouseRepeatTimer.Reset ();
                                lastMouseDownEvent = null;
 
@@ -1803,6 +1813,7 @@ namespace Crow
 
                                return true;
                        } finally {
+                               Monitor.Exit (UpdateMutex);
                                DbgLogger.EndEvent (DbgEvtType.MouseUp);
                        }
                }
index 64549c08124fc21d67ec222690ee508c0b78bb86..9ce5895bad934003035c7ddd7a876e97c4c15bab 100644 (file)
@@ -3,6 +3,7 @@
 // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
 
 using System;
+using Drawing2D;
 
 namespace Crow
 {
index d4d23f36728b51b0f46b25aa14c5dfd732a6b2ad..ae93626dcb54a3c4aeece9c39d13df672fdef474 100644 (file)
@@ -37,7 +37,7 @@ namespace Crow
                                RegisterForGraphicUpdate ();
                        }
                }
-               
+
                public virtual double Hue {
                        get { return hue; }
                        set {
@@ -51,7 +51,7 @@ namespace Crow
                }
                protected override void onDraw (IContext gr)
                {
-                       base.onDraw (gr);
+                       //base.onDraw (gr);
 
                        RectangleD r = ClientRectangle;
                        r.Height -= 4;
@@ -60,13 +60,13 @@ namespace Crow
                        Gradient grad = new Gradient (
                                Orientation == Orientation.Vertical ? GradientType.Vertical : GradientType.Horizontal);
 
-                       grad.Stops.Add (new Gradient.ColorStop (0,     new Color (1, 0, 0, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (0.167, new Color (1, 1, 0, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (0.333, new Color (0, 1, 0, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (0.5,   new Color (0, 1, 1, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (0.667, new Color (0, 0, 1, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (0.833, new Color (1, 0, 1, 1)));
-                       grad.Stops.Add (new Gradient.ColorStop (1,     new Color (1, 0, 0, 1)));
+                       grad.Stops.Add (new Gradient.ColorStop (0,     new Color (1.0, 0.0, 0.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (0.167, new Color (1.0, 1.0, 0.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (0.333, new Color (0.0, 1.0, 0.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (0.5,   new Color (0.0, 1.0, 1.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (0.667, new Color (0.0, 0.0, 1.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (0.833, new Color (1.0, 0.0, 1.0, 1.0)));
+                       grad.Stops.Add (new Gradient.ColorStop (1,     new Color (1.0, 0.0, 0.0, 1.0)));
 
                        grad.SetAsSource (IFace, gr, r);
                        CairoHelpers.CairoRectangle (gr, r, CornerRadius);
@@ -74,7 +74,7 @@ namespace Crow
 
                        r = ClientRectangle;
 
-                       switch (cursor) {
+                       /*switch (cursor) {
                        case CursorType.Rectangle:
                                if (Orientation == Orientation.Horizontal) {
                                        r.Width = 5;
@@ -103,9 +103,9 @@ namespace Crow
                                        gr.LineTo (r.Left, r.Bottom-0.5);
                                        gr.LineTo (r.Left, y);
                                        gr.ClosePath ();
-                               } else { 
+                               } else {
                                }
-                               break;                  
+                               break;
                        }
 
                        gr.SetSource (Colors.Black);
@@ -113,7 +113,7 @@ namespace Crow
                        gr.StrokePreserve ();
                        gr.SetSource (Colors.White);
                        gr.LineWidth = 1.0;
-                       gr.Stroke ();
+                       gr.Stroke ();*/
                }
 
                public override void OnLayoutChanges (LayoutingType layoutType)
index 30908847f6e1923f7aba1a2fa9e00e5c7534992b..2c102c22e0fa2b2f1cd59bf44b87d03f1b011fc6 100644 (file)
@@ -60,7 +60,7 @@ namespace Crow
                }
                /// <summary>
                /// Image file path, may be on disk or embedded. Accepts bitmaps or SVG drawings.
-               /// </summary>        
+               /// </summary>
                public string Path {
                        get { return _pic == null ? "" : _pic.Path; }
                        set {
@@ -84,9 +84,9 @@ namespace Crow
                /// <summary>
                /// Used only for svg images, repaint only node named referenced in SvgSub.
                /// If null, all the svg is rendered
-               /// </summary>          
+               /// </summary>
                public string SvgSub {
-                       get { return _svgSub; }
+                       get => _svgSub;
                        set {
                                if (_svgSub == value)
                                        return;
@@ -95,9 +95,9 @@ namespace Crow
                        }
                }
                /// <summary>
-               /// Object holding the image data once loaded, may be used directely to pupulate this control without 
+               /// Object holding the image data once loaded, may be used directely to pupulate this control without
                /// specifying a path.
-               /// </summary>          
+               /// </summary>
                public Picture Picture {
                        get { return _pic; }
                        set {
index 39ce20e5359fd0838118ef1a3ce8bddeba0358e1..c3f08fd427d963268fdf889b68a64b325cf440c6 100644 (file)
@@ -1685,9 +1685,9 @@ namespace Crow
                /// <param name="layoutable">The children that is calling the constraints</param>
                /// <param name="layoutType">The currently registering layouting types</param>
                public virtual void ChildrenLayoutingConstraints(ILayoutable layoutable, ref LayoutingType layoutType){ }
-               /// <summary> Query a layouting for the type pass as parameter, redraw only if layout changed. </summary>
 
                internal ReaderWriterLockSlim layoutMutex = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion);
+               /// <summary> Query a layouting for the type pass as parameter, redraw only if layout changed. </summary>
                public virtual void RegisterForLayouting(LayoutingType layoutType){
                        if (disposed) {
                                DbgLogger.AddEvent (DbgEvtType.AlreadyDisposed, this);
@@ -1778,6 +1778,7 @@ namespace Crow
                        if (LayoutChanged != null)
                                LayoutChanged.Invoke (this, new LayoutingEventArgs (layoutType));
                }
+               //Todo: is it necessary?? Raise method already check for null handler.
                internal protected void raiseLayoutChanged(LayoutingType layoutingType){
                        if (LayoutChanged != null)
                                LayoutChanged.Raise (this, new LayoutingEventArgs(layoutingType));
index d674fd6ea391adc003747c43d045db4129690b50..37361807d6c600662bd2152cb5768aebe6194943 100644 (file)
@@ -15,7 +15,7 @@
 
                <!-- Compile with logging enabled, this will slow down apps, use this only
                         for debugging purpose-->
-               <CrowDebugLogEnabled>false</CrowDebugLogEnabled>
+               <CrowDebugLogEnabled>true</CrowDebugLogEnabled>
 
                <!-- Collect several statistics on widgets-->
                <CrowDebugStatsEnabled>false</CrowDebugStatsEnabled>
index 198f7539208c81a375ea43bffcb108982f108874..292334c422aa1668eb31466421636dbf05ed1b77 100644 (file)
@@ -18,6 +18,7 @@ namespace Drawing2D
                void Clear ();
                ISurface CreateSimilar (int width, int height);*/
                void Resize (int width, int height);
+               IntPtr Handle { get; }
        }
 }
 
diff --git a/Samples/AnimTest/AnimTest.csproj b/Samples/AnimTest/AnimTest.csproj
new file mode 100644 (file)
index 0000000..178d453
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+
+       <PropertyGroup>
+               <TargetFramework>netcoreapp3.1</TargetFramework>
+               <OutputType>Exe</OutputType>
+       </PropertyGroup>
+       <ItemGroup>
+               <PackageReference Include="Crow" />
+       </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Samples/AnimTest/Animation/AngleAnimation.cs b/Samples/AnimTest/Animation/AngleAnimation.cs
new file mode 100644 (file)
index 0000000..02faef7
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+
+namespace vke
+{
+       public class AngleAnimation : FloatAnimation2
+       {
+               #region CTOR
+               public AngleAnimation(Object instance, string PropertyName, float Target, int stepCount = 20) :
+                       base (instance,PropertyName,MathHelper.NormalizeAngle(Target),stepCount) {
+                               initialValue = MathHelper.NormalizeAngle (initialValue);
+                       }
+               #endregion
+       }
+}
+
diff --git a/Samples/AnimTest/Animation/Animation.cs b/Samples/AnimTest/Animation/Animation.cs
new file mode 100644 (file)
index 0000000..6ca7af8
--- /dev/null
@@ -0,0 +1,155 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Diagnostics;
+
+namespace vke
+{
+       public delegate void AnimationEventHandler(Animation a);
+
+    public delegate float GetterDelegate();
+    public delegate void SetterDelegate(float value);
+
+    public class Animation
+    {
+               public event AnimationEventHandler AnimationFinished;
+
+               public static Random random = new Random ();
+               public static int DelayMs = 0;
+
+        protected GetterDelegate getValue;
+        protected SetterDelegate setValue;
+
+        public string propertyName;
+
+        protected Stopwatch timer = new Stopwatch();
+        protected int delayStartMs = 0;
+               /// <summary>
+               /// Delay before firing ZnimationFinished event.
+               /// </summary>
+               protected int delayFinishMs = 0;
+        public static List<Animation> AnimationList = new List<Animation>();
+               public static bool HasAnimations => AnimationList.Count > 0;
+        //public FieldInfo member;
+        public Object AnimatedInstance;
+
+               #region CTOR
+               public Animation (){}
+               public Animation(Object instance, string _propertyName)
+               {
+                       propertyName = _propertyName;
+                       AnimatedInstance = instance;
+                       PropertyInfo pi = instance.GetType().GetProperty(propertyName);
+                       try {
+                               getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod());
+                               setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod());
+                       } catch (Exception ex) {
+                               Debug.WriteLine (ex.ToString ());
+                       }
+               }
+               #endregion
+
+               public static void StartAnimation(Animation a, int delayMs = 0, AnimationEventHandler OnEnd = null)
+        {
+                       lock (AnimationList) {
+                               Animation aa = null;
+                               if (Animation.GetAnimation (a.AnimatedInstance, a.propertyName, ref aa)) {
+                                       aa.CancelAnimation ();
+                               }
+
+                               //a.AnimationFinished += onAnimationFinished;
+
+                               a.AnimationFinished += OnEnd;
+                               a.delayStartMs = delayMs + DelayMs;
+
+
+                               if (a.delayStartMs > 0)
+                                       a.timer.Start ();
+
+                               AnimationList.Add (a);
+                       }
+
+        }
+
+        static Stack<Animation> anims = new Stack<Animation>();
+               static int frame = 0;
+        public static void ProcessAnimations()
+        {
+                       frame++;
+
+//                     #region FLYING anim
+//                     if (frame % 20 == 0){
+//                             foreach (Player p in MagicEngine.CurrentEngine.Players) {
+//                                     foreach (CardInstance c in p.InPlay.Cards.Where(ci => ci.HasAbility(AbilityEnum.Flying) && ci.z < 0.4f)) {
+//
+//                                     }
+//                             }
+//                     }
+//                     #endregion
+            //Stopwatch animationTime = new Stopwatch();
+            //animationTime.Start();
+
+                       const int maxAnim = 200000;
+                       int count = 0;
+
+
+                       lock (AnimationList) {
+                               if (anims.Count == 0)
+                                       anims = new Stack<Animation> (AnimationList);
+                       }
+
+                       while (anims.Count > 0 && count < maxAnim) {
+                               Animation a = anims.Pop ();
+                               if (a == null)
+                                       continue;
+                               if (a.timer.IsRunning) {
+                                       if (a.timer.ElapsedMilliseconds > a.delayStartMs)
+                                               a.timer.Stop ();
+                                       else
+                                               continue;
+                               }
+
+                               a.Process ();
+                               count++;
+                       }
+
+            //animationTime.Stop();
+            //Debug.WriteLine("animation: {0} ticks \t {1} ms ", animationTime.ElapsedTicks,animationTime.ElapsedMilliseconds);
+        }
+        public static bool GetAnimation(object instance, string PropertyName, ref Animation a)
+        {
+                       for (int i = 0; i < AnimationList.Count; i++) {
+                               Animation anim = AnimationList [i];
+                               if (anim == null) {
+                                       continue;
+                               }
+                               if (anim.AnimatedInstance == instance && anim.propertyName == PropertyName) {
+                                       a = anim;
+                                       return true;
+                               }
+                       }
+
+            return false;
+        }
+               public virtual void Process () {}
+        public void CancelAnimation()
+        {
+                       //Debug.WriteLine("Cancel anim: " + this.ToString());
+            AnimationList.Remove(this);
+        }
+               public void RaiseAnimationFinishedEvent()
+               {
+                       if (AnimationFinished != null)
+                               AnimationFinished (this);
+               }
+
+               public static void onAnimationFinished(Animation a)
+               {
+                       Debug.WriteLine ("\t\tAnimation finished: " + a.ToString ());
+               }
+    }
+}
diff --git a/Samples/AnimTest/Animation/AnimationTemplate.cs b/Samples/AnimTest/Animation/AnimationTemplate.cs
new file mode 100644 (file)
index 0000000..d5c784c
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using MiscUtil;
+using System.Reflection;
+
+namespace vke
+{
+       public class Animation<T> : Animation
+       {
+               public delegate T GetterDelegate();
+               public delegate void SetterDelegate(T value);
+
+               public T TargetValue;
+               T initialValue;
+               T zero;
+               public T Step;
+               public bool Cycle;
+
+               protected GetterDelegate getValue;
+               protected SetterDelegate setValue;
+
+               #region CTOR
+               public Animation(Object instance, string _propertyName)
+               {
+                       propertyName = _propertyName;
+                       AnimatedInstance = instance;
+                       PropertyInfo pi = instance.GetType().GetProperty(propertyName);
+                       getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod());
+                       setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod());
+               }
+               public Animation(Object instance, string _propertyName, T Target, T step)
+               {
+                       propertyName = _propertyName;
+                       AnimatedInstance = instance;
+                       PropertyInfo pi = instance.GetType().GetProperty(propertyName);
+                       getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod());
+                       setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod());
+
+                       TargetValue = Target;
+
+                       T value = getValue();
+                       initialValue = value;
+                       Type t = typeof(T);
+
+                       if (t.IsPrimitive) {
+                               Step = (T)Convert.ChangeType (step, t);
+                               zero = (T)Convert.ChangeType (0, t);
+                       }else {
+                               Step = (T)Activator.CreateInstance (typeof(T), new Object[] { step });
+                               zero = (T)Activator.CreateInstance (typeof(T), 0f);
+                       }
+                       T test = (T)Operator.SubtractAlternative (value, TargetValue);
+
+                       if (Operator.LessThan(test, zero))
+                       {
+                               if (Operator.LessThan (Step, zero))
+                                       Step = Operator.Negate (Step);
+                       }
+                       else if (Operator.GreaterThan(Step, zero))
+                               Step = Operator.Negate (Step);
+               }
+               #endregion
+
+               public override void Process()
+               {
+                       T value = getValue();
+
+                       //Debug.WriteLine ("Anim: {0} <= {1}", value, this.ToString ());
+
+                       if (Operator.GreaterThan(Step, zero))
+                       {
+                               value = Operator.Add (value, Step);
+                               setValue(value);
+                               //Debug.WriteLine(value);
+                               if (Operator.GreaterThan(Operator.Subtract(TargetValue, value), zero))
+                                       return;
+                       }
+                       else
+                       {
+                               value = Operator.Add (value, Step);
+                               setValue(value);
+
+                               if (Operator.LessThan(Operator.Subtract(TargetValue, value), zero))
+                                       return;
+                       }
+
+                       if (Cycle) {
+                               Step = Operator.Negate (Step);
+                               TargetValue = initialValue;
+                               Cycle = false;
+                               return;
+                       }
+
+                       setValue(TargetValue);
+                       AnimationList.Remove(this);
+
+                       RaiseAnimationFinishedEvent ();
+               }
+
+               public override string ToString ()
+               {
+                       return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step);
+               }
+       }
+
+}
+
diff --git a/Samples/AnimTest/Animation/BezierPath.cs b/Samples/AnimTest/Animation/BezierPath.cs
new file mode 100644 (file)
index 0000000..a3126e3
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Numerics;
+
+namespace vke
+{
+       public class BezierPath : Path
+       {
+               public Vector3 ControlPointStart;
+               public Vector3 ControlPointEnd;
+
+               public BezierPath (Vector3 startPos, Vector3 controlPointStart,
+                       Vector3 controlPointEnd, Vector3 endPos)
+                       :base(startPos, endPos)
+               {
+                       ControlPointStart = controlPointStart;
+                       ControlPointEnd = controlPointEnd;
+               }
+               public BezierPath (Vector3 startPos, Vector3 endPos, Vector3 vUp)
+                       :base(startPos, endPos)
+               {
+                       ControlPointStart = startPos + vUp;
+                       ControlPointEnd = endPos + vUp;
+               }
+               public override Vector3 GetStep (float pos)
+               {
+                       return Path.CalculateBezierPoint (pos, Start, ControlPointStart, ControlPointEnd, End);
+               }
+       }
+}
+
diff --git a/Samples/AnimTest/Animation/FloatAnimation.cs b/Samples/AnimTest/Animation/FloatAnimation.cs
new file mode 100644 (file)
index 0000000..a7e4bf7
--- /dev/null
@@ -0,0 +1,83 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+
+namespace vke
+{
+       public class FloatAnimation : Animation
+       {
+
+               public float TargetValue;
+               float initialValue;
+               public float Step;
+               public bool Cycle;
+
+               #region CTOR
+               public FloatAnimation(Object instance, string _propertyName, float Target, float step = 0.2f)
+                       : base(instance, _propertyName)
+               {
+
+                       TargetValue = Target;
+
+                       float value = getValue();
+                       initialValue = value;
+
+                       Step = step;
+
+                       if (value < TargetValue)
+                       {
+                               if (Step < 0)
+                                       Step = -Step;
+                       }
+                       else if (Step > 0)
+                               Step = -Step;
+               }
+               #endregion
+
+               public override void Process()
+               {
+                       float value = getValue();
+
+                       //Debug.WriteLine ("Anim: {0} <= {1}", value, this.ToString ());
+
+                       if (Step > 0f)
+                       {
+                               value += Step;
+                               setValue(value);
+                               //Debug.WriteLine(value);
+                               if (TargetValue > value)
+                                       return;
+                       }
+                       else
+                       {
+                               value += Step;
+                               setValue(value);
+
+                               if (TargetValue < value)
+                                       return;
+                       }
+
+                       if (Cycle) {
+                               Step = -Step;
+                               TargetValue = initialValue;
+                               Cycle = false;
+                               return;
+                       }
+
+                       setValue(TargetValue);
+                       lock(AnimationList)
+                               AnimationList.Remove(this);
+
+                       RaiseAnimationFinishedEvent ();
+               }
+
+               public override string ToString ()
+               {
+                       return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step);
+               }
+       }
+
+}
+
diff --git a/Samples/AnimTest/Animation/FloatAnimation2.cs b/Samples/AnimTest/Animation/FloatAnimation2.cs
new file mode 100644 (file)
index 0000000..1d5c919
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+
+namespace vke
+{
+       public class FloatAnimation2 : Animation
+       {
+
+               public float TargetValue;
+               protected float initialValue;
+               int stepCount, currentStep;
+
+               #region CTOR
+               public FloatAnimation2(Object instance, string _propertyName, float Target, int _stepCount = 20)
+                       : base(instance, _propertyName)
+               {
+
+                       TargetValue = Target;
+
+                       float value = getValue();
+                       initialValue = value;
+
+                       stepCount = _stepCount;
+               }
+               #endregion
+
+               bool smooth = true;
+
+               float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f;
+
+               public override void Process()
+               {
+
+                       currentStep++;
+
+                       float t = (float)currentStep / (float)stepCount;
+                       if (smooth)
+                               t = smoothedStep (t);
+
+                       setValue (initialValue + t * (TargetValue - initialValue));
+
+                       if (currentStep < stepCount)
+                               return;
+
+                       setValue(TargetValue);
+                       AnimationList.Remove(this);
+                       RaiseAnimationFinishedEvent ();
+               }
+
+               public override string ToString ()
+               {
+                       return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,stepCount);
+               }
+       }
+
+}
+
diff --git a/Samples/AnimTest/Animation/MathHelper.cs b/Samples/AnimTest/Animation/MathHelper.cs
new file mode 100644 (file)
index 0000000..35a3009
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright (c) 2019-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+namespace vke {
+    public static class MathHelper {
+        public const float Pi = (float)Math.PI;
+        public const float TwoPi = (float)Math.PI * 2;
+               public static float NormalizeAngle (float a) {
+                       float tmp = a % MathHelper.TwoPi;
+                       if (tmp < 0)
+                               tmp += MathHelper.TwoPi;
+                       return tmp;
+               }
+    }
+}
diff --git a/Samples/AnimTest/Animation/Path.cs b/Samples/AnimTest/Animation/Path.cs
new file mode 100644 (file)
index 0000000..1f04e09
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Numerics;
+
+namespace vke {
+    public class Path 
+       {
+               public Vector3 Start;
+               public Vector3 End;
+
+               float length;
+               Vector3 vDir;
+
+               public Path(Vector3 startPos, Vector3 endPos){
+                       Start = startPos;
+                       End = endPos;
+                       initialComputations ();
+               }
+               protected void initialComputations(){
+                       Vector3 vPath = End - Start;
+                       vDir = Vector3.Normalize (vPath);
+                       length = vPath.Length();
+               }
+               /// <summary>
+               /// Get single step on the path
+               /// </summary>
+               /// <returns>return position on the path</returns>
+               /// <param name="pos">Position expressed as percentage of total length</param>
+               public virtual Vector3 GetStep(float pos){
+                       return Start + vDir * length * pos;
+               }
+
+               public static Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
+               {
+                       float u = 1 - t;
+                       float tt = t * t;
+                       float uu = u * u;
+                       float uuu = uu * u;
+                       float ttt = tt * t;
+
+                       Vector3 p = uuu * p0;
+                       p += 3 * uu * t * p1;
+                       p += 3 * u * tt * p2;
+                       p += ttt * p3;
+
+                       return p;
+               }
+    }
+
+}
diff --git a/Samples/AnimTest/Animation/PathAnimation.cs b/Samples/AnimTest/Animation/PathAnimation.cs
new file mode 100644 (file)
index 0000000..54c30e9
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Numerics;
+
+namespace vke
+{
+       public class PathAnimation : Animation<Vector3>
+       {
+               Path path;
+               int stepCount, currentStep;
+
+               #region CTOR
+               public PathAnimation(Object instance, string _propertyName, Path _path, int _stepCount = 20)
+                       : base(instance, _propertyName)
+               {
+                       path = _path;
+                       stepCount = _stepCount;
+               }
+
+               #endregion
+
+               bool smooth = true;
+
+               float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f;
+
+               public override void Process()
+               {
+                       currentStep++;
+
+                       float t = (float)currentStep / (float)stepCount;
+                       if (smooth)
+                               t = smoothedStep (t);
+
+                       Vector3 pos = path.GetStep (t);
+                       setValue(pos);
+
+                       if (currentStep < stepCount)
+                               return;
+
+                       AnimationList.Remove(this);
+                       RaiseAnimationFinishedEvent ();
+               }
+
+               public override string ToString ()
+               {
+                       return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step);
+               }
+       }
+}
+
diff --git a/Samples/AnimTest/Animation/ShakeAnimation.cs b/Samples/AnimTest/Animation/ShakeAnimation.cs
new file mode 100644 (file)
index 0000000..37a089b
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright (c) 2015-2021  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+
+namespace vke
+{
+       public class ShakeAnimation : Animation
+       {
+               const float stepMin = 0.001f, stepMax = 0.005f;
+               bool rising = true;
+
+               public float LowBound;
+               public float HighBound;
+
+               #region CTOR
+               public ShakeAnimation(
+                       Object instance,
+                       string _propertyName,
+                       float lowBound, float highBound)
+                       : base(instance, _propertyName)
+               {
+
+                       LowBound = Math.Min (lowBound, highBound);
+                       HighBound = Math.Max (lowBound, highBound);
+
+                       float value = getValue ();
+
+                       if (value > HighBound)
+                               rising = false;
+               }
+               #endregion
+
+               public override void Process ()
+               {
+                       float value = getValue ();
+                       float step = stepMin + (float)random.NextDouble () * stepMax;
+
+                       if (rising) {
+                               value += step;
+                               if (value > HighBound) {
+                                       value = HighBound;
+                                       rising = false;
+                               }
+                       } else {
+                               value -= step;
+                               if (value < LowBound) {
+                                       value = LowBound;
+                                       rising = true;
+                               } else if (value > HighBound)
+                                       value -= step * 10f;
+                       }
+                       setValue (value);
+               }
+
+       }
+
+}
+
diff --git a/Samples/AnimTest/MiscUtil/ExpressionUtil.cs b/Samples/AnimTest/MiscUtil/ExpressionUtil.cs
new file mode 100644 (file)
index 0000000..8f9ca4e
--- /dev/null
@@ -0,0 +1,99 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MiscUtil.Linq
+{
+    /// <summary>
+    /// General purpose Expression utilities
+    /// </summary>
+    public static class ExpressionUtil
+    {
+        /// <summary>
+        /// Create a function delegate representing a unary operation
+        /// </summary>
+        /// <typeparam name="TArg1">The parameter type</typeparam>
+        /// <typeparam name="TResult">The return type</typeparam>
+        /// <param name="body">Body factory</param>
+        /// <returns>Compiled function delegate</returns>
+        public static Func<TArg1, TResult> CreateExpression<TArg1, TResult>(
+            Func<Expression, UnaryExpression> body)
+        {
+            ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp");
+            try
+            {
+                return Expression.Lambda<Func<TArg1, TResult>>(body(inp), inp).Compile();
+            }
+            catch (Exception ex)
+            {
+                string msg = ex.Message; // avoid capture of ex itself
+                return delegate { throw new InvalidOperationException(msg); };
+            }
+        }
+
+        /// <summary>
+        /// Create a function delegate representing a binary operation
+        /// </summary>
+        /// <typeparam name="TArg1">The first parameter type</typeparam>
+        /// <typeparam name="TArg2">The second parameter type</typeparam>
+        /// <typeparam name="TResult">The return type</typeparam>
+        /// <param name="body">Body factory</param>
+        /// <returns>Compiled function delegate</returns>
+        public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
+            Func<Expression, Expression, BinaryExpression> body)
+        {
+            return CreateExpression<TArg1, TArg2, TResult>(body, false);
+        }
+
+        /// <summary>
+        /// Create a function delegate representing a binary operation
+        /// </summary>
+        /// <param name="castArgsToResultOnFailure">
+        /// If no matching operation is possible, attempt to convert
+        /// TArg1 and TArg2 to TResult for a match? For example, there is no
+        /// "decimal operator /(decimal, int)", but by converting TArg2 (int) to
+        /// TResult (decimal) a match is found.
+        /// </param>
+        /// <typeparam name="TArg1">The first parameter type</typeparam>
+        /// <typeparam name="TArg2">The second parameter type</typeparam>
+        /// <typeparam name="TResult">The return type</typeparam>
+        /// <param name="body">Body factory</param>
+        /// <returns>Compiled function delegate</returns>
+        public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
+            Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure)
+        {
+            ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs");
+            ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs");
+            try
+            {
+                try
+                {
+                    return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();
+                }
+                catch (InvalidOperationException)
+                {
+                    if (castArgsToResultOnFailure && !(         // if we show retry                                                        
+                            typeof(TArg1) == typeof(TResult) &&  // and the args aren't
+                            typeof(TArg2) == typeof(TResult)))
+                    { // already "TValue, TValue, TValue"...
+                        // convert both lhs and rhs to TResult (as appropriate)
+                        Expression castLhs = typeof(TArg1) == typeof(TResult) ?
+                                (Expression)lhs :
+                                (Expression)Expression.Convert(lhs, typeof(TResult));
+                        Expression castRhs = typeof(TArg2) == typeof(TResult) ?
+                                (Expression)rhs :
+                                (Expression)Expression.Convert(rhs, typeof(TResult));
+
+                        return Expression.Lambda<Func<TArg1, TArg2, TResult>>(
+                            body(castLhs, castRhs), lhs, rhs).Compile();
+                    }
+                    else throw;
+                }
+            }
+            catch (Exception ex)
+            {
+                string msg = ex.Message; // avoid capture of ex itself
+                return delegate { throw new InvalidOperationException(msg); };
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Samples/AnimTest/MiscUtil/NullOp.cs b/Samples/AnimTest/MiscUtil/NullOp.cs
new file mode 100644 (file)
index 0000000..7bb8790
--- /dev/null
@@ -0,0 +1,59 @@
+
+namespace MiscUtil
+{
+    interface INullOp<T>
+    {
+        bool HasValue(T value);
+        bool AddIfNotNull(ref T accumulator, T value);
+    }
+    sealed class StructNullOp<T>
+        : INullOp<T>, INullOp<T?>
+        where T : struct
+    {
+        public bool HasValue(T value)
+        {
+            return true;
+        }
+        public bool AddIfNotNull(ref T accumulator, T value)
+        {
+            accumulator = Operator<T>.Add(accumulator, value);
+            return true;
+        }
+        public bool HasValue(T? value)
+        {
+            return value.HasValue;
+        }
+        public bool AddIfNotNull(ref T? accumulator, T? value)
+        {
+            if (value.HasValue)
+            {
+                accumulator = accumulator.HasValue ?
+                    Operator<T>.Add(
+                        accumulator.GetValueOrDefault(),
+                        value.GetValueOrDefault())
+                    : value;
+                return true;
+            }
+            return false;
+        }
+    }
+    sealed class ClassNullOp<T>
+        : INullOp<T>
+        where T : class
+    {
+        public bool HasValue(T value)
+        {
+            return value != null;
+        }
+        public bool AddIfNotNull(ref T accumulator, T value)
+        {
+            if (value != null)
+            {
+                accumulator = accumulator == null ?
+                    value : Operator<T>.Add(accumulator, value);
+                return true;
+            }
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Samples/AnimTest/MiscUtil/Operator.cs b/Samples/AnimTest/MiscUtil/Operator.cs
new file mode 100644 (file)
index 0000000..ee1168c
--- /dev/null
@@ -0,0 +1,449 @@
+using System;
+using System.Linq.Expressions;
+using MiscUtil.Linq;
+
+namespace MiscUtil
+{
+    /// <summary>
+    /// The Operator class provides easy access to the standard operators
+    /// (addition, etc) for generic types, using type inference to simplify
+    /// usage.
+    /// </summary>
+    public static class Operator
+    {
+
+        /// <summary>
+        /// Indicates if the supplied value is non-null,
+        /// for reference-types or Nullable&lt;T&gt;
+        /// </summary>
+        /// <returns>True for non-null values, else false</returns>
+        public static bool HasValue<T>(T value)
+        {
+            return Operator<T>.NullOp.HasValue(value);
+        }
+        /// <summary>
+        /// Increments the accumulator only
+        /// if the value is non-null. If the accumulator
+        /// is null, then the accumulator is given the new
+        /// value; otherwise the accumulator and value
+        /// are added.
+        /// </summary>
+        /// <param name="accumulator">The current total to be incremented (can be null)</param>
+        /// <param name="value">The value to be tested and added to the accumulator</param>
+        /// <returns>True if the value is non-null, else false - i.e.
+        /// "has the accumulator been updated?"</returns>
+        public static bool AddIfNotNull<T>(ref T accumulator, T value)
+        {
+            return Operator<T>.NullOp.AddIfNotNull(ref accumulator, value);
+        }
+
+        /// <summary>
+        /// Evaluates unary negation (-) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Negate<T>(T value)
+        {
+            return Operator<T>.Negate(value);
+        }
+        /// <summary>
+        /// Evaluates bitwise not (~) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Not<T>(T value)
+        {
+            return Operator<T>.Not(value);
+        }
+        /// <summary>
+        /// Evaluates bitwise or (|) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Or<T>(T value1, T value2)
+        {
+            return Operator<T>.Or(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates bitwise and (&amp;) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T And<T>(T value1, T value2)
+        {
+            return Operator<T>.And(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates bitwise xor (^) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Xor<T>(T value1, T value2)
+        {
+            return Operator<T>.Xor(value1, value2);
+        }
+        /// <summary>
+        /// Performs a conversion between the given types; this will throw
+        /// an InvalidOperationException if the type T does not provide a suitable cast, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this cast.
+        /// </summary>
+        public static TTo Convert<TFrom, TTo>(TFrom value)
+        {
+            return Operator<TFrom, TTo>.Convert(value);
+        }
+        /// <summary>
+        /// Evaluates binary addition (+) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>        
+        public static T Add<T>(T value1, T value2)
+        {
+            return Operator<T>.Add(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary addition (+) for the given type(s); this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static TArg1 AddAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
+        {
+            return Operator<TArg2, TArg1>.Add(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary subtraction (-) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Subtract<T>(T value1, T value2)
+        {
+            return Operator<T>.Subtract(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary subtraction(-) for the given type(s); this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static TArg1 SubtractAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
+        {
+            return Operator<TArg2, TArg1>.Subtract(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary multiplication (*) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Multiply<T>(T value1, T value2)
+        {
+            return Operator<T>.Multiply(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary multiplication (*) for the given type(s); this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
+        {
+            return Operator<TArg2, TArg1>.Multiply(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary division (/) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static T Divide<T>(T value1, T value2)
+        {
+            return Operator<T>.Divide(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary division (/) for the given type(s); this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static TArg1 DivideAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
+        {
+            return Operator<TArg2, TArg1>.Divide(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary equality (==) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool Equal<T>(T value1, T value2)
+        {
+            return Operator<T>.Equal(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary inequality (!=) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool NotEqual<T>(T value1, T value2)
+        {
+            return Operator<T>.NotEqual(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary greater-than (&gt;) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool GreaterThan<T>(T value1, T value2)
+        {
+            return Operator<T>.GreaterThan(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary less-than (&lt;) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool LessThan<T>(T value1, T value2)
+        {
+            return Operator<T>.LessThan(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary greater-than-on-eqauls (&gt;=) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool GreaterThanOrEqual<T>(T value1, T value2)
+        {
+            return Operator<T>.GreaterThanOrEqual(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates binary less-than-or-equal (&lt;=) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static bool LessThanOrEqual<T>(T value1, T value2)
+        {
+            return Operator<T>.LessThanOrEqual(value1, value2);
+        }
+        /// <summary>
+        /// Evaluates integer division (/) for the given type; this will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary><remarks>
+        /// This operation is particularly useful for computing averages and
+        /// similar aggregates.
+        /// </remarks>
+        public static T DivideInt32<T>(T value, int divisor)
+        {
+            return Operator<int,T>.Divide(value, divisor);
+        }
+               public static T Max<T>(T v1, T v2)
+               {
+                       return Operator.GreaterThanOrEqual (v1, v2) ? v1 : v2;
+               }
+               public static T Min<T>(T v1, T v2)
+               {
+                       return Operator.LessThanOrEqual (v1, v2) ? v1 : v2;
+               }
+    }
+    /// <summary>
+    /// Provides standard operators (such as addition) that operate over operands of
+    /// different types. For operators, the return type is assumed to match the first
+    /// operand.
+    /// </summary>
+    /// <seealso cref="Operator&lt;T&gt;"/>
+    /// <seealso cref="Operator"/>
+    public static class Operator<TValue, TResult>
+    {
+        private static readonly Func<TValue, TResult> convert;
+        /// <summary>
+        /// Returns a delegate to convert a value between two types; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide a suitable cast, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this cast.
+        /// </summary>
+        public static Func<TValue, TResult> Convert { get { return convert; } }
+        static Operator()
+        {
+            convert = ExpressionUtil.CreateExpression<TValue, TResult>(body => Expression.Convert(body, typeof(TResult)));
+            add = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Add, true);
+            subtract = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Subtract, true);
+            multiply = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Multiply, true);
+            divide = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Divide, true);
+        }
+
+        private static readonly Func<TResult, TValue, TResult> add, subtract, multiply, divide;
+        /// <summary>
+        /// Returns a delegate to evaluate binary addition (+) for the given types; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<TResult, TValue, TResult> Add { get { return add; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary subtraction (-) for the given types; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<TResult, TValue, TResult> Subtract { get { return subtract; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary multiplication (*) for the given types; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<TResult, TValue, TResult> Multiply { get { return multiply; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary division (/) for the given types; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<TResult, TValue, TResult> Divide { get { return divide; } }
+    }
+
+    /// <summary>
+    /// Provides standard operators (such as addition) over a single type
+    /// </summary>
+    /// <seealso cref="Operator"/>
+    /// <seealso cref="Operator&lt;TValue,TResult&gt;"/>
+    public static class Operator<T>
+    {
+        static readonly INullOp<T> nullOp;
+        internal static INullOp<T> NullOp { get { return nullOp; } }
+
+        static readonly T zero;
+        /// <summary>
+        /// Returns the zero value for value-types (even full Nullable&lt;TInner&gt;) - or null for reference types
+        /// </summary>
+        public static T Zero { get { return zero;} }
+
+        static readonly Func<T, T> negate, not;
+        static readonly Func<T, T, T> or, and, xor;
+        /// <summary>
+        /// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T> Negate { get { return negate; } }
+        /// <summary>
+        /// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T> Not { get { return not; } }
+        /// <summary>
+        /// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Or { get { return or; } }
+        /// <summary>
+        /// Returns a delegate to evaluate bitwise and (&amp;) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> And { get { return and; } }
+        /// <summary>
+        /// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Xor { get { return xor; } }
+
+        static readonly Func<T, T, T> add, subtract, multiply, divide;
+        /// <summary>
+        /// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Add { get { return add; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Subtract { get { return subtract; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Multiply { get { return multiply; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, T> Divide { get { return divide; } }
+
+
+        static readonly Func<T, T, bool> equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual;
+        /// <summary>
+        /// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> Equal { get { return equal; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> NotEqual { get { return notEqual; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary greater-then (&gt;) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> GreaterThan { get { return greaterThan; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary less-than (&lt;) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> LessThan { get { return lessThan; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary greater-than-or-equal (&gt;=) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> GreaterThanOrEqual { get { return greaterThanOrEqual; } }
+        /// <summary>
+        /// Returns a delegate to evaluate binary less-than-or-equal (&lt;=) for the given type; this delegate will throw
+        /// an InvalidOperationException if the type T does not provide this operator, or for
+        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
+        /// </summary>
+        public static Func<T, T, bool> LessThanOrEqual { get { return lessThanOrEqual; } }
+
+        static Operator()
+        {
+            add = ExpressionUtil.CreateExpression<T, T, T>(Expression.Add);
+            subtract = ExpressionUtil.CreateExpression<T, T, T>(Expression.Subtract);
+            divide = ExpressionUtil.CreateExpression<T, T, T>(Expression.Divide);
+            multiply = ExpressionUtil.CreateExpression<T, T, T>(Expression.Multiply);
+            
+            greaterThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThan);
+            greaterThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThanOrEqual);
+            lessThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThan);
+            lessThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThanOrEqual);
+            equal = ExpressionUtil.CreateExpression<T, T, bool>(Expression.Equal);
+            notEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.NotEqual);
+
+            negate = ExpressionUtil.CreateExpression<T, T>(Expression.Negate);
+            and = ExpressionUtil.CreateExpression<T, T, T>(Expression.And);
+            or = ExpressionUtil.CreateExpression<T, T, T>(Expression.Or);
+            not = ExpressionUtil.CreateExpression<T, T>(Expression.Not);
+            xor = ExpressionUtil.CreateExpression<T, T, T>(Expression.ExclusiveOr);
+
+            Type typeT = typeof(T);
+            if(typeT.IsValueType && typeT.IsGenericType && (typeT.GetGenericTypeDefinition() == typeof(Nullable<>))) {
+                // get the *inner* zero (not a null Nullable<TValue>, but default(TValue))
+                Type nullType = typeT.GetGenericArguments()[0];
+                zero = (T)Activator.CreateInstance(nullType);
+                nullOp = (INullOp<T>)Activator.CreateInstance(
+                        typeof(StructNullOp<>).MakeGenericType(nullType));
+            } else {
+                zero = default(T);
+                if (typeT.IsValueType)
+                {
+                    nullOp = (INullOp<T>)Activator.CreateInstance(
+                        typeof(StructNullOp<>).MakeGenericType(typeT));
+                }
+                else
+                {
+                    nullOp = (INullOp<T>)Activator.CreateInstance(
+                        typeof(ClassNullOp<>).MakeGenericType(typeT));
+                }
+            }
+        }
+    }
+}
diff --git a/Samples/AnimTest/main.cs b/Samples/AnimTest/main.cs
new file mode 100644 (file)
index 0000000..6dd0b52
--- /dev/null
@@ -0,0 +1,28 @@
+using System;
+using Crow;
+using Glfw;
+using Samples;
+
+namespace HelloWorld
+{
+       class Program : Interface {
+               Program() : base (800, 600, true) {}
+               static void Main (string[] args) {
+                       //Interface.PreferedBackendType = Drawing2D.BackendType.Egl;
+                       using (Program app = new Program ()) {
+                               //app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Label Text='Hello World' Background='Red' Top='50' Margin='0'/>");
+                               //app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Window Caption='hello world'/>");
+                               app.Run ();
+                       }
+               }
+               protected override void OnInitialized()
+               {
+                       Load ("/mnt/devel/crow/Samples/AnimTest/ui/anim.crow");
+               }
+
+               public override void UpdateFrame()
+               {
+                       base.UpdateFrame();
+               }
+       }
+}
diff --git a/Samples/AnimTest/ui/anim.crow b/Samples/AnimTest/ui/anim.crow
new file mode 100644 (file)
index 0000000..f3fa6c6
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<Container Margin="10" Width="500"  Background="/mnt/devel/crow/Samples/AnimTest/ui/background.jpg">
+<!--   <Image Path="/mnt/devel/crow/Samples/AnimTest/ui/background.jpg" SvgSub="True" Height="200"/>-->
+       <HorizontalStack Height="Fit" Width="Fit" Spacing="10" VerticalAlignment="Bottom" >
+               <Label Background="Teal" Width="30%" Margin="10" CornerRadius="10" Font="bold, 14" TextAlignment="Center"/>
+               <Label Background="Teal" Width="30%" Margin="10" CornerRadius="10" Font="bold, 14" TextAlignment="Center"/>
+               <Label Background="Teal" Width="30%" Margin="10" CornerRadius="10" Font="bold, 14" TextAlignment="Center"/>
+       </HorizontalStack>
+</Container>
+
diff --git a/Samples/AnimTest/ui/background.crow b/Samples/AnimTest/ui/background.crow
new file mode 100644 (file)
index 0000000..a0881df
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<Widget Height="400" Width="260" Background="/mnt/devel/crow/Samples/AnimTest/ui/background.jpg"/>
+<!--<Widget Height="100" Width="100" Background="#Crow.Icons.checkbox.svg"/>-->
+
diff --git a/Samples/AnimTest/ui/background.jpg b/Samples/AnimTest/ui/background.jpg
new file mode 100644 (file)
index 0000000..50ebfbf
Binary files /dev/null and b/Samples/AnimTest/ui/background.jpg differ
index 130171aa45358b255747c668800fd9349575618c..7506d27d521edb3561c488e985b6082027c1d33a 100644 (file)
@@ -12,19 +12,19 @@ namespace Samples
                static void Main ()
                {
                        using (BasicTests app = new BasicTests ()) {
-                               app.SolidBackground = false;
+                               //app.SolidBackground = false;
                                app.Run ();
                        }
                }
 
                protected override void OnInitialized ()
                {
-                       Commands = new CommandGroup (
+                       /*Commands = new CommandGroup (
                                new Crow.Command("command1", new Action(() => Console.WriteLine ("command1 triggered"))),
                                new Crow.Command("command2", new Action(() => Console.WriteLine ("command2 triggered"))),
                                new Crow.Command("command3", new Action(() => Console.WriteLine ("command3 triggered"))),
                                new Crow.Command("command4", new Action(() => Console.WriteLine ("command4 triggered")))
-                       );
+                       );*/
 
                        // += KeyboardKeyDown1;
 
@@ -55,12 +55,10 @@ namespace Samples
 
                int idx = 0;
                string [] testFiles;
-
-
-               public override bool OnKeyDown (Key key)
+               public override bool OnKeyDown (KeyEventArgs e)
                {
                        try {
-                               switch (key) {
+                               switch (e.Key) {
                                case Key.Escape:
                                        Quit ();
                                        break;
@@ -75,7 +73,7 @@ namespace Samples
                                        NotifyValueChanged ("TestList", TestList);
                                        break;
                                default:
-                                       return base.OnKeyDown (key);
+                                       return base.OnKeyDown (e);
                                }
 
                                ClearInterface ();
index fd70a2b96e8db47cd7d449bfa3e315aa361cc24e..fdf75a8df256655ad747aac0289ab33df8992eed 100644 (file)
@@ -8,6 +8,7 @@ namespace HelloWorld
        class Program : Interface {
                Program() : base (800, 600, true) {}
                static void Main (string[] args) {
+                       //Interface.PreferedBackendType = Drawing2D.BackendType.Egl;
                        using (Program app = new Program ()) {
                                //app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Label Text='Hello World' Background='Red' Top='50' Margin='0'/>");
                                //app.Initialized += (sender, e) => app.LoadIMLFragment (@"<Window Caption='hello world'/>");
index d1c30b0f630466ff663d9e78043d3b50b92ccedc..874667c2b32a6bcc9a62cdb16a0aed1966b48791 100644 (file)
@@ -1,2 +1,9 @@
 <?xml version="1.0"?>
-<Label Text="Hello World"/>
+
+       <!--<Widget Background="hgradient|0:Black|0.5:Blue|1:Red" Margin="100" />-->
+       <VerticalStack>
+               <Image Path="#Crow.Icons.checkbox.svg" SvgSub="True" Height="200"/>
+               <CheckBox/>
+
+       </VerticalStack>
+       <!--<ColorPicker/>-->
index 958a6b13d68f46b78e8f6aadf667aa67ddffa6c7..92a71e9f27a9ca6012470bea79cd4ca5ae44f468 100644 (file)
@@ -14,6 +14,7 @@ using Crow.Text;
 using System.Collections.Generic;
 using Encoding = System.Text.Encoding;
 using Samples;
+using System.Threading;
 
 namespace ShowCase
 {
@@ -30,6 +31,8 @@ namespace ShowCase
                };
                static void Main ()
                {
+                       //Interface.PreferedBackendType = Drawing2D.BackendType.Egl;
+                       
                        initDebugLog ();
 
                        Environment.SetEnvironmentVariable ("FONTCONFIG_PATH", @"C:\Users\Jean-Philippe\source\vcpkg\installed\x64-windows\tools\fontconfig\fonts");
@@ -44,6 +47,40 @@ namespace ShowCase
                                app.Run ();
                        }
                }
+               public override void Run () {
+                       initBackend ();
+
+                       if (!SingleThreaded) {
+                               Thread t = new Thread (InterfaceThread) {
+                                       IsBackground = true
+                               };
+                               t.Start ();
+                       }
+
+                       Init ();
+
+                       if (SingleThreaded) {
+                               while (!Glfw3.WindowShouldClose (WindowHandle)) {
+                                       Glfw3.PollEvents ();
+                                       try     {
+                                               Update();
+                                               UpdateFrame ();
+                                       } catch (Exception ex) {
+                                               showError (ex);
+                                               crowContainer.SetChild (null);
+                                       }
+                                       Thread.Sleep (UPDATE_INTERVAL);
+                               }
+                       } else {
+                               while (!Glfw3.WindowShouldClose (hWin)) {
+                                       Glfw3.PollEvents ();
+                                       UpdateFrame ();
+                                       Thread.Sleep (POLLING_INTERVAL);
+                               }
+                       }
+
+                       Terminate ();
+               }
                public Container crowContainer;
 
                Stopwatch reloadChrono = new Stopwatch ();
index 298c8797630443b757f12b81eb79a2e61407562f..b1d6fb886c449f834ccda33132c61db8f9d71e08 100644 (file)
@@ -77,7 +77,7 @@
                <Container Name="CrowContainer" Height="60%" Background="Black"/>
                <Splitter/>
                <VerticalStack CacheEnabled="true">
-                       <Menu Data="{EditorAllCommands}" Height="Fit" Width="Stretched">
+                       <!--<Menu Data="{EditorAllCommands}" Height="Fit" Width="Stretched">
                                <ItemTemplate DataType="Crow.Command" Path="Interfaces/menuCommand.itmp"/>
                                <ItemTemplate DataType="Crow.CommandGroup" >
                                        <MenuItem Data="{Commands}" Width="Fit" IsEnabled="{CanExecute}"
@@ -85,8 +85,8 @@
                                                        ItemTemplate="Interfaces/menuItem.itmp">
                                        </MenuItem>
                                </ItemTemplate>
-                       </Menu>
-               <!--    <Wrapper Orientation="Vertical" Height="Fit">
+                       </Menu>-->
+                       <Wrapper Orientation="Vertical" Height="Fit">
                                <Button Style="IcoButton" Command="{CMDNew}" />
                                <Button Style="IcoButton" Command="{CMDSave}" />
                                <Button Style="IcoButton" Command="{CMDSaveAs}" />
                                                        Tooltip="Add '*source* where you want to load the source in the editor."/>
                                        </VerticalStack>
                                </Popper>
-                       </Wrapper>-->
+                       </Wrapper>
                        <HorizontalStack>
                                <Editor Name="tb" Text="{Source}" Multiline="true" Font="consolas, 12" Focusable="true" Height="Stretched" Width="Stretched"
                                                TextChanged="onTextChanged" KeyDown="textView_KeyDown" ContextCommands="{EditorEditCommands}"
-                                               SelectionChanged="onEditorSelectionChanged"
+                                               SelectionChanged="onEditorSelectionChanged" ClipToClientRect="true"
                                                Foreground="DarkGrey" Background="White" MouseWheelSpeed="20"/>
                                <ScrollBar Value="{²../tb.ScrollY}"
                                                LargeIncrement="{../tb.PageHeight}" SmallIncrement="1"
diff --git a/Samples/common/ui/Interfaces/Experimental/svg.crow b/Samples/common/ui/Interfaces/Experimental/svg.crow
new file mode 100644 (file)
index 0000000..7a93503
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<Image Path="/usr/share/icons/breeze/categories/32/applications-education.svg"/>
\ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/TemplatedContainer/buttons.crow b/Samples/common/ui/Interfaces/TemplatedContainer/buttons.crow
new file mode 100644 (file)
index 0000000..32a5b83
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VerticalStack Margin="10" Background="Grey">
+       <Button
+               Foreground="LightGrey"
+               MouseEnter="{Foreground=White}"
+               MouseLeave="{Foreground=LightGrey}"
+               Background="vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet"
+               MouseUp="{Background=vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet}"
+               MouseDown="{Background=vgradient|0:Jet|0.1:Grey|0.9:Grey|1:LightGrey}">
+               <Template>
+                       <Border Foreground="Black" CornerRadius="10" Background="{./Background}" Margin="7">
+                               <Label Foreground="{./Foreground}" Text="{./Caption}"/>
+                       </Border>
+               </Template>
+       </Button >
+       <CheckBox Width="200"
+               Foreground="Black"
+               Background="vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet"
+               Unchecked="{Background=vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet};{Foreground=Black}"
+               Checked="{Background=vgradient|0:Jet|0.1:Grey|0.9:Grey|1:LightGrey};{Foreground=DodgerBlue}">
+               <Template>
+                       <Border Foreground="{./Foreground}" CornerRadius="10" Background="{./Background}" Margin="7">
+                               <Label Text="{./Caption}"/>
+                       </Border>
+               </Template>
+       </CheckBox >
+       <HorizontalStack Height="Fit" Width="Fit">
+               <RadioButton Width="200"
+                       Foreground="Black"
+                       Background="vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet"
+                       Unchecked="{Background=vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet};{Foreground=Black}"
+                       Checked="{Background=vgradient|0:Jet|0.1:Grey|0.9:Grey|1:LightGrey};{Foreground=DodgerBlue}">
+                       <Template>
+                               <Border Foreground="{./Foreground}" CornerRadius="10" Background="{./Background}" Margin="7">
+                                       <Label Text="{./Caption}"/>
+                               </Border>
+                       </Template>
+               </RadioButton >
+               <RadioButton Width="200"
+                       Foreground="Black"
+                       Background="vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet"
+                       Unchecked="{Background=vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet};{Foreground=Black}"
+                       Checked="{Background=vgradient|0:Jet|0.1:Grey|0.9:Grey|1:LightGrey};{Foreground=DodgerBlue}">
+                       <Template>
+                               <Border Foreground="{./Foreground}" CornerRadius="10" Background="{./Background}" Margin="7">
+                                       <Label Text="{./Caption}"/>
+                               </Border>
+                       </Template>
+               </RadioButton >
+               <RadioButton Width="200"
+                       Foreground="Black"
+                       Background="vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet"
+                       Unchecked="{Background=vgradient|0:LightGrey|0.1:Grey|0.9:Grey|1:Jet};{Foreground=Black}"
+                       Checked="{Background=vgradient|0:Jet|0.1:Grey|0.9:Grey|1:LightGrey};{Foreground=DodgerBlue}">
+                       <Template>
+                               <Border Foreground="{./Foreground}" CornerRadius="10" Background="{./Background}" Margin="7">
+                                       <Label Text="{./Caption}"/>
+                               </Border>
+                       </Template>
+               </RadioButton >
+       </HorizontalStack>
+</VerticalStack>
\ No newline at end of file