Per-word autocomplete

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
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Per-word autocomplete

Post by Nathaniell »

Hi,
I have a wxTextCtrl text field as a search bar in our application and I am implementing search for tags. I am trying to implement per-word suggestions.

Basically, you write 'a' and get suggested (ananas, avodaco, apple)... This part works with autocomplete. But now I want to write space and start writing another word... and get suggestion/autocomplete only for this word - only solution I came up with was to just suggest the whole old text + the new word suggestion.. but that would not look very nice (and ideally, I would like this to work even if you start typing in the middle of 2 already typed words).

Basically, I need per-word suggestions like in visual studio. As far as I can tell it looks to me like autocomplete is only meant to work with one word in the text box.

Am I missing something or autocomplete just can't do this? If it can't - I appreciate any other suggestions what wxWidgets "features" could be used to get this to work.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Per-word autocomplete

Post by doublemax »

I've used the autocomplete feature only once, but AFAIK what you're trying to do is impossible. And i'm afraid the only option left would be to implement the whole functionality from scratch (e.g. with a wxPopupWindow containing a wxListBox).
Use the source, Luke!
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 469
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Per-word autocomplete

Post by New Pagodi »

If you don't mind using wxStyledTextControl, its autocomplete feature can function in the manor you're describing.
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

I haven't tried the styled control yet, but from the wxFormBuilder it looked like it is intended for big text windows and I haven't found a way how to remove its' scroll bars. Because I need to have basically just one line search bar where you can enter multiple words. But I don't need or want any scroll bars around.
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

doublemax wrote: Thu Sep 03, 2020 10:10 am I've used the autocomplete feature only once, but AFAIK what you're trying to do is impossible. And i'm afraid the only option left would be to implement the whole functionality from scratch (e.g. with a wxPopupWindow containing a wxListBox).
Well, I assumed that will be the case when I tried to do it with combo box and autocomplete, but I hoped there might have been some other way... thats why I asked here.

Thanks for answer and I will try to do it as you suggest.
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 469
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Per-word autocomplete

Post by New Pagodi »

Nathaniell wrote: Mon Sep 07, 2020 8:52 am I haven't tried the styled control yet, but from the wxFormBuilder it looked like it is intended for big text windows and I haven't found a way how to remove its' scroll bars. Because I need to have basically just one line search bar where you can enter multiple words. But I don't need or want any scroll bars around.
You can make a wxSTC look and act like a single line text control with something like the following:

Code: Select all

// Hide the left margin.
m_stc->SetMarginWidth(1,0);

// Hide the scrollbars.
m_stc->SetUseVerticalScrollBar(false);
m_stc->SetUseHorizontalScrollBar(false);

// Set the height to be large enough for 1 line of text.
int textHt = m_stc->GetTextExtent("ABCxyz").GetHeight();
m_stc->SetMaxClientSize(wxSize(-1,textHt));

// Eat all enter key presses so that only a single line of text will be shown.
m_stc->Bind(wxEVT_KEY_DOWN,
            [](wxKeyEvent& event)
            {
                if ( event.GetKeyCode() != WXK_RETURN )
                {
                    event.Skip();
                }
            });

// Bind the function the handle autocompletion for the control.
m_stc->Bind(wxEVT_STC_CHARADDED, &MyFrame::OnStcChar, this);
}
Here is a simple sample of autocompletion code that only checks if the character "a" was just typed and shows some dummy options if it was. Obviously any real autocompletion code will need to be more complicated. You can also use the wxEVT_STC_MODIFIED event instead of the wxEVT_STC_CHARADDED event.

Code: Select all

void MyFrame::OnStcChar(wxStyledTextEvent& event)
{
    if ( event.GetKey() == 97 )
    {
        m_stc->AutoCompShow(1,"aaa aab aac aad abb abc");
    }
}
On windows, it looks like this:
singlelinestcwithac.png
singlelinestcwithac.png (3.08 KiB) Viewed 1662 times
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

Hi, I had finally time to get back to this again. I have, for now, decided to try it with my own popup transient window. So far, I have successfully created the popup window and positioned it where I want it with this code:

Code: Select all

class wx_Popup_Transient_Window : wxPopupTransientWindow
{
public:
	enum wxID
	{
		wxID_panel = 110,
		wxID_list = 111,
		wxID_text = 112,
		wxID_button = 113
		
	};
	wx_Popup_Transient_Window(wxWindow *the_parent)	
		:wxPopupTransientWindow::wxPopupTransientWindow(the_parent)
	{
		parent = the_parent;
		sizer = new wxBoxSizer(wxVERTICAL);
		panel = new wxPanel(this, wxID_panel);
		list = new wxListBox(panel, wxID_list);
		button = new wxButton(panel, wxID_button, "Press");
		text = new wxTextCtrl(panel, wxID_text);

		list->AppendString("test1");
		list->AppendString("test2");
		list->AppendString("test3");
		list->AppendString("test4");
		list->Select(1);

		sizer->Add(button, 1, wxEXPAND, 0);
		sizer->Add(list, 1, wxEXPAND, 0);
		sizer->Add(text, 1, wxEXPAND, 0);
		sizer->Add(new wxTextCtrl(panel, wxID_ANY, "Try to type here"),
			1, wxEXPAND | wxALL, 5);

	}
	void Popup(void)
	{
		panel->SetSizer(sizer);
		sizer->Fit(panel);
		this->SetClientSize(panel->GetSize());
		wxTextCtrl *const text_parent = static_cast<wxTextCtrl *>(parent);
		const wxPoint parent_pos = parent->GetScreenPosition();
		wxSize parent_size = text_parent->GetSize();
		const wxPoint right_offset = text_parent->PositionToCoords(text_parent->GetInsertionPoint());
		wxPoint final_position = parent_pos + right_offset;
		Position(final_position, wxSize(0, parent_size.y));
		wxPopupTransientWindow::Popup();
	}
private:
	wxTextCtrl *text;
	wxBoxSizer *sizer;
	wxListBox *list;
	wxButton *button;
	wxPanel *panel;
	wxWindow *parent;
};
Problem is, the window is not reacting to my inputs. I know I haven't registered any events, but for instance, I cant even select anything in my list box (mouse click just does nothing), which is something for which, as far as I am aware, I don't have to bind anything. I also tryed to bind onMouse from the example https://github.com/wxWidgets/wxWidgets/ ... /popup.cpp and that event worked.

I have no idea what I have to do to make the selection on the list work.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Per-word autocomplete

Post by doublemax »

I haven't tested it, but try these two changes:

a) Pass wxPU_CONTAINS_CONTROLS to the wxPopupTransientWindow constructor, like in the sample code you linked to
b) The IDs for the controls should start at wxID_HIGHEST+1, otherwise they can collide with IDs used by wxWidgets internally.
Use the source, Luke!
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

Thank you very much! wxPU_CONTAINS_CONTROLS fixed my problem, I have totaly missed it from the example, as I started using it after some of my things did't work.
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

I have progressed a little bit and now have a functional popup window over my textCtrl, which does not disappear when I am typing in this textCtrl.

When I press down arrow I change focus to the popup window with my listbox and select first item (so I can then use arrows to go through the list).

Now I am trying to achieve that when I start typing again while the listbox is focused, I would focus the textCtrl again and insert the typed character.

I have a handler in popup window which should in case of any other key than ENTER and UP/DOWN arrows send the event back to the textCtrl (parent), but so far I wasn't able to achieve this.

I have tried to call from my Key handler in the listbox (event is the originally received key event):

Code: Select all

parent->GetEventHandler()->ProcessEvent(event);
//////////////////////
event->SetEventObject(parent);
parent->GetEventHandler()->ProcessEvent(event);
////////////////////////////
event->SetEventObject(parent);
wxKeyEvent *tmp_event = new wxKeyEvent(event);
parent->GetEventHandler()->QueueEvent(event);
Nothing worked. Could someone point me in the right direction please?
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Per-word autocomplete

Post by doublemax »

When I press down arrow I change focus to the popup window with my listbox and select first item (so I can then use arrows to go through the list).
This doesn't sound like a good idea. I think it will be more reliable if you catch the cursor keys in the text control and handle the selection change in the listbox yourself.
Use the source, Luke!
Nathaniell
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Sep 03, 2020 9:10 am

Re: Per-word autocomplete

Post by Nathaniell »

Sadly, we have our own layer that separates wxWidgets from our applications and we have our own editbox handler which provides all the function to interact with the textCtrls. And since this popup window which I am creating is kind of special... and not needed most of the time, I wanted to make as little changes to our editbox handler as possible. So I want it to only have functions to initialize the popup window and to popup and possibly dismiss it (the function to call when someting gets "activated" (double click/enter) in the listbox is provided to the popup window in the initialization.) So I understand it might not be really pretty to change focus with the arrow key to the popup window and try to get it back when pressing some other keys in the popup, but it kind of makes sense in my case. My only problem is that I am not able to send the pressed key back to the textCtrl. So lets say I write something, get with the arrow key to the popup listbox and then want to continue writing. Currently, I have to press the key twice, because my first press will just switch my focus back (this thing works without problem), I am just unable to send the key press event to the textCtrl.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Per-word autocomplete

Post by doublemax »

Use the source, Luke!
Post Reply