悬停时突出显示自定义 QWidgetAction [英] highlighting custom QWidgetAction on hover

查看:110
本文介绍了悬停时突出显示自定义 QWidgetAction的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序有一个 QMenuBar 和多个 QMenu,每个都有多个 QAction 和子QMenus.大多数 QAction-items 是 QWidgetAction 的衍生物,重新实现了 QWidgetAction::createWidget 方法.

My application has a QMenuBar with a number of QMenus, each of which having a number of QActions and sub-QMenus. Most of the QAction-items are derivatives of QWidgetAction with re-implemented QWidgetAction::createWidget methods.

通常,QActions 和 QMenu 都会在鼠标悬停时突出显示.即使是 QWidgetAction 也不会在这里惹麻烦:

Usually, both QActions and QMenu become highlighted on mouse hover. Even a QWidgetAction doesn't make trouble until here:

但是一旦我覆盖 QWidgetAction::createWidget 以返回一个自定义的 QWidget

But as soon as I override QWidgetAction::createWidget to return a custom QWidget

QWidget* MyWidgetAction::createWidget(QWidget* parent) { return new MyWidget(parent); }

突出显示不再起作用.所以我自己实现了:

the highlighting does not work anymore. So I implemented it myself:

void MyWidget::set_highlighted(bool h)
{
  setBackgroundRole(h ? QPalette::Highlight : QPalette::Window);
  setAutoFillBackground(h);
}
void MyWidget::enterEvent(QEvent*) override { set_highlighted(true); }
void MyWidget::leaveEvent(QEvent*) override { set_highlighted(false); }

但是,它的行为并不符合预期:

However, it does not behave as expected:

我已经发现在关闭所有子菜单之前不会调用 enterEvent 方法,这只会在鼠标离开子菜单或其操作后发生一些延迟(顺便说一句,我怎么能更改延迟?).与鼠标移动事件相同.

I already figured out that the enterEvent method is not called until all sub-menus are closed, which only happens with some delay after mouse leaves the sub menu or its action (btw, how can I change the delay?). Same with mouse-move events.

问题:如何正确重新实现悬停高亮?用户不会注意到自定义小部件和标准 QAction 的行为不同.默认的 QWidgetAction::createWidget 有什么作用,我该如何重现它?我已经看过 Qt 的源代码 但它很混乱.

Question: How can I re-implement highlight-on-hover properly? The user shall not notice that custom widget and standard QAction behave differently. What does the default QWidgetAction::createWidget do and how can I reproduce it? I've already looked at Qt's source but it's quite confusing.

重现动画的代码

实际生产代码

推荐答案

我认为原因是你没有在你的小部件上启用鼠标跟踪,所以无法通知父菜单鼠标光标改变他的位置.

I think that the reason is you don't enable the mouse tracking on your widget, so the parent menu can't be notify that the mouse cursor change his position.

我建议在你的 MyWidget 类的构造函数中添加这一行:

I suggest to add in the constructor of your MyWidget class this line:

setMousetracking(true);

<小时>

编辑 #1:
我发现了一个丑陋的技巧,但它似乎有效:


Edit #1:
I found an ugly trick but it seems to be working:

// You WidgetAction class
class MyWidgetAction : public QWidgetAction
{
public:
    MyWidgetAction(QObject *parent = nullptr);
    QWidget* createWidget(QWidget* parent) override {
        w = new MyWidget(parent);
        return w;
    }
    void highlight(bool hl) { w->set_highlighted(hl); }

private:
    MyWidget *w;
};

// In your code
QMenu *menu = ui->menuBar->addMenu("The Menu");
menu->addAction("Standard QAction 1");
menu->addAction("Standard QAction 2");
menu->addMenu("submenu")->addAction("subaction1");
QWidgetAction *a = new MyWidgetAction();
a->setText("My action 1");
a->setParent(menu); // Needed for the trick
menu->addAction(a);
menu->addAction("Standard QAction 3");
menu->addAction("Standard QAction 4");

// The ugly trick
connect(menu, &QMenu::hovered, this, [menu](QAction *act){
    QList<MyWidgetAction*> lCustomActions = menu->findChildren<MyWidgetAction*>();
    for (MyWidgetAction *mwa : lCustomActions){
        mwa->highlight(mwa == act);
    }
});

我看到 hovered 信号总是正确发送,所以我将它连接到 lambda 以检查每个自定义 WidgetAction 是否是当前悬停的项目并手动突出显示在这种情况下.

I saw that the hovered signal is always send correctly, so I connect this to a lambda to check for each custom WidgetAction if it's the current hovered item and manually highlight in this case.

编辑 #2:
为了在我的第一次编辑中避免 lambda 中的 for 循环,您还可以创建一个事件过滤器来管理鼠标移动时的突出显示:

Edit #2:
To avoid the for loop in the lambda in my first edit, you can also create an eventfilter to manage the highlight on mouse move:

class WidgetActionFilterObject : public QObject
{
    Q_OBJECT
public:
    explicit WidgetActionFilterObject(QObject *parent = nullptr);

protected:
    bool eventFilter(QObject *obj, QEvent *evt) override {
        if (evt->type() == QEvent::Type::MouseMove){
            QMouseEvent *mouse_evt = static_cast<QMouseEvent*>(evt);
            QAction *a = static_cast<QMenu*>(obj)->actionAt(mouse_evt->pos());
            MyWidgetAction *mwa = dynamic_cast<MyWidgetAction*>(a);
            if (mwa){
                if (last_wa && mwa != last_wa){
                    last_wa->highlight(false);
                }
                mwa->highlight(true);
                last_wa = mwa;
            } else {
                if (last_wa){
                    last_wa->highlight(false);
                    last_wa = nullptr;
                }
            }
        }
        return QObject::eventFilter(obj, evt);
    }

private:
    MyWidgetAction *last_wa = nullptr;
};

那么你唯一需要做的就是安装一个<每个包含自定义 WidgetAction 的菜单上的 em>事件过滤器:

Then the only thing you have to do is to install an event filter on each menu that contain your custom WidgetAction:

menu->installEventFilter(new WidgetActionFilterObject(this));

并且您将获得相同的结果,而无需对每个 hovered 信号进行循环.

And you will obtain the same result without a loop on each hovered signal.

这篇关于悬停时突出显示自定义 QWidgetAction的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