Trapping keyboard event in StringGadget

Just starting out? Need help? Post your questions and find answers here.
Earlington
New User
New User
Posts: 2
Joined: Tue Jul 16, 2013 9:04 pm

Trapping keyboard event in StringGadget

Post by Earlington »

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
User avatar
Demivec
Addict
Addict
Posts: 3892
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Trapping keyboard event in StringGadget

Post by Demivec »

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?
A window callback.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4211
Joined: Sun Apr 12, 2009 6:27 am

Re: Trapping keyboard event in StringGadget

Post by RASHAD »

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)

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  
Edit : Modified for better logic
Last edited by RASHAD on Wed Jul 17, 2013 8:20 pm, edited 1 time in total.
Egypt my love
User avatar
JHPJHP
Addict
Addict
Posts: 1922
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: Trapping keyboard event in StringGadget

Post by JHPJHP »

Removed; duplicate technique posted by TI-994A :?
Last edited by JHPJHP on Sat May 26, 2018 8:52 pm, edited 6 times in total.

STATUS: Mostly Unavailable :: Downloads moved to My PureBasic Stuff; free executables, access key required for source code.
_________________
User avatar
TI-994A
Addict
Addict
Posts: 2490
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore

Re: Trapping keyboard event in StringGadget

Post by TI-994A »

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!
User avatar
JHPJHP
Addict
Addict
Posts: 1922
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: Trapping keyboard event in StringGadget

Post by JHPJHP »

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.

STATUS: Mostly Unavailable :: Downloads moved to My PureBasic Stuff; free executables, access key required for source code.
_________________
User avatar
Danilo
Addict
Addict
Posts: 3027
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Trapping keyboard event in StringGadget

Post by Danilo »

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..)
Earlington
New User
New User
Posts: 2
Joined: Tue Jul 16, 2013 9:04 pm

Re: Trapping keyboard event in StringGadget

Post by Earlington »

Thanks guys for your very generous and speedy responses.
I'll try your suggestions and give feedback.
User avatar
Keya
Addict
Addict
Posts: 1880
Joined: Thu Jun 04, 2015 7:10 am

Re: Trapping keyboard event in StringGadget

Post by Keya »

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! :)

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
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 :)
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4211
Joined: Sun Apr 12, 2009 6:27 am

Re: Trapping keyboard event in StringGadget

Post by RASHAD »

Cross platform
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
User avatar
Keya
Addict
Addict
Posts: 1880
Joined: Thu Jun 04, 2015 7:10 am

Re: Trapping keyboard event in StringGadget

Post by Keya »

RASHAD wrote:Cross platform
YES!!! :)
RASHAD wrote:Add what you need to prevent
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 yet
User avatar
TI-994A
Addict
Addict
Posts: 2490
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore

Re: Trapping keyboard event in StringGadget

Post by TI-994A »

Thanks for saying so, Keya. So glad that you're finding it useful. :D
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?
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.

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!
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4211
Joined: Sun Apr 12, 2009 6:27 am

Re: Trapping keyboard event in StringGadget

Post by RASHAD »

Global Hook (Low Level) is a bad idea
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
Post Reply