InternalFrame: limit internal frame bounds to parent bounds on resize; honor maximum size of internal frame (issue #362)

This commit is contained in:
Karl Tauber
2021-07-29 16:44:50 +02:00
parent ca7f5045ae
commit 60e5861de4
4 changed files with 134 additions and 22 deletions

View File

@@ -3,6 +3,8 @@ FlatLaf Change Log
## 1.5-SNAPSHOT ## 1.5-SNAPSHOT
- InternalFrame: Limit internal frame bounds to parent bounds on resize. Also
honor maximum size of internal frame. (issue #362)
- Popup: Fixed incorrectly placed drop shadow for medium-weight popups in - Popup: Fixed incorrectly placed drop shadow for medium-weight popups in
maximized windows. (issue #358) maximized windows. (issue #358)

View File

@@ -181,8 +181,12 @@ public abstract class FlatWindowResizer
protected abstract boolean isWindowResizable(); protected abstract boolean isWindowResizable();
protected abstract Rectangle getWindowBounds(); protected abstract Rectangle getWindowBounds();
protected abstract void setWindowBounds( Rectangle r ); protected abstract void setWindowBounds( Rectangle r );
protected abstract boolean limitToParentBounds();
protected abstract Rectangle getParentBounds();
protected abstract boolean honorMinimumSizeOnResize(); protected abstract boolean honorMinimumSizeOnResize();
protected abstract boolean honorMaximumSizeOnResize();
protected abstract Dimension getWindowMinimumSize(); protected abstract Dimension getWindowMinimumSize();
protected abstract Dimension getWindowMaximumSize();
protected void beginResizing( int direction ) {} protected void beginResizing( int direction ) {}
protected void endResizing() {} protected void endResizing() {}
@@ -283,6 +287,16 @@ public abstract class FlatWindowResizer
} }
} }
@Override
protected boolean limitToParentBounds() {
return false;
}
@Override
protected Rectangle getParentBounds() {
return null;
}
@Override @Override
protected boolean honorMinimumSizeOnResize() { protected boolean honorMinimumSizeOnResize() {
return return
@@ -290,11 +304,21 @@ public abstract class FlatWindowResizer
(honorDialogMinimumSizeOnResize && window instanceof Dialog); (honorDialogMinimumSizeOnResize && window instanceof Dialog);
} }
@Override
protected boolean honorMaximumSizeOnResize() {
return false;
}
@Override @Override
protected Dimension getWindowMinimumSize() { protected Dimension getWindowMinimumSize() {
return window.getMinimumSize(); return window.getMinimumSize();
} }
@Override
protected Dimension getWindowMaximumSize() {
return window.getMaximumSize();
}
@Override @Override
boolean isDialog() { boolean isDialog() {
return window instanceof Dialog; return window instanceof Dialog;
@@ -354,16 +378,36 @@ public abstract class FlatWindowResizer
desktopManager.get().resizeFrame( getFrame(), r.x, r.y, r.width, r.height ); desktopManager.get().resizeFrame( getFrame(), r.x, r.y, r.width, r.height );
} }
@Override
protected boolean limitToParentBounds() {
return true;
}
@Override
protected Rectangle getParentBounds() {
return getFrame().getParent().getBounds();
}
@Override @Override
protected boolean honorMinimumSizeOnResize() { protected boolean honorMinimumSizeOnResize() {
return true; return true;
} }
@Override
protected boolean honorMaximumSizeOnResize() {
return true;
}
@Override @Override
protected Dimension getWindowMinimumSize() { protected Dimension getWindowMinimumSize() {
return getFrame().getMinimumSize(); return getFrame().getMinimumSize();
} }
@Override
protected Dimension getWindowMaximumSize() {
return getFrame().getMaximumSize();
}
@Override @Override
protected void beginResizing( int direction ) { protected void beginResizing( int direction ) {
desktopManager.get().beginResizingFrame( getFrame(), direction ); desktopManager.get().beginResizingFrame( getFrame(), direction );
@@ -521,7 +565,7 @@ debug*/
int xOnScreen = e.getXOnScreen(); int xOnScreen = e.getXOnScreen();
int yOnScreen = e.getYOnScreen(); int yOnScreen = e.getYOnScreen();
// Get current window bounds and compute new bounds based them. // Get current window bounds and compute new bounds based on them.
// This is necessary because window manager may alter window bounds while resizing. // This is necessary because window manager may alter window bounds while resizing.
// E.g. when having two monitors with different scale factors and resizing // E.g. when having two monitors with different scale factors and resizing
// a window on first screen to the second screen, then the window manager may // a window on first screen to the second screen, then the window manager may
@@ -535,41 +579,72 @@ debug*/
// top // top
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) { if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
newBounds.y = yOnScreen - dragTopOffset; newBounds.y = yOnScreen - dragTopOffset;
if( limitToParentBounds() && newBounds.y < 0 )
newBounds.y = 0;
newBounds.height += (oldBounds.y - newBounds.y); newBounds.height += (oldBounds.y - newBounds.y);
} }
// bottom // bottom
if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR ) if( resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR ) {
newBounds.height = (yOnScreen + dragBottomOffset) - newBounds.y; newBounds.height = (yOnScreen + dragBottomOffset) - newBounds.y;
if( limitToParentBounds() ) {
int parentHeight = getParentBounds().height;
if( newBounds.y + newBounds.height > parentHeight )
newBounds.height = parentHeight - newBounds.y;
}
}
// left // left
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) { if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
newBounds.x = xOnScreen - dragLeftOffset; newBounds.x = xOnScreen - dragLeftOffset;
if( limitToParentBounds() && newBounds.x < 0 )
newBounds.x = 0;
newBounds.width += (oldBounds.x - newBounds.x); newBounds.width += (oldBounds.x - newBounds.x);
} }
// right // right
if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR ) if( resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR ) {
newBounds.width = (xOnScreen + dragRightOffset) - newBounds.x; newBounds.width = (xOnScreen + dragRightOffset) - newBounds.x;
if( limitToParentBounds() ) {
int parentWidth = getParentBounds().width;
if( newBounds.x + newBounds.width > parentWidth )
newBounds.width = parentWidth - newBounds.x;
}
}
// apply minimum window size // apply minimum window size
Dimension minimumSize = honorMinimumSizeOnResize() ? getWindowMinimumSize() : null; Dimension minimumSize = honorMinimumSizeOnResize() ? getWindowMinimumSize() : null;
if( minimumSize == null ) if( minimumSize == null )
minimumSize = UIScale.scale( new Dimension( 150, 50 ) ); minimumSize = UIScale.scale( new Dimension( 150, 50 ) );
if( newBounds.width < minimumSize.width ) { if( newBounds.width < minimumSize.width )
if( newBounds.x != oldBounds.x ) changeWidth( oldBounds, newBounds, minimumSize.width );
newBounds.x -= (minimumSize.width - newBounds.width); if( newBounds.height < minimumSize.height )
newBounds.width = minimumSize.width; changeHeight( oldBounds, newBounds, minimumSize.height );
}
if( newBounds.height < minimumSize.height ) { // apply maximum window size
if( newBounds.y != oldBounds.y ) if( honorMaximumSizeOnResize() ) {
newBounds.y -= (minimumSize.height - newBounds.height); Dimension maximumSize = getWindowMaximumSize();
newBounds.height = minimumSize.height; if( newBounds.width > maximumSize.width )
changeWidth( oldBounds, newBounds, maximumSize.width );
if( newBounds.height > maximumSize.height )
changeHeight( oldBounds, newBounds, maximumSize.height );
} }
// set window bounds // set window bounds
if( !newBounds.equals( oldBounds ) ) if( !newBounds.equals( oldBounds ) )
setWindowBounds( newBounds ); setWindowBounds( newBounds );
} }
private void changeWidth( Rectangle oldBounds, Rectangle newBounds, int width ) {
if( newBounds.x != oldBounds.x )
newBounds.x -= (width - newBounds.width);
newBounds.width = width;
}
private void changeHeight( Rectangle oldBounds, Rectangle newBounds, int height ) {
if( newBounds.y != oldBounds.y )
newBounds.y -= (height - newBounds.height);
newBounds.height = height;
}
} }
} }

