changeset 4:381095ad0a69

Implab.Fx: implemented animation object Implab.Fx: implemented transparency animation helper
author cin
date Tue, 17 Sep 2013 04:27:30 +0400
parents 1e9583086e99
children f2559580b481
files .hgignore Implab.Fx/Animation.cs Implab.Fx/AnimationHelpers.cs Implab.Fx/ControlHelpers.cs Implab.Fx/Implab.Fx.csproj Implab.Fx/PromiseHelpers.cs Implab.Test/AsyncTests.cs Implab.sln Implab.suo Implab/Implab.csproj
diffstat 10 files changed, 319 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Sep 13 12:54:28 2013 +0400
+++ b/.hgignore	Tue Sep 17 04:27:30 2013 +0400
@@ -7,3 +7,4 @@
 Implab/obj/
 TestResults/
 Implab.Fx/obj/
+Implab.Fx/bin/
--- a/Implab.Fx/Animation.cs	Fri Sep 13 12:54:28 2013 +0400
+++ b/Implab.Fx/Animation.cs	Tue Sep 17 04:27:30 2013 +0400
@@ -2,14 +2,96 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Timers;
+using System.ComponentModel;
+using System.Diagnostics;
 
 namespace Implab.Fx
 {
-    public class Animation
+    public delegate void AnimationStep<T>(T target, int elapsed, int duration);
+
+    public class Animation<TArg> where TArg: class
     {
         int m_duration;
-        int m_fps;
+        int m_delay;
+        int m_elapsed;
+        int m_prevTicks;
+        TArg m_arg;
+        ISynchronizeInvoke m_syncronizationObject;
+
+        public event AnimationStep<TArg> Step;
+
+        Promise<TArg> m_promise;
+
+        public Animation(TArg target, int duration, int delay)
+        {
+            if (duration <= 0)
+                throw new ArgumentOutOfRangeException("duration");
+            if (delay <= 0)
+                throw new ArgumentOutOfRangeException("delay");
+
+            m_arg = target;
+            m_syncronizationObject = target as ISynchronizeInvoke;
+            m_duration = duration;
+            m_delay = delay;
+            m_promise = new Promise<TArg>();
+        }
+
+        public Animation(TArg target)
+            : this(target, 500, 30)
+        {
+        }
+
+        public TArg Traget
+        {
+            get { return m_arg; }
+        }
 
+        public Promise<TArg> Play()
+        {
+            var timer = new Timer(m_delay);
+            
+            timer.AutoReset = true;
+            timer.SynchronizingObject = m_syncronizationObject;
+            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
 
+            m_prevTicks = Environment.TickCount;
+
+            timer.Start();
+
+            return m_promise;
+        }
+
+        void timer_Elapsed(object sender, ElapsedEventArgs args)
+        {
+            var timer = sender as Timer;
+
+            var dt = Environment.TickCount - m_prevTicks;
+            m_prevTicks = Environment.TickCount;
+
+            m_elapsed += dt;
+
+            if (m_elapsed > m_duration)
+                m_elapsed = m_duration;
+
+            try
+            {
+                var handler = Step;
+                if (handler != null)
+                    handler(m_arg, m_elapsed, m_duration);
+            }
+            catch (Exception e)
+            {
+                Trace.TraceError(e.ToString());
+            }
+
+            if (m_elapsed < m_duration)
+                timer.Start();
+            else
+            {
+                timer.Dispose();
+                m_promise.Resolve(m_arg);
+            }
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Fx/AnimationHelpers.cs	Tue Sep 17 04:27:30 2013 +0400
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Diagnostics;
+
+namespace Implab.Fx
+{
+    public static class AnimationHelpers
+    {
+        public static Animation<TTarget> AnimateProperty<TTarget,TVal>(this Animation<TTarget> animation, Action<TTarget,TVal> setter, Func<TTarget,TVal> getter, TVal newValue, Func<TVal,TVal,int,int,TVal> fx) where TTarget: class
+        {
+            if (animation == null)
+                throw new ArgumentNullException("animation");
+
+            TVal oldValue = getter(animation.Traget);
+
+            animation.Step += (target, elaped, duration) =>
+            {
+                var value = fx(oldValue, newValue, elaped, duration);
+                setter(target, value);
+            };
+
+            return animation;
+        }
+
+        public static Animation<Form> AnimateTransparency(this Form ctl, float newValue)
+        {
+            var anim = new Animation<Form>(ctl);
+
+            anim.AnimateProperty(
+                (target, value) => target.Opacity = value,
+                target => target.Opacity,
+                newValue,
+                (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
+            );
+            return anim;
+        }
+    }
+}
--- a/Implab.Fx/ControlHelpers.cs	Fri Sep 13 12:54:28 2013 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Windows.Forms;
-
-namespace Implab.Fx
-{
-    public static class ControlHelpers
-    {
-        /// <summary>
-        /// Переключает обработку обещания в поток указанного элемента управления.
-        /// </summary>
-        /// <typeparam name="T">Тип результата обещания</typeparam>
-        /// <param name="that">Исходное обещание</param>
-        /// <param name="ctl">Элемент управления</param>
-        /// <returns>Новое обещание, обработчики которого будут выполнены в потоке элемента управления.</returns>
-        /// <exception cref="ArgumentNullException">Параметр не может быть <c>null</c>.</exception>
-        /// <example>
-        /// client
-        ///     .Get("description.txt") // returns a promise
-        ///     .DirectToControl(m_ctl) // handle the promise in the thread of the control
-        ///     .Then(
-        ///         description => m_ctl.Text = description // now it's safe
-        ///     )
-        /// </example>
-        public static Promise<T> DirectToControl<T>(this Promise<T> that, Control ctl)
-        {
-            if (that == null)
-                throw new ArgumentNullException("that");
-            if (ctl == null)
-                throw new ArgumentNullException("ctl");
-
-            var directed = new Promise<T>();
-
-            that.Then(
-                res =>
-                {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action<T>(directed.Resolve),res);
-                    else
-                        directed.Resolve(res);
-                },
-                err =>
-                {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action<Exception>(directed.Reject), err);
-                    else
-                        directed.Reject(err);
-                }
-            );
-
-            return directed;
-        }
-    }
-}
--- a/Implab.Fx/Implab.Fx.csproj	Fri Sep 13 12:54:28 2013 +0400
+++ b/Implab.Fx/Implab.Fx.csproj	Tue Sep 17 04:27:30 2013 +0400
@@ -41,7 +41,9 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="ControlHelpers.cs" />
+    <Compile Include="Animation.cs" />
+    <Compile Include="AnimationHelpers.cs" />
+    <Compile Include="PromiseHelpers.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Fx/PromiseHelpers.cs	Tue Sep 17 04:27:30 2013 +0400
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Threading;
+
+namespace Implab.Fx
+{
+    public static class PromiseHelpers
+    {
+        /// <summary>
+        /// Перенаправляет обработку обещания в поток указанного элемента управления.
+        /// </summary>
+        /// <typeparam name="T">Тип результата обещания</typeparam>
+        /// <param name="that">Исходное обещание</param>
+        /// <param name="ctl">Элемент управления</param>
+        /// <returns>Новое обещание, обработчики которого будут выполнены в потоке элемента управления.</returns>
+        /// <exception cref="ArgumentNullException">Параметр не может быть <c>null</c>.</exception>
+        /// <example>
+        /// client
+        ///     .Get("description.txt") // returns a promise
+        ///     .DirectToControl(m_ctl) // handle the promise in the thread of the control
+        ///     .Then(
+        ///         description => m_ctl.Text = description // now it's safe
+        ///     )
+        /// </example>
+        public static Promise<T> DispatchToControl<T>(this Promise<T> that, Control ctl)
+        {
+            if (that == null)
+                throw new ArgumentNullException("that");
+            if (ctl == null)
+                throw new ArgumentNullException("ctl");
+
+            var directed = new Promise<T>();
+
+            that.Then(
+                res =>
+                {
+                    if (ctl.InvokeRequired)
+                        ctl.Invoke(new Action<T>(directed.Resolve), res);
+                    else
+                        directed.Resolve(res);
+                },
+                err =>
+                {
+                    if (ctl.InvokeRequired)
+                        ctl.Invoke(new Action<Exception>(directed.Reject), err);
+                    else
+                        directed.Reject(err);
+                }
+            );
+
+            return directed;
+        }
+
+        /// <summary>
+        /// Направляет обработку обещания в текущий поток, если у него существует контекст синхронизации.
+        /// </summary>
+        /// <typeparam name="T">Тип результата обещания.</typeparam>
+        /// <param name="that">Обещание которое нужно обработать в текущем потоке.</param>
+        /// <returns>Перенаправленное обещание.</returns>
+        public static Promise<T> DispatchToCurrentThread<T>(this Promise<T> that)
+        {
+            var sync = SynchronizationContext.Current;
+            if (sync == null)
+                throw new InvalidOperationException("The current thread doesn't have a syncronization context");
+            return DispatchToSyncContext(that, sync);
+        }
+
+        /// <summary>
+        /// Направляет обработку обещания в указанный контекст синхронизации.
+        /// </summary>
+        /// <typeparam name="T">Тип результата обещания.</typeparam>
+        /// <param name="that">Обещание, которое требуется обработать в указанном контексте синхронизации.</param>
+        /// <param name="sync">Контекст синхронизации в который будет направлено обещание.</param>
+        /// <returns>Новое обещание, которое будет обрабатываться в указанном контексте.</returns>
+        public static Promise<T> DispatchToSyncContext<T>(this Promise<T> that, SynchronizationContext sync)
+        {
+            if (that == null)
+                throw new ArgumentNullException("that");
+            if (sync == null)
+                throw new ArgumentNullException("sync");
+
+            var d = new Promise<T>();
+
+            that.Then(
+                res => sync.Post(state => d.Resolve(res), null),
+                err => sync.Post(state => d.Reject(err), null)
+            );
+
+            return d;
+        }
+    }
+}
--- a/Implab.Test/AsyncTests.cs	Fri Sep 13 12:54:28 2013 +0400
+++ b/Implab.Test/AsyncTests.cs	Tue Sep 17 04:27:30 2013 +0400
@@ -1,101 +1,101 @@
-using System;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Implab;
-using System.Reflection;
-using System.Threading;
-
-namespace Implab.Tests
-{
-	[TestClass]
-	public class AsyncTests
-	{
-		[TestMethod]
-		public void ResolveTest ()
-		{
-			int res = -1;
-			var p = new Promise<int> ();
-			p.Then (x => res = x);
-			p.Resolve (100);
-
-			Assert.AreEqual (res, 100);
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Implab;
+using System.Reflection;
+using System.Threading;
+
+namespace Implab.Tests
+{
+	[TestClass]
+	public class AsyncTests
+	{
+		[TestMethod]
+		public void ResolveTest ()
+		{
+			int res = -1;
+			var p = new Promise<int> ();
+			p.Then (x => res = x);
+			p.Resolve (100);
+
+			Assert.AreEqual (res, 100);
 		}
 
-        [TestMethod]
-		public void RejectTest ()
-		{
-			int res = -1;
-			Exception err = null;
-
-			var p = new Promise<int> ();
-			p.Then (x => res = x, e => err = e);
-			p.Reject (new ApplicationException ("error"));
-
-			Assert.AreEqual (res, -1);
-			Assert.AreEqual (err.Message, "error");
-
+        [TestMethod]
+		public void RejectTest ()
+		{
+			int res = -1;
+			Exception err = null;
+
+			var p = new Promise<int> ();
+			p.Then (x => res = x, e => err = e);
+			p.Reject (new ApplicationException ("error"));
+
+			Assert.AreEqual (res, -1);
+			Assert.AreEqual (err.Message, "error");
+
 		}
 
-        [TestMethod]
-		public void JoinSuccessTest ()
-		{
-			var p = new Promise<int> ();
-			p.Resolve (100);
-			Assert.AreEqual (p.Join (), 100);
+        [TestMethod]
+		public void JoinSuccessTest ()
+		{
+			var p = new Promise<int> ();
+			p.Resolve (100);
+			Assert.AreEqual (p.Join (), 100);
 		}
 
-        [TestMethod]
-		public void JoinFailTest ()
-		{
-			var p = new Promise<int> ();
-			p.Reject (new ApplicationException ("failed"));
-
-			try {
-				p.Join ();
-				throw new ApplicationException ("WRONG!");
-			} catch (TargetInvocationException err) {
-				Assert.AreEqual (err.InnerException.Message, "failed");
-			} catch {
-				Assert.Fail ("Got wrong excaption");
-			}
+        [TestMethod]
+		public void JoinFailTest ()
+		{
+			var p = new Promise<int> ();
+			p.Reject (new ApplicationException ("failed"));
+
+			try {
+				p.Join ();
+				throw new ApplicationException ("WRONG!");
+			} catch (TargetInvocationException err) {
+				Assert.AreEqual (err.InnerException.Message, "failed");
+			} catch {
+				Assert.Fail ("Got wrong excaption");
+			}
 		}
 
-        [TestMethod]
-		public void MapTest ()
-		{
-			var p = new Promise<int> ();
-
-			var p2 = p.Map (x => x.ToString ());
-			p.Resolve (100);
-
-			Assert.AreEqual (p2.Join (), "100");
+        [TestMethod]
+		public void MapTest ()
+		{
+			var p = new Promise<int> ();
+
+			var p2 = p.Map (x => x.ToString ());
+			p.Resolve (100);
+
+			Assert.AreEqual (p2.Join (), "100");
 		}
 
-        [TestMethod]
-		public void ChainTest ()
-		{
-			var p1 = new Promise<int> ();
-
-			var p3 = p1.Chain (x => {
-				var p2 = new Promise<string> ();
-				p2.Resolve (x.ToString ());
-				return p2;
-			});
-
-			p1.Resolve (100);
-
-			Assert.AreEqual (p3.Join (), "100");
+        [TestMethod]
+		public void ChainTest ()
+		{
+			var p1 = new Promise<int> ();
+
+			var p3 = p1.Chain (x => {
+				var p2 = new Promise<string> ();
+				p2.Resolve (x.ToString ());
+				return p2;
+			});
+
+			p1.Resolve (100);
+
+			Assert.AreEqual (p3.Join (), "100");
 		}
 
-        [TestMethod]
-		public void PoolTest ()
-		{
-			var pid = Thread.CurrentThread.ManagedThreadId;
-			var p = AsyncPool.Invoke (() => {
-				return Thread.CurrentThread.ManagedThreadId;
-			});
-
-			Assert.AreNotEqual (pid, p.Join ());
-		}
-	}
-}
-
+        [TestMethod]
+		public void PoolTest ()
+		{
+			var pid = Thread.CurrentThread.ManagedThreadId;
+			var p = AsyncPool.Invoke (() => {
+				return Thread.CurrentThread.ManagedThreadId;
+			});
+
+			Assert.AreNotEqual (pid, p.Join ());
+		}
+	}
+}
+
--- a/Implab.sln	Fri Sep 13 12:54:28 2013 +0400
+++ b/Implab.sln	Tue Sep 17 04:27:30 2013 +0400
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}"
 	ProjectSection(SolutionItems) = preProject
@@ -23,10 +23,10 @@
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
 		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
Binary file Implab.suo has changed
--- a/Implab/Implab.csproj	Fri Sep 13 12:54:28 2013 +0400
+++ b/Implab/Implab.csproj	Tue Sep 17 04:27:30 2013 +0400
@@ -5,7 +5,7 @@
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProductVersion>10.0.0</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</ProjectGuid>
+    <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
     <OutputType>Library</OutputType>
     <RootNamespace>Implab</RootNamespace>
     <AssemblyName>Implab</AssemblyName>