Skip to content

Commit

Permalink
Implement gestures on Windows Spans
Browse files Browse the repository at this point in the history
  • Loading branch information
jsuarezruiz authored and mattleibow committed Feb 15, 2024
1 parent ee32869 commit bc0e0aa
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 13 deletions.
97 changes: 94 additions & 3 deletions src/Controls/src/Core/Label/Label.Windows.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
#nullable disable
using System.Collections.Generic;
using Microsoft.Maui.Controls.Platform;
using Microsoft.UI.Xaml.Controls;

namespace Microsoft.Maui.Controls
{
public partial class Label
{
public static void MapDetectReadingOrderFromContent(LabelHandler handler, Label label) => MapDetectReadingOrderFromContent((ILabelHandler)handler, label);
public static void MapText(LabelHandler handler, Label label) => MapText((ILabelHandler)handler, label);
TextBlock _textBlock;

private protected override void OnHandlerChangedCore()
{
base.OnHandlerChangedCore();

if (Handler is not null)
{
if (Handler is LabelHandler labelHandler && labelHandler.PlatformView is TextBlock textBlock)
{
_textBlock = textBlock;
_textBlock.SizeChanged += OnSizeChanged;
}
}
else
{
if (_textBlock is not null)
{
_textBlock.SizeChanged -= OnSizeChanged;
_textBlock = null;
}
}
}

public static void MapDetectReadingOrderFromContent(LabelHandler handler, Label label) =>
MapDetectReadingOrderFromContent((ILabelHandler)handler, label);

public static void MapTextType(LabelHandler handler, Label label) =>
MapTextType((ILabelHandler)handler, label);

public static void MapText(LabelHandler handler, Label label) =>
MapText((ILabelHandler)handler, label);

public static void MapDetectReadingOrderFromContent(ILabelHandler handler, Label label) =>
Platform.TextBlockExtensions.UpdateDetectReadingOrderFromContent(handler.PlatformView, label);
Expand All @@ -18,6 +50,65 @@ public static void MapLineBreakMode(ILabelHandler handler, Label label) =>
handler.PlatformView?.UpdateLineBreakMode(label);

public static void MapMaxLines(ILabelHandler handler, Label label) =>
handler.PlatformView?.UpdateMaxLines(label);
handler.PlatformView?.UpdateMaxLines(label);

void OnSizeChanged(object sender, UI.Xaml.SizeChangedEventArgs e)
{
RecalculateSpanPositions();
}

void RecalculateSpanPositions()
{
if (Handler is LabelHandler labelHandler)
{
var platformView = labelHandler.PlatformView;
var virtualView = labelHandler.VirtualView as Label;

if (platformView is null || virtualView is null)
return;

IList<double> inlineHeights = GetInlineHeights();
platformView.RecalculateSpanPositions(virtualView, inlineHeights);
}
}

IList<double> GetInlineHeights()
{
IList<double> inlineHeights = new List<double>();

if (Handler is LabelHandler labelHandler)
{
var platformView = labelHandler.PlatformView;
var virtualView = labelHandler.VirtualView as Label;

if (virtualView is not null)
{
FormattedString formatted = virtualView.FormattedText;

if (formatted is not null)
{
var fontManager = virtualView.RequireFontManager();
platformView.Inlines.Clear();

// Have to implement a measure here, otherwise inline.ContentStart and ContentEnd will be null, when used in RecalculatePositions
platformView.Measure(new global::Windows.Foundation.Size(double.MaxValue, double.MaxValue));

var heights = new List<double>();
for (var i = 0; i < formatted.Spans.Count; i++)
{
var span = formatted.Spans[i];

var run = span.ToRunAndColorsTuple(fontManager).Item1;
heights.Add(platformView.FindDefaultLineHeight(run));
platformView.Inlines.Add(run);
}

inlineHeights = heights;
}
}
}

return inlineHeights;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Graphics;
using Microsoft.UI.Xaml.Controls;
Expand Down Expand Up @@ -127,5 +128,56 @@ public static Tuple<Run, Color, Color> ToRunAndColorsTuple(

return Tuple.Create(run, span.TextColor, span.BackgroundColor);
}

public static void RecalculateSpanPositions(this TextBlock control, Label element, IList<double> inlineHeights)
{
if (element?.FormattedText?.Spans == null
|| element.FormattedText.Spans.Count == 0)
return;

var labelWidth = control.ActualWidth;

if (labelWidth <= 0 || control.Height <= 0)
return;

for (int i = 0; i < element.FormattedText.Spans.Count; i++)
{
var span = element.FormattedText.Spans[i];

var inline = control.Inlines.ElementAt(i);

// TODO: Alternatives?. GetCharacterRect always return an empty Rect
var startRect = inline.ContentStart.GetCharacterRect(LogicalDirection.Forward);
var endRect = inline.ContentEnd.GetCharacterRect(LogicalDirection.Forward);

var defaultLineHeight = inlineHeights[i];
var yaxis = startRect.Top;

var lineHeights = new List<double>();

while (yaxis < endRect.Bottom)
{
double lineHeight;

if (yaxis == startRect.Top) // First Line
{
lineHeight = startRect.Bottom - startRect.Top;
}
else if (yaxis != endRect.Top) // Middle Line(s)
{
lineHeight = defaultLineHeight;
}
else // Bottom Line
{
lineHeight = endRect.Bottom - endRect.Top;
}

lineHeights.Add(lineHeight);
yaxis += lineHeight;
}

((ISpatialElement)span).Region = Region.FromLines(lineHeights.ToArray(), labelWidth, startRect.X, endRect.X + endRect.Width, startRect.Top).Inflate(10);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommand.get -> S
Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommand.set -> void
Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandParameter.get -> object!
Microsoft.Maui.Controls.PointerGestureRecognizer.PointerReleasedCommandParameter.set -> void
static Microsoft.Maui.Controls.Platform.FormattedStringExtensions.RecalculateSpanPositions(this Microsoft.UI.Xaml.Controls.TextBlock! control, Microsoft.Maui.Controls.Label! element, System.Collections.Generic.IList<double>! inlineHeights) -> void
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
5 changes: 0 additions & 5 deletions src/Controls/tests/UITests/Tests/Issues/Issue3525.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public void SpanRegionClicking()
Assert.Ignore("Click (x, y) pointer type mouse is not implemented.");
}

if (Device == TestDevice.Windows)
{
Assert.Ignore("This test is failing on Windows because the feature is not yet implemented: https://github.com/dotnet/maui/pull/17731");
}

var label = App.WaitForElement(kLabelTestAutomationId);
var location = label.GetRect();

Expand Down
5 changes: 0 additions & 5 deletions src/Controls/tests/UITests/Tests/LabelUITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ public void SpanTapped()
Assert.Ignore("Click (x, y) pointer type mouse is not implemented.");
}

if (Device == TestDevice.Windows)
{
Assert.Ignore("This test is failing on Windows because the feature is not yet implemented: https://github.com/dotnet/maui/issues/4734");
}

var remote = new EventViewContainerRemote(UITestContext, Test.FormattedString.SpanTapped);
remote.GoTo();

Expand Down

0 comments on commit bc0e0aa

Please sign in to comment.