diff options
| -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) { | 
