StringGadget: also use "-" in numeric only
- marcoagpinto
- Addict
- Posts: 1045
- Joined: Sun Mar 10, 2013 3:01 pm
- Location: Portugal
- Contact:
StringGadget: also use "-" in numeric only
Hello!
Freddy, could you enhance the stringgadgets in order that it also accepts negative values in numeric mode?
For example, when I press "-" it gives a warning saying it only accepts numbers.
Thanks!
Kind regards,
>Marco A.G.Pinto
----------------
Freddy, could you enhance the stringgadgets in order that it also accepts negative values in numeric mode?
For example, when I press "-" it gives a warning saying it only accepts numbers.
Thanks!
Kind regards,
>Marco A.G.Pinto
----------------
Re: StringGadget: also use "-" in numeric only
Or how about natively supporting a callback, so you can decide valid inputs yourself. Perhaps with a mask like Date() (e.g. "00-aaa" would allow 2 numbers followed by a minus/dash then 3 letters. 00[-]aaa would make the dash optional and "0(2)-a(3)" would accept 4-x and 51-abc but would not allow you to enter more than 2 digits)
-
- Always Here
- Posts: 6426
- Joined: Fri Oct 23, 2009 2:33 am
- Location: Wales, UK
- Contact:
Re: StringGadget: also use "-" in numeric only
.... and let's support the decimal point too for floats/doubles
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
If it sounds simple, you have not grasped the complexity.
Re: StringGadget: also use "-" in numeric only
And the decimal comma, for the non-english speaking worldIdeasVacuum wrote:.... and let's support the decimal point too for floats/doubles

How about a colon for times like 3:15^^
Re: StringGadget: also use "-" in numeric only
Using BindGadgetEvent() and some old code. Numbers only, but you can modify for dates and commas if needed.
Code: Select all
EnableExplicit
Procedure.i SF_IsNumeric(InString.s, DecimalCharacter.c='.', ThousandsSeparator.c=',')
; REV: 100405, skywalk
; modified PureBasic Forum: Xombie(20061129) + Demivec(20100323)
; removed 3 constants, add Lcase, add 'd' exponent support
; REV: 110311
; enabled leading '.' To Define a numeric: -.1 = -0.1
; Return: 0 = non-numeric values = 0
; 1 = positive integers
; -1 = negative integers
; 2 = positive floats
; -2 = negative floats
InString = LCase(Trim(InString)) ; eliminate comparisons for E & D
Protected.i IsDecimal, CaughtThousand, CaughtDecimal, CaughtE
Protected.i Sgn = 1, IsSignPresent, IsSignAllowed = 1, CountNumeric
Protected.i *CurrentChar.Character = @InString
While *CurrentChar\c
Select *CurrentChar\c
Case '0' To '9'
CountNumeric + 1
If CaughtThousand And CountNumeric > 3
ProcedureReturn 0
EndIf
IsSignAllowed = 0
Case ThousandsSeparator
If CaughtDecimal Or CaughtE Or CountNumeric = 0 Or (CaughtThousand And CountNumeric <> 3)
ProcedureReturn 0
EndIf
CaughtThousand = 1
CountNumeric = 0
Case DecimalCharacter
;If CaughtDecimal Or CaughtE Or CountNumeric = 0 Or (CaughtThousand And CountNumeric <> 3)
If CaughtDecimal Or CaughtE Or (CaughtThousand And CountNumeric <> 3) ; Allow leading decimal to signify a numeric
ProcedureReturn 0
EndIf
CountNumeric = 0
CaughtDecimal = 1
IsDecimal = 1
CaughtThousand = 0
Case '-'
If IsSignPresent Or Not IsSignAllowed
ProcedureReturn 0
EndIf
If Not CaughtE: Sgn = -1: EndIf
IsSignPresent = 1
Case '+'
If IsSignPresent Or Not IsSignAllowed
ProcedureReturn 0
EndIf
IsSignPresent = 1
Case 'e', 'd' ; Lcase = Don't Care -> 'E', 'D'
If CaughtE Or CountNumeric = 0 Or (CaughtThousand And CountNumeric <> 3)
ProcedureReturn 0
EndIf
CaughtE = 1
CountNumeric = 0
CaughtDecimal = 0
CaughtThousand = 0
IsSignPresent = 0
IsSignAllowed = 1
Default
ProcedureReturn 0
EndSelect
*CurrentChar + SizeOf(Character)
Wend
If CountNumeric = 0 Or (CaughtThousand And CountNumeric <> 3)
ProcedureReturn 0
EndIf
If IsDecimal
ProcedureReturn 2 * Sgn ; -> Float
EndIf
ProcedureReturn Sgn ; -> Integer
EndProcedure
Procedure gad_ForceNumeric_CB()
Protected.i gN = EventGadget()
If EventType() = #PB_EventType_Change
; Verify numeric entries...
If SF_IsNumeric(GetGadgetText(gN))
SetGadgetColor(gN, #PB_Gadget_BackColor, #Green)
Else
SetGadgetColor(gN, #PB_Gadget_BackColor, #Red)
EndIf
EndIf
EndProcedure
If OpenWindow(0, 434, 225, 193, 174, "Verify Numeric Entry", #PB_Window_SystemMenu)
TextGadget(0, 10, 10, 150, 20, "Enter Numeric Text Below", #PB_Text_Center)
StringGadget(1, 10, 45, 150, 20, "-1.234e-12", #PB_Text_Center) ;#ES_CENTER)
BindGadgetEvent(1, @gad_ForceNumeric_CB())
SetGadgetText(1, "-1.234e-12")
PostEvent(#PB_Event_Gadget, 0, 1, #PB_EventType_Change)
SendMessage_(GadgetID(1), #EM_SETLIMITTEXT, 16, 0) ; Force character limit = 16
EndIf
Repeat
Select WaitWindowEvent()
Case #PB_Event_Gadget
;UNCOMMENT if not using BindGadgetEvent().
;Select EventGadget()
;Case 1
; If EventType() = #PB_EventType_Change
; gad_ForceNumeric_CB()
; EndIf
;EndSelect
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Re: StringGadget: also use "-" in numeric only
Ah, somebody already posted something.
I just hacked this into my keyboard. Not very pretty, using string functions instead of memory, but it's flexible.
I don't think it's possible to prevent an input from being displayed in the gadget without API, so this code just turns the gadget red when there's an invalid character detected (not very sophisticated detection technique, though, I'm so bad it this...)
I just hacked this into my keyboard. Not very pretty, using string functions instead of memory, but it's flexible.
I don't think it's possible to prevent an input from being displayed in the gadget without API, so this code just turns the gadget red when there's an invalid character detected (not very sophisticated detection technique, though, I'm so bad it this...)
Code: Select all
Structure StringGadgetProperties
exists.i
List chars.s()
EndStructure
NewMap gadgets.StringGadgetProperties()
Procedure AllowCharacters(gadget.i, characters.s)
Shared gadgets()
gadgets(Str(gadget))\exists = #True
Protected i.i
Protected length.i = Len(characters)
For i=1 To length
AddElement(gadgets(Str(gadget))\chars())
gadgets(Str(gadget))\chars() = Mid(characters, i, 1)
Next
EndProcedure
Procedure CheckStringGadgetForInvalidCharacters(EventGadget.i)
Shared gadgets()
If gadgets(Str(EventGadget))\exists <> #True
ProcedureReturn #False
EndIf
Protected text.s = GetGadgetText(EventGadget)
Protected i.i
Protected length.i = Len(text)
Protected char.s
Protected result.i = 0
For i=1 To length
char = Mid(text, i, 1)
result=0
ForEach gadgets(Str(EventGadget))\chars()
If gadgets(Str(EventGadget))\chars() = char
result = 1
EndIf
Next
If result=0
ProcedureReturn #True
EndIf
Next
EndProcedure
#CharacterSet_Alpha$ = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
#CharacterSet_Num$ = "0123456789"
#CharacterSet_AlphaNum$ = #CharacterSet_Alpha$ + #CharacterSet_Num$
; Shows possible flags of StringGadget in action...
If OpenWindow(0, 0, 0, 322, 205, "StringGadget Flags", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(0, 108, 10, 206, 20, "") ;Regular Stringgadge
StringGadget(1, 108, 35, 206, 20, "") : AllowCharacters(1, "abc")
StringGadget(2, 108, 60, 206, 20, "") : AllowCharacters(2, #CharacterSet_Alpha$)
StringGadget(3, 108, 85, 206, 20, "") : AllowCharacters(3, #CharacterSet_Num$)
StringGadget(4, 108, 110, 206, 20, "") : AllowCharacters(4, #CharacterSet_AlphaNum$)
StringGadget(5, 108, 140, 206, 20, "") : AllowCharacters(5, #CharacterSet_AlphaNum$+"-,.")
TextGadget(6, 8, 13, 90, 14, "Regular (all chars)")
TextGadget(7, 8, 35, 90, 14, "abc")
TextGadget(8, 8, 60, 90, 14, "a-z/A-Z")
TextGadget(9, 8, 85, 90, 14, "0-9")
TextGadget(10, 8, 110, 90, 14, "a-z/A-Z/0-9")
TextGadget(11, 8, 140, 90, 14, "AlphaNum+ '-' ',' '.'")
Repeat
event = WaitWindowEvent()
If event = #PB_Event_Gadget
If EventType()=#PB_EventType_Change
If CheckStringGadgetForInvalidCharacters(EventGadget()) = #True
SetGadgetColor(EventGadget(), #PB_Gadget_BackColor, $0000FF) ;Turn red
Else
SetGadgetColor(EventGadget(), #PB_Gadget_BackColor, -1) ;Default
EndIf
EndIf
EndIf
Until event = #PB_Event_CloseWindow
EndIf
Re: StringGadget: also use "-" in numeric only
Two examples for simple number/float (non-scientific notation) input:
The following was for german forum, so it uses comma ',' instead dot '.' => for example "567,88" (can be changed within the regular expression)
Translated English version, uses dot '.' for float numbers:
All examples use WinAPI messages #EM_GETSEL & #EM_SETSEL to save and restore the cursor position. Otherwise it would be platform-independent.
I already requested the possibility of handling the cursor and text selection numerous times, because of such example codes, but the team resists to add it.
Maybe it is too powerful, or just not useful for freak.
Code: Select all
EnableExplicit
Procedure checkFloatInput(gadget)
Protected start, count, pointcount, new$
SendMessage_(GadgetID(gadget), #EM_GETSEL, @start, 0)
Protected txt$ = GetGadgetText(gadget)
Protected *p.Character = @txt$
While *p\c ; <> 0
If *p\c = '.'
pointcount+1
If pointcount < 2
new$ + Chr(*p\c)
Else
If start>count : start-1 : EndIf
EndIf
ElseIf count = 0 And *p\c = '-'
new$ + Chr('-')
ElseIf *p\c >= '0' And *p\c <= '9'
new$ + Chr(*p\c)
Else
start - 1
EndIf
*p + SizeOf(Character)
count + 1
Wend
SetGadgetText(gadget, new$)
SendMessage_(GadgetID(gadget), #EM_SETSEL, start, start)
EndProcedure
Define event
If OpenWindow(0, 0, 0, 322, 205, "StringGadget Flags", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(0, 8, 10, 306, 20, "")
StringGadget(1, 8, 35, 306, 20, "1234567")
Repeat
event = WaitWindowEvent()
If event = #PB_Event_CloseWindow : End
ElseIf event = #PB_Event_Gadget
If EventGadget()=0 Or EventGadget()=1
If EventType() = #PB_EventType_Change
checkFloatInput(EventGadget())
EndIf
EndIf
EndIf
ForEver
EndIf
; ts-soft: Habe Danilos Codebeispiel mal um Minus-Werte erweitert
Code: Select all
;
; by Danilo
;
; http://www.purebasic.fr/german/viewtopic.php?f=16&t=26388&start=7
;
; Nummern-Eingabe Beispiele: 123 567,88 -12 -4,5
;
OpenWindow(0, #PB_Ignore, #PB_Ignore, 250, 80, "")
StringGadget(0, 10, 10, 200, 20, "")
CreateRegularExpression(0,"^\-{0,1}\d*$|^\-{0,1}\d+\,\d{0,2}$|^$") ; ^ = Anfang des Strings
; $ = Ende des Strings
; \d = Dezimalzahl 0-9
; + = 1 oder mehr Vorkommen
; * = 0 oder mehr Vorkommen
; | = alternative Moeglichkeit (Or in PureBasic)
; \, = Komma
; \-{0,1} = Minus{0 oder 1 mal}
; \d{0,2} = Dezimalzahl 0-9 { 0 bis 2 mal }
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget
If EventGadget() = 0 And EventType() = #PB_EventType_Change
txt$ = GetGadgetText(0)
If MatchRegularExpression(0,txt$)=0 ; wenn kein Treffer, dann wieder vorherigen text setzen
SendMessage_(GadgetID(0),#EM_GETSEL,0,@endpos) : endpos - 1 ; cursor position holen
SetGadgetText(0,old$) ; alten text wieder setzen
SendMessage_(GadgetID(0),#EM_SETSEL,endpos,endpos) ; cursor position wieder setzen
Else
old$ = txt$
EndIf
EndIf
EndSelect
ForEver
Code: Select all
;
; by Danilo
;
; Original german version: http://www.purebasic.fr/german/viewtopic.php?f=16&t=26388&start=7
;
; Number-Input, English version
; Examples: 123 567.88 -12 -4.5
;
OpenWindow(0, #PB_Ignore, #PB_Ignore, 250, 80, "")
StringGadget(0, 10, 10, 200, 20, "")
CreateRegularExpression(0,"^\-{0,1}\d*$|^\-{0,1}\d+\.\d{0,2}$|^$") ; ^ = Begin of string
; $ = End of string
; \d = Decimal number 0-9
; + = 1 or more occurences
; * = 0 or more occurences
; | = alternative ('Or' in PureBasic)
; \. = Dot
; \-{0,1} = Minus{0 or 1 times}
; \d{0,2} = Decimal number 0-9 { 0 to 2 times }
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget
If EventGadget() = 0 And EventType() = #PB_EventType_Change
txt$ = GetGadgetText(0)
If MatchRegularExpression(0,txt$)=0 ; If regular expression does not match: set old text
SendMessage_(GadgetID(0),#EM_GETSEL,0,@endpos) : endpos - 1 ; get cursor position
SetGadgetText(0,old$) ; set old text
SendMessage_(GadgetID(0),#EM_SETSEL,endpos,endpos) ; set cursor position
Else
old$ = txt$
EndIf
EndIf
EndSelect
ForEver
I already requested the possibility of handling the cursor and text selection numerous times, because of such example codes, but the team resists to add it.
Maybe it is too powerful, or just not useful for freak.

Re: StringGadget: also use "-" in numeric only
Great solutions guys. Here's a quick and dirty one:
The string evaluation could be stepped up to avoid double minuses or decimals, and to ensure they're in the right places; but basically, the value could simply be extracted into a float variable and you're golden.
Code: Select all
Procedure InputProc(hWnd, uMsg, wParam, lParam)
Shared sysProc
If uMsg = #WM_CHAR And (wParam < 48 Or wParam > 57) And
wParam ! 45 And wParam ! 46 And wParam ! 8
ProcedureReturn
EndIf
ProcedureReturn CallWindowProc_(sysProc, hWnd, uMsg, wParam, lParam)
EndProcedure
wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(0, #PB_Any, #PB_Any, 350, 110, "Subclassed StringGadget", wFlags)
TextGadget(1, 25, 25, 300, 20, "Numeric StringGadget that accepts negatives & decimals:")
StringGadget(2, 25, 50, 270, 20, "")
sysProc = SetWindowLongPtr_(GadgetID(2), #GWL_WNDPROC, @InputProc())
While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

Re: StringGadget: also use "-" in numeric only
Probably be easier just to use a normal StringGadget and then
use any good IsNumeric() procedure to its text; then report an
error to the user if it's not numeric. It'd be cross-platform with
this approach, too.
use any good IsNumeric() procedure to its text; then report an
error to the user if it's not numeric. It'd be cross-platform with
this approach, too.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
"PureBasic won't be object oriented, period" - Fred.
Re: StringGadget: also use "-" in numeric only
Does this work cross platform ?
Unfortunately caret position is lost when entering a wrong character
Unfortunately caret position is lost when entering a wrong character
Code: Select all
Procedure IsNumeric(n.s)
Protected *End.Ascii
CompilerIf #PB_Compiler_Unicode
PokeS(@n, n, -1, #PB_Ascii)
CompilerEndIf
strtod_(@n, @*End)
If *End > @n
While *End\a
If *End\a > 32
ProcedureReturn #False
EndIf
*End + 1
Wend
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Global NewMap OldStringGadgetValue.s()
Procedure NumericCheck(Gadget)
Protected GadgetKey.s = Hex(Gadget)
Protected GadgetText.s = GetGadgetText(Gadget)
If IsNumeric(GadgetText)
OldStringGadgetValue(GadgetKey) = GadgetText
Else
SetGadgetText(Gadget, OldStringGadgetValue(GadgetKey))
EndIf
EndProcedure
If OpenWindow(0, 0, 0, 322, 205, "Numeric only", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(0, 8, 10, 306, 20, "")
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget And EventGadget() = 0
NumericCheck(0)
EndIf
Until Event = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)