Selecting QTreeView Switches Focus on Default QButton
-
Which Qt Version are you running, and which OS is the one that works without switching the default button?
-
Which Qt Version are you running, and which OS is the one that works without switching the default button?
Qt 6.6.0 (Clang 13.0 (Apple), arm64)
I can't verify at this time, but I don't think the same thing happens when I run the same code but on Windows.
I tried adding the following code:
assetSelectTree->setFocusPolicy(Qt::NoFocus);and this seems to resolve what I'm seeing, but I'm not sure if it's the correct way to do it?
-
Qt 6.6.0 (Clang 13.0 (Apple), arm64)
I can't verify at this time, but I don't think the same thing happens when I run the same code but on Windows.
I tried adding the following code:
assetSelectTree->setFocusPolicy(Qt::NoFocus);and this seems to resolve what I'm seeing, but I'm not sure if it's the correct way to do it?
@gsephelec
Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
MacOS treats buttons differently from other OSes at times, e.g. in aQDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
Can you isolate the issue in a minimal, compilable reproducer? -
@gsephelec
Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
MacOS treats buttons differently from other OSes at times, e.g. in aQDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
Can you isolate the issue in a minimal, compilable reproducer?Sure thing, I've uploaded a completely minimal project that recreates the issue here:
-
@gsephelec
Qt::NoFocuscertainly helps, unless you need the tree to acquire focus at some point.
MacOS treats buttons differently from other OSes at times, e.g. in aQDialogButtonBox. But bouncing from OK to Cancel sounds wrong to me. Unless related to application code, it could be a bug in Qt. However, I can't reproduce it on the spot.
A recent focus chain fix related to macOS has landed in 6.6, so you can't be affected from the bug anymore.
Can you isolate the issue in a minimal, compilable reproducer?I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.
Could you reproduce the issue with the code I posted?
Thanks
-
I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.
Could you reproduce the issue with the code I posted?
Thanks
@gsephelec
I'm throwing this thought in. No testing, and I don't have Mac anyway.I note that the the
cancelButtonis the first button you add to theQDialogButtonBox. I don't know why or whether this is desired/intended, but my suspicion is that it resets/selects the first button? Try swapping the order of yourcancelButton/okButton, does it now select theokButton? -
@gsephelec
I'm throwing this thought in. No testing, and I don't have Mac anyway.I note that the the
cancelButtonis the first button you add to theQDialogButtonBox. I don't know why or whether this is desired/intended, but my suspicion is that it resets/selects the first button? Try swapping the order of yourcancelButton/okButton, does it now select theokButton? -
I noticed other implementions of QDialog have similar functionality, eg, if a textbox is selected, the default button swicthes from Ok to Cancel, despite Ok being the default button.
Could you reproduce the issue with the code I posted?
Thanks
@gsephelec
I don't download reproducer code from external sources.
If you want me to reproduce, the code has to be so minimal that you can post it here. -
@gsephelec
I don't download reproducer code from external sources.
If you want me to reproduce, the code has to be so minimal that you can post it here.See code here:
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "assetDialog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { assetAssetSelector dialog(this); dialog.exec(); } MainWindow::~MainWindow() { }mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtWidgets> #include <QTreeView> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; QLabel *assetSelectLabel; QTreeView *assetSelectTree; QDialogButtonBox *buttonBox; QPushButton *okButton; QPushButton *cancelButton; QStandardItemModel *assetSelectItemModel; }; #endif // MAINWINDOW_HassetDialog.cpp
#include "assetDialog.h" assetAssetSelector::assetAssetSelector(QWidget *parent) : QDialog(parent) { assetSelectLabel = new QLabel("Test"); assetSelectTree = new QTreeView(); assetSelectTree->setFixedSize(260, 360); assetSelectTree->setHeaderHidden(true); assetSelectTree->setDragEnabled(false); assetSelectTree->setEditTriggers(QAbstractItemView::NoEditTriggers); assetSelectItemModel = new QStandardItemModel(this); // Data would usually be added here assetSelectTree->setModel(assetSelectItemModel); assetSelectTree->setCurrentIndex(assetSelectItemModel->invisibleRootItem()->index()); assetSelectTree->expandAll(); okButton = new QPushButton(QString("&OK")); okButton->setDefault(true); cancelButton = new QPushButton(QString("&Cancel")); buttonBox = new QDialogButtonBox(Qt::Horizontal); buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole); buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole); QVBoxLayout *assetSelectLayout = new QVBoxLayout; assetSelectLayout->addWidget(assetSelectLabel); assetSelectLayout->addWidget(assetSelectTree); QGridLayout *mainLayout = new QGridLayout; mainLayout->setSizeConstraint(QLayout::SetFixedSize); mainLayout->addLayout(assetSelectLayout, 0, 0); mainLayout->addWidget(buttonBox, 1, 0); setLayout(mainLayout); connect(buttonBox, &QDialogButtonBox::accepted, this, &assetAssetSelector::slotAccept); connect(buttonBox, &QDialogButtonBox::rejected, this, &assetAssetSelector::slotReject); } void assetAssetSelector::slotAccept() { /* Accept the dialog */ accept(); } void assetAssetSelector::slotReject() { /* Reject the dialog */ reject(); }assetDialog.h
#ifndef ASSETDIALOG_H #define ASSETDIALOG_H #pragma once #include "mainWindow.h" #include <QDialog> #include <QtWidgets> #include <QTreeView> #include <string> QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; class QComboBox; //class QCheckBox; class QDialogButtonBox; class QPushButton; QT_END_NAMESPACE class assetAssetSelector : public QDialog { Q_OBJECT public: assetAssetSelector(QWidget *parent = nullptr); protected slots: void slotAccept(); void slotReject(); private: QLabel *assetSelectLabel; QTreeView *assetSelectTree; QDialogButtonBox *buttonBox; QPushButton *okButton; QPushButton *cancelButton; QStandardItemModel *assetSelectItemModel; }; #endif // ASSETDIALOG_HThis repoduces the error on Macbook Pro Nov 2023 M3 Pro, Qt 6.6.0 (Clang 13.0 (Apple), arm64).
Thanks
-
See code here:
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "assetDialog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { assetAssetSelector dialog(this); dialog.exec(); } MainWindow::~MainWindow() { }mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtWidgets> #include <QTreeView> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; QLabel *assetSelectLabel; QTreeView *assetSelectTree; QDialogButtonBox *buttonBox; QPushButton *okButton; QPushButton *cancelButton; QStandardItemModel *assetSelectItemModel; }; #endif // MAINWINDOW_HassetDialog.cpp
#include "assetDialog.h" assetAssetSelector::assetAssetSelector(QWidget *parent) : QDialog(parent) { assetSelectLabel = new QLabel("Test"); assetSelectTree = new QTreeView(); assetSelectTree->setFixedSize(260, 360); assetSelectTree->setHeaderHidden(true); assetSelectTree->setDragEnabled(false); assetSelectTree->setEditTriggers(QAbstractItemView::NoEditTriggers); assetSelectItemModel = new QStandardItemModel(this); // Data would usually be added here assetSelectTree->setModel(assetSelectItemModel); assetSelectTree->setCurrentIndex(assetSelectItemModel->invisibleRootItem()->index()); assetSelectTree->expandAll(); okButton = new QPushButton(QString("&OK")); okButton->setDefault(true); cancelButton = new QPushButton(QString("&Cancel")); buttonBox = new QDialogButtonBox(Qt::Horizontal); buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole); buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole); QVBoxLayout *assetSelectLayout = new QVBoxLayout; assetSelectLayout->addWidget(assetSelectLabel); assetSelectLayout->addWidget(assetSelectTree); QGridLayout *mainLayout = new QGridLayout; mainLayout->setSizeConstraint(QLayout::SetFixedSize); mainLayout->addLayout(assetSelectLayout, 0, 0); mainLayout->addWidget(buttonBox, 1, 0); setLayout(mainLayout); connect(buttonBox, &QDialogButtonBox::accepted, this, &assetAssetSelector::slotAccept); connect(buttonBox, &QDialogButtonBox::rejected, this, &assetAssetSelector::slotReject); } void assetAssetSelector::slotAccept() { /* Accept the dialog */ accept(); } void assetAssetSelector::slotReject() { /* Reject the dialog */ reject(); }assetDialog.h
#ifndef ASSETDIALOG_H #define ASSETDIALOG_H #pragma once #include "mainWindow.h" #include <QDialog> #include <QtWidgets> #include <QTreeView> #include <string> QT_BEGIN_NAMESPACE class QLabel; class QLineEdit; class QComboBox; //class QCheckBox; class QDialogButtonBox; class QPushButton; QT_END_NAMESPACE class assetAssetSelector : public QDialog { Q_OBJECT public: assetAssetSelector(QWidget *parent = nullptr); protected slots: void slotAccept(); void slotReject(); private: QLabel *assetSelectLabel; QTreeView *assetSelectTree; QDialogButtonBox *buttonBox; QPushButton *okButton; QPushButton *cancelButton; QStandardItemModel *assetSelectItemModel; }; #endif // ASSETDIALOG_HThis repoduces the error on Macbook Pro Nov 2023 M3 Pro, Qt 6.6.0 (Clang 13.0 (Apple), arm64).
Thanks
@gsephelec
Sorry for taking so long.The
setVisible()override ofQDialogchecks, if there is aQPushButtonat the next position in the focus chain. If it finds one, it sets it as a default button. That matches almost all use cases. But it can get in the way of aQDialogButtonBoxinside aQDialog. In your case, the cancel button is next in the focus chain. Therefore it wins the prize and becomes default.There is a simple workaround:
Definevoid setVisible(bool visible) override;in the (new)protectedsection ofassetDialog.hand implement it as follows:void assetAssetSelector::setVisible(bool visible) { QDialog::setVisible(visible); okButton->setDefault(true); }It's a bit hacky, but it does the job.
You can file a bugreport if you want - you have a perfect reproducer.
I can't promise if we will actually fix it. I somehow feel, that a fix in Qt would break a lot of other stuff.