Skip to content

Commit

Permalink
Merge pull request unoplatform#14199 from unoplatform/dev/mazi/overla…
Browse files Browse the repository at this point in the history
…y-textbox-transform

fix: Empty `TextBlock` should have non-zero height
  • Loading branch information
MartinZikmund authored Nov 1, 2023
2 parents 4d6fb3f + 6ea201a commit 7228ad4
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 13 deletions.
8 changes: 8 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -3834,6 +3834,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_AnimateHeader.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_PasteEvent.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -7568,6 +7572,10 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_Simple.xaml.cs">
<DependentUpon>TextBox_Simple.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_AnimateHeader.xaml.cs">
<DependentUpon>TextBox_AnimateHeader.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\FromEmptyStringToValueConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_PasteEvent.xaml.cs">
<DependentUpon>TextBox_PasteEvent.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;

#if WinUI
using Microsoft.UI.Xaml.Data;
#else
using Windows.UI.Xaml.Data;
#endif

namespace UITests.Windows_UI_Xaml_Controls.TextBox
{
public class FromEmptyStringToValueConverter : IValueConverter
{
public object NullOrEmptyValue { get; set; }

public object NotNullOrEmptyValue { get; set; }

public object Convert(object value, Type targetType, object parameter, string language)
{
if (!(value is string str) || string.IsNullOrEmpty(str))
{
return NullOrEmptyValue;
}

return NotNullOrEmptyValue;
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<Page
x:Class="UITests.Windows_UI_Xaml_Controls.TextBox.TextBox_AnimateHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UITests.Windows_UI_Xaml_Controls.TextBox"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Page.Resources>
<local:FromEmptyStringToValueConverter x:Key="MaterialEmptyToFalse"
NotNullOrEmptyValue="True"
NullOrEmptyValue="False" />
<local:FromEmptyStringToValueConverter x:Key="MaterialEmptyToTrue"
NotNullOrEmptyValue="False"
NullOrEmptyValue="True" />
<CubicEase x:Key="MaterialEaseInOutFunction"
EasingMode="EaseInOut" />
<Duration x:Key="MaterialTextBoxAnimationDuration">0:0:0.25</Duration>
<Duration x:Key="MaterialAnimationDuration">0:0:0.25</Duration>
<Style x:Key="MaterialOutlinedTextBoxStyle"
TargetType="TextBox">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="PlaceholderForeground" Value="Blue" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />

<Setter Property="Padding" Value="8" />

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

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border x:Name="RootBorder"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Padding="1">

<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<!-- <VisualState.Setters>
<Setter Target="RootBorder.BorderBrush" Value="{ThemeResource OutlinedTextBoxBorderBrushPointerOver}" />
<Setter Target="ContentElement.Foreground" Value="{ThemeResource OutlinedTextBoxForegroundPointerOver}" />
<Setter Target="PlaceholderElement.Foreground" Value="{ThemeResource OutlinedTextBoxPlaceholderForegroundPointerOver}" />
</VisualState.Setters> -->
</VisualState>
<VisualState x:Name="Pressed" />

<VisualState x:Name="Disabled">
</VisualState>

<VisualState x:Name="Focused">

</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<VisualState.Setters>
<Setter Target="DeleteButton.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
<VisualStateGroup x:Name="HeaderStates">
<VisualState x:Name="NotEmpty">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PlaceholderElement_CompositeTransform"
Storyboard.TargetProperty="TranslateY"
Duration="{StaticResource MaterialTextBoxAnimationDuration}"
EasingFunction="{StaticResource MaterialEaseInOutFunction}"
To="-11" />
<!-- ContentElement TranslateY value changing depending if there is a PlaceholderText or not -->
<DoubleAnimation Storyboard.TargetName="ContentElement_CompositeTransform"
Storyboard.TargetProperty="TranslateY"
Duration="{StaticResource MaterialAnimationDuration}"
EasingFunction="{StaticResource MaterialEaseInOutFunction}"
To="8" />
<DoubleAnimation Storyboard.TargetName="PlaceholderElement_CompositeTransform"
Storyboard.TargetProperty="ScaleX"
Duration="{StaticResource MaterialTextBoxAnimationDuration}"
EasingFunction="{StaticResource MaterialEaseInOutFunction}"
To="0.7" />
<DoubleAnimation Storyboard.TargetName="PlaceholderElement_CompositeTransform"
Storyboard.TargetProperty="ScaleY"
Duration="{StaticResource MaterialTextBoxAnimationDuration}"
EasingFunction="{StaticResource MaterialEaseInOutFunction}"
To="0.7" />
</Storyboard>
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding Text, Converter={StaticResource MaterialEmptyToFalse}, RelativeSource={RelativeSource TemplatedParent}}" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="Empty">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding Text, Converter={StaticResource MaterialEmptyToTrue}, RelativeSource={RelativeSource TemplatedParent}}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<Grid x:Name="Root"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Border in place to properly vertically center the icon inside when it's a one-line TextBox -->
<!-- but keep it in the same place and at the top when it's a multiline TextBox -->
<Border Height="20"
VerticalAlignment="Top">
<ContentPresenter x:Name="IconPresenter"
HorizontalAlignment="Center"
Width="20"
Margin="1,0,18,0"
Foreground="Black"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Visibility="Collapsed" />
</Border>

<ScrollViewer x:Name="ContentElement"
Grid.Column="1"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsTabStop="False"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw">
<ScrollViewer.RenderTransform>
<CompositeTransform x:Name="ContentElement_CompositeTransform" />
</ScrollViewer.RenderTransform>
</ScrollViewer>

<!-- Border in place to properly vertically center the placeholder inside when it's a one-line TextBox -->
<!-- but keep it in the same place and at the top when it's a multiline TextBox -->
<Border Grid.Column="1"
Height="20"
VerticalAlignment="Top">
<TextBlock x:Name="PlaceholderElement"
Foreground="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
IsHitTestVisible="False"
RenderTransformOrigin="0,0.5"
Text="{TemplateBinding PlaceholderText}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBlock.RenderTransform>
<CompositeTransform x:Name="PlaceholderElement_CompositeTransform" />
</TextBlock.RenderTransform>
</TextBlock>
</Border>

<Button x:Name="DeleteButton"
Grid.Column="2"
Margin="8,0,0,0"
IsTabStop="False"
VerticalAlignment="Stretch"
Visibility="Collapsed"
AutomationProperties.AccessibilityView="Raw" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

</Page.Resources>
<StackPanel>
<TextBox Style="{StaticResource MaterialOutlinedTextBoxStyle}" PlaceholderText="Test me" />
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Uno.UI.Samples.Controls;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace UITests.Windows_UI_Xaml_Controls.TextBox
{
[Sample(Description = "This simulates the Material UI TextBox Header animation", IsManualTest = true, IgnoreInSnapshotTests = true)]
public sealed partial class TextBox_AnimateHeader : Page
{
public TextBox_AnimateHeader()
{
this.InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,5 +260,90 @@ public async Task When_SolidColorBrush_With_Opacity()

ImageAssert.HasColorInRectangle(bitmap, new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), Colors.Red.WithOpacity(.5));
}

[TestMethod]
[RunsOnUIThread]
public async Task When_Empty_TextBlock_Measure()
{
var container = new Grid()
{
Height = 200,
Width = 200,
};
var SUT = new TextBlock { Text = "" };
container.Children.Add(SUT);
WindowHelper.WindowContent = container;
await WindowHelper.WaitForLoaded(container);
await WindowHelper.WaitFor(() => SUT.DesiredSize != default);

#if !__WASM__ // Disabled due to #14231
Assert.AreEqual(0, SUT.DesiredSize.Width);
#endif
Assert.IsTrue(SUT.DesiredSize.Height > 0);
}

#if !__IOS__ // Line height is not supported on iOS
[TestMethod]
[RunsOnUIThread]
public async Task When_Empty_TextBlock_LineHeight_Override()
{
var container = new Grid()
{
Height = 200,
Width = 200,
};
var SUT = new TextBlock { Text = "", LineHeight = 100 };
container.Children.Add(SUT);
WindowHelper.WindowContent = container;
await WindowHelper.WaitForLoaded(container);
await WindowHelper.WaitFor(() => SUT.DesiredSize != default);

#if !__WASM__ // Disabled due to #14231
Assert.AreEqual(0, SUT.DesiredSize.Width);
#endif
Assert.AreEqual(100, SUT.DesiredSize.Height);
}
#endif

[TestMethod]
[RunsOnUIThread]
public async Task When_Empty_TextBlocks_Stacked()
{
var container = new StackPanel();
for (int i = 0; i < 3; i++)
{
container.Children.Add(new TextBlock { Text = "" });
}

container.Children.Add(new TextBlock { Text = "Some text" });

for (int i = 0; i < 3; i++)
{
container.Children.Add(new TextBlock { Text = "" });
}

WindowHelper.WindowContent = container;
await WindowHelper.WaitForLoaded(container);
foreach (var child in container.Children)
{
await WindowHelper.WaitFor(() => child.DesiredSize != default);
}

// Get the transform of the top left of the container
var previousTransform = container.TransformToVisual(null);
var previousOrigin = previousTransform.TransformPoint(new Point(0, 0));

for (int i = 1; i < container.Children.Count; i++)
{
// Get the same for SUT
var textBlockTransform = container.Children[i].TransformToVisual(null);
var textBlockOrigin = textBlockTransform.TransformPoint(new Point(0, 0));

Assert.AreEqual(previousOrigin.X, textBlockOrigin.X);
Assert.IsTrue(previousOrigin.Y < textBlockOrigin.Y);

previousOrigin = textBlockOrigin;
}
}
}
}
Loading

0 comments on commit 7228ad4

Please sign in to comment.