Skip to content

Commit

Permalink
allow subclasses to customize marker painting
Browse files Browse the repository at this point in the history
  • Loading branch information
CGJennings authored and bobbylight committed Apr 12, 2024
1 parent d6fad8c commit 9347d1a
Showing 1 changed file with 138 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
Expand Down Expand Up @@ -122,6 +124,12 @@ public class ErrorStrip extends JPanel {
*/
private Color caretMarkerColor;


/**
* Whether to draw the caret marker before or after the parser markers.
*/
private boolean paintCaretMarkerOnTop;

/**
* Where we paint the caret marker.
*/
Expand Down Expand Up @@ -162,6 +170,7 @@ public ErrorStrip(RSyntaxTextArea textArea) {
setFollowCaret(true);
setCaretMarkerColor(getDefaultCaretMarkerColor());
setMarkerToolTipProvider(null); // Install default
setCaretMarkerOnTop(false);
}


Expand Down Expand Up @@ -231,6 +240,33 @@ private Color getBrighterColor(Color c) {
public Color getCaretMarkerColor() {
return caretMarkerColor;
}

/**
* Sets whether to paint the caret marker, if enabled, over the markers
* for parser notices. The default is to paint the caret marker under
* notices.
*
* @param paintOver If true, paint the caret marker over other markers.
* @see #isCaretMarkerOnTop()
*/
public void setCaretMarkerOnTop(boolean paintOver) {
if (paintOver != paintCaretMarkerOnTop) {
paintCaretMarkerOnTop = paintOver;
listener.caretUpdate(null); // Force repaint
}
}

/**
* Returns whether the caret marker is painted overtop of any other
* markers.
*
* @return True is the caret marker is drawn overtop of other markers,
* false otherwise.
* @see #setCaretMarkerOnTop(boolean)
*/
public boolean isCaretMarkerOnTop() {
return paintCaretMarkerOnTop;
}


/**
Expand Down Expand Up @@ -335,20 +371,88 @@ private int lineToY(int line, Rectangle r) {
return Math.round((h-1) * line / Math.max(lineCount, linesPerVisibleRect));
}


/**
* Overridden to (possibly) draw the caret's position.
*
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (caretLineY>-1) {
g.setColor(getCaretMarkerColor());
g.fillRect(0, caretLineY, getWidth(), 2);
}
}
/**
* Overridden to (possibly) draw the caret's position.
*
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (!paintCaretMarkerOnTop) {
paintCaretMarker((Graphics2D) g, caretLineY);
}
}

/**
* Overridden to (possibly) draw the caret's position.
*
* @param g The graphics context.
*/
@Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (paintCaretMarkerOnTop) {
paintCaretMarker((Graphics2D) g, caretLineY);
}
}

/**
* Sets up for painting the caret, then delegates painting to
* {@link #paintCaretMarker(java.awt.Graphics2D, int, int)}.
*/
private void paintCaretMarker(Graphics2D g, int caretLineY) {
if (caretLineY > -1) {
AffineTransform oldTransform = g.getTransform();
try {
g.translate(0, caretLineY - MARKER_HEIGHT/2);
g.setColor(getCaretMarkerColor());
paintCaretMarker(g, getWidth(), MARKER_HEIGHT);
} finally {
g.setTransform(oldTransform);
}
}
}

/**
* Paints the caret marker.
* Can be overridden to customize how the marker is drawn.
* All drawing must be kept within the rectangle
* (0, 0, width, height).
*
* @param g the graphics context to paint with
* @param width the width of the painting area
* @param height the height of the painting area
*/
protected void paintCaretMarker(Graphics2D g, int width, int height) {
final int y0 = height / 2 + (height & 1);
g.fillRect(0, y0, width, 2);
}

/**
* Paints the marker for a parser notice.
* Can be overridden to customize how the marker is drawn.
* All drawing must be kept within the rectangle
* (0, 0, width, height).
*
* @param g the graphics context
* @param notice the notice to paint
* @param width the width of the painting area
* @param height the height of the painting area
*/
protected void paintParserNoticeMarker(Graphics2D g, ParserNotice notice, int width, int height) {
Color borderColor = notice.getColor();
if (borderColor==null) {
borderColor = Color.DARK_GRAY;
}
Color fillColor = getBrighterColor(borderColor);

g.setColor(fillColor);
g.fillRect(0,0, width,height);

g.setColor(borderColor);
g.drawRect(0,0, width-1,height-1);
}


/**
Expand Down Expand Up @@ -660,8 +764,13 @@ public void caretUpdate(CaretEvent e) {
int line = textArea.getCaretLineNumber();
caretLineY = lineToY(line, r);
if (caretLineY!=lastLineY) {
repaint(0,lastLineY, getWidth(), 2); // Erase old position
repaint(0,caretLineY, getWidth(), 2);
// Extend caret position to repaint rectangle around it
final int dyRectTop = MARKER_HEIGHT/2 + 1;
final int rectHeight = MARKER_HEIGHT + 3;

// Erase old position
repaint(0,lastLineY - dyRectTop, getWidth(), rectHeight);
repaint(0,caretLineY - dyRectTop, getWidth(), rectHeight);
lastLineY = caretLineY;
}
}
Expand Down Expand Up @@ -832,6 +941,8 @@ public int hashCode() { // FindBugs, since we override equals()

}

/** The height of markers and the cursor painting area. Must be odd. */
private static final int MARKER_HEIGHT = 5;

/**
* A "marker" in this error strip, representing one or more notices.
Expand Down Expand Up @@ -863,23 +974,22 @@ public boolean containsMarkedOccurrence() {
return result;
}

public Color getColor() {
// Return the color for the highest-level parser.
Color c = null;
public ParserNotice getHighestPriorityNotice() {
ParserNotice selectedNotice = null;
int lowestLevel = Integer.MAX_VALUE; // ERROR is 0
for (ParserNotice notice : notices) {
if (notice.getLevel().getNumericValue()<lowestLevel) {
lowestLevel = notice.getLevel().getNumericValue();
c = notice.getColor();
selectedNotice = notice;
}
}
return c;
return selectedNotice;
}

@Override
public Dimension getPreferredSize() {
int w = PREFERRED_WIDTH - 4; // 2-pixel empty border
return new Dimension(w, 5);
return new Dimension(w, MARKER_HEIGHT);
}

@Override
Expand Down Expand Up @@ -910,25 +1020,13 @@ protected void mouseClicked(MouseEvent e) {

@Override
protected void paintComponent(Graphics g) {

// TODO: Give "priorities" and always pick color of a notice with
// highest priority (e.g. parsing errors will usually be red).

Color borderColor = getColor();
if (borderColor==null) {
borderColor = Color.DARK_GRAY;
final ParserNotice notice = getHighestPriorityNotice();
if (notice != null) {
paintParserNoticeMarker(
(Graphics2D) g, notice,
getWidth(), getHeight()
);
}
Color fillColor = getBrighterColor(borderColor);

int w = getWidth();
int h = getHeight();

g.setColor(fillColor);
g.fillRect(0,0, w,h);

g.setColor(borderColor);
g.drawRect(0,0, w-1,h-1);

}

@Override
Expand All @@ -943,8 +1041,6 @@ public void updateLocation() {
int y = lineToY(line - 1, null); // ParserNotices are 1-based
setLocation(2, y);
}

}


}

0 comments on commit 9347d1a

Please sign in to comment.