VAqua.src.org.violetlib.aqua.AquaOptionPaneUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaqua Show documentation
Show all versions of vaqua Show documentation
An improved native Swing look and feel for macOS
The newest version!
/*
* Changes Copyright (c) 2015-2021 Alan Snyder.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the license agreement. For details see
* accompanying license terms.
*/
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.violetlib.aqua;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicOptionPaneUI;
import javax.swing.text.Document;
import javax.swing.text.View;
import org.jetbrains.annotations.NotNull;
public class AquaOptionPaneUI extends BasicOptionPaneUI implements AquaComponentUI {
private static final int kOKCancelButtonWidth = 79;
private static final int kButtonHeight = 23;
private static final int kDialogSmallPadding = 4;
private static final int kDialogLargePadding = 23;
public static final String TEXT_FIELD_DOCUMENT_KEY = "JOptionPane.textFieldDocument";
private static String newline;
static {
newline = System.lineSeparator();
if (newline == null) {
newline = "\n";
}
}
/**
* Creates a new BasicOptionPaneUI instance.
*/
public static ComponentUI createUI(JComponent x) {
return new AquaOptionPaneUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.installProperty(optionPane, "opaque", false);
}
@Override
public void appearanceChanged(@NotNull JComponent c, @NotNull AquaAppearance appearance) {
}
@Override
public void activeStateChanged(@NotNull JComponent c, boolean isActive) {
}
@Override
public final void update(Graphics g, JComponent c) {
AppearanceManager.registerCurrentAppearance(c);
paint(g, c);
}
/**
* Creates and returns a Container containing the buttons. The buttons
* are created by calling getButtons
.
*/
protected Container createButtonArea() {
Container bottom = super.createButtonArea();
// Now replace the Layout
bottom.setLayout(new AquaButtonAreaLayout(true, kDialogSmallPadding));
return bottom;
}
/**
* Messaged from installComponents to create a Container containing the
* body of the message.
* The icon and body should be aligned on their top edges
*/
protected Container createMessageArea() {
JPanel top = new JPanel();
top.setBorder(UIManager.getBorder("OptionPane.messageAreaBorder"));
top.setLayout(new BorderLayout());
JComponent iconPanel = createIconPanel();
JComponent body = createBody();
body.setName("OptionPane.body");
if (iconPanel != null) {
JComponent realBody = new JPanel(new BorderLayout());
realBody.setName("OptionPane.realBody");
realBody.add(Box.createHorizontalStrut(kDialogLargePadding), BorderLayout.BEFORE_LINE_BEGINS);
realBody.add(body, BorderLayout.CENTER);
top.add(realBody, BorderLayout.CENTER);
top.add(iconPanel, BorderLayout.BEFORE_LINE_BEGINS);
} else {
top.add(body, BorderLayout.CENTER);
}
return top;
}
protected JComponent createIconPanel() {
Icon sideIcon = getIcon();
if (sideIcon != null) {
JLabel iconLabel = new JLabel(sideIcon);
iconLabel.setVerticalAlignment(SwingConstants.TOP);
JPanel iconPanel = new JPanel();
iconPanel.add(iconLabel);
return iconPanel;
}
return null;
}
protected JComponent createBody() {
JPanel body = new JPanel();
body.setLayout(new GridBagLayout());
GridBagConstraints cons = new GridBagConstraints();
cons.gridx = cons.gridy = 0;
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.gridheight = 1;
cons.anchor = GridBagConstraints.WEST;
cons.insets = new Insets(0, 0, 3, 0);
addMessageComponents(body, cons, getMessage(), getMaxCharactersPerLineCount(), false);
return body;
}
@Override
protected int getMaxCharactersPerLineCount() {
return optionPane.getMaxCharactersPerLineCount();
}
@Override
protected Object getMessage() {
Object o = super.getMessage();
if (o != null && inputComponent instanceof JTextField) {
Object p = optionPane.getClientProperty(TEXT_FIELD_DOCUMENT_KEY);
if (p instanceof Document) {
Document d = (Document) p;
JTextField tf = (JTextField) inputComponent;
tf.setDocument(d);
}
}
return o;
}
/**
* AquaButtonAreaLayout lays out all
* components according to the HI Guidelines:
* The most important button is always on the far right
* The group of buttons is on the right for left-to-right,
* left for right-to-left
* The widths of each component will be set to the largest preferred size width.
*
*
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of BasicOptionPaneUI.
*
* BasicOptionPaneUI expects that its buttons are layed out with
* a subclass of ButtonAreaLayout
*/
public static class AquaButtonAreaLayout extends ButtonAreaLayout {
public AquaButtonAreaLayout(boolean syncAllWidths, int padding) {
super(true, padding);
}
public void layoutContainer(Container container) {
Component[] children = container.getComponents();
if (children == null || 0 >= children.length) return;
int numChildren = children.length;
int yLocation = container.getInsets().top;
// Always syncAllWidths - and heights!
Dimension maxSize = new Dimension(kOKCancelButtonWidth, kButtonHeight);
for (int i = 0; i < numChildren; i++) {
Dimension sizes = children[i].getPreferredSize();
maxSize.width = Math.max(maxSize.width, sizes.width);
maxSize.height = Math.max(maxSize.height, sizes.height);
}
// ignore getCentersChildren, because we don't
int xLocation = container.getSize().width - (maxSize.width * numChildren + (numChildren - 1) * padding);
int xOffset = maxSize.width + padding;
// most important button (button zero) on far right
for (int i = numChildren - 1; i >= 0; i--) {
children[i].setBounds(xLocation, yLocation, maxSize.width, maxSize.height);
xLocation += xOffset;
}
}
}
protected void addMessageComponents(Container container,
GridBagConstraints cons,
Object msg, int maxll,
boolean internallyCreated) {
if (msg == null) {
return;
}
if (msg instanceof Component) {
// To workaround problem where Gridbag will set child
// to its minimum size if its preferred size will not fit
// within allocated cells
if (msg instanceof JScrollPane || msg instanceof JPanel) {
cons.fill = GridBagConstraints.BOTH;
cons.weighty = 1;
} else {
cons.fill = GridBagConstraints.HORIZONTAL;
}
cons.weightx = 1;
container.add((Component) msg, cons);
cons.weightx = 0;
cons.weighty = 0;
cons.fill = GridBagConstraints.NONE;
cons.gridy++;
if (!internallyCreated) {
hasCustomComponents = true;
}
} else if (msg instanceof Object[]) {
Object [] msgs = (Object[]) msg;
for (Object o : msgs) {
addMessageComponents(container, cons, o, maxll, false);
}
} else if (msg instanceof Icon) {
JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER );
configureMessageLabel(label);
addMessageComponents(container, cons, label, maxll, true);
} else {
String s = msg.toString();
int len = s.length();
if (len <= 0) {
return;
}
if (s.startsWith("")) {
JComponent c = createHTMLTextComponent(s, maxll);
c.setName("OptionPane.label");
configureMessageLabel(c);
addMessageComponents(container, cons, c, maxll, true);
return;
}
int nl;
int nll = 0;
if ((nl = s.indexOf(newline)) >= 0) {
nll = newline.length();
} else if ((nl = s.indexOf("\r\n")) >= 0) {
nll = 2;
} else if ((nl = s.indexOf('\n')) >= 0) {
nll = 1;
}
if (nl >= 0) {
// break up newlines
if (nl == 0) {
@SuppressWarnings("serial") // anonymous class
JPanel breakPanel = new JPanel() {
public Dimension getPreferredSize() {
Font f = getFont();
if (f != null) {
return new Dimension(1, f.getSize() + 2);
}
return new Dimension(0, 0);
}
};
breakPanel.setName("OptionPane.break");
addMessageComponents(container, cons, breakPanel, maxll, true);
} else {
addMessageComponents(container, cons, s.substring(0, nl), maxll, false);
}
addMessageComponents(container, cons, s.substring(nl + nll), maxll, false);
} else {
JComponent c = createTextComponent(s, maxll);
c.setName("OptionPane.label");
configureMessageLabel(c);
addMessageComponents(container, cons, c, maxll, true);
}
}
}
protected JComponent createHTMLTextComponent(String text, int maxll) {
if (maxll > 100000) {
maxll = 80;
}
JLabel label = new JLabel(text, JLabel.LEADING);
Dimension preferredSize = label.getPreferredSize();
int preferredWidth = preferredSize.width;
int columnWidth = label.getFontMetrics(label.getFont()).charWidth('n');
int width = columnWidth * maxll;
if (preferredWidth > width) {
View view = (View) label.getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey);
if (view != null) {
view.setSize(width, 0);
float w = view.getPreferredSpan(View.X_AXIS);
float h = view.getPreferredSpan(View.Y_AXIS);
label.setPreferredSize(new Dimension((int) Math.ceil(w), (int) Math.ceil(h)));
}
}
return label;
}
protected JComponent createTextComponent(String text, int maxll) {
if (maxll > 100000) {
maxll = 80;
}
if (text.length() < maxll) {
return new JLabel(text, JLabel.LEADING);
} else {
AquaOptionPaneTextArea textArea = new AquaOptionPaneTextArea();
textArea.setEditable(false);
textArea.setOpaque(false);
textArea.setFocusable(false);
//textArea.setBackground(UIManager.getColor("Label.background"));
textArea.setFont(UIManager.getFont("Label.font"));
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
textArea.setText(text);
// The following is a pitiful attempt to get JTextArea to compute a plausible
// preferred size. It is better than nothing. Do not set columns, it just increases the
// preferred width.
int columnWidth = textArea.getFontMetrics(textArea.getFont()).charWidth('n');
textArea.setPreferredWidth(maxll * columnWidth);
return textArea;
}
}
private static class AquaOptionPaneTextArea
extends JTextArea
{
private int preferredWidth;
public AquaOptionPaneTextArea() {
}
public void setPreferredWidth(int pw) {
this.preferredWidth = pw;
}
@Override
public @NotNull Dimension getPreferredSize() {
if (preferredWidth > 0) {
setSize(preferredWidth, 10000);
}
return super.getPreferredSize();
}
}
/**
* Configures any necessary colors/fonts for the specified label
* used representing the message.
*/
protected void configureMessageLabel(JComponent label) {
Font messageFont = UIManager.getFont("OptionPane.messageFont", optionPane.getLocale());
if (messageFont != null) {
label.setFont(messageFont);
}
label.setOpaque(false);
label.setBackground(AquaColors.CLEAR);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy