kiran-widgets-qt5/0001-feat-KiranLabel-KiranPasswdEdit-new-control-KiranLab.patch
liuxinhao f1312e10b2 修复kiranui-2.4自测中发现的问题:
- 修复缩放两倍时_GTK_FRAME_EXTENTS属性需要更新的问题
- 文本过宽过长超过分配空间大小的情况下,对文本进行省略
2022-12-02 10:56:05 +08:00

1079 lines
40 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From bb02ffffe0ff6a51bc8fc6dd7ad09d4e7b278aff Mon Sep 17 00:00:00 2001
From: liuxinhao <liuxinhao@kylinsec.com.cn>
Date: Wed, 30 Nov 2022 13:53:05 +0800
Subject: [PATCH 1/3] 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 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+ <metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c138 79.159824, 2016/09/14-01:09:01 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""/>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?></metadata>
+<defs>
+ <style>
+ .cls-1 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <path id="预览开" class="cls-1" d="M877.894,1333.47q-2.61,5.52-7.894,5.53t-7.895-5.53a1.1,1.1,0,0,1,0-.94q2.61-5.52,7.895-5.53t7.894,5.53A1.1,1.1,0,0,1,877.894,1333.47Zm-7.968-3.71a3.24,3.24,0,1,0,3.23,3.24A3.234,3.234,0,0,0,869.926,1329.76Zm0,5.3a2.06,2.06,0,1,1,2.055-2.06A2.056,2.056,0,0,1,869.926,1335.06Z" transform="translate(-862 -1325)"/>
+</svg>
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 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+ <metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c138 79.159824, 2016/09/14-01:09:01 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""/>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?></metadata>
+<defs>
+ <style>
+ .cls-1 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <path id="预览关" class="cls-1" d="M877.894,1333.47q-2.61,5.52-7.894,5.53t-7.895-5.53a1.1,1.1,0,0,1,0-.94q2.61-5.52,7.895-5.53t7.894,5.53A1.1,1.1,0,0,1,877.894,1333.47ZM870,1328.33c-2.96,0-5.127,1.5-6.656,4.67,1.529,3.17,3.7,4.68,6.656,4.68s5.128-1.51,6.655-4.68C875.127,1329.83,872.959,1328.33,870,1328.33Zm-0.074,7.91a3.24,3.24,0,1,1,3.23-3.24A3.234,3.234,0,0,1,869.926,1336.24Zm0-5.3a2.06,2.06,0,1,0,2.055,2.06A2.055,2.055,0,0,0,869.926,1330.94Z" transform="translate(-862 -1325)"/>
+</svg>
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 @@
<file>images/hover-tips/tips-suc.svg</file>
<file>images/hover-tips/tips-warning.svg</file>
+ <file>images/passwd-edit/reveal-passwd.svg</file>
+ <file>images/passwd-edit/unreveal-passwd.svg</file>
</qresource>
<!--主题样式细节-->
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 <liuxinhao@kylinos.com.cn>
+ */
+#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 <liuxinhao@kylinos.com.cn>
+ */
+
+#include "kiran-label.h"
+#include "kiran-label-private.h"
+
+#include <private/qlabel_p.h>
+#include <private/qstylesheetstyle_p.h>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QStyleOption>
+
+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<QLabel *>(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<QLabelPrivate *>(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<bool>(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 <liuxinhao@kylinos.com.cn>
+ */
+#ifndef __KIRAN_LABEL_H__
+#define __KIRAN_LABEL_H__
+
+#include <QLabel>
+
+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 <liuxinhao@kylinos.com.cn>
+ */
+#include "kiran-passwd-edit.h"
+
+#include <QAction>
+#include <QHBoxLayout>
+#include <QIcon>
+
+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 <liuxinhao@kylinos.com.cn>
+ */
+#ifndef __KIRAN_PASSWD_EDIT_H__
+#define __KIRAN_PASSWD_EDIT_H__
+
+#include <QLineEdit>
+#include <QWidget>
+#include <memory>
+
+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 <QTest>
+#include <QWindow>
+#include <QEventLoop>
+#include <QLineEdit>
+
+#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 <QTest>
+#include <QWindow>
+#include <QEventLoop>
+#include <QLineEdit>
+
+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 @@
<source>Restore Defaults</source>
<translation>恢复默认</translation>
</message>
+ <message>
+ <location filename="../src/widgets/kiran-passwd-edit/kiran-passwd-edit.cpp" line="47"/>
+ <source>Change the visibility of the password</source>
+ <comment>@info:tooltip</comment>
+ <translation>更改密码的可见性</translation>
+ </message>
</context>
<context>
<name>TitlebarWindowSimple</name>
--
2.33.0