From bb02ffffe0ff6a51bc8fc6dd7ad09d4e7b278aff Mon Sep 17 00:00:00 2001 From: liuxinhao Date: Wed, 30 Nov 2022 13:53:05 +0800 Subject: [PATCH] feat(KiranLabel,KiranPasswdEdit): new control KiranLabel,KiranPasswdEdit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增控件 KiranLabel以及KiranPasswdEdit --- CMakeLists.txt | 5 +- TODO | 9 + TODO.md | 6 - .../images/passwd-edit/reveal-passwd.svg | 39 ++ .../images/passwd-edit/unreveal-passwd.svg | 39 ++ resources/kiranwidgets-qt5-resources.qrc | 2 + .../kiran-image-selector.cpp | 2 +- src/widgets/kiran-label/kiran-label-private.h | 40 +++ src/widgets/kiran-label/kiran-label.cpp | 337 ++++++++++++++++++ src/widgets/kiran-label/kiran-label.h | 39 ++ .../kiran-passwd-edit/kiran-passwd-edit.cpp | 169 +++++++++ .../kiran-passwd-edit/kiran-passwd-edit.h | 60 ++++ test/CMakeLists.txt | 4 +- test/kiran-label-test.cpp | 94 +++++ test/kiran-passwd-edit-test.cpp | 50 +++ translations/kiranwidgets-qt5.zh_CN.ts | 6 + 16 files changed, 892 insertions(+), 9 deletions(-) create mode 100644 TODO delete mode 100644 TODO.md create mode 100644 resources/images/passwd-edit/reveal-passwd.svg create mode 100644 resources/images/passwd-edit/unreveal-passwd.svg create mode 100644 src/widgets/kiran-label/kiran-label-private.h create mode 100644 src/widgets/kiran-label/kiran-label.cpp create mode 100644 src/widgets/kiran-label/kiran-label.h create mode 100644 src/widgets/kiran-passwd-edit/kiran-passwd-edit.cpp create mode 100644 src/widgets/kiran-passwd-edit/kiran-passwd-edit.h create mode 100644 test/kiran-label-test.cpp create mode 100644 test/kiran-passwd-edit-test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 20792a0..369bdea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,12 +69,15 @@ set(DEVEL_HEADER "src/widgets/kiran-push-button/kiran-push-button.h" "src/widgets/kiran-hover-tips/kiran-hover-tips.h" "src/widgets/kiran-color-block/kiran-color-block.h" + "src/widgets/kiran-label/kiran-label.h" + "src/widgets/kiran-passwd-edit/kiran-passwd-edit.h" + "src/kiran-style/widget-property-helper.h" "src/kiran-style/kiran-style-public-define.h" ) include_directories("${CMAKE_BINARY_DIR}") -find_package(Qt5 COMPONENTS Widgets Svg Network X11Extras Concurrent LinguistTools) +find_package(Qt5 COMPONENTS Widgets Gui Svg Network X11Extras Concurrent LinguistTools) qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) diff --git a/TODO b/TODO new file mode 100644 index 0000000..3dcd0ac --- /dev/null +++ b/TODO @@ -0,0 +1,9 @@ +待优化项目(影响兼容性,需要在升级接口升级大版本时考虑该问题): + +1. kiran-widgets-qt5 d-pointer结构优化,现在的所有私有类指针未携带一个统一的继承关系 (暂时没有必要,现在的kiran-widgets-qt5内部继承层级并不多,修改的话同时也带来了接口不兼容) +eg: 例如 KiranButton --继承与--> KiranWidget , 在构造一个KiranButton时将创建一个KiranButtonPrivate的私有类,同时父类KiranWidget也会创建一个私有类KiranWidgetPrivate,如果继承层数过多时,分配内存的次数也会得到明显的提高, +若后期有必要可以考虑和Qt一致,例如 QFrame --继承于--> QWidget , QFramePrivate --继承与--> QWidgetPrivate , 构造QFrame时,将会分类QFramePrivate内存, 然后将QFramePrivate作为QWidget构造函数的参数,进行隐式装换为QWidgetPrivate*. + +2. kiran-widgets-qt5 d-pointer 成员变量为d_ptr与 Qt d-pointer重复的同时也不便于理解,考虑后续重命名为k_d_ptr + +3. 剔除kiran-widgets-qt5中自带的KiranStyle源码以及安装的头文件接口 \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 9555fff..0000000 --- a/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -#TODO List -- [ ] 将之前提供的Kiran::WidgetPropertyHelper接口以及kiran-style-public-define.h定义的枚举标记废弃,通过转调至kiran-qt5-integration之中的KiranStyle提供的接口,将枚举定义移动到其他位置 -- [ ] 修改之前自定义控件的绘制,确认是否将自动控件的绘制过程不交由Style进行绘制,直接通过kiran-qt5-integration提供的KiranPalette拿到颜色进行绘制 -- [ ] 加入字体绑定的功能,加入字体分级 -- [ ] kiranwidgets-qt5项目之中的kiranstyle后续都将废弃,后续更新接口时删除该部分代码 -- [ ] scrollarea 不占用空间 overlap \ No newline at end of file diff --git a/resources/images/passwd-edit/reveal-passwd.svg b/resources/images/passwd-edit/reveal-passwd.svg new file mode 100644 index 0000000..2a36261 --- /dev/null +++ b/resources/images/passwd-edit/reveal-passwd.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/passwd-edit/unreveal-passwd.svg b/resources/images/passwd-edit/unreveal-passwd.svg new file mode 100644 index 0000000..6d9ebff --- /dev/null +++ b/resources/images/passwd-edit/unreveal-passwd.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/kiranwidgets-qt5-resources.qrc b/resources/kiranwidgets-qt5-resources.qrc index 72dd4fd..76b4ae9 100644 --- a/resources/kiranwidgets-qt5-resources.qrc +++ b/resources/kiranwidgets-qt5-resources.qrc @@ -21,6 +21,8 @@ images/hover-tips/tips-suc.svg images/hover-tips/tips-warning.svg + images/passwd-edit/reveal-passwd.svg + images/passwd-edit/unreveal-passwd.svg diff --git a/src/widgets/kiran-image-selector/kiran-image-selector.cpp b/src/widgets/kiran-image-selector/kiran-image-selector.cpp index 59240fe..35ab27c 100644 --- a/src/widgets/kiran-image-selector/kiran-image-selector.cpp +++ b/src/widgets/kiran-image-selector/kiran-image-selector.cpp @@ -95,7 +95,7 @@ void KiranImageSelector::paintEvent(QPaintEvent *event) QStyleOption option; option.initFrom(this); - + option.state &= ~QStyle::State_MouseOver; auto background = kiranPalette->color(this,&option,StylePalette::Window,StylePalette::Background); auto border = kiranPalette->color(this,&option,StylePalette::Window,StylePalette::Border); diff --git a/src/widgets/kiran-label/kiran-label-private.h b/src/widgets/kiran-label/kiran-label-private.h new file mode 100644 index 0000000..fe96f0f --- /dev/null +++ b/src/widgets/kiran-label/kiran-label-private.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiranwidgets-qt5 is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef __KIRAN_LABEL_PRIVATE_H__ +#define __KIRAN_LABEL_PRIVATE_H__ + +#include "kiran-label.h" + +class KiranLabelPrivate:public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(KiranLabel) +public: + explicit KiranLabelPrivate(KiranLabel* ptr,QObject* parent = nullptr); + ~KiranLabelPrivate(); + + void init(); + + //以下方法由于QLabelPrivate未暴露函数符号,将QLabel::paintEvent中使用QLabelPrivate的方法,移动至KiranLabelPrivate重新实现 + static Qt::LayoutDirection textDirection(QLabelPrivate* ld); + static QRectF layoutRect(QLabelPrivate* ld); + static QRectF documentRect(QLabelPrivate* ld); + static void ensureTextLayouted(QLabelPrivate* ld); + +private: + KiranLabel* q_ptr; + Qt::TextElideMode elideMode = Qt::ElideNone; +}; + +#endif \ No newline at end of file diff --git a/src/widgets/kiran-label/kiran-label.cpp b/src/widgets/kiran-label/kiran-label.cpp new file mode 100644 index 0000000..6302357 --- /dev/null +++ b/src/widgets/kiran-label/kiran-label.cpp @@ -0,0 +1,337 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiranwidgets-qt5 is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "kiran-label.h" +#include "kiran-label-private.h" + +#include +#include +#include +#include +#include + +KiranLabelPrivate::KiranLabelPrivate(KiranLabel *ptr, QObject *parent) + : QObject(parent), + q_ptr(ptr) +{ +} + +KiranLabelPrivate::~KiranLabelPrivate() +{ +} + +void KiranLabelPrivate::init() +{ +} + +Qt::LayoutDirection KiranLabelPrivate::textDirection(QLabelPrivate *ld) +{ + if (ld->control) + { + QTextOption opt = ld->control->document()->defaultTextOption(); + return opt.textDirection(); + } + + return ld->text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; +} + +QRectF KiranLabelPrivate::layoutRect(QLabelPrivate *ld) +{ + QRectF cr = documentRect(ld); + if (!ld->control) + return cr; + ensureTextLayouted(ld); + // Caculate y position manually + qreal rh = ld->control->document()->documentLayout()->documentSize().height(); + qreal yo = 0; + if (ld->align & Qt::AlignVCenter) + yo = qMax((cr.height() - rh) / 2, qreal(0)); + else if (ld->align & Qt::AlignBottom) + yo = qMax(cr.height() - rh, qreal(0)); + return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height()); +} + +QRectF KiranLabelPrivate::documentRect(QLabelPrivate *ld) +{ + QLabel *q = qobject_cast(ld->q_ptr); + Q_ASSERT_X(ld->isTextLabel, "documentRect", "document rect called for label that is not a text label!"); + QRect cr = q->contentsRect(); + cr.adjust(ld->margin, ld->margin, -ld->margin, -ld->margin); + const int align = QStyle::visualAlignment(ld->isTextLabel ? textDirection(ld) + : q->layoutDirection(), + QFlag(ld->align)); + int m = ld->indent; + if (m < 0 && q->frameWidth()) // no indent, but we do have a frame + m = q->fontMetrics().horizontalAdvance(QLatin1Char('x')) / 2 - ld->margin; + if (m > 0) + { + if (align & Qt::AlignLeft) + cr.setLeft(cr.left() + m); + if (align & Qt::AlignRight) + cr.setRight(cr.right() - m); + if (align & Qt::AlignTop) + cr.setTop(cr.top() + m); + if (align & Qt::AlignBottom) + cr.setBottom(cr.bottom() - m); + } + return cr; +} + +void KiranLabelPrivate::ensureTextLayouted(QLabelPrivate *ld) +{ + if (ld->textLayoutDirty) + { + if (ld->textDirty) + { + if (ld->control) + { + QTextDocument *doc = ld->control->document(); + if (ld->textDirty) + { +#ifndef QT_NO_TEXTHTMLPARSER +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + if (ld->textformat == Qt::TextFormat::RichText) +#else + if (ld->isRichText) +#endif + doc->setHtml(ld->text); + else + doc->setPlainText(ld->text); +#else + doc->setPlainText(ld->text); +#endif + doc->setUndoRedoEnabled(false); + +#ifndef QT_NO_SHORTCUT + if (ld->hasShortcut) + { + // Underline the first character that follows an ampersand (and remove the others ampersands) + int from = 0; + bool found = false; + QTextCursor cursor; + while (!(cursor = ld->control->document()->find((QLatin1String("&")), from)).isNull()) + { + cursor.deleteChar(); // remove the ampersand + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + from = cursor.position(); + if (!found && cursor.selectedText() != QLatin1String("&")) + { // not a second & + found = true; + ld->shortcutCursor = cursor; + } + } + } +#endif + } + } + ld->textDirty = false; + } + + if (ld->control) + { + QTextDocument *doc = ld->control->document(); + QTextOption opt = doc->defaultTextOption(); + + opt.setAlignment(QFlag(ld->align)); + + if (ld->align & Qt::TextWordWrap) + opt.setWrapMode(QTextOption::WordWrap); + else + opt.setWrapMode(QTextOption::ManualWrap); + + doc->setDefaultTextOption(opt); + + QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); + fmt.setMargin(0); + doc->rootFrame()->setFrameFormat(fmt); + doc->setTextWidth(documentRect(ld).width()); + } + ld->textLayoutDirty = false; + } +} + +KiranLabel::KiranLabel(QWidget *parent, Qt::WindowFlags f) + : QLabel(parent, f), + kiran_d_ptr(new KiranLabelPrivate(this)) +{ + kiran_d_ptr->init(); +} + +KiranLabel::KiranLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) + : QLabel(text,parent, f), + kiran_d_ptr(new KiranLabelPrivate(this)) +{ + kiran_d_ptr->init(); +} + +KiranLabel::~KiranLabel() +{ + delete kiran_d_ptr; +} + +void KiranLabel::setElideMode(Qt::TextElideMode elideMode) +{ + if (KiranLabel::elideMode() == elideMode) + { + return; + } + + kiran_d_ptr->elideMode = elideMode; + update(); +} + +Qt::TextElideMode KiranLabel::elideMode() const +{ + return kiran_d_ptr->elideMode; +} + +/// @brief 从QLabel::paintEvent修改而来,将QLabelPrivate相关方法(由于未暴露符号)移动至KiranLabelPrivate之中 +void KiranLabel::paintEvent(QPaintEvent *event) +{ + QLabelPrivate *d = static_cast(d_ptr.data()); + QStyle *style = QWidget::style(); + + QPainter p(this); + + drawFrame(&p); + + QRect cr = contentsRect(); + cr.adjust(d->margin, d->margin, d->margin, d->margin); + int align = QStyle::visualAlignment(d->isTextLabel ? KiranLabelPrivate::textDirection(d) : layoutDirection(), QFlag(d->align)); + +#if QT_CONFIG(movie) + if (d->movie && !d->movie->currentPixmap().isNull()) + { + if (d->scaledcontents) + style->drawItemPixmap(&p, cr, align, d->movie->currentPixmap().scaled(cr.size())); + else + style->drawItemPixmap(&p, cr, align, d->movie->currentPixmap()); + } + else +#endif + if (d->isTextLabel) + { + QRectF lr = KiranLabelPrivate::layoutRect(d).toAlignedRect(); + QStyleOption opt; + opt.initFrom(this); + + if (d->control) + { +#ifndef QT_NO_SHORTCUT + const bool underline = static_cast(style->styleHint(QStyle::SH_UnderlineShortcut, + nullptr, this, nullptr)); + if (d->shortcutId != 0 && underline != d->shortcutCursor.charFormat().fontUnderline()) + { + QTextCharFormat fmt; + fmt.setFontUnderline(underline); + d->shortcutCursor.mergeCharFormat(fmt); + } +#endif + KiranLabelPrivate::ensureTextLayouted(d); + + QAbstractTextDocumentLayout::PaintContext context; + // Adjust the palette + context.palette = opt.palette; + + if (foregroundRole() != QPalette::Text && isEnabled()) + context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole())); + + p.save(); + p.translate(lr.topLeft()); + p.setClipRect(lr.translated(-lr.x(), -lr.y())); + d->control->setPalette(context.palette); + d->control->drawContents(&p, QRectF(), this); + p.restore(); + } + else + { + int flags = align | (KiranLabelPrivate::textDirection(d) == Qt::LeftToRight ? Qt::TextForceLeftToRight + : Qt::TextForceRightToLeft); + if (d->hasShortcut) + { + flags |= Qt::TextShowMnemonic; + if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) + flags |= Qt::TextHideMnemonic; + } + + QString elideText = d->text; + if (kiran_d_ptr->elideMode != Qt::ElideNone) + { + const QFontMetrics fm(fontMetrics()); + elideText = fm.elidedText(elideText, elideMode(), width(), Qt::TextShowMnemonic); + } + + style->drawItemText(&p, lr.toRect(), flags, opt.palette, isEnabled(), elideText, foregroundRole()); + } + } + else +#ifndef QT_NO_PICTURE + if (d->picture) + { + QRect br = d->picture->boundingRect(); + int rw = br.width(); + int rh = br.height(); + if (d->scaledcontents) + { + p.save(); + p.translate(cr.x(), cr.y()); + p.scale((double)cr.width() / rw, (double)cr.height() / rh); + p.drawPicture(-br.x(), -br.y(), *d->picture); + p.restore(); + } + else + { + int xo = 0; + int yo = 0; + if (align & Qt::AlignVCenter) + yo = (cr.height() - rh) / 2; + else if (align & Qt::AlignBottom) + yo = cr.height() - rh; + if (align & Qt::AlignRight) + xo = cr.width() - rw; + else if (align & Qt::AlignHCenter) + xo = (cr.width() - rw) / 2; + p.drawPicture(cr.x() + xo - br.x(), cr.y() + yo - br.y(), *d->picture); + } + } + else +#endif + if (d->pixmap && !d->pixmap->isNull()) + { + QPixmap pix; + if (d->scaledcontents) + { + QSize scaledSize = cr.size() * devicePixelRatioF(); + if (!d->scaledpixmap || d->scaledpixmap->size() != scaledSize) + { + if (!d->cachedimage) + d->cachedimage = new QImage(d->pixmap->toImage()); + delete d->scaledpixmap; + QImage scaledImage = + d->cachedimage->scaled(scaledSize, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + d->scaledpixmap = new QPixmap(QPixmap::fromImage(std::move(scaledImage))); + d->scaledpixmap->setDevicePixelRatio(devicePixelRatioF()); + } + pix = *d->scaledpixmap; + } + else + pix = *d->pixmap; + QStyleOption opt; + opt.initFrom(this); + if (!isEnabled()) + pix = style->generatedIconPixmap(QIcon::Disabled, pix, &opt); + style->drawItemPixmap(&p, cr, align, pix); + } +} \ No newline at end of file diff --git a/src/widgets/kiran-label/kiran-label.h b/src/widgets/kiran-label/kiran-label.h new file mode 100644 index 0000000..2266076 --- /dev/null +++ b/src/widgets/kiran-label/kiran-label.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiranwidgets-qt5 is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef __KIRAN_LABEL_H__ +#define __KIRAN_LABEL_H__ + +#include + +class KiranLabelPrivate; +class KiranLabel : public QLabel +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(kiran_d_ptr,KiranLabel) +public: + explicit KiranLabel(QWidget* parent = nullptr,Qt::WindowFlags f=Qt::WindowFlags()); + explicit KiranLabel(const QString &text, QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags()); + ~KiranLabel(); + + void setElideMode(Qt::TextElideMode elideMode); + Qt::TextElideMode elideMode() const; + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + KiranLabelPrivate* kiran_d_ptr; +}; + +#endif \ No newline at end of file diff --git a/src/widgets/kiran-passwd-edit/kiran-passwd-edit.cpp b/src/widgets/kiran-passwd-edit/kiran-passwd-edit.cpp new file mode 100644 index 0000000..a1a474d --- /dev/null +++ b/src/widgets/kiran-passwd-edit/kiran-passwd-edit.cpp @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiranwidgets-qt5 is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "kiran-passwd-edit.h" + +#include +#include +#include + +class KiranPasswdEditPrivate +{ +public: + KiranPasswdEditPrivate(KiranPasswdEdit *qq) + : q(qq) + { + } + void initialize(); + void echoModeToggled(); + void showToggleEchoModeAction(const QString &text); + + QIcon passwordIcon; + QIcon visibleIcon; + + QLineEdit *passwordLineEdit = nullptr; + QAction *toggleEchoModeAction = nullptr; + bool isToggleEchoModeAvailable = true; + bool revealPasswordAvailable = true; + KiranPasswdEdit *const q; +}; + +void KiranPasswdEditPrivate::initialize() +{ + QIcon visibilityIcon = QIcon::fromTheme(QStringLiteral("visibility"), QIcon(QStringLiteral(":/icons/visibility.svg"))); + toggleEchoModeAction = passwordLineEdit->addAction(visibilityIcon, QLineEdit::TrailingPosition); + toggleEchoModeAction->setObjectName(QStringLiteral("visibilityAction")); + toggleEchoModeAction->setVisible(false); + toggleEchoModeAction->setToolTip(QObject::tr("Change the visibility of the password", "@info:tooltip")); + q->connect(toggleEchoModeAction, &QAction::triggered, q, [this]() + { echoModeToggled(); }); + q->connect(passwordLineEdit, &QLineEdit::textChanged, q, [this](const QString &str) + { showToggleEchoModeAction(str); }); +} + +void KiranPasswdEditPrivate::showToggleEchoModeAction(const QString &text) +{ + if (revealPasswordAvailable) + { + toggleEchoModeAction->setVisible(isToggleEchoModeAvailable && (passwordLineEdit->echoMode() == QLineEdit::Normal || !text.isEmpty())); + } + else + { + toggleEchoModeAction->setVisible(false); + } +} + +void KiranPasswdEditPrivate::echoModeToggled() +{ + if (passwordLineEdit->echoMode() == QLineEdit::Password) + { + passwordLineEdit->setEchoMode(QLineEdit::Normal); + if (passwordIcon.isNull()) + { + passwordIcon = QIcon(":/kiranwidgets-qt5/images/passwd-edit/reveal-passwd.svg"); + } + toggleEchoModeAction->setIcon(passwordIcon); + } + else if (passwordLineEdit->echoMode() == QLineEdit::Normal) + { + if (visibleIcon.isNull()) + { + visibleIcon = QIcon(":/kiranwidgets-qt5/images/passwd-edit/unreveal-passwd.svg"); + } + passwordLineEdit->setEchoMode(QLineEdit::Password); + toggleEchoModeAction->setIcon(visibleIcon); + } + Q_EMIT q->echoModeChanged(passwordLineEdit->echoMode()); +} + +KiranPasswdEdit::KiranPasswdEdit(QWidget *parent) + : QWidget(parent), d_ptr(new KiranPasswdEditPrivate(this)) +{ + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setObjectName(QStringLiteral("mainlayout")); + mainLayout->setContentsMargins(0, 0, 0, 0); + d_ptr->passwordLineEdit = new QLineEdit(this); + d_ptr->passwordLineEdit->setObjectName(QStringLiteral("passwordlineedit")); + d_ptr->passwordLineEdit->setEchoMode(QLineEdit::Password); + connect(d_ptr->passwordLineEdit, &QLineEdit::textChanged, this, &KiranPasswdEdit::passwordChanged); + setFocusProxy(d_ptr->passwordLineEdit); + setFocusPolicy(d_ptr->passwordLineEdit->focusPolicy()); + mainLayout->addWidget(d_ptr->passwordLineEdit); + d_ptr->initialize(); + + setStyleSheet("QLineEdit[echoMode=\"2\"]{ lineedit-password-character: 9679; }"); +} + +KiranPasswdEdit::~KiranPasswdEdit() = default; + +void KiranPasswdEdit::setPassword(const QString &password) +{ + if (d_ptr->passwordLineEdit->text() != password) + { + d_ptr->isToggleEchoModeAvailable = password.isEmpty(); + d_ptr->passwordLineEdit->setText(password); + } +} + +QString KiranPasswdEdit::password() const +{ + return d_ptr->passwordLineEdit->text(); +} + +void KiranPasswdEdit::clear() +{ + d_ptr->passwordLineEdit->clear(); +} + +void KiranPasswdEdit::setClearButtonEnabled(bool clear) +{ + d_ptr->passwordLineEdit->setClearButtonEnabled(clear); +} + +bool KiranPasswdEdit::isClearButtonEnabled() const +{ + return d_ptr->passwordLineEdit->isClearButtonEnabled(); +} + +void KiranPasswdEdit::setEchoMode(QLineEdit::EchoMode mode) +{ + d_ptr->passwordLineEdit->setEchoMode(mode); +} + +QLineEdit::EchoMode KiranPasswdEdit::echoMode() const +{ + return d_ptr->passwordLineEdit->echoMode(); +} + +QLineEdit *KiranPasswdEdit::lineEdit() const +{ + return d_ptr->passwordLineEdit; +} + +void KiranPasswdEdit::setRevealPasswordAvailable(bool reveal) +{ + d_ptr->revealPasswordAvailable = reveal; + d_ptr->showToggleEchoModeAction(password()); +} + +bool KiranPasswdEdit::isRevealPasswordAvailable() const +{ + return d_ptr->revealPasswordAvailable; +} + +QAction *KiranPasswdEdit::toggleEchoModeAction() const +{ + return d_ptr->toggleEchoModeAction; +} + +#include "moc_kiran-passwd-edit.cpp" \ No newline at end of file diff --git a/src/widgets/kiran-passwd-edit/kiran-passwd-edit.h b/src/widgets/kiran-passwd-edit/kiran-passwd-edit.h new file mode 100644 index 0000000..39ceba7 --- /dev/null +++ b/src/widgets/kiran-passwd-edit/kiran-passwd-edit.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiranwidgets-qt5 is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef __KIRAN_PASSWD_EDIT_H__ +#define __KIRAN_PASSWD_EDIT_H__ + +#include +#include +#include + +class QAction; +class KiranPasswdEditPrivate; + +class KiranPasswdEdit : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) + Q_PROPERTY(bool clearButtonEnabled READ isClearButtonEnabled WRITE setClearButtonEnabled) + Q_PROPERTY(QLineEdit::EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged) + Q_DECLARE_PRIVATE(KiranPasswdEdit) +public: + explicit KiranPasswdEdit(QWidget *parent = nullptr); + ~KiranPasswdEdit() override; + + void setPassword(const QString &password); + QString password() const; + + void clear(); + + void setClearButtonEnabled(bool clear); + bool isClearButtonEnabled() const; + + void setEchoMode(QLineEdit::EchoMode mode); + QLineEdit::EchoMode echoMode() const; + + void setRevealPasswordAvailable(bool reveal); + bool isRevealPasswordAvailable() const; + + QAction *toggleEchoModeAction() const; + QLineEdit *lineEdit() const; + +Q_SIGNALS: + void echoModeChanged(QLineEdit::EchoMode echoMode); + void passwordChanged(const QString &password); + +private: + KiranPasswdEditPrivate *d_ptr; +}; + +#endif \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc86b89..e8bcaf5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,4 +25,6 @@ ADD_TEST_ENTRY(kiran-message-box-test) ADD_TEST_ENTRY(kiran-sidebar-widget-test) ADD_TEST_ENTRY(kiran-hover-tips-test) ADD_TEST_ENTRY(kiran-tips-test) -ADD_TEST_ENTRY(kiran-color-block-test) \ No newline at end of file +ADD_TEST_ENTRY(kiran-color-block-test) +ADD_TEST_ENTRY(kiran-passwd-edit-test) +ADD_TEST_ENTRY(kiran-label-test) \ No newline at end of file diff --git a/test/kiran-label-test.cpp b/test/kiran-label-test.cpp new file mode 100644 index 0000000..39ccd7a --- /dev/null +++ b/test/kiran-label-test.cpp @@ -0,0 +1,94 @@ +#include "kiran-label/kiran-label.h" + +#include +#include +#include +#include + +#define TEST_TEXT_1 "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend."\ + "hello, my friend." + + +#define TEST_TEXT_2 "wow!!!!"\ + "wow!!!!"\ + "wow!!!!"\ + "wow!!!!"\ + "wow!!!!"\ + "wow!!!!"\ + "wow!!!!"\ + +class KiranLabelTest: public QObject +{ + Q_OBJECT +private slots: + void initTestCase() + { + kiranLabel = new KiranLabel; + kiranLabel->resize(500, 200); + kiranLabel->show(); + QTest::qWaitForWindowExposed(kiranLabel); + } + + void testElideRight() + { + kiranLabel->resize(500, 200); + kiranLabel->setText(TEST_TEXT_1); + kiranLabel->setElideMode(Qt::ElideRight); + QTest::qWait(1000); + kiranLabel->resize(200, 200); + QTest::qWait(1000); + kiranLabel->setText(TEST_TEXT_2); + QTest::qWait(1000); + } + + void testElideLeft() + { + kiranLabel->resize(500, 200); + kiranLabel->setText(TEST_TEXT_1); + kiranLabel->setElideMode(Qt::ElideLeft); + QTest::qWait(1000); + kiranLabel->resize(200, 200); + QTest::qWait(1000); + kiranLabel->setText(TEST_TEXT_2); + QTest::qWait(1000); + } + + void testElideMiddle() + { + kiranLabel->resize(500, 200); + kiranLabel->setText(TEST_TEXT_1); + kiranLabel->setElideMode(Qt::ElideMiddle); + QTest::qWait(1000); + kiranLabel->resize(200, 200); + QTest::qWait(1000); + kiranLabel->setText(TEST_TEXT_2); + QTest::qWait(1000); + } + + void cleanupTestCase() + { + kiranLabel->hide(); + delete kiranLabel; + } + +private: + KiranLabel* kiranLabel; +}; + +QTEST_MAIN(KiranLabelTest) +#include "kiran-label-test.moc" \ No newline at end of file diff --git a/test/kiran-passwd-edit-test.cpp b/test/kiran-passwd-edit-test.cpp new file mode 100644 index 0000000..2b06253 --- /dev/null +++ b/test/kiran-passwd-edit-test.cpp @@ -0,0 +1,50 @@ +#include "kiran-passwd-edit/kiran-passwd-edit.h" + +#include +#include +#include +#include + +class KiranPasswdEditTest: public QObject +{ + Q_OBJECT +private slots: + void initTestCase() + { + passwdEdit = new KiranPasswdEdit; + passwdEdit->resize(500, 200); + passwdEdit->show(); + QTest::qWaitForWindowExposed(passwdEdit); + } + + void testClearButton() + { + passwdEdit->lineEdit()->setText("test clean button...."); + passwdEdit->setClearButtonEnabled(true); + QTest::qWait(1000); + passwdEdit->setClearButtonEnabled(false); + QTest::qWait(1000); + passwdEdit->lineEdit()->clear(); + } + + void testEchoModeButton() + { + passwdEdit->lineEdit()->setText("test text...."); + passwdEdit->setEchoMode(QLineEdit::Normal); + QTest::qWait(1000); + passwdEdit->lineEdit()->clear(); + QTest::qWait(1000); + } + + void cleanupTestCase() + { + passwdEdit->hide(); + delete passwdEdit; + } + +private: + KiranPasswdEdit* passwdEdit; +}; + +QTEST_MAIN(KiranPasswdEditTest) +#include "kiran-passwd-edit-test.moc" \ No newline at end of file diff --git a/translations/kiranwidgets-qt5.zh_CN.ts b/translations/kiranwidgets-qt5.zh_CN.ts index 1cf974b..3ade04d 100644 --- a/translations/kiranwidgets-qt5.zh_CN.ts +++ b/translations/kiranwidgets-qt5.zh_CN.ts @@ -101,6 +101,12 @@ Restore Defaults 恢复默认 + + + Change the visibility of the password + @info:tooltip + 更改密码的可见性 + TitlebarWindowSimple -- 2.33.0