Skip to content

Commit

Permalink
Fix #459: Add API to hide expanded folds except on hover, and do so w…
Browse files Browse the repository at this point in the history
…hen the fold style is 'modern'
  • Loading branch information
bobbylight committed Sep 24, 2022
1 parent 81232b3 commit c4cf27d
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 5 deletions.
Expand Up @@ -11,8 +11,10 @@
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.View;

Expand All @@ -35,6 +37,8 @@ abstract class AbstractGutterComponent extends JPanel {
*/
protected int currentLineCount;

private static Listener listener;


/**
* Constructor.
Expand All @@ -47,6 +51,13 @@ abstract class AbstractGutterComponent extends JPanel {
}


@Override
public void addNotify() {
super.addNotify();
getListener().install(this);
}


/**
* Returns the bounds of a child view as a rectangle, since
* <code>View</code>s tend to use <code>Shape</code>.
Expand Down Expand Up @@ -81,6 +92,19 @@ protected Gutter getGutter() {
}


/**
* Returns the singleton instance of the listener for all gutter components.
*
* @return The singleton instance.
*/
private static Listener getListener() {
if (listener == null) {
listener = new Listener();
}
return listener;
}


/**
* Called when text is inserted to or removed from the text area.
* Implementations can take this opportunity to repaint, revalidate, etc.
Expand All @@ -106,6 +130,13 @@ protected void init() {
abstract void lineHeightsChanged();


@Override
public void removeNotify() {
getListener().uninstall(this);
super.removeNotify();
}


/**
* Sets the text area being displayed. Subclasses can override, but
* should call the super implementation.
Expand All @@ -122,4 +153,35 @@ public void setTextArea(RTextArea textArea) {
}


static class Listener extends MouseAdapter {

private boolean newArmed;

void install(AbstractGutterComponent component) {
component.addMouseListener(this);
component.addMouseMotionListener(this);
}

public void mouseExited(MouseEvent e) {
setArmed((AbstractGutterComponent)e.getComponent(), false);
}

public void mouseMoved(MouseEvent e) {
setArmed((AbstractGutterComponent)e.getComponent(), true);
}

private void setArmed(AbstractGutterComponent component, boolean armed) {
newArmed = armed;
SwingUtilities.invokeLater(() -> {
if (component.getGutter() != null && newArmed != component.getGutter().isArmed()) {
component.getGutter().setArmed(newArmed);
}
});
}

void uninstall(AbstractGutterComponent component) {
component.removeMouseListener(this);
component.removeMouseMotionListener(this);
}
}
}
@@ -0,0 +1,25 @@
/*
* This library is distributed under a modified BSD license. See the included
* LICENSE file for details.
*/
package org.fife.ui.rtextarea;


/**
* The strategy to use when rendering expanded folds in the gutter.
*
* @author Robert Futrell
* @version 1.0
*/
public enum ExpandedFoldRenderStrategy {

/**
* Always render expanded folds.
*/
ALWAYS,

/**
* Only render expanded folds when the mouse is hovered over the gutter.
*/
ON_HOVER
}
Expand Up @@ -106,6 +106,11 @@ public class FoldIndicator extends AbstractGutterComponent {
*/
private int additionalLeftMargin;

/**
* The strategy to use when rendering expanded folds.
*/
private ExpandedFoldRenderStrategy expandedFoldRenderStrategy;

/**
* The color used to paint fold outlines.
*/
Expand Down Expand Up @@ -187,6 +192,17 @@ private Fold findOpenFoldClosestTo(Point p) {
}


/**
* Returns the strategy to use for rendering expanded folds.
*
* @return The strategy to use for rendering expanded folds.
* @see #setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy)
*/
public ExpandedFoldRenderStrategy getExpandedFoldRenderStrategy() {
return expandedFoldRenderStrategy;
}


/**
* Returns the color to use for the "background" of armed fold icons. This
* is ignored if custom icons are used.
Expand Down Expand Up @@ -214,6 +230,16 @@ public Color getFoldIconBackground() {
}


/**
* Returns whether to paint expanded folds.
*
* @return Whether to paint expanded folds.
*/
private boolean getPaintExpandedFolds() {
return expandedFoldRenderStrategy == ExpandedFoldRenderStrategy.ALWAYS || getGutter().isArmed();
}


@Override
public Dimension getPreferredSize() {
int iconWidth = Math.max(expandedFoldIcon.getIconWidth(), collapsedFoldIcon.getIconWidth());
Expand Down Expand Up @@ -350,6 +376,7 @@ protected void init() {
listener = new Listener(this);
visibleRect = new Rectangle();
setShowCollapsedRegionToolTips(true);
setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ALWAYS);
}


Expand Down Expand Up @@ -464,7 +491,7 @@ protected void paintComponent(Graphics g) {
fold = fm.getFoldForLine(line);
} while (fold!=null && fold.isCollapsed());
}
else {
else if (getPaintExpandedFolds()) {
int x = (width - expandedFoldIcon.getIconWidth()) / 2;
paintIcon(expandedFoldIcon, g, x, y);
}
Expand Down Expand Up @@ -585,8 +612,10 @@ private void paintComponentWrapped(Graphics g) {
line += fold.getLineCount() + 1;
}
else {
int x = (width - expandedFoldIcon.getIconWidth()) / 2;
paintIcon(expandedFoldIcon, g, x, y);
if (getPaintExpandedFolds()) {
int x = (width - expandedFoldIcon.getIconWidth()) / 2;
paintIcon(expandedFoldIcon, g, x, y);
}
y += curLineH;
line++;
}
Expand Down Expand Up @@ -645,6 +674,20 @@ public void setAdditionalLeftMargin(int leftMargin) {
}


/**
* Sets the strategy to use for rendering expanded folds.
*
* @param strategy The strategy to use. This cannot be {@code null}.
* @see #getExpandedFoldRenderStrategy()
*/
public void setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy strategy) {
if (strategy == null) {
throw new NullPointerException("strategy cannot be null");
}
expandedFoldRenderStrategy = strategy;
}


/**
* Sets the color to use for the "background" of armed fold icons. This
* will be ignored if custom icons are used.
Expand Down Expand Up @@ -742,11 +785,13 @@ void setStyle(FoldIndicatorStyle style) {
case CLASSIC:
setFoldIcons(new PlusMinusFoldIcon(true), new PlusMinusFoldIcon(false));
setShowArmedFoldRange(true);
setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ALWAYS);
break;

case MODERN:
setFoldIcons(new ChevronFoldIcon(true), new ChevronFoldIcon(false));
setShowArmedFoldRange(false);
setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy.ON_HOVER);
break;
}
}
Expand Down
38 changes: 38 additions & 0 deletions RSyntaxTextArea/src/main/java/org/fife/ui/rtextarea/Gutter.java
Expand Up @@ -171,6 +171,22 @@ public Gutter(RTextArea textArea) {
}


private boolean armed;
public boolean isArmed() {
return armed;
}

public void setArmed(boolean armed) {
if (armed != this.armed) {
this.armed = armed;
if (foldIndicator != null) {
foldIndicator.repaint();
}
}
}



/**
* Adds an icon that tracks an offset in the document, and is displayed
* adjacent to the line numbers. This is useful for marking things such
Expand Down Expand Up @@ -341,6 +357,17 @@ public Color getCurrentLineNumberColor() {
}


/**
* Returns the strategy to use for rendering expanded folds.
*
* @return The strategy to use for rendering expanded folds.
* @see #setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy)
*/
public ExpandedFoldRenderStrategy getExpandedFoldRenderStrategy() {
return foldIndicator.getExpandedFoldRenderStrategy();
}


/**
* Returns the background color used by the (default) fold icons.
*
Expand Down Expand Up @@ -653,6 +680,17 @@ public void setCurrentLineNumberColor(Color color) {
}


/**
* Sets the strategy to use for rendering expanded folds.
*
* @param strategy The strategy to use. This cannot be {@code null}.
* @see #getExpandedFoldRenderStrategy()
*/
public void setExpandedFoldRenderStrategy(ExpandedFoldRenderStrategy strategy) {
foldIndicator.setExpandedFoldRenderStrategy(strategy);
}


/**
* Sets the icons to use to represent collapsed and expanded folds.
* This method can be used for further customization after setting this
Expand Down
Expand Up @@ -91,6 +91,16 @@ void testGetSetAdditionalLeftMargin_error_negativeValue() {
}


@Test
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());
}


@Test
void testGetSetShowCollapsedRegionToolTips() {
RSyntaxTextArea textArea = createTextArea();
Expand Down Expand Up @@ -134,7 +144,7 @@ void testGetToolTipLocation_codeFoldingEnabledAndHoverOverCollapsedFold() {
hackyGutter.add(fi);

// Collapse the top-level fold, and create a synthetic mouse-over
// event over its fold indicataor.
// event over its fold indicator.
textArea.getFoldManager().getFold(0).setCollapsed(true);
MouseEvent e = new MouseEvent(textArea, 0, 0, 0, 3, 3, 0, false);

Expand Down Expand Up @@ -227,9 +237,17 @@ void testPaintComponent_noLineWrap() {

@Test
void testPaintComponent_modernLook() {

RSyntaxTextArea textArea = createTextArea();

// FoldIndicator needs a parent Gutter to calculate where to display
// the tool tip, but Gutter doesn't expose its child FoldIndicator
// via its API. So here we really hack things to get around this.
FoldIndicator fi = new FoldIndicator(textArea);
fi.setStyle(FoldIndicatorStyle.MODERN);
Gutter hackyGutter = new Gutter(textArea);
hackyGutter.add(fi);

fi.paintComponent(createTestGraphics());
}

Expand Down
Expand Up @@ -283,6 +283,16 @@ void testGetSetCurrentLineNumberColor() {
}


@Test
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());
}


@Test
void testGetSetFoldBackground() {

Expand Down

0 comments on commit c4cf27d

Please sign in to comment.