QQuickPaintedItem support (#219)

* First support for QQuickPaintedItem

Implements some basic drawing functionalities

* Less string passing during drawing

Colors get registered and are referenced via id
Font parameters can be set separately

* Color string to id conversion is now an implementation detail of QmlNetPaintedItem

* Adds Font metrics functionality

* Adds DrawText overload with bounding rectangle

* Dont copy the actions on each draw but lock the vector

* Optional Text support for QmlNetPaintedItem (including preedit)

* Adds Flags to the DrawText (Rect) method

* Adds an interface to interact with a QPainter

This interface can be implemented by a QPainter abstraction in the future
For now QmlNetPaintedItem implements INetQPainter

* Native submodule: Adds QMutex include

* Native submodule: Adds missing include

* Adds a lot of the QPainter interface

* Fixes Polygon point handling

Adds new QPainter API to QmlNetPaintedItem

* Renames QmlNetPaintedItem to QmlNetRecordingPaintedItem

* Deactivates painthandler reset in destructor of RecordingPaintedItem

* Registering a QuickPaintedItem

* Hide QmlNetQuickPaintedItem from QML

* First theoretically working PaintedItem implementation

* getStringSize is now static

* new native submodule version

* new native submodule (build error fix)

* Cleanup

Removes RecordingPaintedItem
Removes INetQPainter (functionality moved to NetQPainter)

* references native that fixes type registration

* Draw Image file

* Adds testing infrastructure for QmlNetQuickPaintedItem

* DrawArc test

* DrawConvexPolygon test

* Fixes GCHandle handling in QmlNetQuickPaintedItem

* Adds setWindowIcon support to QGuiApplication

* Adds DrawEllipse and DrawLine test

* Adds DrawPoint test

* Adds DrawPolygon test

* Adds DrawRoundedRect test

* Draw Pie Unit Test

* Test source formatting

* DrawPolyline Unit test

* Erase Rect Unit Test

* Unit Tests for ClipRect and Opacity

* Disables transformations

Real world use cases would want to use the QTransform functionality instead of passing the raw transformation matrix

* Unit Test for Sheer

* Adds Unit test for Translate

* Cleanup

* Link to native/origin/master

* Re-Trigger CI

Co-authored-by: Paul Knopf <pauldotknopf@gmail.com>
This commit is contained in:
Michael Lamers 2020-12-01 20:03:46 +01:00 committed by GitHub
parent 4e54f975cb
commit 0836ae783a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1825 additions and 18 deletions

4
.gitignore vendored
View file

@ -3,4 +3,6 @@ output/
samples/PhotoFrame/\.vscode/
samples/PhotoFrame/\.idea/
build/Qt/
build/qtci/
build/qtci/
src/native/.DS_Store
.DS_Store

@ -1 +1 @@
Subproject commit b4cf9f8dacad14e74a52e26cad1915f34da792f1
Subproject commit 4510e0109d3a6c3e74be5f2876e3681ae3f75723

View file

@ -9,6 +9,7 @@
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />

View file

@ -32,31 +32,61 @@ namespace Qml.Net.Tests.Qml
_registeredTypes.Add(typeof(T));
Net.Qml.RegisterType<T>("tests");
}
protected void RunQmlTest(string instanceId, string componentOnCompletedCode, bool runEvents = false, bool failOnQmlWarnings = true)
protected virtual void RegisterPaintedQuickItemType<T>()
where T : QmlNetQuickPaintedItem
{
var result = NetTestHelper.RunQml(
qmlApplicationEngine,
string.Format(
@"
if (_registeredTypes.Contains(typeof(T))) return;
_registeredTypes.Add(typeof(T));
Net.Qml.RegisterPaintedQuickItemType<T>("tests");
}
protected void RunQmlTest(string instanceId, string componentOnCompletedCode, bool runEvents = false, bool failOnQmlWarnings = true, string additionalProperties = "")
{
var qml = string.Format(
@"
import QtQuick 2.0
import tests 1.0
{0} {{
id: {1}
{2}
property var testQObject: null
function runTest() {{
{2}
{3}
}}
}}
",
typeof(TTypeToRegister).Name,
instanceId,
componentOnCompletedCode),
runEvents,
failOnQmlWarnings);
typeof(TTypeToRegister).Name,
instanceId,
additionalProperties,
componentOnCompletedCode);
var result = true;
Exception exception= null;
try
{
result = NetTestHelper.RunQml(
qmlApplicationEngine,
qml,
runEvents,
failOnQmlWarnings);
}
catch (Exception ex)
{
exception = ex;
result = false;
}
if (result == false)
{
throw new Exception($"Couldn't execute qml: {componentOnCompletedCode}");
var msg = $"Couldn't execute qml: {Environment.NewLine}{qml}";
if (exception != null)
{
throw new Exception(msg, exception);
}
else
{
throw new Exception(msg);
}
}
}
@ -84,6 +114,32 @@ namespace Qml.Net.Tests.Qml
TypeCreator.SetInstance(typeof(T), Mock.Object);
}
}
public abstract class BaseQmlQuickPaintedItemTests<T> : AbstractBaseQmlTests<T>
where T : QmlNetQuickPaintedItem
{
protected readonly Mock<T> Mock;
protected BaseQmlQuickPaintedItemTests()
{
RegisterPaintedQuickItemType<T>();
Mock = new Mock<T>();
TypeCreator.SetInstance(typeof(T), Mock.Object);
}
}
public abstract class BaseQmlQuickPaintedItemTestsWithInstance<T> : AbstractBaseQmlTests<T>
where T : QmlNetQuickPaintedItem, new()
{
protected readonly T Instance;
protected BaseQmlQuickPaintedItemTestsWithInstance()
{
RegisterPaintedQuickItemType<T>();
Instance = new T();
TypeCreator.SetInstance(typeof(T), Instance);
}
}
public abstract class BaseQmlTestsWithInstance<T> : AbstractBaseQmlTests<T>
where T : class, new()

View file

@ -180,7 +180,6 @@ namespace Qml.Net.Tests.Qml
})
viewModelContainer.changeStringPropertyTo('new value')
");
Instance.TestResult.Should().Be(true);
}

View file

