JFormattedTextField is not properly cleared
Asked Answered
S

3

2

I am doing this assignment, make a program that solves sudoku. I have a panel with a grid of SudokuTextBox extends JFormattedTextField. I have a MaskFormatter so that it only accepts one integer per text box. Then in my panel I have this code when a key is relesed.

 public void keyReleased(KeyEvent e) {
  SudokuTextBox tb = (SudokuTextBox) e.getSource();
  int row = tb.getRow();
  int col = tb.getCol();
  int value = toInteger(tb.getText());
  //System.out.println(value);
  if(sudoku.isValid(row, col, value)) {
   sudoku.set(row, col, value);
  }
  else {
   sudoku.set(row, col, 0);
   tb.setText(null);
  }
  tb.setCaretPosition(0);
  sudoku.print();
 }

The thing is, if i put a valid value in a text box, then i go back and enter an invalid value (by the rules of sudoku) the text box is cleared. But then when I tab forward the previous valid value is displayed in the text box. My sudokumatrix that contains all the numbers that has been inputed do clear the value like it should so it is only in the corresponding text box.

To make matters even more confusing when i change "SudokuTextBox extends JFormattedTextField" to "SudokuTextBox extends JTextField" it works like a charm. But i cant set the size of JTextField so that it is square and I can not enforce only one integer per text box.

Am I missing something really obvious?

Stichomythia answered 10/11, 2010 at 19:36 Comment(0)
S
2

Ok so now I found it, "One of the shortcomings of the mask formatter is that as of the current implementation (Java 5), it has no support for letting a user revert a field to the blank value (the initial value of the field prior to any user input) once they have left the field at any point."

So since I am using a MaskFormatter I cannot clear the field.

Stichomythia answered 10/11, 2010 at 19:51 Comment(0)
B
6

Here's an example of a resizable component that might be suitable for such a game. Although it contains no game logic, it handles input reasonably well. Clicking the mouse or pressing the space bar pop's up a menu, and the tab and number keys work as expected. In particular, Digit.EMPTY is a valid value.

alt text

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import javax.swing.*;

/** @see http://stackoverflow.com/questions/4148336 */
public class CellTest extends JPanel {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            //@Override
            public void run() {
                createGUI();
            }
        });
    }

    public static void createGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new GridLayout(3, 3));
        for (Digit d : Digit.digits) {
            frame.add(new CellTest(d));
        }
        frame.pack();
        frame.setVisible(true);
    }

    CellTest(Digit digit) {
        this.setLayout(new BorderLayout());
        this.setBorder(BorderFactory.createLineBorder(Color.black, 1));
        this.setBackground(new Color(0x00e0e0));

        JLabel candidates = new JLabel("123456789");
        candidates.setHorizontalAlignment(JLabel.CENTER);
        this.add(candidates, BorderLayout.NORTH);

        JDigit cellValue = new JDigit(digit);
        add(cellValue, BorderLayout.CENTER);
    }
}

class JDigit extends JButton {

    private static final int SIZE = 128;
    private static final int BASE = SIZE / 32;
    private static final Font FONT = new Font("Serif", Font.BOLD, SIZE);
    private JPopupMenu popup = new JPopupMenu();
    private Digit digit;
    private Image image;
    private int width, height;

    public JDigit(Digit digit) {
        this.digit = digit;
        this.image = getImage(digit);
        this.setPreferredSize(new Dimension(64, 64));
        this.setBackground(new Color(0xe0e000));
        this.setForeground(Color.black);
        this.setBorderPainted(false);
        this.setAction(new ButtonAction());
        this.addFocusListener(new FocusHandler());
        for (Digit d : Digit.values()) {
            Action select = new SelectAction(d);
            JMenuItem item = new JMenuItem(select);
            getInputMap().put(KeyStroke.getKeyStroke(
                KeyEvent.VK_0 + d.value(), 0), d.toString());
            getInputMap().put(KeyStroke.getKeyStroke(
                KeyEvent.VK_NUMPAD0 + d.value(), 0), d.toString());
            getActionMap().put(d.toString(), select);
            popup.add(item);
        }
    }

    public Digit getDigit() {
        return digit;
    }

    public void setDigit(Digit digit) {
        this.digit = digit;
        this.image = getImage(digit);
        this.repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        int w = this.getWidth();
        int h = this.getHeight();
        g.setColor(this.getBackground());
        int dx1 = w * width / height / 4;
        int dx2 = w - dx1;
        g.fillRect(dx1, 0, dx2 - dx1, h);
        g.drawImage(image,
            dx1, 0, dx2, h,
            0, 0, width, height, null);
    }

