Compare commits

...

26 Commits

Author SHA1 Message Date
Niels Laute
9e510f32dd Update App.xaml.cs 2026-01-15 10:38:48 +01:00
Niels Laute
49d27b9a83 Merge branch 'main' into niels9001/additional-cleanup 2026-01-15 09:59:07 +01:00
Niels Laute
4de7cf6c58 Delete settings-ui.sln 2026-01-14 16:20:44 +01:00
Niels Laute
37c5781089 Replacing background with gradient brush and updating assets 2026-01-14 16:18:21 +01:00
Niels Laute
2636c9bf87 Removing and updating existing assets 2026-01-14 15:50:28 +01:00
Niels Laute
753d462da6 Update OobeOverview.xaml.cs 2026-01-14 15:33:29 +01:00
Niels Laute
eba72e04de Update OobeOverview.xaml.cs 2026-01-14 15:31:50 +01:00
Niels Laute
a4be0220c0 Removing shortcut conflicts from OOBE overview page 2026-01-14 15:31:31 +01:00
Niels Laute
4f9cf38f72 Fixing bugs 2026-01-14 15:29:43 +01:00
Niels Laute
a261dbb659 Update App.xaml.cs 2026-01-14 15:05:42 +01:00
Niels Laute
0640bc2c2c More refactoring 2026-01-14 15:04:11 +01:00
Niels Laute
26b3744c81 Refactoring OOBE 2026-01-14 14:48:56 +01:00
Niels Laute
08c55290c8 Simplifying the SCOOBE window 2026-01-14 11:44:42 +01:00
Niels Laute
bd8121dcd3 Remove dead experiments code 2026-01-14 10:58:31 +01:00
Niels Laute
f3ecef8249 Merge branch 'main' into niels9001/release-notes-ux 2026-01-13 15:06:24 +01:00
Niels Laute
f48390049a Last fixes 2026-01-13 14:44:34 +01:00
Niels Laute
c4ff23a348 Fixing icons 2026-01-13 14:23:27 +01:00
Niels Laute
ef84f93f87 Fixing a bunch of stuff 2026-01-12 18:02:03 +01:00
Niels Laute
0420a83b9e Fixes 2026-01-09 17:19:49 +01:00
Niels Laute
4e45b22108 Changes 2026-01-09 14:19:19 +01:00
Niels Laute
c0f3df3e04 Updating markdowntextblock 2026-01-08 20:44:37 +01:00
Niels Laute
d77c244f69 Merge branch 'main' into niels9001/release-notes-ux 2026-01-08 20:44:19 +01:00
Niels Laute
8b9ccd92b8 Improvements 2025-12-28 17:38:44 +01:00
Niels Laute
85d868e2f3 Release notes styling and formatting 2025-12-28 15:07:21 +01:00
Niels Laute
f9f33c8098 Support for Hero image 2025-12-28 14:35:52 +01:00
Niels Laute
2698cfc160 Creating a separate Window for release notes 2025-12-27 17:48:51 +01:00
72 changed files with 775 additions and 2085 deletions

View File

@@ -4,10 +4,6 @@
<Platform Name="x64" />
</Configurations>
<Folder Name="/common/">
<Project Path="src/common/AllExperiments/AllExperiments.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/common/CalculatorEngineCommon/CalculatorEngineCommon.vcxproj" Id="2cf78cf7-8feb-4be1-9591-55fa25b48fc6" />
<Project Path="src/common/Common.Search/Common.Search.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />

View File

@@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TargetName>PowerToys.AllExperiments</TargetName>
<MockDirectory>.\Microsoft.VariantAssignment\</MockDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<!-- Experimentation is live, forcing inclusion -->
<ItemGroup Condition="'$(IsExperimentationLive)'!=''">
<!-- Newtonsoft.Json is included and a version specified in Directory.Packages.props to avoid a vulnerability from older versions. -->
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Microsoft.VariantAssignment.Client" />
<PackageReference Include="Microsoft.VariantAssignment.Contract" />
<Compile Remove=".\$(MockDirectory)\Client\*.cs" />
<Compile Remove=".\$(MockDirectory)\Contract\*.cs" />
</ItemGroup>
</Project>

View File

@@ -1,214 +0,0 @@
// 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.Globalization;
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Telemetry;
using Microsoft.VariantAssignment.Client;
using Microsoft.VariantAssignment.Contract;
using Windows.System.Profile;
namespace AllExperiments
{
// The dependencies required to build this project are only available in the official build pipeline and are internal to Microsoft.
// However, this project is not required to build a test version of the application.
public class Experiments
{
public enum ExperimentState
{
Enabled,
Disabled,
NotLoaded,
}
#pragma warning disable SA1401 // Need to use LandingPageExperiment as a static property in OobeShellPage.xaml.cs
#pragma warning disable CA2211 // Non-constant fields should not be visible
public static ExperimentState LandingPageExperiment = ExperimentState.NotLoaded;
#pragma warning restore CA2211
#pragma warning restore SA1401
public async Task<bool> EnableLandingPageExperimentAsync()
{
if (Experiments.LandingPageExperiment != ExperimentState.NotLoaded)
{
return Experiments.LandingPageExperiment == ExperimentState.Enabled;
}
Experiments varServ = new Experiments();
await varServ.VariantAssignmentProvider_Initialize();
var landingPageExperiment = varServ.IsExperiment;
Experiments.LandingPageExperiment = landingPageExperiment ? ExperimentState.Enabled : ExperimentState.Disabled;
return landingPageExperiment;
}
private async Task VariantAssignmentProvider_Initialize()
{
IsExperiment = false;
string jsonFilePath = CreateFilePath();
var vaSettings = new VariantAssignmentClientSettings
{
Endpoint = new Uri("https://default.exp-tas.com/exptas77/a7a397e7-6fbe-4f21-a4e9-3f542e4b000e-exppowertoys/api/v1/tas"),
EnableCaching = true,
ResponseCacheTime = TimeSpan.FromMinutes(5),
};
try
{
var vaClient = vaSettings.GetTreatmentAssignmentServiceClient();
var vaRequest = GetVariantAssignmentRequest();
using var variantAssignments = await vaClient.GetVariantAssignmentsAsync(vaRequest).ConfigureAwait(false);
if (variantAssignments.AssignedVariants.Count != 0)
{
var dataVersion = variantAssignments.DataVersion;
var featureVariables = variantAssignments.GetFeatureVariables();
var assignmentContext = variantAssignments.GetAssignmentContext();
var featureFlagValue = featureVariables[0].GetStringValue();
var experimentGroup = string.Empty;
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
if (!jsonDictionary.TryGetValue("dataversion", out object? value))
{
value = dataVersion;
jsonDictionary.Add("dataversion", value);
}
if (!jsonDictionary.ContainsKey("variantassignment"))
{
jsonDictionary.Add("variantassignment", featureFlagValue);
}
else
{
var jsonDataVersion = value.ToString();
if (jsonDataVersion != null && int.Parse(jsonDataVersion, CultureInfo.InvariantCulture) < dataVersion)
{
jsonDictionary["dataversion"] = dataVersion;
jsonDictionary["variantassignment"] = featureFlagValue;
}
}
experimentGroup = jsonDictionary["variantassignment"].ToString();
string output = JsonSerializer.Serialize(jsonDictionary);
File.WriteAllText(jsonFilePath, output);
}
if (experimentGroup == "alternate" && AssignmentUnit != string.Empty)
{
IsExperiment = true;
}
PowerToysTelemetry.Log.WriteEvent(new OobeVariantAssignmentEvent() { AssignmentContext = assignmentContext, ClientID = AssignmentUnit });
}
}
catch (HttpRequestException ex)
{
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
if (jsonDictionary.TryGetValue("variantassignment", out object? value))
{
if (value.ToString() == "alternate" && AssignmentUnit != string.Empty)
{
IsExperiment = true;
}
}
else
{
jsonDictionary["variantassignment"] = "current";
}
}
string output = JsonSerializer.Serialize(jsonDictionary);
File.WriteAllText(jsonFilePath, output);
Logger.LogError("Error getting to TAS endpoint", ex);
}
catch (Exception ex)
{
Logger.LogError("Error getting variant assignments for experiment", ex);
}
}
public bool IsExperiment { get; set; }
private string? AssignmentUnit { get; set; }
private VariantAssignmentRequest GetVariantAssignmentRequest()
{
var jsonFilePath = CreateFilePath();
try
{
if (!File.Exists(jsonFilePath))
{
AssignmentUnit = Guid.NewGuid().ToString();
var data = new Dictionary<string, string>()
{
["clientid"] = AssignmentUnit,
};
string jsonData = JsonSerializer.Serialize(data);
File.WriteAllText(jsonFilePath, jsonData);
}
else
{
string json = File.ReadAllText(jsonFilePath);
var jsonDictionary = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(json);
if (jsonDictionary != null)
{
AssignmentUnit = jsonDictionary["clientid"]?.ToString();
}
}
}
catch (Exception ex)
{
Logger.LogError("Error creating/getting AssignmentUnit", ex);
}
var attrNames = new List<string> { "FlightRing", "c:InstallLanguage" };
var attrData = AnalyticsInfo.GetSystemPropertiesAsync(attrNames).AsTask().GetAwaiter().GetResult();
var flightRing = string.Empty;
var installLanguage = string.Empty;
if (attrData.ContainsKey("FlightRing"))
{
flightRing = attrData["FlightRing"];
}
if (attrData.ContainsKey("InstallLanguage"))
{
installLanguage = attrData["InstallLanguage"];
}
return new VariantAssignmentRequest
{
Parameters =
{
{ "installLanguage", installLanguage },
{ "flightRing", flightRing },
{ "clientid", AssignmentUnit },
},
};
}
private string CreateFilePath()
{
var exeDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var settingsPath = @"Microsoft\PowerToys\experimentation.json";
var filePath = Path.Combine(exeDir, settingsPath);
return filePath;
}
}
}

View File

@@ -1,81 +0,0 @@
// 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.Diagnostics;
using System.Globalization;
using System.IO.Abstractions;
namespace AllExperiments
{
public static class Logger
{
private static readonly IFileSystem FileSystem = new FileSystem();
private static readonly IPath Path = FileSystem.Path;
private static readonly IDirectory Directory = FileSystem.Directory;
private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs\\Experimentation");
static Logger()
{
if (!Directory.Exists(ApplicationLogPath))
{
Directory.CreateDirectory(ApplicationLogPath);
}
// Using InvariantCulture since this is used for a log file name
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
public static void LogInfo(string message)
{
Log(message, "INFO");
}
public static void LogError(string message)
{
Log(message, "ERROR");
#if DEBUG
Debugger.Break();
#endif
}
public static void LogError(string message, Exception e)
{
Log(
message + Environment.NewLine +
e?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
e?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
e?.StackTrace,
"ERROR");
#if DEBUG
Debugger.Break();
#endif
}
private static void Log(string message, string type)
{
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
Trace.Indent();
Trace.WriteLine(GetCallerInfo());
Trace.WriteLine(message);
Trace.Unindent();
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType?.Name;
return "[Method]: " + methodName?.Name + " [Class]: " + className;
}
}
}

View File

@@ -1,21 +0,0 @@
// 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 Microsoft.VariantAssignment.Contract;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Client
{
#pragma warning disable SA1200 // Using directives should be placed correctly
using TreatmentAssignmentServiceClient = VariantAssignmentServiceClient<TreatmentAssignmentServiceResponse>;
#pragma warning restore SA1200 // Using directives should be placed correctly
public static class VariantAssignmentClientExtensionMethods
{
public static IVariantAssignmentProvider GetTreatmentAssignmentServiceClient(this VariantAssignmentClientSettings settings)
{
return new TreatmentAssignmentServiceClient();
}
}
}

View File

@@ -1,23 +0,0 @@
// 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 Microsoft.VariantAssignment.Contract;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Client
{
internal sealed partial class VariantAssignmentServiceClient<TServerResponse> : IVariantAssignmentProvider, IDisposable
where TServerResponse : VariantAssignmentServiceResponse
{
public void Dispose()
{
throw new NotImplementedException();
}
public Task<IVariantAssignmentResponse> GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default)
{
return Task.FromResult(EmptyVariantAssignmentResponse.Instance);
}
}
}

View File

@@ -1,54 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public class EmptyVariantAssignmentResponse : IVariantAssignmentResponse
{
/// <summary>
/// Singleton instance of <see cref="EmptyVariantAssignmentResponse"/>.
/// </summary>
public static readonly IVariantAssignmentResponse Instance = new EmptyVariantAssignmentResponse();
public EmptyVariantAssignmentResponse()
{
}
public long DataVersion => 0;
public string Thumbprint => string.Empty;
/// <inheritdoc/>
public IReadOnlyCollection<IAssignedVariant> AssignedVariants => Array.Empty<IAssignedVariant>();
/// <inheritdoc/>
#pragma warning disable CS8603 // Possible null reference return.
public IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path) => null;
#pragma warning restore CS8603 // Possible null reference return.
/// <inheritdoc/>
public IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix) => Array.Empty<IFeatureVariable>();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
}
string IVariantAssignmentResponse.GetAssignmentContext()
{
throw new NotImplementedException();
}
IReadOnlyList<IFeatureVariable> IVariantAssignmentResponse.GetFeatureVariables()
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,11 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IAssignedVariant
{
}
}

View File

@@ -1,16 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IFeatureVariable
{
/// <summary>
/// Gets the variable's value as a string.
/// </summary>
/// <returns>String value of the variable.</returns>
string GetStringValue();
}
}

View File

@@ -1,18 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IVariantAssignmentProvider : IDisposable
{
/// <summary>
/// Computes variant assignments based on <paramref name="request"/> data.
/// </summary>
/// <param name="request">Variant assignment parameters.</param>
/// <param name="ct">Propagates notification that operations should be canceled.</param>
/// <returns>An awaitable task that returns a <see cref="IVariantAssignmentResponse"/>.</returns>
Task<IVariantAssignmentResponse> GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default);
}
}

View File

@@ -1,15 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public interface IVariantAssignmentRequest
{
/// <summary>
/// Gets inputs used for evaluating filters, assignment units, etc.
/// </summary>
IReadOnlyCollection<(string Key, string Value)> Parameters { get; }
}
}

View File

@@ -1,48 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Snapshot of variant assignments.
/// </summary>
public interface IVariantAssignmentResponse : IDisposable
{
///// <summary>
///// Gets the serial number of variant assignment configuration snapshot used when assigning variants.
///// </summary>
long DataVersion { get; }
///// <summary>
///// Get a hash of the response suitable for caching.
///// </summary>
// string Thumbprint { get; }
/// <summary>
/// Gets the variants assigned based on request parameters and a variant configuration snapshot.
/// </summary>
IReadOnlyCollection<IAssignedVariant> AssignedVariants { get; }
/// <summary>
/// Gets feature variables assigned by variants in this response.
/// </summary>
/// <param name="prefix">(Optional) Filter feature variables where <see cref="IFeatureVariable.KeySegments"/> contains the <paramref name="prefix"/>.</param>
/// <returns>Range of matching feature variables.</returns>
IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix);
// this actually part of the interface but gets the job done
IReadOnlyList<IFeatureVariable> GetFeatureVariables();
// this actually part of the interface but gets the job done
string GetAssignmentContext();
/// <summary>
/// Gets a single feature variable assigned by variants in this response.
/// </summary>
/// <param name="path">Exact feature variable path.</param>
/// <returns>Matching feature variable or null.</returns>
IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path);
}
}

View File

@@ -1,11 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
internal sealed class TreatmentAssignmentServiceResponse : VariantAssignmentServiceResponse
{
}
}

View File

@@ -1,31 +0,0 @@
// 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.ComponentModel.DataAnnotations;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Configuration for variant assignment service client.
/// </summary>
public class VariantAssignmentClientSettings
{
/// <summary>
/// Gets or sets the variant assignment service endpoint URL.
/// </summary>
[Required]
public Uri? Endpoint { get; set; }
/// <summary>
/// Gets or sets a value indicating whether gets or sets a value whether client side request caching should be enabled.
/// </summary>
public bool EnableCaching { get; set; }
/// <summary>
/// Gets or sets the maximum time a cached variant assignment response may be used without re-validating.
/// </summary>
public TimeSpan ResponseCacheTime { get; set; }
}
}

View File

@@ -1,21 +0,0 @@
// 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.Collections.Specialized;
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
public class VariantAssignmentRequest : IVariantAssignmentRequest
{
private NameValueCollection _parameters = new NameValueCollection();
/// <summary>
/// Gets or sets mutable <see cref="IVariantAssignmentRequest.Parameters"/>.
/// </summary>
public NameValueCollection Parameters { get => _parameters; set => _parameters = value; }
IReadOnlyCollection<(string Key, string Value)> IVariantAssignmentRequest.Parameters => (IReadOnlyCollection<(string Key, string Value)>)_parameters;
}
}

View File

@@ -1,48 +0,0 @@
// 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.
// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects
namespace Microsoft.VariantAssignment.Contract
{
/// <summary>
/// Mutable implementation of <see cref="IVariantAssignmentResponse"/> for (de)serialization.
/// </summary>
internal class VariantAssignmentServiceResponse : IVariantAssignmentResponse, IDisposable
{
/// <inheritdoc />
public virtual long DataVersion { get; set; }
public virtual IReadOnlyCollection<IAssignedVariant> AssignedVariants { get; set; } = Array.Empty<IAssignedVariant>();
public IFeatureVariable GetFeatureVariable(IReadOnlyList<string> path)
{
throw new NotImplementedException();
}
public IReadOnlyList<IFeatureVariable> GetFeatureVariables(IReadOnlyList<string> prefix)
{
throw new NotImplementedException();
}
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IReadOnlyList<IFeatureVariable> GetFeatureVariables()
{
throw new NotImplementedException();
}
public string GetAssignmentContext()
{
return string.Empty;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

View File

@@ -2,12 +2,72 @@
// 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.Collections.ObjectModel;
using System.Linq;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
namespace Microsoft.PowerToys.Settings.UI.OOBE.ViewModel
{
public class OobeShellViewModel
{
public ObservableCollection<OobePowerToysModule> Modules { get; } = new();
public OobeShellViewModel()
{
Modules = new ObservableCollection<OobePowerToysModule>(
new (PowerToysModules Module, bool IsNew)[]
{
(PowerToysModules.Overview, false),
(PowerToysModules.AdvancedPaste, true),
(PowerToysModules.AlwaysOnTop, false),
(PowerToysModules.Awake, false),
(PowerToysModules.CmdNotFound, false),
(PowerToysModules.CmdPal, true),
(PowerToysModules.ColorPicker, false),
(PowerToysModules.CropAndLock, false),
(PowerToysModules.EnvironmentVariables, false),
(PowerToysModules.FancyZones, false),
(PowerToysModules.FileLocksmith, false),
(PowerToysModules.FileExplorer, false),
(PowerToysModules.ImageResizer, false),
(PowerToysModules.KBM, false),
(PowerToysModules.LightSwitch, true),
(PowerToysModules.MouseUtils, false),
(PowerToysModules.MouseWithoutBorders, false),
(PowerToysModules.Peek, false),
(PowerToysModules.PowerRename, false),
(PowerToysModules.Run, false),
(PowerToysModules.QuickAccent, false),
(PowerToysModules.ShortcutGuide, false),
(PowerToysModules.TextExtractor, false),
(PowerToysModules.MeasureTool, false),
(PowerToysModules.Hosts, false),
(PowerToysModules.Workspaces, true),
(PowerToysModules.RegistryPreview, false),
(PowerToysModules.NewPlus, true),
(PowerToysModules.ZoomIt, true),
}
.Select(x => new OobePowerToysModule
{
ModuleName = x.Module.ToString(),
IsNew = x.IsNew,
}));
}
public OobePowerToysModule GetModule(PowerToysModules module)
{
return Modules.First(m => m.ModuleName == module.ToString());
}
public OobePowerToysModule GetModuleFromTag(string tag)
{
if (!Enum.TryParse<PowerToysModules>(tag, ignoreCase: true, out var module))
{
throw new ArgumentException($"Invalid module tag: {tag}", nameof(tag));
}
return GetModule(module);
}
}
}

View File

@@ -28,8 +28,6 @@
<None Remove="Assets\Settings\Icons\Models\WindowsML.svg" />
<None Remove="Assets\Settings\Modules\APDialog.dark.png" />
<None Remove="Assets\Settings\Modules\APDialog.light.png" />
<None Remove="Assets\Settings\Modules\CmdPal_Background.png" />
<None Remove="Assets\Settings\Modules\CmdPal_Hero.png" />
<None Remove="Assets\Settings\Modules\LightSwitch.png" />
<None Remove="SettingsXAML\Controls\Dashboard\CheckUpdateControl.xaml" />
<None Remove="SettingsXAML\Controls\Dashboard\ShortcutConflictControl.xaml" />
@@ -115,7 +113,6 @@
<!-- HACK: Common.UI is referenced, even if it is not used, to force dll versions to be the same as in other projects that use it. It's still unclear why this is the case, but this is need for flattening the install directory. -->
<ProjectReference Include="..\..\common\Common.Search\Common.Search.csproj" />
<ProjectReference Include="..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\common\AllExperiments\AllExperiments.csproj" />
<ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\modules\ZoomIt\ZoomItSettingsInterop\ZoomItSettingsInterop.vcxproj" />

View File

@@ -13,6 +13,8 @@ using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
using Microsoft.PowerToys.Settings.UI.Services;
using Microsoft.PowerToys.Settings.UI.Views;
@@ -30,6 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI
/// </summary>
public partial class App : Application
{
public static OobeShellViewModel OobeShellViewModel { get; } = new();
private OobeWindow oobeWindow;
private ScoobeWindow scoobeWindow;
private enum Arguments
{
PTPipeName = 1,
@@ -231,10 +239,6 @@ namespace Microsoft.PowerToys.Settings.UI
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
// Need to call SetForegroundWindow to actually gain focus.
WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle());
// https://github.com/microsoft/microsoft-ui-xaml/issues/8948 - A window's top border incorrectly
// renders as black on Windows 10.
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
}
else
{
@@ -245,20 +249,11 @@ namespace Microsoft.PowerToys.Settings.UI
if (ShowOobe)
{
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview);
oobeWindow.Activate();
oobeWindow.ExtendsContentIntoTitleBar = true;
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
SetOobeWindow(oobeWindow);
OpenOobe();
}
else if (ShowScoobe)
{
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
ScoobeWindow newScoobeWindow = new ScoobeWindow();
newScoobeWindow.Activate();
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
SetScoobeWindow(newScoobeWindow);
OpenScoobe();
}
}
}
@@ -268,7 +263,7 @@ namespace Microsoft.PowerToys.Settings.UI
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
var cmdArgs = Environment.GetCommandLineArgs();
@@ -294,8 +289,6 @@ namespace Microsoft.PowerToys.Settings.UI
// For debugging purposes
// Window is also needed to show MessageDialog
settingsWindow = new MainWindow();
settingsWindow.ExtendsContentIntoTitleBar = true;
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
settingsWindow.Activate();
settingsWindow.NavigateToSection(StartupPage);
@@ -303,7 +296,7 @@ namespace Microsoft.PowerToys.Settings.UI
GlobalHotkeyConflictManager.Initialize(message =>
{
// In debug mode, just log or do nothing
System.Diagnostics.Debug.WriteLine($"IPC Message: {message}");
Debug.WriteLine($"IPC Message: {message}");
return 0;
});
#else
@@ -335,8 +328,6 @@ namespace Microsoft.PowerToys.Settings.UI
public static ThemeService ThemeService => themeService;
private static MainWindow settingsWindow;
private static OobeWindow oobeWindow;
private static ScoobeWindow scoobeWindow;
public static void ClearSettingsWindow()
{
@@ -348,34 +339,46 @@ namespace Microsoft.PowerToys.Settings.UI
return settingsWindow;
}
public static OobeWindow GetOobeWindow()
public void OpenScoobe()
{
return oobeWindow;
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
if (scoobeWindow == null)
{
scoobeWindow = new ScoobeWindow();
scoobeWindow.Closed += (_, _) =>
{
scoobeWindow = null;
};
scoobeWindow.Activate();
}
else
{
WindowHelpers.BringToForeground(scoobeWindow.GetWindowHandle());
}
}
public static void SetOobeWindow(OobeWindow window)
public void OpenOobe()
{
oobeWindow = window;
}
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
public static void ClearOobeWindow()
{
oobeWindow = null;
}
if (oobeWindow == null)
{
oobeWindow = new OobeWindow();
public static ScoobeWindow GetScoobeWindow()
{
return scoobeWindow;
}
oobeWindow.Closed += (_, _) =>
{
oobeWindow = null;
};
public static void SetScoobeWindow(ScoobeWindow window)
{
scoobeWindow = window;
}
public static void ClearScoobeWindow()
{
scoobeWindow = null;
oobeWindow.Activate();
}
else
{
WindowHelpers.BringToForeground(oobeWindow.GetWindowHandle());
}
}
public static Type GetPage(string settingWindow)

View File

@@ -12,6 +12,7 @@ using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Windows.Data.Json;
@@ -75,7 +76,7 @@ namespace Microsoft.PowerToys.Settings.UI
// open main window
ShellPage.SetOpenMainWindowCallback(type =>
{
DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
App.OpenSettingsWindow(type));
});
@@ -106,30 +107,8 @@ namespace Microsoft.PowerToys.Settings.UI
return needToUpdate;
});
// open oobe
ShellPage.SetOpenOobeCallback(() =>
{
if (App.GetOobeWindow() == null)
{
App.SetOobeWindow(new OobeWindow(OOBE.Enums.PowerToysModules.Overview));
}
App.GetOobeWindow().Activate();
});
// open whats new window
ShellPage.SetOpenWhatIsNewCallback(() =>
{
if (App.GetScoobeWindow() == null)
{
App.SetScoobeWindow(new ScoobeWindow());
}
App.GetScoobeWindow().Activate();
});
this.InitializeComponent();
SetAppTitleBar();
SetTitleBar();
// receive IPC Message
App.IPCMessageReceivedCallback = (string msg) =>
@@ -156,21 +135,22 @@ namespace Microsoft.PowerToys.Settings.UI
PowerToysTelemetry.Log.WriteEvent(new SettingsBootEvent() { BootTimeMs = bootTime.ElapsedMilliseconds });
}
private void SetAppTitleBar()
private void SetTitleBar()
{
// We need to assign the window here so it can configure the custom title bar area correctly.
shellPage.TitleBar.Window = this;
this.ExtendsContentIntoTitleBar = true;
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(this));
}
public void NavigateToSection(System.Type type)
public void NavigateToSection(Type type)
{
ShellPage.Navigate(type);
}
public void CloseHiddenWindow()
{
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var hWnd = WindowNative.GetWindowHandle(this);
if (!NativeMethods.IsWindowVisible(hWnd))
{
Close();
@@ -179,18 +159,12 @@ namespace Microsoft.PowerToys.Settings.UI
private void Window_Closed(object sender, WindowEventArgs args)
{
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var hWnd = WindowNative.GetWindowHandle(this);
WindowHelper.SerializePlacement(hWnd);
if (App.GetOobeWindow() == null && App.GetScoobeWindow() == null)
{
App.ClearSettingsWindow();
}
else
{
args.Handled = true;
NativeMethods.ShowWindow(hWnd, NativeMethods.SW_HIDE);
}
App.ClearSettingsWindow();
args.Handled = true;
NativeMethods.ShowWindow(hWnd, NativeMethods.SW_HIDE);
App.ThemeService.ThemeChanged -= OnThemeChanged;
}
@@ -198,7 +172,7 @@ namespace Microsoft.PowerToys.Settings.UI
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
{
// Set window icon
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var hWnd = WindowNative.GetWindowHandle(this);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("Assets\\Settings\\icon.ico");

View File

@@ -18,15 +18,16 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeAdvancedPaste()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.AdvancedPaste]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.AdvancedPaste);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(AdvancedPastePage));
OobeWindow.OpenMainWindowCallback(typeof(AdvancedPastePage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeAlwaysOnTop()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.AlwaysOnTop]);
ViewModel = new OobeShellViewModel().Modules[(int)PowerToysModules.AlwaysOnTop];
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(AlwaysOnTopPage));
OobeWindow.OpenMainWindowCallback(typeof(AlwaysOnTopPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -17,15 +17,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeAwake()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Awake]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Awake);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(AwakePage));
OobeWindow.OpenMainWindowCallback(typeof(AwakePage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeCmdNotFound()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.CmdNotFound]);
ViewModel = ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.CmdNotFound);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(CmdNotFoundPage));
OobeWindow.OpenMainWindowCallback(typeof(CmdNotFoundPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeCmdPal()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.CmdPal]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.CmdPal);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(CmdPalPage));
OobeWindow.OpenMainWindowCallback(typeof(CmdPalPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeColorPicker()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.ColorPicker]);
ViewModel = ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.ColorPicker);
DataContext = ViewModel;
}
private void Start_ColorPicker_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.ColorPickerSharedEventCallback != null)
if (OobeWindow.ColorPickerSharedEventCallback != null)
{
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, OobeShellPage.ColorPickerSharedEventCallback()))
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, OobeWindow.ColorPickerSharedEventCallback()))
{
eventHandle.Set();
}
@@ -39,9 +39,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(ColorPickerPage));
OobeWindow.OpenMainWindowCallback(typeof(ColorPickerPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeCropAndLock()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.CropAndLock]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.CropAndLock);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(CropAndLockPage));
OobeWindow.OpenMainWindowCallback(typeof(CropAndLockPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeEnvironmentVariables()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.EnvironmentVariables]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.EnvironmentVariables);
DataContext = ViewModel;
}
@@ -50,9 +50,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void Launch_Settings_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(EnvironmentVariablesPage));
OobeWindow.OpenMainWindowCallback(typeof(EnvironmentVariablesPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeFancyZones()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.FancyZones]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.FancyZones);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(FancyZonesPage));
OobeWindow.OpenMainWindowCallback(typeof(FancyZonesPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -10,9 +10,6 @@ using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OobeFileExplorer : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeFileExplorer()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.FileExplorer]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.FileExplorer);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PowerPreviewPage));
OobeWindow.OpenMainWindowCallback(typeof(PowerPreviewPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -10,9 +10,6 @@ using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OobeFileLocksmith : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeFileLocksmith()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.FileLocksmith]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.FileLocksmith);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(FileLocksmithPage));
OobeWindow.OpenMainWindowCallback(typeof(FileLocksmithPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeHosts()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Hosts]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Hosts);
DataContext = ViewModel;
}
@@ -50,9 +50,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void Launch_Settings_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(HostsPage));
OobeWindow.OpenMainWindowCallback(typeof(HostsPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeImageResizer()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.ImageResizer]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.ImageResizer);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(ImageResizerPage));
OobeWindow.OpenMainWindowCallback(typeof(ImageResizerPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeKBM()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.KBM]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.KBM);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(KeyboardManagerPage));
OobeWindow.OpenMainWindowCallback(typeof(KeyboardManagerPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -17,14 +17,14 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeLightSwitch()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.LightSwitch]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.LightSwitch);
}
private void SettingsLaunchButton_Click(object sender, RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(LightSwitchPage));
OobeWindow.OpenMainWindowCallback(typeof(LightSwitchPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -21,15 +21,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeMeasureTool()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.MeasureTool]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.MeasureTool);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(MeasureToolPage));
OobeWindow.OpenMainWindowCallback(typeof(MeasureToolPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -17,15 +17,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeMouseUtils()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.MouseUtils]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.MouseUtils);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(MouseUtilsPage));
OobeWindow.OpenMainWindowCallback(typeof(MouseUtilsPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeMouseWithoutBorders()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.MouseWithoutBorders]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.MouseWithoutBorders);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(MouseWithoutBordersPage));
OobeWindow.OpenMainWindowCallback(typeof(MouseWithoutBordersPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeNewPlus()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.NewPlus]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.NewPlus);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(NewPlusPage));
OobeWindow.OpenMainWindowCallback(typeof(NewPlusPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -52,26 +52,8 @@
</tkcontrols:SettingsCard.Description>
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind EnableDataDiagnostics, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<!-- Show title and description only when there are conflicts -->
<TextBlock
x:Uid="Oobe_Overview_Hotkey_Conflict_Title"
Margin="0,24,0,8"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<!-- Always show shortcut status card -->
<tkcontrols:SettingsCard Description="{x:Bind ConflictDescription, Mode=OneWay}" Header="{x:Bind ConflictText, Mode=OneWay}">
<tkcontrols:SettingsCard.HeaderIcon>
<FontIcon Foreground="{x:Bind IconForeground, Mode=OneWay}" Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
</tkcontrols:SettingsCard.HeaderIcon>
<!-- Only show button when there are conflicts -->
<Button
x:Uid="ResolveConflicts_Button"
Click="ShortcutConflictBtn_Click"
Visibility="{x:Bind HasConflicts, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
</tkcontrols:SettingsCard>
</StackPanel>
</StackPanel>
</controls:OOBEPageControl.PageContent>
</controls:OOBEPageControl>
</Page>
</Page>

View File

@@ -4,18 +4,11 @@
using System.ComponentModel;
using global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Services;
using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
@@ -25,10 +18,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePowerToysModule ViewModel { get; set; }
private bool _enableDataDiagnostics;
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
private Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
private int _conflictCount;
public bool EnableDataDiagnostics
{
@@ -53,165 +42,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
}
}
public AllHotkeyConflictsData AllHotkeyConflictsData
{
get => _allHotkeyConflictsData;
set
{
if (_allHotkeyConflictsData != value)
{
_allHotkeyConflictsData = value;
UpdateConflictCount();
OnPropertyChanged(nameof(AllHotkeyConflictsData));
OnPropertyChanged(nameof(ConflictCount));
OnPropertyChanged(nameof(ConflictText));
OnPropertyChanged(nameof(ConflictDescription));
OnPropertyChanged(nameof(HasConflicts));
OnPropertyChanged(nameof(IconGlyph));
OnPropertyChanged(nameof(IconForeground));
}
}
}
public int ConflictCount => _conflictCount;
private void UpdateConflictCount()
{
int count = 0;
if (AllHotkeyConflictsData == null)
{
_conflictCount = count;
}
if (AllHotkeyConflictsData.InAppConflicts != null)
{
foreach (var inAppConflict in AllHotkeyConflictsData.InAppConflicts)
{
var hotkey = inAppConflict.Hotkey;
var hotkeySettings = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
if (!HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySettings))
{
count++;
}
}
}
if (AllHotkeyConflictsData.SystemConflicts != null)
{
foreach (var systemConflict in AllHotkeyConflictsData.SystemConflicts)
{
var hotkey = systemConflict.Hotkey;
var hotkeySettings = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
if (!HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySettings))
{
count++;
}
}
}
_conflictCount = count;
}
public string ConflictText
{
get
{
var count = ConflictCount;
if (count == 0)
{
// Return no-conflict message
try
{
return resourceLoader.GetString("ShortcutConflictControl_NoConflictsFound");
}
catch
{
return "No conflicts found";
}
}
else if (count == 1)
{
// Try to get localized string
try
{
return resourceLoader.GetString("ShortcutConflictControl_SingleConflictFound");
}
catch
{
return "1 shortcut conflict";
}
}
else
{
// Try to get localized string
try
{
var template = resourceLoader.GetString("ShortcutConflictControl_MultipleConflictsFound");
return string.Format(System.Globalization.CultureInfo.CurrentCulture, template, count);
}
catch
{
return $"{count} shortcut conflicts";
}
}
}
}
public string ConflictDescription
{
get
{
var count = ConflictCount;
if (count == 0)
{
// Return no-conflict description
try
{
return resourceLoader.GetString("ShortcutConflictWindow_NoConflictsDescription");
}
catch
{
return "All shortcuts function correctly";
}
}
else
{
// Return conflict description
try
{
return resourceLoader.GetString("Oobe_Overview_Hotkey_Conflict_Card_Description");
}
catch
{
return "Shortcuts configured by PowerToys are conflicting";
}
}
}
}
public bool HasConflicts => ConflictCount > 0;
public string IconGlyph => HasConflicts ? "\uE814" : "\uE73E";
public SolidColorBrush IconForeground
{
get
{
if (HasConflicts)
{
// Red color for conflicts
return (SolidColorBrush)App.Current.Resources["SystemFillColorCriticalBrush"];
}
else
{
// Green color for no conflicts
return (SolidColorBrush)App.Current.Resources["SystemFillColorSuccessBrush"];
}
}
}
public bool ShowDataDiagnosticsSetting => GetIsDataDiagnosticsInfoBarEnabled();
public event PropertyChangedEventHandler PropertyChanged;
@@ -229,23 +59,8 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
_enableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Overview);
DataContext = this;
// Subscribe to hotkey conflict updates
if (GlobalHotkeyConflictManager.Instance != null)
{
GlobalHotkeyConflictManager.Instance.ConflictsUpdated += OnConflictsUpdated;
GlobalHotkeyConflictManager.Instance.RequestAllConflicts();
}
}
private void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
{
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
{
AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
});
}
private void OnPropertyChanged(string propertyName)
@@ -255,9 +70,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(DashboardPage));
OobeWindow.OpenMainWindowCallback(typeof(DashboardPage));
}
ViewModel.LogOpeningSettingsEvent();
@@ -265,26 +80,14 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void GeneralSettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage));
OobeWindow.OpenMainWindowCallback(typeof(GeneralPage));
}
ViewModel.LogOpeningSettingsEvent();
}
private void ShortcutConflictBtn_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (AllHotkeyConflictsData == null || !HasConflicts)
{
return;
}
// Create and show the shortcut conflict window
var conflictWindow = new ShortcutConflictWindow();
conflictWindow.Activate();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
@@ -293,12 +96,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
// Unsubscribe from conflict updates when leaving the page
if (GlobalHotkeyConflictManager.Instance != null)
{
GlobalHotkeyConflictManager.Instance.ConflictsUpdated -= OnConflictsUpdated;
}
}
}
}

