From 05477618d0f2aec24a0fae1c452703386c226a4c Mon Sep 17 00:00:00 2001 From: bobbylight Date: Sat, 24 Sep 2022 23:39:59 -0400 Subject: [PATCH] #459: Make the 'expanded folds hide' option run on a timer rather than be immediate --- .../org/fife/ui/rtextarea/FoldIndicator.java | 101 ++++++++++++++++-- .../java/org/fife/ui/rtextarea/Gutter.java | 2 +- .../fife/ui/rtextarea/FoldIndicatorTest.java | 4 +- .../org/fife/ui/rtextarea/GutterTest.java | 4 +- .../ui/rsyntaxtextarea/demo/DemoRootPane.java | 2 +- 5 files changed, 97 insertions(+), 16 deletions(-) diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/FoldIndicator.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/FoldIndicator.java index 2b64fe295..c5c84e013 100755 --- a/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/FoldIndicator.java +++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/FoldIndicator.java @@ -10,6 +10,8 @@ package org.fife.ui.rtextarea; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -121,11 +123,30 @@ public class FoldIndicator extends AbstractGutterComponent { */ public static final Color DEFAULT_FOLD_BACKGROUND = Color.WHITE; + /** + * The alpha used for "collapsed" fold icons. + */ + private float collapsedFoldIconAlpha; + + /** + * Used to update the collapsed fold icons' alpha value on a timer. + */ + private AlphaRunnable alphaRunnable; + + /** + * The timer used to update collapsed fold icons' alpha. + */ + private Timer timer; + /** * Listens for events in this component. */ private Listener listener; + private static final int COLLAPSED_FOLD_ALPHA_DELAY_MILLIS = 16; + + private static final float ALPHA_DELTA = 0.1f; + public FoldIndicator(RTextArea textArea) { super(textArea); @@ -236,7 +257,7 @@ public Color getFoldIconBackground() { * @return Whether to paint expanded folds. */ private boolean getPaintExpandedFolds() { - return expandedFoldRenderStrategy == ExpandedFoldRenderStrategy.ALWAYS || getGutter().isArmed(); + return expandedFoldRenderStrategy == ExpandedFoldRenderStrategy.ALWAYS || collapsedFoldIconAlpha > 0; } @@ -357,6 +378,19 @@ public String getToolTipText(MouseEvent e) { } + void gutterArmedUpdate(boolean armed) { + if (expandedFoldRenderStrategy == ExpandedFoldRenderStrategy.ON_HOVER) { + alphaRunnable.delta = armed ? ALPHA_DELTA : -ALPHA_DELTA; + timer.restart(); + } + else { + collapsedFoldIconAlpha = 1; + timer.stop(); + repaint(); + } + } + + @Override void handleDocumentEvent(DocumentEvent e) { int newLineCount = textArea.getLineCount(); @@ -372,11 +406,13 @@ protected void init() { super.init(); setForeground(DEFAULT_FOREGROUND); setFoldIconBackground(DEFAULT_FOLD_BACKGROUND); - setStyle(FoldIndicatorStyle.CLASSIC); + setStyle(FoldIndicatorStyle.MODERN); listener = new Listener(this); visibleRect = new Rectangle(); setShowCollapsedRegionToolTips(true); - setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ALWAYS); + alphaRunnable = new AlphaRunnable(); + timer = new Timer(COLLAPSED_FOLD_ALPHA_DELAY_MILLIS, alphaRunnable); + timer.setRepeats(true); } @@ -476,7 +512,7 @@ protected void paintComponent(Graphics g) { } if (fold.isCollapsed()) { int x = (width - collapsedFoldIcon.getIconWidth()) / 2; - paintIcon(collapsedFoldIcon, g, x, y); + paintIcon(collapsedFoldIcon, g, x, y, true); // Skip to next line to paint, taking extra care for lines with // block ends and begins together, e.g. "} else {" do { @@ -493,7 +529,7 @@ protected void paintComponent(Graphics g) { } else if (getPaintExpandedFolds()) { int x = (width - expandedFoldIcon.getIconWidth()) / 2; - paintIcon(expandedFoldIcon, g, x, y); + paintIcon(expandedFoldIcon, g, x, y, false); } paintFoldArmed = false; } @@ -606,7 +642,7 @@ private void paintComponentWrapped(Graphics g) { } if (fold.isCollapsed()) { int x = (width - collapsedFoldIcon.getIconWidth()) / 2; - paintIcon(collapsedFoldIcon, g, x, y); + paintIcon(collapsedFoldIcon, g, x, y, true); y += LineNumberList.getChildViewBounds(v, line, visibleEditorRect).height; line += fold.getLineCount() + 1; @@ -614,7 +650,7 @@ private void paintComponentWrapped(Graphics g) { else { if (getPaintExpandedFolds()) { int x = (width - expandedFoldIcon.getIconWidth()) / 2; - paintIcon(expandedFoldIcon, g, x, y); + paintIcon(expandedFoldIcon, g, x, y, false); } y += curLineH; line++; @@ -630,9 +666,26 @@ private void paintComponentWrapped(Graphics g) { } - private void paintIcon(FoldIndicatorIcon icon, Graphics g, int x, int y) { - icon.setArmed(paintFoldArmed); - icon.paintIcon(this, g, x, y); + private void paintIcon(FoldIndicatorIcon icon, Graphics g, int x, int y, boolean collapsed) { + + Graphics2D g2d = (Graphics2D)g; + Composite orig = g2d.getComposite(); + + if (!collapsed) { + if (collapsedFoldIconAlpha == 0) { + return; + } + AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, collapsedFoldIconAlpha); + g2d.setComposite(ac); + } + try { + icon.setArmed(paintFoldArmed); + icon.paintIcon(this, g, x, y); + } finally { + if (!collapsed) { + g2d.setComposite(orig); + } + } } @@ -674,6 +727,15 @@ public void setAdditionalLeftMargin(int leftMargin) { } + private void setCollapsedFoldIconAlpha(float collapsedFoldIconAlpha) { + collapsedFoldIconAlpha = Math.max(0, Math.min(collapsedFoldIconAlpha, 1)); + if (collapsedFoldIconAlpha != this.collapsedFoldIconAlpha) { + this.collapsedFoldIconAlpha = collapsedFoldIconAlpha; + repaint(); + } + } + + /** * Sets the strategy to use for rendering expanded folds. * @@ -685,6 +747,7 @@ public void setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy strategy) { throw new NullPointerException("strategy cannot be null"); } expandedFoldRenderStrategy = strategy; + collapsedFoldIconAlpha = strategy == ExpandedFoldRenderStrategy.ALWAYS ? 1 : 0; } @@ -814,6 +877,24 @@ public void setTextArea(RTextArea textArea) { } + /** + * Updates the alpha used for this component's "collapsed" fold icons, if + * necessary. + */ + private class AlphaRunnable implements ActionListener { + + private float delta; + + @Override + public void actionPerformed(ActionEvent e) { + setCollapsedFoldIconAlpha(collapsedFoldIconAlpha + delta); + if (collapsedFoldIconAlpha == 0 || collapsedFoldIconAlpha == 1) { + timer.stop(); + } + } + } + + /** * Listens for events in this component. */ diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/Gutter.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/Gutter.java index 792f0f66a..862d6498c 100755 --- a/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/Gutter.java +++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/Gutter.java @@ -600,7 +600,7 @@ void setArmed(boolean armed) { if (armed != this.armed) { this.armed = armed; if (foldIndicator != null) { - foldIndicator.repaint(); + foldIndicator.gutterArmedUpdate(armed); } } } diff --git a/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/FoldIndicatorTest.java b/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/FoldIndicatorTest.java index f7be88faf..1d08e33a0 100755 --- a/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/FoldIndicatorTest.java +++ b/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/FoldIndicatorTest.java @@ -95,9 +95,9 @@ void testGetSetAdditionalLeftMargin_error_negativeValue() { void testGetSetExpandedFoldRenderStrategy() { RSyntaxTextArea textArea = createTextArea(); FoldIndicator fi = new FoldIndicator(textArea); - Assertions.assertEquals(ExpandedFoldRenderStrategy.ALWAYS, fi.getExpandedFoldRenderStrategy()); - fi.setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ON_HOVER); Assertions.assertEquals(ExpandedFoldRenderStrategy.ON_HOVER, fi.getExpandedFoldRenderStrategy()); + fi.setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ALWAYS); + Assertions.assertEquals(ExpandedFoldRenderStrategy.ALWAYS, fi.getExpandedFoldRenderStrategy()); } diff --git a/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/GutterTest.java b/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/GutterTest.java index 1e5486c28..5f1698621 100755 --- a/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/GutterTest.java +++ b/RSyntaxTextArea/src/test/java/org/fife/ui/rtextarea/GutterTest.java @@ -287,9 +287,9 @@ void testGetSetCurrentLineNumberColor() { void testGetSetExpandedFoldRenderStrategy() { RTextArea textArea = new RTextArea(PLAIN_TEXT); Gutter gutter = new Gutter(textArea); - Assertions.assertEquals(ExpandedFoldRenderStrategy.ALWAYS, gutter.getExpandedFoldRenderStrategy()); - gutter.setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ON_HOVER); Assertions.assertEquals(ExpandedFoldRenderStrategy.ON_HOVER, gutter.getExpandedFoldRenderStrategy()); + gutter.setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ALWAYS); + Assertions.assertEquals(ExpandedFoldRenderStrategy.ALWAYS, gutter.getExpandedFoldRenderStrategy()); } diff --git a/RSyntaxTextAreaDemo/src/main/java/org/fife/ui/rsyntaxtextarea/demo/DemoRootPane.java b/RSyntaxTextAreaDemo/src/main/java/org/fife/ui/rsyntaxtextarea/demo/DemoRootPane.java index fff71a2d1..f03b16a0c 100755 --- a/RSyntaxTextAreaDemo/src/main/java/org/fife/ui/rsyntaxtextarea/demo/DemoRootPane.java +++ b/RSyntaxTextAreaDemo/src/main/java/org/fife/ui/rsyntaxtextarea/demo/DemoRootPane.java @@ -131,8 +131,8 @@ private JMenuBar createMenuBar() { JMenu foldStyleSubMenu = new JMenu("Fold Region Style"); JRadioButtonMenuItem classicStyleItem = new JRadioButtonMenuItem( new FoldStyleAction(FoldIndicatorStyle.CLASSIC)); - classicStyleItem.setSelected(true); JRadioButtonMenuItem modernStyleItem = new JRadioButtonMenuItem(new FoldStyleAction(FoldIndicatorStyle.MODERN)); + modernStyleItem.setSelected(true); bg = new ButtonGroup(); bg.add(classicStyleItem); bg.add(modernStyleItem);