1
votes

Here the code creates two widgets tab-viewed in an MDI area. Each of the widgets contains two labels. The user can switch between these two widgets by Ctrl+Tab. How could one make sure the left label getting focusInEvent() when the widget is Ctrl+tabbed on?

I have tried activateWindow() and raise() as in the code. They seem to make no differences.

I have tried to use only one label in those widgets - the focusInEvent() happens without any problem. When two or more labels are in those widgets (as in the code), Ctrl+tab will sometimes (but not always) trigger the focusInEvent on the left label, which in turn trigger the MouseMoveEvent to show a tooltip when the mouse is on the left label - This can be observed if you put the mouse on the left and Ctrl+tab many times: sometimes the tooltip is not shown.

If instead, the second label is created by QLabel *lbl2 = new QLabel;, the focusInEvent() also happens for the first label with no problems.

#include <QtCore>
#include <QtWidgets>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QEvent>
#include <QGridLayout>

class MyLabel : public QLabel
{
public:
    MyLabel(QWidget*parent = nullptr) : QLabel(parent)
    {
        setMouseTracking(true);
        setFocusPolicy(Qt::FocusPolicy::StrongFocus);
        setWindowFlag(Qt::ToolTip);
    }

protected:
    virtual void focusInEvent(QFocusEvent *ev) override
    {
        static int count;
        qDebug() << __PRETTY_FUNCTION__ << " count: " << ++count;
        QPoint posGlabal = QCursor::pos();
        QPoint pos = this->mapFromGlobal(posGlabal);
        if(this->rect().contains(pos)) {
            // I want the label show a tooltip by mouseMoveEvent, when it is Ctrl+Tab on, without moving mouse
            QMouseEvent* evt = new QMouseEvent(QEvent::MouseMove, QPointF(pos), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
            QCoreApplication::postEvent(this,evt);
        }

        QLabel::focusInEvent(ev);
    }

    virtual void mouseMoveEvent(QMouseEvent *ev) override
    {
        QString msg(QString::number(ev->pos().x()) +", " +QString::number(ev->pos().y()));
        QToolTip::showText(ev->globalPos(), msg);

        QLabel::mouseMoveEvent(ev);
    }
};

class MyWidget : public QWidget
{
public:
    MyWidget(QWidget *parent=nullptr) : QWidget(parent) {
        QGridLayout *layout = new QGridLayout;

        // First label on left... I want it to show tooptip when Ctrl+Tab on if mouse on it (without moving mouse)
        MyLabel *lbl1 = new MyLabel;
        lbl1->setStyleSheet("QLabel { background-color : green;}");
        layout->addWidget(lbl1, 0, 0);

        // second label
        MyLabel *lbl2 = new MyLabel;
        //QLabel *lbl2 = new QLabel; // if I use this instead, no problem getting the tooltip on the left label.
        lbl2->setStyleSheet("QLabel { background-color : blue;}");
        layout->addWidget(lbl2, 0, 1);

        // I want the left label to get focusInEvent()
        // So I added these two lines in.
        // But they do NOT make differences
        lbl1->activateWindow();
        lbl1->raise();

        setLayout(layout);
    }

};

class MainWindow : public QMainWindow
{
public:
    MainWindow(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        QMdiArea *mdiArea = new QMdiArea;
        this->setCentralWidget(mdiArea);
        mdiArea->setViewMode(QMdiArea::TabbedView);

        MyWidget *w1 = new MyWidget; w1->setWindowTitle("w1");
        mdiArea->addSubWindow(w1);
        MyWidget *w2 = new MyWidget; w2->setWindowTitle("w2");
        mdiArea->addSubWindow(w2);

    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
1

1 Answers

1
votes

I made just a couple changes to help me debugging your issue.
I connected QMdiArea::subWindowActivated() to a new slot in MyWidget that sets the focus to the left label.
I've a feeling that it's a bit of a hack, but you should be able to improve my idea.

#include <QtCore>
#include <QtWidgets>
#include <QApplication>
#include <QMainWindow>
#include <QMouseEvent>
#include <QEvent>
#include <QGridLayout>

class MyLabel : public QLabel
{
public:
    MyLabel(const QString &name, QWidget*parent = nullptr)
        : QLabel(parent)
        , m_name(name)
    {
        setMouseTracking(true);
        setFocusPolicy(Qt::FocusPolicy::StrongFocus);
        setWindowFlag(Qt::ToolTip);
    }

    QString name() const { return m_name; }

protected:
    virtual void focusInEvent(QFocusEvent *ev) override
    {
        static int count;
        qDebug() << __PRETTY_FUNCTION__ << m_name << " count: " << ++count;

        QPoint posGlabal = QCursor::pos();
        QPoint pos = this->mapFromGlobal(posGlabal);

        if(this->rect().contains(pos)) {
            // I want the label show a tooltip by mouseMoveEvent, when it is Ctrl+Tab on, without moving mouse
            QMouseEvent* evt = new QMouseEvent(QEvent::MouseMove, QPointF(pos), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
            QCoreApplication::postEvent(this,evt);
        }

        QLabel::focusInEvent(ev);
    }

    virtual void mouseMoveEvent(QMouseEvent *ev) override
    {
        QString msg(QString::number(ev->pos().x()) +", " +QString::number(ev->pos().y()));

        QToolTip::showText(ev->globalPos(), msg);

        QLabel::mouseMoveEvent(ev);
    }

private:
    QString m_name;
};

class MyWidget : public QWidget
{
public:
    MyWidget(const QString &nameLeft, const QString &colorLeft, const QString &nameRight, const QString &colorRight, QWidget *parent=nullptr)
        : QWidget(parent)
        , lbl1(nameLeft)
        , lbl2(nameRight)
    {
        QGridLayout *layout = new QGridLayout;

        // First label on left... I want it to show tooptip when Ctrl+Tab on if mouse on it (without moving mouse)
        lbl1.setStyleSheet(QString("QLabel { background-color : %1;}").arg(colorLeft));
        layout->addWidget(&lbl1, 0, 0);

        // second label
        //QLabel *lbl2 = new QLabel; // if I use this instead, no problem getting the tooltip on the left label.
        lbl2.setStyleSheet(QString("QLabel { background-color : %1;}").arg(colorRight));
        layout->addWidget(&lbl2, 0, 1);

        setLayout(layout);
    }
// ----------------------------------------------------------------
public slots:
    // The new slot that looks for the left label and sets the focus on it
    void onSubWindowActivated(QMdiSubWindow *window)
    {
        auto lbl = window->findChild<MyLabel *>(lbl1.objectName());

        // The events must be filtered, otherwise they'll trigger each other to death
        if (lbl == nullptr)
            return;

        lbl->setFocus();
    }
// ----------------------------------------------------------------
private:
    MyLabel lbl1;
    MyLabel lbl2;
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QMdiArea *mdiArea = new QMdiArea;
    this->setCentralWidget(mdiArea);
    mdiArea->setViewMode(QMdiArea::TabbedView);

    MyWidget *w1 = new MyWidget("w1_L", "green", "w1_R", "blue");
    w1->setWindowTitle("w1");
    mdiArea->addSubWindow(w1);
// ----------------------------------------------------------------
    connect(mdiArea, &QMdiArea::subWindowActivated,
            w1, &MyWidget::onSubWindowActivated);
// ----------------------------------------------------------------

    MyWidget *w2 = new MyWidget("w2_L", "yellow", "w2_R", "red");
    w2->setWindowTitle("w2");
    mdiArea->addSubWindow(w2);
// ----------------------------------------------------------------
    connect(mdiArea, &QMdiArea::subWindowActivated,
            w2, &MyWidget::onSubWindowActivated);
// ----------------------------------------------------------------
}

Here the official documentation for QMdiArea::subWindowActivated()
https://doc.qt.io/qt-5/qmdiarea.html#subWindowActivated