Trapping keyboard event in StringGadget
-
- New User
- Posts: 2
- Joined: Tue Jul 16, 2013 9:04 pm
Trapping keyboard event in StringGadget
Hi All
(How) can a key press in a StringGadget be captured before the string is changed by the key?
And can the change then be prevented?
Thanks
(How) can a key press in a StringGadget be captured before the string is changed by the key?
And can the change then be prevented?
Thanks
Re: Trapping keyboard event in StringGadget
A window callback.Earlington wrote:(How) can a key press in a StringGadget be captured before the string is changed by the key?
And can the change then be prevented?
Re: Trapping keyboard event in StringGadget
As Demivec mentioned if you are using Windows so you can use Window CallBack OR subclassing the StringGadget()
Next is a cross platform solution
For exam you can prevent a,A & b for StringGadget(0)
Edit : Modified for better logic
Next is a cross platform solution
For exam you can prevent a,A & b for StringGadget(0)
Code: Select all
If OpenWindow(0, 0, 0, 400, 200, "Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
StringGadget(0,10,10,180, 24, "")
StringGadget(1,210,10,180, 24, "")
Repeat
Select WaitWindowEvent()
Case #PB_Event_Menu
;
Case #PB_Event_CloseWindow
Quit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case 0
Select EventType()
Case #PB_EventType_Focus
AddKeyboardShortcut(0, #PB_Shortcut_A, 15)
AddKeyboardShortcut(0, #PB_Shortcut_Shift|#PB_Shortcut_A, 16)
AddKeyboardShortcut(0, #PB_Shortcut_B, 17)
Case #PB_EventType_LostFocus
RemoveKeyboardShortcut(0,#PB_Shortcut_A)
RemoveKeyboardShortcut(0,#PB_Shortcut_Shift|#PB_Shortcut_A)
RemoveKeyboardShortcut(0,#PB_Shortcut_B)
EndSelect
Case 1
EndSelect
EndSelect
Until Quit = 1
EndIf
End
Last edited by RASHAD on Wed Jul 17, 2013 8:20 pm, edited 1 time in total.
Egypt my love
Re: Trapping keyboard event in StringGadget
Removed; duplicate technique posted by TI-994A 

Last edited by JHPJHP on Sat May 26, 2018 8:52 pm, edited 6 times in total.


Re: Trapping keyboard event in StringGadget
Hello Earlington. Here's another subclassing example that traps special keys and allows only alpha-numeric characters to be typed. It also captures and changes the behaviour of the ENTER key. I've adapted it from code which I posted in an earlier thread, meant for password input:
Code: Select all
Enumeration
#MainWindow
#InputBox
#InputBox2
#Label
#Label2
#Label3
EndEnumeration
Procedure InputProc(hWnd, uMsg, wParam, lParam)
Shared sysProc
If uMsg = #WM_CHAR
If wParam = #VK_RETURN
SetGadgetText(#InputBox2, GetGadgetText(#InputBox))
SetActiveGadget(#InputBox2)
ProcedureReturn
;filters the alpha-numerics and Backspace
ElseIf wParam < 48 Or wParam > 57
If wParam < 65 Or wParam > 90
If wParam < 97 Or wParam > 122
If wParam <> 8
ProcedureReturn
EndIf
EndIf
EndIf
EndIf
EndIf
ProcedureReturn CallWindowProc_(sysProc, hWnd, uMsg, wParam, lParam)
EndProcedure
wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(#MainWindow, #PB_Any, #PB_Any, 250, 140, "Subclassed StringGadget",
wFlags)
TextGadget(#Label, 25, 10, 250, 20, "Allows only alpha-numeric characters:")
TextGadget(#Label2, 25, 50, 250, 20, "pressing <ENTER> jumps to next gadget...")
TextGadget(#Label3, 25, 85, 250, 20, "Allows all characters:")
StringGadget(#InputBox, 25, 25, 200, 20, "")
StringGadget(#InputBox2, 25, 100, 200, 20, "")
SetActiveGadget(#InputBox)
sysProc = SetWindowLongPtr_(GadgetID(#InputBox), #GWL_WNDPROC, @InputProc())
While WaitWindowEvent() ! #PB_Event_CloseWindow : 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!
Re: Trapping keyboard event in StringGadget
Global Hook example:
Code: Select all
Structure KBDLLHOOKSTRUCT
vkCode.l
scanCode.l
flags.l
time.l
dwExtraInfo.l
EndStructure
Procedure SendKey(KeySwap.s)
SwapData.INPUT
SwapData\type = #INPUT_KEYBOARD
SwapData\ki\wVk = PeekB(@KeySwap)
SwapData\ki\wScan = 0
SwapData\ki\dwFlags = 0
SwapData\ki\time = 0
SwapData\ki\dwExtraInfo = 0
SendInput_(1, SwapData, SizeOf(INPUT))
EndProcedure
Procedure.s GetKeyName(KeyCode.i)
#MAPVK_VK_TO_VSC = 0
KeyName.s = Space(#MAX_PATH)
GetKeyNameText_(MapVirtualKey_(KeyCode, #MAPVK_VK_TO_VSC) * $10000, @KeyName, #MAX_PATH)
ProcedureReturn KeyName
EndProcedure
Procedure KeyboardHook(nCode, wParam, *kc.KBDLLHOOKSTRUCT)
If nCode = #HC_ACTION
KeyCode = *kc\vkCode
Select KeyCode
Case 65 To 90
Debug GetKeyName(KeyCode)
Default
SendKey(#Null$)
ProcedureReturn 1
EndSelect
EndIf
ProcedureReturn CallNextHookEx_(#Null, nCode, wParam, *kc)
EndProcedure
If OpenWindow(0, 0, 0, 300, 300, "Global Keyboard Hook", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(0, 10, 20, 280, 20, "")
SetActiveGadget(0)
HHOOK = SetWindowsHookEx_(#WH_KEYBOARD_LL, @KeyboardHook(), GetModuleHandle_(0), 0)
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
UnhookWindowsHookEx_(HHOOK)
EndIf
Last edited by JHPJHP on Thu Jul 25, 2013 6:25 pm, edited 2 times in total.


Re: Trapping keyboard event in StringGadget
For Windows, without callback:
- Regular Expression Floating Point Input with Comma (german forum)
- checkFloatInput() (german forum)
Windows-only stuff is just #EM_GETSEL + #EM_SETSEL, because PB does not support getting/setting cursor position cross-platform.
You can modify it to match characters/patterns you want (for example telephone numbers etc..)
- Regular Expression Floating Point Input with Comma (german forum)
- checkFloatInput() (german forum)
Windows-only stuff is just #EM_GETSEL + #EM_SETSEL, because PB does not support getting/setting cursor position cross-platform.
You can modify it to match characters/patterns you want (for example telephone numbers etc..)
-
- New User
- Posts: 2
- Joined: Tue Jul 16, 2013 9:04 pm
Re: Trapping keyboard event in StringGadget
Thanks guys for your very generous and speedy responses.
I'll try your suggestions and give feedback.
I'll try your suggestions and give feedback.
Re: Trapping keyboard event in StringGadget
TI-994A, many thanks for the example! I needed it to make a string gadget only accept hex chars (a-f,A-F,0-9), seems to work great and was quick and easy to implement, and even changes lowercase to uppercase for uniformity just because we can!
i notice there's a few others in this ballpark like WM_KEYDOWN, but i take it WM_CHAR is the best for this? its working great anyway 

Code: Select all
#StringHexGadget = 0
Procedure HexStringGadgetProc(hWnd, uMsg, wParam, lParam)
Shared sysProc
If uMsg = #WM_CHAR
Select wParam
Case 97 To 102: wParam - 32 ;Convert a-f to A-F and allow
Case 8, 65 To 70, 48 To 57: ;A-F, 0-9, and Backspace(8) allowed
Default: ProcedureReturn 0 ;Block all others
EndSelect
EndIf
ProcedureReturn CallWindowProc_(sysProc, hWnd, uMsg, wParam, lParam)
EndProcedure
If OpenWindow(0, 0, 0, 322, 205, "Hex-chars filter for string gadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(#StringHexGadget, 8, 10, 306, 20, "")
SetGadgetAttribute(#StringHexGadget, #PB_String_MaximumLength, 8) ;to also limit to max 8 hex chars
sysProc = SetWindowLongPtr_(GadgetID(#StringHexGadget), #GWL_WNDPROC, @HexStringGadgetProc())
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf

Re: Trapping keyboard event in StringGadget
Cross platform
Add what you need to prevent
Add what you need to prevent
Code: Select all
LoadFont(0,"Arial",12)
If OpenWindow(0, 0, 0, 400, 200, "Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
StringGadget(0,10,34,180, 24, "")
StringGadget(1,210,34,180, 24, "")
TextGadget(2,10,10,180,24,"")
SetGadgetColor(2, #PB_Gadget_FrontColor,#Red)
SetGadgetFont(2,FontID(0))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Quit = 1
Case #PB_Event_Menu
Select EventMenu()
Case 15 To 17
SetGadgetText(2,"Not Allowed")
Delay(400)
SetGadgetText(2,"Ready")
EndSelect
Case #PB_Event_Gadget
Select EventGadget()
Case 0
Select EventType()
Case #PB_EventType_Focus
AddKeyboardShortcut(0, #PB_Shortcut_A, 15)
AddKeyboardShortcut(0, #PB_Shortcut_Shift|#PB_Shortcut_A, 16)
AddKeyboardShortcut(0, #PB_Shortcut_B, 17)
Case #PB_EventType_LostFocus
RemoveKeyboardShortcut(0,#PB_Shortcut_A)
RemoveKeyboardShortcut(0,#PB_Shortcut_Shift|#PB_Shortcut_A)
RemoveKeyboardShortcut(0,#PB_Shortcut_B)
EndSelect
Case 1
EndSelect
EndSelect
Until Quit = 1
EndIf
End
Egypt my love
Re: Trapping keyboard event in StringGadget
YES!!!RASHAD wrote:Cross platform

ooh... in my example case above (hex chars filter) i basically need to prevent everything except a few certain keys, so I don't like the idea of making 100+ AddKeyboardShortcut calls?? but maybe its not as bad as its making me think. Perhaps i'll need a single global keyhook there, though i haven't looked into it much yetRASHAD wrote:Add what you need to prevent
Re: Trapping keyboard event in StringGadget
Thanks for saying so, Keya. So glad that you're finding it useful.
For example, on a US keyboard, pressing the top row number 2 key would trigger the VK_2 code; and in order to determine if the @ character has been input, the shift modifier has to be inspected as well. On the other hand, the WM_CHAR would immediately return the character code for the @ character. Similarly, the WM_KEYUP/WM_KEYDOWN messages would not be able to discern between upper & lower case alphabets, unless the shift modifier is inspected.
Exceptions to this rule are the Enter, Escape, Tab, and Backspace keys, which correspond to both messages.

I believe that the WM_KEYUP/WM_KEYDOWN messages correspond with the physical keyboard keys, while the WM_CHAR messages correlate to the character codes.Keya wrote:i notice there's a few others in this ballpark like WM_KEYDOWN, but i take it WM_CHAR is the best for this?
For example, on a US keyboard, pressing the top row number 2 key would trigger the VK_2 code; and in order to determine if the @ character has been input, the shift modifier has to be inspected as well. On the other hand, the WM_CHAR would immediately return the character code for the @ character. Similarly, the WM_KEYUP/WM_KEYDOWN messages would not be able to discern between upper & lower case alphabets, unless the shift modifier is inspected.
Exceptions to this rule are the Enter, Escape, Tab, and Backspace keys, which correspond to both messages.
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!
Re: Trapping keyboard event in StringGadget
Global Hook (Low Level) is a bad idea
You can use Local Hook for your application
You can use Local Hook for your application
Code: Select all
Global Keyhook
Procedure HookProc(nCode,wParam,lParam)
Select wParam
Case #VK_0 To #VK_9
ProcedureReturn 1
Case #VK_A To #VK_F
ProcedureReturn 1
EndSelect
ProcedureReturn CallNextHookEx_(0,nCode,wParam,lParam)
EndProcedure
LoadFont(0,"Arial",12)
If OpenWindow(0, 0, 0, 400, 200, "Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
StringGadget(0,10,34,180, 24, "")
StringGadget(1,210,34,180, 24, "")
TextGadget(2,10,10,180,24,"")
SetGadgetColor(2, #PB_Gadget_FrontColor,#Red)
SetGadgetFont(2,FontID(0))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Quit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case 0
Select EventType()
Case #PB_EventType_Focus
Keyhook = SetWindowsHookEx_(#WH_KEYBOARD,@HookProc(),0,GetCurrentThreadId_())
Case #PB_EventType_LostFocus
UnhookWindowsHookEx_(Keyhook)
EndSelect
Case 1
EndSelect
EndSelect
Until Quit = 1
EndIf
End
Egypt my love