From 71b7b1df2b39a1d9aabcd2cfce9ee44cb4eb8af5 Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Wed, 19 Oct 2022 14:21:30 -0400 Subject: [PATCH 1/4] style: updated MaterialNavigationView style --- .../Uno.Material/MaterialResourcesV2.cs | 3 +- .../Styles/Controls/v2/NavigationView.xaml | 3917 ++++++++--------- 2 files changed, 1939 insertions(+), 1981 deletions(-) diff --git a/src/library/Uno.Material/MaterialResourcesV2.cs b/src/library/Uno.Material/MaterialResourcesV2.cs index 4be8f0175..84f1a63f0 100644 --- a/src/library/Uno.Material/MaterialResourcesV2.cs +++ b/src/library/Uno.Material/MaterialResourcesV2.cs @@ -107,9 +107,8 @@ private Style GetStyle(string key) Add("MaterialListViewItemStyle", isImplicit: true); Add("MaterialListViewStyle", isImplicit: true); Add("MaterialMenuFlyoutPresenterStyle", isImplicit: true); - Add("MaterialNavigationViewItemStyle", isImplicit: true); - Add("MaterialPaddedNavigationViewStyle"); Add("MaterialNavigationViewStyle", isImplicit: true); + Add("MaterialNavigationViewItemStyle", isImplicit: true); Add("MaterialOutlinedButtonStyle"); Add("MaterialOutlinedPasswordBoxStyle"); Add("MaterialOutlinedTextBoxStyle"); diff --git a/src/library/Uno.Material/Styles/Controls/v2/NavigationView.xaml b/src/library/Uno.Material/Styles/Controls/v2/NavigationView.xaml index aaf209ad7..ec2f4eda2 100644 --- a/src/library/Uno.Material/Styles/Controls/v2/NavigationView.xaml +++ b/src/library/Uno.Material/Styles/Controls/v2/NavigationView.xaml @@ -1,329 +1,199 @@  + mc:Ignorable="todo"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 320 + 48 + 0,0,1,0 + 1,0,0,0 + + 00:00:00.2 + 00:00:00.19999 + 00:00:00.1 + 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 320 + 48 + 0,0,1,0 + 1,0,0,0 + 00:00:00.2 + 00:00:00.19999 + 00:00:00.1 + 0 + + + 320 + 48 + 0,0,1,0 + 1,0,0,0 + + 00:00:00.2 + 00:00:00.19999 + 00:00:00.1 + 0 - 10,0,16,0 - 12,0,12,0 - 40 - 40 - 40 - 40 - 40 - 48 - 48 - 40 - - 40 - 4 - - 1 - 1 - - 1 - 1 - 12,0,12,0 - 10,0,0,0 - 8,5,0,0 - 12,5,0,11 - 8,4,0,0 - 0 - 0 - 16,10 - 16,10 - 10,0 - 0 - 0,0,20,0 - 0,0,0,0 - 8,0,16,0 - 12,0 - -20,0,6,0 - -16,0,0,0 - 0,0,0,0 - -12,0,0,0 - 12,0,20,0 - 16,0,20,0 - -4,0,6,0 - 12,0,12,0 + + + + + + 0,14,14,0 + 14,0,0,14 + + + 56 + 28 + 24 + 24 + 12,0 + + + + + + + + 56 + 28 + 28,0,0,28 + 12,0 + 24 + 12,0,0,0 + 0 + + + + + + + + + + 16,0 + 4,0 + 56 + 80 + 80 + 40 + 48 + 40 + 80 + 40 + 40 + 40 + 40 + + 56 + 56 + 24 + 3 + 24 + 2 + + 1 + 1 + + 0 + 1 + 3 + 12,0 + 16,0 + 12,0 + -24,44,0,0 + 0 + -1,3 + 1,1,0,0 + 0,1,0,0 + 0,1,0,0 + 4,0 + 1 + 56,44,0,0 + 0 + 0 + 0 + 0 + 8,4,0,0 + 0 + 0 + 0,3,0,4 + 0,3,0,4 + 3,0,4,0 + 0 + 4,-1,8,-1 + 0,0,0,0 + 8,-1,12,-1 + 12,0 + 0,0,-14,0 + -16,0,0,0 + 0,0,0,0 + -12,0,0,0 + 12,0,20,0 + 16,0,20,0 + -4,0,-8,0 + 12,0,12,0 + + 8,0,0,0 + 0 + 0 - 0,8 - 0,8 + 0,8 + 0,8 - 12.0 - - - - - - - - - - 0 - - - - 8,2 - 16 - + 8 + - - - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + win:Style="{StaticResource ResetDefaultSplitViewStyle}"> - - - + + + - - + + + - + - + + @@ -1274,82 +1243,130 @@ - + + + + + + + HorizontalContentAlignment="Stretch" + Grid.Row="4" /> - + - + + + + - - + + - - - - - + + + + + + - + - - + - + + HorizontalContentAlignment="Stretch" + Margin="0,0,0,4" + Grid.Row="2" /> - - + + - - - - - + VerticalScrollBarVisibility="Auto" + contract7Present:VerticalAnchorRatio="1"> + + + + + - + - + @@ -1360,35 +1377,43 @@ - - - + + Style="{StaticResource NavigationViewTitleHeaderContentControlTextStyle}" /> - + Content="{TemplateBinding Content}" + Margin="{ThemeResource NavigationViewContentPresenterMargin}" /> + + + + + + @@ -1397,7 +1422,6 @@ button Grid is moved below the content SplitView in the template --> @@ -1407,44 +1431,43 @@ - + - + + @@ -1456,835 +1479,510 @@ - + - - - - + + - + + + - - + + - - - - - - + - - - - - - + - - - - - + From 8ad33e960cbcdbe6e7c3fbf9b342224ba84a1c6c Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Wed, 19 Oct 2022 14:23:17 -0400 Subject: [PATCH 2/4] chore: update sample app for NV changes --- .../Uno.Themes.Samples.Shared/App.xaml | 1 - .../App.xaml.Navigation.cs | 2 - .../Controls/SamplePageLayout.cs | 4 +- .../Helpers/VisualTreeHelperEx.cs | 319 +++++++++++++++++- .../Uno.Themes.Samples.Shared/Shell.xaml | 59 ++-- .../Uno.Themes.Samples.Shared/Shell.xaml.cs | 72 ++-- .../Styles/NavigationView.xaml | 19 -- .../Styles/SamplePageLayout.xaml | 37 +- .../Uno.Themes.Samples.Shared.projitems | 4 - .../Uno.Themes.Samples.UWP.csproj | 6 +- 10 files changed, 413 insertions(+), 110 deletions(-) delete mode 100644 src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Styles/NavigationView.xaml diff --git a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml index c8f1dd14d..0983fd4fb 100644 --- a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml +++ b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml @@ -33,7 +33,6 @@ - diff --git a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml.Navigation.cs b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml.Navigation.cs index 203d0a556..42ea81b88 100644 --- a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml.Navigation.cs +++ b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/App.xaml.Navigation.cs @@ -132,7 +132,6 @@ private void AddNavigationItems(MUXC.NavigationView nv) { Content = category.Key.GetDescription() ?? category.Key.ToString(), SelectsOnInvoked = false, - Style = (Style)Resources[$"T{tier++}NavigationViewItemStyle"] }.Apply(NavViewItemVisualStateFix); AutomationProperties.SetAutomationId(parentItem, "Section_" + parentItem.Content); @@ -145,7 +144,6 @@ private void AddNavigationItems(MUXC.NavigationView nv) { Content = sample.Title, DataContext = sample, - Style = (Style)Resources[$"T{tier}NavigationViewItemStyle"] }.Apply(NavViewItemVisualStateFix); AutomationProperties.SetAutomationId(item, "Section_" + item.Content); diff --git a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Controls/SamplePageLayout.cs b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Controls/SamplePageLayout.cs index 6420b8f3d..cdd2cdf5a 100644 --- a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Controls/SamplePageLayout.cs +++ b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Controls/SamplePageLayout.cs @@ -181,7 +181,7 @@ private double GetRelativeOffset() { #if NETFX_CORE // On UWP we can count on finding a ScrollContentPresenter. - var scp = VisualTreeHelperEx.GetFirstDescendant(_scrollViewer); + var scp = VisualTreeHelperEx.FindFirstDescendant(_scrollViewer); var content = scp?.Content as FrameworkElement; var transform = _scrollingTabs.TransformToVisual(content); return transform.TransformPoint(new Point(0, 0)).Y - _scrollViewer.VerticalOffset; @@ -207,7 +207,7 @@ public T GetSampleChild(Design mode, string name) { var presenter = (ContentPresenter)GetTemplateChild($"{mode}ContentPresenter"); - return VisualTreeHelperEx.GetFirstDescendant(presenter, x => x.Name == name); + return VisualTreeHelperEx.FindFirstDescendant(presenter, x => x.Name == name); } private class LayoutModeMapping diff --git a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Helpers/VisualTreeHelperEx.cs b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Helpers/VisualTreeHelperEx.cs index f6b5104c2..df74c58ef 100644 --- a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Helpers/VisualTreeHelperEx.cs +++ b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Helpers/VisualTreeHelperEx.cs @@ -1,39 +1,344 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; +#if __IOS__ +using UIKit; +using _View = UIKit.UIView; +#elif __MACOS__ +using AppKit; +using _View = AppKit.NSView; +#elif __ANDROID__ +using _View = Android.Views.View; +#else +using _View = Windows.UI.Xaml.DependencyObject; +#endif + namespace Uno.Themes.Samples.Helpers { - public static class VisualTreeHelperEx + public static partial class VisualTreeHelperEx { - public static T GetFirstDescendant(DependencyObject reference) => GetDescendants(reference) + /// + /// Returns the first ancestor of a specified type. + /// + /// The type of ancestor to find. + /// Any node of the visual tree + /// First Counting from the and not from the root of tree. + public static T FindFirstAncestor(this _View reference) => EnumerateAncestors(reference) + .OfType() + .FirstOrDefault(); + + /// + /// Returns the first ancestor of a specified type that satisfies the . + /// + /// The type of ancestor to find. + /// Any node of the visual tree + /// A function to test each node for a condition. + /// First Counting from the and not from the root of tree. + public static T FindFirstAncestor(this _View reference, Func predicate) => EnumerateAncestors(reference) + .OfType() + .FirstOrDefault(predicate); + + /// + /// Returns the first descendant of a specified type. + /// + /// The type of descendant to find. + /// Any node of the visual tree + /// The crawling is done depth first. + public static T FindFirstDescendant(this _View reference) => EnumerateDescendants(reference) .OfType() .FirstOrDefault(); - public static T GetFirstDescendant(DependencyObject reference, Func predicate) => GetDescendants(reference) + /// + /// Returns the first descendant of a specified type that satisfies the . + /// + /// The type of descendant to find. + /// Any node of the visual tree + /// A function to test each node for a condition. + /// The crawling is done depth first. + public static T FindFirstDescendant(this _View reference, Func predicate) => EnumerateDescendants(reference) + .OfType() + .FirstOrDefault(predicate); + + /// + /// Returns the first descendant of a specified type that satisfies the whose ancestors (up to ) and itself satisfy the . + /// + /// The type of descendant to find. + /// Any node of the visual tree + /// A function to test each ancestor for a condition. + /// A function to test each descendant for a condition. + /// The crawling is done depth first. + public static T FindFirstDescendant(this _View reference, Func<_View, bool> hierarchyPredicate, Func predicate) => EnumerateDescendants(reference, hierarchyPredicate) .OfType() .FirstOrDefault(predicate); - public static IEnumerable GetDescendants(DependencyObject reference) + /// + /// Returns all the visual descendants of a node. + /// + /// Any node of the visual tree + public static IEnumerable<_View> EnumerateDescendants(this _View reference) => EnumerateDescendants(reference, x => true); + + /// + /// Returns all the visual descendants of a node that satisfies the . + /// + /// Any node of the visual tree + /// + /// + public static IEnumerable<_View> EnumerateDescendants(this _View reference, Func<_View, bool> hierarchyPredicate) { - foreach (var child in GetChildren(reference)) + foreach (var child in reference.EnumerateChildren().Where(hierarchyPredicate)) { yield return child; - foreach (var grandchild in GetDescendants(child)) + foreach (var grandchild in child.EnumerateDescendants(hierarchyPredicate)) { yield return grandchild; } } } + + + // note: methods for retrieving children/ancestors exist with varying signatures. + // re-implementing them with unified & more inclusive signature for convenience. +#if __IOS__ || __MACOS__ + private static IEnumerable<_View> EnumerateAncestors(this _View o) + { + if (o is null) yield break; + while (o.Superview is _View parent) + { + yield return o = parent; + } + } + + private static IEnumerable<_View> EnumerateChildren(this _View o) + { + if (o is null) return Enumerable.Empty<_View>(); + return o.Subviews; + } +#elif __ANDROID__ + private static IEnumerable<_View> EnumerateAncestors(this _View o) + { + if (o is null) yield break; + + while (o.Parent is _View parent) + { + yield return o = parent; + } + } - public static IEnumerable GetChildren(DependencyObject reference) + private static IEnumerable<_View> EnumerateChildren(this _View reference) + { + if (reference is Android.Views.ViewGroup vg) + { + return Enumerable + .Range(0, vg.ChildCount) + .Select(vg.GetChildAt) + .Where(x => x != null).Cast<_View>(); + } + + return Enumerable.Empty<_View>(); + } +#else + private static IEnumerable<_View> EnumerateAncestors(this _View o) + { + if (o is null) yield break; + while (VisualTreeHelper.GetParent(o) is DependencyObject parent) + { + yield return o = parent; + } + } + + private static IEnumerable<_View> EnumerateChildren(this _View reference) { return Enumerable .Range(0, VisualTreeHelper.GetChildrenCount(reference)) .Select(x => VisualTreeHelper.GetChild(reference, x)); } +#endif + + public static _View GetTemplateRoot(this Control control) + { + return EnumerateChildren(control).FirstOrDefault(); + } + } + public static partial class VisualTreeHelperEx // debugging + { + /// + /// Produces a text representation of the visual tree. + /// + /// Any node of the visual tree + public static string TreeGraph(this _View reference) => TreeGraph(reference, DescribeVTNode); + + /// + /// Produces a text representation of the visual tree, using the provided method of description. + /// + /// Any node of the visual tree + /// A function to describe a visual tree node in a single line. + /// + public static string TreeGraph(this _View reference, Func<_View, string> describe) + { + var buffer = new StringBuilder(); + try + { + Walk(reference); + } + catch (Exception e) + { + buffer.AppendLine(); + buffer.AppendLine("An error has occurred while walking the visual tree:"); + buffer.Append(e.ToString()); + } + return buffer.ToString(); + + void Walk(_View o, int depth = 0) + { + Print(o, depth); + foreach (var child in EnumerateChildren(o)) + { + Walk(child, depth + 1); + } + } + void Print(_View o, int depth) + { + buffer + .Append(new string(' ', depth * 4)) + .Append(describe(o)) + .AppendLine(); + } + } + + public static void MonitorVisualStates(this Control control, bool includeInitialStates, Action onStatesChanged) + { + var root = (FrameworkElement)control.GetTemplateRoot(); + if (root == null) + { + onStatesChanged("The control doesn't have a template root."); + return; + } + var vsgs = VisualStateManager.GetVisualStateGroups(root).ToArray(); + if (!vsgs.Any()) + { + onStatesChanged("The control template doesn't contain any VisualStateGroup."); + return; + } + + foreach (var vsg in vsgs) + { + vsg.CurrentStateChanged += (s, e) => DebugVStates(vsg.Name); + } + if (includeInitialStates) + { + DebugVStates(); + } + + void DebugVStates(string updatedGroupName = null) + { + var summary = string.Join("\n", vsgs + .Where(x => x.CurrentState != null) + .Select(x => $"{x.Name}: {x.CurrentState?.Name}") + ); + onStatesChanged(summary); + } + } + + private static string DescribeVTNode(object x) + { + return new StringBuilder() + .Append(x.GetType().Name) + .Append((x as FrameworkElement)?.Name is string xname && xname.Length > 0 ? $"#{xname}" : string.Empty) + .Append($" // {string.Join(", ", GetDetails())}") + .ToString(); + + bool TryGetDpValue(object owner, string property, out T value) + { + if (owner is DependencyObject @do && + owner.GetType().GetProperty($"{property}Property", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null, null) is DependencyProperty dp) + { + value = (T)@do.GetValue(dp); + return true; + } + + value = default; + return false; + } + string FormatCornerRadius(CornerRadius cr) + { + // format: uniform, [left,top,right,bottom] + if (cr.TopLeft == cr.TopRight && cr.TopRight == cr.BottomRight && cr.BottomRight == cr.BottomLeft) return $"{cr.TopLeft}"; + return $"[{cr.TopLeft},{cr.TopRight},{cr.BottomRight},{cr.BottomLeft}]"; + } + string FormatThickness(Thickness thickness) + { + // format: uniform, [same-left-right,same-top-bottom], [left,top,right,bottom] + if (thickness.Left == thickness.Top && thickness.Top == thickness.Right && thickness.Right == thickness.Bottom) return $"{thickness.Left}"; + if (thickness.Left == thickness.Right && thickness.Top == thickness.Bottom) return $"[{thickness.Left},{thickness.Top}]"; + return $"[{thickness.Left},{thickness.Top},{thickness.Right},{thickness.Bottom}]"; + } + string FormatLengthRange(double min, double value, double max) + { + if (min == 0 && max == double.PositiveInfinity) return value.ToString(); + + return $"[{min},{value},{max}]"; + } + string FormatGridLength(GridLength value) + { + // format: A,*,2.5*,2.5 + if (value.IsAuto) return "A"; + if (value.IsStar) return value.Value == 1 ? "*" : (value.Value.ToString() + "*"); + return value.Value.ToString(); + } + IEnumerable GetDetails() + { + if (x is FrameworkElement fe) + { + yield return $"Actual={fe.ActualWidth}x{fe.ActualHeight}"; + if (fe.TransformToVisual(Window.Current.Content).TransformPoint(default) is var absPos) + { + yield return $"AbsPos={absPos.X},{absPos.Y}"; + } + yield return $"HV={fe.HorizontalAlignment}/{fe.VerticalAlignment}"; + } + if (x is Grid grid) + { + if (grid.ColumnDefinitions.Any()) yield return $"Columns='{string.Join(',', grid.ColumnDefinitions.Select(cd => FormatGridLength(cd.Width)))}'"; + if (grid.RowDefinitions.Any()) yield return $"Rows='{string.Join(',', grid.RowDefinitions.Select(rd => FormatGridLength(rd.Height)))}'"; + } + /*if (x is FrameworkElement fe2 && + Grid.GetRow(fe2) is var row && + Grid.GetRowSpan(fe2) is var rowSpan && + Grid.GetColumn(fe2) is var column && + (row > 0 || rowSpan > 1 || ...) + ) + { + yield return $"R{ + }*/ + if (TryGetDpValue(x, "Margin", out var margin)) yield return $"Margin={FormatThickness(margin)}"; + if (TryGetDpValue(x, "Padding", out var padding)) yield return $"Padding={FormatThickness(padding)}"; + if (TryGetDpValue(x, "CornerRadius", out var cr)) yield return $"CornerRadius={FormatCornerRadius(cr)}"; + if (TryGetDpValue(x, "BorderThickness", out var bt)) yield return $"BorderThickness={FormatThickness(bt)}"; + + if (TryGetDpValue(x, "MinWidth", out var minWidth) && + TryGetDpValue(x, "Width", out var width) && + TryGetDpValue(x, "MaxWidth", out var maxWidth) && + TryGetDpValue(x, "MinHeight", out var minHeight) && + TryGetDpValue(x, "Height", out var height) && + TryGetDpValue(x, "MaxHeight", out var maxHeight)) yield return $"Size={FormatLengthRange(minWidth, width, maxWidth)}x{FormatLengthRange(minHeight, height, maxHeight)}"; + + if (TryGetDpValue(x, "Opacity", out var opacity)) yield return $"Opacity={opacity}"; + if (TryGetDpValue(x, "Visibility", out var visibility)) yield return $"Visibility={visibility}"; + if (x is Control c && + c.GetTemplateRoot() is FrameworkElement templateRoot && + VisualStateManager.GetVisualStateGroups(templateRoot) is var vsgs && + vsgs.Count > 0) + { + //yield return "VisualStates=" + string.Join("|", vsgs.Where(y => y.CurrentState != null).Select(y => $"{y.Name}={y.CurrentState.Name}")); + yield return "VisualStates=" + string.Join("|", vsgs.Where(y => y.CurrentState != null).Select(y => y.CurrentState.Name)); + } + } + } } } diff --git a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Shell.xaml b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Shell.xaml index 6e8e4c7e3..7f69d9163 100644 --- a/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Shell.xaml +++ b/src/samples/Uno.Themes.Samples/Uno.Themes.Samples.Shared/Shell.xaml @@ -7,45 +7,33 @@ mc:Ignorable="xamarin"> - - - + + + - - - - - - - - - - - + Uno.Themes SampleApp + + + @@ -53,5 +41,16 @@ Grid.RowSpan="3" Visibility="Collapsed" xamarin:Style="{StaticResource NativeDefaultFrame}" /> + + + +