📄 rootpaneui.java
字号:
package com.digitprop.tonic;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.awt.*;
/**
* Slight modification of the UI delegate for root panes.
*
* @author Markus Fischer
*
* <p>
* This software is under the <a href="http://www.gnu.org/copyleft/lesser.html"
* target="_blank">GNU Lesser General Public License </a>
*/
/*
* ------------------------------------------------------------------------
* Copyright (C) 2004 Markus Fischer
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 2.1 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* You can contact the author at: Markus Fischer www.digitprop.com
* info@digitprop.com
* ------------------------------------------------------------------------
*/
public class RootPaneUI extends BasicRootPaneUI
{
/**
* Keys to lookup borders in defaults table.
*/
private static final String[] borderKeys = new String[]
{
null, "RootPane.frameBorder", "RootPane.plainDialogBorder",
"RootPane.informationDialogBorder", "RootPane.errorDialogBorder",
"RootPane.colorChooserDialogBorder", "RootPane.fileChooserDialogBorder",
"RootPane.questionDialogBorder", "RootPane.warningDialogBorder"
};
/**
* The amount of space (in pixels) that the cursor is changed on.
*/
private static final int CORNER_DRAG_WIDTH = 16;
/**
* Region from edges that dragging is active from.
*/
private static final int BORDER_DRAG_THICKNESS = 5;
/**
* Window the <code>JRootPane</code> is in.
*/
private Window window;
/**
* <code>JComponent</code> providing window decorations. This will be null
* if not providing window decorations.
*/
private JComponent titlePane;
/**
* <code>MouseInputListener</code> that is added to the parent
* <code>Window</code> the <code>JRootPane</code> is contained in.
*/
private MouseInputListener mouseInputListener;
/**
* The <code>LayoutManager</code> that is set on the <code>JRootPane</code>.
*/
private LayoutManager layoutManager;
/**
* <code>LayoutManager</code> of the <code>JRootPane</code> before we
* replaced it.
*/
private LayoutManager savedOldLayout;
/**
* <code>JRootPane</code> providing the look and feel for.
*/
private JRootPane root;
/**
* <code>Cursor</code> used to track the cursor set by the user. This is
* initially <code>Cursor.DEFAULT_CURSOR</code>.
*/
private Cursor lastCursor = Cursor
.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
/**
* Creates a UI for a <code>JRootPane</code>.
*
* @param c the JRootPane the RootPaneUI will be created for
* @return the RootPaneUI implementation for the passed in JRootPane
*/
public static ComponentUI createUI(JComponent c)
{
return new RootPaneUI();
}
/**
* Invokes supers implementation of <code>installUI</code> to install the
* necessary state onto the passed in <code>JRootPane</code> to render the
* metal look and feel implementation of <code>RootPaneUI</code>. If the
* <code>windowDecorationStyle</code> property of the
* <code>JRootPane</code> is other than <code>JRootPane.NONE</code>,
* this will add a custom <code>Component</code> to render the widgets to
* <code>JRootPane</code>, as well as installing a custom
* <code>Border</code> and <code>LayoutManager</code> on the
* <code>JRootPane</code>.
*
* @param c the JRootPane to install state onto
*/
public void installUI(JComponent c)
{
super.installUI(c);
root = (JRootPane) c;
int style = root.getWindowDecorationStyle();
if (style != JRootPane.NONE)
{
installClientDecorations(root);
}
}
/**
* Invokes supers implementation to uninstall any of its state. This will
* also reset the <code>LayoutManager</code> of the <code>JRootPane</code>.
* If a <code>Component</code> has been added to the <code>JRootPane</code>
* to render the window decoration style, this method will remove it.
* Similarly, this will revert the Border and LayoutManager of the
* <code>JRootPane</code> to what it was before <code>installUI</code>
* was invoked.
*
* @param c the JRootPane to uninstall state from
*/
public void uninstallUI(JComponent c)
{
super.uninstallUI(c);
uninstallClientDecorations(root);
layoutManager = null;
mouseInputListener = null;
root = null;
}
/**
* Installs the appropriate <code>Border</code> onto the
* <code>JRootPane</code>.
*/
void installBorder(JRootPane root)
{
int style = root.getWindowDecorationStyle();
if (style == JRootPane.NONE)
{
LookAndFeel.uninstallBorder(root);
}
else
{
LookAndFeel.installBorder(root, borderKeys[style]);
}
}
/**
* Removes any border that may have been installed.
*/
private void uninstallBorder(JRootPane root)
{
LookAndFeel.uninstallBorder(root);
}
/**
* Installs the necessary Listeners on the parent <code>Window</code>, if
* there is one.
* <p>
* This takes the parent so that cleanup can be done from
* <code>removeNotify</code>, at which point the parent hasn't been reset
* yet.
*
* @param parent The parent of the JRootPane
*/
private void installWindowListeners(JRootPane root, Component parent)
{
if (parent instanceof Window)
{
window = (Window) parent;
}
else
{
window = SwingUtilities.getWindowAncestor(parent);
}
if (window != null)
{
if (mouseInputListener == null)
{
mouseInputListener = createWindowMouseInputListener(root);
}
window.addMouseListener(mouseInputListener);
window.addMouseMotionListener(mouseInputListener);
}
}
/**
* Uninstalls the necessary Listeners on the <code>Window</code> the
* Listeners were last installed on.
*/
private void uninstallWindowListeners(JRootPane root)
{
if (window != null)
{
window.removeMouseListener(mouseInputListener);
window.removeMouseMotionListener(mouseInputListener);
}
}
/**
* Installs the appropriate LayoutManager on the <code>JRootPane</code> to
* render the window decorations.
*/
private void installLayout(JRootPane root)
{
if (layoutManager == null)
{
layoutManager = createLayoutManager();
}
savedOldLayout = root.getLayout();
root.setLayout(layoutManager);
}
/**
* Uninstalls the previously installed <code>LayoutManager</code>.
*/
private void uninstallLayout(JRootPane root)
{
if (savedOldLayout != null)
{
root.setLayout(savedOldLayout);
savedOldLayout = null;
}
}
/**
* Installs the necessary state onto the JRootPane to render client
* decorations. This is ONLY invoked if the <code>JRootPane</code> has a
* decoration style other than <code>JRootPane.NONE</code>.
*/
private void installClientDecorations(JRootPane root)
{
installBorder(root);
JComponent titlePane = createTitlePane(root);
setTitlePane(root, titlePane);
installWindowListeners(root, root.getParent());
installLayout(root);
if (window != null)
{
root.revalidate();
root.repaint();
}
}
/**
* Uninstalls any state that <code>installClientDecorations</code> has
* installed.
* <p>
* NOTE: This may be called if you haven't installed client decorations yet
* (ie before <code>installClientDecorations</code> has been invoked).
*/
private void uninstallClientDecorations(JRootPane root)
{
uninstallBorder(root);
uninstallWindowListeners(root);
setTitlePane(root, null);
uninstallLayout(root);
// We have to revalidate/repaint root if the style is JRootPane.NONE
// only. When we needs to call revalidate/repaint with other styles
// the installClientDecorations is always called after this method
// imediatly and it will cause the revalidate/repaint at the proper
// time.
int style = root.getWindowDecorationStyle();
if (style == JRootPane.NONE)
{
root.repaint();
root.revalidate();
}
// Reset the cursor, as we may have changed it to a resize cursor
if (window != null)
{
window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
window = null;
}
/**
* Returns the <code>JComponent</code> to render the window decoration
* style.
*/
private JComponent createTitlePane(JRootPane root)
{
return new TitlePane(root, this);
}
/**
* Returns a <code>MouseListener</code> that will be added to the
* <code>Window</code> containing the <code>JRootPane</code>.
*/
private MouseInputListener createWindowMouseInputListener(JRootPane root)
{
return new MouseInputHandler();
}
/**
* Returns a <code>LayoutManager</code> that will be set on the
* <code>JRootPane</code>.
*/
private LayoutManager createLayoutManager()
{
return new MetalRootLayout();
}
/**
* Sets the window title pane -- the JComponent used to provide a plaf a way
* to override the native operating system's window title pane with one whose
* look and feel are controlled by the plaf. The plaf creates and sets this
* value; the default is null, implying a native operating system window
* title pane.
*
* @param content the <code>JComponent</code> to use for the window title
* pane.
*/
private void setTitlePane(JRootPane root, JComponent titlePane)
{
JLayeredPane layeredPane = root.getLayeredPane();
JComponent oldTitlePane = getTitlePane();
if (oldTitlePane != null)
{
oldTitlePane.setVisible(false);
layeredPane.remove(oldTitlePane);
}
if (titlePane != null)
{
layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
titlePane.setVisible(true);
}
this.titlePane = titlePane;
}
/**
* Returns the <code>JComponent</code> rendering the title pane. If this
* returns null, it implies there is no need to render window decorations.
*
* @return the current window title pane, or null
* @see #setTitlePane
*/
private JComponent getTitlePane()
{
return titlePane;
}
/**
* Returns the <code>JRootPane</code> we're providing the look and feel
* for.
*/
private JRootPane getRootPane()
{
return root;
}
/**
* Invoked when a property changes. <code>MetalRootPaneUI</code> is
* primarily interested in events originating from the <code>JRootPane</code>
* it has been installed on identifying the property
* <code>windowDecorationStyle</code>. If the
* <code>windowDecorationStyle</code> has changed to a value other than
* <code>JRootPane.NONE</code>, this will add a <code>Component</code>
* to the <code>JRootPane</code> to render the window decorations, as well
* as installing a <code>Border</code> on the <code>JRootPane</code>. On
* the other hand, if the <code>windowDecorationStyle</code> has changed to
* <code>JRootPane.NONE</code>, this will remove the
* <code>Component</code> that has been added to the <code>JRootPane</code>
* as well resetting the Border to what it was before <code>installUI</code>
* was invoked.
*
* @param e A PropertyChangeEvent object describing the event source and the
* property that has changed.
*/
public void propertyChange(PropertyChangeEvent e)
{
super.propertyChange(e);
String propertyName = e.getPropertyName();
if (propertyName == null)
{
return;
}
if (propertyName.equals("windowDecorationStyle"))
{
JRootPane root = (JRootPane) e.getSource();
int style = root.getWindowDecorationStyle();
// This is potentially more than needs to be done,
// but it rarely happens and makes the install/uninstall process
// simpler. MetalTitlePane also assumes it will be recreated if
// the decoration style changes.
uninstallClientDecorations(root);
if (style != JRootPane.NONE)
{
installClientDecorations(root);
}
}
else if (propertyName.equals("ancestor"))
{
uninstallWindowListeners(root);
if (((JRootPane) e.getSource()).getWindowDecorationStyle() != JRootPane.NONE)
{
installWindowListeners(root, root.getParent());
}
}
return;
}
/**
* A custom layout manager that is responsible for the layout of layeredPane,
* glassPane, menuBar and titlePane, if one has been installed.
*/
// NOTE: Ideally this would extends JRootPane.RootLayout, but that
// would force this to be non-static.
private static class MetalRootLayout implements LayoutManager2
{
/**
* Returns the amount of space the layout would like to have.
*
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's preferred size
*/
public Dimension preferredLayoutSize(Container parent)
{
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null)
{
cpd = root.getContentPane().getPreferredSize();
}
else
{
cpd = root.getSize();
}
if (cpd != null)
{
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getMenuBar() != null)
{
mbd = root.getMenuBar().getPreferredSize();
if (mbd != null)
{
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if (root.getWindowDecorationStyle() != JRootPane.NONE
&& (root.getUI() instanceof RootPaneUI))
{
JComponent titlePane = ((RootPaneUI) root.getUI()).getTitlePane();
if (titlePane != null)
{
tpd = titlePane.getPreferredSize();
if (tpd != null)
{
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth) + i.left + i.right,
cpHeight + mbHeight + tpWidth + i.top + i.bottom);
}
/**
* Returns the minimum amount of space the layout needs.
*
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's minimum size
*/
public Dimension minimumLayoutSize(Container parent)
{
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null)
{
cpd = root.getContentPane().getMinimumSize();
}
else
{
cpd = root.getSize();
}
if (cpd != null)
{
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getMenuBar() != null)
{
mbd = root.getMenuBar().getMinimumSize();
if (mbd != null)
{
mbWidth = mbd.width;
mbHeight = mbd.height;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -