Skip to content

Commit

Permalink
Fix up Clipboard on all platforms to behave similarly.
Browse files Browse the repository at this point in the history
- Wpf: Can now specify more than one type in the clipboard by setting various properties.
- Wpf: Added protection when getting the value and retries 10 times after 100ms delay as some times it throws a com exception that it cannot open the clipboard.
- Gtk: Fix up Clipboard.Image so it doesn't wipe all other values in the clipboard
- Gtk: Setting Clipboard.Text sets all text variants, not just utf8.
- Gtk: Getting/Setting Clipboard.Html now works correctly
- Added unit tests to verify behaviour
  • Loading branch information
cwensley committed Sep 29, 2017
1 parent d45bd9c commit e23c89c
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 79 deletions.
77 changes: 46 additions & 31 deletions Source/Eto.Gtk/Forms/ClipboardHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using Eto.GtkSharp.Drawing;
using System.Collections.Generic;
using System.Text;

namespace Eto.GtkSharp.Forms
{
Expand All @@ -13,19 +14,20 @@ public class ClipboardHandler : WidgetHandler<Gtk.Clipboard, Clipboard>, Clipboa

class ClipboardData
{
public Gtk.TargetEntry Target { get; set; }
public string Type { get; set; }

public object Data { get; set; }

public GetClipboardData GetClipboardData { get; set; }

public void GetData(Gtk.SelectionData selectionData)
{
if (GetClipboardData != null)
GetClipboardData(this, selectionData);
GetClipboardData?.Invoke(this, selectionData);
}
}

Gtk.TargetList targets = new Gtk.TargetList();

readonly List<ClipboardData> clipboard = new List<ClipboardData>();

public ClipboardHandler()
Expand All @@ -35,8 +37,7 @@ public ClipboardHandler()

void Update()
{
var targets = clipboard.Select(r => r.Target);
Control.SetWithData(targets.ToArray(), (clip, selectionData, info) =>
Control.SetWithData((Gtk.TargetEntry[])targets, (clip, selectionData, info) =>
{
if (info < clipboard.Count)
{
Expand All @@ -45,15 +46,38 @@ void Update()
}
}, clip =>
{

});
}

void AddEntry(string type, object data, GetClipboardData getData)
{
targets.Add(type, 0, (uint)clipboard.Count);
clipboard.Add(new ClipboardData
{
Type = type,
Data = data,
GetClipboardData = getData
});
Update();
}

void AddTextEntry(string data, GetClipboardData getData)
{
targets.AddTextTargets((uint)clipboard.Count);
clipboard.Add(new ClipboardData
{
Data = data,
GetClipboardData = getData
});
Update();
}

void AddImageEntry(object data, GetClipboardData getData)
{
targets.AddImageTargets((uint)clipboard.Count, false);
clipboard.Add(new ClipboardData
{
Target = new Gtk.TargetEntry(type, 0, (uint)clipboard.Count),
Data = data,
GetClipboardData = getData
});
Expand All @@ -74,44 +98,29 @@ Gtk.SelectionData GetSelectionData(string type)

public void SetString(string value, string type)
{
AddEntry(type, value, (data, selection) => selection.Text = data.Data as string);
AddEntry(type, value, (data, selection) => selection.Set(Gdk.Atom.Intern(data.Type, false), 8, Encoding.UTF8.GetBytes(data.Data as string)));
}

public string Html
{
set
{
AddEntry("text/html", value, (data, selection) => selection.Text = data.Data as string);
}
get
{
var selection = GetSelectionData("text/html");
return selection != null ? selection.Text : null;
}
set { SetString(value, "text/html"); }
get { return GetString("text/html"); }
}

public string Text
{
set
{
AddEntry("UTF8_STRING", value, (data, selection) => selection.Text = data.Data as string);
}
set { AddTextEntry(value, (data, selection) => selection.Text = data.Data as string); }
get { return Control.WaitForText(); }

}

public Image Image
{
set
{
var pixbuf = value.ControlObject as Gdk.Pixbuf;
/* TODO: AddEntry(type, value, delegate(ClipboardData data, Gtk.SelectionData selection) {
selection = value;
});*/
if (pixbuf != null)
Control.Image = pixbuf;
else
var pixbuf = value.ToGdk();
if (pixbuf == null)
throw new NotSupportedException();
AddImageEntry(pixbuf, (data, selection) => selection.SetPixbuf(data.Data as Gdk.Pixbuf));
}
get
{
Expand All @@ -132,8 +141,12 @@ public void SetData(byte[] value, string type)

public string GetString(string type)
{
var selection = GetSelectionData(type);
return selection != null ? selection.Text : null;
var data = GetSelectionData(type)?.Data;
if (data != null)
{
return Encoding.UTF8.GetString(data);
}
return null;
}

public byte[] GetData(string type)
Expand All @@ -145,7 +158,9 @@ public byte[] GetData(string type)
public void Clear()
{
Control.Clear();
targets = new Gtk.TargetList();
clipboard.Clear();
Update();
}

public string[] Types
Expand Down
1 change: 1 addition & 0 deletions Source/Eto.Test/Eto.Test/Eto.Test - pcl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
<Compile Include="UnitTests\Drawing\FontTests.cs" />
<Compile Include="UnitTests\Drawing\GraphicsTests.cs" />
<Compile Include="UnitTests\Drawing\MatrixTests.cs" />
<Compile Include="UnitTests\Forms\ClipboardTests.cs" />
<Compile Include="UnitTests\Forms\Controls\ComboBoxTests.cs" />
<Compile Include="UnitTests\Forms\Controls\CustomCellTests.cs" />
<Compile Include="UnitTests\Forms\Controls\NumericStepperTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public ClipboardSection()
clipboard.Text = "Some text";
Update();
};
var copyHtmlButton = new Button { Text = "Copy Html" };
copyHtmlButton.Click += (sender, e) =>
{
clipboard.Html = "Some <strong style='color:blue'>HTML</strong>";
Update();
};
var copyImageButton = new Button { Text = "Copy Image" };
copyImageButton.Click += (sender, e) =>
{
Expand Down Expand Up @@ -47,7 +53,7 @@ public ClipboardSection()
Orientation = Orientation.Horizontal,
Spacing = 5,
Padding = new Padding(10),
Items = { copyTextButton, copyImageButton, pasteTextButton, clearButton }
Items = { copyTextButton, copyHtmlButton, copyImageButton, pasteTextButton, clearButton }
},
new StackLayoutItem(pasteData, expand: true)
}
Expand Down
85 changes: 85 additions & 0 deletions Source/Eto.Test/Eto.Test/UnitTests/Forms/ClipboardTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Eto.Forms;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Eto.Test.UnitTests.Forms
{
[TestFixture]
public class ClipboardTests : TestBase
{
[Test]
public void GettingAndSettingTextShouldNotCrash()
{
Invoke(() =>
{
for (int i = 0; i < 100; i++)
{
// this crashes on WPF on some machines.. don't know why as I can't repro the issue.
var clipboard = new Clipboard();
var val = "Hello" + i;
clipboard.Text = val;
Assert.AreEqual(val, clipboard.Text);
}
});
}

[Test]
public void SettingMultipleFormatsShouldWork()
{
Invoke(() =>
{
var byteData = new byte[] { 10, 20, 30 };
using (var clipboard = new Clipboard())
{
clipboard.Text = "Text";
clipboard.Html = "<strong>Some Html</strong>";
clipboard.SetString("woot", "eto-woot");
clipboard.SetData(byteData, "eto-byte-data");

Assert.AreEqual("Text", clipboard.Text);
Assert.AreEqual("<strong>Some Html</strong>", clipboard.Html);
Assert.AreEqual("woot", clipboard.GetString("eto-woot"));
Assert.AreEqual(byteData, clipboard.GetData("eto-byte-data"));

Assert.Contains("eto-woot", clipboard.Types);
Assert.Contains("eto-byte-data", clipboard.Types);
}

using (var clipboard = new Clipboard())
{
Assert.AreEqual("Text", clipboard.Text);
Assert.AreEqual("<strong>Some Html</strong>", clipboard.Html);
Assert.AreEqual("woot", clipboard.GetString("eto-woot"));
Assert.AreEqual(byteData, clipboard.GetData("eto-byte-data"));

Assert.Contains("eto-woot", clipboard.Types);
Assert.Contains("eto-byte-data", clipboard.Types);

clipboard.Clear();
CollectionAssert.DoesNotContain("eto-woot", clipboard.Types);
CollectionAssert.DoesNotContain("eto-byte-data", clipboard.Types);
Assert.AreEqual(null, clipboard.Text);
Assert.AreEqual(null, clipboard.Html);
Assert.AreEqual(null, clipboard.Image);
Assert.AreEqual(null, clipboard.GetString("eto-woot"));
Assert.AreEqual(null, clipboard.GetData("eto-byte-data"));
}

using (var clipboard = new Clipboard())
{
CollectionAssert.DoesNotContain("eto-woot", clipboard.Types);
CollectionAssert.DoesNotContain("eto-byte-data", clipboard.Types);
Assert.AreEqual(null, clipboard.Text);
Assert.AreEqual(null, clipboard.Html);
Assert.AreEqual(null, clipboard.Image);
Assert.AreEqual(null, clipboard.GetString("eto-woot"));
Assert.AreEqual(null, clipboard.GetData("eto-byte-data"));
}
});
}
}
}
21 changes: 7 additions & 14 deletions Source/Eto.WinForms/Forms/ClipboardHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ public string Html
{
set
{
Control.SetText(value ?? string.Empty, System.Windows.Forms.TextDataFormat.Html);
Control.SetText(value ?? string.Empty, swf.TextDataFormat.Html);
Update();
}
get
{
return swf.Clipboard.ContainsText(swf.TextDataFormat.Html) ? swf.Clipboard.GetText(swf.TextDataFormat.Html) : null;
return swf.Clipboard.ContainsText(swf.TextDataFormat.Html) ? swf.Clipboard.GetText(swf.TextDataFormat.Html)?.TrimEnd('\0') : null;
}
}

Expand Down Expand Up @@ -76,22 +76,22 @@ public Image Image
get
{
Image result = null;

try
{
var sdimage = GetImageFromClipboard() as sd.Bitmap;

if (sdimage != null)
{
var handler = new BitmapHandler(sdimage);

result = new Bitmap(handler);
}
}
catch
{
}

return result;
}
}
Expand Down Expand Up @@ -203,14 +203,7 @@ public string GetString(string type)
return null;
}

public string[] Types
{
get
{
var data = swf.Clipboard.GetDataObject();
return data != null ? data.GetFormats() : null;
}
}
public string[] Types => swf.Clipboard.GetDataObject()?.GetFormats();

public void Clear()
{
Expand Down
Loading

0 comments on commit e23c89c

Please sign in to comment.