All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mdlaf.utils.FlatWindowResizer Maven / Gradle / Ivy

The newest version!
package mdlaf.utils;
/*
 * Copyright 2020 FormDev Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import static java.awt.Cursor.*;
import static javax.swing.SwingConstants.*;

import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.function.Supplier;
import javax.swing.*;

/**
 * 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 abstract class FlatWindowResizer implements PropertyChangeListener, ComponentListener { protected static final Integer WINDOW_RESIZER_LAYER = JLayeredPane.DRAG_LAYER + 1; protected final JComponent resizeComp; protected final int borderDragThickness = 5; protected final int cornerDragWidth = 5; protected final boolean honorFrameMinimumSizeOnResize = UIManager.getBoolean("RootPane.honorFrameMinimumSizeOnResize"); protected final boolean honorDialogMinimumSizeOnResize = UIManager.getBoolean("RootPane.honorDialogMinimumSizeOnResize"); protected final DragBorderComponent topDragComp; protected final DragBorderComponent bottomDragComp; protected final DragBorderComponent leftDragComp; protected final DragBorderComponent rightDragComp; protected FlatWindowResizer(JComponent resizeComp) { this.resizeComp = resizeComp; 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); 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); resizeComp.addComponentListener(this); resizeComp.addPropertyChangeListener("ancestor", this); if (resizeComp.isDisplayable()) addNotify(); } protected DragBorderComponent createDragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir) { return new DragBorderComponent(leadingResizeDir, centerResizeDir, trailingResizeDir); } public void uninstall() { removeNotify(); resizeComp.removeComponentListener(this); resizeComp.removePropertyChangeListener("ancestor", this); Container cont = topDragComp.getParent(); cont.remove(topDragComp); cont.remove(bottomDragComp); cont.remove(leftDragComp); cont.remove(rightDragComp); } public void doLayout() { if (!topDragComp.isVisible()) return; int x = 0; int y = 0; int width = resizeComp.getWidth(); int height = resizeComp.getHeight(); if (width == 0 || height == 0) return; Insets resizeInsets = getResizeInsets(); int thickness = borderDragThickness; 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; // 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 = 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() { updateVisibility(); } protected void removeNotify() { updateVisibility(); } protected void updateVisibility() { boolean visible = isWindowResizable(); if (visible == topDragComp.isVisible()) return; 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. rightDragComp.setEnabled(visible); if (visible) { rightDragComp.setVisible(true); // necessary because it is initially invisible doLayout(); } else rightDragComp.setBounds(0, 0, 1, 1); } 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()) { case "ancestor": if (e.getNewValue() != null) addNotify(); else removeNotify(); break; case "resizable": updateVisibility(); break; } } // ---- interface ComponentListener ---- @Override public void componentResized(ComponentEvent e) { doLayout(); } @Override public void componentMoved(ComponentEvent e) {} @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( FlatUIUtils.isFullScreen( resizeComp ) ) if (false) return false; 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 desktopManager; public InternalFrameResizer(JInternalFrame frame, Supplier desktopManager) { super(frame); this.desktopManager = desktopManager; frame.addPropertyChangeListener("resizable", this); } @Override public void uninstall() { getFrame().removePropertyChangeListener("resizable", this); super.uninstall(); } private JInternalFrame getFrame() { return (JInternalFrame) resizeComp; } @Override protected Insets getResizeInsets() { return getFrame().getInsets(); } @Override protected boolean isWindowResizable() { return getFrame().isResizable(); } @Override protected Rectangle getWindowBounds() { return getFrame().getBounds(); } @Override protected void setWindowBounds(Rectangle r) { desktopManager.get().resizeFrame(getFrame(), r.x, r.y, r.width, r.height); } @Override protected boolean honorMinimumSizeOnResize() { return true; } @Override protected Dimension getWindowMinimumSize() { return getFrame().getMinimumSize(); } @Override protected void beginResizing(int direction) { desktopManager.get().beginResizingFrame(getFrame(), direction); } @Override protected void endResizing() { desktopManager.get().endResizingFrame(getFrame()); } } // ---- class DragBorderComponent ------------------------------------------ protected class DragBorderComponent extends JComponent implements MouseListener, MouseMotionListener { private final int leadingResizeDir; private final int centerResizeDir; private final int trailingResizeDir; private int resizeDir = -1; private int leadingCornerDragWidth; private int trailingCornerDragWidth; // offsets of mouse position to window edges private int dragLeftOffset; private int dragRightOffset; private int dragTopOffset; private int dragBottomOffset; protected DragBorderComponent( int leadingResizeDir, int centerResizeDir, int trailingResizeDir) { this.leadingResizeDir = leadingResizeDir; this.centerResizeDir = centerResizeDir; this.trailingResizeDir = trailingResizeDir; setResizeDir(centerResizeDir); setVisible(false); addMouseListener(this); addMouseMotionListener(this); } void setCornerDragWidths(int leading, int trailing) { leadingCornerDragWidth = leading; trailingCornerDragWidth = trailing; } protected void setResizeDir(int resizeDir) { if (this.resizeDir == resizeDir) return; this.resizeDir = resizeDir; setCursor(getPredefinedCursor(resizeDir)); } @Override public Dimension getPreferredSize() { int thickness = borderDragThickness; return new Dimension(thickness, thickness); } @Override protected void paintComponent(Graphics g) { super.paintChildren(g); // for dialogs: necessary because Dialog.setResizable() does not fire events // for frames: necessary because GraphicsDevice.setFullScreenWindow() does not fire events updateVisibility(); /*debug int width = getWidth(); int height = getHeight(); g.setColor( java.awt.Color.blue ); boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR); if( topOrBottom ) { g.drawLine( leadingCornerDragWidth, 0, leadingCornerDragWidth, height ); g.drawLine( width - trailingCornerDragWidth, 0, width - trailingCornerDragWidth, height ); } else { g.drawLine( 0, leadingCornerDragWidth, width, leadingCornerDragWidth ); g.drawLine( 0, height - trailingCornerDragWidth, width, height - trailingCornerDragWidth ); } g.setColor( java.awt.Color.red ); g.drawRect( 0, 0, width - 1, height - 1 ); debug*/ } @Override public void mouseClicked(MouseEvent e) {} @Override public void mousePressed(MouseEvent e) { if (!isWindowResizable()) return; int xOnScreen = e.getXOnScreen(); int yOnScreen = e.getYOnScreen(); Rectangle windowBounds = getWindowBounds(); // compute offsets of mouse position to window edges dragLeftOffset = xOnScreen - windowBounds.x; dragTopOffset = yOnScreen - windowBounds.y; dragRightOffset = windowBounds.x + windowBounds.width - xOnScreen; dragBottomOffset = windowBounds.y + windowBounds.height - yOnScreen; int direction = 0; switch (resizeDir) { case N_RESIZE_CURSOR: direction = NORTH; break; case S_RESIZE_CURSOR: direction = SOUTH; break; case W_RESIZE_CURSOR: direction = WEST; break; case E_RESIZE_CURSOR: direction = EAST; break; case NW_RESIZE_CURSOR: direction = NORTH_WEST; break; case NE_RESIZE_CURSOR: direction = NORTH_EAST; break; case SW_RESIZE_CURSOR: direction = SOUTH_WEST; break; case SE_RESIZE_CURSOR: direction = SOUTH_EAST; break; } beginResizing(direction); } @Override public void mouseReleased(MouseEvent e) { if (!isWindowResizable()) return; dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0; endResizing(); } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mouseMoved(MouseEvent e) { boolean topOrBottom = (centerResizeDir == N_RESIZE_CURSOR || centerResizeDir == S_RESIZE_CURSOR); int xy = topOrBottom ? e.getX() : e.getY(); int wh = topOrBottom ? getWidth() : getHeight(); setResizeDir( xy <= leadingCornerDragWidth ? leadingResizeDir : (xy >= wh - trailingCornerDragWidth ? trailingResizeDir : centerResizeDir)); } @Override public void mouseDragged(MouseEvent e) { if (!isWindowResizable()) return; int xOnScreen = e.getXOnScreen(); int yOnScreen = e.getYOnScreen(); // Get current window bounds and compute new bounds based them. // This is necessary because window manager may alter window bounds while 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 // decide at some point that the window should be only on second screen // and adjusts its bounds. Rectangle oldBounds = getWindowBounds(); Rectangle newBounds = new Rectangle(oldBounds); // compute new window bounds // top if (resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR) { newBounds.y = yOnScreen - dragTopOffset; newBounds.height += (oldBounds.y - newBounds.y); } // bottom if (resizeDir == S_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR) newBounds.height = (yOnScreen + dragBottomOffset) - newBounds.y; // left if (resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR) { newBounds.x = xOnScreen - dragLeftOffset; newBounds.width += (oldBounds.x - newBounds.x); } // right if (resizeDir == E_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR || resizeDir == SE_RESIZE_CURSOR) newBounds.width = (xOnScreen + dragRightOffset) - newBounds.x; // apply minimum window size Dimension minimumSize = honorMinimumSizeOnResize() ? getWindowMinimumSize() : null; if (minimumSize == null) minimumSize = new Dimension(150, 50); if (newBounds.width < minimumSize.width) { if (newBounds.x != oldBounds.x) newBounds.x -= (minimumSize.width - newBounds.width); newBounds.width = minimumSize.width; } if (newBounds.height < minimumSize.height) { if (newBounds.y != oldBounds.y) newBounds.y -= (minimumSize.height - newBounds.height); newBounds.height = minimumSize.height; } // set window bounds if (!newBounds.equals(oldBounds)) setWindowBounds(newBounds); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy