How to catch unicode text input in wxWidgets like wxTextCtrl on windows?

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
kilasuelika
In need of some credit
In need of some credit
Posts: 6
Joined: Tue Sep 24, 2019 1:02 pm

How to catch unicode text input in wxWidgets like wxTextCtrl on windows?

Post by kilasuelika » Fri Oct 25, 2019 12:24 pm

I need to build my own text editor. The main problem is I don't know how to get text input from OS just like wxTextCtrl. I had read the source code of msw/wxTextCtrl.cpp but I didn't understand which part of it deal with input.

When use wxTextCtrl, if I press some keys, there will be a candidate window( which is shown by the operating system) and user can send multiple unicode characters to system in one time:

Image


Currently I think that OnChar() and MSWHandleMessage() are most likely to be responsible for that. For example there are nMsg == WM_CHAR, but WM_CHAR is only a keycode, not a text string. I think as there are only a switch statement in OnChar() so that it doesn't necessirialy deal with text input. Can anyone give some instructions?

Code: Select all

void wxTextCtrl::OnChar(wxKeyEvent& event)
{
    switch ( event.GetKeyCode() )
    {
        case WXK_RETURN:
            {
                wxCommandEvent evt(wxEVT_TEXT_ENTER, m_windowId);
                InitCommandEvent(evt);
                evt.SetString(GetValue());
                if ( HandleWindowEvent(evt) )
                    if ( !HasFlag(wxTE_MULTILINE) )
                        return;
                    //else: multiline controls need Enter for themselves
            }
            break;

        case WXK_TAB:
            // ok, so this is getting absolutely ridiculous but I don't see
            // any other way to fix this bug: when a multiline text control is
            // inside a wxFrame, we need to generate the navigation event as
            // otherwise nothing happens at all, but when the same control is
            // created inside a dialog, IsDialogMessage() *does* switch focus
            // all by itself and so if we do it here as well, it is advanced
            // twice and goes to the next control... to prevent this from
            // happening we're doing this ugly check, the logic being that if
            // we don't have focus then it had been already changed to the next
            // control
            //
            // the right thing to do would, of course, be to understand what
            // the hell is IsDialogMessage() doing but this is beyond my feeble
            // forces at the moment unfortunately
            if ( !(m_windowStyle & wxTE_PROCESS_TAB))
            {
                if ( ::GetFocus() == GetHwnd() )
                {
                    int flags = 0;
                    if (!event.ShiftDown())
                        flags |= wxNavigationKeyEvent::IsForward ;
                    if (event.ControlDown())
                        flags |= wxNavigationKeyEvent::WinChange ;
                    if (Navigate(flags))
                        return;
                }
            }
            else
            {
                // Insert tab since calling the default Windows handler
                // doesn't seem to do it
                WriteText(wxT("\t"));
                return;
            }
            break;
    }

    // no, we didn't process it
    event.Skip();
}

#if wxUSE_OLE

void wxTextCtrl::MSWProcessSpecialKey(wxKeyEvent& event)
{
    // It is not a good idea, in general, to manually call another event
    // handler, but here we need to do exactly the same thing as in OnChar()
    // above, so it doesn't seem to make much sense to add another function to
    // forward to when we can just call it directly.
    OnChar(event);
}

#endif // wxUSE_OLE

void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
{
    // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages
    // when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent
    // from working. So we work around it by intercepting these shortcuts
    // ourselves and emitting clipboard events (which richedit will handle,
    // so everything works as before, including pasting of rich text):
    if ( event.GetModifiers() == wxMOD_CONTROL && IsRich() )
    {
        switch ( event.GetKeyCode() )
        {
            case 'C':
                Copy();
                return;
            case 'X':
                Cut();
                return;
            case 'V':
                Paste();
                return;
            default:
                break;
        }
    }

    if ( IsMultiLine() )
    {
        // Default window procedure of multiline edit controls posts WM_CLOSE to
        // the parent window when it gets Escape key press for some reason, prevent
        // it from doing this as this resulted in dialog boxes being closed on
        // Escape even when they shouldn't be (we do handle Escape ourselves
        // correctly in the situations when it should close them).
        if ( event.GetKeyCode() == WXK_ESCAPE )
            return;

        // We also handle Ctrl-A as the native EDIT control doesn't do it by
        // default (but RICHEDIT one does, so there is no need to check for it
        // in the switch above), however it's a de facto standard accelerator
        // and people expect it to work.
        if ( event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A' )
        {
            SelectAll();
            return;
        }
    }

    // no, we didn't process it
    event.Skip();
}

bool
wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
                             WXUINT nMsg,
                             WXWPARAM wParam,
                             WXLPARAM lParam)
{
    bool processed = wxTextCtrlBase::MSWHandleMessage(rc, nMsg, wParam, lParam);

    // Handle the special case of "Enter" key: the user code needs to specify
    // wxTE_PROCESS_ENTER style to get it in the first place, but if this flag
    // is used, then even if the wxEVT_TEXT_ENTER handler skips the event, the
    // normal action of this key is not performed because IsDialogMessage() is
    // not called and, also, an annoying beep is generated by EDIT default
    // WndProc.
    //
    // Fix these problems by explicitly performing the default function of this
    // key (which would be done by MSWProcessMessage() if we didn't have
    // wxTE_PROCESS_ENTER) and preventing the default WndProc from getting it.
    if ( nMsg == WM_CHAR &&
            !processed &&
            HasFlag(wxTE_PROCESS_ENTER) &&
            wParam == VK_RETURN &&
            !wxIsAnyModifierDown() )
    {
        MSWClickButtonIfPossible(MSWGetDefaultButtonFor(this));

        processed = true;
    }

    switch ( nMsg )
    {
        case WM_GETDLGCODE:
            {
                // Ensure that the result value is initialized even if the base
                // class didn't handle WM_GETDLGCODE but just update the value
                // returned by it if it did handle it.
                if ( !processed )
                {
                    *rc = MSWDefWindowProc(nMsg, wParam, lParam);
                    processed = true;
                }

                // we always want the chars and the arrows: the arrows for
                // navigation and the chars because we want Ctrl-C to work even
                // in a read only control
                long lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS;

                if ( IsEditable() )
                {
                    // we may have several different cases:
                    // 1. normal: both TAB and ENTER are used for navigation
                    // 2. ctrl wants TAB for itself: ENTER is used to pass to
                    //    the next control in the dialog
                    // 3. ctrl wants ENTER for itself: TAB is used for dialog
                    //    navigation
                    // 4. ctrl wants both TAB and ENTER: Ctrl-ENTER is used to
                    //    go to the next control (we need some way to do it)

                    // multiline controls should always get ENTER for themselves
                    if ( HasFlag(wxTE_PROCESS_ENTER) || HasFlag(wxTE_MULTILINE) )
                        lDlgCode |= DLGC_WANTMESSAGE;

                    if ( HasFlag(wxTE_PROCESS_TAB) )
                        lDlgCode |= DLGC_WANTTAB;

                    *rc |= lDlgCode;
                }
                else // !editable
                {
                    // NB: use "=", not "|=" as the base class version returns
                    //     the same flags in the disabled state as usual (i.e.
                    //     including DLGC_WANTMESSAGE). This is strange (how
                    //     does it work in the native Win32 apps?) but for now
                    //     live with it.
                    *rc = lDlgCode;
                }

                if ( IsMultiLine() )
                {
                    // The presence of this style, coming from the default EDIT
                    // WndProc, indicates that the control contents should be
                    // selected when it gets focus, but we don't want this to
                    // happen for the multiline controls, so clear it.
                    *rc &= ~DLGC_HASSETSEL;
                }
            }
            break;

#if wxUSE_MENUS
        case WM_SETCURSOR:
            // rich text controls seem to have a bug and don't change the
            // cursor to the standard arrow one from the I-beam cursor usually
            // used by them even when a popup menu is shown (this works fine
            // for plain EDIT controls though), so explicitly work around this
            if ( IsRich() )
            {
                // wxCurrentPopupMenu stores the popup menu that will receive
                // WM_COMMAND, but it may be non-NULL even when the underlying
                // native menu is no longer shown. Use ::IsMenu() to check whether
                // the menu still exists.
                extern wxMenu *wxCurrentPopupMenu;
                if ( wxCurrentPopupMenu &&
                        wxCurrentPopupMenu->GetInvokingWindow() == this &&
                        ::IsMenu(GetHmenuOf(wxCurrentPopupMenu)) )
                    ::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
            }
#endif // wxUSE_MENUS
    }

    return processed;
}

User avatar
doublemax
Moderator
Moderator
Posts: 14277
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to catch unicode text input in wxWidgets like wxTextCtrl on windows?

Post by doublemax » Fri Oct 25, 2019 1:59 pm

As you've already mentioned, this is completely handled by the OS and there is no wxWidgets method to hook into it.

You'll need platform specific code for this. I have no experience with this myself, but i found these two links, maybe they help you into the right direction:
https://social.msdn.microsoft.com/Forum ... directx101
https://docs.microsoft.com/en-us/window ... -functions
Use the source, Luke!

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3568
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: How to catch unicode text input in wxWidgets like wxTextCtrl on windows?

Post by ONEEYEMAN » Fri Oct 25, 2019 2:02 pm

Hi,
What is the requirements?
Are you looking to build a source code editor or just a text editor?
What features you need to support?

If you are looking to low-level OS input - don't. You should be looking for high-end level, especially since OSX may handle this a little differently.

Take a look at the stc and text samples. Build them, run them and then look at their code.

If you have more question - post them here.

Thank you.

Manolo
Can't get richer than this
Can't get richer than this
Posts: 704
Joined: Mon Apr 30, 2012 11:07 pm

Re: How to catch unicode text input in wxWidgets like wxTextCtrl on windows?

Post by Manolo » Fri Oct 25, 2019 3:53 pm

wxWindow can receive keyboard input, as wxTextCtrl does. See the keyboard sample.

You can have a wxWindow (or one of its derivates) and handle its char-events. Joining chars you get the string. Also handle paste-event, you can get a string in just one event.

But you have to draw the text on your own, which means selecting a font, a DC, etc.

Post Reply