Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonBorder.java
41154 views
/*1* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.apple.laf;2627import java.awt.*;2829import javax.swing.*;30import javax.swing.border.Border;31import javax.swing.plaf.*;3233import apple.laf.JRSUIConstants.*;3435import com.apple.laf.AquaUtilControlSize.*;36import com.apple.laf.AquaUtils.*;3738public abstract class AquaButtonBorder extends AquaBorder implements Border, UIResource {39private static final RecyclableSingleton<Dynamic> fDynamic = new RecyclableSingletonFromDefaultConstructor<Dynamic>(Dynamic.class);40public static AquaButtonBorder getDynamicButtonBorder() {41return fDynamic.get();42}4344private static final RecyclableSingleton<Toggle> fToggle = new RecyclableSingletonFromDefaultConstructor<Toggle>(Toggle.class);45public static AquaButtonBorder getToggleButtonBorder() {46return fToggle.get();47}4849private static final RecyclableSingleton<Toolbar> fToolBar = new RecyclableSingletonFromDefaultConstructor<Toolbar>(Toolbar.class);50public static Border getToolBarButtonBorder() {51return fToolBar.get();52}5354private static final RecyclableSingleton<Named> fBevel = new RecyclableSingleton<Named>() {55protected Named getInstance() {56return new Named(Widget.BUTTON_BEVEL, new SizeDescriptor(new SizeVariant().alterMargins(2, 4, 2, 4)));57}58};59public static AquaButtonBorder getBevelButtonBorder() {60return fBevel.get();61}6263public AquaButtonBorder(final SizeDescriptor sizeDescriptor) {64super(sizeDescriptor);65}6667public AquaButtonBorder(final AquaButtonBorder other) {68super(other);69}7071public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {72// for now we don't paint a border. We let the button paint it since there73// needs to be a strict ordering for aqua components.74//paintButton(c, g, x, y, width, height);75}7677public void paintButton(final Component c, final Graphics g, int x, int y, int width, int height) {78final AbstractButton b = (AbstractButton)c;79final ButtonModel model = b.getModel();8081final State state = getButtonState(b, model);82painter.state.set(state);83painter.state.set((state != State.DISABLED && state != State.INACTIVE) && b.isFocusPainted() && isFocused(b) ? Focused.YES : Focused.NO);8485// Full border size of the component.86// g.setColor(new Color(0, 255, 0, 70));87// g.drawRect(x, y, width - 1, height - 1);8889final Insets subInsets = sizeVariant.insets;90x += subInsets.left;91y += subInsets.top;92width -= (subInsets.left + subInsets.right);93height -= (subInsets.top + subInsets.bottom);9495// Where the native border should start to paint.96// g.setColor(new Color(255, 0, 255, 70));97// g.drawRect(x, y, width - 1, height - 1);9899doButtonPaint(b, model, g, x, y, width, height);100}101102protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int width, final int height) {103painter.paint(g, b, x, y, width, height);104}105106protected State getButtonState(final AbstractButton b, final ButtonModel model) {107if (!b.isEnabled()) return State.DISABLED;108109// The default button shouldn't draw its color when the window is inactive.110// Changed for <rdar://problem/3614421>: Aqua LAF Buttons are incorrectly drawn disabled111// all we need to do is make sure we aren't the default button any more and that112// we aren't active, but we still are enabled if the button is enabled.113// if we set dimmed we would appear disabled despite being enabled and click through114// works so this now matches the text drawing and most importantly the HIG115if (!AquaFocusHandler.isActive(b)) return State.INACTIVE;116117if (model.isArmed() && model.isPressed()) return State.PRESSED;118if (model.isSelected() && isSelectionPressing()) return State.PRESSED;119if ((b instanceof JButton) && ((JButton)b).isDefaultButton()) return State.PULSED;120121return State.ACTIVE;122}123124protected boolean isSelectionPressing() {125return true;126}127128public boolean hasSmallerInsets(final JComponent c) {129final Insets inset = c.getInsets();130final Insets margin = sizeVariant.margins;131132if (margin.equals(inset)) return false;133134return (135(inset.top < margin.top) ||136(inset.left < margin.left) ||137(inset.right < margin.right) ||138(inset.bottom < margin.bottom)139);140}141142/**143* Returns the insets of the border.144* @param c the component for which this border insets value applies145*/146public Insets getBorderInsets(final Component c) {147if (c == null || !(c instanceof AbstractButton)) return new Insets(0, 0, 0, 0);148149Insets margin = ((AbstractButton)c).getMargin();150margin = (margin == null) ? new InsetsUIResource(0, 0, 0, 0) : (Insets)margin.clone();151152margin.top += sizeVariant.margins.top;153margin.bottom += sizeVariant.margins.bottom;154margin.left += sizeVariant.margins.left;155margin.right += sizeVariant.margins.right;156157return margin;158}159160public Insets getContentInsets(final AbstractButton b, final int w, final int h) {161return null;162}163164public void alterPreferredSize(final Dimension d) {165if (sizeVariant.h > 0 && sizeVariant.h > d.height) d.height = sizeVariant.h;166if (sizeVariant.w > 0 && sizeVariant.w > d.width) d.width = sizeVariant.w;167}168169/**170* Returns whether or not the border is opaque. If the border171* is opaque, it is responsible for filling in it's own172* background when painting.173*/174public boolean isBorderOpaque() {175return false;176}177178static class SizeConstants {179protected static final int fNormalButtonHeight = 29;180protected static final int fNormalMinButtonWidth = 40;181protected static final int fSquareButtonHeightThreshold = 23;182protected static final int fSquareButtonWidthThreshold = 16;183}184185public static class Dynamic extends AquaButtonBorder {186final Insets ALTERNATE_PUSH_INSETS = new Insets(3, 12, 5, 12);187final Insets ALTERNATE_BEVEL_INSETS = new Insets(0, 5, 0, 5);188final Insets ALTERNATE_SQUARE_INSETS = new Insets(0, 2, 0, 2);189public Dynamic() {190super(new SizeDescriptor(new SizeVariant(75, 29).alterMargins(3, 20, 5, 20)) {191public SizeVariant deriveSmall(final SizeVariant v) {192return super.deriveSmall(v.alterMinSize(0, -2).alterMargins(0, -3, 0, -3).alterInsets(-3, -3, -4, -3));193}194public SizeVariant deriveMini(final SizeVariant v) {195return super.deriveMini(v.alterMinSize(0, -2).alterMargins(0, -3, 0, -3).alterInsets(-3, -3, -1, -3));196}197});198}199200public Dynamic(final Dynamic other) {201super(other);202}203204protected State getButtonState(final AbstractButton b, final ButtonModel model) {205final State state = super.getButtonState(b, model);206painter.state.set(state == State.PULSED ? Animating.YES : Animating.NO);207return state;208}209210public Insets getContentInsets(final AbstractButton b, final int width, final int height) {211final Size size = AquaUtilControlSize.getUserSizeFrom(b);212final Widget style = getStyleForSize(b, size, width, height);213214if (style == Widget.BUTTON_PUSH) {215return ALTERNATE_PUSH_INSETS;216}217if (style == Widget.BUTTON_BEVEL_ROUND) {218return ALTERNATE_BEVEL_INSETS;219}220if (style == Widget.BUTTON_BEVEL) {221return ALTERNATE_SQUARE_INSETS;222}223224return null;225}226227protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, int x, int y, int width, int height) {228final Size size = AquaUtilControlSize.getUserSizeFrom(b);229painter.state.set(size);230231final Widget style = getStyleForSize(b, size, width, height);232painter.state.set(style);233234// custom adjusting235if (style == Widget.BUTTON_PUSH && y % 2 == 0) {236if (size == Size.REGULAR) { y += 1; height -= 1; }237if (size == Size.MINI) { height -= 1; x += 4; width -= 8; }238}239240super.doButtonPaint(b, model, g, x, y, width, height);241}242243protected Widget getStyleForSize(final AbstractButton b, final Size size, final int width, final int height) {244if (size != null && size != Size.REGULAR) {245return Widget.BUTTON_PUSH;246}247248if (height < SizeConstants.fSquareButtonHeightThreshold || width < SizeConstants.fSquareButtonWidthThreshold) {249return Widget.BUTTON_BEVEL;250}251252if (height <= SizeConstants.fNormalButtonHeight + 3 && width < SizeConstants.fNormalMinButtonWidth) {253return Widget.BUTTON_BEVEL;254}255256if ((height > SizeConstants.fNormalButtonHeight + 3) || (b.getIcon() != null) || hasSmallerInsets(b)){257return Widget.BUTTON_BEVEL_ROUND;258}259260return Widget.BUTTON_PUSH;261}262}263264public static class Toggle extends AquaButtonBorder {265public Toggle() {266super(new SizeDescriptor(new SizeVariant().alterMargins(6, 6, 6, 6)));267}268269public Toggle(final Toggle other) {270super(other);271}272273protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int width, final int height) {274if (height < SizeConstants.fSquareButtonHeightThreshold || width < SizeConstants.fSquareButtonWidthThreshold) {275painter.state.set(Widget.BUTTON_BEVEL);276super.doButtonPaint(b, model, g, x, y, width, height);277return;278}279280painter.state.set(Widget.BUTTON_BEVEL_ROUND);281super.doButtonPaint(b, model, g, x, y + 1, width, height - 1);282}283}284285public static class Named extends AquaButtonBorder {286public Named(final Widget widget, final SizeDescriptor sizeDescriptor) {287super(sizeDescriptor);288painter.state.set(widget);289}290291// called by reflection292public Named(final Named sizeDescriptor) {293super(sizeDescriptor);294}295296protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int width, final int height) {297painter.state.set(model.isSelected() ? BooleanValue.YES : BooleanValue.NO);298super.doButtonPaint(b, model, g, x, y, width, height);299}300}301302public static class Toolbar extends AquaButtonBorder {303public Toolbar() {304super(new SizeDescriptor(new SizeVariant().alterMargins(5, 5, 5, 5)));305painter.state.set(Widget.TOOLBAR_ITEM_WELL);306}307308public Toolbar(final Toolbar other) {309super(other);310}311312protected void doButtonPaint(final AbstractButton b, final ButtonModel model, final Graphics g, final int x, final int y, final int w, final int h) {313if (!model.isSelected()) return; // only paint when the toolbar button is selected314super.doButtonPaint(b, model, g, x, y, w, h);315}316}317}318319320