2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Substance Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.substance.api.shaper;
32
import java.awt.Component;
33
import java.awt.Dimension;
34
import java.awt.Insets;
35
import java.awt.geom.GeneralPath;
38
import javax.swing.AbstractButton;
39
import javax.swing.Icon;
40
import javax.swing.JComponent;
41
import javax.swing.border.Border;
43
import org.pushingpixels.substance.api.SubstanceConstants;
44
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
45
import org.pushingpixels.substance.internal.utils.HashMapKey;
46
import org.pushingpixels.substance.internal.utils.LazyResettableHashMap;
47
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
48
import org.pushingpixels.substance.internal.utils.SubstanceInternalArrowButton;
49
import org.pushingpixels.substance.internal.utils.SubstanceOutlineUtilities;
50
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
51
import org.pushingpixels.substance.internal.utils.border.SubstanceBorder;
52
import org.pushingpixels.substance.internal.utils.border.SubstanceButtonBorder;
55
* Button shaper that returns buttons with completely rounded corners (ala Mac
56
* 10.4). This class is part of officially supported API.
58
* @author Kirill Grouchnikov
60
public class StandardButtonShaper implements SubstanceButtonShaper,
61
RectangularButtonShaper {
63
* Cache of already computed contours.
65
private final static LazyResettableHashMap<GeneralPath> contours = new LazyResettableHashMap<GeneralPath>(
66
"StandardButtonShaper");
72
* org.pushingpixels.substance.button.SubstanceButtonShaper#getDisplayName()
75
public String getDisplayName() {
83
* org.pushingpixels.substance.shaper.SubstanceButtonShaper#getButtonOutline
84
* (javax .swing.AbstractButton, java.awt.Insets, int, int, boolean)
87
public GeneralPath getButtonOutline(AbstractButton button, Insets insets,
88
int width, int height, boolean isInner) {
89
Set<SubstanceConstants.Side> straightSides = SubstanceCoreUtilities
90
.getSides(button, SubstanceLookAndFeel.BUTTON_SIDE_PROPERTY);
92
float radius = this.getCornerRadius(button, insets);
94
radius -= (int) SubstanceSizeUtils
95
.getBorderStrokeWidth(SubstanceSizeUtils
96
.getComponentFontSize(button));
101
HashMapKey key = SubstanceCoreUtilities.getHashKey(width, height,
102
straightSides, radius, insets);
104
GeneralPath result = contours.get(key);
105
if (result != null) {
109
result = SubstanceOutlineUtilities.getBaseOutline(width, height,
110
radius, straightSides, insets);
111
contours.put(key, result);
119
* org.pushingpixels.substance.button.SubstanceButtonShaper#getButtonBorder
120
* (javax .swing.AbstractButton)
123
public Border getButtonBorder(final AbstractButton button) {
124
return new SubstanceButtonBorder(StandardButtonShaper.class) {
126
public Insets getBorderInsets(Component c) {
127
int fontSize = SubstanceSizeUtils.getComponentFontSize(button);
128
Insets buttonInsets = SubstanceSizeUtils
129
.getButtonInsets(fontSize);
130
int focusPadding = SubstanceSizeUtils
131
.getFocusRingPadding(fontSize);
132
int lrPadding = SubstanceCoreUtilities.hasText(button) ? SubstanceSizeUtils
133
.getTextButtonLRPadding(fontSize)
135
Set<SubstanceConstants.Side> openSides = SubstanceCoreUtilities
137
SubstanceLookAndFeel.BUTTON_OPEN_SIDE_PROPERTY);
141
+ ((openSides != null)
143
.contains(SubstanceConstants.Side.LEFT) ? -1
145
int right = lrPadding
148
+ ((openSides != null)
150
.contains(SubstanceConstants.Side.RIGHT) ? -1
152
int top = buttonInsets.top
153
+ ((openSides != null)
155
.contains(SubstanceConstants.Side.TOP) ? -1
157
int bottom = buttonInsets.bottom
158
+ ((openSides != null)
160
.contains(SubstanceConstants.Side.BOTTOM) ? -1
162
return new Insets(top, left, bottom, right);
171
* org.pushingpixels.substance.button.SubstanceButtonShaper#getPreferredSize
172
* (javax .swing.AbstractButton, java.awt.Dimension)
175
public Dimension getPreferredSize(AbstractButton button,
176
Dimension uiPreferredSize) {
178
boolean toTweakWidth = false;
179
boolean toTweakHeight = false;
181
Icon icon = button.getIcon();
182
boolean hasIcon = SubstanceCoreUtilities.hasIcon(button);
183
boolean hasText = SubstanceCoreUtilities.hasText(button);
184
Insets margin = button.getMargin();
186
result = uiPreferredSize;
188
boolean hasNoMinSizeProperty = SubstanceCoreUtilities
189
.hasNoMinSizeProperty(button);
190
if ((!hasNoMinSizeProperty) && hasText) {
191
int baseWidth = uiPreferredSize.width;
192
baseWidth = Math.max(baseWidth + uiPreferredSize.height,
193
SubstanceSizeUtils.getMinButtonWidth(SubstanceSizeUtils
194
.getComponentFontSize(button)));
195
// if (baseWidth < DEFAULT_WIDTH) {
196
// baseWidth = DEFAULT_WIDTH;
198
result = new Dimension(baseWidth, uiPreferredSize.height);
199
int baseHeight = result.height;
200
// baseHeight = Math.max(baseHeight, SubstanceSizeUtils
201
// .getMinButtonHeight(SubstanceSizeUtils
202
// .getComponentFontSize(button)));
203
result = new Dimension(result.width, baseHeight);
205
if (hasNoMinSizeProperty) {
206
if (margin != null) {
207
result = new Dimension(result.width + margin.left
208
+ margin.right, result.height + margin.top
214
int extraPadding = SubstanceSizeUtils
215
.getExtraPadding(SubstanceSizeUtils
216
.getComponentFontSize(button));
217
int iconPaddingWidth = 6 + 2 * extraPadding;
218
int iconPaddingHeight = 6 + 2 * extraPadding;
219
if (margin != null) {
220
iconPaddingWidth = Math.max(iconPaddingWidth, margin.left
222
iconPaddingHeight = Math.max(iconPaddingHeight, margin.top
226
// check the icon height
227
int iconHeight = icon.getIconHeight();
228
if (iconHeight > (result.getHeight() - iconPaddingHeight)) {
229
result = new Dimension(result.width, iconHeight);
230
toTweakHeight = true;
232
int iconWidth = icon.getIconWidth();
233
if (iconWidth > (result.getWidth() - iconPaddingWidth)) {
234
result = new Dimension(iconWidth, result.height);
239
if (SubstanceCoreUtilities.isScrollBarButton(button)) {
240
toTweakWidth = false;
241
toTweakHeight = false;
245
result = new Dimension(result.width + iconPaddingWidth,
249
result = new Dimension(result.width, result.height
250
+ iconPaddingHeight);
253
if (result.height % 2 != 0)
260
* Returns indication whether the specified button should be drawn with
261
* completely round corners.
265
* @return <code>true</code> if the specified button should be drawn with
266
* completely round corners, <code>false</code> otherwise.
268
public static boolean isRoundButton(AbstractButton button) {
269
return (!SubstanceCoreUtilities.isComboBoxButton(button))
270
&& (!SubstanceCoreUtilities.isScrollButton(button))
271
&& SubstanceCoreUtilities.hasText(button);
278
* org.pushingpixels.substance.button.SubstanceButtonShaper#isProportionate
282
public boolean isProportionate() {
290
* org.pushingpixels.substance.shaper.RectangularButtonShaper#getCornerRadius
291
* (javax .swing.JComponent, java.awt.Insets)
294
public float getCornerRadius(AbstractButton button, Insets insets) {
295
int width = button.getWidth();
296
int height = button.getHeight();
298
boolean isRoundCorners = isRoundButton(button);
299
float radius = SubstanceSizeUtils
300
.getClassicButtonCornerRadius(SubstanceSizeUtils
301
.getComponentFontSize(button));
302
if (button instanceof SubstanceInternalArrowButton) {
303
Border parentBorder = ((JComponent) button.getParent()).getBorder();
304
if (parentBorder instanceof SubstanceBorder) {
305
radius *= ((SubstanceBorder) parentBorder)
306
.getRadiusScaleFactor();
310
if (insets != null) {
311
width -= (insets.left + insets.right);
312
height -= (insets.top + insets.bottom);
314
if (isRoundCorners) {
315
if (width > height) {
316
radius = (height) / 2.0f;
318
radius = (width) / 2.0f;
322
if (SubstanceCoreUtilities.isToolBarButton(button)) {
323
radius = SubstanceCoreUtilities.getToolbarButtonCornerRadius(