8

I have a custom list implementation (a subclass of QWidget) in QT 5.5. The elements of the list are organized using a QVBoxLayout. At runtime, elements (which are also QWidgets) can be dynamically added to and removed from the list at any position in the layout. This is working fine, except for one detail: the tab order of inserted focusable elements is wrong. The last element inserted will always be the last in the tab order, even if inserted in between two other elements.

How can I fix the tab order to represent the layout order? I already tried iterating over the list elements and using setTabOrder() on each adjacent pair, without success.

A few more details on the implementation:

  • Widgets are not added directly to the list. Each time a widget should be added, a proxy widget is created and added instead, the 'real' widget will be reparented to the proxy (The proxy is doing some graphic stuff).
  • QVBoxLayout::insertWidget() is used to insert proxy widgets, followed by a call to QWidget::show()
  • when removing elements, the element will be hidden, removed from the proxy, the proxy is removed from the list layout and deallocated
  • focusable widgets can be anywhere in the object tree of elements which are added to the list, they are not necessarily the elements themselves

Update: Added a MCVE!

The following minified example demonstrates the problem. For completeness, I also included the headers, main function, and .pro file. You can safely skip those files if you don't want to reproduce the issue, TabOrderTestWindow.cpp is the important one.

TabOrderTestWindow.cpp:

#include "TabOrderTestWindow.h"

#include <QVBoxLayout>
#include <QPushButton>

// create a button inside a proxy widget
QWidget* createButtonProxy(const QString& caption, QWidget* parent) {
    QWidget* proxy = new QWidget(parent);
    QPushButton* button = new QPushButton(caption, proxy);
    proxy->setFocusProxy(button);
    return proxy;
}

TabOrderTestWindow::TabOrderTestWindow()
    : QWidget()
{
    setMinimumHeight(200);
    setMinimumWidth(350);

    QVBoxLayout* layout = new QVBoxLayout(this);

    // create and add 3 buttons in order
    QWidget* button1 = createButtonProxy("button 1", this);
    QWidget* button2 = createButtonProxy("button 2", this);
    QWidget* button3 = createButtonProxy("button 3", this);
    layout->addWidget(button1);
    layout->addWidget(button2);
    layout->addWidget(button3);

    // now insert a fourth button in between the others - incorrect tab order!
    QWidget* buttonInbetween = createButtonProxy("button in between", this);
    layout->insertWidget(1, buttonInbetween);

    // attempt to correct tab order - not working, even with focus proxy set...
    setTabOrder(button1, buttonInbetween);
    setTabOrder(buttonInbetween, button2);
}

TabOrderTestWindow.h:

#ifndef TABORDERTESTWINDOW_H
#define TABORDERTESTWINDOW_H

#include <QMainWindow>

class TabOrderTestWindow : public QWidget
{
    Q_OBJECT

public:
    TabOrderTestWindow();
};

#endif // TABORDERTESTWINDOW_H

main.cpp:

#include "TabOrderTestWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TabOrderTestWindow w;
    w.show();

    return a.exec();
}

TabOrderTest.pro:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = TabOrderTest
TEMPLATE = app


SOURCES += main.cpp\
        TabOrderTestWindow.cpp

HEADERS  += TabOrderTestWindow.h
6
  • You could try setting the focus proxy on the proxy widgets that are actually the parents of the widgets you're adding, then use setTabOrder. Commented Nov 17, 2015 at 15:55
  • I already tried that, not success either
    – Cybran
    Commented Nov 17, 2015 at 15:55
  • Adding an mcve to the question would likely improve your it and probably better help find a solution to your problem. Commented Nov 17, 2015 at 16:17
  • 1
    Thanks, I added one.
    – Cybran
    Commented Dec 2, 2015 at 13:47
  • Have you tried calling setTabOrder for all the buttons, after adding the new one, as in the Qt documentation? Commented Dec 2, 2015 at 14:40

1 Answer 1

3

Here really seems to be a bug as the Doc state that focus proxies would be cared of.

But we can care of them ourself by using:

setTabOrder(button1->focusProxy(), buttonInbetween->focusProxy());
setTabOrder(buttonInbetween->focusProxy(), button2->focusProxy());

So it seems you need to do what Qt should have done for you.

5
  • 1
    This is possible for this simple example, but imagine a more complex object tree - you have to find ALL focusable widgets and link them in the correct order. I already implemented methods for this, but still failed.
    – Cybran
    Commented Dec 2, 2015 at 19:54
  • And what if I want to insert an element at the beginning of the list? How to find the appropriate previous focusable widget outside of the list's scope? I experimented with QWidget::previousInFocusChain() to no avail.
    – Cybran
    Commented Dec 2, 2015 at 19:56
  • Ok, but i think the docs don't promise any more than what my code above does. Let my think about the object tree ...
    – Aaron
    Commented Dec 2, 2015 at 20:11
  • Yes, you're right about that. Ideally, there would be one simple method to rebuild the whole tab order (including all children) of a widget. Well, one can dream ;)
    – Cybran
    Commented Dec 2, 2015 at 20:50
  • Had a look at the designer .. you can assign the tab order as you like across nested QGroupBoxes and everything. Even jumping between different items from different group boxes. In the resulting ui_xyz.h is a block of QWidget::setTabOrder('from', 'to'); calls. But pointers of every individual widget are at hand .. than it's easy. I tried it with nested classes (setting order of button relative to widgets containing other buttons). Was not possible. So it seems you are right. It's something to dream of ; ).
    – Aaron
    Commented Dec 2, 2015 at 21:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.