Skip to content
2 changes: 2 additions & 0 deletions ide/editor.lib/src/org/netbeans/editor/GlyphGutter.java
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,8 @@ public void run() {
Shape pViewAlloc = pViewDesc.getAllocation();
Rectangle pViewRect = pViewAlloc.getBounds();
pViewRect.width = repaintWidth;
pViewRect.y += pViewDesc.getShadowHeight();
pViewRect.height -= pViewDesc.getShadowHeight();
if (pViewRect.y >= endRepaintY) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("GlyphGutter: pViewRect.y=" + pViewRect.y + " >= endRepaintY=" + // NOI18N
Expand Down
3 changes: 2 additions & 1 deletion ide/editor.lib2/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
# under the License.

is.autoload=true
javac.source=1.8
javac.source=17
javac.target=17
Comment on lines -19 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this can't be javac.release, please add a comment why not, otherwise someone might break this in future.

javac.compilerargs=-Xlint:unchecked
spec.version.base=2.50.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,15 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff
// Extends beyond first highlight
Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont);
Object firstPrependText = firstAttrs != null ? firstAttrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null;
Object firstShadowPrependText = firstAttrs != null ? firstAttrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) : null;
int index = 1;
while (true) {
item = get(index);
AttributeSet attrs = item.getAttributes();
Font font = ViewUtils.getFont(attrs, defaultFont);
Object prependText = attrs != null ? attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null;
if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText)) { // Stop at itemEndOffset
Object shadowPrependText = attrs != null ? attrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) : null;
if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText) || !Objects.equals(firstShadowPrependText, shadowPrependText)) { // Stop at itemEndOffset
if (index == 1) { // Just single attribute set
cutStartItems(1);
startOffset = itemEndOffset; // end offset of first item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ int getNextVisualPositionY(DocumentView docView, int offset, Bias bias, Shape do
pAlloc = getChildAllocation(docView, pIndex, docViewAlloc);
}
docView.op.getTextLayoutCache().activate(pView);
retOffset = pView.children.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x);
retOffset = pView.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x);
if (retOffset == -1) {
offset = -1; // Continue by entering the paragraph from outside
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ public final class DocumentViewOp
private int defaultRowHeightInt;

private int defaultAscentInt;
private float defaultDescent;

private float defaultCharWidth;

Expand Down Expand Up @@ -1087,6 +1088,7 @@ private void updateRowHeight(FontInfo fontInfo, boolean force) {
": defaultAscentInt from " + defaultAscentInt + " to " + fontInfo.ascentInt + "\n"); // NOI18N
}
defaultAscentInt = fontInfo.ascentInt;
defaultDescent = fontInfo.descent;
}
if (force || defaultRowHeightInt < fontInfo.rowHeightInt) {
if (LOG.isLoggable(Level.FINE)) {
Expand Down Expand Up @@ -1276,6 +1278,11 @@ public float getDefaultAscent() {
return defaultAscentInt;
}

public float getDefaultDescent() {
checkSettingsInfo();
return defaultDescent;
}

/**
* Return array of default:
* <ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim
}

private @NonNull EditorView wrapWithPrependedText(@NonNull EditorView origView, @NullAllowed AttributeSet attrs) {
if (attrs != null && attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String) {
if (attrs != null && (attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String || attrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) instanceof String)) {
return new PrependedTextView(documentView().op, attrs, origView);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
Expand Down Expand Up @@ -113,6 +117,8 @@ public final class ParagraphView extends EditorView implements EditorView.Parent

private int statusBits; // 44 + 4 = 48 bytes

private List<TextLayout> shadowText; // 44 + 4 = 52 bytes

public ParagraphView(Position startPos) {
super(null);
setStartPosition(startPos);
Expand Down Expand Up @@ -225,6 +231,39 @@ public void replace(int index, int removeLength, View[] addViews) {
assert (removeLength == 0) : "Attempt to remove from null children length=" + removeLength; // NOI18N
children = new ParagraphViewChildren(addViews.length);
}
if (index == 0 && addViews != null) {
if (shadowText != null && !shadowText.isEmpty()) {
shadowText.clear();
}

AttributeSet attrs = addViews[0].getAttributes();
Object shadowTextDataAttr = attrs != null ? attrs.getAttribute(ViewUtils.KEY_VERTICAL_SHADOW_TEXT_PREPEND) : null;
DocumentView docView = getDocumentView();
DocumentViewOp op = docView.op;
float shadowTextHeight = 0;
float shadowTextWidth = 0;

if (shadowTextDataAttr instanceof String shadowTextData) {
if (shadowText == null) {
shadowText = new ArrayList<>();
}
Font shadowTextFont = ViewUtils.getFont(getAttributes(), op.getDefaultFont());
for (String line : shadowTextData.split("\n")) { //XXX: performance!
//duplicated code:
if (line.isEmpty()) {
line = " ";
}
final TextLayout l = op.createTextLayout(line, shadowTextFont);
shadowText.add(l);
shadowTextHeight += height(l); //???
shadowTextWidth = Math.max(shadowTextWidth, l.getAdvance()); //???
}
}

children.shadowHeight = shadowTextHeight;
children.shadowWidth = shadowTextWidth;
}

children.replace(this, index, removeLength, addViews);
}

Expand Down Expand Up @@ -356,7 +395,18 @@ public int getViewIndexChecked(double x, double y, Shape alloc) {
@Override
public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) {
checkChildrenNotNull();
return children.modelToViewChecked(this, offset, alloc, bias);
Shape ret = children.modelToViewChecked(this, offset, alloc, bias);
if (shadowText != null) {
//XXX: cached shadowText height
Rectangle2D bounds = ret.getBounds2D();
float shadowHeight = 0;
for (TextLayout l : shadowText) {
shadowHeight += height(l);
}
bounds.setRect(bounds.getX(), bounds.getY() + shadowHeight, bounds.getWidth(), bounds.getHeight());
ret = bounds;
}
return ret;
}

@Override
Expand All @@ -369,6 +419,24 @@ public int viewToModelChecked(double x, double y, Shape alloc, Bias[] biasReturn
public void paint(Graphics2D g, Shape alloc, Rectangle clipBounds) {
// The background is already cleared by BasicTextUI.paintBackground() which uses component.getBackground()
checkChildrenNotNull();
if (shadowText != null && !shadowText.isEmpty()) {
Rectangle2D.Double bounds = ViewUtils.shape2Bounds(alloc);
Rectangle2D.Double span = new Rectangle2D.Double(0, 0, 0, 0);
double y = bounds.y;
for (TextLayout l : shadowText) {
g.setColor(Color.gray);
float h = height(l);
span.setRect(bounds.x, y, l.getAdvance(), h);
HighlightsViewUtils.paintTextLayout(g, span, l, getDocumentView());
y += h;
}

bounds.height -= y - bounds.y;
bounds.y = y;

alloc = bounds;
}

children.paint(this, g, alloc, clipBounds);

if (getDocumentView().op.isGuideLinesEnable()) {
Expand Down Expand Up @@ -507,6 +575,12 @@ public int getNextVisualPositionFromChecked(int offset, Bias bias, Shape alloc,
retOffset = children.getNextVisualPositionY(this, offset, bias, alloc,
direction == SwingConstants.SOUTH, biasRet,
HighlightsViewUtils.getMagicX(docView, this, offset, bias, alloc));
if (shadowText != null && direction == SwingConstants.SOUTH) {
//TODO: reuse computed height
for (TextLayout l : shadowText) {
retOffset += height(l);
}
}
} else {
retOffset = offset;
}
Expand All @@ -516,7 +590,19 @@ public int getNextVisualPositionFromChecked(int offset, Bias bias, Shape alloc,
}
return retOffset;
}


//TODO: investigate:
int getNextVisualPositionY(ParagraphView pView,
int offset, Bias bias, Shape pAlloc, boolean southDirection, Bias[] biasRet, double x) {
int retOffset = children.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x);
// if (shadowText != null && southDirection) {
// for (TextLayout l : shadowText) {
// retOffset += l.getAscent() + l.getDescent();
// }
// }
return retOffset;
}

void releaseTextLayouts() {
children = null;
markChildrenInvalid();
Expand Down Expand Up @@ -629,4 +715,7 @@ protected String getDumpName() {
return "PV";
}

private float height(TextLayout l) {
return (float) Math.ceil(l.getAscent() + l.getDescent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,21 @@ public ParagraphViewChildren(int capacity) {
boolean isWrapped() {
return (wrapInfo != null);
}


//TODO: move to ParagraphView:
float shadowHeight;
float shadowWidth;

/**
* Height of
* @return
*/
float height() {
return (wrapInfo == null) ? childrenHeight : wrapInfo.height(this);
return ((wrapInfo == null) ? childrenHeight : wrapInfo.height(this)) + shadowHeight;
}

float width() {
return (wrapInfo == null) ? (float) childrenWidth() : wrapInfo.width();
return Math.max((wrapInfo == null) ? (float) childrenWidth() : wrapInfo.width(), shadowWidth);
}

double childrenWidth() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,9 @@ public float getAscent() {
return docView.op.getDefaultAscent(); // Currently the ascent is global
}

public float getShadowHeight() {
ParagraphViewChildren pViewChildren = docView.getParagraphView(pViewIndex).children;
return pViewChildren != null ? pViewChildren.shadowHeight : 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,50 @@ public final class PrependedTextView extends EditorView {
private final EditorView delegate;
private final TextLayout prependedTextLayout;
private final double prependedTextWidth;
private final TextLayout shadowPrependedTextLayout;
private final double shadowPrependedTextWidth;

public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView delegate) {
super(null);
this.attributes = attributes;
this.delegate = delegate;
Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont());
prependedTextLayout = op.createTextLayout((String) attributes.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND), font);
// Advance represents the width of the full string, including leading
// and trailing spaces
float width = prependedTextLayout.getAdvance();
// The prependTextWidth is rounded to full char widths, so that layout
// is not destroyed too much
double em = op.getDefaultCharWidth();
prependedTextWidth = Math.ceil(width / em) * em;

if (attributes.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) instanceof String shadowText) {
Font shadowTextFont = ViewUtils.getFont(attributes, op.getDefaultFont());
shadowPrependedTextLayout = op.createTextLayout(shadowText, shadowTextFont);
// Advance represents the width of the full string, including leading
// and trailing spaces
float width = shadowPrependedTextLayout.getAdvance();
// The prependTextWidth is rounded to full char widths, so that layout
// is not destroyed too much
double em = op.getDefaultCharWidth();
shadowPrependedTextWidth = Math.ceil(width / em) * em;
} else {
shadowPrependedTextLayout = null;
shadowPrependedTextWidth = 0;
}

if (attributes.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String virtualText) {
Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont());
prependedTextLayout = op.createTextLayout(virtualText, font);
// Advance represents the width of the full string, including leading
// and trailing spaces
float width = prependedTextLayout.getAdvance();
// The prependTextWidth is rounded to full char widths, so that layout
// is not destroyed too much
double em = op.getDefaultCharWidth();
prependedTextWidth = Math.ceil(width / em) * em;
} else {
prependedTextLayout = null;
prependedTextWidth = 0;
}
}

