Browse Source

Implemented text clipboard support

pull/116/head
Nikita Tsukanov 11 years ago
parent
commit
7e8d011f90
  1. 40
      src/Gtk/Perspex.Gtk/ClipboardImpl.cs
  2. 2
      src/Gtk/Perspex.Gtk/GtkPlatform.cs
  3. 1
      src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj
  4. 10
      src/Perspex.Application/Application.cs
  5. 48
      src/Perspex.Controls/TextBox.cs
  6. 1
      src/Perspex.Input/Perspex.Input.csproj
  7. 17
      src/Perspex.Input/Platform/IClipboard.cs
  8. 81
      src/Windows/Perspex.Win32/ClipboardImpl.cs
  9. 37
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  10. 1
      src/Windows/Perspex.Win32/Perspex.Win32.csproj
  11. 4
      src/Windows/Perspex.Win32/Win32Platform.cs

40
src/Gtk/Perspex.Gtk/ClipboardImpl.cs

@ -0,0 +1,40 @@
namespace Perspex.Gtk
{
using Gdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Input.Platform;
using Gtk = global::Gtk;
class ClipboardImpl : IClipboard
{
private static Gtk.Clipboard GetClipboard() => Gtk.Clipboard.GetForDisplay(Gdk.Display.Default, new Atom(IntPtr.Zero));
public Task<string> GetTextAsync()
{
var clip = GetClipboard();
var tcs = new TaskCompletionSource<string>();
clip.RequestText((_, text) =>
{
tcs.TrySetResult(text);
});
return tcs.Task;
}
public Task SetTextAsync(string text)
{
using (var cl = GetClipboard())
cl.Text = text;
return Task.FromResult(0);
}
public Task ClearAsync()
{
using (var cl = GetClipboard())
cl.Clear();
return Task.FromResult(0);
}
}
}

2
src/Gtk/Perspex.Gtk/GtkPlatform.cs

@ -8,6 +8,7 @@ namespace Perspex.Gtk
{
using System;
using System.Reactive.Disposables;
using Perspex.Input.Platform;
using Perspex.Input;
using Perspex.Platform;
using Splat;
@ -41,6 +42,7 @@ namespace Perspex.Gtk
var locator = Locator.CurrentMutable;
locator.Register(() => new WindowImpl(), typeof(IWindowImpl));
locator.Register(() => new PopupImpl(), typeof(IPopupImpl));
locator.Register(() => new ClipboardImpl(), typeof (IClipboard));
locator.Register(() => GtkKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => instance, typeof(IPlatformSettings));
locator.Register(() => instance, typeof(IPlatformThreadingInterface));

1
src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj

@ -59,6 +59,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssetLoader.cs" />
<Compile Include="ClipboardImpl.cs" />
<Compile Include="GtkExtensions.cs" />
<Compile Include="PopupImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

10
src/Perspex.Application/Application.cs

@ -4,7 +4,10 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
using Perspex.Input.Platform;
namespace
Perspex
{
using System;
using System.Reflection;
@ -41,6 +44,9 @@ namespace Perspex
/// </summary>
private DataTemplates dataTemplates;
private readonly Lazy<IClipboard> clipboard =
new Lazy<IClipboard>(() => (IClipboard) Locator.Current.GetService(typeof(IClipboard)));
/// <summary>
/// The styler that will be used to apply styles to controls.
/// </summary>
@ -119,6 +125,8 @@ namespace Perspex
private set;
}
public IClipboard Clipboard => this.clipboard.Value;
/// <summary>
/// Gets the application's global styles.
/// </summary>

48
src/Perspex.Controls/TextBox.cs

@ -4,6 +4,9 @@
// </copyright>
// -----------------------------------------------------------------------
using Perspex.Input.Platform;
using Splat;
namespace Perspex.Controls
{
using System;
@ -143,11 +146,28 @@ namespace Perspex.Controls
caretIndex = this.CaretIndex;
text = this.Text ?? string.Empty;
this.Text = text.Substring(0, caretIndex) + input + text.Substring(caretIndex);
++this.CaretIndex;
this.CaretIndex += input.Length;
this.SelectionStart = this.SelectionEnd = this.CaretIndex;
}
}
async void Copy()
{
await ((IClipboard) Locator.Current.GetService(typeof (IClipboard)))
.SetTextAsync(this.GetSelection());
}
async void Paste()
{
var text = await ((IClipboard) Locator.Current.GetService(typeof (IClipboard))).GetTextAsync();
if (text == null)
{
return;
}
HandleTextInput(text);
}
protected override void OnKeyDown(KeyEventArgs e)
{
string text = this.Text ?? string.Empty;
@ -165,7 +185,20 @@ namespace Perspex.Controls
}
break;
case Key.C:
if (modifiers == ModifierKeys.Control)
{
this.Copy();
}
break;
case Key.V:
if (modifiers == ModifierKeys.Control)
{
this.Paste();
}
break;
case Key.Left:
this.MoveHorizontal(-1, modifiers);
movement = true;
@ -435,6 +468,19 @@ namespace Perspex.Controls
}
}
private string GetSelection()
{
var selectionStart = this.SelectionStart;
var selectionEnd = this.SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd);
var end = Math.Max(selectionStart, selectionEnd);
if (start == end || (this.Text?.Length ?? 0) <= end)
{
return "";
}
return this.Text.Substring(start, end - start);
}
private int GetLine(int caretIndex, IList<FormattedTextLine> lines)
{
int pos = 0;

1
src/Perspex.Input/Perspex.Input.csproj

@ -66,6 +66,7 @@
</Compile>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="AccessKeyHandler.cs" />
<Compile Include="Platform\IClipboard.cs" />
<Compile Include="Raw\RawTextInputEventArgs.cs" />
<Compile Include="NavigationMethod.cs" />
<Compile Include="IInputRoot.cs" />

17
src/Perspex.Input/Platform/IClipboard.cs

@ -0,0 +1,17 @@
namespace Perspex.Input.Platform
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public interface IClipboard
{
Task<string> GetTextAsync();
Task SetTextAsync(string text);
Task ClearAsync();
}
}

