IME.SetCustomFilter within a Class

Roger Garstang

Well-Known Member
Licensed User
Longtime User
B4X:
'Class module
Private Sub Class_Globals
   Type MyTextbox(Name As String, ActionBtn As Byte, ActionSub As String, ActionSubModule As Object, MinChar As Int, MaxChar As Int, Uppercase As Boolean, EditView As EditText)
   Private IME As IME ' Put here instead of below so class is considered an Activity
   Private TB As List
   Public Alpha, Numeric, Punctuation As String
End Sub

' Initialize the Class
Public Sub Initialize
   TB.Initialize
   IME.Initialize("IME")
   Alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
   Numeric = "0123456789"
   Punctuation = "!@#$%&*()-+=,.;:?/'" & QUOTE
End Sub

' Name: Name given to the control for reference or use in a Select Case Block
' DataType: 0=Text, 1=Sentence, 2=Alpha, 3=Numeric, 4=AlphaNumeric, 5=Float, 6=Password, 7=Email, 8=URL, 9=Phone, 10=Name, 11=DateTime, 12=Date, 13=Time
' ActionBtn: 0=Next, 1=Previous, 2=Done, 3=Go, 4=Search, 5=Send
' ActionSub: Name of Sub to call for Action Buttons other than Next and Previous
' ActionSubModule: Activity/Module that Sub is in.  (Usually Me can be used if Sub is in Activity Module the View resides in)
Public Sub AddTextBox(Name As String, Text As String, Hint As String, MultiLine As Boolean, DataType As Byte, ActionBtn As Byte, ActionSub As String, ActionSubModule As Object, MinChar As Int, MaxChar As Int, Uppercase As Boolean) As EditText
Dim ref As Reflector
Dim baseOptions As Int
Dim newTextBox As MyTextbox

   newTextBox.Initialize
   newTextBox.EditView.Initialize("Edit")
   newTextBox.ActionBtn = ActionBtn
   newTextBox.ActionSub = ActionSub
   newTextBox.ActionSubModule = ActionSubModule
   newTextBox.MinChar = MinChar
   newTextBox.MaxChar = MaxChar
   newTextBox.Name = Name
   newTextBox.Uppercase = Uppercase
   newTextBox.EditView.Text = Text
   newTextBox.EditView.Hint = Hint
   'flagNoExtractUi= 268435456
   'flagNoEnterAction= 1073741824
   baseOptions = 268435456
   If MultiLine Then
      newTextBox.EditView.SingleLine = False
      newTextBox.EditView.Wrap= True
      baseOptions = Bit.Or(baseOptions, 1073741824)
   Else
      newTextBox.EditView.SingleLine = True
      newTextBox.EditView.Wrap= False
   End If
   Select Case DataType
      Case 0 ' Text
         newTextBox.EditView.InputType = 1
      Case 1 ' Sentence
         newTextBox.EditView.InputType = 16385
         IME.SetCustomFilter(newTextBox.EditView, newTextBox.EditView.InputType, Alpha & Numeric & Punctuation)
      Case 2 ' Alpha
         newTextBox.EditView.InputType = 1
         IME.SetCustomFilter(newTextBox.EditView, newTextBox.EditView.InputType, Alpha)
      Case 3 ' Numeric
         newTextBox.EditView.InputType = 2
         IME.SetCustomFilter(newTextBox.EditView, newTextBox.EditView.InputType, Numeric)
      Case 4 ' AlphaNumeric
         newTextBox.EditView.InputType = 1
         IME.SetCustomFilter(newTextBox.EditView, newTextBox.EditView.InputType, Alpha & Numeric)
      Case 5 ' Float
         newTextBox.EditView.InputType = 12290
         IME.SetCustomFilter(newTextBox.EditView, newTextBox.EditView.InputType, Numeric & "-.")
      Case 6 ' Password
         newTextBox.EditView.InputType = 129
      Case 7 ' Email
         newTextBox.EditView.InputType = 33
      Case 8 ' URL
         newTextBox.EditView.InputType = 17
      Case 9 ' Phone
         newTextBox.EditView.InputType = 3
      Case 10 ' Name
         newTextBox.EditView.InputType = 8289
      Case 11 ' DateTime
         newTextBox.EditView.InputType = 4
      Case 12 ' Date
         newTextBox.EditView.InputType = 20
      Case 13 ' Time
         newTextBox.EditView.InputType = 36
   End Select
   If Uppercase Then newTextBox.EditView.InputType = Bit.Or(newTextBox.EditView.InputType, 4096)
   ref.Target = newTextBox.EditView
   Select Case ActionBtn
      Case 0 ' Next 5
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 5), "java.lang.int")
      Case 1 ' Previous 7
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 7), "java.lang.int")
      Case 2 ' Done 6
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 6), "java.lang.int")
      Case 3 ' Go 2
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 2), "java.lang.int")
      Case 4 ' Search 3
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 3), "java.lang.int")
      Case 5 ' Send 4
         ref.RunMethod2("setImeOptions", Bit.Or(baseOptions, 4), "java.lang.int")
   End Select
   IME.AddHandleActionEvent(newTextBox.EditView)
   newTextBox.EditView.Tag = TB.Size
   TB.Add(newTextBox)
   Return newTextBox.EditView
