forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/4.15'
Conflicts: cmake/QtCreatorIDEBranding.cmake qbs/modules/qtc/qtc.qbs qtcreator_ide_branding.pri src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp Change-Id: I403b236c40d73a61ae22304e289e9d4374366395
This commit is contained in:
@@ -421,17 +421,17 @@ void BoostTestOutputReader::onFinished(int exitCode, QProcess::ExitStatus /*exit
|
||||
if (m_logLevel == LogLevel::Nothing && m_reportLevel == ReportLevel::No) {
|
||||
switch (exitCode) {
|
||||
case 0:
|
||||
reportNoOutputFinish(tr("Running tests exited with ") + "boost::exit_success.",
|
||||
reportNoOutputFinish(tr("Running tests exited with %1").arg("boost::exit_success."),
|
||||
ResultType::Pass);
|
||||
break;
|
||||
case 200:
|
||||
reportNoOutputFinish(
|
||||
tr("Running tests exited with ") + "boost::exit_test_exception.",
|
||||
tr("Running tests exited with %1").arg("boost::exit_test_exception."),
|
||||
ResultType::MessageFatal);
|
||||
break;
|
||||
case 201:
|
||||
reportNoOutputFinish(tr("Running tests exited with ")
|
||||
+ "boost::exit_test_failure.", ResultType::Fail);
|
||||
reportNoOutputFinish(tr("Running tests exited with %1")
|
||||
.arg("boost::exit_test_failure."), ResultType::Fail);
|
||||
break;
|
||||
}
|
||||
} else if (exitCode != 0 && exitCode != 201 && !m_description.isEmpty()) {
|
||||
|
||||
@@ -27,51 +27,54 @@
|
||||
|
||||
namespace ClangRefactoring {
|
||||
|
||||
template<typename Database,
|
||||
typename ReadStatement>
|
||||
template<typename Database>
|
||||
class QuerySqliteStatementFactory
|
||||
{
|
||||
public:
|
||||
using DatabaseType = Database;
|
||||
using ReadStatementType = ReadStatement;
|
||||
template<int ResultCount>
|
||||
using ReadStatement = typename Database::template ReadStatement<ResultCount>;
|
||||
|
||||
QuerySqliteStatementFactory(Database &database)
|
||||
: database(database)
|
||||
{}
|
||||
Database &database;
|
||||
ReadStatement selectLocationsForSymbolLocation{
|
||||
ReadStatement<3> selectLocationsForSymbolLocation{
|
||||
"SELECT sourceId, line, column FROM locations WHERE symbolId = "
|
||||
" (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND column=?) "
|
||||
"ORDER BY sourceId, line, column",
|
||||
database};
|
||||
ReadStatement selectSourceUsagesForSymbolLocation{
|
||||
ReadStatement<3> selectSourceUsagesForSymbolLocation{
|
||||
"SELECT directoryPath || '/' || sourceName, line, column "
|
||||
"FROM locations NATURAL JOIN sources NATURAL JOIN directories "
|
||||
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
|
||||
"column=?)",
|
||||
database};
|
||||
ReadStatement selectSourceUsagesOrderedForSymbolLocation{
|
||||
ReadStatement<3> selectSourceUsagesOrderedForSymbolLocation{
|
||||
"SELECT directoryPath || '/' || sourceName, line, column "
|
||||
"FROM locations NATURAL JOIN sources NATURAL JOIN directories "
|
||||
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
|
||||
"column=?) ORDER BY locationKind LIMIT 2",
|
||||
database};
|
||||
ReadStatement selectSourceUsagesByLocationKindForSymbolLocation{
|
||||
ReadStatement<3> selectSourceUsagesByLocationKindForSymbolLocation{
|
||||
"SELECT directoryPath || '/' || sourceName, line, column "
|
||||
"FROM locations NATURAL JOIN sources NATURAL JOIN directories "
|
||||
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
|
||||
"column=?) AND locationKind = ?",
|
||||
database};
|
||||
ReadStatement selectSymbolsForKindAndStartsWith{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind = ? AND symbolName LIKE ?",
|
||||
ReadStatement<3> selectSymbolsForKindAndStartsWith{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind = ? AND symbolName "
|
||||
"LIKE ?",
|
||||
database};
|
||||
ReadStatement selectSymbolsForKindAndStartsWith2{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind IN (?,?) AND symbolName LIKE ?",
|
||||
ReadStatement<3> selectSymbolsForKindAndStartsWith2{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind IN (?,?) AND "
|
||||
"symbolName LIKE ?",
|
||||
database};
|
||||
ReadStatement selectSymbolsForKindAndStartsWith3{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind IN (?,?,?) AND symbolName LIKE ?",
|
||||
ReadStatement<3> selectSymbolsForKindAndStartsWith3{
|
||||
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind IN (?,?,?) AND "
|
||||
"symbolName LIKE ?",
|
||||
database};
|
||||
ReadStatement selectLocationOfSymbol{
|
||||
ReadStatement<3> selectLocationOfSymbol{
|
||||
"SELECT sourceId, line, column FROM locations AS l WHERE symbolId = ? AND locationKind = ?",
|
||||
database};
|
||||
};
|
||||
|
||||
@@ -40,8 +40,6 @@ namespace ClangRefactoring {
|
||||
template <typename StatementFactory>
|
||||
class SymbolQuery final : public SymbolQueryInterface
|
||||
{
|
||||
using ReadStatement = typename StatementFactory::ReadStatementType;
|
||||
|
||||
public:
|
||||
SymbolQuery(StatementFactory &statementFactory)
|
||||
: m_statementFactory(statementFactory)
|
||||
@@ -51,28 +49,28 @@ public:
|
||||
int line,
|
||||
int utf8Column) const override
|
||||
{
|
||||
ReadStatement &locationsStatement = m_statementFactory.selectLocationsForSymbolLocation;
|
||||
auto &locationsStatement = m_statementFactory.selectLocationsForSymbolLocation;
|
||||
|
||||
const std::size_t reserveSize = 128;
|
||||
|
||||
return locationsStatement.template values<SourceLocation, 3>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
return locationsStatement.template values<SourceLocation>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
}
|
||||
|
||||
CppTools::Usages sourceUsagesAt(ClangBackEnd::FilePathId filePathId,
|
||||
int line,
|
||||
int utf8Column) const override
|
||||
{
|
||||
ReadStatement &locationsStatement = m_statementFactory.selectSourceUsagesForSymbolLocation;
|
||||
auto &locationsStatement = m_statementFactory.selectSourceUsagesForSymbolLocation;
|
||||
|
||||
const std::size_t reserveSize = 128;
|
||||
|
||||
return locationsStatement.template values<CppTools::Usage, 3>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
return locationsStatement.template values<CppTools::Usage>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
}
|
||||
|
||||
CppTools::Usages sourceUsagesAtByLocationKind(ClangBackEnd::FilePathId filePathId,
|
||||
@@ -80,46 +78,46 @@ public:
|
||||
int utf8Column,
|
||||
ClangBackEnd::SourceLocationKind kind) const override
|
||||
{
|
||||
ReadStatement &locationsStatement = m_statementFactory.selectSourceUsagesByLocationKindForSymbolLocation;
|
||||
auto &locationsStatement = m_statementFactory.selectSourceUsagesByLocationKindForSymbolLocation;
|
||||
|
||||
const std::size_t reserveSize = 128;
|
||||
|
||||
return locationsStatement.template values<CppTools::Usage, 3>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column,
|
||||
int(kind));
|
||||
return locationsStatement.template values<CppTools::Usage>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column,
|
||||
int(kind));
|
||||
}
|
||||
|
||||
CppTools::Usages declarationsAt(ClangBackEnd::FilePathId filePathId,
|
||||
int line,
|
||||
int utf8Column) const override
|
||||
{
|
||||
ReadStatement &locationsStatement = m_statementFactory.selectSourceUsagesOrderedForSymbolLocation;
|
||||
auto &locationsStatement = m_statementFactory.selectSourceUsagesOrderedForSymbolLocation;
|
||||
|
||||
const std::size_t reserveSize = 128;
|
||||
|
||||
return locationsStatement.template values<CppTools::Usage, 3>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
return locationsStatement.template values<CppTools::Usage>(reserveSize,
|
||||
filePathId.filePathId,
|
||||
line,
|
||||
utf8Column);
|
||||
}
|
||||
|
||||
Symbols symbolsWithOneSymbolKinds(ClangBackEnd::SymbolKind symbolKind,
|
||||
Utils::SmallStringView searchTerm) const
|
||||
{
|
||||
ReadStatement &statement = m_statementFactory.selectSymbolsForKindAndStartsWith;
|
||||
auto &statement = m_statementFactory.selectSymbolsForKindAndStartsWith;
|
||||
|
||||
return statement.template values<Symbol, 3>(100, int(symbolKind), searchTerm);
|
||||
return statement.template values<Symbol>(100, int(symbolKind), searchTerm);
|
||||
}
|
||||
|
||||
Symbols symbolsWithTwoSymbolKinds(ClangBackEnd::SymbolKind symbolKind1,
|
||||
ClangBackEnd::SymbolKind symbolKind2,
|
||||
Utils::SmallStringView searchTerm) const
|
||||
{
|
||||
ReadStatement &statement = m_statementFactory.selectSymbolsForKindAndStartsWith2;
|
||||
auto &statement = m_statementFactory.selectSymbolsForKindAndStartsWith2;
|
||||
|
||||
return statement.template values<Symbol, 3>(100, int(symbolKind1), int(symbolKind2), searchTerm);
|
||||
return statement.template values<Symbol>(100, int(symbolKind1), int(symbolKind2), searchTerm);
|
||||
}
|
||||
|
||||
Symbols symbolsWithThreeSymbolKinds(ClangBackEnd::SymbolKind symbolKind1,
|
||||
@@ -127,9 +125,13 @@ public:
|
||||
ClangBackEnd::SymbolKind symbolKind3,
|
||||
Utils::SmallStringView searchTerm) const
|
||||
{
|
||||
ReadStatement &statement = m_statementFactory.selectSymbolsForKindAndStartsWith3;
|
||||
auto &statement = m_statementFactory.selectSymbolsForKindAndStartsWith3;
|
||||
|
||||
return statement.template values<Symbol, 3>(100, int(symbolKind1), int(symbolKind2), int(symbolKind3), searchTerm);
|
||||
return statement.template values<Symbol>(100,
|
||||
int(symbolKind1),
|
||||
int(symbolKind2),
|
||||
int(symbolKind3),
|
||||
searchTerm);
|
||||
}
|
||||
|
||||
Symbols symbols(const ClangBackEnd::SymbolKinds &symbolKinds,
|
||||
@@ -148,9 +150,9 @@ public:
|
||||
Utils::optional<SourceLocation> locationForSymbolId(SymbolId symbolId,
|
||||
ClangBackEnd::SourceLocationKind kind) const override
|
||||
{
|
||||
ReadStatement &statement = m_statementFactory.selectLocationOfSymbol;
|
||||
auto &statement = m_statementFactory.selectLocationOfSymbol;
|
||||
|
||||
return statement.template value<SourceLocation, 3>(symbolId, int(kind));
|
||||
return statement.template value<SourceLocation>(symbolId, int(kind));
|
||||
}
|
||||
private:
|
||||
StatementFactory &m_statementFactory;
|
||||
|
||||
@@ -1155,7 +1155,7 @@ void ClangTool::updateForCurrentState()
|
||||
const bool hasErrorText = !m_infoBarWidget->errorText().isEmpty();
|
||||
const bool hasErrors = m_filesFailed > 0;
|
||||
if (hasErrors && !hasErrorText) {
|
||||
const QString text = makeLink( tr("Failed to analyze %1 files.").arg(m_filesFailed));
|
||||
const QString text = makeLink(tr("Failed to analyze %n file(s).", nullptr, m_filesFailed));
|
||||
m_infoBarWidget->setError(InfoBarWidget::Warning, text, [this]() { showOutputPane(); });
|
||||
}
|
||||
|
||||
@@ -1171,9 +1171,8 @@ void ClangTool::updateForCurrentState()
|
||||
if (m_filesCount == 0) {
|
||||
infoText = tr("Analyzing..."); // Not yet fully started/initialized
|
||||
} else {
|
||||
infoText = tr("Analyzing... %1 of %2 files processed.")
|
||||
.arg(m_filesSucceeded + m_filesFailed)
|
||||
.arg(m_filesCount);
|
||||
infoText = tr("Analyzing... %1 of %n file(s) processed.", nullptr, m_filesCount)
|
||||
.arg(m_filesSucceeded + m_filesFailed);
|
||||
}
|
||||
break;
|
||||
case State::PreparationStarted:
|
||||
@@ -1186,7 +1185,7 @@ void ClangTool::updateForCurrentState()
|
||||
infoText = tr("Analysis stopped by user.");
|
||||
break;
|
||||
case State::AnalyzerFinished:
|
||||
infoText = tr("Finished processing %1 files.").arg(m_filesCount);
|
||||
infoText = tr("Finished processing %n file(s).", nullptr, m_filesCount);
|
||||
break;
|
||||
case State::ImportFinished:
|
||||
infoText = tr("Diagnostics imported.");
|
||||
|
||||
@@ -418,7 +418,7 @@ void ClangToolRunWorker::finalize()
|
||||
{
|
||||
const QString toolName = tool()->name();
|
||||
if (m_filesNotAnalyzed.size() != 0) {
|
||||
appendMessage(tr("Error: Failed to analyze %1 files.").arg(m_filesNotAnalyzed.size()),
|
||||
appendMessage(tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()),
|
||||
ErrorMessageFormat);
|
||||
Target *target = runControl()->target();
|
||||
if (target && target->activeBuildConfiguration() && !target->activeBuildConfiguration()->buildDirectory().exists()
|
||||
|
||||
@@ -891,6 +891,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
|
||||
const Kit *k = target->kit();
|
||||
|
||||
QStringList initialArgs = defaultInitialCMakeArguments(k, info.typeName);
|
||||
setIsMultiConfig(CMakeGeneratorKitAspect::isMultiConfigGenerator(k));
|
||||
|
||||
// Android magic:
|
||||
if (DeviceTypeKitAspect::deviceTypeId(k) == Android::Constants::ANDROID_DEVICE_TYPE) {
|
||||
@@ -1335,7 +1336,7 @@ QString CMakeBuildConfiguration::cmakeBuildType() const
|
||||
config = CMakeConfigItem::itemsFromArguments(initialCMakeArguments());
|
||||
}
|
||||
|
||||
if (!config.isEmpty()) {
|
||||
if (!config.isEmpty() && !isMultiConfig()) {
|
||||
cmakeBuildType = QString::fromUtf8(CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config));
|
||||
const_cast<CMakeBuildConfiguration*>(this)
|
||||
->setCMakeBuildType(cmakeBuildType);
|
||||
@@ -1356,7 +1357,12 @@ void CMakeBuildConfiguration::setCMakeBuildType(const QString &cmakeBuildType, b
|
||||
|
||||
bool CMakeBuildConfiguration::isMultiConfig() const
|
||||
{
|
||||
return m_buildSystem->isMultiConfig();
|
||||
return m_isMultiConfig;
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::setIsMultiConfig(bool isMultiConfig)
|
||||
{
|
||||
m_isMultiConfig = isMultiConfig;
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
void setCMakeBuildType(const QString &cmakeBuildType, bool quiet = false);
|
||||
|
||||
bool isMultiConfig() const;
|
||||
void setIsMultiConfig(bool isMultiConfig);
|
||||
|
||||
signals:
|
||||
void errorOccurred(const QString &message);
|
||||
@@ -115,6 +116,7 @@ private:
|
||||
Internal::CMakeBuildSystem *m_buildSystem = nullptr;
|
||||
|
||||
QStringList m_extraCMakeArguments;
|
||||
bool m_isMultiConfig = false;
|
||||
|
||||
friend class Internal::CMakeBuildSettingsWidget;
|
||||
friend class Internal::CMakeBuildSystem;
|
||||
|
||||
@@ -530,8 +530,6 @@ void CMakeBuildStep::recreateBuildTargetsModel()
|
||||
if (idx != -1)
|
||||
m_buildTargets[idx] = QString("INSTALL");
|
||||
}
|
||||
|
||||
targetList.sort();
|
||||
targetList.removeDuplicates();
|
||||
|
||||
addItem(QString(), true);
|
||||
|
||||
@@ -78,26 +78,31 @@ using namespace Utils;
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
static void copySourcePathToClipboard(Utils::optional<QString> srcPath, const ProjectNode *node)
|
||||
static void copySourcePathsToClipboard(QStringList srcPaths, const ProjectNode *node)
|
||||
{
|
||||
QClipboard *clip = QGuiApplication::clipboard();
|
||||
|
||||
QDir projDir{node->filePath().toFileInfo().absoluteFilePath()};
|
||||
clip->setText(QDir::cleanPath(projDir.relativeFilePath(srcPath.value())));
|
||||
QString data = Utils::transform(srcPaths, [projDir](const QString &path) {
|
||||
return QDir::cleanPath(projDir.relativeFilePath(path));
|
||||
}).join(" ");
|
||||
clip->setText(data);
|
||||
}
|
||||
|
||||
static void noAutoAdditionNotify(const QStringList &filePaths, const ProjectNode *node)
|
||||
{
|
||||
Utils::optional<QString> srcPath{};
|
||||
const QStringList srcPaths = Utils::filtered(filePaths, [](const QString& file) {
|
||||
const auto mimeType = Utils::mimeTypeForFile(file).name();
|
||||
return mimeType == CppTools::Constants::C_SOURCE_MIMETYPE ||
|
||||
mimeType == CppTools::Constants::C_HEADER_MIMETYPE ||
|
||||
mimeType == CppTools::Constants::CPP_SOURCE_MIMETYPE ||
|
||||
mimeType == CppTools::Constants::CPP_HEADER_MIMETYPE ||
|
||||
mimeType == ProjectExplorer::Constants::FORM_MIMETYPE ||
|
||||
mimeType == ProjectExplorer::Constants::RESOURCE_MIMETYPE ||
|
||||
mimeType == ProjectExplorer::Constants::SCXML_MIMETYPE;
|
||||
});
|
||||
|
||||
for (const QString &file : filePaths) {
|
||||
if (Utils::mimeTypeForFile(file).name() == CppTools::Constants::CPP_SOURCE_MIMETYPE) {
|
||||
srcPath = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcPath) {
|
||||
if (!srcPaths.empty()) {
|
||||
CMakeSpecificSettings *settings = CMakeProjectPlugin::projectTypeSpecificSettings();
|
||||
switch (settings->afterAddFileSetting.value()) {
|
||||
case AskUser: {
|
||||
@@ -122,13 +127,13 @@ static void noAutoAdditionNotify(const QStringList &filePaths, const ProjectNode
|
||||
}
|
||||
|
||||
if (QDialogButtonBox::Yes == reply)
|
||||
copySourcePathToClipboard(srcPath, node);
|
||||
copySourcePathsToClipboard(srcPaths, node);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CopyFilePath: {
|
||||
copySourcePathToClipboard(srcPath, node);
|
||||
copySourcePathsToClipboard(srcPaths, node);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1063,7 +1068,11 @@ const QList<BuildTargetInfo> CMakeBuildSystem::appTargets() const
|
||||
|
||||
QStringList CMakeBuildSystem::buildTargetTitles() const
|
||||
{
|
||||
return transform(m_buildTargets, &CMakeBuildTarget::title);
|
||||
auto nonUtilityTargets = filtered(m_buildTargets, [this](const CMakeBuildTarget &target){
|
||||
return target.targetType != UtilityType ||
|
||||
CMakeBuildStep::specialTargets(usesAllCapsTargets()).contains(target.title);
|
||||
});
|
||||
return transform(nonUtilityTargets, &CMakeBuildTarget::title);
|
||||
}
|
||||
|
||||
const QList<CMakeBuildTarget> &CMakeBuildSystem::buildTargets() const
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
\"\",
|
||||
\"Alternatively, this plugin 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 plugin. 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.\"
|
||||
],
|
||||
\"Category\" : \"Utilities\",
|
||||
\"Experimental\" : true,
|
||||
\"Description\" : \"Conan integration.\",
|
||||
\"Url\" : \"http://www.qt.io\",
|
||||
|
||||
@@ -186,16 +186,10 @@ extend_qtc_plugin(Core
|
||||
SOURCES progressmanager/progressmanager_x11.cpp
|
||||
)
|
||||
|
||||
if (CRASHPAD_BACKEND_URL
|
||||
AND CRASHPAD_SRC
|
||||
AND EXISTS "${CRASHPAD_SRC}"
|
||||
AND CRASHPAD_BUILD
|
||||
AND EXISTS "${CRASHPAD_BUILD}"
|
||||
AND (WIN32 OR APPLE)) # LINUX isn't supported for now
|
||||
extend_qtc_plugin(Core
|
||||
DEFINES ENABLE_CRASHPAD
|
||||
)
|
||||
endif()
|
||||
extend_qtc_plugin(Core
|
||||
CONDITION BUILD_WITH_CRASHPAD
|
||||
DEFINES ENABLE_CRASHPAD
|
||||
)
|
||||
|
||||
if ((NOT WIN32) AND (NOT APPLE))
|
||||
# install logo
|
||||
|
||||
@@ -91,9 +91,9 @@ static ApplicationProgressView *sharedProgressView = nil;
|
||||
Q_UNUSED(rect)
|
||||
NSRect boundary = [self bounds];
|
||||
[[NSApp applicationIconImage] drawInRect:boundary
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositeCopy
|
||||
fraction:1.0];
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationCopy
|
||||
fraction:1.0];
|
||||
NSRect progressBoundary = boundary;
|
||||
progressBoundary.size.height *= 0.13;
|
||||
progressBoundary.size.width *= 0.8;
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
<height>345</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="pushButton_custom">
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
<height>1074</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
#include <QMutex>
|
||||
#include <QSet>
|
||||
#include <QThreadPool>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace CppTools::Internal;
|
||||
@@ -139,10 +139,10 @@ void StringTablePrivate::GC()
|
||||
QMutexLocker locker(&m_lock);
|
||||
|
||||
int initialSize = 0;
|
||||
QTime startTime;
|
||||
QElapsedTimer timer;
|
||||
if (DebugStringTable) {
|
||||
initialSize = m_strings.size();
|
||||
startTime = QTime::currentTime();
|
||||
timer.start();
|
||||
}
|
||||
|
||||
// Collect all QStrings which have refcount 1. (One reference in m_strings and nowhere else.)
|
||||
@@ -159,7 +159,6 @@ void StringTablePrivate::GC()
|
||||
if (DebugStringTable) {
|
||||
const int currentSize = m_strings.size();
|
||||
qDebug() << "StringTable::GC removed" << initialSize - currentSize
|
||||
<< "strings in" << startTime.msecsTo(QTime::currentTime())
|
||||
<< "ms, size is now" << currentSize;
|
||||
<< "strings in" << timer.elapsed() << "ms, size is now" << currentSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ LiteHtmlHelpViewer::LiteHtmlHelpViewer(QWidget *parent)
|
||||
, m_viewer(new QLiteHtmlWidget)
|
||||
{
|
||||
m_viewer->setResourceHandler([](const QUrl &url) { return getData(url); });
|
||||
m_viewer->setFrameStyle(QFrame::NoFrame);
|
||||
m_viewer->viewport()->installEventFilter(this);
|
||||
connect(m_viewer, &QLiteHtmlWidget::linkClicked, this, &LiteHtmlHelpViewer::setSource);
|
||||
connect(m_viewer,
|
||||
|
||||
@@ -267,6 +267,7 @@ TextBrowserHelpWidget::TextBrowserHelpWidget(TextBrowserHelpViewer *parent)
|
||||
: QTextBrowser(parent)
|
||||
, m_parent(parent)
|
||||
{
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
installEventFilter(this);
|
||||
document()->setDocumentMargin(8);
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ void CompileOutputWindow::registerPositionOf(const Task &task, int linkedOutputL
|
||||
if (linkedOutputLines <= 0)
|
||||
return;
|
||||
|
||||
const int blocknumber = m_outputWindow->document()->blockCount() - offset - 1;
|
||||
const int blocknumber = m_outputWindow->document()->blockCount() - offset;
|
||||
const int firstLine = blocknumber - linkedOutputLines - skipLines;
|
||||
const int lastLine = firstLine + linkedOutputLines - 1;
|
||||
|
||||
|
||||
@@ -431,9 +431,7 @@ RunConfigurationFactory::~RunConfigurationFactory()
|
||||
|
||||
QString RunConfigurationFactory::decoratedTargetName(const QString &targetName, Target *target)
|
||||
{
|
||||
QString displayName;
|
||||
if (!targetName.isEmpty())
|
||||
displayName = QFileInfo(targetName).completeBaseName();
|
||||
QString displayName = targetName;
|
||||
Utils::Id devType = DeviceTypeKitAspect::deviceTypeId(target->kit());
|
||||
if (devType != Constants::DESKTOP_DEVICE_TYPE) {
|
||||
if (IDevice::ConstPtr dev = DeviceKitAspect::device(target->kit())) {
|
||||
|
||||
@@ -31,6 +31,7 @@ add_qtc_plugin(QmlDesigner
|
||||
shortcutmanager.cpp shortcutmanager.h
|
||||
designermcumanager.cpp designermcumanager.h
|
||||
richtexteditordialog.cpp richtexteditordialog.h
|
||||
editorproxy.cpp editorproxy.h
|
||||
EXPLICIT_MOC
|
||||
components/propertyeditor/propertyeditorvalue.h
|
||||
components/connectioneditor/connectionviewwidget.h
|
||||
@@ -637,6 +638,10 @@ extend_qtc_plugin(QmlDesigner
|
||||
globalannotationeditordialog.cpp globalannotationeditordialog.h globalannotationeditordialog.ui
|
||||
annotationeditor.cpp annotationeditor.h
|
||||
globalannotationeditor.cpp globalannotationeditor.h
|
||||
defaultannotations.cpp defaultannotations.h
|
||||
annotationtableview.cpp annotationtableview.h
|
||||
annotationtabwidget.cpp annotationtabwidget.h
|
||||
annotationeditor.qrc
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <private/qquicktext_p.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
const QHash<QString, QString> AlignMapping{
|
||||
{"AlignRight", "RIGHT"},
|
||||
@@ -63,7 +65,10 @@ TextNodeDumper::TextNodeDumper(const QByteArrayList &lineage, const ModelNode &n
|
||||
|
||||
bool TextNodeDumper::isExportable() const
|
||||
{
|
||||
return lineage().contains("QtQuick.Text");
|
||||
const QByteArrayList &baseClasses = lineage();
|
||||
return std::any_of(baseClasses.cbegin(), baseClasses.cend(), [](const QByteArray &type) {
|
||||
return type == "QtQuick.Text" || type == "QtQuick.Controls.Label";
|
||||
});
|
||||
}
|
||||
|
||||
QJsonObject TextNodeDumper::json(Component &component) const
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "annotationcommenttab.h"
|
||||
#include "defaultannotations.h"
|
||||
#include "ui_annotationcommenttab.h"
|
||||
|
||||
#include "richtexteditor/richtexteditor.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include "QStringListModel"
|
||||
|
||||
#include "projectexplorer/session.h"
|
||||
#include "projectexplorer/target.h"
|
||||
@@ -40,7 +40,7 @@ namespace QmlDesigner {
|
||||
|
||||
AnnotationCommentTab::AnnotationCommentTab(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::AnnotationCommentTab)
|
||||
, ui(std::make_unique<Ui::AnnotationCommentTab>())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -57,33 +57,12 @@ AnnotationCommentTab::AnnotationCommentTab(QWidget *parent)
|
||||
|
||||
ui->formLayout->setWidget(3, QFormLayout::FieldRole, m_editor);
|
||||
|
||||
ui->titleEdit->setModel(new QStringListModel{QStringList{"Description",
|
||||
"Display Condition",
|
||||
"helper lines",
|
||||
"position marker",
|
||||
"highlight",
|
||||
"project author",
|
||||
"project confirmed",
|
||||
"project developer",
|
||||
"project distributor",
|
||||
"project modified",
|
||||
"project type",
|
||||
"project version",
|
||||
"Screen Description",
|
||||
"Section",
|
||||
"normalcolor",
|
||||
"focuscolor",
|
||||
"selectedcolor",
|
||||
"pressedcolor"}});
|
||||
|
||||
connect(ui->titleEdit, &QComboBox::currentTextChanged,
|
||||
this, &AnnotationCommentTab::commentTitleChanged);
|
||||
connect(ui->titleEdit, &QComboBox::currentTextChanged, this, [this](QString const &text) {
|
||||
emit titleChanged(text, this);
|
||||
});
|
||||
}
|
||||
|
||||
AnnotationCommentTab::~AnnotationCommentTab()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
AnnotationCommentTab::~AnnotationCommentTab() {}
|
||||
|
||||
Comment AnnotationCommentTab::currentComment() const
|
||||
{
|
||||
@@ -91,7 +70,10 @@ Comment AnnotationCommentTab::currentComment() const
|
||||
|
||||
result.setTitle(ui->titleEdit->currentText().trimmed());
|
||||
result.setAuthor(ui->authorEdit->text().trimmed());
|
||||
result.setText(m_editor->richText().trimmed());
|
||||
if (defaultAnnotations() && !defaultAnnotations()->isRichText(result)) {
|
||||
result.setText(m_editor->plainText().trimmed());
|
||||
} else
|
||||
result.setText(m_editor->richText().trimmed());
|
||||
|
||||
if (m_comment.sameContent(result))
|
||||
result.setTimestamp(m_comment.timestamp());
|
||||
@@ -129,9 +111,15 @@ void AnnotationCommentTab::resetComment()
|
||||
m_comment = currentComment();
|
||||
}
|
||||
|
||||
void AnnotationCommentTab::commentTitleChanged(const QString &text)
|
||||
DefaultAnnotationsModel *AnnotationCommentTab::defaultAnnotations() const
|
||||
{
|
||||
emit titleChanged(text, this);
|
||||
return m_defaults;
|
||||
}
|
||||
|
||||
void AnnotationCommentTab::setDefaultAnnotations(DefaultAnnotationsModel *defaults)
|
||||
{
|
||||
m_defaults = defaults;
|
||||
ui->titleEdit->setModel(m_defaults);
|
||||
}
|
||||
|
||||
QString AnnotationCommentTab::backupFile(const QString &filePath)
|
||||
@@ -153,10 +141,8 @@ QString AnnotationCommentTab::backupFile(const QString &filePath)
|
||||
if (!newFile.exists()) {
|
||||
QFile(oldFile.absoluteFilePath()).copy(newFile.absoluteFilePath());
|
||||
break;
|
||||
} else if (compareFileChecksum(oldFile.absoluteFilePath(),
|
||||
newFile.absoluteFilePath()) == 0) {
|
||||
} else if (compareFileChecksum(oldFile.absoluteFilePath(), newFile.absoluteFilePath()) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
newFile.setFile(imgDir, newName.arg(i));
|
||||
}
|
||||
@@ -166,9 +152,8 @@ QString AnnotationCommentTab::backupFile(const QString &filePath)
|
||||
|
||||
void AnnotationCommentTab::ensureDir(const QDir &dir)
|
||||
{
|
||||
if (!dir.exists()) {
|
||||
if (!dir.exists())
|
||||
dir.mkdir(".");
|
||||
}
|
||||
}
|
||||
|
||||
int AnnotationCommentTab::compareFileChecksum(const QString &firstFile, const QString &secondFile)
|
||||
|
||||
@@ -25,10 +25,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "annotation.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPointer>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDir;
|
||||
QT_END_NAMESPACE
|
||||
@@ -40,6 +43,7 @@ class AnnotationCommentTab;
|
||||
}
|
||||
|
||||
class RichTextEditor;
|
||||
class DefaultAnnotationsModel;
|
||||
|
||||
class AnnotationCommentTab : public QWidget
|
||||
{
|
||||
@@ -57,17 +61,18 @@ public:
|
||||
void resetUI();
|
||||
void resetComment();
|
||||
|
||||
DefaultAnnotationsModel *defaultAnnotations() const;
|
||||
void setDefaultAnnotations(DefaultAnnotationsModel *);
|
||||
|
||||
signals:
|
||||
void titleChanged(const QString &text, QWidget *widget);
|
||||
|
||||
private slots:
|
||||
void commentTitleChanged(const QString &text);
|
||||
|
||||
private:
|
||||
Ui::AnnotationCommentTab *ui;
|
||||
std::unique_ptr<Ui::AnnotationCommentTab> ui;
|
||||
RichTextEditor *m_editor;
|
||||
|
||||
Comment m_comment;
|
||||
QPointer<DefaultAnnotationsModel> m_defaults;
|
||||
|
||||
QString backupFile(const QString &filePath);
|
||||
void ensureDir(const QDir &dir);
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
#include "annotationeditor.h"
|
||||
|
||||
#include "annotationeditordialog.h"
|
||||
#include "annotation.h"
|
||||
#include "annotationeditordialog.h"
|
||||
|
||||
#include "qmlmodelnodeproxy.h"
|
||||
|
||||
@@ -35,166 +35,79 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
AnnotationEditor::AnnotationEditor(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
: ModelNodeEditorProxy(parent)
|
||||
{}
|
||||
|
||||
AnnotationEditor::~AnnotationEditor()
|
||||
AnnotationEditor::~AnnotationEditor() {}
|
||||
|
||||
QWidget *AnnotationEditor::createWidget()
|
||||
{
|
||||
hideWidget();
|
||||
const auto &node = m_modelNode;
|
||||
auto dialog = new AnnotationEditorDialog(Core::ICore::dialogParent(),
|
||||
node.id(),
|
||||
node.customId());
|
||||
dialog->setAnnotation(node.annotation());
|
||||
|
||||
QObject::connect(dialog,
|
||||
&AnnotationEditorDialog::acceptedDialog,
|
||||
this,
|
||||
&AnnotationEditor::acceptedClicked);
|
||||
QObject::connect(dialog,
|
||||
&AnnotationEditorDialog::rejected,
|
||||
this,
|
||||
&AnnotationEditor::cancelClicked);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
void AnnotationEditor::registerDeclarativeType()
|
||||
{
|
||||
qmlRegisterType<AnnotationEditor>("HelperWidgets", 2, 0, "AnnotationEditor");
|
||||
}
|
||||
|
||||
void AnnotationEditor::showWidget()
|
||||
{
|
||||
m_dialog = new AnnotationEditorDialog(Core::ICore::dialogParent(),
|
||||
m_modelNode.id(),
|
||||
m_modelNode.customId(),
|
||||
m_modelNode.annotation());
|
||||
|
||||
QObject::connect(m_dialog, &AnnotationEditorDialog::acceptedDialog,
|
||||
this, &AnnotationEditor::acceptedClicked);
|
||||
QObject::connect(m_dialog, &AnnotationEditorDialog::rejected,
|
||||
this, &AnnotationEditor::cancelClicked);
|
||||
|
||||
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
m_dialog->show();
|
||||
m_dialog->raise();
|
||||
}
|
||||
|
||||
void AnnotationEditor::showWidget(int x, int y)
|
||||
{
|
||||
showWidget();
|
||||
m_dialog->move(x, y);
|
||||
}
|
||||
|
||||
void AnnotationEditor::hideWidget()
|
||||
{
|
||||
if (m_dialog)
|
||||
m_dialog->close();
|
||||
m_dialog = nullptr;
|
||||
}
|
||||
|
||||
AnnotationEditor* AnnotationEditor::showWidget(const ModelNode &modelNode)
|
||||
{
|
||||
auto editor = new AnnotationEditor;
|
||||
|
||||
editor->setModelNode(modelNode);
|
||||
editor->showWidget();
|
||||
|
||||
connect(editor->m_dialog, &QDialog::destroyed,
|
||||
[editor]() { editor->deleteLater(); } );
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
void AnnotationEditor::setModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
m_modelNodeBackend = {};
|
||||
m_modelNode = modelNode;
|
||||
}
|
||||
|
||||
ModelNode AnnotationEditor::modelNode() const
|
||||
{
|
||||
return m_modelNode;
|
||||
}
|
||||
|
||||
void AnnotationEditor::setModelNodeBackend(const QVariant &modelNodeBackend)
|
||||
{
|
||||
if (!modelNodeBackend.isNull() && modelNodeBackend.isValid()) {
|
||||
m_modelNodeBackend = modelNodeBackend;
|
||||
|
||||
const auto modelNodeBackendObject = modelNodeBackend.value<QObject*>();
|
||||
const auto backendObjectCasted =
|
||||
qobject_cast<const QmlDesigner::QmlModelNodeProxy *>(modelNodeBackendObject);
|
||||
|
||||
if (backendObjectCasted)
|
||||
m_modelNode = backendObjectCasted->qmlObjectNode().modelNode();
|
||||
|
||||
emit modelNodeBackendChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AnnotationEditor::modelNodeBackend() const
|
||||
{
|
||||
return m_modelNodeBackend;
|
||||
}
|
||||
|
||||
bool AnnotationEditor::hasCustomId() const
|
||||
{
|
||||
if (m_modelNode.isValid())
|
||||
return m_modelNode.hasCustomId();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnnotationEditor::hasAnnotation() const
|
||||
{
|
||||
if (m_modelNode.isValid())
|
||||
return m_modelNode.hasAnnotation();
|
||||
return false;
|
||||
registerType<AnnotationEditor>("AnnotationEditor");
|
||||
}
|
||||
|
||||
void AnnotationEditor::removeFullAnnotation()
|
||||
{
|
||||
if (!m_modelNode.isValid())
|
||||
auto &node = this->m_modelNode;
|
||||
if (!node.isValid())
|
||||
return;
|
||||
|
||||
QString dialogTitle = tr("Annotation");
|
||||
if (!m_modelNode.customId().isNull()) {
|
||||
dialogTitle = m_modelNode.customId();
|
||||
if (QMessageBox::question(Core::ICore::dialogParent(),
|
||||
node.customId().isNull() ? tr("Annotation") : node.customId(),
|
||||
tr("Delete this annotation?"))
|
||||
== QMessageBox::Yes) {
|
||||
node.removeCustomId();
|
||||
node.removeAnnotation();
|
||||
emit customIdChanged();
|
||||
emit annotationChanged();
|
||||
}
|
||||
QPointer<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.removeCustomId();
|
||||
m_modelNode.removeAnnotation();
|
||||
}
|
||||
|
||||
emit customIdChanged();
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
void AnnotationEditor::acceptedClicked()
|
||||
{
|
||||
if (m_dialog) {
|
||||
if (const auto *dialog = qobject_cast<AnnotationEditorDialog *>(widget())) {
|
||||
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_ANNOTATION_ADDED);
|
||||
QString customId = m_dialog->customId();
|
||||
Annotation annotation = m_dialog->annotation();
|
||||
const QString customId = dialog->customId();
|
||||
const Annotation annotation = dialog->annotation();
|
||||
auto &node = this->m_modelNode;
|
||||
|
||||
m_modelNode.setCustomId(customId);
|
||||
node.setCustomId(customId);
|
||||
|
||||
if (annotation.comments().isEmpty())
|
||||
m_modelNode.removeAnnotation();
|
||||
node.removeAnnotation();
|
||||
else
|
||||
m_modelNode.setAnnotation(annotation);
|
||||
node.setAnnotation(annotation);
|
||||
}
|
||||
|
||||
hideWidget();
|
||||
|
||||
emit accepted();
|
||||
|
||||
emit customIdChanged();
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
@@ -25,65 +25,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQml>
|
||||
#include <QPointer>
|
||||
|
||||
#include "annotationeditordialog.h"
|
||||
#include "annotation.h"
|
||||
|
||||
#include "modelnode.h"
|
||||
#include "editorproxy.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class AnnotationEditor : public QObject
|
||||
class AnnotationEditor : public ModelNodeEditorProxy
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged)
|
||||
Q_PROPERTY(bool hasCustomId READ hasCustomId NOTIFY customIdChanged)
|
||||
Q_PROPERTY(bool hasAnnotation READ hasAnnotation NOTIFY annotationChanged)
|
||||
|
||||
public:
|
||||
explicit AnnotationEditor(QObject *parent = nullptr);
|
||||
~AnnotationEditor();
|
||||
|
||||
static void registerDeclarativeType();
|
||||
|
||||
Q_INVOKABLE void showWidget();
|
||||
Q_INVOKABLE void showWidget(int x, int y);
|
||||
Q_INVOKABLE void hideWidget();
|
||||
|
||||
static AnnotationEditor* showWidget(const ModelNode &modelNode);
|
||||
|
||||
void setModelNode(const ModelNode &modelNode);
|
||||
ModelNode modelNode() const;
|
||||
|
||||
void setModelNodeBackend(const QVariant &modelNodeBackend);
|
||||
QVariant modelNodeBackend() const;
|
||||
|
||||
Q_INVOKABLE bool hasCustomId() const;
|
||||
Q_INVOKABLE bool hasAnnotation() const;
|
||||
|
||||
QWidget *createWidget() override;
|
||||
Q_INVOKABLE void removeFullAnnotation();
|
||||
|
||||
static void registerDeclarativeType();
|
||||
|
||||
signals:
|
||||
void accepted();
|
||||
void canceled();
|
||||
void modelNodeBackendChanged();
|
||||
|
||||
void customIdChanged();
|
||||
void annotationChanged();
|
||||
|
||||
private slots:
|
||||
void acceptedClicked();
|
||||
void cancelClicked();
|
||||
|
||||
private:
|
||||
QPointer<AnnotationEditorDialog> m_dialog;
|
||||
|
||||
ModelNode m_modelNode;
|
||||
QVariant m_modelNodeBackend;
|
||||
};
|
||||
|
||||
} //namespace QmlDesigner
|
||||
|
||||
@@ -3,13 +3,23 @@ HEADERS += $$PWD/annotationeditordialog.h
|
||||
HEADERS += $$PWD/annotationeditor.h
|
||||
HEADERS += $$PWD/globalannotationeditor.h
|
||||
HEADERS += $$PWD/globalannotationeditordialog.h
|
||||
HEADERS += $$PWD/defaultannotations.h
|
||||
HEADERS += $$PWD/annotationtableview.h
|
||||
HEADERS += $$PWD/annotationtabwidget.h
|
||||
|
||||
SOURCES += $$PWD/annotationcommenttab.cpp
|
||||
SOURCES += $$PWD/annotationeditordialog.cpp
|
||||
SOURCES += $$PWD/annotationeditor.cpp
|
||||
SOURCES += $$PWD/globalannotationeditor.cpp
|
||||
SOURCES += $$PWD/globalannotationeditordialog.cpp
|
||||
SOURCES += $$PWD/defaultannotations.cpp
|
||||
SOURCES += $$PWD/annotationtableview.cpp
|
||||
SOURCES += $$PWD/annotationtabwidget.cpp
|
||||
|
||||
FORMS += $$PWD/annotationcommenttab.ui
|
||||
FORMS += $$PWD/annotationeditordialog.ui
|
||||
FORMS += $$PWD/globalannotationeditordialog.ui
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
RESOURCES += $$PWD/annotationeditor.qrc
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/annotationeditor">
|
||||
<file>defaultannotations.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -24,80 +24,65 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "annotationeditordialog.h"
|
||||
#include "ui_annotationeditordialog.h"
|
||||
#include "annotation.h"
|
||||
#include "annotationcommenttab.h"
|
||||
#include "defaultannotations.h"
|
||||
|
||||
#include "ui_annotationcommenttab.h"
|
||||
#include "ui_annotationeditordialog.h"
|
||||
|
||||
#include <timelineicons.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation)
|
||||
BasicAnnotationEditorDialog::BasicAnnotationEditorDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::AnnotationEditorDialog)
|
||||
, m_customId(customId)
|
||||
, m_annotation(annotation)
|
||||
, m_defaults(std::make_unique<DefaultAnnotationsModel>())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowFlag(Qt::Tool, true);
|
||||
setModal(true);
|
||||
loadDefaultAnnotations(DefaultAnnotationsModel::defaultJsonFilePath());
|
||||
|
||||
connect(this, &QDialog::accepted, this, &AnnotationEditorDialog::acceptedClicked);
|
||||
connect(this, &QDialog::accepted, this, &BasicAnnotationEditorDialog::acceptedClicked);
|
||||
}
|
||||
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AnnotationEditorDialog::tabChanged);
|
||||
BasicAnnotationEditorDialog::~BasicAnnotationEditorDialog() {}
|
||||
|
||||
auto *commentCornerWidget = new QToolBar;
|
||||
Annotation const &BasicAnnotationEditorDialog::annotation() const
|
||||
{
|
||||
return m_annotation;
|
||||
}
|
||||
|
||||
auto *commentAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(), tr("Add Comment")); //timeline icons?
|
||||
auto *commentRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(),
|
||||
tr("Remove Comment")); //timeline icons?
|
||||
void BasicAnnotationEditorDialog::setAnnotation(const Annotation &annotation)
|
||||
{
|
||||
m_annotation = annotation;
|
||||
fillFields();
|
||||
}
|
||||
|
||||
connect(commentAddAction, &QAction::triggered, this, [this]() {
|
||||
addComment(Comment());
|
||||
});
|
||||
void BasicAnnotationEditorDialog::loadDefaultAnnotations(QString const &filename)
|
||||
{
|
||||
m_defaults->loadFromFile(filename);
|
||||
}
|
||||
|
||||
connect(commentRemoveAction, &QAction::triggered, this, [this]() {
|
||||
DefaultAnnotationsModel *BasicAnnotationEditorDialog::defaultAnnotations() const
|
||||
{
|
||||
return m_defaults.get();
|
||||
}
|
||||
|
||||
if (ui->tabWidget->count() == 0) { //it is not even supposed to happen but lets be sure
|
||||
QTC_ASSERT(true, return);
|
||||
return;
|
||||
}
|
||||
|
||||
int currentIndex = ui->tabWidget->currentIndex();
|
||||
QString currentTitle = ui->tabWidget->tabText(currentIndex);
|
||||
|
||||
QMessageBox *deleteDialog = new QMessageBox(this);
|
||||
deleteDialog->setWindowTitle(currentTitle);
|
||||
deleteDialog->setText(tr("Delete this comment?"));
|
||||
deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
deleteDialog->setDefaultButton(QMessageBox::Yes);
|
||||
|
||||
int result = deleteDialog->exec();
|
||||
|
||||
if (result == QMessageBox::Yes) {
|
||||
removeComment(currentIndex);
|
||||
}
|
||||
|
||||
if (ui->tabWidget->count() == 0) //lets be sure that tabWidget is never empty
|
||||
addComment(Comment());
|
||||
});
|
||||
|
||||
commentCornerWidget->addAction(commentAddAction);
|
||||
commentCornerWidget->addAction(commentRemoveAction);
|
||||
|
||||
ui->tabWidget->setCornerWidget(commentCornerWidget, Qt::TopRightCorner);
|
||||
AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent,
|
||||
const QString &targetId,
|
||||
const QString &customId)
|
||||
: BasicAnnotationEditorDialog(parent)
|
||||
, ui(new Ui::AnnotationEditorDialog)
|
||||
, m_customId(customId)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->targetIdEdit->setText(targetId);
|
||||
|
||||
fillFields();
|
||||
setWindowTitle(annotationEditorTitle);
|
||||
}
|
||||
|
||||
@@ -106,17 +91,6 @@ AnnotationEditorDialog::~AnnotationEditorDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::setAnnotation(const Annotation &annotation)
|
||||
{
|
||||
m_annotation = annotation;
|
||||
fillFields();
|
||||
}
|
||||
|
||||
Annotation AnnotationEditorDialog::annotation() const
|
||||
{
|
||||
return m_annotation;
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::setCustomId(const QString &customId)
|
||||
{
|
||||
m_customId = customId;
|
||||
@@ -130,117 +104,34 @@ QString AnnotationEditorDialog::customId() const
|
||||
|
||||
void AnnotationEditorDialog::acceptedClicked()
|
||||
{
|
||||
m_customId = ui->customIdEdit->text();
|
||||
|
||||
Annotation annotation;
|
||||
|
||||
annotation.removeComments();
|
||||
|
||||
for (int i = 0; i < ui->tabWidget->count(); i++) {
|
||||
AnnotationCommentTab* tab = reinterpret_cast<AnnotationCommentTab*>(ui->tabWidget->widget(i));
|
||||
if (!tab)
|
||||
continue;
|
||||
|
||||
Comment comment = tab->currentComment();
|
||||
|
||||
if (!comment.isEmpty())
|
||||
annotation.addComment(comment);
|
||||
}
|
||||
|
||||
m_annotation = annotation;
|
||||
|
||||
updateAnnotation();
|
||||
emit AnnotationEditorDialog::acceptedDialog();
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::commentTitleChanged(const QString &text, QWidget *tab)
|
||||
{
|
||||
int tabIndex = ui->tabWidget->indexOf(tab);
|
||||
if (tabIndex >= 0)
|
||||
ui->tabWidget->setTabText(tabIndex, text);
|
||||
|
||||
if (text.isEmpty())
|
||||
ui->tabWidget->setTabText(tabIndex,
|
||||
(defaultTabName + " " + QString::number(tabIndex+1)));
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::fillFields()
|
||||
{
|
||||
ui->customIdEdit->setText(m_customId);
|
||||
setupComments();
|
||||
ui->tabWidget->setupComments(m_annotation.comments());
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::setupComments()
|
||||
void AnnotationEditorDialog::updateAnnotation()
|
||||
{
|
||||
ui->tabWidget->setUpdatesEnabled(false);
|
||||
|
||||
deleteAllTabs();
|
||||
|
||||
const QVector<Comment> comments = m_annotation.comments();
|
||||
|
||||
if (comments.isEmpty())
|
||||
addComment(Comment());
|
||||
|
||||
for (const Comment &comment : comments) {
|
||||
addCommentTab(comment);
|
||||
}
|
||||
|
||||
ui->tabWidget->setUpdatesEnabled(true);
|
||||
m_customId = ui->customIdEdit->text();
|
||||
Annotation annotation;
|
||||
annotation.setComments(ui->tabWidget->fetchComments());
|
||||
m_annotation = annotation;
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::addComment(const Comment &comment)
|
||||
{
|
||||
m_annotation.addComment(comment);
|
||||
addCommentTab(comment);
|
||||
ui->tabWidget->addCommentTab(comment);
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::removeComment(int index)
|
||||
{
|
||||
if ((m_annotation.commentsSize() > index) && (index >= 0)) {
|
||||
m_annotation.removeComment(index);
|
||||
removeCommentTab(index);
|
||||
}
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::addCommentTab(const Comment &comment)
|
||||
{
|
||||
auto commentTab = new AnnotationCommentTab();
|
||||
commentTab->setComment(comment);
|
||||
|
||||
QString tabTitle(comment.title());
|
||||
int tabIndex = ui->tabWidget->addTab(commentTab, tabTitle);
|
||||
ui->tabWidget->setCurrentIndex(tabIndex);
|
||||
|
||||
if (tabTitle.isEmpty()) {
|
||||
const QString appendix = ((tabIndex > 0) ? QString::number(tabIndex+1) : "");
|
||||
|
||||
tabTitle = QString("%1 %2").arg(defaultTabName).arg(appendix);
|
||||
|
||||
ui->tabWidget->setTabText(tabIndex, tabTitle);
|
||||
}
|
||||
|
||||
connect(commentTab, &AnnotationCommentTab::titleChanged,
|
||||
this, &AnnotationEditorDialog::commentTitleChanged);
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::removeCommentTab(int index)
|
||||
{
|
||||
if ((ui->tabWidget->count() > index) && (index >= 0)) {
|
||||
ui->tabWidget->removeTab(index);
|
||||
}
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::deleteAllTabs()
|
||||
{
|
||||
while (ui->tabWidget->count() > 0) {
|
||||
QWidget *w = ui->tabWidget->widget(0);
|
||||
ui->tabWidget->removeTab(0);
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
void AnnotationEditorDialog::tabChanged(int index)
|
||||
{
|
||||
(void) index;
|
||||
m_annotation.removeComment(index);
|
||||
ui->tabWidget->removeTab(index);
|
||||
}
|
||||
|
||||
} //namespace QmlDesigner
|
||||
|
||||
@@ -34,46 +34,62 @@ namespace QmlDesigner {
|
||||
namespace Ui {
|
||||
class AnnotationEditorDialog;
|
||||
}
|
||||
class DefaultAnnotationsModel;
|
||||
|
||||
class AnnotationEditorDialog : public QDialog
|
||||
class BasicAnnotationEditorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation);
|
||||
~AnnotationEditorDialog();
|
||||
explicit BasicAnnotationEditorDialog(QWidget *parent);
|
||||
~BasicAnnotationEditorDialog();
|
||||
|
||||
Annotation const &annotation() const;
|
||||
void setAnnotation(const Annotation &annotation);
|
||||
Annotation annotation() const;
|
||||
|
||||
void setCustomId(const QString &customId);
|
||||
QString customId() const;
|
||||
void loadDefaultAnnotations(QString const &filename);
|
||||
|
||||
DefaultAnnotationsModel *defaultAnnotations() const;
|
||||
|
||||
signals:
|
||||
void acceptedDialog(); //use instead of QDialog::accepted
|
||||
|
||||
protected:
|
||||
virtual void fillFields() = 0;
|
||||
virtual void acceptedClicked() = 0;
|
||||
|
||||
Annotation m_annotation;
|
||||
std::unique_ptr<DefaultAnnotationsModel> m_defaults;
|
||||
};
|
||||
|
||||
class AnnotationEditorDialog : public BasicAnnotationEditorDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnnotationEditorDialog(QWidget *parent,
|
||||
const QString &targetId,
|
||||
const QString &customId);
|
||||
~AnnotationEditorDialog();
|
||||
|
||||
void setCustomId(const QString &customId);
|
||||
QString customId() const;
|
||||
|
||||
private slots:
|
||||
void acceptedClicked();
|
||||
void tabChanged(int index);
|
||||
void commentTitleChanged(const QString &text, QWidget *tab);
|
||||
void acceptedClicked() override;
|
||||
|
||||
private:
|
||||
void fillFields();
|
||||
void setupComments();
|
||||
void fillFields() override;
|
||||
void updateAnnotation();
|
||||
|
||||
void addComment(const Comment &comment);
|
||||
void removeComment(int index);
|
||||
|
||||
void addCommentTab(const Comment &comment);
|
||||
void removeCommentTab(int index);
|
||||
void deleteAllTabs();
|
||||
|
||||
private:
|
||||
const QString annotationEditorTitle = {tr("Annotation Editor")};
|
||||
const QString defaultTabName = {tr("Annotation")};
|
||||
|
||||
Ui::AnnotationEditorDialog *ui;
|
||||
|
||||
QString m_customId;
|
||||
Annotation m_annotation;
|
||||
};
|
||||
|
||||
} //namespace QmlDesigner
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<widget class="AnnotationTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -90,6 +90,14 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>AnnotationTabWidget</class>
|
||||
<extends>QTabWidget</extends>
|
||||
<header>annotationtabwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>targetIdEdit</tabstop>
|
||||
<tabstop>customIdEdit</tabstop>
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "annotationtableview.h"
|
||||
|
||||
#include "defaultannotations.h"
|
||||
|
||||
#include <utils/qtcolorbutton.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCompleter>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QHeaderView>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QStandardItem>
|
||||
#include <QStandardItemModel>
|
||||
#include <QStringListModel>
|
||||
#include <QStyle>
|
||||
#include <QTextEdit>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
struct ColumnId
|
||||
{
|
||||
enum Column {
|
||||
Title = 0,
|
||||
Author = 1,
|
||||
Value = 2,
|
||||
};
|
||||
};
|
||||
|
||||
CommentDelegate::CommentDelegate(QObject *parent)
|
||||
: QItemDelegate(parent)
|
||||
, m_completer(std::make_unique<QCompleter>())
|
||||
{}
|
||||
|
||||
CommentDelegate::~CommentDelegate() {}
|
||||
|
||||
DefaultAnnotationsModel *CommentDelegate::defaultAnnotations() const
|
||||
{
|
||||
return m_defaults;
|
||||
}
|
||||
|
||||
void CommentDelegate::setDefaultAnnotations(DefaultAnnotationsModel *defaults)
|
||||
{
|
||||
m_defaults = defaults;
|
||||
m_completer->setModel(m_defaults);
|
||||
}
|
||||
|
||||
QCompleter *CommentDelegate::completer() const
|
||||
{
|
||||
return m_completer.get();
|
||||
}
|
||||
|
||||
void CommentDelegate::updateEditorGeometry(QWidget *editor,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
Comment CommentDelegate::comment(QModelIndex const &index)
|
||||
{
|
||||
auto *model = index.model();
|
||||
return model->data(model->index(index.row(), ColumnId::Title), CommentRole).value<Comment>();
|
||||
}
|
||||
|
||||
CommentTitleDelegate::CommentTitleDelegate(QObject *parent)
|
||||
: CommentDelegate(parent)
|
||||
{}
|
||||
|
||||
CommentTitleDelegate::~CommentTitleDelegate() {}
|
||||
|
||||
QWidget *CommentTitleDelegate::createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
auto *editor = new QComboBox(parent);
|
||||
editor->setEditable(true);
|
||||
editor->setCompleter(completer());
|
||||
editor->setFrame(false);
|
||||
editor->setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
void CommentTitleDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
QString text = index.model()->data(index, Qt::DisplayRole).toString();
|
||||
auto *comboBox = qobject_cast<QComboBox *>(editor);
|
||||
comboBox->setModel(defaultAnnotations());
|
||||
comboBox->setCurrentText(text);
|
||||
}
|
||||
|
||||
void CommentTitleDelegate::setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
auto *comboBox = qobject_cast<QComboBox *>(editor);
|
||||
auto oldText = model->data(index, Qt::EditRole).toString();
|
||||
auto newText = comboBox->currentText();
|
||||
|
||||
if (oldText != newText) {
|
||||
model->setData(index, comboBox->currentText(), Qt::EditRole);
|
||||
auto comment = model->data(index, CommentRole).value<Comment>();
|
||||
comment.setTitle(newText);
|
||||
model->setData(index, QVariant::fromValue(comment), CommentRole);
|
||||
|
||||
// Set default value to data item
|
||||
auto colIdx = model->index(index.row(), ColumnId::Value);
|
||||
if (defaultAnnotations()->hasDefault(comment))
|
||||
model->setData(colIdx, defaultAnnotations()->defaultValue(comment), Qt::DisplayRole);
|
||||
else
|
||||
// Reset to rich text when there is no default item
|
||||
model->setData(colIdx,
|
||||
QVariant::fromValue<RichTextProxy>({comment.text()}),
|
||||
Qt::DisplayRole);
|
||||
}
|
||||
}
|
||||
|
||||
CommentValueDelegate::CommentValueDelegate(QObject *parent)
|
||||
: CommentDelegate(parent)
|
||||
{}
|
||||
|
||||
CommentValueDelegate::~CommentValueDelegate() {}
|
||||
|
||||
void CommentValueDelegate::paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
auto data = index.model()->data(index, Qt::DisplayRole);
|
||||
if (data.userType() == qMetaTypeId<RichTextProxy>())
|
||||
drawDisplay(painter, option, option.rect, data.value<RichTextProxy>().plainText());
|
||||
else if (data.userType() == QMetaType::QColor)
|
||||
painter->fillRect(option.rect, data.value<QColor>());
|
||||
else
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
|
||||
void CommentValueDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
auto data = index.model()->data(index, Qt::DisplayRole);
|
||||
if (data.userType() == qMetaTypeId<RichTextProxy>()) {
|
||||
auto richText = data.value<RichTextProxy>();
|
||||
auto *e = qobject_cast<RichTextCellEditor *>(editor);
|
||||
e->setText(richText.plainText());
|
||||
e->setupSignal(index.row(), comment(index).title());
|
||||
connect(e,
|
||||
&RichTextCellEditor::richTextClicked,
|
||||
this,
|
||||
&CommentValueDelegate::richTextEditorRequested,
|
||||
Qt::UniqueConnection);
|
||||
} else if (data.userType() == QMetaType::QString) {
|
||||
auto *e = qobject_cast<QLineEdit *>(editor);
|
||||
e->setText(data.toString());
|
||||
} else if (data.userType() == QMetaType::QColor) {
|
||||
auto *e = qobject_cast<Utils::QtColorButton *>(editor);
|
||||
e->setColor(data.value<QColor>());
|
||||
} else
|
||||
QItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
|
||||
void CommentValueDelegate::setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
auto data = model->data(index, Qt::EditRole);
|
||||
if (data.userType() == qMetaTypeId<RichTextProxy>())
|
||||
return;
|
||||
else if (data.userType() == QMetaType::QColor)
|
||||
model->setData(index,
|
||||
qobject_cast<Utils::QtColorButton *>(editor)->color(),
|
||||
Qt::DisplayRole);
|
||||
else if (data.userType() == QMetaType::QString)
|
||||
model->setData(index, qobject_cast<QLineEdit *>(editor)->text(), Qt::DisplayRole);
|
||||
else
|
||||
QItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
|
||||
RichTextCellEditor::RichTextCellEditor(QWidget *parent)
|
||||
: QLabel(parent)
|
||||
{}
|
||||
|
||||
RichTextCellEditor::~RichTextCellEditor() {}
|
||||
|
||||
RichTextProxy RichTextCellEditor::richText() const
|
||||
{
|
||||
return m_richText;
|
||||
}
|
||||
|
||||
void RichTextCellEditor::setRichText(const RichTextProxy &richText)
|
||||
{
|
||||
if (richText.text == m_richText.text)
|
||||
return;
|
||||
|
||||
m_richText = richText;
|
||||
setText(richText.plainText());
|
||||
|
||||
emit richTextChanged();
|
||||
}
|
||||
|
||||
void RichTextCellEditor::setupSignal(int index, const QString &commentTitle)
|
||||
{
|
||||
if (m_connection)
|
||||
disconnect(m_connection);
|
||||
|
||||
m_connection = connect(this, &RichTextCellEditor::clicked, this, [=]() {
|
||||
emit richTextClicked(index, commentTitle);
|
||||
});
|
||||
}
|
||||
|
||||
void RichTextCellEditor::mouseReleaseEvent(QMouseEvent *)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
AnnotationTableView::AnnotationTableView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
, m_model(std::make_unique<QStandardItemModel>())
|
||||
, m_editorFactory(std::make_unique<QItemEditorFactory>())
|
||||
{
|
||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
setSelectionMode(QAbstractItemView::ContiguousSelection);
|
||||
|
||||
setModel(m_model.get());
|
||||
connect(m_model.get(), &QStandardItemModel::itemChanged, this, [this](QStandardItem *item) {
|
||||
if (item->isCheckable())
|
||||
m_model->setData(item->index(), item->checkState() == Qt::Checked);
|
||||
|
||||
if (this->m_modelUpdating)
|
||||
return;
|
||||
|
||||
auto *valueItem = m_model->item(item->row(), ColumnId::Value);
|
||||
|
||||
// When comment title was edited, make value item editable
|
||||
if (item->column() == ColumnId::Title && valueItem) {
|
||||
valueItem->setEditable(!item->text().isEmpty());
|
||||
valueItem->setCheckable(valueItem->data(Qt::DisplayRole).userType() == QMetaType::Bool);
|
||||
}
|
||||
|
||||
m_modelUpdating = true;
|
||||
if (!rowIsEmpty(m_model->rowCount() - 1))
|
||||
addEmptyRow();
|
||||
m_modelUpdating = false;
|
||||
});
|
||||
|
||||
horizontalHeader()->setStretchLastSection(true);
|
||||
horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
|
||||
m_editorFactory->registerEditor(qMetaTypeId<RichTextProxy>(),
|
||||
new QItemEditorCreator<RichTextCellEditor>("richText"));
|
||||
m_editorFactory->registerEditor(QMetaType::QColor,
|
||||
new QItemEditorCreator<Utils::QtColorButton>("color"));
|
||||
|
||||
m_valueDelegate.setItemEditorFactory(m_editorFactory.get());
|
||||
connect(&m_valueDelegate,
|
||||
&CommentValueDelegate::richTextEditorRequested,
|
||||
this,
|
||||
&AnnotationTableView::richTextEditorRequested);
|
||||
|
||||
verticalHeader()->hide();
|
||||
}
|
||||
|
||||
AnnotationTableView::~AnnotationTableView() {}
|
||||
|
||||
QVector<Comment> AnnotationTableView::fetchComments() const
|
||||
{
|
||||
QVector<Comment> comments;
|
||||
|
||||
for (int i = 0; i < m_model->rowCount(); ++i) {
|
||||
Comment comment = fetchComment(i);
|
||||
if (!comment.isEmpty())
|
||||
comments.push_back(comment);
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
Comment AnnotationTableView::fetchComment(int row) const
|
||||
{
|
||||
auto *item = m_model->item(row, ColumnId::Title);
|
||||
if (item->text().isEmpty())
|
||||
return {};
|
||||
|
||||
Comment comment = item->data().value<Comment>();
|
||||
comment.setTitle(item->text());
|
||||
comment.setAuthor(m_model->item(row, ColumnId::Author)->text());
|
||||
comment.setText(dataToCommentText(m_model->item(row, ColumnId::Value)->data(Qt::DisplayRole)));
|
||||
return comment;
|
||||
}
|
||||
|
||||
void AnnotationTableView::setupComments(QVector<Comment> const &comments)
|
||||
{
|
||||
m_model->clear();
|
||||
m_modelUpdating = true;
|
||||
m_model->setColumnCount(3);
|
||||
m_model->setHeaderData(ColumnId::Title, Qt::Horizontal, tr("Title"));
|
||||
m_model->setHeaderData(ColumnId::Author, Qt::Horizontal, tr("Author"));
|
||||
m_model->setHeaderData(ColumnId::Value, Qt::Horizontal, tr("Value"));
|
||||
setItemDelegateForColumn(ColumnId::Title, &m_titleDelegate);
|
||||
setItemDelegateForColumn(ColumnId::Value, &m_valueDelegate);
|
||||
|
||||
for (auto &comment : comments) {
|
||||
if (comment.isEmpty())
|
||||
continue;
|
||||
|
||||
addEmptyRow();
|
||||
changeRow(m_model->rowCount() - 1, comment);
|
||||
}
|
||||
|
||||
addEmptyRow();
|
||||
m_modelUpdating = false;
|
||||
}
|
||||
|
||||
DefaultAnnotationsModel *AnnotationTableView::defaultAnnotations() const
|
||||
{
|
||||
return m_defaults;
|
||||
}
|
||||
|
||||
void AnnotationTableView::setDefaultAnnotations(DefaultAnnotationsModel *defaults)
|
||||
{
|
||||
m_defaults = defaults;
|
||||
m_titleDelegate.setDefaultAnnotations(defaults);
|
||||
m_valueDelegate.setDefaultAnnotations(defaults);
|
||||
}
|
||||
|
||||
void AnnotationTableView::changeRow(int index, Comment const &comment)
|
||||
{
|
||||
auto *titleItem = m_model->item(index, ColumnId::Title);
|
||||
auto *authorItem = m_model->item(index, ColumnId::Author);
|
||||
auto *textItem = m_model->item(index, ColumnId::Value);
|
||||
|
||||
titleItem->setText(comment.title());
|
||||
titleItem->setData(QVariant::fromValue<Comment>(comment));
|
||||
|
||||
authorItem->setText(comment.author());
|
||||
|
||||
QVariant data = commentToData(comment,
|
||||
m_defaults ? m_defaults->defaultType(comment)
|
||||
: QMetaType::UnknownType);
|
||||
|
||||
textItem->setEditable(data.isValid());
|
||||
textItem->setCheckable(data.userType() == QMetaType::Bool);
|
||||
textItem->setData(data, Qt::DisplayRole);
|
||||
}
|
||||
|
||||
void AnnotationTableView::removeRow(int index)
|
||||
{
|
||||
m_model->removeRow(index);
|
||||
}
|
||||
|
||||
void AnnotationTableView::removeSelectedRows()
|
||||
{
|
||||
const auto selRows = selectionModel()->selectedRows();
|
||||
for (auto it = selRows.rbegin(); it != selRows.rend(); ++it)
|
||||
removeRow(it->row());
|
||||
}
|
||||
|
||||
void AnnotationTableView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Delete)
|
||||
removeSelectedRows();
|
||||
}
|
||||
|
||||
void AnnotationTableView::addEmptyRow()
|
||||
{
|
||||
auto *valueItem = new QStandardItem;
|
||||
valueItem->setEditable(false);
|
||||
m_model->appendRow({new QStandardItem, new QStandardItem, valueItem});
|
||||
}
|
||||
|
||||
bool AnnotationTableView::rowIsEmpty(int row) const
|
||||
{
|
||||
auto itemText = [&](int col) {
|
||||
return m_model->item(row, col) ? m_model->item(row, col)->text() : QString();
|
||||
};
|
||||
|
||||
return QString(itemText(0) + itemText(1) + itemText(2)).isEmpty();
|
||||
}
|
||||
|
||||
QString AnnotationTableView::dataToCommentText(QVariant const &data)
|
||||
{
|
||||
auto type = data.userType();
|
||||
if (type == qMetaTypeId<RichTextProxy>())
|
||||
return data.value<RichTextProxy>().text;
|
||||
|
||||
switch (type) {
|
||||
case QMetaType::QColor:
|
||||
return data.value<QColor>().name();
|
||||
case QMetaType::Bool:
|
||||
return data.toBool() ? QStringLiteral("true") : QStringLiteral("false");
|
||||
case QMetaType::QString:
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant AnnotationTableView::commentToData(Comment const& comment, QMetaType::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case QMetaType::Bool:
|
||||
return QVariant::fromValue(comment.deescapedText().toLower().trimmed() == "true");
|
||||
case QMetaType::QColor:
|
||||
return QVariant::fromValue(QColor(comment.deescapedText().toLower().trimmed()));
|
||||
break;
|
||||
case QMetaType::QString:
|
||||
return QVariant::fromValue(comment.text());
|
||||
break;
|
||||
default:
|
||||
if (type == qMetaTypeId<RichTextProxy>() || type == QMetaType::UnknownType)
|
||||
return QVariant::fromValue<RichTextProxy>({comment.text()});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
@@ -0,0 +1,170 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <QItemDelegate>
|
||||
#include <QLabel>
|
||||
#include <QPointer>
|
||||
#include <QTableView>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "annotation.h"
|
||||
#include "defaultannotations.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStandardItemModel;
|
||||
class QCompleter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
class CommentDelegate : public QItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Role { CommentRole = Qt::UserRole + 1 };
|
||||
|
||||
CommentDelegate(QObject *parent = nullptr);
|
||||
~CommentDelegate() override;
|
||||
|
||||
DefaultAnnotationsModel *defaultAnnotations() const;
|
||||
void setDefaultAnnotations(DefaultAnnotationsModel *);
|
||||
|
||||
QCompleter *completer() const;
|
||||
|
||||
void updateEditorGeometry(QWidget *editor,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
static Comment comment(QModelIndex const &);
|
||||
|
||||
private:
|
||||
std::unique_ptr<QCompleter> m_completer;
|
||||
QPointer<DefaultAnnotationsModel> m_defaults;
|
||||
};
|
||||
|
||||
class CommentTitleDelegate : public CommentDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CommentTitleDelegate(QObject *parent = nullptr);
|
||||
~CommentTitleDelegate() override;
|
||||
|
||||
QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model,
|
||||
const QModelIndex &index) const override;
|
||||
signals:
|
||||
void commentChanged(int row, Comment const &);
|
||||
};
|
||||
|
||||
class CommentValueDelegate : public CommentDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CommentValueDelegate(QObject *parent = nullptr);
|
||||
~CommentValueDelegate();
|
||||
|
||||
void paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor,
|
||||
QAbstractItemModel *model,
|
||||
const QModelIndex &index) const override;
|
||||
signals:
|
||||
void richTextEditorRequested(int index, QString const &richText);
|
||||
};
|
||||
|
||||
class RichTextCellEditor : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QmlDesigner::RichTextProxy richText READ richText WRITE setRichText NOTIFY
|
||||
richTextChanged USER true)
|
||||
public:
|
||||
RichTextCellEditor(QWidget *parent = nullptr);
|
||||
~RichTextCellEditor() override;
|
||||
|
||||
RichTextProxy richText() const;
|
||||
void setRichText(RichTextProxy const &);
|
||||
|
||||
void setupSignal(int row, QString const &commentTitle);
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
void richTextChanged();
|
||||
void richTextClicked(int index, QString const &text);
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
|
||||
private:
|
||||
RichTextProxy m_richText;
|
||||
QMetaObject::Connection m_connection;
|
||||
};
|
||||
|
||||
class AnnotationTableView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AnnotationTableView(QWidget *parent = nullptr);
|
||||
~AnnotationTableView();
|
||||
|
||||
QVector<Comment> fetchComments() const;
|
||||
Comment fetchComment(int row) const;
|
||||
void setupComments(QVector<Comment> const &comments);
|
||||
|
||||
DefaultAnnotationsModel *defaultAnnotations() const;
|
||||
void setDefaultAnnotations(DefaultAnnotationsModel *);
|
||||
|
||||
void changeRow(int index, Comment const &comment);
|
||||
void removeRow(int index);
|
||||
void removeSelectedRows();
|
||||
|
||||
signals:
|
||||
void richTextEditorRequested(int index, QString const &commentTitle);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *) override;
|
||||
|
||||
private:
|
||||
void addEmptyRow();
|
||||
bool rowIsEmpty(int row) const;
|
||||
static QString dataToCommentText(QVariant const &);
|
||||
static QVariant commentToData(Comment const&, QMetaType::Type type);
|
||||
|
||||
CommentTitleDelegate m_titleDelegate;
|
||||
CommentValueDelegate m_valueDelegate;
|
||||
|
||||
bool m_modelUpdating = false;
|
||||
std::unique_ptr<QStandardItemModel> m_model;
|
||||
std::unique_ptr<QItemEditorFactory> m_editorFactory;
|
||||
QPointer<DefaultAnnotationsModel> m_defaults;
|
||||
};
|
||||
} // namespace QmlDesigner
|
||||
@@ -0,0 +1,157 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "annotationtabwidget.h"
|
||||
|
||||
#include "annotationcommenttab.h"
|
||||
|
||||
#include <timelineeditor/timelineicons.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QToolBar>
|
||||
|
||||
namespace QmlDesigner {
|
||||
AnnotationTabWidget::AnnotationTabWidget(QWidget *parent)
|
||||
: QTabWidget(parent)
|
||||
{
|
||||
auto *commentCornerWidget = new QToolBar;
|
||||
|
||||
//Making it look similar to timeline editor button:
|
||||
commentCornerWidget->setStyleSheet("QToolBar { background-color: transparent; border-width: 1px; }");
|
||||
|
||||
auto *commentAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(),
|
||||
tr("Add Comment")); //timeline icons?
|
||||
auto *commentRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(),
|
||||
tr("Remove Comment")); //timeline icons?
|
||||
connect(commentAddAction, &QAction::triggered, this, [this]() { addCommentTab(); });
|
||||
|
||||
connect(commentRemoveAction, &QAction::triggered, this, [this]() {
|
||||
int currentIndex = this->currentIndex();
|
||||
QString currentTitle = tabText(currentIndex);
|
||||
if (QMessageBox::question(this,
|
||||
currentTitle,
|
||||
tr("Delete this comment?"))
|
||||
== QMessageBox::Yes) {
|
||||
removeTab(currentIndex);
|
||||
if (count() == 0) //lets be sure that tabWidget is never empty
|
||||
addCommentTab();
|
||||
}
|
||||
});
|
||||
|
||||
commentCornerWidget->addAction(commentAddAction);
|
||||
commentCornerWidget->addAction(commentRemoveAction);
|
||||
setCornerWidget(commentCornerWidget, Qt::TopRightCorner);
|
||||
}
|
||||
|
||||
AnnotationTabWidget::~AnnotationTabWidget() {}
|
||||
|
||||
QVector<Comment> AnnotationTabWidget::fetchComments() const
|
||||
{
|
||||
QVector<Comment> comments;
|
||||
for (int i = 0; i < count(); i++) {
|
||||
auto *tab = qobject_cast<AnnotationCommentTab *>(widget(i));
|
||||
if (!tab)
|
||||
continue;
|
||||
|
||||
Comment comment = tab->currentComment();
|
||||
|
||||
if (!comment.isEmpty())
|
||||
comments.push_back(comment);
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
void AnnotationTabWidget::setupComments(QVector<Comment> const &comments)
|
||||
{
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
deleteAllTabs();
|
||||
if (comments.isEmpty())
|
||||
addCommentTab();
|
||||
|
||||
for (const Comment &comment : comments)
|
||||
addCommentTab(comment);
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
DefaultAnnotationsModel *AnnotationTabWidget::defaultAnnotations() const
|
||||
{
|
||||
return m_defaults;
|
||||
}
|
||||
|
||||
void AnnotationTabWidget::setDefaultAnnotations(DefaultAnnotationsModel *defaults)
|
||||
{
|
||||
m_defaults = defaults;
|
||||
for (int i = 0; i < count(); i++) {
|
||||
// The tab widget might be contain regular QTabs initially, hence we need this qobject_cast test
|
||||
if (auto *tab = qobject_cast<AnnotationCommentTab *>(widget(i)))
|
||||
tab->setDefaultAnnotations(defaults);
|
||||
}
|
||||
}
|
||||
|
||||
void AnnotationTabWidget::onCommentTitleChanged(const QString &text, QWidget *tab)
|
||||
{
|
||||
int tabIndex = indexOf(tab);
|
||||
if (tabIndex >= 0)
|
||||
setTabText(tabIndex, text);
|
||||
|
||||
if (text.isEmpty())
|
||||
setTabText(tabIndex, defaultTabName + " " + QString::number(tabIndex + 1));
|
||||
}
|
||||
|
||||
void AnnotationTabWidget::addCommentTab(const Comment &comment)
|
||||
{
|
||||
auto *commentTab = new AnnotationCommentTab();
|
||||
commentTab->setDefaultAnnotations(m_defaults);
|
||||
commentTab->setComment(comment);
|
||||
|
||||
QString tabTitle(comment.title());
|
||||
int tabIndex = addTab(commentTab, tabTitle);
|
||||
setCurrentIndex(tabIndex);
|
||||
|
||||
if (tabTitle.isEmpty()) {
|
||||
const QString appendix = ((tabIndex > 0) ? QString::number(tabIndex + 1) : "");
|
||||
tabTitle = QString("%1 %2").arg(defaultTabName).arg(appendix);
|
||||
setTabText(tabIndex, tabTitle);
|
||||
}
|
||||
connect(commentTab,
|
||||
&AnnotationCommentTab::titleChanged,
|
||||
this,
|
||||
&AnnotationTabWidget::onCommentTitleChanged);
|
||||
}
|
||||
|
||||
void AnnotationTabWidget::deleteAllTabs()
|
||||
{
|
||||
while (count() > 0) {
|
||||
QWidget *w = widget(0);
|
||||
removeTab(0);
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <QPointer>
|
||||
#include <QTabWidget>
|
||||
|
||||
#include "annotation.h"
|
||||
#include "defaultannotations.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
class AnnotationCommentTab;
|
||||
|
||||
class AnnotationTabWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AnnotationTabWidget(QWidget *parent = nullptr);
|
||||
~AnnotationTabWidget();
|
||||
|
||||
QVector<Comment> fetchComments() const;
|
||||
void setupComments(QVector<Comment> const &comments);
|
||||
|
||||
DefaultAnnotationsModel *defaultAnnotations() const;
|
||||
void setDefaultAnnotations(DefaultAnnotationsModel *);
|
||||
|
||||
public slots:
|
||||
void addCommentTab(const Comment &comment = {});
|
||||
void deleteAllTabs();
|
||||
|
||||
private slots:
|
||||
void onCommentTitleChanged(const QString &text, QWidget *tab);
|
||||
|
||||
private:
|
||||
const QString defaultTabName = {tr("Annotation")};
|
||||
|
||||
QPointer<DefaultAnnotationsModel> m_defaults;
|
||||
};
|
||||
} // namespace QmlDesigner
|
||||
@@ -0,0 +1,182 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "defaultannotations.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace QmlDesigner {
|
||||
DefaultAnnotationsModel::DefaultAnnotationsModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
qRegisterMetaType<RichTextProxy>();
|
||||
}
|
||||
|
||||
DefaultAnnotationsModel::~DefaultAnnotationsModel() {}
|
||||
|
||||
int DefaultAnnotationsModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return static_cast<int>(m_defaults.size());
|
||||
}
|
||||
|
||||
QVariant DefaultAnnotationsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const auto row = static_cast<size_t>(index.row());
|
||||
if (!index.isValid() || m_defaults.size() < row)
|
||||
return {};
|
||||
|
||||
auto &item = m_defaults[row];
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Name:
|
||||
return item.first;
|
||||
case Type:
|
||||
return item.second.typeName();
|
||||
case Default:
|
||||
return item.second;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariantMap DefaultAnnotationsModel::fetchData() const
|
||||
{
|
||||
return m_defaultMap;
|
||||
}
|
||||
|
||||
bool DefaultAnnotationsModel::hasDefault(const Comment &comment) const
|
||||
{
|
||||
return m_defaultMap.count(comment.title().toLower());
|
||||
}
|
||||
|
||||
QMetaType::Type DefaultAnnotationsModel::defaultType(const Comment &comment) const
|
||||
{
|
||||
return hasDefault(comment) ? QMetaType::Type(m_defaultMap[comment.title().toLower()].userType())
|
||||
: QMetaType::UnknownType;
|
||||
}
|
||||
|
||||
QVariant DefaultAnnotationsModel::defaultValue(const Comment &comment) const
|
||||
{
|
||||
return hasDefault(comment) ? m_defaultMap.value(comment.title().toLower()) : QVariant();
|
||||
}
|
||||
|
||||
bool DefaultAnnotationsModel::isRichText(const Comment &comment) const
|
||||
{
|
||||
const auto type = defaultType(comment);
|
||||
return type == QMetaType::UnknownType || type == qMetaTypeId<RichTextProxy>();
|
||||
}
|
||||
|
||||
void DefaultAnnotationsModel::loadFromFile(QString const &filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
loadFromFile(&file);
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultAnnotationsModel::loadFromFile(QIODevice *io)
|
||||
{
|
||||
QJsonParseError error;
|
||||
auto doc = QJsonDocument::fromJson(io->readAll(), &error);
|
||||
|
||||
if (error.error == QJsonParseError::NoError)
|
||||
loadFromJson(doc);
|
||||
else {
|
||||
} // TODO: Error handling
|
||||
}
|
||||
|
||||
void DefaultAnnotationsModel::loadFromJson(const QJsonDocument &doc)
|
||||
{
|
||||
beginResetModel();
|
||||
m_defaultMap = asVariantMapFromJson(doc);
|
||||
m_defaults.clear();
|
||||
m_defaults.reserve(m_defaultMap.size());
|
||||
|
||||
for (auto &key : m_defaultMap.keys())
|
||||
m_defaults.emplace_back(key, m_defaultMap.value(key));
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QVariantMap DefaultAnnotationsModel::asVariantMapFromJson(const QJsonDocument &doc)
|
||||
{
|
||||
QVariantMap map;
|
||||
QJsonObject obj = doc.object();
|
||||
for (auto key : obj.keys()) {
|
||||
key = key.toLower();
|
||||
auto val = obj[key];
|
||||
|
||||
switch (val.type()) {
|
||||
case QJsonValue::Double:
|
||||
map[key] = double{0.0};
|
||||
break;
|
||||
case QJsonValue::String:
|
||||
map[key] = QString{};
|
||||
break;
|
||||
case QJsonValue::Bool:
|
||||
map[key] = false;
|
||||
break;
|
||||
case QJsonValue::Object: {
|
||||
auto o = val.toObject();
|
||||
auto type = o["type"].toString().toLower();
|
||||
auto val = o["value"].toVariant();
|
||||
|
||||
if (type == QStringLiteral("richtext"))
|
||||
map[key] = QVariant::fromValue(RichTextProxy{val.toString()});
|
||||
else if (type == QStringLiteral("string"))
|
||||
map[key] = QVariant::fromValue(val.toString());
|
||||
else if (type == QStringLiteral("bool"))
|
||||
map[key] = QVariant::fromValue(val.toBool());
|
||||
else if (type == QStringLiteral("double"))
|
||||
map[key] = QVariant::fromValue(val.toDouble());
|
||||
else if (type == QStringLiteral("color"))
|
||||
map[key] = QVariant::fromValue(QColor(val.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
QString DefaultAnnotationsModel::defaultJsonFilePath()
|
||||
{
|
||||
return QStringLiteral(":/annotationeditor/defaultannotations.json");
|
||||
}
|
||||
|
||||
QString RichTextProxy::plainText() const
|
||||
{
|
||||
QString plainText(text);
|
||||
plainText.remove(QRegularExpression("<.*?>"));
|
||||
return plainText.mid(plainText.indexOf("}") + 1);
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "annotation.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QJsonDocument;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
// We need this proxy type to distinguish between a 'normal' QString
|
||||
// and a 'richtext' string when they are stored in a QVariant
|
||||
struct RichTextProxy
|
||||
{
|
||||
QString text;
|
||||
|
||||
QString plainText() const;
|
||||
};
|
||||
|
||||
class DefaultAnnotationsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Role { Name = Qt::UserRole + 1, Type, Default };
|
||||
Q_ENUM(Role)
|
||||
|
||||
DefaultAnnotationsModel(QObject *parent = nullptr);
|
||||
~DefaultAnnotationsModel() override;
|
||||
|
||||
int rowCount(const QModelIndex & = {}) const override;
|
||||
QVariant data(const QModelIndex &, int role) const override;
|
||||
|
||||
QVariantMap fetchData() const;
|
||||
|
||||
bool hasDefault(const Comment &comment) const;
|
||||
QMetaType::Type defaultType(const Comment &comment) const;
|
||||
QVariant defaultValue(const Comment &comment) const;
|
||||
bool isRichText(const Comment &comment) const;
|
||||
|
||||
void loadFromFile(QString const &);
|
||||
void loadFromFile(QIODevice *);
|
||||
void loadFromJson(const QJsonDocument &);
|
||||
|
||||
static QVariantMap asVariantMapFromJson(const QJsonDocument &);
|
||||
static QString defaultJsonFilePath();
|
||||
|
||||
private:
|
||||
std::vector<std::pair<QString, QVariant>> m_defaults;
|
||||
QVariantMap m_defaultMap;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
Q_DECLARE_METATYPE(QmlDesigner::RichTextProxy);
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"description" : "",
|
||||
"display condition" : "",
|
||||
"helper lines" : true,
|
||||
"position marker" : true,
|
||||
"highlight" : true,
|
||||
"project author" : "",
|
||||
"project confirmed" : true,
|
||||
"project developer" : "",
|
||||
"project distributor" : "",
|
||||
"project modified" : "",
|
||||
"project type" : "",
|
||||
"project version" : "",
|
||||
"screen description" : "",
|
||||
"section" : "",
|
||||
"normalcolor" : {
|
||||
"type": "color",
|
||||
"value": "#000000"
|
||||
},
|
||||
"focuscolor" : {
|
||||
"type": "color",
|
||||
"value": "#000000"
|
||||
},
|
||||
"selectedcolor" : {
|
||||
"type": "color",
|
||||
"value": "#000000"
|
||||
},
|
||||
"pressedcolor" : {
|
||||
"type": "color",
|
||||
"value": "#000000"
|
||||
},
|
||||
"overview" : {
|
||||
"type": "richtext",
|
||||
"value": ""
|
||||
}
|
||||
}
|
||||
@@ -25,124 +25,74 @@
|
||||
|
||||
#include "globalannotationeditor.h"
|
||||
|
||||
#include "globalannotationeditordialog.h"
|
||||
#include "annotation.h"
|
||||
#include "globalannotationeditordialog.h"
|
||||
|
||||
#include "qmlmodelnodeproxy.h"
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
GlobalAnnotationEditor::GlobalAnnotationEditor(QObject *)
|
||||
GlobalAnnotationEditor::GlobalAnnotationEditor(QObject *parent)
|
||||
: ModelNodeEditorProxy(parent)
|
||||
{}
|
||||
|
||||
GlobalAnnotationEditor::~GlobalAnnotationEditor() {}
|
||||
|
||||
QWidget *GlobalAnnotationEditor::createWidget()
|
||||
{
|
||||
}
|
||||
|
||||
GlobalAnnotationEditor::~GlobalAnnotationEditor()
|
||||
{
|
||||
hideWidget();
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditor::showWidget()
|
||||
{
|
||||
m_dialog = new GlobalAnnotationEditorDialog(Core::ICore::dialogParent(),
|
||||
modelNode().globalAnnotation(),
|
||||
modelNode().globalStatus());
|
||||
|
||||
QObject::connect(m_dialog, &GlobalAnnotationEditorDialog::acceptedDialog,
|
||||
this, &GlobalAnnotationEditor::acceptedClicked);
|
||||
QObject::connect(m_dialog, &GlobalAnnotationEditorDialog::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;
|
||||
}
|
||||
auto* dialog = new GlobalAnnotationEditorDialog(Core::ICore::dialogParent(),
|
||||
this->m_modelNode.globalStatus());
|
||||
dialog->setAnnotation(this->m_modelNode.globalAnnotation());
|
||||
QObject::connect(dialog,
|
||||
&GlobalAnnotationEditorDialog::acceptedDialog,
|
||||
this,
|
||||
&GlobalAnnotationEditor::acceptedClicked);
|
||||
QObject::connect(dialog,
|
||||
&GlobalAnnotationEditorDialog::rejected,
|
||||
this,
|
||||
&GlobalAnnotationEditor::cancelClicked);
|
||||
return dialog;
|
||||
};
|
||||
|
||||
void GlobalAnnotationEditor::removeFullAnnotation()
|
||||
{
|
||||
if (!m_modelNode.isValid())
|
||||
auto &node = this->m_modelNode;
|
||||
if (!node.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();
|
||||
if (QMessageBox::question(Core::ICore::dialogParent(),
|
||||
tr("Global Annotation"),
|
||||
tr("Delete this annotation?"))
|
||||
== QMessageBox::Yes) {
|
||||
node.removeGlobalAnnotation();
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditor::acceptedClicked()
|
||||
{
|
||||
if (m_dialog) {
|
||||
|
||||
Annotation annotation = m_dialog->annotation();
|
||||
if (const auto *dialog = qobject_cast<GlobalAnnotationEditorDialog *>(widget())) {
|
||||
auto &node = this->m_modelNode;
|
||||
const Annotation annotation = dialog->annotation();
|
||||
|
||||
if (annotation.comments().isEmpty())
|
||||
m_modelNode.removeGlobalAnnotation();
|
||||
node.removeGlobalAnnotation();
|
||||
else
|
||||
m_modelNode.setGlobalAnnotation(annotation);
|
||||
node.setGlobalAnnotation(annotation);
|
||||
|
||||
GlobalAnnotationStatus status = m_dialog->globalStatus();
|
||||
const GlobalAnnotationStatus status = dialog->globalStatus();
|
||||
|
||||
if (status.status() == GlobalAnnotationStatus::NoStatus) {
|
||||
if (m_modelNode.hasGlobalStatus()) {
|
||||
m_modelNode.removeGlobalStatus();
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_modelNode.setGlobalStatus(status);
|
||||
}
|
||||
if (status.status() == GlobalAnnotationStatus::NoStatus)
|
||||
node.removeGlobalStatus();
|
||||
else
|
||||
node.setGlobalStatus(status);
|
||||
}
|
||||
|
||||
hideWidget();
|
||||
|
||||
emit accepted();
|
||||
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
@@ -151,7 +101,6 @@ void GlobalAnnotationEditor::cancelClicked()
|
||||
hideWidget();
|
||||
|
||||
emit canceled();
|
||||
|
||||
emit annotationChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,32 +26,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQml>
|
||||
#include <QPointer>
|
||||
#include <QtQml>
|
||||
|
||||
#include "globalannotationeditordialog.h"
|
||||
#include "abstractaction.h"
|
||||
#include "annotation.h"
|
||||
#include "globalannotationeditordialog.h"
|
||||
|
||||
#include "editorproxy.h"
|
||||
#include "modelnode.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class GlobalAnnotationEditor : public QObject
|
||||
class GlobalAnnotationEditor : public ModelNodeEditorProxy
|
||||
{
|
||||
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;
|
||||
QWidget *createWidget() override;
|
||||
|
||||
Q_INVOKABLE void removeFullAnnotation();
|
||||
|
||||
@@ -59,17 +52,11 @@ signals:
|
||||
void accepted();
|
||||
void canceled();
|
||||
void modelNodeBackendChanged();
|
||||
|
||||
void annotationChanged();
|
||||
|
||||
private slots:
|
||||
void acceptedClicked();
|
||||
void cancelClicked();
|
||||
|
||||
private:
|
||||
QPointer<GlobalAnnotationEditorDialog> m_dialog;
|
||||
|
||||
ModelNode m_modelNode;
|
||||
};
|
||||
|
||||
} //namespace QmlDesigner
|
||||
|
||||
@@ -24,87 +24,55 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "globalannotationeditordialog.h"
|
||||
#include "ui_globalannotationeditordialog.h"
|
||||
#include "annotation.h"
|
||||
#include "annotationcommenttab.h"
|
||||
|
||||
#include "ui_annotationcommenttab.h"
|
||||
#include "ui_globalannotationeditordialog.h"
|
||||
|
||||
#include <timelineicons.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QToolBar>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
GlobalAnnotationEditorDialog::GlobalAnnotationEditorDialog(QWidget *parent, const Annotation &annotation, GlobalAnnotationStatus status)
|
||||
: QDialog(parent)
|
||||
GlobalAnnotationEditorDialog::GlobalAnnotationEditorDialog(QWidget *parent,
|
||||
GlobalAnnotationStatus status)
|
||||
: BasicAnnotationEditorDialog(parent)
|
||||
, ui(new Ui::GlobalAnnotationEditorDialog)
|
||||
, m_annotation(annotation)
|
||||
, m_globalStatus(status)
|
||||
, m_statusIsActive(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->tabWidget->setDefaultAnnotations(defaultAnnotations());
|
||||
ui->tableView->setDefaultAnnotations(defaultAnnotations());
|
||||
|
||||
setWindowFlag(Qt::Tool, true);
|
||||
setModal(true);
|
||||
connect(ui->tableView,
|
||||
&AnnotationTableView::richTextEditorRequested,
|
||||
this,
|
||||
[&](int index, QString const &) {
|
||||
switchToTabView();
|
||||
ui->tabWidget->setCurrentIndex(index);
|
||||
});
|
||||
|
||||
connect(this, &QDialog::accepted, this, &GlobalAnnotationEditorDialog::acceptedClicked);
|
||||
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &GlobalAnnotationEditorDialog::tabChanged);
|
||||
|
||||
auto *commentCornerWidget = new QToolBar;
|
||||
|
||||
auto *commentAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(), tr("Add Comment")); //timeline icons?
|
||||
auto *commentRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(),
|
||||
tr("Remove Comment")); //timeline icons?
|
||||
|
||||
connect(commentAddAction, &QAction::triggered, this, [this]() {
|
||||
addComment(Comment());
|
||||
});
|
||||
|
||||
connect(commentRemoveAction, &QAction::triggered, this, [this]() {
|
||||
|
||||
if (ui->tabWidget->count() == 0) { //it is not even supposed to happen but lets be sure
|
||||
QTC_ASSERT(false, return);
|
||||
return;
|
||||
}
|
||||
|
||||
int currentIndex = ui->tabWidget->currentIndex();
|
||||
QString currentTitle = ui->tabWidget->tabText(currentIndex);
|
||||
|
||||
QMessageBox *deleteDialog = new QMessageBox(this);
|
||||
deleteDialog->setWindowTitle(currentTitle);
|
||||
deleteDialog->setText(tr("Delete this comment?"));
|
||||
deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
deleteDialog->setDefaultButton(QMessageBox::Yes);
|
||||
|
||||
int result = deleteDialog->exec();
|
||||
|
||||
if (result == QMessageBox::Yes) {
|
||||
removeComment(currentIndex);
|
||||
}
|
||||
|
||||
if (ui->tabWidget->count() == 0) //lets be sure that tabWidget is never empty
|
||||
addComment(Comment());
|
||||
});
|
||||
|
||||
commentCornerWidget->addAction(commentAddAction);
|
||||
commentCornerWidget->addAction(commentRemoveAction);
|
||||
|
||||
ui->tabWidget->setCornerWidget(commentCornerWidget, Qt::TopRightCorner);
|
||||
|
||||
connect(ui->statusAddButton, &QPushButton::clicked, [&](bool){
|
||||
connect(ui->statusAddButton, &QPushButton::clicked, this, [&](bool) {
|
||||
setStatusVisibility(true);
|
||||
});
|
||||
|
||||
setStatus(m_globalStatus);
|
||||
connect(ui->rbTableView,
|
||||
&QRadioButton::clicked,
|
||||
this,
|
||||
&GlobalAnnotationEditorDialog::switchToTableView);
|
||||
connect(ui->rbTabView,
|
||||
&QRadioButton::clicked,
|
||||
this,
|
||||
&GlobalAnnotationEditorDialog::switchToTabView);
|
||||
|
||||
fillFields();
|
||||
setStatus(m_globalStatus);
|
||||
setWindowTitle(globalEditorTitle);
|
||||
switchToTabView();
|
||||
}
|
||||
|
||||
GlobalAnnotationEditorDialog::~GlobalAnnotationEditorDialog()
|
||||
@@ -112,26 +80,18 @@ GlobalAnnotationEditorDialog::~GlobalAnnotationEditorDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::setAnnotation(const Annotation &annotation)
|
||||
GlobalAnnotationEditorDialog::ViewMode GlobalAnnotationEditorDialog::viewMode() const
|
||||
{
|
||||
m_annotation = annotation;
|
||||
fillFields();
|
||||
}
|
||||
|
||||
Annotation GlobalAnnotationEditorDialog::annotation() const
|
||||
{
|
||||
return m_annotation;
|
||||
return ui->rbTableView->isChecked() ? TableView : TabsView;
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::setStatus(GlobalAnnotationStatus status)
|
||||
{
|
||||
m_globalStatus = status;
|
||||
bool hasStatus = status.status() != GlobalAnnotationStatus::NoStatus;
|
||||
|
||||
bool hasStatus = (status.status() != GlobalAnnotationStatus::NoStatus);
|
||||
|
||||
if (hasStatus) {
|
||||
if (hasStatus)
|
||||
ui->statusComboBox->setCurrentIndex(int(status.status()));
|
||||
}
|
||||
|
||||
setStatusVisibility(hasStatus);
|
||||
}
|
||||
@@ -141,117 +101,73 @@ GlobalAnnotationStatus GlobalAnnotationEditorDialog::globalStatus() const
|
||||
return m_globalStatus;
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::acceptedClicked()
|
||||
void GlobalAnnotationEditorDialog::showStatusContainer(bool show)
|
||||
{
|
||||
Annotation annotation;
|
||||
|
||||
annotation.removeComments();
|
||||
|
||||
for (int i = 0; i < ui->tabWidget->count(); i++) {
|
||||
AnnotationCommentTab* tab = reinterpret_cast<AnnotationCommentTab*>(ui->tabWidget->widget(i));
|
||||
if (!tab)
|
||||
continue;
|
||||
|
||||
Comment comment = tab->currentComment();
|
||||
|
||||
if (!comment.isEmpty())
|
||||
annotation.addComment(comment);
|
||||
}
|
||||
|
||||
m_annotation = annotation;
|
||||
|
||||
if (m_statusIsActive) {
|
||||
m_globalStatus.setStatus(ui->statusComboBox->currentIndex());
|
||||
}
|
||||
|
||||
emit GlobalAnnotationEditorDialog::acceptedDialog();
|
||||
ui->statusContainer->setVisible(show);
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::commentTitleChanged(const QString &text, QWidget *tab)
|
||||
void GlobalAnnotationEditorDialog::switchToTabView()
|
||||
{
|
||||
int tabIndex = ui->tabWidget->indexOf(tab);
|
||||
if (tabIndex >= 0)
|
||||
ui->tabWidget->setTabText(tabIndex, text);
|
||||
m_annotation.setComments(ui->tableView->fetchComments());
|
||||
ui->rbTabView->setChecked(true);
|
||||
ui->tableView->hide();
|
||||
ui->tabWidget->show();
|
||||
fillFields();
|
||||
}
|
||||
|
||||
if (text.isEmpty())
|
||||
ui->tabWidget->setTabText(tabIndex,
|
||||
(defaultTabName + " " + QString::number(tabIndex+1)));
|
||||
void GlobalAnnotationEditorDialog::switchToTableView()
|
||||
{
|
||||
m_annotation.setComments(ui->tabWidget->fetchComments());
|
||||
ui->rbTableView->setChecked(true);
|
||||
ui->tabWidget->hide();
|
||||
ui->tableView->show();
|
||||
fillFields();
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::acceptedClicked()
|
||||
{
|
||||
updateAnnotation();
|
||||
emit GlobalAnnotationEditorDialog::acceptedDialog();
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::fillFields()
|
||||
{
|
||||
setupComments();
|
||||
ui->tabWidget->setupComments(m_annotation.comments());
|
||||
ui->tableView->setupComments(m_annotation.comments());
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::setupComments()
|
||||
void GlobalAnnotationEditorDialog::updateAnnotation()
|
||||
{
|
||||
ui->tabWidget->setUpdatesEnabled(false);
|
||||
|
||||
deleteAllTabs();
|
||||
|
||||
const QVector<Comment> comments = m_annotation.comments();
|
||||
|
||||
if (comments.isEmpty())
|
||||
addComment(Comment());
|
||||
|
||||
for (const Comment &comment : comments) {
|
||||
addCommentTab(comment);
|
||||
Annotation annotation;
|
||||
switch (viewMode()) {
|
||||
case TabsView:
|
||||
annotation.setComments(ui->tabWidget->fetchComments());
|
||||
break;
|
||||
case TableView:
|
||||
annotation.setComments(ui->tableView->fetchComments());
|
||||
break;
|
||||
}
|
||||
|
||||
ui->tabWidget->setUpdatesEnabled(true);
|
||||
m_annotation = annotation;
|
||||
|
||||
if (m_statusIsActive)
|
||||
m_globalStatus.setStatus(ui->statusComboBox->currentIndex());
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::addComment(const Comment &comment)
|
||||
{
|
||||
m_annotation.addComment(comment);
|
||||
addCommentTab(comment);
|
||||
ui->tabWidget->addCommentTab(comment);
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::removeComment(int index)
|
||||
{
|
||||
if ((m_annotation.commentsSize() > index) && (index >= 0)) {
|
||||
m_annotation.removeComment(index);
|
||||
removeCommentTab(index);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::addCommentTab(const Comment &comment)
|
||||
{
|
||||
auto commentTab = new AnnotationCommentTab();
|
||||
commentTab->setComment(comment);
|
||||
|
||||
QString tabTitle(comment.title());
|
||||
int tabIndex = ui->tabWidget->addTab(commentTab, tabTitle);
|
||||
ui->tabWidget->setCurrentIndex(tabIndex);
|
||||
|
||||
if (tabTitle.isEmpty()) {
|
||||
const QString appendix = ((tabIndex > 0) ? QString::number(tabIndex+1) : "");
|
||||
|
||||
tabTitle = QString("%1 %2").arg(defaultTabName).arg(appendix);
|
||||
|
||||
ui->tabWidget->setTabText(tabIndex, tabTitle);
|
||||
}
|
||||
|
||||
connect(commentTab, &AnnotationCommentTab::titleChanged,
|
||||
this, &GlobalAnnotationEditorDialog::commentTitleChanged);
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::removeCommentTab(int index)
|
||||
{
|
||||
if ((ui->tabWidget->count() > index) && (index >= 0)) {
|
||||
ui->tabWidget->removeTab(index);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::deleteAllTabs()
|
||||
{
|
||||
while (ui->tabWidget->count() > 0) {
|
||||
QWidget *w = ui->tabWidget->widget(0);
|
||||
ui->tabWidget->removeTab(0);
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::setStatusVisibility(bool hasStatus)
|
||||
{
|
||||
ui->statusAddButton->setVisible(!hasStatus);
|
||||
@@ -260,9 +176,4 @@ void GlobalAnnotationEditorDialog::setStatusVisibility(bool hasStatus)
|
||||
m_statusIsActive = hasStatus;
|
||||
}
|
||||
|
||||
void GlobalAnnotationEditorDialog::tabChanged(int index)
|
||||
{
|
||||
(void) index;
|
||||
}
|
||||
|
||||
} //namespace QmlDesigner
|
||||
|
||||
@@ -25,9 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "annotation.h"
|
||||
#include "annotationeditordialog.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -35,46 +33,46 @@ namespace Ui {
|
||||
class GlobalAnnotationEditorDialog;
|
||||
}
|
||||
|
||||
class GlobalAnnotationEditorDialog : public QDialog
|
||||
class GlobalAnnotationEditorDialog : public BasicAnnotationEditorDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GlobalAnnotationEditorDialog(QWidget *parent, const Annotation &annotation, GlobalAnnotationStatus status);
|
||||
enum ViewMode {
|
||||
TableView,
|
||||
TabsView
|
||||
};
|
||||
|
||||
explicit GlobalAnnotationEditorDialog(
|
||||
QWidget *parent = nullptr, GlobalAnnotationStatus status = GlobalAnnotationStatus::NoStatus);
|
||||
~GlobalAnnotationEditorDialog();
|
||||
|
||||
void setAnnotation(const Annotation &annotation);
|
||||
Annotation annotation() const;
|
||||
ViewMode viewMode() const;
|
||||
|
||||
void setStatus(GlobalAnnotationStatus status);
|
||||
GlobalAnnotationStatus globalStatus() const;
|
||||
|
||||
signals:
|
||||
void acceptedDialog(); //use instead of QDialog::accepted
|
||||
public slots:
|
||||
void showStatusContainer(bool show);
|
||||
void switchToTabView();
|
||||
void switchToTableView();
|
||||
|
||||
private slots:
|
||||
void acceptedClicked();
|
||||
void tabChanged(int index);
|
||||
void commentTitleChanged(const QString &text, QWidget *tab);
|
||||
void acceptedClicked() override;
|
||||
|
||||
private:
|
||||
void fillFields();
|
||||
void setupComments();
|
||||
|
||||
void fillFields() override;
|
||||
void updateAnnotation();
|
||||
void addComment(const Comment &comment);
|
||||
void removeComment(int index);
|
||||
|
||||
void addCommentTab(const Comment &comment);
|
||||
void removeCommentTab(int index);
|
||||
void deleteAllTabs();
|
||||
|
||||
void setStatusVisibility(bool hasStatus);
|
||||
|
||||
private:
|
||||
const QString globalEditorTitle = {tr("Global Annotation Editor")};
|
||||
const QString defaultTabName = {tr("Annotation")};
|
||||
|
||||
Ui::GlobalAnnotationEditorDialog *ui;
|
||||
|
||||
Annotation m_annotation;
|
||||
GlobalAnnotationStatus m_globalStatus;
|
||||
bool m_statusIsActive;
|
||||
};
|
||||
|
||||
@@ -73,6 +73,23 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbTabView">
|
||||
<property name="text">
|
||||
<string>Tab View</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbTableView">
|
||||
<property name="text">
|
||||
<string>Table View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -90,7 +107,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<widget class="AnnotationTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -109,6 +126,9 @@
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AnnotationTableView" name="tableView"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="focusPolicy">
|
||||
@@ -124,6 +144,19 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>AnnotationTabWidget</class>
|
||||
<extends>QTabWidget</extends>
|
||||
<header>annotationtabwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AnnotationTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>annotationtableview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
</tabstops>
|
||||
|
||||
@@ -96,8 +96,8 @@ void SignalList::prepareDialog()
|
||||
m_dialog = new SignalListDialog(Core::ICore::dialogParent());
|
||||
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_dialog->initialize(m_model);
|
||||
m_dialog->setWindowTitle(::QmlDesigner::SignalList::tr("Signal List for ")
|
||||
+ m_modelNode.validId());
|
||||
m_dialog->setWindowTitle(::QmlDesigner::SignalList::tr("Signal List for %1")
|
||||
.arg(m_modelNode.validId()));
|
||||
|
||||
auto *delegate = static_cast<SignalListDelegate *>(m_dialog->tableView()->itemDelegate());
|
||||
connect(delegate, &SignalListDelegate::connectClicked, this, &SignalList::connectClicked);
|
||||
|
||||
@@ -1556,7 +1556,7 @@ void editAnnotation(const SelectionContext &selectionContext)
|
||||
{
|
||||
ModelNode selectedNode = selectionContext.currentSingleSelectedNode();
|
||||
|
||||
AnnotationEditor::showWidget(selectedNode);
|
||||
ModelNodeEditorProxy::fromModelNode<AnnotationEditor>(selectedNode);
|
||||
}
|
||||
|
||||
QVariant previewImageDataForGenericNode(const ModelNode &modelNode)
|
||||
|
||||
@@ -118,6 +118,11 @@ void CurveEditorView::nodeReparented(const ModelNode &node,
|
||||
updateKeyframes();
|
||||
else if (QmlTimelineKeyframeGroup::checkKeyframesType(node))
|
||||
updateKeyframes();
|
||||
else if (newPropertyParent.isValid() && !oldPropertyParent.isValid()) {
|
||||
if (activeTimeline().hasKeyframeGroupForTarget(node)) {
|
||||
updateKeyframes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurveEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||
|
||||
@@ -163,8 +163,21 @@ void DebugView::nodeIdChanged(const ModelNode &node, const QString &newId, const
|
||||
}
|
||||
}
|
||||
|
||||
void DebugView::propertiesAboutToBeRemoved(const QList<AbstractProperty> & /*propertyList*/)
|
||||
void DebugView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
|
||||
{
|
||||
if (isDebugViewEnabled()) {
|
||||
QTextStream message;
|
||||
QString string;
|
||||
message.setString(&string);
|
||||
for (const AbstractProperty &property : propertyList) {
|
||||
message << property;
|
||||
if (property.isNodeAbstractProperty())
|
||||
message << " is NodeAbstractProperty";
|
||||
if (property.isDefaultProperty())
|
||||
message << " is DefaultProperty";
|
||||
}
|
||||
log("::propertiesAboutToBeRemoved:", string);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugView::variantPropertiesChanged(const QList<VariantProperty> &propertyList,
|
||||
|
||||
@@ -316,7 +316,7 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
|
||||
const QString &author, const QString &text,
|
||||
const QString &date, QGraphicsItem *parent)
|
||||
{
|
||||
static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
|
||||
static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
|
||||
static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
|
||||
static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
|
||||
QFont font;
|
||||
@@ -384,7 +384,7 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
|
||||
|
||||
QGraphicsItem *FormEditorAnnotationIcon::createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent)
|
||||
{
|
||||
static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
|
||||
static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
|
||||
static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
|
||||
static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
|
||||
QFont font;
|
||||
@@ -426,8 +426,8 @@ void FormEditorAnnotationIcon::createAnnotationEditor()
|
||||
|
||||
m_annotationEditor = new AnnotationEditorDialog(Core::ICore::dialogParent(),
|
||||
m_modelNode.displayName(),
|
||||
m_modelNode.customId(),
|
||||
m_modelNode.annotation());
|
||||
m_modelNode.customId());
|
||||
m_annotationEditor->setAnnotation(m_modelNode.annotation());
|
||||
|
||||
connect(m_annotationEditor, &AnnotationEditorDialog::acceptedDialog,
|
||||
this, &FormEditorAnnotationIcon::annotationDialogAccepted);
|
||||
|
||||
@@ -212,6 +212,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
|
||||
QString projectName = project ? project->displayName() : "";
|
||||
|
||||
// create import sections
|
||||
const QList<Import> usedImports = model->usedImports();
|
||||
QHash<QString, ItemLibraryImport *> importHash;
|
||||
for (const Import &import : model->imports()) {
|
||||
if (import.url() != projectName) {
|
||||
@@ -239,6 +240,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
|
||||
auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
|
||||
: ItemLibraryImport::SectionType::Default;
|
||||
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
|
||||
itemLibImport->setImportUsed(usedImports.contains(import));
|
||||
importHash.insert(importUrl, itemLibImport);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ ItemLibraryResourceView::ItemLibraryResourceView(AsynchronousImageCache &fontIma
|
||||
setSpacing(4);
|
||||
|
||||
setViewMode(QListView::IconMode);
|
||||
setMovement(QListView::Static);
|
||||
setMovement(QListView::Snap);
|
||||
setResizeMode(QListView::Adjust);
|
||||
setSelectionRectVisible(false);
|
||||
setWrapping(true);
|
||||
|
||||
@@ -669,7 +669,6 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &type,
|
||||
bool emptyTemplate = true;
|
||||
|
||||
const QString anchorLeftRight = "anchors.left: parent.left\nanchors.right: parent.right\n";
|
||||
const QString paddingLeftTopBottom = "leftPadding: 0\ntopPadding: 0\nbottomPadding: 0\n";
|
||||
|
||||
qmlTemplate += "Column {\n";
|
||||
qmlTemplate += anchorLeftRight;
|
||||
@@ -680,7 +679,6 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &type,
|
||||
qmlTemplate += "Section {\n";
|
||||
qmlTemplate += "caption: \"User added properties\"\n";
|
||||
qmlTemplate += anchorLeftRight;
|
||||
qmlTemplate += paddingLeftTopBottom;
|
||||
qmlTemplate += "Column {\n";
|
||||
qmlTemplate += "width: parent.width\n";
|
||||
|
||||
@@ -747,7 +745,6 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &type,
|
||||
qmlTemplate += "Section {\n";
|
||||
qmlTemplate += QStringLiteral("caption: \"%1 - %2\"\n").arg(QString::fromUtf8(p)).arg(QString::fromUtf8(parentTypeName));
|
||||
qmlTemplate += anchorLeftRight;
|
||||
qmlTemplate += paddingLeftTopBottom;
|
||||
qmlTemplate += "level: 1\n";
|
||||
qmlTemplate += "Column {\n";
|
||||
qmlTemplate += "width: parent.width\n";
|
||||
|
||||
@@ -119,6 +119,14 @@ RichTextEditor::RichTextEditor(QWidget *parent)
|
||||
ui->textEdit->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse);
|
||||
ui->tableBar->setVisible(false);
|
||||
|
||||
const QColor backColor = Theme::getColor(Theme::DSpanelBackground);
|
||||
|
||||
const QString toolBarStyleSheet =
|
||||
QString("QToolBar { background-color: %1; border-width: 1px }").arg(backColor.name());
|
||||
|
||||
ui->toolBar->setStyleSheet(toolBarStyleSheet);
|
||||
ui->tableBar->setStyleSheet(toolBarStyleSheet);
|
||||
|
||||
setupEditActions();
|
||||
setupTextActions();
|
||||
setupImageActions();
|
||||
@@ -201,7 +209,7 @@ void RichTextEditor::setDocumentBaseUrl(const QUrl& url)
|
||||
QIcon RichTextEditor::getIcon(Theme::Icon icon)
|
||||
{
|
||||
const QString fontName = "qtds_propertyIconFont.ttf";
|
||||
const QColor iconColorNormal(Theme::getColor(Theme::IconsBaseColor));
|
||||
const QColor iconColorNormal(Theme::getColor(Theme::DStextColor));
|
||||
|
||||
return Utils::StyleHelper::getIconFromIconFont(
|
||||
fontName, Theme::getIconUnicode(icon), 20, 20, iconColorNormal);
|
||||
|
||||
@@ -205,9 +205,10 @@ TimelineWidget::TimelineWidget(TimelineView *view)
|
||||
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. <br><br> \
|
||||
To create an animation, add a timeline by clicking the + button.");
|
||||
const QString labelText =
|
||||
tr("This file does not contain a timeline. <br><br>"
|
||||
"To create an animation, add a timeline by clicking the + button.");
|
||||
|
||||
onboardingTopLabel->setText(labelText);
|
||||
onboardingTopLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
|
||||
@@ -241,7 +242,7 @@ TimelineWidget::TimelineWidget(TimelineView *view)
|
||||
{
|
||||
QPalette timelinePalette;
|
||||
timelinePalette.setColor(QPalette::Text, Utils::creatorTheme()->color(
|
||||
Utils::Theme::QmlDesigner_FormEditorForegroundColor));
|
||||
Utils::Theme::DStextColor));
|
||||
timelinePalette.setColor(QPalette::WindowText, timelinePalette.color(QPalette::Text));
|
||||
timelinePalette.setColor(QPalette::Window, Utils::creatorTheme()->color(
|
||||
Utils::Theme::QmlDesigner_BackgroundColorDarkAlternate));
|
||||
|
||||
@@ -43,7 +43,8 @@ template<typename DatabaseType>
|
||||
class ImageCacheStorage : public ImageCacheStorageInterface
|
||||
{
|
||||
public:
|
||||
using ReadStatement = typename DatabaseType::ReadStatement;
|
||||
template<int ResultCount>
|
||||
using ReadStatement = typename DatabaseType::template ReadStatement<ResultCount>;
|
||||
using WriteStatement = typename DatabaseType::WriteStatement;
|
||||
|
||||
ImageCacheStorage(DatabaseType &database)
|
||||
@@ -272,11 +273,11 @@ public:
|
||||
DatabaseType &database;
|
||||
Initializer initializer{database};
|
||||
Sqlite::ImmediateNonThrowingDestructorTransaction transaction{database};
|
||||
mutable ReadStatement selectImageStatement{
|
||||
mutable ReadStatement<1> selectImageStatement{
|
||||
"SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database};
|
||||
mutable ReadStatement selectSmallImageStatement{
|
||||
mutable ReadStatement<1> selectSmallImageStatement{
|
||||
"SELECT smallImage FROM images WHERE name=?1 AND mtime >= ?2", database};
|
||||
mutable ReadStatement selectIconStatement{
|
||||
mutable ReadStatement<1> selectIconStatement{
|
||||
"SELECT icon FROM icons WHERE name=?1 AND mtime >= ?2", database};
|
||||
WriteStatement upsertImageStatement{
|
||||
"INSERT INTO images(name, mtime, image, smallImage) VALUES (?1, ?2, ?3, ?4) ON "
|
||||
|
||||
@@ -934,15 +934,23 @@ void TextToModelMerger::setupUsedImports()
|
||||
|
||||
const QList<QmlJS::Import> allImports = imports->all();
|
||||
|
||||
QSet<QString> usedImportsSet;
|
||||
QList<Import> usedImports;
|
||||
|
||||
foreach (const QmlJS::Import &import, allImports) {
|
||||
if (import.used && !import.info.name().isEmpty()) {
|
||||
if (import.info.type() == ImportType::Library) {
|
||||
// populate usedImportsSet from current model nodes
|
||||
const QList<ModelNode> allModelNodes = m_rewriterView->allModelNodes();
|
||||
for (const ModelNode &modelNode : allModelNodes) {
|
||||
QString type = QString::fromUtf8(modelNode.type());
|
||||
if (type.contains('.'))
|
||||
usedImportsSet.insert(type.left(type.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
for (const QmlJS::Import &import : allImports) {
|
||||
if (!import.info.name().isEmpty() && usedImportsSet.contains(import.info.name())) {
|
||||
if (import.info.type() == ImportType::Library)
|
||||
usedImports.append(Import::createLibraryImport(import.info.name(), import.info.version().toString(), import.info.as()));
|
||||
} else if (import.info.type() == ImportType::Directory || import.info.type() == ImportType::File) {
|
||||
else if (import.info.type() == ImportType::Directory || import.info.type() == ImportType::File)
|
||||
usedImports.append(Import::createFileImport(import.info.name(), import.info.version().toString(), import.info.as()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,9 @@ void DocumentManager::addFileToVersionControl(const QString &directoryPath, cons
|
||||
|
||||
Utils::FilePath DocumentManager::currentFilePath()
|
||||
{
|
||||
if (!QmlDesignerPlugin::instance()->currentDesignDocument())
|
||||
return {};
|
||||
|
||||
return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()->fileName();
|
||||
}
|
||||
|
||||
|
||||
118
src/plugins/qmldesigner/editorproxy.cpp
Normal file
118
src/plugins/qmldesigner/editorproxy.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "editorproxy.h"
|
||||
#include "qmlmodelnodeproxy.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
EditorProxy::EditorProxy(QObject *parent)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
EditorProxy::~EditorProxy()
|
||||
{
|
||||
hideWidget();
|
||||
}
|
||||
|
||||
void EditorProxy::showWidget()
|
||||
{
|
||||
if ((m_widget = createWidget())) {
|
||||
m_widget->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_widget->show();
|
||||
m_widget->raise();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorProxy::showWidget(int x, int y)
|
||||
{
|
||||
showWidget();
|
||||
if (m_widget) {
|
||||
m_widget->move(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorProxy::hideWidget()
|
||||
{
|
||||
if (m_widget)
|
||||
m_widget->close();
|
||||
m_widget = nullptr;
|
||||
}
|
||||
|
||||
QWidget *EditorProxy::widget() const
|
||||
{
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
ModelNodeEditorProxy::ModelNodeEditorProxy(QObject *parent)
|
||||
: EditorProxy(parent)
|
||||
{}
|
||||
|
||||
ModelNodeEditorProxy::~ModelNodeEditorProxy() {}
|
||||
|
||||
ModelNode ModelNodeEditorProxy::modelNode() const
|
||||
{
|
||||
return m_modelNode;
|
||||
}
|
||||
|
||||
void ModelNodeEditorProxy::setModelNode(const ModelNode &modelNode)
|
||||
{
|
||||
m_modelNodeBackend = {};
|
||||
m_modelNode = modelNode;
|
||||
}
|
||||
|
||||
void ModelNodeEditorProxy::setModelNodeBackend(const QVariant &modelNodeBackend)
|
||||
{
|
||||
if (!modelNodeBackend.isNull() && modelNodeBackend.isValid()) {
|
||||
const auto modelNodeBackendObject = modelNodeBackend.value<QObject *>();
|
||||
const auto backendObjectCasted = qobject_cast<const QmlDesigner::QmlModelNodeProxy *>(
|
||||
modelNodeBackendObject);
|
||||
|
||||
if (backendObjectCasted)
|
||||
m_modelNode = backendObjectCasted->qmlObjectNode().modelNode();
|
||||
m_modelNodeBackend = modelNodeBackend;
|
||||
|
||||
emit modelNodeBackendChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ModelNodeEditorProxy::modelNodeBackend() const
|
||||
{
|
||||
return m_modelNodeBackend;
|
||||
}
|
||||
|
||||
bool ModelNodeEditorProxy::hasCustomId() const
|
||||
{
|
||||
return m_modelNode.isValid() ? m_modelNode.hasCustomId() : false;
|
||||
}
|
||||
|
||||
bool ModelNodeEditorProxy::hasAnnotation() const
|
||||
{
|
||||
return m_modelNode.isValid() ? m_modelNode.hasAnnotation() : false;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
101
src/plugins/qmldesigner/editorproxy.h
Normal file
101
src/plugins/qmldesigner/editorproxy.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <QQmlEngine>
|
||||
#include <QPointer>
|
||||
|
||||
#include "modelnode.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
class EditorProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EditorProxy(QObject *parent = nullptr);
|
||||
~EditorProxy();
|
||||
|
||||
Q_INVOKABLE virtual void showWidget();
|
||||
Q_INVOKABLE void showWidget(int x, int y);
|
||||
Q_INVOKABLE virtual void hideWidget();
|
||||
|
||||
QWidget *widget() const;
|
||||
virtual QWidget *createWidget() = 0;
|
||||
|
||||
template<typename T>
|
||||
static void registerType(const char *className)
|
||||
{
|
||||
qmlRegisterType<T>("HelperWidgets", 2, 0, className);
|
||||
}
|
||||
|
||||
protected:
|
||||
QPointer<QWidget> m_widget;
|
||||
};
|
||||
|
||||
class ModelNodeEditorProxy : public EditorProxy
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool hasCustomId READ hasCustomId NOTIFY customIdChanged)
|
||||
Q_PROPERTY(bool hasAnnotation READ hasAnnotation NOTIFY annotationChanged)
|
||||
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend
|
||||
NOTIFY modelNodeBackendChanged)
|
||||
public:
|
||||
ModelNodeEditorProxy(QObject *parent = nullptr);
|
||||
~ModelNodeEditorProxy();
|
||||
|
||||
ModelNode modelNode() const;
|
||||
virtual void setModelNode(const ModelNode &modelNode);
|
||||
|
||||
void setModelNodeBackend(const QVariant &modelNodeBackend);
|
||||
QVariant modelNodeBackend() const;
|
||||
|
||||
Q_INVOKABLE bool hasCustomId() const;
|
||||
Q_INVOKABLE bool hasAnnotation() const;
|
||||
|
||||
template<typename T>
|
||||
static T *fromModelNode(const ModelNode &modelNode, QVariant const &modelNodeBackend = {})
|
||||
{
|
||||
auto *editor = new T;
|
||||
editor->setModelNode(modelNode);
|
||||
if (!modelNodeBackend.isNull())
|
||||
editor->setModelNodeBackend(modelNodeBackend);
|
||||
|
||||
editor->showWidget();
|
||||
if (editor->m_widget) {
|
||||
connect(editor->m_widget, &QObject::destroyed, [editor]() { editor->deleteLater(); });
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
signals:
|
||||
void customIdChanged();
|
||||
void annotationChanged();
|
||||
void modelNodeBackendChanged();
|
||||
|
||||
protected:
|
||||
QVariant m_modelNodeBackend;
|
||||
ModelNode m_modelNode;
|
||||
};
|
||||
} // namespace QmlDesigner
|
||||
@@ -3,6 +3,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \
|
||||
$$PWD/qmldesignerplugin.h \
|
||||
$$PWD/designmodewidget.h \
|
||||
$$PWD/designersettings.h \
|
||||
$$PWD/editorproxy.h \
|
||||
$$PWD/generateresource.h \
|
||||
$$PWD/settingspage.h \
|
||||
$$PWD/designmodecontext.h \
|
||||
@@ -17,6 +18,7 @@ SOURCES += $$PWD/qmldesignerplugin.cpp \
|
||||
$$PWD/shortcutmanager.cpp \
|
||||
$$PWD/designmodewidget.cpp \
|
||||
$$PWD/designersettings.cpp \
|
||||
$$PWD/editorproxy.cpp \
|
||||
$$PWD/generateresource.cpp \
|
||||
$$PWD/settingspage.cpp \
|
||||
$$PWD/designmodecontext.cpp \
|
||||
|
||||
@@ -43,6 +43,7 @@ Project {
|
||||
"../../../share/qtcreator/qml/qmlpuppet/commands",
|
||||
"../../../share/qtcreator/qml/qmlpuppet/types",
|
||||
"components",
|
||||
"components/annotationeditor",
|
||||
"components/componentcore",
|
||||
"components/curveeditor",
|
||||
"components/connectioneditor",
|
||||
@@ -730,6 +731,7 @@ Project {
|
||||
"annotationeditor/annotationcommenttab.ui",
|
||||
"annotationeditor/annotationeditor.cpp",
|
||||
"annotationeditor/annotationeditor.h",
|
||||
"annotationeditor/annotationeditor.qrc",
|
||||
"annotationeditor/globalannotationeditor.cpp",
|
||||
"annotationeditor/globalannotationeditor.h",
|
||||
"annotationeditor/annotationeditordialog.cpp",
|
||||
@@ -738,6 +740,12 @@ Project {
|
||||
"annotationeditor/globalannotationeditordialog.cpp",
|
||||
"annotationeditor/globalannotationeditordialog.h",
|
||||
"annotationeditor/globalannotationeditordialog.ui",
|
||||
"annotationeditor/defaultannotations.cpp",
|
||||
"annotationeditor/defaultannotations.h",
|
||||
"annotationeditor/annotationtableview.cpp",
|
||||
"annotationeditor/annotationtableview.h",
|
||||
"annotationeditor/annotationtabwidget.cpp",
|
||||
"annotationeditor/annotationtabwidget.h",
|
||||
"bindingeditor/bindingeditor.cpp",
|
||||
"bindingeditor/bindingeditor.h",
|
||||
"bindingeditor/actioneditor.cpp",
|
||||
@@ -970,6 +978,8 @@ Project {
|
||||
"documentmanager.h",
|
||||
"documentwarningwidget.cpp",
|
||||
"documentwarningwidget.h",
|
||||
"editorproxy.h",
|
||||
"editorproxy.cpp",
|
||||
"openuiqmlfiledialog.cpp",
|
||||
"openuiqmlfiledialog.h",
|
||||
"openuiqmlfiledialog.ui",
|
||||
|
||||
@@ -19,3 +19,8 @@ if (TARGET StudioWelcome)
|
||||
)
|
||||
qtc_add_resources(StudioWelcome StudioWelcome_qml FILES ${qmlfiles})
|
||||
endif()
|
||||
|
||||
extend_qtc_plugin(StudioWelcome
|
||||
CONDITION BUILD_WITH_CRASHPAD
|
||||
DEFINES ENABLE_CRASHPAD
|
||||
)
|
||||
|
||||
@@ -92,11 +92,20 @@ ListModel {
|
||||
}
|
||||
|
||||
ListElement {
|
||||
projectName: "highendivisystem"
|
||||
projectName: "digitalcluster"
|
||||
qmlFileName: "Screen01.ui.qml"
|
||||
thumbnail: "images/digital_cluster_thumbnail.png"
|
||||
displayName: "Digital Cluster"
|
||||
url: "https://download.qt.io/learning/examples/qtdesignstudio/digitalcluster.zip"
|
||||
showDownload: true
|
||||
}
|
||||
|
||||
ListElement {
|
||||
projectName: "effectdemo"
|
||||
qmlFileName: "Screen01.ui.qml"
|
||||
thumbnail: "images/effectdemo_thumbnail.png"
|
||||
displayName: "Effect Demo"
|
||||
url: "https://download.qt.io/learning/examples/qtdesignstudio/effectdemo.zip"
|
||||
showDownload: true
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -163,7 +163,8 @@ public:
|
||||
{
|
||||
const QString projectFile = data(index(row, 0),
|
||||
ProjectModel::FilePathRole).toString();
|
||||
ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
|
||||
if (QFileInfo::exists(projectFile))
|
||||
ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
|
||||
}
|
||||
|
||||
Q_INVOKABLE int get(int)
|
||||
|
||||
@@ -546,10 +546,10 @@ void SubmitEditorWidget::verifyDescription()
|
||||
"<ul>"
|
||||
"<li>Avoid very short commit messages.</li>"
|
||||
"<li>Consider the first line as subject (like in email) "
|
||||
"and keep it shorter than %1 characters.</li>"
|
||||
"and keep it shorter than %n characters.</li>"
|
||||
"<li>After an empty second line, a longer description can be added.</li>"
|
||||
"<li>Describe why the change was done, not how it was done.</li>"
|
||||
"</ul>").arg(MaxSubjectLength));
|
||||
"</ul>", nullptr, MaxSubjectLength));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
@@ -104,10 +104,7 @@ QVersionNumber WebAssemblyEmSdk::version(const FilePath &sdkRoot)
|
||||
return {};
|
||||
const QString cacheKey = sdkRoot.toString();
|
||||
if (!emSdkVersionCache()->contains(cacheKey)) {
|
||||
Environment env;
|
||||
// Non-Windows: Need python in path (not provided by emsdk), thus use systemEnvironment
|
||||
if (!HostOsInfo::isWindowsHost())
|
||||
env = Environment::systemEnvironment();
|
||||
Environment env = Environment::systemEnvironment();
|
||||
WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
|
||||
const QString scriptFile =
|
||||
QLatin1String("emcc") + QLatin1String(HostOsInfo::isWindowsHost() ? ".bat" : "");
|
||||
|
||||
Reference in New Issue
Block a user