VAqua.src.org.violetlib.aqua.AquaButtonUI 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!
/*
* Copyright (c) 2015-2024 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 java.awt.event.*;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.violetlib.aqua.AquaUtils.RecyclableSingleton;
import org.violetlib.aqua.AquaUtils.RecyclableSingletonFromDefaultConstructor;
import org.violetlib.jnr.aqua.AquaUIPainter;
import org.violetlib.jnr.aqua.AquaUIPainter.Size;
import org.violetlib.jnr.aqua.ButtonLayoutConfiguration;
import org.violetlib.jnr.aqua.LayoutConfiguration;
public class AquaButtonUI extends BasicButtonUI
implements AquaUtilControlSize.Sizeable, FocusRingOutlineProvider, ToolbarSensitiveUI, AquaComponentUI,
SystemPropertyChangeManager.SystemPropertyChangeListener {
// This UI is shared.
// Button borders may also be shared.
// All button configuration state must be in the button itself.
// Important programming note:
//
// Do not call b.getDisabledIcon() or b.getDisabledSelectedIcon().
// Use the static methods defined here instead.
public static final String BUTTON_TYPE = "JButton.buttonType";
public static final String SEGMENTED_BUTTON_POSITION = "JButton.segmentPosition";
public static final String SELECTED_STATE_KEY = "JButton.selectedState";
public static final String ENABLE_TRANSLUCENT_COLORS_KEY = "JButton.enableTranslucentColors";
public static final float OUTLINE_OFFSET = 0;
public static final float OUTLINE_CORNER = 9;
public static final String LAYOUT_CONFIGURATION_PROPERTY = "Aqua.Button.LayoutConfiguration";
public static final String DEFAULT_FONT_PROPERTY = "Aqua.Button.DefaultFont";
protected static final String COLOR_CHOOSER_OWNER_PROPERTY = "Aqua.Button.ColorChooserOwner";
protected static final String SPECIAL_ICON_PROPERTY = "Aqua.Button.SpecialIcon";
protected static final String CACHED_TOOLBAR_STATUS_PROPERTY = "Aqua.Button.IsToolbarButton";
protected static final RecyclableSingleton buttonUI = new RecyclableSingletonFromDefaultConstructor<>(AquaButtonUI.class);
private static boolean isConfiguring;
public static ComponentUI createUI(JComponent c) {
return buttonUI.get();
}
@Override
public void installUI(JComponent c) {
super.installUI(c);
removeCachedIcons((AbstractButton) c);
}
protected void installDefaults(@NotNull AbstractButton b) {
// load shared instance defaults
String pp = getPropertyPrefix();
setButtonMarginIfNeeded(b, UIManager.getInsets(pp + "margin"));
AquaUtils.installFont(b, pp + "font");
LookAndFeel.installProperty(b, "opaque", false);
b.putClientProperty(DEFAULT_FONT_PROPERTY, b.getFont());
initializeToolbarStatus(b);
configure(b);
configureFocusable(b);
}
@Override
public void systemPropertyChanged(JComponent c, Object type) {
if (type.equals(OSXSystemProperties.USER_PREFERENCE_CHANGE_TYPE)) {
configureFocusable(c);
}
}
private void configureFocusable(JComponent c) {
boolean isFocusable = OSXSystemProperties.isFullKeyboardAccessEnabled();
c.setFocusable(isFocusable);
}
@Override
public void appearanceChanged(@NotNull JComponent c, @NotNull AquaAppearance appearance) {
// Not needed: all requests for colors ensure the component appearance is up-to-date
}
@Override
public void activeStateChanged(@NotNull JComponent c, boolean isActive) {
// Repaint the button, since its border needs to handle the new state.
c.repaint();
}
/**
* Configure a button.
* @param b The button component to be configured.
*/
public void configure(@NotNull AbstractButton b) {
if (isConfiguring) {
// Avoid recursion caused by installing the default font as part of configuring the button
return;
}
isConfiguring = true;
try {
// The configuration of a button is rather complex. It potentially affects the border, the font, the
// foreground color, the minimum and preferred component sizes. The configuration potentially depends upon
// client properties (for button type, segment position, and size variant), whether the button is contained
// in a tool bar, whether the button is a member of a button group, whether the button has an icon or any
// child components (e.g. images). Instead of trying to figure out exactly which parts of the configuration
// depend upon which inputs, we do a complete reconfiguration whenever an input has potentially changed.
// Unfortunately, there are no notifications when a button is added to or removed from a button group.
// Button group membership potentially affects layout and painting, but only of JToggleButtons.
// Note that the choice of border does not depend upon the size variant, but the border may use the size
// variant as part of its configuration of the button. The border performs all of the configuration based on
// a defined button type.
boolean isToolbar = isToolbar(b);
AquaButtonExtendedTypes.TypeSpecifier type = AquaButtonExtendedTypes.getTypeSpecifier(b, isToolbar);
installBorder(b, type, isToolbar);
LayoutConfiguration g = null;
Border border = b.getBorder();
if (border instanceof AquaButtonBorder) {
AquaButtonBorder bb = (AquaButtonBorder) border;
g = bb.determineLayoutConfiguration(b);
if (bb.isRolloverEnabled(b)) {
LookAndFeel.installProperty(b, "rolloverEnabled", true);
}
int iconTextGap = bb.getIconTextGap(b);
LookAndFeel.installProperty(b, "iconTextGap", iconTextGap);
}
b.putClientProperty(LAYOUT_CONFIGURATION_PROPERTY, g);
// Perform configuration of the button based on the size variant, whether specified or implied.
// This may change the button font, foreground color, and layout sizes.
Size size = AquaUtilControlSize.getUserSizeFrom(b);
if (AquaUtilControlSize.isOKToInstallDefaultFont(b)) {
Font df = getFontForButton(b, size);
b.setFont(df);
}
updateTemplateIconStatus(b);
if (!isColorWell(b)) {
disconnectColorChooser(b);
}
b.setRequestFocusEnabled(false);
b.revalidate();
b.repaint();
} finally {
isConfiguring = false;
}
}
/**
* Invalidate the cached special icon if the template icon status has changed.
*/
protected void updateTemplateIconStatus(AbstractButton b) {
Object o = b.getClientProperty(SPECIAL_ICON_PROPERTY);
if (o instanceof AquaButtonIcon) {
AquaButtonIcon icon = (AquaButtonIcon) o;
boolean isTemplate = AquaButtonSupport.determineTemplateIconStatus(b);
if (icon.isTemplate != isTemplate) {
// Force a new icon to be created with the new status
removeCachedIcons(b);
}
}
}
/**
* Install the appropriate border for a button.
*/
protected void installBorder(AbstractButton b, AquaButtonExtendedTypes.TypeSpecifier type, boolean isToolbar) {
Border customBorder = type != null ? type.getBorder() : null;
if (customBorder != null) {
b.setBorder(customBorder);
} else {
Border oldBorder = b.getBorder();
if (oldBorder == null || oldBorder instanceof UIResource) {
Border border = getDefaultBorder(b, isToolbar);
if (border == null) {
border = new AquaPushButtonBorder();
}
b.setBorder(border);
}
}
}
/**
* Return a default border for a button component that does not specify any client properties that we understand.
* @param b The button component.
* @return the border to use for the button component.
*/
protected Border getDefaultBorder(@NotNull AbstractButton b, boolean isToolbar) {
if (isToolbar) {
if (b instanceof JToggleButton) {
return AquaButtonBorder.getToolBarToggleButtonBorder();
} else {
return AquaButtonBorder.getToolBarPushButtonBorder();
}
} else {
if (b instanceof JToggleButton) {
return AquaButtonBorder.getToggleButtonBorder();
} else {
return AquaButtonBorder.getPushButtonBorder();
}
}
}
@Override
public @Nullable Shape getFocusRingOutline(@NotNull JComponent c) {
Border border = c.getBorder();
if (border instanceof FocusRingOutlineProvider) {
FocusRingOutlineProvider bb = (FocusRingOutlineProvider) border;
return bb.getFocusRingOutline(c);
}
int width = c.getWidth();
int height = c.getHeight();
return new RoundRectangle2D.Double(OUTLINE_OFFSET, OUTLINE_OFFSET, width-2*OUTLINE_OFFSET, height-2*OUTLINE_OFFSET, OUTLINE_CORNER, OUTLINE_CORNER);
}
@Override
public void applySizeFor(JComponent c, Size size, boolean isDefaultSize) {
configure((AbstractButton) c);
}
/**
* Identify the font to use for a button when the application has not installed a font.
* The font may be chosen based on the button type and size.
*/
protected @NotNull Font getFontForButton(@NotNull AbstractButton b, @NotNull Size size) {
Font base = getDefaultFontPropertyValue(b);
if (base == null) {
// should not happen
base = new Font("Default", Font.PLAIN, 12);
}
Border border = b.getBorder();
if (border instanceof AquaButtonBorder) {
AquaButtonBorder bb = (AquaButtonBorder) border;
return bb.getCustomDefaultFont(b, size, base);
} else {
return AquaUtilControlSize.getFontForSize(base, size);
}
}
public static @Nullable Font getDefaultFontPropertyValue(@NotNull AbstractButton b) {
Object o = b.getClientProperty(DEFAULT_FONT_PROPERTY);
if (o instanceof Font) {
return (Font) o;
}
return null;
}
protected Color getDefaultForegroundColor(AbstractButton b) {
boolean isEnabled = b.getModel().isEnabled();
Color existingColor = b.getForeground();
if (existingColor == null || existingColor instanceof UIResource || !isEnabled) {
// Most buttons do not display text differently when the window is inactive
AquaAppearance appearance = AppearanceManager.ensureAppearance(b);
if (useSelectedForeground(b)) {
return appearance.getColor("alternateSelectedControlText");
} else if (useDisabledForeground(b)) {
return appearance.getColor("controlText_disabled");
} else {
return appearance.getColor("controlText");
}
}
return existingColor;
}
private boolean useSelectedForeground(AbstractButton b) {
if (b instanceof JToggleButton && !(b instanceof JCheckBox) && !(b instanceof JRadioButton)) {
return b.getModel().isSelected();
}
return false;
}
private boolean useDisabledForeground(AbstractButton b) {
return !b.getModel().isEnabled();
}
private void initializeToolbarStatus(@NotNull AbstractButton b) {
Boolean isToolbar = AquaUtils.isOnToolbar(b);
b.putClientProperty(CACHED_TOOLBAR_STATUS_PROPERTY, isToolbar);
}
@Override
public void toolbarStatusChanged(@NotNull JComponent c) {
AbstractButton b = (AbstractButton) c;
Boolean isToolbar = AquaUtils.isOnToolbar(b);
Object oldStatus = b.getClientProperty(CACHED_TOOLBAR_STATUS_PROPERTY);
if (!isToolbar.equals(oldStatus)) {
b.putClientProperty(CACHED_TOOLBAR_STATUS_PROPERTY, isToolbar);
configure(b);
}
}
protected void setButtonMarginIfNeeded(AbstractButton b, Insets insets) {
Insets margin = b.getMargin();
if (margin == null || (margin instanceof UIResource)) {
b.setMargin(insets);
}
}
protected void installListeners(AbstractButton b) {
AquaButtonListener listener = createButtonListener(b);
if (listener != null) {
// put the listener in the button's client properties so that
// we can get at it later
b.putClientProperty(this, listener);
b.addMouseListener(listener);
b.addMouseMotionListener(listener);
b.addFocusListener(listener);
b.addPropertyChangeListener(listener);
b.addChangeListener(listener);
b.addActionListener(listener);
b.addItemListener(listener);
}
if (isToolbarSensitive(b)) {
AquaUtils.installToolbarSensitivity(b);
}
AquaUtilControlSize.addSizePropertyListener(b);
OSXSystemProperties.register(b);
AppearanceManager.installListeners(b);
}
protected void installKeyboardActions(AbstractButton b) {
BasicButtonListener listener = (BasicButtonListener)b.getClientProperty(this);
if (listener != null) {
listener.installKeyboardActions(b);
}
}
public void uninstallUI(JComponent c) {
disconnectColorChooser((AbstractButton)c);
uninstallKeyboardActions((AbstractButton)c);
uninstallListeners((AbstractButton)c);
uninstallDefaults((AbstractButton)c);
removeCachedIcons((AbstractButton) c);
}
protected void uninstallKeyboardActions(AbstractButton b) {
BasicButtonListener listener = (BasicButtonListener)b.getClientProperty(this);
if (listener != null) listener.uninstallKeyboardActions(b);
}
protected void uninstallListeners(AbstractButton b) {
AppearanceManager.uninstallListeners(b);
AquaButtonListener listener = (AquaButtonListener)b.getClientProperty(this);
b.putClientProperty(this, null);
if (listener != null) {
b.removeItemListener(listener);
b.removeMouseListener(listener);
b.removeMouseMotionListener(listener);
b.removeFocusListener(listener);
b.removeChangeListener(listener);
b.removePropertyChangeListener(listener);
b.removeActionListener(listener);
}
AquaUtilControlSize.removeSizePropertyListener(b);
OSXSystemProperties.unregister(b);
AquaUtils.uninstallToolbarSensitivity(b);
}
protected void uninstallDefaults(AbstractButton b) {
LookAndFeel.uninstallBorder(b);
AquaUtilControlSize.uninstallDefaultFont(b);
}
protected void removeCachedIcons(AbstractButton b) {
if (b.getSelectedIcon() instanceof UIResource) {
b.setSelectedIcon(null);
}
if (AquaButtonSupport.getDisabledIcon(b) instanceof UIResource) {
b.setDisabledIcon(null);
}
if (AquaButtonSupport.getDisabledSelectedIcon(b) instanceof UIResource) {
b.setDisabledSelectedIcon(null);
}
if (b.getPressedIcon() instanceof UIResource) {
b.setPressedIcon(null);
}
if (b.getRolloverIcon() instanceof UIResource) {
b.setRolloverIcon(null);
}
if (b.getRolloverSelectedIcon() instanceof UIResource) {
b.setRolloverSelectedIcon(null);
}
b.putClientProperty(SPECIAL_ICON_PROPERTY, null);
}
// Create Listeners
protected AquaButtonListener createButtonListener(AbstractButton b) {
return new AquaButtonListener(b);
}
// Paint Methods
@Override
public void update(Graphics g, JComponent c) {
AppearanceManager.registerCurrentAppearance(c);
super.update(g, c);
}
public final void paint(Graphics g, JComponent c) {
paint((Graphics2D) g, (AbstractButton) c);
}
protected void paint(@NotNull Graphics2D g, @NotNull AbstractButton b) {
Rectangle viewRect = new Rectangle(b.getWidth(), b.getHeight());
AquaButtonBorder aquaBorder = null;
if (b.isBorderPainted()) {
Border border = b.getBorder();
if (border instanceof AquaButtonBorder) {
aquaBorder = (AquaButtonBorder) border;
}
}
Icon icon = getIcon(b);
if (aquaBorder != null) {
aquaBorder.paintButton(g, b, icon, viewRect);
} else {
paintButtonDefault(g, b, icon, viewRect);
}
}
/**
* Paint a button whose appearance is not defined by VAqua.
*/
protected void paintButtonDefault(@NotNull Graphics2D g,
@NotNull AbstractButton b,
@Nullable Icon icon,
@NotNull Rectangle viewRect) {
ButtonModel model = b.getModel();
boolean isColorWell = isColorWell(b);
if (isColorWell) {
// we are overdrawing here with translucent colors so we get
// a darkening effect. How can we avoid it. Try clear rect?
if (b.isOpaque()) {
g.setColor(b.getBackground());
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
}
}
int width = b.getWidth();
int height = b.getHeight();
if (b.isOpaque()) {
Insets i = b.getInsets();
viewRect.x = i.left - 2;
viewRect.y = i.top - 2;
viewRect.width = width - (i.right + viewRect.x) + 4;
viewRect.height = height - (i.bottom + viewRect.y) + 4;
if (b.isContentAreaFilled() || model.isSelected()) {
if (model.isSelected()) {
// Toggle buttons
g.setColor(b.getBackground().darker());
} else {
g.setColor(b.getBackground());
}
g.fillRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
}
}
Color textColor = getDefaultForegroundColor(b);
paintIconAndText(g, b, b.getInsets(), icon, textColor, viewRect, null);
}
public static void paintIconAndText(@NotNull Graphics2D g,
@NotNull AbstractButton b,
@NotNull Insets insets,
@Nullable Icon icon,
@NotNull Color textColor,
@NotNull Rectangle viewRect,
@Nullable Dimension iconSize) {
Rectangle iconRect = new Rectangle();
Rectangle textRect = new Rectangle();
String text = AquaButtonUI.layoutAndGetText(g, b, insets, viewRect, iconRect, textRect, iconSize);
if (icon != null) {
paintIcon(g, b, icon, iconRect);
}
paintText(g, b, textRect, textColor, text);
}
public static @NotNull String layoutAndGetText(@Nullable Graphics2D g,
@NotNull AbstractButton b,
@NotNull Insets i,
@NotNull Rectangle viewRect,
@NotNull Rectangle iconRect,
@NotNull Rectangle textRect,
@Nullable Dimension iconSize) {
// re-initialize the view rect to the selected insets
viewRect.x = i.left;
viewRect.y = i.top;
viewRect.width = b.getWidth() - (i.right + viewRect.x);
viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
// reset the text and icon rects
textRect.x = textRect.y = textRect.width = textRect.height = 0;
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
// setup the font metrics
// If no graphics context is provided, the request is to determine the icon location under the
// assumption that the text does not matter.
FontMetrics fm = null;
if (g != null) {
g.setFont(b.getFont());
fm = g.getFontMetrics();
}
// layout the text and icon
String originalText = b.getText();
if (iconSize == null) {
Icon icon = b.getIcon();
if (icon != null) {
iconSize = new Dimension(icon.getIconWidth(), icon.getIconHeight());
}
}
String text = AquaUtils.layoutCompoundLabel(b, fm, originalText, iconSize, b.getVerticalAlignment(),
b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect, originalText == null ? 0 : b.getIconTextGap());
return text;
}
/**
* Paint the icon.
*/
public static void paintIcon(Graphics2D g, @NotNull AbstractButton b, @NotNull Icon icon, Rectangle iconRect) {
Graphics2D gg = null;
if (icon.getIconWidth() != iconRect.width || icon.getIconHeight() != iconRect.height) {
gg = (Graphics2D) g.create();
g = gg;
gg.translate(iconRect.x, iconRect.y);
gg.scale(iconRect.getWidth() / icon.getIconWidth(), iconRect.getHeight() / icon.getIconHeight());
gg.translate(-iconRect.x, -iconRect.y);
}
icon.paintIcon(b, g, iconRect.x, iconRect.y);
if (gg != null) {
gg.dispose();
}
}
/**
* Paint the text.
*/
public static void paintText(@NotNull Graphics2D g,
@NotNull AbstractButton b,
@NotNull Rectangle textRect,
@NotNull Color textColor,
@NotNull String text) {
if (!text.isEmpty()) {
if (textRect.width == 0) {
textRect.width = 50;
}
Object o = b.getClientProperty(BasicHTML.propertyKey);
if (o instanceof View) {
View v = (View) o;
v.paint(g, textRect);
} else {
FontMetrics fm = g.getFontMetrics();
int mnemonicIndex = AquaMnemonicHandler.isMnemonicHidden() ? -1 : b.getDisplayedMnemonicIndex();
g.setColor(textColor);
int x = textRect.x;
int y = textRect.y + fm.getAscent();
JavaSupport.drawStringUnderlineCharAt(b, g, text, mnemonicIndex, x, y);
}
}
}
@Override
protected final void paintText(Graphics g, AbstractButton b, Rectangle localTextRect, String text) {
throw new UnsupportedOperationException("This method should not be used");
}
@Override
protected final void paintText(Graphics g, JComponent c, Rectangle localTextRect, String text) {
throw new UnsupportedOperationException("This method should not be used");
}
/**
* Obtain the icon to use based on the button state.
*/
protected @Nullable Icon getIcon(AbstractButton b) {
ButtonIconState bs = getIconState(b);
Icon definedIcon = getDefinedIcon(b, bs);
if (definedIcon != null && bs != ButtonIconState.DEFAULT) {
return definedIcon;
}
// The special icon creates state specific renderings based on the button default icon.
Icon icon = getSpecialIcon(b);
if (icon != null) {
return icon;
}
if (definedIcon != null) {
return definedIcon;
}
Icon ic = b.getIcon();
if (ic != null) {
return AquaButtonSupport.createIcon(b);
}
return null;
}
/**
* Button states that can have different icons in Swing.
*/
enum ButtonIconState {
DISABLED,
DISABLED_SELECTED,
PRESSED,
ROLLOVER,
ROLLOVER_SELECTED,
SELECTED,
DEFAULT
}
public static boolean isColorWell(@NotNull AbstractButton b) {
AquaUIPainter.ButtonWidget widget = getButtonWidget(b);
return widget == AquaUIPainter.ButtonWidget.BUTTON_COLOR_WELL;
}
private static @Nullable AquaUIPainter.ButtonWidget getButtonWidget(AbstractButton b) {
Object o = b.getClientProperty(LAYOUT_CONFIGURATION_PROPERTY);
if (o instanceof ButtonLayoutConfiguration) {
ButtonLayoutConfiguration bg = (ButtonLayoutConfiguration) o;
return bg.getButtonWidget();
}
return null;
}
/**
* Return a value that can be used to select an appropriate icon based on the current state of the specified button.
* @param b The button.
* @return the button icon state.
*/
public static @NotNull ButtonIconState getIconState(AbstractButton b) {
ButtonModel model = b.getModel();
if (!model.isEnabled()) {
if (model.isSelected()) {
return ButtonIconState.DISABLED_SELECTED;
} else {
return ButtonIconState.DISABLED;
}
} else if (model.isPressed() && model.isArmed()) {
return ButtonIconState.PRESSED;
} else if (b.isRolloverEnabled() && model.isRollover()) {
if (model.isSelected()) {
return ButtonIconState.ROLLOVER_SELECTED;
} else {
return ButtonIconState.ROLLOVER;
}
} else if (model.isSelected()) {
return ButtonIconState.SELECTED;
} else {
return ButtonIconState.DEFAULT;
}
}
/**
* Obtain the explicitly defined icon for a button based on the specified button state.
* If the button uses this class for its UI, then this method will not install any default icons on the button.
* @param b The button.
* @return the icon defined on that button for the specified button state (may return null).
*/
public static @Nullable Icon getDefinedIcon(AbstractButton b, ButtonIconState bs) {
switch (bs) {
case DISABLED: return AquaButtonSupport.getDisabledIcon(b);
case DISABLED_SELECTED: return AquaButtonSupport.getDisabledSelectedIcon(b);
case PRESSED: return getIconForPressedState(b);
case ROLLOVER_SELECTED: return getIconForRolloverSelectedState(b);
case ROLLOVER: return b.getRolloverIcon();
case SELECTED: return b.getSelectedIcon();
default: return b.getIcon();
}
}
private static @Nullable Icon getIconForPressedState(@NotNull AbstractButton b) {
Icon icon = b.getPressedIcon();
return icon != null ? icon : b.getSelectedIcon();
}
private static @Nullable Icon getIconForRolloverSelectedState(@NotNull AbstractButton b) {
Icon icon = b.getRolloverSelectedIcon();
return icon != null ? icon : b.getSelectedIcon();
}
/**
* Obtain a special icon to use for a button. The special icon rendering may be context dependent.
* This method should not be called unless the button has an icon.
* @param b The button.
* @return the special icon for the button, or null if no special icon is defined for the button.
*/
protected @Nullable AquaButtonIcon getSpecialIcon(@NotNull AbstractButton b) {
Object o = b.getClientProperty(SPECIAL_ICON_PROPERTY);
if (o instanceof AquaButtonIcon) {
return (AquaButtonIcon) o;
}
Border border = b.getBorder();
if (border instanceof AquaButtonBorder) {
AquaButtonBorder bb = (AquaButtonBorder) border;
boolean isTemplate = AquaButtonSupport.determineTemplateIconStatus(b);
AquaButtonIcon icon = bb.createIcon(b, isTemplate);
b.putClientProperty(SPECIAL_ICON_PROPERTY, icon);
return icon;
}
return null;
}
/**
* This method is called by AbstractButton via the LAF to obtain an icon to use when a button is disabled.
* @param b The button.
* @param source The button icon.
* @return the icon to use.
*/
public @NotNull Icon createDisabledIcon(AbstractButton b, ImageIcon source) {
AquaButtonIcon specialIcon = getSpecialIcon(b);
if (specialIcon != null) {
return specialIcon;
}
return createDefaultDisabledIcon(source);
}
/**
* This method is called by AbstractButton via the LAF to obtain an icon to use when a button is disabled and
* selected.
* @param b The button.
* @param source The button selected icon.
* @return the icon to use.
*/
public @NotNull Icon createDisabledSelectedIcon(AbstractButton b, ImageIcon source) {
AquaButtonIcon specialIcon = getSpecialIcon(b);
if (specialIcon != null) {
return specialIcon;
}
return createDefaultDisabledIcon(source);
}
protected @NotNull ImageIcon createDefaultDisabledIcon(ImageIcon source) {
return new ImageIconUIResource(AquaImageFactory.getProcessedImage(source.getImage(), AquaImageFactory.LIGHTEN_FOR_DISABLED));
}
protected void paintButtonPressed(Graphics g, AbstractButton b) {
paint(g, b);
}
// Layout Methods
public Dimension getMinimumSize(JComponent c) {
AbstractButton b = (AbstractButton) c;
Border border = b.getBorder();
Dimension d;
if (border instanceof AquaButtonBorder) {
AquaButtonBorder bb = (AquaButtonBorder) border;
d = bb.getPreferredButtonSize(b);
} else {
d = getPreferredSize(b);
}
View v = (View)c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
}
return d;
}
public Dimension getPreferredSize(JComponent c) {
AbstractButton b = (AbstractButton) c;
Border border = b.getBorder();
if (border instanceof AquaButtonBorder) {
AquaButtonBorder bb = (AquaButtonBorder) border;
return bb.getPreferredButtonSize(b);
} else {
return AquaButtonSupport.getPreferredButtonSize(b, b.getIconTextGap(), null);
}
}
public Dimension getMaximumSize(JComponent c) {
Dimension d = getPreferredSize(c);
View v = (View)c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
}
return d;
}
private void toggleButtonStateChanged(@NotNull AbstractButton b) {
JToggleButton leftButton = SegmentedControlModel.getLeftAdjacentButton(b);
if (leftButton != null) {
leftButton.repaint();
}
}
public boolean isToolbar(@NotNull AbstractButton b) {
return Boolean.TRUE.equals(b.getClientProperty(CACHED_TOOLBAR_STATUS_PROPERTY));
}
private boolean isToolbarSensitive(@NotNull AbstractButton b) {
// Checkboxes and radio buttons are not toolbar sensitive
return b instanceof JButton
|| b instanceof JToggleButton && !(b instanceof JCheckBox) && !(b instanceof JRadioButton);
}
class AquaButtonListener extends BasicButtonListener implements ActionListener, ItemListener {
protected AbstractButton b;
public AquaButtonListener(AbstractButton b) {
super(b);
this.b = b;
}
@Override
public void actionPerformed(ActionEvent e) {
// If the button is a color well and no other action listeners are defined, bring up a color chooser.
if (isColorWell(b)) {
ActionListener[] listeners = b.getActionListeners();
if (listeners.length == 1) {
toggleColorChooser(b);
}
}
}
@Override
public void itemStateChanged(ItemEvent e) {
toggleButtonStateChanged(b);
}
public void focusGained(FocusEvent e) {
((Component)e.getSource()).repaint();
}
public void focusLost(FocusEvent e) {
// 10-06-03 VL: [Radar 3187049]
// If focusLost arrives while the button has been left-clicked this would disarm the button,
// causing actionPerformed not to fire on mouse release!
//b.getModel().setArmed(false);
b.getModel().setPressed(false);
((Component)e.getSource()).repaint();
}
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
String propertyName = e.getPropertyName();
if ("icon".equals(propertyName) || "text".equals(propertyName)) {
configure(b);
return;
}
if (propertyName != null && !propertyName.contains(".") && propertyName.endsWith("Icon")) {
updateTemplateIconStatus(b);
return;
}
if (BUTTON_TYPE.equals(propertyName)) {
configure(b);
return;
}
if (SEGMENTED_BUTTON_POSITION.equals(propertyName)) {
configure(b);
return;
}
if (AbstractButton.VERTICAL_ALIGNMENT_CHANGED_PROPERTY.equals(propertyName)
|| AbstractButton.VERTICAL_TEXT_POSITION_CHANGED_PROPERTY.equals(propertyName)
|| "font".equals(propertyName)) {
// A change to the preferred content height can change the selected button widget
configure(b);
return;
}
if ("componentOrientation".equals(propertyName)) {
Border border = b.getBorder();
if (border instanceof AquaSegmentedButtonBorder) {
configure(b);
}
}
if ("ancestor".equals(propertyName)) {
if (!b.isDisplayable()) {
disconnectColorChooser(b);
}
}
}
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) ) {
AbstractButton b = (AbstractButton) e.getSource();
if (b.contains(e.getX(), e.getY())) {
Object data = willHandleButtonPress(b);
super.mousePressed(e);
didHandleButtonPress(b, data);
}
}
}
}
protected Object willHandleButtonPress(AbstractButton b) {
return null;
}
protected void didHandleButtonPress(AbstractButton b, Object data) {
}
protected void disconnectColorChooser(AbstractButton b)
{
Object o = b.getClientProperty(COLOR_CHOOSER_OWNER_PROPERTY);
if (o instanceof SharedColorChooserOwner) {
SharedColorChooserOwner owner = (SharedColorChooserOwner) o;
AquaSharedColorChooser.disconnect(owner);
b.putClientProperty(COLOR_CHOOSER_OWNER_PROPERTY, null);
}
}
protected void toggleColorChooser(AbstractButton b) {
Object o = b.getClientProperty(COLOR_CHOOSER_OWNER_PROPERTY);
if (o instanceof SharedColorChooserOwner) {
SharedColorChooserOwner owner = (SharedColorChooserOwner) o;
AquaSharedColorChooser.disconnect(owner);
b.setSelected(false);
b.putClientProperty(COLOR_CHOOSER_OWNER_PROPERTY, null);
return;
}
SharedColorChooserOwner owner = new SharedColorChooserOwner() {
@Override
public void applyColor(Color c) {
b.setBackground(c);
}
@Override
public void disconnected() {
b.setSelected(false);
b.putClientProperty(COLOR_CHOOSER_OWNER_PROPERTY, null);
}
};
Color c = b.getBackground();
boolean enableTranslucentColors = Boolean.TRUE.equals(b.getClientProperty(ENABLE_TRANSLUCENT_COLORS_KEY));
if (AquaSharedColorChooser.connect(owner, c, enableTranslucentColors)) {
b.putClientProperty(COLOR_CHOOSER_OWNER_PROPERTY, owner);
b.setSelected(true);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy