[Settings] Optimizing and cleaning up code (#44721)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

- Removing dead experiments code
- Refactoring and simplifying OOBE/SCOOBE windowing code
- Removing dead assets
- Optimizing CmdPal assets to lower res shaving off a few MBs
- Scrolling works better on the What's new page
- Should be merged after: #44638

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] Closes: #44958
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
This commit is contained in:
Niels Laute
2026-02-12 03:35:58 +01:00
committed by GitHub
parent 3385d1d741
commit 528fb524d0
78 changed files with 800 additions and 2479 deletions

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;
@@ -25,11 +27,14 @@ using WinUIEx;
namespace Microsoft.PowerToys.Settings.UI
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </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 +236,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 +246,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 +260,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 +286,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 +293,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 +325,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 +336,52 @@ namespace Microsoft.PowerToys.Settings.UI
return settingsWindow;
}
public static OobeWindow GetOobeWindow()
public static bool IsOobeOrScoobeOpen()
{
return oobeWindow;
var app = (App)Current;
return app.oobeWindow != null || app.scoobeWindow != null;
}
public static void SetOobeWindow(OobeWindow window)
public void OpenScoobe()
{
oobeWindow = window;
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 ClearOobeWindow()
public void OpenOobe()
{
oobeWindow = null;
}
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
public static ScoobeWindow GetScoobeWindow()
{
return scoobeWindow;
}
if (oobeWindow == null)
{
oobeWindow = new OobeWindow();
public static void SetScoobeWindow(ScoobeWindow window)
{
scoobeWindow = window;
}
oobeWindow.Closed += (_, _) =>
{
oobeWindow = null;
};
public static void ClearScoobeWindow()
{
scoobeWindow = null;
oobeWindow.Activate();
}
else
{
WindowHelpers.BringToForeground(oobeWindow.GetWindowHandle());
}
}
public static Type GetPage(string settingWindow)
@@ -399,6 +405,7 @@ namespace Microsoft.PowerToys.Settings.UI
case "MouseWithoutBorders": return typeof(MouseWithoutBordersPage);
case "Peek": return typeof(PeekPage);
case "PowerAccent": return typeof(PowerAccentPage);
case "PowerDisplay": return typeof(PowerDisplayPage);
case "PowerLauncher": return typeof(PowerLauncherPage);
case "PowerPreview": return typeof(PowerPreviewPage);
case "PowerRename": return typeof(PowerRenamePage);
@@ -415,7 +422,6 @@ namespace Microsoft.PowerToys.Settings.UI
case "Workspaces": return typeof(WorkspacesPage);
case "CmdPal": return typeof(CmdPalPage);
case "ZoomIt": return typeof(ZoomItPage);
case "PowerDisplay": return typeof(PowerDisplayPage);
default:
// Fallback to Dashboard
Debug.Assert(false, "Unexpected SettingsWindow argument value");

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,10 +159,10 @@ 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)
if (!App.IsOobeOrScoobeOpen())
{
App.ClearSettingsWindow();
}
@@ -198,10 +178,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);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("Assets\\Settings\\icon.ico");
this.SetIcon("Assets\\Settings\\icon.ico");
}
private void Window_Activated(object sender, WindowActivatedEventArgs args)
@@ -209,7 +186,7 @@ namespace Microsoft.PowerToys.Settings.UI
if (args.WindowActivationState != WindowActivationState.Deactivated)
{
this.Activated -= Window_Activated;
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var hWnd = WindowNative.GetWindowHandle(this);
var placement = WindowHelper.DeserializePlacementOrDefault(hWnd);
NativeMethods.SetWindowPlacement(hWnd, ref placement);
}

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 = App.OobeShellViewModel.GetModule(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

@@ -21,15 +21,15 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobeColorPicker()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.ColorPicker]);
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();
}
@@ -40,9 +40,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

@@ -22,7 +22,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;
}
@@ -55,9 +55,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

@@ -22,7 +22,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;
}
@@ -55,9 +55,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

@@ -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 OobeImageResizer : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,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

@@ -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 OobeKBM : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,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

@@ -11,9 +11,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 OobeMeasureTool : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -21,15 +18,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

@@ -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 OobeMouseWithoutBorders : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,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

@@ -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 OobeNewPlus : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,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,191 +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}"
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

@@ -11,9 +11,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 OobePeek : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -21,15 +18,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

@@ -14,9 +14,6 @@ using PowerToys.Interop;
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 OobePowerDisplay : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -24,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
public OobePowerDisplay()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.PowerDisplay]);
ViewModel = App.OobeShellViewModel.GetModule(PowerToysModules.PowerDisplay);
DataContext = ViewModel;
}
@@ -38,9 +35,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(PowerDisplayPage));
OobeWindow.OpenMainWindowCallback(typeof(PowerDisplayPage));
}
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

@@ -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 OobePowerRename : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -20,15 +17,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

@@ -12,9 +12,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 OobeRegistryPreview : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -22,7 +19,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;
}
@@ -33,9 +30,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

@@ -14,9 +14,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 OobeRun : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -24,15 +21,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();
}
@@ -43,9 +40,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,177 +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_PowerDisplay"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerDisplay.png}"
Tag="PowerDisplay" />
<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,349 +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.PowerDisplay, new OobePowerToysModule()
{
ModuleName = "PowerDisplay",
IsNew = true,
});
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 "PowerDisplay": NavigationFrame.Navigate(typeof(OobePowerDisplay)); 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

@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
@@ -17,9 +16,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 OobeShortcutGuide : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -27,7 +23,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;
}
@@ -46,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(ShortcutGuidePage));
OobeWindow.OpenMainWindowCallback(typeof(ShortcutGuidePage));
}
ViewModel.LogOpeningSettingsEvent();

View File

@@ -11,9 +11,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 OobeWorkspaces : Page
{
public OobePowerToysModule ViewModel { get; set; }
@@ -21,15 +18,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

@@ -47,7 +47,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
}
}
private static string GetVersionFromRelease(PowerToysReleaseInfo release)
internal 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";

View File

@@ -29,33 +29,35 @@
</Page.Resources>
<!-- Main layout container -->
<Grid MaxWidth="1000">
<ScrollViewer Padding="0,0,0,0" VerticalScrollBarVisibility="Auto">
<Grid Margin="0,0,0,24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image
x:Name="HeroImageHolder"
Height="186"
HorizontalAlignment="Left"
Stretch="UniformToFill" />
<Grid Grid.Row="1" Margin="24,16,24,24">
<ProgressRing
x:Name="LoadingProgressRing"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True"
Visibility="Visible" />
<tkcontrols:MarkdownTextBlock
x:Name="ReleaseNotesMarkdown"
Config="{StaticResource ReleaseNotesMarkdownConfig}"
UseAutoLinks="True"
UseEmphasisExtras="True"
UseListExtras="True"
UsePipeTables="True"
UseTaskLists="True" />
<Grid>
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid MaxWidth="960">
<Grid Margin="0,0,0,24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image
x:Name="HeroImageHolder"
Height="186"
HorizontalAlignment="Left"
Stretch="UniformToFill" />
<Grid Grid.Row="1" Margin="24,16,24,24">
<ProgressRing
x:Name="LoadingProgressRing"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True"
Visibility="Visible" />
<tkcontrols:MarkdownTextBlock
x:Name="ReleaseNotesMarkdown"
Config="{StaticResource ReleaseNotesMarkdownConfig}"
UseAutoLinks="True"
UseEmphasisExtras="True"
UseListExtras="True"
UsePipeTables="True"
UseTaskLists="True" />
</Grid>
</Grid>
</Grid>
</ScrollViewer>

View File

@@ -1,96 +0,0 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.ScoobeShellPage"
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:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HighContrastAdjustment="None"
Loaded="ShellPage_Loaded"
mc:Ignorable="d">
<Page.Resources>
<!-- Template for NavigationViewItem content with version and date -->
<DataTemplate x:Key="ReleaseNavItemTemplate" 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>
</Page.Resources>
<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"
MenuItemTemplate="{StaticResource ReleaseNavItemTemplate}"
OpenPaneLength="186"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItems>
<!-- Items are added dynamically -->
</NavigationView.MenuItems>
<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>
</Page>

View File

