Capturing ESC key in BTextControl::KeyDown

I have a class inherited from BTextControl and the use case to capture the ESC key to cancel some ongoing process.

My attempt was to overwrite the keyDown method and do what I need to do:
SearchFieldControl.h:

class SearchFieldControl : public BTextControl {
public:
    SearchFieldControl();
// ... 
    virtual void KeyDown(const char *bytes, int32 numBytes) override;
//...

and the SearchFieldControl.cpp:

void
SearchFieldControl::KeyDown(const char *bytes, int32 numBytes) {
    if (numBytes > 0) {
        std::cout << "Key down: " << bytes << std::endl;
        if (bytes[0] == B_ESCAPE) {
            BMessage dismissSearchMessage(M_DISMISS_SEARCH);
            Window()->PostMessage(&dismissSearchMessage);
        }
        return;
    }
    BTextControl::KeyDown(bytes, numBytes);
}

But it seems that this method is not called.
Modification keys. (shift, ctrl, alt) find their way through the MessasgeReceived, but the ESC is swallowed somewhere.

Any Ideas?

I want to answer my own question.
My use case was: I have a Textfield (BTextControl, a search field) and I want to exit a running search by pressing the esc key.
The problem: The BTextControl won’t get the Esc key not as a message (text changed) nor as a keyDown.
I found a EscapeCancelFilter by DarkWyrm (Who also has written the Haiku guides, I think). With this filter attached to the window I was able to forward the message and catch it elsewhere.

EscapeCancelFilter.h:

class EscapeCancelFilter : public BMessageFilter {
public:
    EscapeCancelFilter(void)
            : BMessageFilter(B_PROGRAMMED_DELIVERY,
                             B_ANY_SOURCE, B_KEY_DOWN) {
    }

    ~EscapeCancelFilter(void) {}

    filter_result Filter(BMessage *msg, BHandler **target);
};

EscapeCancelFilter.cpp:

filter_result
EscapeCancelFilter::Filter(BMessage *msg, BHandler **target) {

    int32 rawchar;
    int32 mod;
    msg->FindInt32("raw_char", &rawchar);
    msg->FindInt32("modifiers", &mod);

    if (rawchar == B_ESCAPE && (mod & (B_SHIFT_KEY | B_COMMAND_KEY |
                                       B_OPTION_KEY | B_CONTROL_KEY)) == 0) {
        BLooper *loop = (*target)->Looper();
        if (loop) {
            BMessenger message(loop);
            message.SendMessage(M_DISMISS_SEARCH);
            return B_SKIP_MESSAGE;
        }
    }
    return B_DISPATCH_MESSAGE;

}

Gotcha!

2 Likes

Hmmm, wondering… :thinking:
Not the same but tangentially related:
Could something similar be made at a more general system level, to use the ESC key to cancel drag-n-drop operations?
As in: you select a group of icons, or some text, start dragging it to other place, change your mind while you still havn’t dropped the thing, and pushing ESC (while the mouse button is still pushed) cancels it.
Or it will be something that needs to be implemented in every place that is a dragging origin?
I ask this from a complete ignorance in the matter, no programming skills, it’s just that was thinking about asking for this in the bugtracker and this piqued my interest.

1 Like

Do not know if this is an answer, but have a look at the delivery parameter of the filters constructor:

enum message_delivery {
	B_ANY_DELIVERY,
	B_DROPPED_DELIVERY,
	B_PROGRAMMED_DELIVERY
};

Maybe here you can fine-tune the filter your needs?

Thank you for this reference.
It will not be of much use to me directly, as I really have zero coding knowledge and don’t know what to do with this, but I will link to it when creating the ticket for the feature request.
Many thanks!

That post is the only answer coming up in Google when looking for haikuos "BTextControl::KeyDown" :sweat_smile:

It would be great to add something in the documentation where KeyDown is mentioned, to give a hint that the hook will never be called as this event doesn’t bubble to the parent (BTextControl) from the child (BPrivate::_BTextInput_).

I’m still wondering if it’s a bug or a design shortcoming, but I’m in the process to properly learn the API, so it is hard to assess that.

My use case is that I’m trying to catch every stroke. But I guess I’d have to come up with my own TextControl to do that my way.

I’ll see.

1 Like