Skip to content

Commit

Permalink
feat(ios): RequestedTheme will now affects KeyboardAppearance
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed Oct 21, 2021
1 parent 78f4dad commit bfacd86
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -3245,6 +3245,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Keyboard\Keyboard_iOS_Theme.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Keyboard\Keyboard_Showing_Dismissal.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -6380,6 +6384,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Keyboard\Keyboard_Events.xaml.cs">
<DependentUpon>Keyboard_Events.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Keyboard\Keyboard_iOS_Theme.xaml.cs">
<DependentUpon>Keyboard_iOS_Theme.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Keyboard\Keyboard_Showing_Dismissal.xaml.cs">
<DependentUpon>Keyboard_Showing_Dismissal.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<Page x:Class="UITests.Windows_UI_Xaml_Input.Keyboard.Keyboard_iOS_Theme"
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_Input.Keyboard"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ios="http://uno.ui/ios"
mc:Ignorable="d ios"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<ScrollViewer>
<StackPanel>
<TextBlock Text="Requested Theme:" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<RadioButton Grid.Column="0" Content="Default" Click="UpdateTheme" HorizontalAlignment="Stretch" />
<RadioButton Grid.Column="1" Content="Light" Click="UpdateTheme" HorizontalAlignment="Stretch" />
<RadioButton Grid.Column="2" Content="Dark" Click="UpdateTheme" HorizontalAlignment="Stretch" />
</Grid>

<TextBlock Text="KB Appearance: for 'custom' text/password-box" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<RadioButton Grid.Column="0" Content="Default" Click="UpdateKeyboardAppearance" HorizontalAlignment="Stretch" />
<RadioButton Grid.Column="1" Content="Light" Click="UpdateKeyboardAppearance" HorizontalAlignment="Stretch" />
<RadioButton Grid.Column="2" Content="Dark" Click="UpdateKeyboardAppearance" HorizontalAlignment="Stretch" />
</Grid>
<TextBlock Text="note: Dark will be displayed as Alert because it shared the same value."
TextWrapping="WrapWholeWords" />

<StackPanel x:Name="TestPanel">
<TextBlock Text="TextBox" Margin="0,20,0,0" />
<TextBox PlaceholderText="default" />
<TextBox PlaceholderText="light" ios:KeyboardAppearance="Light" />
<TextBox PlaceholderText="dark" ios:KeyboardAppearance="Dark" />
<TextBox PlaceholderText="custom:" />

<TextBlock Text="PasswordBox" Margin="0,20,0,0" />
<PasswordBox PlaceholderText="default" />
<PasswordBox PlaceholderText="light" ios:KeyboardAppearance="Light" />
<PasswordBox PlaceholderText="dark" ios:KeyboardAppearance="Dark" />
<PasswordBox PlaceholderText="custom:" />
</StackPanel>

<TextBlock TextWrapping="WrapWholeWords">
<Run>The 'device theme' can be changed by (ios)Settings &gt; Developer &gt; Dark Appearance.</Run><LineBreak /><LineBreak />
<Run>'default' should be affected by requested theme or os theme.</Run><LineBreak />
<Run>'dark/light' should always be that theme.</Run><LineBreak />
<Run>'custom' should be affected by 'KB Appearance', requested theme or os theme.</Run><LineBreak />
</TextBlock>
</StackPanel>
</ScrollViewer>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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_Input.Keyboard
{
[SampleControlInfo("Keyboard", nameof(Keyboard_iOS_Theme),
description: Description,
ignoreInSnapshotTests: true,
isManualTest: true)]
public sealed partial class Keyboard_iOS_Theme : Page
{
private const string Description = "[iOS-only] Keyboard theme should be determined based on the following precedences: KeyboardAppearance > RequestedTheme > Device Theme.";

public Keyboard_iOS_Theme()
{
this.InitializeComponent();
}

private void UpdateTheme(object sender, RoutedEventArgs e)
{
var root = global::Windows.UI.Xaml.Window.Current.Content as FrameworkElement;
var theme = (sender as RadioButton).Content switch
{
"Light" => ElementTheme.Light,
"Dark" => ElementTheme.Dark,
"Default" => ElementTheme.Default,

_ => throw new ArgumentOutOfRangeException()
};

root.RequestedTheme = theme;
}

private void UpdateKeyboardAppearance(object sender, RoutedEventArgs e)
{
#if __IOS__
var appearance = (sender as RadioButton).Content switch
{
"Light" => UIKit.UIKeyboardAppearance.Light,
"Dark" => UIKit.UIKeyboardAppearance.Dark,
"Default" => UIKit.UIKeyboardAppearance.Default,

_ => throw new ArgumentOutOfRangeException()
};
foreach (var item in TestPanel.Children.OfType<FrameworkElement>())
{
if (item is TextBox tbox && tbox.PlaceholderText?.StartsWith("custom") == true)
{
tbox.PlaceholderText = $"custom: {appearance}";
tbox.KeyboardAppearance = appearance;
}
else if (item is PasswordBox pbox && pbox.PlaceholderText?.StartsWith("custom") == true)
{
pbox.PlaceholderText = $"custom: {appearance}";
pbox.KeyboardAppearance = appearance;
}
}
#endif
}
}
}
7 changes: 4 additions & 3 deletions src/Uno.UI/UI/Xaml/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public partial class Application
{
private bool _initializationComplete = false;
private readonly static IEventProvider _trace = Tracing.Get(TraceProvider.Id);
private bool _themeSetExplicitly = false;
private ApplicationTheme? _requestedTheme;
private bool _systemThemeChangesObserved = false;
private SpecializedResourceDictionary.ResourceKey _requestedThemeForResources;
Expand Down Expand Up @@ -159,10 +158,12 @@ private set
_ => throw new InvalidOperationException("Application's RequestedTheme is invalid."),
};

