Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented LineHeight on Label #538

Merged
merged 2 commits into from
Mar 17, 2021
Merged
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 @@ -248,6 +248,7 @@ void UpdateCharacterSpacing()
}
}

[PortHandler]
void UpdateLineHeight()
{
_lastSizeRequest = null;
Expand Down
1 change: 1 addition & 0 deletions src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ void UpdateCharacterSpacing()
_perfectSizeValid = false;
}

[PortHandler("Partially. Mapped LineHeight")]
void UpdateText()
{
if (IsElementOrControlEmpty)
Expand Down
1 change: 1 addition & 0 deletions src/Controls/samples/Controls.Sample/Pages/MainPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ void SetupMauiLayout()
verticalStack.Add(new Label { Text = loremIpsum, MaxLines = 2 });
verticalStack.Add(new Label { Text = loremIpsum, LineBreakMode = LineBreakMode.TailTruncation });
verticalStack.Add(new Label { Text = loremIpsum, MaxLines = 2, LineBreakMode = LineBreakMode.TailTruncation });
verticalStack.Add(new Label { Text = "This should have five times the line height!", LineHeight = 5});


var paddingButton = new Button
Expand Down
7 changes: 6 additions & 1 deletion src/Core/src/Core/ILabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ public interface ILabel : IView, IText, ITextAlignment, IPadding

/// <summary>
/// Gets the text decoration applied to the Label.
/// Underline and strikethrough text decorations can be applied.
/// Underline and strike-through text decorations can be applied.
/// </summary>
TextDecorations TextDecorations { get; }
/// <summary>
/// Gets the line height applied to the Label.
/// Underline and strike-through text decorations can be applied.
/// </summary>
double LineHeight { get; }
}
}
8 changes: 8 additions & 0 deletions src/Core/src/Handlers/Label/LabelHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace Microsoft.Maui.Handlers
public partial class LabelHandler : AbstractViewHandler<ILabel, TextView>
{
static Color DefaultTextColor { get; set; }
static float LineSpacingAddDefault { get; set; }
static float LineSpacingMultDefault { get; set; }

protected override TextView CreateNativeView() => new TextView(Context);

Expand All @@ -20,6 +22,8 @@ protected override void SetupDefaults(TextView nativeView)
{
DefaultTextColor = Color.FromUint((uint)nativeView.TextColors.DefaultColor);
}
LineSpacingAddDefault = nativeView.LineSpacingExtra;
LineSpacingMultDefault = nativeView.LineSpacingMultiplier;
}

public static void MapText(LabelHandler handler, ILabel label)
Expand Down Expand Up @@ -70,5 +74,9 @@ public static void MapFont(LabelHandler handler, ILabel label)

handler.TypedNativeView?.UpdateFont(label, fontManager);
}
public static void MapLineHeight(LabelHandler handler, ILabel label)
{
handler.TypedNativeView?.UpdateLineHeight(label, LineSpacingAddDefault, LineSpacingMultDefault);
}
}
}
1 change: 1 addition & 0 deletions src/Core/src/Handlers/Label/LabelHandler.Standard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public static void MapLineBreakMode(LabelHandler handler, ILabel label) { }
public static void MapTextDecorations(LabelHandler handler, ILabel label) { }
public static void MapMaxLines(IViewHandler handler, ILabel label) { }
public static void MapPadding(LabelHandler handler, ILabel label) { }
public static void MapLineHeight(LabelHandler handler, ILabel label) { }
}
}
3 changes: 2 additions & 1 deletion src/Core/src/Handlers/Label/LabelHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public partial class LabelHandler
[nameof(ILabel.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
[nameof(ILabel.LineBreakMode)] = MapLineBreakMode,
[nameof(ILabel.Padding)] = MapPadding,
[nameof(ILabel.TextDecorations)] = MapTextDecorations
[nameof(ILabel.TextDecorations)] = MapTextDecorations,
[nameof(ILabel.LineHeight)] = MapLineHeight
};

public LabelHandler() : base(LabelMapper)
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Handlers/Label/LabelHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ public static void MapFont(LabelHandler handler, ILabel label)

handler.TypedNativeView?.UpdateFont(label, fontManager);
}

public static void MapLineHeight(LabelHandler handler, ILabel label)
{
handler.TypedNativeView?.UpdateLineHeight(label);
}
}
}
8 changes: 8 additions & 0 deletions src/Core/src/Platform/Android/TextViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,13 @@ internal static void SetLineBreakMode(this TextView textView, ILabel label)
textView.SetSingleLine(singleLine);
textView.SetMaxLines(maxLines);
}

internal static void UpdateLineHeight(this TextView textView, ILabel label, float lineSpacingAddDefault, float lineSpacingMultDefault)
{
if (label.LineHeight == - 1)
textView.SetLineSpacing(lineSpacingAddDefault, lineSpacingMultDefault);
else if (label.LineHeight >= 0)
textView.SetLineSpacing(0, (float)label.LineHeight);
}
}
}
60 changes: 34 additions & 26 deletions src/Core/src/Platform/iOS/LabelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,6 @@ public static void UpdatePadding(this MauiLabel nativeLabel, ILabel label)
(float)label.Padding.Right);
}

public static void UpdateTextDecorations(this UILabel nativeLabel, ILabel label)
{
if (nativeLabel.AttributedText != null && !(nativeLabel.AttributedText?.Length > 0))
return;

var textDecorations = label?.TextDecorations;

var newAttributedText = nativeLabel.AttributedText != null ? new NSMutableAttributedString(nativeLabel.AttributedText) : new NSMutableAttributedString(label?.Text ?? string.Empty);
var strikeThroughStyleKey = UIStringAttributeKey.StrikethroughStyle;
var underlineStyleKey = UIStringAttributeKey.UnderlineStyle;

var range = new NSRange(0, newAttributedText.Length);

if ((textDecorations & TextDecorations.Strikethrough) == 0)
newAttributedText.RemoveAttribute(strikeThroughStyleKey, range);
else
newAttributedText.AddAttribute(strikeThroughStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);

if ((textDecorations & TextDecorations.Underline) == 0)
newAttributedText.RemoveAttribute(underlineStyleKey, range);
else
newAttributedText.AddAttribute(underlineStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);

nativeLabel.AttributedText = newAttributedText;
}

internal static void SetLineBreakMode(this UILabel nativeLabel, ILabel label)
{
int maxLines = label.MaxLines;
Expand Down Expand Up @@ -139,5 +113,39 @@ internal static void SetLineBreakMode(this UILabel nativeLabel, ILabel label)

nativeLabel.Lines = maxLines;
}

public static void UpdateTextDecorations(this UILabel nativeLabel, ILabel label)
{
if (nativeLabel.AttributedText != null && !(nativeLabel.AttributedText?.Length > 0))
return;

var textDecorations = label?.TextDecorations;

var newAttributedText = nativeLabel.AttributedText != null ? new NSMutableAttributedString(nativeLabel.AttributedText) : new NSMutableAttributedString(label?.Text ?? string.Empty);
var strikeThroughStyleKey = UIStringAttributeKey.StrikethroughStyle;
var underlineStyleKey = UIStringAttributeKey.UnderlineStyle;

var range = new NSRange(0, newAttributedText.Length);

if ((textDecorations & TextDecorations.Strikethrough) == 0)
newAttributedText.RemoveAttribute(strikeThroughStyleKey, range);
else
newAttributedText.AddAttribute(strikeThroughStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);

if ((textDecorations & TextDecorations.Underline) == 0)
newAttributedText.RemoveAttribute(underlineStyleKey, range);
else
newAttributedText.AddAttribute(underlineStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range);

nativeLabel.AttributedText = newAttributedText;
}

internal static void UpdateLineHeight(this UILabel nativeLabel, ILabel label)
{
var modAttrText = nativeLabel.AttributedText?.WithLineHeight(label.LineHeight);

if (modAttrText != null)
nativeLabel.AttributedText = modAttrText;
}
}
}
25 changes: 25 additions & 0 deletions src/Core/src/Platform/iOS/NSAttributedStringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,30 @@ public static class NSAttributedStringExtensions
);
return mutableAttributedString;
}

