Scenario available at http://www.greenfoot.org/scenarios/11766
I've attempted to hack Ed Parrish's GUI to produce a text based "calculator". The goal was something "simple" using GUI elements - I now know that's not really possible, but I'd like to keep it relatively simple.
Initially, I want to capture user input (Weight) and output it to the lable below.
I can't work out how to use the "<code>getText()</code>" he talks about in the in-line documentation.
I'd appreciate any help.
Textfield
GUI World
Label
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextHitInfo; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; /** * Class <code>TextField</code> is a Greenfoot component that allows the user * to enter a single line of text. * * When users are finished entering text, they can signal they are finished by * pressing the Enter key. Pressing the Enter key will fire the ActionListener * interface. * * The <code>ActionListener</code> interface is like the * <code>ActionListener</code> interface in Java Swing. Scenarios using the * <code>ActionListener</code> callback must include the * <code>ActionListener</code> interface, and the class listening for button * clicks must implement that interface. In addition, the implementing class * must add itself to the callback list by calling the * <code>addActionListener()</code> method. Then when the Enter key is pressed, * the <code>actionPerformed()</code> method in the listening class will be * called. * * The developer can read the text the user entered by calling the * <code>getText()</code> method. * * @author Ed Parrish * @version 1.0 12/28/2010 * @see http://java.sun.com/developer/onlineTraining/Media/2DText/index.html */ public class TextField extends GUIComponent { private static final Color CARET_COLOR = Color.RED; private static final Color HIGHLIGHT_COLOR = Color.PINK; private static final long KEYSTOKE_DELAY = 110; private static final long BLINK_DELAY = 400; // Padding to leave small horizontal gap between box edge and cursor private static final int X_PAD = 1; // Input text buffer private StringBuilder input; // Width of the input field private int numCols; // TextLayout is used for drawing and measuring. Since it is expensive to // create, cache it and invalidate it when text is modified. private TextLayout layout = null; private boolean validTextLayout = false; // Timer variable used to prevent keystrokes from going too fast private long lastKeystrokeTime; // The main insertion index. private int index1 = 0; // The second insertion index used for mouse selection private int index2; // The x-coordinate of the origin relative to the left side of the component private float originX; // The y-coordinate of the origin relative to the top of the component private float originY; // Component with the cursor showing private GreenfootImage cursorOn; // Component without the cursor showing private GreenfootImage cursorOff; // Timer variable to control blinking cursor private long lastBlinkTime; // Variable to control blink state private boolean blinkOn; /** * Constructs a text field with no text and with a width of one column. */ public TextField() { this ("", 1); } /** * Constructs a text field with the initial text displayed in the default * text color and font and with a width of one column. * * @param text The initial text to display on the text field. */ public TextField(String text) { this (text, 1); } /** * Constructs a text field with no text and the specified number of * columns. * * @param columns Number of columns to calculate the width. */ public TextField(int columns) { this ("", columns); } /** * Constructs a text field with the specified number of columns and the * initial text displayed in the default text color and font. * * @param text The initial text to display on the text field. * @param columns Number of columns to calculate the width. */ public TextField(String text, int columns) { this (text, columns, DEFAULT_FONT, DEFAULT_FG, Color.WHITE); } /** * Constructs a text field of the specified width in columns with the * initial text displayed in the specified font and default text color. * * @param text The initial text to display on the text field. * @param columns Number of columns to calculate the width. * @param font The font used to write on this component. */ public TextField(String text, int columns, Font font) { this (text, columns, font, DEFAULT_FG, Color.WHITE); } /** * Constructs a text field of the specified width in columns with the * initial text displayed in the specified font and text color on the * specified background color. * * @param text The initial text to display on the text field. * @param columns Number of columns to calculate the width. * @param font The font used to write on this component. * @param fg The desired foreground color. * @param bg The desired background color. */ public TextField(String text, int columns, Font font, Color fg, Color bg) { super(text, font, fg, bg, null); numCols = columns; input = new StringBuilder(text); super.repaint(); originX = getInsets().left + X_PAD; } /** * Handle keystrokes and mouse actions for this component. */ public void act() { super.act(); // check for focus request if (!isEnabled()) return; // Move caret using mouse pointer if (Greenfoot.mousePressed(this)) { MouseInfo e = Greenfoot.getMouseInfo(); if (e != null && layout != null) { index1 = getHitPosition(e.getX(), e.getY()); index2 = index1; } // Repaint the component so the new caret will be displayed. super.repaint(); } // Highlight text with mouse pointer if (Greenfoot.mouseDragged(this)) { MouseInfo e = Greenfoot.getMouseInfo(); if (e != null && layout != null) { index2 = getHitPosition(e.getX(), e.getY()); } // Repaint the component so the highlight will be displayed. super.repaint(); } // Ignore keystrokes if not the focus owner if (!isFocusOwner()) return; // Blink the cursor if (System.currentTimeMillis() - lastBlinkTime > BLINK_DELAY) { lastBlinkTime = System.currentTimeMillis(); blinkOn = !blinkOn; if (blinkOn) { setImage(cursorOn); } else { setImage(cursorOff); } } // Slow down keystrokes on fast systems if (System.currentTimeMillis() - lastKeystrokeTime < KEYSTOKE_DELAY) { return; } lastKeystrokeTime = System.currentTimeMillis(); // Process keystrokes TextHitInfo newPosition = null; String key = Greenfoot.getKey(); if (key != null && key.length() == 1) { // Single character keystrokes like: 'a'...'z', '0'...'9' deleteHighlighedChars(); input.insert(index1, key); index1++; index2 = index1; repaint(); } else if (Greenfoot.isKeyDown("space")) { deleteHighlighedChars(); input.insert(index1, " "); index1++; index2 = index1; repaint(); } else if (Greenfoot.isKeyDown("backspace")) { if (index1 != index2) { deleteHighlighedChars(); repaint(); } else if (input.length() > 0 && index1 > 0) { --index1; input.deleteCharAt(index1); index2 = index1; repaint(); } } else if (Greenfoot.isKeyDown("enter")) { fireActionEvent(); } else if (Greenfoot.isKeyDown("right") && layout != null) { newPosition = layout.getNextRightHit(index2); } else if (Greenfoot.isKeyDown("left") && layout != null) { newPosition = layout.getNextLeftHit(index1); } if (newPosition != null) { index1 = newPosition.getInsertionIndex(); index2 = index1; super.repaint(); } } /** * Set the text to be displayed. * * @param newText The new text to be displayed on the button. */ public void setText(String newText) { if (newText == null) { input.replace(0, input.length(), ""); } else { input.replace(0, input.length(), newText); } index1 = 0; index2 = index1; repaint(); } /** * Returns the text for this component. * * @return The text displayed on this component. */ public String getText() { return input.toString(); } /** * Sets the number of columns in this <code>TextField</code>. * * @param columns the number of columns >= 0 * @exception IllegalArgumentException if <code>columns</code> * is less than 0 */ public void setColumns(int columns) { int oldVal = numCols; if (columns < 0) { throw new IllegalArgumentException("columns less than zero."); } if (numCols != oldVal) { numCols = columns; repaint(); } } /** * Returns the number of columns in this <code>TextField</code>. * * @return the number of columns >= 0. */ public int getColumns() { return numCols; } /** * Paints the component image, including the background, border and text. * * This method recreates the text layout. To repaint without recreating * the text layout, call <code>super.repaint()</code>. */ public void repaint() { validTextLayout = false; super.repaint(); } /** * Paints the text field's background image. * * @param g The <code>Graphics</code> context in which to paint. */ public void paintComponent(Graphics g) { GreenfootImage img = null; if (isFixedSize()) { img = new GreenfootImage(getWidth(), getHeight()); } else { // Calculate image size based on text height & numCols FontMetrics fm = g.getFontMetrics(); String text = getText(); if (text == null) text = ""; int width = fm.charWidth('m') * numCols; if (width <= 0) width = 1; int height = fm.getHeight(); Insets insets = getInsets(); if (insets != null) { width += insets.left + insets.right; height += insets.top + insets.bottom; } img = new GreenfootImage(width, height); } if (getBackground().getTransparency() > 0) { img.setColor(getBackground()); img.fill(); } // Paint box around the text field if no border supercedes it if (getBorder() == null) { img.setColor(Color.BLACK); img.drawRect(0, 0, img.getWidth() - 1, img.getHeight() - 1); // Draw thicker line if this component has the focus if (isFocusOwner()) { img.drawRect(1, 1, img.getWidth() - 3, img.getHeight() - 3); } } setImage(img); } /** * Paints the text onto the labels background image. * * @param g The <code>Graphics</code> context in which to paint. */ public void paintText(Graphics g) { Graphics2D g2 = (Graphics2D) g; // Draw the text that the user has entered TextLayout tl = getTextLayout(g2); if (tl != null) { scrollToVisible(); AffineTransform at = AffineTransform.getTranslateInstance(originX, originY); if (isFocusOwner()) { // Draw the highlight Shape hilight = tl.getLogicalHighlightShape(index1, index2); hilight = at.createTransformedShape(hilight); g2.setColor(HIGHLIGHT_COLOR); g2.fill(hilight); } if (getForeground() != null) g2.setColor(getForeground()); tl.draw(g2, originX, originY); cursorOff = new GreenfootImage(getImage()); if (isFocusOwner()) { // Draw the caret at the insertion index Shape[] carets = tl.getCaretShapes(index2); g2.setColor(CARET_COLOR); Shape caret = at.createTransformedShape(carets[0]); g2.draw(caret); } cursorOn = getImage(); } else if (isFocusOwner()) { // Draw a caret for the empty box cursorOff = new GreenfootImage(getImage()); g2.setColor(CARET_COLOR); Insets i = getInsets(); g2.drawLine(i.left + X_PAD, i.top, i.right + X_PAD, getHeight() - i.bottom); cursorOn = getImage(); } } /** * Scrolls the field left or right until it is visible. */ private void scrollToVisible() { if (layout == null) return; Insets i = getInsets(); int xMin = i.left + X_PAD; int xMax = getWidth() - i.right - X_PAD; int cursorX = layout.getCaretShapes(index2)[0].getBounds().x; float adjustX = layout.getAdvance() - xMax + originX; if (originX < xMin && adjustX < 0) { // adjust right margin when characters deleted originX = originX - adjustX; if (originX > xMin) originX = xMin; } if (cursorX < xMin - originX) { // Scroll to the left originX = xMin - cursorX; } else if (cursorX > xMax - originX) { // Scroll to the right originX = xMax - cursorX; } } /** * Returns a text layout for the text that the user has entered. The text * layout is cached until <code>invalidateTextLayout</code> is called. * * @param g the <code>Graphics</code> context in which to paint. * @return a <code>TextLayout</code> for the text that the user has * entered, or <code>null</code> if no text has been entered. */ private TextLayout getTextLayout(Graphics g) { if (!validTextLayout) { layout = null; if (input.length() > 0) { Graphics2D g2 = (Graphics2D) g; FontRenderContext frc = g2.getFontRenderContext(); layout = new TextLayout(getText(), getFont(), frc); originY = (getHeight() + layout.getAscent() - layout.getDescent()) / 2; } } validTextLayout = true; return layout; } /** * Computes the character position of the mouse cursor. * * @param mouseX The x-coordinate of the mouse pointer. * @param mouseY The y-coordinate of the mouse pointer. * @return The character position of the mouse cursor. */ private int getHitPosition(int mouseX, int mouseY) { // Compute mouse click location relative to text layout's origin float x = mouseX - getX() + getWidth() / 2 - originX; float y = mouseY - getY() + getHeight() / 2; // Get the character position of the mouse click. TextHitInfo hit = layout.hitTestChar(x, y); return hit.getInsertionIndex(); } /** * Deletes the characters within the highlighted area. */ private void deleteHighlighedChars() { if (index2 > index1) { input.delete(index1, index2); index2 = index1; } else if (index1 > index2) { input.delete(index2, index1); index1 = index2; } } }
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.awt.Color; import java.awt.Dimension; import java.awt.Font; /** * Tests for GUI components in various configurations. * * @author Ed Parrish * @version 1.0 1/4/2011 */ public class GUIWorld extends World implements ActionListener { public static final Font LARGE_FONT = new Font("SansSerif", Font.BOLD, 20); public static final Font MED_FONT = new Font("SansSerif", Font.ITALIC, 14); public static final Font SMALL_FONT = new Font("SansSerif", Font.PLAIN, 10); private Label defaultLabel; private Label label1; private TextField defaultTF; private Label label2; /** * Constructor for objects of class GUIWorld. */ public GUIWorld() { // Create a new world with 600x400 cells with a cell size of 1x1 pixels. super(600, 430, 1); // Test labels // Following use of GreenfootImage works with version 2.0.1 or later // Label label = new Label(new GreenfootImage("Various labels", 16, // Color.BLACK, new Color(0, 0, 0, 0))); defaultLabel = new Label("Weight in Kg"); addObject(defaultLabel, 65, 83); label1 = new Label("Weight for Age"); addObject(label1, 67, 159); label1.setSize(new Dimension(75, 50)); label2 = new Label(""); addObject(label2, 165, 159); label2.setBackground(Color.WHITE); label2.setSize(new Dimension(75, 50)); // Text fields defaultTF = new TextField("", 8); defaultTF.addActionListener(this); defaultTF.requestFocus(); addObject(defaultTF, 164, 84); defaultTF.setSize(new Dimension(75, 50)); } // Test the isPressed() method of Button public void act() { } // Test the actionPerformed() callback public void actionPerformed(GUIComponent c) { } }
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; /** * A label with a transparent background for writing text. * * @author Ed Parrish * @version 1.1 12/25/2010 */ public class Label extends GUIComponent { // FontMetrics makes line height too high so reduce it private static final double ASCENT_MULT = .90; // Label backgrounds are transparent by default private static final Color TRANSPARENT = new Color(0, 0, 0, 0); // Flag to test if background image was set by user private boolean hasBGImage; /** * Constructs a label with the specified background image. The image may * be written upon by calling <code>setText()</code>. * * @param img The background image to display. */ public Label(GreenfootImage img) { hasBGImage = true; setImage(img); setFocusable(false); } /** * Constructs a label with the specified text displayed in the default text * color and font on a transparent background without a border. * * @param text The text to display on the label */ public Label(String text) { this (text, DEFAULT_FONT, DEFAULT_FG); } /** * Constructs a label with the specified text and font using the default * text color on a transparent background without a border. * * @param text The text to display on the label * @param font The font with which to write the text. */ public Label(String text, Font font) { this (text, font, DEFAULT_FG); } /** * Constructs a label with text of the specified color using the default * font on a transparent background without a border. * * @param text The text to display on the label * @param textColor The color to use for displaying text. */ public Label(String text, Color textColor) { this (text, DEFAULT_FONT, textColor); } /** * Constructs a label with text of the specified color and font on a * transparent background without a border. * * @param text The text to display on the label * @param font The font with which to display the text. * @param textColor The color to use for displaying text. */ public Label(String text, Font font, Color textColor) { super(text, font, textColor, TRANSPARENT); setFocusable(false); setBorder(null); // calls repaint } /** * Constructs a label with text of the specified color on a background * of the specified color with a border. * * @param text The text to display on the label * @param font The font with which to display the text. * @param textColor The color to use for displaying text. * @param bgColor The desired background color. */ public Label(String text, Font font, Color textColor, Color bgColor) { super(text, font, textColor, bgColor); setFocusable(false); repaint(); } /** * Prepares the labels background image. * * @param g The <code>Graphics</code> context in which to paint. */ public void paintComponent(Graphics g) { if (hasBGImage) return; GreenfootImage img = null; if (isFixedSize()) { img = new GreenfootImage(getWidth(), getHeight()); } else { // Create image sized for text width and height Dimension d = getTextDimension(getText(), g); img = new GreenfootImage(d.width, d.height); } if (getBackground() != TRANSPARENT) { img.setColor(getBackground()); img.fill(); } setImage(img); } /** * Paints the text onto the labels background image. * * @param g The <code>Graphics</code> context in which to paint. */ public void paintText(Graphics g) { String labelText = getText(); if (labelText == null || labelText.length() == 0) return; String[] lines = splitLines(labelText, g); GreenfootImage img = getImage(); if (getFont() != null) img.setFont(getFont()); if (getForeground() != null) img.setColor(getForeground()); FontMetrics fm = g.getFontMetrics(); int lineHeight = (int) (fm.getHeight() * ASCENT_MULT); int y = lineHeight + (getHeight() - (lineHeight * lines.length) - fm.getDescent()) / 2; for (int i = 0; i < lines.length; i++) { int x = getWidth() / 2 - fm.stringWidth(lines[i]) / 2; img.drawString(lines[i], x, y); y += lineHeight; } } }