Merge branch 'windows-full-window-content'

This commit is contained in:
Karl Tauber
2024-02-19 22:25:04 +01:00
25 changed files with 715 additions and 229 deletions

View File

@@ -5,6 +5,9 @@ FlatLaf Change Log
#### New features and improvements #### New features and improvements
- FlatLaf window decorations (Windows 10/11 and Linux): Support "full window
content" mode, which allows you to extend the content into the window title
bar. (PR #801)
- macOS: Support larger window title bar close/minimize/zoom buttons spacing in - macOS: Support larger window title bar close/minimize/zoom buttons spacing in
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content) [full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
mode and introduced "buttons placeholder". (PR #779) mode and introduced "buttons placeholder". (PR #779)

View File

@@ -257,13 +257,36 @@ public interface FlatClientProperties
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner"; String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
/** /**
* Specifies whether a component in an embedded menu bar should behave as caption * Specifies whether a component shown in a window title bar area should behave as caption
* (left-click allows moving window, right-click shows window system menu). * (left-click allows moving window, right-click shows window system menu).
* The component does not receive mouse pressed/released/clicked/dragged events, * The caption component does not receive mouse pressed/released/clicked/dragged events,
* but it gets mouse entered/exited/moved events. * but it gets mouse entered/exited/moved events.
* <p> * <p>
* Since 3.4, this client property also supports using a function that can check
* whether a given location in the component should behave as caption.
* Useful for components that do not use mouse input on whole component bounds.
*
* <pre>{@code
* myComponent.putClientProperty( "JComponent.titleBarCaption",
* (Function<Point, Boolean>) pt -> {
* // parameter pt contains mouse location (in myComponent coordinates)
* // return true if the component is not interested in mouse input at the given location
* // return false if the component wants process mouse input at the given location
* // return null if the component children should be checked
* return ...; // check here
* } );
* }</pre>
* <b>Warning</b>:
* <ul>
* <li>This function is invoked often when mouse is moved over window title bar area
* and should therefore return quickly.
* <li>This function is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
* while processing Windows messages.
* It <b>must not</b> change any component property or layout because this could cause a dead lock.
* </ul>
* <p>
* <strong>Component</strong> {@link javax.swing.JComponent}<br> * <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Boolean} * <strong>Value type</strong> {@link java.lang.Boolean} or {@link java.util.function.Function}&lt;Point, Boolean&gt;
* *
* @since 2.5 * @since 2.5
*/ */
@@ -274,7 +297,7 @@ public interface FlatClientProperties
/** /**
* Marks the panel as placeholder for the iconfify/maximize/close buttons * Marks the panel as placeholder for the iconfify/maximize/close buttons
* in fullWindowContent mode. * in fullWindowContent mode. See {@link #FULL_WINDOW_CONTENT}.
* <p> * <p>
* If fullWindowContent mode is enabled, the preferred size of the panel is equal * If fullWindowContent mode is enabled, the preferred size of the panel is equal
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}. * to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
@@ -462,6 +485,32 @@ public interface FlatClientProperties
*/ */
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"; String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";
/**
* Specifies whether the content pane (and the glass pane) should be extended
* into the window title bar area
* (requires enabled window decorations). Default is {@code false}.
* <p>
* On macOS, use client property {@code apple.awt.fullWindowContent}
* (see <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">macOS Full window content</a>).
* <p>
* Setting this enables/disables full window content
* for the {@code JFrame} or {@code JDialog} that contains the root pane.
* <p>
* If {@code true}, the content pane (and the glass pane) is extended into
* the title bar area. The window icon and title are hidden.
* Only the iconfify/maximize/close buttons stay visible in the upper right corner
* (and overlap the content pane).
* <p>
* The user can left-click-and-drag on the title bar area to move the window,
* except when clicking on a component that processes mouse events (e.g. buttons or menus).
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT = "FlatLaf.fullWindowContent";
/** /**
* Contains the current bounds of the iconfify/maximize/close buttons * Contains the current bounds of the iconfify/maximize/close buttons
* (in root pane coordinates) if fullWindowContent mode is enabled. * (in root pane coordinates) if fullWindowContent mode is enabled.

View File

@@ -21,11 +21,12 @@ import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.List; import java.util.function.Predicate;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JRootPane; import javax.swing.JRootPane;
@@ -218,13 +219,13 @@ public class FlatNativeWindowBorder
} }
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight,
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds, Predicate<Point> captionHitTestCallback, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds ) Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{ {
if( !isSupported() ) if( !isSupported() )
return; return;
nativeProvider.updateTitleBarInfo( window, titleBarHeight, hitTestSpots, nativeProvider.updateTitleBarInfo( window, titleBarHeight, captionHitTestCallback,
appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds ); appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
} }
@@ -270,7 +271,7 @@ public class FlatNativeWindowBorder
{ {
boolean hasCustomDecoration( Window window ); boolean hasCustomDecoration( Window window );
void setHasCustomDecoration( Window window, boolean hasCustomDecoration ); void setHasCustomDecoration( Window window, boolean hasCustomDecoration );
void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots, void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds, Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds ); Rectangle closeButtonBounds );

View File

@@ -269,15 +269,28 @@ public class FlatRootPaneUI
// layer title pane under frame content layer to allow placing menu bar over title pane // layer title pane under frame content layer to allow placing menu bar over title pane
protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1; protected final static Integer TITLE_PANE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 1;
private final static Integer TITLE_PANE_MOUSE_LAYER = JLayeredPane.FRAME_CONTENT_LAYER - 2;
// for fullWindowContent mode, layer title pane over frame content layer to allow placing title bar buttons over content
/** @since 3.4 */
protected final static Integer TITLE_PANE_FULL_WINDOW_CONTENT_LAYER = JLayeredPane.FRAME_CONTENT_LAYER + 1;
private Integer getLayerForTitlePane() {
return isFullWindowContent( rootPane ) ? TITLE_PANE_FULL_WINDOW_CONTENT_LAYER : TITLE_PANE_LAYER;
}
protected void setTitlePane( FlatTitlePane newTitlePane ) { protected void setTitlePane( FlatTitlePane newTitlePane ) {
JLayeredPane layeredPane = rootPane.getLayeredPane(); JLayeredPane layeredPane = rootPane.getLayeredPane();
if( titlePane != null ) if( titlePane != null ) {
layeredPane.remove( titlePane ); layeredPane.remove( titlePane );
layeredPane.remove( titlePane.mouseLayer );
}
if( newTitlePane != null ) if( newTitlePane != null ) {
layeredPane.add( newTitlePane, TITLE_PANE_LAYER ); layeredPane.add( newTitlePane, getLayerForTitlePane() );
layeredPane.add( newTitlePane.mouseLayer, TITLE_PANE_MOUSE_LAYER );
}
titlePane = newTitlePane; titlePane = newTitlePane;
} }
@@ -430,6 +443,17 @@ public class FlatRootPaneUI
titlePane.titleBarColorsChanged(); titlePane.titleBarColorsChanged();
break; break;
case FlatClientProperties.FULL_WINDOW_CONTENT:
if( titlePane != null ) {
rootPane.getLayeredPane().setLayer( titlePane, getLayerForTitlePane() );
titlePane.updateIcon();
titlePane.updateVisibility();
titlePane.updateFullWindowContentButtonsBoundsProperty();
}
FullWindowContentSupport.revalidatePlaceholders( rootPane );
rootPane.revalidate();
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS: case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS:
FullWindowContentSupport.revalidatePlaceholders( rootPane ); FullWindowContentSupport.revalidatePlaceholders( rootPane );
break; break;
@@ -471,11 +495,14 @@ public class FlatRootPaneUI
} }
} }
/** @since 3.4 */
protected static boolean isFullWindowContent( JRootPane rootPane ) {
return FlatClientProperties.clientPropertyBoolean( rootPane, FlatClientProperties.FULL_WINDOW_CONTENT, false );
}
protected static boolean isMenuBarEmbedded( JRootPane rootPane ) { protected static boolean isMenuBarEmbedded( JRootPane rootPane ) {
RootPaneUI ui = rootPane.getUI(); FlatTitlePane titlePane = getTitlePane( rootPane );
return ui instanceof FlatRootPaneUI && return titlePane != null && titlePane.isMenuBarEmbedded();
((FlatRootPaneUI)ui).titlePane != null &&
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
} }
/** @since 2.4 */ /** @since 2.4 */
@@ -511,24 +538,22 @@ public class FlatRootPaneUI
private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) { private Dimension computeLayoutSize( Container parent, Function<Component, Dimension> getSizeFunc ) {
JRootPane rootPane = (JRootPane) parent; JRootPane rootPane = (JRootPane) parent;
Dimension titlePaneSize = (titlePane != null)
? getSizeFunc.apply( titlePane )
: new Dimension();
Dimension contentSize = (rootPane.getContentPane() != null) Dimension contentSize = (rootPane.getContentPane() != null)
? getSizeFunc.apply( rootPane.getContentPane() ) ? getSizeFunc.apply( rootPane.getContentPane() )
: rootPane.getSize(); : rootPane.getSize(); // same as in JRootPane.RootLayout.preferredLayoutSize()
int width = contentSize.width; // title pane width is not considered here int width = contentSize.width; // title pane width is not considered here
int height = titlePaneSize.height + contentSize.height; int height = contentSize.height;
if( titlePane != null && !isFullWindowContent( rootPane ) )
height += getSizeFunc.apply( titlePane ).height;
if( titlePane == null || !titlePane.isMenuBarEmbedded() ) { if( titlePane == null || !titlePane.isMenuBarEmbedded() ) {
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
Dimension menuBarSize = (menuBar != null && menuBar.isVisible()) if( menuBar != null && menuBar.isVisible() ) {
? getSizeFunc.apply( menuBar ) Dimension menuBarSize = getSizeFunc.apply( menuBar );
: new Dimension();
width = Math.max( width, menuBarSize.width ); width = Math.max( width, menuBarSize.width );
height += menuBarSize.height; height += menuBarSize.height;
} }
}
Insets insets = rootPane.getInsets(); Insets insets = rootPane.getInsets();
@@ -552,11 +577,22 @@ public class FlatRootPaneUI
if( rootPane.getLayeredPane() != null ) if( rootPane.getLayeredPane() != null )
rootPane.getLayeredPane().setBounds( x, y, width, height ); rootPane.getLayeredPane().setBounds( x, y, width, height );
// title pane // title pane (is a child of layered pane)
int nextY = 0; int nextY = 0;
if( titlePane != null ) { if( titlePane != null ) {
int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0; int prefHeight = !isFullScreen ? titlePane.getPreferredSize().height : 0;
boolean isFullWindowContent = isFullWindowContent( rootPane );
if( isFullWindowContent && !UIManager.getBoolean( FlatTitlePane.KEY_DEBUG_SHOW_RECTANGLES ) ) {
// place title bar into top-right corner
int tw = Math.min( titlePane.getPreferredSize().width, width );
int tx = titlePane.getComponentOrientation().isLeftToRight() ? width - tw : 0;
titlePane.setBounds( tx, 0, tw, prefHeight );
} else
titlePane.setBounds( 0, 0, width, prefHeight ); titlePane.setBounds( 0, 0, width, prefHeight );
titlePane.mouseLayer.setBounds( 0, 0, width, prefHeight );
if( !isFullWindowContent )
nextY += prefHeight; nextY += prefHeight;
} }
@@ -568,7 +604,7 @@ public class FlatRootPaneUI
rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset ); rootPane.getGlassPane().setBounds( x, y + offset, width, height - offset );
} }
// menu bar // menu bar (is a child of layered pane)
JMenuBar menuBar = rootPane.getJMenuBar(); JMenuBar menuBar = rootPane.getJMenuBar();
if( menuBar != null && menuBar.isVisible() ) { if( menuBar != null && menuBar.isVisible() ) {
boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded(); boolean embedded = !isFullScreen && titlePane != null && titlePane.isMenuBarEmbedded();
@@ -576,13 +612,23 @@ public class FlatRootPaneUI
titlePane.validate(); titlePane.validate();
menuBar.setBounds( titlePane.getMenuBarBounds() ); menuBar.setBounds( titlePane.getMenuBarBounds() );
} else { } else {
int mx = 0;
int mw = width;
if( titlePane != null && isFullWindowContent( rootPane ) ) {
// make menu bar width smaller to avoid that it overlaps title bar buttons
int tw = Math.min( titlePane.getPreferredSize().width, width );
mw -= tw;
if( !titlePane.getComponentOrientation().isLeftToRight() )
mx = tw;
}
Dimension prefSize = menuBar.getPreferredSize(); Dimension prefSize = menuBar.getPreferredSize();
menuBar.setBounds( 0, nextY, width, prefSize.height ); menuBar.setBounds( mx, nextY, mw, prefSize.height );
nextY += prefSize.height; nextY += prefSize.height;
} }
} }
// content pane // content pane (is a child of layered pane)
Container contentPane = rootPane.getContentPane(); Container contentPane = rootPane.getContentPane();
if( contentPane != null ) if( contentPane != null )
contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) ); contentPane.setBounds( 0, nextY, width, Math.max( height - nextY, 0 ) );

