Search code examples
qtqt4focusqwidget

How to change a parent widget's background when a child widget has focus?


I would like to highlight a QFrame, if one of it's child widgets has focus (so the users know where to look for the cursor ;-)

using something along

ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");

highlights the QFrame when I click on it, but it loses its focus once one of its child widgets is selected.

Possible approaches:

  • I could connect() QApplication::focusChanged(old,now) and check each new object if it is a child of my QFrame, but this gets messy.

  • I could also subclass each child widget and reimplement focusInEvent()/focusOutEvent() and react on that, but with a lot of different widgets, this is also a lot of work.

Is there a more elegant solution?


Solution

  • Well, you can extend QFrame to make it listen on focus change of its children widgets. Or you can also install an event filter on children widgets to catch QFocusEvent.

    Here is an example:

    MyFrame.h

    #ifndef MYFRAME_H
    #define MYFRAME_H
    
    #include <QFrame>
    
    class MyFrame : public QFrame
    {
        Q_OBJECT
    
    public:
    
        explicit MyFrame(QWidget* parent = 0, Qt::WindowFlags f = 0);
    
        void hookChildrenWidgetsFocus();
    
    protected:
    
        bool eventFilter(QObject *object, QEvent *event);
    
    private:
    
        QString m_originalStyleSheet;
    };
    
    #endif // MYFRAME_H
    

    MyFrame.cpp

    #include <QEvent>
    #include "MyFrame.h"
    
    MyFrame::MyFrame(QWidget *parent, Qt::WindowFlags f)
        : QFrame(parent, f)
    {
        m_originalStyleSheet = styleSheet();
    }
    
    void MyFrame::hookChildrenWidgetsFocus()
    {
        foreach (QObject *child, children()) {
            if (child->isWidgetType()) {
                child->installEventFilter(this);
            }
        }
    }
    
    bool MyFrame::eventFilter(QObject *object, QEvent *event)
    {
        if (event->type() == QEvent::FocusIn) {
            setStyleSheet("background-color: #FFFFCC;");
        } else if (event->type() == QEvent::FocusOut) {
            setStyleSheet(m_originalStyleSheet);
        }
    
        return QObject::eventFilter(object, event);
    }
    

    MainWindow.cpp

    #include <QHBoxLayout>
    #include <QVBoxLayout>
    #include <QLineEdit>
    #include "MyFrame.h"
    #include "mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        setWindowTitle(tr("Test"));
    
        MyFrame *frame1 = new MyFrame(this);
        frame1->setLayout(new QVBoxLayout());
        frame1->layout()->addWidget(new QLineEdit());
        frame1->layout()->addWidget(new QLineEdit());
        frame1->layout()->addWidget(new QLineEdit());
        frame1->hookChildrenWidgetsFocus();
    
        MyFrame *frame2 = new MyFrame(this);
        frame2->setLayout(new QVBoxLayout());
        frame2->layout()->addWidget(new QLineEdit());
        frame2->layout()->addWidget(new QLineEdit());
        frame2->layout()->addWidget(new QLineEdit());
        frame2->hookChildrenWidgetsFocus();
    
        QHBoxLayout *centralLayout = new QHBoxLayout();
        centralLayout->addWidget(frame1);
        centralLayout->addWidget(frame2);
    
        QWidget *centralWidget = new QWidget();
        centralWidget->setLayout(centralLayout);
    
        setCentralWidget(centralWidget);
    }