I customized a dialog, and when pressing the Tab key, the focus switches from the button to the table widget. I want the focus to return to the initial button after all the items in the table widget have been traversed. Here’s a minimal reproducible example:
#include <QApplication>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>
class Widget : public QWidget
{
public:
explicit Widget()
{
setLayout(&vl);
tw.setRowCount(1);
tw.setColumnCount(3);
vl.addWidget(&btn);
vl.addWidget(&tw);
}
bool focusNextPrevChild(bool next)
{
QWidget* focusWidget = this->focusWidget();
if (focusWidget == &tw)
{
int currentRow = tw.currentRow();
int currentCol = tw.currentColumn();
int lastRow = tw.rowCount() - 1;
int lastCol = tw.columnCount() - 1;
if (next && currentRow == lastRow && currentCol == lastCol)
{
tw.clearFocus();
return true;
}
}
return QWidget::focusNextPrevChild(next);
}
private:
QPushButton btn;
QTableWidget tw;
QVBoxLayout vl;
};
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
I tried overriding the focusNextPrevChild
function, but it didn’t work. Once the focus enters the table widget, it can’t leave it.
1
musicamante said:
You’re overriding
focusNextPrevChild
of the parent, which is completely pointless since it will never be triggered as long as the table widget manages it. […]
You can check that by adding a qDebug
to check focusWidget
in focusNextPrevChild
. The if (focusWidget == &tw)
check will never be true
.
[…]
moveCursor()
implementation.
Here’s how you could use moveCursor()
to achieve what you want:
#include <QApplication>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>
#include <QKeyEvent>
class TableWidget : public QTableWidget
{
Q_OBJECT
signals:
void firstItemBackTab();
void lastItemTab();
protected:
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
{
QModelIndex current = currentIndex();
int row = current.row();
int col = current.column();
int lastRow = rowCount() - 1;
int lastCol = columnCount() - 1;
//tab press on the last cell
if(cursorAction == MoveNext && row == lastRow && col == lastCol)
emit lastItemTab();
//tab+shift (backtab) press on the first cell
if(cursorAction == MovePrevious && row == 0 && col == 0)
emit firstItemBackTab();
return QTableWidget::moveCursor(cursorAction, modifiers);
}
};
class Widget : public QWidget
{
public:
explicit Widget()
{
setLayout(&vl);
tw.setRowCount(3);
tw.setColumnCount(3);
vl.addWidget(&btn);
vl.addWidget(&tw);
vl.addWidget(&btn1);
//the weakest point of this approach is here (as far as my "expertise" go)
//it's possible the next/previous child is not what you'd expect.
//Tab order and all. Perhaps QWidget::setTabOrder could help with that.
connect(&tw, &TableWidget::firstItemBackTab, [=]()
{
focusPreviousChild();
});
connect(&tw, &TableWidget::lastItemTab, [=]()
{
focusNextChild();
});
}
private:
QPushButton btn{"Button 1"};
TableWidget tw;
QPushButton btn1{"Button 2"};
QVBoxLayout vl;
};
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
moveCursor
here replaces your logic inside focusNextPrevChild
, and replaces the check for next/previous using the already available QAbstractItemView::CursorAction
.
This approach is for basic widget cells. So, I don’t see a reason to reimplement focusNextPrevChild
. You’d probably want that when your cell widgets are complex, and you want to customize the tab navigation order inside them. But based on your implemented logic inside focusNextPrevChild
, the basic approach should suffice.
2