diff options
| -rw-r--r-- | src/EditModel.cxx | 9 | ||||
| -rw-r--r-- | src/EditModel.h | 2 | ||||
| -rw-r--r-- | src/EditView.cxx | 200 | ||||
| -rw-r--r-- | src/EditView.h | 6 | ||||
| -rw-r--r-- | src/Editor.cxx | 15 | ||||
| -rw-r--r-- | src/Editor.h | 2 | ||||
| -rw-r--r-- | src/PositionCache.cxx | 103 | ||||
| -rw-r--r-- | src/PositionCache.h | 36 | ||||
| -rw-r--r-- | win32/PlatWin.cxx | 425 | ||||
| -rw-r--r-- | win32/ScintillaWin.cxx | 1 | 
10 files changed, 748 insertions, 51 deletions
| diff --git a/src/EditModel.cxx b/src/EditModel.cxx index fcca51405..e640e05bb 100644 --- a/src/EditModel.cxx +++ b/src/EditModel.cxx @@ -77,3 +77,12 @@ EditModel::~EditModel() {  	pdoc->Release();  	pdoc = 0;  } + +bool EditModel::BidirectionalEnabled() const { +	return (bidirectional != Bidirectional::bidiDisabled) && +		(SC_CP_UTF8 == pdoc->dbcsCodePage); +} + +bool EditModel::BidirectionalR2L() const { +	return bidirectional == Bidirectional::bidiR2L; +} diff --git a/src/EditModel.h b/src/EditModel.h index 7ad719c12..3da6afb58 100644 --- a/src/EditModel.h +++ b/src/EditModel.h @@ -63,6 +63,8 @@ public:  	virtual Point GetVisibleOriginInMain() const = 0;  	virtual Sci::Line LinesOnScreen() const = 0;  	virtual Range GetHotSpotRange() const = 0; +	bool BidirectionalEnabled() const; +	bool BidirectionalR2L() const;  };  } diff --git a/src/EditView.cxx b/src/EditView.cxx index 6ca22e4f2..76edf1c68 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -589,8 +589,39 @@ void EditView::LayoutLine(const EditModel &model, Sci::Line line, Surface *surfa  	}  } +// Fill the LineLayout bidirectional data fields according to each char style + +void EditView::UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll) { +	if (model.BidirectionalEnabled()) { +		ll->EnsureBidiData(); +		for (int stylesInLine = 0; stylesInLine < ll->numCharsInLine; stylesInLine++) { +			ll->bidiData->stylesFonts[stylesInLine].MakeAlias(vstyle.styles[ll->styles[stylesInLine]].font); +		} +		ll->bidiData->stylesFonts[ll->numCharsInLine].ClearFont(); + +		for (int charsInLine = 0; charsInLine < ll->numCharsInLine; charsInLine++) { +			const int charWidth = UTF8DrawBytes(reinterpret_cast<unsigned char *>(&ll->chars[charsInLine]), ll->numCharsInLine - charsInLine); +			const Representation *repr = model.reprs.RepresentationFromCharacter(&ll->chars[charsInLine], charWidth); + +			ll->bidiData->widthReprs[charsInLine] = 0.0f; +			if (repr && ll->chars[charsInLine] != '\t') { +				ll->bidiData->widthReprs[charsInLine] = ll->positions[charsInLine + charWidth] - ll->positions[charsInLine]; +			} +			if (charWidth > 1) { +				for (int c = 1; c < charWidth; c++) { +					charsInLine++; +					ll->bidiData->widthReprs[charsInLine] = 0.0f; +				} +			} +		} +		ll->bidiData->widthReprs[ll->numCharsInLine] = 0.0f; +	} else { +		ll->bidiData.reset(); +	} +} +  Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine, -				     const ViewStyle &vs, PointEnd pe) { +				     const ViewStyle &vs, PointEnd pe, const PRectangle rcClient) {  	Point pt;  	if (pos.Position() == INVALID_POSITION)  		return pt; @@ -607,8 +638,28 @@ Point EditView::LocationFromPosition(Surface *surface, const EditModel &model, S  		LayoutLine(model, lineDoc, surface, vs, ll, model.wrapWidth);  		const int posInLine = static_cast<int>(pos.Position() - posLineStart);  		pt = ll->PointFromPosition(posInLine, vs.lineHeight, pe); -		pt.y += (lineVisible - topLine) * vs.lineHeight;  		pt.x += vs.textStart - model.xOffset; + +		if (model.BidirectionalEnabled()) { +			// Fill the line bidi data +			UpdateBidiData(model, vs, ll); + +			// Find subLine +			const int subLine = ll->SubLineFromPosition(posInLine, pe); +			const int caretPosition = posInLine - ll->LineStart(subLine); + +			// Get the point from current position +			const ScreenLine screenLine(ll, subLine, vs, rcClient.right, tabWidthMinimumPixels); +			pt.x = surface->XFromPosition(&screenLine, caretPosition); + +			pt.x += vs.textStart - model.xOffset; + +			pt.y = 0; +			if (posInLine >= ll->LineStart(subLine)) { +				pt.y = static_cast<XYPOSITION>(subLine*vs.lineHeight); +			} +		} +		pt.y += (lineVisible - topLine) * vs.lineHeight;  	}  	pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;  	return pt; @@ -639,7 +690,8 @@ Range EditView::RangeDisplayLine(Surface *surface, const EditModel &model, Sci::  	return rangeSubLine;  } -SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid, bool charPosition, bool virtualSpace, const ViewStyle &vs) { +SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid, +	bool charPosition, bool virtualSpace, const ViewStyle &vs, const PRectangle rcClient) {  	pt.x = pt.x - vs.textStart;  	Sci::Line visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));  	if (!canReturnInvalid && (visibleLine < 0)) @@ -661,8 +713,18 @@ SelectionPosition EditView::SPositionFromLocation(Surface *surface, const EditMo  			const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];  			if (subLine > 0)	// Wrapped  				pt.x -= ll->wrapIndent; -			const Sci::Position positionInLine = ll->FindPositionFromX(static_cast<XYPOSITION>(pt.x + subLineStart), -				rangeSubLine, charPosition); +			Sci::Position positionInLine = 0; +			if (model.BidirectionalEnabled()) { +				// Fill the line bidi data +				UpdateBidiData(model, vs, ll); + +				const ScreenLine screenLine(ll, subLine, vs, rcClient.right, tabWidthMinimumPixels); +				positionInLine = surface->PositionFromX(&screenLine, static_cast<XYPOSITION>(pt.x), charPosition) + +					rangeSubLine.start; +			} else { +				positionInLine = ll->FindPositionFromX(static_cast<XYPOSITION>(pt.x + subLineStart), +					rangeSubLine, charPosition); +			}  			if (positionInLine < rangeSubLine.end) {  				return SelectionPosition(model.pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));  			} @@ -869,7 +931,7 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle  		const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;  		virtualSpace = model.sel.VirtualSpaceFor(model.pdoc->LineEnd(line)) * spaceWidth;  	} -	const XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart); +	XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);  	// Fill the virtual space and show selections within it  	if (virtualSpace > 0.0f) { @@ -1017,27 +1079,51 @@ void EditView::DrawEOL(Surface *surface, const EditModel &model, const ViewStyle  }  static void DrawIndicator(int indicNum, Sci::Position startPos, Sci::Position endPos, Surface *surface, const ViewStyle &vsDraw, -	const LineLayout *ll, int xStart, PRectangle rcLine, Sci::Position secondCharacter, int subLine, Indicator::DrawState drawState, int value) { +	const LineLayout *ll, int xStart, PRectangle rcLine, Sci::Position secondCharacter, int subLine, Indicator::DrawState drawState, +	int value, bool bidiEnabled, int tabWidthMinimumPixels) { +  	const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)]; + +	std::vector<PRectangle> rectangles; +  	const PRectangle rcIndic(  		ll->positions[startPos] + xStart - subLineStart,  		rcLine.top + vsDraw.maxAscent,  		ll->positions[endPos] + xStart - subLineStart,  		rcLine.top + vsDraw.maxAscent + 3); -	PRectangle rcFirstCharacter = rcIndic; -	// Allow full descent space for character indicators -	rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent; -	if (secondCharacter >= 0) { -		rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart; + +	if (bidiEnabled) { +		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,  +			startPos - lineRange.start, endPos - lineRange.start); +		for (const Interval &interval : intervals) { +			PRectangle rcInterval = rcIndic; +			rcInterval.left = interval.left + xStart; +			rcInterval.right = interval.right + xStart; +			rectangles.push_back(rcInterval); +		}  	} else { -		// Indicator continued from earlier line so make an empty box and don't draw -		rcFirstCharacter.right = rcFirstCharacter.left; +		rectangles.push_back(rcIndic); +	} + +	for (const PRectangle &rc : rectangles) { +		PRectangle rcFirstCharacter = rc; +		// Allow full descent space for character indicators +		rcFirstCharacter.bottom = rcLine.top + vsDraw.maxAscent + vsDraw.maxDescent; +		if (secondCharacter >= 0) { +			rcFirstCharacter.right = ll->positions[secondCharacter] + xStart - subLineStart; +		} else { +			// Indicator continued from earlier line so make an empty box and don't draw +			rcFirstCharacter.right = rcFirstCharacter.left; +		} +		vsDraw.indicators[indicNum].Draw(surface, rc, rcLine, rcFirstCharacter, drawState, value);  	} -	vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine, rcFirstCharacter, drawState, value);  }  static void DrawIndicators(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, -	Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, bool under, Sci::Position hoverIndicatorPos) { +	Sci::Line line, int xStart, PRectangle rcLine, int subLine, Sci::Position lineEnd, bool under, int tabWidthMinimumPixels) {  	// Draw decorators  	const Sci::Position posLineStart = model.pdoc->LineStart(line);  	const Sci::Position lineStart = ll->LineStart(subLine); @@ -1053,12 +1139,13 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS  				const Range rangeRun(deco->StartRun(startPos), deco->EndRun(startPos));  				const Sci::Position endPos = std::min(rangeRun.end, posLineEnd);  				const bool hover = vsDraw.indicators[deco->Indicator()].IsDynamic() && -					rangeRun.ContainsCharacter(hoverIndicatorPos); +					rangeRun.ContainsCharacter(model.hoverIndicatorPos);  				const int value = deco->ValueAt(startPos);  				const Indicator::DrawState drawState = hover ? Indicator::drawHover : Indicator::drawNormal;  				const Sci::Position posSecond = model.pdoc->MovePositionOutsideChar(rangeRun.First() + 1, 1);  				DrawIndicator(deco->Indicator(), startPos - posLineStart, endPos - posLineStart, -					surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, value); +					surface, vsDraw, ll, xStart, rcLine, posSecond - posLineStart, subLine, drawState, +					value, model.BidirectionalEnabled(), tabWidthMinimumPixels);  				startPos = endPos;  				if (!deco->ValueAt(startPos)) {  					startPos = deco->EndRun(startPos); @@ -1077,14 +1164,16 @@ static void DrawIndicators(Surface *surface, const EditModel &model, const ViewS  				const Sci::Position braceOffset = model.braces[0] - posLineStart;  				if (braceOffset < ll->numCharsInLine) {  					const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[0] + 1, 1) - posLineStart; -					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1); +					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, +						subLine, Indicator::drawNormal, 1, model.BidirectionalEnabled(), tabWidthMinimumPixels);  				}  			}  			if (rangeLine.ContainsCharacter(model.braces[1])) {  				const Sci::Position braceOffset = model.braces[1] - posLineStart;  				if (braceOffset < ll->numCharsInLine) {  					const Sci::Position secondOffset = model.pdoc->MovePositionOutsideChar(model.braces[1] + 1, 1) - posLineStart; -					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, subLine, Indicator::drawNormal, 1); +					DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, ll, xStart, rcLine, secondOffset, +						subLine, Indicator::drawNormal, 1, model.BidirectionalEnabled(), tabWidthMinimumPixels);  				}  			}  		} @@ -1334,6 +1423,17 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt  		const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;  		if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {  			XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)]; +			if (model.BidirectionalEnabled() && (posCaret.VirtualSpace() == 0)) { +				// Get caret point +				const ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels); + +				const int caretPosition = static_cast<int>(posCaret.Position() - posLineStart - ll->LineStart(subLine)); + +				const XYPOSITION caretLeft = surface->XFromPosition(&screenLine, caretPosition); + +				// In case of start of line, the cursor should be at the right +				xposCaret = caretLeft + virtualOffset; +			}  			if (ll->wrapIndent != 0) {  				const Sci::Position lineStart = ll->LineStart(subLine);  				if (lineStart != 0)	// Wrapped @@ -1549,8 +1649,9 @@ static void DrawMarkUnderline(Surface *surface, const EditModel &model, const Vi  		marks >>= 1;  	}  } +  static void DrawTranslucentSelection(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, -	Sci::Line line, PRectangle rcLine, int subLine, Range lineRange, int xStart) { +	Sci::Line line, PRectangle rcLine, int subLine, Range lineRange, int xStart, int tabWidthMinimumPixels) {  	if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA)) {  		const Sci::Position posLineStart = model.pdoc->LineStart(line);  		const XYACCUMULATOR subLineStart = ll->positions[lineRange.start]; @@ -1567,20 +1668,37 @@ static void DrawTranslucentSelection(Surface *surface, const EditModel &model, c  			if (alpha != SC_ALPHA_NOALPHA) {  				const SelectionSegment portion = model.sel.Range(r).Intersect(virtualSpaceRange);  				if (!portion.Empty()) { -					const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; -					PRectangle rcSegment = rcLine; -					rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - -						static_cast<XYPOSITION>(subLineStart)+portion.start.VirtualSpace() * spaceWidth; -					rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - -						static_cast<XYPOSITION>(subLineStart)+portion.end.VirtualSpace() * spaceWidth; -					if ((ll->wrapIndent != 0) && (lineRange.start != 0)) { -						if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1)) -							rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here +					if (model.BidirectionalEnabled()) { +						const int selectionStart = static_cast<int>(portion.start.Position() - posLineStart - lineRange.start); +						const int selectionEnd = static_cast<int>(portion.end.Position() - posLineStart - lineRange.start); + +						const ColourDesired background = SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection); + +						const ScreenLine screenLine(ll, subLine, vsDraw, rcLine.right, tabWidthMinimumPixels); + +						const std::vector<Interval> intervals = surface->FindRangeIntervals(&screenLine, selectionStart, selectionEnd); +						for (const Interval &interval : intervals) { +							const XYPOSITION rcRight = interval.right + xStart; +							const XYPOSITION rcLeft = interval.left + xStart; +							const PRectangle rcSelection(rcLeft, rcLine.top, rcRight, rcLine.bottom); +							SimpleAlphaRectangle(surface, rcSelection, background, alpha); +						} +					} else { +						const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth; +						PRectangle rcSegment = rcLine; +						rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - +							static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth; +						rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - +							static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth; +						if ((ll->wrapIndent != 0) && (lineRange.start != 0)) { +							if ((portion.start.Position() - posLineStart) == lineRange.start && model.sel.Range(r).ContainsCharacter(portion.start.Position() - 1)) +								rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here +						} +						rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; +						rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; +						if (rcSegment.right > rcLine.left) +							SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);  					} -					rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left; -					rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right; -					if (rcSegment.right > rcLine.left) -						SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == model.sel.Main(), model.primarySelection), alpha);  				}  			}  		} @@ -1904,7 +2022,8 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl  		}  		if (phase & drawIndicatorsBack) { -			DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRangeIncludingEnd.end, true, model.hoverIndicatorPos); +			DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, +				lineRangeIncludingEnd.end, true, tabWidthMinimumPixels);  			DrawEdgeLine(surface, vsDraw, ll, rcLine, lineRange, xStart);  			DrawMarkUnderline(surface, model, vsDraw, line, rcLine);  		} @@ -1920,7 +2039,8 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl  	}  	if (phase & drawIndicatorsFore) { -		DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, lineRangeIncludingEnd.end, false, model.hoverIndicatorPos); +		DrawIndicators(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, +			lineRangeIncludingEnd.end, false, tabWidthMinimumPixels);  	}  	DrawFoldDisplayText(surface, model, vsDraw, ll, line, xStart, rcLine, subLine, subLineStart, phase); @@ -1935,7 +2055,7 @@ void EditView::DrawLine(Surface *surface, const EditModel &model, const ViewStyl  	}  	if (!hideSelection && (phase & drawSelectionTranslucent)) { -		DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart); +		DrawTranslucentSelection(surface, model, vsDraw, ll, line, rcLine, subLine, lineRange, xStart, tabWidthMinimumPixels);  	}  	if (phase & drawLineTranslucent) { @@ -1984,6 +2104,7 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan  		}  		surface->SetUnicodeMode(SC_CP_UTF8 == model.pdoc->dbcsCodePage);  		surface->SetDBCSMode(model.pdoc->dbcsCodePage); +		surface->SetBidiR2L(model.BidirectionalR2L());  		const Point ptOrigin = model.GetVisibleOriginInMain(); @@ -2082,6 +2203,11 @@ void EditView::PaintText(Surface *surfaceWindow, const EditModel &model, PRectan  						surface->FillRectangle(rcSpacer, vsDraw.styles[STYLE_DEFAULT].back);  					} +					if (model.BidirectionalEnabled()) { +						// Fill the line bidi data +						UpdateBidiData(model, vsDraw, ll); +					} +  					DrawLine(surface, model, vsDraw, ll, lineDoc, visibleLine, xStart, rcLine, subLine, phase);  #if defined(TIME_PAINTING)  					durPaint += ep.Duration(true); diff --git a/src/EditView.h b/src/EditView.h index 51e2a1e53..16da4e904 100644 --- a/src/EditView.h +++ b/src/EditView.h @@ -116,11 +116,13 @@ public:  	void LayoutLine(const EditModel &model, Sci::Line line, Surface *surface, const ViewStyle &vstyle,  		LineLayout *ll, int width = LineLayout::wrapWidthInfinite); +	void UpdateBidiData(const EditModel &model, const ViewStyle &vstyle, LineLayout *ll); +  	Point LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine, -				   const ViewStyle &vs, PointEnd pe); +		const ViewStyle &vs, PointEnd pe, const PRectangle rcClient);  	Range RangeDisplayLine(Surface *surface, const EditModel &model, Sci::Line lineVisible, const ViewStyle &vs);  	SelectionPosition SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid, -		bool charPosition, bool virtualSpace, const ViewStyle &vs); +		bool charPosition, bool virtualSpace, const ViewStyle &vs, const PRectangle rcClient);  	SelectionPosition SPositionFromLineX(Surface *surface, const EditModel &model, Sci::Line lineDoc, int x, const ViewStyle &vs);  	Sci::Line DisplayFromPosition(Surface *surface, const EditModel &model, Sci::Position pos, const ViewStyle &vs);  	Sci::Position StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs); diff --git a/src/Editor.cxx b/src/Editor.cxx index 04d61bea6..9e81a2ef3 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -377,9 +377,10 @@ SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const  }  Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) { +	const PRectangle rcClient = GetTextRectangle();  	RefreshStyleData();  	AutoSurface surface(this); -	return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe); +	return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe, rcClient);  }  Point Editor::LocationFromPosition(Sci::Position pos, PointEnd pe) { @@ -395,11 +396,12 @@ SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid,  	RefreshStyleData();  	AutoSurface surface(this); +	PRectangle rcClient = GetTextRectangle(); +	// May be in scroll view coordinates so translate back to main view +	const Point ptOrigin = GetVisibleOriginInMain(); +	rcClient.Move(-ptOrigin.x, -ptOrigin.y); +  	if (canReturnInvalid) { -		PRectangle rcClient = GetTextRectangle(); -		// May be in scroll view coordinates so translate back to main view -		const Point ptOrigin = GetVisibleOriginInMain(); -		rcClient.Move(-ptOrigin.x, -ptOrigin.y);  		if (!rcClient.Contains(pt))  			return SelectionPosition(INVALID_POSITION);  		if (pt.x < vs.textStart) @@ -408,7 +410,8 @@ SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid,  			return SelectionPosition(INVALID_POSITION);  	}  	const PointDocument ptdoc = DocumentPointFromView(pt); -	return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid, charPosition, virtualSpace, vs); +	return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid, +		charPosition, virtualSpace, vs, rcClient);  }  Sci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) { diff --git a/src/Editor.h b/src/Editor.h index bc295260e..3c21ee092 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -618,6 +618,7 @@ public:  			surf->Init(ed->wMain.GetID());  			surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());  			surf->SetDBCSMode(ed->CodePage()); +			surf->SetBidiR2L(ed->BidirectionalR2L());  		}  	}  	AutoSurface(SurfaceID sid, Editor *ed, int technology = -1) { @@ -626,6 +627,7 @@ public:  			surf->Init(sid, ed->wMain.GetID());  			surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());  			surf->SetDBCSMode(ed->CodePage()); +			surf->SetBidiR2L(ed->BidirectionalR2L());  		}  	}  	// Deleted so AutoSurface objects can not be copied. diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 31a8601f5..b218c39e0 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -46,6 +46,11 @@  using namespace Scintilla; +void BidiData::Resize(size_t maxLineLength_) { +	stylesFonts.resize(maxLineLength_ + 1); +	widthReprs.resize(maxLineLength_ + 1); +} +  LineLayout::LineLayout(int maxLineLength_) :  	lenLineStarts(0),  	lineNumber(-1), @@ -79,15 +84,27 @@ void LineLayout::Resize(int maxLineLength_) {  		// Extra position allocated as sometimes the Windows  		// GetTextExtentExPoint API writes an extra element.  		positions = std::make_unique<XYPOSITION []>(maxLineLength_ + 1 + 1); +		if (bidiData) { +			bidiData->Resize(maxLineLength_); +		} +  		maxLineLength = maxLineLength_;  	}  } +void LineLayout::EnsureBidiData() { +	if (!bidiData) { +		bidiData = std::make_unique<BidiData>(); +		bidiData->Resize(maxLineLength); +	} +} +  void LineLayout::Free() {  	chars.reset();  	styles.reset();  	positions.reset();  	lineStarts.reset(); +	bidiData.reset();  }  void LineLayout::Invalidate(validLevel validity_) { @@ -105,6 +122,16 @@ int LineLayout::LineStart(int line) const {  	}  } +int Scintilla::LineLayout::LineLength(int line) const { +	if (!lineStarts) { +		return numCharsInLine; +	} if (line >= lines - 1) { +		return numCharsInLine - lineStarts[line]; +	} else { +		return lineStarts[line + 1] - lineStarts[line]; +	} +} +  int LineLayout::LineLastVisible(int line, Scope scope) const {  	if (line < 0) {  		return 0; @@ -124,6 +151,25 @@ bool LineLayout::InLine(int offset, int line) const {  		((offset == numCharsInLine) && (line == (lines-1)));  } +int LineLayout::SubLineFromPosition(int posInLine, PointEnd pe) const { +	if (!lineStarts || (posInLine > maxLineLength)) { +		return lines - 1; +	} + +	for (int line = 0; line < lines; line++) { +		if (pe & peSubLineEnd) { +			// Return subline not start of next +			if (lineStarts[line + 1] <= posInLine + 1) +				return line; +		} else { +			if (lineStarts[line + 1] <= posInLine) +				return line; +		} +	} + +	return lines - 1; +} +  void LineLayout::SetLineStart(int line, int start) {  	if ((line >= lenLineStarts) && (line != 0)) {  		const int newMaxLines = line + 20; @@ -244,6 +290,63 @@ int LineLayout::EndLineStyle() const {  	return styles[numCharsBeforeEOL > 0 ? numCharsBeforeEOL-1 : 0];  } +ScreenLine::ScreenLine( +	const LineLayout *ll_, +	int subLine, +	const ViewStyle &vs, +	XYPOSITION width_, +	int tabWidthMinimumPixels_) : +	ll(ll_), +	start(ll->LineStart(subLine)), +	len(ll->LineLength(subLine)), +	width(width_), +	height(static_cast<float>(vs.lineHeight)), +	ctrlCharPadding(vs.ctrlCharPadding), +	tabWidth(vs.tabWidth), +	tabWidthMinimumPixels(tabWidthMinimumPixels_) { +} + +ScreenLine::~ScreenLine() { +} + +std::string_view ScreenLine::Text() const { +	return std::string_view(&ll->chars[start], len); +} + +size_t ScreenLine::Length() const { +	return len; +} + +size_t ScreenLine::RepresentationCount() const { +	return std::count_if(&ll->bidiData->widthReprs[start], +		&ll->bidiData->widthReprs[start + len], +		[](XYPOSITION w) {return w > 0.0f; }); +} + +XYPOSITION ScreenLine::Width() const { +	return width; +} + +XYPOSITION ScreenLine::Height() const { +	return height; +} + +XYPOSITION ScreenLine::TabWidth() const { +	return tabWidth; +} + +XYPOSITION ScreenLine::TabWidthMinimumPixels() const { +	return static_cast<XYPOSITION>(tabWidthMinimumPixels); +} + +const Font *ScreenLine::FontOfPosition(size_t position) const { +	return &ll->bidiData->stylesFonts[start + position]; +} + +XYPOSITION ScreenLine::RepresentationWidth(size_t position) const { +	return ll->bidiData->widthReprs[start + position]; +} +  LineLayoutCache::LineLayoutCache() :  	level(0),  	allInvalidated(false), styleClock(-1), useCount(0) { diff --git a/src/PositionCache.h b/src/PositionCache.h index 7f50c4dec..512ec13f5 100644 --- a/src/PositionCache.h +++ b/src/PositionCache.h @@ -40,6 +40,13 @@ enum PointEnd {  	peSubLineEnd = 0x2  }; +class BidiData { +public: +	std::vector<FontAlias> stylesFonts; +	std::vector<XYPOSITION> widthReprs; +	void Resize(size_t maxLineLength_); +}; +  /**   */  class LineLayout { @@ -66,6 +73,8 @@ public:  	std::unique_ptr<XYPOSITION[]> positions;  	char bracePreviousStyles[2]; +	std::unique_ptr<BidiData> bidiData; +  	// Hotspot support  	Range hotspot; @@ -82,13 +91,16 @@ public:  	void operator=(LineLayout &&) = delete;  	virtual ~LineLayout();  	void Resize(int maxLineLength_); +	void EnsureBidiData();  	void Free();  	void Invalidate(validLevel validity_);  	int LineStart(int line) const; +	int LineLength(int line) const;  	enum class Scope { visibleOnly, includeEnd };  	int LineLastVisible(int line, Scope scope) const;  	Range SubLineRange(int subLine, Scope scope) const;  	bool InLine(int offset, int line) const; +	int SubLineFromPosition(int posInLine, PointEnd pe) const;  	void SetLineStart(int line, int start);  	void SetBracesHighlight(Range rangeLine, const Sci::Position braces[],  		char bracesMatchStyle, int xHighlight, bool ignoreStyle); @@ -99,6 +111,30 @@ public:  	int EndLineStyle() const;  }; +struct ScreenLine : public IScreenLine { +	const LineLayout *ll; +	size_t start; +	size_t len; +	XYPOSITION width; +	XYPOSITION height; +	int ctrlCharPadding; +	XYPOSITION tabWidth; +	int tabWidthMinimumPixels; + +	ScreenLine(const LineLayout *ll_, int subLine, const ViewStyle &vs, XYPOSITION width_, int tabWidthMinimumPixels_); +	virtual ~ScreenLine(); + +	std::string_view Text() const override; +	size_t Length() const override; +	size_t RepresentationCount() const override; +	XYPOSITION Width() const override; +	XYPOSITION Height() const override; +	XYPOSITION TabWidth() const override; +	XYPOSITION TabWidthMinimumPixels() const override; +	const Font *FontOfPosition(size_t position) const override; +	XYPOSITION RepresentationWidth(size_t position) const override; +}; +  /**   */  class LineLayoutCache { diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index cdaea41e4..82b269c83 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -1094,6 +1094,8 @@ void SurfaceGDI::SetBidiR2L(bool) {  #if defined(USE_D2D) +class BlobInline; +  class SurfaceD2D : public Surface {  	bool unicodeMode;  	int x, y; @@ -1160,6 +1162,11 @@ public:  	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); +  	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;  	void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; @@ -1615,16 +1622,422 @@ void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {  	}  } -size_t SurfaceD2D::PositionFromX(const IScreenLine *, XYPOSITION, bool) { -	return 0; +class BlobInline : public IDWriteInlineObject { +	XYPOSITION width; + +	// IUnknown +	STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override; +	STDMETHODIMP_(ULONG)AddRef() override; +	STDMETHODIMP_(ULONG)Release() override; + +	// IDWriteInlineObject +	STDMETHODIMP Draw( +		void *clientDrawingContext, +		IDWriteTextRenderer *renderer, +		FLOAT originX, +		FLOAT originY, +		BOOL isSideways, +		BOOL isRightToLeft, +		IUnknown *clientDrawingEffect +		) override; +	STDMETHODIMP GetMetrics(DWRITE_INLINE_OBJECT_METRICS *metrics) override; +	STDMETHODIMP GetOverhangMetrics(DWRITE_OVERHANG_METRICS *overhangs) override; +	STDMETHODIMP GetBreakConditions( +		DWRITE_BREAK_CONDITION *breakConditionBefore, +		DWRITE_BREAK_CONDITION *breakConditionAfter) override; +public: +	BlobInline(XYPOSITION width_=0.0f) noexcept : width(width_) { +	} +	virtual ~BlobInline() { +	} +}; + +/// Implement IUnknown +STDMETHODIMP BlobInline::QueryInterface(REFIID riid, PVOID *ppv) { +	// Never called so not checked. +	*ppv = NULL; +	if (riid == IID_IUnknown) +		*ppv = static_cast<IUnknown *>(this); +	if (riid == __uuidof(IDWriteInlineObject)) +		*ppv = static_cast<IDWriteInlineObject *>(this); +	if (!*ppv) +		return E_NOINTERFACE; +	return S_OK; +} + +STDMETHODIMP_(ULONG) BlobInline::AddRef() { +	// Lifetime tied to Platform methods so ignore any reference operations. +	return 1; +} + +STDMETHODIMP_(ULONG) BlobInline::Release() { +	// Lifetime tied to Platform methods so ignore any reference operations. +	return 1; +} + +/// Implement IDWriteInlineObject +HRESULT STDMETHODCALLTYPE BlobInline::Draw( +	void*, +	IDWriteTextRenderer*, +	FLOAT, +	FLOAT, +	BOOL, +	BOOL, +	IUnknown*) { +	// Since not performing drawing, not necessary to implement +	// Could be implemented by calling back into platform-independent code. +	// This would allow more of the drawing to be mediated through DirectWrite. +	return S_OK; +} + +HRESULT STDMETHODCALLTYPE BlobInline::GetMetrics( +	DWRITE_INLINE_OBJECT_METRICS *metrics +) { +	metrics->width = width; +	metrics->height = 2; +	metrics->baseline = 1; +	metrics->supportsSideways = FALSE; +	return S_OK; +} + +HRESULT STDMETHODCALLTYPE BlobInline::GetOverhangMetrics( +	DWRITE_OVERHANG_METRICS *overhangs +) { +	overhangs->left = 0; +	overhangs->top = 0; +	overhangs->right = 0; +	overhangs->bottom = 0; +	return S_OK; +} + +HRESULT STDMETHODCALLTYPE BlobInline::GetBreakConditions( +	DWRITE_BREAK_CONDITION *breakConditionBefore, +	DWRITE_BREAK_CONDITION *breakConditionAfter +) { +	// Since not performing 2D layout, not necessary to implement +	*breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL; +	*breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL; +	return S_OK; +} + +// Get the position from the provided x + +size_t SurfaceD2D::PositionFromX(const IScreenLine *screenLine, XYPOSITION xDistance, bool charPosition) { +	// If the text is empty, then no need to go through this function +	if (!screenLine->Length()) +		return 0; + +	// Get textFormat +	FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID()); + +	if (!pIDWriteFactory || !pfm->pTextFormat) { +		return 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 = 0; + +	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; +	} + +	std::vector<BlobInline> blobs; + +	// Fill the textLayout chars with their own formats +	FillTextLayoutFormats(screenLine, textLayout, blobs); + +	// Returns the text position corresponding to the mouse x,y. +	// If hitting the trailing side of a cluster, return the +	// leading edge of the following text position. + +	BOOL isTrailingHit; +	BOOL isInside; +	DWRITE_HIT_TEST_METRICS caretMetrics; + +	textLayout->HitTestPoint( +		xDistance, +		0.0f, +		&isTrailingHit, +		&isInside, +		&caretMetrics +	); + +	DWRITE_HIT_TEST_METRICS hitTestMetrics = {}; +	if (isTrailingHit) { +		FLOAT caretX = 0.0f; +		FLOAT caretY = 0.0f; + +		// Uses hit-testing to align the current caret position to a whole cluster, +		// rather than residing in the middle of a base character + diacritic, +		// surrogate pair, or character + UVS. + +		// Align the caret to the nearest whole cluster. +		textLayout->HitTestTextPosition( +			caretMetrics.textPosition, +			false, +			&caretX, +			&caretY, +			&hitTestMetrics +		); +	} + +	textLayout->Release(); +	size_t pos; +	if (charPosition) { +		pos = isTrailingHit ? hitTestMetrics.textPosition : caretMetrics.textPosition; +	} else { +		pos = isTrailingHit ? hitTestMetrics.textPosition + hitTestMetrics.length : caretMetrics.textPosition; +	} + +	// Get the character position in original string +	return GetPositionInString(screenLine->Text(), pos);  } -XYPOSITION SurfaceD2D::XFromPosition(const IScreenLine *, size_t) { -	return 0; +// 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; +	} + +	// 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 = 0; + +	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); + +	// Translate text character offset to point x,y. +	DWRITE_HIT_TEST_METRICS caretMetrics; +	Point pt; + +	textLayout->HitTestTextPosition( +		static_cast<UINT32>(position), +		false, // trailing if false, else leading edge +		&pt.x, +		&pt.y, +		&caretMetrics +	); + +	textLayout->Release(); + +	return pt.x;  } -std::vector<Interval> SurfaceD2D::FindRangeIntervals(const IScreenLine *, size_t, size_t) { -	return std::vector<Interval>(); +// Find the selection range rectangles + +std::vector<Interval> SurfaceD2D::FindRangeIntervals(const IScreenLine *screenLine, 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()) { +		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 = 0; + +	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); + +	// Find selection range length +	size_t rangeLength = (endPos > startPos) ? (endPos - startPos) : (startPos - endPos); + +	// 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 +			NULL, +			0, // metrics count +			&actualHitTestCount +		); +	} + +	// Allocate enough room to return all hit-test metrics. +	std::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(actualHitTestCount); + +	if (rangeLength > 0) { +		textLayout->HitTestTextRange( +			static_cast<UINT32>(startPos), +			static_cast<UINT32>(rangeLength), +			0, // x +			0, // y +			&hitTestMetrics[0], +			static_cast<UINT32>(hitTestMetrics.size()), +			&actualHitTestCount +		); +	} + +	// 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); + +		pfm->pTextFormat->GetLocaleName(localName.data(), localNameSize); +		textLayout->SetLocaleName(localName.data(), textRange); + +		textLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange); + +		IDWriteFontCollection *fontCollection; +		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 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;  }  void SurfaceD2D::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) { diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index d60e2b31c..faab3844e 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -3364,6 +3364,7 @@ LRESULT PASCAL ScintillaWin::CTWndProc(  				}  				surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage);  				surfaceWindow->SetDBCSMode(sciThis->ct.codePage); +				surfaceWindow->SetBidiR2L(sciThis->BidirectionalR2L());  				sciThis->ct.PaintCT(surfaceWindow.get());  #if defined(USE_D2D)  				if (pCTRenderTarget) | 
