Child's wxTextCtrl EVT_TEXT conflicting with app's EVT_CHAR_HOOK Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
User avatar
Artifact2238
Earned a small fee
Earned a small fee
Posts: 23
Joined: Mon May 28, 2018 2:43 am

Child's wxTextCtrl EVT_TEXT conflicting with app's EVT_CHAR_HOOK

Post by Artifact2238 »

Hello,

I've made a minimal example that has a main app with a frame, and the frame has a CustomTextCtrl and CustomPanel as children.

I have two questions from this, and would appreciate any help or ideas:

1. When you run the program, you'll see the event order is the following when you type into the TextCtrl
  • MyApp::OnCharHook
  • CustomTextCtrl::OnText
When you type a character in the TextCtrl, both events fire. It's pretty obvious why this happens, as they are separate types of event handlers, but what I want to happen is that if the user is currently typing in the CustomTextCtrl, I want ONLY that event to fire, and to not fire the App's OnCharHook event.

My question is, how should I achieve this? And, is there a way to do this which makes it so the MyApp::OnCharHook doesn't need to manually check the state of the CustomTextCtrl? The reason for this latter desire is that if I have multiple TextCtrls, it would be tedious and not scalable to check every TextCtrl that could possibly be active.

2. If I click into the TextCtrl, it correctly shows the blinking cursor and allows me to type. Clicking anywhere outside the TextCtrl (including the CustomPanel) then removes the blinking cursor, and I can no longer type. This is correct, expected behavior. The problem is that in my actual program, clicking from my TextCtrl to my wxGLCanvas doesn't make the TextCtrl stop blinking.

How can I get the behavior of clicking into a wxPanel to be the same behavior as clicking into a wxGLCanvas? (I didn't paste the code for this, because it's notoriously long to have a minimal OpenGL example, but I could if necessary.)
UPDATE: I solved #2 by simply calling

Code: Select all

    if (event.LeftDown() || event.LeftDClick())
    {
        this->SetFocus();
in the wxGLCanvas's mouse event handler. This seems to work fine, but of course let me know if there's any pitfalls to this. Still not sure about question #1.

Code: Select all

#include "wx/wx.h"
#include <iostream>

class CustomTextCtrl : public wxTextCtrl
{
  public:
    CustomTextCtrl(wxWindow *parent,
                   wxWindowID id)
    : wxTextCtrl(parent, id) { }

    void OnText(wxCommandEvent& event);

    wxDECLARE_EVENT_TABLE();
};

class CustomPanel : public wxPanel {
  public:
    CustomPanel(wxWindow* parent)
    : wxPanel(parent)
    {
        this->SetBackgroundColour(wxColour(100, 100, 200));
        this->Refresh();
    }

    void OnMouse(wxMouseEvent& event);

    wxDECLARE_EVENT_TABLE();
};

enum
{
    TextBox_OnText =  wxID_HIGHEST + 10,
};

class MyApp : public wxApp
{
    wxFrame *frame;
public:

    bool OnInit()
    {
        frame = new wxFrame((wxFrame *)NULL, -1,  wxT("wxWidgets"), wxPoint(50,50), wxSize(800, 600));

        CustomPanel* blue_panel = new CustomPanel(frame);

        int size = 200;
        int spacing = 100;

        blue_panel->SetSize(size, size);
        blue_panel->SetPosition(wxPoint(spacing, spacing));

        CustomTextCtrl* textCtrl = new CustomTextCtrl(frame, TextBox_OnText);
        textCtrl->ChangeValue("test text");
        frame->Show();
        return true;
    }

    wxDECLARE_EVENT_TABLE();

    void OnCharHook(wxKeyEvent& event);
    void OnMouse(wxMouseEvent& event);
};

IMPLEMENT_APP(MyApp)

wxBEGIN_EVENT_TABLE(MyApp, wxApp)
    EVT_CHAR_HOOK(MyApp::OnCharHook)
    EVT_MOUSE_EVENTS(MyApp::OnMouse)
wxEND_EVENT_TABLE()

void MyApp::OnCharHook(wxKeyEvent& event)
{
    std::cout << "MyApp::OnCharHook\n";
    event.Skip();
}

void MyApp::OnMouse(wxMouseEvent& event)
{
    event.Skip();
}

wxBEGIN_EVENT_TABLE(CustomPanel, wxPanel)
    EVT_MOUSE_EVENTS(CustomPanel::OnMouse)
wxEND_EVENT_TABLE()

void CustomPanel::OnMouse(wxMouseEvent& event)
{
    static int iteration = 0;

    if (event.LeftUp())
    {
        std::cout << "Lift at " << "(" << event.GetX() << ", " << event.GetY() << ") [" << iteration++ << "]\n";
    }
    else if (event.LeftDown() || event.LeftDClick())
    {
        std::cout << "Click at " << "(" << event.GetX() << ", " << event.GetY() << ") [" << iteration++ << "]\n";
    }

    event.Skip(); // continue to propagate (to parent?)
}

wxBEGIN_EVENT_TABLE(CustomTextCtrl, wxTextCtrl)
    EVT_TEXT(TextBox_OnText, CustomTextCtrl::OnText)
wxEND_EVENT_TABLE()

void CustomTextCtrl::OnText(wxCommandEvent& event)
{
    std::cout << "CustomTextCtrl::OnText\n";
}
Last edited by Artifact2238 on Wed Jul 03, 2019 6:54 pm, edited 1 time in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child's wxTextCtrl EVT_TEXT conflicting with app's EVT_CHAR_HOOK

Post by doublemax »

My question is, how should I achieve this? And, is there a way to do this which makes it so the MyApp::OnCharHook doesn't need to manually check the state of the CustomTextCtrl? The reason for this latter desire is that if I have multiple TextCtrls, it would be tedious and not scalable to check every TextCtrl that could possibly be active.
As OnCharHook gets called first, i don't see how this can be possible. But if you have a CustomTextCtrl anyway, you have a central place to add some detection code. E.g. you could add a static variable that's set whenever one of them gets focus.
The problem is that in my actual program, clicking from my TextCtrl to my wxGLCanvas doesn't make the TextCtrl stop blinking.
I can't produce this in the "cube" sample that comes with wxWidgets. I added a wxTextCtrl to it and it loses focus when i click into the GLCanvas.
Use the source, Luke!
User avatar
Artifact2238
Earned a small fee
Earned a small fee
Posts: 23
Joined: Mon May 28, 2018 2:43 am

Re: Child's wxTextCtrl EVT_TEXT conflicting with app's EVT_CHAR_HOOK

Post by Artifact2238 »

Thank you for the reply, as always.

I didn't think to make a static/singleton-like variable to encapsulate all possible textCtrls. Seems a bit hackish, but I guess that is a lot cleaner than individually checking every text ctrl, so I'll have to work with that. Thank you.

(For anyone reading, I am on Windows 7. Forgot to mention that.) With regard to the wxGLCanvas, I solved it by simply calling SetFocus() on mouse down. I'm not sure why it happens either. If I run into this problem again, I'll force myself to whittle down my program with the wxGLCanvas to see what the actual difference is. For now, it seems to be solved.
Post Reply