QLineEdit增加点击回显功能

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

最近开发一个需求需要在QLineEdit上增加回显功能,这个回显要集成在QLineEdit里面,而且要好看。一开始我想到的是创建一个QWidget同时集成QLineEdit和QToolButton来实现联动,看了QLineEdit源代码之后直到其实不用这么麻烦。

思路就是QLineEdit本身自带一个Clear功能,就是一键清除,其实它提前预留了一些扩展,可以方便我们增加自己的定制功能,而且不用担心UI适配问题。


一、QAction方案

简单来说Clear功能也是QAction方案,只不过这个功能集成在QLineEdit内部,你只能操作有限的几个方法,不能过多的定制行为。我们的回显就是QAction方案,总体来说可以实现一定的定制化。

我也不太理解像回显这么重要的功能为什么框架没有直接提供,所幸官方预留了接口,定制化也比较简单。

二、部分代码

因为我的代码引用了另一个框架,所以原封不动复制进来你们也跑不起来,这里我贴出来我的代码,虽然它不能跑起来,但是你应该看的明白原理。

总体思路就是创建一个QAction,然后定义信号连接,将Action加到QLineEdit的尾部。

cpp 复制代码
    _pIsEchoButtonEnable = isEcho;
    if (isEcho) {
        _echoAction = new QAction(XIcon::getElaIcon(ElaIconType::EyeSlash, XThemeColor(eTheme->getThemeMode(), BasicText)), "", this);
        _echoAction->setCheckable(true);
        Q_Q(XLineEdit);
        connect(_echoAction, &QAction::triggered, this, [=](bool checked) {
            if (checked) {
                            _echoAction->setIcon(XIcon::getElaIcon(ElaIconType::EyeSlash, XThemeColor(eTheme->getThemeMode(), PrimaryNormal)));
                q->setEchoMode(QLineEdit::Normal);
            } else {
                _echoAction->setIcon(XIcon::getElaIcon(ElaIconType::EyeSlash, XThemeColor(eTheme->getThemeMode(), BasicText)));
                q->setEchoMode(QLineEdit::Password);
                            }
        });
         // 加到尾部
        q->addAction(_echoAction, QLineEdit::TrailingPosition);

三、QLineEdit源代码

我们去框架源代码看看,先看看他是怎么设置清除按钮逻辑的。QLineEdit的源代码在D:\Work\Qt\5.15.2\Src\qtbase\src\widgets\widgets\qlineedit.cpp,把我的安装路径替换成你的。

cpp 复制代码
void QLineEdit::setClearButtonEnabled(bool enable)
{
#if QT_CONFIG(action)
    Q_D(QLineEdit);
    if (enable == isClearButtonEnabled())
        return;
    if (enable) {
        QAction *clearAction = new QAction(d->clearButtonIcon(), QString(), this);
        clearAction->setEnabled(!isReadOnly());
        clearAction->setObjectName(QLatin1String(clearButtonActionNameC));

        int flags = QLineEditPrivate::SideWidgetClearButton | QLineEditPrivate::SideWidgetFadeInWithText;
        auto widgetAction = d->addAction(clearAction, nullptr, QLineEdit::TrailingPosition, flags);
        widgetAction->setVisible(!text().isEmpty());
    } else {
        QAction *clearAction = findChild<QAction *>(QLatin1String(clearButtonActionNameC));
        Q_ASSERT(clearAction);
        d->removeAction(clearAction);
        delete clearAction;
    }
#else
    Q_UNUSED(enable);
#endif // QT_CONFIG(action)
}

看到了嘛,创建了Action;如果是移除就找到已经创建的Action然后移除,因为默认情况下这个函数是可以随时调用的。

重点是下面这一句,进入了private代码

cpp 复制代码
auto widgetAction = d->addAction(clearAction, nullptr, QLineEdit::TrailingPosition, flags);

private代码在D:\Work\Qt\5.15.2\Src\qtbase\src\widgets\widgets\qlineedit_p.cpp

cpp 复制代码
QWidget *QLineEditPrivate::addAction(QAction *newAction, QAction *before, QLineEdit::ActionPosition position, int flags)
{
    Q_Q(QLineEdit);
    if (!newAction)
        return nullptr;
    if (!hasSideWidgets()) { // initial setup.
        QObject::connect(q, SIGNAL(textChanged(QString)), q, SLOT(_q_textChanged(QString)));
        lastTextSize = q->text().size();
    }
    QWidget *w = nullptr;
    // Store flags about QWidgetAction here since removeAction() may be called from ~QAction,
    // in which a qobject_cast<> no longer works.
#if QT_CONFIG(action)
    if (QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(newAction)) {
        if ((w = widgetAction->requestWidget(q)))
            flags |= SideWidgetCreatedByWidgetAction;
    }
#endif
    if (!w) {
#if QT_CONFIG(toolbutton)
        QLineEditIconButton *toolButton = new QLineEditIconButton(q);
        toolButton->setIcon(newAction->icon());
        toolButton->setOpacity(lastTextSize > 0 || !(flags & SideWidgetFadeInWithText) ? 1 : 0);
        if (flags & SideWidgetClearButton) {
            QObject::connect(toolButton, SIGNAL(clicked()), q, SLOT(_q_clearButtonClicked()));

#if QT_CONFIG(animation)
            // The clear button is handled only by this widget. The button should be really
            // shown/hidden in order to calculate size hints correctly.
            toolButton->setHideWithText(true);
#endif
        }
        toolButton->setDefaultAction(newAction);
        w = toolButton;
#else
        return nullptr;
#endif
    }

    // QTBUG-59957: clear button should be the leftmost action.
    if (!before && !(flags & SideWidgetClearButton) && position == QLineEdit::TrailingPosition) {
        for (const SideWidgetEntry &e : trailingSideWidgets) {
            if (e.flags & SideWidgetClearButton) {
                before = e.action;
                break;
            }
        }
    }

    // If there is a 'before' action, it takes preference

    // There's a bug in GHS compiler that causes internal error on the following code.
    // The affected GHS compiler versions are 2016.5.4 and 2017.1. GHS internal reference
    // to track the progress of this issue is TOOLS-26637.
    // This temporary workaround allows to compile with GHS toolchain and should be
    // removed when GHS provides a patch to fix the compiler issue.

#if defined(Q_CC_GHS)
    const SideWidgetLocation loc = {position, -1};
    const auto location = before ? findSideWidget(before) : loc;
#else
    const auto location = before ? findSideWidget(before) : SideWidgetLocation{position, -1};
#endif

    SideWidgetEntryList &list = location.position == QLineEdit::TrailingPosition ? trailingSideWidgets : leadingSideWidgets;
    list.insert(location.isValid() ? list.begin() + location.index : list.end(),
                SideWidgetEntry(w, newAction, flags));
    positionSideWidgets();
    w->show();
    return w;
}

这个代码比较长,就不一句句解读了,重点在这一句:

cpp 复制代码
SideWidgetEntryList &list = location.position == QLineEdit::TrailingPosition ? trailingSideWidgets : leadingSideWidgets;

其实就是两个位置QLineEdit::TrailingPositionQLineEdit::LeadingPosition,很好理解就是头部尾部。就是说你可以在头部和尾部添加Action,它其实头尾各有一个列表,理论上可以添加很多,但是我们用不了那么多。

Action的信号绑定可以在外面绑定,这样的话可以完全的自定义新Action行为。


总结

1、列表是有序的,后加入的Action总是在前加入的Action的后面

2、这种方式创建的行为会自动适配UI,不会和文字重叠,这一点很好,省去了适配的繁琐

相关推荐
dot to one10 分钟前
Qt 中 QWidget涉及的常用核心属性介绍
开发语言·c++·qt
二年级程序员36 分钟前
C++的历史与发展
c++
迷茫不知归路41 分钟前
操作系统实验习题解析 上篇
c++·算法·操作系统·实验课设
一个尚在学习的计算机小白42 分钟前
文件相关操作
c++
码农新猿类1 小时前
初入OpenCV
qt·opencv·计算机视觉
愚润求学1 小时前
【递归、搜索与回溯】专题一:递归(二)
c++·笔记·算法·leetcode
h汉堡1 小时前
C/C++内存管理
java·c语言·开发语言·c++·学习
大鱼BIGFISH1 小时前
C++ 字符格式化输出
c++·字符格式化输出
愚润求学2 小时前
【Linux】基础 IO(一)
linux·运维·服务器·开发语言·c++·笔记
June`2 小时前
专题四:综合练习( 找出所有子集的异或总和再求和)
c++·算法·深度优先·剪枝