[EnvVar][Hosts][RegPrev]Decouple and refactor to make it "packable" as nuget package (#32604)

* WIP Hosts - remove deps

* Add consumer app

* Move App and MainWindow to Consumer app. Make Hosts dll

* Try consume it

* Fix errors

* Make it work with custom build targets

* Dependency injection
Refactor
Explicit page creation
Wire missing dependencies

* Fix installer

* Remove unneeded stuff

* Fix build again

* Extract UI and logic from MainWindow to RegistryPreviewMainPage

* Convert to lib
Change namespace to RegistryPreviewUILib
Remove PT deps

* Add exe app and move App.xaml and MainWindow.xaml

* Consume the lib

* Update Hosts package creation

* Fix RegistryPreview package creation

* Rename RegistryPreviewUI back to RegistryPreview

* Back to consuming lib

* Ship icons and assets in nuget packages

* Rename to EnvironmentVariablesUILib and convert to lib

* Add app and consume

* Telemetry

* GPO

* nuget

* Rename HostsPackageConsumer to Hosts and Hosts lib to HostsUILib

* Assets cleanup

* nuget struct

* v0

* assets

* [Hosts] Re-add AppList to Lib Assets, [RegPrev] Copy lib assets to out dir

* Sign UI dlls

* Revert WinUIEx bump

* Cleanup

* Align deps

* version exception dll

* Fix RegistryPreview crashes

* XAML format

* XAML format 2

* Pack .pri files in lib/ dir

---------

Co-authored-by: Darshak Bhatti <dabhatti@microsoft.com>
This commit is contained in:
Stefan Markovic
2024-04-26 19:41:44 +02:00
committed by GitHub
parent 28ba2bd301
commit 41a0114efe
125 changed files with 2097 additions and 1212 deletions

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Application
x:Class="RegistryPreview.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RegistryPreview">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Web;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace RegistryPreview
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
// Keeping commented out but this is invaluable for protocol activation testing.
// #if DEBUG
// System.Diagnostics.Debugger.Launch();
// #endif
// Open With... handler - gets activation arguments if they are available.
AppActivationArguments activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activatedArgs.Kind == ExtendedActivationKind.File)
{
// Covers the double click exe scenario and treated as no file loaded
AppFilename = string.Empty;
if (activatedArgs.Data != null)
{
IFileActivatedEventArgs eventArgs = (IFileActivatedEventArgs)activatedArgs.Data;
if (eventArgs.Files.Count > 0)
{
AppFilename = eventArgs.Files[0].Path;
}
}
}
else if (activatedArgs.Kind == ExtendedActivationKind.Protocol)
{
// When the app is the default handler for REG files and the filename has non-ASCII characters, the app gets activated by Protocol
AppFilename = string.Empty;
if (activatedArgs.Data != null)
{
IProtocolActivatedEventArgs eventArgs = (IProtocolActivatedEventArgs)activatedArgs.Data;
if (eventArgs.Uri.AbsoluteUri.Length > 0)
{
AppFilename = eventArgs.Uri.Query.Replace("?ContractId=Windows.File&Verb=open&File=", string.Empty);
AppFilename = HttpUtility.UrlDecode(AppFilename);
}
}
}
else
{
// Right click on a REG file and selected Preview
// Grab the command line parameters directly from the Environment since this is expected to be run
// via Context Menu of a REG file.
string[] cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs == null)
{
// Covers the double click exe scenario and treated as no file loaded
AppFilename = string.Empty;
}
else if (cmdArgs.Length == 2)
{
// GetCommandLineArgs() send in the called EXE as 0 and the selected filename as 1
AppFilename = cmdArgs[1];
}
else
{
// Anything else should be treated as no file loaded
AppFilename = string.Empty;
}
}
// Start the application
appWindow = new MainWindow();
appWindow.Activate();
}
private Window appWindow;
#pragma warning disable SA1401 // Fields should be private
#pragma warning disable CA2211 // Non-constant fields should not be visible. TODO: consider making it a property
public static string AppFilename;
#pragma warning restore CA2211 // Non-constant fields should not be visible
#pragma warning restore SA1401 // Fields should be private
}
}

View File

