Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

GH-3106 Implemented LineBreakMode to Button #11147

Merged
merged 9 commits into from
Sep 1, 2020
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
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 3106, "Added LineBreakMode on Button")]
public class Issue3106 : TestContentPage
{
int count;
const string content = "Welcome to Xamarin.Forms! Welcome to Xamarin.Forms! Welcome to Xamarin.Forms! Welcome to Xamarin.Forms!";
const string content2 = "Now users can set a line break mode to texts on Button, the default value doesn't affect any user.";

Button mainButton;
Button materialButton;
Label lineBreakModeType;

protected override void Init()
{
mainButton = new Button
{
Text = content,
LineBreakMode = LineBreakMode.WordWrap,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand
};
mainButton.Clicked += MainButton_Clicked;

materialButton = new Button
{
Text = content,
LineBreakMode = LineBreakMode.WordWrap,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
Visual = VisualMarker.Material
};
materialButton.Clicked += MaterialButton_Clicked;

lineBreakModeType = new Label
{
Text = LineBreakMode.WordWrap.ToString(),
VerticalOptions = LayoutOptions.EndAndExpand,
LineBreakMode = LineBreakMode.WordWrap,
};
var layout = new StackLayout
{
Children =
{
new Label
{
Text = "Press the first button to change the LineBreakMode. Press the second button to change the text",
VerticalOptions = LayoutOptions.StartAndExpand
},
mainButton,
materialButton,
lineBreakModeType
}
};

Content = layout;
}

void MaterialButton_Clicked(object sender, EventArgs e)
{
if (materialButton.Text.Equals(content2))
materialButton.Text = mainButton.Text = content;
else
materialButton.Text = mainButton.Text = content2;
}

void MainButton_Clicked(object sender, EventArgs e)
{
materialButton.LineBreakMode = mainButton.LineBreakMode = SelectLineBreakMode();
lineBreakModeType.Text = mainButton.LineBreakMode.ToString();
}

LineBreakMode SelectLineBreakMode()
{
count++;
switch (count)
{
case 1:
return LineBreakMode.CharacterWrap;
case 2:
return LineBreakMode.HeadTruncation;
case 3:
return LineBreakMode.MiddleTruncation;
case 4:
return LineBreakMode.NoWrap;
case 5:
return LineBreakMode.TailTruncation;
default:
count = 0;
return LineBreakMode.WordWrap;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue10744.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10909.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3106.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8613.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue9137.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8691.cs" />
Expand Down
9 changes: 9 additions & 0 deletions Xamarin.Forms.Core/Button.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public class Button : View, IFontElement, ITextElement, IBorderElement, IButtonC

public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;

public static readonly BindableProperty LineBreakModeProperty = BindableProperty.Create(nameof(LineBreakMode), typeof(LineBreakMode), typeof(Button), LineBreakMode.NoWrap,
propertyChanged: (bindable, oldvalue, newvalue) => ((Button)bindable).InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged));

public Thickness Padding
{
get { return (Thickness)GetValue(PaddingElement.PaddingProperty); }
Expand All @@ -70,6 +73,12 @@ Thickness IPaddingElement.PaddingDefaultValueCreator()
return default(Thickness);
}

public LineBreakMode LineBreakMode
pictos marked this conversation as resolved.
Show resolved Hide resolved
{
get { return (LineBreakMode)GetValue(LineBreakModeProperty); }
set { SetValue(LineBreakModeProperty, value); }
}

void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue)
{
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
Expand Down
3 changes: 2 additions & 1 deletion Xamarin.Forms.CustomAttributes/TestAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ public enum Button
BorderRadius,
Image,
Padding,
Pressed
Pressed,
LineBreakMode
}

public enum VisualElement
Expand Down
15 changes: 15 additions & 0 deletions Xamarin.Forms.Platform.Android/ButtonLayoutManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using ARect = Android.Graphics.Rect;
using AView = Android.Views.View;
using AButton = Android.Widget.Button;
using Android.Text;

namespace Xamarin.Forms.Platform.Android
{
Expand Down Expand Up @@ -191,6 +192,7 @@ public void Update()
if (!UpdateTextAndImage())
UpdateImage();
UpdatePadding();
UpdateLineBreakMode();
}

void OnElementChanged(object sender, VisualElementChangedEventArgs e)
Expand Down Expand Up @@ -223,6 +225,8 @@ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
UpdateTextAndImage();
else if (e.PropertyName == Button.BorderWidthProperty.PropertyName && _borderAdjustsPadding)
_element.InvalidateMeasureNonVirtual(InvalidationTrigger.MeasureChanged);
else if (e.PropertyName == Button.LineBreakModeProperty.PropertyName)
UpdateLineBreakMode();
}

