Skip to content

Commit

Permalink
chore: add tool to debug visual tree for SampleApp (#559)
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed May 4, 2023
1 parent 034b98d commit d7ef434
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Uno.Extensions;
using Uno.Toolkit.UI;
using Uno.Toolkit.Samples.Entities;
using Uno.Toolkit.Samples.Content;
using Uno.Toolkit.Samples.Content.Controls;
using Uno.Toolkit.Samples.Content.NestedSamples;
using Uno.Toolkit.Samples.Helpers;

#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
Expand All @@ -17,13 +25,6 @@

using MUXC = Microsoft.UI.Xaml.Controls;
using MUXCP = Microsoft.UI.Xaml.Controls.Primitives;
using Uno.Toolkit.Samples.Content.Controls;
using Uno.Toolkit.Samples.Helpers;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using VisualTreeHelperEx = Uno.Toolkit.Samples.Helpers.VisualTreeHelperEx;
using Uno.Toolkit.Samples.Content.NestedSamples;
using Uno.Toolkit.Samples.Content;

namespace Uno.Toolkit.Samples
{
Expand Down Expand Up @@ -184,7 +185,7 @@ void NavViewItemVisualStateFix(MUXC.NavigationViewItem nvi)
if (!nvi.IsSelected)
{
// depending on the DisplayMode, a NVIP may or may not be used.
var nvip = VisualTreeHelperEx.GetFirstDescendant<MUXCP.NavigationViewItemPresenter>(nvi, x => x.Name == "NavigationViewItemPresenter");
var nvip = nvi.GetFirstDescendant<MUXCP.NavigationViewItemPresenter>(x => x.Name == "NavigationViewItemPresenter");
VisualStateManager.GoToState((Control)nvip ?? nvi, "Normal", true);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Uno.Toolkit.Samples.Content.NestedSamples;
using Uno.Toolkit.UI;
using Uno.Toolkit.Samples.Helpers;
using VisualTreeHelperEx = Uno.Toolkit.Samples.Helpers.VisualTreeHelperEx;
#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
using System.Text;
using Uno.Disposables;
using Uno.Extensions;
using Uno.Toolkit.UI;
using Uno.Toolkit.Samples.Entities;
using Uno.Toolkit.Samples.Helpers;
using VisualTreeHelperEx = Uno.Toolkit.Samples.Helpers.VisualTreeHelperEx;

#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
Expand Down Expand Up @@ -218,16 +219,32 @@ private double GetRelativeOffset()
public T GetSampleChild<T>(Design mode, string name)
where T : FrameworkElement
{
var presenterName = mode switch
var presenter = mode switch
{
// M2/3 update broke this. Controls in MaterialTemplate(M2) are still not loaded (not until the combo is switched m2).
Design.Material => //"MaterialContentPanel",
throw new InvalidOperationException(),
_ => $"{mode}ContentPresenter",
Design.Material => this
.GetFirstDescendant<ContentPresenter>(x =>
x.Name is "M2MaterialContentPresenter" or "M3MaterialContentPresenter" &&
x.Visibility == Visibility.Visible),
_ => GetTemplateChild($"{mode}ContentPresenter"),
};
var presenter = GetTemplateChild(presenterName);

return VisualTreeHelperEx.GetFirstDescendant<T>(presenter, x => x.Name == name);
return presenter.GetFirstDescendant<T>(x => x.Name == name);
}

/// <summary>
/// Get the active presenter for the selected design system.
/// </summary>
/// <returns></returns>
public ContentPresenter GetActivePresenter()
{
return _design switch
{
Design.Material => this
.GetFirstDescendant<ContentPresenter>(x =>
x.Name is "M2MaterialContentPresenter" or "M3MaterialContentPresenter" &&
x.Visibility == Visibility.Visible),
_ => (ContentPresenter)GetTemplateChild($"{_design}ContentPresenter"),
};
}

private class LayoutModeMapping
Expand Down

This file was deleted.

17 changes: 15 additions & 2 deletions samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Shell.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@
HorizontalAlignment="Left"
Style="{StaticResource PaneToggleButtonStyle}" />

<local:ModalDialog x:Name="ModalDialog" />
</Grid>
<local:ModalDialog x:Name="ModalDialog" />

<StackPanel x:Name="DebugPanel"
x:Load="False"
Grid.RowSpan="3"
Background="Pink"
Padding="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal">
<Button Content="DebugVT" Click="DebugVT" />
<Button Content="T" Click="DebugVTAsync" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#if !IS_WINUI || HAS_UNO
#define SYS_NAV_MGR_SUPPORTED
#endif

using System;
using Windows.UI.Core;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Core;
using Uno.Extensions;
using Uno.Toolkit.Samples.Content;
using Uno.Toolkit.Samples.Content.Controls;
using Uno.Toolkit.Samples.Content.NestedSamples;
using Uno.Toolkit.Samples.Helpers;
using Uno.Toolkit.UI;
using Uno.Toolkit.Samples.Content.Controls;

#if __IOS__
using Foundation;
Expand Down Expand Up @@ -61,6 +68,9 @@ public Shell()

private void OnLoaded(object sender, RoutedEventArgs e)
{
#if DEBUG
this.FindName("DebugPanel"); // materialized x:Load=false element
#endif
SetDarkLightToggleInitialState();
}

Expand Down Expand Up @@ -221,5 +231,67 @@ private void NavigationViewControl_SizeChanged(object sender, SizeChangedEventAr
NavigationViewControl.PaneDisplayMode = MUXC.NavigationViewPaneDisplayMode.LeftMinimal;
}
}

private void DebugVT(object sender, RoutedEventArgs e)
{
object FindViewOfInterest()
{
// the view of interest is:
// - in RuntimeTestRunner, the "UnitTestsUIContentHelper.Content"
// - for any page, the active section of SamplePageLayout if present OR the content of that page
// ...
// todo: add support for [RequiresFullWindow], flyout, nested navigation sample

var content = NavigationViewControl.Content;
if (content is RuntimeTestRunner runner)
{
return runner.GetFirstDescendant<ContentControl>(x => x.Name == "unitTestContentRoot")?.Content;
}
else if (content is Page page)
{
if (page.GetFirstDescendant<SamplePageLayout>() is { } layout &&
layout.GetActivePresenter() is { } presenter)
{
// presenter.Content is the optional [SampleAttribute].DataType instance
return presenter.GetChildren().FirstOrDefault();
}
else
{
return page.Content;
}
}

return null;
}

// for the best viewing experience, paste the tree in VSCode (you can collapse node) with `ini` syntax highlighting
var tree = this.TreeGraph();
var target = FindViewOfInterest();
var targetTree = (target as DependencyObject)?.TreeGraph();

// note: you can also tag element with unique x:Name to inspect here
//var sut = this.GetFirstDescendant<Chip>(x => x.Name == "SUT");
//var tree = sut?.TreeGraph();

#if WINDOWS || WINDOWS_UWP
var data = new DataPackage();
data.SetText(targetTree ?? tree);

Clipboard.SetContent(data);
#elif __WASM__
Console.WriteLine(targetTree ?? tree);
#endif

// note: insert a breakpoint around here or uncomment the next line to debug.
//if (Debugger.IsAttached) Debugger.Break();
}

private async void DebugVTAsync(object sender, RoutedEventArgs e)
{
// leave some time to perform action like: opening combo/flyout or navigation
await Task.Delay(5000);

DebugVT(sender, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Helpers\EnumHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\HierarchyHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MetaAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\VisualTreeHelperEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Shell.xaml.cs">
<DependentUpon>Shell.xaml</DependentUpon>
</Compile>
Expand Down
65 changes: 65 additions & 0 deletions src/Uno.Toolkit.UI/Helpers/PrettyPrint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


#if true // shared between UWP vs WinUI
using Windows.Foundation;
using Windows.UI;
#endif

#if IS_WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#endif

namespace Uno.Toolkit.UI;

internal static class PrettyPrint
{
private static readonly Lazy<Dictionary<Color, string>> _knownColors = new(() => typeof(Colors)
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
.Where(x => x.PropertyType == typeof(Color))
.Where(x => !"Aqua=Cyan,Fuchsia=Magenta".Split(',').SelectMany(y => y.Split('=').Skip(1)).Contains(x.Name)) // skip duplicated colors
.ToDictionary(x => (Color)x.GetValue(null)!, x => x.Name)
);

internal static string FormatCornerRadius(CornerRadius x)
{
// format: uniform, [left,top,right,bottom]
if (x.TopLeft == x.TopRight && x.TopRight == x.BottomRight && x.BottomRight == x.BottomLeft) return $"{x.TopLeft:0.#}";
return $"[{x.TopLeft:0.#},{x.TopRight:0.#},{x.BottomRight:0.#},{x.BottomLeft:0.#}]";
}
internal static string FormatThickness(Thickness x)
{
// format: uniform, [same-left-right,same-top-bottom], [left,top,right,bottom]
if (x.Left == x.Top && x.Top == x.Right && x.Right == x.Bottom) return $"{x.Left:0.#}";
if (x.Left == x.Right && x.Top == x.Bottom) return $"[{x.Left:0.#},{x.Top:0.#}]";
return $"[{x.Left:0.#},{x.Top:0.#},{x.Right:0.#},{x.Bottom:0.#}]";
}
internal static string FormatRect(Rect x)
{
return $"[{x.Width:0.#}x{x.Height:0.#}@{x.Left:0.#},{x.Top:0.#}]";
}
#if __ANDROID__
internal static string FormatViewRect(Android.Views.View x)
{
return $"[{x.Width:0.#}x{x.Height:0.#}@{x.Left:0.#},{x.Top:0.#}]";
}
#endif
internal static string FormatSize(Size size) => $"{size.Width:0.#}x{size.Height:0.#}";
internal static string FormatBrush(Brush b)
{
if (b is SolidColorBrush scb) return
// ColorWithOpacity compounds Brush::Opacity into Color::Alpha, but we want to keep the context of both.
(_knownColors.Value.TryGetValue(scb.Color, out var name) ? name : $"#{scb.Color.A:X2}{scb.Color.R:X2}{scb.Color.G:X2}{scb.Color.B:X2}") +
(scb.Opacity != 1 ? $"*{scb.Opacity:#.###}" : "");

return b.GetType().Name;
}
}
Loading

0 comments on commit d7ef434

Please sign in to comment.