mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-06 12:27:01 +01:00
Compare commits
10 Commits
leilzh/fui
...
leilzh/w10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75d6ad74d1 | ||
|
|
ef4e619350 | ||
|
|
7f3349b3f5 | ||
|
|
9c285856bf | ||
|
|
d90575b8da | ||
|
|
da36d410e3 | ||
|
|
a50d548a07 | ||
|
|
8c4a3a6944 | ||
|
|
efc68bc0c9 | ||
|
|
bf74bc43d4 |
2
.github/actions/spell-check/expect.txt
vendored
2
.github/actions/spell-check/expect.txt
vendored
@@ -168,6 +168,7 @@ callbackptr
|
||||
calpwstr
|
||||
Cangjie
|
||||
CANRENAME
|
||||
Canvascustomlayout
|
||||
CAPTUREBLT
|
||||
CAPTURECHANGED
|
||||
CARETBLINKING
|
||||
@@ -574,6 +575,7 @@ GPOCA
|
||||
gpp
|
||||
gpu
|
||||
gradians
|
||||
Gridcustomlayout
|
||||
GSM
|
||||
gtm
|
||||
guiddata
|
||||
|
||||
@@ -88,12 +88,9 @@ public:
|
||||
package::RegisterSparsePackage(path, packageUri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
FileLocksmithRuntimeRegistration::EnsureRegistered();
|
||||
FileLocksmithRuntimeRegistration::EnsureRegistered();
|
||||
#endif
|
||||
}
|
||||
|
||||
m_enabled = true;
|
||||
}
|
||||
@@ -101,13 +98,10 @@ public:
|
||||
virtual void disable() override
|
||||
{
|
||||
Logger::info(L"File Locksmith disabled");
|
||||
if (!package::IsWin11OrGreater())
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
FileLocksmithRuntimeRegistration::Unregister();
|
||||
Logger::info(L"File Locksmith context menu unregistered (Win10)");
|
||||
FileLocksmithRuntimeRegistration::Unregister();
|
||||
Logger::info(L"File Locksmith context menu unregistered (Win10)");
|
||||
#endif
|
||||
}
|
||||
m_enabled = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@@ -1560,5 +1561,98 @@ namespace MouseWithoutBorders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DisableEasyMouseWhenForegroundWindowIsFullscreenSetting()
|
||||
{
|
||||
return Setting.Values.DisableEasyMouseWhenForegroundWindowIsFullscreen;
|
||||
}
|
||||
|
||||
private static bool IsAppIgnoredByEasyMouseFullscreenCheck(IntPtr foregroundWindowHandle)
|
||||
{
|
||||
if (NativeMethods.GetWindowThreadProcessId(foregroundWindowHandle, out var processId) == 0)
|
||||
{
|
||||
Logger.LogDebug($"GetWindowThreadProcessId failed with error : {Marshal.GetLastWin32Error()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var processHandle = NativeMethods.OpenProcess(0x1000, false, processId);
|
||||
if (processHandle == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint maxPath = 260;
|
||||
var nameBuffer = new char[maxPath];
|
||||
if (!NativeMethods.QueryFullProcessImageName(
|
||||
processHandle, NativeMethods.QUERY_FULL_PROCESS_NAME_FLAGS.DEFAULT, nameBuffer, ref maxPath))
|
||||
{
|
||||
Logger.LogDebug($"QueryFullProcessImageName failed with error : {Marshal.GetLastWin32Error()}");
|
||||
NativeMethods.CloseHandle(processHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
NativeMethods.CloseHandle(processHandle);
|
||||
|
||||
var name = new string(nameBuffer, 0, (int)maxPath);
|
||||
|
||||
var excludedApps = Setting.Values.EasyMouseFullscreenSwitchBlockExcludedApps;
|
||||
|
||||
return excludedApps.Contains(Path.GetFileNameWithoutExtension(name), StringComparer.OrdinalIgnoreCase)
|
||||
|| excludedApps.Contains(Path.GetFileName(name), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
internal static bool IsEasyMouseBlockedByFullscreenWindow()
|
||||
{
|
||||
var shellHandle = NativeMethods.GetShellWindow();
|
||||
var desktopHandle = NativeMethods.GetDesktopWindow();
|
||||
var foregroundHandle = NativeMethods.GetForegroundWindow();
|
||||
|
||||
// If the foreground window is either the desktop or the Windows shell, we are not in fullscreen mode.
|
||||
if (foregroundHandle.Equals(shellHandle) || foregroundHandle.Equals(desktopHandle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NativeMethods.SHQueryUserNotificationState(out var userNotificationState) != 0)
|
||||
{
|
||||
Logger.LogDebug($"SHQueryUserNotificationState failed with error : {Marshal.GetLastWin32Error()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (userNotificationState)
|
||||
{
|
||||
// An application running in full screen mode, check if the foreground window is
|
||||
// listed as ignored in the settings.
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.BUSY:
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.RUNNING_D3D_FULL_SCREEN:
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.PRESENTATION_MODE:
|
||||
return !IsAppIgnoredByEasyMouseFullscreenCheck(foregroundHandle);
|
||||
|
||||
// No full screen app running.
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.NOT_PRESENT:
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.ACCEPTS_NOTIFICATIONS:
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.QUIET_TIME:
|
||||
// Cannot determine
|
||||
case NativeMethods.USER_NOTIFICATION_STATE.APP:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a machine switch triggered by EasyMouse would be allowed to proceed due to other settings.
|
||||
/// </summary>
|
||||
/// <returns>A boolean that tells us if the switch isn't blocked by any other settings</returns>
|
||||
internal static bool IsEasyMouseSwitchAllowed()
|
||||
{
|
||||
// Never prevent a switch if we are not moving out of the host machine.
|
||||
if (!DisableEasyMouseWhenForegroundWindowIsFullscreenSetting() || DesMachineID != MachineID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the switch is blocked by a full-screen window running in the foreground
|
||||
return !IsEasyMouseBlockedByFullscreenWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +122,16 @@ namespace MouseWithoutBorders.Class
|
||||
[DllImport("user32.dll", SetLastError = false)]
|
||||
internal static extern IntPtr GetDesktopWindow();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
internal static partial IntPtr GetShellWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern IntPtr GetWindowDC(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern int DrawText(IntPtr hDC, string lpString, int nCount, ref RECT lpRect, uint uFormat);
|
||||
|
||||
@@ -291,6 +298,17 @@ namespace MouseWithoutBorders.Class
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[LibraryImport("kernel32.dll",
|
||||
EntryPoint = "QueryFullProcessImageNameW",
|
||||
SetLastError = true,
|
||||
StringMarshalling = StringMarshalling.Utf16)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool QueryFullProcessImageName(
|
||||
IntPtr hProcess, QUERY_FULL_PROCESS_NAME_FLAGS dwFlags, [Out] char[] lpExeName, ref uint lpdwSize);
|
||||
|
||||
[LibraryImport("shell32.dll", SetLastError = true)]
|
||||
internal static partial int SHQueryUserNotificationState(out USER_NOTIFICATION_STATE state);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct POINT
|
||||
{
|
||||
@@ -333,11 +351,11 @@ namespace MouseWithoutBorders.Class
|
||||
|
||||
[DllImport("ntdll.dll")]
|
||||
internal static extern int NtQueryInformationProcess(
|
||||
IntPtr hProcess,
|
||||
int processInformationClass /* 0 */,
|
||||
ref PROCESS_BASIC_INFORMATION processBasicInformation,
|
||||
uint processInformationLength,
|
||||
out uint returnLength);
|
||||
IntPtr hProcess,
|
||||
int processInformationClass /* 0 */,
|
||||
ref PROCESS_BASIC_INFORMATION processBasicInformation,
|
||||
uint processInformationLength,
|
||||
out uint returnLength);
|
||||
#endif
|
||||
|
||||
#if USE_GetSecurityDescriptorSacl
|
||||
@@ -632,14 +650,14 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
internal int LowPart;
|
||||
internal int HighPart;
|
||||
}// end struct
|
||||
} // end struct
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct LUID_AND_ATTRIBUTES
|
||||
{
|
||||
internal LUID Luid;
|
||||
internal int Attributes;
|
||||
}// end struct
|
||||
} // end struct
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TOKEN_PRIVILEGES
|
||||
@@ -670,23 +688,23 @@ namespace MouseWithoutBorders.Class
|
||||
internal const int TOKEN_ADJUST_SESSIONID = 0x0100;
|
||||
|
||||
internal const int TOKEN_ALL_ACCESS_P = STANDARD_RIGHTS_REQUIRED |
|
||||
TOKEN_ASSIGN_PRIMARY |
|
||||
TOKEN_DUPLICATE |
|
||||
TOKEN_IMPERSONATE |
|
||||
TOKEN_QUERY |
|
||||
TOKEN_QUERY_SOURCE |
|
||||
TOKEN_ADJUST_PRIVILEGES |
|
||||
TOKEN_ADJUST_GROUPS |
|
||||
TOKEN_ADJUST_DEFAULT;
|
||||
TOKEN_ASSIGN_PRIMARY |
|
||||
TOKEN_DUPLICATE |
|
||||
TOKEN_IMPERSONATE |
|
||||
TOKEN_QUERY |
|
||||
TOKEN_QUERY_SOURCE |
|
||||
TOKEN_ADJUST_PRIVILEGES |
|
||||
TOKEN_ADJUST_GROUPS |
|
||||
TOKEN_ADJUST_DEFAULT;
|
||||
|
||||
internal const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
|
||||
|
||||
internal const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
|
||||
|
||||
internal const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
|
||||
TOKEN_ADJUST_PRIVILEGES |
|
||||
TOKEN_ADJUST_GROUPS |
|
||||
TOKEN_ADJUST_DEFAULT;
|
||||
TOKEN_ADJUST_PRIVILEGES |
|
||||
TOKEN_ADJUST_GROUPS |
|
||||
TOKEN_ADJUST_DEFAULT;
|
||||
|
||||
internal const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
|
||||
|
||||
@@ -940,6 +958,30 @@ namespace MouseWithoutBorders.Class
|
||||
NameDnsDomain = 12,
|
||||
}
|
||||
|
||||
internal enum MONITOR_FROM_WINDOW_FLAGS : uint
|
||||
{
|
||||
DEFAULT_TO_NULL = 0x00000000,
|
||||
DEFAULT_TO_PRIMARY = 0x00000001,
|
||||
DEFAULT_TO_NEAREST = 0x00000002,
|
||||
}
|
||||
|
||||
internal enum QUERY_FULL_PROCESS_NAME_FLAGS : uint
|
||||
{
|
||||
DEFAULT = 0x00000000,
|
||||
PROCESS_NAME_NATIVE = 0x00000001,
|
||||
}
|
||||
|
||||
internal enum USER_NOTIFICATION_STATE
|
||||
{
|
||||
NOT_PRESENT = 1,
|
||||
BUSY = 2,
|
||||
RUNNING_D3D_FULL_SCREEN = 3,
|
||||
PRESENTATION_MODE = 4,
|
||||
ACCEPTS_NOTIFICATIONS = 5,
|
||||
QUIET_TIME = 6,
|
||||
APP = 7,
|
||||
}
|
||||
|
||||
[DllImport("secur32.dll", CharSet = CharSet.Unicode)]
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
internal static extern bool GetUserNameEx(int nameFormat, StringBuilder userName, ref uint userNameSize);
|
||||
|
||||
@@ -414,6 +414,44 @@ namespace MouseWithoutBorders.Class
|
||||
}
|
||||
}
|
||||
|
||||
internal bool DisableEasyMouseWhenForegroundWindowIsFullscreen
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
return _properties.DisableEasyMouseWhenForegroundWindowIsFullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
_properties.DisableEasyMouseWhenForegroundWindowIsFullscreen = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal HashSet<string> EasyMouseFullscreenSwitchBlockExcludedApps
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
return _properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
_properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal string Enc(string st, bool dec, DataProtectionScope protectionScope)
|
||||
{
|
||||
if (st == null || st.Length < 1)
|
||||
|
||||
@@ -66,13 +66,17 @@ internal static class Event
|
||||
try
|
||||
{
|
||||
Common.PaintCount = 0;
|
||||
bool switchByMouseEnabled = IsSwitchingByMouseEnabled();
|
||||
|
||||
if (switchByMouseEnabled && Common.Sk != null && (Common.DesMachineID == Common.MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == Common.WM_MOUSEMOVE)
|
||||
// Check if easy mouse setting is enabled.
|
||||
bool isEasyMouseEnabled = IsSwitchingByMouseEnabled();
|
||||
|
||||
if (isEasyMouseEnabled && Common.Sk != null && (Common.DesMachineID == Common.MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == Common.WM_MOUSEMOVE)
|
||||
{
|
||||
Point p = MachineStuff.MoveToMyNeighbourIfNeeded(e.X, e.Y, MachineStuff.desMachineID);
|
||||
|
||||
if (!p.IsEmpty)
|
||||
// Check if easy mouse switches are disabled when an application is running in fullscreen mode,
|
||||
// if they are, check that there is no application running in fullscreen mode before switching.
|
||||
if (!p.IsEmpty && Common.IsEasyMouseSwitchAllowed())
|
||||
{
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
|
||||
@@ -165,7 +169,8 @@ internal static class Event
|
||||
string newDesMachineName = MachineStuff.NameFromID(newDesMachineID);
|
||||
|
||||
if (!Common.IsConnectedTo(newDesMachineID))
|
||||
{// Connection lost, cancel switching
|
||||
{
|
||||
// Connection lost, cancel switching
|
||||
Logger.LogDebug("No active connection found for " + newDesMachineName);
|
||||
|
||||
// ShowToolTip("No active connection found for [" + newDesMachineName + "]!", 500);
|
||||
|
||||
@@ -246,9 +246,17 @@ LRESULT WindowBorder::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexce
|
||||
case WM_ERASEBKGND:
|
||||
return TRUE;
|
||||
|
||||
// prevent from beeping if the border was clicked
|
||||
// Prevent from beeping if the border was clicked
|
||||
case WM_SETCURSOR:
|
||||
{
|
||||
HCURSOR hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
if (hCursor)
|
||||
{
|
||||
SetCursor(hCursor);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
@@ -181,15 +182,15 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
if (more is not null)
|
||||
{
|
||||
MoreCommands = more
|
||||
.Select(item =>
|
||||
.Select<IContextItem, IContextItemViewModel>(item =>
|
||||
{
|
||||
if (item is ICommandContextItem contextItem)
|
||||
{
|
||||
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
|
||||
return new CommandContextItemViewModel(contextItem, PageContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SeparatorViewModel() as IContextItemViewModel;
|
||||
return new SeparatorViewModel();
|
||||
}
|
||||
})
|
||||
.ToList();
|
||||
@@ -237,8 +238,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
FastInitializeProperties();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("error fast initializing CommandItemViewModel", ex);
|
||||
Command = new(null, PageContext);
|
||||
_itemTitle = "Error";
|
||||
Subtitle = "Item failed to load";
|
||||
@@ -257,9 +259,10 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
SlowInitializeProperties();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Initialized |= InitializedState.Error;
|
||||
Logger.LogError("error slow initializing CommandItemViewModel", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -272,8 +275,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
InitializeProperties();
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("error initializing CommandItemViewModel", ex);
|
||||
Command = new(null, PageContext);
|
||||
_itemTitle = "Error";
|
||||
Subtitle = "Item failed to load";
|
||||
@@ -342,15 +346,15 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
if (more is not null)
|
||||
{
|
||||
var newContextMenu = more
|
||||
.Select(item =>
|
||||
.Select<IContextItem, IContextItemViewModel>(item =>
|
||||
{
|
||||
if (item is ICommandContextItem contextItem)
|
||||
{
|
||||
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
|
||||
return new CommandContextItemViewModel(contextItem, PageContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SeparatorViewModel() as IContextItemViewModel;
|
||||
return new SeparatorViewModel();
|
||||
}
|
||||
})
|
||||
.ToList();
|
||||
|
||||
@@ -7,6 +7,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Core.ViewModels;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -162,11 +163,11 @@ public sealed partial class ListPage : Page,
|
||||
if (listViewPeer is not null && li is not null)
|
||||
{
|
||||
var notificationText = li.Title;
|
||||
listViewPeer.RaiseNotificationEvent(
|
||||
Microsoft.UI.Xaml.Automation.Peers.AutomationNotificationKind.Other,
|
||||
Microsoft.UI.Xaml.Automation.Peers.AutomationNotificationProcessing.MostRecent,
|
||||
notificationText,
|
||||
"CommandPaletteSelectedItemChanged");
|
||||
|
||||
UIHelper.AnnounceActionForAccessibility(
|
||||
ItemsList,
|
||||
notificationText,
|
||||
"CommandPaletteSelectedItemChanged");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/UIHelper.cs
Normal file
32
src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/UIHelper.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Helpers;
|
||||
|
||||
public static partial class UIHelper
|
||||
{
|
||||
static UIHelper()
|
||||
{
|
||||
}
|
||||
|
||||
public static void AnnounceActionForAccessibility(UIElement ue, string announcement, string activityID)
|
||||
{
|
||||
if (FrameworkElementAutomationPeer.FromElement(ue) is AutomationPeer peer)
|
||||
{
|
||||
peer.RaiseNotificationEvent(
|
||||
AutomationNotificationKind.ActionCompleted,
|
||||
AutomationNotificationProcessing.ImportantMostRecent,
|
||||
announcement,
|
||||
activityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,12 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- For debugging purposes, uncomment this block to enable AOT builds -->
|
||||
<!-- <PropertyGroup>
|
||||
<EnableCmdPalAOT>true</EnableCmdPalAOT>
|
||||
<CIBuild>true</CIBuild>
|
||||
</PropertyGroup> -->
|
||||
|
||||
<PropertyGroup Condition="'$(EnableCmdPalAOT)' == 'true'">
|
||||
<SelfContained>true</SelfContained>
|
||||
|
||||
@@ -25,17 +25,11 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- TO DO: Replace this with WinUI TitleBar once that ships. -->
|
||||
<Button
|
||||
x:Name="PaneToggleBtn"
|
||||
Width="48"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Click="PaneToggleBtn_Click"
|
||||
Style="{StaticResource PaneToggleButtonStyle}" />
|
||||
<StackPanel
|
||||
x:Name="AppTitleBar"
|
||||
Grid.Row="0"
|
||||
Height="48"
|
||||
Margin="16,0,0,0"
|
||||
Orientation="Horizontal">
|
||||
<Image
|
||||
Width="16"
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation.Peers;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using WinUIEx;
|
||||
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
|
||||
@@ -22,6 +23,9 @@ public sealed partial class SettingsWindow : WindowEx,
|
||||
{
|
||||
public ObservableCollection<Crumb> BreadCrumbs { get; } = [];
|
||||
|
||||
// Gets or sets optional action invoked after NavigationView is loaded.
|
||||
public Action NavigationViewLoaded { get; set; } = () => { };
|
||||
|
||||
public SettingsWindow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -35,10 +39,33 @@ public sealed partial class SettingsWindow : WindowEx,
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
}
|
||||
|
||||
// Handles NavigationView loaded event.
|
||||
// Sets up initial navigation and accessibility notifications.
|
||||
private void NavView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Delay necessary to ensure NavigationView visual state can match navigation
|
||||
Task.Delay(500).ContinueWith(_ => this.NavigationViewLoaded?.Invoke(), TaskScheduler.FromCurrentSynchronizationContext());
|
||||
|
||||
NavView.SelectedItem = NavView.MenuItems[0];
|
||||
Navigate("General");
|
||||
|
||||
if (sender is NavigationView navigationView)
|
||||
{
|
||||
// Register for pane open/close changes to announce to screen readers
|
||||
navigationView.RegisterPropertyChangedCallback(NavigationView.IsPaneOpenProperty, AnnounceNavigationPaneStateChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// Announces navigation pane open/close state to screen readers for accessibility.
|
||||
private void AnnounceNavigationPaneStateChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
if (sender is NavigationView navigationView)
|
||||
{
|
||||
UIHelper.AnnounceActionForAccessibility(
|
||||
ue: (UIElement)sender,
|
||||
(sender as NavigationView)?.IsPaneOpen == true ? RS_.GetString("NavigationPaneOpened") : RS_.GetString("NavigationPaneClosed"),
|
||||
"NavigationViewPaneIsOpenChangeNotificationId");
|
||||
}
|
||||
}
|
||||
|
||||
private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
||||
@@ -109,24 +136,15 @@ public sealed partial class SettingsWindow : WindowEx,
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
}
|
||||
|
||||
private void PaneToggleBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavView.IsPaneOpen = !NavView.IsPaneOpen;
|
||||
}
|
||||
|
||||
private void NavView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
||||
{
|
||||
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
|
||||
{
|
||||
PaneToggleBtn.Visibility = Visibility.Visible;
|
||||
NavView.IsPaneToggleButtonVisible = false;
|
||||
AppTitleBar.Margin = new Thickness(48, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PaneToggleBtn.Visibility = Visibility.Collapsed;
|
||||
NavView.IsPaneToggleButtonVisible = true;
|
||||
AppTitleBar.Margin = new Thickness(16, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,4 +435,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="StatusMessagesButton.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Show status messages</value>
|
||||
</data>
|
||||
<data name="NavigationPaneClosed" xml:space="preserve">
|
||||
<value>Navigation pane closed</value>
|
||||
</data>
|
||||
<data name="NavigationPageOpened" xml:space="preserve">
|
||||
<value>Navigation page opened</value>
|
||||
</data>
|
||||
</root>
|
||||
22
src/modules/cmdpal/ext/Common.ExtDependencies.props
Normal file
22
src/modules/cmdpal/ext/Common.ExtDependencies.props
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
|
||||
<!--
|
||||
Common external dependencies for all CmdPal extensions:
|
||||
- Microsoft.WindowsAppSDK
|
||||
- Microsoft.Web.WebView2
|
||||
- Microsoft.CommandPalette.Extensions.Toolkit (via project reference)
|
||||
|
||||
We need the WASDK reference because without it, our image assets won't get
|
||||
placed into our final package correctly.
|
||||
|
||||
-->
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,13 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Apps</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
|
||||
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
|
||||
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>Microsoft.CmdPal.Ext.Apps.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -20,8 +24,8 @@
|
||||
<PackageReference Include="WyHash" />
|
||||
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Calc</RootNamespace>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
@@ -17,7 +18,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\CalculatorEngineCommon\CalculatorEngineCommon.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CsWinRTInputs Include="..\..\..\..\..\$(Platform)\$(Configuration)\CalculatorEngineCommon.winmd" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.ClipboardHistory</RootNamespace>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
@@ -10,10 +11,10 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
|
||||
<!-- String resources -->
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Indexer</RootNamespace>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
@@ -17,8 +18,7 @@
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Registry</RootNamespace>
|
||||
@@ -16,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Shell</RootNamespace>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.System</RootNamespace>
|
||||
@@ -8,9 +10,9 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.TimeDate</RootNamespace>
|
||||
@@ -21,7 +22,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.WebSearch</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -11,7 +12,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.WinGet</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
@@ -39,7 +40,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -32,7 +32,16 @@ public partial class InstallPackageListItem : ListItem
|
||||
{
|
||||
_package = package;
|
||||
|
||||
var version = _package.DefaultInstallVersion ?? _package.InstalledVersion;
|
||||
PackageVersionInfo? version = null;
|
||||
try
|
||||
{
|
||||
version = _package.DefaultInstallVersion ?? _package.InstalledVersion;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError("Could not get package version", e);
|
||||
}
|
||||
|
||||
var versionTagText = "Unknown";
|
||||
if (version is not null)
|
||||
{
|
||||
@@ -57,20 +66,27 @@ public partial class InstallPackageListItem : ListItem
|
||||
}
|
||||
catch (COMException ex)
|
||||
{
|
||||
Logger.LogWarning($"{ex.ErrorCode}");
|
||||
Logger.LogWarning($"GetCatalogPackageMetadata error {ex.ErrorCode}");
|
||||
}
|
||||
|
||||
if (metadata is not null)
|
||||
{
|
||||
if (metadata.Tags.Where(t => t.Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase)).Any())
|
||||
for (var i = 0; i < metadata.Tags.Count; i++)
|
||||
{
|
||||
if (_installCommand is not null)
|
||||
if (metadata.Tags[i].Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_installCommand.SkipDependencies = true;
|
||||
if (_installCommand is not null)
|
||||
{
|
||||
_installCommand.SkipDependencies = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var description = string.IsNullOrEmpty(metadata.Description) ? metadata.ShortDescription : metadata.Description;
|
||||
var description = string.IsNullOrEmpty(metadata.Description) ?
|
||||
metadata.ShortDescription :
|
||||
metadata.Description;
|
||||
var detailsBody = $"""
|
||||
|
||||
{description}
|
||||
@@ -116,9 +132,11 @@ public partial class InstallPackageListItem : ListItem
|
||||
// These can be l o n g
|
||||
{ Properties.Resources.winget_release_notes, (metadata.ReleaseNotes, string.Empty) },
|
||||
};
|
||||
var docs = metadata.Documentations.ToArray();
|
||||
foreach (var item in docs)
|
||||
var docs = metadata.Documentations;
|
||||
var count = docs.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = docs[i];
|
||||
simpleData.Add(item.DocumentLabel, (string.Empty, item.DocumentUrl));
|
||||
}
|
||||
|
||||
@@ -141,7 +159,7 @@ public partial class InstallPackageListItem : ListItem
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata.Tags.Any())
|
||||
if (metadata.Tags.Count > 0)
|
||||
{
|
||||
DetailsElement pair = new()
|
||||
{
|
||||
@@ -169,6 +187,7 @@ public partial class InstallPackageListItem : ListItem
|
||||
{
|
||||
// Handle other exceptions
|
||||
ExtensionHost.LogMessage($"[WinGet] UpdatedInstalledStatus throw exception: {ex.Message}");
|
||||
Logger.LogError($"[WinGet] UpdatedInstalledStatus throw exception", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -233,10 +252,10 @@ public partial class InstallPackageListItem : ListItem
|
||||
Stopwatch s = new();
|
||||
Logger.LogDebug($"Starting RefreshPackageCatalogAsync");
|
||||
s.Start();
|
||||
var refs = WinGetStatics.AvailableCatalogs.ToArray();
|
||||
|
||||
foreach (var catalog in refs)
|
||||
var refs = WinGetStatics.AvailableCatalogs;
|
||||
for (var i = 0; i < refs.Count; i++)
|
||||
{
|
||||
var catalog = refs[i];
|
||||
var operation = catalog.RefreshPackageCatalogAsync();
|
||||
operation.Wait();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private Task<IEnumerable<CatalogPackage>>? _currentSearchTask;
|
||||
|
||||
private IEnumerable<CatalogPackage>? _results;
|
||||
private List<CatalogPackage>? _results;
|
||||
|
||||
public static string ExtensionsTag => "windows-commandpalette-extension";
|
||||
|
||||
@@ -48,7 +48,6 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
IListItem[] items = [];
|
||||
lock (_resultsLock)
|
||||
{
|
||||
// emptySearchForTag ===
|
||||
@@ -61,12 +60,28 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
{
|
||||
IsLoading = true;
|
||||
DoUpdateSearchText(string.Empty);
|
||||
return items;
|
||||
return [];
|
||||
}
|
||||
|
||||
if (_results is not null && _results.Any())
|
||||
if (_results is not null && _results.Count != 0)
|
||||
{
|
||||
ListItem[] results = _results.Select(PackageToListItem).ToArray();
|
||||
var count = _results.Count;
|
||||
var results = new ListItem[count];
|
||||
var next = 0;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var li = PackageToListItem(_results[i]);
|
||||
results[next] = li;
|
||||
next++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("error converting result to listitem", ex);
|
||||
}
|
||||
}
|
||||
|
||||
IsLoading = false;
|
||||
return results;
|
||||
}
|
||||
@@ -82,7 +97,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
|
||||
IsLoading = false;
|
||||
|
||||
return items;
|
||||
return [];
|
||||
}
|
||||
|
||||
private static ListItem PackageToListItem(CatalogPackage p) => new InstallPackageListItem(p);
|
||||
@@ -108,7 +123,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
CancellationToken cancellationToken = _cancellationTokenSource.Token;
|
||||
var cancellationToken = _cancellationTokenSource.Token;
|
||||
|
||||
IsLoading = true;
|
||||
|
||||
@@ -139,7 +154,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<CatalogPackage> results = await searchTask;
|
||||
var results = await searchTask;
|
||||
|
||||
// Ensure this is still the latest task
|
||||
if (_currentSearchTask == searchTask)
|
||||
@@ -156,7 +171,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle other exceptions
|
||||
Logger.LogError(ex.Message);
|
||||
Logger.LogError("Unexpected error while processing results", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,10 +180,10 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
Logger.LogDebug($"Completed search for '{query}'");
|
||||
lock (_resultsLock)
|
||||
{
|
||||
this._results = results;
|
||||
this._results = results.ToList();
|
||||
}
|
||||
|
||||
RaiseItemsChanged(this._results.Count());
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<CatalogPackage>> DoSearchAsync(string query, CancellationToken ct)
|
||||
@@ -190,12 +205,12 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
HashSet<CatalogPackage> results = new(new PackageIdCompare());
|
||||
|
||||
// Default selector: this is the way to do a `winget search <query>`
|
||||
PackageMatchFilter selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||
var selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||
selector.Field = Microsoft.Management.Deployment.PackageMatchField.CatalogDefault;
|
||||
selector.Value = query;
|
||||
selector.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
||||
|
||||
FindPackagesOptions opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
|
||||
var opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
|
||||
opts.Selectors.Add(selector);
|
||||
|
||||
// testing
|
||||
@@ -204,7 +219,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
// Selectors is "OR", Filters is "AND"
|
||||
if (HasTag)
|
||||
{
|
||||
PackageMatchFilter tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||
var tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
|
||||
tagFilter.Field = Microsoft.Management.Deployment.PackageMatchField.Tag;
|
||||
tagFilter.Value = _tag;
|
||||
tagFilter.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
|
||||
@@ -215,11 +230,11 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
// Clean up here, then...
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
Lazy<Task<PackageCatalog>> catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
|
||||
var catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
|
||||
|
||||
// Both these catalogs should have been instantiated by the
|
||||
// WinGetStatics static ctor when we were created.
|
||||
PackageCatalog catalog = await catalogTask.Value;
|
||||
var catalog = await catalogTask.Value;
|
||||
|
||||
if (catalog is null)
|
||||
{
|
||||
@@ -235,8 +250,8 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
|
||||
// BODGY, re: microsoft/winget-cli#5151
|
||||
// FindPackagesAsync isn't actually async.
|
||||
Task<FindPackagesResult> internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
|
||||
FindPackagesResult searchResults = await internalSearchTask;
|
||||
var internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
|
||||
var searchResults = await internalSearchTask;
|
||||
|
||||
// TODO more error handling like this:
|
||||
if (searchResults.Status != FindPackagesResultStatus.Ok)
|
||||
@@ -247,13 +262,17 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
|
||||
}
|
||||
|
||||
Logger.LogDebug($" got results for ({query})", memberName: nameof(DoSearchAsync));
|
||||
foreach (Management.Deployment.MatchResult? match in searchResults.Matches.ToArray())
|
||||
|
||||
// FYI Using .ToArray or any other kind of enumerable loop
|
||||
// on arrays returned by the winget API are NOT trim safe
|
||||
var count = searchResults.Matches.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var match = searchResults.Matches[i];
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
// Print the packages
|
||||
CatalogPackage package = match.CatalogPackage;
|
||||
|
||||
var package = match.CatalogPackage;
|
||||
results.Add(package);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -14,8 +15,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.WindowsServices</RootNamespace>
|
||||
@@ -15,10 +16,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" />
|
||||
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.WindowsSettings</RootNamespace>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
@@ -18,10 +19,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" />
|
||||
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
<Import Project="..\Common.ExtDependencies.props" />
|
||||
<Import Project="..\..\Microsoft.CmdPal.UI\CmdPal.pre.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -16,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<!-- CmdPal Toolkit reference now included via Common.ExtDependencies.props -->
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -283,8 +283,7 @@ namespace UITests_FancyZones
|
||||
|
||||
// Set Hotkey
|
||||
this.AttachFancyZonesEditor();
|
||||
var layout = "Grid custom layout";
|
||||
Session.Find<Element>(layout).Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
|
||||
Session.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard)).Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
|
||||
const string key = "0";
|
||||
var hotkeyComboBox = Session.Find<Element>(By.AccessibilityId(AccessibilityId.HotkeyComboBox));
|
||||
Assert.IsNotNull(hotkeyComboBox);
|
||||
@@ -299,7 +298,7 @@ namespace UITests_FancyZones
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num0);
|
||||
Task.Delay(3000).Wait();
|
||||
this.AttachFancyZonesEditor();
|
||||
var element = this.Find<Element>(layout);
|
||||
var element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Grid custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
@@ -307,7 +306,7 @@ namespace UITests_FancyZones
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num1);
|
||||
Task.Delay(3000).Wait();
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Grid-9");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.Grid9LayoutCard));
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Grid-9 is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
@@ -315,7 +314,7 @@ namespace UITests_FancyZones
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num2);
|
||||
Task.Delay(3000).Wait();
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Canvas custom layout");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.CanvasCustomLayoutCard));
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
@@ -424,21 +423,21 @@ namespace UITests_FancyZones
|
||||
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num0);
|
||||
this.AttachFancyZonesEditor();
|
||||
var element = this.Find<Element>("Grid custom layout");
|
||||
var element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
Assert.IsFalse(element.Selected, $"{element.Selected} Grid custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num1);
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Grid-9");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.Grid9LayoutCard));
|
||||
Assert.IsFalse(element.Selected, $"{element.Selected} Grid-9 is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
|
||||
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num2);
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Canvas custom layout");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.CanvasCustomLayoutCard));
|
||||
Assert.IsFalse(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.AttachPowertoySetting();
|
||||
@@ -453,7 +452,7 @@ namespace UITests_FancyZones
|
||||
this.OpenFancyZonesPanel();
|
||||
|
||||
this.AttachFancyZonesEditor();
|
||||
var element = this.Find<Element>("Grid custom layout");
|
||||
var element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
element.Click();
|
||||
this.CloseFancyZonesEditor();
|
||||
this.ExitScopeExe();
|
||||
@@ -463,8 +462,8 @@ namespace UITests_FancyZones
|
||||
this.RestartScopeExe();
|
||||
this.OpenFancyZonesPanel();
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Grid custom layout");
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Grid custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
|
||||
// close the virtual desktop
|
||||
@@ -483,7 +482,7 @@ namespace UITests_FancyZones
|
||||
this.OpenFancyZonesPanel();
|
||||
|
||||
this.AttachFancyZonesEditor();
|
||||
var element = this.Find<Element>("Grid custom layout");
|
||||
var element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
element.Click();
|
||||
this.CloseFancyZonesEditor();
|
||||
this.ExitScopeExe();
|
||||
@@ -493,7 +492,7 @@ namespace UITests_FancyZones
|
||||
this.RestartScopeExe();
|
||||
this.OpenFancyZonesPanel();
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Grid-9");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.Grid9LayoutCard));
|
||||
element.Click();
|
||||
this.CloseFancyZonesEditor();
|
||||
this.ExitScopeExe();
|
||||
@@ -502,7 +501,7 @@ namespace UITests_FancyZones
|
||||
this.RestartScopeExe();
|
||||
this.OpenFancyZonesPanel();
|
||||
this.AttachFancyZonesEditor();
|
||||
element = this.Find<Element>("Grid custom layout");
|
||||
element = this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard));
|
||||
Assert.IsTrue(element.Selected, $"{element.Selected} Grid custom layout is not visible");
|
||||
this.CloseFancyZonesEditor();
|
||||
this.ExitScopeExe();
|
||||
@@ -523,8 +522,8 @@ namespace UITests_FancyZones
|
||||
this.OpenFancyZonesPanel();
|
||||
|
||||
this.AttachFancyZonesEditor();
|
||||
this.Find<Element>("Grid custom layout").Click();
|
||||
this.Find<Element>("Grid custom layout").Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
|
||||
this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard)).Click();
|
||||
this.Find<Element>(By.AccessibilityId(AccessibilityId.GridCustomLayoutCard)).Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
|
||||
Session.Find<Button>(By.AccessibilityId(AccessibilityId.DeleteLayoutButton)).Click();
|
||||
Session.SendKeySequence(Key.Tab, Key.Enter);
|
||||
|
||||
@@ -619,7 +618,7 @@ namespace UITests_FancyZones
|
||||
private void AttachFancyZonesEditor()
|
||||
{
|
||||
Task.Delay(4000).Wait();
|
||||
this.Find<Button>("Launch layout editor").Click();
|
||||
this.Find<Button>(By.AccessibilityId(AccessibilityId.LaunchLayoutEditorButton)).Click();
|
||||
|
||||
Task.Delay(3000).Wait();
|
||||
this.Session.Attach(PowerToysModule.FancyZone);
|
||||
|
||||
@@ -41,9 +41,13 @@ namespace Microsoft.FancyZonesEditor.UnitTests.Utils
|
||||
public const string MainWindow = "MainWindow1";
|
||||
public const string Monitors = "Monitors";
|
||||
public const string NewLayoutButton = "NewLayoutButton";
|
||||
public const string LaunchLayoutEditorButton = "LaunchLayoutEditorButton";
|
||||
|
||||
// layout card
|
||||
public const string EditLayoutButton = "EditLayoutButton";
|
||||
public const string GridCustomLayoutCard = "GridcustomlayoutCard";
|
||||
public const string Grid9LayoutCard = "Grid-9Card";
|
||||
public const string CanvasCustomLayoutCard = "CanvascustomlayoutCard";
|
||||
|
||||
// edit layout window: common for template and custom layouts
|
||||
public const string DialogTitle = "EditLayoutDialogTitle";
|
||||
|
||||
@@ -71,12 +71,22 @@ namespace FancyZonesEditor.Models
|
||||
{
|
||||
_name = value;
|
||||
FirePropertyChanged(nameof(Name));
|
||||
FirePropertyChanged(nameof(AutomationId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _name;
|
||||
|
||||
// AutomationId - used for UI automation testing
|
||||
public virtual string AutomationId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name?.Replace(" ", string.Empty) + "Card";
|
||||
}
|
||||
}
|
||||
|
||||
public LayoutType Type { get; set; }
|
||||
|
||||
#pragma warning disable CA1720 // Identifier contains type name (Not worth the effort to change this now.)
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
<Setter Property="Background" Value="{DynamicResource LayoutItemBackgroundBrush}" />
|
||||
<Setter Property="IsSelected" Value="{Binding IsApplied, Mode=OneWay}" />
|
||||
<Setter Property="AutomationProperties.Name" Value="{Binding Name}" />
|
||||
<Setter Property="AutomationProperties.AutomationId" Value="{Binding AutomationId}" />
|
||||
<Setter Property="KeyboardNavigation.TabNavigation" Value="Local" />
|
||||
<!--<Setter Property="IsHoldingEnabled" Value="True" />-->
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
|
||||
@@ -112,12 +112,9 @@ public:
|
||||
package::RegisterSparsePackage(path, packageUri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
ImageResizerRuntimeRegistration::EnsureRegistered();
|
||||
ImageResizerRuntimeRegistration::EnsureRegistered();
|
||||
#endif
|
||||
}
|
||||
|
||||
Trace::EnableImageResizer(m_enabled);
|
||||
}
|
||||
@@ -127,13 +124,10 @@ public:
|
||||
{
|
||||
m_enabled = false;
|
||||
Trace::EnableImageResizer(m_enabled);
|
||||
if (!package::IsWin11OrGreater())
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
ImageResizerRuntimeRegistration::Unregister();
|
||||
Logger::info(L"ImageResizer context menu unregistered (Win10)");
|
||||
ImageResizerRuntimeRegistration::Unregister();
|
||||
Logger::info(L"ImageResizer context menu unregistered (Win10)");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Returns if the powertoys is enabled
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
namespace PowerAccent.Core.Models
|
||||
{
|
||||
public class UsageInfoData
|
||||
{
|
||||
public Dictionary<string, uint> CharacterUsageCounters { get; set; } = [];
|
||||
|
||||
public Dictionary<string, long> CharacterUsageTimestamp { get; set; } = [];
|
||||
}
|
||||
}
|
||||
@@ -117,8 +117,8 @@ public partial class PowerAccent : IDisposable
|
||||
if (_settingService.SortByUsageFrequency)
|
||||
{
|
||||
characters = characters.OrderByDescending(character => _usageInfo.GetUsageFrequency(character))
|
||||
.ThenByDescending(character => _usageInfo.GetLastUsageTimestamp(character)).
|
||||
ToArray<string>();
|
||||
.ThenByDescending(character => _usageInfo.GetLastUsageTimestamp(character))
|
||||
.ToArray<string>();
|
||||
}
|
||||
else if (!_usageInfo.Empty())
|
||||
{
|
||||
@@ -337,6 +337,14 @@ public partial class PowerAccent : IDisposable
|
||||
return _settingService.Position;
|
||||
}
|
||||
|
||||
public void SaveUsageInfo()
|
||||
{
|
||||
if (_settingService.SortByUsageFrequency)
|
||||
{
|
||||
_usageInfo.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_keyboardListener.UnInitHook();
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using PowerAccent.Core.Models;
|
||||
using PowerAccent.Core.Services;
|
||||
|
||||
namespace PowerAccent.Core.SerializationContext;
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(SettingsService))]
|
||||
[JsonSerializable(typeof(UsageInfoData))]
|
||||
public partial class SourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,12 +2,28 @@
|
||||
// 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.IO;
|
||||
using System.Text.Json;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerAccent.Core.Models;
|
||||
using PowerAccent.Core.SerializationContext;
|
||||
|
||||
namespace PowerAccent.Core.Tools
|
||||
{
|
||||
public class CharactersUsageInfo
|
||||
{
|
||||
private Dictionary<string, uint> _characterUsageCounters = new Dictionary<string, uint>();
|
||||
private Dictionary<string, long> _characterUsageTimestamp = new Dictionary<string, long>();
|
||||
private readonly string _filePath;
|
||||
private readonly Dictionary<string, uint> _characterUsageCounters;
|
||||
private readonly Dictionary<string, long> _characterUsageTimestamp;
|
||||
|
||||
public CharactersUsageInfo()
|
||||
{
|
||||
_filePath = new SettingsUtils().GetSettingsFilePath(PowerAccentSettings.ModuleName, "UsageInfo.json");
|
||||
var data = GetUsageInfoData();
|
||||
_characterUsageCounters = data.CharacterUsageCounters;
|
||||
_characterUsageTimestamp = data.CharacterUsageTimestamp;
|
||||
}
|
||||
|
||||
public bool Empty()
|
||||
{
|
||||
@@ -18,19 +34,18 @@ namespace PowerAccent.Core.Tools
|
||||
{
|
||||
_characterUsageCounters.Clear();
|
||||
_characterUsageTimestamp.Clear();
|
||||
Delete();
|
||||
}
|
||||
|
||||
public uint GetUsageFrequency(string character)
|
||||
{
|
||||
_characterUsageCounters.TryGetValue(character, out uint frequency);
|
||||
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public long GetLastUsageTimestamp(string character)
|
||||
{
|
||||
_characterUsageTimestamp.TryGetValue(character, out long timestamp);
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@@ -47,5 +62,58 @@ namespace PowerAccent.Core.Tools
|
||||
|
||||
_characterUsageTimestamp[character] = DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var data = new UsageInfoData
|
||||
{
|
||||
CharacterUsageCounters = _characterUsageCounters,
|
||||
CharacterUsageTimestamp = _characterUsageTimestamp,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(data, SourceGenerationContext.Default.UsageInfoData);
|
||||
File.WriteAllText(_filePath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to save usage file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_filePath))
|
||||
{
|
||||
File.Delete(_filePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to delete usage file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private UsageInfoData GetUsageInfoData()
|
||||
{
|
||||
if (!File.Exists(_filePath))
|
||||
{
|
||||
return new UsageInfoData();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(_filePath);
|
||||
return JsonSerializer.Deserialize(json, SourceGenerationContext.Default.UsageInfoData) ?? new UsageInfoData();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to read usage file", ex);
|
||||
return new UsageInfoData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
|
||||
|
||||
Application.Current.MainWindow.ShowActivated = false;
|
||||
Application.Current.MainWindow.Topmost = true;
|
||||
}
|
||||
|
||||
protected override void OnSourceInitialized(EventArgs e)
|
||||
@@ -64,6 +63,9 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
|
||||
private void PowerAccent_OnChangeDisplay(bool isActive, string[] chars)
|
||||
{
|
||||
// Topmost is conditionally set here to address hybrid graphics issues on laptops.
|
||||
this.Topmost = isActive;
|
||||
|
||||
CharacterNameVisibility = _powerAccent.ShowUnicodeDescription ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
if (isActive)
|
||||
@@ -96,6 +98,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_powerAccent.SaveUsageInfo();
|
||||
_powerAccent.Dispose();
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
@@ -202,12 +202,9 @@ public:
|
||||
package::RegisterSparsePackage(path, packageUri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
PowerRenameRuntimeRegistration::EnsureRegistered();
|
||||
PowerRenameRuntimeRegistration::EnsureRegistered();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the powertoy
|
||||
@@ -215,13 +212,10 @@ public:
|
||||
{
|
||||
m_enabled = false;
|
||||
Logger::info(L"PowerRename disabled");
|
||||
if (!package::IsWin11OrGreater())
|
||||
{
|
||||
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
|
||||
PowerRenameRuntimeRegistration::Unregister();
|
||||
Logger::info(L"PowerRename context menu unregistered (Win10)");
|
||||
PowerRenameRuntimeRegistration::Unregister();
|
||||
Logger::info(L"PowerRename context menu unregistered (Win10)");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Returns if the powertoy is enabled
|
||||
|
||||
@@ -92,6 +92,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
public IntProperty EasyMouse { get; set; }
|
||||
|
||||
[JsonConverter(typeof(BoolPropertyJsonConverter))]
|
||||
public bool DisableEasyMouseWhenForegroundWindowIsFullscreen { get; set; }
|
||||
|
||||
// Apps that are to be excluded when using DisableEasyMouseWhenForegroundWindowIsFullscreen
|
||||
// meaning that it is possible to switch screen when these apps are running in fullscreen.
|
||||
[CmdConfigureIgnore]
|
||||
public GenericProperty<HashSet<string>> EasyMouseFullscreenSwitchBlockExcludedApps { get; set; }
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public IntProperty MachineID { get; set; }
|
||||
|
||||
@@ -173,6 +181,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ShowOriginalUI = false;
|
||||
UseService = false;
|
||||
|
||||
DisableEasyMouseWhenForegroundWindowIsFullscreen = true;
|
||||
EasyMouseFullscreenSwitchBlockExcludedApps = new GenericProperty<HashSet<string>>(new HashSet<string>(StringComparer.OrdinalIgnoreCase));
|
||||
|
||||
HotKeySwitchMachine = new IntProperty(0x70); // VK.F1
|
||||
ToggleEasyMouseShortcut = DefaultHotKeyToggleEasyMouse;
|
||||
LockMachineShortcut = DefaultHotKeyLockMachine;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="FancyZones_LaunchEditorButtonControl"
|
||||
ActionIcon="{ui:FontIcon Glyph=}"
|
||||
AutomationProperties.AutomationId="LaunchLayoutEditorButton"
|
||||
Command="{x:Bind ViewModel.LaunchEditorEventHandler}"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsClickEnabled="True" />
|
||||
|
||||
@@ -267,7 +267,8 @@
|
||||
<ToggleSwitch x:Uid="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages_ToggleSwitch" IsOn="{x:Bind ViewModel.ShowClipboardAndNetworkStatusMessages, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeyboardShortcuts_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseWithoutBorders_EasyMouseSettings_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard x:Uid="MouseWithoutBorders_EasyMouseOption" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind Path=ViewModel.EasyMouseOptionIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Disabled" />
|
||||
@@ -276,7 +277,46 @@
|
||||
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Shift" />
|
||||
</ComboBox>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.EasyMouseEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_ToggleSwitch" IsOn="{x:Bind ViewModel.DisableEasyMouseWhenForegroundWindowIsFullscreen, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<InfoBar
|
||||
x:Uid="MouseWithoutBorders_CanOnlyStopEasyMouseIfMovingFromHostMachine"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Severity="Informational" />
|
||||
|
||||
<tkcontrols:SettingsExpander
|
||||
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsEasyMouseBlockingOnFullscreenEnabled, Mode=OneWay}"
|
||||
IsExpanded="False">
|
||||
<tkcontrols:SettingsExpander.Items>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_ExcludedApps"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ContentAlignment="Vertical">
|
||||
<TextBox
|
||||
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_TextBoxControl"
|
||||
MinWidth="240"
|
||||
MinHeight="160"
|
||||
AcceptsReturn="True"
|
||||
IsSpellCheckEnabled="False"
|
||||
ScrollViewer.IsVerticalRailEnabled="True"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||
ScrollViewer.VerticalScrollMode="Enabled"
|
||||
Text="{x:Bind ViewModel.EasyMouseFullscreenSwitchBlockExcludedApps, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextWrapping="Wrap" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeyboardShortcuts_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard x:Uid="MouseWithoutBorders_ToggleEasyMouseShortcut" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<controls:ShortcutControl
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
|
||||
@@ -398,6 +398,9 @@
|
||||
<data name="MouseWithoutBorders_AdvancedSettings_Group.Header" xml:space="preserve">
|
||||
<value>Advanced Settings</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_EasyMouseSettings_Group.Header" xml:space="preserve">
|
||||
<value>Easy Mouse</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_EasyMouseOption.Header" xml:space="preserve">
|
||||
<value>Easy Mouse: move between machines by moving the mouse pointer to the screen edges.</value>
|
||||
</data>
|
||||
@@ -419,6 +422,27 @@
|
||||
<value>Shift</value>
|
||||
<comment>This is the Shift keyboard key</comment>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen.Header" xml:space="preserve">
|
||||
<value>Disable Easy Mouse when an application is running in full screen.</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen.Description" xml:space="preserve">
|
||||
<value>Prevent Easy Mouse from moving to another machine when an application is in full-screen mode.</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_CanOnlyStopEasyMouseIfMovingFromHostMachine.Title" xml:space="preserve">
|
||||
<value>Disabling Easy Mouse in full-screen mode only affects the host PC. It won’t stop the mouse from moving away from remote machines.</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_TextBoxControl.PlaceholderText" xml:space="preserve">
|
||||
<value>msedge.exe
|
||||
firefox.exe
|
||||
opera.exe</value>
|
||||
<comment>Allow easy mouse when chrome is in fullscreen mode.</comment>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander.Header" xml:space="preserve">
|
||||
<value>Ignored fullscreen applications</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander.Description" xml:space="preserve">
|
||||
<value>Allow Easy Mouse to move between machines even if one of these applications is running in full screen, separate each executable with a new line.</value>
|
||||
</data>
|
||||
<data name="MouseWithoutBorders_LockMachinesShortcut.Header" xml:space="preserve">
|
||||
<value>Shortcut to lock all machines.</value>
|
||||
</data>
|
||||
@@ -485,7 +509,7 @@
|
||||
</data>
|
||||
<data name="Shell_Awake.Content" xml:space="preserve">
|
||||
<value>Awake</value>
|
||||
<comment>Product name: Navigation view item name for Awake</comment>
|
||||
<comment>{Locked}</comment>
|
||||
</data>
|
||||
<data name="Shell_PowerLauncher.Content" xml:space="preserve">
|
||||
<value>PowerToys Run</value>
|
||||
@@ -2280,6 +2304,7 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
</data>
|
||||
<data name="Awake.ModuleTitle" xml:space="preserve">
|
||||
<value>Awake</value>
|
||||
<comment>Awake is a product name, do not localize</comment>
|
||||
</data>
|
||||
<data name="Awake.ModuleDescription" xml:space="preserve">
|
||||
<value>A convenient way to keep your PC awake on-demand.</value>
|
||||
@@ -2333,12 +2358,15 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
</data>
|
||||
<data name="Oobe_Awake.Description" xml:space="preserve">
|
||||
<value>Awake is a Windows tool designed to keep your PC awake on-demand without having to manage its power settings. This behavior can be helpful when running time-consuming tasks while ensuring that your PC does not go to sleep or turn off its screens.</value>
|
||||
<comment>Awake is a product name, do not localize</comment>
|
||||
</data>
|
||||
<data name="Oobe_Awake_HowToUse.Text" xml:space="preserve">
|
||||
<value>Open **PowerToys Settings** and enable Awake</value>
|
||||
<comment>Awake is a product name, do not localize</comment>
|
||||
</data>
|
||||
<data name="Oobe_Awake_TipsAndTricks.Text" xml:space="preserve">
|
||||
<value>You can always change modes quickly by **right-clicking the Awake icon** in the system tray.</value>
|
||||
<comment>Awake is a product name, do not localize</comment>
|
||||
</data>
|
||||
<data name="General_FailedToDownloadTheNewVersion.Title" xml:space="preserve">
|
||||
<value>An error occurred trying to install this update:</value>
|
||||
@@ -2433,6 +2461,7 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
</data>
|
||||
<data name="Awake_ModeSettingsCard.Description" xml:space="preserve">
|
||||
<value>Manage the state of your device when Awake is active</value>
|
||||
<comment>Awake is a product name, do not localize</comment>
|
||||
</data>
|
||||
<data name="ExcludedApps.Header" xml:space="preserve">
|
||||
<value>Excluded apps</value>
|
||||
|
||||
@@ -904,6 +904,43 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private string _easyMouseIgnoredFullscreenAppsString;
|
||||
|
||||
public string EasyMouseFullscreenSwitchBlockExcludedApps
|
||||
{
|
||||
// Convert the list of excluded apps retrieved from the settings
|
||||
// to a single string that can be displayed in the bound textbox
|
||||
get
|
||||
{
|
||||
if (_easyMouseIgnoredFullscreenAppsString == null)
|
||||
{
|
||||
var excludedApps = Settings.Properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value;
|
||||
_easyMouseIgnoredFullscreenAppsString = excludedApps.Count == 0 ? string.Empty : string.Join('\r', excludedApps);
|
||||
}
|
||||
|
||||
return _easyMouseIgnoredFullscreenAppsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (EasyMouseFullscreenSwitchBlockExcludedApps == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_easyMouseIgnoredFullscreenAppsString = value;
|
||||
|
||||
var ignoredAppsSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (value != string.Empty)
|
||||
{
|
||||
ignoredAppsSet.UnionWith(value.Split('\r', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
|
||||
Settings.Properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value = ignoredAppsSet;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CardForName2IpSettingIsEnabled => _disableUserDefinedIpMappingRulesIsGPOConfigured == false;
|
||||
|
||||
public bool ShowPolicyConfiguredInfoForName2IPSetting => _disableUserDefinedIpMappingRulesIsGPOConfigured && IsEnabled;
|
||||
@@ -1005,6 +1042,30 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool EasyMouseEnabled => (EasyMouseOption)EasyMouseOptionIndex != EasyMouseOption.Disable;
|
||||
|
||||
public bool IsEasyMouseBlockingOnFullscreenEnabled =>
|
||||
EasyMouseEnabled && DisableEasyMouseWhenForegroundWindowIsFullscreen;
|
||||
|
||||
public bool DisableEasyMouseWhenForegroundWindowIsFullscreen
|
||||
{
|
||||
get
|
||||
{
|
||||
return Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public HotkeySettings ToggleEasyMouseShortcut
|
||||
{
|
||||
get => Settings.Properties.ToggleEasyMouseShortcut;
|
||||
|
||||
Reference in New Issue
Block a user