diff --git a/examples/mvvmcore/SampleCore/sampleviewmodel.cpp b/examples/mvvmcore/SampleCore/sampleviewmodel.cpp index ee7f830..149c642 100644 --- a/examples/mvvmcore/SampleCore/sampleviewmodel.cpp +++ b/examples/mvvmcore/SampleCore/sampleviewmodel.cpp @@ -96,6 +96,13 @@ void SampleViewModel::getFiles() }, tr("Open Files:"), {QStringLiteral("text/plain"), QStringLiteral("application/pdf")}); } +void SampleViewModel::getColor() +{ + QtMvvm::getColor(this, [this](const QColor &color) { + addEvent(color.name(QColor::HexArgb)); + }, tr("Select a color:"), true); +} + void SampleViewModel::getResult() { showForResult(ResCode, { diff --git a/examples/mvvmcore/SampleCore/sampleviewmodel.h b/examples/mvvmcore/SampleCore/sampleviewmodel.h index 3a4293c..5bb8be6 100644 --- a/examples/mvvmcore/SampleCore/sampleviewmodel.h +++ b/examples/mvvmcore/SampleCore/sampleviewmodel.h @@ -37,6 +37,7 @@ public Q_SLOTS: void getInput(); void getFiles(); + void getColor(); void getResult(); void clearEvents(); void about(); diff --git a/examples/mvvmcore/SampleCore/settings.xml b/examples/mvvmcore/SampleCore/settings.xml index 0fdb18c..37c58fe 100644 --- a/examples/mvvmcore/SampleCore/settings.xml +++ b/examples/mvvmcore/SampleCore/settings.xml @@ -147,6 +147,10 @@ default="2018-10-10T14:30"> Selected date and time: %1 + diff --git a/examples/mvvmwidgets/SampleWidgets/sampleview.cpp b/examples/mvvmwidgets/SampleWidgets/sampleview.cpp index 004cbd7..60d6cac 100644 --- a/examples/mvvmwidgets/SampleWidgets/sampleview.cpp +++ b/examples/mvvmwidgets/SampleWidgets/sampleview.cpp @@ -28,6 +28,8 @@ SampleView::SampleView(QtMvvm::ViewModel *viewModel, QWidget *parent) : _viewModel, &SampleViewModel::getInput); connect(ui->actionAdd_Files, &QAction::triggered, _viewModel, &SampleViewModel::getFiles); + connect(ui->actionAdd_Color, &QAction::triggered, + _viewModel, &SampleViewModel::getColor); connect(ui->actionShow_Tabs, &QAction::triggered, _viewModel, &SampleViewModel::showTabs); connect(ui->actionShow_Settings, &QAction::triggered, diff --git a/examples/mvvmwidgets/SampleWidgets/sampleview.ui b/examples/mvvmwidgets/SampleWidgets/sampleview.ui index 129b546..ce36925 100644 --- a/examples/mvvmwidgets/SampleWidgets/sampleview.ui +++ b/examples/mvvmwidgets/SampleWidgets/sampleview.ui @@ -111,6 +111,7 @@ + @@ -141,6 +142,11 @@ Show &Settings + + + Add &Color + + diff --git a/src/imports/mvvmcore/plugins.qmltypes b/src/imports/mvvmcore/plugins.qmltypes index 24e6eca..f53cd4e 100644 --- a/src/imports/mvvmcore/plugins.qmltypes +++ b/src/imports/mvvmcore/plugins.qmltypes @@ -457,6 +457,33 @@ Module { name: "getSaveFile" Parameter { name: "onResult"; type: "QJSValue" } } + Method { + name: "getColor" + revision: 1 + Parameter { name: "onResult"; type: "QJSValue" } + Parameter { name: "title"; type: "string" } + Parameter { name: "color"; type: "QColor" } + Parameter { name: "argb"; type: "bool" } + } + Method { + name: "getColor" + revision: 1 + Parameter { name: "onResult"; type: "QJSValue" } + Parameter { name: "title"; type: "string" } + Parameter { name: "color"; type: "QColor" } + } + Method { + name: "getColor" + revision: 1 + Parameter { name: "onResult"; type: "QJSValue" } + Parameter { name: "title"; type: "string" } + } + Method { + name: "getColor" + revision: 1 + Parameter { name: "onResult"; type: "QJSValue" } + } + Method { name: "getColor"; revision: 1 } } Component { name: "QtMvvm::QQmlServiceRegistry" diff --git a/src/imports/mvvmcore/qqmlmvvmmessage.cpp b/src/imports/mvvmcore/qqmlmvvmmessage.cpp index c83b02e..31dd6aa 100644 --- a/src/imports/mvvmcore/qqmlmvvmmessage.cpp +++ b/src/imports/mvvmcore/qqmlmvvmmessage.cpp @@ -105,3 +105,14 @@ void QQmlMvvmMessage::getSaveFile(const QJSValue &onResult, const QString &title } else QtMvvm::getSaveFile(title, supportedMimeTypes, dir); } + +void QtMvvm::QQmlMvvmMessage::getColor(const QJSValue &onResult, const QString &title, const QColor &color, bool argb) +{ + if(onResult.isCallable()) { + auto engine = _engine; + QtMvvm::getColor(this, [engine, onResult](const QColor &color){ + QJSValue(onResult).call({engine->toScriptValue(color)}); + }, title, color, argb); + } else + QtMvvm::getColor(title, color, argb); +} diff --git a/src/imports/mvvmcore/qqmlmvvmmessage.h b/src/imports/mvvmcore/qqmlmvvmmessage.h index 1fa3343..e2ecfcb 100644 --- a/src/imports/mvvmcore/qqmlmvvmmessage.h +++ b/src/imports/mvvmcore/qqmlmvvmmessage.h @@ -110,6 +110,11 @@ public Q_SLOTS: const QStringList &supportedMimeTypes = {}, const QUrl &dir = {}); + Q_REVISION(1) static void getColor(const QJSValue &onResult = {}, + const QString &title = {}, + const QColor &color = {}, + bool argb = false); + #ifndef DOXYGEN_RUN #undef static #endif diff --git a/src/mvvmcore/message.cpp b/src/mvvmcore/message.cpp index 4c8e969..92999dd 100644 --- a/src/mvvmcore/message.cpp +++ b/src/mvvmcore/message.cpp @@ -10,6 +10,7 @@ using namespace QtMvvm; const QByteArray MessageConfig::TypeMessageBox = "msgbox"; const QByteArray MessageConfig::TypeInputDialog = "input"; const QByteArray MessageConfig::TypeFileDialog = "file"; +const QByteArray MessageConfig::TypeColorDialog = "color"; const QByteArray MessageConfig::SubTypeInformation = "information"; const QByteArray MessageConfig::SubTypeWarning = "warning"; @@ -22,6 +23,9 @@ const QByteArray MessageConfig::SubTypeOpenFile = "open"; const QByteArray MessageConfig::SubTypeOpenFiles = "files"; const QByteArray MessageConfig::SubTypeSaveFile = "save"; +const QByteArray MessageConfig::SubTypeRgb = "rgb"; +const QByteArray MessageConfig::SubTypeArgb = "argb"; + MessageConfig::MessageConfig(const QByteArray &type, const QByteArray &subType) : d(new MessageConfigPrivate(type, subType)) { @@ -573,3 +577,27 @@ void QtMvvm::getSaveFile(const std::function &onResult, const QStri { getSaveFile(CoreApp::instance(), onResult, title, supportedMimeTypes, dir); } + +MessageResult *QtMvvm::getColor(const QString &title, const QColor &color, bool argb) +{ + MessageConfig config(MessageConfig::TypeColorDialog, argb ? MessageConfig::SubTypeArgb : MessageConfig::SubTypeRgb); + config.setTitle(title); + config.setDefaultValue(color); + return CoreApp::showDialog(config); +} + +void QtMvvm::getColor(QObject *scope, const std::function &onResult, const QString &title, const QColor &color, bool argb) +{ + auto result = getColor(title, color, argb); + if(result) { + QObject::connect(result, &MessageResult::dialogDone, + scope, [onResult, result](MessageConfig::StandardButton type) { + onResult(type == MessageConfig::Ok ? result->result().value() : QColor{}); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getColor(const std::function &onResult, const QString &title, const QColor &color, bool argb) +{ + getColor(CoreApp::instance(), onResult, title, color, argb); +} diff --git a/src/mvvmcore/message.h b/src/mvvmcore/message.h index a875296..c8bd982 100644 --- a/src/mvvmcore/message.h +++ b/src/mvvmcore/message.h @@ -9,6 +9,8 @@ #include #include +#include + #include "QtMvvmCore/qtmvvmcore_global.h" namespace QtMvvm { @@ -77,6 +79,7 @@ public: //! A type to show a generic file dialog static const QByteArray TypeFileDialog; //! @} + static const QByteArray TypeColorDialog; /** * @name Possible values for MessageConfig::subType when using the type MessageConfig::TypeMessageBox @@ -108,6 +111,10 @@ public: static const QByteArray SubTypeSaveFile; //! @} + + static const QByteArray SubTypeRgb; + static const QByteArray SubTypeArgb; + //! Default constructor, can take a type and a subtype MessageConfig(const QByteArray &type = TypeMessageBox, const QByteArray &subType = {}); //! Copy constructor @@ -448,6 +455,19 @@ Q_MVVMCORE_EXPORT void getSaveFile(const std::function &onResult, const QUrl &dir = {}); //! @} +Q_MVVMCORE_EXPORT MessageResult *getColor(const QString &title = {}, + const QColor &color = {}, + bool argb = false); +Q_MVVMCORE_EXPORT void getColor(QObject *scope, + const std::function &onResult, + const QString &title = {}, + const QColor &color = {}, + bool argb = false); +Q_MVVMCORE_EXPORT void getColor(const std::function &onResult, + const QString &title = {}, + const QColor &color = {}, + bool argb = false); + } Q_DECLARE_METATYPE(QtMvvm::MessageConfig) diff --git a/src/mvvmwidgets/coloredit.cpp b/src/mvvmwidgets/coloredit.cpp new file mode 100644 index 0000000..e526752 --- /dev/null +++ b/src/mvvmwidgets/coloredit.cpp @@ -0,0 +1,137 @@ +#include "coloredit_p.h" +#include +#include +#include +#include +#include +using namespace QtMvvm; + +ColorEdit::ColorEdit(QWidget *parent) : + QWidget{parent}, + _btn{new ColorButton{this}}, + _edit{new QLineEdit{this}}, + _validator{new QRegularExpressionValidator{this}} +{ + auto layout = new QHBoxLayout{this}; + layout->addWidget(_btn); + layout->addWidget(_edit); + layout->addStretch(1); + layout->setStretch(0, 0); + layout->setStretch(1, 0); + layout->setContentsMargins({0,0,0,0}); + setLayout(layout); + + _edit->setValidator(_validator); + connect(_edit, &QLineEdit::textEdited, + this, &ColorEdit::updateColor); + connect(_edit, &QLineEdit::editingFinished, + this, [this](){ + _edit->setText(_color.name(_alpha ? QColor::HexArgb : QColor::HexRgb)); + }); + + connect(_btn, &ColorButton::clicked, + this, &ColorEdit::showDialog); + + setAlpha(false); + setColor(palette().color(QPalette::Highlight)); +} + +QColor ColorEdit::color() const +{ + return _color; +} + +bool ColorEdit::useAlpha() const +{ + return _alpha; +} + +void ColorEdit::setColor(QColor color) +{ + if (_color == color) + return; + + _color = std::move(color); + _btn->setColor(_color); + if(!_skipEditUpdate) + _edit->setText(_color.name(_alpha ? QColor::HexArgb : QColor::HexRgb)); + emit colorChanged(_color); +} + +void ColorEdit::setAlpha(bool alpha) +{ + if (_alpha == alpha) + return; + + _alpha = alpha; + _validator->setRegularExpression(QRegularExpression{ + QStringLiteral(R"__(^#(?:[0-9a-f]{%1}){1,2}$)__").arg(_alpha ? 4 : 3), + QRegularExpression::CaseInsensitiveOption | QRegularExpression::DontCaptureOption + }); + _edit->setMaxLength(_alpha ? 9 : 7); + _edit->setText(_color.name(_alpha ? QColor::HexArgb : QColor::HexRgb)); + emit alphaChanged(_alpha); +} + +void ColorEdit::showDialog() +{ + QtMvvm::getColor(this, [this](const QColor &color) { + if(color.isValid()) + setColor(color); + }, tr("Select a color"), _color, _alpha); +} + +void ColorEdit::updateColor(const QString &text) +{ + if(_edit->hasAcceptableInput()) { + QColor color{text}; + if(color.isValid()) { + _skipEditUpdate = true; + setColor(color); + _skipEditUpdate = false; + } + } +} + + + +ColorButton::ColorButton(QWidget *parent) : + QAbstractButton{parent} +{ + setSizePolicy({QSizePolicy::Expanding, QSizePolicy::Preferred}); +} + +QColor ColorButton::color() const +{ + return _color; +} + +QSize ColorButton::sizeHint() const +{ + return {50, 25}; +} + +void ColorButton::setColor(QColor color) +{ + _color = color; + repaint(); +} + +void ColorButton::paintEvent(QPaintEvent *) +{ + QStylePainter painter{this}; + + painter.fillRect(painter.window(), _color); + + QStyleOptionFrame option; + option.initFrom(this); + option.frameShape = QFrame::Panel; + option.lineWidth = 2; + option.midLineWidth = 2; + if(isDown()) + option.state |= QStyle::State_Sunken; + else + option.state |= QStyle::State_Raised; + + painter.drawControl(QStyle::CE_ShapedFrame, option); +} diff --git a/src/mvvmwidgets/coloredit_p.h b/src/mvvmwidgets/coloredit_p.h new file mode 100644 index 0000000..a324959 --- /dev/null +++ b/src/mvvmwidgets/coloredit_p.h @@ -0,0 +1,74 @@ +#ifndef COLOREDIT_P_H +#define COLOREDIT_P_H + +#include +#include +#include +#include +#include + +#include "qtmvvmwidgets_global.h" + +namespace QtMvvm { + +class ColorButton : public QAbstractButton +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + explicit ColorButton(QWidget *parent = nullptr); + + QColor color() const; + + QSize sizeHint() const override; + +public Q_SLOTS: + void setColor(QColor color); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QColor _color; +}; + +class ColorEdit : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged USER true) + Q_PROPERTY(bool alpha READ useAlpha WRITE setAlpha NOTIFY alphaChanged) + +public: + explicit ColorEdit(QWidget *parent = nullptr); + + QColor color() const; + bool useAlpha() const; + +public Q_SLOTS: + void setColor(QColor color); + void setAlpha(bool alpha); + +Q_SIGNALS: + void colorChanged(const QColor &color); + void alphaChanged(bool alpha); + +private Q_SLOTS: + void showDialog(); + void updateColor(const QString &text); + +private: + QColor _color; + bool _alpha = true; //in order to be set to false in the constructor + + ColorButton *_btn; + QLineEdit *_edit; + QRegularExpressionValidator *_validator; + bool _skipEditUpdate = false; +}; + +} + +#endif // COLOREDIT_P_H diff --git a/src/mvvmwidgets/inputwidgetfactory.cpp b/src/mvvmwidgets/inputwidgetfactory.cpp index ae1de18..a97bb97 100644 --- a/src/mvvmwidgets/inputwidgetfactory.cpp +++ b/src/mvvmwidgets/inputwidgetfactory.cpp @@ -13,6 +13,7 @@ #include "fontcombobox_p.h" #include "selectcombobox_p.h" #include "tooltipslider_p.h" +#include "coloredit_p.h" #include @@ -62,7 +63,9 @@ QWidget *InputWidgetFactory::createInput(const QByteArray &type, QWidget *parent else if(type == QMetaType::typeName(QMetaType::QDateTime) || type == "date") { widget = new QDateTimeEdit(parent); static_cast(widget)->setCalendarPopup(true); - } else if(type == QMetaType::typeName(QMetaType::QFont)) + } else if(type == QMetaType::typeName(QMetaType::QColor) || type == "color") + widget = new ColorEdit(parent); + else if(type == QMetaType::typeName(QMetaType::QFont)) widget = new FontComboBox(parent); else if(type == QMetaType::typeName(QMetaType::QKeySequence)) widget = new QKeySequenceEdit(parent); diff --git a/src/mvvmwidgets/mvvmwidgets.pro b/src/mvvmwidgets/mvvmwidgets.pro index 371db97..2f56c3b 100644 --- a/src/mvvmwidgets/mvvmwidgets.pro +++ b/src/mvvmwidgets/mvvmwidgets.pro @@ -13,7 +13,8 @@ HEADERS += \ inputwidgetfactory_p.h \ settingsdialog_p.h \ settingsdialog.h \ - tooltipslider_p.h + tooltipslider_p.h \ + coloredit_p.h SOURCES += \ widgetspresenter.cpp \ @@ -21,7 +22,8 @@ SOURCES += \ selectcombobox.cpp \ inputwidgetfactory.cpp \ settingsdialog.cpp \ - tooltipslider.cpp + tooltipslider.cpp \ + coloredit.cpp FORMS += \ settingsdialog.ui diff --git a/src/mvvmwidgets/widgetspresenter.cpp b/src/mvvmwidgets/widgetspresenter.cpp index 9b22a91..bfa9bc9 100644 --- a/src/mvvmwidgets/widgetspresenter.cpp +++ b/src/mvvmwidgets/widgetspresenter.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -116,6 +117,8 @@ void WidgetsPresenter::showDialog(const MessageConfig &config, MessageResult *re presentInputDialog(config, result); else if(config.type() == MessageConfig::TypeFileDialog) presentFileDialog(config, result); + else if(config.type() == MessageConfig::TypeColorDialog) + presentColorDialog(config, result); else presentOtherDialog(config, result); logDebug() << "Presented dialog of type" << config.type(); @@ -294,6 +297,7 @@ void WidgetsPresenter::presentMessageBox(const MessageConfig &config, QPointersetAttribute(Qt::WA_DeleteOnClose); connect(msgBox, &QMessageBox::finished, result, [msgBox, qtHelp, checked, result](){ int sBtn = msgBox->standardButton(msgBox->clickedButton()); @@ -375,6 +379,7 @@ void WidgetsPresenter::presentFileDialog(const MessageConfig &config, QPointersetAttribute(Qt::WA_DeleteOnClose); //prepare the dialog auto title = config.title(); @@ -426,6 +431,52 @@ void WidgetsPresenter::presentFileDialog(const MessageConfig &config, QPointeropen(); } +void WidgetsPresenter::presentColorDialog(const MessageConfig &config, QPointer result) +{ + auto props = config.viewProperties(); + + QWidget *parent = nullptr; + if(!props.value(QStringLiteral("modal"), false).toBool()) + parent = QApplication::activeWindow(); + auto dialog = new QColorDialog{parent}; + dialog->setAttribute(Qt::WA_DeleteOnClose); + + //prepare the dialog + auto title = config.title(); + if(!title.isNull()) + dialog->setWindowTitle(title); + auto color = config.defaultValue().value(); + if(color.isValid()) + dialog->setCurrentColor(color); + + //set the color mode + if(config.subType() == MessageConfig::SubTypeArgb) + dialog->setOptions(QColorDialog::ShowAlphaChannel); + else if(config.subType() == MessageConfig::SubTypeRgb) + dialog->setOptions(nullptr); + + //set extra props + for(auto it = props.constBegin(); it != props.constEnd(); it++) + dialog->setProperty(qUtf8Printable(it.key()), it.value()); + + //connect stuff + QObject::connect(dialog, &QDialog::finished, + dialog, [dialog, result](int resCode){ + if(result) { + if(resCode == QDialog::Accepted) { + result->setResult(dialog->currentColor()); + result->complete(MessageConfig::Ok); + } else + result->complete(MessageConfig::Cancel); + } + }); + + //finalize and show + dialog->adjustSize(); + DialogMaster::masterDialog(dialog); + dialog->open(); +} + void WidgetsPresenter::presentOtherDialog(const MessageConfig &config, QPointer result) { Q_UNUSED(result) diff --git a/src/mvvmwidgets/widgetspresenter.h b/src/mvvmwidgets/widgetspresenter.h index 052233f..82c9750 100644 --- a/src/mvvmwidgets/widgetspresenter.h +++ b/src/mvvmwidgets/widgetspresenter.h @@ -77,6 +77,7 @@ protected: virtual void presentInputDialog(const MessageConfig &config, QPointer result); //! Called to present a dialog of MessageConfig::TypeFileDialog virtual void presentFileDialog(const MessageConfig &config, QPointer result); + void presentColorDialog(const MessageConfig &config, QPointer result); //MAJOR make virtual //! Called to present a dialog of a non standard MessageConfig::type virtual void presentOtherDialog(const MessageConfig &config, QPointer result);