View File

@@ -84,7 +84,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatSplitPaneUI public class FlatSplitPaneUI
extends BasicSplitPaneUI extends BasicSplitPaneUI
implements StyleableUI implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{ {
@Styleable protected String arrowType; @Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor; /** @since 3.3 */ @Styleable protected Color draggingColor;
@@ -227,6 +227,15 @@ public class FlatSplitPaneUI
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height ); ((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
} }
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
// necessary because BasicSplitPaneDivider adds some mouse listeners for dragging divider
return null; // check children
}
//---- class FlatSplitPaneDivider ----------------------------------------- //---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider

View File

@@ -182,7 +182,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatTabbedPaneUI public class FlatTabbedPaneUI
extends BasicTabbedPaneUI extends BasicTabbedPaneUI
implements StyleableUI implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{ {
// tab type // tab type
/** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0; /** @since 2 */ protected static final int TAB_TYPE_UNDERLINED = 0;
@@ -2300,6 +2300,17 @@ debug*/
return (rects[last].y + rects[last].height) - rects[0].y; return (rects[last].y + rects[last].height) - rects[0].y;
} }
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
if( tabForCoordinate( tabPane, x, y ) >= 0 )
return false;
return null; // check children
}
//---- class TabCloseButton ----------------------------------------------- //---- class TabCloseButton -----------------------------------------------
private static class TabCloseButton private static class TabCloseButton

View File

