mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 00:24:42 +01:00
implement custom renderer and make × semiopaque
This commit is contained in:
@@ -114,6 +114,7 @@ namespace
|
||||
L"%.0f × %.0f",
|
||||
width,
|
||||
height);
|
||||
std::optional<size_t> crossSymbolPos = wcsstr(text.buffer.data(), L" ") - text.buffer.data() + 1;
|
||||
|
||||
commonState.overlayBoxText.Access([&](OverlayBoxText& v) {
|
||||
v = text;
|
||||
@@ -129,6 +130,7 @@ namespace
|
||||
|
||||
d2dState.DrawTextBox(text.buffer.data(),
|
||||
textLen,
|
||||
crossSymbolPos,
|
||||
cornerX,
|
||||
cornerY,
|
||||
screenQuadrantAware,
|
||||
|
||||
@@ -68,10 +68,13 @@ D2DState::D2DState(HWND overlayWindow, std::vector<D2D1::ColorF> solidBrushesCol
|
||||
|
||||
winrt::check_hresult(deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &affineTransformEffect));
|
||||
affineTransformEffect->SetInputEffect(0, shadowEffect.get());
|
||||
|
||||
textRenderer = winrt::make_self<PerGlyphOpacityTextRender>(d2dFactory, rt, solidBrushes[Brush::foreground]);
|
||||
}
|
||||
|
||||
void D2DState::DrawTextBox(const wchar_t* text,
|
||||
const uint32_t textLen,
|
||||
const std::optional<size_t> halfOpaqueSymbolPos,
|
||||
const float centerX,
|
||||
const float centerY,
|
||||
const bool screenQuadrantAware,
|
||||
@@ -147,5 +150,13 @@ void D2DState::DrawTextBox(const wchar_t* text,
|
||||
textBoxRect.rect.right -= TEXT_BOX_PADDING;
|
||||
// Draw text & its box
|
||||
rt->FillRoundedRectangle(textBoxRect, solidBrushes[Brush::background].get());
|
||||
rt->DrawTextLayout(D2D1_POINT_2F{ .x = textRect.left, .y = textRect.top }, textLayout.get(), solidBrushes[Brush::foreground].get());
|
||||
|
||||
if (halfOpaqueSymbolPos.has_value())
|
||||
{
|
||||
DWRITE_TEXT_RANGE textRange = { static_cast<uint32_t>(*halfOpaqueSymbolPos), 2 };
|
||||
auto opacityEffect = winrt::make_self<OpacityEffect>();
|
||||
opacityEffect->alpha = consts::CROSS_OPACITY;
|
||||
winrt::check_hresult(textLayout->SetDrawingEffect(opacityEffect.get(), textRange));
|
||||
}
|
||||
winrt::check_hresult(textLayout->Draw(nullptr, textRenderer.get(), textRect.left, textRect.top));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <d2d1_3.h>
|
||||
#include <wil/com.h>
|
||||
#include <windef.h>
|
||||
|
||||
#include <PerGlyphOpacityTextRender.h>
|
||||
|
||||
enum Brush : size_t
|
||||
{
|
||||
line,
|
||||
@@ -21,14 +24,17 @@ struct D2DState
|
||||
wil::com_ptr<ID2D1HwndRenderTarget> rt;
|
||||
wil::com_ptr<ID2D1BitmapRenderTarget> bitmapRt;
|
||||
wil::com_ptr<IDWriteTextFormat> textFormat;
|
||||
winrt::com_ptr<PerGlyphOpacityTextRender> textRenderer;
|
||||
std::vector<wil::com_ptr<ID2D1SolidColorBrush>> solidBrushes;
|
||||
wil::com_ptr<ID2D1Effect> shadowEffect;
|
||||
wil::com_ptr<ID2D1Effect> affineTransformEffect;
|
||||
|
||||
float dpiScale = 1.f;
|
||||
|
||||
D2DState(HWND window, std::vector<D2D1::ColorF> solidBrushesColors);
|
||||
void DrawTextBox(const wchar_t* text,
|
||||
const uint32_t textLen,
|
||||
const std::optional<size_t> halfOpaqueSymbolPos,
|
||||
const float centerX,
|
||||
const float centerY,
|
||||
const bool screenQuadrantAware,
|
||||
|
||||
@@ -176,6 +176,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
|
||||
uint32_t measureStringBufLen = 0;
|
||||
|
||||
OverlayBoxText text;
|
||||
std::optional<size_t> crossSymbolPos;
|
||||
switch (mts.mode)
|
||||
{
|
||||
case MeasureToolState::Mode::Cross:
|
||||
@@ -184,6 +185,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
|
||||
L"%.0f × %.0f",
|
||||
hMeasure,
|
||||
vMeasure);
|
||||
crossSymbolPos = wcsstr(text.buffer.data(), L" ") - text.buffer.data() + 1;
|
||||
break;
|
||||
case MeasureToolState::Mode::Vertical:
|
||||
measureStringBufLen = swprintf_s(text.buffer.data(),
|
||||
@@ -205,6 +207,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
|
||||
|
||||
d2dState.DrawTextBox(text.buffer.data(),
|
||||
measureStringBufLen,
|
||||
crossSymbolPos,
|
||||
static_cast<float>(cursorPos.x),
|
||||
static_cast<float>(cursorPos.y),
|
||||
true,
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "PerGlyphOpacityTextRender.h"
|
||||
|
||||
PerGlyphOpacityTextRender::PerGlyphOpacityTextRender(
|
||||
wil::com_ptr<ID2D1Factory> pD2DFactory,
|
||||
wil::com_ptr<ID2D1HwndRenderTarget> rt,
|
||||
wil::com_ptr<ID2D1SolidColorBrush> baseBrush) :
|
||||
_pD2DFactory{ pD2DFactory },
|
||||
_rt{ rt },
|
||||
_baseBrush{ baseBrush }
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::DrawGlyphRun(void* /*clientDrawingContext*/,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/,
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if (!clientDrawingEffect)
|
||||
{
|
||||
_rt->DrawGlyphRun(D2D1_POINT_2F{ .x = baselineOriginX, .y = baselineOriginY }, glyphRun, _baseBrush.get(), measuringMode);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Create the path geometry.
|
||||
wil::com_ptr<ID2D1PathGeometry> pathGeometry;
|
||||
hr = _pD2DFactory->CreatePathGeometry(&pathGeometry);
|
||||
|
||||
// Write to the path geometry using the geometry sink.
|
||||
ID2D1GeometrySink* pSink = nullptr;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pathGeometry->Open(&pSink);
|
||||
}
|
||||
|
||||
// Get the glyph run outline geometries back from DirectWrite and place them within the
|
||||
// geometry sink.
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = glyphRun->fontFace->GetGlyphRunOutline(
|
||||
glyphRun->fontEmSize,
|
||||
glyphRun->glyphIndices,
|
||||
glyphRun->glyphAdvances,
|
||||
glyphRun->glyphOffsets,
|
||||
glyphRun->glyphCount,
|
||||
glyphRun->isSideways,
|
||||
glyphRun->bidiLevel % 2,
|
||||
pSink);
|
||||
}
|
||||
|
||||
// Close the geometry sink
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pSink->Close();
|
||||
}
|
||||
|
||||
// Initialize a matrix to translate the origin of the glyph run.
|
||||
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
|
||||
1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY);
|
||||
|
||||
// Create the transformed geometry
|
||||
wil::com_ptr<ID2D1TransformedGeometry> pTransformedGeometry;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = _pD2DFactory->CreateTransformedGeometry(pathGeometry.get(), &matrix, &pTransformedGeometry);
|
||||
}
|
||||
|
||||
float prevOpacity = _baseBrush->GetOpacity();
|
||||
OpacityEffect* opacityEffect = nullptr;
|
||||
hr = clientDrawingEffect->QueryInterface(__uuidof(IDrawingEffect), reinterpret_cast<void**>(&opacityEffect));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
_baseBrush->SetOpacity(opacityEffect->alpha);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
_rt->DrawGeometry(pTransformedGeometry.get(), _baseBrush.get());
|
||||
_rt->FillGeometry(pTransformedGeometry.get(), _baseBrush.get());
|
||||
_baseBrush->SetOpacity(prevOpacity);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::DrawUnderline(void* /*clientDrawingContext*/,
|
||||
FLOAT /*baselineOriginX*/,
|
||||
FLOAT /*baselineOriginY*/,
|
||||
_In_ const DWRITE_UNDERLINE* /*underline*/,
|
||||
IUnknown* /*clientDrawingEffect*/) noexcept
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::DrawStrikethrough(void* /*clientDrawingContext*/,
|
||||
FLOAT /*baselineOriginX*/,
|
||||
FLOAT /*baselineOriginY*/,
|
||||
_In_ const DWRITE_STRIKETHROUGH* /*strikethrough*/,
|
||||
IUnknown* /*clientDrawingEffect*/) noexcept
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::DrawInlineObject(void* /*clientDrawingContext*/,
|
||||
FLOAT /*originX*/,
|
||||
FLOAT /*originY*/,
|
||||
IDWriteInlineObject* /*inlineObject*/,
|
||||
BOOL /*isSideways*/,
|
||||
BOOL /*isRightToLeft*/,
|
||||
IUnknown* /*clientDrawingEffect*/) noexcept
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::IsPixelSnappingDisabled(void* /*clientDrawingContext*/, BOOL* isDisabled) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled);
|
||||
|
||||
*isDisabled = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::GetCurrentTransform(void* /*clientDrawingContext*/, DWRITE_MATRIX* transform) noexcept
|
||||
{
|
||||
D2D1_MATRIX_3X2_F d2d1Matrix{};
|
||||
|
||||
_rt->GetTransform(&d2d1Matrix);
|
||||
|
||||
transform->dx = d2d1Matrix.dx;
|
||||
transform->dy = d2d1Matrix.dy;
|
||||
transform->m11 = d2d1Matrix.m11;
|
||||
transform->m12 = d2d1Matrix.m12;
|
||||
transform->m21 = d2d1Matrix.m21;
|
||||
transform->m22 = d2d1Matrix.m22;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PerGlyphOpacityTextRender::GetPixelsPerDip(void* /*clientDrawingContext*/, FLOAT* pixelsPerDip) noexcept
|
||||
{
|
||||
*pixelsPerDip = 1.f;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <winrt/base.h>
|
||||
#include <wil/resource.h>
|
||||
#include <Windows.h>
|
||||
#include <dwrite.h>
|
||||
|
||||
struct __declspec(uuid("{01557C9F-E3DD-4C28-AE64-E731EAB479CC}")) IDrawingEffect : IUnknown
|
||||
{
|
||||
};
|
||||
|
||||
struct OpacityEffect : winrt::implements<OpacityEffect, IDrawingEffect>
|
||||
{
|
||||
float alpha = 1.f;
|
||||
};
|
||||
|
||||
struct PerGlyphOpacityTextRender : winrt::implements<PerGlyphOpacityTextRender, IDWriteTextRenderer>
|
||||
{
|
||||
wil::com_ptr<ID2D1Factory> _pD2DFactory;
|
||||
wil::com_ptr<ID2D1HwndRenderTarget> _rt;
|
||||
wil::com_ptr<ID2D1SolidColorBrush> _baseBrush;
|
||||
|
||||
PerGlyphOpacityTextRender(
|
||||
wil::com_ptr<ID2D1Factory> pD2DFactory,
|
||||
wil::com_ptr<ID2D1HwndRenderTarget> rt,
|
||||
wil::com_ptr<ID2D1SolidColorBrush> baseBrush);
|
||||
HRESULT __stdcall DrawGlyphRun(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
HRESULT __stdcall DrawUnderline(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_UNDERLINE* underline,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
HRESULT __stdcall DrawStrikethrough(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_STRIKETHROUGH* strikethrough,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
HRESULT __stdcall DrawInlineObject(void* clientDrawingContext,
|
||||
FLOAT originX,
|
||||
FLOAT originY,
|
||||
IDWriteInlineObject* inlineObject,
|
||||
BOOL isSideways,
|
||||
BOOL isRightToLeft,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
HRESULT __stdcall IsPixelSnappingDisabled(void* clientDrawingContext, BOOL* isDisabled) noexcept override;
|
||||
HRESULT __stdcall GetCurrentTransform(void* clientDrawingContext, DWRITE_MATRIX* transform) noexcept override;
|
||||
HRESULT __stdcall GetPixelsPerDip(void* clientDrawingContext, FLOAT* pixelsPerDip) noexcept override;
|
||||
};
|
||||
@@ -77,6 +77,7 @@
|
||||
<ClInclude Include="constants.h" />
|
||||
<ClInclude Include="D2DState.h" />
|
||||
<ClInclude Include="MeasureToolOverlayUI.h" />
|
||||
<ClInclude Include="PerGlyphOpacityTextRender.h" />
|
||||
<ClInclude Include="Settings.h" />
|
||||
<ClInclude Include="PowerToys.MeasureToolCore.h">
|
||||
<DependentUpon>PowerToys.MeasureToolCore.idl</DependentUpon>
|
||||
@@ -97,6 +98,7 @@
|
||||
<ClCompile Include="EdgeDetection.cpp" />
|
||||
<ClCompile Include="MeasureToolOverlayUI.cpp" />
|
||||
<ClCompile Include="OverlayUI.cpp" />
|
||||
<ClCompile Include="PerGlyphOpacityTextRender.cpp" />
|
||||
<ClCompile Include="PowerToys.MeasureToolCore.cpp">
|
||||
<DependentUpon>PowerToys.MeasureToolCore.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
<ClCompile Include="BoundsToolOverlayUI.cpp" />
|
||||
<ClCompile Include="MeasureToolOverlayUI.cpp" />
|
||||
<ClCompile Include="..\MeasureToolModuleInterface\trace.cpp" />
|
||||
<ClCompile Include="Clipboard.cpp" />
|
||||
<ClCompile Include="PerGlyphOpacityTextRender.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -29,6 +31,8 @@
|
||||
<ClInclude Include="MeasureToolOverlayUI.h" />
|
||||
<ClInclude Include="constants.h" />
|
||||
<ClInclude Include="..\MeasureToolModuleInterface\trace.h" />
|
||||
<ClInclude Include="Clipboard.h" />
|
||||
<ClInclude Include="PerGlyphOpacityTextRender.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Assets">
|
||||
|
||||
@@ -13,9 +13,10 @@ namespace consts
|
||||
constexpr inline float FEET_HALF_LENGTH = 2.f;
|
||||
constexpr inline float SHADOW_OPACITY = .4f;
|
||||
constexpr inline float SHADOW_RADIUS = 6.f;
|
||||
const inline auto SHADOW_OFFSET = 5.f;
|
||||
constexpr inline float SHADOW_OFFSET = 5.f;
|
||||
constexpr inline float CROSS_OPACITY = .25f;
|
||||
|
||||
/* Offset to not try not to use the cursor immediate pixels in measuring, but it seems only necessary for continuous mode. */
|
||||
constexpr inline long CURSOR_OFFSET_AMOUNT_X = 4;
|
||||
constexpr inline long CURSOR_OFFSET_AMOUNT_Y = 4;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user