File: EPTabPainter.java

package info (click to toggle)
mac-widgets 0.10.0%2Bsvn416-dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,968 kB
  • ctags: 2,003
  • sloc: java: 9,909; makefile: 13; sh: 12
file content (237 lines) | stat: -rw-r--r-- 11,529 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package com.explodingpixels.widgets.plaf;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.GeneralPath;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicGraphicsUtils;

public class EPTabPainter {

    private CloseButtonLocation fCloseButtonLocation = CloseButtonLocation.LEFT;
    private CloseButtonIcon fCloseButtonIcon = new DefaultCloseButtonIcon();

    private static final int CONTENT_DISTANCE_FROM_EDGE = 5;
    private static final int CLOSE_BUTTON_DISTANCE_FROM_EDGE = 5;
    private static final int CLOSE_BUTTON_DISTANCE_FROM_CONTENT = 3;

    // create single variable to reuse when layout the tab. this reuse of rectangle is also seen in BasicLabelUI and
    // was used there for performance gains -- we're following that example, though it's unclear whether or not there
    // are still performance gains to be had by doing this.
    private static final Rectangle ADJUSTED_TAB_BOUNDS = new Rectangle();
    private static final Rectangle TEXT_BOUNDS = new Rectangle();
    private static final Rectangle ICON_BOUNDS = new Rectangle();

    // the colors used to draw the tab backround and border.
    static final Color SELECTED_BORDER_COLOR = new Color(0x666666);
    private static final Color UNSELECTED_BORDER_COLOR = new Color(0x888888);
    private static final Color SELECTED_BACKGROUND_COLOR = Color.WHITE;
    private static final Color UNSELECTED_BACKGROUND_COLOR = new Color(0xcccccc);

    private static final int CORNER_ARC_DIAMETER = 6;

    public void setCloseButtonLocation(CloseButtonLocation closeButtonLocation) {
        fCloseButtonLocation = closeButtonLocation;
    }

    public void paintTab(Graphics2D graphics, JTabbedPane tabPane, Rectangle tabBounds,
                         String tabText, Icon tabIcon, boolean isSelected,
                         boolean isMouseOverCloseButton, boolean isMousePressedOverCloseButton) {

        paintTabBackgroundAndBorder(graphics, tabBounds, isSelected);
        paintCloseButton(graphics, tabBounds, isSelected, isMouseOverCloseButton,
                isMousePressedOverCloseButton);

        FontMetrics fontMetrics = graphics.getFontMetrics();
        int closeButtonWidth = fCloseButtonIcon.getWidth();

        int textWidth = fontMetrics.stringWidth(tabText);

        int widthRequiredForCloseButton =
                fCloseButtonLocation.calculateWidthRequiredForCloseButton(closeButtonWidth);
        boolean tooWide = textWidth > tabBounds.width - widthRequiredForCloseButton - CONTENT_DISTANCE_FROM_EDGE;

        Rectangle adjustedTabRect = new Rectangle();
        adjustedTabRect.x = tooWide
                ? fCloseButtonLocation.calculateContentX(tabBounds, closeButtonWidth) : tabBounds.x;
        adjustedTabRect.y = tabBounds.y;
        adjustedTabRect.width = tooWide
                ? tabBounds.width - widthRequiredForCloseButton - CONTENT_DISTANCE_FROM_EDGE
                : tabBounds.width;
        adjustedTabRect.height = tabBounds.height;

        ICON_BOUNDS.x = 0;
        ICON_BOUNDS.y = 0;

        String clippedText = SwingUtilities.layoutCompoundLabel(tabPane, fontMetrics, tabText, tabIcon,
                SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.TRAILING,
                adjustedTabRect, ICON_BOUNDS, TEXT_BOUNDS, 4);

        int textX = fCloseButtonLocation.adjustXToPreventEncroachment(tabBounds, closeButtonWidth, TEXT_BOUNDS);
        int textY = TEXT_BOUNDS.y + fontMetrics.getAscent();
        graphics.setColor(tabPane.getForeground());
        BasicGraphicsUtils.drawString(graphics, clippedText, -1, textX, textY);
    }

    private void paintTabBackgroundAndBorder(Graphics2D graphics, Rectangle tabBounds, boolean isSelected) {
        int extendedHeight = tabBounds.height + CORNER_ARC_DIAMETER / 2;
        // paint the background.
        graphics.setColor(isSelected ? SELECTED_BACKGROUND_COLOR : UNSELECTED_BACKGROUND_COLOR);
        graphics.fillRoundRect(tabBounds.x, tabBounds.y, tabBounds.width, extendedHeight,
                CORNER_ARC_DIAMETER, CORNER_ARC_DIAMETER);

        // paint the border.
        graphics.setColor(isSelected ? SELECTED_BORDER_COLOR : UNSELECTED_BORDER_COLOR);
        graphics.drawRoundRect(tabBounds.x, tabBounds.y, tabBounds.width, extendedHeight,
                CORNER_ARC_DIAMETER, CORNER_ARC_DIAMETER);
    }

    private void paintCloseButton(Graphics2D graphics, Rectangle tabBounds, boolean isSelected,
                                  boolean isMouseOverCloseButton, boolean isMousePressedOverCloseButton) {
        int x = fCloseButtonLocation.calculateCloseButtonX(tabBounds, fCloseButtonIcon.getWidth());
        int y = fCloseButtonLocation.calculateCloseButtonY(tabBounds, fCloseButtonIcon.getHeight());
        ImageIcon closeImageIcon =
                fCloseButtonIcon.getImageIcon(isSelected, isMouseOverCloseButton, isMousePressedOverCloseButton);
        graphics.drawImage(closeImageIcon.getImage(), x, y, null);
    }


    public boolean isPointOverCloseButton(Rectangle tabBounds, Point point) {
        int closeButtonWidth = fCloseButtonIcon.getWidth();
        int closeButtonHeight = fCloseButtonIcon.getHeight();
        int closeButtonX = fCloseButtonLocation.calculateCloseButtonX(tabBounds, closeButtonWidth);
        int closeButtonY = fCloseButtonLocation.calculateCloseButtonY(tabBounds, closeButtonHeight);
        boolean overHorizontally = closeButtonX <= point.x && point.x <= closeButtonX + closeButtonWidth;
        boolean overVertically = closeButtonY <= point.y && point.y <= closeButtonY + closeButtonHeight;
        return overHorizontally && overVertically;
    }

    private Shape createTabShape(Rectangle tabBounds) {
        int topIndent = 3;
        int topCurveRadius = 3;
        int bottomCurveRadius = 4;
        int topLeftX = tabBounds.x + topIndent;
        int topRightX = tabBounds.x + tabBounds.width - topIndent;
        int bottomLeftX = tabBounds.x;
        int bottomRightX = tabBounds.x + tabBounds.width;

        GeneralPath path = new GeneralPath();
        path.moveTo(topLeftX + topCurveRadius, tabBounds.y);
        path.quadTo(topLeftX, tabBounds.y, topLeftX, tabBounds.y + topCurveRadius);
//        path.lineTo(topLeftX,  tabBounds.y + tabBounds.height - bottomCurveRadius);
        path.quadTo(topLeftX, tabBounds.y + tabBounds.height, bottomLeftX, tabBounds.y + tabBounds.height);
//        path.moveTo(tabBounds.x + curveRadius, tabBounds.y);
//        path.quadTo(topLeftX, tabBounds.y + tabBounds.height, bottomLeftX, tabBounds.y + tabBounds.height);
//        path.lineTo(tabBounds.x,  tabBounds.y + tabBounds.height);
//        path.lineTo(tabBounds.x + tabBounds.width, tabBounds.y + tabBounds.height);
//        path.lineTo(tabBounds.x + tabBounds.width, tabBounds.y + topCurveRadius);
//        path.quadTo(tabBounds.x + tabBounds.width, tabBounds.y, tabBounds.x + tabBounds.width - topCurveRadius, tabBounds.y);
        path.lineTo(bottomRightX, tabBounds.y + tabBounds.height);
        path.quadTo(topRightX, tabBounds.y + tabBounds.height, topRightX, tabBounds.y + topCurveRadius);
        path.quadTo(topRightX, tabBounds.y, topRightX - topCurveRadius, tabBounds.y);
        path.closePath();
        return path;
    }

