added simple UI inspector to FlatTestFrame

This commit is contained in:
Karl Tauber
2019-09-23 10:48:41 +02:00
parent e004401772
commit c5ab5d636e
3 changed files with 290 additions and 9 deletions

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2019 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.lang.reflect.Field;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.JToolBar;
import javax.swing.JToolTip;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.ui.FlatToolTipUI;
import com.formdev.flatlaf.util.UIScale;
/**
* @author Karl Tauber
*/
public class FlatInspector
{
private static final Integer HIGHLIGHT_LAYER = 401;
private static final Integer TOOLTIP_LAYER = 402;
private final JRootPane rootPane;
private Component lastComponent;
private int lastX;
private int lastY;
private JComponent highlightFigure;
private JToolTip tip;
public FlatInspector( JRootPane rootPane ) {
this.rootPane = rootPane;
rootPane.getGlassPane().addMouseMotionListener( new MouseMotionAdapter() {
@Override
public void mouseMoved( MouseEvent e ) {
lastX = e.getX();
lastY = e.getY();
inspect( lastX, lastY );
}
} );
}
public void setEnabled( boolean enabled ) {
rootPane.getGlassPane().setVisible( enabled );
if( !enabled ) {
lastComponent = null;
if( highlightFigure != null )
highlightFigure.getParent().remove( highlightFigure );
highlightFigure = null;
if( tip != null )
tip.getParent().remove( tip );
tip = null;
}
}
public void update() {
if( !rootPane.getGlassPane().isVisible() )
return;
EventQueue.invokeLater( () -> {
setEnabled( false );
setEnabled( true );
inspect( lastX, lastY );
} );
}
private void inspect( int x, int y ) {
Container contentPane = rootPane.getContentPane();
Component c = SwingUtilities.getDeepestComponentAt( contentPane, x, y );
if( c == contentPane || c.getParent() == contentPane )
c = null;
if( c == lastComponent )
return;
lastComponent = c;
highlight( c );
showToolTip( c, x, y );
}
private void highlight( Component c ) {
if( highlightFigure == null ) {
highlightFigure = createHighlightFigure();
rootPane.getLayeredPane().add( highlightFigure, HIGHLIGHT_LAYER );
}
highlightFigure.setVisible( c != null );
if( c != null ) {
Rectangle bounds = c.getBounds();
Rectangle highlightBounds = SwingUtilities.convertRectangle( c.getParent(), bounds, rootPane );
highlightFigure.setBounds( highlightBounds );
}
}
private JComponent createHighlightFigure() {
JComponent c = new JComponent() {
@Override
protected void paintComponent( Graphics g ) {
g.setColor( getBackground() );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
};
c.setBackground( new Color( 255, 0, 0, 32 ) );
c.setBorder( new LineBorder( Color.red ) );
return c;
}
private void showToolTip( Component c, int x, int y ) {
if( c == null ) {
if( tip != null )
tip.setVisible( false );
return;
}
if( tip == null ) {
tip = new JToolTip() {
@Override
public void updateUI() {
setUI( FlatToolTipUI.createUI( this ) );
}
};
rootPane.getLayeredPane().add( tip, TOOLTIP_LAYER );
} else
tip.setVisible( true );
tip.setTipText( buildToolTipText( c ) );
int tx = x + UIScale.scale( 8 );
int ty = y + UIScale.scale( 16 );
Dimension size = tip.getPreferredSize();
// position the tip in the visible area
Rectangle visibleRect = rootPane.getVisibleRect();
if( tx + size.width > visibleRect.x + visibleRect.width )
tx = visibleRect.x + visibleRect.width - size.width;
if( ty + size.height > visibleRect.y + visibleRect.height )
ty = visibleRect.y + visibleRect.height - size.height;
if( tx < visibleRect.x )
tx = visibleRect.x;
if( ty < visibleRect.y )
ty = visibleRect.y;
tip.setBounds( tx, ty, size.width, size.height );
tip.repaint();
}
private String buildToolTipText( Component c ) {
String text =
"Class: " + c.getClass().getSimpleName() + " (" + c.getClass().getPackage().getName() + ")\n" +
"Size: " + c.getWidth() + ',' + c.getHeight() + " @ " + c.getX() + ',' + c.getY() + '\n';
if( c instanceof Container ) {
Insets i = ((Container)c).getInsets();
text += "Insets: " + i.top + ',' + i.left + ',' + i.bottom + ',' + i.right + '\n';
}
Insets margin = null;
if( c instanceof AbstractButton )
margin = ((AbstractButton) c).getMargin();
else if( c instanceof JToolBar )
margin = ((JToolBar) c).getMargin();
else if( c instanceof JTextComponent )
margin = ((JTextComponent) c).getMargin();
if( margin != null )
text += "Margin: " + margin.top + ',' + margin.left + ',' + margin.bottom + ',' + margin.right + '\n';
Dimension prefSize = c.getPreferredSize();
Dimension minSize = c.getMinimumSize();
Dimension maxSize = c.getMaximumSize();
text += "Pref size: " + prefSize.width + ',' + prefSize.height + '\n' +
"Min size: " + minSize.width + ',' + minSize.height + '\n' +
"Max size: " + maxSize.width + ',' + maxSize.height + '\n';
if( c instanceof JComponent ) {
Border border = ((JComponent)c).getBorder();
text += "Border: " + (border != null ? border.getClass().getName() : "null") + '\n';
}
text += "Background: " + toString( c.getBackground() ) + '\n' +
"Foreground: " + toString( c.getForeground() ) + '\n' +
"Font: " + toString( c.getFont() ) + '\n';
if( c instanceof JComponent ) {
try {
Field f = JComponent.class.getDeclaredField( "ui" );
f.setAccessible( true );
Object ui = f.get( c );
text += "UI: " + (ui != null ? ui.getClass().getName() : "null") + '\n';
} catch( NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex ) {
// ignore
}
}
text += "Parent: " + c.getParent().getClass().getName();
return text;
}
private static String toString( Color c ) {
if( c == null )
return "null";
String s = Long.toString( c.getRGB() & 0xffffffffl, 16 );
if( c instanceof UIResource )
s += " UI";
return s;
}
private static String toString( Font f ) {
if( f == null )
return "null";
return f.getFamily() + " " + f.getSize() + " " + f.getStyle()
+ (f instanceof UIResource ? " UI" : "");
}
}

View File

@@ -36,6 +36,7 @@ public class FlatTestFrame
private static final String KEY_LAF = "laf";
private JComponent content;
private FlatInspector inspector;
public static FlatTestFrame create( String[] args, String title ) {
// set look and feel
@@ -143,7 +144,7 @@ public class FlatTestFrame
protected void showFrame( JComponent content ) {
this.content = content;
contentPanel.add( content );
contentPanel.getContentPane().add( content );
pack();
setLocationRelativeTo( null );
setVisible( true );
@@ -185,6 +186,9 @@ public class FlatTestFrame
if( prefSize.width > width || prefSize.height > height )
setSize( Math.max( prefSize.width, width ), Math.max( prefSize.height, height ) );
if( inspector != null )
inspector.update();
} catch( Exception ex ) {
ex.printStackTrace();
}
@@ -279,15 +283,22 @@ public class FlatTestFrame
}
}
private void inspectChanged() {
if( inspector == null )
inspector = new FlatInspector( contentPanel );
inspector.setEnabled( inspectCheckBox.isSelected() );
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
dialogPane = new JPanel();
contentPanel = new JPanel();
contentPanel = new JRootPane();
buttonBar = new JPanel();
lookAndFeelComboBox = new JComboBox<>();
explicitColorsCheckBox = new JCheckBox();
rightToLeftCheckBox = new JCheckBox();
enabledCheckBox = new JCheckBox();
inspectCheckBox = new JCheckBox();
closeButton = new JButton();
//======== this ========
@@ -301,7 +312,8 @@ public class FlatTestFrame
//======== contentPanel ========
{
contentPanel.setLayout(new MigLayout(
Container contentPanelContentPane = contentPanel.getContentPane();
contentPanelContentPane.setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[grow,fill]",
@@ -319,6 +331,7 @@ public class FlatTestFrame
"[fill]" +
"[fill]" +
"[fill]" +
"[fill]" +
"[grow,fill]" +
"[button,fill]",
// rows
@@ -347,9 +360,15 @@ public class FlatTestFrame
enabledCheckBox.addActionListener(e -> enabledChanged());
buttonBar.add(enabledCheckBox, "cell 3 0");
//---- inspectCheckBox ----
inspectCheckBox.setText("inspect");
inspectCheckBox.setMnemonic('I');
inspectCheckBox.addActionListener(e -> inspectChanged());
buttonBar.add(inspectCheckBox, "cell 4 0");
//---- closeButton ----
closeButton.setText("Close");
buttonBar.add(closeButton, "cell 5 0");
buttonBar.add(closeButton, "cell 6 0");
}
dialogPane.add(buttonBar, BorderLayout.SOUTH);
}
@@ -359,12 +378,13 @@ public class FlatTestFrame
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel dialogPane;
private JPanel contentPanel;
private JRootPane contentPanel;
private JPanel buttonBar;
private JComboBox<LafInfo> lookAndFeelComboBox;
private JCheckBox explicitColorsCheckBox;
private JCheckBox rightToLeftCheckBox;
private JCheckBox enabledCheckBox;
private JCheckBox inspectCheckBox;
private JButton closeButton;
// JFormDesigner - End of variables declaration //GEN-END:variables

View File

@@ -10,7 +10,7 @@ new FormModel {
"defaultCloseOperation": 2
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) {
name: "dialogPane"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
add( new FormContainer( "javax.swing.JRootPane", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[grow,fill]"
@@ -21,7 +21,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog"
"$columnConstraints": "[fill][fill][fill][fill][grow,fill][button,fill]"
"$columnConstraints": "[fill][fill][fill][fill][fill][grow,fill][button,fill]"
"$rowSpecs": "[fill]"
} ) {
name: "buttonBar"
@@ -59,11 +59,19 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "inspectCheckBox"
"text": "inspect"
"mnemonic": 73
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "inspectChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "closeButton"
"text": "Close"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 5 0"
"value": "cell 6 0"
} )
}, new FormLayoutConstraints( class java.lang.String ) {
"value": "South"
@@ -73,7 +81,7 @@ new FormModel {
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 400, 300 )
"size": new java.awt.Dimension( 510, 300 )
} )
}
}