Skip to content

Commit

Permalink
Merge pull request #14389 from unoplatform/mergify/bp/release/stable/…
Browse files Browse the repository at this point in the history
…5.0/pr-14297

fix: Adjust TextBox ScrollViewer workaround for Material TextBox to wrap properly (backport #14297)
  • Loading branch information
jeromelaban authored Nov 10, 2023
2 parents 7311662 + 3454b42 commit d17477c
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -694,5 +694,63 @@ public async Task When_TextBox_ImeAction_Enter()
act.Should().NotThrow();
}
#endif

[TestMethod]
#if __SKIA__
[Ignore("Fails on Skia")]
#endif
public async Task When_TextBox_Wrap_Custom_Style()
{
var page = new TextBox_Wrapping();
await UITestHelper.Load(page);

page.SUT.Text = "Short";
await WindowHelper.WaitForIdle();
var height1 = page.SUT.ActualHeight;

page.SUT.Text = "This is a very very very much longer text. This TextBox should now wrap and have a larger height.";
await WindowHelper.WaitForIdle();
var height2 = page.SUT.ActualHeight;

page.SUT.Text = "Short";
await WindowHelper.WaitForIdle();
var height3 = page.SUT.ActualHeight;

Assert.AreEqual(height1, height3);
height2.Should().BeGreaterThan(height1);
}

[TestMethod]
#if __SKIA__ || __IOS__
[Ignore("Fails on Skia and iOS")]
// On iOS, the failure is: AssertFailedException: Expected value to be greater than 1199.0, but found 1199.0.
// Since the number is large, it looks like the TextBox is taking the full height.
#endif
public async Task When_TextBox_Wrap_Fluent()
{
var SUT = new TextBox()
{
Width = 200,
TextWrapping = TextWrapping.Wrap,
AcceptsReturn = true,
};

await UITestHelper.Load(SUT);

SUT.Text = "Short";
await WindowHelper.WaitForIdle();
var height1 = SUT.ActualHeight;

SUT.Text = "This is a very very very much longer text. This TextBox should now wrap and have a larger height.";
await WindowHelper.WaitForIdle();
var height2 = SUT.ActualHeight;

SUT.Text = "Short";
await WindowHelper.WaitForIdle();
var height3 = SUT.ActualHeight;

Assert.AreEqual(height1, height3);
height2.Should().BeGreaterThan(height1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<Page
x:Class="Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls.TextBox_Wrapping"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<StackPanel>
<StackPanel.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />


<Setter Property="MinHeight" Value="58" />

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid x:Name="Root"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">

<ScrollViewer x:Name="ContentElement"
Background="Red"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsTabStop="False"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw">
</ScrollViewer>

</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<StackPanel Margin="10" VerticalAlignment="Center" Width="200">
<TextBox AutomationProperties.AutomationId="NewTextBox"
x:Name="SUT"
x:FieldModifier="public"
AcceptsReturn="True"
IsSpellCheckEnabled="True"
Margin="0,10"
MaxLength="500"
PlaceholderText="Enter text here"
Style="{StaticResource TextBoxStyle}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Windows.UI.Xaml.Controls;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls;

public sealed partial class TextBox_Wrapping : Page
{
public TextBox_Wrapping()
{
this.InitializeComponent();
}
}
6 changes: 0 additions & 6 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,6 @@ private protected override void OnUnloaded()
}
}

private protected override void OnLoaded()
{
base.OnLoaded();
SetupTextBoxView();
}

partial void InitializePropertiesPartial()
{
OnImeOptionsChanged(ImeOptions);
Expand Down
67 changes: 40 additions & 27 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,46 @@ public TextBox()
SizeChanged += OnSizeChanged;
}

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

#if __ANDROID__
SetupTextBoxView();
#endif

// This workaround is added in OnLoaded rather than OnApplyTemplate.
// Apparently, sometimes (e.g, Material style), the TextBox style setters are executed after OnApplyTemplate
// So, the style setters would override what the workaround does.
// OnLoaded appears to be executed after both OnApplyTemplate and after the style setters, making sure the values set here are not modified after.
if (_contentElement is ScrollViewer scrollViewer)
{
#if __IOS__ || __MACOS__
// We disable scrolling because the inner ITextBoxView provides its own scrolling
scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
scrollViewer.VerticalScrollMode = ScrollMode.Disabled;
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
#else
// The template of TextBox contains the following:
/*
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
*/
// Historically, TemplateBinding for attached DPs wasn't supported, and TextBox worked perfectly fine.
// When support for TemplateBinding for attached DPs was added, TextBox broke (test: TextBox_AutoGrow_Vertically_Wrapping_Test) because of
// change in the values of these properties. The following code serves as a workaround to set the values to what they used to be
// before the support for TemplateBinding for attached DPs.
scrollViewer.HorizontalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
scrollViewer.VerticalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; // The template sets this to Hidden
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; // The template sets this to Hidden
#endif
}
}

internal bool IsUserModifying => _isInputModifyingText || _isInputClearingText;

private void OnSizeChanged(object sender, SizeChangedEventArgs args)
Expand Down Expand Up @@ -156,33 +196,6 @@ protected override void OnApplyTemplate()
_contentElement = GetTemplateChild(TextBoxConstants.ContentElementPartName) as ContentControl;
_header = GetTemplateChild(TextBoxConstants.HeaderContentPartName) as ContentPresenter;

if (_contentElement is ScrollViewer scrollViewer)
{
#if __IOS__ || __MACOS__
// We disable scrolling because the inner ITextBoxView provides its own scrolling
scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
scrollViewer.VerticalScrollMode = ScrollMode.Disabled;
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
#else
// The template of TextBox contains the following:
/*
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
*/
// Historically, TemplateBinding for attached DPs wasn't supported, and TextBox worked perfectly fine.
// When support for TemplateBinding for attached DPs was added, TextBox broke (test: TextBox_AutoGrow_Vertically_Wrapping_Test) because of
// change in the values of these properties. The following code serves as a workaround to set the values to what they used to be
// before the support for TemplateBinding for attached DPs.
scrollViewer.HorizontalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
scrollViewer.VerticalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; // The template sets this to Hidden
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; // The template sets this to Hidden
#endif
}

if (GetTemplateChild(TextBoxConstants.DeleteButtonPartName) is Button button)
{
_deleteButton = new WeakReference<Button>(button);
Expand Down

0 comments on commit d17477c

Please sign in to comment.