Browse Source

Run animation on UI thread.

pull/16/head
Steven Kirk 11 years ago
parent
commit
1c9cc11501
  1. 1
      Perspex.Base/Perspex.Base.csproj
  2. 2
      Perspex.Base/Platform/IPlatformThreadingInterface.cs
  3. 19
      Perspex.Base/Threading/DispatcherTimer.cs
  4. 26
      Perspex.Base/Threading/MainLoop.cs
  5. 31
      Perspex.Base/Threading/PerspexScheduler.cs
  6. 2
      Perspex.Controls/Presenters/ScrollContentPresenter.cs
  7. 3
      TestApplication/Program.cs
  8. 3
      Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  9. 16
      Windows/Perspex.Win32/Win32Platform.cs

1
Perspex.Base/Perspex.Base.csproj

@ -54,6 +54,7 @@
<Compile Include="Threading\DispatcherPriority.cs" />
<Compile Include="Threading\DispatcherTimer.cs" />
<Compile Include="Threading\MainLoop.cs" />
<Compile Include="Threading\PerspexScheduler.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Splat">

2
Perspex.Base/Platform/IPlatformThreadingInterface.cs

@ -16,6 +16,8 @@ namespace Perspex.Platform
/// </summary>
public interface IPlatformThreadingInterface
{
bool HasMessages();
/// <summary>
/// Process a single message from the windowing system, blocking until one is available.
/// </summary>

19
Perspex.Base/Threading/DispatcherTimer.cs

@ -7,6 +7,7 @@
namespace Perspex.Threading
{
using System;
using System.Reactive.Disposables;
using Perspex.Platform;
using Splat;
@ -105,6 +106,24 @@ namespace Perspex.Threading
set;
}
public static IDisposable Run(Func<bool> action, TimeSpan interval, DispatcherPriority priority = DispatcherPriority.Normal)
{
var timer = new DispatcherTimer(priority);
timer.Interval = interval;
timer.Tick += (s, e) =>
{
if (!action())
{
timer.Stop();
}
};
timer.Start();
return Disposable.Create(() => timer.Stop());
}
public void Start()
{
if (!this.IsEnabled)

26
Perspex.Base/Threading/MainLoop.cs

@ -30,14 +30,21 @@ namespace Perspex.Win32.Threading
{
while (!cancellationToken.IsCancellationRequested)
{
Job job;
Job job = null;
// TODO: Dispatch windows messages in preference to lower priority jobs.
while (this.queue.Count > 0)
while (job != null || this.queue.Count > 0)
{
lock (this.queue)
if (job == null)
{
job = this.queue.Dequeue();
lock (this.queue)
{
job = this.queue.Dequeue();
}
}
if (job.Priority < DispatcherPriority.Input && platform.HasMessages())
{
break;
}
try
@ -49,6 +56,8 @@ namespace Perspex.Win32.Threading
{
job.TaskCompletionSource.SetException(e);
}
job = null;
}
platform.ProcessMessage();
@ -57,7 +66,7 @@ namespace Perspex.Win32.Threading
public Task InvokeAsync(Action action, DispatcherPriority priority)
{
var job = new Job(action);
var job = new Job(action, priority);
lock (this.queue)
{
@ -70,14 +79,17 @@ namespace Perspex.Win32.Threading
private class Job
{
public Job(Action action)
public Job(Action action, DispatcherPriority priority)
{
this.Action = action;
this.Priority = priority;
this.TaskCompletionSource = new TaskCompletionSource<object>();
}
public Action Action { get; private set; }
public DispatcherPriority Priority { get; private set; }
public TaskCompletionSource<object> TaskCompletionSource { get; set; }
}
}

31
Perspex.Base/Threading/PerspexScheduler.cs

@ -0,0 +1,31 @@
// -----------------------------------------------------------------------
// <copyright file="PerspexScheduler.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Threading
{
using System;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
public class PerspexScheduler : LocalScheduler
{
private static readonly PerspexScheduler instance = new PerspexScheduler();
public static PerspexScheduler Instance
{
get { return instance; }
}
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
return DispatcherTimer.Run(() =>
{
action(this, state);
return false;
}, dueTime);
}
}
}

2
Perspex.Controls/Presenters/ScrollContentPresenter.cs

@ -8,8 +8,6 @@ namespace Perspex.Controls.Presenters
{
using System;
using System.Linq;
using System.Reactive.Disposables;
using Perspex.Controls.Primitives;
using Perspex.Input;
using Perspex.Layout;

3
TestApplication/Program.cs

@ -8,6 +8,7 @@ using Perspex.Diagnostics;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Media.Imaging;
using Perspex.Threading;
#if PERSPEX_GTK
using Perspex.Gtk;
#else
@ -462,7 +463,7 @@ namespace TestApplication
},
};
Observable.Interval(TimeSpan.FromMilliseconds(10))
Observable.Interval(TimeSpan.FromMilliseconds(10), PerspexScheduler.Instance)
.Subscribe(x =>
{
((RotateTransform)rect1.RenderTransform).Angle = x;

3
Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -511,6 +511,9 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll")]
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

16
Windows/Perspex.Win32/Win32Platform.cs

@ -57,6 +57,12 @@ namespace Perspex.Win32
locator.Register(() => instance, typeof(IPlatformThreadingInterface));
}
public bool HasMessages()
{
UnmanagedMethods.MSG msg;
return UnmanagedMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
public void ProcessMessage()
{
UnmanagedMethods.MSG msg;
@ -88,11 +94,11 @@ namespace Perspex.Win32
public void Wake()
{
UnmanagedMethods.PostMessage(
this.hwnd,
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
IntPtr.Zero,
IntPtr.Zero);
//UnmanagedMethods.PostMessage(
// this.hwnd,
// (int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
// IntPtr.Zero,
// IntPtr.Zero);
}
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]

Loading…
Cancel
Save