Browse Source

Make TextBox caret flash.

pull/4/head
Steven Kirk 12 years ago
parent
commit
7dc715d27e
  1. 5
      Perspex.Direct2D1/Direct2D1Platform.cs
  2. 5
      Perspex.Windows/Perspex.Windows.csproj
  3. 637
      Perspex.Windows/Threading/Dispatcher.cs
  4. 371
      Perspex.Windows/Threading/WindowsDispatcher.cs
  5. 7
      Perspex.Windows/Window.cs
  6. 58
      Perspex.Windows/WindowsPlatform.cs
  7. 2
      Perspex/Controls/TextBox.cs
  8. 27
      Perspex/Controls/TextBoxView.cs
  9. 2
      Perspex/Media/FormattedText.cs
  10. 2
      Perspex/Media/Imaging/Bitmap.cs
  11. 2
      Perspex/Media/Imaging/RenderTargetBitmap.cs
  12. 2
      Perspex/Media/RectangleGeometry.cs
  13. 2
      Perspex/Media/StreamGeometry.cs
  14. 7
      Perspex/Perspex.csproj
  15. 3
      Perspex/Platform/IPlatformRenderInterface.cs
  16. 20
      Perspex/Platform/IPlatformThreadingInterface.cs
  17. 102
      Perspex/Threading/Dispatcher.cs
  18. 8
      Perspex/Threading/DispatcherFrame.cs
  19. 18
      Perspex/Threading/DispatcherOperation.cs
  20. 140
      Perspex/Threading/DispatcherTimer.cs
  21. 2
      TestApplication/App.cs
  22. 1
      TestApplication/Program.cs

5
Perspex.Direct2D1/Direct2D1Platform.cs

