Compare commits

..

3 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
a3f8d78e4b Address review: quote template path and use full explorer.exe path
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 16:29:04 +08:00
copilot-swe-agent[bot]
c27534331b Fix: use explorer.exe explicitly to open NewPlus templates folder when running as different admin user
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/481445da-c05e-4da6-af6c-07c83ecbf675

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 10:29:55 +00:00
copilot-swe-agent[bot]
aaadd5dd13 Initial plan 2026-04-29 08:51:05 +00:00
7 changed files with 53 additions and 81 deletions

View File

@@ -7,6 +7,7 @@
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
<ResourceDictionary Source="pack://application:,,,/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

View File

@@ -153,23 +153,6 @@ namespace PowerLauncher
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
_mainVM = new MainViewModel(_settings, NativeThreadCTS.Token);
// Set ThemeMode before MainWindow creation so Fluent static resources
// (e.g. DefaultTextBoxStyle, CaptionTextBlockStyle referenced via BasedOn)
// are available for XAML parsing. ThemeMode.None cannot be used here even for
// high-contrast themes, because WPF's BasedOn StaticResource lookups require
// the named Fluent resources to exist at parse time.
// HighContrastWhite → Light (visually closer); other HC → Dark fallback.
// ThemeManager.SetSystemTheme will apply the correct ThemeMode (None for HC)
// once the window is ready.
var themeHelper = new ThemeHelper();
var initialTheme = themeHelper.DetermineTheme(_settings.Theme);
#pragma warning disable WPF0001
Application.Current.ThemeMode = initialTheme is Theme.Light or Theme.HighContrastWhite
? ThemeMode.Light
: ThemeMode.Dark;
#pragma warning restore WPF0001
_mainWindow = new MainWindow(_settings, _mainVM, NativeThreadCTS.Token);
_themeManager = new ThemeManager(_settings, _mainWindow);
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet, _themeManager);

View File

@@ -23,7 +23,6 @@ namespace PowerLauncher.Helper
private readonly ThemeHelper _themeHelper = new();
private bool _disposed;
private bool _isHighContrastMode;
private CancellationTokenSource _themeUpdateTokenSource;
private const int MaxRetries = 5;
private const int InitialDelayMs = 2000;
@@ -32,15 +31,6 @@ namespace PowerLauncher.Helper
public event Common.UI.ThemeChangedHandler ThemeChanged;
internal static ThemeMode GetThemeMode(Theme theme) => theme switch
{
Theme.Light => ThemeMode.Light,
Theme.Dark => ThemeMode.Dark,
Theme.HighContrastBlack or Theme.HighContrastWhite or Theme.HighContrastOne or
Theme.HighContrastTwo => ThemeMode.None,
_ => ThemeMode.Dark,
};
public ThemeManager(PowerToysRunSettings settings, MainWindow mainWindow)
{
_settings = settings;
@@ -61,22 +51,14 @@ namespace PowerLauncher.Helper
{
_mainWindow.Background = !OSVersionHelper.IsWindows11() ? SystemColors.WindowBrush : null;
if (theme is Theme.Dark or Theme.Light)
{
// When returning from a high-contrast theme, clear the high-contrast resource
// dictionaries that were applied to the window.
if (_isHighContrastMode)
{
_mainWindow.Resources.MergedDictionaries.Clear();
_isHighContrastMode = false;
}
// Need to disable WPF0001 since setting Application.Current.ThemeMode is experimental
// https://learn.microsoft.com/en-us/dotnet/desktop/wpf/whats-new/net90#set-in-code
// Need to disable WPF0001 since setting Application.Current.ThemeMode is experimental
// https://learn.microsoft.com/en-us/dotnet/desktop/wpf/whats-new/net90#set-in-code
#pragma warning disable WPF0001
Application.Current.ThemeMode = GetThemeMode(theme);
Application.Current.ThemeMode = theme == Theme.Light ? ThemeMode.Light : ThemeMode.Dark;
#pragma warning restore WPF0001
if (theme is Theme.Dark or Theme.Light)
{
if (!OSVersionHelper.IsWindows11())
{
// Apply background only on Windows 10
@@ -89,14 +71,6 @@ namespace PowerLauncher.Helper
}
else
{
// For high-contrast themes, disable WPF's Fluent theme manager to avoid conflicts
// with the custom high-contrast resource dictionaries.
#pragma warning disable WPF0001
Application.Current.ThemeMode = GetThemeMode(theme);
#pragma warning restore WPF0001
_isHighContrastMode = true;
string styleThemeString = theme switch
{
Theme.HighContrastOne => "Themes/HighContrast1.xaml",

View File

@@ -50,7 +50,7 @@
</Style>
<Style x:Key="SubtleButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}" />
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Focusable" Value="False" />

View File

@@ -1,26 +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.Windows;
using ManagedCommon;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PowerLauncher.Helper;
namespace Wox.Test;
[TestClass]
public class ThemeManagerTest
{
[DataTestMethod]
[DataRow(Theme.Light, ThemeMode.Light)]
[DataRow(Theme.Dark, ThemeMode.Dark)]
[DataRow(Theme.HighContrastBlack, ThemeMode.None)]
[DataRow(Theme.HighContrastWhite, ThemeMode.None)]
[DataRow(Theme.HighContrastOne, ThemeMode.None)]
[DataRow(Theme.HighContrastTwo, ThemeMode.None)]
public void GetThemeMode_ReturnsExpectedThemeMode(Theme theme, ThemeMode expectedThemeMode)
{
Assert.AreEqual(expectedThemeMode, ThemeManager.GetThemeMode(theme));
}
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ViewModelTests
{
[TestClass]
public class NewPlus
{
[TestMethod]
public void CreateExplorerProcessStartInfoShouldQuoteTemplatePathAndUseFullExplorerPath()
{
// Arrange
const string templatePath = @"C:\Users\Test User\Documents\My Templates";
var createExplorerProcessStartInfoMethod = typeof(NewPlusViewModel).GetMethod("CreateExplorerProcessStartInfo", BindingFlags.NonPublic | BindingFlags.Static);
// Act
var processStartInfo = (ProcessStartInfo)createExplorerProcessStartInfoMethod.Invoke(null, new object[] { templatePath });
// Assert
Assert.IsNotNull(processStartInfo);
Assert.AreEqual(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"), processStartInfo.FileName);
Assert.AreEqual($"\"{templatePath}\"", processStartInfo.Arguments);
}
}
}

View File

@@ -330,6 +330,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
private static readonly string ExplorerExePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
private bool _isNewPlusEnabled;
private string _templateLocation;
private bool _hideFileExtension;
@@ -356,12 +358,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
CopyTemplateExamples(_templateLocation);
var process = new ProcessStartInfo()
{
FileName = _templateLocation,
UseShellExecute = true,
};
Process.Start(process);
Process.Start(CreateExplorerProcessStartInfo(_templateLocation));
}
catch (Exception ex)
{
@@ -369,6 +366,15 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
private static ProcessStartInfo CreateExplorerProcessStartInfo(string templateLocation)
{
return new ProcessStartInfo
{
FileName = ExplorerExePath,
Arguments = $"\"{templateLocation}\"",
};
}
private async void PickNewTemplateFolder()
{
var newPath = await PickFolderDialog();