Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed Dec 18, 2024
1 parent 27016a2 commit bbd3dda
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#if __WASM__

using System;
using Microsoft.UI.Xaml.Media;
using Uno.Xaml;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Media;

[TestClass]
[RunsOnUIThread]
public class Given_GeometryData
{
[DataTestMethod]
[DataRow("", FillRule.EvenOdd, "")]
[DataRow("F0", FillRule.EvenOdd, "")]
[DataRow("F1", FillRule.Nonzero, "")]
[DataRow(" F1", FillRule.Nonzero, "")]
[DataRow(" F 1", FillRule.Nonzero, "")]
[DataRow("F1 M0 0", FillRule.Nonzero, "M0 0")]
[DataRow(" F1 M0 0", FillRule.Nonzero, " M0 0")]
[DataRow(" F 1 M0 0", FillRule.Nonzero, " M0 0")]
public void When_GeometryData_ParseData_Valid(string rawdata, FillRule rule, string data)
{
var result = GeometryData.ParseData(rawdata);

Assert.AreEqual(rule, result.FillRule);
Assert.AreEqual(data, result.Data);
}

[DataTestMethod]
[DataRow("F")]
[DataRow("F2")]
[DataRow("FF")]
[DataRow("F 2")]
[DataRow("F M0 0")]
public void When_GeometryData_ParseData_Invalid(string rawdata)
{
Assert.ThrowsException<XamlParseException>(() =>
GeometryData.ParseData(rawdata)
);
}
}
#endif
77 changes: 64 additions & 13 deletions src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Microsoft.UI.Xaml.Wasm;
using Uno.UI.Xaml;
using Uno.Xaml;

namespace Microsoft.UI.Xaml.Media
{
Expand All @@ -14,38 +16,87 @@ public class GeometryData : Geometry
//
// <Path Data="M0,0 L10,10 20,10 20,20, 10,20 Z" />

private const FillRule DefaultFillRule = FillRule.EvenOdd;

private readonly SvgElement _svgElement = new SvgElement("path");

public string Data { get; }

public FillRule FillRule { get; } = FillRule.EvenOdd;
public FillRule FillRule { get; } = DefaultFillRule;

public GeometryData()
{
}

public GeometryData(string data)
{
if ((data.StartsWith('F') || data.StartsWith('f')) && data.Length > 2)
(FillRule, Data) = ParseData(data);

WindowManagerInterop.SetSvgPathAttributes(_svgElement.HtmlId, FillRule == FillRule.Nonzero, Data);
}

internal static (FillRule FillRule, string Data) ParseData(string data)
{
if (data == "F")
{
// TODO: support spaces between the F and the 0/1
// uncompleted fill-rule block: missing value (just 'F' without 0/1 after)
throw new XamlParseException($"Failed to create a 'Data' from the text '{data}'.");
}

FillRule = data[1] == '1' ? FillRule.Nonzero : FillRule.EvenOdd;
Data = data.Substring(2);
if (data.Length >= 2 && TryExtractFillRule(data) is { } result)
{
return (result.Value, data[result.CurrentPosition..]);
}
else
{
Data = data;
return (DefaultFillRule, data);
}
}
private static (FillRule Value, int CurrentPosition)? TryExtractFillRule(string data)
{
// XamlParseException: 'Failed to create a 'Data' from the text 'F2'.' Line number '1' and line position '7'.
// "F1" just fill-rule without data is okay

_svgElement.SetAttribute("d", Data);
var rule = FillRule switch
// syntax: [fillRule] moveCommand drawCommand [drawCommand*] [closeCommand]
// Fill rule:
// There are two possible values for the optional fill rule: F0 or F1. (The F is always uppercase.)
// F0 is the default value; it produces EvenOdd fill behavior, so you don't typically specify it.
// Use F1 to get the Nonzero fill behavior. These fill values align with the values of the FillRule enumeration.
// -- https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/move-draw-commands-syntax#the-basic-syntax

// remark: despite explicitly stated: "The F is always uppercase", WinAppSDK is happily to accept lowercase 'f'.
// remark: you can use any number of whitespaces before/inbetween/after fill-rule/commands/command-parameters.

var inFillRule = false;
for (int i = 0; i < data.Length; i++)
{
var c = data[i];

if (char.IsWhiteSpace(c)) continue;

Check failure on line 75 in src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs#L75

Add curly braces around the nested statement(s) in this 'if' block.
if (inFillRule)
{
if (c is '1') return (FillRule.Nonzero, i + 1);

Check failure on line 78 in src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs#L78

Add curly braces around the nested statement(s) in this 'if' block.
if (c is '0') // legacy uno behavior would be to use an `else` instead here

Check failure on line 79 in src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/Media/GeometryData.wasm.cs#L79

Add curly braces around the nested statement(s) in this 'if' block.
return (FillRule.EvenOdd, i + 1);

throw new XamlParseException($"Failed to create a 'Data' from the text '{data}'.");
}
else if (c is 'F' or 'f')
{
inFillRule = true;
}
else
{
return null;
}
}

if (inFillRule)
{
FillRule.EvenOdd => "evenodd",
FillRule.Nonzero => "nonzero",
_ => "evenodd"
};
_svgElement.SetAttribute("fill-rule", rule);
// uncompleted fill-rule block: missing value (just 'F' without 0/1 after)
throw new XamlParseException($"Failed to create a 'Data' from the text '{data}'.");
}
return null;
}