@ -0,0 +1,709 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Moq;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
using Color = System.Drawing.Color;
using Point = System.Drawing.Point;
namespace Qml.Net.Tests.Qml
{
public class QmlNetQuickPaintedItemTests : BaseQmlQuickPaintedItemTests<QmlNetQuickPaintedItemTests.TestPaintedItem>
{
public class TestPaintedItem : QmlNetQuickPaintedItem
{
public TestPaintedItem()
{
}
public virtual string SomeProperty { get; set; }
public object QmlValue { get; set; }
public override void Paint(NetQPainter painter)
{
}
}
[Fact]
public void Can_get_and_set_additional_properties()
{
Mock.SetupGet(x => x.SomeProperty).Returns("Some property value");
RunQmlTest(
"test",
@"
test.someProperty = test.someProperty
");
Mock.VerifyGet(x => x.SomeProperty, Times.Once);
Mock.VerifySet(x => x.SomeProperty = "Some property value");
}
}
public class QmlNetQuickPaintedItemInstanceTests : BaseQmlQuickPaintedItemTestsWithInstance<
QmlNetQuickPaintedItemInstanceTests.TestPaintedItem>
{
public class TestPaintedItem : QmlNetQuickPaintedItem
{
public TestPaintedItem()
{
}
public virtual string SomeProperty { get; set; }
public object QmlValue { get; set; }
public override void Paint(NetQPainter painter)
{
}
}
[Fact]
public void Do_quickitem_properties_exist()
{
RunQmlTest(
"test",
@"
test.height = 20;
test.qmlValue = test.height;
");
Assert.NotNull(Instance.QmlValue);
Assert.Equal(20d, Instance.QmlValue);
}
}
public class QmlNetQuickPaintedItemRenderingTests : BaseQmlQuickPaintedItemTestsWithInstance<
QmlNetQuickPaintedItemRenderingTests.TestPaintedItem>
{
private readonly ITestOutputHelper _testOutputHelper;
public QmlNetQuickPaintedItemRenderingTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
public class TestPaintedItem : QmlNetQuickPaintedItem
{
private byte[] _imgData;
public byte[] ImageData => _imgData;
public TestPaintedItem()
{
}
public override void Paint(NetQPainter painter)
{
foreach (var paintAction in _paintActions)
{
paintAction(painter);
}
}
public void AddPaintAction(Action<NetQPainter> paintAction)
{
_paintActions.Add(paintAction);
}
private List<Action<NetQPainter>> _paintActions = new List<Action<NetQPainter>>();
public void DoStoreImageData()
{
_imgData = PaintToImage("bmp");
}
}
Image<Rgba32> RunQmlRendering(params Action<NetQPainter>[] paintActions)
{
foreach (var pa in paintActions)
{
Instance.AddPaintAction(pa);
}
RunQmlTest(
"test",
@"
test.doStoreImageData();
",
additionalProperties: "height: 200" + Environment.NewLine + "width:300" + Environment.NewLine +
"fillColor:'#FFFFFFFF'");
return Image.Load<Rgba32>(Instance.ImageData);
}
[Fact]
public void FillRect_works()
{
var img = RunQmlRendering((p) => { p.FillRect(0, 0, 150, 100, "#FF0000"); });
var red = new Rgba32(0xFF, 0x00, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(white, img[299, 199]);
Assert.Equal(red, img[0, 0]);
Assert.Equal(red, img[149, 0]);
Assert.Equal(red, img[0, 99]);
Assert.Equal(red, img[149, 99]);
Assert.Equal(white, img[150, 0]);
Assert.Equal(white, img[149, 100]);
Assert.Equal(white, img[150, 99]);
}
[Fact]
public void FillRectWithImplicitColor_works()
{
var img = RunQmlRendering((p) =>
{
p.SetBrush("#FF0000");
p.FillRect(0, 0, 150, 100);
});
var red = new Rgba32(0xFF, 0x00, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(white, img[299, 199]);
Assert.Equal(red, img[0, 0]);
Assert.Equal(red, img[149, 0]);
Assert.Equal(red, img[0, 99]);
Assert.Equal(red, img[149, 99]);
Assert.Equal(white, img[150, 0]);
Assert.Equal(white, img[149, 100]);
Assert.Equal(white, img[150, 99]);
}
[Fact]
public void DrawRect_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawRect(0, 0, 150, 100);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(white, img[299, 199]);
Assert.Equal(green, img[0, 0]);
Assert.Equal(green, img[149, 0]);
Assert.Equal(white, img[151, 0]);
Assert.Equal(white, img[148, 5]);
Assert.Equal(green, img[0, 99]);
Assert.Equal(white, img[5, 98]);
Assert.Equal(green, img[150, 100]);
Assert.Equal(white, img[151, 0]);
Assert.Equal(white, img[149, 101]);
Assert.Equal(white, img[151, 100]);
}
[Fact]
public void DrawArc_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawArc(0, 0, 150, 100, 0, 45 * 16);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[128, 14]);
Assert.Equal(white, img[129, 14]);
Assert.Equal(white, img[127, 14]);
Assert.Equal(white, img[128, 15]);
Assert.Equal(white, img[128, 13]);
Assert.Equal(green, img[129, 15]);
Assert.Equal(green, img[130, 16]);
Assert.Equal(green, img[131, 17]);
Assert.Equal(green, img[132, 17]);
Assert.Equal(green, img[150, 50]);
Assert.Equal(white, img[151, 50]);
Assert.Equal(white, img[149, 50]);
Assert.Equal(white, img[150, 51]);
Assert.Equal(white, img[150, 49]);
}
[Fact]
public void DrawConvexPolygon_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawConvexPolygon(new[]
{
new Point(10, 10),
new Point(10, 100),
new Point(100, 100),
new Point(100, 10),
new Point(10, 10)
});
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[10, 10]);
Assert.Equal(green, img[10, 50]);
Assert.Equal(green, img[10, 100]);
Assert.Equal(green, img[50, 100]);
Assert.Equal(green, img[100, 100]);
Assert.Equal(green, img[100, 50]);
Assert.Equal(green, img[100, 10]);
Assert.Equal(green, img[50, 10]);
Assert.Equal(white, img[8, 10]);
Assert.Equal(white, img[12, 12]);
Assert.Equal(white, img[8, 100]);
Assert.Equal(white, img[100, 8]);
Assert.Equal(white, img[102, 10]);
Assert.Equal(white, img[102, 100]);
Assert.Equal(white, img[50, 102]);
}
[Fact]
public void DrawEllipse_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawEllipse(10, 10, 100, 20);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
// left side
Assert.Equal(green, img[10, 19]);
Assert.Equal(green, img[10, 20]);
Assert.Equal(green, img[10, 21]);
Assert.Equal(white, img[9, 20]);
Assert.Equal(white, img[9, 21]);
Assert.Equal(white, img[10, 22]);
Assert.Equal(white, img[10, 18]);
Assert.Equal(white, img[11, 20]);
Assert.Equal(white, img[11, 21]);
Assert.Equal(green, img[11, 18]);
Assert.Equal(green, img[11, 22]);
// right side
Assert.Equal(green, img[110, 19]);
Assert.Equal(green, img[110, 20]);
Assert.Equal(green, img[110, 21]);
Assert.Equal(white, img[111, 20]);
Assert.Equal(white, img[111, 21]);
Assert.Equal(white, img[110, 22]);
Assert.Equal(white, img[110, 18]);
Assert.Equal(white, img[109, 20]);
Assert.Equal(white, img[109, 21]);
Assert.Equal(green, img[109, 18]);
Assert.Equal(green, img[109, 22]);
}
[Fact]
public void DrawLine_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawLine(10, 10, 100, 100);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(white, img[9, 8]);
Assert.Equal(white, img[9, 9]);
Assert.Equal(white, img[9, 10]);
Assert.Equal(green, img[10, 10]);
Assert.Equal(green, img[11, 11]);
Assert.Equal(green, img[12, 12]);
Assert.Equal(green, img[98, 98]);
Assert.Equal(green, img[99, 99]);
Assert.Equal(green, img[100, 100]);
Assert.Equal(white, img[101, 101]);
Assert.Equal(white, img[10, 9]);
Assert.Equal(white, img[10, 11]);
Assert.Equal(white, img[11, 10]);
Assert.Equal(white, img[11, 12]);
Assert.Equal(white, img[99, 98]);
Assert.Equal(white, img[99, 100]);
Assert.Equal(white, img[100, 99]);
Assert.Equal(white, img[100, 101]);
Assert.Equal(white, img[101, 100]);
Assert.Equal(white, img[101, 101]);
Assert.Equal(white, img[101, 102]);
}
[Fact]
public void DrawPoint_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawPoint(47, 11);
p.DrawPoint(78, 110);
p.DrawPoint(60, 3);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[47, 11]);
Assert.Equal(white, img[46, 11]);
Assert.Equal(white, img[48, 11]);
Assert.Equal(white, img[47, 10]);
Assert.Equal(white, img[47, 12]);
Assert.Equal(green, img[78, 110]);
Assert.Equal(white, img[77, 110]);
Assert.Equal(white, img[79, 110]);
Assert.Equal(white, img[78, 109]);
Assert.Equal(white, img[78, 111]);
Assert.Equal(green, img[60, 3]);
Assert.Equal(white, img[59, 3]);
Assert.Equal(white, img[61, 3]);
Assert.Equal(white, img[60, 2]);
Assert.Equal(white, img[60, 4]);
}
[Fact]
public void DrawPolygon_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawPolygon(
new[]
{
new Point(10, 10),
new Point(10, 100),
new Point(100, 100),
new Point(100, 10),
new Point(10, 10)
},
false);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[10, 10]);
Assert.Equal(green, img[10, 50]);
Assert.Equal(green, img[10, 100]);
Assert.Equal(green, img[50, 100]);
Assert.Equal(green, img[100, 100]);
Assert.Equal(green, img[100, 50]);
Assert.Equal(green, img[100, 10]);
Assert.Equal(green, img[50, 10]);
Assert.Equal(white, img[8, 10]);
Assert.Equal(white, img[12, 12]);
Assert.Equal(white, img[8, 100]);
Assert.Equal(white, img[100, 8]);
Assert.Equal(white, img[102, 10]);
Assert.Equal(white, img[102, 100]);
Assert.Equal(white, img[50, 102]);
}
[Fact]
public void DrawRoundedRect_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawRoundedRect(10, 10, 50, 50, 20, 20, false);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
// check vertical left
Assert.Equal(white, img[10, 10]);
Assert.Equal(white, img[10, 11]);
Assert.Equal(white, img[10, 12]);
Assert.Equal(green, img[10, 13]);
Assert.Equal(green, img[10, 35]);
Assert.Equal(green, img[10, 57]);
Assert.Equal(white, img[10, 58]);
Assert.Equal(white, img[10, 59]);
Assert.Equal(white, img[10, 60]);
// check horizontal top
Assert.Equal(white, img[10, 10]);
Assert.Equal(white, img[11, 10]);
Assert.Equal(white, img[12, 10]);
Assert.Equal(green, img[13, 10]);
Assert.Equal(green, img[35, 10]);
Assert.Equal(green, img[57, 10]);
Assert.Equal(white, img[58, 10]);
Assert.Equal(white, img[59, 10]);
Assert.Equal(white, img[60, 10]);
}
[Fact]
public void DrawPie_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawPie(0, 0, 150, 100, 0, 45 * 16);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[128, 14]);
Assert.Equal(white, img[129, 14]);
Assert.Equal(white, img[127, 14]);
Assert.Equal(white, img[128, 15]);
Assert.Equal(white, img[128, 13]);
Assert.Equal(green, img[129, 15]);
Assert.Equal(green, img[130, 16]);
Assert.Equal(green, img[131, 17]);
Assert.Equal(green, img[132, 17]);
Assert.Equal(green, img[149, 50]);
Assert.Equal(white, img[150, 50]);
Assert.Equal(green, img[148, 50]);
Assert.Equal(white, img[149, 51]);
Assert.Equal(green, img[149, 49]);
Assert.Equal(green, img[147, 50]);
Assert.Equal(green, img[146, 50]);
Assert.Equal(green, img[145, 50]);
Assert.Equal(green, img[144, 50]);
}
[Fact]
public void DrawPolyline_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.DrawPolyline(
new[]
{
new Point(10, 10),
new Point(10, 100),
new Point(100, 100),
new Point(100, 10),
new Point(10, 10)
});
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[10, 10]);
Assert.Equal(green, img[10, 50]);
Assert.Equal(green, img[10, 100]);
Assert.Equal(green, img[50, 100]);
Assert.Equal(green, img[100, 100]);
Assert.Equal(green, img[100, 50]);
Assert.Equal(green, img[100, 10]);
Assert.Equal(green, img[50, 10]);
Assert.Equal(white, img[8, 10]);
Assert.Equal(white, img[12, 12]);
Assert.Equal(white, img[8, 100]);
Assert.Equal(white, img[100, 8]);
Assert.Equal(white, img[102, 10]);
Assert.Equal(white, img[102, 100]);
Assert.Equal(white, img[50, 102]);
}
[Fact]
public void EraseRect_works()
{
var img = RunQmlRendering((p) =>
{
p.SetBrush("#00FF00");
p.SetBackground("#FFFFFF");
p.FillRect(0, 0, 150, 150);
p.EraseRect(10, 10, 10, 10);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(green, img[0, 0]);
Assert.Equal(green, img[9, 0]);
Assert.Equal(green, img[0, 9]);
Assert.Equal(white, img[10, 10]);
Assert.Equal(white, img[19, 10]);
Assert.Equal(green, img[20, 10]);
Assert.Equal(white, img[10, 19]);
Assert.Equal(green, img[10, 20]);
Assert.Equal(white, img[19, 19]);
Assert.Equal(green, img[20, 20]);
Assert.Equal(green, img[19, 20]);
Assert.Equal(green, img[20, 19]);
}
[Fact]
public void SetClipRect_works()
{
var img = RunQmlRendering((p) =>
{
p.SetBrush("#00FF00");
p.SetClipRect(10, 10, 10, 10, NetQPainter.ClipOperation.IntersectClip);
p.FillRect(0, 0, 150, 150);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
var white = new Rgba32(0xFF, 0xFF, 0xFF);
Assert.Equal(white, img[0, 0]);
Assert.Equal(white, img[9, 0]);
Assert.Equal(white, img[0, 9]);
Assert.Equal(green, img[10, 10]);
Assert.Equal(green, img[19, 10]);
Assert.Equal(white, img[20, 10]);
Assert.Equal(green, img[10, 19]);
Assert.Equal(white, img[10, 20]);
Assert.Equal(green, img[19, 19]);
Assert.Equal(white, img[20, 20]);
Assert.Equal(white, img[19, 20]);
Assert.Equal(white, img[20, 19]);
}
[Fact]
public void SetOpacity_works()
{
var img = RunQmlRendering((p) =>
{
p.SetOpacity(0.5);
p.SetBrush("#00FF00");
p.FillRect(0, 0, 150, 150);
});
var opaqueGreen = new Rgba32(0x80, 0xFF, 0x80);
Assert.Equal(opaqueGreen, img[50, 50]);
}
[Fact]
public void Shear_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.Shear(0.5, 1);
p.DrawPoint(50, 50);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
Assert.Equal(green, img[75, 100]);
}
[Fact]
public void Translate_works()
{
var img = RunQmlRendering((p) =>
{
p.SetPen("#00FF00", 1);
p.Translate(30, 20);
p.DrawPoint(50, 50);
});
var green = new Rgba32(0x00, 0xFF, 0x00);
Assert.Equal(green, img[80, 70]);
}
}
public class QmlNetQuickPaintedItemTwoLevelClassHierarchyTests : BaseQmlQuickPaintedItemTests<QmlNetQuickPaintedItemTwoLevelClassHierarchyTests.TestPaintedItem>
{
public class TestPaintedItem : TestPaintedItemBase
{
public virtual string SomeProperty { get; set; }
}
public class TestPaintedItemBase : QmlNetQuickPaintedItem
{
public virtual string SomeBaseProperty { get; set; }
public override void Paint(NetQPainter painter)
{
}
}
[Fact]
public void Can_get_and_set_additional_properties()
{
Mock.SetupGet(x => x.SomeProperty).Returns("Some property value");
Mock.SetupGet(x => x.SomeBaseProperty).Returns("Some base property value");
RunQmlTest(
"test",
@"
test.someProperty = test.someProperty
test.someBaseProperty = test.someBaseProperty
");
Mock.VerifyGet(x => x.SomeProperty, Times.Once);
Mock.VerifyGet(x => x.SomeBaseProperty, Times.Once);
Mock.VerifySet(x => x.SomeProperty = "Some property value");
Mock.VerifySet(x => x.SomeBaseProperty = "Some base property value");
}
}
}

View file

@ -36,7 +36,7 @@ namespace Qml.Net.Internal
}
var baseType = typeInfo.BaseType;
if (baseType != null)
if (baseType != null && !Helpers.ShouldBeHiddenFromQml(baseType))
{
type.BaseType = baseType.AssemblyQualifiedName;
}
@ -58,7 +58,7 @@ namespace Qml.Net.Internal
}
// Don't grab properties and methods for system-level types.
if (Helpers.IsPrimitive(typeInfo))
if (Helpers.IsPrimitive(typeInfo) || Helpers.ShouldBeHiddenFromQml(typeInfo))
{
return;
}

View file

@ -31,6 +31,17 @@ namespace Qml.Net.Internal
return false;
}
public static bool ShouldBeHiddenFromQml(Type type)
{
// we don't mirror QmlNetQuickPaintedItem properties and methods into QML because they (partly) mirror the QML type (QQuickPaintedItem)
if (type == typeof(QmlNetQuickPaintedItem))
{
return true;
}
return false;
}
public static void Pack(object source, NetVariant destination, Type type)
{
if (type == typeof(bool))

View file

@ -76,6 +76,8 @@ namespace Qml.Net.Internal
NetQObject = LoadInteropType<NetQObjectInterop>(library, loader);
NetQObjectSignalConnection = LoadInteropType<NetQObjectSignalConnectionInterop>(library, loader);
QLocale = LoadInteropType<QLocaleInterop>(library, loader);
QmlNetPaintedItem = LoadInteropType<QmlNetPaintedItemInterop>(library, loader);
NetQPainter = LoadInteropType<NetQPainterInterop>(library, loader);
// RuntimeManager.ConfigureRuntimeDirectory may set these environment variables.
// However, they only really work when called with Qt.PutEnv.
@ -131,6 +133,10 @@ namespace Qml.Net.Internal
public static NetQObjectSignalConnectionInterop NetQObjectSignalConnection { get; }
public static QLocaleInterop QLocale { get; set; }
public static QmlNetPaintedItemInterop QmlNetPaintedItem { get; set; }
public static NetQPainterInterop NetQPainter { get; set; }
private static T LoadInteropType<T>(IntPtr library, IPlatformLoader loader)
where T : new()

View file

@ -0,0 +1,779 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security;
using Qml.Net.Internal;
using static System.Runtime.InteropServices.UnmanagedType;
// ReSharper disable InconsistentNaming
namespace Qml.Net
{
public class NetQPainter
{
private IntPtr _qPainterRef;
private Dictionary<string, int> _registeredColors = new Dictionary<string, int>();
private Dictionary<string, int> _registeredFontFamilies = new Dictionary<string, int>();
public NetQPainter(IntPtr qPainterRef)
{
_qPainterRef = qPainterRef;
}
public void SetPen(string colorString, int width)
{
var colorId = GetColorId(colorString);
Interop.NetQPainter.SetPen(_qPainterRef, colorId, width);
}
public void ResetPen()
{
Interop.NetQPainter.ResetPen(_qPainterRef);
}
public void SetBrush(string colorString)
{
var colorId = GetColorId(colorString);
Interop.NetQPainter.SetBrush(_qPainterRef, colorId);
}
public void ResetBrush()
{
Interop.NetQPainter.ResetBrush(_qPainterRef);
}
public void SetFont(string fontFamilyName, bool isBold, bool isItalic, bool isUnderline, int pxSize)
{
int fontFamilyId = GetFontFamilyId(fontFamilyName);
Interop.NetQPainter.SetFont(_qPainterRef, fontFamilyId, isBold, isItalic, isUnderline, pxSize);
}
public void SetFontFamily(string fontFamilyName)
{
int fontFamilyId = GetFontFamilyId(fontFamilyName);
Interop.NetQPainter.SetFontFamily(_qPainterRef, fontFamilyId);
}
public void SetFontBold(bool isBold)
{
Interop.NetQPainter.SetFontBold(_qPainterRef, isBold);
}
public void SetFontItalic(bool isItalic)
{
Interop.NetQPainter.SetFontItalic(_qPainterRef, isItalic);
}
public void SetFontUnderline(bool isUnderline)
{
Interop.NetQPainter.SetFontUnderline(_qPainterRef, isUnderline);
}
public void SetFontSize(int pxSize)
{
Interop.NetQPainter.SetFontSize(_qPainterRef, pxSize);
}
public void DrawText(int x, int y, string text)
{
Interop.NetQPainter.DrawText(_qPainterRef, x, y, text);
}
[Flags]
public enum DrawTextFlags
{
None = 0x0000,
AlignLeft = 0x0001,
AlignRight = 0x0002,
AlignHCenter = 0x0004,
AlignJustify = 0x0008,
AlignTop = 0x0020,
AlignBottom = 0x0040,
AlignVCenter = 0x0080,
AlignCenter = AlignVCenter | AlignHCenter,
TextSingleLine = 0x0100,
TextDontClip = 0x0200,
TextExpandTabs = 0x0400,
TextShowMnemonic = 0x0800,
TextWordWrap = 0x1000,
TextIncludeTrailingSpaces = 0x08000000
}
public void DrawText(int x, int y, int width, int height, DrawTextFlags flags, string text)
{
Interop.NetQPainter.DrawTextRect(_qPainterRef, x, y, width, height, (int)flags, text);
}
public void DrawRect(int x, int y, int width, int height)
{
Interop.NetQPainter.DrawRect(_qPainterRef, x, y, width, height);
}
public void FillRect(int x, int y, int width, int height, string color)
{
var colorId = GetColorId(color);
Interop.NetQPainter.FillRectColor(_qPainterRef, x, y, width, height, colorId);
}
public void FillRect(int x, int y, int width, int height)
{
Interop.NetQPainter.FillRect(_qPainterRef, x, y, width, height);
}
private int RegisterColor(string colorString)
{
return Interop.NetQPainter.RegisterColor(_qPainterRef, colorString);
}
// ReSharper disable once UnusedMember.Local
private void FreeColor(int colorId)
{
Interop.NetQPainter.FreeColor(_qPainterRef, colorId);
}
private int RegisterFontFamily(string fontFamily)
{
return Interop.NetQPainter.RegisterFontFamily(_qPainterRef, fontFamily);
}
// ReSharper disable once UnusedMember.Local
private void FreeFontFamily(int fontFamilyId)
{
Interop.NetQPainter.FreeFontFamily(_qPainterRef, fontFamilyId);
}
public static Size GetStringSize(string fontFamily, int fontSizePx, string text)
{
var res = Interop.NetQPainter.GetStringSize(fontFamily, fontSizePx, text);
return new Size(res.width, res.height);
}
public void DrawArc(int x, int y, int width, int height, int startAngle, int spanAngle)
{
Interop.NetQPainter.DrawArc(_qPainterRef, x, y, width, height, startAngle, spanAngle);
}
public void DrawChord(int x, int y, int width, int height, int startAngle, int spanAngle)
{
Interop.NetQPainter.DrawChord(_qPainterRef, x, y, width, height, startAngle, spanAngle);
}
public void DrawConvexPolygon(Point[] points)
{
var count = points.Length;
var pointsArray = new NetQPainterInterop.netqpainter_Point[count];
for (var i = 0; i < count; i++)
{
var p = points[i];
pointsArray[i] = new NetQPainterInterop.netqpainter_Point()
{
x = p.X,
y = p.Y
};
}
Interop.NetQPainter.DrawConvexPolygon(_qPainterRef, pointsArray, pointsArray.Length);
}
public void DrawEllipse(int x, int y, int width, int height)
{
Interop.NetQPainter.DrawEllipse(_qPainterRef, x, y, width, height);
}
public enum ImageConversionFlag
{
None = 0x00000000,
ColorMode_Mask = 0x00000003,
AutoColor = 0x00000000,
ColorOnly = 0x00000003,
MonoOnly = 0x00000002,
AlphaDither_Mask = 0x0000000c,
ThresholdAlphaDither = 0x00000000,
OrderedAlphaDither = 0x00000004,
DiffuseAlphaDither = 0x00000008,
NoAlpha = 0x0000000c, // Not supported
Dither_Mask = 0x00000030,
DiffuseDither = 0x00000000,
OrderedDither = 0x00000010,
ThresholdDither = 0x00000020,
DitherMode_Mask = 0x000000c0,
AutoDither = 0x00000000,
PreferDither = 0x00000040,
AvoidDither = 0x00000080,
NoOpaqueDetection = 0x00000100,
NoFormatConversion = 0x00000200
}
public void DrawImage(Point start, string imagePath, Rectangle source, ImageConversionFlag flags)
{
var p = new NetQPainterInterop.netqpainter_Point()
{
x = start.X,
y = start.Y
};
var sourceRect = new NetQPainterInterop.netqpainter_Rect()
{
x = source.X,
y = source.Y,
width = source.Width,
height = source.Height
};
Interop.NetQPainter.DrawImageFile(_qPainterRef, p, imagePath, sourceRect, flags);
}
public void DrawImage(Point start, byte[] imageData, Rectangle source, ImageConversionFlag flags)
{
var p = new NetQPainterInterop.netqpainter_Point()
{
x = start.X,
y = start.Y
};
var sourceRect = new NetQPainterInterop.netqpainter_Rect()
{
x = source.X,
y = source.Y,
width = source.Width,
height = source.Height
};
Interop.NetQPainter.DrawImage(_qPainterRef, p, imageData, imageData.Length, sourceRect, flags);
}
public void DrawLine(int x1, int y1, int x2, int y2)
{
Interop.NetQPainter.DrawLine(_qPainterRef, x1, y1, x2, y2);
}
public void DrawPie(int x, int y, int width, int height, int startAngle, int spanAngle)
{
Interop.NetQPainter.DrawPie(_qPainterRef, x, y, width, height, startAngle, spanAngle);
}
public void DrawPoint(int x, int y)
{
Interop.NetQPainter.DrawPoint(_qPainterRef, x, y);
}
public void DrawPolygon(Point[] points, bool oddFill)
{
var count = points.Length;
var pointsArray = new NetQPainterInterop.netqpainter_Point[count];
for (var i = 0; i < count; i++)
{
var p = points[i];
pointsArray[i] = new NetQPainterInterop.netqpainter_Point()
{
x = p.X,
y = p.Y
};
}
Interop.NetQPainter.DrawPolygon(_qPainterRef, pointsArray, pointsArray.Length, oddFill);
}
public void DrawPolyline(Point[] points)
{
var count = points.Length;
var pointsArray = new NetQPainterInterop.netqpainter_Point[count];
for (var i = 0; i < count; i++)
{
var p = points[i];
pointsArray[i] = new NetQPainterInterop.netqpainter_Point()
{
x = p.X,
y = p.Y
};
}
Interop.NetQPainter.DrawPolyline(_qPainterRef, pointsArray, pointsArray.Length);
}
public void DrawRoundedRect(int x, int y, int w, int h, double xRadius, double yRadius, bool absoluteSize)
{
Interop.NetQPainter.DrawRoundedRect(_qPainterRef, x, y, w, h, xRadius, yRadius, absoluteSize);
}
public void EraseRect(int x, int y, int width, int height)
{
Interop.NetQPainter.EraseRect(_qPainterRef, x, y, width, height);
}
public void SetBackground(string color)
{
var colorId = GetColorId(color);
Interop.NetQPainter.SetBackground(_qPainterRef, colorId);
}
public void SetBackgroundMode(bool opaque)
{
Interop.NetQPainter.SetBackgroundMode(_qPainterRef, opaque);
}
public enum ClipOperation
{
NoClip = 1,
ReplaceClip,
IntersectClip
}
public void SetClipRect(int x, int y, int width, int height, ClipOperation operation)
{
Interop.NetQPainter.SetClipRect(_qPainterRef, x, y, width, height, operation);
}
public void SetClipping(bool enable)
{
Interop.NetQPainter.SetClipping(_qPainterRef, enable);
}
public enum CompositionMode
{
CompositionMode_SourceOver = 1,
CompositionMode_DestinationOver,
CompositionMode_Clear,
CompositionMode_Source,
CompositionMode_Destination,
CompositionMode_SourceIn,
CompositionMode_DestinationIn,
CompositionMode_SourceOut,
CompositionMode_DestinationOut,
CompositionMode_SourceAtop,
CompositionMode_DestinationAtop,
CompositionMode_Xor,
// svg 1.2 blend modes
CompositionMode_Plus,
CompositionMode_Multiply,
CompositionMode_Screen,
CompositionMode_Overlay,
CompositionMode_Darken,
CompositionMode_Lighten,
CompositionMode_ColorDodge,
CompositionMode_ColorBurn,
CompositionMode_HardLight,
CompositionMode_SoftLight,
CompositionMode_Difference,
CompositionMode_Exclusion,
// ROPs
RasterOp_SourceOrDestination,
RasterOp_SourceAndDestination,
RasterOp_SourceXorDestination,
RasterOp_NotSourceAndNotDestination,
RasterOp_NotSourceOrNotDestination,
RasterOp_NotSourceXorDestination,
RasterOp_NotSource,
RasterOp_NotSourceAndDestination,
RasterOp_SourceAndNotDestination,
RasterOp_NotSourceOrDestination,
RasterOp_SourceOrNotDestination,
RasterOp_ClearDestination,
RasterOp_SetDestination,
RasterOp_NotDestination
}
public void SetCompositionMode(CompositionMode mode)
{
Interop.NetQPainter.SetCompositionMode(_qPainterRef, mode);
}
public enum LayoutDirection
{
LeftToRight = 1,
RightToLeft,
LayoutDirectionAuto
}
public void SetLayoutDirection(LayoutDirection direction)
{
Interop.NetQPainter.SetLayoutDirection(_qPainterRef, direction);
}
public void SetOpacity(double opacity)
{
Interop.NetQPainter.SetOpacity(_qPainterRef, opacity);
}
public enum RenderHint
{
Antialiasing = 0x01,
TextAntialiasing = 0x02,
SmoothPixmapTransform = 0x04,
Qt4CompatiblePainting = 0x20,
LosslessImageRendering = 0x40,
}
public void SetRenderHint(RenderHint hint, bool on)
{
Interop.NetQPainter.SetRenderHint(_qPainterRef, hint, on);
}
public void Shear(double sh, double sv)
{
Interop.NetQPainter.Shear(_qPainterRef, sh, sv);
}
public void Translate(double dx, double dy)
{
Interop.NetQPainter.Translate(_qPainterRef, dx, dy);
}
private int GetColorId(string colorString)
{
if (_registeredColors.ContainsKey(colorString))
{
return _registeredColors[colorString];
}
var colorId = RegisterColor(colorString);
_registeredColors.Add(colorString, colorId);
return colorId;
}
private int GetFontFamilyId(string fontFamilyName)
{
if (_registeredFontFamilies.ContainsKey(fontFamilyName))
{
return _registeredFontFamilies[fontFamilyName];
}
var fontFamilyId = RegisterFontFamily(fontFamilyName);
_registeredFontFamilies.Add(fontFamilyName, fontFamilyId);
return fontFamilyId;
}
}
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Properties get set via Reflection")]
internal class NetQPainterInterop
{
[NativeSymbol(Entrypoint = "netqpainter_setPen")]
public SetPenDel SetPen { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetPenDel(IntPtr paintedItem, int colorId, int width);
[NativeSymbol(Entrypoint = "netqpainter_resetPen")]
public ResetPenDel ResetPen { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ResetPenDel(IntPtr paintedItem);
[NativeSymbol(Entrypoint = "netqpainter_setBrush")]
public SetBrushDel SetBrush { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetBrushDel(IntPtr paintedItem, int colorId);
[NativeSymbol(Entrypoint = "netqpainter_resetBrush")]
public ResetBrushDel ResetBrush { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ResetBrushDel(IntPtr paintedItem);
[NativeSymbol(Entrypoint = "netqpainter_setFont")]
public SetFontDel SetFont { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontDel(IntPtr paintedItem, int fontFamilyId, bool isBold, bool isItalic, bool isUnderline, int pxSize);
[NativeSymbol(Entrypoint = "netqpainter_setFontFamily")]
public SetFontFamilyDel SetFontFamily { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontFamilyDel(IntPtr paintedItem, int fontFamilyId);
[NativeSymbol(Entrypoint = "netqpainter_setFontBold")]
public SetFontBoldDel SetFontBold { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontBoldDel(IntPtr paintedItem, bool isBold);
[NativeSymbol(Entrypoint = "netqpainter_setFontItalic")]
public SetFontItalicDel SetFontItalic { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontItalicDel(IntPtr paintedItem, bool isItalic);
[NativeSymbol(Entrypoint = "netqpainter_setFontUnderline")]
public SetFontUnderlineDel SetFontUnderline { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontUnderlineDel(IntPtr paintedItem, bool isUnderline);
[NativeSymbol(Entrypoint = "netqpainter_setFontSize")]
public SetFontSizeDel SetFontSize { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetFontSizeDel(IntPtr paintedItem, int pxSize);
[NativeSymbol(Entrypoint = "netqpainter_drawText")]
public DrawTextDel DrawText { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawTextDel(IntPtr paintedItem, int x, int y, [MarshalAs(LPWStr)] string text);
[NativeSymbol(Entrypoint = "netqpainter_drawTextRect")]
public DrawTextRectDel DrawTextRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawTextRectDel(IntPtr paintedItem, int x, int y, int width, int height, int flags, [MarshalAs(LPWStr)] string text);
[NativeSymbol(Entrypoint = "netqpainter_drawRect")]
public DrawRectDel DrawRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawRectDel(IntPtr paintedItem, int x, int y, int width, int height);
[NativeSymbol(Entrypoint = "netqpainter_fillRectColor")]
public FillRectColorDel FillRectColor { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FillRectColorDel(IntPtr paintedItem, int x, int y, int width, int height, int colorId);
[NativeSymbol(Entrypoint = "netqpainter_fillRect")]
public FillRectDel FillRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FillRectDel(IntPtr paintedItem, int x, int y, int width, int height);
[NativeSymbol(Entrypoint = "netqpainter_registerColor")]
public RegisterColorDel RegisterColor { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int RegisterColorDel(IntPtr paintedItem, [MarshalAs(LPWStr)] string colorString);
[NativeSymbol(Entrypoint = "netqpainter_freeColor")]
public FreeColorDel FreeColor { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FreeColorDel(IntPtr paintedItem, int colorId);
[NativeSymbol(Entrypoint = "netqpainter_registerFontFamily")]
public RegisterFontFamilyDel RegisterFontFamily { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int RegisterFontFamilyDel(
IntPtr paintedItem,
[MarshalAs(LPWStr)] string fontFamilyString);
[NativeSymbol(Entrypoint = "netqpainter_freeFontFamily")]
public FreeFontFamilyDel FreeFontFamily { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FreeFontFamilyDel(IntPtr paintedItem, int fontFamilyId);
[StructLayout(LayoutKind.Sequential)]
public struct StringSizeResult
{
public int width;
public int height;
}
[NativeSymbol(Entrypoint = "netqpainter_getStringSize")]
public GetStringSizeDel GetStringSize { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate StringSizeResult GetStringSizeDel([MarshalAs(LPWStr)] string fontFamily, int sizePx, [MarshalAs(LPWStr)] string text);
[NativeSymbol(Entrypoint = "netqpainter_drawArc")]
public DrawArcDel DrawArc { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawArcDel(IntPtr paintedItem, int x, int y, int width, int height, int startAngle, int spanAngle);
[NativeSymbol(Entrypoint = "netqpainter_drawChord")]
public DrawChordDel DrawChord { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawChordDel(IntPtr paintedItem, int x, int y, int width, int height, int startAngle, int spanAngle);
[StructLayout(LayoutKind.Sequential)]
public struct netqpainter_Point
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct netqpainter_Rect
{
public int x;
public int y;
public int width;
public int height;
}
[NativeSymbol(Entrypoint = "netqpainter_drawConvexPolygon")]
public DrawConvexPolygonDel DrawConvexPolygon { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawConvexPolygonDel(IntPtr paintedItem, netqpainter_Point[] points, int pointCount);
[NativeSymbol(Entrypoint = "netqpainter_drawEllipse")]
public DrawEllipseDel DrawEllipse { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawEllipseDel(IntPtr paintedItem, int x, int y, int width, int height);
[NativeSymbol(Entrypoint = "netqpainter_drawImage")]
public DrawImageDel DrawImage { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawImageDel(IntPtr paintedItem, netqpainter_Point point, byte[] imgData, int imgDataSize, netqpainter_Rect sourceRect, NetQPainter.ImageConversionFlag flags);
[NativeSymbol(Entrypoint = "netqpainter_drawImageFile")]
public DrawImageFileDel DrawImageFile { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawImageFileDel(IntPtr paintedItem, netqpainter_Point point, [MarshalAs(LPWStr)] string imageFilePath, netqpainter_Rect sourceRect, NetQPainter.ImageConversionFlag flags);
[NativeSymbol(Entrypoint = "netqpainter_drawLine")]
public DrawLineDel DrawLine { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawLineDel(IntPtr paintedItem, int x1, int y1, int x2, int y2);
[NativeSymbol(Entrypoint = "netqpainter_drawPie")]
public DrawPieDel DrawPie { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawPieDel(IntPtr paintedItem, int x, int y, int width, int height, int startAngle, int spanAngle);
[NativeSymbol(Entrypoint = "netqpainter_drawPoint")]
public DrawPointDel DrawPoint { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawPointDel(IntPtr paintedItem, int x, int y);
[NativeSymbol(Entrypoint = "netqpainter_drawPolygon")]
public DrawPolygonDel DrawPolygon { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawPolygonDel(IntPtr paintedItem, netqpainter_Point[] points, int pointCount, bool oddFill);
[NativeSymbol(Entrypoint = "netqpainter_drawPolyline")]
public DrawPolylineDel DrawPolyline { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawPolylineDel(IntPtr paintedItem, netqpainter_Point[] points, int pointCount);
[NativeSymbol(Entrypoint = "netqpainter_drawRoundedRect")]
public DrawRoundedRectDel DrawRoundedRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DrawRoundedRectDel(IntPtr paintedItem, int x, int y, int w, int h, double xRadius, double yRadius, bool absoluteSize);
[NativeSymbol(Entrypoint = "netqpainter_eraseRect")]
public EraseRectDel EraseRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void EraseRectDel(IntPtr paintedItem, int x, int y, int width, int height);
[NativeSymbol(Entrypoint = "netqpainter_setBackground")]
public SetBackgroundDel SetBackground { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetBackgroundDel(IntPtr paintedItem, int colorId);
[NativeSymbol(Entrypoint = "netqpainter_setBackgroundMode")]
public SetBackgroundModeDel SetBackgroundMode { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetBackgroundModeDel(IntPtr paintedItem, bool opaque);
[NativeSymbol(Entrypoint = "netqpainter_setClipRect")]
public SetClipRectDel SetClipRect { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetClipRectDel(IntPtr paintedItem, int x, int y, int width, int height, NetQPainter.ClipOperation operation);
[NativeSymbol(Entrypoint = "netqpainter_setClipping")]
public SetClippingDel SetClipping { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetClippingDel(IntPtr paintedItem, bool enable);
[NativeSymbol(Entrypoint = "netqpainter_setCompositionMode")]
public SetCompositionModeDel SetCompositionMode { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetCompositionModeDel(IntPtr paintedItem, NetQPainter.CompositionMode mode);
[NativeSymbol(Entrypoint = "netqpainter_setLayoutDirection")]
public SetLayoutDirectionDel SetLayoutDirection { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetLayoutDirectionDel(IntPtr paintedItem, NetQPainter.LayoutDirection dir);
[NativeSymbol(Entrypoint = "netqpainter_setOpacity")]
public SetOpacityDel SetOpacity { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetOpacityDel(IntPtr paintedItem, double opacity);
[NativeSymbol(Entrypoint = "netqpainter_setRenderHint")]
public SetRenderHintDel SetRenderHint { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetRenderHintDel(IntPtr paintedItem, NetQPainter.RenderHint hint, bool on);
[NativeSymbol(Entrypoint = "netqpainter_shear")]
public ShearDel Shear { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ShearDel(IntPtr paintedItem, double sh, double sv);
[NativeSymbol(Entrypoint = "netqpainter_translate")]
public TranslateDel Translate { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TranslateDel(IntPtr paintedItem, double dx, double dy);
}
}

View file

@ -434,6 +434,13 @@ namespace Qml.Net
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddCallbacksDel(IntPtr app, ref QCoreAppCallbacks callbacks);
[NativeSymbol(Entrypoint = "qapp_setWindowIcon")]
public SetWindowIconDel SetWindowIcon { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetWindowIconDel(IntPtr app, [MarshalAs(UnmanagedType.LPWStr)]string pngFilePath);
[NativeSymbol(Entrypoint = "qapp_requestTrigger")]
public RequestTriggerDel RequestTrigger { get; set; }

View file

@ -24,5 +24,10 @@ namespace Qml.Net
: base(existingApp)
{
}
public void SetWindowIcon(string pngFilePath)
{
Interop.QCoreApplication.SetWindowIcon(Handle, pngFilePath);
}
}
}

View file

@ -80,6 +80,11 @@ namespace Qml.Net
return Interop.QQmlApplicationEngine.RegisterType(type.Handle, uri, versionMajor, versionMinor, qmlName);
}
internal static int RegisterPaintedQuickItemType(NetTypeInfo type, string uri, string qmlName, int versionMajor = 1, int versionMinor = 0)
{
return Interop.QQmlApplicationEngine.RegisterPaintedQuickItemType(type.Handle, uri, versionMajor, versionMinor, qmlName);
}
/// <summary>
/// Activates the MVVM behavior.
/// This Behavior automatically connects INotifyPropertyChanged instances with appropriate signals on the QML side
@ -137,6 +142,13 @@ namespace Qml.Net
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int RegisterTypeDel(IntPtr type, [MarshalAs(UnmanagedType.LPWStr)]string uri, int versionMajor, int versionMinor, [MarshalAs(UnmanagedType.LPWStr)]string qmlName);
[NativeSymbol(Entrypoint = "qqmlapplicationengine_registerPaintedQuickItemType")]
public RegisterPaintedQuickItemTypeDel RegisterPaintedQuickItemType { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int RegisterPaintedQuickItemTypeDel(IntPtr type, [MarshalAs(UnmanagedType.LPWStr)]string uri, int versionMajor, int versionMinor, [MarshalAs(UnmanagedType.LPWStr)]string qmlName);
[NativeSymbol(Entrypoint = "qqmlapplicationengine_registerSingletonTypeQml")]
public RegisterSingletonTypeQmlDel RegisterSingletonTypeQml { get; set; }

View file

@ -20,6 +20,20 @@ namespace Qml.Net
}
}
public static int RegisterPaintedQuickItemType<T>(string uri, int versionMajor = 1, int versionMinor = 0)
where T : QmlNetQuickPaintedItem
{
return RegisterPaintedQuickItemType(typeof(T), typeof(T).Name, uri, versionMajor, versionMinor);
}
public static int RegisterPaintedQuickItemType(Type type, string qmlName, string uri, int versionMajor = 1, int versionMinor = 0)
{
using (var typeInfo = NetTypeManager.GetTypeInfo(type))
{
return QQmlApplicationEngine.RegisterPaintedQuickItemType(typeInfo, uri, qmlName, versionMajor, versionMinor);
}
}
public static int RegisterSingletonType(string url, string qmlName, string uri, int versionMajor = 1, int versionMinor = 0)
{
return Interop.QQmlApplicationEngine.RegisterSingletonTypeQml(url, uri, versionMajor, versionMinor, qmlName);

View file

@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using Qml.Net.Internal;
using Qml.Net.Internal.Types;
using static System.Runtime.InteropServices.UnmanagedType;
namespace Qml.Net
{
public abstract class QmlNetQuickPaintedItem
{
private IntPtr _ref;
public int Height => _ref != IntPtr.Zero ? Interop.QmlNetPaintedItem.GetHeight(_ref) : 0;
public int Width => _ref != IntPtr.Zero ? Interop.QmlNetPaintedItem.GetWidth(_ref) : 0;
public QmlNetQuickPaintedItem()
{
if (_callbacks == null)
{
_callbacks = new QmlNetPaintedItemCallbacksImpl();
}
}
internal void SetRef(IntPtr @ref)
{
_ref = @ref;
}
public void Update()
{
if (_ref != IntPtr.Zero)
{
Interop.QmlNetPaintedItem.Update(_ref);
}
}
public byte[] PaintToImage(string format)
{
var arrayPtr = Interop.QmlNetPaintedItem.PaintToImage(_ref, out var size, format);
byte[] result = new byte[size];
Marshal.Copy(arrayPtr, result, 0, (int)size);
Marshal.FreeHGlobal(arrayPtr);
return result;
}
public abstract void Paint(NetQPainter painter);
public virtual void OnHeightChanged(int height)
{
}
public virtual void OnWidthChanged(int width)
{
}
private static QmlNetPaintedItemCallbacksImpl _callbacks;
}
internal class QmlNetPaintedItemCallbacksImpl
{
private List<GCHandle> _handles = new List<GCHandle>();
internal QmlNetPaintedItemCallbacksImpl()
{
_setRef = SetRef;
_handles.Add(GCHandle.Alloc(_setRef));
_paint = Paint;
_handles.Add(GCHandle.Alloc(_paint));
_heightChanged = HeightChanged;
_handles.Add(GCHandle.Alloc(_heightChanged));
_widthChanged = WidthChanged;
_handles.Add(GCHandle.Alloc(_widthChanged));
var cb = Callbacks();
Interop.QmlNetPaintedItem.SetCallbacks(ref cb);
}
~QmlNetPaintedItemCallbacksImpl()
{
foreach (var gcHandle in _handles)
{
gcHandle.Free();
}
_handles.Clear();
}
internal QmlNetPaintedItemCallbacks Callbacks()
{
return new QmlNetPaintedItemCallbacks
{
SetRef = Marshal.GetFunctionPointerForDelegate(_setRef),
Paint = Marshal.GetFunctionPointerForDelegate(_paint),
HeightChanged = Marshal.GetFunctionPointerForDelegate(_heightChanged),
WidthChanged = Marshal.GetFunctionPointerForDelegate(_widthChanged)
};
}
SetRefDel _setRef;
PaintDel _paint;
HeightChangedDel _heightChanged;
WidthChangedDel _widthChanged;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[SuppressUnmanagedCodeSecurity]
delegate void SetRefDel(ulong objectId, IntPtr @ref);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[SuppressUnmanagedCodeSecurity]
delegate void PaintDel(ulong objectId, IntPtr netQPainter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[SuppressUnmanagedCodeSecurity]
delegate void HeightChangedDel(ulong objectId, int height);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[SuppressUnmanagedCodeSecurity]
delegate void WidthChangedDel(ulong objectId, int height);
internal void SetRef(ulong objectId, IntPtr @ref)
{
object obj = null;
if (ObjectIdReferenceTracker.TryGetObjectFor(objectId, out obj))
{
(obj as QmlNetQuickPaintedItem)?.SetRef(@ref);
}
}
internal void Paint(ulong objectId, IntPtr netQPainter)
{
object obj = null;
if (ObjectIdReferenceTracker.TryGetObjectFor(objectId, out obj))
{
(obj as QmlNetQuickPaintedItem)?.Paint(new NetQPainter(netQPainter));
}
}
internal void HeightChanged(ulong objectId, int height)
{
object obj = null;
if (ObjectIdReferenceTracker.TryGetObjectFor(objectId, out obj))
{
(obj as QmlNetQuickPaintedItem)?.OnHeightChanged(height);
}
}
internal void WidthChanged(ulong objectId, int width)
{
object obj = null;
if (ObjectIdReferenceTracker.TryGetObjectFor(objectId, out obj))
{
(obj as QmlNetQuickPaintedItem)?.OnWidthChanged(width);
}
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct QmlNetPaintedItemCallbacks
{
public IntPtr SetRef;
public IntPtr Paint;
public IntPtr HeightChanged;
public IntPtr WidthChanged;
}
internal class QmlNetPaintedItemInterop
{
[NativeSymbol(Entrypoint = "qqmlnetpainteditembase_setCallbacks")]
public SetCallbacksDel SetCallbacks { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetCallbacksDel(ref QmlNetPaintedItemCallbacks callbacks);
[NativeSymbol(Entrypoint = "qqmlnetpainteditembase_update")]
public UpdateDel Update { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UpdateDel(IntPtr paintedItem);
[NativeSymbol(Entrypoint = "qqmlnetpainteditembase_getHeight")]
public GetHeightDel GetHeight { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetHeightDel(IntPtr paintedItem);
[NativeSymbol(Entrypoint = "qqmlnetpainteditembase_getWidth")]
public GetWidthDel GetWidth { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetWidthDel(IntPtr paintedItem);
[NativeSymbol(Entrypoint = "qqmlnetpainteditembase_paintToImage")]
public PaintToImageDel PaintToImage { get; set; }
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr PaintToImageDel(IntPtr paintedItem, out long sizeOut, [MarshalAs(LPWStr)] string format);
}
}