diff options
author | Neil <nyamatongwe@gmail.com> | 2018-06-02 08:37:47 +1000 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2018-06-02 08:37:47 +1000 |
commit | 2e003602f29edff5d38137472edee82fab9f5376 (patch) | |
tree | efcd2d4466c2bb894d5354d0652e986fdfcbea22 | |
parent | e67783d907b0ae186d794b205cf89c645fe50270 (diff) | |
download | scintilla-mirror-2e003602f29edff5d38137472edee82fab9f5376.tar.gz |
Define IScreenLineLayout as the main interface for implementing bidirectional
features by platform code.
Implement IScreenLineLayout for Win32 / DirectWrite as ScreenLineLayout.
-rw-r--r-- | cocoa/PlatCocoa.h | 4 | ||||
-rw-r--r-- | cocoa/PlatCocoa.mm | 12 | ||||
-rw-r--r-- | gtk/PlatGTK.cxx | 16 | ||||
-rw-r--r-- | include/Platform.h | 12 | ||||
-rw-r--r-- | qt/ScintillaEditBase/PlatQt.cpp | 14 | ||||
-rw-r--r-- | qt/ScintillaEditBase/PlatQt.h | 4 | ||||
-rw-r--r-- | src/EditView.cxx | 15 | ||||
-rw-r--r-- | win32/PlatWin.cxx | 382 |
8 files changed, 194 insertions, 265 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index bc80e9fce..7a2c578e9 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -98,9 +98,7 @@ public: void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override; void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override; void Copy(PRectangle rc, Scintilla::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 DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index 62a0ba5f0..aca57b81a 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -821,16 +821,8 @@ void SurfaceImpl::Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSou // Bidirectional text support for Arabic and Hebrew not currently implemented on Cocoa. -size_t SurfaceImpl::PositionFromX(const IScreenLine *, XYPOSITION, bool) { - return 0; -} - -XYPOSITION SurfaceImpl::XFromPosition(const IScreenLine *, size_t) { - return 0; -} - -std::vector<Interval> SurfaceImpl::FindRangeIntervals(const IScreenLine *, size_t, size_t) { - return std::vector<Interval>(); +std::unique_ptr<IScreenLineLayout> SurfaceImpl::Layout(const IScreenLine *screenLine) { + return {}; } //-------------------------------------------------------------------------------------------------- diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index ce0863462..1fbdf4835 100644 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -166,9 +166,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 DrawTextBase(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore); void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; @@ -630,16 +628,8 @@ void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) { } } -size_t SurfaceImpl::PositionFromX(const IScreenLine *, XYPOSITION, bool) { - return 0; -} - -XYPOSITION SurfaceImpl::XFromPosition(const IScreenLine *, size_t) { - return 0; -} - -std::vector<Interval> SurfaceImpl::FindRangeIntervals(const IScreenLine *, size_t, size_t) { - return std::vector<Interval>(); +std::unique_ptr<IScreenLineLayout> SurfaceImpl::Layout(const IScreenLine *) { + return {}; } std::string UTF8FromLatin1(std::string_view text) { diff --git a/include/Platform.h b/include/Platform.h index 30f05d88a..a70a00b78 100644 --- a/include/Platform.h +++ b/include/Platform.h @@ -336,6 +336,14 @@ struct Interval { XYPOSITION right; }; +class IScreenLineLayout { +public: + virtual ~IScreenLineLayout() = default; + virtual size_t PositionFromX(XYPOSITION xDistance, bool charPosition) = 0; + virtual XYPOSITION XFromPosition(size_t caretPosition) = 0; + virtual std::vector<Interval> FindRangeIntervals(size_t start, size_t end) = 0; +}; + /** * A surface abstracts a place to draw. */ @@ -373,9 +381,7 @@ public: virtual void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)=0; virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0; - virtual size_t PositionFromX(const IScreenLine *screenLine, XYPOSITION xDistance, bool charPosition)=0; - virtual XYPOSITION XFromPosition(const IScreenLine *screenLine, size_t caretPosition)=0; - virtual std::vector<Interval> FindRangeIntervals(const IScreenLine *screenLine, size_t start, size_t end)=0; + virtual std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) = 0; virtual void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) = 0; virtual void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) = 0; diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp index 325878ae5..9ae9ce03b 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -409,19 +409,9 @@ void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) GetPainter()->drawPixmap(rc.left, rc.top, *pixmap, from.x, from.y, -1, -1); } -size_t SurfaceImpl::PositionFromX(const IScreenLine *, XYPOSITION, bool) +std::unique_ptr<IScreenLineLayout> SurfaceImpl::Layout(const IScreenLine *) { - return 0; -} - -XYPOSITION SurfaceImpl::XFromPosition(const IScreenLine *, size_t) -{ - return 0; -} - -std::vector<Interval> SurfaceImpl::FindRangeIntervals(const IScreenLine *, size_t, size_t) -{ - return std::vector<Interval>(); + return {}; } void SurfaceImpl::DrawTextNoClip(PRectangle rc, diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h index 1b83035a1..7c2ed2fa4 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -105,9 +105,7 @@ public: 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 DrawTextNoClip(PRectangle rc, Font &font, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; diff --git a/src/EditView.cxx b/src/EditView.cxx index 5b58d367a..7e2694366 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -650,7 +650,8 @@ Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, S // Get the point from current position const ScreenLine screenLine(ll, subLine, vs, rcClient.right, tabWidthMinimumPixels); - pt.x = surface->XFromPosition(&screenLine, caretPosition); + std::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine); + pt.x = slLayout->XFromPosition(caretPosition); pt.x += vs.textStart - model.xOffset; @@ -719,7 +720,8 @@ SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditMo UpdateBidiData(model, vs, ll); const ScreenLine screenLine(ll, subLine, vs, rcClient.right, tabWidthMinimumPixels); - positionInLine = surface->PositionFromX(&screenLine, static_cast<XYPOSITION>(pt.x), charPosition) + + std::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine); + positionInLine = slLayout->PositionFromX(static_cast<XYPOSITION>(pt.x), charPosition) + rangeSubLine.start; } else { positionInLine = ll->FindPositionFromX(static_cast<XYPOSITION>(pt.x + subLineStart), @@ -1096,7 +1098,8 @@ static void DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position en ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right - xStart, tabWidthMinimumPixels); const Range lineRange = ll->SubLineRange(subLine, LineLayout::Scope::visibleOnly); - std::vector<Interval> intervals = surface->FindRangeIntervals(&screenLine, + std::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine); + std::vector<Interval> intervals = slLayout->FindRangeIntervals( startPos - lineRange.start, endPos - lineRange.start); for (const Interval &interval : intervals) { PRectangle rcInterval = rcIndic; @@ -1429,7 +1432,8 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt const int caretPosition = static_cast<int>(posCaret.Position() - posLineStart - ll->LineStart(subLine)); - const XYPOSITION caretLeft = surface->XFromPosition(&screenLine, caretPosition); + std::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine); + const XYPOSITION caretLeft = slLayout->XFromPosition(caretPosition); // In case of start of line, the cursor should be at the right xposCaret = caretLeft + virtualOffset; @@ -1675,8 +1679,9 @@ static void DrawTranslucentSelection(Surface *surface, const EditModel &model, c const ColourDesired background = SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection); const ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels); + std::unique_ptr<IScreenLineLayout> slLayout = surface->Layout(&screenLine); - const std::vector<Interval> intervals = surface->FindRangeIntervals(&screenLine, selectionStart, selectionEnd); + const std::vector<Interval> intervals = slLayout->FindRangeIntervals(selectionStart, selectionEnd); for (const Interval &interval : intervals) { const XYPOSITION rcRight = interval.right + xStart; const XYPOSITION rcLeft = interval.left + xStart; 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) { |