[Mouse Crosshairs] Hide crosshairs when cursor hides (#27381)

* hide crosshairs when cursor hides

* fix formatting
This commit is contained in:
Davide Giacometti 2023-07-18 14:33:32 +02:00 committed by GitHub
parent ed44db25e3
commit 7e4e8f59bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 18 deletions

View File

@ -45,6 +45,7 @@ private:
static constexpr auto m_className = L"MousePointerCrosshairs";
static constexpr auto m_windowTitle = L"PowerToys Mouse Pointer Crosshairs";
static constexpr DWORD AUTO_HIDE_TIMER_ID = 101;
HWND m_hwndOwner = NULL;
HWND m_hwnd = NULL;
HINSTANCE m_hinstance = NULL;
@ -65,8 +66,10 @@ private:
winrt::SpriteVisual m_bottom_crosshairs_border{ nullptr };
winrt::SpriteVisual m_bottom_crosshairs{ nullptr };
bool m_visible = false;
bool m_drawing = false;
bool m_destroyed = false;
bool m_hiddenCursor = false;
void SetAutoHideTimer() noexcept;
// Configurable Settings
winrt::Windows::UI::Color m_crosshairs_border_color = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_BORDER_COLOR;
@ -75,6 +78,7 @@ private:
int m_crosshairs_thickness = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_THICKNESS;
int m_crosshairs_border_size = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_BORDER_SIZE;
float m_crosshairs_opacity = max(0.f, min(1.f, (float)INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_OPACITY / 100.0f));
bool m_crosshairs_auto_hide = INCLUSIVE_MOUSE_DEFAULT_AUTO_HIDE;
};
InclusiveCrosshairs* InclusiveCrosshairs::instance = nullptr;
@ -217,7 +221,7 @@ void InclusiveCrosshairs::UpdateCrosshairsPosition()
m_left_crosshairs.Offset({ ptCursor.x - m_crosshairs_radius + halfPixelAdjustment * 2.f, ptCursor.y + halfPixelAdjustment, .0f });
m_left_crosshairs.Size({ ptCursor.x - ptMonitorUpperLeft.x - m_crosshairs_radius + halfPixelAdjustment * 2, static_cast<float>(m_crosshairs_thickness) });
m_right_crosshairs_border.Offset({static_cast<float>(ptCursor.x) + m_crosshairs_radius - m_crosshairs_border_size, ptCursor.y + halfPixelAdjustment, .0f });
m_right_crosshairs_border.Offset({ static_cast<float>(ptCursor.x) + m_crosshairs_radius - m_crosshairs_border_size, ptCursor.y + halfPixelAdjustment, .0f });
m_right_crosshairs_border.Size({ static_cast<float>(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size, m_crosshairs_thickness + m_crosshairs_border_size * 2.f });
m_right_crosshairs.Offset({ static_cast<float>(ptCursor.x) + m_crosshairs_radius, ptCursor.y + halfPixelAdjustment, .0f });
m_right_crosshairs.Size({ static_cast<float>(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius, static_cast<float>(m_crosshairs_thickness) });
@ -227,11 +231,10 @@ void InclusiveCrosshairs::UpdateCrosshairsPosition()
m_top_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, ptCursor.y - m_crosshairs_radius + halfPixelAdjustment * 2, .0f });
m_top_crosshairs.Size({ static_cast<float>(m_crosshairs_thickness), ptCursor.y - ptMonitorUpperLeft.y - m_crosshairs_radius + halfPixelAdjustment * 2 });
m_bottom_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, static_cast<float>(ptCursor.y) + m_crosshairs_radius - m_crosshairs_border_size, .0f });
m_bottom_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, static_cast<float>(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size });
m_bottom_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, static_cast<float>(ptCursor.y) + m_crosshairs_radius, .0f });
m_bottom_crosshairs.Size({ static_cast<float>(m_crosshairs_thickness), static_cast<float>(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius });
m_bottom_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, static_cast<float>(ptCursor.y) + m_crosshairs_radius - m_crosshairs_border_size, .0f });
m_bottom_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, static_cast<float>(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size });
m_bottom_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, static_cast<float>(ptCursor.y) + m_crosshairs_radius, .0f });
m_bottom_crosshairs.Size({ static_cast<float>(m_crosshairs_thickness), static_cast<float>(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius });
}
LRESULT CALLBACK InclusiveCrosshairs::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept
@ -239,7 +242,8 @@ LRESULT CALLBACK InclusiveCrosshairs::MouseHookProc(int nCode, WPARAM wParam, LP
if (nCode >= 0)
{
MSLLHOOKSTRUCT* hookData = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
if (wParam == WM_MOUSEMOVE) {
if (wParam == WM_MOUSEMOVE)
{
instance->UpdateCrosshairsPosition();
}
}
@ -251,18 +255,37 @@ void InclusiveCrosshairs::StartDrawing()
Logger::info("Start drawing crosshairs.");
Trace::StartDrawingCrosshairs();
UpdateCrosshairsPosition();
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
m_visible = true;
m_hiddenCursor = false;
if (m_crosshairs_auto_hide)
{
CURSORINFO cursorInfo{};
cursorInfo.cbSize = sizeof(cursorInfo);
if (GetCursorInfo(&cursorInfo))
{
m_hiddenCursor = !(cursorInfo.flags & CURSOR_SHOWING);
}
SetAutoHideTimer();
}
if (!m_hiddenCursor)
{
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
}
m_drawing = true;
m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0);
}
void InclusiveCrosshairs::StopDrawing()
{
Logger::info("Stop drawing crosshairs.");
m_visible = false;
m_drawing = false;
ShowWindow(m_hwnd, SW_HIDE);
UnhookWindowsHookEx(m_mouseHook);
m_mouseHook = NULL;
KillTimer(m_hwnd, AUTO_HIDE_TIMER_ID);
}
void InclusiveCrosshairs::SwitchActivationMode()
@ -278,9 +301,30 @@ void InclusiveCrosshairs::ApplySettings(InclusiveCrosshairsSettings& settings, b
m_crosshairs_opacity = max(0.f, min(1.f, (float)settings.crosshairsOpacity / 100.0f));
m_crosshairs_border_color = settings.crosshairsBorderColor;
m_crosshairs_border_size = settings.crosshairsBorderSize;
bool autoHideChanged = m_crosshairs_auto_hide != settings.crosshairsAutoHide;
m_crosshairs_auto_hide = settings.crosshairsAutoHide;
if (applyToRunTimeObjects)
{
if (autoHideChanged)
{
if (m_crosshairs_auto_hide)
{
SetAutoHideTimer();
}
else
{
KillTimer(m_hwnd, AUTO_HIDE_TIMER_ID);
// Edge case of settings being changed with hidden crosshairs: timer time-out is 1 seconds
if (m_drawing && m_hiddenCursor)
{
instance->m_hiddenCursor = false;
ShowWindow(instance->m_hwnd, SW_SHOWNOACTIVATE);
}
}
}
// Runtime objects already created. Should update in the owner thread.
if (m_dispatcherQueueController == nullptr)
{
@ -331,7 +375,7 @@ LRESULT CALLBACK InclusiveCrosshairs::WndProc(HWND hWnd, UINT message, WPARAM wP
case WM_NCHITTEST:
return HTTRANSPARENT;
case WM_SWITCH_ACTIVATION_MODE:
if (instance->m_visible)
if (instance->m_drawing)
{
instance->StopDrawing();
}
@ -343,6 +387,32 @@ LRESULT CALLBACK InclusiveCrosshairs::WndProc(HWND hWnd, UINT message, WPARAM wP
case WM_DESTROY:
instance->DestroyInclusiveCrosshairs();
break;
case WM_TIMER:
if (wParam == AUTO_HIDE_TIMER_ID && instance->m_drawing)
{
CURSORINFO cursorInfo{};
cursorInfo.cbSize = sizeof(cursorInfo);
if (GetCursorInfo(&cursorInfo))
{
if (cursorInfo.flags & CURSOR_SHOWING)
{
if (instance->m_hiddenCursor)
{
instance->m_hiddenCursor = false;
ShowWindow(instance->m_hwnd, SW_SHOWNOACTIVATE);
}
}
else
{
if (!instance->m_hiddenCursor)
{
instance->m_hiddenCursor = true;
ShowWindow(instance->m_hwnd, SW_HIDE);
}
}
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
@ -390,6 +460,15 @@ void InclusiveCrosshairs::Terminate()
}
}
void InclusiveCrosshairs::SetAutoHideTimer() noexcept
{
if (SetTimer(m_hwnd, AUTO_HIDE_TIMER_ID, 1000, NULL) == 0)
{
int error = GetLastError();
Logger::trace("Failed to create auto hide timer. Last error: {}", error);
}
}
#pragma region InclusiveCrosshairs_API
void InclusiveCrosshairsApplySettings(InclusiveCrosshairsSettings& settings)

View File

@ -7,6 +7,7 @@ const winrt::Windows::UI::Color INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_BORDER_COLOR
constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_RADIUS = 20;
constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_THICKNESS = 5;
constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_BORDER_SIZE = 1;
constexpr bool INCLUSIVE_MOUSE_DEFAULT_AUTO_HIDE = false;
struct InclusiveCrosshairsSettings
{
@ -16,6 +17,7 @@ struct InclusiveCrosshairsSettings
int crosshairsThickness = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_THICKNESS;
int crosshairsOpacity = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_OPACITY;
int crosshairsBorderSize = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIRS_BORDER_SIZE;
bool crosshairsAutoHide = INCLUSIVE_MOUSE_DEFAULT_AUTO_HIDE;
};
int InclusiveCrosshairsMain(HINSTANCE hinst, InclusiveCrosshairsSettings& settings);

View File

@ -17,6 +17,7 @@ namespace
const wchar_t JSON_KEY_CROSSHAIRS_THICKNESS[] = L"crosshairs_thickness";
const wchar_t JSON_KEY_CROSSHAIRS_BORDER_COLOR[] = L"crosshairs_border_color";
const wchar_t JSON_KEY_CROSSHAIRS_BORDER_SIZE[] = L"crosshairs_border_size";
const wchar_t JSON_KEY_CROSSHAIRS_AUTO_HIDE[] = L"crosshairs_auto_hide";
}
extern "C" IMAGE_DOS_HEADER __ImageBase;
@ -330,6 +331,16 @@ public:
{
Logger::warn("Failed to initialize border color from settings. Will use default value");
}
try
{
// Parse auto hide
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_AUTO_HIDE);
inclusiveCrosshairsSettings.crosshairsAutoHide = static_cast<bool>(jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE));
}
catch (...)
{
Logger::warn("Failed to initialize auto hide from settings. Will use default value");
}
}
else
{

View File

@ -31,6 +31,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("crosshairs_border_size")]
public IntProperty CrosshairsBorderSize { get; set; }
[JsonPropertyName("crosshairs_auto_hide")]
public BoolProperty CrosshairsAutoHide { get; set; }
public MousePointerCrosshairsProperties()
{
ActivationShortcut = DefaultActivationShortcut;
@ -40,6 +43,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
CrosshairsThickness = new IntProperty(5);
CrosshairsBorderColor = new StringProperty("#FFFFFF");
CrosshairsBorderSize = new IntProperty(1);
CrosshairsAutoHide = new BoolProperty(false);
}
}
}

