view src/luan/modules/swing/TextAreaLuan.java @ 1900:aa24812aaf98

minor
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 14 Apr 2025 19:06:46 -0600
parents ef0438da68ae
children
line wrap: on
line source

package luan.modules.swing;

import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.Timer;
import javax.swing.text.DefaultCaret;
import javax.swing.text.BadLocationException;
import goodjava.logging.Logger;
import goodjava.logging.LoggerFactory;


public class TextAreaLuan extends JTextArea {
	private static final Logger logger = LoggerFactory.getLogger(TextAreaLuan.class);

	private static final DefaultCaret flatLafCaret = new DefaultCaret() {
		private final Timer blinkTimer = new Timer(500, new ActionListener() {
			@Override public void actionPerformed(ActionEvent evt) {
				setVisible(!isVisible());
			}
		});

		@Override public void focusGained(FocusEvent e) {
			super.focusGained(e);
			blinkTimer.start();
		}

		@Override public void focusLost(FocusEvent e) {
			// Don't call super — we want to keep caret visible
			blinkTimer.stop();
			setVisible(true);
		}
	};

	private boolean showWhitespace = false;

	public TextAreaLuan() {
		super();
		//logger.info(UIManager.getLookAndFeel().getName());
		if( UIManager.getLookAndFeel().getName().startsWith("FlatLaf") ) {
			setCaret(flatLafCaret);
		}
	}

	public int getLineHeight(int line) throws BadLocationException {
		if( !getLineWrap() )
			return getRowHeight();
		int startOffset = modelToView(getLineStartOffset(line)).y;
		int endOffset = modelToView(getLineEndOffset(line)).y;
		int height = endOffset - startOffset;
		if( height == 0 )
			height = getRowHeight();
		return height;
	}

	public int getLineRows(int line) throws BadLocationException {
		return getLineHeight(line) / getRowHeight();
	}

	public void showWhitespace(boolean showWhitespace) {
		this.showWhitespace = showWhitespace;
		repaint();
	}

	@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;
			}
			Rectangle r;
			try {
				r = modelToView(pos);
			} catch(BadLocationException e) {
				throw new RuntimeException(e);
			}
			if( visible.contains(r)	)
				g.drawString( symbol, r.x, r.y + ascent );
		}
	}
}