B4J Library [ABMaterial]: MashKeyPress (Capture Keyboard Input e.g. Shift+a)

Hi there

This is in response to CableGuy's PM for this functionality here. So far the preliminary results are indicating that its working...at page level.

The original js is here. Download the js files and add the necessary link to this js in BuildPage.

KeyPress.png


I must admit this is some nifty piece of work, its very impressive actually.

Capturing keyboard input with this lib can happen at html page level and within an element. So far Ive managed to get this to work at page level. I will continue the rest later, bushed.

Step 1: Create the ABMComponent
Project > Add New Module > Class Module > ABM Custom Component
Type in the class name in the prompt and continue.

We want to be able to add the combinations that we will capture and return an event in b4j. To do that we will use b4j_raiseevent. We define a list to store all our combo definitions, call this javacalls.

We want to trap the shift control key, so we define this as a constant, it will help us to refer back to it later..

B4X:
Public const MashKeyPressShift As String = "shift"

This component is not a page element, what we can do is create a hidden div and then add it to R1C1 on our page. This is just fishing anyway.

B4X:
'build the component, lets just add an empty div that we hide
Sub ABMComp_Build(InternalPage As ABMPage, internalID As String) As String
    Return $"<div id="${compid}" style="display:none"></div>"$
End Sub

We want to be able to trap a couple of combination keys so, rather lets define a simple method to do so.

B4X:
'add a simple combo like shift+a
private Sub AddSimple(ctlKey As String, key As String)
    Dim strComb As String = $"${ctlKey} ${key}"$
    Dim script As String = $"${compid}listener.simple_combo("${strComb}", function() {
    b4j_raiseEvent('${compid}_keypress', {'value':'${strComb}'});
    });"$
    JavaCalls.Add(script)
End Sub

This we later, define it as a self referencing sub so that we can chain our calls to it.

B4X:
'add a combination that uses shift
Sub AddShiftPlus(key As String) As MashKeyPress
    AddSimple(MashKeyPressShift,key)
    Return Me
End Sub
B4X:
'add a combination that uses shift
Sub AddShiftPlus(key As String) As MashKeyPress
    AddSimple(MashKeyPressShift,key)
    Return Me
End Sub

So when calling this method, you just specify the key to trap e.g. a. In our connect page for example, we have initialized this as...

B4X:
mashkpress.Initialize(page,"mashkpress")
    mashkpress.AddShiftPlus("a").AddShiftPlus("b").AddShiftPlus("z")
    page.Cell(1,1).AddComponent(mashkpress.ABMComp)

After having defined mashkpress in Class_Globals as

B4X:
Public mashkpress As MashKeyPress

This is defined in Class_Globals because we want to trap the events from it within the scope of the page, remember above, when each combo key press is done, the control must return what was pressed. This is done with..

B4X:
b4j_raiseEvent('${compid}_keypress', {'value':'${strComb}'});

so if 'shift a' is pressed, the returned key combination should be ...

As a demo, we want to show the returned combinations in a label of our page, so a label is defined in class_globals as

B4X:
Private lblKeyPress As ABMLabel

In ConnectPage we also add this label.

B4X:
lblKeyPress.Initialize(page,"lblKeyPress","Key Press Values",ABM.SIZE_PARAGRAPH,False,"")
    page.Cell(2,1).AddComponent(lblKeyPress)

This basically means that when we press shift+a, shift+b and shift+z, these should be trapped and something should be done. To do this, we define the event trapper in the page.

B4X:
'a keypress has been done
Sub mashkpress_keypress(value As Map)
    'read the keypress done
    Dim kp As String = value.Get("value")
    lblKeyPress.Text = kp
    lblKeyPress.refresh
    select case kp
    case "shift a"
       do this
    case "shift b"
      do that
    case "shift z"
     back to then
    end select

End Sub

Remember: In BuildPage add

B4X:
page.AddExtraJavaScriptFile("custom/keypress-2.1.5.min.js")

Ta!

Upcoming: Linking keyboard input to an html element.
 

Attachments

  • MashKeyPress.bas
    5.3 KB · Views: 467
Last edited:

Cableguy

Expert
Licensed User
Longtime User
I know I have asked a LOT from you already, but, what do I need to change in order to trap a 3-key combo, like "Shift+a+z" or "ctrl+shift+a"?