@@ -36,6 +36,7 @@ import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@@ -46,9 +47,9 @@ import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
@@ -57,7 +58,6 @@ import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JPanel; import javax.swing.JPanel;
@@ -66,6 +66,7 @@ import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.AbstractBorder; import javax.swing.border.AbstractBorder;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder; import com.formdev.flatlaf.ui.FlatNativeWindowBorder.WindowTopBorder;
@@ -98,7 +99,6 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.showIconBesideTitle boolean * @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int * @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarTitleMinimumGap int * @uiDefault TitlePane.menuBarTitleMinimumGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon * @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon * @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon * @uiDefault TitlePane.maximizeIcon Icon
@@ -109,7 +109,7 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatTitlePane public class FlatTitlePane
extends JComponent extends JComponent
{ {
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles"; static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
/** @since 2.5 */ protected final Font titleFont; /** @since 2.5 */ protected final Font titleFont;
protected final Color activeBackground; protected final Color activeBackground;
@@ -131,7 +131,6 @@ public class FlatTitlePane
/** @since 2.4 */ protected final boolean showIconBesideTitle; /** @since 2.4 */ protected final boolean showIconBesideTitle;
protected final int menuBarTitleGap; protected final int menuBarTitleGap;
/** @since 2.4 */ protected final int menuBarTitleMinimumGap; /** @since 2.4 */ protected final int menuBarTitleMinimumGap;
/** @since 2.4 */ protected final int menuBarResizeHeight;
protected final JRootPane rootPane; protected final JRootPane rootPane;
protected final String windowStyle; protected final String windowStyle;
@@ -150,6 +149,23 @@ public class FlatTitlePane
private final Handler handler; private final Handler handler;
/**
* This panel handles mouse events if FlatLaf window decorations are used
* without native window border. E.g. on Linux.
* <p>
* This panel usually has same bounds as the title pane,
* except if fullWindowContent mode is enabled.
* <p>
* This panel is not a child of the title pane.
* Instead it is added by FlatRootPaneUI to the layered pane at a layer
* under the title pane and under the frame content.
* The separation is necessary for fullWindowContent mode, where the title pane
* is layered over the frame content (for title pane buttons), but the mousePanel
* needs to be layered under the frame content so that components on content pane
* can receive mouse events when located in title area.
*/
final JPanel mouseLayer;
public FlatTitlePane( JRootPane rootPane ) { public FlatTitlePane( JRootPane rootPane ) {
this.rootPane = rootPane; this.rootPane = rootPane;
@@ -178,7 +194,6 @@ public class FlatTitlePane
showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false ); showIconBesideTitle = FlatUIUtils.getSubUIBoolean( "TitlePane.showIconBesideTitle", windowStyle, false );
menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 ); menuBarTitleGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleGap", windowStyle, 40 );
menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 ); menuBarTitleMinimumGap = FlatUIUtils.getSubUIInt( "TitlePane.menuBarTitleMinimumGap", windowStyle, 12 );
menuBarResizeHeight = FlatUIUtils.getSubUIInt( "TitlePane.menuBarResizeHeight", windowStyle, 4 );
handler = createHandler(); handler = createHandler();
@@ -187,11 +202,10 @@ public class FlatTitlePane
addSubComponents(); addSubComponents();
activeChanged( true ); activeChanged( true );
addMouseListener( handler ); mouseLayer = new JPanel();
addMouseMotionListener( handler ); mouseLayer.setOpaque( false );
mouseLayer.addMouseListener( handler );
// necessary for closing window with double-click on icon mouseLayer.addMouseMotionListener( handler );
iconLabel.addMouseListener( handler );
applyComponentOrientation( rootPane.getComponentOrientation() ); applyComponentOrientation( rootPane.getComponentOrientation() );
} }
@@ -234,6 +248,11 @@ public class FlatTitlePane
setLayout( new BorderLayout() { setLayout( new BorderLayout() {
@Override @Override
public void layoutContainer( Container target ) { public void layoutContainer( Container target ) {
if( isFullWindowContent() ) {
super.layoutContainer( target );
return;
}
// compute available bounds // compute available bounds
Insets insets = target.getInsets(); Insets insets = target.getInsets();
int x = insets.left; int x = insets.left;
@@ -247,7 +266,7 @@ public class FlatTitlePane
int titleWidth = w - leftWidth - buttonsWidth; int titleWidth = w - leftWidth - buttonsWidth;
int minTitleWidth = UIScale.scale( titleMinimumWidth ); int minTitleWidth = UIScale.scale( titleMinimumWidth );
// increase minimum width if icon is show besides the title // increase minimum width if icon is shown besides the title
Icon icon = titleLabel.getIcon(); Icon icon = titleLabel.getIcon();
if( icon != null ) { if( icon != null ) {
Insets iconInsets = iconLabel.getInsets(); Insets iconInsets = iconLabel.getInsets();
@@ -295,6 +314,9 @@ public class FlatTitlePane
horizontalGlue.getWidth(), titleLabel.getHeight() ); horizontalGlue.getWidth(), titleLabel.getHeight() );
} }
} }
// clear hit-test cache
lastCaptionHitTestTime = 0;
} }
} ); } );
@@ -338,6 +360,13 @@ public class FlatTitlePane
buttonPanel.add( restoreButton ); buttonPanel.add( restoreButton );
} }
buttonPanel.add( closeButton ); buttonPanel.add( closeButton );
ComponentListener l = new ComponentAdapter() {
@Override public void componentResized( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
@Override public void componentMoved( ComponentEvent e ) { updateFullWindowContentButtonsBoundsProperty(); }
};
buttonPanel.addComponentListener( l );
addComponentListener( l );
} }
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) { protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
@@ -417,7 +446,9 @@ public class FlatTitlePane
/** @since 3 */ /** @since 3 */
protected void updateVisibility() { protected void updateVisibility() {
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) ); boolean isFullWindowContent = isFullWindowContent();
leftPanel.setVisible( !isFullWindowContent );
titleLabel.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_TITLE, true ) && !isFullWindowContent );
closeButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_CLOSE, true ) ); closeButton.setVisible( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_CLOSE, true ) );
if( window instanceof Frame ) { if( window instanceof Frame ) {
@@ -443,7 +474,7 @@ public class FlatTitlePane
// get window images // get window images
List<Image> images = null; List<Image> images = null;
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) ) { if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) && !isFullWindowContent() ) {
images = window.getIconImages(); images = window.getIconImages();
if( images.isEmpty() ) { if( images.isEmpty() ) {
// search in owners // search in owners
@@ -468,6 +499,13 @@ public class FlatTitlePane
updateNativeTitleBarHeightAndHitTestSpotsLater(); updateNativeTitleBarHeightAndHitTestSpotsLater();
} }
void updateFullWindowContentButtonsBoundsProperty() {
Rectangle bounds = isFullWindowContent()
? new Rectangle( SwingUtilities.convertPoint( buttonPanel, 0, 0, rootPane ), buttonPanel.getSize() )
: null;
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, bounds );
}
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
@@ -522,6 +560,11 @@ public class FlatTitlePane
window.removeComponentListener( handler ); window.removeComponentListener( handler );
} }
/** @since 3.4 */
protected boolean isFullWindowContent() {
return FlatRootPaneUI.isFullWindowContent( rootPane );
}
/** /**
* Returns whether this title pane currently has a visible and embedded menubar. * Returns whether this title pane currently has a visible and embedded menubar.
*/ */
@@ -533,6 +576,9 @@ public class FlatTitlePane
* Returns whether the menubar should be embedded into the title pane. * Returns whether the menubar should be embedded into the title pane.
*/ */
protected boolean isMenuBarEmbedded() { protected boolean isMenuBarEmbedded() {
if( isFullWindowContent() )
return false;
// not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime // not storing value of "TitlePane.menuBarEmbedded" in class to allow changing at runtime
return FlatUIUtils.getBoolean( rootPane, return FlatUIUtils.getBoolean( rootPane,
FlatSystemProperties.MENUBAR_EMBEDDED, FlatSystemProperties.MENUBAR_EMBEDDED,
@@ -620,21 +666,45 @@ public class FlatTitlePane
return; return;
if( debugTitleBarHeight > 0 ) { if( debugTitleBarHeight > 0 ) {
// title bar height is measured from window top edge
int y = SwingUtilities.convertPoint( window, 0, debugTitleBarHeight, this ).y;
g.setColor( Color.green ); g.setColor( Color.green );
g.drawLine( 0, debugTitleBarHeight, getWidth(), debugTitleBarHeight ); g.drawLine( 0, y, getWidth(), y );
}
if( debugHitTestSpots != null ) {
for( Rectangle r : debugHitTestSpots )
paintRect( g, Color.red, r );
}
paintRect( g, Color.cyan, debugCloseButtonBounds );
paintRect( g, Color.blue, debugAppIconBounds );
paintRect( g, Color.blue, debugMinimizeButtonBounds );
paintRect( g, Color.magenta, debugMaximizeButtonBounds );
paintRect( g, Color.cyan, debugCloseButtonBounds );
} }
private void paintRect( Graphics g, Color color, Rectangle r ) { g.setColor( Color.red );
debugPaintComponentWithMouseListener( g, Color.red, rootPane.getLayeredPane(), 0, 0 );
debugPaintRect( g, Color.blue, debugAppIconBounds );
debugPaintRect( g, Color.blue, debugMinimizeButtonBounds );
debugPaintRect( g, Color.magenta, debugMaximizeButtonBounds );
debugPaintRect( g, Color.cyan, debugCloseButtonBounds );
}
private void debugPaintComponentWithMouseListener( Graphics g, Color color, Component c, int x, int y ) {
if( !c.isDisplayable() || !c.isVisible() || c == mouseLayer ||
c == iconifyButton || c == maximizeButton || c == restoreButton || c == closeButton )
return;
if( c.getMouseListeners().length > 0 ||
c.getMouseMotionListeners().length > 0 ||
c.getMouseWheelListeners().length > 0 )
{
g.drawRect( x, y, c.getWidth(), c.getHeight() );
return;
}
if( c instanceof Container ) {
Rectangle titlePaneBoundsOnWindow = SwingUtilities.convertRectangle( this, new Rectangle( getSize() ), window );
for( Component child : ((Container)c).getComponents() ) {
Rectangle compBoundsOnWindow = SwingUtilities.convertRectangle( c, new Rectangle( c.getSize() ), window );
if( compBoundsOnWindow.intersects( titlePaneBoundsOnWindow ) )
debugPaintComponentWithMouseListener( g, color, child, x + child.getX(), y + child.getY() );
}
}
}
private void debugPaintRect( Graphics g, Color color, Rectangle r ) {
if( r == null ) if( r == null )
return; return;
@@ -645,6 +715,9 @@ public class FlatTitlePane
@Override @Override
protected void paintComponent( Graphics g ) { protected void paintComponent( Graphics g ) {
if( isFullWindowContent() )
return;
// not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime // not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime
g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) && g.setColor( (UIManager.getBoolean( "TitlePane.unifiedBackground" ) &&
clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null) clientPropertyColor( rootPane, TITLE_BAR_BACKGROUND, null ) == null)
@@ -866,11 +939,14 @@ public class FlatTitlePane
return; return;
int titleBarHeight = getHeight(); int titleBarHeight = getHeight();
// title bar height must be measured from window top edge
// (when window is maximized, window y location is e.g. -11 and window top inset is 11)
for( Component c = this; c != window && c != null; c = c.getParent() )
titleBarHeight += c.getY();
// slightly reduce height so that component receives mouseExit events // slightly reduce height so that component receives mouseExit events
if( titleBarHeight > 0 ) if( titleBarHeight > 0 )
titleBarHeight--; titleBarHeight--;
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null; Rectangle appIconBounds = null;
if( !showIconBesideTitle && iconLabel.isVisible() ) { if( !showIconBesideTitle && iconLabel.isVisible() ) {
@@ -928,71 +1004,17 @@ public class FlatTitlePane
} }
} }
Rectangle r = getNativeHitTestSpot( buttonPanel );
if( r != null )
hitTestSpots.add( r );
JMenuBar menuBar = rootPane.getJMenuBar();
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar );
if( r != null ) {
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
// to have a small area above the menu bar to resize the window
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
// limit to 8, because Windows does not use a larger height
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
r.y += resizeHeight;
r.height -= resizeHeight;
}
int count = menuBar.getComponentCount();
for( int i = count - 1; i >= 0; i-- ) {
Component c = menuBar.getComponent( i );
if( c instanceof Box.Filler ||
(c instanceof JComponent && clientPropertyBoolean( (JComponent) c, COMPONENT_TITLE_BAR_CAPTION, false ) ) )
{
// If menu bar is embedded and contains a horizontal glue or caption component,
// then split the hit test spot so that
// the glue/caption component area can be used to move the window.
Point glueLocation = SwingUtilities.convertPoint( c, 0, 0, window );
int x2 = glueLocation.x + c.getWidth();
Rectangle r2;
if( getComponentOrientation().isLeftToRight() ) {
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
r.width = glueLocation.x - r.x;
} else {
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
r.width = (r.x + r.width) - x2;
r.x = x2;
}
if( r2.width > 0 )
hitTestSpots.add( r2 );
}
}
hitTestSpots.add( r );
}
}
// allow internal frames in layered pane to be moved/resized when placed over title bar
for( Component c : rootPane.getLayeredPane().getComponents() ) {
r = (c instanceof JInternalFrame) ? getNativeHitTestSpot( (JInternalFrame) c ) : null;
if( r != null )
hitTestSpots.add( r );
}
Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton ); Rectangle minimizeButtonBounds = boundsInWindow( iconifyButton );
Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton ); Rectangle maximizeButtonBounds = boundsInWindow( maximizeButton.isVisible() ? maximizeButton : restoreButton );
Rectangle closeButtonBounds = boundsInWindow( closeButton ); Rectangle closeButtonBounds = boundsInWindow( closeButton );
// clear hit-test cache
lastCaptionHitTestTime = 0;
FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, FlatNativeWindowBorder.setTitleBarHeightAndHitTestSpots( window, titleBarHeight,
hitTestSpots, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds ); this::captionHitTest, appIconBounds, minimizeButtonBounds, maximizeButtonBounds, closeButtonBounds );
debugTitleBarHeight = titleBarHeight; debugTitleBarHeight = titleBarHeight;
debugHitTestSpots = hitTestSpots;
debugAppIconBounds = appIconBounds; debugAppIconBounds = appIconBounds;
debugMinimizeButtonBounds = minimizeButtonBounds; debugMinimizeButtonBounds = minimizeButtonBounds;
debugMaximizeButtonBounds = maximizeButtonBounds; debugMaximizeButtonBounds = maximizeButtonBounds;
@@ -1007,18 +1029,101 @@ public class FlatTitlePane
: null; : null;
} }
protected Rectangle getNativeHitTestSpot( JComponent c ) { /**
Dimension size = c.getSize(); * Returns whether there is a component at the given location, that processes
if( size.width <= 0 || size.height <= 0 ) * mouse events. E.g. buttons, menus, etc.
return null; * <p>
* Note:
Point location = SwingUtilities.convertPoint( c, 0, 0, window ); * <ul>
Rectangle r = new Rectangle( location, size ); * <li>This method is invoked often when mouse is moved over title bar
return r; * and should therefore return quickly.
* <li>This method is invoked on 'AWT-Windows' thread (not 'AWT-EventQueue' thread)
* while processing Windows messages.
* </ul>
*/
private boolean captionHitTest( Point pt ) {
// Windows invokes this method every ~200ms, even if the mouse has not moved
long time = System.currentTimeMillis();
if( pt.x == lastCaptionHitTestX && pt.y == lastCaptionHitTestY && time < lastCaptionHitTestTime + 300 ) {
lastCaptionHitTestTime = time;
return lastCaptionHitTestResult;
} }
// convert pt from window coordinates to layeredPane coordinates
Component layeredPane = rootPane.getLayeredPane();
int x = pt.x;
int y = pt.y;
for( Component c = layeredPane; c != window && c != null; c = c.getParent() ) {
x -= c.getX();
y -= c.getY();
}
lastCaptionHitTestX = pt.x;
lastCaptionHitTestY = pt.y;
lastCaptionHitTestTime = time;
lastCaptionHitTestResult = isTitleBarCaptionAt( layeredPane, x, y );
return lastCaptionHitTestResult;
}
private boolean isTitleBarCaptionAt( Component c, int x, int y ) {
if( !c.isDisplayable() || !c.isVisible() || !c.contains( x, y ) || c == mouseLayer )
return true; // continue checking with next component
if( c.isEnabled() &&
(c.getMouseListeners().length > 0 ||
c.getMouseMotionListeners().length > 0) )
{
if( !(c instanceof JComponent) )
return false; // assume that this is not a caption because the component has mouse listeners
// check client property boolean value
Object caption = ((JComponent)c).getClientProperty( COMPONENT_TITLE_BAR_CAPTION );
if( caption instanceof Boolean )
return (boolean) caption;
// if component is not fully layouted, do not invoke function
// because it is too dangerous that the function tries to layout the component,
// which could cause a dead lock
if( !c.isValid() )
return false; // assume that this is not a caption because the component has mouse listeners
if( caption instanceof Function ) {
// check client property function value
@SuppressWarnings( "unchecked" )
Function<Point, Boolean> hitTest = (Function<Point, Boolean>) caption;
Boolean result = hitTest.apply( new Point( x, y ) );
if( result != null )
return result;
} else {
// check component UI
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) c );
if( !(ui instanceof TitleBarCaptionHitTest) )
return false; // assume that this is not a caption because the component has mouse listeners
Boolean result = ((TitleBarCaptionHitTest)ui).isTitleBarCaptionAt( x, y );
if( result != null )
return result;
}
// else continue checking children
}
// check children
if( c instanceof Container ) {
for( Component child : ((Container)c).getComponents() ) {
if( !isTitleBarCaptionAt( child, x - child.getX(), y - child.getY() ) )
return false;
}
}
return true;
}
private int lastCaptionHitTestX;
private int lastCaptionHitTestY;
private long lastCaptionHitTestTime;
private boolean lastCaptionHitTestResult;
private int debugTitleBarHeight; private int debugTitleBarHeight;
private List<Rectangle> debugHitTestSpots;
private Rectangle debugAppIconBounds; private Rectangle debugAppIconBounds;
private Rectangle debugMinimizeButtonBounds; private Rectangle debugMinimizeButtonBounds;
private Rectangle debugMaximizeButtonBounds; private Rectangle debugMaximizeButtonBounds;
@@ -1116,7 +1221,7 @@ public class FlatTitlePane
} }
} }
// compute icon width and gap (if icon is show besides the title) // compute icon width and gap (if icon is shown besides the title)
int iconTextGap = 0; int iconTextGap = 0;
int iconWidthAndGap = 0; int iconWidthAndGap = 0;
if( icon != null ) { if( icon != null ) {
@@ -1125,7 +1230,7 @@ public class FlatTitlePane
iconWidthAndGap = icon.getIconWidth() + iconTextGap; iconWidthAndGap = icon.getIconWidth() + iconTextGap;
} }
// layout title and icon (if show besides the title) // layout title and icon (if shown besides the title)
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon, String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
label.getVerticalAlignment(), label.getHorizontalAlignment(), label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
@@ -1275,7 +1380,7 @@ debug*/
} }
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) { if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
if( e.getSource() == iconLabel ) { if( SwingUtilities.getDeepestComponentAt( FlatTitlePane.this, e.getX(), e.getY() ) == iconLabel ) {
// double-click on icon closes window // double-click on icon closes window
close(); close();
} else if( !hasNativeCustomDecoration() ) { } else if( !hasNativeCustomDecoration() ) {
@@ -1302,7 +1407,7 @@ debug*/
if( !SwingUtilities.isLeftMouseButton( e ) ) if( !SwingUtilities.isLeftMouseButton( e ) )
return; return;
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window ); dragOffset = SwingUtilities.convertPoint( mouseLayer, e.getPoint(), window );
linuxNativeMove = false; linuxNativeMove = false;
// on Linux, move or maximize/restore window // on Linux, move or maximize/restore window
@@ -1415,4 +1520,27 @@ debug*/
@Override public void componentMoved( ComponentEvent e ) {} @Override public void componentMoved( ComponentEvent e ) {}
@Override public void componentHidden( ComponentEvent e ) {} @Override public void componentHidden( ComponentEvent e ) {}
} }
//---- interface TitleBarCaptionHitTest -----------------------------------
/**
* For custom components use {@link FlatClientProperties#COMPONENT_TITLE_BAR_CAPTION}
* instead of this interface.
*
* @since 3.4
*/
public interface TitleBarCaptionHitTest {
/**
* Invoked for a component that is enabled and has mouse listeners,
* to check whether it processes mouse input at the given x/y location.
* Useful for components that do not use mouse input on whole component bounds.
* E.g. a tabbed pane with a few tabs has some empty space beside the tabs
* that can be used to move the window.
*
* @return {@code true} if the component is not interested in mouse input at the given location
* {@code false} if the component wants process mouse input at the given location
* {@code null} if the component children should be checked
*/
Boolean isTitleBarCaptionAt( int x, int y );
}
} }