@@ -1,194 +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.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.SerializationContext;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class ScoobeShellPage : Page
{
public static Action<Type> OpenMainWindowCallback { get; set; }
public static void SetOpenMainWindowCallback(Action<Type> implementation)
{
OpenMainWindowCallback = implementation;
}
/// <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 ScoobeShellPage ScoobeShellHandler { get; set; }
/// <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 ScoobeShellPage()
{
InitializeComponent();
ScoobeShellHandler = this;
}
private async void ShellPage_Loaded(object sender, RoutedEventArgs e)
{
SetTitleBar();
await LoadReleasesAsync();
}
private async Task LoadReleasesAsync()
{
if (_isLoading)
{
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;
}
}
private static async Task<IList<PowerToysReleaseInfo>> FetchReleasesFromGitHubAsync()
{
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 SetTitleBar()
{
var window = App.GetScoobeWindow();
if (window != null)
{
window.ExtendsContentIntoTitleBar = true;
window.SetTitleBar(AppTitleBar);
}
}
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;
}
}
}

View File

@@ -5,13 +5,183 @@
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_PowerDisplay"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/PowerDisplay.png}"
Tag="PowerDisplay" />
<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,125 +3,157 @@
// See the LICENSE file in the project root for more information.
using System;
using ManagedCommon;
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;
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);
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");
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (navigationView.SelectedItem is NavigationViewItem selectedItem)
{
shellPage.NavigateToModule(module);
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 "PowerDisplay": NavigationFrame.Navigate(typeof(OobePowerDisplay)); 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 Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
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();
if (navigationView.SelectedItem is NavigationViewItem selectedItem && selectedItem.Tag is string tag)
{
App.OobeShellViewModel.GetModuleFromTag(tag).LogClosingModuleEvent();
}
var mainWindow = App.GetSettingsWindow();
if (mainWindow != null)
if (App.GetSettingsWindow() is MainWindow mainWindow)
{
mainWindow.CloseHiddenWindow();
}
@@ -134,19 +166,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,95 @@
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"
AutomationProperties.Name="{x:Bind DateText}"
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,38 @@
// 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.UI;
using Microsoft.UI.Windowing;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
using Microsoft.UI.Xaml;
using PowerToys.Interop;
using Windows.Graphics;
using Microsoft.UI.Xaml.Controls;
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 +43,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 +80,133 @@ 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);
if (allReleases is null || allReleases.Count == 0)
{
return [];
}
return allReleases
.OrderByDescending(r => r.PublishedDate)
.ToList();
}
private static IList<IList<PowerToysReleaseInfo>> GroupReleasesByMajorMinor(IList<PowerToysReleaseInfo> releases)
{
return releases
.GroupBy(GetMajorMinorVersion)
.Select(g => g.OrderByDescending(r => r.PublishedDate).ToList() as IList<PowerToysReleaseInfo>)
.ToList();
}
private static string GetMajorMinorVersion(PowerToysReleaseInfo release)
{
string version = ScoobeReleaseGroupViewModel.GetVersionFromRelease(release);
var parts = version.Split('.');
if (parts.Length >= 2)
{
return $"{parts[0]}.{parts[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
x:Uid="CmdPal_HeroTitle"
Margin="0,24,0,12"
@@ -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

@@ -48,16 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
/// </summary>
public delegate bool UpdatingGeneralSettingsCallback(ModuleType moduleType, bool isEnabled);
/// <summary>
/// Declaration for opening oobe window callback function.
/// </summary>
public delegate void OobeOpeningCallback();
/// <summary>
/// Declaration for opening whats new window callback function.
/// </summary>
public delegate void WhatIsNewOpeningCallback();
/// <summary>
/// Gets or sets a shell handler to be used to update contents of the shell dynamically from page within the frame.
/// </summary>
@@ -88,16 +78,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
/// </summary>
public static UpdatingGeneralSettingsCallback UpdateGeneralSettingsCallback { get; set; }
/// <summary>
/// Gets or sets callback function for opening oobe window
/// </summary>
public static OobeOpeningCallback OpenOobeWindowCallback { get; set; }
/// <summary>
/// Gets or sets callback function for opening oobe window
/// </summary>
public static WhatIsNewOpeningCallback OpenWhatIsNewWindowCallback { get; set; }
/// <summary>
/// Gets view model.
/// </summary>
@@ -223,24 +203,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 +287,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 +300,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 +370,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();
}