internal override SvgElement GetSvgElement() => _svgElement;
Expand Down
9 changes: 2 additions & 7 deletions src/Uno.UI/UI/Xaml/Media/GeometryGroup.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Uno.UI.DataBinding;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml.Wasm;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Media
{
Expand All @@ -28,13 +29,7 @@ private void OnPropertyChanged(ManagedWeakReference? instance, DependencyPropert

if (property == FillRuleProperty)
{
var rule = FillRule switch
{
FillRule.EvenOdd => "evenodd",
FillRule.Nonzero => "nonzero",
_ => "evenodd"
};
_svgElement.SetAttribute("fill-rule", rule);
WindowManagerInterop.SetSvgFillRule(_svgElement.HtmlId, FillRule == FillRule.Nonzero);
}
else if (property == ChildrenProperty)
{
Expand Down
20 changes: 12 additions & 8 deletions src/Uno.UI/UI/Xaml/Media/PointCollection.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ namespace Microsoft.UI.Xaml.Media
{
public partial class PointCollection : IEnumerable<Point>, IList<Point>
{
internal string ToCssString()
internal double[] Flatten()
{
var sb = new StringBuilder();
foreach (var p in _points)
if (_points.Count == 0)
{
sb.Append(p.X.ToStringInvariant());
sb.Append(',');
sb.Append(p.Y.ToStringInvariant());
sb.Append(' '); // We will have an extra space at the end ... which is going to be ignored by browsers!
return Array.Empty<double>();
}
return sb.ToString();

var buffer = new double[_points.Count * 2];
for (int i = 0; i < _points.Count; i++)
{
buffer[i * 2] = _points[i].X;
buffer[i * 2 + 1] = _points[i].Y;
}

return buffer;
}
}
}
8 changes: 2 additions & 6 deletions src/Uno.UI/UI/Xaml/Shapes/Ellipse.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Uno.Extensions;
using Windows.Foundation;
using Microsoft.UI.Xaml.Wasm;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Shapes
{
Expand All @@ -18,14 +19,9 @@ protected override Size ArrangeOverride(Size finalSize)

var cx = shapeSize.Width / 2;
var cy = shapeSize.Height / 2;

var halfStrokeThickness = ActualStrokeThickness / 2;

_mainSvgElement.SetAttribute(
("cx", cx.ToStringInvariant()),
("cy", cy.ToStringInvariant()),
("rx", (cx - halfStrokeThickness).ToStringInvariant()),
("ry", (cy - halfStrokeThickness).ToStringInvariant()));
WindowManagerInterop.SetSvgEllipseAttributes(_mainSvgElement.HtmlId, cx, cy, cx - halfStrokeThickness, cy - halfStrokeThickness);

return finalSize;
}
Expand Down
9 changes: 3 additions & 6 deletions src/Uno.UI/UI/Xaml/Shapes/Line.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Uno.Extensions;
using Windows.Foundation;
using Microsoft.UI.Xaml.Wasm;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Shapes
{
Expand All @@ -13,12 +14,8 @@ public Line() : base("line")

protected override Size MeasureOverride(Size availableSize)
{
_mainSvgElement.SetAttribute(
("x1", X1.ToStringInvariant()),
("x2", X2.ToStringInvariant()),
("y1", Y1.ToStringInvariant()),
("y2", Y2.ToStringInvariant())
);
WindowManagerInterop.SetSvgLineAttributes(_mainSvgElement.HtmlId, X1, X2, Y1, Y2);

return MeasureAbsoluteShape(availableSize, this);
}

Expand Down
13 changes: 3 additions & 10 deletions src/Uno.UI/UI/Xaml/Shapes/Polygon.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,15 @@
using Windows.Foundation;
using Microsoft.UI.Xaml.Wasm;
using Uno.Extensions;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Shapes
{
partial class Polygon
{
protected override Size MeasureOverride(Size availableSize)
{
var points = Points;
if (points == null)
{
_mainSvgElement.RemoveAttribute("points");
}
else
{
_mainSvgElement.SetAttribute("points", points.ToCssString());
}
WindowManagerInterop.SetSvgPolyPoints(_mainSvgElement.HtmlId, Points?.Flatten());

return MeasureAbsoluteShape(availableSize, this);
}
Expand All @@ -42,7 +35,7 @@ internal override void OnPropertyChanged2(DependencyPropertyChangedEventArgs arg

private protected override string GetBBoxCacheKeyImpl() =>
Points is { } points
? ("polygone:" + points.ToCssString())
? ("polygone:" + string.Join(',', points.Flatten()))
: null;
}
}
13 changes: 3 additions & 10 deletions src/Uno.UI/UI/Xaml/Shapes/Polyline.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,15 @@
using Uno.Extensions;
using Microsoft.UI.Xaml.Media;
using System.Collections.Generic;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Shapes
{
partial class Polyline
{
protected override Size MeasureOverride(Size availableSize)
{
var points = Points;
if (points == null)
{
_mainSvgElement.RemoveAttribute("points");
}
else
{
_mainSvgElement.SetAttribute("points", points.ToCssString());
}
WindowManagerInterop.SetSvgPolyPoints(_mainSvgElement.HtmlId, Points?.Flatten());

return MeasureAbsoluteShape(availableSize, this);
}
Expand All @@ -46,7 +39,7 @@ internal override void OnPropertyChanged2(DependencyPropertyChangedEventArgs arg

private protected override string GetBBoxCacheKeyImpl() =>
Points is { } points
? ("polygone:" + points.ToCssString())
? ("polyline:" + string.Join(',', points.Flatten()))
: null;
}
}
16 changes: 10 additions & 6 deletions src/Uno.UI/UI/Xaml/Shapes/Rectangle.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Windows.Foundation;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Wasm;
using Uno.UI.Xaml;

namespace Microsoft.UI.Xaml.Shapes
{
Expand All @@ -16,15 +17,18 @@ public Rectangle() : base("rect")
protected override Size ArrangeOverride(Size finalSize)
{
UpdateRender();
_mainSvgElement.SetAttribute(
("rx", RadiusX.ToStringInvariant()),
("ry", RadiusY.ToStringInvariant())
);
var (shapeSize, renderingArea) = ArrangeRelativeShape(finalSize);

Uno.UI.Xaml.WindowManagerInterop.SetSvgElementRect(_mainSvgElement.HtmlId, renderingArea);
WindowManagerInterop.SetSvgRectangleAttributes(
_mainSvgElement.HtmlId,
renderingArea.X, renderingArea.Y, renderingArea.Width, renderingArea.Height,
RadiusX, RadiusY
);

_mainSvgElement.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, finalSize.Width, finalSize.Height) };
_mainSvgElement.Clip = new RectangleGeometry()
{
Rect = new Rect(0, 0, finalSize.Width, finalSize.Height)
};

return finalSize;
}
Expand Down
Loading

0 comments on commit bbd3dda

Please sign in to comment.