View File

@@ -82,7 +82,7 @@ import com.formdev.flatlaf.util.UIScale;
*/ */
public class FlatToolBarUI public class FlatToolBarUI
extends BasicToolBarUI extends BasicToolBarUI
implements StyleableUI implements StyleableUI, FlatTitlePane.TitleBarCaptionHitTest
{ {
/** @since 1.4 */ @Styleable protected boolean focusableButtons; /** @since 1.4 */ @Styleable protected boolean focusableButtons;
/** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation; /** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation;
@@ -453,6 +453,15 @@ public class FlatToolBarUI
: null; : null;
} }
//---- interface FlatTitlePane.TitleBarCaptionHitTest ----
/** @since 3.4 */
@Override
public Boolean isTitleBarCaptionAt( int x, int y ) {
// necessary because BasicToolBarUI adds some mouse listeners for dragging when toolbar is floatable
return null; // check children
}
//---- class FlatToolBarFocusTraversalPolicy ------------------------------ //---- class FlatToolBarFocusTraversalPolicy ------------------------------
/** /**

View File

@@ -29,8 +29,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.Timer; import javax.swing.Timer;
@@ -159,7 +159,7 @@ class FlatWindowsNativeWindowBorder
} }
@Override @Override
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots, public void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds, Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds ) Rectangle closeButtonBounds )
{ {
@@ -168,7 +168,7 @@ class FlatWindowsNativeWindowBorder
return; return;
wndProc.titleBarHeight = titleBarHeight; wndProc.titleBarHeight = titleBarHeight;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] ); wndProc.captionHitTestCallback = captionHitTestCallback;
wndProc.appIconBounds = cloneRectange( appIconBounds ); wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds ); wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds ); wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
@@ -288,8 +288,8 @@ class FlatWindowsNativeWindowBorder
private final long hwnd; private final long hwnd;
// Swing coordinates/values may be scaled on a HiDPI screen // Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight; private int titleBarHeight; // measured from window top edge, which may be out-of-screen if maximized
private Rectangle[] hitTestSpots; private Predicate<Point> captionHitTestCallback;
private Rectangle appIconBounds; private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds; private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds; private Rectangle maximizeButtonBounds;
@@ -340,50 +340,61 @@ class FlatWindowsNativeWindowBorder
private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) { private int onNcHitTest( int x, int y, boolean isOnResizeBorder ) {
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen // scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y ); Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon // return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu // - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE // - double-left-click sends WM_CLOSE
if( contains( appIconBounds, sx, sy ) ) if( contains( appIconBounds, pt ) )
return HTSYSMENU; return HTSYSMENU;
// return HTMINBUTTON if mouse is over minimize button // return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11 // - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) ) if( contains( minimizeButtonBounds, pt ) )
return HTMINBUTTON; return HTMINBUTTON;
// return HTMAXBUTTON if mouse is over maximize/restore button // return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10 // - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11 // - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu // https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) ) if( contains( maximizeButtonBounds, pt ) )
return HTMAXBUTTON; return HTMAXBUTTON;
// return HTCLOSE if mouse is over close button // return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11 // - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) ) if( contains( closeButtonBounds, pt ) )
return HTCLOSE; return HTCLOSE;
boolean isOnTitleBar = (sy < titleBarHeight); // return HTTOP if mouse is over top resize border
// - hovering mouse shows vertical resize cursor
// - left-click and drag vertically resizes window
if( isOnResizeBorder )
return HTTOP;
boolean isOnTitleBar = (pt.y < titleBarHeight);
if( isOnTitleBar ) { if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed // return HTCLIENT if mouse is over any Swing component in title bar
// in another thread while processing the array // that processes mouse events (e.g. buttons, menus, etc)
Rectangle[] hitTestSpots2 = hitTestSpots; // - Windows ignores mouse events in this area
for( Rectangle spot : hitTestSpots2 ) { try {
if( spot.contains( sx, sy ) ) if( captionHitTestCallback != null && !captionHitTestCallback.test( pt ) )
return HTCLIENT;
} catch( Throwable ex ) {
// ignore
}
// return HTCAPTION if mouse is over title bar
// - right-click shows system menu
// - double-left-click maximizes/restores window size
return HTCAPTION;
}
// return HTCLIENT
// - Windows ignores mouse events in this area
return HTCLIENT; return HTCLIENT;
} }
return isOnResizeBorder ? HTTOP : HTCAPTION;
}
return isOnResizeBorder ? HTTOP : HTCLIENT; private boolean contains( Rectangle rect, Point pt ) {
} return (rect != null && rect.contains( pt ) );
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
} }
/** /**

View File

@@ -209,9 +209,11 @@ class FullWindowContentSupport
g.drawRect( r.x, r.y, r.width - 1, r.height - 1 ); g.drawRect( r.x, r.y, r.width - 1, r.height - 1 );
// draw diagonal cross // draw diagonal cross
int x2 = r.x + r.width - 1;
int y2 = r.y + r.height - 1;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.drawLine( r.x, r.y, r.width - 1, r.height - 1 ); g.drawLine( r.x, r.y, x2, y2 );
g.drawLine( r.x, r.height - 1, r.width - 1, r.y ); g.drawLine( r.x, y2, x2, r.y );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
} }

View File

@@ -831,7 +831,6 @@ TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.showIconBesideTitle = false TitlePane.showIconBesideTitle = false
TitlePane.menuBarTitleGap = 40 TitlePane.menuBarTitleGap = 40
TitlePane.menuBarTitleMinimumGap = 12 TitlePane.menuBarTitleMinimumGap = 12
TitlePane.menuBarResizeHeight = 4
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon

View File

@@ -73,6 +73,7 @@ class DemoFrame
initComponents(); initComponents();
updateFontMenuItems(); updateFontMenuItems();
initAccentColors(); initAccentColors();
initFullWindowContent();
controlBar.initialize( this, tabbedPane ); controlBar.initialize( this, tabbedPane );
setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) ); setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) );
@@ -101,9 +102,6 @@ class DemoFrame
rootPane.putClientProperty( "apple.awt.windowTitleVisible", false ); rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
else else
setTitle( null ); setTitle( null );
// uncomment this line to see title bar buttons placeholders in fullWindowContent mode
// UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );
} }
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+) // enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
@@ -463,9 +461,37 @@ class DemoFrame
accentColorButtons[i].setVisible( isAccentColorSupported ); accentColorButtons[i].setVisible( isAccentColorSupported );
} }
private void initFullWindowContent() {
if( !supportsFlatLafWindowDecorations() )
return;
// create fullWindowContent mode toggle button
Icon expandIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/expand.svg" );
Icon collapseIcon = new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/collapse.svg" );
JToggleButton fullWindowContentButton = new JToggleButton( expandIcon );
fullWindowContentButton.setToolTipText( "Toggle full window content" );
fullWindowContentButton.addActionListener( e -> {
boolean fullWindowContent = fullWindowContentButton.isSelected();
fullWindowContentButton.setIcon( fullWindowContent ? collapseIcon : expandIcon );
menuBar.setVisible( !fullWindowContent );
toolBar.setVisible( !fullWindowContent );
getRootPane().putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT, fullWindowContent );
} );
// add fullWindowContent mode toggle button to tabbed pane
JToolBar trailingToolBar = new JToolBar();
trailingToolBar.add( Box.createGlue() );
trailingToolBar.add( fullWindowContentButton );
tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT, trailingToolBar );
}
private boolean supportsFlatLafWindowDecorations() {
return FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated());
}
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JMenuBar menuBar1 = new JMenuBar(); menuBar = new JMenuBar();
JMenu fileMenu = new JMenu(); JMenu fileMenu = new JMenu();
JMenuItem newMenuItem = new JMenuItem(); JMenuItem newMenuItem = new JMenuItem();
JMenuItem openMenuItem = new JMenuItem(); JMenuItem openMenuItem = new JMenuItem();
@@ -528,8 +554,10 @@ class DemoFrame
DataComponentsPanel dataComponentsPanel = new DataComponentsPanel(); DataComponentsPanel dataComponentsPanel = new DataComponentsPanel();
TabsPanel tabsPanel = new TabsPanel(); TabsPanel tabsPanel = new TabsPanel();
OptionPanePanel optionPanePanel = new OptionPanePanel(); OptionPanePanel optionPanePanel = new OptionPanePanel();
ExtrasPanel extrasPanel1 = new ExtrasPanel(); ExtrasPanel extrasPanel = new ExtrasPanel();
controlBar = new ControlBar(); controlBar = new ControlBar();
JPanel themesPanelPanel = new JPanel();
JPanel winFullWindowContentButtonsPlaceholder = new JPanel();
themesPanel = new IJThemesPanel(); themesPanel = new IJThemesPanel();
//======== this ======== //======== this ========
@@ -538,7 +566,7 @@ class DemoFrame
Container contentPane = getContentPane(); Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout()); contentPane.setLayout(new BorderLayout());
//======== menuBar1 ======== //======== menuBar ========
{ {
//======== fileMenu ======== //======== fileMenu ========
@@ -583,7 +611,7 @@ class DemoFrame
exitMenuItem.addActionListener(e -> exitActionPerformed()); exitMenuItem.addActionListener(e -> exitActionPerformed());
fileMenu.add(exitMenuItem); fileMenu.add(exitMenuItem);
} }
menuBar1.add(fileMenu); menuBar.add(fileMenu);
//======== editMenu ======== //======== editMenu ========
{ {
@@ -636,7 +664,7 @@ class DemoFrame
deleteMenuItem.addActionListener(e -> menuItemActionPerformed(e)); deleteMenuItem.addActionListener(e -> menuItemActionPerformed(e));
editMenu.add(deleteMenuItem); editMenu.add(deleteMenuItem);
} }
menuBar1.add(editMenu); menuBar.add(editMenu);
//======== viewMenu ======== //======== viewMenu ========
{ {
@@ -736,7 +764,7 @@ class DemoFrame
radioButtonMenuItem3.addActionListener(e -> menuItemActionPerformed(e)); radioButtonMenuItem3.addActionListener(e -> menuItemActionPerformed(e));
viewMenu.add(radioButtonMenuItem3); viewMenu.add(radioButtonMenuItem3);
} }
menuBar1.add(viewMenu); menuBar.add(viewMenu);
//======== fontMenu ======== //======== fontMenu ========
{ {
@@ -760,7 +788,7 @@ class DemoFrame
decrFontMenuItem.addActionListener(e -> decrFont()); decrFontMenuItem.addActionListener(e -> decrFont());
fontMenu.add(decrFontMenuItem); fontMenu.add(decrFontMenuItem);
} }
menuBar1.add(fontMenu); menuBar.add(fontMenu);
//======== optionsMenu ======== //======== optionsMenu ========
{ {
@@ -812,7 +840,7 @@ class DemoFrame
showUIDefaultsInspectorMenuItem.addActionListener(e -> showUIDefaultsInspector()); showUIDefaultsInspectorMenuItem.addActionListener(e -> showUIDefaultsInspector());
optionsMenu.add(showUIDefaultsInspectorMenuItem); optionsMenu.add(showUIDefaultsInspectorMenuItem);
} }
menuBar1.add(optionsMenu); menuBar.add(optionsMenu);
//======== helpMenu ======== //======== helpMenu ========
{ {
@@ -825,9 +853,9 @@ class DemoFrame
aboutMenuItem.addActionListener(e -> aboutActionPerformed()); aboutMenuItem.addActionListener(e -> aboutActionPerformed());
helpMenu.add(aboutMenuItem); helpMenu.add(aboutMenuItem);
} }
menuBar1.add(helpMenu); menuBar.add(helpMenu);
} }
setJMenuBar(menuBar1); setJMenuBar(menuBar);
//======== toolBarPanel ======== //======== toolBarPanel ========
{ {
@@ -884,7 +912,7 @@ class DemoFrame
} }
toolBarPanel.add(toolBar, BorderLayout.CENTER); toolBarPanel.add(toolBar, BorderLayout.CENTER);
} }
contentPane.add(toolBarPanel, BorderLayout.NORTH); contentPane.add(toolBarPanel, BorderLayout.PAGE_START);
//======== contentPanel ======== //======== contentPanel ========
{ {
@@ -904,13 +932,25 @@ class DemoFrame
tabbedPane.addTab("Data Components", dataComponentsPanel); tabbedPane.addTab("Data Components", dataComponentsPanel);
tabbedPane.addTab("Tabs", tabsPanel); tabbedPane.addTab("Tabs", tabsPanel);
tabbedPane.addTab("Option Pane", optionPanePanel); tabbedPane.addTab("Option Pane", optionPanePanel);
tabbedPane.addTab("Extras", extrasPanel1); tabbedPane.addTab("Extras", extrasPanel);
} }
contentPanel.add(tabbedPane, "cell 0 0"); contentPanel.add(tabbedPane, "cell 0 0");
} }
contentPane.add(contentPanel, BorderLayout.CENTER); contentPane.add(contentPanel, BorderLayout.CENTER);
contentPane.add(controlBar, BorderLayout.SOUTH); contentPane.add(controlBar, BorderLayout.PAGE_END);
contentPane.add(themesPanel, BorderLayout.EAST);
//======== themesPanelPanel ========
{
themesPanelPanel.setLayout(new BorderLayout());
//======== winFullWindowContentButtonsPlaceholder ========
{
winFullWindowContentButtonsPlaceholder.setLayout(new FlowLayout());
}
themesPanelPanel.add(winFullWindowContentButtonsPlaceholder, BorderLayout.NORTH);
themesPanelPanel.add(themesPanel, BorderLayout.CENTER);
}
contentPane.add(themesPanelPanel, BorderLayout.LINE_END);
//---- buttonGroup1 ---- //---- buttonGroup1 ----
ButtonGroup buttonGroup1 = new ButtonGroup(); ButtonGroup buttonGroup1 = new ButtonGroup();
@@ -925,8 +965,8 @@ class DemoFrame
usersButton.setButtonType( ButtonType.toolBarButton ); usersButton.setButtonType( ButtonType.toolBarButton );
usersButton.setFocusable( false ); usersButton.setFocusable( false );
usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) ); usersButton.addActionListener( e -> JOptionPane.showMessageDialog( null, "Hello User! How are you?", "User", JOptionPane.INFORMATION_MESSAGE ) );
menuBar1.add( Box.createGlue() ); menuBar.add( Box.createGlue() );
menuBar1.add( usersButton ); menuBar.add( usersButton );
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() ); cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
@@ -938,7 +978,7 @@ class DemoFrame
for( int i = 1; i <= 100; i++ ) for( int i = 1; i <= 100; i++ )
scrollingPopupMenu.add( "Item " + i ); scrollingPopupMenu.add( "Item " + i );
if( FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated()) ) { if( supportsFlatLafWindowDecorations() ) {
if( SystemInfo.isLinux ) if( SystemInfo.isLinux )
unsupported( windowDecorationsCheckBoxMenuItem ); unsupported( windowDecorationsCheckBoxMenuItem );
else else
@@ -959,9 +999,17 @@ class DemoFrame
if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) ) if( "false".equals( System.getProperty( "flatlaf.animatedLafChange" ) ) )
animatedLafChangeMenuItem.setSelected( false ); animatedLafChangeMenuItem.setSelected( false );
// on macOS, panel left to toolBar is a placeholder for title bar buttons in fullWindowContent mode // on macOS, panel left to toolBar is a placeholder for title bar buttons in fullWindowContent mode
macFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" ); macFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" );
// on Windows/Linux, panel above themesPanel is a placeholder for title bar buttons in fullWindowContent mode
winFullWindowContentButtonsPlaceholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "win" );
// uncomment this line to see title bar buttons placeholders in fullWindowContent mode
// UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );
// remove contentPanel bottom insets // remove contentPanel bottom insets
MigLayout layout = (MigLayout) contentPanel.getLayout(); MigLayout layout = (MigLayout) contentPanel.getLayout();
LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() ); LC lc = ConstraintParser.parseLayoutConstraint( (String) layout.getLayoutConstraints() );
@@ -982,6 +1030,7 @@ class DemoFrame
} }
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenuBar menuBar;
private JMenuItem exitMenuItem; private JMenuItem exitMenuItem;
private JMenu scrollingPopupMenu; private JMenu scrollingPopupMenu;
private JMenuItem htmlMenuItem; private JMenuItem htmlMenuItem;

View File

@@ -74,7 +74,7 @@ new FormModel {
"value": "Center" "value": "Center"
} ) } )
}, new FormLayoutConstraints( class java.lang.String ) { }, new FormLayoutConstraints( class java.lang.String ) {
"value": "North" "value": "First"
} ) } )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3" "$layoutConstraints": "insets dialog,hidemode 3"
@@ -115,7 +115,7 @@ new FormModel {
"title": "Option Pane" "title": "Option Pane"
} ) } )
add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) { add( new FormComponent( "com.formdev.flatlaf.demo.extras.ExtrasPanel" ) {
name: "extrasPanel1" name: "extrasPanel"
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"title": "Extras" "title": "Extras"
} ) } )
@@ -131,7 +131,14 @@ new FormModel {
"JavaCodeGenerator.variableLocal": false "JavaCodeGenerator.variableLocal": false
} }
}, new FormLayoutConstraints( class java.lang.String ) { }, new FormLayoutConstraints( class java.lang.String ) {
"value": "South" "value": "Last"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "themesPanelPanel"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "winFullWindowContentButtonsPlaceholder"
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "North"
} ) } )
add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) { add( new FormComponent( "com.formdev.flatlaf.demo.intellijthemes.IJThemesPanel" ) {
name: "themesPanel" name: "themesPanel"
@@ -140,10 +147,16 @@ new FormModel {
"JavaCodeGenerator.variableModifiers": 0 "JavaCodeGenerator.variableModifiers": 0
} }
}, new FormLayoutConstraints( class java.lang.String ) { }, new FormLayoutConstraints( class java.lang.String ) {
"value": "East" "value": "Center"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "After"
} ) } )
menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) { menuBar: new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "menuBar1" name: "menuBar"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) { add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "fileMenu" name: "fileMenu"
"text": "File" "text": "File"

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 14.5L6 10M14.5 1.5L9.99998 6.00001M2.5 9.50001H6.5L6.5 13.5M13.5 6.5L9.5 6.50003V2.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 396 B

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.5 9.5L2 14M9.50003 6.50004L14 2.00001M5.5 14.5H1.5V10.5M10.5 1.5L14.5 1.50002V5.5" stroke="#6E6E6E" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.jideoss.ui; package com.formdev.flatlaf.jideoss.ui;
import static com.formdev.flatlaf.FlatClientProperties.COMPONENT_TITLE_BAR_CAPTION;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER; import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS; import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_SHOW_TAB_SEPARATORS;
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean; import static com.formdev.flatlaf.FlatClientProperties.clientPropertyBoolean;
@@ -30,6 +31,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
@@ -37,6 +39,7 @@ import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.function.Function;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -100,6 +103,25 @@ public class FlatJideTabbedPaneUI
return new FlatJideTabbedPaneUI(); return new FlatJideTabbedPaneUI();
} }
@Override
public void installUI( JComponent c ) {
super.installUI( c );
c.putClientProperty( COMPONENT_TITLE_BAR_CAPTION,
(Function<Point, Boolean>) pt -> {
if( tabForCoordinate( _tabPane, pt.x, pt.y ) >= 0 )
return false;
return null; // check children
} );
}
@Override
public void uninstallUI( JComponent c ) {
super.uninstallUI( c );
c.putClientProperty( COMPONENT_TITLE_BAR_CAPTION, null );
}
@Override @Override
protected void installDefaults() { protected void installDefaults() {
super.installDefaults(); super.installDefaults();

View File

@@ -32,8 +32,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.Timer; import javax.swing.Timer;
@@ -164,7 +164,7 @@ public class FlatWindowsNativeWindowBorder
} }
@Override @Override
public void updateTitleBarInfo( Window window, int titleBarHeight, List<Rectangle> hitTestSpots, public void updateTitleBarInfo( Window window, int titleBarHeight, Predicate<Point> captionHitTestCallback,
Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds, Rectangle appIconBounds, Rectangle minimizeButtonBounds, Rectangle maximizeButtonBounds,
Rectangle closeButtonBounds ) Rectangle closeButtonBounds )
{ {
@@ -173,7 +173,7 @@ public class FlatWindowsNativeWindowBorder
return; return;
wndProc.titleBarHeight = titleBarHeight; wndProc.titleBarHeight = titleBarHeight;
wndProc.hitTestSpots = hitTestSpots.toArray( new Rectangle[hitTestSpots.size()] ); wndProc.captionHitTestCallback = captionHitTestCallback;
wndProc.appIconBounds = cloneRectange( appIconBounds ); wndProc.appIconBounds = cloneRectange( appIconBounds );
wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds ); wndProc.minimizeButtonBounds = cloneRectange( minimizeButtonBounds );
wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds ); wndProc.maximizeButtonBounds = cloneRectange( maximizeButtonBounds );
@@ -351,7 +351,7 @@ public class FlatWindowsNativeWindowBorder
// Swing coordinates/values may be scaled on a HiDPI screen // Swing coordinates/values may be scaled on a HiDPI screen
private int titleBarHeight; private int titleBarHeight;
private Rectangle[] hitTestSpots; private Predicate<Point> captionHitTestCallback;
private Rectangle appIconBounds; private Rectangle appIconBounds;
private Rectangle minimizeButtonBounds; private Rectangle minimizeButtonBounds;
private Rectangle maximizeButtonBounds; private Rectangle maximizeButtonBounds;
@@ -644,53 +644,65 @@ public class FlatWindowsNativeWindowBorder
// scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen // scale-down mouse x/y because Swing coordinates/values may be scaled on a HiDPI screen
Point pt = scaleDown( x, y ); Point pt = scaleDown( x, y );
int sx = pt.x;
int sy = pt.y;
// return HTSYSMENU if mouse is over application icon // return HTSYSMENU if mouse is over application icon
// - left-click on HTSYSMENU area shows system menu // - left-click on HTSYSMENU area shows system menu
// - double-left-click sends WM_CLOSE // - double-left-click sends WM_CLOSE
if( contains( appIconBounds, sx, sy ) ) if( contains( appIconBounds, pt ) )
return new LRESULT( HTSYSMENU ); return new LRESULT( HTSYSMENU );
// return HTMINBUTTON if mouse is over minimize button // return HTMINBUTTON if mouse is over minimize button
// - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11 // - hovering mouse over HTMINBUTTON area shows tooltip on Windows 10/11
if( contains( minimizeButtonBounds, sx, sy ) ) if( contains( minimizeButtonBounds, pt ) )
return new LRESULT( HTMINBUTTON ); return new LRESULT( HTMINBUTTON );
// return HTMAXBUTTON if mouse is over maximize/restore button // return HTMAXBUTTON if mouse is over maximize/restore button
// - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10 // - hovering mouse over HTMAXBUTTON area shows tooltip on Windows 10
// - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11 // - hovering mouse over HTMAXBUTTON area shows snap layouts menu on Windows 11
// https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu // https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/apply-snap-layout-menu
if( contains( maximizeButtonBounds, sx, sy ) ) if( contains( maximizeButtonBounds, pt ) )
return new LRESULT( HTMAXBUTTON ); return new LRESULT( HTMAXBUTTON );
// return HTCLOSE if mouse is over close button // return HTCLOSE if mouse is over close button
// - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11 // - hovering mouse over HTCLOSE area shows tooltip on Windows 10/11
if( contains( closeButtonBounds, sx, sy ) ) if( contains( closeButtonBounds, pt ) )
return new LRESULT( HTCLOSE ); return new LRESULT( HTCLOSE );
int resizeBorderHeight = getResizeHandleHeight(); int resizeBorderHeight = getResizeHandleHeight();
boolean isOnResizeBorder = (y < resizeBorderHeight) && boolean isOnResizeBorder = (y < resizeBorderHeight) &&
(User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0; (User32.INSTANCE.GetWindowLong( hwnd, GWL_STYLE ) & WS_THICKFRAME) != 0;
boolean isOnTitleBar = (sy < titleBarHeight);
// return HTTOP if mouse is over top resize border
// - hovering mouse shows vertical resize cursor
// - left-click and drag vertically resizes window
if( isOnResizeBorder )
return new LRESULT( HTTOP );
boolean isOnTitleBar = (pt.y < titleBarHeight);
if( isOnTitleBar ) { if( isOnTitleBar ) {
// use a second reference to the array to avoid that it can be changed // return HTCLIENT if mouse is over any Swing component in title bar
// in another thread while processing the array // that processes mouse events (e.g. buttons, menus, etc)
Rectangle[] hitTestSpots2 = hitTestSpots; // - Windows ignores mouse events in this area
for( Rectangle spot : hitTestSpots2 ) { try {
if( spot.contains( sx, sy ) ) if( captionHitTestCallback != null && !captionHitTestCallback.test( pt ) )
return new LRESULT( HTCLIENT );
} catch( Throwable ex ) {
// ignore
}
// return HTCAPTION if mouse is over title bar
// - right-click shows system menu
// - double-left-click maximizes/restores window size
return new LRESULT( HTCAPTION );
}
// return HTCLIENT
// - Windows ignores mouse events in this area
return new LRESULT( HTCLIENT ); return new LRESULT( HTCLIENT );
} }
return new LRESULT( isOnResizeBorder ? HTTOP : HTCAPTION );
}
return new LRESULT( isOnResizeBorder ? HTTOP : HTCLIENT ); private boolean contains( Rectangle rect, Point pt ) {
} return (rect != null && rect.contains( pt ) );
private boolean contains( Rectangle rect, int x, int y ) {
return (rect != null && rect.contains( x, y ) );
} }
/** /**

View File

@@ -1266,7 +1266,6 @@ TitlePane.inactiveBackground #303234 HSL 210 4 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -1271,7 +1271,6 @@ TitlePane.inactiveBackground #ffffff HSL 0 0 100 javax.swing.plaf.Colo
TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -1276,7 +1276,6 @@ TitlePane.inactiveBackground #323232 HSL 0 0 20 javax.swing.plaf.Colo
TitlePane.inactiveForeground #9a9a9a HSL 0 0 60 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #9a9a9a HSL 0 0 60 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -1280,7 +1280,6 @@ TitlePane.inactiveBackground #ececec HSL 0 0 93 javax.swing.plaf.Colo
TitlePane.inactiveForeground #b6b6b6 HSL 0 0 71 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #b6b6b6 HSL 0 0 71 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -1307,7 +1307,6 @@ TitlePane.inactiveBackground #008800 HSL 120 100 27 javax.swing.plaf.Colo
TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI]
TitlePane.menuBarEmbedded true TitlePane.menuBarEmbedded true
TitlePane.menuBarResizeHeight 4
TitlePane.menuBarTitleGap 40 TitlePane.menuBarTitleGap 40
TitlePane.menuBarTitleMinimumGap 12 TitlePane.menuBarTitleMinimumGap 12
TitlePane.noIconLeftGap 8 TitlePane.noIconLeftGap 8

View File

@@ -41,6 +41,9 @@ import net.miginfocom.swing.*;
public class FlatWindowDecorationsTest public class FlatWindowDecorationsTest
extends FlatTestPanel extends FlatTestPanel
{ {
// same as in FlatTitlePane
private static final String KEY_DEBUG_SHOW_RECTANGLES = "FlatLaf.debug.titlebar.showRectangles";
public static void main( String[] args ) { public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> { SwingUtilities.invokeLater( () -> {
if( SystemInfo.isLinux ) { if( SystemInfo.isLinux ) {
@@ -51,7 +54,7 @@ public class FlatWindowDecorationsTest
FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" ); FlatTestFrame frame = FlatTestFrame.create( args, "FlatWindowDecorationsTest" );
frame.applyComponentOrientationToFrame = true; frame.applyComponentOrientationToFrame = true;
UIManager.put( "FlatLaf.debug.titlebar.showRectangles", true ); UIManager.put( KEY_DEBUG_SHOW_RECTANGLES, true );
Class<?> cls = FlatWindowDecorationsTest.class; Class<?> cls = FlatWindowDecorationsTest.class;
List<Image> images = Arrays.asList( List<Image> images = Arrays.asList(
@@ -117,6 +120,14 @@ public class FlatWindowDecorationsTest
rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> { rootPane.addPropertyChangeListener( "windowDecorationStyle", e -> {
updateDecorationStyleRadioButtons( rootPane ); updateDecorationStyleRadioButtons( rootPane );
} ); } );
rootPane.addPropertyChangeListener( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS, e -> {
Rectangle bounds = (Rectangle) e.getNewValue();
if( bounds != null ) {
fullWindowContentButtonsBoundsField.setText( bounds.width + ", " + bounds.height
+ " @ " + bounds.x + ", " + bounds.y );
} else
fullWindowContentButtonsBoundsField.setText( "null" );
} );
} }
} }
@@ -309,12 +320,21 @@ debug*/
JLabel caption = new JLabel( "Caption" ); JLabel caption = new JLabel( "Caption" );
caption.setBackground( Color.green ); caption.setBackground( Color.green );
caption.setOpaque( true ); caption.setOpaque( true );
caption.putClientProperty( FlatClientProperties.COMPONENT_TITLE_BAR_CAPTION, true );
menuBar.add( caption ); menuBar.add( caption );
menuBar.revalidate(); menuBar.revalidate();
} }
private void addTextField() {
JTextField textField = new JTextField( "text", 10 );
JPanel panel = new JPanel( new GridBagLayout() );
panel.add( textField, new GridBagConstraints() );
menuBar.add( panel );
menuBar.revalidate();
}
private void removeMenu() { private void removeMenu() {
int menuCount = menuBar.getMenuCount(); int menuCount = menuBar.getMenuCount();
if( menuCount <= 0 ) if( menuCount <= 0 )
@@ -515,13 +535,31 @@ debug*/
rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_CLOSE, showCloseCheckBox.isSelected() ? null : false ); rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_SHOW_CLOSE, showCloseCheckBox.isSelected() ? null : false );
} }
private void fullWindowContentChanged() {
JRootPane rootPane = getWindowRootPane();
if( rootPane != null ) {
boolean selected = fullWindowContentCheckBox.isSelected();
rootPane.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT, selected ? true : null );
showIconCheckBox.setEnabled( !selected );
showTitleCheckBox.setEnabled( !selected );
}
}
private JRootPane getWindowRootPane() { private JRootPane getWindowRootPane() {
Window window = SwingUtilities.windowForComponent( this ); Window window = SwingUtilities.windowForComponent( this );
if( window instanceof JFrame ) return (window instanceof RootPaneContainer)
return ((JFrame)window).getRootPane(); ? ((RootPaneContainer)window).getRootPane()
else if( window instanceof JDialog ) : null;
return ((JDialog)window).getRootPane(); }
return null;
private void showRectangles() {
JRootPane rootPane = getWindowRootPane();
if( rootPane != null ) {
UIManager.put( KEY_DEBUG_SHOW_RECTANGLES, showRectanglesCheckBox.isSelected() );
rootPane.revalidate();
rootPane.repaint();
}
} }
private void initComponents() { private void initComponents() {
@@ -538,6 +576,9 @@ debug*/
showIconifyCheckBox = new JCheckBox(); showIconifyCheckBox = new JCheckBox();
showMaximizeCheckBox = new JCheckBox(); showMaximizeCheckBox = new JCheckBox();
showCloseCheckBox = new JCheckBox(); showCloseCheckBox = new JCheckBox();
fullWindowContentCheckBox = new JCheckBox();
JLabel fullWindowContentButtonsBoundsLabel = new JLabel();
fullWindowContentButtonsBoundsField = new JLabel();
JPanel panel6 = new JPanel(); JPanel panel6 = new JPanel();
menuBarCheckBox = new JCheckBox(); menuBarCheckBox = new JCheckBox();
menuBarEmbeddedCheckBox = new JCheckBox(); menuBarEmbeddedCheckBox = new JCheckBox();
@@ -548,6 +589,7 @@ debug*/
addMenuButton = new JButton(); addMenuButton = new JButton();
addGlueButton = new JButton(); addGlueButton = new JButton();
addCaptionButton = new JButton(); addCaptionButton = new JButton();
addTextFieldButton = new JButton();
removeMenuButton = new JButton(); removeMenuButton = new JButton();
changeMenuButton = new JButton(); changeMenuButton = new JButton();
changeTitleButton = new JButton(); changeTitleButton = new JButton();
@@ -578,6 +620,7 @@ debug*/
typeNormalRadioButton = new JRadioButton(); typeNormalRadioButton = new JRadioButton();
typeUtilityRadioButton = new JRadioButton(); typeUtilityRadioButton = new JRadioButton();
typeSmallRadioButton = new JRadioButton(); typeSmallRadioButton = new JRadioButton();
showRectanglesCheckBox = new JCheckBox();
menuBar = new JMenuBar(); menuBar = new JMenuBar();
JMenu fileMenu = new JMenu(); JMenu fileMenu = new JMenu();
JMenuItem newMenuItem = new JMenuItem(); JMenuItem newMenuItem = new JMenuItem();
@@ -616,6 +659,7 @@ debug*/
// rows // rows
"[fill]" + "[fill]" +
"[fill]" + "[fill]" +
"[]" +
"[]")); "[]"));
//======== panel7 ======== //======== panel7 ========
@@ -673,6 +717,8 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]rel" +
"[]rel" +
"[]")); "[]"));
//---- showIconCheckBox ---- //---- showIconCheckBox ----
@@ -703,6 +749,19 @@ debug*/
showCloseCheckBox.setSelected(true); showCloseCheckBox.setSelected(true);
showCloseCheckBox.addActionListener(e -> showCloseChanged()); showCloseCheckBox.addActionListener(e -> showCloseChanged());
panel4.add(showCloseCheckBox, "cell 0 4"); panel4.add(showCloseCheckBox, "cell 0 4");
//---- fullWindowContentCheckBox ----
fullWindowContentCheckBox.setText("full window content");
fullWindowContentCheckBox.addActionListener(e -> fullWindowContentChanged());
panel4.add(fullWindowContentCheckBox, "cell 0 5");
//---- fullWindowContentButtonsBoundsLabel ----
fullWindowContentButtonsBoundsLabel.setText("Buttons bounds:");
panel4.add(fullWindowContentButtonsBoundsLabel, "cell 0 6");
//---- fullWindowContentButtonsBoundsField ----
fullWindowContentButtonsBoundsField.setText("null");
panel4.add(fullWindowContentButtonsBoundsField, "cell 0 6");
} }
add(panel4, "cell 1 0"); add(panel4, "cell 1 0");
@@ -761,6 +820,7 @@ debug*/
"[]" + "[]" +
"[]" + "[]" +
"[]" + "[]" +
"[]" +
"[]unrel" + "[]unrel" +
"[]")); "[]"));
@@ -779,20 +839,25 @@ debug*/
addCaptionButton.addActionListener(e -> addCaption()); addCaptionButton.addActionListener(e -> addCaption());
panel3.add(addCaptionButton, "cell 0 2"); panel3.add(addCaptionButton, "cell 0 2");
//---- addTextFieldButton ----
addTextFieldButton.setText("Add text field");
addTextFieldButton.addActionListener(e -> addTextField());
panel3.add(addTextFieldButton, "cell 0 3");
//---- removeMenuButton ---- //---- removeMenuButton ----
removeMenuButton.setText("Remove menu"); removeMenuButton.setText("Remove menu");
removeMenuButton.addActionListener(e -> removeMenu()); removeMenuButton.addActionListener(e -> removeMenu());
panel3.add(removeMenuButton, "cell 0 3"); panel3.add(removeMenuButton, "cell 0 4");
//---- changeMenuButton ---- //---- changeMenuButton ----
changeMenuButton.setText("Change menu"); changeMenuButton.setText("Change menu");
changeMenuButton.addActionListener(e -> changeMenu()); changeMenuButton.addActionListener(e -> changeMenu());
panel3.add(changeMenuButton, "cell 0 4"); panel3.add(changeMenuButton, "cell 0 5");
//---- changeTitleButton ---- //---- changeTitleButton ----
changeTitleButton.setText("Change title"); changeTitleButton.setText("Change title");
changeTitleButton.addActionListener(e -> changeTitle()); changeTitleButton.addActionListener(e -> changeTitle());
panel3.add(changeTitleButton, "cell 0 5"); panel3.add(changeTitleButton, "cell 0 6");
} }
add(panel3, "cell 3 0 1 2,aligny top,growy 0"); add(panel3, "cell 3 0 1 2,aligny top,growy 0");
@@ -969,6 +1034,12 @@ debug*/
typeSmallRadioButton.setText("Small"); typeSmallRadioButton.setText("Small");
add(typeSmallRadioButton, "cell 0 2 3 1"); add(typeSmallRadioButton, "cell 0 2 3 1");
//---- showRectanglesCheckBox ----
showRectanglesCheckBox.setText("Show debug title bar rectangles");
showRectanglesCheckBox.setSelected(true);
showRectanglesCheckBox.addActionListener(e -> showRectangles());
add(showRectanglesCheckBox, "cell 0 3");
//======== menuBar ======== //======== menuBar ========
{ {
@@ -1176,6 +1247,8 @@ debug*/
private JCheckBox showIconifyCheckBox; private JCheckBox showIconifyCheckBox;
private JCheckBox showMaximizeCheckBox; private JCheckBox showMaximizeCheckBox;
private JCheckBox showCloseCheckBox; private JCheckBox showCloseCheckBox;
private JCheckBox fullWindowContentCheckBox;
private JLabel fullWindowContentButtonsBoundsField;
private JCheckBox menuBarCheckBox; private JCheckBox menuBarCheckBox;
private JCheckBox menuBarEmbeddedCheckBox; private JCheckBox menuBarEmbeddedCheckBox;
private JCheckBox menuBarVisibleCheckBox; private JCheckBox menuBarVisibleCheckBox;
@@ -1184,6 +1257,7 @@ debug*/
private JButton addMenuButton; private JButton addMenuButton;
private JButton addGlueButton; private JButton addGlueButton;
private JButton addCaptionButton; private JButton addCaptionButton;
private JButton addTextFieldButton;
private JButton removeMenuButton; private JButton removeMenuButton;
private JButton changeMenuButton; private JButton changeMenuButton;
private JButton changeTitleButton; private JButton changeTitleButton;
@@ -1209,6 +1283,7 @@ debug*/
private JRadioButton typeNormalRadioButton; private JRadioButton typeNormalRadioButton;
private JRadioButton typeUtilityRadioButton; private JRadioButton typeUtilityRadioButton;
private JRadioButton typeSmallRadioButton; private JRadioButton typeSmallRadioButton;
private JCheckBox showRectanglesCheckBox;
private JMenuBar menuBar; private JMenuBar menuBar;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
} }

View File

@@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3" "$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[left][fill][fill][fill]" "$columnConstraints": "[left][fill][fill][fill]"
"$rowConstraints": "[fill][fill][]" "$rowConstraints": "[fill][fill][][]"
} ) { } ) {
name: "this" name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
@@ -77,7 +77,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,hidemode 3,gap 0 0" "$layoutConstraints": "ltr,hidemode 3,gap 0 0"
"$columnConstraints": "[grow,left]" "$columnConstraints": "[grow,left]"
"$rowConstraints": "[][][][][]" "$rowConstraints": "[][][][][]rel[]rel[]"
} ) { } ) {
name: "panel4" name: "panel4"
"border": new javax.swing.border.TitledBorder( "Title Bar" ) "border": new javax.swing.border.TitledBorder( "Title Bar" )
@@ -135,6 +135,31 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4" "value": "cell 0 4"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "fullWindowContentCheckBox"
"text": "full window content"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fullWindowContentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsLabel"
"text": "Buttons bounds:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fullWindowContentButtonsBoundsField"
"text": "null"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 6"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0" "value": "cell 1 0"
} ) } )
@@ -204,7 +229,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3" "$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill]" "$columnConstraints": "[fill]"
"$rowConstraints": "[][][][][]unrel[]" "$rowConstraints": "[][][][][][]unrel[]"
} ) { } ) {
name: "panel3" name: "panel3"
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
@@ -237,6 +262,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2" "value": "cell 0 2"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) {
name: "addTextFieldButton"
"text": "Add text field"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "addTextField", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "removeMenuButton" name: "removeMenuButton"
"text": "Remove menu" "text": "Remove menu"
@@ -245,7 +280,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "removeMenu", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "removeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3" "value": "cell 0 4"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "changeMenuButton" name: "changeMenuButton"
@@ -255,7 +290,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeMenu", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeMenu", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4" "value": "cell 0 5"
} ) } )
add( new FormComponent( "javax.swing.JButton" ) { add( new FormComponent( "javax.swing.JButton" ) {
name: "changeTitleButton" name: "changeTitleButton"
@@ -265,7 +300,7 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "changeTitle", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5" "value": "cell 0 6"
} ) } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0 1 2,aligny top,growy 0" "value": "cell 3 0 1 2,aligny top,growy 0"
@@ -552,6 +587,17 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 3 1" "value": "cell 0 2 3 1"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showRectanglesCheckBox"
"text": "Show debug title bar rectangles"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showRectangles", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 ) "location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 960, 495 ) "size": new java.awt.Dimension( 960, 495 )

View File

@@ -1039,7 +1039,6 @@ TitlePane.inactiveBackground
TitlePane.inactiveForeground TitlePane.inactiveForeground
TitlePane.maximizeIcon TitlePane.maximizeIcon
TitlePane.menuBarEmbedded TitlePane.menuBarEmbedded
TitlePane.menuBarResizeHeight
TitlePane.menuBarTitleGap TitlePane.menuBarTitleGap
TitlePane.menuBarTitleMinimumGap TitlePane.menuBarTitleMinimumGap
TitlePane.noIconLeftGap TitlePane.noIconLeftGap
@@ -1071,7 +1070,6 @@ TitlePane.small.iconifyIcon
TitlePane.small.inactiveBackground TitlePane.small.inactiveBackground
TitlePane.small.inactiveForeground TitlePane.small.inactiveForeground
TitlePane.small.maximizeIcon TitlePane.small.maximizeIcon
TitlePane.small.menuBarResizeHeight
TitlePane.small.menuBarTitleGap TitlePane.small.menuBarTitleGap
TitlePane.small.menuBarTitleMinimumGap TitlePane.small.menuBarTitleMinimumGap
TitlePane.small.noIconLeftGap TitlePane.small.noIconLeftGap