Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customization of ErrorStrip painting #535

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}

}


}