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;