mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-08 12:18:50 +02:00
"What's new" improvements (#44638)
<!-- 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 TO DO: Upgrade to the latest version of MarkdownTextBlock: https://github.com/CommunityToolkit/Labs-Windows/pull/771 This PR introduces the following changes: **Removed the custom titlebars on the OOBE Window, and replaced it with the inbox WinUI `TitleBar` control.** **New "What's new" experience following the VS Code release notes experience** - Created a new SCOOBE Windows that is a standalone window to better visualize release notes. - Adding a nav menu on the left to easily switch between release notes, instead of a long page. - Point releases are combined with the latest main release.. e.g. 0.96.1 is rendered above 0.96.0. - The 'hero image' on main release notes will automatically be rendered at the top of the page. - Improved markdown styling for better readability. - Pull requests links can now be clicked. - Upgraded `CommunityToolkit.Labs.MarkdownTextblock` to the latest version as it includes much needed bugfixes. <img width="1234" height="819" alt="image" src="https://github.com/user-attachments/assets/447b3136-306b-4f24-bc7a-c022a99e8e51" /> Note: the blurry image shown above will be replaced in new releases by an image that fits the right dimensions. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: #xxx <!-- - [ ] 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: Leilei Zhang <leilzh@microsoft.com>
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.251002-build.2316" />
|
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.260107-build.2454" />
|
||||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||||
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
|
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums
|
|||||||
MeasureTool,
|
MeasureTool,
|
||||||
Hosts,
|
Hosts,
|
||||||
Workspaces,
|
Workspaces,
|
||||||
WhatsNew,
|
|
||||||
RegistryPreview,
|
RegistryPreview,
|
||||||
NewPlus,
|
NewPlus,
|
||||||
ZoomIt,
|
ZoomIt,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library;
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
@@ -227,7 +226,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
{
|
{
|
||||||
settingsWindow = new MainWindow();
|
settingsWindow = new MainWindow();
|
||||||
settingsWindow.Activate();
|
settingsWindow.Activate();
|
||||||
settingsWindow.ExtendsContentIntoTitleBar = true;
|
|
||||||
settingsWindow.NavigateToSection(StartupPage);
|
settingsWindow.NavigateToSection(StartupPage);
|
||||||
|
|
||||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
|
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
|
||||||
@@ -257,11 +255,10 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
else if (ShowScoobe)
|
else if (ShowScoobe)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
|
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
|
||||||
OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew);
|
ScoobeWindow newScoobeWindow = new ScoobeWindow();
|
||||||
scoobeWindow.Activate();
|
newScoobeWindow.Activate();
|
||||||
scoobeWindow.ExtendsContentIntoTitleBar = true;
|
|
||||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
|
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
|
||||||
SetOobeWindow(scoobeWindow);
|
SetScoobeWindow(newScoobeWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,6 +336,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
|
|
||||||
private static MainWindow settingsWindow;
|
private static MainWindow settingsWindow;
|
||||||
private static OobeWindow oobeWindow;
|
private static OobeWindow oobeWindow;
|
||||||
|
private static ScoobeWindow scoobeWindow;
|
||||||
|
|
||||||
public static void ClearSettingsWindow()
|
public static void ClearSettingsWindow()
|
||||||
{
|
{
|
||||||
@@ -365,6 +363,21 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
oobeWindow = null;
|
oobeWindow = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ScoobeWindow GetScoobeWindow()
|
||||||
|
{
|
||||||
|
return scoobeWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetScoobeWindow(ScoobeWindow window)
|
||||||
|
{
|
||||||
|
scoobeWindow = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearScoobeWindow()
|
||||||
|
{
|
||||||
|
scoobeWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Type GetPage(string settingWindow)
|
public static Type GetPage(string settingWindow)
|
||||||
{
|
{
|
||||||
switch (settingWindow)
|
switch (settingWindow)
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ using WinUIEx;
|
|||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI
|
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 MainWindow : WindowEx
|
public sealed partial class MainWindow : WindowEx
|
||||||
{
|
{
|
||||||
public MainWindow(bool createHidden = false)
|
public MainWindow(bool createHidden = false)
|
||||||
@@ -35,10 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
App.ThemeService.ThemeChanged += OnThemeChanged;
|
App.ThemeService.ThemeChanged += OnThemeChanged;
|
||||||
App.ThemeService.ApplyTheme();
|
App.ThemeService.ApplyTheme();
|
||||||
|
|
||||||
|
this.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
ShellPage.SetElevationStatus(App.IsElevated);
|
ShellPage.SetElevationStatus(App.IsElevated);
|
||||||
ShellPage.SetIsUserAnAdmin(App.IsUserAnAdmin);
|
ShellPage.SetIsUserAnAdmin(App.IsUserAnAdmin);
|
||||||
|
|
||||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
var hWnd = WindowNative.GetWindowHandle(this);
|
||||||
var placement = WindowHelper.DeserializePlacementOrDefault(hWnd);
|
var placement = WindowHelper.DeserializePlacementOrDefault(hWnd);
|
||||||
if (createHidden)
|
if (createHidden)
|
||||||
{
|
{
|
||||||
@@ -121,16 +120,12 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
// open whats new window
|
// open whats new window
|
||||||
ShellPage.SetOpenWhatIsNewCallback(() =>
|
ShellPage.SetOpenWhatIsNewCallback(() =>
|
||||||
{
|
{
|
||||||
if (App.GetOobeWindow() == null)
|
if (App.GetScoobeWindow() == null)
|
||||||
{
|
{
|
||||||
App.SetOobeWindow(new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew));
|
App.SetScoobeWindow(new ScoobeWindow());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.GetOobeWindow().SetAppWindow(OOBE.Enums.PowerToysModules.WhatsNew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
App.GetOobeWindow().Activate();
|
App.GetScoobeWindow().Activate();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.InitializeComponent();
|
this.InitializeComponent();
|
||||||
@@ -187,7 +182,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||||
WindowHelper.SerializePlacement(hWnd);
|
WindowHelper.SerializePlacement(hWnd);
|
||||||
|
|
||||||
if (App.GetOobeWindow() == null)
|
if (App.GetOobeWindow() == null && App.GetScoobeWindow() == null)
|
||||||
{
|
{
|
||||||
App.ClearSettingsWindow();
|
App.ClearSettingsWindow();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,44 @@
|
|||||||
<UserControl
|
<Page
|
||||||
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeShellPage"
|
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeShellPage"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||||
HighContrastAdjustment="None"
|
HighContrastAdjustment="None"
|
||||||
Loaded="ShellPage_Loaded"
|
Loaded="ShellPage_Loaded"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="48" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Button
|
<TitleBar
|
||||||
x:Name="PaneToggleBtn"
|
|
||||||
Width="48"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Click="PaneToggleBtn_Click"
|
|
||||||
Style="{StaticResource PaneToggleButtonStyle}" />
|
|
||||||
<Grid
|
|
||||||
x:Name="AppTitleBar"
|
x:Name="AppTitleBar"
|
||||||
Height="48"
|
x:Uid="OobeWindow_TitleTxt"
|
||||||
Margin="48,0,0,0"
|
IsBackButtonVisible="False"
|
||||||
VerticalAlignment="Center"
|
IsPaneToggleButtonVisible="False"
|
||||||
IsHitTestVisible="True">
|
PaneToggleRequested="TitleBar_PaneButtonClick">
|
||||||
<animations:Implicit.Animations>
|
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
|
||||||
<animations:OffsetAnimation Duration="0:0:0.3" />
|
<TitleBar.LeftHeader>
|
||||||
</animations:Implicit.Animations>
|
<ImageIcon
|
||||||
<StackPanel Orientation="Horizontal">
|
x:Name="TitleBarIcon"
|
||||||
<Image
|
|
||||||
Width="16"
|
|
||||||
Height="16"
|
Height="16"
|
||||||
|
Margin="16,0,0,0"
|
||||||
Source="/Assets/Settings/icon.ico" />
|
Source="/Assets/Settings/icon.ico" />
|
||||||
<TextBlock
|
</TitleBar.LeftHeader>
|
||||||
x:Name="AppTitleBarText"
|
</TitleBar>
|
||||||
x:Uid="OobeWindow_TitleTxt"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
TextWrapping="NoWrap" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<NavigationView
|
<NavigationView
|
||||||
x:Name="navigationView"
|
x:Name="navigationView"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
CompactModeThresholdWidth="1007"
|
||||||
DisplayModeChanged="NavigationView_DisplayModeChanged"
|
DisplayModeChanged="NavigationView_DisplayModeChanged"
|
||||||
|
ExpandedModeThresholdWidth="1007"
|
||||||
IsBackButtonVisible="Collapsed"
|
IsBackButtonVisible="Collapsed"
|
||||||
IsPaneOpen="True"
|
IsPaneOpen="True"
|
||||||
IsPaneToggleButtonVisible="False"
|
IsPaneToggleButtonVisible="False"
|
||||||
IsSettingsVisible="False"
|
IsSettingsVisible="False"
|
||||||
OpenPaneLength="296"
|
OpenPaneLength="296"
|
||||||
PaneDisplayMode="Left"
|
|
||||||
SelectionChanged="NavigationView_SelectionChanged">
|
SelectionChanged="NavigationView_SelectionChanged">
|
||||||
<NavigationView.MenuItems>
|
<NavigationView.MenuItems>
|
||||||
<NavigationViewItem
|
<NavigationViewItem
|
||||||
@@ -174,34 +158,16 @@
|
|||||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}"
|
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}"
|
||||||
Tag="ZoomIt" />
|
Tag="ZoomIt" />
|
||||||
</NavigationView.MenuItems>
|
</NavigationView.MenuItems>
|
||||||
<NavigationView.FooterMenuItems>
|
<NavigationView.PaneFooter>
|
||||||
<NavigationViewItem
|
<NavigationViewItem
|
||||||
x:Uid="Shell_WhatsNew"
|
x:Uid="Shell_WhatsNew"
|
||||||
|
AutomationProperties.AutomationId="WhatIsNewNavItem"
|
||||||
Icon="{ui:FontIcon Glyph=}"
|
Icon="{ui:FontIcon Glyph=}"
|
||||||
Tag="WhatsNew" />
|
Tapped="WhatIsNewItem_Tapped" />
|
||||||
</NavigationView.FooterMenuItems>
|
</NavigationView.PaneFooter>
|
||||||
<NavigationView.Content>
|
<NavigationView.Content>
|
||||||
<Frame x:Name="NavigationFrame" />
|
<Frame x:Name="NavigationFrame" />
|
||||||
</NavigationView.Content>
|
</NavigationView.Content>
|
||||||
</NavigationView>
|
</NavigationView>
|
||||||
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="LayoutVisualStates">
|
|
||||||
<VisualState x:Name="WideLayout">
|
|
||||||
<VisualState.StateTriggers>
|
|
||||||
<AdaptiveTrigger MinWindowWidth="720" />
|
|
||||||
</VisualState.StateTriggers>
|
|
||||||
</VisualState>
|
|
||||||
<VisualState x:Name="SmallLayout">
|
|
||||||
<VisualState.StateTriggers>
|
|
||||||
<AdaptiveTrigger MinWindowWidth="600" />
|
|
||||||
<AdaptiveTrigger MinWindowWidth="0" />
|
|
||||||
</VisualState.StateTriggers>
|
|
||||||
<VisualState.Setters>
|
|
||||||
<Setter Target="navigationView.PaneDisplayMode" Value="LeftMinimal" />
|
|
||||||
</VisualState.Setters>
|
|
||||||
</VisualState>
|
|
||||||
</VisualStateGroup>
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</Page>
|
||||||
|
|||||||
@@ -5,18 +5,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library;
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
||||||
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using WinRT.Interop;
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||||
{
|
{
|
||||||
public sealed partial class OobeShellPage : UserControl
|
public sealed partial class OobeShellPage : Page
|
||||||
{
|
{
|
||||||
public static Func<string> RunSharedEventCallback { get; set; }
|
public static Func<string> RunSharedEventCallback { get; set; }
|
||||||
|
|
||||||
@@ -63,7 +62,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
|
|
||||||
// NOTE: Experimentation for OOBE is currently turned off on server side. Keeping this code in a comment to allow future experiments.
|
// 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;
|
// ExperimentationToggleSwitchEnabled = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation;
|
||||||
SetTitleBar();
|
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
OobeShellHandler = this;
|
OobeShellHandler = this;
|
||||||
Modules = new ObservableCollection<OobePowerToysModule>();
|
Modules = new ObservableCollection<OobePowerToysModule>();
|
||||||
@@ -202,12 +200,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
IsNew = true,
|
IsNew = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
Modules.Insert((int)PowerToysModules.WhatsNew, new OobePowerToysModule()
|
|
||||||
{
|
|
||||||
ModuleName = "WhatsNew",
|
|
||||||
IsNew = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
Modules.Insert((int)PowerToysModules.RegistryPreview, new OobePowerToysModule()
|
Modules.Insert((int)PowerToysModules.RegistryPreview, new OobePowerToysModule()
|
||||||
{
|
{
|
||||||
ModuleName = "RegistryPreview",
|
ModuleName = "RegistryPreview",
|
||||||
@@ -229,7 +221,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
|
|
||||||
public void OnClosing()
|
public void OnClosing()
|
||||||
{
|
{
|
||||||
Microsoft.UI.Xaml.Controls.NavigationViewItem selectedItem = this.navigationView.SelectedItem as Microsoft.UI.Xaml.Controls.NavigationViewItem;
|
NavigationViewItem selectedItem = this.navigationView.SelectedItem as NavigationViewItem;
|
||||||
if (selectedItem != null)
|
if (selectedItem != null)
|
||||||
{
|
{
|
||||||
Modules[(int)(PowerToysModules)Enum.Parse(typeof(PowerToysModules), (string)selectedItem.Tag, true)].LogClosingModuleEvent();
|
Modules[(int)(PowerToysModules)Enum.Parse(typeof(PowerToysModules), (string)selectedItem.Tag, true)].LogClosingModuleEvent();
|
||||||
@@ -238,19 +230,22 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
|
|
||||||
public void NavigateToModule(PowerToysModules selectedModule)
|
public void NavigateToModule(PowerToysModules selectedModule)
|
||||||
{
|
{
|
||||||
if (selectedModule == PowerToysModules.WhatsNew)
|
navigationView.SelectedItem = navigationView.MenuItems[(int)selectedModule];
|
||||||
{
|
|
||||||
navigationView.SelectedItem = navigationView.FooterMenuItems[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
navigationView.SelectedItem = navigationView.MenuItems[(int)selectedModule];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NavigationView_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
|
private static void OpenScoobeWindow()
|
||||||
{
|
{
|
||||||
Microsoft.UI.Xaml.Controls.NavigationViewItem selectedItem = args.SelectedItem as Microsoft.UI.Xaml.Controls.NavigationViewItem;
|
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)
|
if (selectedItem != null)
|
||||||
{
|
{
|
||||||
@@ -278,7 +273,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
case "WhatsNew": NavigationFrame.Navigate(typeof(OobeWhatsNew)); break;
|
|
||||||
case "AdvancedPaste": NavigationFrame.Navigate(typeof(OobeAdvancedPaste)); break;
|
case "AdvancedPaste": NavigationFrame.Navigate(typeof(OobeAdvancedPaste)); break;
|
||||||
case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
|
case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break;
|
||||||
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
|
case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break;
|
||||||
@@ -311,43 +306,37 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetTitleBar()
|
|
||||||
{
|
|
||||||
var u = App.GetOobeWindow();
|
|
||||||
if (u != null)
|
|
||||||
{
|
|
||||||
// A custom title bar is required for full window theme and Mica support.
|
|
||||||
// https://docs.microsoft.com/windows/apps/develop/title-bar?tabs=winui3#full-customization
|
|
||||||
u.ExtendsContentIntoTitleBar = true;
|
|
||||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(u));
|
|
||||||
u.SetTitleBar(AppTitleBar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShellPage_Loaded(object sender, RoutedEventArgs e)
|
private void ShellPage_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SetTitleBar();
|
// Select the first module by default
|
||||||
|
if (navigationView.MenuItems.Count > 0)
|
||||||
|
{
|
||||||
|
navigationView.SelectedItem = navigationView.MenuItems[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
|
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
|
||||||
{
|
{
|
||||||
PaneToggleBtn.Visibility = Visibility.Visible;
|
TitleBarIcon.Margin = new Thickness(0, 0, 8, 0); // Workaround, see XAML comment
|
||||||
AppTitleBar.Margin = new Thickness(48, 0, 0, 0);
|
AppTitleBar.IsPaneToggleButtonVisible = true;
|
||||||
AppTitleBarText.Margin = new Thickness(12, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PaneToggleBtn.Visibility = Visibility.Collapsed;
|
TitleBarIcon.Margin = new Thickness(16, 0, 0, 0); // Workaround, see XAML comment
|
||||||
AppTitleBar.Margin = new Thickness(16, 0, 0, 0);
|
AppTitleBar.IsPaneToggleButtonVisible = false;
|
||||||
AppTitleBarText.Margin = new Thickness(16, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PaneToggleBtn_Click(object sender, RoutedEventArgs e)
|
private void TitleBar_PaneButtonClick(TitleBar sender, object args)
|
||||||
{
|
{
|
||||||
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
|
navigationView.IsPaneOpen = !navigationView.IsPaneOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WhatIsNewItem_Tapped(object sender, TappedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
OpenScoobeWindow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
<Page
|
|
||||||
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeWhatsNew"
|
|
||||||
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"
|
|
||||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
|
||||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
|
||||||
Loaded="Page_Loaded"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
<Page.Resources>
|
|
||||||
<tkcontrols:MarkdownThemes
|
|
||||||
x:Key="ReleaseNotesMarkdownThemeConfig"
|
|
||||||
H1FontSize="22"
|
|
||||||
H1FontWeight="SemiBold"
|
|
||||||
H1Margin="0, 36, 0, 8"
|
|
||||||
H2FontSize="16"
|
|
||||||
H2FontWeight="SemiBold"
|
|
||||||
H2Margin="0, 16, 0, 4"
|
|
||||||
H3FontSize="16"
|
|
||||||
H3FontWeight="SemiBold"
|
|
||||||
H3Margin="0, 16, 0, 4" />
|
|
||||||
<tkcontrols:MarkdownConfig x:Key="ReleaseNotesMarkdownConfig" Themes="{StaticResource ReleaseNotesMarkdownThemeConfig}" />
|
|
||||||
</Page.Resources>
|
|
||||||
|
|
||||||
<!-- Main layout container -->
|
|
||||||
<Grid>
|
|
||||||
<!-- Main content grid -->
|
|
||||||
<Grid Margin="0,24,0,0">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<!-- Compact Header overlay that covers both InfoBar and Title sections -->
|
|
||||||
<Border
|
|
||||||
x:Name="HeaderOverlay"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Margin="0,-24,0,0"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
BorderThickness="0"
|
|
||||||
Canvas.ZIndex="1">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<tkcontrols:SettingsCard
|
|
||||||
x:Name="WhatsNewDataDiagnosticsInfoBar"
|
|
||||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar"
|
|
||||||
Grid.Row="0"
|
|
||||||
Padding="12,8,12,8"
|
|
||||||
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
|
|
||||||
IsTabStop="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay}"
|
|
||||||
Visibility="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<tkcontrols:SettingsCard.HeaderIcon>
|
|
||||||
<FontIcon Foreground="{ThemeResource InfoBarInformationalSeverityIconBackground}" Glyph="" />
|
|
||||||
</tkcontrols:SettingsCard.HeaderIcon>
|
|
||||||
<tkcontrols:SettingsCard.Description>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescText">
|
|
||||||
<Hyperlink NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation">
|
|
||||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar_Desc" />
|
|
||||||
</Hyperlink>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescTextYesClicked" Visibility="Collapsed">
|
|
||||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_InfoBar_Desc" />
|
|
||||||
<Hyperlink Click="DataDiagnostics_OpenSettings_Click">
|
|
||||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_OpenSettings_Text" />
|
|
||||||
</Hyperlink>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</tkcontrols:SettingsCard.Description>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
||||||
<Button
|
|
||||||
x:Name="DataDiagnosticsButtonYes"
|
|
||||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_Yes"
|
|
||||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
|
||||||
CommandParameter="Yes" />
|
|
||||||
<HyperlinkButton
|
|
||||||
x:Name="DataDiagnosticsButtonNo"
|
|
||||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_No"
|
|
||||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
|
||||||
CommandParameter="No" />
|
|
||||||
<Button
|
|
||||||
Margin="16,0,0,0"
|
|
||||||
Click="DataDiagnostics_InfoBar_Close_Click"
|
|
||||||
Content="{ui:FontIcon Glyph=,
|
|
||||||
FontSize=16}"
|
|
||||||
Style="{StaticResource SubtleButtonStyle}" />
|
|
||||||
</StackPanel>
|
|
||||||
</tkcontrols:SettingsCard>
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Margin="16,12,0,12">
|
|
||||||
<StackPanel
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Orientation="Vertical"
|
|
||||||
Spacing="4">
|
|
||||||
<TextBlock
|
|
||||||
x:Uid="Oobe_WhatsNew"
|
|
||||||
AutomationProperties.HeadingLevel="Level1"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}" />
|
|
||||||
<HyperlinkButton NavigateUri="https://github.com/microsoft/PowerToys/releases" Style="{StaticResource TextButtonStyle}">
|
|
||||||
<TextBlock x:Uid="Oobe_WhatsNew_DetailedReleaseNotesLink" TextWrapping="Wrap" />
|
|
||||||
</HyperlinkButton>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- ShortcutConflictControl positioned at the right side -->
|
|
||||||
<controls:ShortcutConflictControl
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Margin="0,0,16,0"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
AllHotkeyConflictsData="{x:Bind AllHotkeyConflictsData, Mode=OneWay}"
|
|
||||||
Visibility="{x:Bind HasConflicts, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Reduced spacer for the compact header overlay -->
|
|
||||||
<Grid Grid.Row="0" Height="0" />
|
|
||||||
<Grid Grid.Row="1" Height="80" />
|
|
||||||
|
|
||||||
<InfoBar
|
|
||||||
x:Name="ErrorInfoBar"
|
|
||||||
x:Uid="Oobe_WhatsNew_LoadingError"
|
|
||||||
Grid.Row="2"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
IsClosable="False"
|
|
||||||
IsTabStop="False"
|
|
||||||
Severity="Error">
|
|
||||||
<InfoBar.ActionButton>
|
|
||||||
<Button
|
|
||||||
x:Uid="RetryBtn"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Click="LoadReleaseNotes_Click">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
||||||
<FontIcon FontSize="16" Glyph="" />
|
|
||||||
<TextBlock x:Uid="RetryLabel" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</InfoBar.ActionButton>
|
|
||||||
</InfoBar>
|
|
||||||
<InfoBar
|
|
||||||
x:Name="ProxyWarningInfoBar"
|
|
||||||
x:Uid="Oobe_WhatsNew_ProxyAuthenticationWarning"
|
|
||||||
Grid.Row="2"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
IsClosable="False"
|
|
||||||
IsTabStop="False"
|
|
||||||
Severity="Warning">
|
|
||||||
<InfoBar.ActionButton>
|
|
||||||
<Button
|
|
||||||
x:Uid="RetryBtn"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Click="LoadReleaseNotes_Click">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
|
||||||
<FontIcon FontSize="16" Glyph="" />
|
|
||||||
<TextBlock x:Uid="RetryLabel" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</InfoBar.ActionButton>
|
|
||||||
</InfoBar>
|
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
|
|
||||||
<Grid Margin="32,16,32,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>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Page>
|
|
||||||
@@ -1,359 +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.ComponentModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CommunityToolkit.WinUI.Controls;
|
|
||||||
using global::PowerToys.GPOWrapper;
|
|
||||||
using ManagedCommon;
|
|
||||||
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.SerializationContext;
|
|
||||||
using Microsoft.PowerToys.Settings.UI.Services;
|
|
||||||
using Microsoft.PowerToys.Settings.UI.Views;
|
|
||||||
using Microsoft.PowerToys.Telemetry;
|
|
||||||
using Microsoft.UI.Text;
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using Microsoft.UI.Xaml.Media;
|
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|
||||||
{
|
|
||||||
public sealed partial class OobeWhatsNew : Page, INotifyPropertyChanged
|
|
||||||
{
|
|
||||||
public OobePowerToysModule ViewModel { get; set; }
|
|
||||||
|
|
||||||
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
|
||||||
|
|
||||||
public bool ShowDataDiagnosticsInfoBar => GetShowDataDiagnosticsInfoBar();
|
|
||||||
|
|
||||||
private int _conflictCount;
|
|
||||||
|
|
||||||
public AllHotkeyConflictsData AllHotkeyConflictsData
|
|
||||||
{
|
|
||||||
get => _allHotkeyConflictsData;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_allHotkeyConflictsData != value)
|
|
||||||
{
|
|
||||||
_allHotkeyConflictsData = value;
|
|
||||||
|
|
||||||
UpdateConflictCount();
|
|
||||||
|
|
||||||
OnPropertyChanged(nameof(AllHotkeyConflictsData));
|
|
||||||
OnPropertyChanged(nameof(HasConflicts));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasConflicts => _conflictCount > 0;
|
|
||||||
|
|
||||||
private void UpdateConflictCount()
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
if (AllHotkeyConflictsData == null)
|
|
||||||
{
|
|
||||||
_conflictCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AllHotkeyConflictsData.InAppConflicts != null)
|
|
||||||
{
|
|
||||||
foreach (var inAppConflict in AllHotkeyConflictsData.InAppConflicts)
|
|
||||||
{
|
|
||||||
if (!inAppConflict.ConflictIgnored)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AllHotkeyConflictsData.SystemConflicts != null)
|
|
||||||
{
|
|
||||||
foreach (var systemConflict in AllHotkeyConflictsData.SystemConflicts)
|
|
||||||
{
|
|
||||||
if (!systemConflict.ConflictIgnored)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_conflictCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="OobeWhatsNew"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public OobeWhatsNew()
|
|
||||||
{
|
|
||||||
this.InitializeComponent();
|
|
||||||
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.WhatsNew]);
|
|
||||||
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, () =>
|
|
||||||
{
|
|
||||||
var allConflictData = e.Conflicts;
|
|
||||||
foreach (var inAppConflict in allConflictData.InAppConflicts)
|
|
||||||
{
|
|
||||||
var hotkey = inAppConflict.Hotkey;
|
|
||||||
var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
|
|
||||||
inAppConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var systemConflict in allConflictData.SystemConflicts)
|
|
||||||
{
|
|
||||||
var hotkey = systemConflict.Hotkey;
|
|
||||||
var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
|
|
||||||
systemConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
|
|
||||||
}
|
|
||||||
|
|
||||||
AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPropertyChanged(string propertyName)
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool GetShowDataDiagnosticsInfoBar()
|
|
||||||
{
|
|
||||||
var isDataDiagnosticsGpoDisallowed = GPOWrapper.GetAllowDataDiagnosticsValue() == GpoRuleConfigured.Disabled;
|
|
||||||
|
|
||||||
if (isDataDiagnosticsGpoDisallowed)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool userActed = DataDiagnosticsSettings.GetUserActionValue();
|
|
||||||
|
|
||||||
if (userActed)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool registryValue = DataDiagnosticsSettings.GetEnabledValue();
|
|
||||||
|
|
||||||
bool isFirstRunAfterUpdate = (App.Current as Microsoft.PowerToys.Settings.UI.App).ShowScoobe;
|
|
||||||
if (isFirstRunAfterUpdate && registryValue == false)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Regex to remove installer hash sections from the release notes.
|
|
||||||
/// </summary>
|
|
||||||
private const string RemoveInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+## Highlights";
|
|
||||||
private const string RemoveHotFixInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+$";
|
|
||||||
private const RegexOptions RemoveInstallerHashesRegexOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
|
|
||||||
private bool _loadingReleaseNotes;
|
|
||||||
|
|
||||||
private static async Task<string> GetReleaseNotesMarkdown()
|
|
||||||
{
|
|
||||||
string releaseNotesJSON = string.Empty;
|
|
||||||
|
|
||||||
// Let's use system proxy
|
|
||||||
using var proxyClientHandler = new HttpClientHandler
|
|
||||||
{
|
|
||||||
DefaultProxyCredentials = CredentialCache.DefaultCredentials,
|
|
||||||
Proxy = WebRequest.GetSystemWebProxy(),
|
|
||||||
PreAuthenticate = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
using var getReleaseInfoClient = new HttpClient(proxyClientHandler);
|
|
||||||
|
|
||||||
// GitHub APIs require sending an user agent
|
|
||||||
// https://docs.github.com/rest/overview/resources-in-the-rest-api#user-agent-required
|
|
||||||
getReleaseInfoClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "PowerToys");
|
|
||||||
releaseNotesJSON = await getReleaseInfoClient.GetStringAsync("https://api.github.com/repos/microsoft/PowerToys/releases");
|
|
||||||
IList<PowerToysReleaseInfo> releases = JsonSerializer.Deserialize<IList<PowerToysReleaseInfo>>(releaseNotesJSON, SourceGenerationContextContext.Default.IListPowerToysReleaseInfo);
|
|
||||||
|
|
||||||
// Get the latest releases
|
|
||||||
var latestReleases = releases.OrderByDescending(release => release.PublishedDate).Take(5);
|
|
||||||
|
|
||||||
StringBuilder releaseNotesHtmlBuilder = new StringBuilder(string.Empty);
|
|
||||||
|
|
||||||
// Regex to remove installer hash sections from the release notes.
|
|
||||||
Regex removeHashRegex = new Regex(RemoveInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
|
|
||||||
|
|
||||||
// Regex to remove installer hash sections from the release notes, since there'll be no Highlights section for hotfix releases.
|
|
||||||
Regex removeHotfixHashRegex = new Regex(RemoveHotFixInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
|
|
||||||
int counter = 0;
|
|
||||||
foreach (var release in latestReleases)
|
|
||||||
{
|
|
||||||
releaseNotesHtmlBuilder.AppendLine("# " + release.Name);
|
|
||||||
var notes = removeHashRegex.Replace(release.ReleaseNotes, "\r\n### Highlights");
|
|
||||||
|
|
||||||
// Add a unique counter to [github-current-release-work] to distinguish each release,
|
|
||||||
// since this variable is used for all latest releases when they are merged.
|
|
||||||
notes = notes.Replace("[github-current-release-work]", $"[github-current-release-work{++counter}]");
|
|
||||||
notes = removeHotfixHashRegex.Replace(notes, string.Empty);
|
|
||||||
releaseNotesHtmlBuilder.AppendLine(notes);
|
|
||||||
releaseNotesHtmlBuilder.AppendLine(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return releaseNotesHtmlBuilder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Reload()
|
|
||||||
{
|
|
||||||
if (_loadingReleaseNotes)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_loadingReleaseNotes = true;
|
|
||||||
ReleaseNotesMarkdown.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
LoadingProgressRing.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
|
||||||
string releaseNotesMarkdown = await GetReleaseNotesMarkdown();
|
|
||||||
ProxyWarningInfoBar.IsOpen = false;
|
|
||||||
ErrorInfoBar.IsOpen = false;
|
|
||||||
|
|
||||||
ReleaseNotesMarkdown.Text = releaseNotesMarkdown;
|
|
||||||
ReleaseNotesMarkdown.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
|
||||||
LoadingProgressRing.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
catch (HttpRequestException httpEx)
|
|
||||||
{
|
|
||||||
Logger.LogError("Exception when loading the release notes", httpEx);
|
|
||||||
if (httpEx.Message.Contains("407", StringComparison.CurrentCulture))
|
|
||||||
{
|
|
||||||
ProxyWarningInfoBar.IsOpen = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ErrorInfoBar.IsOpen = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError("Exception when loading the release notes", ex);
|
|
||||||
ErrorInfoBar.IsOpen = true;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LoadingProgressRing.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
_loadingReleaseNotes = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
await Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.LogOpeningModuleEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.LogClosingModuleEvent();
|
|
||||||
|
|
||||||
// Unsubscribe from conflict updates when leaving the page
|
|
||||||
if (GlobalHotkeyConflictManager.Instance != null)
|
|
||||||
{
|
|
||||||
GlobalHotkeyConflictManager.Instance.ConflictsUpdated -= OnConflictsUpdated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataDiagnostics_InfoBar_YesNo_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
string commandArg = string.Empty;
|
|
||||||
if (sender is Button senderBtn)
|
|
||||||
{
|
|
||||||
commandArg = senderBtn.CommandParameter.ToString();
|
|
||||||
}
|
|
||||||
else if (sender is HyperlinkButton senderLink)
|
|
||||||
{
|
|
||||||
commandArg = senderLink.CommandParameter.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(commandArg))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
if (commandArg == "Yes")
|
|
||||||
{
|
|
||||||
WhatsNewDataDiagnosticsInfoBar.Header = ResourceLoaderInstance.ResourceLoader.GetString("Oobe_WhatsNew_DataDiagnostics_Yes_Click_InfoBar_Title");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WhatsNewDataDiagnosticsInfoBar.Header = ResourceLoaderInstance.ResourceLoader.GetString("Oobe_WhatsNew_DataDiagnostics_No_Click_InfoBar_Title");
|
|
||||||
}
|
|
||||||
|
|
||||||
WhatsNewDataDiagnosticsInfoBarDescText.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
WhatsNewDataDiagnosticsInfoBarDescTextYesClicked.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
|
||||||
DataDiagnosticsButtonYes.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
DataDiagnosticsButtonNo.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
|
|
||||||
// Set Data Diagnostics registry values
|
|
||||||
if (commandArg == "Yes")
|
|
||||||
{
|
|
||||||
DataDiagnosticsSettings.SetEnabledValue(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DataDiagnosticsSettings.SetEnabledValue(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataDiagnosticsSettings.SetUserActionValue(true);
|
|
||||||
|
|
||||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
|
||||||
{
|
|
||||||
ShellPage.ShellHandler?.SignalGeneralDataUpdate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataDiagnostics_InfoBar_Close_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
WhatsNewDataDiagnosticsInfoBar.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataDiagnostics_OpenSettings_Click(Microsoft.UI.Xaml.Documents.Hyperlink sender, Microsoft.UI.Xaml.Documents.HyperlinkClickEventArgs args)
|
|
||||||
{
|
|
||||||
Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Overview);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void LoadReleaseNotes_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
await Reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// View model for a group of releases (grouped by major.minor version).
|
||||||
|
/// </summary>
|
||||||
|
public class ScoobeReleaseGroupViewModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of releases in this group.
|
||||||
|
/// </summary>
|
||||||
|
public IList<PowerToysReleaseInfo> Releases { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the version text to display (e.g., "0.96.0").
|
||||||
|
/// </summary>
|
||||||
|
public string VersionText { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the date text to display (e.g., "December 2025").
|
||||||
|
/// </summary>
|
||||||
|
public string DateText { get; }
|
||||||
|
|
||||||
|
public ScoobeReleaseGroupViewModel(IList<PowerToysReleaseInfo> releases)
|
||||||
|
{
|
||||||
|
Releases = releases ?? throw new ArgumentNullException(nameof(releases));
|
||||||
|
|
||||||
|
if (releases.Count > 0)
|
||||||
|
{
|
||||||
|
var latestRelease = releases[0];
|
||||||
|
VersionText = GetVersionFromRelease(latestRelease);
|
||||||
|
DateText = latestRelease.PublishedDate.ToString("MMMM yyyy", CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VersionText = "Unknown";
|
||||||
|
DateText = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.ScoobeReleaseNotesPage"
|
||||||
|
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:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||||
|
Loaded="Page_Loaded"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Page.Resources>
|
||||||
|
<tkcontrols:MarkdownThemes
|
||||||
|
x:Key="ReleaseNotesMarkdownThemeConfig"
|
||||||
|
BoldFontWeight="SemiBold"
|
||||||
|
H1FontSize="28"
|
||||||
|
H1FontWeight="SemiBold"
|
||||||
|
H1Margin="0, 36, 0, 8"
|
||||||
|
H2FontSize="20"
|
||||||
|
H2FontWeight="SemiBold"
|
||||||
|
H2Margin="0, 16, 0, 4"
|
||||||
|
H3FontSize="16"
|
||||||
|
H3FontWeight="SemiBold"
|
||||||
|
H3Margin="0, 16, 0, 4"
|
||||||
|
HorizontalRuleBrush="{StaticResource DividerStrokeColorDefaultBrush}"
|
||||||
|
HorizontalRuleThickness="1"
|
||||||
|
ImageStretch="Uniform"
|
||||||
|
ListBulletSpacing="1"
|
||||||
|
ListGutterWidth="10" />
|
||||||
|
<tkcontrols:MarkdownConfig x:Key="ReleaseNotesMarkdownConfig" Themes="{StaticResource ReleaseNotesMarkdownThemeConfig}" />
|
||||||
|
</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>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using ManagedCommon;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||||
|
{
|
||||||
|
public sealed partial class ScoobeReleaseNotesPage : Page
|
||||||
|
{
|
||||||
|
private IList<PowerToysReleaseInfo> _currentReleases;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ScoobeReleaseNotesPage"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ScoobeReleaseNotesPage()
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regex to remove installer hash sections from the release notes.
|
||||||
|
/// </summary>
|
||||||
|
private const string RemoveInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+## Highlights";
|
||||||
|
private const string RemoveHotFixInstallerHashesRegex = @"(\r\n)+## Installer Hashes(\r\n.*)+$";
|
||||||
|
private const RegexOptions RemoveInstallerHashesRegexOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regex to match markdown images with 'Hero' in the alt text.
|
||||||
|
/// Matches: 
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex HeroImageRegex = new Regex(
|
||||||
|
@"!\[([^\]]*Hero[^\]]*)\]\(([^)]+)\)",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regex to match GitHub PR/Issue references (e.g., #41029).
|
||||||
|
/// Only matches # followed by digits that are not already part of a markdown link.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex GitHubPrReferenceRegex = new Regex(
|
||||||
|
@"(?<!\[)#(\d+)(?!\])",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static readonly CompositeFormat GitHubPrLinkTemplate = CompositeFormat.Parse("[#{0}](https://github.com/microsoft/PowerToys/pull/{0})");
|
||||||
|
private static readonly CompositeFormat GitHubReleaseLinkTemplate = CompositeFormat.Parse("https://github.com/microsoft/PowerToys/releases/tag/{0}");
|
||||||
|
|
||||||
|
private static (string Markdown, string HeroImageUrl) ProcessReleaseNotesMarkdown(IList<PowerToysReleaseInfo> releases)
|
||||||
|
{
|
||||||
|
if (releases == null || releases.Count == 0)
|
||||||
|
{
|
||||||
|
return (string.Empty, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder releaseNotesHtmlBuilder = new StringBuilder(string.Empty);
|
||||||
|
|
||||||
|
// Regex to remove installer hash sections from the release notes.
|
||||||
|
Regex removeHashRegex = new Regex(RemoveInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
|
||||||
|
|
||||||
|
// Regex to remove installer hash sections from the release notes, since there'll be no Highlights section for hotfix releases.
|
||||||
|
Regex removeHotfixHashRegex = new Regex(RemoveHotFixInstallerHashesRegex, RemoveInstallerHashesRegexOptions);
|
||||||
|
|
||||||
|
string lastHeroImageUrl = null;
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
bool isFirst = true;
|
||||||
|
foreach (var release in releases)
|
||||||
|
{
|
||||||
|
// Add separator between releases
|
||||||
|
if (!isFirst)
|
||||||
|
{
|
||||||
|
releaseNotesHtmlBuilder.AppendLine("---");
|
||||||
|
releaseNotesHtmlBuilder.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirst = false;
|
||||||
|
|
||||||
|
var releaseUrl = string.Format(CultureInfo.InvariantCulture, GitHubReleaseLinkTemplate, release.TagName);
|
||||||
|
releaseNotesHtmlBuilder.AppendLine(CultureInfo.InvariantCulture, $"# {release.Name}");
|
||||||
|
releaseNotesHtmlBuilder.AppendLine(CultureInfo.InvariantCulture, $"{release.PublishedDate.ToString("MMMM d, yyyy", CultureInfo.CurrentCulture)} <20> [View on GitHub]({releaseUrl})");
|
||||||
|
releaseNotesHtmlBuilder.AppendLine();
|
||||||
|
releaseNotesHtmlBuilder.AppendLine(" ");
|
||||||
|
releaseNotesHtmlBuilder.AppendLine();
|
||||||
|
var notes = removeHashRegex.Replace(release.ReleaseNotes, "\r\n## Highlights");
|
||||||
|
notes = notes.Replace("[github-current-release-work]", $"[github-current-release-work{++counter}]");
|
||||||
|
notes = removeHotfixHashRegex.Replace(notes, string.Empty);
|
||||||
|
|
||||||
|
// Find all Hero images and keep track of the last one
|
||||||
|
var heroMatches = HeroImageRegex.Matches(notes);
|
||||||
|
foreach (Match match in heroMatches)
|
||||||
|
{
|
||||||
|
lastHeroImageUrl = match.Groups[2].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove Hero images from the markdown
|
||||||
|
notes = HeroImageRegex.Replace(notes, string.Empty);
|
||||||
|
|
||||||
|
// Convert GitHub PR/Issue references to hyperlinks
|
||||||
|
notes = GitHubPrReferenceRegex.Replace(notes, match =>
|
||||||
|
string.Format(CultureInfo.InvariantCulture, GitHubPrLinkTemplate, match.Groups[1].Value));
|
||||||
|
|
||||||
|
releaseNotesHtmlBuilder.AppendLine(notes);
|
||||||
|
releaseNotesHtmlBuilder.AppendLine(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (releaseNotesHtmlBuilder.ToString(), lastHeroImageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisplayReleaseNotes()
|
||||||
|
{
|
||||||
|
if (_currentReleases == null || _currentReleases.Count == 0)
|
||||||
|
{
|
||||||
|
ReleaseNotesMarkdown.Visibility = Visibility.Collapsed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadingProgressRing.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
var (releaseNotesMarkdown, heroImageUrl) = ProcessReleaseNotesMarkdown(_currentReleases);
|
||||||
|
|
||||||
|
// Set the Hero image if found
|
||||||
|
if (!string.IsNullOrEmpty(heroImageUrl))
|
||||||
|
{
|
||||||
|
HeroImageHolder.Source = new BitmapImage(new Uri(heroImageUrl));
|
||||||
|
HeroImageHolder.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeroImageHolder.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseNotesMarkdown.Text = releaseNotesMarkdown;
|
||||||
|
ReleaseNotesMarkdown.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("Exception when displaying the release notes", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DisplayReleaseNotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Parameter is IList<PowerToysReleaseInfo> releases)
|
||||||
|
{
|
||||||
|
_currentReleases = releases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<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="" />
|
||||||
|
<TextBlock x:Uid="RetryLabel" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</InfoBar.ActionButton>
|
||||||
|
</InfoBar>
|
||||||
|
<Frame x:Name="NavigationFrame" />
|
||||||
|
</Grid>
|
||||||
|
</NavigationView.Content>
|
||||||
|
</NavigationView>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
|
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
|
||||||
_appWindow = AppWindow.GetFromWindowId(_windowId);
|
_appWindow = AppWindow.GetFromWindowId(_windowId);
|
||||||
this.Activated += Window_Activated_SetIcon;
|
this.Activated += Window_Activated_SetIcon;
|
||||||
|
this.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||||
_currentDPI = dpi;
|
_currentDPI = dpi;
|
||||||
@@ -60,7 +61,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
|
|
||||||
this.SizeChanged += OobeWindow_SizeChanged;
|
this.SizeChanged += OobeWindow_SizeChanged;
|
||||||
|
|
||||||
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
var loader = ResourceLoaderInstance.ResourceLoader;
|
||||||
Title = loader.GetString("OobeWindow_Title");
|
Title = loader.GetString("OobeWindow_Title");
|
||||||
|
|
||||||
if (shellPage != null)
|
if (shellPage != null)
|
||||||
|
|||||||
17
src/settings-ui/Settings.UI/SettingsXAML/ScoobeWindow.xaml
Normal file
17
src/settings-ui/Settings.UI/SettingsXAML/ScoobeWindow.xaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<winuiex:WindowEx
|
||||||
|
x:Class="Microsoft.PowerToys.Settings.UI.ScoobeWindow"
|
||||||
|
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"
|
||||||
|
xmlns:winuiex="using:WinUIEx"
|
||||||
|
MinWidth="480"
|
||||||
|
MinHeight="480"
|
||||||
|
Closed="Window_Closed"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Window.SystemBackdrop>
|
||||||
|
<MicaBackdrop />
|
||||||
|
</Window.SystemBackdrop>
|
||||||
|
<local:ScoobeShellPage x:Name="shellPage" />
|
||||||
|
</winuiex:WindowEx>
|
||||||
121
src/settings-ui/Settings.UI/SettingsXAML/ScoobeWindow.xaml.cs
Normal file
121
src/settings-ui/Settings.UI/SettingsXAML/ScoobeWindow.xaml.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// 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 Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.OOBE.Views;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using Microsoft.UI.Windowing;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using PowerToys.Interop;
|
||||||
|
using Windows.Graphics;
|
||||||
|
using WinUIEx;
|
||||||
|
using WinUIEx.Messaging;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI
|
||||||
|
{
|
||||||
|
public sealed partial class ScoobeWindow : WindowEx, IDisposable
|
||||||
|
{
|
||||||
|
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 ScoobeWindow()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) =>
|
||||||
|
{
|
||||||
|
App.OpenSettingsWindow(type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
|
||||||
|
{
|
||||||
|
// Set window icon
|
||||||
|
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScoobeWindow_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closed(object sender, WindowEventArgs args)
|
||||||
|
{
|
||||||
|
App.ClearScoobeWindow();
|
||||||
|
|
||||||
|
var mainWindow = App.GetSettingsWindow();
|
||||||
|
if (mainWindow != null)
|
||||||
|
{
|
||||||
|
mainWindow.CloseHiddenWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
App.ThemeService.ThemeChanged -= OnThemeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnThemeChanged(object sender, ElementTheme theme)
|
||||||
|
{
|
||||||
|
WindowHelper.SetTheme(this, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,6 @@ using System.Threading.Tasks;
|
|||||||
using ManagedCommon;
|
using ManagedCommon;
|
||||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||||
using Microsoft.PowerToys.Settings.UI.Library;
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
|
||||||
using Microsoft.PowerToys.Settings.UI.OOBE.Views;
|
|
||||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
@@ -50,16 +48,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||||||
|
|
||||||
private void WhatsNewButton_Click(object sender, RoutedEventArgs e)
|
private void WhatsNewButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (App.GetOobeWindow() == null)
|
if (App.GetScoobeWindow() == null)
|
||||||
{
|
{
|
||||||
App.SetOobeWindow(new OobeWindow(PowerToysModules.WhatsNew));
|
App.SetScoobeWindow(new ScoobeWindow());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.GetOobeWindow().SetAppWindow(PowerToysModules.WhatsNew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
App.GetOobeWindow().Activate();
|
App.GetScoobeWindow().Activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
|
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -2419,9 +2419,15 @@ From there, simply click on one of the supported files in the File Explorer and
|
|||||||
<data name="OobeWindow_Title" xml:space="preserve">
|
<data name="OobeWindow_Title" xml:space="preserve">
|
||||||
<value>Welcome to PowerToys</value>
|
<value>Welcome to PowerToys</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OobeWindow_TitleTxt.Text" xml:space="preserve">
|
<data name="OobeWindow_TitleTxt.Title" xml:space="preserve">
|
||||||
<value>Welcome to PowerToys</value>
|
<value>Welcome to PowerToys</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ScoobeWindow_Title" xml:space="preserve">
|
||||||
|
<value>What's new in PowerToys</value>
|
||||||
|
</data>
|
||||||
|
<data name="ScoobeWindow_TitleTxt.Title" xml:space="preserve">
|
||||||
|
<value>What's new in PowerToys</value>
|
||||||
|
</data>
|
||||||
<data name="SettingsWindow_Title" xml:space="preserve">
|
<data name="SettingsWindow_Title" xml:space="preserve">
|
||||||
<value>PowerToys Settings</value>
|
<value>PowerToys Settings</value>
|
||||||
<comment>Title of the settings window when running as user</comment>
|
<comment>Title of the settings window when running as user</comment>
|
||||||
|
|||||||
Reference in New Issue
Block a user