Mercurial Hosting > luan
view src/luan/modules/swing/TextAreaLuan.java @ 1957:269e78ad8a85
swing for windows
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 29 May 2025 22:29:15 -0600 |
parents | b785eff96faf |
children |
line wrap: on
line source
package luan.modules.swing; import java.util.List; import java.util.Collections; 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 javax.swing.text.View; import javax.swing.text.Element; import javax.swing.text.WrappedPlainView; import javax.swing.text.Segment; import javax.swing.text.DefaultEditorKit; import javax.swing.event.DocumentEvent; import goodjava.logging.Logger; import goodjava.logging.LoggerFactory; public class TextAreaLuan extends JTextArea implements DocumentUpdateListener { private static final Logger logger = LoggerFactory.getLogger(TextAreaLuan.class); private 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); } }; public static class Range { private final int start; private final int end; public Range(int start,int end) { this.start = start; this.end = end; } } static class CustomWrappedPlainView extends WrappedPlainView { public CustomWrappedPlainView(Element elem) { super(elem,false); } @Override protected int calculateBreakPosition(int p0, int p1) { try { int candidate = super.calculateBreakPosition(p0, p1); if (candidate == p1) return p1; // Everything fits — don't wrap early Segment segment = new Segment(); getDocument().getText(p0, candidate - p0 + 1, segment); char[] a = segment.array; int start = segment.offset; int i = start + segment.count - 1; if( !SwingLuan.isWordChar(a[i]) ) return candidate; do { if( --i < start ) return candidate; } while( SwingLuan.isWordChar(a[i]) ); int breakPos = i + 1 - start + p0; do { if( --i < start ) return candidate; } while( !SwingLuan.isWordChar(a[i]) ); return breakPos; } catch (BadLocationException e) { throw new RuntimeException(e); } } } private boolean showWhitespace = false; private List<Range> highlights = Collections.emptyList(); private static final Color highlightColor = new Color(0x2dada3); private final LuanDocumentListener ldl = new LuanDocumentListener(this); public TextAreaLuan() { super(); //logger.info(""+getFont().getSize()); if( UIManager.getLookAndFeel().getName().startsWith("FlatLaf") ) { setCaret(flatLafCaret); } getDocument().addDocumentListener(new WeakDocumentListener(ldl)); getActionMap().put( DefaultEditorKit.selectWordAction, SwingLuan.selectWordAction ); getActionMap().put( DefaultEditorKit.selectLineAction, SwingLuan.selectWordAndDotsAction ); } @Override public void updateUI() { super.updateUI(); if (UIManager.getLookAndFeel().getName().startsWith("FlatLaf")) { setUI(new com.formdev.flatlaf.ui.FlatTextAreaUI() { @Override public View create(Element elem) { if (getLineWrap() && getWrapStyleWord()) return new CustomWrappedPlainView(elem); return super.create(elem); } }); } } @Override public int getRowHeight() { return super.getRowHeight(); } 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 boolean isWhitespaceVisible() { return showWhitespace; } public void setWhitespaceVisible(boolean showWhitespace) { this.showWhitespace = showWhitespace; 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); 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 ); } } 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 ); } } } } catch(BadLocationException e) { throw new RuntimeException(e); } } private UndoManagerLuan getUndoManagerLuan() { return (UndoManagerLuan)getDocument().getProperty("undo"); } @Override public void paste() { UndoManagerLuan undo = getUndoManagerLuan(); undo.beginTransaction(); try { super.paste(); } finally { undo.endTransaction(); } } }