mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 02:06:36 +02:00
[MouseHighlighter]Add always-on pointer (#27186)
* [MouseHighlighter]Add always pointer Make color pick UI ARGB Delete Opacity setting * Fix opacity and color Fix "always color" incorrectly initialized Revert default opacity to 65%, which was unintentionally lowered to 25% when changing to percent * Fix crash when opening MouseUtils settings page Migration code was bugged, made malformed json and led to settings page crash. * Implement migration in module side
This commit is contained in:
@@ -31,7 +31,8 @@ private:
|
||||
enum class MouseButton
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
Right,
|
||||
None
|
||||
};
|
||||
|
||||
void DestroyHighlighter();
|
||||
@@ -42,6 +43,7 @@ private:
|
||||
void AddDrawingPoint(MouseButton button);
|
||||
void UpdateDrawingPointPosition(MouseButton button);
|
||||
void StartDrawingPointFading(MouseButton button);
|
||||
void ClearDrawingPoint(MouseButton button);
|
||||
void ClearDrawing();
|
||||
HHOOK m_mouseHook = NULL;
|
||||
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
@@ -62,6 +64,12 @@ private:
|
||||
|
||||
winrt::CompositionSpriteShape m_leftPointer{ nullptr };
|
||||
winrt::CompositionSpriteShape m_rightPointer{ nullptr };
|
||||
winrt::CompositionSpriteShape m_alwaysPointer{ nullptr };
|
||||
|
||||
bool m_leftPointerEnabled = true;
|
||||
bool m_rightPointerEnabled = true;
|
||||
bool m_alwaysPointerEnabled = true;
|
||||
|
||||
bool m_leftButtonPressed = false;
|
||||
bool m_rightButtonPressed = false;
|
||||
|
||||
@@ -75,6 +83,7 @@ private:
|
||||
|
||||
winrt::Windows::UI::Color m_leftClickColor = MOUSE_HIGHLIGHTER_DEFAULT_LEFT_BUTTON_COLOR;
|
||||
winrt::Windows::UI::Color m_rightClickColor = MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR;
|
||||
winrt::Windows::UI::Color m_alwaysColor = MOUSE_HIGHLIGHTER_DEFAULT_ALWAYS_COLOR;
|
||||
};
|
||||
|
||||
Highlighter* Highlighter::instance = nullptr;
|
||||
@@ -132,18 +141,23 @@ void Highlighter::AddDrawingPoint(MouseButton button)
|
||||
auto circleGeometry = m_compositor.CreateEllipseGeometry();
|
||||
circleGeometry.Radius({ m_radius, m_radius });
|
||||
auto circleShape = m_compositor.CreateSpriteShape(circleGeometry);
|
||||
circleShape.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y )});
|
||||
circleShape.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
||||
if (button == MouseButton::Left)
|
||||
{
|
||||
circleShape.FillBrush(m_compositor.CreateColorBrush(m_leftClickColor));
|
||||
m_leftPointer = circleShape;
|
||||
}
|
||||
else
|
||||
else if (button == MouseButton::Right)
|
||||
{
|
||||
//right
|
||||
circleShape.FillBrush(m_compositor.CreateColorBrush(m_rightClickColor));
|
||||
m_rightPointer = circleShape;
|
||||
}
|
||||
else
|
||||
{
|
||||
// always
|
||||
circleShape.FillBrush(m_compositor.CreateColorBrush(m_alwaysColor));
|
||||
m_alwaysPointer = circleShape;
|
||||
}
|
||||
m_shape.Shapes().Append(circleShape);
|
||||
|
||||
// TODO: We're leaking shapes for long drawing sessions.
|
||||
@@ -166,12 +180,16 @@ void Highlighter::UpdateDrawingPointPosition(MouseButton button)
|
||||
|
||||
if (button == MouseButton::Left)
|
||||
{
|
||||
m_leftPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y )});
|
||||
m_leftPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
||||
}
|
||||
else if (button == MouseButton::Right)
|
||||
{
|
||||
m_rightPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
||||
}
|
||||
else
|
||||
{
|
||||
//right
|
||||
m_rightPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y )});
|
||||
// always
|
||||
m_alwaysPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
||||
}
|
||||
}
|
||||
void Highlighter::StartDrawingPointFading(MouseButton button)
|
||||
@@ -183,7 +201,7 @@ void Highlighter::StartDrawingPointFading(MouseButton button)
|
||||
}
|
||||
else
|
||||
{
|
||||
//right
|
||||
// right
|
||||
circleShape = m_rightPointer;
|
||||
}
|
||||
|
||||
@@ -201,6 +219,15 @@ void Highlighter::StartDrawingPointFading(MouseButton button)
|
||||
circleShape.FillBrush().StartAnimation(L"Color", animation);
|
||||
}
|
||||
|
||||
void Highlighter::ClearDrawingPoint(MouseButton _button)
|
||||
{
|
||||
winrt::Windows::UI::Composition::CompositionSpriteShape circleShape{ nullptr };
|
||||
|
||||
// always
|
||||
circleShape = m_alwaysPointer;
|
||||
|
||||
circleShape.FillBrush().as<winrt::Windows::UI::Composition::CompositionColorBrush>().Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
void Highlighter::ClearDrawing()
|
||||
{
|
||||
@@ -221,12 +248,28 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
|
||||
switch (wParam)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
instance->AddDrawingPoint(MouseButton::Left);
|
||||
instance->m_leftButtonPressed = true;
|
||||
if (instance->m_leftPointerEnabled)
|
||||
{
|
||||
if (instance->m_alwaysPointerEnabled && !instance->m_rightButtonPressed)
|
||||
{
|
||||
// Clear AlwaysPointer only when it's enabled and RightPointer is not active
|
||||
instance->ClearDrawingPoint(MouseButton::None);
|
||||
}
|
||||
instance->AddDrawingPoint(MouseButton::Left);
|
||||
instance->m_leftButtonPressed = true;
|
||||
}
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
instance->AddDrawingPoint(MouseButton::Right);
|
||||
instance->m_rightButtonPressed = true;
|
||||
if (instance->m_rightPointerEnabled)
|
||||
{
|
||||
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed)
|
||||
{
|
||||
// Clear AlwaysPointer only when it's enabled and LeftPointer is not active
|
||||
instance->ClearDrawingPoint(MouseButton::None);
|
||||
}
|
||||
instance->AddDrawingPoint(MouseButton::Right);
|
||||
instance->m_rightButtonPressed = true;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
if (instance->m_leftButtonPressed)
|
||||
@@ -237,12 +280,21 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
|
||||
{
|
||||
instance->UpdateDrawingPointPosition(MouseButton::Right);
|
||||
}
|
||||
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed && !instance->m_rightButtonPressed)
|
||||
{
|
||||
instance->UpdateDrawingPointPosition(MouseButton::None);
|
||||
}
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
if (instance->m_leftButtonPressed)
|
||||
{
|
||||
instance->StartDrawingPointFading(MouseButton::Left);
|
||||
instance->m_leftButtonPressed = false;
|
||||
if (instance->m_alwaysPointerEnabled && !instance->m_rightButtonPressed)
|
||||
{
|
||||
// Add AlwaysPointer only when it's enabled and RightPointer is not active
|
||||
instance->AddDrawingPoint(MouseButton::None);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
@@ -250,6 +302,11 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
|
||||
{
|
||||
instance->StartDrawingPointFading(MouseButton::Right);
|
||||
instance->m_rightButtonPressed = false;
|
||||
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed)
|
||||
{
|
||||
// Add AlwaysPointer only when it's enabled and LeftPointer is not active
|
||||
instance->AddDrawingPoint(MouseButton::None);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -270,6 +327,7 @@ void Highlighter::StartDrawing()
|
||||
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
||||
ClearDrawing();
|
||||
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
||||
instance->AddDrawingPoint(MouseButton::None);
|
||||
m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0);
|
||||
}
|
||||
|
||||
@@ -281,6 +339,7 @@ void Highlighter::StopDrawing()
|
||||
m_rightButtonPressed = false;
|
||||
m_leftPointer = nullptr;
|
||||
m_rightPointer = nullptr;
|
||||
m_alwaysPointer = nullptr;
|
||||
ShowWindow(m_hwnd, SW_HIDE);
|
||||
UnhookWindowsHookEx(m_mouseHook);
|
||||
ClearDrawing();
|
||||
@@ -298,6 +357,10 @@ void Highlighter::ApplySettings(MouseHighlighterSettings settings) {
|
||||
m_fadeDuration_ms = settings.fadeDurationMs;
|
||||
m_leftClickColor = settings.leftButtonColor;
|
||||
m_rightClickColor = settings.rightButtonColor;
|
||||
m_alwaysColor = settings.alwaysColor;
|
||||
m_leftPointerEnabled = settings.leftButtonColor.A != 0;
|
||||
m_rightPointerEnabled = settings.rightButtonColor.A != 0;
|
||||
m_alwaysPointerEnabled = settings.alwaysColor.A != 0;
|
||||
}
|
||||
|
||||
void Highlighter::DestroyHighlighter()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#include "pch.h"
|
||||
|
||||
constexpr int MOUSE_HIGHLIGHTER_DEFAULT_OPACITY = 65;
|
||||
const winrt::Windows::UI::Color MOUSE_HIGHLIGHTER_DEFAULT_LEFT_BUTTON_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(MOUSE_HIGHLIGHTER_DEFAULT_OPACITY, 255, 255, 0);
|
||||
const winrt::Windows::UI::Color MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(MOUSE_HIGHLIGHTER_DEFAULT_OPACITY, 0, 0, 255);
|
||||
const winrt::Windows::UI::Color MOUSE_HIGHLIGHTER_DEFAULT_LEFT_BUTTON_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(166, 255, 255, 0);
|
||||
const winrt::Windows::UI::Color MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(166, 0, 0, 255);
|
||||
const winrt::Windows::UI::Color MOUSE_HIGHLIGHTER_DEFAULT_ALWAYS_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(0, 255, 0, 0);
|
||||
constexpr int MOUSE_HIGHLIGHTER_DEFAULT_RADIUS = 20;
|
||||
constexpr int MOUSE_HIGHLIGHTER_DEFAULT_DELAY_MS = 500;
|
||||
constexpr int MOUSE_HIGHLIGHTER_DEFAULT_DURATION_MS = 250;
|
||||
@@ -12,6 +12,7 @@ struct MouseHighlighterSettings
|
||||
{
|
||||
winrt::Windows::UI::Color leftButtonColor = MOUSE_HIGHLIGHTER_DEFAULT_LEFT_BUTTON_COLOR;
|
||||
winrt::Windows::UI::Color rightButtonColor = MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR;
|
||||
winrt::Windows::UI::Color alwaysColor = MOUSE_HIGHLIGHTER_DEFAULT_ALWAYS_COLOR;
|
||||
int radius = MOUSE_HIGHLIGHTER_DEFAULT_RADIUS;
|
||||
int fadeDelayMs = MOUSE_HIGHLIGHTER_DEFAULT_DELAY_MS;
|
||||
int fadeDurationMs = MOUSE_HIGHLIGHTER_DEFAULT_DURATION_MS;
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace
|
||||
const wchar_t JSON_KEY_LEFT_BUTTON_CLICK_COLOR[] = L"left_button_click_color";
|
||||
const wchar_t JSON_KEY_RIGHT_BUTTON_CLICK_COLOR[] = L"right_button_click_color";
|
||||
const wchar_t JSON_KEY_HIGHLIGHT_OPACITY[] = L"highlight_opacity";
|
||||
const wchar_t JSON_KEY_ALWAYS_COLOR[] = L"always_color";
|
||||
const wchar_t JSON_KEY_HIGHLIGHT_RADIUS[] = L"highlight_radius";
|
||||
const wchar_t JSON_KEY_HIGHLIGHT_FADE_DELAY_MS[] = L"highlight_fade_delay_ms";
|
||||
const wchar_t JSON_KEY_HIGHLIGHT_FADE_DURATION_MS[] = L"highlight_fade_duration_ms";
|
||||
@@ -210,45 +211,53 @@ public:
|
||||
{
|
||||
Logger::warn("Failed to initialize Mouse Highlighter activation shortcut");
|
||||
}
|
||||
uint8_t opacity = MOUSE_HIGHLIGHTER_DEFAULT_OPACITY;
|
||||
try
|
||||
// Migration from <=1.1
|
||||
auto version = (std::wstring)settingsObject.GetNamedString(L"version");
|
||||
auto migration = false;
|
||||
uint8_t opacity = 166;
|
||||
if (version == L"1.0" || version == L"1.1")
|
||||
{
|
||||
// Parse Opacity
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_OPACITY);
|
||||
int value = static_cast<int>(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
|
||||
if (value >= 0)
|
||||
migration = true;
|
||||
try
|
||||
{
|
||||
opacity = value;
|
||||
// Parse Opacity
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_OPACITY);
|
||||
int value = static_cast<int>(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
|
||||
if (value >= 0)
|
||||
{
|
||||
if (version == L"1.0")
|
||||
{
|
||||
opacity = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.1
|
||||
opacity = value * 255 / 100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (...)
|
||||
{
|
||||
throw;
|
||||
Logger::warn("Failed to initialize Opacity from settings. Will use default value");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize Opacity from settings. Will use default value");
|
||||
}
|
||||
|
||||
// Convert % to uint8_t
|
||||
if ((std::wstring)settingsObject.GetNamedString(L"version") != L"1.0")
|
||||
{
|
||||
opacity = opacity * 255 / 100;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Parse left button click color
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_LEFT_BUTTON_CLICK_COLOR);
|
||||
auto leftColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t r, g, b;
|
||||
if (!checkValidRGB(leftColor, &r, &g, &b))
|
||||
uint8_t a = opacity, r, g, b;
|
||||
if (!migration && !checkValidARGB(leftColor, &a, &r, &g, &b) || migration && !checkValidRGB(leftColor, &r, &g, &b))
|
||||
{
|
||||
Logger::error("Left click color RGB value is invalid. Will use default value");
|
||||
Logger::error("Left click color ARGB value is invalid. Will use default value");
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightSettings.leftButtonColor = winrt::Windows::UI::ColorHelper::FromArgb(opacity, r, g, b);
|
||||
highlightSettings.leftButtonColor = winrt::Windows::UI::ColorHelper::FromArgb(a, r, g, b);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -260,14 +269,14 @@ public:
|
||||
// Parse right button click color
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_RIGHT_BUTTON_CLICK_COLOR);
|
||||
auto rightColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t r, g, b;
|
||||
if (!checkValidRGB(rightColor, &r, &g, &b))
|
||||
uint8_t a = opacity, r, g, b;
|
||||
if (!migration && !checkValidARGB(rightColor, &a, &r, &g, &b) || migration && !checkValidRGB(rightColor, &r, &g, &b))
|
||||
{
|
||||
Logger::error("Right click color RGB value is invalid. Will use default value");
|
||||
Logger::error("Right click color ARGB value is invalid. Will use default value");
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightSettings.rightButtonColor = winrt::Windows::UI::ColorHelper::FromArgb(opacity, r, g, b);
|
||||
highlightSettings.rightButtonColor = winrt::Windows::UI::ColorHelper::FromArgb(a, r, g, b);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -275,6 +284,25 @@ public:
|
||||
Logger::warn("Failed to initialize right click color from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse always color
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ALWAYS_COLOR);
|
||||
auto alwaysColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t a, r, g, b;
|
||||
if (!migration && !checkValidARGB(alwaysColor, &a, &r, &g, &b))
|
||||
{
|
||||
Logger::error("Always color ARGB value is invalid. Will use default value");
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightSettings.alwaysColor = winrt::Windows::UI::ColorHelper::FromArgb(a, r, g, b);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize always color from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse Radius
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_RADIUS);
|
||||
@@ -346,4 +374,4 @@ public:
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new MouseHighlighter();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user