diff options
Diffstat (limited to 'src/EditView.cxx')
-rw-r--r-- | src/EditView.cxx | 200 |
1 files changed, 163 insertions, 37 deletions
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); |