Viewing entries tagged with 'Swing'

FlatLaf 1.0 - Swing Look and Feel

FlatLaf 1.0 is here 🎉 😀

Finally, after one and a half years of development, forty 0.x releases, 32 merged PRs, 191 closed issues and hundreds cups of coffee. 😄 It took longer than expected. Looking back at all the features already implemented, we could also name it 3.0. 😁

FlatLaf is a modern open-source cross-platform Look and Feel for Java Swing desktop applications. It comes with more than 60 themes, scales on HiDPI displays and runs on Java 8 or newer.

FlatLaf is already used in a lot of open-source and commercial applications. To name only a few: Apache NetBeans, jclasslib, KeyStore Explorer, install4j, DbVisualizer, MagicPlot, OWASP ZAP. Here is a longer list.

Many, many thanks to the community. 🏆 🥇 🚀 Without your feedback, support, feature requests, bug reports and pull requests, FlatLaf would not be there where it is now.

What's next? The development continues! Stay tuned... 😉

If you like FlatLaf, please give it a star @ GitHub. Thanks!

For more information and documentation visit FlatLaf Home.

Swing Tip: JSplitPane with zero-size divider

Modern GUIs are becoming more and more minimalistic. Most controls (e.g. text fields or buttons) nowadays use 1 pixel thin borders. Everybody is removing borders from scroll and split panes. Even the split pane divider is often reduced to 1 pixel (e.g. on Mac OS X since years or in current Mozilla Thunderbird).

But how to create a 1 pixel thin divider with JSplitPane?

First idea was of course to invoke splitPane.setDividerSize(1). This seems to work, but has the disadvantage that it is very hard for the user to hit that single pixel line to move the divider. What we need is a transparent divider that has an easy-to-hit width (e.g. 9 pixels) and is placed between and over the left and right split pane components.

zeros-size split divider

Thanks to Swing's flexible design, it is relative easy to implement this.

First we set the divider size to 1 and let the split pane layout manager do its work. The trick is now to override JSplitPane.layout() and modify the bounds of the divider (e.g. increase width and move left):

public class JSplitPaneWithZeroSizeDivider extends JSplitPane {
    private int dividerDragSize = 9;
    private int dividerDragOffset = 4;

    public JSplitPaneWithZeroSizeDivider() {
        setDividerSize( 1 );
        setContinuousLayout( true );
    }

    @Override
    public void layout() {
        super.layout();

        // increase divider width or height
        BasicSplitPaneDivider divider = ((BasicSplitPaneUI)getUI()).getDivider();
        Rectangle bounds = divider.getBounds();
        if( orientation == HORIZONTAL_SPLIT ) {
            bounds.x -= dividerDragOffset;
            bounds.width = dividerDragSize;
        } else {
            bounds.y -= dividerDragOffset;
            bounds.height = dividerDragSize;
        }
        divider.setBounds( bounds );
    }

Then we need our own UI delegate that creates our divider.

    @Override
    public void updateUI() {
        setUI( new SplitPaneWithZeroSizeDividerUI() );
        revalidate();
    }

    private class SplitPaneWithZeroSizeDividerUI extends BasicSplitPaneUI {
        @Override
        public BasicSplitPaneDivider createDefaultDivider() {
            return new ZeroSizeDivider( this );
        }
    }

And finally our divider, which draws the divider line and updates the drag locations.

    private class ZeroSizeDivider extends BasicSplitPaneDivider {
        public ZeroSizeDivider( BasicSplitPaneUI ui ) {
            super( ui );
            super.setBorder( null );
            setBackground( UIManager.getColor( "controlShadow" ) );
        }

        @Override
        public void setBorder( Border border ) {
            // ignore
        }

        @Override
        public void paint( Graphics g ) {
            g.setColor( getBackground() );
            if( orientation == HORIZONTAL_SPLIT )
                g.drawLine( dividerDragOffset, 0, dividerDragOffset, getHeight() - 1 );
            else
                g.drawLine( 0, dividerDragOffset, getWidth() - 1, dividerDragOffset );
        }

        @Override
        protected void dragDividerTo( int location ) {
            super.dragDividerTo( location + dividerDragOffset );
        }

        @Override
        protected void finishDraggingTo( int location ) {
            super.finishDraggingTo( location + dividerDragOffset );
        }
    }
}

That's it.

Download Source

Tested with Oracle/Sun Java 5, 6, and 7. Licensed under BSD-2-Clause with clause 2 removed.