mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 19:27:56 +01:00
[FindMyMouse] Add setting to activate by shaking mouse (#16244)
* [FindMyMouse]Initial shaking activation implementation * Add setting to change activation method * Update Mouse Snooping on settings change * fix spellchecker * Place activation method setting outside the expander * Address PR Comments
This commit is contained in:
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1126,6 +1126,7 @@ logon
|
||||
LOGPIXELSX
|
||||
LOn
|
||||
longdate
|
||||
LONGLONG
|
||||
lookbehind
|
||||
lowlevel
|
||||
LOWORD
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "FindMyMouse.h"
|
||||
#include "trace.h"
|
||||
#include "common/utils/game_mode.h"
|
||||
#include <vector>
|
||||
|
||||
#ifdef COMPOSITION
|
||||
namespace winrt
|
||||
@@ -43,6 +44,7 @@ protected:
|
||||
void BeforeMoveSonar() {}
|
||||
void AfterMoveSonar() {}
|
||||
void SetSonarVisibility(bool visible) = delete;
|
||||
void UpdateMouseSnooping();
|
||||
|
||||
protected:
|
||||
// Base class members you can access.
|
||||
@@ -57,7 +59,8 @@ protected:
|
||||
static const int MIN_DOUBLE_CLICK_TIME = 100;
|
||||
|
||||
bool m_destroyed = false;
|
||||
bool m_doNotActivateOnGameMode = true;
|
||||
FindMyMouseActivationMethod m_activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
bool m_doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
int m_sonarRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
|
||||
int m_sonarZoomFactor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
|
||||
DWORD m_fadeDuration = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
|
||||
@@ -66,13 +69,37 @@ protected:
|
||||
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };
|
||||
|
||||
private:
|
||||
|
||||
// Save the mouse movement that occurred in any direction.
|
||||
struct PointerRecentMovement
|
||||
{
|
||||
POINT diff;
|
||||
ULONGLONG tick;
|
||||
};
|
||||
std::vector<PointerRecentMovement> m_movementHistory;
|
||||
// Raw Input may give relative or absolute values. Need to take each case into account.
|
||||
bool m_seenAnAbsoluteMousePosition = false;
|
||||
POINT m_lastAbsolutePosition = { 0, 0 };
|
||||
// Don't consider movements started past these milliseconds to detect shaking.
|
||||
static constexpr LONG ShakeIntervalMs = 1000;
|
||||
// By which factor must travelled distance be than the diagonal of the rectangle containing the movements.
|
||||
static constexpr float ShakeFactor = 4.0f;
|
||||
|
||||
static inline byte GetSign(LONG const& num)
|
||||
{
|
||||
if (num > 0)
|
||||
return 1;
|
||||
if (num < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool IsEqual(POINT const& p1, POINT const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
static constexpr POINT ptNowhere = { -1, -1 };
|
||||
|
||||
static constexpr DWORD TIMER_ID_TRACK = 100;
|
||||
static constexpr DWORD IdlePeriod = 1000;
|
||||
|
||||
@@ -89,11 +116,11 @@ private:
|
||||
HWND m_hwndOwner;
|
||||
SonarState m_sonarState = SonarState::Idle;
|
||||
POINT m_lastKeyPos{};
|
||||
DWORD m_lastKeyTime{};
|
||||
ULONGLONG m_lastKeyTime{};
|
||||
|
||||
static constexpr DWORD NoSonar = 0;
|
||||
static constexpr DWORD SonarWaitingForMouseMove = 1;
|
||||
DWORD m_sonarStart = NoSonar;
|
||||
ULONGLONG m_sonarStart = NoSonar;
|
||||
bool m_isSnoopingMouse = false;
|
||||
|
||||
private:
|
||||
@@ -110,10 +137,10 @@ private:
|
||||
void OnSonarMouseInput(RAWINPUT const& input);
|
||||
void OnMouseTimer();
|
||||
|
||||
void DetectShake();
|
||||
|
||||
void StartSonar();
|
||||
void StopSonar();
|
||||
|
||||
void UpdateMouseSnooping();
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
@@ -189,7 +216,9 @@ LRESULT SuperSonar<D>::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
return OnSonarCreate() ? 0 : -1;
|
||||
if(!OnSonarCreate()) return -1;
|
||||
UpdateMouseSnooping();
|
||||
return 0;
|
||||
|
||||
case WM_DESTROY:
|
||||
OnSonarDestroy();
|
||||
@@ -257,13 +286,7 @@ void SuperSonar<D>::OnSonarInput(WPARAM flags, HRAWINPUT hInput)
|
||||
template<typename D>
|
||||
void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
{
|
||||
// Don't activate if game mode is on.
|
||||
if (m_doNotActivateOnGameMode && detect_game_mode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.data.keyboard.VKey != VK_CONTROL)
|
||||
if ( m_activationMethod != FindMyMouseActivationMethod::DoubleControlKey || input.data.keyboard.VKey != VK_CONTROL)
|
||||
{
|
||||
StopSonar();
|
||||
return;
|
||||
@@ -293,7 +316,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
if (pressed)
|
||||
{
|
||||
m_sonarState = SonarState::ControlDown1;
|
||||
m_lastKeyTime = GetTickCount();
|
||||
m_lastKeyTime = GetTickCount64();
|
||||
m_lastKeyPos = {};
|
||||
GetCursorPos(&m_lastKeyPos);
|
||||
UpdateMouseSnooping();
|
||||
@@ -310,7 +333,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
case SonarState::ControlUp1:
|
||||
if (pressed)
|
||||
{
|
||||
auto now = GetTickCount();
|
||||
auto now = GetTickCount64();
|
||||
auto doubleClickInterval = now - m_lastKeyTime;
|
||||
POINT ptCursor{};
|
||||
auto doubleClickTimeSetting = GetDoubleClickTime();
|
||||
@@ -325,7 +348,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
else
|
||||
{
|
||||
m_sonarState = SonarState::ControlDown1;
|
||||
m_lastKeyTime = GetTickCount();
|
||||
m_lastKeyTime = GetTickCount64();
|
||||
m_lastKeyPos = {};
|
||||
GetCursorPos(&m_lastKeyPos);
|
||||
UpdateMouseSnooping();
|
||||
@@ -351,9 +374,92 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
}
|
||||
}
|
||||
|
||||
// Shaking detection algorithm is: Has distance travelled been much greater than the diagonal of the rectangle containing the movement?
|
||||
template<typename D>
|
||||
void SuperSonar<D>::DetectShake()
|
||||
{
|
||||
ULONGLONG shakeStartTick = GetTickCount64() - ShakeIntervalMs;
|
||||
|
||||
// Prune the story of movements for those movements that started too long ago.
|
||||
std::erase_if(m_movementHistory, [shakeStartTick](const PointerRecentMovement& movement) { return movement.tick < shakeStartTick; });
|
||||
|
||||
|
||||
double distanceTravelled = 0;
|
||||
LONGLONG currentX=0, minX=0, maxX=0;
|
||||
LONGLONG currentY=0, minY=0, maxY=0;
|
||||
|
||||
for (const PointerRecentMovement& movement : m_movementHistory)
|
||||
{
|
||||
currentX += movement.diff.x;
|
||||
currentY += movement.diff.y;
|
||||
distanceTravelled += sqrt((double)movement.diff.x * movement.diff.x + (double)movement.diff.y * movement.diff.y); // Pythagorean theorem
|
||||
minX = min(currentX, minX);
|
||||
maxX = max(currentX, maxX);
|
||||
minY = min(currentY, minY);
|
||||
maxY = max(currentY, maxY);
|
||||
}
|
||||
|
||||
// Size of the rectangle the pointer moved in.
|
||||
double rectangleWidth = (double)maxX - minX;
|
||||
double rectangleHeight = (double)maxY - minY;
|
||||
|
||||
double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight);
|
||||
if (diagonal > 0 && distanceTravelled / diagonal > ShakeFactor)
|
||||
{
|
||||
m_movementHistory.clear();
|
||||
StartSonar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
{
|
||||
if (m_activationMethod == FindMyMouseActivationMethod::ShakeMouse)
|
||||
{
|
||||
LONG relativeX = 0;
|
||||
LONG relativeY = 0;
|
||||
if ((input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE)
|
||||
{
|
||||
// Getting absolute mouse coordinates. Likely inside a VM / RDP session.
|
||||
if (m_seenAnAbsoluteMousePosition)
|
||||
{
|
||||
relativeX = input.data.mouse.lLastX - m_lastAbsolutePosition.x;
|
||||
relativeY = input.data.mouse.lLastY - m_lastAbsolutePosition.y;
|
||||
m_lastAbsolutePosition.x = input.data.mouse.lLastX;
|
||||
m_lastAbsolutePosition.y = input.data.mouse.lLastY;
|
||||
}
|
||||
m_seenAnAbsoluteMousePosition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeX = input.data.mouse.lLastX;
|
||||
relativeY = input.data.mouse.lLastY;
|
||||
}
|
||||
if (m_movementHistory.size() > 0)
|
||||
{
|
||||
PointerRecentMovement& lastMovement = m_movementHistory.back();
|
||||
// If the pointer is still moving in the same direction, just add to that movement instead of adding a new movement.
|
||||
// This helps in keeping the list of movements smaller even in cases where a high number of messages is sent.
|
||||
if (GetSign(lastMovement.diff.x) == GetSign(relativeX) && GetSign(lastMovement.diff.y) == GetSign(relativeY))
|
||||
{
|
||||
lastMovement.diff.x += relativeX;
|
||||
lastMovement.diff.y += relativeY;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_movementHistory.push_back({ .diff = { .x=relativeX, .y=relativeY }, .tick = GetTickCount64() });
|
||||
// Mouse movement changed directions. Take the opportunity do detect shake.
|
||||
DetectShake();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_movementHistory.push_back({ .diff = { .x = relativeX, .y = relativeY }, .tick = GetTickCount64() });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (input.data.mouse.usButtonFlags)
|
||||
{
|
||||
StopSonar();
|
||||
@@ -367,6 +473,12 @@ void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
template<typename D>
|
||||
void SuperSonar<D>::StartSonar()
|
||||
{
|
||||
// Don't activate if game mode is on.
|
||||
if (m_doNotActivateOnGameMode && detect_game_mode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info("Focusing the sonar on the mouse cursor.");
|
||||
Trace::MousePointerFocused();
|
||||
// Cover the entire virtual screen.
|
||||
@@ -393,7 +505,7 @@ void SuperSonar<D>::StopSonar()
|
||||
template<typename D>
|
||||
void SuperSonar<D>::OnMouseTimer()
|
||||
{
|
||||
auto now = GetTickCount();
|
||||
auto now = GetTickCount64();
|
||||
|
||||
// If mouse has moved, then reset the sonar timer.
|
||||
POINT ptCursor{};
|
||||
@@ -433,7 +545,7 @@ void SuperSonar<D>::OnMouseTimer()
|
||||
template<typename D>
|
||||
void SuperSonar<D>::UpdateMouseSnooping()
|
||||
{
|
||||
bool wantSnoopingMouse = m_sonarStart != NoSonar || m_sonarState != SonarState::Idle;
|
||||
bool wantSnoopingMouse = m_sonarStart != NoSonar || m_sonarState != SonarState::Idle || m_activationMethod == FindMyMouseActivationMethod::ShakeMouse;
|
||||
if (m_isSnoopingMouse != wantSnoopingMouse)
|
||||
{
|
||||
m_isSnoopingMouse = wantSnoopingMouse;
|
||||
@@ -590,6 +702,7 @@ public:
|
||||
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
|
||||
m_backgroundColor = settings.backgroundColor;
|
||||
m_spotlightColor = settings.spotlightColor;
|
||||
m_activationMethod = settings.activationMethod;
|
||||
m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = settings.overlayOpacity;
|
||||
@@ -614,11 +727,13 @@ public:
|
||||
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
|
||||
m_backgroundColor = localSettings.backgroundColor;
|
||||
m_spotlightColor = localSettings.spotlightColor;
|
||||
m_activationMethod = settings.activationMethod;
|
||||
m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = localSettings.overlayOpacity;
|
||||
m_sonarZoomFactor = localSettings.spotlightInitialZoom;
|
||||
|
||||
UpdateMouseSnooping(); // For the shake mouse activation method
|
||||
|
||||
// Apply new settings to runtime composition objects.
|
||||
m_backdrop.Brush().as<winrt::CompositionColorBrush>().Color(m_backgroundColor);
|
||||
m_circleShape.FillBrush().as<winrt::CompositionColorBrush>().Color(m_spotlightColor);
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
#pragma once
|
||||
#include "pch.h"
|
||||
|
||||
enum struct FindMyMouseActivationMethod : int
|
||||
{
|
||||
DoubleControlKey = 0,
|
||||
ShakeMouse = 1,
|
||||
EnumElements = 2, // number of elements in the enum, not counting this
|
||||
};
|
||||
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true;
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 0, 0, 0);
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255);
|
||||
@@ -8,9 +15,11 @@ constexpr int FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY = 50;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
|
||||
constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleControlKey;
|
||||
|
||||
struct FindMyMouseSettings
|
||||
{
|
||||
FindMyMouseActivationMethod activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
bool doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
winrt::Windows::UI::Color backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
|
||||
winrt::Windows::UI::Color spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR;
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace
|
||||
{
|
||||
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_ACTIVATION_METHOD[] = L"activation_method";
|
||||
const wchar_t JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE[] = L"do_not_activate_on_game_mode";
|
||||
const wchar_t JSON_KEY_BACKGROUND_COLOR[] = L"background_color";
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_COLOR[] = L"spotlight_color";
|
||||
@@ -171,6 +172,20 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
FindMyMouseSettings findMyMouseSettings;
|
||||
if (settingsObject.GetView().Size())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse Activation Method
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_METHOD);
|
||||
UINT value = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE);
|
||||
if (value < (int)FindMyMouseActivationMethod::EnumElements)
|
||||
{
|
||||
findMyMouseSettings.activationMethod = (FindMyMouseActivationMethod)value;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize Activation Method from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE);
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class FindMyMouseProperties
|
||||
{
|
||||
[JsonPropertyName("activation_method")]
|
||||
public IntProperty ActivationMethod { get; set; }
|
||||
|
||||
[JsonPropertyName("do_not_activate_on_game_mode")]
|
||||
public BoolProperty DoNotActivateOnGameMode { get; set; }
|
||||
|
||||
@@ -31,6 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
public FindMyMouseProperties()
|
||||
{
|
||||
ActivationMethod = new IntProperty(0);
|
||||
DoNotActivateOnGameMode = new BoolProperty(true);
|
||||
BackgroundColor = new StringProperty("#000000");
|
||||
SpotlightColor = new StringProperty("#FFFFFF");
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
||||
}
|
||||
|
||||
FindMyMouseSettingsConfig = findMyMouseSettingsRepository.SettingsConfig;
|
||||
_findMyMouseActivationMethod = FindMyMouseSettingsConfig.Properties.ActivationMethod.Value;
|
||||
_findMyMouseDoNotActivateOnGameMode = FindMyMouseSettingsConfig.Properties.DoNotActivateOnGameMode.Value;
|
||||
|
||||
string backgroundColor = FindMyMouseSettingsConfig.Properties.BackgroundColor.Value;
|
||||
@@ -119,6 +120,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public int FindMyMouseActivationMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _findMyMouseActivationMethod;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _findMyMouseActivationMethod)
|
||||
{
|
||||
_findMyMouseActivationMethod = value;
|
||||
FindMyMouseSettingsConfig.Properties.ActivationMethod.Value = value;
|
||||
NotifyFindMyMousePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool FindMyMouseDoNotActivateOnGameMode
|
||||
{
|
||||
get
|
||||
@@ -586,6 +605,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
||||
private Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
private bool _isFindMyMouseEnabled;
|
||||
private int _findMyMouseActivationMethod;
|
||||
private bool _findMyMouseDoNotActivateOnGameMode;
|
||||
private string _findMyMouseBackgroundColor;
|
||||
private string _findMyMouseSpotlightColor;
|
||||
|
||||
@@ -1648,7 +1648,7 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
|
||||
<comment>Mouse as in the hardware peripheral</comment>
|
||||
</data>
|
||||
<data name="Oobe_MouseUtils_FindMyMouse_Description.Text" xml:space="preserve">
|
||||
<value>Press the left Ctrl key twice to focus the mouse pointer.</value>
|
||||
<value>Shake the mouse or press the left Ctrl key twice to focus the mouse pointer.</value>
|
||||
<comment>Mouse as in the hardware peripheral. Key as in a keyboard key</comment>
|
||||
</data>
|
||||
<data name="Oobe_MouseUtils_MouseHighlighter.Text" xml:space="preserve">
|
||||
@@ -1757,13 +1757,24 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
|
||||
<comment>Refers to the utility name</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse.Description" xml:space="preserve">
|
||||
<value>Find My Mouse highlights the position of the cursor when pressing the left Ctrl key twice.</value>
|
||||
<value>Find My Mouse highlights the position of the cursor when shaking the mouse or pressing the left Ctrl key twice.</value>
|
||||
<comment>"Ctrl" is a keyboard key. "Find My Mouse" is the name of the utility</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_Enable_FindMyMouse.Header" xml:space="preserve">
|
||||
<value>Enable Find My Mouse</value>
|
||||
<comment>"Find My Mouse" is the name of the utility.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse_ActivationMethod.Header" xml:space="preserve">
|
||||
<value>Activation method</value>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse_ActivationDoubleControlPress.Content" xml:space="preserve">
|
||||
<value>Press Left Control twice</value>
|
||||
<comment>Left control is the physical key on the keyboard.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse_ActivationShakeMouse.Content" xml:space="preserve">
|
||||
<value>Shake mouse</value>
|
||||
<comment>Mouse is the hardware peripheral.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_Prevent_Activation_On_Game_Mode.Content" xml:space="preserve">
|
||||
<value>Do not activate when Game Mode is on</value>
|
||||
<comment>"Game mode" is the Windows feature to prevent notification when playing a game.</comment>
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFindMyMouseEnabled, Mode=TwoWay}" x:Uid="ToggleSwitch"/>
|
||||
</controls:Setting.ActionContent>
|
||||
</controls:Setting>
|
||||
<controls:Setting x:Uid="MouseUtils_FindMyMouse_ActivationMethod" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsFindMyMouseEnabled}">
|
||||
<controls:Setting.ActionContent>
|
||||
<ComboBox SelectedIndex="{x:Bind Path=ViewModel.FindMyMouseActivationMethod, Mode=TwoWay}" MinWidth="{StaticResource SettingActionControlMinWidth}">
|
||||
<ComboBoxItem x:Uid="MouseUtils_FindMyMouse_ActivationDoubleControlPress" />
|
||||
<ComboBoxItem x:Uid="MouseUtils_FindMyMouse_ActivationShakeMouse" />
|
||||
</ComboBox>
|
||||
</controls:Setting.ActionContent>
|
||||
</controls:Setting>
|
||||
<controls:SettingExpander IsEnabled="{x:Bind ViewModel.IsFindMyMouseEnabled, Mode=OneWay}" IsExpanded="False" >
|
||||
<controls:SettingExpander.Header>
|
||||
<controls:Setting x:Uid="ShortcutGuide_Appearance_Behavior" Icon="" />
|
||||
|
||||
Reference in New Issue
Block a user