From 9347d1ad9b801bd9c248e60e82261dd8ca55095d Mon Sep 17 00:00:00 2001 From: Christopher Jennings <25697451+CGJennings@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:40:44 -0500 Subject: [PATCH] allow subclasses to customize marker painting --- .../fife/ui/rsyntaxtextarea/ErrorStrip.java | 180 ++++++++++++++---- 1 file changed, 138 insertions(+), 42 deletions(-) diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/ErrorStrip.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/ErrorStrip.java index 887cbb27..a22073a4 100755 --- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/ErrorStrip.java +++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/ErrorStrip.java @@ -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; @@ -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. */ @@ -162,6 +170,7 @@ public ErrorStrip(RSyntaxTextArea textArea) { setFollowCaret(true); setCaretMarkerColor(getDefaultCaretMarkerColor()); setMarkerToolTipProvider(null); // Install default + setCaretMarkerOnTop(false); } @@ -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; + } /** @@ -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); + } /** @@ -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; } } @@ -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. @@ -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()