View File

@@ -1,192 +0,0 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeOverviewAlternate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<controls:OOBEPageControl
x:Uid="Oobe_Overview"
HeroImage="ms-appx:///Assets/Settings/Modules/OOBE/PTHeroShort.png"
HeroImageHeight="120">
<controls:OOBEPageControl.PageContent>
<StackPanel Orientation="Vertical" Spacing="12">
<TextBlock
x:Uid="Alternate_OOBE_Description"
Margin="0,24,0,12"
FontWeight="SemiBold" />
<GridView SelectionMode="None">
<GridViewItem>
<Grid
Width="280"
Margin="8"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/Settings/Icons/FancyZones.png" />
<TextBlock
x:Uid="Alternate_OOBE_FancyZones_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_FancyZones_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="FancyZonesHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/Settings/Icons/PowerToysRun.png" />
<TextBlock
x:Uid="Alternate_OOBE_Run_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_Run_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="RunHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/Settings/Icons/ColorPicker.png" />
<TextBlock
x:Uid="Alternate_OOBE_ColorPicker_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_ColorPicker_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="To pick a color:"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="ColorPickerHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
<GridViewItem>
<Grid
Width="280"
Padding="16,16,16,10"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Width="36"
HorizontalAlignment="Left"
Source="ms-appx:///Assets/Settings/Icons/AlwaysOnTop.png" />
<TextBlock
x:Uid="Alternate_OOBE_AlwaysOnTop_Title"
Grid.Row="1"
Margin="0,12,0,6"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
TextWrapping="Wrap" />
<TextBlock
x:Uid="Alternate_OOBE_AlwaysOnTop_Description"
Grid.Row="2"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<controls:ShortcutWithTextLabelControl
x:Name="AlwaysOnTopHotkeyControl"
Grid.Row="3"
Margin="0,8,0,0" />
</Grid>
</GridViewItem>
</GridView>
</StackPanel>
</controls:OOBEPageControl.PageContent>
</controls:OOBEPageControl>
</Page>

View File

