int getVisualLineWidth( VisualLinesIterator visualLinesIterator, @Nullable Runnable quickEvaluationListener) { assert !visualLinesIterator.atEnd(); int visualLine = visualLinesIterator.getVisualLine(); FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel(); if (quickEvaluationListener != null && (topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() && !myView.getTextLayoutCache().hasCachedLayoutFor(visualLine)) { // fast path - speeds up editor opening quickEvaluationListener.run(); return myView .getLogicalPositionCache() .offsetToLogicalColumn( visualLine, myDocument.getLineEndOffset(visualLine) - myDocument.getLineStartOffset(visualLine)) * myView.getMaxCharWidth(); } float x = 0; int maxOffset = visualLinesIterator.getVisualLineStartOffset(); for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLinesIterator, quickEvaluationListener)) { x = fragment.getEndX(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) { x += myEditor .getSoftWrapModel() .getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } return (int) x; }
private void paintCaret(Graphics2D g_) { EditorImpl.CaretRectangle[] locations = myEditor.getCaretLocations(true); if (locations == null) return; Graphics2D g = IdeBackgroundUtil.getOriginalGraphics(g_); int lineHeight = myView.getLineHeight(); EditorSettings settings = myEditor.getSettings(); Color caretColor = myEditor.getColorsScheme().getColor(EditorColors.CARET_COLOR); if (caretColor == null) caretColor = new JBColor(CARET_DARK, CARET_LIGHT); g.setColor(caretColor); for (EditorImpl.CaretRectangle location : locations) { int x = location.myPoint.x; int y = location.myPoint.y; Caret caret = location.myCaret; boolean isRtl = location.myIsRtl; if (myEditor.isInsertMode() != settings.isBlockCursor()) { int lineWidth = JBUI.scale(settings.getLineCursorWidth()); g.fillRect(x, y, lineWidth, lineHeight); if (myDocument.getTextLength() > 0 && caret != null && !myView.getLineLayout(caret.getLogicalPosition().line).isLtr()) { g.fillPolygon( new int[] { isRtl ? x + lineWidth : x, isRtl ? x + lineWidth - CARET_DIRECTION_MARK_SIZE : x + CARET_DIRECTION_MARK_SIZE, isRtl ? x + lineWidth : x }, new int[] {y, y, y + CARET_DIRECTION_MARK_SIZE}, 3); } } else { int width = location.myWidth; int startX = Math.max(0, isRtl ? x - width : x); g.fillRect(startX, y, width, lineHeight - 1); if (myDocument.getTextLength() > 0 && caret != null) { int targetVisualColumn = caret.getVisualPosition().column; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, caret.getVisualLineStart(), false)) { int startVisualColumn = fragment.getStartVisualColumn(); int endVisualColumn = fragment.getEndVisualColumn(); if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn || startVisualColumn == targetVisualColumn && !isRtl || endVisualColumn == targetVisualColumn && isRtl) { g.setColor(ColorUtil.isDark(caretColor) ? CARET_LIGHT : CARET_DARK); fragment.draw( g, startX, y + myView.getAscent(), targetVisualColumn - startVisualColumn - (isRtl ? 1 : 0), targetVisualColumn - startVisualColumn + (isRtl ? 0 : 1)); break; } } } } } }
private void paintWhitespace( Graphics2D g, CharSequence text, float x, int y, int start, int end, EditorImpl.LineWhitespacePaintingStrategy whitespacePaintingStrategy, VisualLineFragmentsIterator.Fragment fragment) { g.setColor(myEditor.getColorsScheme().getColor(EditorColors.WHITESPACES_COLOR)); boolean isRtl = fragment.isRtl(); int baseStartOffset = fragment.getStartOffset(); int startOffset = isRtl ? baseStartOffset - start : baseStartOffset + start; for (int i = start; i < end; i++) { int charOffset = isRtl ? baseStartOffset - i - 1 : baseStartOffset + i; char c = text.charAt(charOffset); if (" \t\u3000".indexOf(c) >= 0 && whitespacePaintingStrategy.showWhitespaceAtOffset(charOffset)) { int startX = (int) fragment.offsetToX( x, startOffset, isRtl ? baseStartOffset - i : baseStartOffset + i); int endX = (int) fragment.offsetToX( x, startOffset, isRtl ? baseStartOffset - i - 1 : baseStartOffset + i + 1); if (c == ' ') { g.fillRect((startX + endX) / 2, y, 1, 1); } else if (c == '\t') { endX -= myView.getPlainSpaceWidth() / 4; int height = myView.getCharHeight(); int halfHeight = height / 2; int mid = y - halfHeight; int top = y - height; UIUtil.drawLine(g, startX, mid, endX, mid); UIUtil.drawLine(g, endX, y, endX, top); g.fillPolygon( new int[] {endX - halfHeight, endX - halfHeight, endX}, new int[] {y, y - height, y - halfHeight}, 3); } else if (c == '\u3000') { // ideographic space final int charHeight = myView.getCharHeight(); g.drawRect(startX + 2, y - charHeight, endX - startX - 4, charHeight); } } } }
/** * Returns a list of pairs of x coordinates for visual ranges representing given logical range. If * <code>startOffset == endOffset</code>, a pair of equal numbers is returned, corresponding to * target position. Target offsets are supposed to be located on the same visual line. */ private TFloatArrayList logicalRangeToVisualRanges(int startOffset, int endOffset) { assert startOffset <= endOffset; TFloatArrayList result = new TFloatArrayList(); for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, startOffset, false)) { int minOffset = fragment.getMinOffset(); int maxOffset = fragment.getMaxOffset(); if (startOffset == endOffset) { if (startOffset >= minOffset && startOffset <= maxOffset) { float x = fragment.offsetToX(startOffset); result.add(x); result.add(x); break; } } else if (startOffset < maxOffset && endOffset > minOffset) { float x1 = fragment.offsetToX(Math.max(minOffset, startOffset)); float x2 = fragment.offsetToX(Math.min(maxOffset, endOffset)); if (x1 > x2) { float tmp = x1; x1 = x2; x2 = tmp; } if (result.isEmpty() || x1 > result.get(result.size() - 1)) { result.add(x1); result.add(x2); } else { result.set(result.size() - 1, x2); } } } return result; }
private void paintLineFragments( Graphics2D g, Rectangle clip, int visualLine, int y, LineFragmentPainter painter) { float x = visualLine == 0 ? myView.getPrefixTextWidthInPixels() : 0; int offset = myView.visualPositionToOffset(new VisualPosition(visualLine, 0)); int visualLineEndOffset = myView.visualPositionToOffset(new VisualPosition(visualLine, Integer.MAX_VALUE, true)); IterationState it = null; int prevEndOffset = -1; boolean firstFragment = true; int maxColumn = 0; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, offset, false)) { int fragmentStartOffset = fragment.getStartOffset(); int start = fragmentStartOffset; int end = fragment.getEndOffset(); x = fragment.getStartX(); if (firstFragment) { firstFragment = false; SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset); if (softWrap != null) { prevEndOffset = offset; it = new IterationState( myEditor, offset == 0 ? 0 : offset - 1, visualLineEndOffset, true, false, false, false); if (it.getEndOffset() <= offset) { it.advance(); } painter.paintBeforeLineStart( g, it.getStartOffset() == offset ? it.getBeforeLineStartBackgroundAttributes() : it.getMergedAttributes(), fragment.getStartVisualColumn(), fragment.getStartX(), y); } } FoldRegion foldRegion = fragment.getCurrentFoldRegion(); if (foldRegion == null) { if (start != prevEndOffset) { it = new IterationState( myEditor, start, fragment.isRtl() ? offset : visualLineEndOffset, true, false, false, fragment.isRtl()); } prevEndOffset = end; assert it != null; while (fragment.isRtl() ? start > end : start < end) { if (fragment.isRtl() ? it.getEndOffset() >= start : it.getEndOffset() <= start) { assert !it.atEnd(); it.advance(); } TextAttributes attributes = it.getMergedAttributes(); int curEnd = fragment.isRtl() ? Math.max(it.getEndOffset(), end) : Math.min(it.getEndOffset(), end); float xNew = fragment.offsetToX(x, start, curEnd); painter.paint( g, fragment, fragment.isRtl() ? fragmentStartOffset - start : start - fragmentStartOffset, fragment.isRtl() ? fragmentStartOffset - curEnd : curEnd - fragmentStartOffset, attributes, x, xNew, y); x = xNew; start = curEnd; } } else { float xNew = fragment.getEndX(); painter.paint( g, fragment, 0, fragment.getEndVisualColumn() - fragment.getStartVisualColumn(), getFoldRegionAttributes(foldRegion), x, xNew, y); x = xNew; prevEndOffset = -1; it = null; } maxColumn = fragment.getEndVisualColumn(); } if (it == null || it.getEndOffset() != visualLineEndOffset) { it = new IterationState( myEditor, visualLineEndOffset == offset ? visualLineEndOffset : visualLineEndOffset - 1, visualLineEndOffset, true, false, false, false); } if (!it.atEnd()) { it.advance(); } assert it.atEnd(); painter.paintAfterLineEnd(g, clip, it, maxColumn, x, y); }