mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-19 01:30:03 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034759f949 | ||
|
|
934c3bbce9 | ||
|
|
ac548297c9 | ||
|
|
17a215d321 | ||
|
|
1b6a8c54ff | ||
|
|
67518dd754 | ||
|
|
18d1fd568c | ||
|
|
3eae35f356 | ||
|
|
e349779766 |
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1017,6 +1017,7 @@ MERGEPAINT
|
||||
Metacharacter
|
||||
metadatamatters
|
||||
Metadatas
|
||||
Metacharacter
|
||||
metafile
|
||||
mfc
|
||||
Mgmt
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
<Custom Action="UnRegisterCmdPalPackage" Before="RemoveFiles" Condition="Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
||||
<Custom Action="UninstallCommandNotFound" Before="RemoveFiles" Condition="Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
||||
<Custom Action="UpgradeCommandNotFound" After="InstallFiles" Condition="WIX_UPGRADE_DETECTED" />
|
||||
<Custom Action="UninstallPackageIdentityMSIX" Before="RemoveFiles" Condition="Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
||||
<Custom Action="UninstallPackageIdentityMSIX" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
||||
<Custom Action="UninstallServicesTask" After="InstallFinalize" Condition="Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
||||
<!-- TODO: Use to activate embedded MSIX -->
|
||||
<!--<Custom Action="UninstallEmbeddedMSIXTask" After="InstallFinalize">
|
||||
|
||||
@@ -367,6 +367,12 @@
|
||||
</RegistryKey>
|
||||
<File Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.BgcodePreviewHandler.resources.dll" />
|
||||
</Component>
|
||||
<Component Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER" Guid="$(var.CompGUIDPrefix)23">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\Microsoft.CmdPal.Ext.PowerToys.resources.dll" />
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?undef CompGUIDPrefix?>
|
||||
<?endforeach?>
|
||||
|
||||
@@ -250,7 +250,6 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
||||
|
||||
Logger::info(L"[LightSwitchService] Initialized at {:02d}:{:02d}.", st.wHour, st.wMinute);
|
||||
stateManager.SyncInitialThemeState();
|
||||
stateManager.OnTick(nowMinutes);
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Worker Loop
|
||||
@@ -281,7 +280,7 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
||||
GetLocalTime(&st);
|
||||
nowMinutes = st.wHour * 60 + st.wMinute;
|
||||
DetectAndHandleExternalThemeChange(stateManager);
|
||||
stateManager.OnTick(nowMinutes);
|
||||
stateManager.OnTick();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ void LightSwitchStateManager::OnSettingsChanged()
|
||||
}
|
||||
|
||||
// Called once per minute
|
||||
void LightSwitchStateManager::OnTick(int currentMinutes)
|
||||
void LightSwitchStateManager::OnTick()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_stateMutex);
|
||||
if (_state.lastAppliedMode != ScheduleMode::FollowNightLight)
|
||||
@@ -109,10 +109,14 @@ void LightSwitchStateManager::SyncInitialThemeState()
|
||||
std::lock_guard<std::mutex> lock(_stateMutex);
|
||||
_state.isSystemLightActive = GetCurrentSystemTheme();
|
||||
_state.isAppsLightActive = GetCurrentAppsTheme();
|
||||
_state.isNightLightActive = IsNightLightEnabled();
|
||||
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current system theme ({})",
|
||||
_state.isSystemLightActive ? L"light" : L"dark");
|
||||
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})",
|
||||
_state.isAppsLightActive ? L"light" : L"dark");
|
||||
|
||||
// This will ensure that the theme is applied according to current settings at startup
|
||||
EvaluateAndApplyIfNeeded();
|
||||
}
|
||||
|
||||
static std::pair<int, int> update_sun_times(auto& settings)
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
void OnSettingsChanged();
|
||||
|
||||
// Called every minute (from service worker tick).
|
||||
void OnTick(int currentMinutes);
|
||||
void OnTick();
|
||||
|
||||
// Called when manual override is toggled (via shortcut or system change).
|
||||
void OnManualOverride();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
IsMaximizable="False"
|
||||
IsMinimizable="False"
|
||||
IsResizable="False"
|
||||
IsShownInSwitchers="False"
|
||||
IsTitleBarVisible="False"
|
||||
mc:Ignorable="d">
|
||||
<winuiex:WindowEx.Backdrop>
|
||||
|
||||
@@ -52,12 +52,23 @@ namespace MeasureToolUI
|
||||
var presenter = _appWindow.Presenter as OverlappedPresenter;
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
this.SetIsAlwaysOnTop(true);
|
||||
this.SetIsShownInSwitchers(false);
|
||||
this.SetIsResizable(false);
|
||||
this.SetIsMinimizable(false);
|
||||
this.SetIsMaximizable(false);
|
||||
IsTitleBarVisible = false;
|
||||
|
||||
try
|
||||
{
|
||||
this.SetIsShownInSwitchers(false);
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
// WinUI will throw if explorer is not running, safely ignore
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
// Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear for Measure Tool. This code removes it.
|
||||
var windowStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||||
windowStyle = windowStyle & (~WS_CAPTION);
|
||||
|
||||
@@ -163,8 +163,22 @@ void CursorWrapCore::UpdateMonitorInfo()
|
||||
Logger::info(L"======= UPDATE MONITOR INFO END =======");
|
||||
}
|
||||
|
||||
POINT CursorWrapCore::HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode)
|
||||
POINT CursorWrapCore::HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode, bool disableOnSingleMonitor)
|
||||
{
|
||||
// Check if wrapping should be disabled on single monitor
|
||||
if (disableOnSingleMonitor && m_monitors.size() <= 1)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
static bool loggedOnce = false;
|
||||
if (!loggedOnce)
|
||||
{
|
||||
OutputDebugStringW(L"[CursorWrap] Single monitor detected - cursor wrapping disabled\n");
|
||||
loggedOnce = true;
|
||||
}
|
||||
#endif
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
// Check if wrapping should be disabled during drag
|
||||
if (disableWrapDuringDrag && (GetAsyncKeyState(VK_LBUTTON) & 0x8000))
|
||||
{
|
||||
|
||||
@@ -18,9 +18,11 @@ public:
|
||||
|
||||
// Handle mouse move with wrap mode filtering
|
||||
// wrapMode: 0=Both, 1=VerticalOnly, 2=HorizontalOnly
|
||||
POINT HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode);
|
||||
// disableOnSingleMonitor: if true, cursor wrapping is disabled when only one monitor is connected
|
||||
POINT HandleMouseMove(const POINT& currentPos, bool disableWrapDuringDrag, int wrapMode, bool disableOnSingleMonitor);
|
||||
|
||||
const std::vector<MonitorInfo>& GetMonitors() const { return m_monitors; }
|
||||
size_t GetMonitorCount() const { return m_monitors.size(); }
|
||||
const MonitorTopology& GetTopology() const { return m_topology; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace
|
||||
const wchar_t JSON_KEY_AUTO_ACTIVATE[] = L"auto_activate";
|
||||
const wchar_t JSON_KEY_DISABLE_WRAP_DURING_DRAG[] = L"disable_wrap_during_drag";
|
||||
const wchar_t JSON_KEY_WRAP_MODE[] = L"wrap_mode";
|
||||
const wchar_t JSON_KEY_DISABLE_ON_SINGLE_MONITOR[] = L"disable_cursor_wrap_on_single_monitor";
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
@@ -80,6 +81,7 @@ private:
|
||||
bool m_enabled = false;
|
||||
bool m_autoActivate = false;
|
||||
bool m_disableWrapDuringDrag = true; // Default to true to prevent wrap during drag
|
||||
bool m_disableOnSingleMonitor = false; // Default to false
|
||||
int m_wrapMode = 0; // 0=Both (default), 1=VerticalOnly, 2=HorizontalOnly
|
||||
|
||||
// Mouse hook
|
||||
@@ -196,6 +198,10 @@ public:
|
||||
// Start listening for external trigger event so we can invoke the same logic as the activation hotkey.
|
||||
m_triggerEventHandle = CreateEventW(nullptr, false, false, CommonSharedConstants::CURSOR_WRAP_TRIGGER_EVENT);
|
||||
m_terminateEventHandle = CreateEventW(nullptr, false, false, nullptr);
|
||||
if (m_triggerEventHandle)
|
||||
{
|
||||
ResetEvent(m_triggerEventHandle);
|
||||
}
|
||||
if (m_triggerEventHandle && m_terminateEventHandle)
|
||||
{
|
||||
m_listening = true;
|
||||
@@ -210,8 +216,16 @@ public:
|
||||
// Create message window for display change notifications
|
||||
RegisterForDisplayChanges();
|
||||
|
||||
StartMouseHook();
|
||||
Logger::info("CursorWrap enabled - mouse hook started");
|
||||
// Only start the mouse hook automatically if auto-activate is enabled
|
||||
if (m_autoActivate)
|
||||
{
|
||||
StartMouseHook();
|
||||
Logger::info("CursorWrap enabled - mouse hook started (auto-activate on)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info("CursorWrap enabled - waiting for activation hotkey (auto-activate off)");
|
||||
}
|
||||
|
||||
while (m_listening)
|
||||
{
|
||||
@@ -415,6 +429,21 @@ private:
|
||||
{
|
||||
Logger::warn("Failed to initialize CursorWrap wrap mode from settings. Will use default value (0=Both)");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Parse disable on single monitor
|
||||
auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
|
||||
if (propertiesObject.HasKey(JSON_KEY_DISABLE_ON_SINGLE_MONITOR))
|
||||
{
|
||||
auto disableOnSingleMonitorObject = propertiesObject.GetNamedObject(JSON_KEY_DISABLE_ON_SINGLE_MONITOR);
|
||||
m_disableOnSingleMonitor = disableOnSingleMonitorObject.GetNamedBoolean(JSON_KEY_VALUE);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize CursorWrap disable on single monitor from settings. Will use default value (false)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -646,7 +675,8 @@ private:
|
||||
POINT newPos = g_cursorWrapInstance->m_core.HandleMouseMove(
|
||||
currentPos,
|
||||
g_cursorWrapInstance->m_disableWrapDuringDrag,
|
||||
g_cursorWrapInstance->m_wrapMode);
|
||||
g_cursorWrapInstance->m_wrapMode,
|
||||
g_cursorWrapInstance->m_disableOnSingleMonitor);
|
||||
|
||||
if (newPos.x != currentPos.x || newPos.y != currentPos.y)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
// 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.Windows;
|
||||
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
@@ -11,9 +14,40 @@ namespace WorkspacesEditor
|
||||
/// </summary>
|
||||
public partial class OverlayWindow : Window
|
||||
{
|
||||
private int _targetX;
|
||||
private int _targetY;
|
||||
private int _targetWidth;
|
||||
private int _targetHeight;
|
||||
|
||||
public OverlayWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
SourceInitialized += OnWindowSourceInitialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target bounds for the overlay window.
|
||||
/// The window will be positioned using DPI-unaware context after initialization.
|
||||
/// </summary>
|
||||
public void SetTargetBounds(int x, int y, int width, int height)
|
||||
{
|
||||
_targetX = x;
|
||||
_targetY = y;
|
||||
_targetWidth = width;
|
||||
_targetHeight = height;
|
||||
|
||||
// Set initial WPF properties (will be corrected after HWND creation)
|
||||
Left = x;
|
||||
Top = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
private void OnWindowSourceInitialized(object sender, EventArgs e)
|
||||
{
|
||||
// Reposition window using DPI-unaware context to match the virtual coordinates.
|
||||
// This fixes overlay positioning on mixed-DPI multi-monitor setups.
|
||||
NativeMethods.SetWindowPositionDpiUnaware(this, _targetX, _targetY, _targetWidth, _targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
@@ -17,6 +19,39 @@ namespace WorkspacesEditor.Utils
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
|
||||
|
||||
private const uint SWP_NOZORDER = 0x0004;
|
||||
private const uint SWP_NOACTIVATE = 0x0010;
|
||||
|
||||
private static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = new IntPtr(-1);
|
||||
|
||||
/// <summary>
|
||||
/// Positions a WPF window using DPI-unaware context to match the virtual coordinates.
|
||||
/// This fixes overlay positioning on mixed-DPI multi-monitor setups.
|
||||
/// </summary>
|
||||
public static void SetWindowPositionDpiUnaware(Window window, int x, int y, int width, int height)
|
||||
{
|
||||
var helper = new WindowInteropHelper(window).Handle;
|
||||
if (helper != IntPtr.Zero)
|
||||
{
|
||||
// Temporarily switch to DPI-unaware context to position window.
|
||||
IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
|
||||
try
|
||||
{
|
||||
SetWindowPos(helper, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetThreadDpiAwarenessContext(oldContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("USER32.DLL")]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
|
||||
@@ -495,10 +495,10 @@ namespace WorkspacesEditor.ViewModels
|
||||
{
|
||||
var bounds = screen.Bounds;
|
||||
OverlayWindow overlayWindow = new OverlayWindow();
|
||||
overlayWindow.Top = bounds.Top;
|
||||
overlayWindow.Left = bounds.Left;
|
||||
overlayWindow.Width = bounds.Width;
|
||||
overlayWindow.Height = bounds.Height;
|
||||
|
||||
// Use DPI-unaware positioning to fix overlay on mixed-DPI multi-monitor setups
|
||||
overlayWindow.SetTargetBounds(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
|
||||
overlayWindow.ShowActivated = true;
|
||||
overlayWindow.Topmost = true;
|
||||
overlayWindow.Show();
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
<TextBlock
|
||||
x:Name="FormatNameTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- <PropertyGroup>
|
||||
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
</PropertyGroup> -->
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
IsMaximizable="False"
|
||||
IsMinimizable="False"
|
||||
IsResizable="False"
|
||||
IsShownInSwitchers="False"
|
||||
IsTitleBarVisible="False"
|
||||
mc:Ignorable="d">
|
||||
<winuiEx:WindowEx.Backdrop>
|
||||
|
||||
@@ -305,7 +305,17 @@ public sealed partial class MainWindow : WindowEx, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
_appWindow.IsShownInSwitchers = false;
|
||||
try
|
||||
{
|
||||
_appWindow.IsShownInSwitchers = false;
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
// WinUI Will throw if explorer is not running, safely ignore
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private bool CloakWindow()
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
public sealed class AdvancedPasteAdditionalActions
|
||||
{
|
||||
private AdvancedPasteAdditionalAction _imageToText = new();
|
||||
private AdvancedPastePasteAsFileAction _pasteAsFile = new();
|
||||
private AdvancedPasteTranscodeAction _transcode = new();
|
||||
|
||||
public static class PropertyNames
|
||||
{
|
||||
public const string ImageToText = "image-to-text";
|
||||
@@ -18,13 +22,25 @@ public sealed class AdvancedPasteAdditionalActions
|
||||
}
|
||||
|
||||
[JsonPropertyName(PropertyNames.ImageToText)]
|
||||
public AdvancedPasteAdditionalAction ImageToText { get; init; } = new();
|
||||
public AdvancedPasteAdditionalAction ImageToText
|
||||
{
|
||||
get => _imageToText;
|
||||
init => _imageToText = value ?? new();
|
||||
}
|
||||
|
||||
[JsonPropertyName(PropertyNames.PasteAsFile)]
|
||||
public AdvancedPastePasteAsFileAction PasteAsFile { get; init; } = new();
|
||||
public AdvancedPastePasteAsFileAction PasteAsFile
|
||||
{
|
||||
get => _pasteAsFile;
|
||||
init => _pasteAsFile = value ?? new();
|
||||
}
|
||||
|
||||
[JsonPropertyName(PropertyNames.Transcode)]
|
||||
public AdvancedPasteTranscodeAction Transcode { get; init; } = new();
|
||||
public AdvancedPasteTranscodeAction Transcode
|
||||
{
|
||||
get => _transcode;
|
||||
init => _transcode = value ?? new();
|
||||
}
|
||||
|
||||
public IEnumerable<IAdvancedPasteAction> GetAllActions()
|
||||
{
|
||||
|
||||
@@ -25,12 +25,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("wrap_mode")]
|
||||
public IntProperty WrapMode { get; set; }
|
||||
|
||||
[JsonPropertyName("disable_cursor_wrap_on_single_monitor")]
|
||||
public BoolProperty DisableCursorWrapOnSingleMonitor { get; set; }
|
||||
|
||||
public CursorWrapProperties()
|
||||
{
|
||||
ActivationShortcut = DefaultActivationShortcut;
|
||||
AutoActivate = new BoolProperty(false);
|
||||
DisableWrapDuringDrag = new BoolProperty(true);
|
||||
WrapMode = new IntProperty(0); // 0=Both (default), 1=VerticalOnly, 2=HorizontalOnly
|
||||
DisableCursorWrapOnSingleMonitor = new BoolProperty(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
settingsUpgraded = true;
|
||||
}
|
||||
|
||||
// Add DisableCursorWrapOnSingleMonitor property if it doesn't exist (for users upgrading from older versions)
|
||||
if (Properties.DisableCursorWrapOnSingleMonitor == null)
|
||||
{
|
||||
Properties.DisableCursorWrapOnSingleMonitor = new BoolProperty(false); // Default to false
|
||||
settingsUpgraded = true;
|
||||
}
|
||||
|
||||
return settingsUpgraded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
<ComboBoxItem x:Uid="MouseUtils_CursorWrap_WrapMode_HorizontalOnly" />
|
||||
</ComboBox>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard ContentAlignment="Left" IsEnabled="{x:Bind ViewModel.IsCursorWrapEnabled, Mode=OneWay}">
|
||||
<CheckBox x:Uid="MouseUtils_CursorWrap_DisableOnSingleMonitor" IsChecked="{x:Bind ViewModel.CursorWrapDisableOnSingleMonitor, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
@@ -2722,6 +2722,9 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
<data name="MouseUtils_CursorWrap_DisableWrapDuringDrag.Content" xml:space="preserve">
|
||||
<value>Disable wrapping while dragging</value>
|
||||
</data>
|
||||
<data name="MouseUtils_CursorWrap_DisableOnSingleMonitor.Content" xml:space="preserve">
|
||||
<value>Disable wrapping when using a single monitor</value>
|
||||
</data>
|
||||
<data name="MouseUtils_CursorWrap_AutoActivate.Header" xml:space="preserve">
|
||||
<value>Auto-activate on startup</value>
|
||||
</data>
|
||||
|
||||
@@ -116,6 +116,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// Null-safe access in case property wasn't upgraded yet - default to 0 (Both)
|
||||
_cursorWrapWrapMode = CursorWrapSettingsConfig.Properties.WrapMode?.Value ?? 0;
|
||||
|
||||
// Null-safe access in case property wasn't upgraded yet - default to false
|
||||
_cursorWrapDisableOnSingleMonitor = CursorWrapSettingsConfig.Properties.DisableCursorWrapOnSingleMonitor?.Value ?? false;
|
||||
|
||||
int isEnabled = 0;
|
||||
|
||||
Utilities.NativeMethods.SystemParametersInfo(Utilities.NativeMethods.SPI_GETCLIENTAREAANIMATION, 0, ref isEnabled, 0);
|
||||
@@ -1003,13 +1006,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
GeneralSettingsConfig.Enabled.CursorWrap = value;
|
||||
OnPropertyChanged(nameof(IsCursorWrapEnabled));
|
||||
|
||||
// Auto-enable the AutoActivate setting when CursorWrap is enabled
|
||||
// This ensures cursor wrapping is active immediately after enabling
|
||||
if (value && !_cursorWrapAutoActivate)
|
||||
{
|
||||
CursorWrapAutoActivate = true;
|
||||
}
|
||||
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
|
||||
@@ -1114,6 +1110,34 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool CursorWrapDisableOnSingleMonitor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cursorWrapDisableOnSingleMonitor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _cursorWrapDisableOnSingleMonitor)
|
||||
{
|
||||
_cursorWrapDisableOnSingleMonitor = value;
|
||||
|
||||
// Ensure the property exists before setting value
|
||||
if (CursorWrapSettingsConfig.Properties.DisableCursorWrapOnSingleMonitor == null)
|
||||
{
|
||||
CursorWrapSettingsConfig.Properties.DisableCursorWrapOnSingleMonitor = new BoolProperty(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CursorWrapSettingsConfig.Properties.DisableCursorWrapOnSingleMonitor.Value = value;
|
||||
}
|
||||
|
||||
NotifyCursorWrapPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyCursorWrapPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
@@ -1186,5 +1210,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private bool _cursorWrapAutoActivate;
|
||||
private bool _cursorWrapDisableWrapDuringDrag; // Will be initialized in constructor from settings
|
||||
private int _cursorWrapWrapMode; // 0=Both, 1=VerticalOnly, 2=HorizontalOnly
|
||||
private bool _cursorWrapDisableOnSingleMonitor; // Disable cursor wrap when only one monitor is connected
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user