@@ -1,50 +0,0 @@
// 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 Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeOverviewAlternate : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeOverviewAlternate()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
DataContext = ViewModel;
FancyZonesHotkeyControl.Keys = SettingsRepository<FancyZonesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList();
RunHotkeyControl.Keys = SettingsRepository<PowerLauncherSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList();
ColorPickerHotkeyControl.Keys = SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.GetKeysList();
AlwaysOnTopHotkeyControl.Keys = SettingsRepository<AlwaysOnTopSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.GetKeysList();
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(DashboardPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@@ -1,19 +0,0 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<!-- Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeOverviewPlaceholder"
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"
Loaded="Page_Loaded"
mc:Ignorable="d">
<ProgressRing
x:Name="LoadingProgressRing"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True"
Visibility="Visible" />
</Page>

View File

@@ -1,74 +0,0 @@
// 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.Threading.Tasks;
using AllExperiments;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Services;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeOverviewPlaceholder : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeOverviewPlaceholder()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
DataContext = ViewModel;
}
private static async Task<bool> GetIsExperiment()
{
Experiments landingPageExp = new Experiments();
var experimentEnabled = await landingPageExp.EnableLandingPageExperimentAsync();
return experimentEnabled;
}
private async void Reload()
{
var isExperiment = await GetIsExperiment();
if (isExperiment)
{
this.Frame.Navigate(typeof(OobeOverviewAlternate));
}
else
{
this.Frame.Navigate(typeof(OobeOverview));
}
}
private void Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
Reload();
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(DashboardPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@@ -21,15 +21,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePeek()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Peek]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Peek);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PeekPage));
OobeWindow.OpenMainWindowCallback(typeof(PeekPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -17,15 +17,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePowerAccent()
{
InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.QuickAccent]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.QuickAccent);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PowerAccentPage));
OobeWindow.OpenMainWindowCallback(typeof(PowerAccentPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePowerOCR()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.TextExtractor]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.TextExtractor);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PowerOcrPage));
OobeWindow.OpenMainWindowCallback(typeof(PowerOcrPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -20,15 +20,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePowerRename()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.PowerRename]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.PowerRename);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PowerRenamePage));
OobeWindow.OpenMainWindowCallback(typeof(PowerRenamePage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeRegistryPreview()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.RegistryPreview]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.RegistryPreview);
DataContext = ViewModel;
}
@@ -32,9 +32,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(RegistryPreviewPage));
OobeWindow.OpenMainWindowCallback(typeof(RegistryPreviewPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -23,15 +23,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeRun()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Run]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Run);
DataContext = ViewModel;
}
private void Start_Run_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.RunSharedEventCallback != null)
if (OobeWindow.RunSharedEventCallback != null)
{
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, OobeShellPage.RunSharedEventCallback()))
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, OobeWindow.RunSharedEventCallback()))
{
eventHandle.Set();
}
@@ -42,9 +42,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(PowerLauncherPage));
OobeWindow.OpenMainWindowCallback(typeof(PowerLauncherPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -1,173 +0,0 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeShellPage"
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:ui="using:CommunityToolkit.WinUI"
HighContrastAdjustment="None"
Loaded="ShellPage_Loaded"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="AppTitleBar"
x:Uid="OobeWindow_TitleTxt"
IsBackButtonVisible="False"
IsPaneToggleButtonVisible="False"
PaneToggleRequested="TitleBar_PaneButtonClick">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
x:Name="TitleBarIcon"
Height="16"
Margin="16,0,0,0"
Source="/Assets/Settings/icon.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<NavigationView
x:Name="navigationView"
Grid.Row="1"
CompactModeThresholdWidth="1007"
DisplayModeChanged="NavigationView_DisplayModeChanged"
ExpandedModeThresholdWidth="1007"
IsBackButtonVisible="Collapsed"
IsPaneOpen="True"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
OpenPaneLength="296"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItems>
<NavigationViewItem
x:Uid="Shell_General"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerToys.png}"
Tag="Overview" />
<NavigationViewItem
x:Uid="Shell_AdvancedPaste"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/AdvancedPaste.png}"
Tag="AdvancedPaste" />
<NavigationViewItem
x:Uid="Shell_AlwaysOnTop"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/AlwaysOnTop.png}"
Tag="AlwaysOnTop" />
<NavigationViewItem
x:Uid="Shell_Awake"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Awake.png}"
Tag="Awake" />
<NavigationViewItem
x:Uid="Shell_ColorPicker"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ColorPicker.png}"
Tag="ColorPicker" />
<NavigationViewItem
x:Uid="Shell_CmdPal"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CmdPal.png}"
Tag="CmdPal" />
<NavigationViewItem
x:Uid="Shell_CmdNotFound"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CommandNotFound.png}"
Tag="CmdNotFound" />
<NavigationViewItem
x:Uid="Shell_CropAndLock"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CropAndLock.png}"
Tag="CropAndLock" />
<NavigationViewItem
x:Uid="Shell_EnvironmentVariables"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/EnvironmentVariables.png}"
Tag="EnvironmentVariables" />
<NavigationViewItem
x:Uid="Shell_FancyZones"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FancyZones.png}"
Tag="FancyZones" />
<NavigationViewItem
x:Uid="Shell_FileLocksmith"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FileLocksmith.png}"
Tag="FileLocksmith" />
<NavigationViewItem
x:Uid="Shell_PowerPreview"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FileExplorerPreview.png}"
Tag="FileExplorer" />
<NavigationViewItem
x:Uid="Shell_Hosts"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Hosts.png}"
Tag="Hosts" />
<NavigationViewItem
x:Uid="Shell_ImageResizer"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ImageResizer.png}"
Tag="ImageResizer" />
<NavigationViewItem
x:Uid="Shell_KeyboardManager"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/KeyboardManager.png}"
Tag="KBM" />
<NavigationViewItem
x:Uid="Shell_LightSwitch"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/LightSwitch.png}"
Tag="LightSwitch" />
<NavigationViewItem
x:Uid="Shell_MouseUtilities"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}"
Tag="MouseUtils" />
<NavigationViewItem
x:Uid="Shell_MouseWithoutBorders"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseWithoutBorders.png}"
Tag="MouseWithoutBorders" />
<NavigationViewItem
x:Uid="NewPlus_Product_Name"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/NewPlus.png}"
Tag="NewPlus" />
<NavigationViewItem
x:Uid="Shell_Peek"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Peek.png}"
Tag="Peek" />
<NavigationViewItem
x:Uid="Shell_PowerRename"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerRename.png}"
Tag="PowerRename" />
<NavigationViewItem
x:Uid="Shell_PowerLauncher"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerToysRun.png}"
Tag="Run" />
<NavigationViewItem
x:Uid="Shell_QuickAccent"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/QuickAccent.png}"
Tag="QuickAccent" />
<NavigationViewItem
x:Uid="Shell_RegistryPreview"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/RegistryPreview.png}"
Tag="RegistryPreview" />
<NavigationViewItem
x:Uid="Shell_MeasureTool"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ScreenRuler.png}"
Tag="MeasureTool" />
<NavigationViewItem
x:Uid="Shell_ShortcutGuide"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ShortcutGuide.png}"
Tag="ShortcutGuide" />
<NavigationViewItem
x:Uid="Shell_TextExtractor"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/TextExtractor.png}"
Tag="TextExtractor" />
<NavigationViewItem
x:Uid="Shell_Workspaces"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Workspaces.png}"
Tag="Workspaces" />
<NavigationViewItem
x:Uid="Shell_ZoomIt"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}"
Tag="ZoomIt" />
</NavigationView.MenuItems>
<NavigationView.PaneFooter>
<NavigationViewItem
x:Uid="Shell_WhatsNew"
AutomationProperties.AutomationId="WhatIsNewNavItem"
Icon="{ui:FontIcon Glyph=&#xE789;}"
Tapped="WhatIsNewItem_Tapped" />
</NavigationView.PaneFooter>
<NavigationView.Content>
<Frame x:Name="NavigationFrame" />
</NavigationView.Content>
</NavigationView>
</Grid>
</Page>

View File