    private Image getImage(Digit digit) {
        BufferedImage bi = new BufferedImage(
            SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(this.getForeground());
        g2d.setFont(FONT);
        FontMetrics fm = g2d.getFontMetrics();
        width = fm.stringWidth(digit.toString());
        height = fm.getAscent();
        g2d.drawString(digit.toString(), 0, height - BASE);
        g2d.dispose();
        return bi;
    }

    private class ButtonAction extends AbstractAction {
        //@Override
        public void actionPerformed(ActionEvent e) {
            popup.show(JDigit.this, getWidth() - width, getHeight() / 2);
        }
    }

    private class SelectAction extends AbstractAction {

        private Digit digit;

        public SelectAction(Digit digit) {
            this.digit = digit;
            this.putValue(Action.NAME, digit.toString());
        }

        //@Override
        public void actionPerformed(ActionEvent e) {
            setDigit(digit);
        }
    }

    private class FocusHandler implements FocusListener {

        private Color background = getBackground();

        //@Override
        public void focusGained(FocusEvent e) {
            setBackground(background.brighter());
        }

        //@Override
        public void focusLost(FocusEvent e) {
            setBackground(background);
        }
    }
}

enum Digit {

    EMPTY(0, " "), ONE(1, "1"), TWO(2, "2"), THREE(3, "3"), FOUR(4, "4"),
    FIVE(5, "5"), SIX(6, "6"), SEVEN(7, "7"), EIGHT(8, "8"), NINE(9, "9");
    public static EnumSet<Digit> digits = EnumSet.range(Digit.ONE, Digit.NINE);
    private int i;
    private String s;

    Digit(int i, String s) {
        this.i = i;
        this.s = s;
    }

    @Override
    public String toString() {
        return s;
    }

    public int value() {
        return i;
    }
}
Bordeaux answered 11/11, 2010 at 4:39 Comment(2)
I'm sorry for not accepting your answer. I was however more interested in why I could not get JFormattedTextField to do what I wanted it to do than how to roll my own component. Thanks though!Stichomythia
@refuser: No problem; I was exploring key binding.Bordeaux
S
2

Ok so now I found it, "One of the shortcomings of the mask formatter is that as of the current implementation (Java 5), it has no support for letting a user revert a field to the blank value (the initial value of the field prior to any user input) once they have left the field at any point."

So since I am using a MaskFormatter I cannot clear the field.

Stichomythia answered 10/11, 2010 at 19:51 Comment(0)
D
0

Although not the identical question, I was searching through the (too many) questions like this one. In my case I wanted to be able to fill two other fields (jtfStarePatReq and jtfStarePatFound which are JTextFields) either by looking up an index directly or by using some spinners and creating a string and then looking that string up (okay, maybe that is too vague but I think it is enough context). What I wanted is that if the user deleted or cleared out the value in the JFormattedTextField jftfStareOpsIndex, then the other two fields would be cleared as well. I was using the .isEmpty() method on the JFormattedTextField to decide if I should use that field OR use the longer computed search method. So I NEED it to be empty if someone goes from looking up their own index to letting the software search for the index. Anyway, I tried catching the commitEdit() exception and setting the value to null and it seems to do the trick.

public class stareOpsIndexListener extends KeyAdapter implements FocusListener {

    public void focusGained(FocusEvent e) {
    }

    public void focusLost(FocusEvent e) {
        try {
            JFormattedTextField jftf = (JFormattedTextField) e.getComponent();
            jftf.commitEdit();
            updateStareOpsIndex(jftf);
        } catch (ParseException ex) {
                jtfStarePatReq.setText("");
                jtfStarePatFound.setText("");
                jftfStareOpsIndex.setValue(null);
        }
    }

    public void keyPressed(KeyEvent e) {

        int key = e.getKeyCode();

        if (key == KeyEvent.VK_ENTER) {
            try {
                JFormattedTextField jftf = (JFormattedTextField) e.getComponent();
                jftf.commitEdit();
                updateStareOpsIndex(jftf);
            } catch (ParseException ex) {
                jtfStarePatReq.setText("");
                jtfStarePatFound.setText("");
                jftfStareOpsIndex.setValue(null);
            }
        }
    }
}
Dysuria answered 24/7, 2013 at 16:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.