changeset 1912:9fa922236aff

highlights
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 16 Apr 2025 21:59:49 -0600
parents cd4c11d7dc7e
children 4f0c14fad13b
files src/luan/modules/editor/find.luan src/luan/modules/swing/DocumentUpdateListener.java src/luan/modules/swing/LuanDocumentListener.java src/luan/modules/swing/TextAreaLineNumbersLuan.java src/luan/modules/swing/TextAreaLuan.java src/luan/modules/swing/Text_area.luan
diffstat 6 files changed, 137 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/modules/editor/find.luan	Wed Apr 16 09:59:48 2025 -0600
+++ b/src/luan/modules/editor/find.luan	Wed Apr 16 21:59:49 2025 -0600
@@ -60,6 +60,7 @@
 			status_bar.text = "Regex error: "..e.get_message()
 			return
 		end
+		text_area.set_hightlights(matches)
 		local n_matches = #matches
 		if n_matches == 0 then
 			status_bar.text = "0 matches"
@@ -206,6 +207,8 @@
 		find_panel.visible = visible
 		if visible then
 			find_field.request_focus_in_window()
+		else
+			text_area.clear_hightlights()
 		end
 	end
 	function window.find_case_insensitive(_)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/swing/DocumentUpdateListener.java	Wed Apr 16 21:59:49 2025 -0600
