1 /* 2 * @(#)QuaquaLabelUI.java 1.6 2007-11-18 3 * 4 * Copyright (c) 2005-2007 Werner Randelshofer 5 * Staldenmattweg 2, Immensee, CH-6405, Switzerland. 6 * All rights reserved. 7 * 8 * The copyright of this software is owned by Werner Randelshofer. 9 * You may not use, copy or modify this software, except in 10 * accordance with the license agreement you entered into with 11 * Werner Randelshofer. For details see accompanying license terms. 12 */ 13 package ch.randelshofer.quaqua; 14 15 import ch.randelshofer.quaqua.util.*; 16 import ch.randelshofer.quaqua.border.BackgroundBorder; 17 import ch.randelshofer.quaqua.color.InactivatableColorUIResource; 18 import ch.randelshofer.quaqua.util.Debug; 19 import ch.randelshofer.quaqua.color.PaintableColor; 20 import java.awt.*; 21 import java.awt.event.*; 22 import java.beans.*; 23 24 import javax.swing.*; 25 import javax.swing.border.*; 26 import javax.swing.plaf.*; 27 import javax.swing.plaf.basic.*; 28 import javax.swing.text.*; 29 30 /** 31 * QuaquaLabelUI. 32 * 33 * @author Werner Randelshofer 34 * @version 1.6.1 2009-02-01 Added support for property "Quaqua.Label.style"= 35 * "row", "rowSelected", "category" and "categorySelected" to support side bar JTrees. 36 * <br>1.6 2007-11-18 Added support for property "Quaqua.Label.style"= 37 * "emboss" and "shadow". 38 * <br>1.5.1 2007-01-15 Perceiced bounds.height must reflect font size 39 * even if the label is empty. 40 * <br>1.5 2006-02-18 Tweaked perceived text bounds. Draw disabled label 41 * with disabled text color. Draw background again if we are opaque. 42 * <br>1.4 2005-12-08 Support for background border added. 43 * <br>1.3 2005-07-17 Adapted to changes in interface VisuallyLayoutable. 44 * <br>1.2 2006-06-20 Paint text antialiased. 45 * <br>1.0 02 April 2005 Created. 46 */ 47 public class QuaquaLabelUI extends BasicLabelUI implements VisuallyLayoutable { 48 49 protected static QuaquaLabelUI labelUI = new QuaquaLabelUI(); 50 /* These rectangles/insets are allocated once for this shared LabelUI 51 * implementation. Re-using rectangles rather than allocating 52 * them in each getPreferredSize call sped up the method substantially. 53 */ 54 private static Rectangle iconR = new Rectangle(); 55 private static Rectangle textR = new Rectangle(); 56 private static Rectangle viewR = new Rectangle(); 57 private static Insets viewInsets = new Insets(0, 0, 0, 0); 58 59 /** 60 * Preferred spacing between labels and other components. 61 * Pixels from colon and associated controls (RadioButton, 62 * CheckBox) 63 * / 64 * private final static Insets associatedRegularSpacing = new Insets(8,8,8,8); 65 * private final static Insets associatedSmallSpacing = new Insets(6,6,6,6); 66 * private final static Insets associatedMiniSpacing = new Insets(5,5,5,5); 67 */ createUI(JComponent c)68 public static ComponentUI createUI(JComponent c) { 69 return labelUI; 70 } 71 installDefaults(JLabel b)72 protected void installDefaults(JLabel b) { 73 super.installDefaults(b); 74 75 // load shared instance defaults 76 LookAndFeel.installBorder(b, "Label.border"); 77 78 // FIXME - Very, very dirty trick to achieve small labels on sliders 79 // This hack should be removed, when we implement a SliderUI 80 // on our own. 81 if (b.getClass().getName().endsWith("LabelUIResource")) { 82 b.setFont(UIManager.getFont("Slider.labelFont")); 83 } 84 } 85 paint(Graphics gr, JComponent c)86 public void paint(Graphics gr, JComponent c) { 87 Graphics2D g = (Graphics2D) gr; 88 Object oldHints = QuaquaUtilities.beginGraphics(g); 89 90 // Paint background again so that the texture paint is drawn 91 if (c.isOpaque()) { 92 g.setPaint(PaintableColor.getPaint(c.getBackground(), c)); 93 g.fillRect(0, 0, c.getWidth(), c.getHeight()); 94 } 95 96 // Paint background border 97 Border b = c.getBorder(); 98 if (b != null && b instanceof BackgroundBorder) { 99 ((BackgroundBorder) b).getBackgroundBorder().paintBorder(c, g, 0, 0, c.getWidth(), c.getHeight()); 100 } 101 102 super.paint(g, c); 103 QuaquaUtilities.endGraphics(g, oldHints); 104 Debug.paint(g, c, this); 105 } 106 107 /** 108 * Paint label with disabled text color. 109 * 110 * @see #paint 111 * @see #paintEnabledText 112 */ paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)113 protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY) { 114 // Make sure we render with the right drawing properties and make sure 115 // we can edit them by client properties 116 Font font = l.getFont(); 117 Color foreground = UIManager.getColor("Label.disabledForeground"); 118 int accChar = -1; //l.getDisplayedMnemonicIndex(); 119 120 String style = (String) l.getClientProperty("Quaqua.Label.style"); 121 if (style != null) { 122 boolean selected = style.endsWith("Selected"); 123 124 if ((style.equals("category") || style.equals("categorySelected")) 125 && UIManager.getFont("Tree.category.font.sideBar") != null 126 && UIManager.getColor("Tree.category.foreground.sideBar") != null) { 127 128 s = s.toUpperCase(); 129 font = UIManager.getFont("Tree.category.font.sideBar"); 130 style = (selected) ? "shadow" : "emboss"; 131 } else if ((style.equals("row") || style.equals("rowSelected")) 132 && UIManager.getFont("Tree.font.sideBar") != null 133 && UIManager.getFont("Tree.font.selected.sideBar") != null 134 && UIManager.getColor("Tree.foreground.sideBar") != null) { 135 136 font = selected ? UIManager 137 .getFont("Tree.font.selected.sideBar") : UIManager 138 .getFont("Tree.font.sideBar"); 139 style = selected ? "shadow" : null; 140 } 141 142 if (style != null && style.equals("emboss") 143 && UIManager.getColor("Label.embossForeground") != null) { 144 g.setFont(font); 145 g.setColor(UIManager.getColor("Label.embossForeground")); 146 QuaquaUtilities.drawString(g, s, accChar, textX, textY + 1); 147 } else if (style != null && style.equals("shadow") 148 && UIManager.getColor("Label.shadowForeground") != null) { 149 g.setFont(font); 150 g.setColor(UIManager.getColor("Label.shadowForeground")); 151 QuaquaUtilities.drawString(g, s, accChar, textX, textY + 1); 152 } 153 } 154 155 g.setFont(font); 156 g.setColor(foreground); 157 QuaquaUtilities.drawString(g, s, accChar, 158 textX, textY); 159 } 160 paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)161 protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) { 162 int mnemIndex = l.getDisplayedMnemonicIndex(); 163 164 // Make sure we render with the right drawing properties and make sure 165 // we can edit them by client properties 166 Font font = l.getFont(); 167 Color foreground = l.getForeground(); 168 169 String style = (String) l.getClientProperty("Quaqua.Label.style"); 170 if (style != null) { 171 boolean selected = style.endsWith("Selected"); 172 173 if ((style.equals("category") || style.equals("categorySelected")) 174 && UIManager.getFont("Tree.category.font.sideBar") != null 175 && UIManager.getColor("Tree.category.foreground.sideBar") != null) { 176 177 s = s.toUpperCase(); 178 font = UIManager.getFont("Tree.category.font.sideBar"); 179 foreground = UIManager 180 .getColor("Tree.category.foreground.sideBar"); 181 if (foreground instanceof InactivatableColorUIResource) 182 ((InactivatableColorUIResource) foreground) 183 .setActive(selected); 184 style = (selected) ? "shadow" : "emboss"; 185 186 } else if ((style.equals("row") || style.equals("rowSelected")) 187 && UIManager.getFont("Tree.font.sideBar") != null 188 && UIManager.getColor("Tree.foreground.sideBar") != null) { 189 190 font = UIManager.getFont("Tree.font.sideBar"); 191 foreground = UIManager.getColor("Tree.foreground.sideBar"); 192 if (foreground instanceof InactivatableColorUIResource) 193 ((InactivatableColorUIResource) foreground) 194 .setActive(selected); 195 style = (selected) ? "shadow" : null; 196 } 197 198 if (style != null && style.equals("emboss") 199 && UIManager.getColor("Label.embossForeground") != null) { 200 g.setFont(font); 201 g.setColor(UIManager.getColor("Label.embossForeground")); 202 QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY + 1); 203 } else if (style != null && style.equals("shadow") 204 && UIManager.getColor("Label.shadowForeground") != null) { 205 g.setFont(font); 206 g.setColor(UIManager.getColor("Label.shadowForeground")); 207 QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY + 1); 208 } 209 } 210 211 g.setFont(font); 212 g.setColor(foreground); 213 QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY); 214 //SwingUtilities2.drawStringUnderlineCharAt(l, g, s, mnemIndex, 215 // textX, textY); 216 } 217 218 /** 219 * Forwards the call to SwingUtilities.layoutCompoundLabel(). 220 * This method is here so that a subclass could do Label specific 221 * layout and to shorten the method name a little. 222 * 223 * @see SwingUtilities#layoutCompoundLabel 224 */ layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR)225 protected String layoutCL( 226 JLabel label, 227 FontMetrics fontMetrics, 228 String text, 229 Icon icon, 230 Rectangle viewR, 231 Rectangle iconR, 232 Rectangle textR) { 233 return SwingUtilities.layoutCompoundLabel( 234 (JComponent) label, 235 fontMetrics, 236 text, 237 icon, 238 label.getVerticalAlignment(), 239 label.getHorizontalAlignment(), 240 label.getVerticalTextPosition(), 241 label.getHorizontalTextPosition(), 242 viewR, 243 iconR, 244 textR, 245 label.getIconTextGap()); 246 } 247 getBaseline(JComponent c, int width, int height)248 public int getBaseline(JComponent c, int width, int height) { 249 Rectangle vb = getVisualBounds(c, VisuallyLayoutable.TEXT_BOUNDS, width, height); 250 return (vb == null) ? -1 : vb.y + vb.height; 251 } 252 getVisualBounds(JComponent c, int type, int width, int height)253 public Rectangle getVisualBounds(JComponent c, int type, int width, int height) { 254 Rectangle rect = new Rectangle(0, 0, width, height); 255 if (type == VisuallyLayoutable.CLIP_BOUNDS) { 256 return rect; 257 } 258 259 JLabel b = (JLabel) c; 260 String text = b.getText(); 261 boolean isEmpty = (text == null || text.length() == 0); 262 if (isEmpty) { 263 text = " "; 264 } 265 Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon(); 266 267 Font f = c.getFont(); 268 FontMetrics fm = c.getFontMetrics(f); 269 Insets insets = c.getInsets(viewInsets); 270 271 viewR.x = insets.left; 272 viewR.y = insets.top; 273 viewR.width = width - (insets.left + insets.right); 274 viewR.height = height - (insets.top + insets.bottom); 275 276 iconR.x = iconR.y = iconR.width = iconR.height = 0; 277 textR.x = textR.y = textR.width = textR.height = 0; 278 279 String clippedText = 280 layoutCL(b, fm, text, icon, viewR, iconR, textR); 281 282 Rectangle textBounds = Fonts.getPerceivedBounds(text, f, c); 283 if (isEmpty) { 284 textBounds.width = 0; 285 } 286 int ascent = fm.getAscent(); 287 textR.x += textBounds.x; 288 textR.width = textBounds.width; 289 textR.y += ascent + textBounds.y; 290 textR.height -= fm.getHeight() - textBounds.height; 291 292 // Determine rect rectangle 293 switch (type) { 294 case VisuallyLayoutable.COMPONENT_BOUNDS: 295 if (icon != null) { 296 rect = textR.union(iconR); 297 } else { 298 rect.setBounds(textR); 299 } 300 break; 301 case VisuallyLayoutable.TEXT_BOUNDS: 302 if (text == null) { 303 return rect; 304 } 305 rect.setBounds(textR); 306 break; 307 } 308 309 return rect; 310 } 311 propertyChange(PropertyChangeEvent evt)312 public void propertyChange(PropertyChangeEvent evt) { 313 String name = evt.getPropertyName(); 314 315 if (name.equals("JComponent.sizeVariant")) { 316 QuaquaUtilities.applySizeVariant((JLabel) evt.getSource()); 317 } else { 318 super.propertyChange(evt); 319 } 320 } 321 } 322