@ -9,9 +9,10 @@ namespace Perspex.Direct2D1
using System;
using Perspex.Direct2D1.Media;
using Perspex.Platform;
using Perspex.Threading;
using Splat;
public class Direct2D1Platform : IPlatformInterface
public class Direct2D1Platform : IPlatformRenderInterface
{
private static Direct2D1Platform instance = new Direct2D1Platform();
@ -26,7 +27,7 @@ namespace Perspex.Direct2D1
public static void Initialize()
{
var locator = Locator.CurrentMutable;
locator.Register(() => instance, typeof(IPlatformInterface));
locator.Register(() => instance, typeof(IPlatformRenderInterface));
locator.Register(() => d2d1Factory, typeof(SharpDX.Direct2D1.Factory));
locator.Register(() => dwFactory, typeof(SharpDX.DirectWrite.Factory));
locator.Register(() => imagingFactory, typeof(SharpDX.WIC.ImagingFactory));

5
Perspex.Windows/Perspex.Windows.csproj

@ -59,12 +59,11 @@
<Compile Include="Input\KeyInterop.cs" />
<Compile Include="Input\WindowsKeyboardDevice.cs" />
<Compile Include="Input\WindowsMouseDevice.cs" />
<Compile Include="Threading\Dispatcher.cs" />
<Compile Include="Threading\DispatcherFrame.cs" />
<Compile Include="Threading\DispatcherOperation.cs" />
<Compile Include="Threading\WindowsDispatcher.cs" />
<Compile Include="Interop\UnmanagedMethods.cs" />
<Compile Include="Window.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowsPlatform.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Perspex\Perspex.csproj">

637
Perspex.Windows/Threading/Dispatcher.cs

@ -1,637 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="Dispatcher.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Windows.Threading
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security;
using System.Threading;
using Perspex.Windows.Interop;
public enum DispatcherPriority
{
Invalid = -1,
Inactive = 0,
SystemIdle = 1,
ApplicationIdle = 2,
ContextIdle = 3,
Background = 4,
Input = 5,
Loaded = 6,
Render = 7,
DataBind = 8,
Normal = 9,
Send = 10,
}
[Flags]
internal enum Flags
{
ShutdownStarted = 1,
Shutdown = 2,
Disabled = 4
}
public sealed class Dispatcher
{
private const int TopPriority = (int)DispatcherPriority.Send;
private static Dictionary<Thread, Dispatcher> dispatchers = new Dictionary<Thread, Dispatcher>();
private static object olock = new object();
private static DispatcherFrame mainExecutionFrame = new DispatcherFrame();
private Thread baseThread;
private PokableQueue[] priorityQueues = new PokableQueue[TopPriority + 1];
private Flags flags;
private int queueBits;
private DispatcherFrame currentFrame;
private Dispatcher(Thread t)
{
this.baseThread = t;
for (int i = 1; i <= (int)DispatcherPriority.Send; i++)
{
this.priorityQueues[i] = new PokableQueue();
}
}
public event EventHandler ShutdownStarted;
public event EventHandler ShutdownFinished;
public static Dispatcher CurrentDispatcher
{
get
{
lock (olock)
{
Thread t = Thread.CurrentThread;
Dispatcher dis = FromThread(t);
if (dis != null)
{
return dis;
}
dis = new Dispatcher(t);
dispatchers[t] = dis;
return dis;
}
}
}
public Thread Thread
{
get
{
return this.baseThread;
}
}
public bool HasShutdownStarted
{
get
{
return (this.flags & Flags.ShutdownStarted) != 0;
}
}
public bool HasShutdownFinished
{
get
{
return (this.flags & Flags.Shutdown) != 0;
}
}
[SecurityCritical]
public static void ExitAllFrames()
{
Dispatcher dis = CurrentDispatcher;
for (DispatcherFrame frame = dis.currentFrame; frame != null; frame = frame.ParentFrame)
{
if (frame.ExitOnRequest)
{
frame.Continue = false;
}
else
{
break;
}
}
}
public static Dispatcher FromThread(Thread thread)
{
Dispatcher dis;
if (dispatchers.TryGetValue(thread, out dis))
{
return dis;
}
return null;
}
[SecurityCritical]
public static void PushFrame(DispatcherFrame frame)
{
if (frame == null)
{
throw new ArgumentNullException("frame");
}
Dispatcher dis = CurrentDispatcher;
if (dis.HasShutdownFinished)
{
throw new InvalidOperationException("The Dispatcher has shut down");
}
if (frame.Running != null)
{
throw new InvalidOperationException("Frame is already running on a different dispatcher");
}
if ((dis.flags & Flags.Disabled) != 0)
{
throw new InvalidOperationException("Dispatcher processing has been disabled");
}
frame.ParentFrame = dis.currentFrame;
dis.currentFrame = frame;
frame.Running = dis;
dis.RunFrame(frame);
}
[SecurityCritical]
public static void Run()
{
PushFrame(mainExecutionFrame);
}
public static void ValidatePriority(DispatcherPriority priority, string parameterName)
{
if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException(parameterName);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool CheckAccess()
{
return Thread.CurrentThread == this.baseThread;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public void VerifyAccess()
{
if (Thread.CurrentThread != this.baseThread)
{
throw new InvalidOperationException("Invoked from a different thread");
}
}
public DispatcherOperation BeginInvoke(Delegate method, params object[] args)
{
throw new NotImplementedException();
}
public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
{
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation op = new DispatcherOperation(this, priority, method);
this.Queue(priority, op);
return op;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation op = new DispatcherOperation(this, priority, method, arg);
this.Queue(priority, op);
return op;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method, object arg, params object[] args)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation op = new DispatcherOperation(this, priority, method, arg, args);
this.Queue(priority, op);
return op;
}
public object Invoke(Delegate method, params object[] args)
{
throw new NotImplementedException();
}
public object Invoke(Delegate method, TimeSpan timeout, params object[] args)
{
throw new NotImplementedException();
}
public object Invoke(Delegate method, TimeSpan timeout, DispatcherPriority priority, params object[] args)
{
throw new NotImplementedException();
}
public object Invoke(Delegate method, DispatcherPriority priority, params object[] args)
{
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation op = new DispatcherOperation(this, priority, method);
this.Queue(priority, op);
PushFrame(new DispatcherFrame());
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method, object arg)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
this.Queue(priority, new DispatcherOperation(this, priority, method, arg));
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, Delegate method, object arg, params object[] args)
{
if (priority < 0 || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
this.Queue(priority, new DispatcherOperation(this, priority, method, arg, args));
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method)
{
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg)
{
throw new NotImplementedException();
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object Invoke(DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg, params object[] args)
{
throw new NotImplementedException();
}
[SecurityCritical]
public void InvokeShutdown()
{
this.flags |= Flags.ShutdownStarted;
UnmanagedMethods.PostMessage(
IntPtr.Zero,
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
IntPtr.Zero,
IntPtr.Zero);
}
[SecurityCritical]
public void BeginInvokeShutdown(DispatcherPriority priority)
{
throw new NotImplementedException();
}
internal void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority)
{
int oldp = (int)oldpriority;
PokableQueue q = this.priorityQueues[oldp];
lock (q)
{
q.Remove(op);
}
this.Queue(op.Priority, op);
}
private void Queue(DispatcherPriority priority, DispatcherOperation x)
{
int p = (int)priority;
PokableQueue q = this.priorityQueues[p];
lock (q)
{
int flag = 1 << p;
q.Enqueue(x);
this.queueBits |= flag;
}
if (Thread.CurrentThread != this.baseThread)
{
UnmanagedMethods.PostMessage(
IntPtr.Zero,
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
IntPtr.Zero,
IntPtr.Zero);
}
}
private void PerformShutdown()
{
EventHandler h;
h = this.ShutdownStarted;
if (h != null)
{
h(this, new EventArgs());
}
this.flags |= Flags.Shutdown;
h = this.ShutdownFinished;
if (h != null)
{
h(this, new EventArgs());
}
this.priorityQueues = null;
}
private void RunFrame(DispatcherFrame frame)
{
do
{
while (this.queueBits != 0)
{
for (int i = TopPriority; i > 0 && this.queueBits != 0; i--)
{
int currentBit = this.queueBits & (1 << i);
if (currentBit != 0)
{
PokableQueue q = this.priorityQueues[i];
do
{
DispatcherOperation task;
lock (q)
{
task = (DispatcherOperation)q.Dequeue();
}
task.Invoke();
if (!frame.Continue)
{
return;
}
if (this.HasShutdownStarted)
{
this.PerformShutdown();
return;
}
lock (q)
{
if (q.Count == 0)
{
this.queueBits &= ~(1 << i);
break;
}
}
if (currentBit < (this.queueBits & ~currentBit))
{
break;
}
}
while (true);
}
}
}
UnmanagedMethods.MSG msg;
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0);
UnmanagedMethods.TranslateMessage(ref msg);
UnmanagedMethods.DispatchMessage(ref msg);
if (this.HasShutdownStarted)
{
this.PerformShutdown();
return;
}
}
while (frame.Continue);
}
private class PokableQueue
{
private const int InitialCapacity = 32;
private int size, head, tail;
private object[] array;
internal PokableQueue(int capacity)
{
this.array = new object[capacity];
}
internal PokableQueue()
: this(InitialCapacity)
{
}
public int Count
{
get
{
return this.size;
}
}
public void Enqueue(object obj)
{
if (this.size == this.array.Length)
{
this.Grow();
}
this.array[this.tail] = obj;
this.tail = (this.tail + 1) % this.array.Length;
this.size++;
}
public object Dequeue()
{
if (this.size < 1)
{
throw new InvalidOperationException();
}
object result = this.array[this.head];
this.array[this.head] = null;
this.head = (this.head + 1) % this.array.Length;
this.size--;
return result;
}
public void Remove(object obj)
{
for (int i = 0; i < this.size; i++)
{
if (this.array[(this.head + i) % this.array.Length] == obj)
{
for (int j = i; j < this.size - i; j++)
{
this.array[(this.head + j) % this.array.Length] = this.array[(this.head + j + 1) % this.array.Length];
}
this.size--;
if (this.size < 0)
{
this.size = this.array.Length - 1;
}
this.tail--;
}
}
}
private void Grow()
{
int newc = this.array.Length * 2;
object[] newContents = new object[newc];
this.array.CopyTo(newContents, 0);
this.array = newContents;
this.head = 0;
this.tail = this.head + this.size;
}
}
}
}

371
Perspex.Windows/Threading/WindowsDispatcher.cs

@ -0,0 +1,371 @@
// -----------------------------------------------------------------------
// <copyright file="Dispatcher.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Windows.Threading
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security;
using System.Threading;
using Perspex.Threading;
using Perspex.Windows.Interop;
[Flags]
internal enum Flags
{
ShutdownStarted = 1,
Shutdown = 2,
Disabled = 4
}
public sealed class WindowsDispatcher : Dispatcher
{
private const int TopPriority = (int)DispatcherPriority.Send;
private static Dictionary<Thread, WindowsDispatcher> dispatchers = new Dictionary<Thread, WindowsDispatcher>();
private static object olock = new object();
private Thread baseThread;
private PokableQueue[] priorityQueues = new PokableQueue[TopPriority + 1];
private Flags flags;
private int queueBits;
private WindowsDispatcher(Thread t)
{
this.baseThread = t;
for (int i = 1; i <= (int)DispatcherPriority.Send; i++)
{
this.priorityQueues[i] = new PokableQueue();
}
}
public event EventHandler ShutdownStarted;
public event EventHandler ShutdownFinished;
public override DispatcherFrame CurrentFrame
{
get;
set;
}
public Thread Thread
{
get { return this.baseThread; }
}
public bool HasShutdownStarted
{
get { return (this.flags & Flags.ShutdownStarted) != 0; }
}
public override bool HasShutdownFinished
{
get { return (this.flags & Flags.Shutdown) != 0; }
}
[SecurityCritical]
public static void ExitAllFrames()
{
Dispatcher dis = CurrentDispatcher;
for (DispatcherFrame frame = dis.CurrentFrame; frame != null; frame = frame.ParentFrame)
{
if (frame.ExitOnRequest)
{
frame.Continue = false;
}
else
{
break;
}
}
}
public static WindowsDispatcher FromThread(Thread thread)
{
WindowsDispatcher dis;
if (dispatchers.TryGetValue(thread, out dis))
{
return dis;
}
return null;
}
public static WindowsDispatcher GetThreadDispatcher()
{
lock (olock)
{
Thread t = Thread.CurrentThread;
WindowsDispatcher dis = FromThread(t);
if (dis != null)
{
return dis;
}
dis = new WindowsDispatcher(t);
dispatchers[t] = dis;
return dis;
}
}
public override DispatcherOperation BeginInvoke(Action method)
{
return this.BeginInvoke(DispatcherPriority.Normal, method);
}
public override DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method)
{
if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send)
{
throw new InvalidEnumArgumentException("priority");
}
if (priority == DispatcherPriority.Inactive)
{
throw new ArgumentException("priority can not be inactive", "priority");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation op = new DispatcherOperation(this, priority, method);
this.Queue(priority, op);
return op;
}
[SecurityCritical]
public void InvokeShutdown()
{
this.flags |= Flags.ShutdownStarted;
UnmanagedMethods.PostMessage(
IntPtr.Zero,
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
IntPtr.Zero,
IntPtr.Zero);
}
protected override void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority)
{
int oldp = (int)oldpriority;
PokableQueue q = this.priorityQueues[oldp];
lock (q)
{
q.Remove(op);
}
this.Queue(op.Priority, op);
}
private void Queue(DispatcherPriority priority, DispatcherOperation x)
{
int p = (int)priority;
PokableQueue q = this.priorityQueues[p];
lock (q)
{
int flag = 1 << p;
q.Enqueue(x);
this.queueBits |= flag;
}
if (Thread.CurrentThread != this.baseThread)
{
UnmanagedMethods.PostMessage(
IntPtr.Zero,
(int)UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM,
IntPtr.Zero,
IntPtr.Zero);
}
}
private void PerformShutdown()
{
EventHandler h;
h = this.ShutdownStarted;
if (h != null)
{
h(this, new EventArgs());
}
this.flags |= Flags.Shutdown;
h = this.ShutdownFinished;
if (h != null)
{
h(this, new EventArgs());
}
this.priorityQueues = null;
}
protected override void RunFrame(DispatcherFrame frame)
{
do
{
while (this.queueBits != 0)
{
for (int i = TopPriority; i > 0 && this.queueBits != 0; i--)
{
int currentBit = this.queueBits & (1 << i);
if (currentBit != 0)
{
PokableQueue q = this.priorityQueues[i];
do
{
DispatcherOperation task;
lock (q)
{
task = (DispatcherOperation)q.Dequeue();
}
task.Invoke();
if (!frame.Continue)
{
return;
}
if (this.HasShutdownStarted)
{
this.PerformShutdown();
return;
}
lock (q)
{
if (q.Count == 0)
{
this.queueBits &= ~(1 << i);
break;
}
}
if (currentBit < (this.queueBits & ~currentBit))
{
break;
}
}
while (true);
}
}
}
UnmanagedMethods.MSG msg;
UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0);
UnmanagedMethods.TranslateMessage(ref msg);
UnmanagedMethods.DispatchMessage(ref msg);
if (this.HasShutdownStarted)
{
this.PerformShutdown();
return;
}
}
while (frame.Continue);
}
private class PokableQueue
{
private const int InitialCapacity = 32;
private int size, head, tail;
private object[] array;
internal PokableQueue(int capacity)
{
this.array = new object[capacity];
}
internal PokableQueue()
: this(InitialCapacity)
{
}
public int Count
{
get
{
return this.size;
}
}
public void Enqueue(object obj)
{
if (this.size == this.array.Length)
{
this.Grow();
}
this.array[this.tail] = obj;
this.tail = (this.tail + 1) % this.array.Length;
this.size++;
}
public object Dequeue()
{
if (this.size < 1)
{
throw new InvalidOperationException();
}
object result = this.array[this.head];
this.array[this.head] = null;
this.head = (this.head + 1) % this.array.Length;
this.size--;
return result;
}
public void Remove(object obj)
{
for (int i = 0; i < this.size; i++)
{
if (this.array[(this.head + i) % this.array.Length] == obj)
{
for (int j = i; j < this.size - i; j++)
{
this.array[(this.head + j) % this.array.Length] = this.array[(this.head + j + 1) % this.array.Length];
}
this.size--;
if (this.size < 0)
{
this.size = this.array.Length - 1;
}
this.tail--;
}
}
}
private void Grow()
{
int newc = this.array.Length * 2;
object[] newContents = new object[newc];
this.array.CopyTo(newContents, 0);
this.array = newContents;
this.head = 0;
this.tail = this.head + this.size;
}
}
}
}

