package magic.ui.dialog.prefs; import java.awt.*; import javax.swing.*; /** * https://tips4java.wordpress.com/2009/12/20/scrollable-panel/ * * A panel that implements the Scrollable interface. * This class allows you to customize the scrollable features by using newly provided * setter methods so you don't have to extend this class every time. * * Scrollable amounts can be specifed as a percentage of the viewport size or as an * actual pixel value. The amount can be changed for both unit and block scrolling * for both horizontal and vertical scrollbars. * * The Scrollable interface only provides a boolean value for determining whether or * not the viewport size (width or height) should be used by the scrollpane when * determining if scrollbars should be made visible. This class supports the concept * of dynamically changing this value based on the size of the viewport. In this case * the viewport size will only be used when it is larger than the panels size. This * has the effect of ensuring the viewport is always full as components added to the * panel will be size to fill the area available, based on the rules of the applicable * layout manager of course. */ @SuppressWarnings("serial") public class ScrollablePanel extends JPanel implements Scrollable, SwingConstants { public enum ScrollableSizeHint { NONE, FIT, STRETCH; } public enum IncrementType { PERCENT, PIXELS; } private ScrollableSizeHint scrollableHeight = ScrollableSizeHint.NONE; private ScrollableSizeHint scrollableWidth = ScrollableSizeHint.NONE; private IncrementInfo horizontalBlock; private IncrementInfo horizontalUnit; private IncrementInfo verticalBlock; private IncrementInfo verticalUnit; /** * Default constructor that uses a FlowLayout */ public ScrollablePanel() { this(new FlowLayout()); } /** * Constuctor for specifying the LayoutManager of the panel. * * @param layout the LayountManger for the panel */ public ScrollablePanel(LayoutManager layout) { super(layout); IncrementInfo block = new IncrementInfo(IncrementType.PERCENT, 100); IncrementInfo unit = new IncrementInfo(IncrementType.PERCENT, 10); setScrollableBlockIncrement(HORIZONTAL, block); setScrollableBlockIncrement(VERTICAL, block); setScrollableUnitIncrement(HORIZONTAL, unit); setScrollableUnitIncrement(VERTICAL, unit); } /** * Get the height ScrollableSizeHint enum * * @return the ScrollableSizeHint enum for the height */ public ScrollableSizeHint getScrollableHeight() { return scrollableHeight; } /** * Set the ScrollableSizeHint enum for the height. The enum is used to determine the boolean value that is returned by the getScrollableTracksViewportHeight() method. The valid values are: * * ScrollableSizeHint.NONE - return "false", which causes the height of the panel to be used when laying out the children ScrollableSizeHint.FIT - return "true", which causes the height of the viewport to be used when laying out the children ScrollableSizeHint.STRETCH - return "true" when the viewport height is greater than the height of the panel, "false" otherwise. * * @param scrollableHeight as represented by the ScrollableSizeHint enum. */ public void setScrollableHeight(ScrollableSizeHint scrollableHeight) { this.scrollableHeight = scrollableHeight; revalidate(); } /** * Get the width ScrollableSizeHint enum * * @return the ScrollableSizeHint enum for the width */ public ScrollableSizeHint getScrollableWidth() { return scrollableWidth; } /** * Set the ScrollableSizeHint enum for the width. The enum is used to determine the boolean value that is returned by the getScrollableTracksViewportWidth() method. The valid values are: * * ScrollableSizeHint.NONE - return "false", which causes the width of the panel to be used when laying out the children ScrollableSizeHint.FIT - return "true", which causes the width of the viewport to be used when laying out the children ScrollableSizeHint.STRETCH - return "true" when the viewport width is greater than the width of the panel, "false" otherwise. * * @param scrollableWidth as represented by the ScrollableSizeHint enum. */ public void setScrollableWidth(ScrollableSizeHint scrollableWidth) { this.scrollableWidth = scrollableWidth; revalidate(); } /** * Get the block IncrementInfo for the specified orientation * * @return the block IncrementInfo for the specified orientation */ public IncrementInfo getScrollableBlockIncrement(int orientation) { return orientation == SwingConstants.HORIZONTAL ? horizontalBlock : verticalBlock; } /** * Specify the information needed to do block scrolling. * * @param orientation specify the scrolling orientation. Must be either: SwingContants.HORIZONTAL or SwingContants.VERTICAL. * @paran type specify how the amount parameter in the calculation of the scrollable amount. Valid values are: IncrementType.PERCENT - treat the amount as a % of the viewport size IncrementType.PIXEL - treat the amount as the scrollable amount * @param amount a value used with the IncrementType to determine the scrollable amount */ public void setScrollableBlockIncrement(int orientation, IncrementType type, int amount) { IncrementInfo info = new IncrementInfo(type, amount); setScrollableBlockIncrement(orientation, info); } /** * Specify the information needed to do block scrolling. * * @param orientation specify the scrolling orientation. Must be either: SwingContants.HORIZONTAL or SwingContants.VERTICAL. * @param info An IncrementInfo object containing information of how to calculate the scrollable amount. */ public final void setScrollableBlockIncrement(int orientation, IncrementInfo info) { switch (orientation) { case SwingConstants.HORIZONTAL: horizontalBlock = info; break; case SwingConstants.VERTICAL: verticalBlock = info; break; default: throw new IllegalArgumentException("Invalid orientation: " + orientation); } } /** * Get the unit IncrementInfo for the specified orientation * * @return the unit IncrementInfo for the specified orientation */ public IncrementInfo getScrollableUnitIncrement(int orientation) { return orientation == SwingConstants.HORIZONTAL ? horizontalUnit : verticalUnit; } /** * Specify the information needed to do unit scrolling. * * @param orientation specify the scrolling orientation. Must be either: SwingContants.HORIZONTAL or SwingContants.VERTICAL. * @paran type specify how the amount parameter in the calculation of the scrollable amount. Valid values are: IncrementType.PERCENT - treat the amount as a % of the viewport size IncrementType.PIXEL - treat the amount as the scrollable amount * @param amount a value used with the IncrementType to determine the scrollable amount */ public void setScrollableUnitIncrement(int orientation, IncrementType type, int amount) { IncrementInfo info = new IncrementInfo(type, amount); setScrollableUnitIncrement(orientation, info); } /** * Specify the information needed to do unit scrolling. * * @param orientation specify the scrolling orientation. Must be either: SwingContants.HORIZONTAL or SwingContants.VERTICAL. * @param info An IncrementInfo object containing information of how to calculate the scrollable amount. */ public final void setScrollableUnitIncrement(int orientation, IncrementInfo info) { switch (orientation) { case SwingConstants.HORIZONTAL: horizontalUnit = info; break; case SwingConstants.VERTICAL: verticalUnit = info; break; default: throw new IllegalArgumentException("Invalid orientation: " + orientation); } } // Implement Scrollable interface @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public int getScrollableUnitIncrement( Rectangle visible, int orientation, int direction) { switch (orientation) { case SwingConstants.HORIZONTAL: return getScrollableIncrement(horizontalUnit, visible.width); case SwingConstants.VERTICAL: return getScrollableIncrement(verticalUnit, visible.height); default: throw new IllegalArgumentException("Invalid orientation: " + orientation); } } @Override public int getScrollableBlockIncrement( Rectangle visible, int orientation, int direction) { switch (orientation) { case SwingConstants.HORIZONTAL: return getScrollableIncrement(horizontalBlock, visible.width); case SwingConstants.VERTICAL: return getScrollableIncrement(verticalBlock, visible.height); default: throw new IllegalArgumentException("Invalid orientation: " + orientation); } } protected int getScrollableIncrement(IncrementInfo info, int distance) { if (info.getIncrement() == IncrementType.PIXELS) { return info.getAmount(); } else { return distance * info.getAmount() / 100; } } @Override public boolean getScrollableTracksViewportWidth() { if (scrollableWidth == ScrollableSizeHint.NONE) { return false; } if (scrollableWidth == ScrollableSizeHint.FIT) { return true; } // STRETCH sizing, use the greater of the panel or viewport width if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getWidth() > getPreferredSize().width); } return false; } @Override public boolean getScrollableTracksViewportHeight() { if (scrollableHeight == ScrollableSizeHint.NONE) { return false; } if (scrollableHeight == ScrollableSizeHint.FIT) { return true; } // STRETCH sizing, use the greater of the panel or viewport height if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getHeight() > getPreferredSize().height); } return false; } /** * Helper class to hold the information required to calculate the scroll amount. */ public static class IncrementInfo { private final IncrementType type; private final int amount; public IncrementInfo(IncrementType type, int amount) { this.type = type; this.amount = amount; } public IncrementType getIncrement() { return type; } public int getAmount() { return amount; } @Override public String toString() { return "ScrollablePanel[" + type + ", " + amount + "]"; } } }