aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/EditModel.cxx9
-rw-r--r--src/EditModel.h2
-rw-r--r--src/EditView.cxx200
-rw-r--r--src/EditView.h6
-rw-r--r--src/Editor.cxx15
-rw-r--r--src/Editor.h2
-rw-r--r--src/PositionCache.cxx103
-rw-r--r--src/PositionCache.h36
8 files changed, 328 insertions, 45 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 {