void UpdatePadding()
Expand Down Expand Up @@ -355,5 +359,16 @@ void UpdateImage()
});
}
}

void UpdateLineBreakMode()
{
AButton view = View;

if (view == null || _element == null || _renderer?.View == null)
return;

view.SetLineBreakMode(_element);
_renderer.View.SetAllCaps(_element.TextTransform == TextTransform.Default);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@samhouts I used the same approach used here, to preserve the text as AllCaps.

}
}
}
23 changes: 11 additions & 12 deletions Xamarin.Forms.Platform.Android/Extensions/TextViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,26 @@ public static void SetMaxLines(this TextView textView, Label label)
textView.SetMaxLines(maxLines);
}

static void SetMaxLines(this TextView textView, Label label, int lines)
public static void SetLineBreakMode(this TextView textView, Label label)
{
// If the Label's MaxLines has been explicitly set, we should not set it here
if (label.MaxLines != (int)Label.MaxLinesProperty.DefaultValue)
{
return;
}

textView.SetMaxLines(lines);
var maxLines = SetLineBreak(textView, label.LineBreakMode);
textView.SetMaxLines(maxLines);
}

public static void SetLineBreakMode(this TextView textView, Label label)
{
var lineBreakMode = label.LineBreakMode;
public static void SetLineBreakMode(this TextView textView, Button button) =>
SetLineBreak(textView, button.LineBreakMode);


public static int SetLineBreak( TextView textView, LineBreakMode lineBreakMode)
{
int maxLines = Int32.MaxValue;
bool singleLine = false;

switch (lineBreakMode)
{
case LineBreakMode.NoWrap:
maxLines = 1;
singleLine = true;
textView.Ellipsize = null;
break;
case LineBreakMode.WordWrap:
Expand All @@ -59,6 +57,7 @@ public static void SetLineBreakMode(this TextView textView, Label label)
break;
case LineBreakMode.TailTruncation:
maxLines = 1;
singleLine = true;
textView.Ellipsize = TextUtils.TruncateAt.End;
break;
case LineBreakMode.MiddleTruncation:
Expand All @@ -69,7 +68,7 @@ public static void SetLineBreakMode(this TextView textView, Label label)
}

textView.SetSingleLine(singleLine);
textView.SetMaxLines(label, maxLines);
return maxLines;
}

public static void RecalculateSpanPositions(this TextView textView, Label element, SpannableString spannableString, SizeRequest finalSize)
Expand Down
25 changes: 22 additions & 3 deletions Xamarin.Forms.Platform.UAP/ButtonRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
using WImage = Windows.UI.Xaml.Controls.Image;
using WStretch = Windows.UI.Xaml.Media.Stretch;
using WThickness = Windows.UI.Xaml.Thickness;
using System;
using Xamarin.Forms.Platform.UAP.Extensions;
using System.Linq;

namespace Xamarin.Forms.Platform.UWP
{
public class ButtonRenderer : ViewRenderer<Button, FormsButton>
{
bool _fontApplied;
TextBlock _textBlock = null;

FormsButton _button;
PointerEventHandler _pointerPressedHandler;
Expand Down Expand Up @@ -74,6 +78,15 @@ protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
void ButtonOnLoaded(object o, RoutedEventArgs routedEventArgs)
{
WireUpFormsVsm();
UpdateLineBreakMode();
}


void UpdateLineBreakMode()
{
_textBlock = Control.GetTextBlock(Control.Content);

_textBlock?.UpdateLineBreakMode(Element.LineBreakMode);
}

void WireUpFormsVsm()
Expand Down Expand Up @@ -124,6 +137,8 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE
{
UpdatePadding();
}
else if (e.PropertyName == Button.LineBreakModeProperty.PropertyName)
UpdateLineBreakMode();
}

protected override void UpdateBackgroundColor()
Expand Down Expand Up @@ -188,8 +203,9 @@ async void UpdateContent()
// No image, just the text
if (elementImage == null)
{
Control.Content = text;
Control.Content = new TextBlock { Text = text };
Element?.InvalidateMeasureNonVirtual(InvalidationTrigger.RendererReady);
UpdateLineBreakMode();
return;
}

Expand All @@ -208,7 +224,8 @@ async void UpdateContent()
// when this happens, we want to resize the button
if (elementImage is BitmapImage bmp)
{
bmp.ImageOpened += (sender, args) => {
bmp.ImageOpened += (sender, args) =>
{
var actualSize = bmp.GetImageSourceSize();
image.Width = actualSize.Width;
image.Height = actualSize.Height;
Expand All @@ -227,12 +244,14 @@ async void UpdateContent()
// Both image and text, so we need to build a container for them
Control.Content = CreateContentContainer(Element.ContentLayout, image, text);
Element?.InvalidateMeasureNonVirtual(InvalidationTrigger.RendererReady);
UpdateLineBreakMode();
}

static StackPanel CreateContentContainer(Button.ButtonContentLayout layout, WImage image, string text)
{
var container = new StackPanel();
var textBlock = new TextBlock {
var textBlock = new TextBlock
{
Text = text,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
Expand Down
50 changes: 50 additions & 0 deletions Xamarin.Forms.Platform.UAP/Extensions/TextBlockExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Xamarin.Forms.Platform.UAP.Extensions
{
internal static class TextBlockExtensions
{
public static void UpdateLineBreakMode(this TextBlock textBlock, LineBreakMode lineBreakMode)
{
if (textBlock == null)
return;

switch (lineBreakMode)
{
case LineBreakMode.NoWrap:
textBlock.TextTrimming = TextTrimming.Clip;
textBlock.TextWrapping = TextWrapping.NoWrap;
break;
case LineBreakMode.WordWrap:
textBlock.TextTrimming = TextTrimming.None;
textBlock.TextWrapping = TextWrapping.Wrap;
break;
case LineBreakMode.CharacterWrap:
textBlock.TextTrimming = TextTrimming.WordEllipsis;
textBlock.TextWrapping = TextWrapping.Wrap;
break;
case LineBreakMode.HeadTruncation:
// TODO: This truncates at the end.
Copy link

Choose a reason for hiding this comment

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

Nit: Remove TODO

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@riverar, as you can see here those comments exist before my change, I just moved all the code around. I don't think this is a good idea to remove it.

Copy link
Member

Choose a reason for hiding this comment

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

.....and I think I may have added those myself 😅

textBlock.TextTrimming = TextTrimming.WordEllipsis;
DetermineTruncatedTextWrapping(textBlock);
break;
case LineBreakMode.TailTruncation:
textBlock.TextTrimming = TextTrimming.CharacterEllipsis;
DetermineTruncatedTextWrapping(textBlock);
break;
case LineBreakMode.MiddleTruncation:
// TODO: This truncates at the end.
Copy link

Choose a reason for hiding this comment

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

Nit: Remove TODO

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@riverar, as you can see here those comments exist before my change, I just moved all the code around. I don't think this is a good idea to remove it.

textBlock.TextTrimming = TextTrimming.WordEllipsis;
DetermineTruncatedTextWrapping(textBlock);
break;
default:
throw new ArgumentOutOfRangeException();
}
}

static void DetermineTruncatedTextWrapping(TextBlock textBlock) =>
textBlock.TextWrapping = textBlock.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
}
}
Loading