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 a0671d6..37070c7 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
@@ -52,303 +52,313 @@
*/
public class PText extends PNode {
- /**
- * The property name that identifies a change of this node's text (see
- * {@link #getText getText}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_TEXT = "text";
- public static final int PROPERTY_CODE_TEXT = 1 << 19;
+ /**
+ * The property name that identifies a change of this node's text (see
+ * {@link #getText getText}). Both old and new value will be set in any
+ * property change event.
+ */
+ public static final String PROPERTY_TEXT = "text";
+ public static final int PROPERTY_CODE_TEXT = 1 << 19;
- /**
- * The property name that identifies a change of this node's font (see
- * {@link #getFont getFont}). Both old and new value will be set in any
- * property change event.
- */
- public static final String PROPERTY_FONT = "font";
- public static final int PROPERTY_CODE_FONT = 1 << 20;
+ /**
+ * The property name that identifies a change of this node's font (see
+ * {@link #getFont getFont}). Both old and new value will be set in any
+ * property change event.
+ */
+ 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;
+ public static Font DEFAULT_FONT = new Font("Helvetica", Font.PLAIN, 12);
+ public static double DEFAULT_GREEK_THRESHOLD = 5.5;
- private String text;
- private Paint textPaint;
- private Font font;
- protected double greekThreshold = DEFAULT_GREEK_THRESHOLD;
- private float justification = javax.swing.JLabel.LEFT_ALIGNMENT;
- private boolean constrainHeightToTextHeight = true;
- private boolean constrainWidthToTextWidth = true;
- private transient TextLayout[] lines;
+ private String text;
+ private Paint textPaint;
+ private Font font;
+ protected double greekThreshold = DEFAULT_GREEK_THRESHOLD;
+ private float justification = javax.swing.JLabel.LEFT_ALIGNMENT;
+ private boolean constrainHeightToTextHeight = true;
+ private boolean constrainWidthToTextWidth = true;
+ private transient TextLayout[] lines;
- public PText() {
- super();
- setTextPaint(Color.BLACK);
- text = "";
- }
+ public PText() {
+ super();
+ setTextPaint(Color.BLACK);
+ text = "";
+ }
- public PText(String aText) {
- this();
- setText(aText);
- }
+ public PText(String aText) {
+ this();
+ setText(aText);
+ }
- /**
- * Return the justificaiton of the text in the bounds.
- *
- * @return float
- */
- public float getJustification() {
- return justification;
- }
+ /**
+ * Return the justificaiton of the text in the bounds.
+ *
+ * @return float
+ */
+ public float getJustification() {
+ return justification;
+ }
- /**
- * Sets the justificaiton of the text in the bounds.
- *
- * @param just
- */
- public void setJustification(float just) {
- justification = just;
- recomputeLayout();
- }
+ /**
+ * Sets the justificaiton of the text in the bounds.
+ *
+ * @param just
+ */
+ public void setJustification(float just) {
+ justification = just;
+ recomputeLayout();
+ }
- /**
- * Get the paint used to paint this nodes text.
- *
- * @return Paint
- */
- public Paint getTextPaint() {
- return textPaint;
- }
+ /**
+ * Get the paint used to paint this nodes text.
+ *
+ * @return Paint
+ */
+ public Paint getTextPaint() {
+ return textPaint;
+ }
- /**
- * Set the paint used to paint this node's text background.
- *
- * @param textPaint
- */
- public void setTextPaint(Paint textPaint) {
- this.textPaint = textPaint;
- invalidatePaint();
- }
+ /**
+ * Set the paint used to paint this node's text background.
+ *
+ * @param textPaint
+ */
+ public void setTextPaint(Paint textPaint) {
+ this.textPaint = textPaint;
+ invalidatePaint();
+ }
- public boolean isConstrainWidthToTextWidth() {
- return constrainWidthToTextWidth;
- }
+ 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
- */
- public void setConstrainWidthToTextWidth(boolean constrainWidthToTextWidth) {
- this.constrainWidthToTextWidth = constrainWidthToTextWidth;
- recomputeLayout();
- }
+ /**
+ * 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
+ */
+ public void setConstrainWidthToTextWidth(boolean constrainWidthToTextWidth) {
+ this.constrainWidthToTextWidth = constrainWidthToTextWidth;
+ recomputeLayout();
+ }
- public boolean isConstrainHeightToTextHeight() {
- return constrainHeightToTextHeight;
- }
+ 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
- */
- public void setConstrainHeightToTextHeight(boolean constrainHeightToTextHeight) {
- this.constrainHeightToTextHeight = constrainHeightToTextHeight;
- recomputeLayout();
- }
+ /**
+ * 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
+ */
+ public void setConstrainHeightToTextHeight(
+ boolean constrainHeightToTextHeight) {
+ this.constrainHeightToTextHeight = constrainHeightToTextHeight;
+ recomputeLayout();
+ }
- /**
- * Returns the current greek threshold. When the screen font size will be
- * below this threshold the text is rendered as 'greek' instead of drawing
- * the text glyphs.
- */
- public double getGreekThreshold() {
- return greekThreshold;
- }
+ /**
+ * Returns the current greek threshold. When the screen font size will be
+ * below this threshold the text is rendered as 'greek' instead of drawing
+ * the text glyphs.
+ */
+ 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
- * text glyphs.
- *
- * @param threshold minimum screen font size.
- */
- public void setGreekThreshold(double threshold) {
- greekThreshold = threshold;
- invalidatePaint();
- }
+ /**
+ * 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
+ * text glyphs.
+ *
+ * @param threshold
+ * minimum screen font size.
+ */
+ public void setGreekThreshold(double threshold) {
+ greekThreshold = threshold;
+ invalidatePaint();
+ }
- public String getText() {
- return text;
- }
+ public String getText() {
+ return text;
+ }
- /**
- * Set the text for this node. The text will be broken up into multiple
- * lines based on the size of the text and the bounds width of this node.
- */
- public void setText(String aText) {
- String old = text;
- text = (aText == null) ? "" : aText;
- lines = null;
- recomputeLayout();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, old, text);
- }
+ /**
+ * Set the text for this node. The text will be broken up into multiple
+ * lines based on the size of the text and the bounds width of this node.
+ */
+ public void setText(String aText) {
+ String old = text;
+ text = (aText == null) ? "" : aText;
+ lines = null;
+ recomputeLayout();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_TEXT, PROPERTY_TEXT, old, text);
+ }
- /**
- * Returns the font of this PText.
- *
- * @return the font of this PText.
- */
- public Font getFont() {
- if (font == null) {
- font = DEFAULT_FONT;
- }
- return font;
- }
+ /**
+ * Returns the font of this PText.
+ *
+ * @return the font of this PText.
+ */
+ 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.
- */
- public void setFont(Font aFont) {
- Font old = font;
- font = aFont;
- lines = null;
- recomputeLayout();
- invalidatePaint();
- firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, old, 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.
+ */
+ public void setFont(Font aFont) {
+ Font old = font;
+ font = aFont;
+ lines = null;
+ recomputeLayout();
+ invalidatePaint();
+ firePropertyChange(PROPERTY_CODE_FONT, PROPERTY_FONT, old, font);
+ }
- private static final TextLayout[] EMPTY_TEXT_LAYOUT_ARRAY = new TextLayout[0];
+ 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.
- */
- public void recomputeLayout() {
- ArrayList linesList = new ArrayList();
- double textWidth = 0;
- double textHeight = 0;
+ /**
+ * Compute the bounds of the text wrapped by this node. The text layout is
+ * wrapped based on the bounds of this node.
+ */
+ public void recomputeLayout() {
+ ArrayList linesList = new ArrayList();
+ double textWidth = 0;
+ double textHeight = 0;
- if (text != null && text.length() > 0) {
- AttributedString atString = new AttributedString(text);
- atString.addAttribute(TextAttribute.FONT, getFont());
- AttributedCharacterIterator itr = atString.getIterator();
- LineBreakMeasurer measurer = new LineBreakMeasurer(itr, PPaintContext.RENDER_QUALITY_HIGH_FRC);
- float availableWidth = constrainWidthToTextWidth ? Float.MAX_VALUE : (float) getWidth();
+ if (text != null && text.length() > 0) {
+ AttributedString atString = new AttributedString(text);
+ atString.addAttribute(TextAttribute.FONT, getFont());
+ AttributedCharacterIterator itr = atString.getIterator();
+ LineBreakMeasurer measurer = new LineBreakMeasurer(itr,
+ PPaintContext.RENDER_QUALITY_HIGH_FRC);
+ float availableWidth = constrainWidthToTextWidth ? Float.MAX_VALUE
+ : (float) getWidth();
- int nextLineBreakOffset = text.indexOf('\n');
- if (nextLineBreakOffset == -1) {
- nextLineBreakOffset = Integer.MAX_VALUE;
- }
- else {
- nextLineBreakOffset++;
- }
+ int nextLineBreakOffset = text.indexOf('\n');
+ if (nextLineBreakOffset == -1) {
+ nextLineBreakOffset = Integer.MAX_VALUE;
+ } else {
+ nextLineBreakOffset++;
+ }
- while (measurer.getPosition() < itr.getEndIndex()) {
- TextLayout aTextLayout = computeNextLayout(measurer, availableWidth, nextLineBreakOffset);
+ while (measurer.getPosition() < itr.getEndIndex()) {
+ TextLayout aTextLayout = computeNextLayout(measurer,
+ availableWidth, nextLineBreakOffset);
- if (nextLineBreakOffset == measurer.getPosition()) {
- nextLineBreakOffset = text.indexOf('\n', measurer.getPosition());
- if (nextLineBreakOffset == -1) {
- nextLineBreakOffset = Integer.MAX_VALUE;
- }
- else {
- nextLineBreakOffset++;
- }
- }
+ if (nextLineBreakOffset == measurer.getPosition()) {
+ nextLineBreakOffset = text.indexOf('\n', measurer
+ .getPosition());
+ if (nextLineBreakOffset == -1) {
+ nextLineBreakOffset = Integer.MAX_VALUE;
+ } else {
+ nextLineBreakOffset++;
+ }
+ }
- linesList.add(aTextLayout);
- textHeight += aTextLayout.getAscent();
- textHeight += aTextLayout.getDescent() + aTextLayout.getLeading();
- textWidth = Math.max(textWidth, aTextLayout.getAdvance());
- }
- }
+ linesList.add(aTextLayout);
+ textHeight += aTextLayout.getAscent();
+ textHeight += aTextLayout.getDescent()
+ + aTextLayout.getLeading();
+ textWidth = Math.max(textWidth, aTextLayout.getAdvance());
+ }
+ }
- lines = (TextLayout[]) linesList.toArray(EMPTY_TEXT_LAYOUT_ARRAY);
+ lines = (TextLayout[]) linesList.toArray(EMPTY_TEXT_LAYOUT_ARRAY);
- if (constrainWidthToTextWidth || constrainHeightToTextHeight) {
- double newWidth = getWidth();
- double newHeight = getHeight();
+ if (constrainWidthToTextWidth || constrainHeightToTextHeight) {
+ double newWidth = getWidth();
+ double newHeight = getHeight();
- if (constrainWidthToTextWidth) {
- newWidth = textWidth;
- }
+ if (constrainWidthToTextWidth) {
+ newWidth = textWidth;
+ }
- if (constrainHeightToTextHeight) {
- newHeight = textHeight;
- }
+ if (constrainHeightToTextHeight) {
+ newHeight = textHeight;
+ }
- super.setBounds(getX(), getY(), newWidth, newHeight);
- }
- }
+ super.setBounds(getX(), getY(), newWidth, newHeight);
+ }
+ }
- // provided in case someone needs to override the way that lines are
- // wrapped.
- protected TextLayout computeNextLayout(LineBreakMeasurer measurer, float availibleWidth, int nextLineBreakOffset) {
- return measurer.nextLayout(availibleWidth, nextLineBreakOffset, false);
- }
+ // provided in case someone needs to override the way that lines are
+ // wrapped.
+ protected TextLayout computeNextLayout(LineBreakMeasurer measurer,
+ float availibleWidth, int nextLineBreakOffset) {
+ return measurer.nextLayout(availibleWidth, nextLineBreakOffset, false);
+ }
- protected void paint(PPaintContext paintContext) {
- super.paint(paintContext);
+ protected void paint(PPaintContext paintContext) {
+ super.paint(paintContext);
- float screenFontSize = getFont().getSize() * (float) paintContext.getScale();
- if (textPaint != null && screenFontSize > greekThreshold) {
- float x = (float) getX();
- float y = (float) getY();
- float bottomY = (float) getHeight() + y;
+ float screenFontSize = getFont().getSize()
+ * (float) paintContext.getScale();
+ if (textPaint == null || screenFontSize <= greekThreshold)
+ return;
- Graphics2D g2 = paintContext.getGraphics();
+ float x = (float) getX();
+ float y = (float) getY();
+ float bottomY = (float) getHeight() + y;
- if (lines == null) {
- recomputeLayout();
- repaint();
- return;
- }
+ Graphics2D g2 = paintContext.getGraphics();
- g2.setPaint(textPaint);
+ if (lines == null) {
+ recomputeLayout();
+ repaint();
+ return;
+ }
- for (int i = 0; i < lines.length; i++) {
- TextLayout tl = lines[i];
- y += tl.getAscent();
+ g2.setPaint(textPaint);
- if (bottomY < y) {
- return;
- }
+ for (int i = 0; i < lines.length; i++) {
+ TextLayout tl = lines[i];
+ y += tl.getAscent();
- float offset = (float) (getWidth() - tl.getAdvance()) * justification;
- tl.draw(g2, x + offset, y);
+ if (bottomY < y) {
+ return;
+ }
- y += tl.getDescent() + tl.getLeading();
- }
- }
- }
+ float offset = (float) (getWidth() - tl.getAdvance())
+ * justification;
+ tl.draw(g2, x + offset, y);
- protected void internalUpdateBounds(double x, double y, double width, double height) {
- recomputeLayout();
- }
+ y += tl.getDescent() + tl.getLeading();
+ }
+ }
- // ****************************************************************
- // Debugging - methods for debugging
- // ****************************************************************
+ protected void internalUpdateBounds(double x, double y, double width,
+ double height) {
+ recomputeLayout();
+ }
- /**
- * Returns a string representing the state of this node. This method is
- * intended to be used only for debugging purposes, and the content and
- * format of the returned string may vary between implementations. The
- * returned string may be empty but may not be null
.
- *
- * @return a string representation of this node's state
- */
- protected String paramString() {
- StringBuffer result = new StringBuffer();
+ // ****************************************************************
+ // Debugging - methods for debugging
+ // ****************************************************************
- result.append("text=" + (text == null ? "null" : text));
- result.append(",font=" + (font == null ? "null" : font.toString()));
- result.append(',');
- result.append(super.paramString());
+ /**
+ * Returns a string representing the state of this node. This method is
+ * intended to be used only for debugging purposes, and the content and
+ * format of the returned string may vary between implementations. The
+ * returned string may be empty but may not be null
.
+ *
+ * @return a string representation of this node's state
+ */
+ protected String paramString() {
+ StringBuffer result = new StringBuffer();
- return result.toString();
- }
+ result.append("text=" + (text == null ? "null" : text));
+ result.append(",font=" + (font == null ? "null" : font.toString()));
+ result.append(',');
+ result.append(super.paramString());
+
+ return result.toString();
+ }
}
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 c29f2e4..81fc8a4 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
@@ -28,18 +28,32 @@
*/
package edu.umd.cs.piccolo.nodes;
-import junit.framework.TestCase;
+import java.awt.Color;
+import java.awt.Font;
+import java.beans.PropertyChangeListener;
-import edu.umd.cs.piccolo.nodes.PText;
+import javax.swing.JLabel;
+
+import edu.umd.cs.piccolo.MockPropertyChangeListener;
+
+import junit.framework.TestCase;
public class PTextTest extends TestCase {
- public PTextTest(String name) {
+ private PText textNode;
+ private MockPropertyChangeListener mockListener;
+
+ public PTextTest(String name) {
super(name);
}
+ public void setUp() {
+ textNode = new PText();
+ mockListener = new MockPropertyChangeListener();
+ }
+
public void testClone() {
- PText textNode = new PText("Boo");
+ textNode.setText("Boo");
PText clonedNode = (PText) textNode.clone();
assertEquals("Boo", clonedNode.getText());
assertEquals(textNode.getFont(), clonedNode.getFont());
@@ -50,16 +64,14 @@
assertEquals("", textNode.getText());
}
- public void testTextMayBeAssignedEmptyString() {
- PText textNode = new PText("Before");
+ public void testTextMayBeAssignedEmptyString() {
textNode.setText("");
assertEquals("", textNode.getText());
}
- public void testTextNullGetsInterpretedAsEmptyString() {
- PText text = new PText("Before");
- text.setText(null);
- assertEquals("", text.getText());
+ public void testTextNullGetsInterpretedAsEmptyString() {
+ textNode.setText(null);
+ assertEquals("", textNode.getText());
}
@@ -75,16 +87,78 @@
}
public void testBoundsOfEmptyString() {
- PText t = new PText();
- t.setText("");
- assertEquals(0, t.getBoundsReference().getWidth(), 0.000001);
- t.setText(null);
- assertEquals(0, t.getBoundsReference().getWidth(), 0.000001);
+ textNode.setText("");
+ assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001);
+ textNode.setText(null);
+ assertEquals(0, textNode.getBoundsReference().getWidth(), 0.000001);
}
public void testToString() {
- PText t = new PText();
- t.setText("hello world");
- assertNotNull(t.toString());
+ textNode.setText("hello world");
+ assertNotNull(textNode.toString());
+ }
+
+ public void testJustificationIsLeftByDefault() {
+ assertEquals(JLabel.LEFT_ALIGNMENT, textNode.getJustification(), 0.000001);
+ }
+
+ public void testSetJustificationPersists() {
+ textNode.setJustification(JLabel.RIGHT_ALIGNMENT);
+ assertEquals(JLabel.RIGHT_ALIGNMENT, textNode.getJustification(), 0.000001);
+ }
+
+ public void testTextPaintIsBlackByDefault() {
+ assertEquals(Color.BLACK, textNode.getTextPaint());
+ }
+
+ public void testSetTextPaintPersists() {
+ textNode.setTextPaint(Color.RED);
+ assertEquals(Color.RED, textNode.getTextPaint());
+ }
+
+ public void testConstrainWidthToTextTrueByDefault() {
+ assertTrue(textNode.isConstrainWidthToTextWidth());
+ }
+
+ public void testConstrainHeightToTextTrueByDefault() {
+ assertTrue(textNode.isConstrainHeightToTextHeight());
+ }
+
+ public void testConstrainWidthPersists() {
+ textNode.setConstrainWidthToTextWidth(true);
+ assertTrue(textNode.isConstrainWidthToTextWidth());
+ }
+
+ public void testConstrainHeightPersists() {
+ textNode.setConstrainHeightToTextHeight(true);
+ assertTrue(textNode.isConstrainHeightToTextHeight());
+ }
+
+ public void testDefaultGreekThreshold() {
+ assertEquals(PText.DEFAULT_GREEK_THRESHOLD, textNode.getGreekThreshold(), 0.000001);
+ }
+
+ public void testSetGreekThreshold() {
+ textNode.setGreekThreshold(2);
+ assertEquals(2, textNode.getGreekThreshold(), 0.000001);
+ }
+
+ public void testDefaultFont() {
+ assertEquals(PText.DEFAULT_FONT, textNode.getFont());
+ }
+
+ public void testSetFontPersists() {
+ Font newFont = new Font("Arial", Font.BOLD, 10);
+ textNode.setFont(newFont);
+ assertEquals(newFont, textNode.getFont());
+ }
+
+ public void testSetFontFiresPropertyChangedEvent() {
+ textNode.addPropertyChangeListener(PText.PROPERTY_FONT, mockListener);
+ Font newFont = new Font("Arial", Font.BOLD, 10);
+ textNode.setFont(newFont);
+
+ assertEquals(1, mockListener.getPropertyChangeCount());
+ assertEquals(PText.PROPERTY_FONT, mockListener.getPropertyChange(0).getPropertyName());
}
}