@@ -0,0 +1,52 @@
<winuiex:WindowEx
x:Class="RegistryPreview.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tk7controls="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:winuiex="using:WinUIEx"
MinWidth="480"
MinHeight="320"
Closed="Window_Closed"
mc:Ignorable="d">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid x:Name="MainGrid" Loaded="Grid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
x:Name="titleBar"
Grid.Row="0"
Height="32"
Margin="16,0"
ColumnSpacing="16"
IsHitTestVisible="True">
<Grid.ColumnDefinitions>
<!--<ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>-->
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="TitleColumn" Width="Auto" />
<!--<ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>-->
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Width="16"
Height="16"
VerticalAlignment="Center"
Source="../Assets/RegistryPreview/RegistryPreview.ico" />
<TextBlock
x:Name="titleBarText"
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding ApplicationTitle}" />
</Grid>
</Grid>
</winuiex:WindowEx>

View File

@@ -0,0 +1,136 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using ManagedCommon;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using RegistryPreviewUILib;
using Windows.Data.Json;
using Windows.Graphics;
using WinUIEx;
namespace RegistryPreview
{
public sealed partial class MainWindow : WindowEx
{
// Const values
private const string APPNAME = "RegistryPreview";
// private members
private Microsoft.UI.Windowing.AppWindow appWindow;
private JsonObject jsonWindowPlacement;
private string settingsFolder = string.Empty;
private string windowPlacementFile = "app-placement.json";
private RegistryPreviewMainPage MainPage { get; }
internal MainWindow()
{
this.InitializeComponent();
// Open settings file; this moved to after the window tweak because it gives the window time to start up
settingsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerToys\" + APPNAME;
OpenWindowPlacementFile(settingsFolder, windowPlacementFile);
// Update the Win32 looking window with the correct icon (and grab the appWindow handle for later)
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this);
Microsoft.UI.WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("Assets\\RegistryPreview\\RegistryPreview.ico");
// TODO(stefan)
appWindow.Closing += AppWindow_Closing;
Activated += MainWindow_Activated;
// Extend the canvas to include the title bar so the app can support theming
ExtendsContentIntoTitleBar = true;
SetTitleBar(titleBar);
// if have settings, update the location of the window
if (jsonWindowPlacement != null)
{
// resize the window
if (jsonWindowPlacement.ContainsKey("appWindow.Size.Width") && jsonWindowPlacement.ContainsKey("appWindow.Size.Height"))
{
SizeInt32 size;
size.Width = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Size.Width");
size.Height = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Size.Height");
// check to make sure the size values are reasonable before attempting to restore the last saved size
if (size.Width >= 320 && size.Height >= 240)
{
appWindow.Resize(size);
}
}
// reposition the window
if (jsonWindowPlacement.ContainsKey("appWindow.Position.X") && jsonWindowPlacement.ContainsKey("appWindow.Position.Y"))
{
PointInt32 point;
point.X = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Position.X");
point.Y = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Position.Y");
// check to make sure the move values are reasonable before attempting to restore the last saved location
if (point.X >= 0 && point.Y >= 0)
{
appWindow.Move(point);
}
}
}
MainPage = new RegistryPreviewMainPage(this, this.UpdateWindowTitle, App.AppFilename);
WindowHelpers.BringToForeground(windowHandle);
}
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
titleBarText.Foreground =
(SolidColorBrush)Application.Current.Resources["WindowCaptionForegroundDisabled"];
}
else
{
titleBarText.Foreground =
(SolidColorBrush)Application.Current.Resources["WindowCaptionForeground"];
}
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
MainGrid.Children.Add(MainPage);
Grid.SetRow(MainPage, 1);
}
public void UpdateWindowTitle(string title)
{
string filename = title;
if (string.IsNullOrEmpty(filename))
{
titleBarText.Text = APPNAME;
appWindow.Title = APPNAME;
}
else
{
string[] file = filename.Split('\\');
if (file.Length > 0)
{
titleBarText.Text = file[file.Length - 1] + " - " + APPNAME;
}
else
{
titleBarText.Text = filename + " - " + APPNAME;
}
// Continue to update the window's title, after updating the custom title bar
appWindow.Title = titleBarText.Text;
}
}
}
}