@@ -1,342 +0,0 @@
// 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.Collections.ObjectModel;
using System.Globalization;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeShellPage : Page
{
public static Func<string> RunSharedEventCallback { get; set; }
public static void SetRunSharedEventCallback(Func<string> implementation)
{
RunSharedEventCallback = implementation;
}
public static Func<string> ColorPickerSharedEventCallback { get; set; }
public static void SetColorPickerSharedEventCallback(Func<string> implementation)
{
ColorPickerSharedEventCallback = implementation;
}
public static Action<Type> OpenMainWindowCallback { get; set; }
public static void SetOpenMainWindowCallback(Action<Type> implementation)
{
OpenMainWindowCallback = implementation;
}
/// <summary>
/// Gets view model.
/// </summary>
public OobeShellViewModel ViewModel { get; } = new OobeShellViewModel();
/// <summary>
/// Gets or sets a shell handler to be used to update contents of the shell dynamically from page within the frame.
/// </summary>
public static OobeShellPage OobeShellHandler { get; set; }
public ObservableCollection<OobePowerToysModule> Modules { get; }
private static SettingsUtils settingsUtils = SettingsUtils.Default;
/* NOTE: Experimentation for OOBE is currently turned off on server side. Keeping this code in a comment to allow future experiments.
private bool ExperimentationToggleSwitchEnabled { get; set; } = true;
*/
public OobeShellPage()
{
InitializeComponent();
// NOTE: Experimentation for OOBE is currently turned off on server side. Keeping this code in a comment to allow future experiments.
// ExperimentationToggleSwitchEnabled = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation;
DataContext = ViewModel;
OobeShellHandler = this;
Modules = new ObservableCollection<OobePowerToysModule>();
Modules.Insert((int)PowerToysModules.Overview, new OobePowerToysModule()
{
ModuleName = "Overview",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.AdvancedPaste, new OobePowerToysModule()
{
ModuleName = "AdvancedPaste",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.AlwaysOnTop, new OobePowerToysModule()
{
ModuleName = "AlwaysOnTop",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.Awake, new OobePowerToysModule()
{
ModuleName = "Awake",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.CmdNotFound, new OobePowerToysModule()
{
ModuleName = "CmdNotFound",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.CmdPal, new OobePowerToysModule()
{
ModuleName = "CmdPal",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.ColorPicker, new OobePowerToysModule()
{
ModuleName = "ColorPicker",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.CropAndLock, new OobePowerToysModule()
{
ModuleName = "CropAndLock",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.EnvironmentVariables, new OobePowerToysModule()
{
ModuleName = "EnvironmentVariables",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.FancyZones, new OobePowerToysModule()
{
ModuleName = "FancyZones",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.FileLocksmith, new OobePowerToysModule()
{
ModuleName = "FileLocksmith",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.FileExplorer, new OobePowerToysModule()
{
ModuleName = "FileExplorer",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.ImageResizer, new OobePowerToysModule()
{
ModuleName = "ImageResizer",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.KBM, new OobePowerToysModule()
{
ModuleName = "KBM",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.LightSwitch, new OobePowerToysModule()
{
ModuleName = "LightSwitch",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.MouseUtils, new OobePowerToysModule()
{
ModuleName = "MouseUtils",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.MouseWithoutBorders, new OobePowerToysModule()
{
ModuleName = "MouseWithoutBorders",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.Peek, new OobePowerToysModule()
{
ModuleName = "Peek",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.PowerRename, new OobePowerToysModule()
{
ModuleName = "PowerRename",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.Run, new OobePowerToysModule()
{
ModuleName = "Run",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.QuickAccent, new OobePowerToysModule()
{
ModuleName = "QuickAccent",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.ShortcutGuide, new OobePowerToysModule()
{
ModuleName = "ShortcutGuide",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.TextExtractor, new OobePowerToysModule()
{
ModuleName = "TextExtractor",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.MeasureTool, new OobePowerToysModule()
{
ModuleName = "MeasureTool",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.Hosts, new OobePowerToysModule()
{
ModuleName = "Hosts",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.Workspaces, new OobePowerToysModule()
{
ModuleName = "Workspaces",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.RegistryPreview, new OobePowerToysModule()
{
ModuleName = "RegistryPreview",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.NewPlus, new OobePowerToysModule()
{
ModuleName = "NewPlus",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.ZoomIt, new OobePowerToysModule()
{
ModuleName = "ZoomIt",
IsNew = true,
});
}
public void OnClosing()
{
NavigationViewItem selectedItem = this.navigationView.SelectedItem as NavigationViewItem;
if (selectedItem != null)
{
Modules[(int)(PowerToysModules)Enum.Parse(typeof(PowerToysModules), (string)selectedItem.Tag, true)].LogClosingModuleEvent();
}
}
public void NavigateToModule(PowerToysModules selectedModule)
{
navigationView.SelectedItem = navigationView.MenuItems[(int)selectedModule];
}
private static void OpenScoobeWindow()
{
if (App.GetScoobeWindow() == null)
{
App.SetScoobeWindow(new ScoobeWindow());
}
App.GetScoobeWindow().Activate();
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
NavigationViewItem selectedItem = args.SelectedItem as NavigationViewItem;
if (selectedItem != null)
{
switch (selectedItem.Tag)
{
case "Overview": NavigationFrame.Navigate(typeof(OobeOverview)); break;
/* NOTE: Experimentation for OOBE is currently turned off on server side. Keeping this code in a comment to allow future experiments.
if (ExperimentationToggleSwitchEnabled && GPOWrapper.GetAllowExperimentationValue() != GpoRuleConfigured.Disabled)
{
switch (AllExperiments.Experiments.LandingPageExperiment)
{
case Experiments.ExperimentState.Enabled:
NavigationFrame.Navigate(typeof(OobeOverviewAlternate)); break;
case Experiments.ExperimentState.Disabled:
NavigationFrame.Navigate(typeof(OobeOverview)); break;
case Experiments.ExperimentState.NotLoaded:
NavigationFrame.Navigate(typeof(OobeOverviewPlaceholder)); break;
}
break;
}
else
{
NavigationFrame.Navigate(typeof(OobeOverview));
break;
}
*/
case "AdvancedPaste": NavigationFrame.Navigate(typeof(OobeAdvancedPaste)); break;
case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
case "CmdNotFound": NavigationFrame.Navigate(typeof(OobeCmdNotFound)); break;
case "CmdPal": NavigationFrame.Navigate(typeof(OobeCmdPal)); break;
case "ColorPicker": NavigationFrame.Navigate(typeof(OobeColorPicker)); break;
case "CropAndLock": NavigationFrame.Navigate(typeof(OobeCropAndLock)); break;
case "EnvironmentVariables": NavigationFrame.Navigate(typeof(OobeEnvironmentVariables)); break;
case "FancyZones": NavigationFrame.Navigate(typeof(OobeFancyZones)); break;
case "FileLocksmith": NavigationFrame.Navigate(typeof(OobeFileLocksmith)); break;
case "Run": NavigationFrame.Navigate(typeof(OobeRun)); break;
case "ImageResizer": NavigationFrame.Navigate(typeof(OobeImageResizer)); break;
case "KBM": NavigationFrame.Navigate(typeof(OobeKBM)); break;
case "LightSwitch": NavigationFrame.Navigate(typeof(OobeLightSwitch)); break;
case "PowerRename": NavigationFrame.Navigate(typeof(OobePowerRename)); break;
case "QuickAccent": NavigationFrame.Navigate(typeof(OobePowerAccent)); break;
case "FileExplorer": NavigationFrame.Navigate(typeof(OobeFileExplorer)); break;
case "ShortcutGuide": NavigationFrame.Navigate(typeof(OobeShortcutGuide)); break;
case "TextExtractor": NavigationFrame.Navigate(typeof(OobePowerOCR)); break;
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
case "MouseWithoutBorders": NavigationFrame.Navigate(typeof(OobeMouseWithoutBorders)); break;
case "MeasureTool": NavigationFrame.Navigate(typeof(OobeMeasureTool)); break;
case "Hosts": NavigationFrame.Navigate(typeof(OobeHosts)); break;
case "RegistryPreview": NavigationFrame.Navigate(typeof(OobeRegistryPreview)); break;
case "Peek": NavigationFrame.Navigate(typeof(OobePeek)); break;
case "NewPlus": NavigationFrame.Navigate(typeof(OobeNewPlus)); break;
case "Workspaces": NavigationFrame.Navigate(typeof(OobeWorkspaces)); break;
case "ZoomIt": NavigationFrame.Navigate(typeof(OobeZoomIt)); break;
}
}
}
private void ShellPage_Loaded(object sender, RoutedEventArgs e)
{
// Select the first module by default
if (navigationView.MenuItems.Count > 0)
{
navigationView.SelectedItem = navigationView.MenuItems[0];
}
}
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
{
TitleBarIcon.Margin = new Thickness(0, 0, 8, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = true;
}
else
{
TitleBarIcon.Margin = new Thickness(16, 0, 0, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = false;
}
}
private void TitleBar_PaneButtonClick(TitleBar sender, object args)
{
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
}
private void WhatIsNewItem_Tapped(object sender, TappedRoutedEventArgs e)
{
OpenScoobeWindow();
}
}
}

View File

@@ -26,7 +26,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeShortcutGuide()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.ShortcutGuide]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.ShortcutGuide);
DataContext = ViewModel;
}
@@ -45,9 +45,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(ShortcutGuidePage));
OobeWindow.OpenMainWindowCallback(typeof(ShortcutGuidePage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -21,15 +21,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeWorkspaces()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Workspaces]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.Workspaces);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(WorkspacesPage));
OobeWindow.OpenMainWindowCallback(typeof(WorkspacesPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -18,15 +18,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeZoomIt()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.ZoomIt]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.ZoomIt);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
if (OobeWindow.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(ZoomItPage));
OobeWindow.OpenMainWindowCallback(typeof(ZoomItPage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -5,13 +5,179 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:winuiex="using:WinUIEx"
Width="1100"
Height="700"
MinWidth="480"
MinHeight="480"
Activated="Window_Activated"
Closed="Window_Closed"
mc:Ignorable="d">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<local:OobeShellPage x:Name="shellPage" />
<Grid x:Name="RootGrid">
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="AppTitleBar"
x:Uid="OobeWindow_TitleTxt"
IsBackButtonVisible="False"
IsPaneToggleButtonVisible="False"
PaneToggleRequested="TitleBar_PaneButtonClick">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
x:Name="TitleBarIcon"
Height="16"
Margin="16,0,0,0"
Source="/Assets/Settings/icon.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<NavigationView
x:Name="navigationView"
Grid.Row="1"
CompactModeThresholdWidth="1007"
DisplayModeChanged="NavigationView_DisplayModeChanged"
ExpandedModeThresholdWidth="1007"
IsBackButtonVisible="Collapsed"
IsPaneOpen="True"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
Loaded="NavigationView_Loaded"
OpenPaneLength="296"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItems>
<NavigationViewItem
x:Uid="Shell_General"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerToys.png}"
Tag="Overview" />
<NavigationViewItem
x:Uid="Shell_AdvancedPaste"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/AdvancedPaste.png}"
Tag="AdvancedPaste" />
<NavigationViewItem
x:Uid="Shell_AlwaysOnTop"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/AlwaysOnTop.png}"
Tag="AlwaysOnTop" />
<NavigationViewItem
x:Uid="Shell_Awake"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Awake.png}"
Tag="Awake" />
<NavigationViewItem
x:Uid="Shell_ColorPicker"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ColorPicker.png}"
Tag="ColorPicker" />
<NavigationViewItem
x:Uid="Shell_CmdPal"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CmdPal.png}"
Tag="CmdPal" />
<NavigationViewItem
x:Uid="Shell_CmdNotFound"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CommandNotFound.png}"
Tag="CmdNotFound" />
<NavigationViewItem
x:Uid="Shell_CropAndLock"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CropAndLock.png}"
Tag="CropAndLock" />
<NavigationViewItem
x:Uid="Shell_EnvironmentVariables"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/EnvironmentVariables.png}"
Tag="EnvironmentVariables" />
<NavigationViewItem
x:Uid="Shell_FancyZones"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FancyZones.png}"
Tag="FancyZones" />
<NavigationViewItem
x:Uid="Shell_FileLocksmith"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FileLocksmith.png}"
Tag="FileLocksmith" />
<NavigationViewItem
x:Uid="Shell_PowerPreview"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/FileExplorerPreview.png}"
Tag="FileExplorer" />
<NavigationViewItem
x:Uid="Shell_Hosts"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Hosts.png}"
Tag="Hosts" />
<NavigationViewItem
x:Uid="Shell_ImageResizer"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ImageResizer.png}"
Tag="ImageResizer" />
<NavigationViewItem
x:Uid="Shell_KeyboardManager"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/KeyboardManager.png}"
Tag="KBM" />
<NavigationViewItem
x:Uid="Shell_LightSwitch"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/LightSwitch.png}"
Tag="LightSwitch" />
<NavigationViewItem
x:Uid="Shell_MouseUtilities"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}"
Tag="MouseUtils" />
<NavigationViewItem
x:Uid="Shell_MouseWithoutBorders"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseWithoutBorders.png}"
Tag="MouseWithoutBorders" />
<NavigationViewItem
x:Uid="NewPlus_Product_Name"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/NewPlus.png}"
Tag="NewPlus" />
<NavigationViewItem
x:Uid="Shell_Peek"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Peek.png}"
Tag="Peek" />
<NavigationViewItem
x:Uid="Shell_PowerRename"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerRename.png}"
Tag="PowerRename" />
<NavigationViewItem
x:Uid="Shell_PowerLauncher"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerToysRun.png}"
Tag="Run" />
<NavigationViewItem
x:Uid="Shell_QuickAccent"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/QuickAccent.png}"
Tag="QuickAccent" />
<NavigationViewItem
x:Uid="Shell_RegistryPreview"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/RegistryPreview.png}"
Tag="RegistryPreview" />
<NavigationViewItem
x:Uid="Shell_MeasureTool"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ScreenRuler.png}"
Tag="MeasureTool" />
<NavigationViewItem
x:Uid="Shell_ShortcutGuide"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ShortcutGuide.png}"
Tag="ShortcutGuide" />
<NavigationViewItem
x:Uid="Shell_TextExtractor"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/TextExtractor.png}"
Tag="TextExtractor" />
<NavigationViewItem
x:Uid="Shell_Workspaces"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Workspaces.png}"
Tag="Workspaces" />
<NavigationViewItem
x:Uid="Shell_ZoomIt"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}"
Tag="ZoomIt" />
</NavigationView.MenuItems>
<NavigationView.PaneFooter>
<NavigationViewItem
x:Uid="Shell_WhatsNew"
AutomationProperties.AutomationId="WhatIsNewNavItem"
Icon="{ui:FontIcon Glyph=&#xE789;}"
Tapped="WhatIsNewItem_Tapped" />
</NavigationView.PaneFooter>
<NavigationView.Content>
<Frame x:Name="NavigationFrame" />
</NavigationView.Content>
</NavigationView>
</Grid>
</winuiex:WindowEx>

View File