81
src/Windows/Perspex.Win32/ClipboardImpl.cs

@ -0,0 +1,81 @@
namespace Perspex.Win32
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Input.Platform;
using Perspex.Win32.Interop;
using System.Runtime.InteropServices;
class ClipboardImpl : IClipboard
{
async Task OpenClipboard()
{
while (!UnmanagedMethods.OpenClipboard(IntPtr.Zero))
{
await Task.Delay(100);
}
}
public async Task<string> GetTextAsync()
{
await this.OpenClipboard();
try
{
IntPtr hText = UnmanagedMethods.GetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT);
if (hText == IntPtr.Zero)
{
return null;
}
var pText = UnmanagedMethods.GlobalLock(hText);
if (pText == IntPtr.Zero)
{
return null;
}
var rv = Marshal.PtrToStringUni(pText);
UnmanagedMethods.GlobalUnlock(hText);
return rv;
}
finally
{
UnmanagedMethods.CloseClipboard();
}
}
public async Task SetTextAsync(string text)
{
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
await this.OpenClipboard();
try
{
var hGlobal = Marshal.StringToHGlobalUni(text);
UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, hGlobal);
}
finally
{
UnmanagedMethods.CloseClipboard();
}
}
public async Task ClearAsync()
{
await this.OpenClipboard();
try
{
UnmanagedMethods.EmptyClipboard();
}
finally
{
UnmanagedMethods.CloseClipboard();
}
}
}
}

37
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -627,6 +627,43 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetWindowText(IntPtr hwnd, string lpString);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool OpenClipboard(IntPtr hWndOwner);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool CloseClipboard();
[DllImport("user32.dll")]
public static extern bool EmptyClipboard();
[DllImport("user32.dll")]
public static extern IntPtr GetClipboardData(ClipboardFormat uFormat);
[DllImport("user32.dll")]
public static extern IntPtr SetClipboardData(ClipboardFormat uFormat, IntPtr hMem);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GlobalAlloc(int uFlags, int dwBytes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GlobalFree(IntPtr hMem);
public enum ClipboardFormat
{
CF_TEXT = 1,
CF_UNICODETEXT = 13
}
public struct MSG
{
public IntPtr hwnd;

1
src/Windows/Perspex.Win32/Perspex.Win32.csproj

@ -66,6 +66,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AssetLoader.cs" />
<Compile Include="ClipboardImpl.cs" />
<Compile Include="Input\KeyInterop.cs" />
<Compile Include="Input\WindowsKeyboardDevice.cs" />
<Compile Include="Input\WindowsMouseDevice.cs" />

4
src/Windows/Perspex.Win32/Win32Platform.cs

@ -4,6 +4,8 @@
// </copyright>
// -----------------------------------------------------------------------
using Perspex.Input.Platform;
namespace Perspex.Win32
{
using System;
@ -52,7 +54,7 @@ namespace Perspex.Win32
{
var locator = Locator.CurrentMutable;
locator.Register(() => new PopupImpl(), typeof(IPopupImpl));
locator.Register(() => new ClipboardImpl(), typeof(IClipboard));
locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => WindowsMouseDevice.Instance, typeof(IMouseDevice));
locator.Register(() => instance, typeof(IPlatformSettings));

Loading…
Cancel
Save