aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2018-06-02 08:37:47 +1000
committerNeil <nyamatongwe@gmail.com>2018-06-02 08:37:47 +1000
commit2e003602f29edff5d38137472edee82fab9f5376 (patch)
treeefcd2d4466c2bb894d5354d0652e986fdfcbea22
parente67783d907b0ae186d794b205cf89c645fe50270 (diff)
downloadscintilla-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.h4
-rw-r--r--cocoa/PlatCocoa.mm12
-rw-r--r--gtk/PlatGTK.cxx16
-rw-r--r--include/Platform.h12
-rw-r--r--qt/ScintillaEditBase/PlatQt.cpp14
-rw-r--r--qt/ScintillaEditBase/PlatQt.h4
-rw-r--r--src/EditView.cxx15
-rw-r--r--win32/PlatWin.cxx382
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) {