diff --git a/CHANGELOG.md b/CHANGELOG.md index 98b556f4..1e972e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ FlatLaf Change Log - Demo: Improved "SplitPane & Tabs" and "Data Components" tabs. - Menu items "File > Open" and "File > Save As" now show file choosers. +- InternalFrame: Support draggable border for resizing frame inside of the + visible frame border. (issue #121) #### Fixed bugs diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java index ac753d97..efc3bfaf 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatInternalFrameUI.java @@ -84,6 +84,8 @@ import javax.swing.plaf.basic.BasicInternalFrameUI; public class FlatInternalFrameUI extends BasicInternalFrameUI { + protected FlatWindowResizer windowResizer; + public static ComponentUI createUI( JComponent c ) { return new FlatInternalFrameUI( (JInternalFrame) c ); } @@ -97,6 +99,18 @@ public class FlatInternalFrameUI super.installUI( c ); LookAndFeel.installProperty( frame, "opaque", false ); + + windowResizer = createWindowResizer(); + } + + @Override + public void uninstallUI( JComponent c ) { + super.uninstallUI( c ); + + if( windowResizer != null ) { + windowResizer.uninstall(); + windowResizer = null; + } } @Override @@ -104,6 +118,10 @@ public class FlatInternalFrameUI return new FlatInternalFrameTitlePane( w ); } + protected FlatWindowResizer createWindowResizer() { + return new FlatWindowResizer.InternalFrameResizer( frame, this::getDesktopManager ); + } + //---- class FlatInternalFrameBorder -------------------------------------- public static class FlatInternalFrameBorder diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java index 048d3ac6..49b687f9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java @@ -172,7 +172,7 @@ public class FlatRootPaneUI } protected FlatWindowResizer createWindowResizer() { - return new FlatWindowResizer( rootPane ); + return new FlatWindowResizer.WindowResizer( rootPane ); } protected FlatTitlePane createTitlePane() { diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowResizer.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowResizer.java index 46f0c1cc..cd1c3784 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowResizer.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatWindowResizer.java @@ -17,11 +17,13 @@ package com.formdev.flatlaf.ui; import static java.awt.Cursor.*; +import static javax.swing.SwingConstants.*; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; @@ -34,54 +36,59 @@ import java.awt.event.WindowEvent; import java.awt.event.WindowStateListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.function.Supplier; +import javax.swing.DesktopManager; import javax.swing.JComponent; +import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import javax.swing.JRootPane; import javax.swing.UIManager; import com.formdev.flatlaf.util.UIScale; /** - * Resizes frames and dialogs. + * Resizes frames, dialogs or internal frames. + *
+ * Could also be used to implement resize support for any Swing component
+ * by creating a new subclass.
*
* @author Karl Tauber
*/
-public class FlatWindowResizer
- implements PropertyChangeListener, WindowStateListener, ComponentListener
+public abstract class FlatWindowResizer
+ implements PropertyChangeListener, ComponentListener
{
protected final static Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1;
- protected final JRootPane rootPane;
+ protected final JComponent resizeComp;
protected final int borderDragThickness = FlatUIUtils.getUIInt( "RootPane.borderDragThickness", 5 );
protected final int cornerDragWidth = FlatUIUtils.getUIInt( "RootPane.cornerDragWidth", 16 );
protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorFrameMinimumSizeOnResize" );
protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean( "RootPane.honorDialogMinimumSizeOnResize" );
- protected final JComponent north;
- protected final JComponent south;
- protected final JComponent west;
- protected final JComponent east;
+ protected final DragBorderComponent topDragComp;
+ protected final DragBorderComponent bottomDragComp;
+ protected final DragBorderComponent leftDragComp;
+ protected final DragBorderComponent rightDragComp;
- protected Window window;
+ protected FlatWindowResizer( JComponent resizeComp ) {
+ this.resizeComp = resizeComp;
- public FlatWindowResizer( JRootPane rootPane ) {
- this.rootPane = rootPane;
+ topDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR );
+ bottomDragComp = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR );
+ leftDragComp = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR );
+ rightDragComp = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR );
- north = createDragBorderComponent( NW_RESIZE_CURSOR, N_RESIZE_CURSOR, NE_RESIZE_CURSOR );
- south = createDragBorderComponent( SW_RESIZE_CURSOR, S_RESIZE_CURSOR, SE_RESIZE_CURSOR );
- west = createDragBorderComponent( NW_RESIZE_CURSOR, W_RESIZE_CURSOR, SW_RESIZE_CURSOR );
- east = createDragBorderComponent( NE_RESIZE_CURSOR, E_RESIZE_CURSOR, SE_RESIZE_CURSOR );
+ Container cont = (resizeComp instanceof JRootPane) ? ((JRootPane)resizeComp).getLayeredPane() : resizeComp;
+ Object cons = (cont instanceof JLayeredPane) ? WINDOW_RESIZER_LAYER : null;
+ cont.add( topDragComp, cons, 0 );
+ cont.add( bottomDragComp, cons, 1 );
+ cont.add( leftDragComp, cons, 2 );
+ cont.add( rightDragComp, cons, 3 );
- JLayeredPane layeredPane = rootPane.getLayeredPane();
- layeredPane.add( north, WINDOW_RESIZER_LAYER );
- layeredPane.add( south, WINDOW_RESIZER_LAYER );
- layeredPane.add( west, WINDOW_RESIZER_LAYER );
- layeredPane.add( east, WINDOW_RESIZER_LAYER );
+ resizeComp.addComponentListener( this );
+ resizeComp.addPropertyChangeListener( "ancestor", this );
- rootPane.addComponentListener( this );
- rootPane.addPropertyChangeListener( "ancestor", this );
-
- if( rootPane.isDisplayable() )
+ if( resizeComp.isDisplayable() )
addNotify();
}
@@ -92,85 +99,96 @@ public class FlatWindowResizer
public void uninstall() {
removeNotify();
- rootPane.removeComponentListener( this );
- rootPane.removePropertyChangeListener( "ancestor", this );
+ resizeComp.removeComponentListener( this );
+ resizeComp.removePropertyChangeListener( "ancestor", this );
- JLayeredPane layeredPane = rootPane.getLayeredPane();
- layeredPane.remove( north );
- layeredPane.remove( south );
- layeredPane.remove( west );
- layeredPane.remove( east );
+ Container cont = topDragComp.getParent();
+ cont.remove( topDragComp );
+ cont.remove( bottomDragComp );
+ cont.remove( leftDragComp );
+ cont.remove( rightDragComp );
}
public void doLayout() {
- if( !north.isVisible() )
+ if( !topDragComp.isVisible() )
return;
int x = 0;
int y = 0;
- int width = rootPane.getWidth();
- int height = rootPane.getHeight();
+ int width = resizeComp.getWidth();
+ int height = resizeComp.getHeight();
if( width == 0 || height == 0 )
return;
+ Insets resizeInsets = getResizeInsets();
int thickness = UIScale.scale( borderDragThickness );
- int y2 = y + thickness;
- int height2 = height - (thickness * 2);
+ int topThickness = Math.max( resizeInsets.top, thickness );
+ int bottomThickness = Math.max( resizeInsets.bottom, thickness );
+ int leftThickness = Math.max( resizeInsets.left, thickness );
+ int rightThickness = Math.max( resizeInsets.right, thickness );
+ int y2 = y + topThickness;
+ int height2 = height - topThickness - bottomThickness;
- north.setBounds( x, y, width, thickness );
- south.setBounds( x, y + height - thickness, width, thickness );
- west.setBounds( x, y2, thickness, height2 );
- east.setBounds( x + width - thickness, y2, thickness, height2 );
+ // set bounds of drag components
+ topDragComp.setBounds( x, y, width, topThickness );
+ bottomDragComp.setBounds( x, y + height - bottomThickness, width, bottomThickness );
+ leftDragComp.setBounds( x, y2, leftThickness, height2 );
+ rightDragComp.setBounds( x + width - rightThickness, y2, rightThickness, height2 );
+
+ // set corner drag widths
+ int cornerDelta = UIScale.scale( cornerDragWidth - borderDragThickness );
+ topDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
+ bottomDragComp.setCornerDragWidths( leftThickness + cornerDelta, rightThickness + cornerDelta );
+ leftDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
+ rightDragComp.setCornerDragWidths( cornerDelta, cornerDelta );
+ }
+
+ protected Insets getResizeInsets() {
+ return new Insets( 0, 0, 0, 0 );
}
protected void addNotify() {
- Container parent = rootPane.getParent();
- window = (parent instanceof Window) ? (Window) parent : null;
- if( window instanceof Frame ) {
- window.addPropertyChangeListener( "resizable", this );
- window.addWindowStateListener( this );
- }
-
updateVisibility();
}
protected void removeNotify() {
- if( window instanceof Frame ) {
- window.removePropertyChangeListener( "resizable", this );
- window.removeWindowStateListener( this );
- }
- window = null;
-
updateVisibility();
}
protected void updateVisibility() {
boolean visible = isWindowResizable();
- if( visible == north.isVisible() )
+ if( visible == topDragComp.isVisible() )
return;
- north.setVisible( visible );
- south.setVisible( visible );
- west.setVisible( visible );
+ topDragComp.setVisible( visible );
+ bottomDragComp.setVisible( visible );
+ leftDragComp.setVisible( visible );
// The east component is not hidden, instead its bounds are set to 0,0,1,1 and
// it is disabled. This is necessary so that DragBorderComponent.paintComponent() is invoked.
- east.setEnabled( visible );
+ rightDragComp.setEnabled( visible );
if( visible ) {
- east.setVisible( true ); // necessary because it is initially invisible
+ rightDragComp.setVisible( true ); // necessary because it is initially invisible
doLayout();
} else
- east.setBounds( 0, 0, 1, 1 );
+ rightDragComp.setBounds( 0, 0, 1, 1 );
}
- protected boolean isWindowResizable() {
- if( window instanceof Frame )
- return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
- if( window instanceof Dialog )
- return ((Dialog)window).isResizable();
+ boolean isDialog() {
return false;
}
+ protected abstract boolean isWindowResizable();
+ protected abstract Rectangle getWindowBounds();
+ protected abstract void setWindowBounds( Rectangle r );
+ protected abstract boolean honorMinimumSizeOnResize();
+ protected abstract Dimension getWindowMinimumSize();
+
+ protected void beginResizing( int direction ) {}
+ protected void endResizing() {}
+
+ //---- interface PropertyChangeListener ----
+
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
@@ -187,10 +205,7 @@ public class FlatWindowResizer
}
}
- @Override
- public void windowStateChanged( WindowEvent e ) {
- updateVisibility();
- }
+ //---- interface ComponentListener ----
@Override
public void componentResized( ComponentEvent e ) {
@@ -201,6 +216,163 @@ public class FlatWindowResizer
@Override public void componentShown( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {}
+ //---- class WindowResizer ------------------------------------------------
+
+ /**
+ * Resizes frames and dialogs.
+ */
+ public static class WindowResizer
+ extends FlatWindowResizer
+ implements WindowStateListener
+ {
+ protected Window window;
+
+ public WindowResizer( JRootPane rootPane ) {
+ super( rootPane );
+ }
+
+ @Override
+ protected void addNotify() {
+ Container parent = resizeComp.getParent();
+ window = (parent instanceof Window) ? (Window) parent : null;
+ if( window instanceof Frame ) {
+ window.addPropertyChangeListener( "resizable", this );
+ window.addWindowStateListener( this );
+ }
+
+ super.addNotify();
+ }
+
+ @Override
+ protected void removeNotify() {
+ if( window instanceof Frame ) {
+ window.removePropertyChangeListener( "resizable", this );
+ window.removeWindowStateListener( this );
+ }
+ window = null;
+
+ super.removeNotify();
+ }
+
+ @Override
+ protected boolean isWindowResizable() {
+ if( window instanceof Frame )
+ return ((Frame)window).isResizable() && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) == 0;
+ if( window instanceof Dialog )
+ return ((Dialog)window).isResizable();
+ return false;
+ }
+
+ @Override
+ protected Rectangle getWindowBounds() {
+ return window.getBounds();
+ }
+
+ @Override
+ protected void setWindowBounds( Rectangle r ) {
+ window.setBounds( r );
+
+ // immediately layout drag border components
+ doLayout();
+
+ if( Toolkit.getDefaultToolkit().isDynamicLayoutActive() ) {
+ window.validate();
+ resizeComp.repaint();
+ }
+ }
+
+ @Override
+ protected boolean honorMinimumSizeOnResize() {
+ return
+ (honorFrameMinimumSizeOnResize && window instanceof Frame) ||
+ (honorDialogMinimumSizeOnResize && window instanceof Dialog);
+ }
+
+ @Override
+ protected Dimension getWindowMinimumSize() {
+ return window.getMinimumSize();
+ }
+
+ @Override
+ boolean isDialog() {
+ return window instanceof Dialog;
+ }
+
+ @Override
+ public void windowStateChanged( WindowEvent e ) {
+ updateVisibility();
+ }
+ }
+
+ //---- class InternalFrameResizer -----------------------------------------
+
+ /**
+ * Resizes internal frames.
+ */
+ public static class InternalFrameResizer
+ extends FlatWindowResizer
+ {
+ protected final Supplier