Btw, regarding the conflict you mentioned in our PM exchange, trigering key combo detection at component level would it not cause the conflict we talked about?
I will try to setup a proper test page with a Text input field and see if, with the Page level working, the input gets afected or not.
 

Cableguy

Expert
Licensed User
Longtime User
Done!

Just replaced the label declaration by an input filed, initialized it as text area set to false, and, VERY IMPORTANT, deleted the 2 code lines from the mashkpress_keypress sub, so that the input does not get overwriten.

lblKeyPress.Text = kp
lblKeyPress.refresh

This way the page is still listening for the key combos, and the text input works properly, as long as we dont try to enter one of the combos... like "Shift+a" to get a capital A
 

Mashiane

Expert
Licensed User
Longtime User
I know I have asked a LOT from you already, but, what do I need to change in order to trap a 3-key combo, like "Shift+a+z" or "ctrl+shift+a"?

Btw, regarding the conflict you mentioned in our PM exchange, trigering key combo detection at component level would it not cause the conflict we talked about?
I will try to setup a proper test page with a Text input field and see if, with the Page level working, the input gets afected or not.

I will add the count and combination calls later on and upload an update.
 

Mashiane

Expert
Licensed User
Longtime User
Due to load shedding been off for a while. This is what we suffer in RSA, electricity shutdowns. Anyway to close this off, a few things.

1. Added two extra events and two methods to add counters and sequence.
2. Added some key constants for ease of reference
3. Changed a few things to make this easier e.g. consistent key names
4. For ease of use, your key combination can just be passed as an array / list


5. Combination Sequence: Shift+a+z for example

This is called a sequence. To add the sequence use
B4X:
mashkpress.AddSequence(Array As String(mashkpress.KeyShift,"a","z"))

Please note that the 'simple' will receive priority. For example YOU CANNOT have this sequence crtl+a+c and also have a simple crtl+a, the latter will be trapped and your sequence NOT.

To trap this use

B4X:
'a keypress has been done
Sub mashkpress_sequence(value As Map)
    Dim kp As String = value.Get("value")
    lblKeyPress.Text = kp
    lblKeyPress.refresh
End Sub

6. Counters: How many times a combination has been pressed. Should you pause, the counters start from 1 again

B4X:
mashkpress.AddCount(Array As String(mashkpress.KeyShift,mashkpress.KeySpace))

In this example, we want to know each time a shift+space has been pressed. This returns a JSON string that you can parse to a map to read the values.

To trap this use

B4X:
'a count combination
Sub mashkpress_count(value As Map)
    Dim kp As String = value.Get("value")
    Log(kp)
    lblKeyPress.Text = kp
    lblKeyPress.refresh
End Sub

The returned JSON, will have this structure:
B4X:
{"sequence":"shift space","count":5}

7. Defined Keys (check & test these THOROUGHY during development)

B4X:
Public const KeyShift As String = "shift"
    Public const KeyTab As String = "tab"
    Public const KeySpace As String = "space"
    Public const KeyUp As String = "up"
    Public const KeyDown As String = "down"
    Public const KeyLeft As String = "left"
    Public const KeyRight As String = "right"
    Public const KeyEnter As String = "enter"
    Public const KeyAlt As String = "alt"
    Public const KeyMeta As String = "meta"
    Public const KeyOption As String = "option"
    Public const KeyCmd As String = "cmd"
    Public const KeyCtrl As String = "ctrl"
    Public const KeyScroll As String = "scroll"
    Public const KeyPause As String = "pause"
    Public const KeyCaps As String = "caps"
    Public const KeyNum As String = "num"
    Public const KeyPrint As String = "print"
    Public const KeyF1 As String = "f1"
    Public const KeyF2 As String = "f2"
    Public const KeyF3 As String = "f3"
    Public const KeyF4 As String = "f4"
    Public const KeyF5 As String = "f5"
    Public const KeyF6 As String = "f6"
    Public const KeyF7 As String = "f7"
    Public const KeyF8 As String = "f8"
    Public const KeyF9 As String = "f9"
    Public const KeyF10 As String = "f10"
    Public const KeyF11 As String = "f11"
    Public const KeyF12 As String = "f12"
    Public const KeyBackSpace As String = "backspace"
    Public const KeyCaps As String = "caps"
    Public const KeyEsc As String = "esc"
    Public const KeyPageUp As String = "pageup"
    Public const KeyPageDown As String = "pagedown"
    Public const KeyEnd As String = "end"
    Public const KeyHome As String = "home"
    Public const KeyInsert As String = "insert"
    Public const KeyDelete As String = "delete"

