4

How can I restrict a users input to be only numeric (allowing decimals) when typing into certain fields in a desktop application?

~Roger

Monte Goulding
  • 2,380
  • 1
  • 21
  • 25
Roger Eller
  • 113
  • 1
  • 6

8 Answers8

5

Note that Ben's answer doesn't cover every case

  • doesn't allow negative numbers
  • doesn't detect the case where you type "." and there is already a trailing "." in the field
  • doesn't handle pasting into the field.

You can do all those with

local sExisting

on openfield
   put the text of me into sExisting
end openfield

on textChanged
   put the selectedText && sExisting && the text of me into msg
   if the text of me is a number or me is empty then
      put the text of me into sExisting
   else
      put sExisting into me
   end if
end textChanged

NB - if you type an invalid character, the input cursor is moved to the start of the line; if you want to do anything different then you can lockscreen (as the first action in 'textChanged') and unlock when you are done.

Alex Tweedly
  • 114
  • 1
4

A keydown message is sent to your field everytime a user presses a key:

on keydown pKey
  -- check the input --
  -- pass on the keydown message --
end keydown

Add the following code to check that the key typed is a number of a "." and that the field doesn't already contain a ".":

on keydown pKey
   if pKey is a number then 
      pass keydown
   else if pKey is "." then
      set the itemdel to "."
      if the number of items of me < 2 then
         pass keydown
      end if
   end if
end keydown
Benjamin Beaumont
  • 910
  • 1
  • 6
  • 14
2

Alex, I like your your script, but it doesn't cover every case also. It's not working if you want to type negative number (if you start with "-" sign in empty field). So below is corrected script, I hope this time cover every case, but it losing selection if you type not allowed character (something to do there yet).

local sExisting, sSelected

on openfield
   put the text of me into sExisting
end openfield

on textChanged
   --put the selectedText && sExisting && the text of me into msg
   put the selectedChunk into sSelected
   if the text of me is a number or me is empty or me is "-" then
      put the text of me into sExisting
   else
      put sExisting into me
   end if
   select sSelected
end textChanged
  • Thanks for that Marek, I had missed that case. Thre is another case your version misses - numbers beginning with a period - which can easily be included by simply adding `or me is "."` to the if test. Dealing with the selection/insertion is harder. I'll update my answer to cover that also. – Alex Tweedly Mar 10 '13 at 18:04
1

I found it was easier and probably better to add this as a separate answer than to edit my earlier one. This takes Marek's improvement, adds one more case of leading "." and also allows leading and trailing blank spaces generally, rather than only after a number has been typed.

It also restores the selection / insertion point after a failed attempt to type or paste additional characters (selection can't be fully restored, but the insertion point should always finish up immediately after any previous selection.

Note that we are definitely trading off ease of understanding for more completeness ...

We still do not fully cover the case of inserting scientific notation (e.g. 1.2e34) becuae in that case the final value is valid but one of the intervening states (1.2e) is not.

local sExisting

on openfield
   put the text of me into sExisting
end openfield

on textChanged
   local tMe, tChunk, tDeltaLength, tWhere
   put the selectedChunk of me into tChunk
   put word 1 to -1 of the text of me into tMe    -- strip leading/trailing spaces
   if tMe is a number or tMe = "-" or tMe = "." or tMe is empty then
      put the text of me into sExisting
   else
      -- calculate how many characters were inserted, 
      --      and move the insertion point back to there
      put the number of chars in me - the number of chars in sExisting into tDeltaLength
      put sExisting into me
      put word 4 of tChunk - tDeltaLength into tWhere
      put tWhere into  word 4 of tChunk
      put tWhere into  word 2 of tChunk
      do ("select after " & tChunk & " of me")
   end if
end textChanged
Alex Tweedly
  • 114
  • 1
0

How about using "is a number"? The only special case is that you need to allow for "-" at the beginning:

on KeyDown theKey
   if (the text of me & theKey) is a number then
      pass keyDown
   else if the text of me is empty and theKey is "-" then
      pass keyDown
   end if
end KeyDown
hliljegren
  • 426
  • 3
  • 9
0

Alex,

Your script doesn't work properly if:

  1. you type the "-"sign on the beginning of typing (when field is empty without any digit)
  2. as you mentioned, when type invalid character, the input cursor is moved to the start of the line

Please check this:

local sExisting, sSelected, sSelected1

on openField
   put the text of me into sExisting
end openField

on selectionChanged
   put the selectedChunk into sSelected1
   put "sSelected1: " & sSelected1 into line 1 of msg
end selectionChanged

on arrowKey
   send "selectionChanged" to me in 0
   pass arrowKey
end arrowKey

on textChanged
   local tMinus, tDot
   put empty into tDot
   put empty into tMinus
   --
   put the selectedChunk into sSelected
   put "sSelected: " & sSelected into line 2 of msg
   --
   get matchChunk (me, "^(-?)[0-9]*(\.)?([0-9]*)$", tMinus, tMinus, tDot, tDot)
   if it then
      switch tDot
         case 2
            if tMinus = 1 then put "-0." & char 3 to -1 of me into me  
            put (word 2 of sSelected) + 1 into word 2 of sSelected
            put (word 4 of sSelected) + 1 into word 4 of sSelected             
            break
         case 1
            put "0." & char 2 to -1 of me into me
            put (word 2 of sSelected) + 1 into word 2 of sSelected
            put (word 4 of sSelected) + 1 into word 4 of sSelected
            break
         default
      end switch
      send "selectionChanged" to me in 0
      put me into sExisting
   else
      if not (me is empty) then
         put sExisting into me
         put sSelected1 into sSelected
      end if
   end if
   select sSelected
end textChanged

It's probably not perfect, but selection after paste and any character typing (even not allowed) should be correct. You can start typing with "-" or "." also, with "autocorrection" - if you type ".34" or "-.34" you will be corrected to "0.34" or "-0.34".

You can change cursor position by arrows and after paste not allowed string it doesn't change the cursor position. After paste proper string, new cursor position is at the end of pasted string.

Known fault I don't like there is missing remove leading zeros (and maybe trailing also should be removed).

Please let me know if you find some errors, so I'll try to repair it (with my pleasure). The script is little bit long, yesterday I have seen some bug, but will have not time in next few days so decided to put it there to play by somebody.

  • In Ben's solution you can type 2 dots one after other and no negative numbers. You can also paste anything you like into the field. Too simple solution to work perfect. – Marek Niesiobędzki Apr 22 '13 at 17:07
  • I'm looking for way to put such comment under somebody's answer (this above I should put under Ben's answer), but I can choose "comment" link only below my answer. Any help? – Marek Niesiobędzki Apr 22 '13 at 17:11
0

This is actually trickier than it looks. But this works:

on keyDown pKey
   if pKey = "-" and me  <> "" then exit keyDown
   if pKey = "." and "." is in me then exit keyDown
   if pKey is in "-0123456789."  then pass keydown
end keyDown
dunbarx
  • 756
  • 5
  • 4
-2

this can also work:

on KeyDown pKey if pKey is among the characters of "0123456789." and (the length of me) < 15 then pass keyDown end Keydown

LoKi
  • 1