internal bool IsThemeSetExplicitly { get; private set; } = false;

internal void SetExplicitRequestedTheme(ApplicationTheme? explicitTheme)
{
// this flag makes sure the app will not respond to OS events
_themeSetExplicitly = explicitTheme.HasValue;
IsThemeSetExplicitly = explicitTheme.HasValue;
var theme = explicitTheme ?? GetDefaultSystemTheme();
SetRequestedTheme(theme);
}
Expand Down Expand Up @@ -202,7 +203,7 @@ internal void SetExplicitRequestedTheme(ApplicationTheme? explicitTheme)
public void OnSystemThemeChanged()
{
// if user overrides theme, don't apply system theme
if (!_themeSetExplicitly)
if (!IsThemeSetExplicitly)
{
var theme = GetDefaultSystemTheme();
SetRequestedTheme(theme);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Windows.UI.Xaml.Controls
{
public partial class PasswordBox : TextBox
{
// TODO: copy UpdateThemeBindings+UpdateKeyboardThemePartial impl when PasswordBox no longer inherits from TextBox

partial void SetPasswordScope(bool shouldHideText)
{
SetSecureTextEntry(shouldHideText);
Expand Down
9 changes: 9 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -854,5 +854,14 @@ public void Select(int start, int length)
partial void SelectAllPartial();

internal override bool CanHaveChildren() => true;

internal override void UpdateThemeBindings()
{
base.UpdateThemeBindings();

UpdateKeyboardThemePartial();
}

partial void UpdateKeyboardThemePartial();
}
}
32 changes: 32 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Uno.UI;
using Windows.UI.Xaml.Data;
Expand Down Expand Up @@ -27,6 +28,7 @@ public partial class TextBox
OnTextAlignmentChanged(CreateInitialValueChangerEventArgs(TextAlignmentProperty, null, TextAlignment));
OnReturnKeyTypeChanged(ReturnKeyType);
OnKeyboardAppearanceChanged(KeyboardAppearance);
UpdateKeyboardThemePartial();
}

partial void OnFocusStateChangedPartial(FocusState focusState)
Expand Down Expand Up @@ -334,5 +336,35 @@ private void OnKeyboardAppearanceChanged(UIKeyboardAppearance newValue)
}

#endregion

partial void UpdateKeyboardThemePartial()
{
if (_textBoxView == null) return;

// if KeyboardAppearance has been explicitly set, we leave it as is.
if (KeyboardAppearance != UIKeyboardAppearance.Default) return;

ElementTheme? GetExplicitlySetAppTheme() => Application.Current.IsThemeSetExplicitly
? Application.Current.ActualElementTheme as ElementTheme?
: null;

// the appearance will be determined by the first parent/self that has a non-default RequestedTheme,
// or the explicitly requested application theme.
// note: the literal "Default" value is lost on the ActualTheme property, which is why we are not using it here.
var theme = VisualTreeHelper.EnumerateAncestors(this).OfType<FrameworkElement>()
.Prepend(this)
.Select(x => x.RequestedTheme as ElementTheme?)
.Append(GetExplicitlySetAppTheme())
.FirstOrDefault(x => x != ElementTheme.Default);
var appearance = theme switch
{
ElementTheme.Light => UIKeyboardAppearance.Light,
ElementTheme.Dark => UIKeyboardAppearance.Dark,

_ => UIKeyboardAppearance.Default
};

_textBoxView.KeyboardAppearance = appearance;
}
}
}
9 changes: 9 additions & 0 deletions src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,15 @@ internal static UIElement SearchDownForLeaf(UIElement root, Predicate<UIElement>
return root;
}

internal static IEnumerable<DependencyObject> EnumerateAncestors(DependencyObject o)
{
while ((o as FrameworkElement)?.Parent is { } parent)
{
yield return parent;
o = parent;
}
}

#region Helpers
private static Func<IEnumerable<UIElement>, IEnumerable<UIElement>> Except(UIElement element)
=> children => children.Except(element);
Expand Down

0 comments on commit bfacd86

Please sign in to comment.