8. Updated Examples

B4X:
mashkpress.Initialize(page,"mashkpress")
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,"a","z"))
    mashkpress.AddSequence(Array As String(mashkpress.KeyCtrl, mashkpress.keyshift,"a"))
  
    'mashkpress.AddShiftPlus("a").AddShiftPlus("b").AddShiftPlus("z")
    mashkpress.AddCount(Array As String(mashkpress.KeyShift,mashkpress.KeySpace))
  
    mashkpress.AddControlPlus("c").AddControlPlus("v").AddControlPlus("x")
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,mashkpress.KeyUp))
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,mashkpress.KeyDown))
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,mashkpress.KeyLeft))
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,mashkpress.KeyRight))
  
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,"a"))
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,"b"))
    mashkpress.AddSequence(Array As String(mashkpress.KeyShift,"c"))
  
    mashkpress.AddSequence(Array As String(mashkpress.KeyCtrl,mashkpress.KeyF1))
    mashkpress.AddSequence(Array As String(mashkpress.KeyCtrl,mashkpress.KeyDelete))
  
    page.Cell(1,1).AddComponent(mashkpress.ABMComp)

That's all folks!!

Final Updated ABM Custom Component in Post #1.

Thanks @Cableguy for this reference, this js has opened my eyes hey..;)
 

Cableguy

Expert
Licensed User
Longtime User
This is why I joined this community about 10 years ago...
I asked for Something, You responded...
It is I who should Thank YOU, instead you end up thanking me (?)
B4X Rocks, and this community is just amazing
 

Cableguy

Expert
Licensed User
Longtime User
So, I've been playing around with this great piece of code, and found a few things I'd like to share with you.

First, a few subs can be deleted as they are not needed and redundant, being them the "AddSimple", "AddShift" and "AddCtrl".
The "AddSequence can efectively handle all of those.
Secondly, most of the time I am getting a double-fire, but from time to time, I get a normal single-fire
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
So, I've been playing around with this great piece of code, and found a few things I'd like to share with you.

First, a few subs can be deleted as they are not needed and redundant, being them the "AddSimple", "AddShift" and "AddCtrl".
The "AddSequence can efectively handle all of those.
Secondly, most of the time I am getting a double-fire, but from time to time, I get a normal single-fire
Great, I was also asking myself the same questions because if I add simple Crt+a and add a sequence Crt+a+c the sequence won’t fire but the simple call would. I didn’t look at the provided js to see what they were doing differently about the calls though they call different methods in their js. As long as you are happy with the result all the better. The heads up is good...
 

Cableguy

Expert
Licensed User
Longtime User
So, been playing a bit more...

Since I will be setting a few Key Seequences for several different pages, I decided to try and integrate the needed code into the Shared Module...

This is what I've got working:

After declaring the mashkpress component as public in the Shared module Class Globals I added...
B4X:
'Register a ComboKey Listener component onto Page.cell(1,1)
Public Sub RegisterComboKeyListener(Page As ABMPage,ComboKeySetName As String)
 mashkpress.Initialize(Page,"mashkpress")
 Select ComboKeySetName
  Case "KeyCombo1"
   Dim KeyCombo(2) As List
   KeyCombo(0) = Array As String(mashkpress.KeyCtrl, mashkpress.KeyAlt)
   KeyCombo(1) = Array As String(mashkpress.KeyCtrl, mashkpress.KeySpace)   
 End Select
 
 For a = 0 To KeyCombo.Length -1
 mashkpress.AddSequence(KeyCombo(a))
 Next
 Page.Cell(1,1).AddComponent(mashkpress.ABMComp)
End Sub

Of course, the js still has to be referenced in each page that will use it...

So to create the page listenner, I just call...
B4X:
 ABMShared.RegisterComboKeyListener(AppPage,"KeyCombo1")

With this approach we only need the event sub in each page, and the call to the Shared module.

B4X:
  Case "KeyCombo1"
   Dim KeyCombo(2) As List
   KeyCombo(0) = Array As String(mashkpress.KeyCtrl, mashkpress.KeyAlt)
   KeyCombo(1) = Array As String(mashkpress.KeyCtrl, mashkpress.KeySpace)   
 End Select
With this part, we can create as much KeyCombos as we want, even 1 keyCombo set per page if we want to, so that its easier to maintain.

Well, these were my two cents for now...
 
Top