@@ -3,123 +3,172 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using ManagedCommon;
using Microsoft.Extensions.AI;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.OOBE.Views;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using PowerToys.Interop;
using Windows.Graphics;
using WinRT.Interop;
using WinUIEx;
using WinUIEx.Messaging;
namespace Microsoft.PowerToys.Settings.UI
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OobeWindow : WindowEx, IDisposable
public sealed partial class OobeWindow : WindowEx
{
private PowerToysModules initialModule;
public OobeShellViewModel ViewModel => App.OobeShellViewModel;
private const int ExpectedWidth = 1100;
private const int ExpectedHeight = 700;
private const int DefaultDPI = 96;
private int _currentDPI;
private WindowId _windowId;
private IntPtr _hWnd;
private AppWindow _appWindow;
private bool disposedValue;
public static Func<string> RunSharedEventCallback { get; set; }
public OobeWindow(PowerToysModules initialModule)
public static void SetRunSharedEventCallback(Func<string> implementation)
{
RunSharedEventCallback = implementation;
}
public static Func<string> ColorPickerSharedEventCallback { get; set; }
public static void SetColorPickerSharedEventCallback(Func<string> implementation)
{
ColorPickerSharedEventCallback = implementation;
}
public static Action<Type> OpenMainWindowCallback { get; set; }
public static void SetOpenMainWindowCallback(Action<Type> implementation)
{
OpenMainWindowCallback = implementation;
}
public OobeWindow()
{
App.ThemeService.ThemeChanged += OnThemeChanged;
App.ThemeService.ApplyTheme();
this.InitializeComponent();
_hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
_appWindow = AppWindow.GetFromWindowId(_windowId);
this.Activated += Window_Activated_SetIcon;
SetTitleBar();
this.ExtendsContentIntoTitleBar = true;
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
_currentDPI = dpi;
float scalingFactor = (float)dpi / DefaultDPI;
int width = (int)(ExpectedWidth * scalingFactor);
int height = (int)(ExpectedHeight * scalingFactor);
RootGrid.DataContext = ViewModel;
SizeInt32 size;
size.Width = width;
size.Height = height;
_appWindow.Resize(size);
this.initialModule = initialModule;
this.SizeChanged += OobeWindow_SizeChanged;
var loader = ResourceLoaderInstance.ResourceLoader;
Title = loader.GetString("OobeWindow_Title");
if (shellPage != null)
{
shellPage.NavigateToModule(this.initialModule);
}
OobeShellPage.SetRunSharedEventCallback(() =>
SetRunSharedEventCallback(() =>
{
return Constants.PowerLauncherSharedEvent();
});
OobeShellPage.SetColorPickerSharedEventCallback(() =>
SetColorPickerSharedEventCallback(() =>
{
return Constants.ShowColorPickerSharedEvent();
});
OobeShellPage.SetOpenMainWindowCallback((Type type) =>
SetOpenMainWindowCallback((Type type) =>
{
App.OpenSettingsWindow(type);
});
}
public void SetAppWindow(PowerToysModules module)
private void SetTitleBar()
{
if (shellPage != null)
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(this));
this.ExtendsContentIntoTitleBar = true;
this.SetTitleBar(AppTitleBar);
Title = ResourceLoaderInstance.ResourceLoader.GetString("OobeWindow_Title");
}
public void OnClosing()
{
if (navigationView.SelectedItem is NavigationViewItem selectedItem)
{
shellPage.NavigateToModule(module);
App.OobeShellViewModel.GetModuleFromTag((string)selectedItem.Tag).LogClosingModuleEvent();
}
}
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
public void NavigateToModule(PowerToysModules selectedModule)
{
navigationView.SelectedItem = navigationView.MenuItems[(int)selectedModule];
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (navigationView.SelectedItem is NavigationViewItem selectedItem)
{
switch (selectedItem.Tag)
{
case "Overview": NavigationFrame.Navigate(typeof(OobeOverview)); break;
case "AdvancedPaste": NavigationFrame.Navigate(typeof(OobeAdvancedPaste)); break;
case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
case "CmdNotFound": NavigationFrame.Navigate(typeof(OobeCmdNotFound)); break;
case "CmdPal": NavigationFrame.Navigate(typeof(OobeCmdPal)); break;
case "ColorPicker": NavigationFrame.Navigate(typeof(OobeColorPicker)); break;
case "CropAndLock": NavigationFrame.Navigate(typeof(OobeCropAndLock)); break;
case "EnvironmentVariables": NavigationFrame.Navigate(typeof(OobeEnvironmentVariables)); break;
case "FancyZones": NavigationFrame.Navigate(typeof(OobeFancyZones)); break;
case "FileLocksmith": NavigationFrame.Navigate(typeof(OobeFileLocksmith)); break;
case "Run": NavigationFrame.Navigate(typeof(OobeRun)); break;
case "ImageResizer": NavigationFrame.Navigate(typeof(OobeImageResizer)); break;
case "KBM": NavigationFrame.Navigate(typeof(OobeKBM)); break;
case "LightSwitch": NavigationFrame.Navigate(typeof(OobeLightSwitch)); break;
case "PowerRename": NavigationFrame.Navigate(typeof(OobePowerRename)); break;
case "QuickAccent": NavigationFrame.Navigate(typeof(OobePowerAccent)); break;
case "FileExplorer": NavigationFrame.Navigate(typeof(OobeFileExplorer)); break;
case "ShortcutGuide": NavigationFrame.Navigate(typeof(OobeShortcutGuide)); break;
case "TextExtractor": NavigationFrame.Navigate(typeof(OobePowerOCR)); break;
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
case "MouseWithoutBorders": NavigationFrame.Navigate(typeof(OobeMouseWithoutBorders)); break;
case "MeasureTool": NavigationFrame.Navigate(typeof(OobeMeasureTool)); break;
case "Hosts": NavigationFrame.Navigate(typeof(OobeHosts)); break;
case "RegistryPreview": NavigationFrame.Navigate(typeof(OobeRegistryPreview)); break;
case "Peek": NavigationFrame.Navigate(typeof(OobePeek)); break;
case "NewPlus": NavigationFrame.Navigate(typeof(OobeNewPlus)); break;
case "Workspaces": NavigationFrame.Navigate(typeof(OobeWorkspaces)); break;
case "ZoomIt": NavigationFrame.Navigate(typeof(OobeZoomIt)); break;
}
}
}
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
{
TitleBarIcon.Margin = new Thickness(0, 0, 8, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = true;
}
else
{
TitleBarIcon.Margin = new Thickness(16, 0, 0, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = false;
}
}
private void TitleBar_PaneButtonClick(Microsoft.UI.Xaml.Controls.TitleBar sender, object args)
{
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
}
private void WhatIsNewItem_Tapped(object sender, TappedRoutedEventArgs e)
{
((App)App.Current)!.OpenScoobe();
}
private void Window_Activated(object sender, WindowActivatedEventArgs args)
{
// Set window icon
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
}
private void OobeWindow_SizeChanged(object sender, WindowSizeChangedEventArgs args)
{
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
if (_currentDPI != dpi)
{
// Reacting to a DPI change. Should not cause a resize -> sizeChanged loop.
_currentDPI = dpi;
float scalingFactor = (float)dpi / DefaultDPI;
int width = (int)(ExpectedWidth * scalingFactor);
int height = (int)(ExpectedHeight * scalingFactor);
SizeInt32 size;
size.Width = width;
size.Height = height;
_appWindow.Resize(size);
}
this.SetIcon("Assets\\Settings\\icon.ico");
}
private void Window_Closed(object sender, WindowEventArgs args)
{
App.ClearOobeWindow();
var mainWindow = App.GetSettingsWindow();
if (mainWindow != null)
{
@@ -134,19 +183,13 @@ namespace Microsoft.PowerToys.Settings.UI
WindowHelper.SetTheme(this, theme);
}
private void Dispose(bool disposing)
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
{
if (!disposedValue)
// Select the first module by default
if (navigationView.MenuItems.Count > 0)
{
disposedValue = true;
navigationView.SelectedItem = navigationView.MenuItems[0];
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -6,12 +6,94 @@
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winuiex="using:WinUIEx"
Width="1100"
Height="700"
MinWidth="480"
MinHeight="480"
Activated="Window_Activated"
Closed="Window_Closed"
mc:Ignorable="d">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<local:ScoobeShellPage x:Name="shellPage" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="AppTitleBar"
x:Uid="ScoobeWindow_TitleTxt"
IsBackButtonVisible="False"
IsPaneToggleButtonVisible="False"
PaneToggleRequested="TitleBar_PaneButtonClick">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
x:Name="TitleBarIcon"
Height="16"
Margin="16,0,0,0"
Source="/Assets/Settings/icon.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<NavigationView
x:Name="navigationView"
Grid.Row="1"
CompactModeThresholdWidth="1007"
DisplayModeChanged="NavigationView_DisplayModeChanged"
ExpandedModeThresholdWidth="1007"
IsBackButtonVisible="Collapsed"
IsPaneOpen="True"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
Loaded="NavigationView_Loaded"
OpenPaneLength="186"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItemTemplate>
<DataTemplate x:DataType="local:ScoobeReleaseGroupViewModel">
<StackPanel
Margin="0,8,0,8"
Orientation="Vertical"
Spacing="4">
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Bind DateText}" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind VersionText}" />
</StackPanel>
</DataTemplate>
</NavigationView.MenuItemTemplate>
<NavigationView.Content>
<Grid>
<ProgressRing
x:Name="LoadingProgressRing"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True"
Visibility="Collapsed" />
<InfoBar
x:Name="ErrorInfoBar"
x:Uid="Oobe_WhatsNew_LoadingError"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsClosable="False"
IsOpen="False"
Severity="Error">
<InfoBar.ActionButton>
<Button
x:Uid="RetryBtn"
HorizontalAlignment="Right"
Click="RetryButton_Click">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="16" Glyph="&#xE72C;" />
<TextBlock x:Uid="RetryLabel" />
</StackPanel>
</Button>
</InfoBar.ActionButton>
</InfoBar>
<Frame x:Name="NavigationFrame" />
</Grid>
</NavigationView.Content>
</NavigationView>
</Grid>
</winuiex:WindowEx>

View File

@@ -3,29 +3,41 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.OOBE.Views;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using PowerToys.Interop;
using Microsoft.UI.Xaml.Controls;
using Windows.Graphics;
using WinRT.Interop;
using WinUIEx;
using WinUIEx.Messaging;
namespace Microsoft.PowerToys.Settings.UI
{
public sealed partial class ScoobeWindow : WindowEx, IDisposable
public sealed partial class ScoobeWindow : WindowEx
{
private const int ExpectedWidth = 1100;
private const int ExpectedHeight = 700;
private const int DefaultDPI = 96;
private int _currentDPI;
private WindowId _windowId;
private IntPtr _hWnd;
private AppWindow _appWindow;
private bool disposedValue;
public static Action<Type> OpenMainWindowCallback { get; set; }
public static void SetOpenMainWindowCallback(Action<Type> implementation)
{
OpenMainWindowCallback = implementation;
}
/// <summary>
/// Gets the list of release groups loaded from GitHub (grouped by major.minor version).
/// </summary>
public IList<IList<PowerToysReleaseInfo>> ReleaseGroups { get; private set; }
private bool _isLoading;
public ScoobeWindow()
{
@@ -34,63 +46,31 @@ namespace Microsoft.PowerToys.Settings.UI
this.InitializeComponent();
_hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
_appWindow = AppWindow.GetFromWindowId(_windowId);
this.Activated += Window_Activated_SetIcon;
this.ExtendsContentIntoTitleBar = true;
SetTitleBar();
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
_currentDPI = dpi;
float scalingFactor = (float)dpi / DefaultDPI;
int width = (int)(ExpectedWidth * scalingFactor);
int height = (int)(ExpectedHeight * scalingFactor);
SizeInt32 size;
size.Width = width;
size.Height = height;
_appWindow.Resize(size);
this.SizeChanged += ScoobeWindow_SizeChanged;
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
Title = loader.GetString("ScoobeWindow_Title");
ScoobeShellPage.SetOpenMainWindowCallback((Type type) =>
SetOpenMainWindowCallback((Type type) =>
{
App.OpenSettingsWindow(type);
});
}
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
private void SetTitleBar()
{
// Set window icon
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(this));
this.ExtendsContentIntoTitleBar = true;
this.SetTitleBar(AppTitleBar);
Title = ResourceLoaderInstance.ResourceLoader.GetString("ScoobeWindow_Title");
}
private void ScoobeWindow_SizeChanged(object sender, WindowSizeChangedEventArgs args)
private void Window_Activated(object sender, WindowActivatedEventArgs args)
{
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
if (_currentDPI != dpi)
{
// Reacting to a DPI change. Should not cause a resize -> sizeChanged loop.
_currentDPI = dpi;
float scalingFactor = (float)dpi / DefaultDPI;
int width = (int)(ExpectedWidth * scalingFactor);
int height = (int)(ExpectedHeight * scalingFactor);
SizeInt32 size;
size.Width = width;
size.Height = height;
_appWindow.Resize(size);
}
// Set window icon
this.SetIcon("Assets\\Settings\\icon.ico");
}
private void Window_Closed(object sender, WindowEventArgs args)
{
App.ClearScoobeWindow();
var mainWindow = App.GetSettingsWindow();
if (mainWindow != null)
if (App.GetSettingsWindow() is MainWindow mainWindow)
{
mainWindow.CloseHiddenWindow();
}
@@ -103,19 +83,140 @@ namespace Microsoft.PowerToys.Settings.UI
WindowHelper.SetTheme(this, theme);
}
private void Dispose(bool disposing)
private async void NavigationView_Loaded(object sender, RoutedEventArgs e)
{
if (!disposedValue)
await LoadReleasesAsync();
}
private async Task LoadReleasesAsync()
{
if (_isLoading)
{
disposedValue = true;
return;
}
_isLoading = true;
LoadingProgressRing.Visibility = Visibility.Visible;
ErrorInfoBar.IsOpen = false;
navigationView.MenuItems.Clear();
try
{
var releases = await FetchReleasesFromGitHubAsync();
ReleaseGroups = GroupReleasesByMajorMinor(releases);
PopulateNavigationItems();
}
catch (Exception ex)
{
Logger.LogError("Failed to load releases", ex);
ErrorInfoBar.IsOpen = true;
}
finally
{
LoadingProgressRing.Visibility = Visibility.Collapsed;
_isLoading = false;
}
}
public void Dispose()
private static async Task<IList<PowerToysReleaseInfo>> FetchReleasesFromGitHubAsync()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
using var proxyClientHandler = new HttpClientHandler
{
DefaultProxyCredentials = CredentialCache.DefaultCredentials,
Proxy = WebRequest.GetSystemWebProxy(),
PreAuthenticate = true,
};
using var httpClient = new HttpClient(proxyClientHandler);
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "PowerToys");
string json = await httpClient.GetStringAsync("https://api.github.com/repos/microsoft/PowerToys/releases?per_page=20");
var allReleases = JsonSerializer.Deserialize<IList<PowerToysReleaseInfo>>(json, SourceGenerationContextContext.Default.IListPowerToysReleaseInfo);
return allReleases
.OrderByDescending(r => r.PublishedDate)
.ToList();
}
private static IList<IList<PowerToysReleaseInfo>> GroupReleasesByMajorMinor(IList<PowerToysReleaseInfo> releases)
{
return releases
.GroupBy(r => GetMajorMinorVersion(r))
.Select(g => g.OrderByDescending(r => r.PublishedDate).ToList() as IList<PowerToysReleaseInfo>)
.ToList();
}
private static string GetMajorMinorVersion(PowerToysReleaseInfo release)
{
string version = GetVersionFromRelease(release);
var parts = version.Split('.');
if (parts.Length >= 2)
{
return $"{parts[0]}.{parts[1]}";
}
return version;
}
private static string GetVersionFromRelease(PowerToysReleaseInfo release)
{
// TagName is typically like "v0.96.0", Name might be "Release v0.96.0"
string version = release.TagName ?? release.Name ?? "Unknown";
if (version.StartsWith("v", StringComparison.OrdinalIgnoreCase))
{
version = version.Substring(1);
}
return version;
}
private void PopulateNavigationItems()
{
if (ReleaseGroups == null || ReleaseGroups.Count == 0)
{
return;
}
foreach (var releaseGroup in ReleaseGroups)
{
var viewModel = new ScoobeReleaseGroupViewModel(releaseGroup);
navigationView.MenuItems.Add(viewModel);
}
// Select the first item to trigger navigation
navigationView.SelectedItem = navigationView.MenuItems[0];
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is ScoobeReleaseGroupViewModel viewModel)
{
NavigationFrame.Navigate(typeof(ScoobeReleaseNotesPage), viewModel.Releases);
}
}
private async void RetryButton_Click(object sender, RoutedEventArgs e)
{
await LoadReleasesAsync();
}
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
{
TitleBarIcon.Margin = new Thickness(0, 0, 8, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = true;
}
else
{
TitleBarIcon.Margin = new Thickness(16, 0, 0, 0); // Workaround, see XAML comment
AppTitleBar.IsPaneToggleButtonVisible = false;
}
}
private void TitleBar_PaneButtonClick(Microsoft.UI.Xaml.Controls.TitleBar sender, object args)
{
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
}
}
}

View File

@@ -21,15 +21,12 @@
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<ImageSource x:Key="DialogHeaderImage">ms-appx:///Assets/Settings/Modules/APDialog.dark.png</ImageSource>
<ImageSource x:Key="OpenAIIconImage">ms-appx:///Assets/Settings/Icons/Models/OpenAI.dark.svg</ImageSource>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<ImageSource x:Key="DialogHeaderImage">ms-appx:///Assets/Settings/Modules/APDialog.light.png</ImageSource>
<ImageSource x:Key="OpenAIIconImage">ms-appx:///Assets/Settings/Icons/Models/OpenAI.light.svg</ImageSource>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<ImageSource x:Key="DialogHeaderImage">ms-appx:///Assets/Settings/Modules/APDialog.light.png</ImageSource>
<ImageSource x:Key="OpenAIIconImage">ms-appx:///Assets/Settings/Icons/Models/OpenAI.light.svg</ImageSource>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

View File

@@ -28,7 +28,10 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<tkcontrols:OpacityMaskView Margin="-16,0,-16,0" HorizontalAlignment="Stretch">
<tkcontrols:OpacityMaskView
Margin="-16,0,-16,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<tkcontrols:OpacityMaskView.OpacityMask>
<Rectangle>
<Rectangle.Fill>
@@ -40,17 +43,24 @@
</Rectangle.Fill>
</Rectangle>
</tkcontrols:OpacityMaskView.OpacityMask>
<Grid Height="560">
<Grid MaxHeight="560">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image
Grid.RowSpan="3"
HorizontalAlignment="Stretch"
Source="/Assets/Settings/Modules/CmdPal_Background.png"
Stretch="UniformToFill" />
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<!-- Top-left: light cyan/blue -->
<GradientStop Offset="0.0" Color="#5FAFC9" />
<!-- Mid transition -->
<GradientStop Offset="0.45" Color="#3E7FB0" />
<!-- Bottom-right: deep blue -->
<GradientStop Offset="1.0" Color="#2C3E8F" />
</LinearGradientBrush>
</Grid.Background>
<TextBlock
Margin="0,24,0,12"
HorizontalAlignment="Center"
@@ -81,7 +91,7 @@
Margin="0,16,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Source="/Assets/Settings/Modules/CmdPal_Hero.png"
Source="/Assets/Settings/Modules/CmdPal.png"
Stretch="Uniform" />
</Grid>
</tkcontrols:OpacityMaskView>

View File

@@ -48,12 +48,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void WhatsNewButton_Click(object sender, RoutedEventArgs e)
{
if (App.GetScoobeWindow() == null)
{
App.SetScoobeWindow(new ScoobeWindow());
}
App.GetScoobeWindow().Activate();
((App)App.Current)!.OpenScoobe();
}
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)

View File

@@ -94,7 +94,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public static OobeOpeningCallback OpenOobeWindowCallback { get; set; }
/// <summary>
/// Gets or sets callback function for opening oobe window
/// Gets or sets callback function for opening scoobe window
/// </summary>
public static WhatIsNewOpeningCallback OpenWhatIsNewWindowCallback { get; set; }
@@ -223,24 +223,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
UpdateGeneralSettingsCallback = implementation;
}
/// <summary>
/// Set oobe opening callback function
/// </summary>
/// <param name="implementation">delegate function implementation.</param>
public static void SetOpenOobeCallback(OobeOpeningCallback implementation)
{
OpenOobeWindowCallback = implementation;
}
/// <summary>
/// Set whats new opening callback function
/// </summary>
/// <param name="implementation">delegate function implementation.</param>
public static void SetOpenWhatIsNewCallback(WhatIsNewOpeningCallback implementation)
{
OpenWhatIsNewWindowCallback = implementation;
}
public static void SetElevationStatus(bool isElevated)
{
IsElevated = isElevated;
@@ -325,7 +307,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void OOBEItem_Tapped(object sender, TappedRoutedEventArgs e)
{
OpenOobeWindowCallback();
((App)App.Current)!.OpenOobe();
}
private void WhatIsNewItem_Tapped(object sender, TappedRoutedEventArgs e)
{
((App)App.Current)!.OpenScoobe();
}
private async void FeedbackItem_Tapped(object sender, TappedRoutedEventArgs e)
@@ -333,15 +320,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
await Launcher.LaunchUriAsync(new Uri("https://aka.ms/powerToysGiveFeedback"));
}
private void WhatIsNewItem_Tapped(object sender, TappedRoutedEventArgs e)
{
OpenWhatIsNewWindowCallback();
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
NavigationViewItem selectedItem = args.SelectedItem as NavigationViewItem;
if (selectedItem != null)
if (args.SelectedItem is NavigationViewItem selectedItem)
{
Type pageType = selectedItem.GetValue(NavHelper.NavigateToProperty) as Type;
@@ -409,7 +390,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
}
private async void Close_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
private async void Close_Tapped(object sender, TappedRoutedEventArgs e)
{
await CloseDialog.ShowAsync();
}

View File

@@ -8,8 +8,8 @@ using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.Json;
using AllExperiments;
using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;