diff options
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r-- | win32/PlatWin.cxx | 382 |
1 files changed, 166 insertions, 216 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 5529b7ba6..cc6d83fb1 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -566,9 +566,7 @@ public: void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override; void Copy(PRectangle rc, Point from, Surface &surfaceSource) override; - size_t PositionFromX(const IScreenLine *screenLine, XYPOSITION xDistance, bool charPosition) override; - XYPOSITION XFromPosition(const IScreenLine *screenLine, size_t caretPosition) override; - std::vector<Interval> FindRangeIntervals(const IScreenLine *screenLine, size_t start, size_t end) override; + std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override; void DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions); void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; @@ -924,16 +922,8 @@ void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) { static_cast<int>(from.x), static_cast<int>(from.y), SRCCOPY); } -size_t SurfaceGDI::PositionFromX(const IScreenLine *, XYPOSITION, bool) { - return 0; -} - -XYPOSITION SurfaceGDI::XFromPosition(const IScreenLine *, size_t) { - return 0; -} - -std::vector<Interval> SurfaceGDI::FindRangeIntervals(const IScreenLine *, size_t, size_t) { - return std::vector<Interval>(); +std::unique_ptr<IScreenLineLayout> SurfaceGDI::Layout(const IScreenLine *) { + return {}; } typedef VarBuffer<int, stackBufferLength> TextPositionsI; @@ -1158,14 +1148,7 @@ public: void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override; void Copy(PRectangle rc, Point from, Surface &surfaceSource) override; - size_t PositionFromX(const IScreenLine *screenLine, XYPOSITION xDistance, bool charPosition) override; - XYPOSITION XFromPosition(const IScreenLine *screenLine, size_t caretPosition) override; - std::vector<Interval> FindRangeIntervals(const IScreenLine *screenLine, size_t start, size_t end) override; - - static void FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs); - static std::wstring ReplaceRepresentation(std::string_view text); - static size_t GetPositionInLayout(std::string_view text, size_t position); - static size_t GetPositionInString(std::string_view text, size_t position); + std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override; void DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions); void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; @@ -1725,36 +1708,150 @@ HRESULT STDMETHODCALLTYPE BlobInline::GetBreakConditions( return S_OK; } -// Get the position from the provided x +class ScreenLineLayout : public IScreenLineLayout { + IDWriteTextLayout *textLayout = nullptr; + std::string text; + std::wstring buffer; + std::vector<BlobInline> blobs; + static void FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs); + static std::wstring ReplaceRepresentation(std::string_view text); + static size_t GetPositionInLayout(std::string_view text, size_t position); +public: + ScreenLineLayout(const IScreenLine *screenLine); + // Deleted so ScreenLineLayout objects can not be copied + ScreenLineLayout(const ScreenLineLayout &) = delete; + ScreenLineLayout(ScreenLineLayout &&) = delete; + ScreenLineLayout &operator=(const ScreenLineLayout &) = delete; + ScreenLineLayout &operator=(ScreenLineLayout &&) = delete; + ~ScreenLineLayout() override; + size_t PositionFromX(XYPOSITION xDistance, bool charPosition) override; + XYPOSITION XFromPosition(size_t caretPosition) override; + std::vector<Interval> FindRangeIntervals(size_t start, size_t end) override; +}; + +// Each char can have its own style, so we fill the textLayout with the textFormat of each char + +void ScreenLineLayout::FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs) { + // Reserve enough entries up front so they are not moved and the pointers handed + // to textLayout remain valid. + const ptrdiff_t numRepresentations = screenLine->RepresentationCount(); + std::string_view text = screenLine->Text(); + const ptrdiff_t numTabs = std::count(std::begin(text), std::end(text), '\t'); + blobs.reserve(numRepresentations + numTabs); + + UINT32 layoutPosition = 0; + + for (size_t bytePosition = 0; bytePosition < screenLine->Length();) { + const unsigned char uch = screenLine->Text()[bytePosition]; + const unsigned int byteCount = UTF8BytesOfLead[uch]; + const UINT32 codeUnits = static_cast<UINT32>(UTF16LengthFromUTF8ByteCount(byteCount)); + const DWRITE_TEXT_RANGE textRange = { layoutPosition, codeUnits }; + + XYPOSITION representationWidth = screenLine->RepresentationWidth(bytePosition); + if ((representationWidth == 0.0f) && (screenLine->Text()[bytePosition] == '\t')) { + Point realPt; + DWRITE_HIT_TEST_METRICS realCaretMetrics; + textLayout->HitTestTextPosition( + layoutPosition, + false, // trailing if false, else leading edge + &realPt.x, + &realPt.y, + &realCaretMetrics + ); + + const XYPOSITION nextTab = screenLine->TabPositionAfter(realPt.x); + representationWidth = nextTab - realPt.x; + } + if (representationWidth > 0.0f) { + blobs.push_back(BlobInline(representationWidth)); + textLayout->SetInlineObject(&blobs.back(), textRange); + }; + + FormatAndMetrics *pfm = + static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(bytePosition)->GetID()); + + const unsigned int fontFamilyNameSize = pfm->pTextFormat->GetFontFamilyNameLength(); + std::vector<WCHAR> fontFamilyName(fontFamilyNameSize + 1); + + pfm->pTextFormat->GetFontFamilyName(fontFamilyName.data(), fontFamilyNameSize + 1); + textLayout->SetFontFamilyName(fontFamilyName.data(), textRange); + + textLayout->SetFontSize(pfm->pTextFormat->GetFontSize(), textRange); + textLayout->SetFontWeight(pfm->pTextFormat->GetFontWeight(), textRange); + textLayout->SetFontStyle(pfm->pTextFormat->GetFontStyle(), textRange); -size_t SurfaceD2D::PositionFromX(const IScreenLine *screenLine, XYPOSITION xDistance, bool charPosition) { + const unsigned int localNameSize = pfm->pTextFormat->GetLocaleNameLength(); + std::vector<WCHAR> localName(localNameSize + 1); + + pfm->pTextFormat->GetLocaleName(localName.data(), localNameSize); + textLayout->SetLocaleName(localName.data(), textRange); + + textLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange); + + IDWriteFontCollection *fontCollection = nullptr; + if (SUCCEEDED(pfm->pTextFormat->GetFontCollection(&fontCollection))) { + textLayout->SetFontCollection(fontCollection, textRange); + } + + bytePosition += byteCount; + layoutPosition += codeUnits; + } + +} + +/* Convert to a wide character string and replace tabs with X to stop DirectWrite tab expansion */ + +std::wstring ScreenLineLayout::ReplaceRepresentation(std::string_view text) { + const TextWide wideText(text, true); + std::wstring ws(wideText.buffer, wideText.tlen); + std::replace(ws.begin(), ws.end(), L'\t', L'X'); + return ws; +} + +// Finds the position in the wide character version of the text. + +size_t ScreenLineLayout::GetPositionInLayout(std::string_view text, size_t position) { + const std::string_view textUptoPosition = text.substr(0, position); + return UTF16Length(textUptoPosition); +} + +ScreenLineLayout::ScreenLineLayout(const IScreenLine *screenLine) { // If the text is empty, then no need to go through this function if (!screenLine->Length()) - return 0; + return; + + text = screenLine->Text(); // Get textFormat FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID()); if (!pIDWriteFactory || !pfm->pTextFormat) { - return 0; + return; } // Convert the string to wstring and replace the original control characters with their representative chars. - std::wstring buffer = ReplaceRepresentation(screenLine->Text()); + buffer = ReplaceRepresentation(screenLine->Text()); // Create a text layout - IDWriteTextLayout *textLayout = nullptr; - const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(buffer.c_str(), static_cast<UINT32>(buffer.length()), pfm->pTextFormat, screenLine->Width(), screenLine->Height(), &textLayout); if (!SUCCEEDED(hrCreate)) { - return 0; + return; } - std::vector<BlobInline> blobs; - // Fill the textLayout chars with their own formats FillTextLayoutFormats(screenLine, textLayout, blobs); +} + +ScreenLineLayout::~ScreenLineLayout() { +} + +// Get the position from the provided x + +size_t ScreenLineLayout::PositionFromX(XYPOSITION xDistance, bool charPosition) { + if (!textLayout) { + return 0; + } // Returns the text position corresponding to the mouse x,y. // If hitting the trailing side of a cluster, return the @@ -1800,43 +1897,17 @@ size_t SurfaceD2D::PositionFromX(const IScreenLine *screenLine, XYPOSITION xDist } // Get the character position in original string - return GetPositionInString(screenLine->Text(), pos); + return UTF8PositionFromUTF16Position(text, pos); } // Finds the point of the caret position -XYPOSITION SurfaceD2D::XFromPosition(const IScreenLine *screenLine, size_t caretPosition) { - // If the text is empty, then no need to go through this function - if (!screenLine->Length()) - return 0.0f; - - // Convert byte positions to wchar_t positions - const size_t position = GetPositionInLayout(screenLine->Text(), caretPosition); - - // Get textFormat - FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID()); - - if (!pIDWriteFactory || !pfm->pTextFormat) { - return 0.0f; +XYPOSITION ScreenLineLayout::XFromPosition(size_t caretPosition) { + if (!textLayout) { + return 0.0; } - - // Convert the string to wstring and replace the original control characters with their representative chars. - std::wstring buffer = ReplaceRepresentation(screenLine->Text()); - - // Create a text layout - IDWriteTextLayout *textLayout = nullptr; - - const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(buffer.c_str(), static_cast<UINT32>(buffer.length()+1), - pfm->pTextFormat, screenLine->Width(), screenLine->Height(), &textLayout); - - if (!SUCCEEDED(hrCreate)) { - return 0.0f; - } - - std::vector<BlobInline> blobs; - - // Fill the textLayout chars with their own formats - FillTextLayoutFormats(screenLine, textLayout, blobs); + // Convert byte positions to wchar_t positions + const size_t position = GetPositionInLayout(text, caretPosition); // Translate text character offset to point x,y. DWRITE_HIT_TEST_METRICS caretMetrics; @@ -1857,42 +1928,16 @@ XYPOSITION SurfaceD2D::XFromPosition(const IScreenLine *screenLine, size_t caret // Find the selection range rectangles -std::vector<Interval> SurfaceD2D::FindRangeIntervals(const IScreenLine *screenLine, size_t start, size_t end) { +std::vector<Interval> ScreenLineLayout::FindRangeIntervals(size_t start, size_t end) { std::vector<Interval> ret; - // If the text is empty, then no need to go through this function - if (!screenLine->Length()) { + if (!textLayout || (start == end)) { return ret; } // Convert byte positions to wchar_t positions - const size_t startPos = GetPositionInLayout(screenLine->Text(), start); - const size_t endPos = GetPositionInLayout(screenLine->Text(), end); - - // Get textFormat - FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID()); - - if (!pIDWriteFactory || !pfm->pTextFormat) { - return ret; - } - - // Convert the string to wstring and replace the original control characters with their representative chars. - std::wstring buffer = ReplaceRepresentation(screenLine->Text()); - - // Create a text layout - IDWriteTextLayout *textLayout = nullptr; - - const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(buffer.c_str(), static_cast<UINT32>(buffer.length() + 1), - pfm->pTextFormat, screenLine->Width(), screenLine->Height(), &textLayout); - - if (!SUCCEEDED(hrCreate)) { - return ret; - } - - std::vector<BlobInline> blobs; - - // Fill the textLayout chars with their own formats - FillTextLayoutFormats(screenLine, textLayout, blobs); + const size_t startPos = GetPositionInLayout(text, start); + const size_t endPos = GetPositionInLayout(text, end); // Find selection range length const size_t rangeLength = (endPos > startPos) ? (endPos - startPos) : (startPos - endPos); @@ -1900,28 +1945,31 @@ std::vector<Interval> SurfaceD2D::FindRangeIntervals(const IScreenLine *screenLi // Determine actual number of hit-test ranges UINT32 actualHitTestCount = 0; - if (rangeLength > 0) { - textLayout->HitTestTextRange( - static_cast<UINT32>(startPos), - static_cast<UINT32>(rangeLength), - 0, // x - 0, // y - nullptr, - 0, // metrics count - &actualHitTestCount - ); - } + // First try with 2 elements and if more needed, allocate. + std::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(2); + textLayout->HitTestTextRange( + static_cast<UINT32>(startPos), + static_cast<UINT32>(rangeLength), + 0, // x + 0, // y + hitTestMetrics.data(), + static_cast<UINT32>(hitTestMetrics.size()), + &actualHitTestCount + ); - // Allocate enough room to return all hit-test metrics. - std::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(actualHitTestCount); + if (actualHitTestCount == 0) { + return ret; + } - if (rangeLength > 0) { + if (hitTestMetrics.size() < actualHitTestCount) { + // Allocate enough room to return all hit-test metrics. + hitTestMetrics.resize(actualHitTestCount); textLayout->HitTestTextRange( static_cast<UINT32>(startPos), static_cast<UINT32>(rangeLength), 0, // x 0, // y - &hitTestMetrics[0], + hitTestMetrics.data(), static_cast<UINT32>(hitTestMetrics.size()), &actualHitTestCount ); @@ -1929,120 +1977,22 @@ std::vector<Interval> SurfaceD2D::FindRangeIntervals(const IScreenLine *screenLi // Get the selection ranges behind the text. - if (actualHitTestCount > 0) { - for (size_t i = 0; i < actualHitTestCount; ++i) { - // Draw selection rectangle - const DWRITE_HIT_TEST_METRICS &htm = hitTestMetrics[i]; - Interval selectionInterval; - - selectionInterval.left = htm.left; - selectionInterval.right = htm.left + htm.width; - - ret.push_back(selectionInterval); - } - } - return ret; -} - -// Each char can have its own style, so we fill the textLayout with the textFormat of each char - -void SurfaceD2D::FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs) { - // Reserve enough entries up front so they are not moved and the pointers handed - // to textLayout remain valid. - const ptrdiff_t numRepresentations = screenLine->RepresentationCount(); - std::string_view text = screenLine->Text(); - const ptrdiff_t numTabs = std::count(std::begin(text), std::end(text), '\t'); - blobs.reserve(numRepresentations + numTabs); - - UINT32 layoutPosition = 0; - - for (size_t bytePosition = 0; bytePosition < screenLine->Length();) { - const unsigned char uch = screenLine->Text()[bytePosition]; - const unsigned int byteCount = UTF8BytesOfLead[uch]; - const UINT32 codeUnits = static_cast<UINT32>(UTF16LengthFromUTF8ByteCount(byteCount)); - const DWRITE_TEXT_RANGE textRange = { layoutPosition, codeUnits }; - - XYPOSITION representationWidth = screenLine->RepresentationWidth(bytePosition); - if ((representationWidth == 0.0f) && (screenLine->Text()[bytePosition] == '\t')) { - Point realPt; - DWRITE_HIT_TEST_METRICS realCaretMetrics; - textLayout->HitTestTextPosition( - layoutPosition, - false, // trailing if false, else leading edge - &realPt.x, - &realPt.y, - &realCaretMetrics - ); - - const XYPOSITION nextTab = (static_cast<int>((realPt.x + screenLine->TabWidthMinimumPixels()) / screenLine->TabWidth()) + 1) * screenLine->TabWidth(); - representationWidth = nextTab - realPt.x; - } - if (representationWidth > 0.0f) { - blobs.push_back(BlobInline(representationWidth)); - textLayout->SetInlineObject(&blobs.back(), textRange); - }; - - FormatAndMetrics *pfm = - static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(bytePosition)->GetID()); - - const unsigned int fontFamilyNameSize = pfm->pTextFormat->GetFontFamilyNameLength(); - std::vector<WCHAR> fontFamilyName(fontFamilyNameSize + 1); - - pfm->pTextFormat->GetFontFamilyName(fontFamilyName.data(), fontFamilyNameSize + 1); - textLayout->SetFontFamilyName(fontFamilyName.data(), textRange); - - textLayout->SetFontSize(pfm->pTextFormat->GetFontSize(), textRange); - textLayout->SetFontWeight(pfm->pTextFormat->GetFontWeight(), textRange); - textLayout->SetFontStyle(pfm->pTextFormat->GetFontStyle(), textRange); - - const unsigned int localNameSize = pfm->pTextFormat->GetLocaleNameLength(); - std::vector<WCHAR> localName(localNameSize + 1); + for (size_t i = 0; i < actualHitTestCount; ++i) { + // Store selection rectangle + const DWRITE_HIT_TEST_METRICS &htm = hitTestMetrics[i]; + Interval selectionInterval; - pfm->pTextFormat->GetLocaleName(localName.data(), localNameSize); - textLayout->SetLocaleName(localName.data(), textRange); - - textLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange); + selectionInterval.left = htm.left; + selectionInterval.right = htm.left + htm.width; - IDWriteFontCollection *fontCollection; - if (SUCCEEDED(pfm->pTextFormat->GetFontCollection(&fontCollection))) { - textLayout->SetFontCollection(fontCollection, textRange); - } - - bytePosition += byteCount; - layoutPosition += codeUnits; + ret.push_back(selectionInterval); } + return ret; } -/* Convert to a wide character string and replace tabs with X to stop DirectWrite tab expansion */ - -std::wstring SurfaceD2D::ReplaceRepresentation(std::string_view text) { - const TextWide wideText(text, true); - std::wstring ws(wideText.buffer, wideText.tlen); - std::replace(ws.begin(), ws.end(), L'\t', L'X'); - return ws; -} - -// Finds the position in the wide character version of the text. - -size_t SurfaceD2D::GetPositionInLayout(std::string_view text, size_t position) { - const std::string_view textUptoPosition = text.substr(0, position); - return UTF16Length(textUptoPosition); -} - -// Converts position in wide character string to position in string. - -size_t SurfaceD2D::GetPositionInString(std::string_view text, size_t position) { - size_t posInString = 0; - for (size_t wLength = 0; (!text.empty()) && (wLength < position);) { - const unsigned char uch = text[0]; - const unsigned int byteCount = UTF8BytesOfLead[uch]; - wLength += UTF16LengthFromUTF8ByteCount(byteCount); - posInString += byteCount; - text.remove_prefix(byteCount); - } - - return posInString; +std::unique_ptr<IScreenLineLayout> SurfaceD2D::Layout(const IScreenLine *screenLine) { + return std::make_unique<ScreenLineLayout>(screenLine); } void SurfaceD2D::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) { |