From a669df832ff1e08ce996d26dfa31dd29d59e5105 Mon Sep 17 00:00:00 2001 From: johanneshilden Date: Fri, 29 Sep 2017 01:01:01 +0300 Subject: [PATCH] Reimplement Badge component --- components/components.pro | 9 +- components/lib/qtmaterialoverlaywidget.cpp | 72 +++++ components/lib/qtmaterialoverlaywidget.h | 24 ++ components/qtmaterialbadge.cpp | 290 +++++++++++++++++++++ components/qtmaterialbadge.h | 61 +++++ components/qtmaterialbadge_p.h | 34 +++ examples/avatarsettingseditor.cpp | 3 +- examples/badgesettingseditor.cpp | 103 ++++++++ examples/badgesettingseditor.h | 29 +++ examples/badgesettingsform.ui | 142 ++++++++++ examples/examples.pro | 9 +- examples/mainwindow.cpp | 6 +- 12 files changed, 775 insertions(+), 7 deletions(-) create mode 100644 components/lib/qtmaterialoverlaywidget.cpp create mode 100644 components/lib/qtmaterialoverlaywidget.h create mode 100644 components/qtmaterialbadge.cpp create mode 100644 components/qtmaterialbadge.h create mode 100644 components/qtmaterialbadge_p.h create mode 100644 examples/badgesettingseditor.cpp create mode 100644 examples/badgesettingseditor.h create mode 100644 examples/badgesettingsform.ui diff --git a/components/components.pro b/components/components.pro index fb889ad..5bdb6a8 100644 --- a/components/components.pro +++ b/components/components.pro @@ -3,13 +3,18 @@ CONFIG += staticlib SOURCES = \ qtmaterialavatar.cpp \ lib/qtmaterialstyle.cpp \ - lib/qtmaterialtheme.cpp + lib/qtmaterialtheme.cpp \ + qtmaterialbadge.cpp \ + lib/qtmaterialoverlaywidget.cpp HEADERS = \ qtmaterialavatar_p.h \ qtmaterialavatar.h \ lib/qtmaterialstyle_p.h \ lib/qtmaterialstyle.h \ lib/qtmaterialtheme_p.h \ - lib/qtmaterialtheme.h + lib/qtmaterialtheme.h \ + qtmaterialbadge_p.h \ + qtmaterialbadge.h \ + lib/qtmaterialoverlaywidget.h RESOURCES += \ resources.qrc diff --git a/components/lib/qtmaterialoverlaywidget.cpp b/components/lib/qtmaterialoverlaywidget.cpp new file mode 100644 index 0000000..ec0e7fe --- /dev/null +++ b/components/lib/qtmaterialoverlaywidget.cpp @@ -0,0 +1,72 @@ +#include "lib/qtmaterialoverlaywidget.h" +#include + +/*! + * \class QtMaterialOverlayWidget + * \internal + */ + +QtMaterialOverlayWidget::QtMaterialOverlayWidget(QWidget *parent) + : QWidget(parent) +{ + if (parent) { + parent->installEventFilter(this); + } +} + +QtMaterialOverlayWidget::~QtMaterialOverlayWidget() +{ +} + +/*! + * \reimp + */ +bool QtMaterialOverlayWidget::event(QEvent *event) +{ + if (!parent()) { + return QWidget::event(event); + } + switch (event->type()) + { + case QEvent::ParentChange: + { + parent()->installEventFilter(this); + setGeometry(overlayGeometry()); + break; + } + case QEvent::ParentAboutToChange: + { + parent()->removeEventFilter(this); + break; + } + default: + break; + } + return QWidget::event(event); +} + +/*! + * \reimp + */ +bool QtMaterialOverlayWidget::eventFilter(QObject *obj, QEvent *event) +{ + switch (event->type()) + { + case QEvent::Move: + case QEvent::Resize: + setGeometry(overlayGeometry()); + break; + default: + break; + } + return QWidget::eventFilter(obj, event); +} + +QRect QtMaterialOverlayWidget::overlayGeometry() const +{ + QWidget *widget = parentWidget(); + if (!widget) { + return QRect(); + } + return widget->rect(); +} diff --git a/components/lib/qtmaterialoverlaywidget.h b/components/lib/qtmaterialoverlaywidget.h new file mode 100644 index 0000000..11acba0 --- /dev/null +++ b/components/lib/qtmaterialoverlaywidget.h @@ -0,0 +1,24 @@ +#ifndef QTMATERIALOVERLAYWIDGET_H +#define QTMATERIALOVERLAYWIDGET_H + +#include + +class QtMaterialOverlayWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QtMaterialOverlayWidget(QWidget *parent = 0); + ~QtMaterialOverlayWidget(); + +protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; + bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; + + virtual QRect overlayGeometry() const; + +private: + Q_DISABLE_COPY(QtMaterialOverlayWidget) +}; + +#endif // QTMATERIALOVERLAYWIDGET_H diff --git a/components/qtmaterialbadge.cpp b/components/qtmaterialbadge.cpp new file mode 100644 index 0000000..5e1eaa5 --- /dev/null +++ b/components/qtmaterialbadge.cpp @@ -0,0 +1,290 @@ +#include "qtmaterialbadge.h" +#include "qtmaterialbadge_p.h" +#include +#include "lib/qtmaterialstyle.h" + +/*! + * \class QtMaterialBadgePrivate + * \internal + */ + +/*! + * \internal + */ +QtMaterialBadgePrivate::QtMaterialBadgePrivate(QtMaterialBadge *q) + : q_ptr(q) +{ +} + +/*! + * \internal + */ +QtMaterialBadgePrivate::~QtMaterialBadgePrivate() +{ +} + +/*! + * \internal + */ +void QtMaterialBadgePrivate::init() +{ + Q_Q(QtMaterialBadge); + + x = 0; + y = 0; + padding = 10; + useThemeColors = true; + + q->setAttribute(Qt::WA_TransparentForMouseEvents); + + QFont font(q->font()); + font.setPointSizeF(10); + font.setStyleName("Bold"); + q->setFont(font); + + q->setText("+1"); +} + +/*! + * \class QtMaterialBadge + */ + +QtMaterialBadge::QtMaterialBadge(QWidget *parent) + : QtMaterialOverlayWidget(parent), + d_ptr(new QtMaterialBadgePrivate(this)) +{ + d_func()->init(); +} + +QtMaterialBadge::QtMaterialBadge(const QIcon &icon, QWidget *parent) + : QtMaterialOverlayWidget(parent), + d_ptr(new QtMaterialBadgePrivate(this)) +{ + d_func()->init(); + + setIcon(icon); +} + +QtMaterialBadge::QtMaterialBadge(const QString &text, QWidget *parent) + : QtMaterialOverlayWidget(parent), + d_ptr(new QtMaterialBadgePrivate(this)) +{ + d_func()->init(); + + setText(text); +} + +QtMaterialBadge::~QtMaterialBadge() +{ +} + +void QtMaterialBadge::setUseThemeColors(bool value) +{ + Q_D(QtMaterialBadge); + + if (d->useThemeColors == value) { + return; + } + + d->useThemeColors = value; + update(); +} + +bool QtMaterialBadge::useThemeColors() const +{ + Q_D(const QtMaterialBadge); + + return d->useThemeColors; +} + +void QtMaterialBadge::setTextColor(const QColor &color) +{ + Q_D(QtMaterialBadge); + + d->textColor = color; + setUseThemeColors(false); +} + +QColor QtMaterialBadge::textColor() const +{ + Q_D(const QtMaterialBadge); + + if (d->useThemeColors || !d->textColor.isValid()) { + return QtMaterialStyle::instance().themeColor("canvas"); + } else { + return d->textColor; + } +} + +void QtMaterialBadge::setBackgroundColor(const QColor &color) +{ + Q_D(QtMaterialBadge); + + d->backgroundColor = color; + setUseThemeColors(false); +} + +QColor QtMaterialBadge::backgroundColor() const +{ + Q_D(const QtMaterialBadge); + + if (d->useThemeColors || !d->backgroundColor.isValid()) { + return QtMaterialStyle::instance().themeColor("accent1"); + } else { + return d->backgroundColor; + } +} + +void QtMaterialBadge::setRelativePosition(const QPointF &pos) +{ + setRelativePosition(pos.x(), pos.y()); +} + +void QtMaterialBadge::setRelativePosition(qreal x, qreal y) +{ + Q_D(QtMaterialBadge); + + d->x = x; + d->y = y; + update(); +} + +QPointF QtMaterialBadge::relativePosition() const +{ + Q_D(const QtMaterialBadge); + + return QPointF(d->x, d->y); +} + +void QtMaterialBadge::setRelativeXPosition(qreal x) +{ + Q_D(QtMaterialBadge); + + d->x = x; + update(); +} + +qreal QtMaterialBadge::relativeXPosition() const +{ + Q_D(const QtMaterialBadge); + + return d->x; +} + +void QtMaterialBadge::setRelativeYPosition(qreal y) +{ + Q_D(QtMaterialBadge); + + d->y = y; + update(); +} + +qreal QtMaterialBadge::relativeYPosition() const +{ + Q_D(const QtMaterialBadge); + + return d->y; +} + +/*! + * \reimp + */ +QSize QtMaterialBadge::sizeHint() const +{ + const int s = getDiameter(); + return QSize(s+4, s+4); +} + +void QtMaterialBadge::setIcon(const QIcon &icon) +{ + Q_D(QtMaterialBadge); + + d->icon = icon; + update(); +} + +QIcon QtMaterialBadge::icon() const +{ + Q_D(const QtMaterialBadge); + + return d->icon; +} + +void QtMaterialBadge::setText(const QString &text) +{ + Q_D(QtMaterialBadge); + + d->text = text; + + if (!d->icon.isNull()) { + d->icon = QIcon(); + } + + d->size = fontMetrics().size(Qt::TextShowMnemonic, text); + + update(); +} + +QString QtMaterialBadge::text() const +{ + Q_D(const QtMaterialBadge); + + return d->text; +} + +/*! + * \reimp + */ +void QtMaterialBadge::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + Q_D(QtMaterialBadge); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + painter.translate(d->x, d->y); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(isEnabled() ? backgroundColor() + : QtMaterialStyle::instance().themeColor("disabled")); + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + + const int s = getDiameter(); + + QRectF r(0, 0, s, s); + r.translate(QPointF((width()-s), (height()-s))/2); + + if (d->icon.isNull()) + { + painter.drawEllipse(r); + painter.setPen(textColor()); + painter.setBrush(Qt::NoBrush); + painter.drawText(r.translated(0, -0.5), Qt::AlignCenter, d->text); + } + else + { + painter.drawEllipse(r); + QRectF q(0, 0, 16, 16); + q.moveCenter(r.center()); + QPixmap pixmap = icon().pixmap(16, 16); + QPainter icon(&pixmap); + icon.setCompositionMode(QPainter::CompositionMode_SourceIn); + icon.fillRect(pixmap.rect(), textColor()); + painter.drawPixmap(q.toRect(), pixmap); + } +} + +int QtMaterialBadge::getDiameter() const +{ + Q_D(const QtMaterialBadge); + + if (d->icon.isNull()) { + return qMax(d->size.width(), d->size.height()) + d->padding; + } else { + return 24; + } +} diff --git a/components/qtmaterialbadge.h b/components/qtmaterialbadge.h new file mode 100644 index 0000000..deac3ab --- /dev/null +++ b/components/qtmaterialbadge.h @@ -0,0 +1,61 @@ +#ifndef QTMATERIALBADGE_H +#define QTMATERIALBADGE_H + +#include "lib/qtmaterialoverlaywidget.h" + +class QtMaterialBadgePrivate; + +class QtMaterialBadge : public QtMaterialOverlayWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor) + Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) + Q_PROPERTY(QPointF relativePosition WRITE setRelativePosition READ relativePosition) + +public: + explicit QtMaterialBadge(QWidget *parent = 0); + explicit QtMaterialBadge(const QIcon &icon, QWidget *parent = 0); + explicit QtMaterialBadge(const QString &text, QWidget *parent = 0); + ~QtMaterialBadge(); + + void setUseThemeColors(bool value); + bool useThemeColors() const; + + void setTextColor(const QColor &color); + QColor textColor() const; + + void setBackgroundColor(const QColor &color); + QColor backgroundColor() const; + + void setRelativePosition(const QPointF &pos); + void setRelativePosition(qreal x, qreal y); + QPointF relativePosition() const; + + void setRelativeXPosition(qreal x); + qreal relativeXPosition() const; + + void setRelativeYPosition(qreal y); + qreal relativeYPosition() const; + + QSize sizeHint() const Q_DECL_OVERRIDE; + + void setIcon(const QIcon &icon); + QIcon icon() const; + + void setText(const QString &text); + QString text() const; + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + int getDiameter() const; + + const QScopedPointer d_ptr; + +private: + Q_DISABLE_COPY(QtMaterialBadge) + Q_DECLARE_PRIVATE(QtMaterialBadge) +}; + +#endif // QTMATERIALBADGE_H diff --git a/components/qtmaterialbadge_p.h b/components/qtmaterialbadge_p.h new file mode 100644 index 0000000..19757e7 --- /dev/null +++ b/components/qtmaterialbadge_p.h @@ -0,0 +1,34 @@ +#ifndef QTMATERIALBADGE_P_H +#define QTMATERIALBADGE_P_H + +#include +#include +#include +#include + +class QtMaterialBadge; + +class QtMaterialBadgePrivate +{ + Q_DISABLE_COPY(QtMaterialBadgePrivate) + Q_DECLARE_PUBLIC(QtMaterialBadge) + +public: + QtMaterialBadgePrivate(QtMaterialBadge *q); + ~QtMaterialBadgePrivate(); + + void init(); + + QtMaterialBadge *const q_ptr; + QString text; + QColor textColor; + QColor backgroundColor; + QSize size; + QIcon icon; + qreal x; + qreal y; + int padding; + bool useThemeColors; +}; + +#endif // QTMATERIALBADGE_P_H diff --git a/examples/avatarsettingseditor.cpp b/examples/avatarsettingseditor.cpp index 83f0629..ed8ddf3 100644 --- a/examples/avatarsettingseditor.cpp +++ b/examples/avatarsettingseditor.cpp @@ -1,6 +1,7 @@ #include "avatarsettingseditor.h" #include #include "qtmaterialavatar.h" +#include "lib/qtmaterialtheme.h" AvatarSettingsEditor::AvatarSettingsEditor(QWidget *parent) : QWidget(parent), @@ -75,7 +76,7 @@ void AvatarSettingsEditor::updateWidget() m_avatar->setImage(QImage(":/images/assets/sikh.jpg")); break; case 2: - m_avatar->setIcon(QIcon(":/icons/icons/communication/svg/production/ic_message_24px.svg")); + m_avatar->setIcon(QIcon(QtMaterialTheme::icon("communication", "message"))); break; default: break; diff --git a/examples/badgesettingseditor.cpp b/examples/badgesettingseditor.cpp new file mode 100644 index 0000000..cb8e578 --- /dev/null +++ b/examples/badgesettingseditor.cpp @@ -0,0 +1,103 @@ +#include "badgesettingseditor.h" +#include +#include "qtmaterialavatar.h" +#include "qtmaterialbadge.h" + +BadgeSettingsEditor::BadgeSettingsEditor(QWidget *parent) + : QWidget(parent), + ui(new Ui::BadgeSettingsForm), + m_avatar(new QtMaterialAvatar(QImage(":/images/assets/sikh.jpg"))), + m_badge(new QtMaterialBadge) +{ + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + QWidget *widget = new QWidget; + layout->addWidget(widget); + + QWidget *canvas = new QWidget; + canvas->setStyleSheet("QWidget { background: white; }"); + layout->addWidget(canvas); + + ui->setupUi(widget); + layout->setContentsMargins(20, 20, 20, 20); + + layout = new QVBoxLayout; + canvas->setLayout(layout); + layout->addWidget(m_avatar); + layout->setAlignment(m_avatar, Qt::AlignCenter); + m_avatar->setSize(60); + + m_badge->setParent(m_avatar); + m_badge->setRelativePosition(18, 18); + m_badge->setText("3"); + + setupForm(); + + connect(ui->disabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateWidget())); + connect(ui->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateWidget())); + connect(ui->useThemeColorsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateWidget())); + connect(ui->horizontalOffsetSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateWidget())); + connect(ui->verticalOffsetSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateWidget())); + connect(ui->backgroundColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + connect(ui->textColorToolButton, SIGNAL(pressed()), this, SLOT(selectColor())); + + ui->verticalOffsetSpinBox->setRange(-40, 40); + ui->horizontalOffsetSpinBox->setRange(-40, 40); +} + +BadgeSettingsEditor::~BadgeSettingsEditor() +{ + delete ui; +} + +void BadgeSettingsEditor::setupForm() +{ + if (m_badge->icon().isNull()) { + ui->typeComboBox->setCurrentIndex(0); + } else { + ui->typeComboBox->setCurrentIndex(1); + } + + ui->verticalOffsetSpinBox->setValue(m_badge->relativeYPosition()); + ui->horizontalOffsetSpinBox->setValue(m_badge->relativeXPosition()); + ui->disabledCheckBox->setChecked(!m_badge->isEnabled()); + ui->useThemeColorsCheckBox->setChecked(m_badge->useThemeColors()); +} + +void BadgeSettingsEditor::updateWidget() +{ + switch (ui->typeComboBox->currentIndex()) + { + case 0: + m_badge->setText("3"); + break; + case 1: + m_badge->setIcon(QIcon(QtMaterialTheme::icon("communication", "message"))); + break; + default: + break; + } + + m_badge->setRelativeYPosition(ui->verticalOffsetSpinBox->value()); + m_badge->setRelativeXPosition(ui->horizontalOffsetSpinBox->value()); + m_badge->setDisabled(ui->disabledCheckBox->isChecked()); + m_badge->setUseThemeColors(ui->useThemeColorsCheckBox->isChecked()); +} + +void BadgeSettingsEditor::selectColor() +{ + QColorDialog dialog; + if (dialog.exec()) { + QColor color = dialog.selectedColor(); + QString senderName = sender()->objectName(); + if ("textColorToolButton" == senderName) { + m_badge->setTextColor(color); + ui->textColorLineEdit->setText(color.name(QColor::HexRgb)); + } else if ("backgroundColorToolButton" == senderName) { + m_badge->setBackgroundColor(color); + ui->backgroundColorLineEdit->setText(color.name(QColor::HexRgb)); + } + } + setupForm(); +} diff --git a/examples/badgesettingseditor.h b/examples/badgesettingseditor.h new file mode 100644 index 0000000..8f74808 --- /dev/null +++ b/examples/badgesettingseditor.h @@ -0,0 +1,29 @@ +#ifndef BADGESETTINGSEDITOR_H +#define BADGESETTINGSEDITOR_H + +#include +#include "ui_badgesettingsform.h" + +class QtMaterialAvatar; +class QtMaterialBadge; + +class BadgeSettingsEditor : public QWidget +{ + Q_OBJECT + +public: + explicit BadgeSettingsEditor(QWidget *parent = 0); + ~BadgeSettingsEditor(); + +protected slots: + void setupForm(); + void updateWidget(); + void selectColor(); + +private: + Ui::BadgeSettingsForm *const ui; + QtMaterialAvatar *const m_avatar; + QtMaterialBadge *const m_badge; +}; + +#endif // BADGESETTINGSEDITOR_H diff --git a/examples/badgesettingsform.ui b/examples/badgesettingsform.ui new file mode 100644 index 0000000..5c80925 --- /dev/null +++ b/examples/badgesettingsform.ui @@ -0,0 +1,142 @@ + + + BadgeSettingsForm + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 0 + 0 + 201 + 231 + + + + + + + Disabled + + + + + + + + + + Use theme colors + + + + + + + + + + Background color + + + + + + + Text color + + + + + + + Horizontal offset + + + + + + + + + + Vertical offset + + + + + + + + + + Type + + + + + + + + Text + + + + + Icon + + + + + + + + + + false + + + + + + + ... + + + + + + + + + + + false + + + + + + + ... + + + + + + + + + + + diff --git a/examples/examples.pro b/examples/examples.pro index 13fa432..76b1058 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -2,9 +2,11 @@ QT += core gui widgets TEMPLATE = app SOURCES = mainwindow.cpp \ main.cpp \ - avatarsettingseditor.cpp + avatarsettingseditor.cpp \ + badgesettingseditor.cpp HEADERS = mainwindow.h \ - avatarsettingseditor.h + avatarsettingseditor.h \ + badgesettingseditor.h LIBS += ../components/libcomponents.a INCLUDEPATH += ../components/ TARGET = ../examples-exe @@ -13,4 +15,5 @@ RESOURCES += \ examples.qrc FORMS += \ - avatarsettingsform.ui + avatarsettingsform.ui \ + badgesettingsform.ui diff --git a/examples/mainwindow.cpp b/examples/mainwindow.cpp index df2b244..d8c78bd 100644 --- a/examples/mainwindow.cpp +++ b/examples/mainwindow.cpp @@ -1,11 +1,15 @@ #include "mainwindow.h" #include #include "avatarsettingseditor.h" +#include "badgesettingseditor.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { - AvatarSettingsEditor *editor = new AvatarSettingsEditor; + //AvatarSettingsEditor *editor = new AvatarSettingsEditor; + //setCentralWidget(editor); + + BadgeSettingsEditor *editor = new BadgeSettingsEditor; setCentralWidget(editor); }