@Override
public float getPreferredSpan(int axis) {
float superSpan = delegate.getPreferredSpan(axis);
if (axis == View.X_AXIS) {
superSpan += prependedTextWidth;
superSpan += prependedTextWidth + shadowPrependedTextWidth;
}
return superSpan;
}
Expand All @@ -73,16 +96,16 @@ public AttributeSet getAttributes() {
public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) {
Shape res = delegate.modelToViewChecked(offset, alloc, bias);
Rectangle2D rect = ViewUtils.shapeAsRect(res);
rect.setRect(rect.getX() + prependedTextWidth, rect.getY(), rect.getWidth(), rect.getHeight());
rect.setRect(rect.getX(), rect.getY(), rect.getWidth() + prependedTextWidth + shadowPrependedTextWidth, rect.getHeight());
return rect;
}

@Override
public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) {
Rectangle2D span = ViewUtils.shapeAsRect(hViewAlloc);
span.setRect(span.getX() + prependedTextWidth, span.getY(), span.getWidth() - prependedTextWidth, span.getHeight());
span.setRect(span.getX() + prependedTextWidth + shadowPrependedTextWidth, span.getY(), span.getWidth() - prependedTextWidth - shadowPrependedTextWidth, span.getHeight());
delegate.paint(g, span, clipBounds);
span.setRect(span.getX() - prependedTextWidth, span.getY(), prependedTextWidth, span.getHeight());
span.setRect(span.getX() - prependedTextWidth - shadowPrependedTextWidth, span.getY(), prependedTextWidth + shadowPrependedTextWidth, span.getHeight());

HighlightsSequence highlights = getDocumentView().getPaintHighlights(this, 0);

Expand All @@ -92,9 +115,17 @@ public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) {
HighlightsViewUtils.paintBackgroundHighlights(g, span, attrs, getDocumentView()); //TODO: clear some attributes (like boxes)???
}