View File

@ -2150,7 +2150,7 @@ From there, simply click on one of the supported files in the File Explorer and
<data name="OobeWindow_Title" xml:space="preserve">
<value>Welcome to PowerToys</value>
</data>
<data name="OobeWindow_TitleTxt.Text" xml:space="preserve">
<data name="OobeWindow_TitleTxt.Text" xml:space="preserve">
<value>Welcome to PowerToys</value>
</data>
<data name="SettingsWindow_Title" xml:space="preserve">
@ -2469,10 +2469,10 @@ From there, simply click on one of the supported files in the File Explorer and
<value>Cancel</value>
</data>
<data name="Activation_Shortcut_Description.Text" xml:space="preserve">
<value>Press a combination of keys to change this shortcut</value>
<value>Press a combination of keys to change this shortcut</value>
</data>
<data name="Activation_Shortcut_Reset" xml:space="preserve">
<value>Reset</value>
<value>Reset</value>
</data>
<data name="Activation_Shortcut_Save" xml:space="preserve">
<value>Save</value>
@ -3318,7 +3318,7 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="FileLocksmith_Toggle_StandardContextMenu.Content" xml:space="preserve">
<value>Default and extended context menu</value>
</data>
<data name="FileLocksmith_Toggle_ExtendedContextMenu.Content" xml:space="preserve">
<data name="FileLocksmith_Toggle_ExtendedContextMenu.Content" xml:space="preserve">
<value>Extended context menu only</value>
</data>
<data name="FileLocksmith_Toggle_ContextMenu.Header" xml:space="preserve">
@ -3551,9 +3551,9 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Maximum width (px)</value>
<comment>px = pixels</comment>
</data>
<data name="SettingsWindow_TitleTxt.Text" xml:space="preserve">
<data name="SettingsWindow_TitleTxt.Text" xml:space="preserve">
<value>PowerToys Settings</value>
</data>
</data>
<data name="Oobe_Peek.Description" xml:space="preserve">
<value>A lightning fast file preview feature for Windows.</value>
</data>
@ -3582,4 +3582,7 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Hosts_Encoding_Utf8Bom.Content" xml:space="preserve">
<value>UTF-8 with BOM</value>
</data>
<data name="MouseUtils_MousePointerCrosshairs_CrosshairsAutoHide.Content" xml:space="preserve">
<value>Automatically hide crosshairs when the mouse pointer is hidden</value>
</data>
</root>

