diff --git a/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java b/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java index 586c8bf..32aeb4b 100644 --- a/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java +++ b/core/src/main/java/edu/umd/cs/piccolo/nodes/PText.java @@ -46,7 +46,6 @@ /** * PText is a multi-line text node. The text will flow to base on the * width of the node's bounds. - *
*
* @version 1.1
* @author Jesse Grosjean
@@ -75,87 +74,170 @@
public static final String PROPERTY_FONT = "font";
public static final int PROPERTY_CODE_FONT = 1 << 20;
- public static Font DEFAULT_FONT = new Font("Helvetica", Font.PLAIN, 12);
- public static double DEFAULT_GREEK_THRESHOLD = 5.5;
+ /**
+ * The property name that identifies a change of this node's text paint (see
+ * {@link #getTextPaint getTextPaint}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_TEXT_PAINT = "text paint";
+ public static final int PROPERTY_CODE_TEXT_PAINT = 1 << 21;
- private String text;
- private Paint textPaint;
- private Font font;
+ /** Default font, 12 point "SansSerif"
. Will be made final in version 2.0. */
+ //public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 12); jdk 1.6+
+ public static Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 12);
+
+ /** Default greek threshold, 5.5d
. Will be made final in version 2.0. */
+ public static double DEFAULT_GREEK_THRESHOLD = 5.5d;
+
+ /** Default horizontal alignment, Component.LEFT_ALIGNMENT
. */
+ public static final float DEFAULT_HORIZONTAL_ALIGNMENT = Component.LEFT_ALIGNMENT;
+
+ /** Default text, ""
. */
+ public static final String DEFAULT_TEXT = "";
+
+ /** Default text paint, Color.BLACK
. */
+ public static final Paint DEFAULT_TEXT_PAINT = Color.BLACK;
+
+ private static final TextLayout[] EMPTY_TEXT_LAYOUT_ARRAY = new TextLayout[0];
+ private String text = DEFAULT_TEXT;
+ private Paint textPaint = DEFAULT_TEXT_PAINT;
+ private Font font = DEFAULT_FONT;
+ /** Will be made private in version 2.0. */
protected double greekThreshold = DEFAULT_GREEK_THRESHOLD;
- private float justification = Component.LEFT_ALIGNMENT;
+ private float horizontalAlignment = DEFAULT_HORIZONTAL_ALIGNMENT;
private boolean constrainHeightToTextHeight = true;
private boolean constrainWidthToTextWidth = true;
private transient TextLayout[] lines;
+
+ /**
+ * Create a new text node with no text (""
).
+ */
public PText() {
super();
- setTextPaint(Color.BLACK);
- text = "";
}
- public PText(final String aText) {
+ /**
+ * Create a new text node with the specified text.
+ *
+ * @param text text for this text node
+ */
+ public PText(final String text) {
this();
- setText(aText);
+ setText(text);
}
- /**
- * Return the justificaiton of the text in the bounds.
- *
- * @return float
- */
+
+ /** @deprecated by {@link #getHorizontalAlignment()} */
public float getJustification() {
- return justification;
+ return getHorizontalAlignment();
+ }
+
+ /** @deprecated by {@link #setHorizontalAlignment(float)} */
+ public void setJustification(final float justification) {
+ setHorizontalAlignment(justification);
}
/**
- * Sets the justificaiton of the text in the bounds.
- *
- * @param just
+ * Return the horizontal alignment for this text node. The horizontal alignment will be one of
+ * Component.LEFT_ALIGNMENT
, Component.CENTER_ALIGNMENT
,
+ * or Component.RIGHT_ALIGNMENT
. Defaults to {@link #DEFAULT_HORIZONTAL_ALIGNMENT}.
+ *
+ * @return the horizontal alignment for this text node
*/
- public void setJustification(final float just) {
- justification = just;
- recomputeLayout();
+ public float getHorizontalAlignment() {
+ return horizontalAlignment;
}
/**
- * Get the paint used to paint this nodes text.
+ * Set the horizontal alignment for this text node to horizontalAlignment
.
+ *
+ * @param horizontalAlignment horizontal alignment, must be one of
+ * Component.LEFT_ALIGNMENT
, Component.CENTER_ALIGNMENT
,
+ * or Component.RIGHT_ALIGNMENT
+ */
+ public void setHorizontalAlignment(final float horizontalAlignment) {
+ if (!validHorizontalAlignment(horizontalAlignment)) {
+ throw new IllegalArgumentException("horizontalAlignment must be one of Component.LEFT_ALIGNMENT, "
+ + "Component.CENTER_ALIGNMENT, or Component.RIGHT_ALIGNMENT");
+ }
+ this.horizontalAlignment = horizontalAlignment;
+ }
+
+ /**
+ * Return true if the specified horizontal alignment is one of Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or Component.RIGHT_ALIGNMENT
.
+ *
+ * @param horizontalAlignment horizontal alignment
+ * @return true if the specified horizontal alignment is one of Component.LEFT_ALIGNMENT
,
+ * Component.CENTER_ALIGNMENT
, or Component.RIGHT_ALIGNMENT
+ */
+ private static boolean validHorizontalAlignment(final float horizontalAlignment) {
+ return Component.LEFT_ALIGNMENT == horizontalAlignment
+ || Component.CENTER_ALIGNMENT == horizontalAlignment
+ || Component.RIGHT_ALIGNMENT == horizontalAlignment;
+ }
+
+ /**
+ * Return the paint used to paint this node's text.
*
- * @return Paint
+ * @return the paint used to paint this node's text
*/
public Paint getTextPaint() {
return textPaint;
}
/**
- * Set the paint used to paint this node's text background.
- *
- * @param textPaint
+ * Set the paint used to paint this node's text to textPaint
.
+ *
+ *
This is a bound property.
+ * + * @param textPaint text paint */ public void setTextPaint(final Paint textPaint) { + if (textPaint == this.textPaint) { + return; + } + final Paint oldTextPaint = this.textPaint; this.textPaint = textPaint; invalidatePaint(); + firePropertyChange(PROPERTY_CODE_TEXT_PAINT, PROPERTY_TEXT_PAINT, oldTextPaint, this.textPaint); } + /** + * Return true if this text node should constrain its width to the width of its text. + * Defaults totrue
.
+ *
+ * @return true if this text node should constrain its width to the width of its text
+ */
public boolean isConstrainWidthToTextWidth() {
return constrainWidthToTextWidth;
}
/**
- * Controls whether this node changes its width to fit the width of its
- * text. If flag is true it does; if flag is false it doesn't
+ * Set to true
if this text node should constrain its width the width of its text.
+ *
+ * @param constrainWidthToTextWidth true if this text node should constrain its width to the width of its text
*/
public void setConstrainWidthToTextWidth(final boolean constrainWidthToTextWidth) {
this.constrainWidthToTextWidth = constrainWidthToTextWidth;
recomputeLayout();
}
+ /**
+ * Return true if this text node should constrain its height to the height of its text.
+ * Defaults to true
.
+ *
+ * @return true if this text node should constrain its height to the height of its text
+ */
public boolean isConstrainHeightToTextHeight() {
return constrainHeightToTextHeight;
}
/**
- * Controls whether this node changes its height to fit the height of its
- * text. If flag is true it does; if flag is false it doesn't
+ * Set to true
if this text node should constrain its height the height of its text.
+ *
+ * @param constrainHeightToTextHeight true if this text node should constrain its height to the width of its height
*/
public void setConstrainHeightToTextHeight(final boolean constrainHeightToTextHeight) {
this.constrainHeightToTextHeight = constrainHeightToTextHeight;
@@ -163,72 +245,87 @@
}
/**
- * Returns the current greek threshold. When the screen font size will be
+ * Return the greek threshold in screen font size. When the screen font size will be
* below this threshold the text is rendered as 'greek' instead of drawing
- * the text glyphs.
+ * the text glyphs. Defaults to {@link DEFAULT_GREEK_THRESHOLD}.
+ *
+ * @return the current greek threshold in screen font size
*/
public double getGreekThreshold() {
return greekThreshold;
}
/**
- * Sets the current greek threshold. When the screen font size will be below
- * this threshold the text is rendered as 'greek' instead of drawing the
+ * Set the greek threshold in screen font size to greekThreshold
. When the
+ * screen font size will be below this threshold the text is rendered as 'greek' instead of drawing the
* text glyphs.
*
- * @param threshold minimum screen font size.
+ * @param greekThreshold greek threshold in screen font size
*/
- public void setGreekThreshold(final double threshold) {
- greekThreshold = threshold;
+ public void setGreekThreshold(final double greekThreshold) {
+ this.greekThreshold = greekThreshold;
invalidatePaint();
}
+ /**
+ * Return the text for this text node. Defaults to {@link #DEFAULT_TEXT}.
+ *
+ * @return the text for this text node
+ */
public String getText() {
return text;
}
/**
- * Set the text for this node. The text will be broken up into multiple
+ * Set the text for this node to text
. The text will be broken up into multiple
* lines based on the size of the text and the bounds width of this node.
+ *
+ * This is a bound property.
+ * + * @param text text for this text node */ - public void setText(final String aText) { - final String old = text; - text = aText == null ? "" : aText; + public void setText(final String text) { + if (text == this.text) { + return; + } + final String oldText = this.text; + this.text = text == null ? DEFAULT_TEXT : text; lines = null; recomputeLayout(); invalidatePaint(); - firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, old, text); + firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, oldText, this.text); } /** - * Returns the font of this PText. - * - * @return the font of this PText. + * Return the font for this text node. Defaults to {@link #DEFAULT_FONT}. + * + * @return the font for this text node */ public Font getFont() { - if (font == null) { - font = DEFAULT_FONT; - } return font; } /** - * Set the font of this PText. Note that in Piccolo if you want to change - * the size of a text object it's often a better idea to scale the PText - * node instead of changing the font size to get that same effect. Using - * very large font sizes can slow performance. + * Set the font for this text node tofont
. Note that in Piccolo if you want to change
+ * the size of a text object it's often a better idea to scale the PText node instead of changing the font
+ * size to get that same effect. Using very large font sizes can slow performance.
+ *
+ * This is a bound property.
+ * + * @param font font for this text node */ - public void setFont(final Font aFont) { - final Font old = font; - font = aFont; + public void setFont(final Font font) { + if (font == this.font) { + return; + } + final Font oldFont = this.font; + this.font = font == null ? DEFAULT_FONT : font; lines = null; recomputeLayout(); invalidatePaint(); - firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, old, font); + firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, oldFont, this.font); } - private static final TextLayout[] EMPTY_TEXT_LAYOUT_ARRAY = new TextLayout[0]; - /** * Compute the bounds of the text wrapped by this node. The text layout is * wrapped based on the bounds of this node. @@ -291,21 +388,36 @@ } } - // provided in case someone needs to override the way that lines are - // wrapped. - protected TextLayout computeNextLayout(final LineBreakMeasurer measurer, final float availibleWidth, + /** + * Compute the next layout using the specified line break measurer, available width, + * and next line break offset. + * + * @param lineBreakMeasurer line break measurer + * @param availableWidth available width + * @param nextLineBreakOffset next line break offset + * @return the next layout computed using the specified line break measurer, available width, + * and next line break offset + */ + protected TextLayout computeNextLayout(final LineBreakMeasurer lineBreakMeasurer, final float availableWidth, final int nextLineBreakOffset) { - return measurer.nextLayout(availibleWidth, nextLineBreakOffset, false); + return lineBreakMeasurer.nextLayout(availableWidth, nextLineBreakOffset, false); } - protected void paint(final PPaintContext paintContext) { - super.paint(paintContext); + /** + * Paint greek with the specified paint context + * + * @param paintContext paint context + */ + protected void paintGreek(final PPaintContext paintContext) { + // empty + } - final float screenFontSize = getFont().getSize() * (float) paintContext.getScale(); - if (textPaint == null || screenFontSize <= greekThreshold) { - return; - } - + /** + * Paint text with the specified paint context. + * + * @param paintContext paint context + */ + protected void paintText(final PPaintContext paintContext) { final float x = (float) getX(); float y = (float) getY(); final float bottomY = (float) getHeight() + y; @@ -328,13 +440,25 @@ return; } - final float offset = (float) (getWidth() - tl.getAdvance()) * justification; + final float offset = (float) (getWidth() - tl.getAdvance()) * horizontalAlignment; tl.draw(g2, x + offset, y); y += tl.getDescent() + tl.getLeading(); } } + protected void paint(final PPaintContext paintContext) { + super.paint(paintContext); + if (textPaint == null) { + return; + } + final float screenFontSize = getFont().getSize() * (float) paintContext.getScale(); + if (screenFontSize <= greekThreshold) { + paintGreek(paintContext); + } + paintText(paintContext); + } + protected void internalUpdateBounds(final double x, final double y, final double width, final double height) { recomputeLayout(); } diff --git a/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java b/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java index 1e8ef49..e77a4bc 100644 --- a/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java +++ b/core/src/test/java/edu/umd/cs/piccolo/nodes/PTextTest.java @@ -103,6 +103,54 @@ assertEquals(Component.RIGHT_ALIGNMENT, textNode.getJustification(), 0.000001); } + public void testHorizontalAlignmentIsLeftByDefault() { + assertEquals(Component.LEFT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); + } + + public void testSetHorizontalAlignmentPersists() { + textNode.setHorizontalAlignment(Component.RIGHT_ALIGNMENT); + assertEquals(Component.RIGHT_ALIGNMENT, textNode.getHorizontalAlignment(), 0.000001); + } + + public void testSetHorizontalAlignmentInvalidValues() { + try { + textNode.setHorizontalAlignment(-2.0f); + } + catch (IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(2.0f); + } + catch (IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(-Float.MAX_VALUE); + } + catch (IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(Float.MAX_VALUE); + } + catch (IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(-1.00f); + } + catch (IllegalArgumentException e) { + // expected + } + try { + textNode.setHorizontalAlignment(1.00f); + } + catch (IllegalArgumentException e) { + // expected + } + } + public void testTextPaintIsBlackByDefault() { assertEquals(Color.BLACK, textNode.getTextPaint()); }