How to set a Mask to a SWT Text to only allow Decimals
Asked Answered
G

4

4

What I want is that the user can only input decimal numbers on a Text, I don't want it to allow text input as:

  1. HELLO
  2. ABC.34
  3. 34.HEY
  4. 32.3333.123

I have been trying using VerifyListener, but it only gives me the portion of the text that got inserted, so I end up having the text that I want to insert and the text before the insertion, tried also combining the text, but I got problems when you delete a key (backspace) and I end up having a String like 234[BACKSPACE]455.

Is there a way to set a Mask on a Text or successfully combine VerifyEvent with the current text to obtain the "new text" before setting it to the Text?

George answered 6/8, 2012 at 16:6 Comment(2)
I edited my answer. This fixes the problem you were facing with not having the complete text, but just the inserted and the old text. Give it a try.Rampageous
About your problem with the complete text you could use a ModifyListener.Holmes
R
13

You will have to add a Listener on the Text using SWT.Verify. Within this Listener you can verify that the input contains only a decimal number.

The following will only allow the insertion of decimals into the text field. It will check the value each time you change something in the text and reject it, if it's not a decimal. This will solve your problem, since the VerifyListener is executed BEFORE the new text is inserted. The new text has to pass the listener to be accepted.

public static void main(String[] args) {
    Display display = Display.getDefault();
    final Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    
    final Text textField = new Text(shell, SWT.BORDER);
    
    textField.addVerifyListener(new VerifyListener() {
        
        @Override
        public void verifyText(VerifyEvent e) {
            
            Text text = (Text)e.getSource();
            
            // Get old text and create new text by using the VerifyEvent.text
            final String oldS = text.getText();
            String newS = oldS.substring(0, e.start) + e.text + oldS.substring(e.end);
            
            try
            {
                Float.parseFloat(newS);
            }
            catch(NumberFormatException ex)
            {
                // Prevent the input from happening, as it's not a float
                e.doit = false;
            }
            
            System.out.println(newS);
        }
    });
    
    shell.pack();
    shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch())
            display.sleep();
    }
}
Rampageous answered 6/8, 2012 at 16:12 Comment(13)
Thanks, but i already tried with the VerifyListener, with that i can only verify what is being input, i want to verify what was in the text plus what is being input as a whole, if i only allow digits i won't be able to write decimals as 123.123, and if i allow also the dot ('.') they could write text as 32.3333.123, that's why i wanted to verify the whole text.George
Sorry, my problem is not the validation, my problem is that i cannot find where to put the validation, basically i need to obtain the String that has thew new value BEFORE it is actually written on the Text, i tried combining both the oldText and the VerifyEvent's text but i had problems with backspaces and deletes.George
@George Just use the code I provided above in the SWT.Verify Listener. You should be able to figure it out now, gotta go...Rampageous
Thanks, though i was already using the SWT VerifyListener (as stated in the question), maybe is just not possible to do.George
@George Ok, last try. Edited answer. It will only allow decimals in the text. the user has no change of getting a non-decimal into the field. If this is still not what you want, I would suggest clarifying your answer as I am obviously not able to understand it correctly,Rampageous
Sorry, that does not work, as in my question i already tried that and was facing problems when the users pressed "backspace" (i ended up having "123\b3.3") and couldn't work that out.George
@George Did you even try the code?? It does work. There is no "backspace" symbol shown in the text.Rampageous
Sorry for being so hasty on the answer, you are right, there is no backspace in your implementation, i don't know where did the \b appear in my code. thanks for your help.George
@George Great, have fun with it :)Rampageous
while typing, a "partial expression" like "4." or "5e" may occur. That type of expression should be valid while typing. However It can not be parsed to a float value with Float.parseFloat(expression). The individual characters should be checked and only digits and ,.eE+- have to be allowed.Procambium
I suggest a condition to allow clear the text input if(! "".equals(newS)) Float.parseFloat(newS); }Abstraction
This allows for numbers that start with a 0 like 01234Twink
@Twink That's because 01234 is a valid number.Rampageous
T
2

Have you tried the FormattedText Widgets from Nebula? - They are an easy way to implement these kind of input fields, see http://eclipse.org/nebula/widgets/formattedtext/formattedtext.php

Tectrix answered 7/8, 2012 at 6:19 Comment(1)
Nebula Formattedtext is still in Alpha... i needs a full revision / rewrite, mainly in the DATE and TIME formatters (way too much code there, omg!). Still buggy, and i wish there were better alternatives... SWT sucks in the formatting department.Donothing
P
2

In order to get the behavior that I wanted I had to use several listeners:

  • A VerifyListner restricts the characters that are accepted as partial input while typing
  • A FocusListener validates the total input when leaving focus. If the total input is not valid, an error decoration will be shown.
  • A ModifyListener checks if the error decoration can be hidden while typing. It does not show the error decoration since invalid partial input like "4e-" is allowed to finally enter "4e-3"

    valueField.addVerifyListener((event) -> restrictInput(event));
    valueField.addModifyListener((event) -> validateValueOnChange(valueField.getText()));
    
    valueField.addFocusListener(new FocusListener() {
        @Override
        public void focusGained(org.eclipse.swt.events.FocusEvent e) {}
        @Override
        public void focusLost(org.eclipse.swt.events.FocusEvent event) {
            validateValueOnFocusLoss(valueField.getText());
        }
    });
    
    protected void restrictInput(VerifyEvent event) {
        String allowedCharacters = "0123456789.,eE+-";
        String text = event.text;
        for (int index = 0; index < text.length(); index++) {
            char character = text.charAt(index);
            boolean isAllowed = allowedCharacters.indexOf(character) > -1;
            if (!isAllowed) {
                event.doit = false;
                return;
            }
        }
    }
    
    protected void validateValueOnChange(String text) {
        try {
            Double.parseDouble(valueField.getText());
            valueErrorDecorator.hide();
        } catch (NumberFormatException exception) {
            //expressions like "5e-" are allowed while typing
        }
    }
    
    protected void validateValueOnFocusLoss(String value) {
        try {
            Double.parseDouble(valueField.getText());
            valueErrorDecorator.hide();
        } catch (NumberFormatException exception) {
            valueErrorDecorator.show();
        }
    }
    

The ModifyListener could be further improved to check for partial input that is not able to finally give a valid total input, e.g. "4e-....3". In that special case the ModifyListener should activate the error decoration while typing.

Procambium answered 17/3, 2016 at 18:6 Comment(0)
H
0

In addition to @Tom Seidel's answer:
You could use a org.eclipse.swt.widgets.Spinner. This allows only digits. You can specify min and max value and the return value is an int so no need to cast a String.

final Composite composite parent = new Composite(superParent, SWT.NONE);
parent.setLayout(new FillLayout());
final Spinner spinner = new Spinner(parent, SWT.BORDER);
spinner.setvalues(0, 10, Integer.MAX_VALUE, 0, 1, 10);

The value of the Spinner can than be retrieved by calling:

int selectedValue = spinner.getSelection();
Holmes answered 3/3, 2017 at 11:52 Comment(1)
Thanks, it could work on some cases. I was trying to parse (AFAIK) a Decimal number and this could be hard to use for that.George

© 2022 - 2024 — McMap. All rights reserved.