@@ -0,0 +1,8 @@
+package luan.modules.swing;
+
+import javax.swing.event.DocumentEvent;
+
+
+public interface DocumentUpdateListener {
+	void updated(DocumentEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/swing/LuanDocumentListener.java	Wed Apr 16 21:59:49 2025 -0600
@@ -0,0 +1,25 @@
+package luan.modules.swing;
+
+import javax.swing.event.DocumentListener;
+import javax.swing.event.DocumentEvent;
+
+
+public class LuanDocumentListener implements DocumentListener {
+	private final DocumentUpdateListener dul;
+
+	public LuanDocumentListener(DocumentUpdateListener dul) {
+		this.dul = dul;
+	}
+
+	@Override public void changedUpdate(DocumentEvent e) {
+		dul.updated(e);
+	}
+
+	@Override public void removeUpdate(DocumentEvent e) {
+		dul.updated(e);
+	}
+
+	@Override public void insertUpdate(DocumentEvent e) {
+		dul.updated(e);
+	}
+}
--- a/src/luan/modules/swing/TextAreaLineNumbersLuan.java	Wed Apr 16 09:59:48 2025 -0600
+++ b/src/luan/modules/swing/TextAreaLineNumbersLuan.java	Wed Apr 16 21:59:49 2025 -0600
@@ -23,7 +23,7 @@
 import goodjava.logging.LoggerFactory;
 
 
-public class TextAreaLineNumbersLuan extends JPanel implements DocumentListener {
+public class TextAreaLineNumbersLuan extends JPanel implements DocumentUpdateListener {
 	private static final Logger logger = LoggerFactory.getLogger(TextAreaLineNumbersLuan.class);
 
 	private final TextAreaLuan textArea;
@@ -43,7 +43,7 @@
 		}
 		fixWidth();
 		fixHeights();
-		textArea.getDocument().addDocumentListener(new WeakDocumentListener(this));
+		textArea.getDocument().addDocumentListener(new WeakDocumentListener(new LuanDocumentListener(this)));
 		textArea.addPropertyChangeListener("lineWrap",lineWrapListener);
 		textArea.addComponentListener(resizeListener);
 	}
@@ -125,7 +125,7 @@
 		boolean changed = false;
 		for( int i=start; i<=end; i++ ) {
 			Component label = getComponent(i);
-			Dimension size = label.getPreferredSize();
+			Dimension size = label.getMaximumSize();
 			int height;
 			try {
 				height = textArea.getLineHeight(i);
@@ -135,8 +135,9 @@
 			//logger.info("height "+i+" "+height);
 			if( height != size.height ) {
 				changed = true;
+				//logger.info("width "+i+" "+size.width);
 				Dimension newSize = new Dimension( size.width, height );
-				label.setPreferredSize(newSize);
+				//label.setPreferredSize(newSize);
 				label.setMaximumSize(newSize);
 			}
 		}
@@ -163,7 +164,7 @@
 		}
 	}
 
-	@Override public void changedUpdate(DocumentEvent event) {
+	@Override public void updated(DocumentEvent event) {
 		//logger.info(event.getType().toString()+" "+event.getOffset()+" "+event.getLength());
 		int n = textArea.getLineCount();
 		if( lines == n ) {
@@ -204,14 +205,6 @@
 		}
 	}
 
-	@Override public void removeUpdate(DocumentEvent e) {
-		changedUpdate(e);
-	}
-
-	@Override public void insertUpdate(DocumentEvent e) {
-		changedUpdate(e);
-	}
-
 	private final PropertyChangeListener lineWrapListener = new PropertyChangeListener() {
 		@Override public void propertyChange(PropertyChangeEvent evt) {
 			fixHeights();
--- a/src/luan/modules/swing/TextAreaLuan.java	Wed Apr 16 09:59:48 2025 -0600
+++ b/src/luan/modules/swing/TextAreaLuan.java	Wed Apr 16 21:59:49 2025 -0600
@@ -1,5 +1,7 @@
 package luan.modules.swing;
 
+import java.util.List;
+import java.util.Collections;
 import java.awt.Rectangle;
 import java.awt.Graphics;
 import java.awt.Color;
@@ -12,11 +14,12 @@
 import javax.swing.Timer;
 import javax.swing.text.DefaultCaret;
 import javax.swing.text.BadLocationException;
+import javax.swing.event.DocumentEvent;
 import goodjava.logging.Logger;
 import goodjava.logging.LoggerFactory;
 
 
-public class TextAreaLuan extends JTextArea {
+public class TextAreaLuan extends JTextArea implements DocumentUpdateListener {
 	private static final Logger logger = LoggerFactory.getLogger(TextAreaLuan.class);
 
 	private static final DefaultCaret flatLafCaret = new DefaultCaret() {
@@ -38,7 +41,19 @@
 		}
 	};
 
+	public static class Range {
+		private final int start;
+		private final int end;
+
+		public Range(int start,int end) {
+			this.start = start;
+			this.end = end;
+		}
+	}
+
 	private boolean showWhitespace = false;
+	private List<Range> highlights = Collections.emptyList();
+	private static final Color highlightColor = new Color(0x2dada3);
 
 	public TextAreaLuan() {
 		super();
@@ -46,6 +61,7 @@
 		if( UIManager.getLookAndFeel().getName().startsWith("FlatLaf") ) {
 			setCaret(flatLafCaret);
 		}
+		getDocument().addDocumentListener(new WeakDocumentListener(new LuanDocumentListener(this)));
 	}
 
 	public int getLineHeight(int line) throws BadLocationException {
@@ -68,31 +84,79 @@
 		repaint();
 	}
 
+	public void setHightlights(List<Range> highlights) {
+		this.highlights = highlights;
+		repaint();
+	}
+
+	public void clearHighlights() {
+		if( highlights.size() > 0 )
+			setHightlights(Collections.emptyList());
+	}
+
+	@Override public void updated(DocumentEvent event) {
+		clearHighlights();
+	}
+
 	@Override protected void paintComponent(Graphics g) {
 		super.paintComponent(g);
-		if( !showWhitespace )
-			return;
-		g.setColor(Color.LIGHT_GRAY);
-		String text = getText();
-		int ascent = g.getFontMetrics().getAscent();
-		Rectangle visible = getVisibleRect();
-		for( int pos=0; pos<text.length(); pos++ ) {
-			char ch = text.charAt(pos);
-			String symbol;
-			switch(ch) {
-				case ' ': symbol = "·"; break;
-				case '\t': symbol = "→"; break;
-				case '\n': symbol = "¶"; break;
-				default: continue;
+		try {
+			if( showWhitespace ) {
+				g.setColor(Color.LIGHT_GRAY);
+				String text = getText();
+				int ascent = g.getFontMetrics().getAscent();
+				Rectangle visible = getVisibleRect();
+				for( int pos=0; pos<text.length(); pos++ ) {
+					char ch = text.charAt(pos);
+					String symbol;
+					switch(ch) {
+						case ' ': symbol = "·"; break;
+						case '\t': symbol = "→"; break;
+						case '\n': symbol = "¶"; break;
+						default: continue;
+					}
+					Rectangle r = modelToView(pos);
+					if( visible.contains(r)	)
+						g.drawString( symbol, r.x, r.y + ascent );
+				}
 			}
-			Rectangle r;
-			try {
-				r = modelToView(pos);
-			} catch(BadLocationException e) {
-				throw new RuntimeException(e);
+			g.setColor(highlightColor);
+			for( Range range : highlights ) {
+				Rectangle r1 = modelToView(range.start);
+				Rectangle r2 = modelToView(range.end);
+				if( r1.y == r2.y ) {
+					g.drawRect( r1.x, r1.y, r2.x - r1.x, r1.height );
+				} else {
+					g.drawLine( r1.x, r1.y, r1.x, r1.y+r1.height );
+					g.drawLine( r2.x, r2.y, r2.x, r2.y+r2.height );
+					for( int i=range.start; i<range.end; i++ ) {
+						Rectangle r = modelToView(i);
+						if( r1.y == r.y ) {
+							r2 = r;
+						} else {
+							int x1 = r1.x;
+							int x2 = r2.x + r2.width;
+							int y = r1.y;
+							g.drawLine( x1, y, x2, y );
+							y += r1.height;
+							g.drawLine( x1, y, x2, y );
+							r1 = r;
+							r2 = r;
+						}
+					}
+					r2 = modelToView(range.end);
+					if( r2.x > r1.x ) {
+						int x1 = r1.x;
+						int x2 = r2.x;
+						int y = r1.y;
+						g.drawLine( x1, y, x2, y );
+						y += r1.height;
+						g.drawLine( x1, y, x2, y );
+					}
+				}
 			}
-			if( visible.contains(r)	)
-				g.drawString( symbol, r.x, r.y + ascent );
+		} catch(BadLocationException e) {
+			throw new RuntimeException(e);
 		}
 	}
 
--- a/src/luan/modules/swing/Text_area.luan	Wed Apr 16 09:59:48 2025 -0600
+++ b/src/luan/modules/swing/Text_area.luan	Wed Apr 16 21:59:49 2025 -0600
@@ -1,6 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local set_metatable = Luan.set_metatable or error()
+local ipairs = Luan.ipairs or error()
 local Utils = require "luan:swing/Utils.luan"
 local fail = Utils.fail or error()
 local make_metatable = Utils.make_metatable or error()
@@ -103,6 +104,14 @@
 			return jtext_area.getDocument().replace(start_pos-1,length,text)
 		end)
 	end
+	function text_area.set_hightlights(hightlights)
+		local list = {}
+		for _, hightlight in ipairs(hightlights) do
+			list[#list+1] = TextAreaLuan.Range.new(hightlight.start-1,hightlight.end_-1)
+		end
+		jtext_area.setHightlights(list)
+	end
+	text_area.clear_hightlights = jtext_area.clearHighlights
 	set_metatable(text_area,mt)
 	return text_area
 end