End Sub

Private Sub IME_HandleAction As Boolean
Dim ET As EditText
Dim MTB As MyTextbox
Dim index As Int

   ET = Sender
   index = ET.Tag
   If index > -1 Then
      MTB = TB.Get(index)
      Select Case MTB.ActionBtn
         Case 0 ' Next
            If TB.Size > index + 1 Then
               MTB = TB.Get(index + 1)
               MTB.EditView.RequestFocus
               Return True
            End If
         Case 1 ' Prev
            If index > 0 Then
               MTB = TB.Get(index - 1)
               MTB.EditView.RequestFocus
               Return True
            End If
         Case Else ' Done, Go, Search, Send
            If MTB.ActionSub.Length > 0 Then CallSubDelayed(MTB.ActionSubModule, MTB.ActionSub)
      End Select
   End If
End Sub

Private Sub Edit_TextChanged (Old As String, New As String)
Dim cursorPOS As Int
Dim ET As EditText
Dim MTB As MyTextbox
Dim index As Int

   If New.CompareTo(Old) <> 0 Then
      ET = Sender
      index = ET.Tag
      If index > -1 Then
         MTB = TB.Get(index)
         cursorPOS = MTB.EditView.SelectionStart
         If MTB.MaxChar > 0 Then
            MTB.EditView.Text = New.SubString2(0, Min(MTB.MaxChar, New.Length))
         End If
         If MTB.Uppercase Then MTB.EditView.Text = MTB.EditView.Text.ToUpperCase
         MTB.EditView.SelectionStart = Min(cursorPOS, MTB.EditView.Text.Length)
      End If
   End If
End Sub
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Maybe the problem is somewhere else.
This simple test works fine:
B4X:
'Class module
Sub Class_Globals
   Private et As EditText
End Sub

'Initializes the object. You can add parameters to this method if needed.
Sub Initialize 
   et.Initialize("et")
   et.InputType = et.INPUT_TYPE_NUMBERS
   Dim i As IME
   i.SetCustomFilter(et, et.INPUT_TYPE_NUMBERS, "0123.")
End Sub

Sub AsView As View
   Return et
End Sub

'Main activity
Sub Activity_Create(FirstTime As Boolean)
   Dim m As MyEditText
   m.Initialize
   Activity.AddView(m.AsView, 0, 0, 300dip, 100dip)
End Sub
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
The type is stored in a list and each view is their own index in it. It may end up being a layout manager using a panel when finished... If I can get this working. I have other subs too for validating all the views in the list, returning a map of names and the views values, etc. Storing them this way allows next and previous buttons to work too. B4A internally needs something like this. I had the exact same code in a code module before and it worked.
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
I don't think you fully looked at/used/understood the Class. When you create the Textbox with the class you pass it a Name. If you give it a name then it will be in the map returned (Map code not included as I just gave the part to show the issue). The part I meant B4A needs wasn't the Name part either, but storing them in a way to have a type of Tab Order like I do so when you set the Action Button (The button that shows in the keyboard) to Next or Previous they actually work and go to the Next/Previous Control. I also added the ability to specify a sub to call like an event when the other types of action buttons are pressed. B4A also could benefit from the more advanced Input Types and restrictions (Once they work). This is a self contained Form really now where you setup all of your EditTexts within it and draw them where you want and the Class handles all the events and requirements you specify. All you have to worry about is getting the data from it without caring about all the IME settings and other stuff needed for them to behave how you expect.

Classes make this a little easier...although it could be better. The Code Module version was mostly the same except it stored the Type in the view's Tag and the Type needed to store a Previous and Next view like a Linked List. You also had to pass the previous view when creating it so it could use it to set the current view's previous view then navigate to the previous view and set its Next View. The List makes this process a lot easier and automated. Now the class handles it all and the Tag just needs to hold an Index number that is used in the internal Private Subs of the class mostly. I may end up storing an Map of Names and Values within the class so single values can be got easier than scanning the list.
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
I have to run some more tests, but I think it is my code for Uppercase:

B4X:
If Uppercase Then newTextBox.EditView.InputType = Bit.Or(newTextBox.EditView.InputType, 4096)

I may have had that up in the Select Case or before it in the early Code Module code and moved it there so it only executed once. Appears that if after you call SetCustomFilter then change InputType something is lost or disconnects.

How exactly does SetCustomFilter work so I can design this the best way and not clobber anything that it needs?

Also, I kept my IME.AddHeightChangedEvent back in main with the assumption it may not work in a class (I could be wrong though), but if I further this along and use a panel and have the class control layout and respond to the panel size changing and such I may need it within the class. Will it work properly or does it need to remain in the activity? Just checking before I change the code around.
 
Top