    // CloseButton enumeration implementation. ////////////////////////////////////////////////////////////////////////

    public enum CloseButtonLocation {

        LEFT {
            int calculateCloseButtonX(Rectangle tabBounds, int closeButtonWidth) {
                return tabBounds.x + CLOSE_BUTTON_DISTANCE_FROM_EDGE;
            }
            int calculateContentX(Rectangle tabBounds, int closeButtonWidth) {
                return tabBounds.x + calculateWidthRequiredForCloseButton(closeButtonWidth);
            }
            int adjustXToPreventEncroachment(Rectangle tabBounds, int closeButtonWidth, Rectangle contentBounds) {
                int closeButtonX = calculateCloseButtonX(tabBounds, closeButtonWidth);
                int closeButtonXWithPad = closeButtonX + closeButtonWidth + CLOSE_BUTTON_DISTANCE_FROM_CONTENT;
                return Math.max(closeButtonXWithPad, contentBounds.x);
            }
        },
        RIGHT {
            int calculateCloseButtonX(Rectangle tabBounds, int closeButtonWidth) {
                return tabBounds.x + tabBounds.width - CLOSE_BUTTON_DISTANCE_FROM_CONTENT - closeButtonWidth;
            }
            int calculateContentX(Rectangle tabBounds, int closeButtonWidth) {
                return tabBounds.x + CONTENT_DISTANCE_FROM_EDGE;
            }
            int adjustXToPreventEncroachment(Rectangle tabBounds, int closeButtonWidth, Rectangle contentBounds) {
                int closeButtonX = calculateCloseButtonX(tabBounds, closeButtonWidth);
                int closeButtonXWithPad = closeButtonX - CLOSE_BUTTON_DISTANCE_FROM_CONTENT;
                int shiftAmount = (contentBounds.x + contentBounds.width) - closeButtonXWithPad;
                return shiftAmount > 0 ? contentBounds.x - shiftAmount : contentBounds.x;
            }
        };

        abstract int calculateCloseButtonX(Rectangle tabBounds, int closeButtonWidth);

        abstract int calculateContentX(Rectangle tabBounds, int closeButtonWidth);

        abstract int adjustXToPreventEncroachment(Rectangle tabBounds, int closeButtonWidth, Rectangle contentBounds);

        private int calculateCloseButtonY(Rectangle tabBounds, int closeButtonHeight) {
            return tabBounds.y + tabBounds.height / 2 - closeButtonHeight / 2;
        }

        int calculateWidthRequiredForCloseButton(int closeButtonWidth) {
            return closeButtonWidth + CLOSE_BUTTON_DISTANCE_FROM_EDGE + CLOSE_BUTTON_DISTANCE_FROM_CONTENT;
        }

    }

    // CloseButton interface and implementations. /////////////////////////////////////////////////////////////////////

    private interface CloseButtonIcon {
        int getWidth();

        int getHeight();

        ImageIcon getImageIcon(boolean isSelected, boolean isOver, boolean isPressed);
    }

    private static class DefaultCloseButtonIcon implements CloseButtonIcon {

        private ImageIcon fSelected = createImageIcon("close.png");
        private ImageIcon fUnselected = createImageIcon("close_unselected.png");
        private ImageIcon fOver = createImageIcon("close_over.png");
        private ImageIcon fPressed = createImageIcon("close_pressed.png");

        public int getWidth() {
            return fSelected.getIconWidth();
        }

        public int getHeight() {
            return fSelected.getIconHeight();
        }

        public ImageIcon getImageIcon(boolean isSelected, boolean isOver, boolean isPressed) {
            ImageIcon closeImageIcon;

            if (isOver && isPressed) {
                closeImageIcon = fPressed;
            } else if (isOver) {
                closeImageIcon = fOver;
            } else if (isSelected) {
                closeImageIcon = fSelected;
            } else {
                closeImageIcon = fUnselected;
            }

            return closeImageIcon;
        }

        private static ImageIcon createImageIcon(String fileName) {
            return new ImageIcon(EPTabbedPaneUI.class.getResource("/com/explodingpixels/widgets/images/" + fileName));
        }
    }
}