A Nicer Query Builder Widget

Published on mar 16 juillet 2013 in Nepomuk, (Comments)

After 10 days of vacations, I'm now back at work for the rest of the GSoC period. Before my departure, I presented a syntax-highlighted query builder widget. It was based on a QTextEdit made to look like a QLineEdit, and a QSyntaxHighlighter subclass was responsible for the highlighting.

The result was quite nice, but not as nice as what Ivan Čukić imagined for this control. Since the first instant I saw his mockup, I wanted to have a widget like that in Nepomuk. The problem is that such a widget is very difficult to implement (and even more to implement correctly), and that I haven't found any existing code on Google, and the only application using this widget I know of is Yahoo! Mail. I therefore decided to implement this widget myself.

General Idea

The code of the widget lives in my branch of the nepomuk-widgets repository. Even though I tried to keep the widget general (the GroupedLineEdit class does not reference any Nepomuk class), I don't think it can already be useful to other projects. Don't hesitate to prove me wrong, though. If this widget one time becomes general enough and more sane than it is currently, I would like to have it merged into Qt (the widget doesn't use any KDE class).

The widget

The widget

The idea of the widget is to "group" terms into blocks. A block is a rounded rectangle, each of a different color, and having a small cross. When the user clicks the cross, the group is deleted. The blocks must be completely cosmetic for the user. That means that the full query builder still needs to behave like a QLineEdit: the user must be able to move the cursor using the arrow keys of his or her keyboard, and the cursor must not be stuck at one end of a block. It must be able to flow from a block to the next or the previous. The user must also be able to add text anywhere, even between blocks.

Blocks are added to the widget by the application, one at a time. Blocks cannot be removed, but the widget can be cleared (every block is removed, the text being preserved). When blocks need to change, the application thus clears all the blocks, then re-add the ones it needs. This is not the most efficient operation, but doing otherwise would have greatly complicated the API and the code itself.

Flowing From a Line Edit to Another One

"Flowing" is an operation needed when there is two line edits next to each other. When the user presses the arrow keys, the cursor moves in one of the line edits. What I want is to detect when the user tried to move left/right when the cursor was already at the left/right of one line edit. When that occurs, the cursor is placed at the right/left of the previous/next line edit. [ ][| ], with the vertical bar representing the cursor, becomes [ |][ ] when the cursor presses the left key.

Unfortunately, QLineEdit does not allow that. When the cursor moves, the cursorPositionChanged signal is emitted. This signal contains information about the old and the new position of the cursor. The problem is that this signal is only fired when the cursor actually moved. If the user presses Left when the cursor is already on the left side of the widget, the signal is not emitted.

I looked at the code of QLineEdit and QLineControl, and there was nothing hidden there to help me. The solution I used is to subclass QLineEdit and to reimplement its keyPressEvent method. I still need to find how to make all that work with right-to-left languages:

void GroupedLineEditEdit::keyPressEvent(QKeyEvent *e)
    if (e->key() == Qt::Key_Left && cursorPosition() == 0) {
        emit cursorBeforeStart();
    } else if (e->key() == Qt::Key_Right && cursorPosition() == text().length()) {
        emit cursorAfterEnd();


What Next ?

I invite you to test my code. This can be done by cloning the gsoc2013 branch of http://public.steckdenis.be/git/nepomuk-core and http://public.steckdenis.be/git/nepomuk-widgets. Then, build and install nepomuk-core, and build nepomuk-widgets. When everything is built, launch your_build_dir/test/querybuilderapp. A small window like the one shown at the beginning of this post pops up. Enter a query, and see it being highlighted in real-time.

If you have a slow computer, don't hesitate to test my code on it. Currently, the highlighting is done every time you type a character. If your CPU usage becomes too high when you test the widget, one solution would be to highlight the query not at each character typed, but every half second or so.

My next step now is to investigate the auto-completion stuff. Ivan also proposed a nice interface, and I have ideas on how to implement that. I hope to have something to show by the end of this week.

« Syntax-Highlighting Experiments   The Journey to Auto-Completion »