diff options
46 files changed, 1550 insertions, 137 deletions
diff --git a/Backends/CairoBackend/src/Context.cs b/Backends/CairoBackend/src/Context.cs index c15ecaa3..f9af1bf1 100644 --- a/Backends/CairoBackend/src/Context.cs +++ b/Backends/CairoBackend/src/Context.cs @@ -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); + } } } diff --git a/Backends/CairoBackend/src/EglBackend.cs b/Backends/CairoBackend/src/EglBackend.cs index 909c3cd9..073793e7 100644 --- a/Backends/CairoBackend/src/EglBackend.cs +++ b/Backends/CairoBackend/src/EglBackend.cs @@ -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); } } } diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs index 96899dbd..a90d275a 100644 --- a/Backends/CairoBackend/src/ImageBackend.cs +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -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); + } + } } } diff --git a/Backends/CairoBackend/src/ImageSurface.cs b/Backends/CairoBackend/src/ImageSurface.cs index 2cfec61c..8870366a 100644 --- a/Backends/CairoBackend/src/ImageSurface.cs +++ b/Backends/CairoBackend/src/ImageSurface.cs @@ -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) + { + + } } } diff --git a/Backends/CairoBackend/src/Surface.cs b/Backends/CairoBackend/src/Surface.cs index 80435b5e..1f44d3ab 100644 --- a/Backends/CairoBackend/src/Surface.cs +++ b/Backends/CairoBackend/src/Surface.cs @@ -41,6 +41,8 @@ namespace Crow.CairoBackend { public class Surface : ISurface { internal IntPtr handle = IntPtr.Zero; + public IntPtr Handle => handle; + protected Surface() { diff --git a/Backends/VkvgBackend/Crow.VkvgBackend.csproj b/Backends/VkvgBackend/Crow.VkvgBackend.csproj index 13386d36..b6fbb004 100644 --- a/Backends/VkvgBackend/Crow.VkvgBackend.csproj +++ b/Backends/VkvgBackend/Crow.VkvgBackend.csproj @@ -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> diff --git a/Backends/VkvgBackend/src/DefaultBackend.cs b/Backends/VkvgBackend/src/DefaultBackend.cs index 8521d503..eeed4d0a 100644 --- a/Backends/VkvgBackend/src/DefaultBackend.cs +++ b/Backends/VkvgBackend/src/DefaultBackend.cs @@ -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 @@ -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} diff --git a/Crow/Default.style b/Crow/Default.style index 066eb21b..6e14f802 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -346,6 +346,6 @@ MessageBox, Popper, Slider, Spinner, TextInheritBox, NumericControl { Margin="0"; BubbleEvents="MouseWheel|Keyboard"; } -TemplatedControl, GenericStack { +TemplatedControl, GenericStack, Image { CacheEnabled="true"; } diff --git a/Crow/Icons/expandable.svg b/Crow/Icons/expandable.svg index e2bffbc5..a689ee98 100644 --- a/Crow/Icons/expandable.svg +++ b/Crow/Icons/expandable.svg @@ -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"/> diff --git a/Crow/Icons/radiobutton.svg b/Crow/Icons/radiobutton.svg index b102b3e1..49a0c328 100644 --- a/Crow/Icons/radiobutton.svg +++ b/Crow/Icons/radiobutton.svg @@ -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 diff --git a/Crow/Templates/ColorPicker2.template b/Crow/Templates/ColorPicker2.template index b4355517..34fe51fd 100644 --- a/Crow/Templates/ColorPicker2.template +++ b/Crow/Templates/ColorPicker2.template @@ -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"/> diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs index 813e8dec..7cf8983c 100644 --- a/Crow/src/Fill/BmpPicture.cs +++ b/Crow/src/Fill/BmpPicture.cs @@ -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 (); } diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs index e6e54aff..e2c73bbd 100644 --- a/Crow/src/Fill/SvgPicture.cs +++ b/Crow/src/Fill/SvgPicture.cs @@ -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); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 1505ed12..507bed69 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -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); } } diff --git a/Crow/src/LayoutingQueueItem.cs b/Crow/src/LayoutingQueueItem.cs index 64549c08..9ce5895b 100644 --- a/Crow/src/LayoutingQueueItem.cs +++ b/Crow/src/LayoutingQueueItem.cs @@ -3,6 +3,7 @@ // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; +using Drawing2D; namespace Crow { diff --git a/Crow/src/Widgets/HueSelector.cs b/Crow/src/Widgets/HueSelector.cs index d4d23f36..ae93626d 100644 --- a/Crow/src/Widgets/HueSelector.cs +++ b/Crow/src/Widgets/HueSelector.cs @@ -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) diff --git a/Crow/src/Widgets/Image.cs b/Crow/src/Widgets/Image.cs index 30908847..2c102c22 100644 --- a/Crow/src/Widgets/Image.cs +++ b/Crow/src/Widgets/Image.cs @@ -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 { diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 39ce20e5..c3f08fd4 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -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)); diff --git a/Directory.Build.props b/Directory.Build.props index d674fd6e..37361807 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -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> diff --git a/Drawing2D/src/ISurface.cs b/Drawing2D/src/ISurface.cs index 198f7539..292334c4 100644 --- a/Drawing2D/src/ISurface.cs +++ b/Drawing2D/src/ISurface.cs @@ -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 index 00000000..178d4537 --- /dev/null +++ b/Samples/AnimTest/AnimTest.csproj @@ -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 index 00000000..02faef76 --- /dev/null +++ b/Samples/AnimTest/Animation/AngleAnimation.cs @@ -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 index 00000000..6ca7af8c --- /dev/null +++ b/Samples/AnimTest/Animation/Animation.cs @@ -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 index 00000000..d5c784c3 --- /dev/null +++ b/Samples/AnimTest/Animation/AnimationTemplate.cs @@ -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 index 00000000..a3126e3e --- /dev/null +++ b/Samples/AnimTest/Animation/BezierPath.cs @@ -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 index 00000000..a7e4bf7c --- /dev/null +++ b/Samples/AnimTest/Animation/FloatAnimation.cs @@ -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 index 00000000..1d5c9193 --- /dev/null +++ b/Samples/AnimTest/Animation/FloatAnimation2.cs @@ -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 index 00000000..35a3009f --- /dev/null +++ b/Samples/AnimTest/Animation/MathHelper.cs @@ -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 index 00000000..1f04e090 --- /dev/null +++ b/Samples/AnimTest/Animation/Path.cs @@ -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 index 00000000..54c30e9a --- /dev/null +++ b/Samples/AnimTest/Animation/PathAnimation.cs @@ -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 index 00000000..37a089bd --- /dev/null +++ b/Samples/AnimTest/Animation/ShakeAnimation.cs @@ -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 index 00000000..8f9ca4e6 --- /dev/null +++ b/Samples/AnimTest/MiscUtil/ExpressionUtil.cs @@ -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 index 00000000..7bb8790a --- /dev/null +++ b/Samples/AnimTest/MiscUtil/NullOp.cs @@ -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 index 00000000..ee1168cf --- /dev/null +++ b/Samples/AnimTest/MiscUtil/Operator.cs @@ -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<T> + /// </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<TInner> 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<TInner> 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<TInner> 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 (&) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (<) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (>=) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (<=) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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<TInner> 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<T>"/> + /// <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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TValue,TResult>"/> + 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<TInner>) - 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<TInner> 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<TInner> 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<TInner> 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 (&) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (<) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (>=) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 (<=) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> 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 index 00000000..6dd0b525 --- /dev/null +++ b/Samples/AnimTest/main.cs @@ -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 index 00000000..f3fa6c6f --- /dev/null +++ b/Samples/AnimTest/ui/anim.crow @@ -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 index 00000000..a0881df6 --- /dev/null +++ b/Samples/AnimTest/ui/background.crow @@ -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 Binary files differnew file mode 100644 index 00000000..50ebfbf0 --- /dev/null +++ b/Samples/AnimTest/ui/background.jpg diff --git a/Samples/BasicTests/BasicTests.cs b/Samples/BasicTests/BasicTests.cs index 130171aa..7506d27d 100644 --- a/Samples/BasicTests/BasicTests.cs +++ b/Samples/BasicTests/BasicTests.cs @@ -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 (); diff --git a/Samples/HelloWorld/main.cs b/Samples/HelloWorld/main.cs index fd70a2b9..fdf75a8d 100644 --- a/Samples/HelloWorld/main.cs +++ b/Samples/HelloWorld/main.cs @@ -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'/>"); diff --git a/Samples/HelloWorld/ui/helloworld.crow b/Samples/HelloWorld/ui/helloworld.crow index d1c30b0f..874667c2 100644 --- a/Samples/HelloWorld/ui/helloworld.crow +++ b/Samples/HelloWorld/ui/helloworld.crow @@ -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/>--> diff --git a/Samples/ShowCase/ShowCase.cs b/Samples/ShowCase/ShowCase.cs index 958a6b13..92a71e9f 100644 --- a/Samples/ShowCase/ShowCase.cs +++ b/Samples/ShowCase/ShowCase.cs @@ -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 (); diff --git a/Samples/ShowCase/ui/showcase.crow b/Samples/ShowCase/ui/showcase.crow index 298c8797..b1d6fb88 100644 --- a/Samples/ShowCase/ui/showcase.crow +++ b/Samples/ShowCase/ui/showcase.crow @@ -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}" /> @@ -160,11 +160,11 @@ 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 index 00000000..7a935032 --- /dev/null +++ b/Samples/common/ui/Interfaces/Experimental/svg.crow @@ -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 index 00000000..32a5b83f --- /dev/null +++ b/Samples/common/ui/Interfaces/TemplatedContainer/buttons.crow @@ -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 |