org.netbeans.api.visual.widget.LabelWidget Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.api.visual.widget;
import org.netbeans.modules.visual.util.GeomUtil;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
* A widget representing a text. The widget is not opaque and is checking clipping for by default.
*
* It allows to set 4 types of horizontal and vertical alignments (by default LEFT as horizontal and BASELINE as vertical).
*
* Swing Font-hinting feature may cause the labels are not rendered completely.
* Using setUseGlyphVector
you may force the label to be converted to a glyph vector
* which scales correctly for any zoom factor.
*
* @author David Kaspar
*/
public class LabelWidget extends Widget {
/**
* The text alignment
*/
public enum Alignment {
LEFT, RIGHT, CENTER, BASELINE
}
/**
* The text vertical alignment
*/
public enum VerticalAlignment {
TOP, BOTTOM, CENTER, BASELINE
}
/**
* The text orientation
* @since 2.1
*/
public enum Orientation {
NORMAL, ROTATE_90//, ROTATE_180, ROTATE_270, MIRROR, MIRROR_ROTATE_90, MIRROR_ROTATE_180, MIRROR_ROTATE_270
}
private String label;
private Alignment alignment = Alignment.LEFT;
private VerticalAlignment verticalAlignment = VerticalAlignment.BASELINE;
private Orientation orientation = Orientation.NORMAL;
private boolean paintAsDisabled;
private boolean useGlyphVector = false;
private GlyphVector cacheGlyphVector;
private String cacheLabel;
private Font cacheFont;
/**
* Creates a label widget.
* @param scene the scene
*/
public LabelWidget (Scene scene) {
this (scene, null);
}
/**
* Creates a label widget with a label.
* @param scene the scene
* @param label the label
*/
public LabelWidget (Scene scene, String label) {
super (scene);
setOpaque (false);
// setCursor (new Cursor (Cursor.TEXT_CURSOR));
setLabel (label);
setCheckClipping (true);
}
/**
* Returns a label.
* @return the label
*/
public String getLabel () {
return label;
}
/**
* Sets a label.
* @param label the label
*/
public void setLabel (String label) {
if (GeomUtil.equals (this.label, label))
return;
this.label = label;
revalidate ();
}
/**
* Returns a text horizontal alignment.
* @return the text horizontal alignment
*/
public Alignment getAlignment () {
return alignment;
}
/**
* Sets a text horizontal alignment.
* @param alignment the text horizontal alignment
*/
public void setAlignment (Alignment alignment) {
this.alignment = alignment;
repaint ();
}
/**
* Gets a text vertical alignment.
* @return the text vertical alignment
*/
public VerticalAlignment getVerticalAlignment () {
return verticalAlignment;
}
/**
* Sets a text vertical alignment.
* @param verticalAlignment the text vertical alignment
*/
public void setVerticalAlignment (VerticalAlignment verticalAlignment) {
this.verticalAlignment = verticalAlignment;
repaint ();
}
/**
* Gets a text orientation.
* @return the text orientation
* @since 2.1
*/
public Orientation getOrientation() {
return orientation;
}
/**
* Sets a text orientation.
* @param orientation the text orientation
* @since 2.1
*/
public void setOrientation(Orientation orientation) {
this.orientation = orientation;
revalidate();
}
/**
* Returns whether the label is painted as disabled.
* @return true, if the label is painted as disabled
*/
public boolean isPaintAsDisabled () {
return paintAsDisabled;
}
/**
* Sets whether the label is painted as disabled.
* @param paintAsDisabled if true, then the label is painted as disabled
*/
public void setPaintAsDisabled (boolean paintAsDisabled) {
boolean repaint = this.paintAsDisabled != paintAsDisabled;
this.paintAsDisabled = paintAsDisabled;
if (repaint)
repaint ();
}
/**
* Returns whether the label widget is using glyph vector for rendering text.
* @return true, if the label widget is using glyph vector
* @since 2.7
*/
public boolean isUseGlyphVector () {
return useGlyphVector;
}
/**
* Sets whether the label widget is using glyph vector for rendering text.
*
* Note that using glyph vector could slow-down the rendering performance.
* Note that if you are not using glyph vector then the text may be clipped when a scene has zoom factor different from 1.0.
* @param useGlyphVector if true, then a glyph vector is used for rendering text
* @since 2.7
*/
public void setUseGlyphVector (boolean useGlyphVector) {
if (this.useGlyphVector == useGlyphVector)
return;
this.useGlyphVector = useGlyphVector;
cacheGlyphVector = null;
cacheLabel = null;
cacheFont = null;
revalidate ();
}
private void assureGlyphVector () {
Font font = getFont ();
FontRenderContext fontRenderContext = getGraphics ().getFontRenderContext ();
if (cacheGlyphVector != null && cacheFont == font && cacheLabel == label)
return;
cacheFont = font;
cacheLabel = label;
cacheGlyphVector = font.createGlyphVector (new FontRenderContext (new AffineTransform (), fontRenderContext.isAntiAliased (), fontRenderContext.usesFractionalMetrics ()), cacheLabel);
}
/**
* Calculates a client area for the label.
* @return the client area
*/
protected Rectangle calculateClientArea () {
if (label == null)
return super.calculateClientArea ();
Rectangle rectangle;
if (useGlyphVector) {
assureGlyphVector ();
rectangle = GeomUtil.roundRectangle (cacheGlyphVector.getVisualBounds ());
rectangle.grow (1, 1); // WORKAROUND - even text antialiasing is included into the boundary
} else {
Graphics2D gr = getGraphics ();
if (gr == null) { // #192529
return super.calculateClientArea();
}
FontMetrics fontMetrics = gr.getFontMetrics (getFont ());
Rectangle2D stringBounds = fontMetrics.getStringBounds (label, gr);
rectangle = GeomUtil.roundRectangle (stringBounds);
}
switch (orientation) {
case NORMAL:
return rectangle;
case ROTATE_90:
return new Rectangle (rectangle.y, - rectangle.x - rectangle.width, rectangle.height, rectangle.width);
default:
throw new IllegalStateException ();
}
}
/**
* Paints the label widget.
*/
protected void paintWidget () {
if (label == null)
return;
Graphics2D gr = getGraphics ();
if (useGlyphVector)
assureGlyphVector ();
else
gr.setFont (getFont ());
FontMetrics fontMetrics = gr.getFontMetrics ();
Rectangle clientArea = getClientArea ();
int x;
int y;
switch (orientation) {
case NORMAL:
switch (alignment) {
case BASELINE:
x = 0;
break;
case LEFT:
x = clientArea.x;
break;
case CENTER:
if (useGlyphVector)
x = clientArea.x + (clientArea.width - getCacheGlyphVectorWidth ()) / 2;
else
x = clientArea.x + (clientArea.width - fontMetrics.stringWidth (label)) / 2;
break;
case RIGHT:
if (useGlyphVector)
x = clientArea.x + clientArea.width - getCacheGlyphVectorWidth ();
else
x = clientArea.x + clientArea.width - fontMetrics.stringWidth (label);
break;
default:
return;
}
switch (verticalAlignment) {
case BASELINE:
y = 0;
break;
case TOP:
y = clientArea.y + fontMetrics.getAscent ();
break;
case CENTER:
y = clientArea.y + (clientArea.height + fontMetrics.getAscent () - fontMetrics.getDescent ()) / 2;
break;
case BOTTOM:
y = clientArea.y + clientArea.height - fontMetrics.getDescent ();
break;
default:
return;
}
break;
case ROTATE_90:
switch (alignment) {
case BASELINE:
x = 0;
break;
case LEFT:
x = clientArea.x + fontMetrics.getAscent ();
break;
case CENTER:
x = clientArea.x + (clientArea.width + fontMetrics.getAscent () - fontMetrics.getDescent ()) / 2;
break;
case RIGHT:
x = clientArea.x + clientArea.width - fontMetrics.getDescent ();
break;
default:
return;
}
switch (verticalAlignment) {
case BASELINE:
y = 0;
break;
case TOP:
if (useGlyphVector)
y = clientArea.y + getCacheGlyphVectorWidth ();
else
y = clientArea.y + fontMetrics.stringWidth (label);
break;
case CENTER:
if (useGlyphVector)
y = clientArea.y + (clientArea.height + getCacheGlyphVectorWidth ()) / 2;
else
y = clientArea.y + (clientArea.height + fontMetrics.stringWidth (label)) / 2;
break;
case BOTTOM:
y = clientArea.y + clientArea.height;
break;
default:
return;
}
break;
default:
return;
}
AffineTransform previousTransform = gr.getTransform ();
gr.translate (x, y);
switch (orientation) {
case NORMAL:
break;
case ROTATE_90:
gr.rotate (- GeomUtil.M_PI_2);
break;
default:
throw new IllegalStateException ();
}
Paint background = getBackground ();
if (paintAsDisabled && background instanceof Color) {
Color color = ((Color) background);
gr.setColor (color.brighter ());
if (useGlyphVector)
gr.fill (cacheGlyphVector.getOutline (1, 1));
else
gr.drawString (label, 1, 1);
gr.setColor (color.darker ());
if (useGlyphVector)
gr.fill (cacheGlyphVector.getOutline ());
else
gr.drawString (label, 0, 0);
} else {
gr.setColor (getForeground ());
if (useGlyphVector)
gr.fill (cacheGlyphVector.getOutline ());
else
gr.drawString (label, 0, 0);
}
gr.setTransform(previousTransform);
}
private int getCacheGlyphVectorWidth () {
return GeomUtil.roundRectangle (cacheGlyphVector.getVisualBounds ()).width;
}
}