View File

@@ -89,6 +89,15 @@ public class FlatInternalFrameTest
}; };
internalFrame.setContentPane( panel ); internalFrame.setContentPane( panel );
if( minSizeCheckBox.isSelected() ) {
internalFrame.setMinimumSize( new Dimension( 300, 150 ) );
panel.add( new JLabel( "min 300,150" ) );
}
if( maxSizeCheckBox.isSelected() ) {
internalFrame.setMaximumSize( new Dimension( 400, 200 ) );
panel.add( new JLabel( "max 400,200" ) );
}
if( !palette.getComponentOrientation().isLeftToRight() ) if( !palette.getComponentOrientation().isLeftToRight() )
internalFrame.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); internalFrame.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT );
@@ -123,6 +132,8 @@ public class FlatInternalFrameTest
maximizableCheckBox = new JCheckBox(); maximizableCheckBox = new JCheckBox();
iconCheckBox = new FlatTriStateCheckBox(); iconCheckBox = new FlatTriStateCheckBox();
menuBarCheckBox = new JCheckBox(); menuBarCheckBox = new JCheckBox();
minSizeCheckBox = new JCheckBox();
maxSizeCheckBox = new JCheckBox();
titleLabel = new JLabel(); titleLabel = new JLabel();
titleField = new JTextField(); titleField = new JTextField();
createFrameButton = new JButton(); createFrameButton = new JButton();
@@ -158,6 +169,8 @@ public class FlatInternalFrameTest
"[fill]0" + "[fill]0" +
"[]0" + "[]0" +
"[]0" + "[]0" +
"[]0" +
"[]0" +
"[]unrel" + "[]unrel" +
"[]unrel")); "[]unrel"));
@@ -189,18 +202,26 @@ public class FlatInternalFrameTest
menuBarCheckBox.setText("Menu Bar"); menuBarCheckBox.setText("Menu Bar");
paletteContentPane.add(menuBarCheckBox, "cell 1 2"); paletteContentPane.add(menuBarCheckBox, "cell 1 2");
//---- minSizeCheckBox ----
minSizeCheckBox.setText("Minimum size 300,150");
paletteContentPane.add(minSizeCheckBox, "cell 0 3 2 1,alignx left,growx 0");
//---- maxSizeCheckBox ----
maxSizeCheckBox.setText("Maximum size 400,200");
paletteContentPane.add(maxSizeCheckBox, "cell 0 4 2 1,alignx left,growx 0");
//---- titleLabel ---- //---- titleLabel ----
titleLabel.setText("Frame title:"); titleLabel.setText("Frame title:");
paletteContentPane.add(titleLabel, "cell 0 3"); paletteContentPane.add(titleLabel, "cell 0 5");
paletteContentPane.add(titleField, "cell 1 3"); paletteContentPane.add(titleField, "cell 1 5");
//---- createFrameButton ---- //---- createFrameButton ----
createFrameButton.setText("Create Frame"); createFrameButton.setText("Create Frame");
createFrameButton.addActionListener(e -> createInternalFrame()); createFrameButton.addActionListener(e -> createInternalFrame());
paletteContentPane.add(createFrameButton, "cell 1 4,alignx right,growx 0"); paletteContentPane.add(createFrameButton, "cell 1 6,alignx right,growx 0");
} }
desktopPane.add(palette, JLayeredPane.PALETTE_LAYER); desktopPane.add(palette, JLayeredPane.PALETTE_LAYER);
palette.setBounds(15, 25, 275, 185); palette.setBounds(15, 25, 275, 275);
} }
add(desktopPane, "cell 0 0,width 600,height 600"); add(desktopPane, "cell 0 0,width 600,height 600");
@@ -234,6 +255,8 @@ public class FlatInternalFrameTest
private JCheckBox maximizableCheckBox; private JCheckBox maximizableCheckBox;
private FlatTriStateCheckBox iconCheckBox; private FlatTriStateCheckBox iconCheckBox;
private JCheckBox menuBarCheckBox; private JCheckBox menuBarCheckBox;
private JCheckBox minSizeCheckBox;
private JCheckBox maxSizeCheckBox;
private JLabel titleLabel; private JLabel titleLabel;
private JTextField titleField; private JTextField titleField;
private JButton createFrameButton; private JButton createFrameButton;

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8" JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -14,7 +14,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JInternalFrame", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JInternalFrame", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3" "$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill]" "$columnConstraints": "[fill][fill]"
"$rowConstraints": "[fill]0[]0[]0[]unrel[]unrel" "$rowConstraints": "[fill]0[]0[]0[]0[]0[]unrel[]unrel"
} ) { } ) {
name: "palette" name: "palette"
"visible": true "visible": true
@@ -62,29 +62,41 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2" "value": "cell 1 2"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "minSizeCheckBox"
"text": "Minimum size 300,150"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "maxSizeCheckBox"
"text": "Maximum size 400,200"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
name: "titleLabel" name: "titleLabel"
"text": "Frame title:" "text": "Frame title:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3" "value": "cell 0 5"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) { add( new FormComponent( "javax.swing.JTextField" ) {
name: "titleField" name: "titleField"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3" "value": "cell 1 5"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "createFrameButton" name: "createFrameButton"
"text": "Create Frame" "text": "Create Frame"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "createInternalFrame", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "createInternalFrame", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 4,alignx right,growx 0" "value": "cell 1 6,alignx right,growx 0"
} ) } )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"x": 15 "x": 15
"y": 25 "y": 25
"width": 275 "width": 275
"height": 185 "height": 275
"layer": 100 "layer": 100
} ) } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {