From ab5a0c9cb18a69bf0b014807d9a03193feeedcb4 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 7 Apr 2020 19:17:33 +0200 Subject: [PATCH 01/28] QDS IconFont to Icons importer Change-Id: Idae58499cf3a3e3967e701597b0f00c16367c602 Reviewed-by: Thomas Hartmann --- src/libs/utils/stylehelper.cpp | 28 ++++++++++++++++++++++++++++ src/libs/utils/stylehelper.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index f91c24b52ac..28d07392271 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include // Clamps float color values within (0, 255) @@ -542,6 +543,33 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect) return grad; } +QPixmap StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize) +{ + QFontDatabase a; + + Q_ASSERT(a.hasFamily(fontName)); + + if (a.hasFamily(fontName)) { + QPixmap icon(iconSize, iconSize); + icon.fill(Qt::transparent); + QPainter painter(&icon); + QFont font(fontName); + font.setPixelSize(fontSize); + QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText); + + painter.save(); + painter.setPen(penColor); + painter.setFont(font); + painter.drawText(QRectF(0, 0, iconSize, iconSize), iconSymbol); + + painter.restore(); + + return icon; + } + + return {}; +} + QString StyleHelper::dpiSpecificImageFile(const QString &fileName) { // See QIcon::addFile() diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 4bcec960ad5..e9261b7c8bb 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -93,6 +93,8 @@ public: static void tintImage(QImage &img, const QColor &tintColor); static QLinearGradient statusBarGradient(const QRect &statusBarRect); + static QPixmap getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize); + static QString dpiSpecificImageFile(const QString &fileName); static QString imageFileWithResolution(const QString &fileName, int dpr); static QList availableImageResolutions(const QString &fileName); From fd62e363325f59e448df435c735f1faffae8f993 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Tue, 7 Apr 2020 18:06:35 +0200 Subject: [PATCH 02/28] QmlDesigner: RichTextEditor for Annotation Change-Id: Ia49f9e77ef5f98aec598673a9e8c09f19205a1c8 Task: QDS-1814 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 7 + .../richtexteditor/hyperlinkdialog.cpp | 69 ++ .../richtexteditor/hyperlinkdialog.h | 54 ++ .../richtexteditor/hyperlinkdialog.ui | 88 +++ .../richtexteditor/richtexteditor.cpp | 684 ++++++++++++++++++ .../richtexteditor/richtexteditor.h | 128 ++++ .../richtexteditor/richtexteditor.pri | 8 + .../richtexteditor/richtexteditor.ui | 50 ++ src/plugins/qmldesigner/qmldesignerplugin.pro | 1 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 6 + 10 files changed, 1095 insertions(+) create mode 100644 src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp create mode 100644 src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h create mode 100644 src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui create mode 100644 src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp create mode 100644 src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h create mode 100644 src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri create mode 100644 src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 85e0c67ec9e..f28f5733ec5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -588,6 +588,13 @@ extend_qtc_plugin(QmlDesigner texttool/texttool.cpp texttool/texttool.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/richtexteditor + SOURCES + hyperlinkdialog.cpp hyperlinkdialog.h hyperlinkdialog.ui + richtexteditor.cpp richtexteditor.h hyperlinkdialog.ui +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/timelineeditor SOURCES diff --git a/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp new file mode 100644 index 00000000000..82d24f6839e --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "hyperlinkdialog.h" +#include "ui_hyperlinkdialog.h" + +#include + +namespace QmlDesigner { + + +HyperlinkDialog::HyperlinkDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::HyperlinkDialog) +{ + ui->setupUi(this); + connect (ui->linkEdit, &QLineEdit::textChanged, [this] () { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!(ui->linkEdit->text().isEmpty())); + }); +} + +HyperlinkDialog::~HyperlinkDialog() +{ + delete ui; +} + +QString HyperlinkDialog::getLink() const +{ + return ui->linkEdit->text().trimmed(); +} + +void HyperlinkDialog::setLink(const QString &link) +{ + ui->linkEdit->setText(link); +} + +QString HyperlinkDialog::getAnchor() const +{ + return ui->anchorEdit->text().trimmed(); +} + +void HyperlinkDialog::setAnchor(const QString &anchor) +{ + ui->anchorEdit->setText(anchor); +} + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h new file mode 100644 index 00000000000..dcf8759e492 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace QmlDesigner { + +namespace Ui { +class HyperlinkDialog; +} + +class HyperlinkDialog : public QDialog +{ + Q_OBJECT + +public: + explicit HyperlinkDialog(QWidget *parent = nullptr); + ~HyperlinkDialog(); + + QString getLink() const; + void setLink(const QString &link); + + QString getAnchor() const; + void setAnchor(const QString &anchor); + +private: + Ui::HyperlinkDialog *ui; +}; + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui new file mode 100644 index 00000000000..7906a1fccb1 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/hyperlinkdialog.ui @@ -0,0 +1,88 @@ + + + QmlDesigner::HyperlinkDialog + + + + 0 + 0 + 403 + 156 + + + + Dialog + + + + + + + + Link + + + + + + + + + + Anchor + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QmlDesigner::HyperlinkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QmlDesigner::HyperlinkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp new file mode 100644 index 00000000000..66d8ca70d0d --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp @@ -0,0 +1,684 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "richtexteditor.h" +#include "ui_richtexteditor.h" +#include "hyperlinkdialog.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +template +class FontWidgetActions : public QWidgetAction { +public: + FontWidgetActions(QObject *parent = nullptr) + : QWidgetAction(parent) {} + + ~FontWidgetActions () override {} + + void setInitializer(std::function func) + { + m_initializer = func; + } + + QList createdWidgets() + { + return QWidgetAction::createdWidgets(); + } + +protected: + QWidget *createWidget(QWidget *parent) override + { + T *w = new T(parent); + if (m_initializer) + m_initializer(w); + return w; + } + + void deleteWidget(QWidget *widget) override + { + widget->deleteLater(); + } + +private: + std::function m_initializer; +}; + +static void cursorEditBlock(QTextCursor& cursor, std::function f) { + cursor.beginEditBlock(); + f(); + cursor.endEditBlock(); +} + +RichTextEditor::RichTextEditor(QWidget *parent) + : QWidget(parent) + , ui(new Ui::RichTextEditor) + , m_linkDialog(new HyperlinkDialog(this)) +{ + ui->setupUi(this); + ui->textEdit->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse); + ui->tableBar->setVisible(false); + + setupEditActions(); + setupTextActions(); + setupHyperlinkActions(); + setupAlignActions(); + setupListActions(); + setupFontActions(); + setupTableActions(); + + connect(ui->textEdit, &QTextEdit::currentCharFormatChanged, + this, &RichTextEditor::currentCharFormatChanged); + connect(ui->textEdit, &QTextEdit::cursorPositionChanged, + this, &RichTextEditor::cursorPositionChanged); + connect(m_linkDialog, &QDialog::accepted, [this]() { + QTextCharFormat oldFormat = ui->textEdit->textCursor().charFormat(); + + QTextCursor tcursor = ui->textEdit->textCursor(); + QTextCharFormat charFormat = tcursor.charFormat(); + + charFormat.setForeground(QApplication::palette().color(QPalette::Link)); + charFormat.setFontUnderline(true); + + QString link = m_linkDialog->getLink(); + QString anchor = m_linkDialog->getAnchor(); + + if (anchor.isEmpty()) + anchor = link; + + charFormat.setAnchor(true); + charFormat.setAnchorHref(link); + charFormat.setAnchorNames(QStringList(anchor)); + + tcursor.insertText(anchor, charFormat); + + tcursor.insertText(" ", oldFormat); + + m_linkDialog->hide(); + }); + + ui->textEdit->setFocus(); + m_linkDialog->hide(); +} + +RichTextEditor::~RichTextEditor() +{ +} + +void RichTextEditor::setPlainText(const QString &text) +{ + ui->textEdit->setPlainText(text); +} + +QString RichTextEditor::plainText() const +{ + return ui->textEdit->toPlainText(); +} + +void RichTextEditor::setRichText(const QString &text) +{ + ui->textEdit->setHtml(text); +} + +void RichTextEditor::setTabChangesFocus(bool change) +{ + ui->textEdit->setTabChangesFocus(change); +} + +QPixmap RichTextEditor::getIcon(const QString &iconName) +{ + const QString fontName = "qtds_propertyIconFont.ttf"; + + return Utils::StyleHelper::getIconFromIconFont(fontName, iconName, 36, 36); +} + +QString RichTextEditor::richText() const +{ + return ui->textEdit->toHtml(); +} + +void RichTextEditor::currentCharFormatChanged(const QTextCharFormat &format) +{ + fontChanged(format.font()); + colorChanged(format.foreground().color()); +} + +void RichTextEditor::cursorPositionChanged() +{ + alignmentChanged(ui->textEdit->alignment()); + styleChanged(ui->textEdit->textCursor()); + tableChanged(ui->textEdit->textCursor()); +} + +void RichTextEditor::mergeFormatOnWordOrSelection(const QTextCharFormat &format) +{ + QTextCursor cursor = ui->textEdit->textCursor(); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); + ui->textEdit->mergeCurrentCharFormat(format); +} + +void RichTextEditor::fontChanged(const QFont &f) +{ + for (QWidget* w: m_fontNameAction->createdWidgets() ) { + QFontComboBox* box = qobject_cast(w); + if (box) + box->setCurrentFont(f); + } + for (QWidget* w: m_fontSizeAction->createdWidgets() ) { + QComboBox* box = qobject_cast(w); + if (box) + box->setCurrentText(QString::number(f.pointSize())); + } + + m_actionTextBold->setChecked(f.bold()); + m_actionTextItalic->setChecked(f.italic()); + m_actionTextUnderline->setChecked(f.underline()); +} + +void RichTextEditor::colorChanged(const QColor &c) +{ + QPixmap colorBox(ui->tableBar->iconSize()); + colorBox.fill(c); + m_actionTextColor->setIcon(colorBox); +} + +void RichTextEditor::alignmentChanged(Qt::Alignment a) +{ + if (a & Qt::AlignLeft) + m_actionAlignLeft->setChecked(true); + else if (a & Qt::AlignHCenter) + m_actionAlignCenter->setChecked(true); + else if (a & Qt::AlignRight) + m_actionAlignRight->setChecked(true); + else if (a & Qt::AlignJustify) + m_actionAlignJustify->setChecked(true); +} + +void RichTextEditor::styleChanged(const QTextCursor &cursor) +{ + if (!m_actionBulletList || !m_actionNumberedList) return; + + QTextList *currentList = cursor.currentList(); + + if (currentList) { + if (currentList->format().style() == QTextListFormat::ListDisc) { + m_actionBulletList->setChecked(true); + m_actionNumberedList->setChecked(false); + } + else if (currentList->format().style() == QTextListFormat::ListDecimal) { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(true); + } + else { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(false); + } + } + else { + m_actionBulletList->setChecked(false); + m_actionNumberedList->setChecked(false); + } +} + +void RichTextEditor::tableChanged(const QTextCursor &cursor) +{ + if (!m_actionTableSettings) return; + + QTextTable *currentTable = cursor.currentTable(); + + if (currentTable) { + m_actionTableSettings->setChecked(true); + ui->tableBar->setVisible(true); + + setTableActionsActive(true); + } + else { + setTableActionsActive(false); + } +} + +void RichTextEditor::setupEditActions() +{ + const QIcon undoIcon(getIcon("\u005F")); + QAction *actionUndo = ui->toolBar->addAction(undoIcon, tr("&Undo"), ui->textEdit, &QTextEdit::undo); + actionUndo->setShortcut(QKeySequence::Undo); + connect(ui->textEdit->document(), &QTextDocument::undoAvailable, + actionUndo, &QAction::setEnabled); + + const QIcon redoIcon(getIcon("\u0050")); + QAction *actionRedo = ui->toolBar->addAction(redoIcon, tr("&Redo"), ui->textEdit, &QTextEdit::redo); + actionRedo->setShortcut(QKeySequence::Redo); + connect(ui->textEdit->document(), &QTextDocument::redoAvailable, + actionRedo, &QAction::setEnabled); + + actionUndo->setEnabled(ui->textEdit->document()->isUndoAvailable()); + actionRedo->setEnabled(ui->textEdit->document()->isRedoAvailable()); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupTextActions() +{ + const QIcon boldIcon(getIcon("\u004B")); + m_actionTextBold = ui->toolBar->addAction(boldIcon, tr("&Bold"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextBold->setShortcut(Qt::CTRL + Qt::Key_B); + QFont bold; + bold.setBold(true); + m_actionTextBold->setFont(bold); + m_actionTextBold->setCheckable(true); + + const QIcon italicIcon(getIcon("\u004C")); + m_actionTextItalic = ui->toolBar->addAction(italicIcon, tr("&Italic"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontItalic(checked); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextItalic->setShortcut(Qt::CTRL + Qt::Key_I); + QFont italic; + italic.setItalic(true); + m_actionTextItalic->setFont(italic); + m_actionTextItalic->setCheckable(true); + + const QIcon underlineIcon(getIcon("\u004E")); + m_actionTextUnderline = ui->toolBar->addAction(underlineIcon, tr("&Underline"), + [this](bool checked) { + QTextCharFormat fmt; + fmt.setFontUnderline(checked); + mergeFormatOnWordOrSelection(fmt); + }); + m_actionTextUnderline->setShortcut(Qt::CTRL + Qt::Key_U); + QFont underline; + underline.setUnderline(true); + m_actionTextUnderline->setFont(underline); + m_actionTextUnderline->setCheckable(true); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupHyperlinkActions() +{ + const QIcon bulletIcon(getIcon("\u0022")); + m_actionHyperlink = ui->toolBar->addAction(bulletIcon, tr("Hyperlink Settings"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + QTextCharFormat linkFormat = cursor.charFormat(); + if (linkFormat.isAnchor()) { + m_linkDialog->setLink(linkFormat.anchorHref()); + m_linkDialog->setAnchor(linkFormat.anchorName()); + } + else { + m_linkDialog->setLink("http://"); + m_linkDialog->setAnchor(""); + } + + m_linkDialog->show(); + }); + m_actionHyperlink->setCheckable(false); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupAlignActions() +{ + const QIcon leftIcon(getIcon("\u0056")); + m_actionAlignLeft = ui->toolBar->addAction(leftIcon, tr("&Left"), [this]() { ui->textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); }); + m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); + m_actionAlignLeft->setCheckable(true); + m_actionAlignLeft->setPriority(QAction::LowPriority); + + const QIcon centerIcon(getIcon("\u0055")); + m_actionAlignCenter = ui->toolBar->addAction(centerIcon, tr("C&enter"), [this]() { ui->textEdit->setAlignment(Qt::AlignHCenter); }); + m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); + m_actionAlignCenter->setCheckable(true); + m_actionAlignCenter->setPriority(QAction::LowPriority); + + const QIcon rightIcon(getIcon("\u0058")); + m_actionAlignRight = ui->toolBar->addAction(rightIcon, tr("&Right"), [this]() { ui->textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); }); + m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); + m_actionAlignRight->setCheckable(true); + m_actionAlignRight->setPriority(QAction::LowPriority); + + const QIcon fillIcon(getIcon("\u005B")); + m_actionAlignJustify = ui->toolBar->addAction(fillIcon, tr("&Justify"), [this]() { ui->textEdit->setAlignment(Qt::AlignJustify); }); + m_actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); + m_actionAlignJustify->setCheckable(true); + m_actionAlignJustify->setPriority(QAction::LowPriority); + + // Make sure the alignLeft is always left of the alignRight + QActionGroup *alignGroup = new QActionGroup(ui->toolBar); + + if (QApplication::isLeftToRight()) { + alignGroup->addAction(m_actionAlignLeft); + alignGroup->addAction(m_actionAlignCenter); + alignGroup->addAction(m_actionAlignRight); + } else { + alignGroup->addAction(m_actionAlignRight); + alignGroup->addAction(m_actionAlignCenter); + alignGroup->addAction(m_actionAlignLeft); + } + alignGroup->addAction(m_actionAlignJustify); + + ui->toolBar->addActions(alignGroup->actions()); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupListActions() +{ + const QIcon bulletIcon(getIcon("\u005A")); + m_actionBulletList = ui->toolBar->addAction(bulletIcon, tr("Bullet List"), [this](bool checked) { + if (checked) { + m_actionNumberedList->setChecked(false); + textStyle(QTextListFormat::ListDisc); + } + else if (!m_actionNumberedList->isChecked()) { + textStyle(QTextListFormat::ListStyleUndefined); + } + }); + m_actionBulletList->setCheckable(true); + + const QIcon numberedIcon(getIcon("\u005C")); + m_actionNumberedList = ui->toolBar->addAction(numberedIcon, tr("Numbered List"), [this](bool checked) { + if (checked) { + m_actionBulletList->setChecked(false); + textStyle(QTextListFormat::ListDecimal); + } + else if (!m_actionBulletList->isChecked()) { + textStyle(QTextListFormat::ListStyleUndefined); + } + }); + m_actionNumberedList->setCheckable(true); + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupFontActions() +{ + QPixmap colorBox(ui->tableBar->iconSize()); + colorBox.fill(ui->textEdit->textColor()); + + m_actionTextColor = ui->toolBar->addAction(colorBox, tr("&Color..."), [this]() { + QColor col = QColorDialog::getColor(ui->textEdit->textColor(), this); + if (!col.isValid()) + return; + QTextCharFormat fmt; + fmt.setForeground(col); + mergeFormatOnWordOrSelection(fmt); + colorChanged(col); + }); + + m_fontNameAction = new FontWidgetActions(this); + m_fontNameAction->setInitializer([this](QFontComboBox *w) { + if (!w) return; + + w->setCurrentIndex(w->findText(ui->textEdit->currentCharFormat().font().family())); + connect(w, QOverload::of(&QComboBox::activated), [this](const QString &f) { + QTextCharFormat fmt; + fmt.setFontFamily(f); + mergeFormatOnWordOrSelection(fmt); + }); + }); + + m_fontNameAction->setDefaultWidget(new QFontComboBox); + ui->toolBar->addAction(m_fontNameAction); + + m_fontSizeAction = new FontWidgetActions(this); + m_fontSizeAction->setInitializer([this](QComboBox *w) { + if (!w) return; + + w->setEditable(true); + + const QList standardSizes = QFontDatabase::standardSizes(); + foreach (int size, standardSizes) + w->addItem(QString::number(size)); + w->setCurrentText(QString::number(ui->textEdit->currentCharFormat().font().pointSize())); + connect(w, QOverload::of(&QComboBox::activated), [this](const QString &p) { + qreal pointSize = p.toDouble(); + if (pointSize > 0.0) { + QTextCharFormat fmt; + fmt.setFontPointSize(pointSize); + mergeFormatOnWordOrSelection(fmt); + } + }); + }); + + m_fontSizeAction->setDefaultWidget(new QComboBox); + ui->toolBar->addAction(m_fontSizeAction); + + + ui->toolBar->addSeparator(); +} + +void RichTextEditor::setupTableActions() +{ + const QIcon tableIcon(getIcon("\u0028")); + m_actionTableSettings = ui->toolBar->addAction(tableIcon, tr("&Table Settings"), [this](bool checked) { + ui->tableBar->setVisible(checked); + }); + m_actionTableSettings->setShortcut(Qt::CTRL + Qt::Key_T); + m_actionTableSettings->setCheckable(true); + m_actionTableSettings->setPriority(QAction::LowPriority); + +//table bar: + + const QIcon createTableIcon(getIcon("\u0028")); + m_actionCreateTable = ui->tableBar->addAction(createTableIcon, tr("Create Table"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + cursorEditBlock(cursor, [&] () { + cursor.insertTable(1,1); + }); + }); + m_actionCreateTable->setCheckable(false); + + const QIcon removeTableIcon(getIcon("\u003D")); + m_actionRemoveTable = ui->tableBar->addAction(removeTableIcon, tr("Remove Table"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->removeRows(0, currentTable->rows()); + }); + } + }); + m_actionRemoveTable->setCheckable(false); + + ui->tableBar->addSeparator(); + + const QIcon addRowIcon(getIcon("\u0026")); //addRowAfter + m_actionAddRow = ui->tableBar->addAction(addRowIcon, tr("Add Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertRows(currentTable->cellAt(cursor).row()+1, 1); + }); + } + }); + m_actionAddRow->setCheckable(false); + + const QIcon addColumnIcon(getIcon("\u0023")); //addColumnAfter + m_actionAddColumn = ui->tableBar->addAction(addColumnIcon, tr("Add Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertColumns(currentTable->cellAt(cursor).column()+1, 1); + }); + } + }); + m_actionAddColumn->setCheckable(false); + + const QIcon removeRowIcon(getIcon("\u003C")); + m_actionRemoveRow = ui->tableBar->addAction(removeRowIcon, tr("Remove Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->insertColumns(currentTable->cellAt(cursor).column()+1, 1); + + int firstRow = 0; + int numRows = 0; + int firstColumn = 0; + int numColumns = 0; + + if (cursor.hasSelection()) + cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); + + if (numRows < 1) + currentTable->removeRows(currentTable->cellAt(cursor).row(), 1); + else + currentTable->removeRows(firstRow, numRows); + }); + } + }); + m_actionRemoveRow->setCheckable(false); + + const QIcon removeColumnIcon(getIcon("\u003B")); + m_actionRemoveColumn = ui->tableBar->addAction(removeColumnIcon, tr("Remove Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + int firstRow = 0; + int numRows = 0; + int firstColumn = 0; + int numColumns = 0; + + if (cursor.hasSelection()) + cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); + + if (numColumns < 1) + currentTable->removeColumns(currentTable->cellAt(cursor).column(), 1); + else + currentTable->removeColumns(firstColumn, numColumns); + }); + } + }); + m_actionRemoveColumn->setCheckable(false); + + ui->tableBar->addSeparator(); + + const QIcon mergeCellsIcon(getIcon("\u004F")); + m_actionMergeCells = ui->tableBar->addAction(mergeCellsIcon, tr("Merge Cells"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + if (cursor.hasSelection()) { + cursorEditBlock(cursor, [&] () { + currentTable->mergeCells(cursor); + }); + } + } + }); + m_actionMergeCells->setCheckable(false); + + const QIcon splitRowIcon(getIcon("\u0052")); + m_actionSplitRow = ui->tableBar->addAction(splitRowIcon, tr("Split Row"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->splitCell(currentTable->cellAt(cursor).row(), + currentTable->cellAt(cursor).column(), + 2, 1); + }); + } + }); + m_actionSplitRow->setCheckable(false); + + const QIcon splitColumnIcon(getIcon("\u0051")); + m_actionSplitColumn = ui->tableBar->addAction(splitRowIcon, tr("Split Column"), [this]() { + QTextCursor cursor = ui->textEdit->textCursor(); + if (QTextTable *currentTable = cursor.currentTable()) { + cursorEditBlock(cursor, [&] () { + currentTable->splitCell(currentTable->cellAt(cursor).row(), + currentTable->cellAt(cursor).column(), + 1, 2); + }); + } + }); + m_actionSplitColumn->setCheckable(false); +} + +void RichTextEditor::textStyle(QTextListFormat::Style style) +{ + QTextCursor cursor = ui->textEdit->textCursor(); + cursorEditBlock(cursor, [&] () { + if (style != QTextListFormat::ListStyleUndefined) { + QTextBlockFormat blockFmt = cursor.blockFormat(); + QTextListFormat listFmt; + + if (cursor.currentList()) { + listFmt = cursor.currentList()->format(); + } else { + listFmt.setIndent(blockFmt.indent() + 1); + blockFmt.setIndent(0); + cursor.setBlockFormat(blockFmt); + } + + listFmt.setStyle(style); + + cursor.createList(listFmt); + } else { + QTextList* currentList = cursor.currentList(); + QTextBlock currentBlock = cursor.block(); + currentList->remove(currentBlock); + + QTextBlockFormat blockFormat = cursor.blockFormat(); + blockFormat.setIndent(0); + cursor.setBlockFormat(blockFormat); + } + }); +} + +void RichTextEditor::setTableActionsActive(bool active) +{ + m_actionCreateTable->setEnabled(!active); + m_actionRemoveTable->setEnabled(active); + + m_actionAddRow->setEnabled(active); + m_actionAddColumn->setEnabled(active); + m_actionRemoveRow->setEnabled(active); + m_actionRemoveColumn->setEnabled(active); + + m_actionMergeCells->setEnabled(active); + m_actionSplitRow->setEnabled(active); + m_actionSplitColumn->setEnabled(active); +} + +} diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h new file mode 100644 index 00000000000..3b9ff7864ca --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +namespace Ui { +class RichTextEditor; +} + +template +class FontWidgetActions; + +class HyperlinkDialog; + +class RichTextEditor : public QWidget +{ + Q_OBJECT + +public: + explicit RichTextEditor(QWidget *parent = nullptr); + ~RichTextEditor(); + + void setPlainText(const QString &text); + QString plainText() const; + + void setRichText(const QString &text); + QString richText() const; + + void setTabChangesFocus(bool change); + +private slots: + void currentCharFormatChanged(const QTextCharFormat &format); + void cursorPositionChanged(); + +private: + QPixmap getIcon(const QString &iconName); + void mergeFormatOnWordOrSelection(const QTextCharFormat &format); + + void fontChanged(const QFont &f); + void colorChanged(const QColor &c); + void alignmentChanged(Qt::Alignment a); + void styleChanged(const QTextCursor &cursor); + void tableChanged(const QTextCursor &cursor); + + void setupEditActions(); + void setupTextActions(); + void setupHyperlinkActions(); + void setupAlignActions(); + void setupListActions(); + void setupFontActions(); + void setupTableActions(); + + void textStyle(QTextListFormat::Style style); + + void setTableActionsActive(bool active); //switches between "has table/has no table" ui setup + +private: + QScopedPointer ui; + QPointer m_linkDialog; + + QAction *m_actionTextBold; + QAction *m_actionTextItalic; + QAction *m_actionTextUnderline; + + QAction *m_actionHyperlink; + + QAction *m_actionAlignLeft; + QAction *m_actionAlignCenter; + QAction *m_actionAlignRight; + QAction *m_actionAlignJustify; + + QAction *m_actionTextColor; + + QAction *m_actionBulletList; + QAction *m_actionNumberedList; + + QAction *m_actionTableSettings; + + QAction *m_actionCreateTable; + QAction *m_actionRemoveTable; + + QAction *m_actionAddRow; + QAction *m_actionAddColumn; + QAction *m_actionRemoveRow; + QAction *m_actionRemoveColumn; + + QAction *m_actionMergeCells; + QAction *m_actionSplitRow; + QAction *m_actionSplitColumn; + + QPointer> m_fontNameAction; + QPointer> m_fontSizeAction; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri new file mode 100644 index 00000000000..68b6dbe0268 --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri @@ -0,0 +1,8 @@ +HEADERS += $$PWD/richtexteditor.h +HEADERS += $$PWD/hyperlinkdialog.h + +SOURCES += $$PWD/richtexteditor.cpp +SOURCES += $$PWD/hyperlinkdialog.cpp + +FORMS += $$PWD/richtexteditor.ui +FORMS += $$PWD/hyperlinkdialog.ui diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui new file mode 100644 index 00000000000..25a7b5d244a --- /dev/null +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui @@ -0,0 +1,50 @@ + + + QmlDesigner::RichTextEditor + + + + 0 + 0 + 428 + 283 + + + + + 0 + 5 + + + + Form + + + + + + + 36 + 36 + + + + + + + + + 36 + 36 + + + + + + + + + + + + diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index ea017e3ceff..0095aa8f10b 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -31,6 +31,7 @@ include(components/connectioneditor/connectioneditor.pri) include(components/curveeditor/curveeditor.pri) include(components/bindingeditor/bindingeditor.pri) include(components/annotationeditor/annotationeditor.pri) +include(components/richtexteditor/richtexteditor.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 3c434788cd9..63fa8a07432 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -746,6 +746,12 @@ Project { "pathtool/pathtool.h", "pathtool/pathtoolview.cpp", "pathtool/pathtoolview.h", + "richtexteditor/hyperlinkdialog.cpp", + "richtexteditor/hyperlinkdialog.h", + "richtexteditor/hyperlinkdialog.ui", + "richtexteditor/richtexteditor.cpp", + "richtexteditor/richtexteditor.h", + "richtexteditor/richtexteditor.ui", "sourcetool/sourcetool.cpp", "sourcetool/sourcetool.h", "texttool/textedititem.cpp", From c2de17b2800b4a8b89686774f99e4fbcf7a80f4e Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 8 Apr 2020 18:21:57 +0200 Subject: [PATCH 03/28] QmlDesigner: Rich Text Editor integration Change-Id: I36403a596b1d4c8241663a4b0e5b1d0e2f9cb45e Reviewed-by: Tim Jenssen --- .../components/annotationeditor/annotationcommenttab.cpp | 9 +++++++-- .../components/annotationeditor/annotationcommenttab.h | 3 +++ .../components/annotationeditor/annotationcommenttab.ui | 8 -------- .../annotationeditor/annotationeditordialog.ui | 4 ++-- .../components/formeditor/formeditorannotationicon.cpp | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp index bd7aed67d02..bfe18e952b8 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp @@ -26,6 +26,8 @@ #include "annotationcommenttab.h" #include "ui_annotationcommenttab.h" +#include "richtexteditor/richtexteditor.h" + namespace QmlDesigner { AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) : @@ -34,6 +36,9 @@ AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) : { ui->setupUi(this); + m_editor = new RichTextEditor; + ui->formLayout->setWidget(3, QFormLayout::FieldRole, m_editor); + connect(ui->titleEdit, &QLineEdit::textEdited, this, &AnnotationCommentTab::commentTitleChanged); } @@ -49,7 +54,7 @@ Comment AnnotationCommentTab::currentComment() const result.setTitle(ui->titleEdit->text().trimmed()); result.setAuthor(ui->authorEdit->text().trimmed()); - result.setText(ui->textEdit->toPlainText().trimmed()); + result.setText(m_editor->richText().trimmed()); if (m_comment.sameContent(result)) result.setTimestamp(m_comment.timestamp()); @@ -74,7 +79,7 @@ void AnnotationCommentTab::resetUI() { ui->titleEdit->setText(m_comment.title()); ui->authorEdit->setText(m_comment.author()); - ui->textEdit->setText(m_comment.text()); + m_editor->setRichText(m_comment.text()); if (m_comment.timestamp() > 0) ui->timeLabel->setText(m_comment.timestampStr()); diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h index cc8d4c3d769..55fcf6ff1ed 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h @@ -35,6 +35,8 @@ namespace Ui { class AnnotationCommentTab; } +class RichTextEditor; + class AnnotationCommentTab : public QWidget { Q_OBJECT @@ -59,6 +61,7 @@ private slots: private: Ui::AnnotationCommentTab *ui; + RichTextEditor *m_editor; Comment m_comment; }; diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui index f6bf277eb7c..4a69703d57d 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui @@ -33,13 +33,6 @@ - - - - true - - - @@ -64,7 +57,6 @@ titleEdit authorEdit - textEdit diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui index 8ca9b75136c..93ce92b0450 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui @@ -6,8 +6,8 @@ 0 0 - 700 - 487 + 1100 + 600 diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp index 5fa24f1a5c0..81072179f36 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp @@ -339,7 +339,7 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const authorItem->update(); QGraphicsTextItem *textItem = new QGraphicsTextItem(frameItem); - textItem->setPlainText(text); + textItem->setHtml(text); textItem->setDefaultTextColor(textColor); textItem->setTextWidth(rect.width()); textItem->setPos(authorItem->x(), authorItem->boundingRect().height() + authorItem->y() + 5); From a35a9e72d9ccb4e7769cccf008575921374e4ee9 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 20 Apr 2020 09:34:38 +0200 Subject: [PATCH 04/28] QmlDesigner: Extend icon font support * Use ADS::IconProvider to make use of font icons * Add support for IconProvider in FloatingWidgetTitlebar * Add support for named font icon entities in QmlDesigner::Theme * Add level of indirection to Constants.qml to be able to parse it from inside C++ * Add color and HighDPI support in stylehelper * Update RichTextEditor * Update icon font and related constants Task-number: QDS-1913 Change-Id: I7212f4dfe480dbb340fd042f59c69b8f5ca7a8e4 Reviewed-by: Aleksei German Reviewed-by: Thomas Hartmann --- .../imports/StudioTheme/Constants.qml | 96 +------------ .../imports/StudioTheme/InternalConstants.qml | 131 ++++++++++++++++++ .../imports/StudioTheme/icons.ttf | Bin 10568 -> 11448 bytes .../imports/StudioTheme/qmldir | 2 + src/libs/advanceddockingsystem/ads_globals.h | 9 +- .../dockareatitlebar.cpp | 5 +- .../advanceddockingsystem/dockwidgettab.cpp | 16 ++- .../advanceddockingsystem/iconprovider.cpp | 2 +- .../linux/floatingwidgettitlebar.cpp | 24 ++-- src/libs/utils/stylehelper.cpp | 43 ++++-- src/libs/utils/stylehelper.h | 3 +- .../components/componentcore/theme.cpp | 46 ++++++ .../components/componentcore/theme.h | 82 +++++++++++ .../richtexteditor/richtexteditor.cpp | 48 +++---- .../richtexteditor/richtexteditor.h | 4 +- src/plugins/qmldesigner/designmodewidget.cpp | 22 +++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 5 + 17 files changed, 380 insertions(+), 158 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml index 3d692af671c..74edf9ccee8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml @@ -26,98 +26,4 @@ pragma Singleton import QtQuick 2.10 -QtObject { - readonly property int width: 1920 - readonly property int height: 1080 - readonly property FontLoader mySystemFont: FontLoader { - name: "Arial" - } - readonly property FontLoader controlIcons: FontLoader { - source: "icons.ttf" - } - - readonly property string actionIcon: "\u0021" - readonly property string actionIconBinding: "\u0022" - readonly property string addColumnAfter: "\u0023" - readonly property string addColumnBefore: "\u0024" - readonly property string addFile: "\u0025" - readonly property string addRowAfter: "\u0026" - readonly property string addRowBefore: "\u0027" - readonly property string addTable: "\u0028" - readonly property string alignBottom: "\u0029" - readonly property string alignCenterHorizontal: "\u002A" - readonly property string alignCenterVertical: "\u002B" - readonly property string alignLeft: "\u002C" - readonly property string alignRight: "\u002D" - readonly property string alignTo: "\u002E" - readonly property string alignTop: "\u002F" - readonly property string anchorBaseline: "\u0030" - readonly property string anchorBottom: "\u0031" - readonly property string anchorFill: "\u0032" - readonly property string anchorLeft: "\u0033" - readonly property string anchorRight: "\u0034" - readonly property string anchorTop: "\u0035" - readonly property string annotationBubble: "\u0036" - readonly property string annotationDecal: "\u0037" - readonly property string centerHorizontal: "\u0038" - readonly property string centerVertical: "\u0039" - readonly property string closeCross: "\u003A" - readonly property string deleteColumn: "\u003B" - readonly property string deleteRow: "\u003C" - readonly property string deleteTable: "\u003D" - readonly property string distributeBottom: "\u003E" - readonly property string distributeCenterHorizontal: "\u003F" - readonly property string distributeCenterVertical: "\u0040" - readonly property string distributeLeft: "\u0041" - readonly property string distributeOriginBottomRight: "\u0042" - readonly property string distributeOriginCenter: "\u0043" - readonly property string distributeOriginNone: "\u0044" - readonly property string distributeOriginTopLeft: "\u0045" - readonly property string distributeRight: "\u0046" - readonly property string distributeSpacingHorizontal: "\u0047" - readonly property string distributeSpacingVertical: "\u0048" - readonly property string distributeTop: "\u0049" - readonly property string edit: "\u004A" - readonly property string fontStyleBold: "\u004B" - readonly property string fontStyleItalic: "\u004C" - readonly property string fontStyleStrikethrough: "\u004D" - readonly property string fontStyleUnderline: "\u004E" - readonly property string mergeCells: "\u004F" - readonly property string redo: "\u0050" - readonly property string splitColumns: "\u0051" - readonly property string splitRows: "\u0052" - readonly property string testIcon: "\u0053" - readonly property string textAlignBottom: "\u0054" - readonly property string textAlignCenter: "\u0055" - readonly property string textAlignLeft: "\u0056" - readonly property string textAlignMiddle: "\u0057" - readonly property string textAlignRight: "\u0058" - readonly property string textAlignTop: "\u0059" - readonly property string textBulletList: "\u005A" - readonly property string textFullJustification: "\u005B" - readonly property string textNumberedList: "\u005C" - readonly property string tickIcon: "\u005D" - readonly property string triState: "\u005E" - readonly property string undo: "\u005F" - readonly property string upDownIcon: "\u0060" - readonly property string upDownSquare2: "\u0061" - - readonly property font iconFont: Qt.font({ - "family": controlIcons.name, - "pixelSize": 12 - }) - - readonly property font font: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize - }) - - readonly property font largeFont: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize * 1.6 - }) - - readonly property color backgroundColor: "#c2c2c2" - - readonly property bool showActionIndicatorBackground: false -} +InternalConstants {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml new file mode 100644 index 00000000000..4d45032eae9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.10 + +QtObject { + readonly property int width: 1920 + readonly property int height: 1080 + readonly property FontLoader mySystemFont: FontLoader { + name: "Arial" + } + readonly property FontLoader controlIcons: FontLoader { + source: "icons.ttf" + } + + objectName: "internalConstantsObject" + + readonly property string actionIcon: "\u0021" + readonly property string actionIconBinding: "\u0022" + readonly property string addColumnAfter: "\u0023" + readonly property string addColumnBefore: "\u0024" + readonly property string addFile: "\u0025" + readonly property string addRowAfter: "\u0026" + readonly property string addRowBefore: "\u0027" + readonly property string addTable: "\u0028" + readonly property string adsClose: "\u0029" + readonly property string adsDetach: "\u002A" + readonly property string adsDropDown: "\u002B" + readonly property string alignBottom: "\u002C" + readonly property string alignCenterHorizontal: "\u002D" + readonly property string alignCenterVertical: "\u002E" + readonly property string alignLeft: "\u002F" + readonly property string alignRight: "\u0030" + readonly property string alignTo: "\u0031" + readonly property string alignTop: "\u0032" + readonly property string anchorBaseline: "\u0033" + readonly property string anchorBottom: "\u0034" + readonly property string anchorFill: "\u0035" + readonly property string anchorLeft: "\u0036" + readonly property string anchorRight: "\u0037" + readonly property string anchorTop: "\u0038" + readonly property string annotationBubble: "\u0039" + readonly property string annotationDecal: "\u003A" + readonly property string centerHorizontal: "\u003B" + readonly property string centerVertical: "\u003C" + readonly property string closeCross: "\u003D" + readonly property string decisionNode: "\u003E" + readonly property string deleteColumn: "\u003F" + readonly property string deleteRow: "\u0040" + readonly property string deleteTable: "\u0041" + readonly property string detach: "\u0042" + readonly property string distributeBottom: "\u0043" + readonly property string distributeCenterHorizontal: "\u0044" + readonly property string distributeCenterVertical: "\u0045" + readonly property string distributeLeft: "\u0046" + readonly property string distributeOriginBottomRight: "\u0047" + readonly property string distributeOriginCenter: "\u0048" + readonly property string distributeOriginNone: "\u0049" + readonly property string distributeOriginTopLeft: "\u004A" + readonly property string distributeRight: "\u004B" + readonly property string distributeSpacingHorizontal: "\u004C" + readonly property string distributeSpacingVertical: "\u004D" + readonly property string distributeTop: "\u004E" + readonly property string edit: "\u004F" + readonly property string fontStyleBold: "\u0050" + readonly property string fontStyleItalic: "\u0051" + readonly property string fontStyleStrikethrough: "\u0052" + readonly property string fontStyleUnderline: "\u0053" + readonly property string mergeCells: "\u0054" + readonly property string redo: "\u0055" + readonly property string splitColumns: "\u0056" + readonly property string splitRows: "\u0057" + readonly property string startNode: "\u0058" + readonly property string testIcon: "\u0059" + readonly property string textAlignBottom: "\u005A" + readonly property string textAlignCenter: "\u005B" + readonly property string textAlignLeft: "\u005C" + readonly property string textAlignMiddle: "\u005D" + readonly property string textAlignRight: "\u005E" + readonly property string textAlignTop: "\u005F" + readonly property string textBulletList: "\u0060" + readonly property string textFullJustification: "\u0061" + readonly property string textNumberedList: "\u0062" + readonly property string tickIcon: "\u0063" + readonly property string triState: "\u0064" + readonly property string undo: "\u0065" + readonly property string upDownIcon: "\u0066" + readonly property string upDownSquare2: "\u0067" + readonly property string wildcard: "\u0068" + + readonly property font iconFont: Qt.font({ + "family": controlIcons.name, + "pixelSize": 12 + }) + + readonly property font font: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize + }) + + readonly property font largeFont: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize * 1.6 + }) + + readonly property color backgroundColor: "#c2c2c2" + + readonly property bool showActionIndicatorBackground: false +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index d65b065195211fbf5da4488b6063bf24c82e3ef8..9e3ed42406252fcc604753970c23dfa1173cc6c5 100644 GIT binary patch delta 1890 zcmX>Rv?H>ffsuiMftR6yftew|%`L>YztE+Sfk9^u0|SGMf3Ut$WNXSl1_s6m1_p+N z9WTe(7a{c;0iGhJpgn@xUB_p?_LLiLI znSp_E2Ll7cyqx^xMDddfbqoy53m6y}X5=PT6fjI>Sj51Bm)CjHv_Z8oW=-y87O3Z7v}5dG+{5^ZNs7seX$I34rb|qpm_?Y4m_wLnFmGc%!NSAR#Bz+4 zi`9>{jCCIC8#WuZBDNK5_t-_)o!A$!A7OvN!NXy|k;1W#;~Xaorv|47X9i~v=P}N2 zTs&N1Ty0$ExH-6;xU0B3xR-JN;ql^`!SjyShIbP0J3cGEYdn0v_=Wg=_%rxh_^)l2 zVOh#JIhS3|S6R@QQP5bBQBYCP+|=0ASkzclSyb6npSkN#8ROP9YyMTOVQTo-8Rw_( z=cmuKc{StTex^c3{l9ORL=gBFNYdY*VRH_fDpNfZL+=0i%sEW+7+4v&85ral8TA<% zSo(0Gfn#EA6#A@%-F@r$H)1vOv_y>+JSMSLlnOdU)k2jvYMK*AmTp* zNcu5|Xe|$BJQZBVC(NhmuJzB%Av)S&D@c56a5>{1h-#1)Mh8Alu=>ZGd<={XrYis6 zF`s5)X5eIC5N2l-H)qsmzX=4ym|W)4o_nnYcb?#^4ht z{;g&1V_;xlVbJ?Om3bG_W(IZ!ZU%k^X$FSP3Id9Z^%RCbrNM6l3Sw}0k`ex!9R#Vri25|;u1}z3x zhJwj?a%!B6?CR#mqRQgNljq3E)QgIVimMQ=^QP9&MAY+G1q`t|9+c@_~{t=Gfgv4VB_ZG zVpc;?>jVXJc0qROSj}QB>FA6J(mE>!_#a zsQd4Uu9L2=lkUGKdXBofj(SX1iYi90K51(j%5(Ax@^P}WsBt^X^Gb;--D6^7VG_lA4JLGYW!oo2a0lV#M5*|}1{YBzhUU18+nEiOqcDgnuDme$~8Y-z8F$fk9J;fq_BBKUm)=vNeT)fq^lCfq@|* zIXAID$^qk5x zsr2-h3=AwX3=A?SGEx&$q+iX8VPIgm!@$5`mXVQKpUCy=|0D(mMiB-E29=E5k_y4a z%-b0l7{`2_<51H-KqYx^KH z)7rjCjJC`!40)3s7^6iKnHBz9Fhnt{Fq{PW8l;9vaqq;b8G$nZR;|3RI*uJ2FF5{jdT>^79^m}NWx|!i)y4IITaUYj z`x1`=PZ`f8UOC=vytjBi@#*k+@TD*?Y_?!o%2?0Ap!a_&^Dd^%49pDt3^ELg4C)NJ z48{zW3=G1iilVHF#oXQC`TK-P`R`rE`ZH(J#Kk2({XNTMyxD_Im1*-r z&Kma3oq{Hen=6I4uy6L1PGc4J{C|-7DARNXZU%7%Wd<>M;ol z3QO1g`ynnWEy&8o#m>siD8i<~&%w(h{GE}RnTd;Cl#`Q1ltqY*Q;ds^k6DPFgG*3o z^CQK0#?4QacQLXXGZ=y*ZSpcz3r=H0U1MD%10#dU_f-NWo2xBibeVihtz&Y5dfR42 g4Qs~9xtcngmuOyK+}xzi$~bw7jwClc@vrRz0D1BES^xk5 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index b768fe63a2b..4f689f9f630 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -1,2 +1,4 @@ singleton Values 1.0 Values.qml singleton Constants 1.0 Constants.qml +InternalConstants 1.0 InternalConstants.qml + diff --git a/src/libs/advanceddockingsystem/ads_globals.h b/src/libs/advanceddockingsystem/ads_globals.h index 4c0b8d6896a..e61bfa7ceb4 100644 --- a/src/libs/advanceddockingsystem/ads_globals.h +++ b/src/libs/advanceddockingsystem/ads_globals.h @@ -107,10 +107,11 @@ enum eDragState { * The different icons used in the UI */ enum eIcon { - TabCloseIcon, //!< TabCloseIcon - DockAreaMenuIcon, //!< DockAreaMenuIcon - DockAreaUndockIcon, //!< DockAreaUndockIcon - DockAreaCloseIcon, //!< DockAreaCloseIcon + TabCloseIcon, //!< TabCloseIcon + DockAreaMenuIcon, //!< DockAreaMenuIcon + DockAreaUndockIcon, //!< DockAreaUndockIcon + DockAreaCloseIcon, //!< DockAreaCloseIcon + FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon IconCount, //!< just a delimiter for range checks }; diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 9ee647265d9..79ba7ae70d5 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -132,6 +132,7 @@ namespace ADS void DockAreaTitleBarPrivate::createButtons() { + const QSize iconSize(14, 14); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Tabs menu button m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton)); @@ -149,6 +150,7 @@ namespace ADS m_tabsMenuButton->setMenu(tabsMenu); internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs")); m_tabsMenuButton->setSizePolicy(sizePolicy); + m_tabsMenuButton->setIconSize(iconSize); m_layout->addWidget(m_tabsMenuButton, 0); QObject::connect(m_tabsMenuButton->menu(), &QMenu::triggered, @@ -164,6 +166,7 @@ namespace ADS QStyle::SP_TitleBarNormalButton, ADS::DockAreaUndockIcon); m_undockButton->setSizePolicy(sizePolicy); + m_undockButton->setIconSize(iconSize); m_layout->addWidget(m_undockButton, 0); QObject::connect(m_undockButton, &QToolButton::clicked, @@ -183,7 +186,7 @@ namespace ADS internal::setToolTip(m_closeButton, QObject::tr("Close Group")); } m_closeButton->setSizePolicy(sizePolicy); - m_closeButton->setIconSize(QSize(16, 16)); + m_closeButton->setIconSize(iconSize); m_layout->addWidget(m_closeButton, 0); QObject::connect(m_closeButton, &QToolButton::clicked, diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index 611580d3685..b46d1720509 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -168,12 +168,18 @@ namespace ADS m_titleLabel->setText(m_dockWidget->windowTitle()); m_titleLabel->setObjectName("dockWidgetTabLabel"); m_titleLabel->setAlignment(Qt::AlignCenter); - QObject::connect(m_titleLabel, &ElidingLabel::elidedChanged, q, &DockWidgetTab::elidedChanged); + QObject::connect(m_titleLabel, + &ElidingLabel::elidedChanged, + q, + &DockWidgetTab::elidedChanged); m_closeButton = createCloseButton(); m_closeButton->setObjectName("tabCloseButton"); - internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + TabCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); q->onDockWidgetFeaturesChanged(); internal::setToolTip(m_closeButton, QObject::tr("Close Tab")); QObject::connect(m_closeButton, @@ -189,11 +195,11 @@ namespace ADS boxLayout->setContentsMargins(2 * spacing, 0, 0, 0); boxLayout->setSpacing(0); q->setLayout(boxLayout); - boxLayout->addWidget(m_titleLabel, 1); + boxLayout->addWidget(m_titleLabel, 1, Qt::AlignVCenter); boxLayout->addSpacing(spacing); - boxLayout->addWidget(m_closeButton); + boxLayout->addWidget(m_closeButton, 0, Qt::AlignVCenter); boxLayout->addSpacing(qRound(spacing * 4.0 / 3.0)); - boxLayout->setAlignment(Qt::AlignCenter); + boxLayout->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); m_titleLabel->setVisible(true); } diff --git a/src/libs/advanceddockingsystem/iconprovider.cpp b/src/libs/advanceddockingsystem/iconprovider.cpp index 6a6f46752bb..f220418220d 100644 --- a/src/libs/advanceddockingsystem/iconprovider.cpp +++ b/src/libs/advanceddockingsystem/iconprovider.cpp @@ -51,7 +51,7 @@ namespace ADS { */ IconProviderPrivate(IconProvider *parent); }; - // struct LedArrayPanelPrivate + // struct IconProviderPrivate IconProviderPrivate::IconProviderPrivate(IconProvider *parent) : q(parent) diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp index 64e71a3b1c3..7d92952d700 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp @@ -41,7 +41,7 @@ namespace ADS { using TabLabelType = ElidingLabel; -using tCloseButton = QPushButton; +using CloseButtonType = QPushButton; /** * @brief Private data class of public interface CFloatingWidgetTitleBar @@ -52,7 +52,7 @@ public: FloatingWidgetTitleBar *q; ///< public interface class QLabel *m_iconLabel = nullptr; TabLabelType *m_titleLabel = nullptr; - tCloseButton *m_closeButton = nullptr; + CloseButtonType *m_closeButton = nullptr; FloatingDockContainer *m_floatingWidget = nullptr; eDragState m_dragState = DraggingInactive; @@ -74,22 +74,20 @@ void FloatingWidgetTitleBarPrivate::createLayout() m_titleLabel->setObjectName("floatingTitleLabel"); m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - m_closeButton = new tCloseButton(); + m_closeButton = new CloseButtonType(); m_closeButton->setObjectName("floatingTitleCloseButton"); m_closeButton->setFlat(true); - - // The standard icons do does not look good on high DPI screens - QIcon closeIcon; - QPixmap normalPixmap = q->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, - nullptr, - m_closeButton); - closeIcon.addPixmap(normalPixmap, QIcon::Normal); - closeIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + ADS::FloatingWidgetCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); m_closeButton->setVisible(true); m_closeButton->setFocusPolicy(Qt::NoFocus); - q->connect(m_closeButton, &QPushButton::clicked, q, &FloatingWidgetTitleBar::closeRequested); + QObject::connect(m_closeButton, + &QPushButton::clicked, + q, + &FloatingWidgetTitleBar::closeRequested); QFontMetrics fontMetrics(m_titleLabel->font()); int spacing = qRound(fontMetrics.height() / 4.0); diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index 28d07392271..68d40accbc6 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -28,6 +28,8 @@ #include "theme/theme.h" #include "hostosinfo.h" +#include + #include #include #include @@ -543,26 +545,35 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect) return grad; } -QPixmap StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize) +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color) { QFontDatabase a; - Q_ASSERT(a.hasFamily(fontName)); + QTC_ASSERT(a.hasFamily(fontName), {}); if (a.hasFamily(fontName)) { - QPixmap icon(iconSize, iconSize); - icon.fill(Qt::transparent); - QPainter painter(&icon); - QFont font(fontName); - font.setPixelSize(fontSize); - QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText); - painter.save(); - painter.setPen(penColor); - painter.setFont(font); - painter.drawText(QRectF(0, 0, iconSize, iconSize), iconSymbol); + QIcon icon; + QSize size(iconSize, iconSize); - painter.restore(); + const int maxDpr = qRound(qApp->devicePixelRatio()); + for (int dpr = 1; dpr <= maxDpr; dpr++) { + QPixmap pixmap(size * dpr); + pixmap.setDevicePixelRatio(dpr); + pixmap.fill(Qt::transparent); + + QFont font(fontName); + font.setPixelSize(fontSize * dpr); + + QPainter painter(&pixmap); + painter.save(); + painter.setPen(color); + painter.setFont(font); + painter.drawText(QRectF(QPoint(0, 0), size), iconSymbol); + painter.restore(); + + icon.addPixmap(pixmap); + } return icon; } @@ -570,6 +581,12 @@ QPixmap StyleHelper::getIconFromIconFont(const QString &fontName, const QString return {}; } +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize) +{ + QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText); + return getIconFromIconFont(fontName, iconSymbol, fontSize, iconSize, penColor); +} + QString StyleHelper::dpiSpecificImageFile(const QString &fileName) { // See QIcon::addFile() diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index e9261b7c8bb..09f32534352 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -93,7 +93,8 @@ public: static void tintImage(QImage &img, const QColor &tintColor); static QLinearGradient statusBarGradient(const QRect &statusBarRect); - static QPixmap getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize); static QString dpiSpecificImageFile(const QString &fileName); static QString imageFileWithResolution(const QString &fileName, int dpr); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index ec4128479fe..29b937a502a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -28,19 +28,46 @@ #include +#include + #include #include #include #include #include +#include +#include +#include #include +static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg) + namespace QmlDesigner { Theme::Theme(Utils::Theme *originTheme, QObject *parent) : Utils::Theme(originTheme, parent) + , m_constants(nullptr) { + QString constantsPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml"); + + QQmlEngine* engine = new QQmlEngine(this); + QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath)); + + if (component.status() == QQmlComponent::Ready) { + m_constants = component.create(); + } + else if (component.status() == QQmlComponent::Error ) { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "due to the following error(s):"; + for (QQmlError error : component.errors()) + qCWarning(themeLog) << error.toString(); + } + else { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "the status of the QQmlComponent is" << component.status(); + } } QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName) @@ -129,6 +156,25 @@ QPixmap Theme::getPixmap(const QString &id) return QmlDesignerIconProvider::getPixmap(id); } +QString Theme::getIconUnicode(Theme::Icon i) +{ + if (!instance()->m_constants) + return QString(); + + const QMetaObject *m = instance()->metaObject(); + const char *enumName = "Icon"; + int enumIndex = m->indexOfEnumerator(enumName); + + if (enumIndex == -1) { + qCWarning(themeLog) << "Couldn't find enum" << enumName; + return QString(); + } + + QMetaEnum e = m->enumerator(enumIndex); + + return instance()->m_constants->property(e.valueToKey(i)).toString(); +} + QColor Theme::qmlDesignerBackgroundColorDarker() const { return getColor(QmlDesigner_BackgroundColorDarker); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 6940c0c1cf0..07ee4caca8a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -41,12 +41,91 @@ namespace QmlDesigner { class QMLDESIGNERCORE_EXPORT Theme : public Utils::Theme { Q_OBJECT + + Q_ENUMS(Icon) + public: + enum Icon { + actionIcon, + actionIconBinding, + addColumnAfter, + addColumnBefore, + addFile, + addRowAfter, + addRowBefore, + addTable, + adsClose, + adsDetach, + adsDropDown, + alignBottom, + alignCenterHorizontal, + alignCenterVertical, + alignLeft, + alignRight, + alignTo, + alignTop, + anchorBaseline, + anchorBottom, + anchorFill, + anchorLeft, + anchorRight, + anchorTop, + annotationBubble, + annotationDecal, + centerHorizontal, + centerVertical, + closeCross, + decisionNode, + deleteColumn, + deleteRow, + deleteTable, + detach, + distributeBottom, + distributeCenterHorizontal, + distributeCenterVertical, + distributeLeft, + distributeOriginBottomRight, + distributeOriginCenter, + distributeOriginNone, + distributeOriginTopLeft, + distributeRight, + distributeSpacingHorizontal, + distributeSpacingVertical, + distributeTop, + edit, + fontStyleBold, + fontStyleItalic, + fontStyleStrikethrough, + fontStyleUnderline, + mergeCells, + redo, + splitColumns, + splitRows, + startNode, + testIcon, + textAlignBottom, + textAlignCenter, + textAlignLeft, + textAlignMiddle, + textAlignRight, + textAlignTop, + textBulletList, + textFullJustification, + textNumberedList, + tickIcon, + triState, + undo, + upDownIcon, + upDownSquare2, + wildcard + }; + static Theme *instance(); static QString replaceCssColors(const QString &input); static void setupTheme(QQmlEngine *engine); static QColor getColor(Color role); static QPixmap getPixmap(const QString &id); + static QString getIconUnicode(Theme::Icon i); Q_INVOKABLE QColor qmlDesignerBackgroundColorDarker() const; Q_INVOKABLE QColor qmlDesignerBackgroundColorDarkAlternate() const; @@ -58,9 +137,12 @@ public: Q_INVOKABLE int smallFontPixelSize() const; Q_INVOKABLE int captionFontPixelSize() const; Q_INVOKABLE bool highPixelDensity() const; + private: Theme(Utils::Theme *originTheme, QObject *parent); QColor evaluateColorAtThemeInstance(const QString &themeColorName); + + QObject *m_constants; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp index 66d8ca70d0d..9ad7c920636 100644 --- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp @@ -160,11 +160,11 @@ void RichTextEditor::setTabChangesFocus(bool change) ui->textEdit->setTabChangesFocus(change); } -QPixmap RichTextEditor::getIcon(const QString &iconName) +QIcon RichTextEditor::getIcon(Theme::Icon icon) { const QString fontName = "qtds_propertyIconFont.ttf"; - return Utils::StyleHelper::getIconFromIconFont(fontName, iconName, 36, 36); + return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 36, 36); } QString RichTextEditor::richText() const @@ -276,13 +276,13 @@ void RichTextEditor::tableChanged(const QTextCursor &cursor) void RichTextEditor::setupEditActions() { - const QIcon undoIcon(getIcon("\u005F")); + const QIcon undoIcon(getIcon(Theme::Icon::undo)); QAction *actionUndo = ui->toolBar->addAction(undoIcon, tr("&Undo"), ui->textEdit, &QTextEdit::undo); actionUndo->setShortcut(QKeySequence::Undo); connect(ui->textEdit->document(), &QTextDocument::undoAvailable, actionUndo, &QAction::setEnabled); - const QIcon redoIcon(getIcon("\u0050")); + const QIcon redoIcon(getIcon(Theme::Icon::redo)); QAction *actionRedo = ui->toolBar->addAction(redoIcon, tr("&Redo"), ui->textEdit, &QTextEdit::redo); actionRedo->setShortcut(QKeySequence::Redo); connect(ui->textEdit->document(), &QTextDocument::redoAvailable, @@ -296,7 +296,7 @@ void RichTextEditor::setupEditActions() void RichTextEditor::setupTextActions() { - const QIcon boldIcon(getIcon("\u004B")); + const QIcon boldIcon(getIcon(Theme::Icon::fontStyleBold)); m_actionTextBold = ui->toolBar->addAction(boldIcon, tr("&Bold"), [this](bool checked) { QTextCharFormat fmt; @@ -309,7 +309,7 @@ void RichTextEditor::setupTextActions() m_actionTextBold->setFont(bold); m_actionTextBold->setCheckable(true); - const QIcon italicIcon(getIcon("\u004C")); + const QIcon italicIcon(getIcon(Theme::Icon::fontStyleItalic)); m_actionTextItalic = ui->toolBar->addAction(italicIcon, tr("&Italic"), [this](bool checked) { QTextCharFormat fmt; @@ -322,7 +322,7 @@ void RichTextEditor::setupTextActions() m_actionTextItalic->setFont(italic); m_actionTextItalic->setCheckable(true); - const QIcon underlineIcon(getIcon("\u004E")); + const QIcon underlineIcon(getIcon(Theme::Icon::fontStyleUnderline)); m_actionTextUnderline = ui->toolBar->addAction(underlineIcon, tr("&Underline"), [this](bool checked) { QTextCharFormat fmt; @@ -340,7 +340,7 @@ void RichTextEditor::setupTextActions() void RichTextEditor::setupHyperlinkActions() { - const QIcon bulletIcon(getIcon("\u0022")); + const QIcon bulletIcon(getIcon(Theme::Icon::actionIconBinding)); m_actionHyperlink = ui->toolBar->addAction(bulletIcon, tr("Hyperlink Settings"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); QTextCharFormat linkFormat = cursor.charFormat(); @@ -362,25 +362,25 @@ void RichTextEditor::setupHyperlinkActions() void RichTextEditor::setupAlignActions() { - const QIcon leftIcon(getIcon("\u0056")); + const QIcon leftIcon(getIcon(Theme::Icon::textAlignLeft)); m_actionAlignLeft = ui->toolBar->addAction(leftIcon, tr("&Left"), [this]() { ui->textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); }); m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); m_actionAlignLeft->setCheckable(true); m_actionAlignLeft->setPriority(QAction::LowPriority); - const QIcon centerIcon(getIcon("\u0055")); + const QIcon centerIcon(getIcon(Theme::Icon::textAlignCenter)); m_actionAlignCenter = ui->toolBar->addAction(centerIcon, tr("C&enter"), [this]() { ui->textEdit->setAlignment(Qt::AlignHCenter); }); m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); m_actionAlignCenter->setCheckable(true); m_actionAlignCenter->setPriority(QAction::LowPriority); - const QIcon rightIcon(getIcon("\u0058")); + const QIcon rightIcon(getIcon(Theme::Icon::textAlignRight)); m_actionAlignRight = ui->toolBar->addAction(rightIcon, tr("&Right"), [this]() { ui->textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); }); m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); m_actionAlignRight->setCheckable(true); m_actionAlignRight->setPriority(QAction::LowPriority); - const QIcon fillIcon(getIcon("\u005B")); + const QIcon fillIcon(getIcon(Theme::Icon::textFullJustification)); m_actionAlignJustify = ui->toolBar->addAction(fillIcon, tr("&Justify"), [this]() { ui->textEdit->setAlignment(Qt::AlignJustify); }); m_actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); m_actionAlignJustify->setCheckable(true); @@ -407,7 +407,7 @@ void RichTextEditor::setupAlignActions() void RichTextEditor::setupListActions() { - const QIcon bulletIcon(getIcon("\u005A")); + const QIcon bulletIcon(getIcon(Theme::Icon::textBulletList)); m_actionBulletList = ui->toolBar->addAction(bulletIcon, tr("Bullet List"), [this](bool checked) { if (checked) { m_actionNumberedList->setChecked(false); @@ -419,7 +419,7 @@ void RichTextEditor::setupListActions() }); m_actionBulletList->setCheckable(true); - const QIcon numberedIcon(getIcon("\u005C")); + const QIcon numberedIcon(getIcon(Theme::Icon::textNumberedList)); m_actionNumberedList = ui->toolBar->addAction(numberedIcon, tr("Numbered List"), [this](bool checked) { if (checked) { m_actionBulletList->setChecked(false); @@ -493,7 +493,7 @@ void RichTextEditor::setupFontActions() void RichTextEditor::setupTableActions() { - const QIcon tableIcon(getIcon("\u0028")); + const QIcon tableIcon(getIcon(Theme::Icon::addTable)); m_actionTableSettings = ui->toolBar->addAction(tableIcon, tr("&Table Settings"), [this](bool checked) { ui->tableBar->setVisible(checked); }); @@ -503,7 +503,7 @@ void RichTextEditor::setupTableActions() //table bar: - const QIcon createTableIcon(getIcon("\u0028")); + const QIcon createTableIcon(getIcon(Theme::Icon::addTable)); m_actionCreateTable = ui->tableBar->addAction(createTableIcon, tr("Create Table"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); cursorEditBlock(cursor, [&] () { @@ -512,7 +512,7 @@ void RichTextEditor::setupTableActions() }); m_actionCreateTable->setCheckable(false); - const QIcon removeTableIcon(getIcon("\u003D")); + const QIcon removeTableIcon(getIcon(Theme::Icon::deleteTable)); m_actionRemoveTable = ui->tableBar->addAction(removeTableIcon, tr("Remove Table"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { @@ -525,7 +525,7 @@ void RichTextEditor::setupTableActions() ui->tableBar->addSeparator(); - const QIcon addRowIcon(getIcon("\u0026")); //addRowAfter + const QIcon addRowIcon(getIcon(Theme::Icon::addRowAfter)); //addRowAfter m_actionAddRow = ui->tableBar->addAction(addRowIcon, tr("Add Row"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { @@ -536,7 +536,7 @@ void RichTextEditor::setupTableActions() }); m_actionAddRow->setCheckable(false); - const QIcon addColumnIcon(getIcon("\u0023")); //addColumnAfter + const QIcon addColumnIcon(getIcon(Theme::Icon::addColumnAfter)); //addColumnAfter m_actionAddColumn = ui->tableBar->addAction(addColumnIcon, tr("Add Column"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = ui->textEdit->textCursor().currentTable()) { @@ -547,7 +547,7 @@ void RichTextEditor::setupTableActions() }); m_actionAddColumn->setCheckable(false); - const QIcon removeRowIcon(getIcon("\u003C")); + const QIcon removeRowIcon(getIcon(Theme::Icon::deleteRow)); m_actionRemoveRow = ui->tableBar->addAction(removeRowIcon, tr("Remove Row"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = cursor.currentTable()) { @@ -571,7 +571,7 @@ void RichTextEditor::setupTableActions() }); m_actionRemoveRow->setCheckable(false); - const QIcon removeColumnIcon(getIcon("\u003B")); + const QIcon removeColumnIcon(getIcon(Theme::Icon::deleteColumn)); m_actionRemoveColumn = ui->tableBar->addAction(removeColumnIcon, tr("Remove Column"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = cursor.currentTable()) { @@ -595,7 +595,7 @@ void RichTextEditor::setupTableActions() ui->tableBar->addSeparator(); - const QIcon mergeCellsIcon(getIcon("\u004F")); + const QIcon mergeCellsIcon(getIcon(Theme::Icon::mergeCells)); m_actionMergeCells = ui->tableBar->addAction(mergeCellsIcon, tr("Merge Cells"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = cursor.currentTable()) { @@ -608,7 +608,7 @@ void RichTextEditor::setupTableActions() }); m_actionMergeCells->setCheckable(false); - const QIcon splitRowIcon(getIcon("\u0052")); + const QIcon splitRowIcon(getIcon(Theme::Icon::splitRows)); m_actionSplitRow = ui->tableBar->addAction(splitRowIcon, tr("Split Row"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = cursor.currentTable()) { @@ -621,7 +621,7 @@ void RichTextEditor::setupTableActions() }); m_actionSplitRow->setCheckable(false); - const QIcon splitColumnIcon(getIcon("\u0051")); + const QIcon splitColumnIcon(getIcon(Theme::Icon::splitColumns)); m_actionSplitColumn = ui->tableBar->addAction(splitRowIcon, tr("Split Column"), [this]() { QTextCursor cursor = ui->textEdit->textCursor(); if (QTextTable *currentTable = cursor.currentTable()) { diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h index 3b9ff7864ca..50053c1ab3c 100644 --- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.h @@ -25,6 +25,8 @@ #pragma once +#include + #include #include #include @@ -66,7 +68,7 @@ private slots: void cursorPositionChanged(); private: - QPixmap getIcon(const QString &iconName); + QIcon getIcon(Theme::Icon icon); void mergeFormatOnWordOrSelection(const QTextCharFormat &format); void fontChanged(const QFont &f); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 68c1170f2b7..75cf1c373e4 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include +#include using Core::MiniSplitter; using Core::IEditor; @@ -243,6 +245,26 @@ void DesignModeWidget::setup() QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); + // Setup icons + QColor buttonColor(Theme::getColor(Theme::QmlDesigner_TabLight)); // TODO Use correct color roles + QColor tabColor(Theme::getColor(Theme::QmlDesigner_TabDark)); + + const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::adsClose); + const QString menuUnicode = Theme::getIconUnicode(Theme::Icon::adsDropDown); + const QString undockUnicode = Theme::getIconUnicode(Theme::Icon::adsDetach); + + const QString fontName = "qtds_propertyIconFont.ttf"; + const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, tabColor); + const QIcon menuIcon = Utils::StyleHelper::getIconFromIconFont(fontName, menuUnicode, 28, 28, buttonColor); + const QIcon undockIcon = Utils::StyleHelper::getIconFromIconFont(fontName, undockUnicode, 28, 28, buttonColor); + const QIcon closeIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, buttonColor); + + m_dockManager->iconProvider().registerCustomIcon(ADS::TabCloseIcon, tabsCloseIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaMenuIcon, menuIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaUndockIcon, undockIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaCloseIcon, closeIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::FloatingWidgetCloseIcon, closeIcon); + // Setup Actions and Menus Core::ActionContainer *mwindow = Core::ActionManager::actionContainer(Core::Constants::M_WINDOW); // Window > Views diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a65ff3600be..cd5b23783aa 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -216,6 +216,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool()) GenerateResource::generateMenuEntry(); + QString fontPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf"); + if (QFontDatabase::addApplicationFont(fontPath) < 0) + qCWarning(qmldesignerLog) << "Could not add font " << fontPath << "to font database"; + return true; } From 00f4145de8cd9eedf4c74ba9582aa89d86f45728 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 16 Apr 2020 18:00:48 +0200 Subject: [PATCH 05/28] QmlDesigner: Global Annotations Editor Task: QDS-1904 Change-Id: I92682b235f9c69dd7069172807954f0a99a946b0 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../annotationeditor/annotationeditor.pri | 2 + .../annotationeditordialog.cpp | 38 ++++- .../annotationeditor/annotationeditordialog.h | 13 +- .../globalannotationeditor.cpp | 148 ++++++++++++++++++ .../annotationeditor/globalannotationeditor.h | 75 +++++++++ .../designercore/include/annotation.h | 1 + .../designercore/include/modelnode.h | 5 + .../designercore/model/modelnode.cpp | 29 ++++ src/plugins/qmldesigner/designmodewidget.cpp | 13 ++ src/plugins/qmldesigner/designmodewidget.h | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 12 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 411c019af8a..34c506ddce5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -552,6 +552,7 @@ extend_qtc_plugin(QmlDesigner annotationeditordialog.cpp annotationeditordialog.h annotationeditordialog.ui annotationeditor.cpp annotationeditor.h annotationtool.cpp annotationtool.h + globalannotationeditor.cpp globalannotationeditor.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri index e221b3bb615..9a3a00e2da3 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri @@ -2,11 +2,13 @@ HEADERS += $$PWD/annotationtool.h HEADERS += $$PWD/annotationcommenttab.h HEADERS += $$PWD/annotationeditordialog.h HEADERS += $$PWD/annotationeditor.h +HEADERS += $$PWD/globalannotationeditor.h SOURCES += $$PWD/annotationtool.cpp SOURCES += $$PWD/annotationcommenttab.cpp SOURCES += $$PWD/annotationeditordialog.cpp SOURCES += $$PWD/annotationeditor.cpp +SOURCES += $$PWD/globalannotationeditor.cpp FORMS += $$PWD/annotationcommenttab.ui FORMS += $$PWD/annotationeditordialog.ui diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp index dd234a074cb..a25d93b666a 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp @@ -40,16 +40,16 @@ namespace QmlDesigner { -AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation) +AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation, EditorMode mode) : QDialog(parent) , ui(new Ui::AnnotationEditorDialog) , m_customId(customId) , m_annotation(annotation) + , m_editorMode(mode) { ui->setupUi(this); setWindowFlag(Qt::Tool, true); - setWindowTitle(titleString); setModal(true); connect(this, &QDialog::accepted, this, &AnnotationEditorDialog::acceptedClicked); @@ -98,6 +98,7 @@ AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &t ui->tabWidget->setCornerWidget(commentCornerWidget, Qt::TopRightCorner); ui->targetIdEdit->setText(targetId); + changeEditorMode(m_editorMode); fillFields(); } @@ -128,6 +129,39 @@ QString AnnotationEditorDialog::customId() const return m_customId; } +void AnnotationEditorDialog::changeEditorMode(AnnotationEditorDialog::EditorMode mode) +{ + switch (mode) { + case ItemAnnotation: { + ui->customIdEdit->setVisible(true); + ui->customIdLabel->setVisible(true); + ui->targetIdEdit->setVisible(true); + ui->targetIdLabel->setVisible(true); + setWindowTitle(annotationEditorTitle); + + break; + } + case GlobalAnnotation: { + ui->customIdEdit->clear(); + ui->targetIdEdit->clear(); + ui->customIdEdit->setVisible(false); + ui->customIdLabel->setVisible(false); + ui->targetIdEdit->setVisible(false); + ui->targetIdLabel->setVisible(false); + setWindowTitle(globalEditorTitle); + + break; + } + } + + m_editorMode = mode; +} + +AnnotationEditorDialog::EditorMode AnnotationEditorDialog::editorMode() const +{ + return m_editorMode; +} + void AnnotationEditorDialog::acceptedClicked() { m_customId = ui->customIdEdit->text(); diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h index 1324115a724..0c3cb50c807 100644 --- a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h @@ -40,7 +40,10 @@ class AnnotationEditorDialog : public QDialog Q_OBJECT public: - explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation); + enum EditorMode { ItemAnnotation, GlobalAnnotation }; + + explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation, + EditorMode mode = EditorMode::ItemAnnotation); ~AnnotationEditorDialog(); void setAnnotation(const Annotation &annotation); @@ -49,6 +52,9 @@ public: void setCustomId(const QString &customId); QString customId() const; + void changeEditorMode(EditorMode mode); + EditorMode editorMode() const; + signals: void accepted(); @@ -68,12 +74,15 @@ private: void deleteAllTabs(); private: - const QString titleString = {tr("Annotation Editor")}; + const QString annotationEditorTitle = {tr("Annotation Editor")}; + const QString globalEditorTitle = {tr("Global Annotation Editor")}; const QString defaultTabName = {tr("Annotation")}; Ui::AnnotationEditorDialog *ui; QString m_customId; Annotation m_annotation; + + EditorMode m_editorMode; }; } //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp new file mode 100644 index 00000000000..1beeacaf74c --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "globalannotationeditor.h" + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "qmlmodelnodeproxy.h" +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +GlobalAnnotationEditor::GlobalAnnotationEditor(QObject *) +{ +} + +GlobalAnnotationEditor::~GlobalAnnotationEditor() +{ + hideWidget(); +} + +void GlobalAnnotationEditor::showWidget() +{ + m_dialog = new AnnotationEditorDialog(Core::ICore::dialogParent(), + modelNode().validId(), + "", + modelNode().globalAnnotation(), + AnnotationEditorDialog::EditorMode::GlobalAnnotation); + + QObject::connect(m_dialog, &AnnotationEditorDialog::accepted, + this, &GlobalAnnotationEditor::acceptedClicked); + QObject::connect(m_dialog, &AnnotationEditorDialog::rejected, + this, &GlobalAnnotationEditor::cancelClicked); + + m_dialog->setAttribute(Qt::WA_DeleteOnClose); + + m_dialog->show(); + m_dialog->raise(); +} + +void GlobalAnnotationEditor::showWidget(int x, int y) +{ + showWidget(); + m_dialog->move(x, y); +} + +void GlobalAnnotationEditor::hideWidget() +{ + if (m_dialog) + m_dialog->close(); + m_dialog = nullptr; +} + +void GlobalAnnotationEditor::setModelNode(const ModelNode &modelNode) +{ + m_modelNode = modelNode; +} + +ModelNode GlobalAnnotationEditor::modelNode() const +{ + return m_modelNode; +} + +bool GlobalAnnotationEditor::hasAnnotation() const +{ + if (m_modelNode.isValid()) + return m_modelNode.hasGlobalAnnotation(); + return false; +} + +void GlobalAnnotationEditor::removeFullAnnotation() +{ + if (!m_modelNode.isValid()) + return; + + QString dialogTitle = tr("Global Annotation"); + QMessageBox *deleteDialog = new QMessageBox(Core::ICore::dialogParent()); + deleteDialog->setWindowTitle(dialogTitle); + deleteDialog->setText(tr("Delete this annotation?")); + deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + deleteDialog->setDefaultButton(QMessageBox::Yes); + + int result = deleteDialog->exec(); + if (deleteDialog) deleteDialog->deleteLater(); + + if (result == QMessageBox::Yes) { + m_modelNode.removeGlobalAnnotation(); + } + + emit annotationChanged(); +} + +void GlobalAnnotationEditor::acceptedClicked() +{ + if (m_dialog) { + Annotation annotation = m_dialog->annotation(); + + if (annotation.comments().isEmpty()) + m_modelNode.removeGlobalAnnotation(); + else + m_modelNode.setGlobalAnnotation(annotation); + } + + hideWidget(); + + emit accepted(); + + emit annotationChanged(); +} + +void GlobalAnnotationEditor::cancelClicked() +{ + hideWidget(); + + emit canceled(); + + emit annotationChanged(); +} + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h new file mode 100644 index 00000000000..e0aa66a1193 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/globalannotationeditor.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "modelnode.h" + +namespace QmlDesigner { + +class GlobalAnnotationEditor : public QObject +{ + Q_OBJECT + +public: + explicit GlobalAnnotationEditor(QObject *parent = nullptr); + ~GlobalAnnotationEditor(); + + Q_INVOKABLE void showWidget(); + Q_INVOKABLE void showWidget(int x, int y); + Q_INVOKABLE void hideWidget(); + + void setModelNode(const ModelNode &modelNode); + ModelNode modelNode() const; + + Q_INVOKABLE bool hasAnnotation() const; + + Q_INVOKABLE void removeFullAnnotation(); + +signals: + void accepted(); + void canceled(); + void modelNodeBackendChanged(); + + void annotationChanged(); + +private slots: + void acceptedClicked(); + void cancelClicked(); + +private: + QPointer m_dialog; + + ModelNode m_modelNode; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/annotation.h b/src/plugins/qmldesigner/designercore/include/annotation.h index 95fcf81fa21..2b7c64bb4a2 100644 --- a/src/plugins/qmldesigner/designercore/include/annotation.h +++ b/src/plugins/qmldesigner/designercore/include/annotation.h @@ -35,6 +35,7 @@ namespace QmlDesigner { static const PropertyName customIdProperty = {("customId")}; static const PropertyName annotationProperty = {("annotation")}; +static const PropertyName globalAnnotationProperty = {("globalAnnotation")}; class Comment { diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index ac65de0240f..85302ad2c42 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -202,6 +202,11 @@ public: void setAnnotation(const Annotation &annotation); void removeAnnotation(); + Annotation globalAnnotation() const; + bool hasGlobalAnnotation() const; + void setGlobalAnnotation(const Annotation &annotation); + void removeGlobalAnnotation(); + qint32 internalId() const; void setNodeSource(const QString&); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 61d0f93cf56..1d1a5386f21 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1150,6 +1150,35 @@ void ModelNode::removeAnnotation() } } +Annotation ModelNode::globalAnnotation() const +{ + Annotation result; + ModelNode root = view()->rootModelNode(); + + if (hasGlobalAnnotation()) + result.fromQString(root.auxiliaryData(globalAnnotationProperty).value()); + + return result; +} + +bool ModelNode::hasGlobalAnnotation() const +{ + return view()->rootModelNode().hasAuxiliaryData(globalAnnotationProperty); +} + +void ModelNode::setGlobalAnnotation(const Annotation &annotation) +{ + view()->rootModelNode().setAuxiliaryData(globalAnnotationProperty, + QVariant::fromValue(annotation.toQString())); +} + +void ModelNode::removeGlobalAnnotation() +{ + if (hasGlobalAnnotation()) { + view()->rootModelNode().removeAuxiliaryData(globalAnnotationProperty); + } +} + void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { model()->d->setScriptFunctions(internalNode(), scriptFunctionList); diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 9bc485fd906..9f49b0a6d02 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -431,6 +431,19 @@ void DesignModeWidget::setup() m_dockManager->openWorkspace(workspaceComboBox->currentText()); }); + const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::annotationBubble), 36, 36); + toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){ + ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); + + if (node.isValid()) { + m_globalAnnotationEditor.setModelNode(node); + m_globalAnnotationEditor.showWidget(); + } + }); + + + viewManager().enableWidgets(); readSettings(); show(); diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 8e44ce8c49f..67f8f3d554d 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -36,6 +36,7 @@ #include #include +#include namespace Core { class SideBar; @@ -120,6 +121,7 @@ private: // variables ADS::DockManager *m_dockManager = nullptr; ADS::DockWidget *m_outputPaneDockWidget = nullptr; + GlobalAnnotationEditor m_globalAnnotationEditor; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 63fa8a07432..3eb8c5e2d55 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -654,6 +654,8 @@ Project { "annotationeditor/annotationcommenttab.ui", "annotationeditor/annotationeditor.cpp", "annotationeditor/annotationeditor.h", + "annotationeditor/globalannotationeditor.cpp", + "annotationeditor/globalannotationeditor.h", "annotationeditor/annotationeditordialog.cpp", "annotationeditor/annotationeditordialog.h", "annotationeditor/annotationeditordialog.ui", From 652596dc16718875d20d62657c32e7b0abdb4822 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 21 Apr 2020 13:30:44 +0300 Subject: [PATCH 06/28] QmlDesigner: Update UI when an asset is reimported Item library thumbnail and navigator icon are refreshed if asset is reimported, and the puppet is restarted in case the asset is in use. Change-Id: Ic5577eff3a24bcce87b504e9c059c29f6cb887a5 Fixes: QDS-1957 Reviewed-by: Thomas Hartmann --- .../itemLibraryQmlSources/ItemDelegate.qml | 2 ++ .../components/edit3d/edit3dview.cpp | 11 +++++++++++ .../qmldesigner/components/edit3d/edit3dview.h | 1 + .../importmanager/importmanagerview.cpp | 2 -- .../itemlibrary/itemlibraryassetimporter.cpp | 18 ++++++++++++------ .../navigator/navigatormodelinterface.h | 1 + .../navigator/navigatortreemodel.cpp | 5 +++++ .../components/navigator/navigatortreemodel.h | 1 + .../components/navigator/navigatorview.cpp | 11 +++++++++++ .../components/navigator/navigatorview.h | 2 ++ .../metainfo/subcomponentmanager.cpp | 3 +-- 11 files changed, 47 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 37f97126785..cd3ddef34db 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -47,6 +47,8 @@ Item { width: itemLibraryIconWidth // to be set in Qml context height: itemLibraryIconHeight // to be set in Qml context source: itemLibraryIconPath // to be set by model + + cache: false // Allow thumbnail to be dynamically updated } Text { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index c8e1e222e54..52a286a16ab 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -179,6 +179,17 @@ void Edit3DView::importsChanged(const QList &addedImports, checkImports(); } +void Edit3DView::customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &data) +{ + Q_UNUSED(view) + Q_UNUSED(nodeList) + Q_UNUSED(data) + + if (identifier == "asset_import_update") + resetPuppet(); +} + void Edit3DView::sendInputEvent(QInputEvent *e) const { if (nodeInstanceView()) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index d1646470b75..6c3ae892a91 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -58,6 +58,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; void importsChanged(const QList &addedImports, const QList &removedImports) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; void sendInputEvent(QInputEvent *e) const; void edit3DViewResized(const QSize &size) const; diff --git a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp index df9590af646..f26178c4562 100644 --- a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp +++ b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp @@ -87,8 +87,6 @@ void ImportManagerView::importsChanged(const QList &/*addedImports*/, co void ImportManagerView::possibleImportsChanged(const QList &/*possibleImports*/) { - QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); - if (m_importsWidget) m_importsWidget->setPossibleImports(model()->possibleImports()); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 34fa71222d6..374de839189 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -512,18 +512,24 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() addInfo(progressTitle); notifyProgress(0, progressTitle); - // There is an inbuilt delay before rewriter change actually updates the data model, - // so we need to wait for a moment to allow the change to take effect. + // First we have to wait a while to ensure qmljs detects new files and updates its + // internal model. Then we make a non-change to the document to trigger qmljs snapshot + // update. There is an inbuilt delay before rewriter change actually updates the data + // model, so we need to wait for another moment to allow the change to take effect. // Otherwise subsequent subcomponent manager update won't detect new imports properly. QTimer *timer = new QTimer(parent()); static int counter; counter = 0; - timer->callOnTimeout([this, timer, progressTitle, model]() { + timer->callOnTimeout([this, timer, progressTitle, model, doc]() { if (!isCancelled()) { - notifyProgress(++counter * 10, progressTitle); - if (counter >= 10) { - // Trigger underlying qmljs snapshot update by making a non-change to the doc + notifyProgress(++counter * 5, progressTitle); + if (counter == 10) { model->rewriterView()->textModifier()->replace(0, 0, {}); + } else if (counter == 19) { + doc->updateSubcomponentManager(); + } else if (counter >= 20) { + if (!m_overwrittenImports.isEmpty()) + model->rewriterView()->emitCustomNotification("asset_import_update"); timer->stop(); notifyFinished(); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h index bedcc2f561a..d560eb2824a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h +++ b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h @@ -43,6 +43,7 @@ public: virtual void notifyModelNodesRemoved(const QList &modelNodes) = 0; virtual void notifyModelNodesInserted(const QList &modelNodes) = 0; virtual void notifyModelNodesMoved(const QList &modelNodes) = 0; + virtual void notifyIconsChanged() = 0; virtual void setFilter(bool showObjects) = 0; virtual void resetModel() = 0; }; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index c91ff78626f..70d2e405f9d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -695,6 +695,11 @@ void NavigatorTreeModel::notifyModelNodesMoved(const QList &modelNode emit layoutChanged(indexes); } +void NavigatorTreeModel::notifyIconsChanged() +{ + emit dataChanged(index(0, 0), index(rowCount(), 0), {Qt::DecorationRole}); +} + void NavigatorTreeModel::setFilter(bool showOnlyVisibleItems) { m_showOnlyVisibleItems = showOnlyVisibleItems; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index f10198adcc0..15e89d36367 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -87,6 +87,7 @@ public: void notifyModelNodesRemoved(const QList &modelNodes) override; void notifyModelNodesInserted(const QList &modelNodes) override; void notifyModelNodesMoved(const QList &modelNodes) override; + void notifyIconsChanged() override; void setFilter(bool showOnlyVisibleItems) override; void resetModel() override; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 86404e0ea0a..e953d6b6df5 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -147,6 +147,17 @@ void NavigatorView::bindingPropertiesChanged(const QList & prop } } +void NavigatorView::customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &data) +{ + Q_UNUSED(view) + Q_UNUSED(nodeList) + Q_UNUSED(data) + + if (identifier == "asset_import_update") + m_currentModelInterface->notifyIconsChanged(); +} + void NavigatorView::handleChangedExport(const ModelNode &modelNode, bool exported) { const ModelNode rootNode = rootModelNode(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index 852dddc70fc..529d263d407 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -84,6 +84,8 @@ public: void bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; + void handleChangedExport(const ModelNode &modelNode, bool exported); bool isNodeInvisible(const ModelNode &modelNode) const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index ff65187f46e..b4b043847e2 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -420,8 +420,7 @@ void SubComponentManager::parseQuick3DAssetDir(const QString &assetPath) itemLibraryEntry.addHints(hints); } - if (!model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry)) - model()->metaInfo().itemLibraryInfo()->addEntries({itemLibraryEntry}); + model()->metaInfo().itemLibraryInfo()->addEntries({itemLibraryEntry}, true); } } } From 0cac09e757251cf77050dcd789a4f10aefeec2a2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 21 Apr 2020 15:12:22 +0300 Subject: [PATCH 07/28] QmlDesigner: Fix importing existing asset with different capitalization In case sensitive file systems, revert to old capitalization after reimporting existing asset with different capitalization. Change-Id: If26443b34e408c84aeb12940262c8b9f696f0bbf Fixes: QDS-1958 Reviewed-by: Thomas Hartmann --- .../itemlibrary/itemlibraryassetimporter.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 374de839189..0f484a11324 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -280,7 +280,16 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar return; } + QString originalAssetName = assetName; if (targetDir.exists(assetName)) { + // If we have a file system with case insensitive filenames, assetName may be + // different from the existing name. Modify assetName to ensure exact match to + // the overwritten old asset capitalization + const QStringList assetDirs = targetDir.entryList({assetName}, QDir::Dirs); + if (assetDirs.size() == 1) { + assetName = assetDirs[0]; + targetDirPath = targetDir.filePath(assetName); + } if (!confirmAssetOverwrite(assetName)) { addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName)); return; @@ -306,6 +315,13 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar return; } + if (originalAssetName != assetName) { + // Fix the generated qml file name + const QString assetQml = originalAssetName + ".qml"; + if (outDir.exists(assetQml)) + outDir.rename(assetQml, assetName + ".qml"); + } + QHash assetFiles; const int outDirPathSize = outDir.path().size(); auto insertAsset = [&](const QString &filePath) { From d39f89d13bab9abeb431eb4d6508300dc82f6c6e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 21 Apr 2020 16:45:48 +0300 Subject: [PATCH 08/28] QmlDesigner: Reset quick3D importer between each file Quick3D importer caches various things when it does the import, making reusing the instance for multiple imports error prone. Change-Id: I6e8e3dedf579683cdda58624172395a392f4e567 Fixes: QDS-1960 Reviewed-by: Thomas Hartmann --- .../components/itemlibrary/itemlibraryassetimporter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 0f484a11324..6a5edb18a39 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -315,6 +315,9 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar return; } + // The importer is reset after every import to avoid issues with it caching various things + m_quick3DAssetImporter.reset(new QSSGAssetImportManager); + if (originalAssetName != assetName) { // Fix the generated qml file name const QString assetQml = originalAssetName + ".qml"; From 031d12cc5acc9f7464d1ad0cae93c834b7eeb2b9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 Apr 2020 11:33:59 +0300 Subject: [PATCH 09/28] QmlDesigner: Fix importing asset when open doc is in project subfolder Change-Id: I9853dd9c6b919d2061e77bee12bfc6345848c846 Fixes: QDS-1823 Reviewed-by: Thomas Hartmann --- .../itemlibraryassetimportdialog.cpp | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 3f403d03133..0beb5bce729 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -32,6 +32,9 @@ #include "utils/outputformatter.h" #include "theme.h" +#include +#include + #include #include #include @@ -97,6 +100,20 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); + QStringList importPaths; + auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); + if (doc) { + Model *model = doc->currentModel(); + if (model) + importPaths = model->importPaths(); + } + + QString targetDir = defaulTargetDirectory; + + ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(doc->fileName()); + if (currentProject) + targetDir = currentProject->projectDirectory().toString(); + // Import is always done under known folder. The order of preference for folder is: // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path @@ -105,19 +122,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER); const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); - QString candidatePath = defaulTargetDirectory + defaultAssetFolder + quick3DFolder; + QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder; int candidatePriority = 5; - QStringList importPaths; - - auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); - if (doc) { - Model *model = doc->currentModel(); - if (model) - importPaths = model->importPaths(); - } for (auto importPath : qAsConst(importPaths)) { - if (importPath.startsWith(defaulTargetDirectory)) { + if (importPath.startsWith(targetDir)) { const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder); const QString assetFolder = importPath + quick3DFolder; const bool exists = QFileInfo(assetFolder).exists(); From ff211e57f42e20fc4fc855e3a195f1c3b3027b5e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 22 Apr 2020 12:47:19 +0200 Subject: [PATCH 10/28] QmlDesigner: Adjust icon size The icons were scaled down and became really muddy on low dpi screens. With Hennings additions we create high dpi versions behind the scene in any case. Change-Id: Iab94989b6cfae7b5cefcd7bcded4efa3a1d35a6f Reviewed-by: Thomas Hartmann --- .../components/richtexteditor/richtexteditor.cpp | 2 +- .../components/richtexteditor/richtexteditor.ui | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp index 9ad7c920636..af04bb676a4 100644 --- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp @@ -164,7 +164,7 @@ QIcon RichTextEditor::getIcon(Theme::Icon icon) { const QString fontName = "qtds_propertyIconFont.ttf"; - return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 36, 36); + return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 20, 20); } QString RichTextEditor::richText() const diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui index 25a7b5d244a..3c21f9147fb 100644 --- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.ui @@ -24,8 +24,8 @@ - 36 - 36 + 20 + 20 @@ -34,8 +34,8 @@ - 36 - 36 + 20 + 20 From 51993ea62d9e2d004e368999ef8fbddca6ae0034 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 Apr 2020 17:50:17 +0300 Subject: [PATCH 11/28] QmlDesigner: Fix import removal issues Fixes the issue of removing all versions of import when one is removed via item library. Also fixes a bug in item library that allowed removal of used imports in some situations, such as directly after another import was removed. Change-Id: I0ad879c8be708873b716fe6326655bcbc66a566a Fixes: QDS-1989 Reviewed-by: Thomas Hartmann --- .../importmanager/importmanagerview.cpp | 6 ++++- .../filemanager/changeimportsvisitor.cpp | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp index f26178c4562..c2bce0b0ce5 100644 --- a/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp +++ b/src/plugins/qmldesigner/components/importmanager/importmanagerview.cpp @@ -81,8 +81,12 @@ void ImportManagerView::modelAboutToBeDetached(Model *model) void ImportManagerView::importsChanged(const QList &/*addedImports*/, const QList &/*removedImports*/) { - if (m_importsWidget) + if (m_importsWidget) { m_importsWidget->setImports(model()->imports()); + // setImports recreates labels, so we need to update used imports, as it is not guaranteed + // usedImportsChanged notification will come after this. + m_importsWidget->setUsedImports(model()->usedImports()); + } } void ImportManagerView::possibleImportsChanged(const QList &/*possibleImports*/) diff --git a/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp index a35ff98ef4b..cc82a5607eb 100644 --- a/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp +++ b/src/plugins/qmldesigner/designercore/filemanager/changeimportsvisitor.cpp @@ -85,10 +85,25 @@ bool ChangeImportsVisitor::remove(QmlJS::AST::UiProgram *ast, const Import &impo bool ChangeImportsVisitor::equals(QmlJS::AST::UiImport *ast, const Import &import) { + bool equal = false; if (import.isLibraryImport()) - return toString(ast->importUri) == import.url(); + equal = toString(ast->importUri) == import.url(); else if (import.isFileImport()) - return ast->fileName == import.file(); - else - return false; + equal = ast->fileName == import.file(); + + if (equal) { + equal = (!ast->version || (ast->version->minorVersion == 0 && ast->version->majorVersion == 0)) + && import.version().isEmpty(); + if (!equal && ast->version) { + const QStringList versions = import.version().split('.'); + if (versions.size() >= 1 && versions[0].toInt() == ast->version->majorVersion) { + if (versions.size() >= 2) + equal = versions[1].toInt() == ast->version->minorVersion; + else + equal = ast->version->minorVersion == 0; + } + } + } + + return equal; } From 923bd79323eedb982ff9c2fe1cdda6d8e7819a39 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 Apr 2020 14:12:15 +0300 Subject: [PATCH 12/28] QmlDesigner: Don't expand the selected node on selection change Point of expanding items at selection change is to ensure the selected item is visible. It is not necessary to also expand the selected item. Change-Id: I2435daf4845b5066ddb41a4aeae71d66e12fd94d Fixes: QDS-1940 Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/navigator/navigatorview.cpp | 6 +++--- .../qmldesigner/components/navigator/navigatorview.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index e953d6b6df5..1c2d73be6dc 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -442,7 +442,7 @@ void NavigatorView::updateItemSelection() // make sure selected nodes a visible foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) { if (selectedIndex.column() == 0) - expandRecursively(selectedIndex); + expandAncestors(selectedIndex); } } @@ -466,9 +466,9 @@ bool NavigatorView::blockSelectionChangedSignal(bool block) return oldValue; } -void NavigatorView::expandRecursively(const QModelIndex &index) +void NavigatorView::expandAncestors(const QModelIndex &index) { - QModelIndex currentIndex = index; + QModelIndex currentIndex = index.parent(); while (currentIndex.isValid()) { if (!treeWidget()->isExpanded(currentIndex)) treeWidget()->expand(currentIndex); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index 529d263d407..3bafe0fa80b 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -110,7 +110,7 @@ protected: //functions QTreeView *treeWidget() const; NavigatorTreeModel *treeModel(); bool blockSelectionChangedSignal(bool block); - void expandRecursively(const QModelIndex &index); + void expandAncestors(const QModelIndex &index); void reparentAndCatch(NodeAbstractProperty property, const ModelNode &modelNode); void setupWidget(); From a2481386850e486af977ad90345426069488cc73 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 Apr 2020 12:21:35 +0300 Subject: [PATCH 13/28] QmlDesigner: Make 3D edit view onboarding text translatable Also add a potential related nullptr check. Change-Id: I8c6e4facbdb0c2d94f7c2af54f83eef0eca0b796 Fixes: QDS-1993 Reviewed-by: Mahmoud Badri Reviewed-by: Leena Miettinen --- .../qmldesigner/components/edit3d/edit3dview.cpp | 16 +++++++++------- .../components/edit3d/edit3dwidget.cpp | 11 +++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 52a286a16ab..9e882be2fde 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -312,14 +312,16 @@ QVector Edit3DView::rightActions() const void Edit3DView::addQuick3DImport() { - const QList imports = model()->possibleImports(); - for (const auto &import : imports) { - if (import.url() == "QtQuick3D") { - model()->changeImports({import}, {}); + if (model()) { + const QList imports = model()->possibleImports(); + for (const auto &import : imports) { + if (import.url() == "QtQuick3D") { + model()->changeImports({import}, {}); - // Subcomponent manager update needed to make item library entries appear - QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); - return; + // Subcomponent manager update needed to make item library entries appear + QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); + return; + } } } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index c9482272335..3d0d3f2f640 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -106,12 +106,11 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : // Onboarding label contains instructions for new users how to get 3D content into the project m_onboardingLabel = new QLabel(this); QString labelText = - "No 3D import here yet!

" - "To create a 3D View you need to add the QtQuick3D import to your file.
" - "You can add the import via the QML Imports tab of the Library view, or alternatively click" - " here " - "to add it straight away.

" - "If you want to import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view."; + tr("Your file does not import Qt Quick 3D.

" + "To create a 3D view, add the QtQuick3D import to your file in the QML Imports tab of the Library view. Or click" + " here " + "here to add it immediately.

" + "To import 3D assets from another tool, click on the \"Add New Assets...\" button in the Assets tab of the Library view."); m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name())); m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); connect(m_onboardingLabel, &QLabel::linkActivated, this, &Edit3DWidget::linkActivated); From 742d2d7086e01e1ed7361853aa93ed9a5d602fda Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Wed, 22 Apr 2020 19:05:42 +0200 Subject: [PATCH 14/28] QmlDesigner Timeline Onboarding text Change-Id: I06e3c357ec257b33b51c9d4c6a62fd7ebdc107fb Task: QDS-1890 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- .../timelineeditor/timelinewidget.cpp | 48 +++++++++++++++++++ .../timelineeditor/timelinewidget.h | 2 + 2 files changed, 50 insertions(+) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 098fe766a02..d399eb19527 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -58,6 +58,7 @@ #include #include #include +#include namespace QmlDesigner { @@ -118,6 +119,7 @@ TimelineWidget::TimelineWidget(TimelineView *view) , m_timelineView(view) , m_graphicsScene(new TimelineGraphicsScene(this)) , m_addButton(new QPushButton(this)) + , m_onboardingContainer(new QWidget(this)) { setWindowTitle(tr("Timeline", "Title of timeline view")); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -185,6 +187,50 @@ TimelineWidget::TimelineWidget(TimelineView *view) m_addButton->setFlat(true); m_addButton->setFixedSize(32, 32); + + widgetLayout->addWidget(m_onboardingContainer); + + auto *onboardingTopLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomIcon = new QLabel(m_onboardingContainer); + + auto *onboardingLayout = new QVBoxLayout; + auto *onboardingSublayout = new QHBoxLayout; + auto *leftSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *rightSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *topSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + auto *bottomSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + + QString labelText = + tr("This file does not contain a timeline.

