Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,22 @@ public static NSAttributedString ToNSAttributedString(
{
style.LineHeightMultiple = new nfloat(lineHeight);
}

style.Alignment = defaultHorizontalAlignment switch

if (span.Parent is FormattedString formattedString && formattedString.Parent is Label parentLabel)
{
var flowDirection = parentLabel.FlowDirection;
style.Alignment = defaultHorizontalAlignment.ToPlatformHorizontal(flowDirection.ToUIUserInterfaceLayoutDirection());
}
else
{
TextAlignment.Start => UITextAlignment.Left,
TextAlignment.Center => UITextAlignment.Center,
TextAlignment.End => UITextAlignment.Right,
_ => UITextAlignment.Left
};
style.Alignment = defaultHorizontalAlignment switch
{
TextAlignment.Start => UITextAlignment.Left,
TextAlignment.Center => UITextAlignment.Center,
TextAlignment.End => UITextAlignment.Right,
_ => UITextAlignment.Left
};
}

var font = span.ToFont(defaultFontSize);
if (font.IsDefault && defaultFont.HasValue)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
143 changes: 143 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue31480.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 31480, "RightToLeft does not apply for FormattedText", PlatformAffected.iOS | PlatformAffected.macOS)]
public class Issue31480 : TestContentPage
{
const string ToggleButton = "ToggleFlowDirection";
const string FormattedTextLabel = "FormattedTextLabel";

Label _formattedTextLabel;
bool _isRtl = false;

protected override void Init()
{
var layout = new StackLayout { Padding = new Thickness(20), Spacing = 20 };

var instructions = new Label
{
Text = "This test demonstrates FormattedText alignment with FlowDirection. " +
"Tap the button to toggle between LTR and RTL. " +
"The text should align properly based on the FlowDirection.",
FontSize = 14
};

layout.Children.Add(instructions);

// Create FormattedText label
var formattedString = new FormattedString();
formattedString.Spans.Add(new Span
{
Text = "This is RTL formatted text that should align correctly", FontSize = 16
});

_formattedTextLabel = new Label
{
AutomationId = FormattedTextLabel,
FormattedText = formattedString,
FlowDirection = FlowDirection.LeftToRight,
HorizontalTextAlignment = TextAlignment.Start,
BackgroundColor = Colors.LightGray,
Padding = new Thickness(10),
Margin = new Thickness(0, 10)
};

layout.Children.Add(_formattedTextLabel);

// Add a status label to show current flow direction
var statusLabel = new Label
{
Text = "Current FlowDirection: LeftToRight", FontSize = 12, TextColor = Colors.Blue
};

layout.Children.Add(statusLabel);

// Add toggle button
var toggleButton = new Button
{
AutomationId = ToggleButton,
Text = "Toggle FlowDirection",
BackgroundColor = Colors.Blue,
TextColor = Colors.White
};

toggleButton.Clicked += (sender, e) =>
{
_isRtl = !_isRtl;
_formattedTextLabel.FlowDirection = _isRtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
statusLabel.Text = $"Current FlowDirection: {_formattedTextLabel.FlowDirection}";
};

layout.Children.Add(toggleButton);

// Add additional test labels for different scenarios
AddTestScenarios(layout);

Content = layout;
}

private void AddTestScenarios(StackLayout layout)
{
// Test scenario 1: RTL with Start alignment
var formattedStringRtlStart = new FormattedString();
formattedStringRtlStart.Spans.Add(new Span { Text = "RTL Start alignment test", FontSize = 14 });

var rtlStartLabel = new Label
{
AutomationId = "FormattedTextStartRTL",
FormattedText = formattedStringRtlStart,
FlowDirection = FlowDirection.RightToLeft,
HorizontalTextAlignment = TextAlignment.Start,
BackgroundColor = Colors.LightYellow,
Padding = new Thickness(10),
Margin = new Thickness(0, 5)
};

layout.Children.Add(new Label
{
Text = "RTL with Start alignment:", FontSize = 12, FontAttributes = FontAttributes.Bold
});
layout.Children.Add(rtlStartLabel);

// Test scenario 2: LTR with Start alignment
var formattedStringLtrStart = new FormattedString();
formattedStringLtrStart.Spans.Add(new Span { Text = "LTR Start alignment test", FontSize = 14 });

var ltrStartLabel = new Label
{
AutomationId = "FormattedTextStartLTR",
FormattedText = formattedStringLtrStart,
FlowDirection = FlowDirection.LeftToRight,
HorizontalTextAlignment = TextAlignment.Start,
BackgroundColor = Colors.LightGreen,
Padding = new Thickness(10),
Margin = new Thickness(0, 5)
};

layout.Children.Add(new Label
{
Text = "LTR with Start alignment:", FontSize = 12, FontAttributes = FontAttributes.Bold
});
layout.Children.Add(ltrStartLabel);

// Test scenario 3: RTL label for main test
var formattedStringRtl = new FormattedString();
formattedStringRtl.Spans.Add(new Span { Text = "Fixed RTL FormattedText alignment", FontSize = 14 });

var rtlLabel = new Label
{
AutomationId = "FormattedTextRTLLabel",
FormattedText = formattedStringRtl,
FlowDirection = FlowDirection.RightToLeft,
HorizontalTextAlignment = TextAlignment.Start,
BackgroundColor = Colors.LightPink,
Padding = new Thickness(10),
Margin = new Thickness(0, 5)
};

layout.Children.Add(new Label
{
Text = "RTL FormattedText (should align right):", FontSize = 12, FontAttributes = FontAttributes.Bold
});
layout.Children.Add(rtlLabel);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue31480 : _IssuesUITest
{
public Issue31480(TestDevice testDevice) : base(testDevice)
{
}

public override string Issue => "RightToLeft does not apply for FormattedText";

[Test]
[Category(UITestCategories.Label)]
public void FormattedTextToggleFlowDirectionTest()
{
Exception? exception = null;

// Verify initial state (LTR)
App.WaitForElement("FormattedTextLabel");
App.WaitForElement("ToggleFlowDirection");
VerifyScreenshotOrSetException(ref exception, "Issue81_InitialLTR");

// Toggle to RTL
App.Tap("ToggleFlowDirection");
VerifyScreenshotOrSetException(ref exception, "Issue81_ToggledRTL");

// Toggle back to LTR
App.Tap("ToggleFlowDirection");
VerifyScreenshotOrSetException(ref exception, "Issue81_ToggledBackLTR");

if (exception != null)
{
throw exception;
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/Core/src/Platform/iOS/FlowDirectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,19 @@ public static FlowDirection ToFlowDirection(this UIUserInterfaceLayoutDirection
throw new NotSupportedException($"ToFlowDirection: {direction}");
}
}

// TODO: Make it public in .NET 10
internal static UIUserInterfaceLayoutDirection ToUIUserInterfaceLayoutDirection(this FlowDirection direction)
{
switch (direction)
{
case FlowDirection.LeftToRight:
return UIUserInterfaceLayoutDirection.LeftToRight;
case FlowDirection.RightToLeft:
return UIUserInterfaceLayoutDirection.RightToLeft;
default:
throw new NotSupportedException($"ToUIUserInterfaceLayoutDirection: {direction}");
}
}
}
}
36 changes: 36 additions & 0 deletions src/Core/tests/DeviceTests/Handlers/Label/LabelHandlerTests.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Foundation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
Expand All @@ -14,6 +15,41 @@ namespace Microsoft.Maui.DeviceTests
{
public partial class LabelHandlerTests
{
[Theory(DisplayName = "FormattedText HorizontalTextAlignment adjusts for FlowDirection")]
[InlineData(TextAlignment.Start, FlowDirection.LeftToRight, UITextAlignment.Left)]
[InlineData(TextAlignment.End, FlowDirection.LeftToRight, UITextAlignment.Right)]
[InlineData(TextAlignment.Start, FlowDirection.RightToLeft, UITextAlignment.Right)]
[InlineData(TextAlignment.End, FlowDirection.RightToLeft, UITextAlignment.Left)]
public async Task FormattedTextHorizontalTextAlignmentAdjustsForFlowDirection(TextAlignment alignment, FlowDirection flowDirection, UITextAlignment expected)
{
var formattedString = new FormattedString();
formattedString.Spans.Add(new Span { Text = "This is formatted TEXT!" });

var label = new LabelStub
{
FormattedText = formattedString,
HorizontalTextAlignment = alignment,
FlowDirection = flowDirection
};

var handler = await CreateHandlerAsync(label);
var platformLabel = GetPlatformLabel(handler);

var actualAlignment = await InvokeOnMainThreadAsync(() =>
{
// Get the alignment from the attributed text's paragraph style
var attributedText = platformLabel.AttributedText;
if (attributedText != null && attributedText.Length > 0)
{
var paragraphStyle = (NSParagraphStyle)attributedText.GetAttribute(UIStringAttributeKey.ParagraphStyle, 0, out _);
return paragraphStyle?.Alignment ?? platformLabel.TextAlignment;
}
return platformLabel.TextAlignment;
});

Assert.Equal(expected, actualAlignment);
}

[Fact(DisplayName = "Horizontal TextAlignment Updates Correctly")]
public async Task HorizontalTextAlignmentInitializesCorrectly()
{
Expand Down
3 changes: 3 additions & 0 deletions src/Core/tests/DeviceTests/Stubs/LabelStub.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;

namespace Microsoft.Maui.DeviceTests.Stubs
{
public partial class LabelStub : StubBase, ILabel
{
public FormattedString FormattedText { get; set; }

public string Text { get; set; }

public TextType TextType { get; set; } = TextType.Text;
Expand Down
Loading