From 594ae205c55f8340a970bdb7795f26faecba8385 Mon Sep 17 00:00:00 2001 From: liuxinhao Date: Wed, 30 Nov 2022 14:09:04 +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 --- ...KiranPasswdEdit-new-control-KiranLab.patch | 1078 +++++++++++++++++ kiran-widgets-qt5.spec | 6 +- 2 files changed, 1083 insertions(+), 1 deletion(-) create mode 100644 0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch diff --git a/0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch b/0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch new file mode 100644 index 0000000..edb4c96 --- /dev/null +++ b/0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch @@ -0,0 +1,1078 @@ +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 + diff --git a/kiran-widgets-qt5.spec b/kiran-widgets-qt5.spec index c9c28dd..e769cec 100644 --- a/kiran-widgets-qt5.spec +++ b/kiran-widgets-qt5.spec @@ -1,11 +1,12 @@ Name: kiran-widgets-qt5 Version: 2.4.0 -Release: 1 +Release: 2 Summary: Encapsulated QT Widget Summary(zh_CN): 封装的Qt小部件 License: MulanPSL-2.0 Source0: %{name}-%{version}.tar.gz +Patch0001: 0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch BuildRequires: cmake >= 3.5 BuildRequires: gcc-c++ @@ -76,6 +77,9 @@ make %{?_smp_mflags} rm -rf %{buildroot} %changelog +* Wed Nov 30 2022 liuxinhao - 2.4.0-2 +- KYOS-F: add KiranLabel,KiranPasswdEdit + * Mon Oct 31 2022 liuxinhao - 2.4.0-1 - KYOS-F: Modify KiranColorBlock and KiranTitlebarWindow to support the 2.4 color block design of the control center -- Gitee