\ + To create an animation, add a timeline by clicking the + button."); + onboardingTopLabel->setText(labelText); + onboardingTopLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + + m_onboardingContainer->setLayout(onboardingLayout); + onboardingLayout->setContentsMargins(0, 0, 0, 0); + onboardingLayout->setSpacing(0); + onboardingLayout->addSpacerItem(topSpacer); + onboardingLayout->addWidget(onboardingTopLabel); + onboardingLayout->addLayout(onboardingSublayout); + + onboardingSublayout->setContentsMargins(0, 0, 0, 0); + onboardingSublayout->setSpacing(0); + onboardingSublayout->addSpacerItem(leftSpacer); + + onboardingBottomLabel->setAlignment(Qt::AlignRight | Qt::AlignTop); + onboardingBottomLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomLabel); + onboardingBottomLabel->setText(tr("To edit the timeline settings, click ")); + + onboardingBottomIcon->setAlignment(Qt::AlignLeft | Qt::AlignTop); + onboardingBottomIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomIcon); + onboardingBottomIcon->setPixmap(TimelineIcons::ANIMATION.pixmap()); + + onboardingSublayout->addSpacerItem(rightSpacer); + onboardingLayout->addSpacerItem(bottomSpacer); + widgetLayout->addLayout(contentLayout); this->setLayout(widgetLayout); @@ -532,6 +578,7 @@ void TimelineWidget::setTimelineActive(bool b) m_rulerView->setVisible(true); m_scrollbar->setVisible(true); m_addButton->setVisible(false); + m_onboardingContainer->setVisible(false); m_graphicsView->update(); m_rulerView->update(); } else { @@ -540,6 +587,7 @@ void TimelineWidget::setTimelineActive(bool b) m_rulerView->setVisible(false); m_scrollbar->setVisible(false); m_addButton->setVisible(true); + m_onboardingContainer->setVisible(true); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h index 681181dbe91..4d0e4711cbe 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h @@ -104,6 +104,8 @@ private: TimelineGraphicsScene *m_graphicsScene; QPushButton *m_addButton = nullptr; + + QWidget *m_onboardingContainer = nullptr; }; } // namespace QmlDesigner From d1a7afa28fb1050b7e72cad0aa9ab2ff9dbc5287 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 23 Apr 2020 15:40:41 +0300 Subject: [PATCH 15/28] QmlDesigner: Add "closeup" option to focusObjectToCamera function The closeup option is used for itemlibrary thumbnail generation, where the regular fit produces too small images on most imports. Change-Id: I2901a2e20d100b155111f002b22489df945e9e01 Fixes: QDS-1899 Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri --- share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml | 2 +- .../qml/qmlpuppet/mockfiles/EditCameraController.qml | 5 +++-- share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml | 2 +- share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml | 2 +- .../qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp | 6 ++++-- .../qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h | 5 +++-- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml index 7481d242252..7ae7bd44d91 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/AxisHelper.qml @@ -121,7 +121,7 @@ View3D { pick(mouse); if (pickObj) { axisHelperView.editCameraCtrl.focusObject(axisHelperView.selectedNode, - pickObj.cameraRotation, false); + pickObj.cameraRotation, false, false); } else { mouse.accepted = false; } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml index 207dc941994..20dd112d614 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditCameraController.qml @@ -87,14 +87,15 @@ Item { } - function focusObject(targetObject, rotation, updateZoom) + function focusObject(targetObject, rotation, updateZoom, closeUp) { if (!camera) return; camera.eulerRotation = rotation; var newLookAtAndZoom = _generalHelper.focusObjectToCamera( - camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, updateZoom); + camera, _defaultCameraLookAtDistance, targetObject, view3d, _zoomFactor, + updateZoom, closeUp); _lookAtPoint = newLookAtAndZoom.toVector3d(); _zoomFactor = newLookAtAndZoom.w; storeCameraState(0); diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index db8d4fb4eab..6527d06e6e7 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -146,7 +146,7 @@ Item { if (editView) { var targetNode = selectionBoxes.length > 0 ? selectionBoxes[0].model : null; - cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true); + cameraControl.focusObject(targetNode, editView.camera.eulerRotation, true, false); } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml index b16b27406d9..afaf8481ca6 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/IconRenderer3D.qml @@ -42,7 +42,7 @@ Item { function fitAndHideBox() : bool { - cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true); + cameraControl.focusObject(selectionBox.model, viewCamera.eulerRotation, true, true); if (cameraControl._zoomFactor < 0.1) { view3D.importScene.scale = view3D.importScene.scale.times(10); return false; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index f99d219c5c5..f62a7e51736 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -148,7 +148,7 @@ float GeneralHelper::zoomCamera(QQuick3DCamera *camera, float distance, float de // Return value contains new lookAt point (xyz) and zoom factor (w) QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance, QQuick3DNode *targetObject, QQuick3DViewport *viewPort, - float oldZoom, bool updateZoom) + float oldZoom, bool updateZoom, bool closeUp) { if (!camera) return QVector4D(0.f, 0.f, 0.f, 1.f); @@ -200,7 +200,9 @@ QVector4D GeneralHelper::focusObjectToCamera(QQuick3DCamera *camera, float defau camera->setPosition(lookAt + newLookVector); - float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / 900.), 100.f) : oldZoom; + qreal divisor = closeUp ? 900. : 725.; + + float newZoomFactor = updateZoom ? qBound(.01f, float(maxExtent / divisor), 100.f) : oldZoom; float cameraZoomFactor = zoomCamera(camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); return QVector4D(lookAt, cameraZoomFactor); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index e9d8e525458..15795ac28bd 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -66,8 +66,9 @@ public: float defaultLookAtDistance, const QVector3D &lookAt, float zoomFactor, bool relative); Q_INVOKABLE QVector4D focusObjectToCamera(QQuick3DCamera *camera, float defaultLookAtDistance, - QQuick3DNode *targetObject, QQuick3DViewport *viewPort, - float oldZoom, bool updateZoom = true); + QQuick3DNode *targetObject, QQuick3DViewport *viewPort, + float oldZoom, bool updateZoom = true, + bool closeUp = false); Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property, const QVariant& value); Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode); From 5578a79953474f5e34b21c7a7b9f5ed4c8d87831 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 24 Apr 2020 13:18:52 +0300 Subject: [PATCH 16/28] QmlDesigner: Fix crash on some operations when done on 3D nodes Added a node validity check on some operations that convert ModelNode to QmlItemNode. Change-Id: Ia60aaa2df71db9fd0e7edbbde11e7f8b11559aa6 Fixes: QDS-1894 Reviewed-by: Mahmoud Badri --- .../componentcore/modelnodeoperations.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 2c239a8aa94..43efe42069b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -311,8 +311,10 @@ void resetSize(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetSize",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("width"); - itemNode.removeProperty("height"); + if (itemNode.isValid()) { + itemNode.removeProperty("width"); + itemNode.removeProperty("height"); + } } }); } @@ -325,8 +327,10 @@ void resetPosition(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetPosition",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("x"); - itemNode.removeProperty("y"); + if (itemNode.isValid()) { + itemNode.removeProperty("x"); + itemNode.removeProperty("y"); + } } }); } @@ -348,7 +352,8 @@ void resetZ(const SelectionContext &selectionState) selectionState.view()->executeInTransaction("DesignerActionManager|resetZ",[selectionState](){ foreach (ModelNode node, selectionState.selectedModelNodes()) { QmlItemNode itemNode(node); - itemNode.removeProperty("z"); + if (itemNode.isValid()) + itemNode.removeProperty("z"); } }); } From 14b86100b52990570308f0ec3f7c727801d4ab85 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 16 Apr 2020 18:19:11 +0200 Subject: [PATCH 17/28] QmlDesigner: Add action for setting initial FlowItem Change-Id: I23170f08b45a9560c3d6cae6f6d56396de9aab8b Reviewed-by: Thomas Hartmann --- .../componentcore/componentcore_constants.h | 2 ++ .../componentcore/designeractionmanager.cpp | 11 +++++++++++ .../componentcore/modelnodeoperations.cpp | 19 ++++++++++++++++++- .../componentcore/modelnodeoperations.h | 1 + 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 2965bcd90b3..f2f0885fabd 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -57,6 +57,7 @@ const char anchorsFillCommandId[] = "AnchorsFill"; const char anchorsResetCommandId[] = "AnchorsReset"; const char removePositionerCommandId[] = "RemovePositioner"; const char createFlowActionAreaCommandId[] = "CreateFlowActionArea"; +const char setFlowStartCommandId[] = "SetFlowStart"; const char layoutRowPositionerCommandId[] = "LayoutRowPositioner"; const char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner"; const char layoutGridPositionerCommandId[] = "LayoutGridPositioner"; @@ -124,6 +125,7 @@ const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerCon const char layoutFlowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position in Flow"); const char removePositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Positioner"); const char createFlowActionAreaDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Flow Action"); +const char setFlowStartDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Flow Start"); const char removeLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Layout"); const char addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Item"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 42f95952db1..9b07ebff385 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -874,6 +874,17 @@ void DesignerActionManager::createDefaultDesignerActions() &isFlowItem, &flowOptionVisible)); + addDesignerAction(new ModelNodeContextMenuAction( + setFlowStartCommandId, + setFlowStartDisplayName, + {}, + flowCategory, + priorityFirst, + {}, + &setFlowStartItem, + &isFlowItem, + &flowOptionVisible)); + addDesignerAction(new FlowActionConnectAction( flowConnectionCategoryDisplayName, flowConnectionCategory, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 43efe42069b..48a409a2a90 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1100,7 +1100,24 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ container.nodeProperty("effect").reparentHere(effectNode); view->setSelectedModelNode(effectNode); } - }); + }); +} + +void setFlowStartItem(const SelectionContext &selectionContext) +{ + AbstractView *view = selectionContext.view(); + + QTC_ASSERT(view && selectionContext.hasSingleSelectedModelNode(), return); + ModelNode node = selectionContext.currentSingleSelectedNode(); + QTC_ASSERT(node.isValid(), return); + QTC_ASSERT(node.metaInfo().isValid(), return); + QmlFlowItemNode flowItem(node); + QTC_ASSERT(flowItem.isValid(), return); + QTC_ASSERT(flowItem.flowView().isValid(), return); + view->executeInTransaction("DesignerActionManager:setFlowStartItem", + [&flowItem](){ + flowItem.flowView().setStartFlowItem(flowItem); + }); } } // namespace Mode diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 994110297e0..57fd0ea12ac 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -77,6 +77,7 @@ bool addFontToProject(const QStringList &fileNames, const QString &directory); void createFlowActionArea(const SelectionContext &selectionContext); void addTransition(const SelectionContext &selectionState); void addFlowEffect(const SelectionContext &selectionState, const TypeName &typeName); +void setFlowStartItem(const SelectionContext &selectionContext); } // namespace ModelNodeOperationso } //QmlDesigner From 49cfa2d05029777ebc32dad6375fc2000e631a47 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 15 Apr 2020 19:25:06 +0200 Subject: [PATCH 18/28] QmlDesigner: Move flow effects to submenu Change-Id: I2afdcc5068501b7b29543228668b543b0b68a4d1 Reviewed-by: Thomas Hartmann --- .../componentcore/componentcore_constants.h | 2 ++ .../componentcore/designeractionmanager.cpp | 27 ++++++++++++++----- .../modelnodecontextmenu_helper.h | 8 +++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index f2f0885fabd..c0f7af85c26 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -41,6 +41,7 @@ const char anchorsCategory[] = "Anchors"; const char positionCategory[] = "Position"; const char layoutCategory[] = "Layout"; const char flowCategory[] = "Flow"; +const char flowEffectCategory[] = "FlowEffect"; const char flowConnectionCategory[] = "FlowConnection"; const char stackedContainerCategory[] = "StackedContainer"; const char genericToolBarCategory[] = "GenericToolBar"; @@ -86,6 +87,7 @@ const char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextM const char positionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Position"); const char layoutCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout"); const char flowCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow"); +const char flowEffectCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow Effects"); const char stackedContainerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stacked Container"); const char cutSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Cut"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 9b07ebff385..cf3fb5e1524 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -346,6 +346,12 @@ bool isFlowItem(const SelectionContext &context) && QmlFlowItemNode::isValidQmlFlowItemNode(context.currentSingleSelectedNode()); } +bool isFlowTarget(const SelectionContext &context) +{ + return context.singleNodeIsSelected() + && QmlFlowTargetNode::isFlowEditorTarget(context.currentSingleSelectedNode()); +} + bool isFlowTransitionItem(const SelectionContext &context) { return context.singleNodeIsSelected() @@ -362,9 +368,9 @@ bool isFlowActionItemItem(const SelectionContext &context) || QmlVisualNode::isFlowWildcard(selectedNode)); } -bool isFlowItemOrTransition(const SelectionContext &context) +bool isFlowTargetOrTransition(const SelectionContext &context) { - return isFlowItem(context) || isFlowTransitionItem(context); + return isFlowTarget(context) || isFlowTransitionItem(context); } class FlowActionConnectAction : public ActionGroup @@ -853,15 +859,24 @@ void DesignerActionManager::createDefaultDesignerActions() priorityLayoutCategory, &layoutOptionVisible)); - //isFlowTransitionItem - addDesignerAction(new ActionGroup( flowCategoryDisplayName, flowCategory, priorityFlowCategory, - &isFlowItemOrTransition, + &isFlowTargetOrTransition, &flowOptionVisible)); + + auto effectMenu = new ActionGroup( + flowEffectCategoryDisplayName, + flowEffectCategory, + priorityFlowCategory, + &isFlowTransitionItem, + &flowOptionVisible); + + effectMenu->setCategory(flowCategory); + addDesignerAction(effectMenu); + addDesignerAction(new ModelNodeFormEditorAction( createFlowActionAreaCommandId, createFlowActionAreaDisplayName, @@ -1186,7 +1201,7 @@ void DesignerActionManager::addTransitionEffectAction(const TypeName &typeName) QByteArray(ComponentCoreConstants::flowAssignEffectCommandId) + typeName, QLatin1String(ComponentCoreConstants::flowAssignEffectDisplayName) + typeName, {}, - ComponentCoreConstants::flowCategory, + ComponentCoreConstants::flowEffectCategory, {}, typeName == "None" ? 100 : 140, [typeName](const SelectionContext &context) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 67a53ef9972..59029400e5a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -132,16 +132,22 @@ public: bool isVisible(const SelectionContext &m_selectionState) const override { return m_visibility(m_selectionState); } bool isEnabled(const SelectionContext &m_selectionState) const override { return m_enabled(m_selectionState); } - QByteArray category() const override { return QByteArray(); } + QByteArray category() const override { return m_category; } QByteArray menuId() const override { return m_menuId; } int priority() const override { return m_priority; } Type type() const override { return ContextMenu; } + void setCategory(const QByteArray &catageoryId) + { + m_category = catageoryId; + } + private: const QByteArray m_menuId; const int m_priority; SelectionContextPredicate m_enabled; SelectionContextPredicate m_visibility; + QByteArray m_category; }; class SeperatorDesignerAction : public AbstractAction From 8c50ceb294dbdfd7a50e81d3d7d6277b414c16b7 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 16 Apr 2020 18:17:28 +0200 Subject: [PATCH 19/28] QmlDesigner: Add setStartFlowItem Change-Id: I6ad377bcca4e1db84a802909c1b5d8b9ccfe5a14 Reviewed-by: Tim Jenssen --- .../designercore/include/qmlitemnode.h | 5 ++ .../designercore/model/qmlitemnode.cpp | 56 +++++++++++++++---- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index ef8f1b28ba2..d779cc15bf3 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -182,8 +182,13 @@ public: const QList wildcards() const; const QList decicions() const; QList transitionsForTarget(const ModelNode &modelNode); + QList transitionsForSource(const ModelNode &modelNode); void removeDanglingTransitions(); void removeAllTransitions(); + void setStartFlowItem(const QmlFlowItemNode &flowItem); + ModelNode createTransition(); +protected: + QList transitionsForProperty(const PropertyName &propertyName, const ModelNode &modelNode); }; diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 098f8317a5d..e2f0602cc24 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -664,9 +664,7 @@ QList QmlFlowViewNode::flowItems() const ModelNode QmlFlowViewNode::addTransition(const QmlFlowTargetNode &from, const QmlFlowTargetNode &to) { - ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0); - - nodeListProperty("flowTransitions").reparentHere(transition); + ModelNode transition = createTransition(); QmlFlowTargetNode f = from; QmlFlowTargetNode t = to; @@ -684,8 +682,6 @@ const QList QmlFlowViewNode::transitions() const return modelNode().nodeListProperty("flowTransitions").toModelNodeList(); return {}; - - } const QList QmlFlowViewNode::wildcards() const @@ -706,13 +702,12 @@ const QList QmlFlowViewNode::decicions() const QList QmlFlowViewNode::transitionsForTarget(const ModelNode &modelNode) { - QList list; - for (const ModelNode &transition : transitions()) { - if (transition.hasBindingProperty("to") - && transition.bindingProperty("to").resolveToModelNode() == modelNode) - list.append(transition); - } - return list; + return transitionsForProperty("to", modelNode); +} + +QList QmlFlowViewNode::transitionsForSource(const ModelNode &modelNode) +{ + return transitionsForProperty("from", modelNode); } void QmlFlowViewNode::removeDanglingTransitions() @@ -830,4 +825,41 @@ void QmlFlowViewNode::removeAllTransitions() removeProperty("flowTransitions"); } +void QmlFlowViewNode::setStartFlowItem(const QmlFlowItemNode &flowItem) +{ + QTC_ASSERT(flowItem.isValid(), return); + QmlFlowItemNode item = flowItem; + + ModelNode transition; + + for (const ModelNode &node : transitionsForSource(modelNode())) + transition = node; + if (!transition.isValid()) + transition = createTransition(); + + transition.bindingProperty("from").setExpression(modelNode().validId()); + transition.bindingProperty("to").setExpression(item.validId()); +} + +ModelNode QmlFlowViewNode::createTransition() +{ + ModelNode transition = view()->createModelNode("FlowView.FlowTransition", 1, 0); + nodeListProperty("flowTransitions").reparentHere(transition); + + return transition; +} + +QList QmlFlowViewNode::transitionsForProperty(const PropertyName &propertyName, + const ModelNode &modelNode) +{ + QList list; + for (const ModelNode &transition : transitions()) { + if (transition.hasBindingProperty(propertyName) + && transition.bindingProperty(propertyName).resolveToModelNode() == modelNode) + list.append(transition); + } + return list; + +} + } //QmlDesigner From 88e07d8a7a5bb38207060652e013d50f02629e5c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 14 Apr 2020 18:26:04 +0200 Subject: [PATCH 20/28] QmlDesigner: Implement transition tool Implementing a custom tool to create transitions in flows. Change-Id: I721895c67084707bea8504c657ec9af2b5df2c22 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/formeditor/formeditor.pri | 6 +- .../components/formeditor/transitiontool.cpp | 438 ++++++++++++++++++ .../components/formeditor/transitiontool.h | 98 ++++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 6 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmldesigner/components/formeditor/transitiontool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/transitiontool.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 42feb7c13f7..f6ac4817141 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -243,6 +243,7 @@ extend_qtc_plugin(QmlDesigner snapper.cpp snapper.h snappinglinecreator.cpp snappinglinecreator.h toolbox.cpp toolbox.h + transitiontool.cpp transitiontool.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri index 3464ba3afe4..4609b277f90 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditor.pri +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri @@ -36,7 +36,8 @@ SOURCES += formeditoritem.cpp \ contentnoteditableindicator.cpp \ backgroundaction.cpp \ formeditortoolbutton.cpp \ - formeditorannotationicon.cpp + formeditorannotationicon.cpp \ + transitiontool.cpp HEADERS += formeditorscene.h \ formeditorwidget.h \ @@ -75,6 +76,7 @@ HEADERS += formeditorscene.h \ contentnoteditableindicator.h \ backgroundaction.h \ formeditortoolbutton.h \ - formeditorannotationicon.h + formeditorannotationicon.h \ + transitiontool.h RESOURCES += formeditor.qrc diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp new file mode 100644 index 00000000000..b1cee8c443b --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "transitiontool.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +static bool isTransitionSource(const ModelNode &node) +{ + return QmlFlowTargetNode::isFlowEditorTarget(node); +} + +static bool isTransitionTarget(const QmlItemNode &node) +{ + return QmlFlowTargetNode::isFlowEditorTarget(node) + && !node.isFlowActionArea() + && !node.isFlowWildcard(); +} + +class TransitionToolAction : public AbstractAction +{ +public: + TransitionToolAction(const QString &name) : AbstractAction(name) {} + + QByteArray category() const override + { + return QByteArray(); + } + + QByteArray menuId() const override + { + return "TransitionTool"; + } + + int priority() const override + { + return CustomActionsPriority; + } + + Type type() const override + { + return ContextMenuAction; + } + +protected: + bool isVisible(const SelectionContext &selectionContext) const override + { + if (selectionContext.scenePosition().isNull()) + return false; + + if (selectionContext.singleNodeIsSelected()) + return isTransitionSource(selectionContext.currentSingleSelectedNode()); + + return false; + } + + bool isEnabled(const SelectionContext &selectionContext) const override + { + return isVisible(selectionContext); + } +}; + +class TransitionCustomAction : public TransitionToolAction +{ +public: + TransitionCustomAction(const QString &name) : TransitionToolAction(name) {} + + QByteArray category() const override + { + return ComponentCoreConstants::flowCategory; + } + + SelectionContext selectionContext() const + { + return AbstractAction::selectionContext(); + } + +}; + +static QRectF paintedBoundingRect(FormEditorItem *item) +{ + QRectF boundingRect = item->qmlItemNode().instanceBoundingRect(); + if (boundingRect.width() < 4) + boundingRect = item->boundingRect(); + return boundingRect; +} + +static QPointF centerPoint(FormEditorItem *item) +{ + QRectF boundingRect = paintedBoundingRect(item); + return QPointF(item->scenePos().x() + boundingRect.width() / 2, + item->scenePos().y() + boundingRect.height() / 2); +} + +void static setToBoundingRect(QGraphicsRectItem *rect, FormEditorItem *item) +{ + QPolygonF boundingRectInSceneSpace(item->mapToScene(paintedBoundingRect(item))); + rect->setRect(boundingRectInSceneSpace.boundingRect()); +} + +TransitionTool::TransitionTool() + : QObject(), AbstractCustomTool() +{ + + TransitionToolAction *transitionToolAction = new TransitionToolAction(tr("Add Transition")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(transitionToolAction); + + connect(transitionToolAction->action(), &QAction::triggered, + this, &TransitionTool::activateTool); + + TransitionCustomAction *removeAction = new TransitionCustomAction(tr("Remove Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAction); + + connect(removeAction->action(), &QAction::triggered, + this, [removeAction](){ + + SelectionContext context = removeAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove Transitions", [&node](){ + if (node.isValid()) + node.removeTransitions(); + }); + }); + + TransitionCustomAction *removeAllTransitionsAction = new TransitionCustomAction(tr("Remove All Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAllTransitionsAction); + + connect(removeAllTransitionsAction->action(), &QAction::triggered, + this, [removeAllTransitionsAction](){ + + if (QMessageBox::question(Core::ICore::dialogParent(), + tr("Remove All Transitions"), + tr("Do you really want to remove all transitions?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return; + + SelectionContext context = removeAllTransitionsAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove All Transitions", [&node](){ + if (node.isValid() && node.flowView().isValid()) + node.flowView().removeAllTransitions(); + }); + }); + + TransitionCustomAction *removeDanglingTransitionAction = new TransitionCustomAction(tr("Remove Dangling Transitions")); + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeDanglingTransitionAction); + + connect(removeDanglingTransitionAction->action(), &QAction::triggered, + this, [removeDanglingTransitionAction](){ + + SelectionContext context = removeDanglingTransitionAction->selectionContext(); + QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode()); + + context.view()->executeInTransaction("Remove Dangling Transitions", [&node](){ + if (node.isValid() && node.flowView().isValid()) + node.flowView().removeDanglingTransitions(); + }); + }); +} + +TransitionTool::~TransitionTool() +{ +} + +void TransitionTool::clear() +{ + m_lineItem.reset(nullptr); + m_rectangleItem1.reset(nullptr); + m_rectangleItem2.reset(nullptr); + + AbstractFormEditorTool::clear(); +} + +void TransitionTool::mousePressEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_block) + return; + + if (event->button() != Qt::LeftButton) + return; + + AbstractFormEditorTool::mousePressEvent(itemList, event); + TransitionTool::mouseMoveEvent(itemList, event); +} + +void TransitionTool::mouseMoveEvent(const QList & itemList, + QGraphicsSceneMouseEvent * event) +{ + if (!m_lineItem) + return; + + QTC_ASSERT(currentFormEditorItem(), return); + + const QPointF pos = centerPoint(m_formEditorItem); + lineItem()->setLine(pos.x(), + pos.y(), + event->scenePos().x(), + event->scenePos().y()); + + FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList); + + if (formEditorItem + && formEditorItem->qmlItemNode().isValid() + && isTransitionTarget(formEditorItem->qmlItemNode().modelNode())) { + rectangleItem2()->setVisible(true); + setToBoundingRect(rectangleItem2(), formEditorItem); + } else { + rectangleItem2()->setVisible(false); + } +} + +void TransitionTool::hoverMoveEvent(const QList & itemList, + QGraphicsSceneMouseEvent *event) +{ + mouseMoveEvent(itemList, event); +} + +void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/) +{ +} + +void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ + view()->changeToSelectionTool(); +} + +void TransitionTool::dragLeaveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::dragMoveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void TransitionTool::mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) +{ + if (m_block) + return; + + if (event->button() == Qt::LeftButton) { + FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList); + + if (formEditorItem + && QmlFlowTargetNode(formEditorItem->qmlItemNode().modelNode()).isValid()) + createTransition(m_formEditorItem, formEditorItem); + } + + view()->changeToSelectionTool(); +} + + +void TransitionTool::mouseDoubleClickEvent(const QList &itemList, QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event); +} + +void TransitionTool::itemsAboutToRemoved(const QList &) +{ + view()->changeCurrentToolTo(this); +} + +void TransitionTool::selectedItemsChanged(const QList &itemList) +{ + if (!itemList.isEmpty()) { + createItems(); + + m_formEditorItem = itemList.first(); + setToBoundingRect(rectangleItem1(), m_formEditorItem); + } +} + +void TransitionTool::instancesCompleted(const QList & /*itemList*/) +{ +} + +void TransitionTool::instancesParentChanged(const QList & /*itemList*/) +{ +} + +void TransitionTool::instancePropertyChange(const QList > & /*propertyList*/) +{ +} + +void TransitionTool::formEditorItemsChanged(const QList & /*itemList*/) +{ +} + +int TransitionTool::wantHandleItem(const ModelNode &modelNode) const +{ + if (isTransitionSource(modelNode)) + return 10; + + return 0; +} + +QString TransitionTool::name() const +{ + return tr("Transition Tool"); +} + +void TransitionTool::activateTool() +{ + view()->changeToCustomTool(); +} + +void TransitionTool::unblock() +{ + m_block = false; +} + +QGraphicsLineItem *TransitionTool::lineItem() +{ + return m_lineItem.get(); +} + +QGraphicsRectItem *TransitionTool::rectangleItem1() +{ + return m_rectangleItem1.get(); +} + +QGraphicsRectItem *TransitionTool::rectangleItem2() +{ + return m_rectangleItem2.get(); +} + +FormEditorItem *TransitionTool::currentFormEditorItem() const +{ + if (scene()->items().contains(m_formEditorItem)) + return m_formEditorItem; + + return nullptr; +} + +void TransitionTool::createItems() { + m_block = true; + QTimer::singleShot(200, this, [this](){ unblock(); }); + + if (!lineItem()) + m_lineItem.reset(new QGraphicsLineItem(scene()->manipulatorLayerItem())); + + if (!rectangleItem1()) + m_rectangleItem1.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem())); + + if (!rectangleItem2()) + m_rectangleItem2.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem())); + + m_rectangleItem2->setVisible(false); + + QPen pen; + pen.setColor(QColor(Qt::lightGray)); + pen.setStyle(Qt::DashLine); + pen.setWidth(0); + m_lineItem->setPen(pen); + + pen.setColor(QColor(108, 141, 221)); + pen.setStyle(Qt::SolidLine); + pen.setWidth(4); + pen.setCosmetic(true); + m_rectangleItem1->setPen(pen); + + m_rectangleItem2->setPen(pen); +} + +void TransitionTool::createTransition(FormEditorItem *source, FormEditorItem *target) +{ + QmlFlowTargetNode sourceNode(source->qmlItemNode().modelNode()); + QmlFlowTargetNode targetNode(target->qmlItemNode().modelNode()); + + if (sourceNode.isValid() && targetNode.isValid() + && sourceNode != targetNode + && !targetNode.isFlowActionArea() + && !targetNode.isFlowWildcard()) { + view()->executeInTransaction("create transition", [&sourceNode, targetNode](){ + sourceNode.assignTargetItem(targetNode); + }); + } else { + qWarning() << Q_FUNC_INFO << "nodes invalid"; + } +} + +} diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.h b/src/plugins/qmldesigner/components/formeditor/transitiontool.h new file mode 100644 index 00000000000..6efa1d177a1 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "abstractcustomtool.h" +#include "selectionindicator.h" + +#include +#include +#include + +#include + +namespace QmlDesigner { + +class TransitionTool : public QObject, public AbstractCustomTool +{ + Q_OBJECT +public: + TransitionTool(); + ~TransitionTool(); + + void mousePressEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseDoubleClickEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + + void dragLeaveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + void dragMoveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + + void itemsAboutToRemoved(const QList &itemList) override; + + void selectedItemsChanged(const QList &itemList) override; + + void instancesCompleted(const QList &itemList) override; + void instancesParentChanged(const QList &itemList) override; + void instancePropertyChange(const QList > &propertyList) override; + + void clear() override; + + void formEditorItemsChanged(const QList &itemList) override; + + int wantHandleItem(const ModelNode &modelNode) const override; + + QString name() const override; + + void activateTool(); + void unblock(); + + QGraphicsLineItem *lineItem(); + QGraphicsRectItem *rectangleItem1(); + QGraphicsRectItem *rectangleItem2(); + +private: + FormEditorItem *currentFormEditorItem() const; + void createItems(); + void createTransition(FormEditorItem *item1, FormEditorItem *item2); + + FormEditorItem* m_formEditorItem; + std::unique_ptr m_lineItem; + std::unique_ptr m_rectangleItem1; + std::unique_ptr m_rectangleItem2; + bool m_block = true; +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a65ff3600be..5fd0b7990db 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -242,6 +243,7 @@ bool QmlDesignerPlugin::delayedInitialize() d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TextTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::PathTool); + d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TransitionTool); return true; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 3c434788cd9..c69f5e5c227 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -530,6 +530,8 @@ Project { "formeditor/toolbox.h", "formeditor/formeditortoolbutton.cpp", "formeditor/formeditortoolbutton.h", + "formeditor/transitiontool.cpp", + "formeditor/transitiontool.h", "importmanager/importlabel.cpp", "importmanager/importlabel.h", "importmanager/importmanagercombobox.cpp", From 02226300120a2c3c3795159736ed0b84d46fbb44 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 6 Apr 2020 15:50:54 +0200 Subject: [PATCH 21/28] QmlDesigner: Add fontStyle support Task-number: QDS-1884 Change-Id: Ifb3ec8cf92668adb2e4e5f01e0e0b2403eb4cbac Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/FontSection.qml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml index a97923a032c..4ef079af4c7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml @@ -68,9 +68,12 @@ Section { text: qsTr("Font") } FontComboBox { + id: fontComboBox backendValue: fontSection.fontFamily Layout.fillWidth: true width: 160 + property string familyName: backendValue.value + onFamilyNameChanged: print(styleNamesForFamily(familyName)) } Label { @@ -151,6 +154,7 @@ Section { italic: fontSection.italicStyle underline: fontSection.underlineStyle strikeout: fontSection.strikeoutStyle + enabled: !styleComboBox.styleSet } Label { @@ -175,6 +179,21 @@ Section { backendValue: getBackendValue("weight") model: ["Normal", "Light", "ExtraLight", "Thin", "Medium", "DemiBold", "Bold", "ExtraBold", "Black"] scope: "Font" + enabled: !styleComboBox.styleSet + } + + Label { + text: qsTr("Style name") + toolTip: qsTr("Sets the font's style.") + } + + ComboBox { + id: styleComboBox + property bool styleSet: backendValue.isInModel + Layout.fillWidth: true + backendValue: getBackendValue("styleName") + model: styleNamesForFamily(fontComboBox.familyName) + useString: true } Label { From 9c17fd0dbe6ca18c98741abc27f8729926b95d8c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 24 Apr 2020 19:49:50 +0200 Subject: [PATCH 22/28] QmlDesigner: Extend icon font support * Use ADS::IconProvider to make use of font icons * Add support for IconProvider in FloatingWidgetTitlebar * Add support for named font icon entities in QmlDesigner::Theme * Add level of indirection to Constants.qml to be able to parse it from inside C++ * Add color and HighDPI support in stylehelper * Update icon font and related constants Change-Id: I31ac33917d2db002697ce63f50f5c181c5fdb103 Reviewed-by: Tim Jenssen --- .../imports/StudioTheme/Constants.qml | 96 +------------ .../imports/StudioTheme/InternalConstants.qml | 131 ++++++++++++++++++ .../imports/StudioTheme/icons.ttf | Bin 10568 -> 11448 bytes .../imports/StudioTheme/qmldir | 2 + src/libs/advanceddockingsystem/ads_globals.h | 9 +- .../dockareatitlebar.cpp | 5 +- .../advanceddockingsystem/dockwidgettab.cpp | 16 ++- .../advanceddockingsystem/iconprovider.cpp | 2 +- .../linux/floatingwidgettitlebar.cpp | 24 ++-- .../components/componentcore/theme.cpp | 46 ++++++ .../components/componentcore/theme.h | 82 +++++++++++ src/plugins/qmldesigner/designmodewidget.cpp | 22 +++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 5 + 13 files changed, 321 insertions(+), 119 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml index 3d692af671c..74edf9ccee8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Constants.qml @@ -26,98 +26,4 @@ pragma Singleton import QtQuick 2.10 -QtObject { - readonly property int width: 1920 - readonly property int height: 1080 - readonly property FontLoader mySystemFont: FontLoader { - name: "Arial" - } - readonly property FontLoader controlIcons: FontLoader { - source: "icons.ttf" - } - - readonly property string actionIcon: "\u0021" - readonly property string actionIconBinding: "\u0022" - readonly property string addColumnAfter: "\u0023" - readonly property string addColumnBefore: "\u0024" - readonly property string addFile: "\u0025" - readonly property string addRowAfter: "\u0026" - readonly property string addRowBefore: "\u0027" - readonly property string addTable: "\u0028" - readonly property string alignBottom: "\u0029" - readonly property string alignCenterHorizontal: "\u002A" - readonly property string alignCenterVertical: "\u002B" - readonly property string alignLeft: "\u002C" - readonly property string alignRight: "\u002D" - readonly property string alignTo: "\u002E" - readonly property string alignTop: "\u002F" - readonly property string anchorBaseline: "\u0030" - readonly property string anchorBottom: "\u0031" - readonly property string anchorFill: "\u0032" - readonly property string anchorLeft: "\u0033" - readonly property string anchorRight: "\u0034" - readonly property string anchorTop: "\u0035" - readonly property string annotationBubble: "\u0036" - readonly property string annotationDecal: "\u0037" - readonly property string centerHorizontal: "\u0038" - readonly property string centerVertical: "\u0039" - readonly property string closeCross: "\u003A" - readonly property string deleteColumn: "\u003B" - readonly property string deleteRow: "\u003C" - readonly property string deleteTable: "\u003D" - readonly property string distributeBottom: "\u003E" - readonly property string distributeCenterHorizontal: "\u003F" - readonly property string distributeCenterVertical: "\u0040" - readonly property string distributeLeft: "\u0041" - readonly property string distributeOriginBottomRight: "\u0042" - readonly property string distributeOriginCenter: "\u0043" - readonly property string distributeOriginNone: "\u0044" - readonly property string distributeOriginTopLeft: "\u0045" - readonly property string distributeRight: "\u0046" - readonly property string distributeSpacingHorizontal: "\u0047" - readonly property string distributeSpacingVertical: "\u0048" - readonly property string distributeTop: "\u0049" - readonly property string edit: "\u004A" - readonly property string fontStyleBold: "\u004B" - readonly property string fontStyleItalic: "\u004C" - readonly property string fontStyleStrikethrough: "\u004D" - readonly property string fontStyleUnderline: "\u004E" - readonly property string mergeCells: "\u004F" - readonly property string redo: "\u0050" - readonly property string splitColumns: "\u0051" - readonly property string splitRows: "\u0052" - readonly property string testIcon: "\u0053" - readonly property string textAlignBottom: "\u0054" - readonly property string textAlignCenter: "\u0055" - readonly property string textAlignLeft: "\u0056" - readonly property string textAlignMiddle: "\u0057" - readonly property string textAlignRight: "\u0058" - readonly property string textAlignTop: "\u0059" - readonly property string textBulletList: "\u005A" - readonly property string textFullJustification: "\u005B" - readonly property string textNumberedList: "\u005C" - readonly property string tickIcon: "\u005D" - readonly property string triState: "\u005E" - readonly property string undo: "\u005F" - readonly property string upDownIcon: "\u0060" - readonly property string upDownSquare2: "\u0061" - - readonly property font iconFont: Qt.font({ - "family": controlIcons.name, - "pixelSize": 12 - }) - - readonly property font font: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize - }) - - readonly property font largeFont: Qt.font({ - "family": mySystemFont.name, - "pointSize": Qt.application.font.pixelSize * 1.6 - }) - - readonly property color backgroundColor: "#c2c2c2" - - readonly property bool showActionIndicatorBackground: false -} +InternalConstants {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml new file mode 100644 index 00000000000..4d45032eae9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.10 + +QtObject { + readonly property int width: 1920 + readonly property int height: 1080 + readonly property FontLoader mySystemFont: FontLoader { + name: "Arial" + } + readonly property FontLoader controlIcons: FontLoader { + source: "icons.ttf" + } + + objectName: "internalConstantsObject" + + readonly property string actionIcon: "\u0021" + readonly property string actionIconBinding: "\u0022" + readonly property string addColumnAfter: "\u0023" + readonly property string addColumnBefore: "\u0024" + readonly property string addFile: "\u0025" + readonly property string addRowAfter: "\u0026" + readonly property string addRowBefore: "\u0027" + readonly property string addTable: "\u0028" + readonly property string adsClose: "\u0029" + readonly property string adsDetach: "\u002A" + readonly property string adsDropDown: "\u002B" + readonly property string alignBottom: "\u002C" + readonly property string alignCenterHorizontal: "\u002D" + readonly property string alignCenterVertical: "\u002E" + readonly property string alignLeft: "\u002F" + readonly property string alignRight: "\u0030" + readonly property string alignTo: "\u0031" + readonly property string alignTop: "\u0032" + readonly property string anchorBaseline: "\u0033" + readonly property string anchorBottom: "\u0034" + readonly property string anchorFill: "\u0035" + readonly property string anchorLeft: "\u0036" + readonly property string anchorRight: "\u0037" + readonly property string anchorTop: "\u0038" + readonly property string annotationBubble: "\u0039" + readonly property string annotationDecal: "\u003A" + readonly property string centerHorizontal: "\u003B" + readonly property string centerVertical: "\u003C" + readonly property string closeCross: "\u003D" + readonly property string decisionNode: "\u003E" + readonly property string deleteColumn: "\u003F" + readonly property string deleteRow: "\u0040" + readonly property string deleteTable: "\u0041" + readonly property string detach: "\u0042" + readonly property string distributeBottom: "\u0043" + readonly property string distributeCenterHorizontal: "\u0044" + readonly property string distributeCenterVertical: "\u0045" + readonly property string distributeLeft: "\u0046" + readonly property string distributeOriginBottomRight: "\u0047" + readonly property string distributeOriginCenter: "\u0048" + readonly property string distributeOriginNone: "\u0049" + readonly property string distributeOriginTopLeft: "\u004A" + readonly property string distributeRight: "\u004B" + readonly property string distributeSpacingHorizontal: "\u004C" + readonly property string distributeSpacingVertical: "\u004D" + readonly property string distributeTop: "\u004E" + readonly property string edit: "\u004F" + readonly property string fontStyleBold: "\u0050" + readonly property string fontStyleItalic: "\u0051" + readonly property string fontStyleStrikethrough: "\u0052" + readonly property string fontStyleUnderline: "\u0053" + readonly property string mergeCells: "\u0054" + readonly property string redo: "\u0055" + readonly property string splitColumns: "\u0056" + readonly property string splitRows: "\u0057" + readonly property string startNode: "\u0058" + readonly property string testIcon: "\u0059" + readonly property string textAlignBottom: "\u005A" + readonly property string textAlignCenter: "\u005B" + readonly property string textAlignLeft: "\u005C" + readonly property string textAlignMiddle: "\u005D" + readonly property string textAlignRight: "\u005E" + readonly property string textAlignTop: "\u005F" + readonly property string textBulletList: "\u0060" + readonly property string textFullJustification: "\u0061" + readonly property string textNumberedList: "\u0062" + readonly property string tickIcon: "\u0063" + readonly property string triState: "\u0064" + readonly property string undo: "\u0065" + readonly property string upDownIcon: "\u0066" + readonly property string upDownSquare2: "\u0067" + readonly property string wildcard: "\u0068" + + readonly property font iconFont: Qt.font({ + "family": controlIcons.name, + "pixelSize": 12 + }) + + readonly property font font: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize + }) + + readonly property font largeFont: Qt.font({ + "family": mySystemFont.name, + "pointSize": Qt.application.font.pixelSize * 1.6 + }) + + readonly property color backgroundColor: "#c2c2c2" + + readonly property bool showActionIndicatorBackground: false +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index d65b065195211fbf5da4488b6063bf24c82e3ef8..9e3ed42406252fcc604753970c23dfa1173cc6c5 100644 GIT binary patch delta 1890 zcmX>Rv?H>ffsuiMftR6yftew|%`L>YztE+Sfk9^u0|SGMf3Ut$WNXSl1_s6m1_p+N z9WTe(7a{c;0iGhJpgn@xUB_p?_LLiLI znSp_E2Ll7cyqx^xMDddfbqoy53m6y}X5=PT6fjI>Sj51Bm)CjHv_Z8oW=-y87O3Z7v}5dG+{5^ZNs7seX$I34rb|qpm_?Y4m_wLnFmGc%!NSAR#Bz+4 zi`9>{jCCIC8#WuZBDNK5_t-_)o!A$!A7OvN!NXy|k;1W#;~Xaorv|47X9i~v=P}N2 zTs&N1Ty0$ExH-6;xU0B3xR-JN;ql^`!SjyShIbP0J3cGEYdn0v_=Wg=_%rxh_^)l2 zVOh#JIhS3|S6R@QQP5bBQBYCP+|=0ASkzclSyb6npSkN#8ROP9YyMTOVQTo-8Rw_( z=cmuKc{StTex^c3{l9ORL=gBFNYdY*VRH_fDpNfZL+=0i%sEW+7+4v&85ral8TA<% zSo(0Gfn#EA6#A@%-F@r$H)1vOv_y>+JSMSLlnOdU)k2jvYMK*AmTp* zNcu5|Xe|$BJQZBVC(NhmuJzB%Av)S&D@c56a5>{1h-#1)Mh8Alu=>ZGd<={XrYis6 zF`s5)X5eIC5N2l-H)qsmzX=4ym|W)4o_nnYcb?#^4ht z{;g&1V_;xlVbJ?Om3bG_W(IZ!ZU%k^X$FSP3Id9Z^%RCbrNM6l3Sw}0k`ex!9R#Vri25|;u1}z3x zhJwj?a%!B6?CR#mqRQgNljq3E)QgIVimMQ=^QP9&MAY+G1q`t|9+c@_~{t=Gfgv4VB_ZG zVpc;?>jVXJc0qROSj}QB>FA6J(mE>!_#a zsQd4Uu9L2=lkUGKdXBofj(SX1iYi90K51(j%5(Ax@^P}WsBt^X^Gb;--D6^7VG_lA4JLGYW!oo2a0lV#M5*|}1{YBzhUU18+nEiOqcDgnuDme$~8Y-z8F$fk9J;fq_BBKUm)=vNeT)fq^lCfq@|* zIXAID$^qk5x zsr2-h3=AwX3=A?SGEx&$q+iX8VPIgm!@$5`mXVQKpUCy=|0D(mMiB-E29=E5k_y4a z%-b0l7{`2_<51H-KqYx^KH z)7rjCjJC`!40)3s7^6iKnHBz9Fhnt{Fq{PW8l;9vaqq;b8G$nZR;|3RI*uJ2FF5{jdT>^79^m}NWx|!i)y4IITaUYj z`x1`=PZ`f8UOC=vytjBi@#*k+@TD*?Y_?!o%2?0Ap!a_&^Dd^%49pDt3^ELg4C)NJ z48{zW3=G1iilVHF#oXQC`TK-P`R`rE`ZH(J#Kk2({XNTMyxD_Im1*-r z&Kma3oq{Hen=6I4uy6L1PGc4J{C|-7DARNXZU%7%Wd<>M;ol z3QO1g`ynnWEy&8o#m>siD8i<~&%w(h{GE}RnTd;Cl#`Q1ltqY*Q;ds^k6DPFgG*3o z^CQK0#?4QacQLXXGZ=y*ZSpcz3r=H0U1MD%10#dU_f-NWo2xBibeVihtz&Y5dfR42 g4Qs~9xtcngmuOyK+}xzi$~bw7jwClc@vrRz0D1BES^xk5 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir index b768fe63a2b..4f689f9f630 100755 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/qmldir @@ -1,2 +1,4 @@ singleton Values 1.0 Values.qml singleton Constants 1.0 Constants.qml +InternalConstants 1.0 InternalConstants.qml + diff --git a/src/libs/advanceddockingsystem/ads_globals.h b/src/libs/advanceddockingsystem/ads_globals.h index 4c0b8d6896a..e61bfa7ceb4 100644 --- a/src/libs/advanceddockingsystem/ads_globals.h +++ b/src/libs/advanceddockingsystem/ads_globals.h @@ -107,10 +107,11 @@ enum eDragState { * The different icons used in the UI */ enum eIcon { - TabCloseIcon, //!< TabCloseIcon - DockAreaMenuIcon, //!< DockAreaMenuIcon - DockAreaUndockIcon, //!< DockAreaUndockIcon - DockAreaCloseIcon, //!< DockAreaCloseIcon + TabCloseIcon, //!< TabCloseIcon + DockAreaMenuIcon, //!< DockAreaMenuIcon + DockAreaUndockIcon, //!< DockAreaUndockIcon + DockAreaCloseIcon, //!< DockAreaCloseIcon + FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon IconCount, //!< just a delimiter for range checks }; diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 9ee647265d9..79ba7ae70d5 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -132,6 +132,7 @@ namespace ADS void DockAreaTitleBarPrivate::createButtons() { + const QSize iconSize(14, 14); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Tabs menu button m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton)); @@ -149,6 +150,7 @@ namespace ADS m_tabsMenuButton->setMenu(tabsMenu); internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs")); m_tabsMenuButton->setSizePolicy(sizePolicy); + m_tabsMenuButton->setIconSize(iconSize); m_layout->addWidget(m_tabsMenuButton, 0); QObject::connect(m_tabsMenuButton->menu(), &QMenu::triggered, @@ -164,6 +166,7 @@ namespace ADS QStyle::SP_TitleBarNormalButton, ADS::DockAreaUndockIcon); m_undockButton->setSizePolicy(sizePolicy); + m_undockButton->setIconSize(iconSize); m_layout->addWidget(m_undockButton, 0); QObject::connect(m_undockButton, &QToolButton::clicked, @@ -183,7 +186,7 @@ namespace ADS internal::setToolTip(m_closeButton, QObject::tr("Close Group")); } m_closeButton->setSizePolicy(sizePolicy); - m_closeButton->setIconSize(QSize(16, 16)); + m_closeButton->setIconSize(iconSize); m_layout->addWidget(m_closeButton, 0); QObject::connect(m_closeButton, &QToolButton::clicked, diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index 611580d3685..b46d1720509 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -168,12 +168,18 @@ namespace ADS m_titleLabel->setText(m_dockWidget->windowTitle()); m_titleLabel->setObjectName("dockWidgetTabLabel"); m_titleLabel->setAlignment(Qt::AlignCenter); - QObject::connect(m_titleLabel, &ElidingLabel::elidedChanged, q, &DockWidgetTab::elidedChanged); + QObject::connect(m_titleLabel, + &ElidingLabel::elidedChanged, + q, + &DockWidgetTab::elidedChanged); m_closeButton = createCloseButton(); m_closeButton->setObjectName("tabCloseButton"); - internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + TabCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); q->onDockWidgetFeaturesChanged(); internal::setToolTip(m_closeButton, QObject::tr("Close Tab")); QObject::connect(m_closeButton, @@ -189,11 +195,11 @@ namespace ADS boxLayout->setContentsMargins(2 * spacing, 0, 0, 0); boxLayout->setSpacing(0); q->setLayout(boxLayout); - boxLayout->addWidget(m_titleLabel, 1); + boxLayout->addWidget(m_titleLabel, 1, Qt::AlignVCenter); boxLayout->addSpacing(spacing); - boxLayout->addWidget(m_closeButton); + boxLayout->addWidget(m_closeButton, 0, Qt::AlignVCenter); boxLayout->addSpacing(qRound(spacing * 4.0 / 3.0)); - boxLayout->setAlignment(Qt::AlignCenter); + boxLayout->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); m_titleLabel->setVisible(true); } diff --git a/src/libs/advanceddockingsystem/iconprovider.cpp b/src/libs/advanceddockingsystem/iconprovider.cpp index 6a6f46752bb..f220418220d 100644 --- a/src/libs/advanceddockingsystem/iconprovider.cpp +++ b/src/libs/advanceddockingsystem/iconprovider.cpp @@ -51,7 +51,7 @@ namespace ADS { */ IconProviderPrivate(IconProvider *parent); }; - // struct LedArrayPanelPrivate + // struct IconProviderPrivate IconProviderPrivate::IconProviderPrivate(IconProvider *parent) : q(parent) diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp index 64e71a3b1c3..7d92952d700 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp @@ -41,7 +41,7 @@ namespace ADS { using TabLabelType = ElidingLabel; -using tCloseButton = QPushButton; +using CloseButtonType = QPushButton; /** * @brief Private data class of public interface CFloatingWidgetTitleBar @@ -52,7 +52,7 @@ public: FloatingWidgetTitleBar *q; ///< public interface class QLabel *m_iconLabel = nullptr; TabLabelType *m_titleLabel = nullptr; - tCloseButton *m_closeButton = nullptr; + CloseButtonType *m_closeButton = nullptr; FloatingDockContainer *m_floatingWidget = nullptr; eDragState m_dragState = DraggingInactive; @@ -74,22 +74,20 @@ void FloatingWidgetTitleBarPrivate::createLayout() m_titleLabel->setObjectName("floatingTitleLabel"); m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - m_closeButton = new tCloseButton(); + m_closeButton = new CloseButtonType(); m_closeButton->setObjectName("floatingTitleCloseButton"); m_closeButton->setFlat(true); - - // The standard icons do does not look good on high DPI screens - QIcon closeIcon; - QPixmap normalPixmap = q->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, - nullptr, - m_closeButton); - closeIcon.addPixmap(normalPixmap, QIcon::Normal); - closeIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + internal::setButtonIcon(m_closeButton, + QStyle::SP_TitleBarCloseButton, + ADS::FloatingWidgetCloseIcon); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_closeButton->setIconSize(QSize(14, 14)); m_closeButton->setVisible(true); m_closeButton->setFocusPolicy(Qt::NoFocus); - q->connect(m_closeButton, &QPushButton::clicked, q, &FloatingWidgetTitleBar::closeRequested); + QObject::connect(m_closeButton, + &QPushButton::clicked, + q, + &FloatingWidgetTitleBar::closeRequested); QFontMetrics fontMetrics(m_titleLabel->font()); int spacing = qRound(fontMetrics.height() / 4.0); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index ec4128479fe..29b937a502a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -28,19 +28,46 @@ #include +#include + #include #include #include #include #include +#include +#include +#include #include +static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg) + namespace QmlDesigner { Theme::Theme(Utils::Theme *originTheme, QObject *parent) : Utils::Theme(originTheme, parent) + , m_constants(nullptr) { + QString constantsPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml"); + + QQmlEngine* engine = new QQmlEngine(this); + QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath)); + + if (component.status() == QQmlComponent::Ready) { + m_constants = component.create(); + } + else if (component.status() == QQmlComponent::Error ) { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "due to the following error(s):"; + for (QQmlError error : component.errors()) + qCWarning(themeLog) << error.toString(); + } + else { + qCWarning(themeLog) << "Couldn't load" << constantsPath + << "the status of the QQmlComponent is" << component.status(); + } } QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName) @@ -129,6 +156,25 @@ QPixmap Theme::getPixmap(const QString &id) return QmlDesignerIconProvider::getPixmap(id); } +QString Theme::getIconUnicode(Theme::Icon i) +{ + if (!instance()->m_constants) + return QString(); + + const QMetaObject *m = instance()->metaObject(); + const char *enumName = "Icon"; + int enumIndex = m->indexOfEnumerator(enumName); + + if (enumIndex == -1) { + qCWarning(themeLog) << "Couldn't find enum" << enumName; + return QString(); + } + + QMetaEnum e = m->enumerator(enumIndex); + + return instance()->m_constants->property(e.valueToKey(i)).toString(); +} + QColor Theme::qmlDesignerBackgroundColorDarker() const { return getColor(QmlDesigner_BackgroundColorDarker); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 6940c0c1cf0..07ee4caca8a 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -41,12 +41,91 @@ namespace QmlDesigner { class QMLDESIGNERCORE_EXPORT Theme : public Utils::Theme { Q_OBJECT + + Q_ENUMS(Icon) + public: + enum Icon { + actionIcon, + actionIconBinding, + addColumnAfter, + addColumnBefore, + addFile, + addRowAfter, + addRowBefore, + addTable, + adsClose, + adsDetach, + adsDropDown, + alignBottom, + alignCenterHorizontal, + alignCenterVertical, + alignLeft, + alignRight, + alignTo, + alignTop, + anchorBaseline, + anchorBottom, + anchorFill, + anchorLeft, + anchorRight, + anchorTop, + annotationBubble, + annotationDecal, + centerHorizontal, + centerVertical, + closeCross, + decisionNode, + deleteColumn, + deleteRow, + deleteTable, + detach, + distributeBottom, + distributeCenterHorizontal, + distributeCenterVertical, + distributeLeft, + distributeOriginBottomRight, + distributeOriginCenter, + distributeOriginNone, + distributeOriginTopLeft, + distributeRight, + distributeSpacingHorizontal, + distributeSpacingVertical, + distributeTop, + edit, + fontStyleBold, + fontStyleItalic, + fontStyleStrikethrough, + fontStyleUnderline, + mergeCells, + redo, + splitColumns, + splitRows, + startNode, + testIcon, + textAlignBottom, + textAlignCenter, + textAlignLeft, + textAlignMiddle, + textAlignRight, + textAlignTop, + textBulletList, + textFullJustification, + textNumberedList, + tickIcon, + triState, + undo, + upDownIcon, + upDownSquare2, + wildcard + }; + static Theme *instance(); static QString replaceCssColors(const QString &input); static void setupTheme(QQmlEngine *engine); static QColor getColor(Color role); static QPixmap getPixmap(const QString &id); + static QString getIconUnicode(Theme::Icon i); Q_INVOKABLE QColor qmlDesignerBackgroundColorDarker() const; Q_INVOKABLE QColor qmlDesignerBackgroundColorDarkAlternate() const; @@ -58,9 +137,12 @@ public: Q_INVOKABLE int smallFontPixelSize() const; Q_INVOKABLE int captionFontPixelSize() const; Q_INVOKABLE bool highPixelDensity() const; + private: Theme(Utils::Theme *originTheme, QObject *parent); QColor evaluateColorAtThemeInstance(const QString &themeColorName); + + QObject *m_constants; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 7769fb43a91..9bc485fd906 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include +#include using Core::MiniSplitter; using Core::IEditor; @@ -227,6 +229,26 @@ void DesignModeWidget::setup() QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); + // Setup icons + QColor buttonColor(Theme::getColor(Theme::QmlDesigner_TabLight)); // TODO Use correct color roles + QColor tabColor(Theme::getColor(Theme::QmlDesigner_TabDark)); + + const QString closeUnicode = Theme::getIconUnicode(Theme::Icon::adsClose); + const QString menuUnicode = Theme::getIconUnicode(Theme::Icon::adsDropDown); + const QString undockUnicode = Theme::getIconUnicode(Theme::Icon::adsDetach); + + const QString fontName = "qtds_propertyIconFont.ttf"; + const QIcon tabsCloseIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, tabColor); + const QIcon menuIcon = Utils::StyleHelper::getIconFromIconFont(fontName, menuUnicode, 28, 28, buttonColor); + const QIcon undockIcon = Utils::StyleHelper::getIconFromIconFont(fontName, undockUnicode, 28, 28, buttonColor); + const QIcon closeIcon = Utils::StyleHelper::getIconFromIconFont(fontName, closeUnicode, 28, 28, buttonColor); + + m_dockManager->iconProvider().registerCustomIcon(ADS::TabCloseIcon, tabsCloseIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaMenuIcon, menuIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaUndockIcon, undockIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::DockAreaCloseIcon, closeIcon); + m_dockManager->iconProvider().registerCustomIcon(ADS::FloatingWidgetCloseIcon, closeIcon); + // Setup Actions and Menus Core::ActionContainer *mwindow = Core::ActionManager::actionContainer(Core::Constants::M_WINDOW); // Window > Views diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 5fd0b7990db..21d1ffadf22 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -217,6 +217,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool()) GenerateResource::generateMenuEntry(); + QString fontPath = Core::ICore::resourcePath() + + QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf"); + if (QFontDatabase::addApplicationFont(fontPath) < 0) + qCWarning(qmldesignerLog) << "Could not add font " << fontPath << "to font database"; + return true; } From 104852e09d30be2758dd606016349405ce147883 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 24 Apr 2020 13:22:55 +0200 Subject: [PATCH 23/28] QmlDesigner: Update properties in property editor Add missing and cleanup existing properties in property editor. Task-number: QDS-1502 Change-Id: I7bff7bad32b3bfd0742dd1d06f7c3ba12ef1bbca Reviewed-by: Thomas Hartmann --- .../QtQuick/AdvancedSection.qml | 53 ++++- .../QtQuick/AnimatedImageSpecifics.qml | 95 +++++++++ .../QtQuick/BorderImageSpecifics.qml | 68 +++++- .../QtQuick/GridSpecifics.qml | 25 +++ .../QtQuick/ImageSpecifics.qml | 132 +++++++++++- .../QtQuick/MouseAreaSpecifics.qml | 193 +++++++++++++++++- .../QtQuick/TextInputSection.qml | 13 ++ .../HelperWidgets/FlickableSection.qml | 60 +++++- .../imports/HelperWidgets/FontSection.qml | 12 ++ .../HelperWidgets/StandardTextSection.qml | 72 ++++++- .../designercore/model/texttomodelmerger.cpp | 9 +- 11 files changed, 722 insertions(+), 10 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml index 4eb7420d740..9990ddf8c66 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml @@ -100,7 +100,7 @@ Section { } Label { - text: "State" + text: qsTr("State") } SecondColumnLayout { LineEdit { @@ -158,5 +158,56 @@ Section { } } + Label { + text: qsTr("Focus") + tooltip: qsTr("Holds whether the item has focus within the enclosing FocusScope.") + disabledState: !backendValues.focus.isAvailable + } + SecondColumnLayout { + CheckBox { + backendValue: backendValues.focus + text: backendValues.focus.valueToString + enabled: backendValues.focus.isAvailable + implicitWidth: 180 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Active focus on tab") + tooltip: qsTr("Holds whether the item wants to be in the tab focus chain.") + disabledState: !backendValues.activeFocusOnTab.isAvailable + } + SecondColumnLayout { + CheckBox { + backendValue: backendValues.activeFocusOnTab + text: backendValues.activeFocusOnTab.valueToString + enabled: backendValues.activeFocusOnTab.isAvailable + implicitWidth: 180 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Baseline offset") + tooltip: qsTr("Specifies the position of the item's baseline in local coordinates.") + disabledState: !backendValues.baselineOffset.isAvailable + } + SecondColumnLayout { + SpinBox { + sliderIndicatorVisible: true + backendValue: backendValues.baselineOffset + hasSlider: true + decimals: 0 + minimumValue: -1000 + maximumValue: 1000 + Layout.preferredWidth: 140 + enabled: backendValues.baselineOffset.isAvailable + } + ExpandingSpacer { + } + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml new file mode 100644 index 00000000000..787e390cbde --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimatedImageSpecifics.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.1 +import HelperWidgets 2.0 +import QtQuick.Layouts 1.0 + +Column { + anchors.left: parent.left + anchors.right: parent.right + + ImageSpecifics { + } + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Animated Image") + + SectionLayout { + Label { + text: qsTr("Speed") + disabledState: !backendValues.speed.isAvailable + } + SecondColumnLayout { + SpinBox { + sliderIndicatorVisible: true + backendValue: backendValues.speed + hasSlider: true + decimals: 2 + minimumValue: 0 + maximumValue: 100 + Layout.preferredWidth: 140 + enabled: backendValues.speed.isAvailable + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Paused") + tooltip: qsTr("Holds whether the animated image is paused.") + disabledState: !backendValues.paused.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.paused.isAvailable + text: backendValues.paused.valueToString + backendValue: backendValues.paused + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Playing") + tooltip: qsTr("Holds whether the animated image is playing.") + disabledState: !backendValues.playing.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.playing.isAvailable + text: backendValues.playing.valueToString + backendValue: backendValues.playing + implicitWidth: 180 + } + ExpandingSpacer {} + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml index 3d39d44e8fb..362ba144c56 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/BorderImageSpecifics.qml @@ -124,7 +124,7 @@ Column { } Label { - text: qsTr("Horizontal Fill mode") + text: qsTr("Horizontal Tile mode") } SecondColumnLayout { @@ -138,7 +138,7 @@ Column { } Label { - text: qsTr("Vertical Fill mode") + text: qsTr("Vertical Tile mode") } SecondColumnLayout { @@ -186,6 +186,70 @@ Column { } } + + Label { + text: qsTr("Mirror") + tooltip: qsTr("Specifies whether the image should be horizontally inverted.") + disabledState: !backendValues.mirror.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mirror.isAvailable + text: backendValues.mirror.valueToString + backendValue: backendValues.mirror + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Smooth") + tooltip: qsTr("Specifies whether the image is smoothly filtered when scaled or transformed.") + disabledState: !backendValues.smooth.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.smooth.isAvailable + text: backendValues.smooth.valueToString + backendValue: backendValues.smooth + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Cache") + tooltip: qsTr("Specifies whether the image should be cached.") + disabledState: !backendValues.cache.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.cache.isAvailable + text: backendValues.cache.valueToString + backendValue: backendValues.cache + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Asynchronous") + tooltip: qsTr("Specifies that images on the local filesystem should be loaded asynchronously in a separate thread.") + disabledState: !backendValues.asynchronous.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.asynchronous.isAvailable + text: backendValues.asynchronous.valueToString + backendValue: backendValues.asynchronous + implicitWidth: 180 + } + ExpandingSpacer {} + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml index 37be5e57701..284849a4158 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/GridSpecifics.qml @@ -95,7 +95,32 @@ Column { Layout.fillWidth: true scope: "Qt" } + } + Label { + text: qsTr("Horizontal item alignment") + } + + SecondColumnLayout { + ComboBox { + model: ["AlignLeft", "AlignRight" ,"AlignHCenter"] + backendValue: backendValues.horizontalItemAlignment + Layout.fillWidth: true + scope: "Grid" + } + } + + Label { + text: qsTr("Vertical item alignment") + } + + SecondColumnLayout { + ComboBox { + model: ["AlignTop", "AlignBottom" ,"AlignVCenter"] + backendValue: backendValues.verticalItemAlignment + Layout.fillWidth: true + scope: "Grid" + } } Label { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml index 8f742f06e24..60b97a11796 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ImageSpecifics.qml @@ -58,7 +58,7 @@ Column { SecondColumnLayout { ComboBox { scope: "Image" - model: ["Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically", "TileHorizontally"] + model: ["Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically", "TileHorizontally", "Pad"] backendValue: backendValues.fillMode implicitWidth: 180 Layout.fillWidth: true @@ -105,6 +105,136 @@ Column { ExpandingSpacer { } } + + Label { + text: qsTr("Horizontal alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignLeft", "AlignRight", "AlignHCenter"] + backendValue: backendValues.horizontalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Vertical alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignTop", "AlignBottom", "AlignVCenter"] + backendValue: backendValues.verticalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Asynchronous") + tooltip: qsTr("Specifies that images on the local filesystem should be loaded asynchronously in a separate thread.") + disabledState: !backendValues.asynchronous.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.asynchronous.isAvailable + text: backendValues.asynchronous.valueToString + backendValue: backendValues.asynchronous + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Auto transform") + tooltip: qsTr("Specifies whether the image should automatically apply image transformation metadata such as EXIF orientation.") + disabledState: !backendValues.autoTransform.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.autoTransform.isAvailable + text: backendValues.autoTransform.valueToString + backendValue: backendValues.autoTransform + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Cache") + tooltip: qsTr("Specifies whether the image should be cached.") + disabledState: !backendValues.cache.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.cache.isAvailable + text: backendValues.cache.valueToString + backendValue: backendValues.cache + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mipmap") + tooltip: qsTr("Specifies whether the image uses mipmap filtering when scaled or transformed.") + disabledState: !backendValues.mipmap.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mipmap.isAvailable + text: backendValues.mipmap.valueToString + backendValue: backendValues.mipmap + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mirror") + tooltip: qsTr("Specifies whether the image should be horizontally inverted.") + disabledState: !backendValues.mirror.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.mirror.isAvailable + text: backendValues.mirror.valueToString + backendValue: backendValues.mirror + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Smooth") + tooltip: qsTr("Specifies whether the image is smoothly filtered when scaled or transformed.") + disabledState: !backendValues.smooth.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.smooth.isAvailable + text: backendValues.smooth.valueToString + backendValue: backendValues.smooth + implicitWidth: 180 + } + ExpandingSpacer {} + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml index 71ff0260ba0..0ca7f3d888d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/MouseAreaSpecifics.qml @@ -54,7 +54,7 @@ Column { } Label { - text: qsTr("Hover Enabled") + text: qsTr("Hover enabled") tooltip: qsTr("This property holds whether hover events are handled.") } @@ -68,6 +68,197 @@ Column { ExpandingSpacer { } } + + Label { + text: qsTr("Accepted buttons") + tooltip: qsTr("This property holds the mouse buttons that the mouse area reacts to.") + } + + SecondColumnLayout { + ComboBox { + backendValue: backendValues.acceptedButtons + model: ["LeftButton", "RightButton", "MiddleButton", "BackButton", "ForwardButton", "AllButtons"] + Layout.fillWidth: true + scope: "Qt" + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Press and hold interval") + tooltip: qsTr("This property overrides the elapsed time in milliseconds before pressAndHold is emitted.") + } + + SecondColumnLayout { + SpinBox { + backendValue: backendValues.pressAndHoldInterval + minimumValue: 0 + maximumValue: 2000 + decimals: 0 + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Scroll gesture enabled") + tooltip: qsTr("This property controls whether this MouseArea responds to scroll gestures from non-mouse devices.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.scrollGestureEnabled + text: backendValues.scrollGestureEnabled.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Cursor shape") + tooltip: qsTr("This property holds the cursor shape for this mouse area.") + } + + SecondColumnLayout { + ComboBox { + backendValue: backendValues.cursorShape + model: ["ArrowCursor", "UpArrowCursor", "CrossCursor", "WaitCursor", + "IBeamCursor", "SizeVerCursor", "SizeHorCursor", "SizeBDiagCursor", + "SizeFDiagCursor", "SizeAllCursor", "BlankCursor", "SplitVCursor", + "SplitHCursor", "PointingHandCursor", "ForbiddenCursor", "WhatsThisCursor", + "BusyCursor", "OpenHandCursor", "ClosedHandCursor", "DragCopyCursor", + "DragMoveCursor", "DragLinkCursor"] + Layout.fillWidth: true + scope: "Qt" + } + ExpandingSpacer { + } + } + + Label { + text: qsTr("Prevent stealing") + tooltip: qsTr("This property controls whether the mouse events may be stolen from this MouseArea.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.preventStealing + text: backendValues.preventStealing.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Propagate composed events") + tooltip: qsTr("This property controls whether composed mouse events will automatically propagate to other MouseAreas.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.propagateComposedEvents + text: backendValues.propagateComposedEvents.valueToString + } + + ExpandingSpacer { + } + } + } + } + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Drag") + + SectionLayout { + Label { + text: qsTr("Target") + tooltip: qsTr("Sets the id of the item to drag.") + } + SecondColumnLayout { + ItemFilterComboBox { + typeFilter: "QtQuick.QtObject" + validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ } + backendValue: backendValues.drag_target + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Axis") + tooltip: qsTr("Specifies whether dragging can be done horizontally, vertically, or both.") + } + SecondColumnLayout { + ComboBox { + scope: "Drag" + model: ["XAxis", "YAxis", "XAndYAxis"] + backendValue: backendValues.drag_axis + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Filter children") + tooltip: qsTr("Specifies whether a drag overrides descendant MouseAreas.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.drag_filterChildren + text: backendValues.drag_filterChildren.valueToString + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Threshold") + tooltip: qsTr("Determines the threshold in pixels of when the drag operation should start.") + } + + SecondColumnLayout { + SpinBox { + backendValue: backendValues.drag_threshold + minimumValue: 0 + maximumValue: 5000 + decimals: 0 + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Smoothed") + tooltip: qsTr("If set to true, the target will be moved only after the drag operation has started.\n" + + "If set to false, the target will be moved straight to the current mouse position.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.drag_smoothed + text: backendValues.drag_smoothed.valueToString + } + + ExpandingSpacer { + } + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml index cfd972b0854..5c0e84d039b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/TextInputSection.qml @@ -113,6 +113,19 @@ Section { minimumValue: -200 } + Label { + visible: textInputSection.isTextInput + text: qsTr("Maximum length") + tooltip: qsTr("Sets the maximum permitted length of the text in the TextInput.") + } + SpinBox { + visible: textInputSection.isTextInput + Layout.fillWidth: true + backendValue: backendValues.maximumLength + minimumValue: 0 + maximumValue: 32767 + } + Label { text: qsTr("Flags") Layout.alignment: Qt.AlignTop diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml index 2b97df06467..11f46d29c52 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlickableSection.qml @@ -47,7 +47,7 @@ Section { SecondColumnLayout { ComboBox { backendValue: backendValues.flickableDirection - model: ["AutoFlickDirection", "HorizontalFlick", "VerticalFlick", "HorizontalAndVerticalFlick"] + model: ["AutoFlickDirection", "AutoFlickIfNeeded", "HorizontalFlick", "VerticalFlick", "HorizontalAndVerticalFlick"] Layout.fillWidth: true scope: "Flickable" } @@ -63,7 +63,7 @@ Section { SecondColumnLayout { ComboBox { backendValue: backendValues.boundsBehavior - model: ["StopAtBounds", "DragOverBounds", "DragAndOvershootBounds"] + model: ["StopAtBounds", "DragOverBounds", "OvershootBounds", "DragAndOvershootBounds"] Layout.fillWidth: true scope: "Flickable" } @@ -166,6 +166,23 @@ Section { } } + Label { + text: qsTr("Synchronous drag") + tooltip: qsTr("If set to true, then when the mouse or touchpoint moves far enough to begin dragging\n" + + "the content, the content will jump, such that the content pixel which was under the\n" + + "cursor or touchpoint when pressed remains under that point.") + } + + SecondColumnLayout { + CheckBox { + Layout.fillWidth: true + backendValue: backendValues.synchronousDrag + text: backendValues.synchronousDrag.valueToString + } + ExpandingSpacer { + } + } + Label { text: qsTr("Content size") } @@ -246,6 +263,45 @@ Section { } } + Label { + text: qsTr("Origin") + } + + SecondColumnLayout { + Label { + text: "X" + width: root.labelWidth + } + + SpinBox { + backendValue: backendValues.originX + minimumValue: -8000 + maximumValue: 8000 + implicitWidth: root.spinBoxWidth + Layout.fillWidth: true + } + + Item { + width: 4 + height: 4 + } + + Label { + text: "Y" + width: root.labelWidth + } + + SpinBox { + backendValue: backendValues.originY + minimumValue: -8000 + maximumValue: 8000 + implicitWidth: root.spinBoxWidth + Layout.fillWidth: true + } + ExpandingSpacer { + } + } + Label { text: qsTr("Margins") } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml index 4ef079af4c7..94a43424dac 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontSection.qml @@ -276,5 +276,17 @@ Section { "Latin script,\n it is merely a cosmetic feature. Setting the preferShaping property to false will disable all such features\nwhen they are not required, which will improve performance in most cases.") } } + + Label { + text: qsTr("Hinting preference") + toolTip: qsTr("Sets the preferred hinting on the text.") + } + + ComboBox { + Layout.fillWidth: true + backendValue: getBackendValue("hintingPreference") + model: ["PreferDefaultHinting", "PreferNoHinting", "PreferVerticalHinting", "PreferFullHinting"] + scope: "Font" + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml index 6ecc8d525ce..6c5e6fde867 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml @@ -61,7 +61,7 @@ Section { Layout.fillWidth: true backendValue: backendValues.wrapMode scope: "Text" - model: ["NoWrap", "WordWrap", "WrapAnywhere", "WrapAtWordBoundaryOrAnywhere"] + model: ["NoWrap", "WordWrap", "WrapAnywhere", "Wrap"] } Label { @@ -77,6 +77,21 @@ Section { model: ["ElideNone", "ElideLeft", "ElideMiddle", "ElideRight"] } + Label { + visible: showElide + text: qsTr("Maximum line count") + tooltip: qsTr("Limits the number of lines that the text item will show.") + } + + SpinBox { + visible: showElide + Layout.fillWidth: true + backendValue: backendValues.maximumLineCount + minimumValue: 0 + maximumValue: 10000 + decimals: 0 + } + Label { text: qsTr("Alignment") } @@ -124,6 +139,7 @@ Section { toolTip: qsTr("Specifies how the font size of the displayed text is determined.") } ComboBox { + id: fontSizeMode visible: showFontSizeMode scope: "Text" model: ["FixedSize", "HorizontalFit", "VerticalFit", "Fit"] @@ -131,6 +147,48 @@ Section { Layout.fillWidth: true } + Label { + visible: showFontSizeMode + text: qsTr("Minimum size") + } + SecondColumnLayout { + visible: showFontSizeMode + + SpinBox { + enabled: fontSizeMode.currentIndex !== 0 + minimumValue: 0 + maximumValue: 500 + decimals: 0 + backendValue: backendValues.minimumPixelSize + Layout.fillWidth: true + Layout.minimumWidth: 60 + } + Label { + text: qsTr("Pixel") + tooltip: qsTr("Specifies the minimum font pixel size of scaled text.") + width: 42 + } + + Item { + width: 4 + height: 4 + } + + SpinBox { + enabled: fontSizeMode.currentIndex !== 0 + minimumValue: 0 + maximumValue: 500 + decimals: 0 + backendValue: backendValues.minimumPointSize + Layout.fillWidth: true + Layout.minimumWidth: 60 + } + Label { + text: qsTr("Point") + tooltip: qsTr("Specifies the minimum font point size of scaled text.") + width: 42 + } + } Label { visible: showLineHeight @@ -148,5 +206,17 @@ Section { stepSize: 0.1 } + Label { + visible: showLineHeight + text: qsTr("Line height mode") + toolTip: qsTr("Determines how the line height is specified.") + } + ComboBox { + visible: showLineHeight + scope: "Text" + model: ["ProportionalHeight", "FixedHeight"] + backendValue: backendValues.lineHeightMode + Layout.fillWidth: true + } } } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index b7426c7a43c..91e4c9c7b05 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -91,7 +91,12 @@ QStringList globalQtEnums() "Horizontal", "Vertical", "AlignVCenter", "AlignLeft", "LeftToRight", "RightToLeft", "AlignHCenter", "AlignRight", "AlignBottom", "AlignBaseline", "AlignTop", "BottomLeft", "LeftEdge", "RightEdge", "BottomEdge", "TopEdge", "TabFocus", "ClickFocus", "StrongFocus", - "WheelFocus", "NoFocus" + "WheelFocus", "NoFocus", "ArrowCursor", "UpArrowCursor", "CrossCursor", "WaitCursor", + "IBeamCursor", "SizeVerCursor", "SizeHorCursor", "SizeBDiagCursor", "SizeFDiagCursor", + "SizeAllCursor", "BlankCursor", "SplitVCursor", "SplitHCursor", "PointingHandCursor", + "ForbiddenCursor", "WhatsThisCursor", "BusyCursor", "OpenHandCursor", "ClosedHandCursor", + "DragCopyCursor", "DragMoveCursor", "DragLinkCursor", "TopToBottom", + "LeftButton", "RightButton", "MiddleButton", "BackButton", "ForwardButton", "AllButtons" }; return list; @@ -101,7 +106,7 @@ QStringList knownEnumScopes() { static const QStringList list = { "TextInput", "TextEdit", "Material", "Universal", "Font", "Shape", "ShapePath", - "AbstractButton", "Text", "ShaderEffectSource" + "AbstractButton", "Text", "ShaderEffectSource", "Grid" }; return list; } From 4435a1997903bbc6d7a554de1daea9d25a21790f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 24 Apr 2020 19:45:17 +0200 Subject: [PATCH 24/28] QmlDesigner: Add Combo box for states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I91494e297cc2c1aa4b192081b5cd31acbca87ce9 Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/AdvancedSection.qml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml index 9990ddf8c66..5434e4705bf 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AdvancedSection.qml @@ -103,11 +103,14 @@ Section { text: qsTr("State") } SecondColumnLayout { - LineEdit { + + ComboBox { + Layout.fillWidth: true backendValue: backendValues.state - showTranslateCheckBox: false - enabled: anchorBackend.hasParent || isBaseState + model: allStateNames + valueType: ComboBox.String } + ExpandingSpacer { } } From 5ac3f2958f005eb093c2f0b7fb366c5669bfb62e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 24 Apr 2020 19:44:37 +0200 Subject: [PATCH 25/28] Add StyleHelper functions for icon font Change-Id: I783f3405dafbd51bf7433084709a2362ed2bd759 Reviewed-by: Tim Jenssen --- src/libs/utils/stylehelper.cpp | 45 ++++++++++++++++++++++++++++++++++ src/libs/utils/stylehelper.h | 3 +++ 2 files changed, 48 insertions(+) diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index f91c24b52ac..68d40accbc6 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -28,6 +28,8 @@ #include "theme/theme.h" #include "hostosinfo.h" +#include + #include #include #include @@ -35,6 +37,7 @@ #include #include #include +#include #include // Clamps float color values within (0, 255) @@ -542,6 +545,48 @@ QLinearGradient StyleHelper::statusBarGradient(const QRect &statusBarRect) return grad; } +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color) +{ + QFontDatabase a; + + QTC_ASSERT(a.hasFamily(fontName), {}); + + if (a.hasFamily(fontName)) { + + QIcon icon; + QSize size(iconSize, iconSize); + + const int maxDpr = qRound(qApp->devicePixelRatio()); + for (int dpr = 1; dpr <= maxDpr; dpr++) { + QPixmap pixmap(size * dpr); + pixmap.setDevicePixelRatio(dpr); + pixmap.fill(Qt::transparent); + + QFont font(fontName); + font.setPixelSize(fontSize * dpr); + + QPainter painter(&pixmap); + painter.save(); + painter.setPen(color); + painter.setFont(font); + painter.drawText(QRectF(QPoint(0, 0), size), iconSymbol); + painter.restore(); + + icon.addPixmap(pixmap); + } + + return icon; + } + + return {}; +} + +QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize) +{ + QColor penColor = QApplication::palette("QWidget").color(QPalette::Normal, QPalette::ButtonText); + return getIconFromIconFont(fontName, iconSymbol, fontSize, iconSize, penColor); +} + QString StyleHelper::dpiSpecificImageFile(const QString &fileName) { // See QIcon::addFile() diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 4bcec960ad5..09f32534352 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -93,6 +93,9 @@ public: static void tintImage(QImage &img, const QColor &tintColor); static QLinearGradient statusBarGradient(const QRect &statusBarRect); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color); + static QIcon getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize); + static QString dpiSpecificImageFile(const QString &fileName); static QString imageFileWithResolution(const QString &fileName, int dpr); static QList availableImageResolutions(const QString &fileName); From a0385503d5cbc6f0565269ae5db20e07b9271429 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 16 Apr 2020 18:18:17 +0200 Subject: [PATCH 26/28] QmlDesigner: Use proper icons in FlowEditor The icons are part of the icon font. Change-Id: I5f501ebf74561429738b5b2d4fd007ca22cd7e2d Reviewed-by: Tim Jenssen --- .../components/formeditor/formeditoritem.cpp | 105 ++++++++++++++---- .../components/formeditor/formeditoritem.h | 17 ++- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 96a4cc5adf6..337cc0f7fc9 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -32,10 +32,13 @@ #include #include +#include + #include #include #include +#include #include #include #include @@ -47,6 +50,36 @@ namespace QmlDesigner { const int flowBlockSize = 200; +const int blockRadius = 18; +const int blockAdjust = 40; + +const char startNodeIcon[] = "\u0055"; + +void drawIcon(QPainter *painter, + int x, + int y, + const QString &iconSymbol, + int fontSize, int iconSize, + const QColor &penColor) +{ + static QFontDatabase a; + + const QString fontName = "qtds_propertyIconFont.ttf"; + + Q_ASSERT(a.hasFamily(fontName)); + + if (a.hasFamily(fontName)) { + QFont font(fontName); + font.setPixelSize(fontSize); + + painter->save(); + painter->setPen(penColor); + painter->setFont(font); + painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol); + + painter->restore(); + } +} FormEditorScene *FormEditorItem::scene() const { return qobject_cast(QGraphicsItem::scene()); @@ -578,6 +611,7 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi return; painter->save(); + painter->setRenderHint(QPainter::Antialiasing); QPen pen; pen.setJoinStyle(Qt::MiterJoin); @@ -622,10 +656,9 @@ void FormEditorFlowActionItem::paint(QPainter *painter, const QStyleOptionGraphi fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value(); if (fillColor.alpha() > 0) - painter->fillRect(boundingRect(), fillColor); - - painter->drawRect(boundingRect()); + painter->setBrush(fillColor); + painter->drawRoundedRect(boundingRect(), blockRadius, blockRadius); painter->restore(); } @@ -990,6 +1023,7 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi return; painter->save(); + painter->setRenderHint(QPainter::Antialiasing); ResolveConnection resolved(qmlItemNode()); @@ -1022,8 +1056,8 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi toRect.translate(QmlItemNode(resolved.to).flowPosition()); if (resolved.isStartLine) { - fromRect = QRectF(0,0,50,50); - fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-120, toRect.height() / 2 - 25)); + fromRect = QRectF(0, 0, 96, 96); + fromRect.translate(QmlItemNode(resolved.to).flowPosition() + QPoint(-180, toRect.height() / 2 - 96 / 2)); } toRect.translate(-pos()); @@ -1076,23 +1110,28 @@ void FormEditorTransitionItem::paint(QPainter *painter, const QStyleOptionGraphi if (qmlItemNode().modelNode().hasAuxiliaryData("breakPoint")) breakOffset = qmlItemNode().modelNode().auxiliaryData("breakPoint").toInt(); + if (resolved.isStartLine) + fromRect.translate(0, inOffset); + paintConnection(painter, fromRect, toRect, width, adjustedWidth ,color, dash, outOffset, inOffset, breakOffset); if (resolved.isStartLine) { + + const QString icon = Theme::getIconUnicode(Theme::startNode); + QPen pen; pen.setCosmetic(true); - pen.setColor(color); painter->setPen(pen); - painter->drawRect(fromRect); - if (scaleFactor > 0.4) { - painter->drawLine(fromRect.topRight() + QPoint(20,10), fromRect.bottomRight() + QPoint(20,-10)); - painter->drawLine(fromRect.topRight() + QPoint(25,12), fromRect.bottomRight() + QPoint(25,-12)); - painter->drawLine(fromRect.topRight() + QPoint(30,15), fromRect.bottomRight() + QPoint(30,-15)); - painter->drawLine(fromRect.topRight() + QPoint(35,17), fromRect.bottomRight() + QPoint(35,-17)); - painter->drawLine(fromRect.topRight() + QPoint(40,20), fromRect.bottomRight() + QPoint(40,-20)); - } + const int iconAdjust = 48; + const int offset = 96; + const int size = fromRect.width(); + const int iconSize = size - iconAdjust; + const int x = fromRect.topRight().x() - offset; + const int y = fromRect.topRight().y(); + painter->drawRoundedRect(x, y , size - 10, size, size / 2, iconSize / 2); + drawIcon(painter, x + iconAdjust / 2, y + iconAdjust / 2, icon, iconSize, iconSize, color); } painter->restore(); @@ -1141,6 +1180,9 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setRenderHint(QPainter::SmoothPixmapTransform); + QPen pen; pen.setJoinStyle(Qt::MiterJoin); pen.setCosmetic(true); @@ -1179,20 +1221,37 @@ void FormEditorFlowDecisionItem::paint(QPainter *painter, const QStyleOptionGrap if (qmlItemNode().modelNode().hasAuxiliaryData("fillColor")) fillColor = qmlItemNode().modelNode().auxiliaryData("fillColor").value(); + painter->save(); + + if (m_iconType == DecisionIcon) { + painter->translate(boundingRect().center()); + painter->rotate(45); + painter->translate(-boundingRect().center()); + } + if (fillColor.alpha() > 0) - painter->fillRect(boundingRect(), fillColor); + painter->setBrush(fillColor); - painter->drawLine(boundingRect().left(), boundingRect().center().y(), - boundingRect().center().x(), boundingRect().top()); + int radius = blockRadius; - painter->drawLine(boundingRect().center().x(), boundingRect().top(), - boundingRect().right(), boundingRect().center().y()); + const QRectF adjustedRect = boundingRect().adjusted(blockAdjust, + blockAdjust, + -blockAdjust, + -blockAdjust); - painter->drawLine(boundingRect().right(), boundingRect().center().y(), - boundingRect().center().x(), boundingRect().bottom()); + painter->drawRoundedRect(adjustedRect, radius, radius); - painter->drawLine(boundingRect().center().x(), boundingRect().bottom(), - boundingRect().left(), boundingRect().center().y()); + const int iconDecrement = 32; + const int iconSize = adjustedRect.width() - iconDecrement; + const int offset = iconDecrement / 2 + blockAdjust; + + painter->restore(); + + const QString icon = (m_iconType == + WildcardIcon) ? Theme::getIconUnicode(Theme::wildcard) + : Theme::getIconUnicode(Theme::decisionNode); + + drawIcon(painter, offset, offset, icon, iconSize, iconSize, flowColor); painter->restore(); } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index d725afd0b05..91dcad44a42 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -207,9 +207,17 @@ public: bool flowHitTest(const QPointF &point) const override; protected: - FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) - : FormEditorFlowItem(qmlItemNode, scene) + enum IconType { + DecisionIcon, + WildcardIcon + }; + + FormEditorFlowDecisionItem(const QmlItemNode &qmlItemNode, + FormEditorScene* scene, + IconType iconType = DecisionIcon) + : FormEditorFlowItem(qmlItemNode, scene), m_iconType(iconType) {} + IconType m_iconType; }; class FormEditorFlowWildcardItem : FormEditorFlowDecisionItem @@ -221,8 +229,9 @@ public: protected: FormEditorFlowWildcardItem(const QmlItemNode &qmlItemNode, FormEditorScene* scene) - : FormEditorFlowDecisionItem(qmlItemNode, scene) - {} + : FormEditorFlowDecisionItem(qmlItemNode, scene, WildcardIcon) + { + } }; inline int FormEditorItem::type() const From 18dfb7d42b5e6de3c33f2426d9d3e4f10c1902de Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 24 Apr 2020 19:52:32 +0200 Subject: [PATCH 27/28] QmlDesigner: Rename member Change-Id: Ia4f249b39b14dc18def62f4d275e3fd1f9f2b0bc Reviewed-by: Tim Jenssen --- .../qmldesigner/components/formeditor/transitiontool.cpp | 8 ++++---- .../qmldesigner/components/formeditor/transitiontool.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp index b1cee8c443b..e8222e8ac8a 100644 --- a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp @@ -229,7 +229,7 @@ void TransitionTool::clear() void TransitionTool::mousePressEvent(const QList &itemList, QGraphicsSceneMouseEvent *event) { - if (m_block) + if (m_blockEvents) return; if (event->button() != Qt::LeftButton) @@ -291,7 +291,7 @@ void TransitionTool::dragMoveEvent(const QList &/*itemList*/, Q void TransitionTool::mouseReleaseEvent(const QList &itemList, QGraphicsSceneMouseEvent *event) { - if (m_block) + if (m_blockEvents) return; if (event->button() == Qt::LeftButton) { @@ -362,7 +362,7 @@ void TransitionTool::activateTool() void TransitionTool::unblock() { - m_block = false; + m_blockEvents = false; } QGraphicsLineItem *TransitionTool::lineItem() @@ -389,7 +389,7 @@ FormEditorItem *TransitionTool::currentFormEditorItem() const } void TransitionTool::createItems() { - m_block = true; + m_blockEvents = true; QTimer::singleShot(200, this, [this](){ unblock(); }); if (!lineItem()) diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.h b/src/plugins/qmldesigner/components/formeditor/transitiontool.h index 6efa1d177a1..43c3894933e 100644 --- a/src/plugins/qmldesigner/components/formeditor/transitiontool.h +++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.h @@ -92,7 +92,7 @@ private: std::unique_ptr m_lineItem; std::unique_ptr m_rectangleItem1; std::unique_ptr m_rectangleItem2; - bool m_block = true; + bool m_blockEvents = true; }; } //QmlDesigner From 0e1870368c8ab7de0f7a90f7149d0b83a1d52090 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 27 Apr 2020 20:01:38 +0200 Subject: [PATCH 28/28] Sqlite: Add Sqlite::Value It adds a layer if you don't know if the type is integer, float or string. It does not handle bytearrays here because so far there is no need. There are two classes, Sqlite::Value and Sqlite::ValueView. Value owns the string, ValueView holds only a view the string. So there is no allocation. It is designed to hold Utf-8 string like Sqlite but it can be easily converted in and from QString or QVariant but mind about that this is not free. ValueView has no constructors on perpose because it would be ambiguous if there would be constructors for the other primitives of the Sqlite layer like "int64", "double" and "string view". Change-Id: Ia39364eb2fc1998e5c59fdb4316add22c748507d Reviewed-by: Tim Jenssen --- src/libs/sqlite/CMakeLists.txt | 1 + src/libs/sqlite/sqlite-lib.pri | 1 + src/libs/sqlite/sqlitebasestatement.cpp | 43 ++- src/libs/sqlite/sqlitebasestatement.h | 42 +-- src/libs/sqlite/sqliteexception.h | 8 + src/libs/sqlite/sqlitevalue.h | 300 +++++++++++++++ tests/unit/unittest/CMakeLists.txt | 15 +- tests/unit/unittest/creator_dependency.pri | 2 +- .../unit/unittest/gtest-creator-printing.cpp | 29 +- tests/unit/unittest/gtest-creator-printing.h | 6 + tests/unit/unittest/sqlitestatement-test.cpp | 48 ++- tests/unit/unittest/sqlitevalue-test.cpp | 348 ++++++++++++++++++ tests/unit/unittest/unittest.pro | 19 +- 13 files changed, 801 insertions(+), 61 deletions(-) create mode 100644 src/libs/sqlite/sqlitevalue.h create mode 100644 tests/unit/unittest/sqlitevalue-test.cpp diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index 62c8609a7af..af6482e4a32 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -20,6 +20,7 @@ add_qtc_library(Sqlite sqlitetable.h sqlitetransaction.h sqlitewritestatement.cpp sqlitewritestatement.h + sqlitevalue.h sqlstatementbuilder.cpp sqlstatementbuilder.h sqlstatementbuilderexception.h utf8string.cpp utf8string.h diff --git a/src/libs/sqlite/sqlite-lib.pri b/src/libs/sqlite/sqlite-lib.pri index 4d7f906a199..1ce343b6247 100644 --- a/src/libs/sqlite/sqlite-lib.pri +++ b/src/libs/sqlite/sqlite-lib.pri @@ -32,6 +32,7 @@ HEADERS += \ $$PWD/sqlitereadstatement.h \ $$PWD/sqlitereadwritestatement.h \ $$PWD/sqlitetransaction.h \ + $$PWD/sqlitevalue.h \ $$PWD/sqlitewritestatement.h \ $$PWD/sqlstatementbuilder.h \ $$PWD/sqlstatementbuilderexception.h \ diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 87aa3f68b37..85a6db2ddcd 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -190,6 +190,21 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) checkForBindingError(resultCode); } +void BaseStatement::bind(int index, const Value &value) +{ + switch (value.type()) { + case ValueType::Integer: + bind(index, value.toInteger()); + break; + case ValueType::Float: + bind(index, value.toFloat()); + break; + case ValueType::String: + bind(index, value.toStringView()); + break; + } +} + template void BaseStatement::bind(Utils::SmallStringView name, Type value) { @@ -498,12 +513,34 @@ StringType BaseStatement::fetchValue(int column) const return convertToTextForColumn(m_compiledStatement.get(), column); } +template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue( + int column) const; +template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue( + int column) const; +template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue( + int column) const; + Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const { return fetchValue(column); } -template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue(int column) const; -template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue(int column) const; -template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue(int column) const; +ValueView BaseStatement::fetchValueView(int column) const +{ + int dataType = sqlite3_column_type(m_compiledStatement.get(), column); + switch (dataType) { + case SQLITE_INTEGER: + return ValueView::create(fetchLongLongValue(column)); + case SQLITE_FLOAT: + return ValueView::create(fetchDoubleValue(column)); + case SQLITE3_TEXT: + return ValueView::create(fetchValue(column)); + case SQLITE_BLOB: + case SQLITE_NULL: + break; + } + + return ValueView::create(0LL); +} + } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 86d03fd8553..aa40e0c6ea2 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -28,6 +28,7 @@ #include "sqliteglobal.h" #include "sqliteexception.h" +#include "sqlitevalue.h" #include @@ -67,6 +68,7 @@ public: long long fetchLongLongValue(int column) const; double fetchDoubleValue(int column) const; Utils::SmallStringView fetchSmallStringViewValue(int column) const; + ValueView fetchValueView(int column) const; template Type fetchValue(int column) const; int columnCount() const; @@ -76,11 +78,9 @@ public: void bind(int index, long long fetchValue); void bind(int index, double fetchValue); void bind(int index, Utils::SmallStringView fetchValue); + void bind(int index, const Value &fetchValue); - void bind(int index, uint value) - { - bind(index, static_cast(value)); - } + void bind(int index, uint value) { bind(index, static_cast(value)); } void bind(int index, long value) { @@ -345,34 +345,16 @@ private: struct ValueGetter { ValueGetter(StatementImplementation &statement, int column) - : statement(statement), - column(column) + : statement(statement) + , column(column) {} - operator int() - { - return statement.fetchIntValue(column); - } - - operator long() - { - return statement.fetchLongValue(column); - } - - operator long long() - { - return statement.fetchLongLongValue(column); - } - - operator double() - { - return statement.fetchDoubleValue(column); - } - - operator Utils::SmallStringView() - { - return statement.fetchSmallStringViewValue(column); - } + operator int() { return statement.fetchIntValue(column); } + operator long() { return statement.fetchLongValue(column); } + operator long long() { return statement.fetchLongLongValue(column); } + operator double() { return statement.fetchDoubleValue(column); } + operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); } + operator ValueView() { return statement.fetchValueView(column); } StatementImplementation &statement; int column; diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h index 8edfd984ef4..6f898504a4c 100644 --- a/src/libs/sqlite/sqliteexception.h +++ b/src/libs/sqlite/sqliteexception.h @@ -264,4 +264,12 @@ public: } }; +class CannotConvert : public Exception +{ +public: + CannotConvert(const char *whatErrorHasHappen) + : Exception(whatErrorHasHappen) + {} +}; + } // namespace Sqlite diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h new file mode 100644 index 00000000000..ca576421777 --- /dev/null +++ b/src/libs/sqlite/sqlitevalue.h @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sqliteexception.h" + +#include +#include + +#include + +#pragma once + +namespace Sqlite { + +enum class ValueType : unsigned char { Integer, Float, String }; + +template +class ValueBase +{ +public: + using VariantType = Utils::variant; + + explicit ValueBase(VariantType &&value) + : value(value) + {} + + explicit ValueBase(const char *value) + : value(Utils::SmallStringView{value}) + {} + + explicit ValueBase(long long value) + : value(value) + {} + explicit ValueBase(int value) + : value(static_cast(value)) + {} + + explicit ValueBase(uint value) + : value(static_cast(value)) + {} + + explicit ValueBase(double value) + : value(value) + {} + + explicit ValueBase(Utils::SmallStringView value) + : value(value) + + {} + + long long toInteger() const { return Utils::get(value); } + + double toFloat() const { return Utils::get(value); } + + Utils::SmallStringView toStringView() const + { + return Utils::get(value); + } + + explicit operator QVariant() const + { + switch (type()) { + case ValueType::Integer: + return QVariant(toInteger()); + case ValueType::Float: + return QVariant(toFloat()); + case ValueType::String: + return QVariant(QString(toStringView())); + } + + return {}; + } + + friend bool operator==(const ValueBase &first, long long second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(long long first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, double second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(const ValueBase &first, int second) + { + return first == static_cast(second); + } + + friend bool operator==(int first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, uint second) + { + return first == static_cast(second); + } + + friend bool operator==(uint first, const ValueBase &second) { return second == first; } + + friend bool operator==(double first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, Utils::SmallStringView second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger && *maybeInteger == second; + } + + friend bool operator==(Utils::SmallStringView first, const ValueBase &second) + { + return second == first; + } + + friend bool operator==(const ValueBase &first, const QString &second) + { + auto maybeInteger = Utils::get_if(&first.value); + + return maybeInteger + && second == QLatin1String{maybeInteger->data(), int(maybeInteger->size())}; + } + + friend bool operator==(const QString &first, const ValueBase &second) + { + return second == first; + } + + friend bool operator==(const ValueBase &first, const char *second) + { + return first == Utils::SmallStringView{second}; + } + + friend bool operator==(const char *first, const ValueBase &second) { return second == first; } + + friend bool operator==(const ValueBase &first, const ValueBase &second) + { + return first.value == second.value; + } + + friend bool operator!=(const ValueBase &first, const ValueBase &second) + { + return !(first.value == second.value); + } + + ValueType type() const + { + switch (value.index()) { + case 0: + return ValueType::Integer; + case 1: + return ValueType::Float; + case 2: + return ValueType::String; + } + + return {}; + } + +public: + VariantType value; +}; + +class ValueView : public ValueBase +{ +public: + explicit ValueView(ValueBase &&base) + : ValueBase(std::move(base)) + {} + + template + static ValueView create(Type &&value) + { + return ValueView{ValueBase{value}}; + } +}; + +class Value : public ValueBase +{ + using Base = ValueBase; + +public: + using Base::Base; + + explicit Value(ValueView view) + : ValueBase(convert(view)) + {} + + explicit Value(const QVariant &value) + : ValueBase(convert(value)) + {} + + explicit Value(Utils::SmallString &&value) + : ValueBase(VariantType{std::move(value)}) + {} + + explicit Value(const Utils::SmallString &value) + : ValueBase(Utils::SmallStringView(value)) + {} + + explicit Value(const QString &value) + : ValueBase(VariantType{Utils::SmallString(value)}) + {} + + friend bool operator!=(const Value &first, const Value &second) + { + return !(first.value == second.value); + } + + template + friend bool operator!=(const Value &first, const Type &second) + { + return !(first == second); + } + + template + friend bool operator!=(const Type &first, const Value &second) + { + return !(first == second); + } + + friend bool operator==(const Value &first, const ValueView &second) + { + if (first.type() != second.type()) + return false; + + switch (first.type()) { + case ValueType::Integer: + return first.toInteger() == second.toInteger(); + case ValueType::Float: + return first.toFloat() == second.toFloat(); + case ValueType::String: + return first.toStringView() == second.toStringView(); + } + + return false; + } + + friend bool operator==(const ValueView &first, const Value &second) { return second == first; } + +private: + static Base::VariantType convert(const QVariant &value) + { + switch (value.type()) { + case QVariant::Int: + return VariantType{static_cast(value.toInt())}; + case QVariant::LongLong: + return VariantType{value.toLongLong()}; + case QVariant::UInt: + return VariantType{static_cast(value.toUInt())}; + case QVariant::Double: + return VariantType{value.toFloat()}; + case QVariant::String: + return VariantType{value.toString()}; + default: + throw CannotConvert("Cannot convert this QVariant to a ValueBase"); + } + } + + static Base::VariantType convert(ValueView view) + { + switch (view.type()) { + case ValueType::Integer: + return VariantType{view.toInteger()}; + case ValueType::Float: + return VariantType{view.toFloat()}; + case ValueType::String: + return VariantType{view.toStringView()}; + default: + throw CannotConvert("Cannot convert this QVariant to a ValueBase"); + } + } +}; + +using Values = std::vector; +} // namespace Sqlite diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 4e7b97d53b7..904af9b6d16 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -164,6 +164,14 @@ add_qtc_test(unittest GTEST unittest-utility-functions.h usedmacrofilter-test.cpp utf8-test.cpp + sqlitecolumn-test.cpp + sqlitedatabasebackend-test.cpp + sqlitedatabase-test.cpp + sqlitestatement-test.cpp + sqlitetable-test.cpp + sqlstatementbuilder-test.cpp + createtablesqlstatementbuilder-test.cpp + sqlitevalue-test.cpp ) # Do not work on the source directory data @@ -219,7 +227,6 @@ if (TARGET libclang) codecompleter-test.cpp codecompletionsextractor-test.cpp completionchunkstotextconverter-test.cpp - createtablesqlstatementbuilder-test.cpp cursor-test.cpp diagnosticset-test.cpp diagnostic-test.cpp @@ -228,12 +235,6 @@ if (TARGET libclang) skippedsourceranges-test.cpp sourcelocation-test.cpp sourcerange-test.cpp - sqlitecolumn-test.cpp - sqlitedatabasebackend-test.cpp - sqlitedatabase-test.cpp - sqlitestatement-test.cpp - sqlitetable-test.cpp - sqlstatementbuilder-test.cpp token-test.cpp translationunitupdater-test.cpp unsavedfiles-test.cpp diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index 2e84e1cdcab..e5b10f65ae8 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -14,11 +14,11 @@ include($$PWD/../../../src/tools/clangpchmanagerbackend/source/clangpchmanagerba include($$PWD/../../../src/plugins/clangrefactoring/clangrefactoring-source.pri) include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) -include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) include($$PWD/../../../src/plugins/debugger/debuggerunittestfiles.pri) include($$PWD/../../../src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri) include(cplusplus.pri) !isEmpty(LLVM_VERSION) { +include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) include($$PWD/../../../src/shared/clang/clang_defines.pri) include($$PWD/../../../src/tools/clangbackend/source/clangbackendclangipc-source.pri) include($$PWD/../../../src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri) diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 2bbcdbaa45e..6d5f7a2cf34 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -64,12 +66,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include namespace { ClangBackEnd::FilePathCaching *filePathCache = nullptr; @@ -306,6 +302,27 @@ void PrintTo(const Utils::PathString &text, ::std::ostream *os) } // namespace Utils +namespace Sqlite { +std::ostream &operator<<(std::ostream &out, const Value &value) +{ + out << "("; + + switch (value.type()) { + case Sqlite::ValueType::Integer: + out << value.toInteger(); + break; + case Sqlite::ValueType::Float: + out << value.toFloat(); + break; + case Sqlite::ValueType::String: + out << "\"" << value.toStringView() << "\""; + break; + } + + return out << ")"; +} +} // namespace Sqlite + namespace ClangBackEnd { std::ostream &operator<<(std::ostream &out, const FilePathId &id) diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index e26809ca519..b91c8d46beb 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -64,6 +64,12 @@ void PrintTo(const TextRange &range, ::std::ostream *os); } // namespace TextPosition } // namespace TextPosition +namespace Sqlite { +class Value; + +std::ostream &operator<<(std::ostream &out, const Value &value); +} // namespace Sqlite + namespace ProjectExplorer { enum class MacroType; diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp index cb8206d8757..c7da3971e4f 100644 --- a/tests/unit/unittest/sqlitestatement-test.cpp +++ b/tests/unit/unittest/sqlitestatement-test.cpp @@ -40,11 +40,12 @@ namespace { -using Sqlite::JournalMode; -using Sqlite::Exception; using Sqlite::Database; +using Sqlite::Exception; +using Sqlite::JournalMode; using Sqlite::ReadStatement; using Sqlite::ReadWriteStatement; +using Sqlite::Value; using Sqlite::WriteStatement; MATCHER_P3(HasValues, value1, value2, rowid, @@ -125,7 +126,7 @@ TEST_F(SqliteStatement, CountRows) TEST_F(SqliteStatement, Value) { - SqliteTestStatement statement("SELECT name, number FROM test ORDER BY name", database); + SqliteTestStatement statement("SELECT name, number, value FROM test ORDER BY name", database); statement.next(); statement.next(); @@ -142,6 +143,9 @@ TEST_F(SqliteStatement, Value) ASSERT_THAT(statement.fetchValue(1), "23.3"); ASSERT_THAT(statement.fetchValue(1), "23.3"); ASSERT_THAT(statement.fetchSmallStringViewValue(1), "23.3"); + ASSERT_THAT(statement.fetchValueView(0), Eq("foo")); + ASSERT_THAT(statement.fetchValueView(1), Eq(23.3)); + ASSERT_THAT(statement.fetchValueView(2), Eq(2)); } TEST_F(SqliteStatement, ThrowNoValuesToFetchForNotSteppedStatement) @@ -175,14 +179,14 @@ TEST_F(SqliteStatement, ThrowInvalidColumnFetchedForNotExistingColumn) ASSERT_THROW(statement.fetchValue(2), Sqlite::InvalidColumnFetched); } -TEST_F(SqliteStatement, ToIntergerValue) +TEST_F(SqliteStatement, ToIntegerValue) { auto value = ReadStatement::toValue("SELECT number FROM test WHERE name='foo'", database); ASSERT_THAT(value, 23); } -TEST_F(SqliteStatement, ToLongIntergerValue) +TEST_F(SqliteStatement, ToLongIntegerValue) { ASSERT_THAT(ReadStatement::toValue("SELECT number FROM test WHERE name='foo'", database), Eq(23)); } @@ -319,6 +323,15 @@ TEST_F(SqliteStatement, WriteValues) ASSERT_THAT(statement, HasValues("see", "7.23", 1)); } +TEST_F(SqliteStatement, WriteSqliteValues) +{ + WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database); + + statement.write(Value{"see"}, Value{7.23}, Value{1}); + + ASSERT_THAT(statement, HasValues("see", "7.23", 1)); +} + TEST_F(SqliteStatement, BindNamedValues) { SqliteTestStatement statement("UPDATE test SET name=@name, number=@number WHERE rowid=@id", database); @@ -375,6 +388,31 @@ TEST_F(SqliteStatement, GetSingleValuesWithoutArguments) ASSERT_THAT(values, ElementsAre("bar", "foo", "poo")); } +class FooValue +{ +public: + FooValue(Sqlite::ValueView value) + : value(value) + {} + + Sqlite::Value value; + + template + friend bool operator==(const FooValue &value, const Type &other) + { + return value.value == other; + } +}; + +TEST_F(SqliteStatement, GetSingleSqliteValuesWithoutArguments) +{ + ReadStatement statement("SELECT number FROM test", database); + + std::vector values = statement.values(3); + + ASSERT_THAT(values, ElementsAre(Eq("blah"), Eq(23.3), Eq(40))); +} + TEST_F(SqliteStatement, GetStructValuesWithoutArguments) { ReadStatement statement("SELECT name, number, value FROM test", database); diff --git a/tests/unit/unittest/sqlitevalue-test.cpp b/tests/unit/unittest/sqlitevalue-test.cpp new file mode 100644 index 00000000000..e9cc5c9e408 --- /dev/null +++ b/tests/unit/unittest/sqlitevalue-test.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "googletest.h" + +#include + +namespace { + +TEST(SqliteValue, ConstructLongLong) +{ + Sqlite::Value value{1LL}; + + ASSERT_THAT(value.toInteger(), Eq(1LL)); +} + +TEST(SqliteValue, Construct) +{ + Sqlite::Value value{1}; + + ASSERT_THAT(value.toInteger(), Eq(1LL)); +} + +TEST(SqliteValue, ConstructFloatingPoint) +{ + Sqlite::Value value{1.1}; + + ASSERT_THAT(value.toFloat(), Eq(1.1)); +} + +TEST(SqliteValue, ConstructStringFromCString) +{ + Sqlite::Value value{"foo"}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromUtilsString) +{ + Sqlite::Value value{Utils::SmallString{"foo"}}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromQString) +{ + Sqlite::Value value{QString{"foo"}}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConstructStringFromIntQVariant) +{ + QVariant variant{1}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromLongLongQVariant) +{ + QVariant variant{1LL}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromUintQVariant) +{ + QVariant variant{1u}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toInteger(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromFloatQVariant) +{ + QVariant variant{1.}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toFloat(), Eq(1)); +} + +TEST(SqliteValue, ConstructStringFromStringQVariant) +{ + QVariant variant{QString{"foo"}}; + + Sqlite::Value value{variant}; + + ASSERT_THAT(value.toStringView(), Eq("foo")); +} + +TEST(SqliteValue, ConvertToStringQVariant) +{ + Sqlite::Value value{"foo"}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq("foo")); +} + +TEST(SqliteValue, ConvertToIntegerQVariant) +{ + Sqlite::Value value{1}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq(1)); +} + +TEST(SqliteValue, ConvertToFloatQVariant) +{ + Sqlite::Value value{1.1}; + + auto variant = QVariant{value}; + + ASSERT_THAT(variant, Eq(1.1)); +} + +TEST(SqliteValue, IntegerEquals) +{ + bool isEqual = Sqlite::Value{1} == 1LL; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerEqualsInverse) +{ + bool isEqual = 1LL == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatEquals) +{ + bool isEqual = Sqlite::Value{1.0} == 1.; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatEqualsInverse) +{ + bool isEqual = 1. == Sqlite::Value{1.0}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringEquals) +{ + bool isEqual = Sqlite::Value{"foo"} == "foo"; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringEqualsInverse) +{ + bool isEqual = "foo" == Sqlite::Value{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatAreNotEquals) +{ + bool isEqual = Sqlite::Value{1} == 1.; + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, IntegerValuesAreEquals) +{ + bool isEqual = Sqlite::Value{1} == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatValuesAreNotEquals) +{ + bool isEqual = Sqlite::Value{1} == Sqlite::Value{1.}; + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, StringAndQStringAreEquals) +{ + bool isEqual = Sqlite::Value{"foo"} == QString{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerAndFloatValuesAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != Sqlite::Value{1.0}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerAndFloatAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != 1.0; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerAndFloatAreUnequalInverse) +{ + bool isUnequal = 1.0 != Sqlite::Value{1}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegersAreUnequal) +{ + bool isUnequal = Sqlite::Value{1} != 2; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegersAreUnequalInverse) +{ + bool isUnequal = 2 != Sqlite::Value{1}; + + ASSERT_TRUE(isUnequal); +} + +TEST(SqliteValue, IntegerType) +{ + auto type = Sqlite::Value{1}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::Integer); +} + +TEST(SqliteValue, FloatType) +{ + auto type = Sqlite::Value{1.}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::Float); +} + +TEST(SqliteValue, StringType) +{ + auto type = Sqlite::Value{"foo"}.type(); + + ASSERT_THAT(type, Sqlite::ValueType::String); +} + +TEST(SqliteValue, StringValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create("foo") == Sqlite::Value{"foo"}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{"foo"} == Sqlite::ValueView::create("foo"); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create(1) == Sqlite::Value{1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, IntegerValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{2} == Sqlite::ValueView::create(2); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatValueAndValueViewEquals) +{ + bool isEqual = Sqlite::ValueView::create(1.1) == Sqlite::Value{1.1}; + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, FloatValueAndValueViewEqualsInverse) +{ + bool isEqual = Sqlite::Value{1.1} == Sqlite::ValueView::create(1.1); + + ASSERT_TRUE(isEqual); +} + +TEST(SqliteValue, StringValueAndIntergerValueViewAreNotEqual) +{ + bool isEqual = Sqlite::Value{"foo"} == Sqlite::ValueView::create(1); + + ASSERT_FALSE(isEqual); +} + +TEST(SqliteValue, ConvertStringValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create("foo"); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq("foo")); +} + +TEST(SqliteValue, ConvertIntegerValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create(1); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq(1)); +} + +TEST(SqliteValue, ConvertFloatValueViewIntoValue) +{ + auto view = Sqlite::ValueView::create(1.4); + + Sqlite::Value value{view}; + + ASSERT_THAT(value, Eq(1.4)); +} + +} // namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 1db48813e85..6155cf32128 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -80,6 +80,7 @@ SOURCES += \ smallstring-test.cpp \ sourcerangefilter-test.cpp \ spydummy.cpp \ + sqlitevalue-test.cpp \ symbolindexer-test.cpp \ symbolsfindfilter-test.cpp \ stringcache-test.cpp \ @@ -122,7 +123,13 @@ SOURCES += \ headerpathfilter-test.cpp \ toolchainargumentscache-test.cpp \ modifiedtimechecker-test.cpp \ - readexporteddiagnostics-test.cpp + sqlitecolumn-test.cpp \ + sqlitedatabasebackend-test.cpp \ + sqlitedatabase-test.cpp \ + sqlitestatement-test.cpp \ + sqlitetable-test.cpp \ + sqlstatementbuilder-test.cpp \ + createtablesqlstatementbuilder-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \ @@ -158,7 +165,6 @@ SOURCES += \ codecompleter-test.cpp \ codecompletionsextractor-test.cpp \ completionchunkstotextconverter-test.cpp \ - createtablesqlstatementbuilder-test.cpp \ cursor-test.cpp \ diagnosticset-test.cpp \ diagnostic-test.cpp \ @@ -168,17 +174,12 @@ SOURCES += \ skippedsourceranges-test.cpp \ sourcelocation-test.cpp \ sourcerange-test.cpp \ - sqlitecolumn-test.cpp \ - sqlitedatabasebackend-test.cpp \ - sqlitedatabase-test.cpp \ - sqlitestatement-test.cpp \ - sqlitetable-test.cpp \ - sqlstatementbuilder-test.cpp \ token-test.cpp \ translationunitupdater-test.cpp \ unsavedfiles-test.cpp \ unsavedfile-test.cpp \ - utf8positionfromlinecolumn-test.cpp + utf8positionfromlinecolumn-test.cpp \ + readexporteddiagnostics-test.cpp } !isEmpty(LIBTOOLING_LIBS) {