mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
[Screen Ruler] Improve UX (#20400)
* [Screen Ruler] add 7% opacity to tooltip background * [Screen Ruler] restrict mouse cursor to monitor while in bounds mode * [Screen Ruler] Do not preview overlay ui on all virtual desktops (Win + tab) * [Screen Ruler] add hotkeys for toolbar #20345 * [Screen Ruler] Make single snapshot capture mode a default and update warning * [Screen Ruler] Fix touch input in bounds mode #20286 * [Screen Ruler] activate window and set HWND_TOPMOST flag again after initialization
This commit is contained in:
8
.github/actions/spell-check/expect.txt
vendored
8
.github/actions/spell-check/expect.txt
vendored
@@ -644,6 +644,7 @@ FRAMECHANGED
|
|||||||
franky
|
franky
|
||||||
frankychen
|
frankychen
|
||||||
Froml
|
Froml
|
||||||
|
FROMTOUCH
|
||||||
fstream
|
fstream
|
||||||
FTYPE
|
FTYPE
|
||||||
func
|
func
|
||||||
@@ -784,6 +785,7 @@ hsv
|
|||||||
htcfreek
|
htcfreek
|
||||||
HTCLIENT
|
HTCLIENT
|
||||||
HTHUMBNAIL
|
HTHUMBNAIL
|
||||||
|
HTOUCHINPUT
|
||||||
HTTRANSPARENT
|
HTTRANSPARENT
|
||||||
HValue
|
HValue
|
||||||
Hvci
|
Hvci
|
||||||
@@ -869,6 +871,7 @@ IImage
|
|||||||
Iindex
|
Iindex
|
||||||
IInitialize
|
IInitialize
|
||||||
IInspectable
|
IInspectable
|
||||||
|
IInvoke
|
||||||
IIO
|
IIO
|
||||||
IItem
|
IItem
|
||||||
IJson
|
IJson
|
||||||
@@ -1276,6 +1279,7 @@ monitorinfof
|
|||||||
Monthand
|
Monthand
|
||||||
Moq
|
Moq
|
||||||
MOUSEACTIVATE
|
MOUSEACTIVATE
|
||||||
|
MOUSEEVENTF
|
||||||
MOUSEHWHEEL
|
MOUSEHWHEEL
|
||||||
MOUSEINPUT
|
MOUSEINPUT
|
||||||
MOUSELEAVE
|
MOUSELEAVE
|
||||||
@@ -2089,6 +2093,8 @@ Toolset
|
|||||||
toolwindow
|
toolwindow
|
||||||
TOPDOWNDIB
|
TOPDOWNDIB
|
||||||
toplevel
|
toplevel
|
||||||
|
TOUCHEVENTF
|
||||||
|
TOUCHINPUT
|
||||||
touchpad
|
touchpad
|
||||||
toupper
|
toupper
|
||||||
Towindow
|
Towindow
|
||||||
@@ -2108,6 +2114,7 @@ Tshuapa
|
|||||||
TStr
|
TStr
|
||||||
Tuva
|
Tuva
|
||||||
TValue
|
TValue
|
||||||
|
TWF
|
||||||
TYMED
|
TYMED
|
||||||
typedef
|
typedef
|
||||||
TYPEKEY
|
TYPEKEY
|
||||||
@@ -2262,6 +2269,7 @@ VSTT
|
|||||||
vswhere
|
vswhere
|
||||||
vtable
|
vtable
|
||||||
Vtbl
|
Vtbl
|
||||||
|
WANTPALM
|
||||||
wbem
|
wbem
|
||||||
wbemuuid
|
wbemuuid
|
||||||
WBounds
|
WBounds
|
||||||
|
|||||||
@@ -5,6 +5,72 @@
|
|||||||
|
|
||||||
#include <common/utils/window.h>
|
#include <common/utils/window.h>
|
||||||
|
|
||||||
|
#define MOUSEEVENTF_FROMTOUCH 0xFF515700
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void ToggleCursor(const bool show)
|
||||||
|
{
|
||||||
|
if (show)
|
||||||
|
{
|
||||||
|
for (; ShowCursor(show) < 0;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (; ShowCursor(show) >= 0;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCursorMove(HWND window, BoundsToolState* toolState, const POINT cursorPos, const DWORD touchID = 0)
|
||||||
|
{
|
||||||
|
if (!toolState->perScreen[window].currentBounds || (toolState->perScreen[window].currentBounds->touchID != touchID))
|
||||||
|
return;
|
||||||
|
|
||||||
|
toolState->perScreen[window].currentBounds->currentPos =
|
||||||
|
D2D_POINT_2F{ .x = static_cast<float>(cursorPos.x), .y = static_cast<float>(cursorPos.y) };
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCursorDown(HWND window, BoundsToolState* toolState, const POINT cursorPos, const DWORD touchID = 0)
|
||||||
|
{
|
||||||
|
ToggleCursor(false);
|
||||||
|
|
||||||
|
RECT windowRect;
|
||||||
|
if (GetWindowRect(window, &windowRect))
|
||||||
|
ClipCursor(&windowRect);
|
||||||
|
|
||||||
|
const D2D_POINT_2F newBoundsStart = { .x = static_cast<float>(cursorPos.x), .y = static_cast<float>(cursorPos.y) };
|
||||||
|
toolState->perScreen[window].currentBounds = CursorDrag{
|
||||||
|
.startPos = newBoundsStart,
|
||||||
|
.currentPos = newBoundsStart,
|
||||||
|
.touchID = touchID
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCursorUp(HWND window, BoundsToolState* toolState, const POINT cursorPos)
|
||||||
|
{
|
||||||
|
ToggleCursor(true);
|
||||||
|
ClipCursor(nullptr);
|
||||||
|
|
||||||
|
toolState->commonState->overlayBoxText.Read([](const OverlayBoxText& text) {
|
||||||
|
SetClipBoardToText(text.buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (const bool shiftPress = GetKeyState(VK_SHIFT) & 0x8000; shiftPress && toolState->perScreen[window].currentBounds)
|
||||||
|
{
|
||||||
|
D2D1_RECT_F rect;
|
||||||
|
std::tie(rect.left, rect.right) =
|
||||||
|
std::minmax(static_cast<float>(cursorPos.x), toolState->perScreen[window].currentBounds->startPos.x);
|
||||||
|
std::tie(rect.top, rect.bottom) =
|
||||||
|
std::minmax(static_cast<float>(cursorPos.y), toolState->perScreen[window].currentBounds->startPos.y);
|
||||||
|
toolState->perScreen[window].measurements.push_back(Measurement{ rect });
|
||||||
|
}
|
||||||
|
|
||||||
|
toolState->perScreen[window].currentBounds = std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||||
{
|
{
|
||||||
switch (message)
|
switch (message)
|
||||||
@@ -25,64 +91,122 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
|
|||||||
break;
|
break;
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
{
|
{
|
||||||
for (; ShowCursor(false) >= 0;)
|
const bool touchEvent = (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH;
|
||||||
;
|
if (touchEvent)
|
||||||
|
break;
|
||||||
|
|
||||||
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
if (!toolState)
|
if (!toolState)
|
||||||
break;
|
break;
|
||||||
const POINT cursorPos = convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace);
|
|
||||||
|
|
||||||
D2D_POINT_2F newRegionStart = { .x = static_cast<float>(cursorPos.x), .y = static_cast<float>(cursorPos.y) };
|
HandleCursorDown(window,
|
||||||
toolState->perScreen[window].currentRegionStart = newRegionStart;
|
toolState,
|
||||||
|
convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_CURSOR_LEFT_MONITOR:
|
case WM_CURSOR_LEFT_MONITOR:
|
||||||
{
|
{
|
||||||
for (; ShowCursor(true) < 0;)
|
ToggleCursor(true);
|
||||||
;
|
|
||||||
|
ClipCursor(nullptr);
|
||||||
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
if (!toolState)
|
if (!toolState)
|
||||||
break;
|
break;
|
||||||
toolState->perScreen[window].currentRegionStart = std::nullopt;
|
toolState->perScreen[window].currentBounds = std::nullopt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_LBUTTONUP:
|
case WM_TOUCH:
|
||||||
{
|
{
|
||||||
for (; ShowCursor(true) < 0;)
|
|
||||||
;
|
|
||||||
|
|
||||||
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
if (!toolState || !toolState->perScreen[window].currentRegionStart)
|
if (!toolState)
|
||||||
break;
|
break;
|
||||||
|
std::array<TOUCHINPUT, 8> inputs;
|
||||||
|
const size_t nInputs = std::min(static_cast<size_t>(LOWORD(wparam)), inputs.size());
|
||||||
|
const auto inputHandle = std::bit_cast<HTOUCHINPUT>(lparam);
|
||||||
|
GetTouchInputInfo(inputHandle, static_cast<UINT>(nInputs), inputs.data(), sizeof(TOUCHINPUT));
|
||||||
|
|
||||||
toolState->commonState->overlayBoxText.Read([](const OverlayBoxText& text) {
|
for (UINT i = 0; i < nInputs; ++i)
|
||||||
SetClipBoardToText(text.buffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (const bool shiftPress = GetKeyState(VK_SHIFT) & 0x8000; shiftPress)
|
|
||||||
{
|
{
|
||||||
const auto cursorPos = convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace);
|
const auto& input = inputs[i];
|
||||||
|
|
||||||
D2D1_RECT_F rect;
|
if (const bool down = (input.dwFlags & TOUCHEVENTF_DOWN) && (input.dwFlags & TOUCHEVENTF_PRIMARY); down)
|
||||||
std::tie(rect.left, rect.right) = std::minmax(static_cast<float>(cursorPos.x), toolState->perScreen[window].currentRegionStart->x);
|
{
|
||||||
std::tie(rect.top, rect.bottom) = std::minmax(static_cast<float>(cursorPos.y), toolState->perScreen[window].currentRegionStart->y);
|
HandleCursorDown(
|
||||||
toolState->perScreen[window].measurements.push_back(Measurement{ rect });
|
window,
|
||||||
|
toolState,
|
||||||
|
POINT{ TOUCH_COORD_TO_PIXEL(input.x), TOUCH_COORD_TO_PIXEL(input.y) },
|
||||||
|
input.dwID);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const bool up = input.dwFlags & TOUCHEVENTF_UP; up)
|
||||||
|
{
|
||||||
|
HandleCursorUp(
|
||||||
|
window,
|
||||||
|
toolState,
|
||||||
|
POINT{ TOUCH_COORD_TO_PIXEL(input.x), TOUCH_COORD_TO_PIXEL(input.y) });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const bool move = input.dwFlags & TOUCHEVENTF_MOVE; move)
|
||||||
|
{
|
||||||
|
HandleCursorMove(window,
|
||||||
|
toolState,
|
||||||
|
POINT{ TOUCH_COORD_TO_PIXEL(input.x), TOUCH_COORD_TO_PIXEL(input.y) },
|
||||||
|
input.dwID);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toolState->perScreen[window].currentRegionStart = std::nullopt;
|
CloseTouchInputHandle(inputHandle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
{
|
||||||
|
const bool touchEvent = (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH;
|
||||||
|
if (touchEvent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
|
if (!toolState)
|
||||||
|
break;
|
||||||
|
|
||||||
|
HandleCursorMove(window,
|
||||||
|
toolState,
|
||||||
|
convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
{
|
||||||
|
const bool touchEvent = (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH;
|
||||||
|
if (touchEvent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
|
if (!toolState)
|
||||||
|
break;
|
||||||
|
|
||||||
|
HandleCursorUp(window,
|
||||||
|
toolState,
|
||||||
|
convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_RBUTTONUP:
|
case WM_RBUTTONUP:
|
||||||
{
|
{
|
||||||
for (; ShowCursor(true) < 0;)
|
const bool touchEvent = (GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH;
|
||||||
;
|
if (touchEvent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ToggleCursor(true);
|
||||||
|
|
||||||
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
auto toolState = GetWindowParam<BoundsToolState*>(window);
|
||||||
if (!toolState)
|
if (!toolState)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (toolState->perScreen[window].currentRegionStart)
|
if (toolState->perScreen[window].currentBounds)
|
||||||
toolState->perScreen[window].currentRegionStart = std::nullopt;
|
toolState->perScreen[window].currentBounds = std::nullopt;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (toolState->perScreen[window].measurements.empty())
|
if (toolState->perScreen[window].measurements.empty())
|
||||||
@@ -100,14 +224,12 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void DrawMeasurement(const Measurement& measurement,
|
void DrawMeasurement(const Measurement& measurement,
|
||||||
const bool alignTextBoxToCenter,
|
|
||||||
const CommonState& commonState,
|
const CommonState& commonState,
|
||||||
HWND window,
|
HWND window,
|
||||||
const D2DState& d2dState,
|
const D2DState& d2dState,
|
||||||
float mouseX,
|
std::optional<D2D_POINT_2F> textBoxCenter)
|
||||||
float mouseY)
|
|
||||||
{
|
{
|
||||||
const bool screenQuadrantAware = !alignTextBoxToCenter;
|
const bool screenQuadrantAware = textBoxCenter.has_value();
|
||||||
d2dState.ToggleAliasedLinesMode(true);
|
d2dState.ToggleAliasedLinesMode(true);
|
||||||
d2dState.dxgiWindowState.rt->DrawRectangle(measurement.rect, d2dState.solidBrushes[Brush::line].get());
|
d2dState.dxgiWindowState.rt->DrawRectangle(measurement.rect, d2dState.solidBrushes[Brush::line].get());
|
||||||
d2dState.ToggleAliasedLinesMode(false);
|
d2dState.ToggleAliasedLinesMode(false);
|
||||||
@@ -124,17 +246,19 @@ namespace
|
|||||||
v = text;
|
v = text;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (alignTextBoxToCenter)
|
D2D_POINT_2F textBoxPos;
|
||||||
|
if (textBoxCenter)
|
||||||
|
textBoxPos = *textBoxCenter;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mouseX = measurement.rect.left + measurement.Width(Measurement::Unit::Pixel) / 2;
|
textBoxPos.x = measurement.rect.left + measurement.Width(Measurement::Unit::Pixel) / 2;
|
||||||
mouseY = measurement.rect.top + measurement.Height(Measurement::Unit::Pixel) / 2;
|
textBoxPos.y = measurement.rect.top + measurement.Height(Measurement::Unit::Pixel) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
d2dState.DrawTextBox(text.buffer.data(),
|
d2dState.DrawTextBox(text.buffer.data(),
|
||||||
measureStringBufLen,
|
measureStringBufLen,
|
||||||
crossSymbolPos,
|
crossSymbolPos,
|
||||||
mouseX,
|
textBoxPos,
|
||||||
mouseY,
|
|
||||||
screenQuadrantAware,
|
screenQuadrantAware,
|
||||||
window);
|
window);
|
||||||
}
|
}
|
||||||
@@ -153,17 +277,13 @@ void DrawBoundsToolTick(const CommonState& commonState,
|
|||||||
|
|
||||||
const auto& perScreen = it->second;
|
const auto& perScreen = it->second;
|
||||||
for (const auto& measure : perScreen.measurements)
|
for (const auto& measure : perScreen.measurements)
|
||||||
DrawMeasurement(measure, true, commonState, window, d2dState, measure.rect.right, measure.rect.bottom);
|
DrawMeasurement(measure, commonState, window, d2dState, {});
|
||||||
|
|
||||||
if (!perScreen.currentRegionStart.has_value())
|
if (perScreen.currentBounds.has_value())
|
||||||
return;
|
{
|
||||||
|
D2D1_RECT_F rect;
|
||||||
const auto cursorPos = convert::FromSystemToWindow(window, commonState.cursorPosSystemSpace);
|
std::tie(rect.left, rect.right) = std::minmax(perScreen.currentBounds->startPos.x, perScreen.currentBounds->currentPos.x);
|
||||||
|
std::tie(rect.top, rect.bottom) = std::minmax(perScreen.currentBounds->startPos.y, perScreen.currentBounds->currentPos.y);
|
||||||
D2D1_RECT_F rect;
|
DrawMeasurement(Measurement{ rect }, commonState, window, d2dState, perScreen.currentBounds->currentPos);
|
||||||
const float cursorX = static_cast<float>(cursorPos.x);
|
}
|
||||||
const float cursorY = static_cast<float>(cursorPos.y);
|
|
||||||
std::tie(rect.left, rect.right) = std::minmax(cursorX, perScreen.currentRegionStart->x);
|
|
||||||
std::tie(rect.top, rect.bottom) = std::minmax(cursorY, perScreen.currentRegionStart->y);
|
|
||||||
DrawMeasurement(Measurement{ rect }, false, commonState, window, d2dState, cursorX, cursorY);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ D2DState::D2DState(const DxgiAPI* dxgi,
|
|||||||
void D2DState::DrawTextBox(const wchar_t* text,
|
void D2DState::DrawTextBox(const wchar_t* text,
|
||||||
const size_t textLen,
|
const size_t textLen,
|
||||||
const std::optional<size_t> halfOpaqueSymbolPos,
|
const std::optional<size_t> halfOpaqueSymbolPos,
|
||||||
const float centerX,
|
const D2D_POINT_2F center,
|
||||||
const float centerY,
|
|
||||||
const bool screenQuadrantAware,
|
const bool screenQuadrantAware,
|
||||||
const HWND window) const
|
const HWND window) const
|
||||||
{
|
{
|
||||||
@@ -88,10 +87,10 @@ void D2DState::DrawTextBox(const wchar_t* text,
|
|||||||
winrt::check_hresult(textLayout->SetMaxWidth(textMetrics.width));
|
winrt::check_hresult(textLayout->SetMaxWidth(textMetrics.width));
|
||||||
winrt::check_hresult(textLayout->SetMaxHeight(textMetrics.height));
|
winrt::check_hresult(textLayout->SetMaxHeight(textMetrics.height));
|
||||||
|
|
||||||
D2D1_RECT_F textRect{ .left = centerX - textMetrics.width / 2.f,
|
D2D1_RECT_F textRect{ .left = center.x - textMetrics.width / 2.f,
|
||||||
.top = centerY - textMetrics.height / 2.f,
|
.top = center.y - textMetrics.height / 2.f,
|
||||||
.right = centerX + textMetrics.width / 2.f,
|
.right = center.x + textMetrics.width / 2.f,
|
||||||
.bottom = centerY + textMetrics.height / 2.f };
|
.bottom = center.y + textMetrics.height / 2.f };
|
||||||
|
|
||||||
const float SHADOW_OFFSET = consts::SHADOW_OFFSET * dpiScale;
|
const float SHADOW_OFFSET = consts::SHADOW_OFFSET * dpiScale;
|
||||||
if (screenQuadrantAware)
|
if (screenQuadrantAware)
|
||||||
@@ -99,8 +98,8 @@ void D2DState::DrawTextBox(const wchar_t* text,
|
|||||||
bool cursorInLeftScreenHalf = false;
|
bool cursorInLeftScreenHalf = false;
|
||||||
bool cursorInTopScreenHalf = false;
|
bool cursorInTopScreenHalf = false;
|
||||||
DetermineScreenQuadrant(window,
|
DetermineScreenQuadrant(window,
|
||||||
static_cast<long>(centerX),
|
static_cast<long>(center.x),
|
||||||
static_cast<long>(centerY),
|
static_cast<long>(center.y),
|
||||||
cursorInLeftScreenHalf,
|
cursorInLeftScreenHalf,
|
||||||
cursorInTopScreenHalf);
|
cursorInTopScreenHalf);
|
||||||
float textQuadrantOffsetX = textMetrics.width / 2.f + SHADOW_OFFSET;
|
float textQuadrantOffsetX = textMetrics.width / 2.f + SHADOW_OFFSET;
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ struct D2DState
|
|||||||
void DrawTextBox(const wchar_t* text,
|
void DrawTextBox(const wchar_t* text,
|
||||||
const size_t textLen,
|
const size_t textLen,
|
||||||
const std::optional<size_t> halfOpaqueSymbolPos,
|
const std::optional<size_t> halfOpaqueSymbolPos,
|
||||||
const float centerX,
|
const D2D_POINT_2F center,
|
||||||
const float centerY,
|
|
||||||
const bool screenQuadrantAware,
|
const bool screenQuadrantAware,
|
||||||
const HWND window) const;
|
const HWND window) const;
|
||||||
void ToggleAliasedLinesMode(const bool enabled) const;
|
void ToggleAliasedLinesMode(const bool enabled) const;
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!gotMeasurement)
|
if (!gotMeasurement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -257,8 +257,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
|
|||||||
d2dState.DrawTextBox(text.buffer.data(),
|
d2dState.DrawTextBox(text.buffer.data(),
|
||||||
measureStringBufLen,
|
measureStringBufLen,
|
||||||
crossSymbolPos,
|
crossSymbolPos,
|
||||||
static_cast<float>(cursorPos.x),
|
D2D_POINT_2F{ static_cast<float>(cursorPos.x), static_cast<float>(cursorPos.y) },
|
||||||
static_cast<float>(cursorPos.y),
|
|
||||||
true,
|
true,
|
||||||
window);
|
window);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,15 @@ HWND CreateOverlayUIWindow(const CommonState& commonState,
|
|||||||
extraParam)
|
extraParam)
|
||||||
};
|
};
|
||||||
winrt::check_bool(window);
|
winrt::check_bool(window);
|
||||||
|
|
||||||
|
// Exclude overlay window from displaying in WIN+TAB preview, since WS_EX_TOOLWINDOW windows are displayed simultaneously on all virtual desktops.
|
||||||
|
// We can't remove WS_EX_TOOLWINDOW/WS_EX_NOACTIVATE flag, since we want to exclude the window from taskbar
|
||||||
|
BOOL val = TRUE;
|
||||||
|
DwmSetWindowAttribute(window, DWMWA_EXCLUDED_FROM_PEEK, &val, sizeof(val));
|
||||||
|
|
||||||
|
// We want to receive input events as soon as possible to prevent issues with touch input
|
||||||
|
RegisterTouchWindow(window, TWF_WANTPALM);
|
||||||
|
|
||||||
ShowWindow(window, SW_SHOWNORMAL);
|
ShowWindow(window, SW_SHOWNORMAL);
|
||||||
UpdateWindow(window);
|
UpdateWindow(window);
|
||||||
if (excludeFromCapture)
|
if (excludeFromCapture)
|
||||||
@@ -100,13 +109,13 @@ HWND CreateOverlayUIWindow(const CommonState& commonState,
|
|||||||
std::vector<D2D1::ColorF> AppendCommonOverlayUIColors(const D2D1::ColorF& lineColor)
|
std::vector<D2D1::ColorF> AppendCommonOverlayUIColors(const D2D1::ColorF& lineColor)
|
||||||
{
|
{
|
||||||
D2D1::ColorF foreground = D2D1::ColorF::Black;
|
D2D1::ColorF foreground = D2D1::ColorF::Black;
|
||||||
D2D1::ColorF background = D2D1::ColorF(0.96f, 0.96f, 0.96f, 1.0f);
|
D2D1::ColorF background = D2D1::ColorF(0.96f, 0.96f, 0.96f, .93f);
|
||||||
D2D1::ColorF border = D2D1::ColorF(0.44f, 0.44f, 0.44f, 0.4f);
|
D2D1::ColorF border = D2D1::ColorF(0.44f, 0.44f, 0.44f, 0.4f);
|
||||||
|
|
||||||
if (WindowsColors::is_dark_mode())
|
if (WindowsColors::is_dark_mode())
|
||||||
{
|
{
|
||||||
foreground = D2D1::ColorF::White;
|
foreground = D2D1::ColorF::White;
|
||||||
background = D2D1::ColorF(0.17f, 0.17f, 0.17f, 1.0f);
|
background = D2D1::ColorF(0.17f, 0.17f, 0.17f, .93f);
|
||||||
border = D2D1::ColorF(0.44f, 0.44f, 0.44f, 0.4f);
|
border = D2D1::ColorF(0.44f, 0.44f, 0.44f, 0.4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +124,7 @@ std::vector<D2D1::ColorF> AppendCommonOverlayUIColors(const D2D1::ColorF& lineCo
|
|||||||
|
|
||||||
void OverlayUIState::RunUILoop()
|
void OverlayUIState::RunUILoop()
|
||||||
{
|
{
|
||||||
bool cursorOnScreen = true;
|
bool cursorOnScreen = false;
|
||||||
|
|
||||||
while (IsWindow(_window) && !_commonState.closeOnOtherMonitors)
|
while (IsWindow(_window) && !_commonState.closeOnOtherMonitors)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
struct Settings
|
struct Settings
|
||||||
{
|
{
|
||||||
uint8_t pixelTolerance = 30;
|
uint8_t pixelTolerance = 30;
|
||||||
bool continuousCapture = true;
|
bool continuousCapture = false;
|
||||||
bool drawFeetOnCross = true;
|
bool drawFeetOnCross = true;
|
||||||
bool perColorChannelEdgeDetection = false;
|
bool perColorChannelEdgeDetection = false;
|
||||||
std::array<uint8_t, 3> lineColor = {255, 69, 0};
|
std::array<uint8_t, 3> lineColor = {255, 69, 0};
|
||||||
|
|||||||
@@ -36,13 +36,22 @@ struct CommonState
|
|||||||
std::atomic_bool closeOnOtherMonitors = false;
|
std::atomic_bool closeOnOtherMonitors = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CursorDrag
|
||||||
|
{
|
||||||
|
D2D_POINT_2F startPos = {};
|
||||||
|
D2D_POINT_2F currentPos = {};
|
||||||
|
DWORD touchID = 0; // indicate whether the drag belongs to a touch input sequence
|
||||||
|
};
|
||||||
|
|
||||||
struct BoundsToolState
|
struct BoundsToolState
|
||||||
{
|
{
|
||||||
struct PerScreen
|
struct PerScreen
|
||||||
{
|
{
|
||||||
std::optional<D2D_POINT_2F> currentRegionStart;
|
std::optional<CursorDrag> currentBounds;
|
||||||
std::vector<Measurement> measurements;
|
std::vector<Measurement> measurements;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: refactor so we don't need unordered_map
|
||||||
std::unordered_map<HWND, PerScreen> perScreen;
|
std::unordered_map<HWND, PerScreen> perScreen;
|
||||||
|
|
||||||
CommonState* commonState = nullptr; // required for WndProc
|
CommonState* commonState = nullptr; // required for WndProc
|
||||||
@@ -60,7 +69,7 @@ struct MeasureToolState
|
|||||||
struct Global
|
struct Global
|
||||||
{
|
{
|
||||||
uint8_t pixelTolerance = 30;
|
uint8_t pixelTolerance = 30;
|
||||||
bool continuousCapture = true;
|
bool continuousCapture = false;
|
||||||
bool drawFeetOnCross = true;
|
bool drawFeetOnCross = true;
|
||||||
bool perColorChannelEdgeDetection = false;
|
bool perColorChannelEdgeDetection = false;
|
||||||
Mode mode = Mode::Cross;
|
Mode mode = Mode::Cross;
|
||||||
|
|||||||
@@ -261,13 +261,21 @@
|
|||||||
Click="BoundsTool_Click"
|
Click="BoundsTool_Click"
|
||||||
Content=""
|
Content=""
|
||||||
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
||||||
ToolTipService.ToolTip="{x:Bind p:Resources.Bounds}" />
|
KeyboardAcceleratorPlacementMode="Auto"
|
||||||
|
ToolTipService.ToolTip="{x:Bind p:Resources.Bounds}">
|
||||||
|
<ToggleButton.KeyboardAccelerators>
|
||||||
|
<KeyboardAccelerator Key="Number1" Modifiers="Control" Invoked="KeyboardAccelerator_Invoked"/>
|
||||||
|
</ToggleButton.KeyboardAccelerators>
|
||||||
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
AutomationProperties.Name="{x:Bind p:Resources.Spacing}"
|
AutomationProperties.Name="{x:Bind p:Resources.Spacing}"
|
||||||
Click="MeasureTool_Click"
|
Click="MeasureTool_Click"
|
||||||
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
||||||
ToolTipService.ToolTip="{x:Bind p:Resources.Spacing}">
|
ToolTipService.ToolTip="{x:Bind p:Resources.Spacing}">
|
||||||
<FontIcon Margin="1,0,0,0" Glyph="" />
|
<FontIcon Margin="1,0,0,0" Glyph="" />
|
||||||
|
<ToggleButton.KeyboardAccelerators>
|
||||||
|
<KeyboardAccelerator Key="Number2" Modifiers="Control" Invoked="KeyboardAccelerator_Invoked"/>
|
||||||
|
</ToggleButton.KeyboardAccelerators>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
|
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
@@ -276,6 +284,9 @@
|
|||||||
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
Style="{StaticResource ToggleButtonRadioButtonStyle}"
|
||||||
ToolTipService.ToolTip="{x:Bind p:Resources.HorizontalSpacing}">
|
ToolTipService.ToolTip="{x:Bind p:Resources.HorizontalSpacing}">
|
||||||
<FontIcon Margin="1,0,0,0" Glyph="" />
|
<FontIcon Margin="1,0,0,0" Glyph="" />
|
||||||
|
<ToggleButton.KeyboardAccelerators>
|
||||||
|
<KeyboardAccelerator Key="Number3" Modifiers="Control" Invoked="KeyboardAccelerator_Invoked"/>
|
||||||
|
</ToggleButton.KeyboardAccelerators>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
AutomationProperties.Name="{x:Bind p:Resources.VerticalSpacing}"
|
AutomationProperties.Name="{x:Bind p:Resources.VerticalSpacing}"
|
||||||
@@ -287,12 +298,19 @@
|
|||||||
<RotateTransform Angle="90" />
|
<RotateTransform Angle="90" />
|
||||||
</FontIcon.RenderTransform>
|
</FontIcon.RenderTransform>
|
||||||
</FontIcon>
|
</FontIcon>
|
||||||
|
<ToggleButton.KeyboardAccelerators>
|
||||||
|
<KeyboardAccelerator Key="Number4" Modifiers="Control" Invoked="KeyboardAccelerator_Invoked"/>
|
||||||
|
</ToggleButton.KeyboardAccelerators>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<AppBarSeparator Height="36" />
|
<AppBarSeparator Height="36" />
|
||||||
<Button
|
<Button
|
||||||
Click="ClosePanelTool_Click"
|
Click="ClosePanelTool_Click"
|
||||||
Content=""
|
Content=""
|
||||||
ToolTipService.ToolTip="Close" />
|
ToolTipService.ToolTip="Close">
|
||||||
|
<Button.KeyboardAccelerators>
|
||||||
|
<KeyboardAccelerator Key="Escape" Invoked="KeyboardAccelerator_Invoked"/>
|
||||||
|
</Button.KeyboardAccelerators>
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</winuiex:WindowEx>
|
</winuiex:WindowEx>
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ using System;
|
|||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Automation.Peers;
|
||||||
|
using Microsoft.UI.Xaml.Automation.Provider;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
using WinUIEx;
|
using WinUIEx;
|
||||||
|
|
||||||
@@ -64,6 +67,7 @@ namespace MeasureToolUI
|
|||||||
_initialPosition.X + (int)(dpiScale * WindowWidth),
|
_initialPosition.X + (int)(dpiScale * WindowWidth),
|
||||||
_initialPosition.Y + (int)(dpiScale * WindowHeight));
|
_initialPosition.Y + (int)(dpiScale * WindowHeight));
|
||||||
OnPositionChanged(_initialPosition);
|
OnPositionChanged(_initialPosition);
|
||||||
|
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindow_Closed(object sender, WindowEventArgs args)
|
private void MainWindow_Closed(object sender, WindowEventArgs args)
|
||||||
@@ -149,5 +153,24 @@ namespace MeasureToolUI
|
|||||||
{
|
{
|
||||||
_coreLogic?.Dispose();
|
_coreLogic?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void KeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Element is ToggleButton toggle)
|
||||||
|
{
|
||||||
|
var peer = new ToggleButtonAutomationPeer(toggle);
|
||||||
|
peer.Toggle();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
else if (args.Element is Button button)
|
||||||
|
{
|
||||||
|
var peer = new ButtonAutomationPeer(button);
|
||||||
|
if (peer.GetPattern(PatternInterface.Invoke) is IInvokeProvider provider)
|
||||||
|
{
|
||||||
|
provider.Invoke();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
ActivationShortcut = new HotkeySettings(true, false, false, true, 0x4D);
|
ActivationShortcut = new HotkeySettings(true, false, false, true, 0x4D);
|
||||||
UnitsOfMeasure = new IntProperty(0);
|
UnitsOfMeasure = new IntProperty(0);
|
||||||
PixelTolerance = new IntProperty(30);
|
PixelTolerance = new IntProperty(30);
|
||||||
ContinuousCapture = true;
|
ContinuousCapture = false;
|
||||||
DrawFeetOnCross = true;
|
DrawFeetOnCross = true;
|
||||||
PerColorChannelEdgeDetection = false;
|
PerColorChannelEdgeDetection = false;
|
||||||
MeasureCrossColor = new StringProperty("#FF4500");
|
MeasureCrossColor = new StringProperty("#FF4500");
|
||||||
|
|||||||
@@ -2396,7 +2396,7 @@ Activate by holding the key for the character you want to add an accent to, then
|
|||||||
<value>Learn more about conflicting activation commands</value>
|
<value>Learn more about conflicting activation commands</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MeasureTool_ContinuousCapture_Information.Title" xml:space="preserve">
|
<data name="MeasureTool_ContinuousCapture_Information.Title" xml:space="preserve">
|
||||||
<value>The continuous capture mode will consume more resources when in use.</value>
|
<value>The continuous capture mode will consume more resources when in use. Also, measurements will be excluded from screenshots and screen capture.</value>
|
||||||
<comment>pointer as in mouse pointer. Resources refer to things like CPU, GPU, RAM</comment>
|
<comment>pointer as in mouse pointer. Resources refer to things like CPU, GPU, RAM</comment>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
Reference in New Issue
Block a user