View File

@ -106,6 +106,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_mousePointerCrosshairsRadius = MousePointerCrosshairsSettingsConfig.Properties.CrosshairsRadius.Value;
_mousePointerCrosshairsThickness = MousePointerCrosshairsSettingsConfig.Properties.CrosshairsThickness.Value;
_mousePointerCrosshairsBorderSize = MousePointerCrosshairsSettingsConfig.Properties.CrosshairsBorderSize.Value;
_mousePointerCrosshairsAutoHide = MousePointerCrosshairsSettingsConfig.Properties.CrosshairsAutoHide.Value;
// set the callback functions value to handle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
@ -792,6 +793,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool MousePointerCrosshairsAutoHide
{
get
{
return _mousePointerCrosshairsAutoHide;
}
set
{
if (value != _mousePointerCrosshairsAutoHide)
{
_mousePointerCrosshairsAutoHide = value;
MousePointerCrosshairsSettingsConfig.Properties.CrosshairsAutoHide.Value = value;
NotifyMousePointerCrosshairsPropertyChanged();
}
}
}
public void NotifyMousePointerCrosshairsPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
@ -850,5 +869,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private int _mousePointerCrosshairsThickness;
private string _mousePointerCrosshairsBorderColor;
private int _mousePointerCrosshairsBorderSize;
private bool _mousePointerCrosshairsAutoHide;
}
}

View File

@ -382,6 +382,12 @@
SpinButtonPlacementMode="Compact"
Value="{x:Bind Mode=TwoWay, Path=ViewModel.MousePointerCrosshairsBorderSize}" />
</labs:SettingsCard>
<labs:SettingsCard ContentAlignment="Left">
<CheckBox
x:Uid="MouseUtils_MousePointerCrosshairs_CrosshairsAutoHide"
IsChecked="{x:Bind ViewModel.MousePointerCrosshairsAutoHide, Mode=TwoWay}" />
</labs:SettingsCard>
</labs:SettingsExpander.Items>
</labs:SettingsExpander>
</controls:SettingsGroup>