EditText: intercept/discard keystrokes



  • Hello there,

    is it possible to intercept/discard keystrokes on a gadget (EditText) in a GeDialog?
    Seems like all relevant messages are sent after the content has changed.
    With the Dialogs Message() method, I'm not able to retrieve the previous value, when a user enters a text. How's that actually supposed to be done?

    Thanks in advance,
    Robert



  • Hi @mp5gosu, You can react to BFM_INTERACTSTART and detect what key was pressed and in this case, and return True, so next message will not be dispatched and processed.

    class MyDialog(c4d.gui.GeDialog):
        
    
        def CreateLayout(self):
            self.SetTitle("My Python Dialog")
    
            self.AddEditText(1000, c4d.CUSTOMGUI_DESCRIPTION, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 100, 500)        
            self.AddEditText(1001, c4d.CUSTOMGUI_DESCRIPTION, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 100, 500)
            
            return True
    
        def Message(self, msg, result):         
            if msg.GetId() == c4d.BFM_INTERACTSTART:
                bc = c4d.BaseContainer()
                if c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.KEY_LEFT, bc):
                    if bc[c4d.BFM_INPUT_VALUE] == 1:
                        if self.IsActive(1000): return True
    
                if c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.KEY_RIGHT, bc):
                    if bc[c4d.BFM_INPUT_VALUE] == 1:
                        if self.IsActive(1000): return True           
    
            return super(MyDialog, self).Message(msg, result)
    

    If you have any question please let me know.
    Cheers,
    Maxime.



  • Hi Maxime

    thank you for the example.
    I was in fact also trying BFM_INTERACTSTART but had to scratch that, because at the same time I wanted to poll the actual key being pressed, not testing for a specific key.

    I simply want to implement some "evaluation" when the user types something in, with regards to a certain scheme. Maybe I should have been more precise here. :)
    So for now I was able observe the following: BFM_INTERACTSTART does indeed what I want. If the user types something in, I'm able to check the current value of the text box, before it gets changed and may discard the user input. The downside here is, that the msg value is actually an empty BC with its sole purpose to hold its ID.
    (Maybe the result container could carry some more contextual information when starting to interact with gadgets)

    Then, checking the input state is not working either, because it lets me only check for a specific key.
    GetInputEvent doesn't work either, because it's just an event stack and not reliable at all due to its nature.

    Maybe (and theres a good chance) I'm overcomplicating things now, in germany we call it something like "to not see the forest for the trees". :)

    Thank you so far,
    Robert



  • May I ask you what's the final goal?



  • Of course! :)
    I have an EditText, into which users may enter some text.
    This EditText has to follow these rules:

    1. It must always have 3 characters.
    2. Those 3 characters are always digits (0-9, in any order)
    3. if 1) or 2) do not apply when the user enters or pastes something, set it to the last valid value. This has to be done while the user enters something.

    My workaround so far is to hold a member variable that always keeps the last valid value and replaces it when a user entered something that does not comply to the rules above. The rules are checked with a regular expression.

    I thought, in Cinema may it be possible as well, to intercept messages and to pass, discard or modify them (like in Windows message queue for example).



  • Hi mp5gosu, according to your description, I think you should handle everything in the command.

    class MyDialog(c4d.gui.GeDialog):
        
        def __init__(self):
            self.lastValid = ""
    
        def CreateLayout(self):
            self.AddEditText(1000, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 100, 50)
            return True
    
        def isCorrect(self, s):
            # It's not correct if it's larger than 3
            if len(s) > 3:
                return False
            
            # It's do not conatin only number
            try:
                int(s)
            except ValueError:
                return False
            
            return True
        
        
        def Command(self, id, msg):
            if id == 1000:
                newStr = self.GetString(id)
                
                # If their is no string stored, we have nothing to do
                if not newStr:
                    self.lastValid = newStr
                    return True
                
                # Check if the string fit our format, if yes store the value
                elif self.isCorrect(newStr):
                    self.lastValid = newStr
                    
                # Finally if it does not fil the format, this means we have to revert to last valid
                else:
                    self.SetString(id, self.lastValid)
                    
            return True
    

    Actually, you can catch and intercept the message, from the UI element, not from the parent (dlg), so if you want to do so, you have to create your own gadget but this is not possible in Python.

    Cheers,
    Maxime.



  • Thank you Maxime.

    My current solution looks pretty similar to yours and works .
    Implementing an own gadget is of course out of scope - I don't even get paid for that. ☺