diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
index be5d15c58f6..ad7c9dcc07f 100644
--- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
+++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj
@@ -198,6 +198,9 @@
{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}
false
+
+ {ef3e32a7-5ff6-42b4-b6e2-96cd7d033f00}
+
diff --git a/src/host/ft_fuzzer/fuzzmain.cpp b/src/host/ft_fuzzer/fuzzmain.cpp
index 674e898d376..fefe1225aa2 100644
--- a/src/host/ft_fuzzer/fuzzmain.cpp
+++ b/src/host/ft_fuzzer/fuzzmain.cpp
@@ -82,8 +82,8 @@ struct NullDeviceComm : public IDeviceComm
CONSOLE_API_CONNECTINFO fakeConnectInfo{};
fakeConnectInfo.ConsoleInfo.SetShowWindow(SW_NORMAL);
- fakeConnectInfo.ConsoleInfo.SetScreenBufferSize(til::size{ 80, 25 });
- fakeConnectInfo.ConsoleInfo.SetWindowSize(til::size{ 80, 25 });
+ fakeConnectInfo.ConsoleInfo.SetScreenBufferSize(til::size{ 80, 25 }.to_win32_coord());
+ fakeConnectInfo.ConsoleInfo.SetWindowSize(til::size{ 80, 25 }.to_win32_coord());
fakeConnectInfo.ConsoleInfo.SetStartupFlags(STARTF_USECOUNTCHARS);
wcscpy_s(fakeConnectInfo.Title, fakeTitle.data());
fakeConnectInfo.TitleLength = gsl::narrow_cast(fakeTitle.size() * sizeof(wchar_t)); // bytes, not wchars
diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp
index c8e9ca67b2a..b7a267422c7 100644
--- a/src/renderer/atlas/AtlasEngine.api.cpp
+++ b/src/renderer/atlas/AtlasEngine.api.cpp
@@ -494,6 +494,7 @@ CATCH_RETURN()
void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
{
+ _api.hyperlinkHoveredId = hoveredId;
}
#pragma endregion
@@ -580,7 +581,7 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo
const auto strikethroughOffsetInPx = static_cast(-metrics.strikethroughPosition) * designUnitsPerPx;
const auto strikethroughThicknessInPx = static_cast(metrics.strikethroughThickness) * designUnitsPerPx;
const auto lineThickness = gsl::narrow(std::round(std::min(underlineThicknessInPx, strikethroughThicknessInPx)));
- const auto underlinePos = gsl::narrow(std::round(baseline + underlineOffsetInPx - lineThickness / 2.0));
+ const auto underlinePos = gsl::narrow(std::ceil(baseline + underlineOffsetInPx - lineThickness / 2.0));
const auto strikethroughPos = gsl::narrow(std::round(baseline + strikethroughOffsetInPx - lineThickness / 2.0));
auto fontName = wil::make_process_heap_string(requestedFaceName);
diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp
index 4e4e75966a0..9e953305b93 100644
--- a/src/renderer/atlas/AtlasEngine.cpp
+++ b/src/renderer/atlas/AtlasEngine.cpp
@@ -464,12 +464,13 @@ try
const auto x = gsl::narrow_cast(clamp(coord.X, 0, _api.cellCount.x));
const auto y = gsl::narrow_cast(clamp(coord.Y, 0, _api.cellCount.y));
- if (_api.currentRow != y)
+ if (_api.lastPaintBufferLineCoord.y != y)
{
_flushBufferLine();
}
- _api.currentRow = y;
+ _api.lastPaintBufferLineCoord = { x, y };
+ _api.bufferLineWasHyperlinked = false;
// Due to the current IRenderEngine interface (that wasn't refactored yet) we need to assemble
// the current buffer line first as the remaining function operates on whole lines of text.
@@ -502,9 +503,21 @@ try
CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const COORD coordTarget) noexcept
+try
{
+ if (!_api.bufferLineWasHyperlinked && lines.test(GridLines::Underline) && WI_IsFlagClear(_api.flags, CellFlags::Underline))
+ {
+ _api.bufferLineWasHyperlinked = true;
+
+ WI_UpdateFlagsInMask(_api.flags, CellFlags::Underline | CellFlags::UnderlineDotted | CellFlags::UnderlineDouble, CellFlags::Underline);
+
+ const BufferLineMetadata metadata{ _api.currentColor, _api.flags };
+ const size_t x = _api.lastPaintBufferLineCoord.x;
+ std::fill_n(_api.bufferLineMetadata.data() + x, _api.bufferLineMetadata.size() - x, metadata);
+ }
return S_OK;
}
+CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::PaintSelection(SMALL_RECT rect) noexcept
try
@@ -568,16 +581,24 @@ try
if (!isSettingDefaultBrushes)
{
+ const auto hyperlinkId = textAttributes.GetHyperlinkId();
+
auto flags = CellFlags::None;
WI_SetFlagIf(flags, CellFlags::BorderLeft, textAttributes.IsLeftVerticalDisplayed());
WI_SetFlagIf(flags, CellFlags::BorderTop, textAttributes.IsTopHorizontalDisplayed());
WI_SetFlagIf(flags, CellFlags::BorderRight, textAttributes.IsRightVerticalDisplayed());
WI_SetFlagIf(flags, CellFlags::BorderBottom, textAttributes.IsBottomHorizontalDisplayed());
WI_SetFlagIf(flags, CellFlags::Underline, textAttributes.IsUnderlined());
- WI_SetFlagIf(flags, CellFlags::UnderlineDotted, textAttributes.IsHyperlink());
+ WI_SetFlagIf(flags, CellFlags::UnderlineDotted, hyperlinkId != 0);
WI_SetFlagIf(flags, CellFlags::UnderlineDouble, textAttributes.IsDoublyUnderlined());
WI_SetFlagIf(flags, CellFlags::Strikethrough, textAttributes.IsCrossedOut());
+ if (_api.hyperlinkHoveredId && _api.hyperlinkHoveredId == hyperlinkId)
+ {
+ WI_SetFlag(flags, CellFlags::Underline);
+ WI_ClearAllFlags(flags, CellFlags::UnderlineDotted | CellFlags::UnderlineDouble);
+ }
+
const u32x2 newColors{ gsl::narrow_cast(fg | 0xff000000), gsl::narrow_cast(bg | _api.backgroundOpaqueMixin) };
const AtlasKeyAttributes attributes{ 0, textAttributes.IsBold(), textAttributes.IsItalic(), 0 };
@@ -1444,7 +1465,7 @@ void AtlasEngine::_emplaceGlyph(IDWriteFontFace* fontFace, float scale, size_t b
const auto valueData = value.data();
const auto coords = &valueData->coords[0];
- const auto data = _getCell(x1, _api.currentRow);
+ const auto data = _getCell(x1, _api.lastPaintBufferLineCoord.y);
for (u32 i = 0; i < cellCount; ++i)
{
diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h
index 4f930de2154..e11d3fb3c1a 100644
--- a/src/renderer/atlas/AtlasEngine.h
+++ b/src/renderer/atlas/AtlasEngine.h
@@ -735,10 +735,13 @@ namespace Microsoft::Console::Render
u32 backgroundOpaqueMixin = 0xff000000; // changes are flagged as ApiInvalidations::Device
u32x2 currentColor;
AtlasKeyAttributes attributes{};
- u16 currentRow = 0;
+ u16x2 lastPaintBufferLineCoord;
CellFlags flags = CellFlags::None;
// SetSelectionBackground()
u32 selectionColor = 0x7fffffff;
+ // UpdateHyperlinkHoveredId()
+ u16 hyperlinkHoveredId = 0;
+ bool bufferLineWasHyperlinked = false;
// dirtyRect is a computed value based on invalidatedRows.
til::rect dirtyRect;