g.setColor(Color.gray);
span.setRect(span.getX(), span.getY(), prependedTextWidth, span.getHeight());
HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView());
if (shadowPrependedTextLayout != null) {
g.setColor(Color.gray);
span.setRect(span.getX(), span.getY(), shadowPrependedTextWidth, span.getHeight());
HighlightsViewUtils.paintTextLayout(g, span, shadowPrependedTextLayout, getDocumentView());
}

if (prependedTextLayout != null) {
g.setColor(Color.gray);
span.setRect(span.getX() + shadowPrependedTextWidth, span.getY(), prependedTextWidth, span.getHeight());
HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView());
}
}

ParagraphView getParagraphView() {
Expand All @@ -119,8 +150,8 @@ public void setRawEndOffset(int offset) {
@Override
public int viewToModelChecked(double x, double y, Shape alloc, Position.Bias[] biasReturn) {
Rectangle2D bounds = ViewUtils.shapeAsRect(alloc);
bounds.setRect(bounds.getX() + prependedTextWidth, bounds.getY(),
bounds.getWidth() - prependedTextWidth, bounds.getHeight());
bounds.setRect(bounds.getX() + prependedTextWidth + shadowPrependedTextWidth, bounds.getY(),
bounds.getWidth() - prependedTextWidth + shadowPrependedTextWidth, bounds.getHeight());
if (x <= bounds.getX()) {
return getStartOffset();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
public final class ViewUtils {

public static final String KEY_VIRTUAL_TEXT_PREPEND = "virtual-text-prepend"; //NOI18N
public static final String KEY_SHADOW_TEXT_PREPEND = "shadow-text-prepend"; //NOI18N
public static final String KEY_VERTICAL_SHADOW_TEXT_PREPEND = "vertical-shadow-text-prepend"; //NOI18N

private ViewUtils() { // No instances
}
Expand Down
Loading
Loading