aboutsummaryrefslogtreecommitdiffhomepage
path: root/win32/PlatWin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r--win32/PlatWin.cxx382
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) {