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

Make Label display HTML from a string #4527

Merged
merged 16 commits into from Aug 28, 2019
@@ -0,0 +1,88 @@
using System;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
using System.Linq;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.Label)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.None, 0, "Implementation of Label TextType", PlatformAffected.All)]
public class LabelTextType : TestContentPage
{
protected override void Init()
{
var label = new Label
{
AutomationId = "TextTypeLabel",
Text = "<h1>Hello World!</h1>"
};

var button = new Button
{
AutomationId = "ToggleTextTypeButton",
Text = "Toggle HTML/Plain"
};

button.Clicked += (s, a) =>
{
label.TextType = label.TextType == TextType.Html ? TextType.Text : TextType.Html;
};


Label htmlLabel = new Label() { TextType = TextType.Html };
Label normalLabel = new Label();
Label nullLabel = new Label() { TextType = TextType.Html };

Button toggle = new Button()
{
Text = "Toggle some more things",
Command = new Command(() =>
{
htmlLabel.Text = $"<b>{DateTime.UtcNow}</b>";
normalLabel.Text = $"<b>{DateTime.UtcNow}</b>";

if (String.IsNullOrWhiteSpace(nullLabel.Text))
nullLabel.Text = "hi there";
else
nullLabel.Text = null;
})
};


var stacklayout = new StackLayout();
stacklayout.Children.Add(label);
stacklayout.Children.Add(button);
stacklayout.Children.Add(htmlLabel);
stacklayout.Children.Add(normalLabel);
stacklayout.Children.Add(nullLabel);
stacklayout.Children.Add(toggle);

Content = stacklayout;
}

#if UITEST
[Test]
public void LabelToggleHtmlAndPlainTextTest()
{
RunningApp.WaitForElement ("TextTypeLabel");
RunningApp.Screenshot ("I see plain text");

Assert.IsTrue(RunningApp.Query("TextTypeLabel").FirstOrDefault()?.Text == "<h1>Hello World!</h1>");

RunningApp.Tap("ToggleTextTypeButton");
RunningApp.Screenshot ("I see HTML text");

Assert.IsFalse(RunningApp.Query("TextTypeLabel").FirstOrDefault()?.Text.Contains("<h1>") ?? true);
}
#endif
}
}
@@ -1023,6 +1023,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue6738.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GitHub6926.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5503.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LabelTextType.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
@@ -236,6 +236,40 @@ protected override void Build (StackLayout stackLayout)
Padding = new Thickness(40, 20)
}
);

var htmlLabelContainer = new ViewContainer<Label>(Test.Label.TextType,
new Label
{
Text = "<h1>Hello world!</h1>",
TextType = TextType.Html
});

var htmlLabelMultipleLinesContainer = new ViewContainer<Label>(Test.Label.TextType,
new Label
{
Text = "<h1>Hello world!</h1><p>Lorem <strong>ipsum</strong> bla di bla <i>blabla</i> blablabl ablabla blablablablabl ablabl ablablabl ablablabla blablablablablablab lablablabla blablab lablablabla blablabl ablablablab lablabla blab lablablabla blablab lablabla blablablablab lablabla blablab lablablabl ablablabla blablablablablabla blablabla</p>",
TextType = TextType.Html,
MaxLines = 3
});

var toggleLabel = new Label
{
TextType = TextType.Html,
Text = "<h1>Hello world!</h1><p>Lorem <strong>ipsum</strong></p>"

};

var gestureRecognizer = new TapGestureRecognizer();

gestureRecognizer.Tapped += (s, a) =>
{
toggleLabel.TextType = toggleLabel.TextType == TextType.Html ? TextType.Text : TextType.Html;
};

toggleLabel.GestureRecognizers.Add(gestureRecognizer);

var toggleHtmlPlainTextLabelContainer = new ViewContainer<Label>(Test.Label.TextType,
toggleLabel);

Add (namedSizeMediumBoldContainer);
Add (namedSizeMediumItalicContainer);
@@ -272,6 +306,9 @@ protected override void Build (StackLayout stackLayout)
Add (maxlinesTailTruncContainer);
Add (maxlinesWordWrapContainer);
Add(paddingContainer);
Add (htmlLabelContainer);
Add (htmlLabelMultipleLinesContainer);
Add (toggleHtmlPlainTextLabelContainer);
}
}
}
@@ -89,6 +89,9 @@ public class Label : View, IFontElement, ITextElement, ITextAlignmentElement, IL
});

public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;

public static readonly BindableProperty TextTypeProperty = BindableProperty.Create(nameof(TextType), typeof(TextType), typeof(Label), TextType.Text,
propertyChanged: (bindable, oldvalue, newvalue) => ((Label)bindable).InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged));

readonly Lazy<PlatformConfigurationRegistry<Label>> _platformConfigurationRegistry;

@@ -212,6 +215,12 @@ public Thickness Padding
get { return (Thickness)GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}

public TextType TextType
{
get => (TextType)GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}

double IFontElement.FontSizeDefaultValueCreator() =>
Device.GetNamedSize(NamedSize.Default, (Label)this);
@@ -0,0 +1,8 @@
namespace Xamarin.Forms
{
public enum TextType
{
Text,
Html
}
}
@@ -634,7 +634,8 @@ public enum Label
VerticalTextAlignmentStart,
VerticalTextAlignmentCenter,
VerticalTextAlignmentEnd,
MaxLines
MaxLines,
TextType
}

public enum MasterDetailPage
@@ -251,6 +251,7 @@ protected virtual void OnElementChanged(ElementChangedEventArgs<Label> e)
UpdateGravity();
if (e.OldElement?.MaxLines != e.NewElement.MaxLines)
UpdateMaxLines();

UpdatePadding();

ElevationHelper.SetElevation(this, e.NewElement);
@@ -263,7 +264,8 @@ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEv

if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName || e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName)
UpdateGravity();
else if (e.PropertyName == Label.TextColorProperty.PropertyName)
else if (e.PropertyName == Label.TextColorProperty.PropertyName ||
e.PropertyName == Label.TextTypeProperty.PropertyName)
UpdateText();
else if (e.PropertyName == Label.FontProperty.PropertyName)
UpdateText();
@@ -380,7 +382,23 @@ void UpdateText()
SetTextColor(_labelTextColorDefault);
_lastUpdateColor = Color.Default;
}
Text = Element.Text;

switch (Element.TextType)
{
case TextType.Html:
if (Forms.IsNougatOrNewer)
Control.SetText(Html.FromHtml(Element.Text ?? string.Empty, FromHtmlOptions.ModeCompact), BufferType.Spannable);
else
#pragma warning disable CS0618 // Type or member is obsolete
Control.SetText(Html.FromHtml(Element.Text ?? string.Empty), BufferType.Spannable);
#pragma warning restore CS0618 // Type or member is obsolete
break;

default:
Text = Element.Text;
break;
}

UpdateColor();
UpdateFont();

@@ -416,4 +434,4 @@ void UpdatePadding()
_lastSizeRequest = null;
}
}
}
}
@@ -56,6 +56,7 @@ public static class Forms
static BuildVersionCodes? s_sdkInt;
static bool? s_isLollipopOrNewer;
static bool? s_isMarshmallowOrNewer;
static bool? s_isNougatOrNewer;

[Obsolete("Context is obsolete as of version 2.5. Please use a local context instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -98,6 +99,16 @@ internal static bool IsMarshmallowOrNewer
}
}

internal static bool IsNougatOrNewer
{
get
{
if (!s_isNougatOrNewer.HasValue)
s_isNougatOrNewer = (int)Build.VERSION.SdkInt >= 24;
return s_isNougatOrNewer.Value;
}
}

public static float GetFontSizeNormal(Context context)
{
float size = 50;
@@ -135,7 +135,6 @@ protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
UpdateMaxLines();
if (e.OldElement.CharacterSpacing != e.NewElement.CharacterSpacing)
UpdateCharacterSpacing();

}
UpdateTextDecorations();
UpdatePadding();
@@ -158,13 +157,13 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE
UpdateLineBreakMode();
else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
UpdateTextDecorations();
else if (e.PropertyName == Label.TextProperty.PropertyName || e.PropertyName == Label.FormattedTextProperty.PropertyName)
else if (e.IsOneOf(Label.TextProperty, Label.FormattedTextProperty, Label.TextTypeProperty))
UpdateText();
else if (e.PropertyName == Label.LineHeightProperty.PropertyName)
UpdateLineHeight();
else if (e.PropertyName == Label.MaxLinesProperty.PropertyName)
UpdateMaxLines();
else if (e.PropertyName == ImageButton.PaddingProperty.PropertyName)

This comment has been minimized.

Copy link
@jfversluis

jfversluis Jul 20, 2019

Author Member

I noticed this coming in from another commit and although it probably works because of the same string value, it felt wrong.

else if (e.PropertyName == Label.PaddingProperty.PropertyName)
UpdatePadding();
}

@@ -242,7 +241,6 @@ void UpdateCharacterSpacing()
}
}


void UpdateLineHeight()
{
_lastSizeRequest = null;
@@ -274,7 +272,25 @@ void UpdateText()
_view.SetTextColor(_labelTextColorDefault);
_lastUpdateColor = Color.Default;
}
_view.Text = Element.Text;

switch (Element.TextType)
{

case TextType.Html:
if (Forms.IsNougatOrNewer)
Control.SetText(Html.FromHtml(Element.Text ?? string.Empty, FromHtmlOptions.ModeCompact), TextView.BufferType.Spannable);
else
#pragma warning disable CS0618 // Type or member is obsolete
Control.SetText(Html.FromHtml(Element.Text ?? string.Empty), TextView.BufferType.Spannable);
#pragma warning restore CS0618 // Type or member is obsolete
break;

default:
_view.Text = Element.Text;

break;
}

UpdateColor();
UpdateFont();

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.