public static NSMutableAttributedString? WithLineHeight(this NSAttributedString attributedString, double lineHeight)
{
if (attributedString == null || attributedString.Length == 0)
return null;

var attribute = (NSParagraphStyle) attributedString.GetAttribute(UIStringAttributeKey.ParagraphStyle, 0, out _);

// if we need to un-set the line height but there is no attribute to modify then we do nothing
if (lineHeight == -1 && attribute == null)
return null;

var mutableParagraphStyle = new NSMutableParagraphStyle();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hartez Sorry about the mistake here 😅. I was working blind in VS with no IntelliSense. You can add mutableParagraphStyle.SetParagraphStyle(attribute) right after this line so it copies whatever paragraph style was already there instead of overriding it with the default.

mutableParagraphStyle.LineHeightMultiple = new System.nfloat(lineHeight >= 0 ? lineHeight : -1);

var mutableAttributedString = new NSMutableAttributedString(attributedString);
mutableAttributedString.AddAttribute
(
UIStringAttributeKey.ParagraphStyle,
mutableParagraphStyle ,
new NSRange(0, mutableAttributedString.Length)
);

return mutableAttributedString;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,31 @@ public async Task TextDecorationsInitializesCorrectly()
values.NativeViewValue.AssertHasFlag(expectedValue);
}

[Fact(DisplayName = "LineHeight Initializes Correctly")]
public async Task LineHeightInitializesCorrectly()
{
var xplatLineHeight = 1.5d;

var labelHandler = new LabelStub()
{
LineHeight = xplatLineHeight
};

var values = await GetValueAsync(labelHandler, (handler) =>
{
return new
{
ViewValue = labelHandler.LineHeight,
NativeViewValue = GetNativeLineHeight(handler)
};
});

float expectedValue = 1.5f;

Assert.Equal(xplatLineHeight, values.ViewValue);
Assert.Equal(expectedValue, values.NativeViewValue);
}

TextView GetNativeLabel(LabelHandler labelHandler) =>
(TextView)labelHandler.View;

Expand Down Expand Up @@ -188,5 +213,8 @@ TextUtils.TruncateAt GetNativeLineBreakMode(LabelHandler labelHandler) =>

PaintFlags GetNativeTextDecorations(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).PaintFlags;

float GetNativeLineHeight(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).LineSpacingMultiplier;
}
}
41 changes: 41 additions & 0 deletions src/Core/tests/DeviceTests/Handlers/Label/LabelHandlerTests.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks;
using Foundation;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -118,6 +119,31 @@ public async Task TextDecorationsInitializesCorrectly()
Assert.NotNull(values.NativeViewValue);
}

[Fact(DisplayName = "LineHeight Initializes Correctly")]
public async Task LineHeightInitializesCorrectly()
{
var xplatLineHeight = 1.5d;

var labelHandler = new LabelStub()
{
Text = "test",
LineHeight = xplatLineHeight
};

var values = await GetValueAsync(labelHandler, (handler) =>
{
return new
{
ViewValue = labelHandler.LineHeight,
NativeViewValue = GetNativeLineHeight(handler)
};
});

nfloat expectedValue = new nfloat(1.5f);
Assert.Equal(xplatLineHeight, values.ViewValue);
Assert.Equal(expectedValue, values.NativeViewValue);
}

UILabel GetNativeLabel(LabelHandler labelHandler) =>
(UILabel)labelHandler.View;

Expand Down Expand Up @@ -162,5 +188,20 @@ UILineBreakMode GetNativeLineBreakMode(LabelHandler labelHandler) =>

NSAttributedString GetNativeTextDecorations(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).AttributedText;

nfloat GetNativeLineHeight(LabelHandler labelHandler)
{
var attrText = GetNativeLabel(labelHandler).AttributedText;

if(attrText == null)
return new nfloat(-1.0f);

var paragraphStyle = (NSParagraphStyle)attrText.GetAttribute(UIStringAttributeKey.ParagraphStyle, 0, out _);

if(paragraphStyle == null)
return new nfloat(-1.0f);

return paragraphStyle.LineHeightMultiple;
}
}
}
2 changes: 2 additions & 0 deletions src/Core/tests/DeviceTests/Stubs/LabelStub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ public partial class LabelStub : StubBase, ILabel
public TextDecorations TextDecorations { get; set; }

public int MaxLines { get; set; } = -1;

public double LineHeight { get; set; } = -1;
}
}