7
Perspex.Windows/Window.cs

@ -18,6 +18,7 @@ namespace Perspex.Windows
using Perspex.Layout;
using Perspex.Platform;
using Perspex.Rendering;
using Perspex.Threading;
using Perspex.Windows.Input;
using Perspex.Windows.Interop;
using Perspex.Windows.Threading;
@ -42,7 +43,7 @@ namespace Perspex.Windows
public Window()
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
this.CreateWindow();
Size clientSize = this.ClientSize;
@ -55,7 +56,7 @@ namespace Perspex.Windows
this.LayoutManager.LayoutNeeded.Subscribe(x =>
{
this.layoutPending = true;
Dispatcher.CurrentDispatcher.BeginInvoke(
WindowsDispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Render,
() =>
{
@ -68,7 +69,7 @@ namespace Perspex.Windows
this.RenderManager.RenderNeeded
.Subscribe(x =>
{
Dispatcher.CurrentDispatcher.BeginInvoke(
WindowsDispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Render,
() =>
{

58
Perspex.Windows/WindowsPlatform.cs

@ -0,0 +1,58 @@
// -----------------------------------------------------------------------
// <copyright file="WindowsPlatform.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Windows
{
using System;
using System.Collections.Generic;
using Perspex.Platform;
using Perspex.Threading;
using Perspex.Windows.Interop;
using Perspex.Windows.Threading;
using Splat;
public class WindowsPlatform : IPlatformThreadingInterface
{
private static WindowsPlatform instance = new WindowsPlatform();
private Dictionary<IntPtr, UnmanagedMethods.TimerProc> timerCallbacks =
new Dictionary<IntPtr, UnmanagedMethods.TimerProc>();
public static void Initialize()
{
var locator = Locator.CurrentMutable;
locator.Register(() => instance, typeof(IPlatformThreadingInterface));
}
public Dispatcher GetThreadDispatcher()
{
return WindowsDispatcher.GetThreadDispatcher();
}
public void KillTimer(object handle)
{
this.timerCallbacks.Remove((IntPtr)handle);
UnmanagedMethods.KillTimer(IntPtr.Zero, (IntPtr)handle);
}
public object StartTimer(TimeSpan interval, Action callback)
{
UnmanagedMethods.TimerProc timerDelegate = (UnmanagedMethods.TimerProc)
((hWnd, uMsg, nIDEvent, dwTime) => callback());
IntPtr handle = UnmanagedMethods.SetTimer(
IntPtr.Zero,
IntPtr.Zero,
(uint)interval.TotalMilliseconds,
timerDelegate);
this.timerCallbacks.Add(handle, timerDelegate);
return handle;
}
}
}

2
Perspex/Controls/TextBox.cs

@ -123,7 +123,7 @@ namespace Perspex.Controls
private void OnPointerPressed(object sender, PointerEventArgs e)
{
IPlatformInterface platform = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface platform = Locator.Current.GetService<IPlatformRenderInterface>();
this.CaretIndex = platform.TextService.GetCaretIndex(
this.textBoxView.FormattedText,
e.GetPosition(this.textBoxView));

27
Perspex/Controls/TextBoxView.cs

@ -10,6 +10,7 @@ namespace Perspex.Controls
using System.Globalization;
using Perspex.Media;
using Perspex.Platform;
using Perspex.Threading;
using Splat;
internal class TextBoxView : Control
@ -18,15 +19,15 @@ namespace Perspex.Controls
private FormattedText formattedText;
//private DispatcherTimer caretTimer;
private DispatcherTimer caretTimer;
private bool caretBlink;
public TextBoxView(TextBox parent)
{
//this.caretTimer = new DispatcherTimer();
//this.caretTimer.Interval = PlatformInterface.Instance.CaretBlinkTime;
//this.caretTimer.Tick += this.CaretTimerTick;
this.caretTimer = new DispatcherTimer();
this.caretTimer.Interval = TimeSpan.FromMilliseconds(500);
this.caretTimer.Tick += this.CaretTimerTick;
this.parent = parent;
}
@ -43,15 +44,15 @@ namespace Perspex.Controls
}
}
public void GotFocus()
public new void GotFocus()
{
this.caretBlink = true;
//this.caretTimer.Start();
this.caretTimer.Start();
}
public void LostFocus()
public new void LostFocus()
{
//this.caretTimer.Stop();
this.caretTimer.Stop();
this.InvalidateVisual();
}
@ -63,9 +64,9 @@ namespace Perspex.Controls
internal void CaretMoved()
{
//this.caretBlink = true;
//this.caretTimer.Stop();
//this.caretTimer.Start();
this.caretBlink = true;
this.caretTimer.Stop();
this.caretTimer.Start();
this.InvalidateVisual();
}
@ -77,11 +78,11 @@ namespace Perspex.Controls
if (this.parent.IsFocused)
{
IPlatformInterface platform = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface platform = Locator.Current.GetService<IPlatformRenderInterface>();
Point caretPos = platform.TextService.GetCaretPosition(this.formattedText, this.parent.CaretIndex);
Brush caretBrush = Brushes.Black;
//if (!this.caretBlink)
if (this.caretBlink)
{
context.DrawLine(
new Pen(caretBrush, 1),

2
Perspex/Media/FormattedText.cs

@ -21,7 +21,7 @@ namespace Perspex.Media
{
get
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
return factory.TextService.Measure(this);
}
}

2
Perspex/Media/Imaging/Bitmap.cs

@ -13,7 +13,7 @@ namespace Perspex.Media
{
public Bitmap(int width, int height)
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
this.PlatformImpl = factory.CreateBitmap(width, height);
}

2
Perspex/Media/Imaging/RenderTargetBitmap.cs

@ -28,7 +28,7 @@ namespace Perspex.Media
private static IBitmapImpl CreateImpl(int width, int height)
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
return factory.CreateRenderTargetBitmap(width, height);
}
}

2
Perspex/Media/RectangleGeometry.cs

@ -13,7 +13,7 @@ namespace Perspex.Media
{
public RectangleGeometry(Rect rect)
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
using (IStreamGeometryContextImpl context = impl.Open())

2
Perspex/Media/StreamGeometry.cs

@ -13,7 +13,7 @@ namespace Perspex.Media
{
public StreamGeometry()
{
IPlatformInterface factory = Locator.Current.GetService<IPlatformInterface>();
IPlatformRenderInterface factory = Locator.Current.GetService<IPlatformRenderInterface>();
this.PlatformImpl = factory.CreateStreamGeometry();
}

7
Perspex/Perspex.csproj

@ -104,9 +104,10 @@
<Compile Include="Media\Brushes.cs" />
<Compile Include="Media\Imaging\Bitmap.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmap.cs" />
<Compile Include="Platform\IPlatformThreadingInterface.cs" />
<Compile Include="Platform\IRenderTargetBitmapImpl.cs" />
<Compile Include="Platform\IBitmapImpl.cs" />
<Compile Include="Platform\IPlatformInterface.cs" />
<Compile Include="Platform\IPlatformRenderInterface.cs" />
<Compile Include="Platform\IStreamGeometryContextImpl.cs" />
<Compile Include="Platform\IGeometryImpl.cs" />
<Compile Include="Platform\IStreamGeometryImpl.cs" />
@ -179,6 +180,10 @@
<Compile Include="Themes\Default\ButtonStyle.cs" />
<Compile Include="Themes\Default\DefaultTheme.cs" />
<Compile Include="Thickness.cs" />
<Compile Include="Threading\Dispatcher.cs" />
<Compile Include="Threading\DispatcherFrame.cs" />
<Compile Include="Threading\DispatcherOperation.cs" />
<Compile Include="Threading\DispatcherTimer.cs" />
<Compile Include="Visual.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VisualExtensions.cs" />

3
Perspex/Platform/IPlatformInterface.cs → Perspex/Platform/IPlatformRenderInterface.cs

@ -7,8 +7,9 @@
namespace Perspex.Platform
{
using System;
using Perspex.Threading;
public interface IPlatformInterface
public interface IPlatformRenderInterface
{
ITextService TextService { get; }

20
Perspex/Platform/IPlatformThreadingInterface.cs

@ -0,0 +1,20 @@
// -----------------------------------------------------------------------
// <copyright file="IPlatformThreadingInterface.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Platform
{
using System;
using Perspex.Threading;
public interface IPlatformThreadingInterface
{
Dispatcher GetThreadDispatcher();
void KillTimer(object timerHandle);
object StartTimer(TimeSpan interval, Action internalTick);
}
}

102
Perspex/Threading/Dispatcher.cs

@ -0,0 +1,102 @@
// -----------------------------------------------------------------------
// <copyright file="Dispatcher.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Threading
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security;
using System.Threading;
using Perspex.Platform;
using Splat;
public enum DispatcherPriority
{
Invalid = -1,
Inactive = 0,
SystemIdle = 1,
ApplicationIdle = 2,
ContextIdle = 3,
Background = 4,
Input = 5,
Loaded = 6,
Render = 7,
DataBind = 8,
Normal = 9,
Send = 10,
}
[Flags]
internal enum Flags
{
ShutdownStarted = 1,
Shutdown = 2,
Disabled = 4
}
public abstract class Dispatcher
{
private static DispatcherFrame mainExecutionFrame = new DispatcherFrame();
public static Dispatcher CurrentDispatcher
{
get { return Locator.Current.GetService<IPlatformThreadingInterface>().GetThreadDispatcher(); }
}
public abstract bool HasShutdownFinished
{
get;
}
public abstract DispatcherFrame CurrentFrame
{
get;
set;
}
public static void PushFrame(DispatcherFrame frame)
{
if (frame == null)
{
throw new ArgumentNullException("frame");
}
Dispatcher dis = CurrentDispatcher;
if (dis.HasShutdownFinished)
{
throw new InvalidOperationException("The Dispatcher has shut down");
}
if (frame.Running != null)
{
throw new InvalidOperationException("Frame is already running on a different dispatcher");
}
frame.ParentFrame = dis.CurrentFrame;
dis.CurrentFrame = frame;
frame.Running = dis;
dis.RunFrame(frame);
}
public static void Run()
{
PushFrame(mainExecutionFrame);
}
public abstract DispatcherOperation BeginInvoke(Action method);
public abstract DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method);
protected internal abstract void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority);
protected abstract void RunFrame(DispatcherFrame frame);
}
}

8
Perspex.Windows/Threading/DispatcherFrame.cs → Perspex/Threading/DispatcherFrame.cs

@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Windows.Threading
namespace Perspex.Threading
{
public class DispatcherFrame
{
@ -16,10 +16,10 @@ namespace Perspex.Windows.Threading
public bool Continue { get; set; }
internal bool ExitOnRequest { get; set; }
public bool ExitOnRequest { get; set; }
internal Dispatcher Running { get; set; }
public Dispatcher Running { get; set; }
internal DispatcherFrame ParentFrame { get; set; }
public DispatcherFrame ParentFrame { get; set; }
}
}

18
Perspex.Windows/Threading/DispatcherOperation.cs → Perspex/Threading/DispatcherOperation.cs

@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Windows.Threading
namespace Perspex.Threading
{
using System;
using System.Security;
@ -26,7 +26,7 @@ namespace Perspex.Windows.Threading
private Delegate delegateMethod;
private object[] delegateArgs;
internal DispatcherOperation(Dispatcher dis, DispatcherPriority prio)
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio)
{
this.dispatcher = dis;
this.priority = prio;
@ -40,22 +40,22 @@ namespace Perspex.Windows.Threading
}
}
internal DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d)
: this(dis, prio)
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d)
: this(dis, prio)
{
this.delegateMethod = d;
}
internal DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg)
: this(dis, prio)
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg)
: this(dis, prio)
{
this.delegateMethod = d;
this.delegateArgs = new object[1];
this.delegateArgs[0] = arg;
}
internal DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg, object[] args)
: this(dis, prio)
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg, object[] args)
: this(dis, prio)
{
this.delegateMethod = d;
this.delegateArgs = new object[args.Length + 1];
@ -139,7 +139,7 @@ namespace Perspex.Windows.Threading
throw new NotImplementedException();
}
internal void Invoke()
public void Invoke()
{
this.status = DispatcherOperationStatus.Executing;

140
Perspex/Threading/DispatcherTimer.cs

@ -0,0 +1,140 @@
// -----------------------------------------------------------------------
// <copyright file="DispatcherTimer.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Threading
{
using System;
using Perspex.Platform;
using Splat;
public class DispatcherTimer
{
private object timerHandle;
private DispatcherPriority priority;
private TimeSpan interval;
public DispatcherTimer()
{
this.priority = DispatcherPriority.Normal;
this.Dispatcher = Dispatcher.CurrentDispatcher;
}
public DispatcherTimer(DispatcherPriority priority)
{
this.priority = priority;
this.Dispatcher = Dispatcher.CurrentDispatcher;
}
public DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher)
{
this.priority = priority;
this.Dispatcher = dispatcher;
}
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback, Dispatcher dispatcher)
{
this.priority = priority;
this.Dispatcher = dispatcher;
this.Interval = interval;
this.Tick += callback;
}
~DispatcherTimer()
{
if (this.timerHandle != null)
{
this.Stop();
}
}
public event EventHandler Tick;
public Dispatcher Dispatcher
{
get;
private set;
}
public TimeSpan Interval
{
get
{
return this.interval;
}
set
{
bool enabled = this.IsEnabled;
this.Stop();
this.interval = value;
this.IsEnabled = enabled;
}
}
public bool IsEnabled
{
get
{
return this.timerHandle != null;
}
set
{
if (this.IsEnabled != value)
{
if (value)
{
this.Start();
}
else
{
this.Stop();
}
}
}
}
public object Tag
{
get;
set;
}
public void Start()
{
if (!this.IsEnabled)
{
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>();
this.timerHandle = threading.StartTimer(this.Interval, this.InternalTick);
}
}
public void Stop()
{
if (this.IsEnabled)
{
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>();
threading.KillTimer(this.timerHandle);
this.timerHandle = null;
}
}
private void InternalTick()
{
this.Dispatcher.BeginInvoke(this.priority, (Action)this.RaiseTick);
}
private void RaiseTick()
{
if (this.Tick != null)
{
this.Tick(this, EventArgs.Empty);
}
}
}
}

2
TestApplication/App.cs

@ -3,6 +3,7 @@
using Perspex;
using Perspex.Direct2D1;
using Perspex.Themes.Default;
using Perspex.Windows;
public class App : Application
{
@ -10,6 +11,7 @@
{
this.RegisterServices();
Direct2D1Platform.Initialize();
WindowsPlatform.Initialize();
this.Styles = new DefaultTheme();
}
}

1
TestApplication/Program.cs

@ -12,6 +12,7 @@ using Perspex.Media;
using Perspex.Shapes;
using Perspex.Styling;
using Perspex.Themes.Default;
using Perspex.Threading;
using Perspex.Windows;
using Perspex.Windows.Threading;
using Splat;

Loading…
Cancel
Save