Merge remote-tracking branch 'origin/4.15'

Change-Id: I528bff4d19dc4dfcb600e1e18b0d375c232eb417
This commit is contained in:
Eike Ziller
2021-03-05 17:55:17 +01:00
111 changed files with 2819 additions and 335 deletions

View File

@@ -519,15 +519,14 @@ void CMakeBuildSettingsWidget::updateButtonState()
break;
case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN:
default:
ni.type = CMakeConfigItem::INTERNAL;
ni.type = CMakeConfigItem::UNINITIALIZED;
break;
}
return ni;
});
m_resetButton->setEnabled(m_configModel->hasChanges() && !isParsing);
m_reconfigureButton->setEnabled((!configChanges.isEmpty() || m_configModel->hasCMakeChanges())
&& !isParsing);
m_reconfigureButton->setEnabled(!configChanges.isEmpty() && !isParsing);
m_buildConfiguration->setConfigurationChanges(configChanges);
}

View File

@@ -56,6 +56,7 @@
#include <utils/checkablemessagebox.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
#include <utils/mimetypes/mimetype.h>
#include <utils/qtcassert.h>
@@ -283,9 +284,8 @@ void CMakeBuildSystem::triggerParsing()
}
if ((0 == (reparseParameters & REPARSE_FORCE_EXTRA_CONFIGURATION))
&& !m_parameters.extraCMakeArguments.isEmpty()) {
if (mustApplyExtraArguments())
reparseParameters |= REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION;
&& mustApplyExtraArguments(m_parameters)) {
reparseParameters |= REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION;
}
qCDebug(cmakeBuildSystemLog) << "Asking reader to parse";
@@ -372,6 +372,27 @@ QString CMakeBuildSystem::reparseParametersString(int reparseFlags)
return result.trimmed();
}
void CMakeBuildSystem::writeConfigurationIntoBuildDirectory()
{
const Utils::MacroExpander *expander = cmakeBuildConfiguration()->macroExpander();
const FilePath buildDir = workDirectory(m_parameters);
QTC_ASSERT(buildDir.exists(), return );
const FilePath settingsFile = buildDir.pathAppended("qtcsettings.cmake");
QByteArray contents;
contents.append("# This file is managed by Qt Creator, do not edit!\n\n");
contents.append(
transform(cmakeBuildConfiguration()->configurationChanges(),
[expander](const CMakeConfigItem &item) { return item.toCMakeSetLine(expander); })
.join('\n')
.toUtf8());
QFile file(settingsFile.toString());
QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return );
file.write(contents);
}
void CMakeBuildSystem::setParametersAndRequestParse(const BuildDirParameters &parameters,
const int reparseParameters)
{
@@ -405,6 +426,8 @@ void CMakeBuildSystem::setParametersAndRequestParse(const BuildDirParameters &pa
m_reader.setParameters(m_parameters);
writeConfigurationIntoBuildDirectory();
if (reparseParameters & REPARSE_URGENT) {
qCDebug(cmakeBuildSystemLog) << "calling requestReparse";
requestParse();
@@ -414,15 +437,15 @@ void CMakeBuildSystem::setParametersAndRequestParse(const BuildDirParameters &pa
}
}
bool CMakeBuildSystem::mustApplyExtraArguments() const
bool CMakeBuildSystem::mustApplyExtraArguments(const BuildDirParameters &parameters) const
{
if (m_parameters.extraCMakeArguments.isEmpty())
if (parameters.extraCMakeArguments.isEmpty())
return false;
auto answer = QMessageBox::question(Core::ICore::mainWindow(),
tr("Apply configuration changes?"),
tr("Run CMake with \"%1\"?")
.arg(m_parameters.extraCMakeArguments.join(" ")),
.arg(parameters.extraCMakeArguments.join(" ")),
QMessageBox::Apply | QMessageBox::Discard,
QMessageBox::Apply);
return answer == QMessageBox::Apply;
@@ -482,12 +505,10 @@ bool CMakeBuildSystem::persistCMakeState()
qCDebug(cmakeBuildSystemLog) << "Checking whether build system needs to be persisted:"
<< "workdir:" << parameters.workDirectory
<< "buildDir:" << parameters.buildDirectory
<< "Has extraargs:" << !parameters.extraCMakeArguments.isEmpty()
<< "must apply extra Args:"
<< mustApplyExtraArguments();
<< "Has extraargs:" << !parameters.extraCMakeArguments.isEmpty();
if (parameters.workDirectory == parameters.buildDirectory
&& !parameters.extraCMakeArguments.isEmpty() && mustApplyExtraArguments()) {
&& mustApplyExtraArguments(parameters)) {
reparseFlags = REPARSE_FORCE_EXTRA_CONFIGURATION;
qCDebug(cmakeBuildSystemLog) << " -> must run CMake with extra arguments.";
}

View File

@@ -29,7 +29,6 @@
#include "cmakebuildtarget.h"
#include "cmakeprojectnodes.h"
#include "fileapireader.h"
#include "utils/macroexpander.h"
#include <projectexplorer/buildsystem.h>
@@ -117,7 +116,7 @@ private:
void setParametersAndRequestParse(const BuildDirParameters &parameters,
const int reparseParameters);
bool mustApplyExtraArguments() const;
bool mustApplyExtraArguments(const BuildDirParameters &parameters) const;
// State handling:
// Parser states:
@@ -155,6 +154,8 @@ private:
void runCTest();
void writeConfigurationIntoBuildDirectory();
ProjectExplorer::TreeScanner m_treeScanner;
QHash<QString, bool> m_mimeBinaryCache;
QList<const ProjectExplorer::FileNode *> m_allFiles;

View File

@@ -145,9 +145,11 @@ CMakeConfigItem::Type CMakeConfigItem::typeStringToType(const QByteArray &type)
return CMakeConfigItem::PATH;
if (type == "STATIC")
return CMakeConfigItem::STATIC;
if (type == "INTERNAL")
return CMakeConfigItem::INTERNAL;
QTC_CHECK(type == "INTERNAL" || type == "UNINITIALIZED");
return CMakeConfigItem::INTERNAL;
QTC_CHECK(type == "UNINITIALIZED");
return CMakeConfigItem::UNINITIALIZED;
}
QString CMakeConfigItem::typeToTypeString(const CMakeConfigItem::Type t)
@@ -163,8 +165,10 @@ QString CMakeConfigItem::typeToTypeString(const CMakeConfigItem::Type t)
return {"INTERNAL"};
case CMakeProjectManager::CMakeConfigItem::STATIC:
return {"STATIC"};
case CMakeConfigItem::BOOL:
case CMakeProjectManager::CMakeConfigItem::BOOL:
return {"BOOL"};
case CMakeProjectManager::CMakeConfigItem::UNINITIALIZED:
return {"UNINITIALIZED"};
}
QTC_CHECK(false);
return {};
@@ -418,6 +422,9 @@ QString CMakeConfigItem::toString(const Utils::MacroExpander *expander) const
case CMakeProjectManager::CMakeConfigItem::INTERNAL:
typeStr = QLatin1String("INTERNAL");
break;
case CMakeProjectManager::CMakeConfigItem::UNINITIALIZED:
typeStr = QLatin1String("UNINITIALIZED");
break;
case CMakeProjectManager::CMakeConfigItem::STRING:
default:
typeStr = QLatin1String("STRING");

View File

@@ -45,7 +45,7 @@ namespace CMakeProjectManager {
class CMAKE_EXPORT CMakeConfigItem {
public:
enum Type { FILEPATH, PATH, BOOL, STRING, INTERNAL, STATIC };
enum Type { FILEPATH, PATH, BOOL, STRING, INTERNAL, STATIC, UNINITIALIZED };
CMakeConfigItem();
CMakeConfigItem(const QByteArray &k, Type t, const QByteArray &d, const QByteArray &v, const QStringList &s = {});
CMakeConfigItem(const QByteArray &k, const QByteArray &v);

View File

@@ -46,7 +46,9 @@ CMakeInputsNode::CMakeInputsNode(const Utils::FilePath &cmakeLists) :
{
setPriority(Node::DefaultPriority - 10); // Bottom most!
setDisplayName(QCoreApplication::translate("CMakeFilesProjectNode", "CMake Modules"));
setIcon(QIcon(":/projectexplorer/images/session.png")); // TODO: Use a better icon!
static const QIcon modulesIcon = Core::FileIconProvider::directoryIcon(
ProjectExplorer::Constants::FILEOVERLAY_MODULES);
setIcon(modulesIcon);
setListInProject(false);
}

View File

@@ -123,11 +123,6 @@ bool ConfigModel::hasChanges() const
});
}
bool ConfigModel::hasCMakeChanges() const
{
return Utils::contains(m_configuration, [](const InternalDataItem &i) { return i.isCMakeChanged; });
}
bool ConfigModel::canForceTo(const QModelIndex &idx, const ConfigModel::DataItem::Type type) const
{
if (idx.model() != const_cast<ConfigModel *>(this) || idx.column() != 1)
@@ -264,7 +259,6 @@ void ConfigModel::setConfiguration(const QList<ConfigModel::InternalDataItem> &c
// merge old/new entry:
InternalDataItem item(*newIt);
item.newValue = (newIt->value != oldIt->newValue) ? oldIt->newValue : QString();
item.isCMakeChanged = (oldIt->value != newIt->value);
item.isUserChanged = !item.newValue.isEmpty() && (item.newValue != item.value);
result << item;
++newIt;
@@ -390,7 +384,6 @@ QVariant ConfigModelTreeItem::data(int column, int role) const
return toolTip();
case Qt::FontRole: {
QFont font;
font.setItalic(dataItem->isCMakeChanged);
font.setBold(dataItem->isUserNew);
font.setStrikeOut((!dataItem->inCMakeCache && !dataItem->isUserNew) || dataItem->isUnset);
return font;
@@ -414,7 +407,6 @@ QVariant ConfigModelTreeItem::data(int column, int role) const
case Qt::FontRole: {
QFont font;
font.setBold((dataItem->isUserChanged || dataItem->isUserNew) && !dataItem->isUnset);
font.setItalic(dataItem->isCMakeChanged);
font.setStrikeOut((!dataItem->inCMakeCache && !dataItem->isUserNew) || dataItem->isUnset);
return font;
}

View File

@@ -101,7 +101,7 @@ public:
cmi.type = CMakeConfigItem::STRING;
break;
case DataItem::UNKNOWN:
cmi.type = CMakeConfigItem::INTERNAL;
cmi.type = CMakeConfigItem::UNINITIALIZED;
break;
}
cmi.isUnset = isUnset;
@@ -144,7 +144,6 @@ public:
void resetAllChanges();
bool hasChanges() const;
bool hasCMakeChanges() const;
bool canForceTo(const QModelIndex &idx, const DataItem::Type type) const;
void forceTo(const QModelIndex &idx, const DataItem::Type type);
@@ -168,7 +167,6 @@ private:
bool isUserChanged = false;
bool isUserNew = false;
bool isCMakeChanged = false;
QString newValue;
QString kitValue;
};

View File

@@ -179,6 +179,14 @@ QVector<FolderNode::LocationInfo> extractBacktraceInformation(const BacktraceInf
return info;
}
static bool isChildOf(const FilePath &path, const QStringList &prefixes)
{
for (const QString &prefix : prefixes)
if (path.isChildOf(FilePath::fromString(prefix)))
return true;
return false;
}
QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
const FilePath &sourceDirectory,
const FilePath &buildDirectory)
@@ -269,16 +277,28 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
if (f.role == "libraries")
tmp = tmp.parentDir();
if (!tmp.isEmpty()
&& tmp.isDir()) { // f.role is libraryPath or frameworkPath
librarySeachPaths.append(tmp);
// Libraries often have their import libs in ../lib and the
// actual dll files in ../bin on windows. Qt is one example of that.
if (tmp.fileName() == "lib" && HostOsInfo::isWindowsHost()) {
const FilePath path = tmp.parentDir().pathAppended("bin");
if (!tmp.isEmpty() && tmp.isDir()) {
// f.role is libraryPath or frameworkPath
// On Linux, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and
// "/usr/local/lib" since these are usually in the standard search
// paths. There probably are more, but the naming schemes are arbitrary
// so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR").
if (!HostOsInfo::isLinuxHost()
|| !isChildOf(tmp,
{"/lib",
"/lib64",
"/usr/lib",
"/usr/lib64",
"/usr/local/lib"})) {
librarySeachPaths.append(tmp);
// Libraries often have their import libs in ../lib and the
// actual dll files in ../bin on windows. Qt is one example of that.
if (tmp.fileName() == "lib" && HostOsInfo::isWindowsHost()) {
const FilePath path = tmp.parentDir().pathAppended("bin");
if (path.isDir())
librarySeachPaths.append(path);
if (path.isDir())
librarySeachPaths.append(path);
}
}
}
}

View File

@@ -892,7 +892,9 @@ FileApiData FileApiParser::parseData(const QFileInfo &replyFileInfo, const QStri
}
auto it = std::find_if(codeModels.cbegin(), codeModels.cend(),
[cmakeBuildType](const Configuration& cfg) { return cfg.name == cmakeBuildType; });
[cmakeBuildType](const Configuration& cfg) {
return QString::compare(cfg.name, cmakeBuildType, Qt::CaseInsensitive) == 0;
});
if (it == codeModels.cend()) {
errorMessage = QString("No '%1' CMake configuration found!").arg(cmakeBuildType);
qWarning() << errorMessage;

View File

@@ -321,9 +321,10 @@ void FileApiReader::makeBackupConfiguration(bool store)
if (!store)
std::swap(cmakeCacheTxt, cmakeCacheTxtPrev);
if (!FileUtils::copyIfDifferent(cmakeCacheTxt, cmakeCacheTxtPrev))
Core::MessageManager::writeFlashing(tr("Failed to copy %1 to %2.")
.arg(cmakeCacheTxt.toString(), cmakeCacheTxtPrev.toString()));
if (cmakeCacheTxt.exists())
if (!FileUtils::copyIfDifferent(cmakeCacheTxt, cmakeCacheTxtPrev))
Core::MessageManager::writeFlashing(tr("Failed to copy %1 to %2.")
.arg(cmakeCacheTxt.toString(), cmakeCacheTxtPrev.toString()));
}

View File

@@ -57,7 +57,7 @@ IWelcomePage::~IWelcomePage()
g_welcomePages.removeOne(this);
}
static QPalette buttonPalette(bool isActive, bool isCursorInside, bool forText)
QPalette WelcomePageFrame::buttonPalette(bool isActive, bool isCursorInside, bool forText)
{
QPalette pal;
Theme *theme = Utils::creatorTheme();
@@ -174,8 +174,8 @@ bool WelcomePageButtonPrivate::isActive() const
void WelcomePageButtonPrivate::doUpdate(bool cursorInside)
{
const bool active = isActive();
q->setPalette(buttonPalette(active, cursorInside, false));
const QPalette lpal = buttonPalette(active, cursorInside, true);
q->setPalette(WelcomePageFrame::buttonPalette(active, cursorInside, false));
const QPalette lpal = WelcomePageFrame::buttonPalette(active, cursorInside, true);
m_label->setPalette(lpal);
if (m_icon)
m_icon->setPalette(lpal);

View File

@@ -68,6 +68,8 @@ public:
WelcomePageFrame(QWidget *parent);
void paintEvent(QPaintEvent *event) override;
static QPalette buttonPalette(bool isActive, bool isCursorInside, bool forText);
};
class CORE_EXPORT WelcomePageButton : public WelcomePageFrame

View File

@@ -171,6 +171,12 @@ ThemeChooser::~ThemeChooser()
delete d;
}
static QString defaultThemeId()
{
return Theme::systemUsesDarkMode() ? QString(Constants::DEFAULT_DARK_THEME)
: QString(Constants::DEFAULT_THEME);
}
void ThemeChooser::apply()
{
const int index = d->m_themeComboBox->currentIndex();
@@ -181,9 +187,7 @@ void ThemeChooser::apply()
const QString currentThemeId = ThemeEntry::themeSetting().toString();
if (currentThemeId != themeId) {
// save filename of selected theme in global config
settings->setValueWithDefault(Constants::SETTINGS_THEME,
themeId,
QString(Constants::DEFAULT_THEME));
settings->setValueWithDefault(Constants::SETTINGS_THEME, themeId, defaultThemeId());
RestartDialog restartDialog(ICore::dialogParent(),
tr("The theme change will take effect after restart."));
restartDialog.exec();
@@ -225,10 +229,8 @@ QList<ThemeEntry> ThemeEntry::availableThemes()
Id ThemeEntry::themeSetting()
{
auto defaultId = Theme::systemUsesDarkMode() ? Constants::DEFAULT_DARK_THEME
: Constants::DEFAULT_THEME;
const Id setting = Id::fromSetting(
ICore::settings()->value(Constants::SETTINGS_THEME, defaultId));
ICore::settings()->value(Constants::SETTINGS_THEME, defaultThemeId()));
const QList<ThemeEntry> themes = availableThemes();
if (themes.empty())

View File

@@ -57,15 +57,17 @@ static QFont sizedFont(int size, const QWidget *widget)
SearchBox::SearchBox(QWidget *parent)
: WelcomePageFrame(parent)
{
QPalette pal;
QPalette pal = buttonPalette(false, false, true);
pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundColor));
// for macOS dark mode
pal.setColor(QPalette::Text, themeColor(Theme::Welcome_TextColor));
setPalette(pal);
m_lineEdit = new FancyLineEdit;
m_lineEdit->setFiltering(true);
m_lineEdit->setFrame(false);
m_lineEdit->setFont(sizedFont(14, this));
m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
m_lineEdit->setPalette(pal);
auto box = new QHBoxLayout(this);
box->setContentsMargins(10, 3, 3, 3);
@@ -84,6 +86,7 @@ GridView::GridView(QWidget *parent)
setSelectionMode(QAbstractItemView::NoSelection);
setFrameShape(QFrame::NoFrame);
setGridStyle(Qt::NoPen);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QPalette pal;
pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundColor));

View File

@@ -421,6 +421,7 @@ static void addSearchResults(CppTools::Usages usages, SearchResult &search, cons
item.setFilePath(FilePath::fromString(usage.path));
item.setLineText(lineContent);
item.setMainRange(range);
item.setUseTextEditorFont(true);
search.addResult(item);
}
}

View File

@@ -636,6 +636,7 @@ static void displayResults(SearchResult *search, QFutureWatcher<CPlusPlus::Usage
item.setLineText(result.lineText);
item.setUserData(int(result.type));
item.setStyle(colorStyleForUsageType(result.type));
item.setUseTextEditorFont(true);
search->addResult(item);
if (parameters.prettySymbolName.isEmpty())
@@ -831,6 +832,7 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro &macro, const QStri
item.setFilePath(Utils::FilePath::fromString(macro.fileName()));
item.setLineText(line);
item.setMainRange(macro.line(), column, macro.nameToQString().length());
item.setUseTextEditorFont(true);
search->addResult(item);
}

View File

@@ -31,6 +31,7 @@
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
@@ -45,6 +46,8 @@ static Q_LOGGING_CATEGORY(log, "qtc.cpptools.semantichighlighter", QtWarningMsg)
namespace CppTools {
static Utils::Id parenSource() { return "CppTools"; }
static const QList<std::pair<HighlightingResult, QTextBlock>>
splitRawStringLiteral(const HighlightingResult &result, const QTextBlock &startBlock)
{
@@ -147,6 +150,13 @@ void SemanticHighlighter::run()
m_watcher->setFuture(m_highlightingRunner());
}
static Parentheses getClearedParentheses(const QTextBlock &block)
{
return Utils::filtered(TextDocumentLayout::parentheses(block), [](const Parenthesis &p) {
return p.source != parenSource();
});
}
void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
{
if (documentRevision() != m_revision)
@@ -169,6 +179,9 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
const HighlightingResult &result = m_watcher->future().resultAt(i);
if (result.kind != AngleBracketOpen && result.kind != AngleBracketClose
&& result.kind != TernaryIf && result.kind != TernaryElse) {
const QTextBlock block =
m_baseTextDocument->document()->findBlockByNumber(result.line - 1);
TextDocumentLayout::setParentheses(block, getClearedParentheses(block));
continue;
}
if (parentheses.first.isValid() && result.line - 1 > parentheses.first.blockNumber()) {
@@ -177,16 +190,20 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
}
if (!parentheses.first.isValid()) {
parentheses.first = m_baseTextDocument->document()->findBlockByNumber(result.line - 1);
parentheses.second = TextDocumentLayout::parentheses(parentheses.first);
parentheses.second = getClearedParentheses(parentheses.first);
}
Parenthesis paren;
if (result.kind == AngleBracketOpen)
parentheses.second << Parenthesis(Parenthesis::Opened, '<', result.column - 1);
paren = {Parenthesis::Opened, '<', result.column - 1};
else if (result.kind == AngleBracketClose)
parentheses.second << Parenthesis(Parenthesis::Closed, '>', result.column - 1);
paren = {Parenthesis::Closed, '>', result.column - 1};
else if (result.kind == TernaryIf)
parentheses.second << Parenthesis(Parenthesis::Opened, '?', result.column - 1);
paren = {Parenthesis::Opened, '?', result.column - 1};
else if (result.kind == TernaryElse)
parentheses.second << Parenthesis(Parenthesis::Closed, ':', result.column - 1);
paren = {Parenthesis::Closed, ':', result.column - 1};
QTC_ASSERT(paren.pos != -1, continue);
paren.source = parenSource();
parentheses.second << paren;
}
if (parentheses.first.isValid())
TextDocumentLayout::setParentheses(parentheses.first, parentheses.second);
@@ -202,6 +219,27 @@ void SemanticHighlighter::onHighlighterFinished()
clearExtraAdditionalFormatsUntilEnd(highlighter, m_watcher->future());
}
}
// Clear out previous "semantic parentheses".
QTextBlock firstResultBlock;
QTextBlock lastResultBlock;
if (m_watcher->future().resultCount() == 0) {
firstResultBlock = lastResultBlock = m_baseTextDocument->document()->lastBlock();
} else {
firstResultBlock = m_baseTextDocument->document()->findBlockByNumber(
m_watcher->resultAt(0).line - 1);
lastResultBlock = m_baseTextDocument->document()->findBlockByNumber(
m_watcher->future().resultAt(m_watcher->future().resultCount() - 1).line - 1);
}
for (QTextBlock currentBlock = m_baseTextDocument->document()->firstBlock();
currentBlock != firstResultBlock; currentBlock = currentBlock.next()) {
TextDocumentLayout::setParentheses(currentBlock, getClearedParentheses(currentBlock));
}
for (QTextBlock currentBlock = lastResultBlock.next(); currentBlock.isValid();
currentBlock = currentBlock.next()) {
TextDocumentLayout::setParentheses(currentBlock, getClearedParentheses(currentBlock));
}
m_watcher.reset();
}

View File

@@ -1,4 +1,5 @@
add_qtc_plugin(CtfVisualizer
CONDITION TARGET Tracing
DEPENDS Tracing Qt5::QuickWidgets
INCLUDES ${PROJECT_SOURCE_DIR}/src
PLUGIN_DEPENDS Core Debugger ProjectExplorer

View File

@@ -79,6 +79,7 @@ constexpr char executableKey[] = "executable";
constexpr char argumentsKey[] = "arguments";
constexpr char settingsGroupKey[] = "LanguageClient";
constexpr char clientsKey[] = "clients";
constexpr char typedClientsKey[] = "typedClients";
constexpr char mimeType[] = "application/language.client.setting";
namespace LanguageClient {
@@ -615,16 +616,21 @@ QList<BaseSettings *> LanguageClientSettings::fromSettings(QSettings *settingsIn
{
settingsIn->beginGroup(settingsGroupKey);
QList<BaseSettings *> result;
for (const QVariant& var : settingsIn->value(clientsKey).toList()) {
const QMap<QString, QVariant> &map = var.toMap();
Utils::Id typeId = Utils::Id::fromSetting(map.value(typeIdKey));
if (!typeId.isValid())
typeId = Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID;
if (BaseSettings *settings = generateSettings(typeId)) {
settings->fromMap(var.toMap());
result << settings;
for (auto varList :
{settingsIn->value(clientsKey).toList(), settingsIn->value(typedClientsKey).toList()}) {
for (const QVariant &var : varList) {
const QMap<QString, QVariant> &map = var.toMap();
Utils::Id typeId = Utils::Id::fromSetting(map.value(typeIdKey));
if (!typeId.isValid())
typeId = Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID;
if (BaseSettings *settings = generateSettings(typeId)) {
settings->fromMap(map);
result << settings;
}
}
}
settingsIn->endGroup();
return result;
}
@@ -659,10 +665,16 @@ void LanguageClientSettings::toSettings(QSettings *settings,
const QList<BaseSettings *> &languageClientSettings)
{
settings->beginGroup(settingsGroupKey);
settings->setValue(clientsKey, Utils::transform(languageClientSettings,
[](const BaseSettings *setting){
return QVariant(setting->toMap());
}));
auto transform = [](const QList<BaseSettings *> &settings) {
return Utils::transform(settings, [](const BaseSettings *setting) {
return QVariant(setting->toMap());
});
};
auto isStdioSetting = Utils::equal(&BaseSettings::m_settingsTypeId,
Utils::Id(Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID));
auto [stdioSettings, typedSettings] = Utils::partition(languageClientSettings, isStdioSetting);
settings->setValue(clientsKey, transform(stdioSettings));
settings->setValue(typedClientsKey, transform(typedSettings));
settings->endGroup();
}

View File

@@ -1,4 +1,5 @@
add_qtc_plugin(PerfProfiler
CONDITION TARGET Tracing
DEPENDS Tracing Qt5::QuickWidgets
PLUGIN_DEPENDS Core Debugger ProjectExplorer QtSupport
SOURCES

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

View File

@@ -24,7 +24,6 @@
<file>images/debugger_overlay_small@2x.png</file>
<file>images/analyzer_overlay_small.png</file>
<file>images/analyzer_overlay_small@2x.png</file>
<file>images/session.png</file>
<file>images/BuildSettings.png</file>
<file>images/CodeStyleSettings.png</file>
<file>images/RunSettings.png</file>
@@ -67,6 +66,8 @@
<file>images/fileoverlay_product@2x.png</file>
<file>images/fileoverlay_group.png</file>
<file>images/fileoverlay_group@2x.png</file>
<file>images/fileoverlay_modules.png</file>
<file>images/fileoverlay_modules@2x.png</file>
<file>images/fileoverlay_ui.png</file>
<file>images/fileoverlay_ui@2x.png</file>
<file>images/fileoverlay_scxml.png</file>

View File

@@ -215,6 +215,7 @@ const char PROJECTTREE_ID[] = "Projects";
const char FILEOVERLAY_QT[]=":/projectexplorer/images/fileoverlay_qt.png";
const char FILEOVERLAY_GROUP[] = ":/projectexplorer/images/fileoverlay_group.png";
const char FILEOVERLAY_PRODUCT[] = ":/projectexplorer/images/fileoverlay_product.png";
const char FILEOVERLAY_MODULES[] = ":/projectexplorer/images/fileoverlay_modules.png";
const char FILEOVERLAY_QML[]=":/projectexplorer/images/fileoverlay_qml.png";
const char FILEOVERLAY_UI[]=":/projectexplorer/images/fileoverlay_ui.png";
const char FILEOVERLAY_QRC[]=":/projectexplorer/images/fileoverlay_qrc.png";

View File

@@ -6,7 +6,7 @@ endif()
add_qtc_plugin(QmlDesigner
DEPENDS
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
Qt5::QuickWidgets Qt5::CorePrivate Sqlite
Qt5::QuickWidgets Qt5::CorePrivate Sqlite Threads::Threads
DEFINES
DESIGNER_CORE_LIBRARY
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"

View File

@@ -100,7 +100,9 @@ QToolBar *CurveEditor::createToolBar(CurveEditorModel *model)
auto setLinearInterpolation = [this]() {
m_view->setInterpolation(Keyframe::Interpolation::Linear);
};
auto setStepInterpolation = [this]() { m_view->setInterpolation(Keyframe::Interpolation::Step); };
auto setStepInterpolation = [this]() {
m_view->setInterpolation(Keyframe::Interpolation::Step);
};
auto setSplineInterpolation = [this]() {
m_view->setInterpolation(Keyframe::Interpolation::Bezier);
};

View File

@@ -338,7 +338,8 @@ void CurveEditorView::commitKeyframes(TreeItem *item)
group.setValue(QVariant(pos.y()), pos.x());
if (previous.isValid()) {
if (frame.interpolation() == Keyframe::Interpolation::Bezier) {
if (frame.interpolation() == Keyframe::Interpolation::Bezier ||
frame.interpolation() == Keyframe::Interpolation::Step ) {
CurveSegment segment(previous, frame);
if (segment.isValid())
attachEasingCurve(group, pos.x(), segment.easingCurve());
@@ -346,8 +347,6 @@ void CurveEditorView::commitKeyframes(TreeItem *item)
QVariant data = frame.data();
if (data.type() == static_cast<int>(QMetaType::QEasingCurve))
attachEasingCurve(group, pos.x(), data.value<QEasingCurve>());
} else if (frame.interpolation() == Keyframe::Interpolation::Step) {
// Warning: Keyframe::Interpolation::Step not yet implemented
}
}

View File

@@ -294,6 +294,13 @@ void CurveSegment::extend(QPainterPath &path) const
QEasingCurve CurveSegment::easingCurve() const
{
if (interpolation() == Keyframe::Interpolation::Step) {
QEasingCurve curve;
curve.addCubicBezierSegment(QPointF(0.1, 0.0), QPointF(0.9, 0.0), QPointF(1.0, 0.0));
curve.addCubicBezierSegment(QPointF(1.0, 0.1), QPointF(1.0, 0.9), QPointF(1.0, 1.0));
return curve;
}
auto mapPosition = [this](const QPointF &position) {
QPointF min = m_left.position();
QPointF max = m_right.position();

View File

@@ -370,7 +370,8 @@ void DesignDocument::close()
void DesignDocument::updateSubcomponentManager()
{
Q_ASSERT(m_subComponentManager);
m_subComponentManager->update(QUrl::fromLocalFile(fileName().toString()), currentModel()->imports());
m_subComponentManager->update(QUrl::fromLocalFile(fileName().toString()),
currentModel()->imports() + currentModel()->possibleImports());
}
void DesignDocument::deleteSelected()

View File

@@ -40,6 +40,7 @@ ItemLibraryAddImportModel::ItemLibraryAddImportModel(QObject *parent)
// add role names
m_roleNames.insert(Qt::UserRole + 1, "importUrl");
m_roleNames.insert(Qt::UserRole + 2, "importVisible");
m_roleNames.insert(Qt::UserRole + 3, "isSeparator");
}
ItemLibraryAddImportModel::~ItemLibraryAddImportModel()
@@ -63,7 +64,10 @@ QVariant ItemLibraryAddImportModel::data(const QModelIndex &index, int role) con
return importUrl;
if (m_roleNames[role] == "importVisible")
return m_searchText.isEmpty() || m_importFilterList.contains(importUrl);
return m_searchText.isEmpty() || importUrl.isEmpty() || m_importFilterList.contains(importUrl);
if (m_roleNames[role] == "isSeparator")
return importUrl.isEmpty();
qWarning() << Q_FUNC_INFO << "invalid role requested";
@@ -83,9 +87,9 @@ void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports)
const DesignerMcuManager &mcuManager = DesignerMcuManager::instance();
const bool isQtForMCUs = mcuManager.isMCUProject();
QList<Import> filteredImports;
const QStringList mcuAllowedList = mcuManager.allowedImports();
const QStringList mcuBannedList = mcuManager.bannedImports();
if (isQtForMCUs) {
const QStringList mcuAllowedList = mcuManager.allowedImports();
const QStringList mcuBannedList = mcuManager.bannedImports();
filteredImports = Utils::filtered(possibleImports,
[&](const Import &import) {
return (mcuAllowedList.contains(import.url())
@@ -96,7 +100,7 @@ void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports)
filteredImports = possibleImports;
}
Utils::sort(filteredImports, [](const Import &firstImport, const Import &secondImport) {
Utils::sort(filteredImports, [this](const Import &firstImport, const Import &secondImport) {
if (firstImport.url() == secondImport.url())
return firstImport.toString() < secondImport.toString();
@@ -106,6 +110,10 @@ void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports)
if (secondImport.url() == "QtQuick")
return false;
const bool firstPriority = m_priorityImports.contains(firstImport.url());
if (firstPriority != m_priorityImports.contains(secondImport.url()))
return firstPriority;
if (firstImport.isLibraryImport() && secondImport.isFileImport())
return false;
@@ -122,9 +130,15 @@ void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports)
});
// create import sections
bool previousIsPriority = false;
for (const Import &import : std::as_const(filteredImports)) {
if (import.isLibraryImport())
if (import.isLibraryImport()) {
bool currentIsPriority = m_priorityImports.contains(import.url());
if (previousIsPriority && !currentIsPriority)
m_importList.append(Import::empty()); // empty import acts as a separator
m_importList.append(import);
previousIsPriority = currentIsPriority;
}
}
endResetModel();
@@ -153,4 +167,9 @@ Import ItemLibraryAddImportModel::getImportAt(int index) const
return m_importList.at(index);
}
void ItemLibraryAddImportModel::setPriorityImports(const QSet<QString> &priorityImports)
{
m_priorityImports = priorityImports;
}
} // namespace QmlDesigner

View File

@@ -50,11 +50,14 @@ public:
void setSearchText(const QString &searchText);
Import getImportAt(int index) const;
void setPriorityImports(const QSet<QString> &priorityImports);
private:
QString m_searchText;
QList<Import> m_importList;
QSet<QString> m_importFilterList;
QHash<int, QByteArray> m_roleNames;
QSet<QString> m_priorityImports;
};
} // namespace QmlDesigner

View File

@@ -664,7 +664,12 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
timer->callOnTimeout([this, timer, progressTitle, model, doc]() {
if (!isCancelled()) {
notifyProgress(++counter * 5, progressTitle);
if (counter == 10) {
if (counter < 10) {
// Do not proceed while application isn't active as the filesystem
// watcher qmljs uses won't trigger unless application is active
if (QApplication::applicationState() != Qt::ApplicationActive)
--counter;
} else if (counter == 10) {
model->rewriterView()->textModifier()->replace(0, 0, {});
} else if (counter == 19) {
try {

View File

@@ -127,6 +127,9 @@ void ItemLibraryCategoriesModel::sortCategorySections()
};
std::sort(m_categoryList.begin(), m_categoryList.end(), categorySort);
for (const auto &category : qAsConst(m_categoryList))
category->sortItems();
}
void ItemLibraryCategoriesModel::resetModel()

View File

@@ -70,6 +70,8 @@ bool ItemLibraryCategory::updateItemVisibility(const QString &searchText, bool *
bool itemVisible = item->itemName().toLower().contains(searchText)
|| item->typeName().toLower().contains(searchText);
if (searchText.isEmpty() && !item->isUsable())
itemVisible = false;
bool itemChanged = item->setVisible(itemVisible);
*changed |= itemChanged;

View File

@@ -28,18 +28,22 @@
namespace QmlDesigner {
ItemLibraryImport::ItemLibraryImport(const Import &import, QObject *parent, bool isUserSection)
ItemLibraryImport::ItemLibraryImport(const Import &import, QObject *parent, SectionType sectionType)
: QObject(parent),
m_import(import),
m_isUserSection(isUserSection)
m_sectionType(sectionType)
{
updateRemovable();
}
QString ItemLibraryImport::importName() const
{
if (m_isUserSection)
if (m_sectionType == SectionType::User)
return userComponentsTitle();
if (m_sectionType == SectionType::Unimported)
return unimportedComponentsTitle();
if (importUrl() == "QtQuick")
return tr("Default Components");
@@ -48,9 +52,12 @@ QString ItemLibraryImport::importName() const
QString ItemLibraryImport::importUrl() const
{
if (m_isUserSection)
if (m_sectionType == SectionType::User)
return userComponentsTitle();
if (m_sectionType == SectionType::Unimported)
return unimportedComponentsTitle();
return m_import.url();
}
@@ -61,11 +68,14 @@ bool ItemLibraryImport::importExpanded() const
QString ItemLibraryImport::sortingName() const
{
if (m_isUserSection) // user components always come first
return "_";
if (m_sectionType == SectionType::User)
return "_"; // user components always come first
if (m_sectionType == SectionType::Unimported)
return "zzzzzz"; // Unimported components always come last
if (!hasCategories()) // imports with no categories are at the bottom of the list
return "zzzzz" + importName();
return "zzzzz_" + importName();
return importName();
}
@@ -113,6 +123,7 @@ bool ItemLibraryImport::setVisible(bool isVisible)
{
if (isVisible != m_isVisible) {
m_isVisible = isVisible;
emit importVisibleChanged();
return true;
}
@@ -126,7 +137,11 @@ bool ItemLibraryImport::importVisible() const
void ItemLibraryImport::setImportUsed(bool importUsed)
{
m_importUsed = importUsed;
if (importUsed != m_importUsed) {
m_importUsed = importUsed;
updateRemovable();
emit importUsedChanged();
}
}
bool ItemLibraryImport::importUsed() const
@@ -134,6 +149,11 @@ bool ItemLibraryImport::importUsed() const
return m_importUsed;
}
bool ItemLibraryImport::importRemovable() const
{
return m_importRemovable;
}
bool ItemLibraryImport::hasCategories() const
{
return m_categoryModel.rowCount() > 0;
@@ -146,7 +166,10 @@ void ItemLibraryImport::sortCategorySections()
void ItemLibraryImport::setImportExpanded(bool expanded)
{
m_importExpanded = expanded;
if (expanded != m_importExpanded) {
m_importExpanded = expanded;
emit importExpandChanged();
}
}
ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &categoryName) const
@@ -159,15 +182,30 @@ ItemLibraryCategory *ItemLibraryImport::getCategorySection(const QString &catego
return nullptr;
}
bool ItemLibraryImport::isUserSection() const
{
return m_isUserSection;
}
// static
QString ItemLibraryImport::userComponentsTitle()
{
return tr("My Components");
}
QString ItemLibraryImport::unimportedComponentsTitle()
{
return tr("All Other Components");
}
ItemLibraryImport::SectionType ItemLibraryImport::sectionType() const
{
return m_sectionType;
}
void ItemLibraryImport::updateRemovable()
{
bool importRemovable = !m_importUsed && m_sectionType == SectionType::Default
&& m_import.url() != "QtQuick";
if (importRemovable != m_importRemovable) {
m_importRemovable = importRemovable;
emit importRemovableChanged();
}
}
} // namespace QmlDesigner

View File

@@ -41,10 +41,18 @@ class ItemLibraryImport : public QObject
Q_PROPERTY(bool importVisible READ importVisible NOTIFY importVisibleChanged FINAL)
Q_PROPERTY(bool importUsed READ importUsed NOTIFY importUsedChanged FINAL)
Q_PROPERTY(bool importExpanded READ importExpanded WRITE setImportExpanded NOTIFY importExpandChanged FINAL)
Q_PROPERTY(bool importRemovable READ importRemovable NOTIFY importRemovableChanged FINAL)
Q_PROPERTY(QObject *categoryModel READ categoryModel NOTIFY categoryModelChanged FINAL)
public:
ItemLibraryImport(const Import &import, QObject *parent = nullptr, bool isUserSection = false);
enum class SectionType {
Default,
User,
Unimported
};
ItemLibraryImport(const Import &import, QObject *parent = nullptr,
SectionType sectionType = SectionType::Default);
QString importName() const;
QString importUrl() const;
@@ -53,6 +61,7 @@ public:
Import importEntry() const;
bool importVisible() const;
bool importUsed() const;
bool importRemovable() const;
bool hasCategories() const;
ItemLibraryCategory *getCategorySection(const QString &categoryName) const;
@@ -66,21 +75,26 @@ public:
void expandCategories(bool expand = true);
static QString userComponentsTitle();
static QString unimportedComponentsTitle();
bool isUserSection() const;
SectionType sectionType() const;
signals:
void categoryModelChanged();
void importVisibleChanged();
void importUsedChanged();
void importExpandChanged();
void importRemovableChanged();
private:
void updateRemovable();
Import m_import;
bool m_importExpanded = true;
bool m_isVisible = true;
bool m_importUsed = false;
bool m_isUserSection = false; // user components import section
bool m_importRemovable = false;
SectionType m_sectionType = SectionType::Default;
ItemLibraryCategoriesModel m_categoryModel;
};

View File

@@ -27,9 +27,10 @@
namespace QmlDesigner {
ItemLibraryItem::ItemLibraryItem(const ItemLibraryEntry &itemLibraryEntry, QObject *parent)
: QObject(parent),
m_itemLibraryEntry(itemLibraryEntry)
ItemLibraryItem::ItemLibraryItem(const ItemLibraryEntry &itemLibraryEntry, bool isUsable, QObject *parent)
: QObject(parent)
, m_itemLibraryEntry(itemLibraryEntry)
, m_isUsable(isUsable)
{
}
@@ -61,6 +62,11 @@ QString ItemLibraryItem::componentPath() const
return m_itemLibraryEntry.customComponentSource();
}
QString ItemLibraryItem::requiredImport() const
{
return m_itemLibraryEntry.requiredImport();
}
bool ItemLibraryItem::setVisible(bool isVisible)
{
if (isVisible != m_isVisible) {
@@ -77,6 +83,11 @@ bool ItemLibraryItem::isVisible() const
return m_isVisible;
}
bool ItemLibraryItem::isUsable() const
{
return m_isUsable;
}
QVariant ItemLibraryItem::itemLibraryEntry() const
{
return QVariant::fromValue(m_itemLibraryEntry);

View File

@@ -43,18 +43,22 @@ class ItemLibraryItem: public QObject
Q_PROPERTY(QString itemLibraryIconPath READ itemLibraryIconPath FINAL)
Q_PROPERTY(bool itemVisible READ isVisible NOTIFY visibilityChanged FINAL)
Q_PROPERTY(QString componentPath READ componentPath FINAL)
Q_PROPERTY(bool itemUsable READ isUsable FINAL)
Q_PROPERTY(QString itemRequiredImport READ requiredImport FINAL)
public:
ItemLibraryItem(const ItemLibraryEntry &itemLibraryEntry, QObject *parent);
ItemLibraryItem(const ItemLibraryEntry &itemLibraryEntry, bool isImported, QObject *parent);
~ItemLibraryItem() override;
QString itemName() const;
QString typeName() const;
QString itemLibraryIconPath() const;
QString componentPath() const;
QString requiredImport() const;
bool setVisible(bool isVisible);
bool isVisible() const;
bool isUsable() const;
QVariant itemLibraryEntry() const;
@@ -64,6 +68,7 @@ signals:
private:
ItemLibraryEntry m_itemLibraryEntry;
bool m_isVisible = true;
bool m_isUsable = false;
};
} // namespace QmlDesigner

View File

@@ -70,7 +70,7 @@ void ItemLibraryItemsModel::addItem(ItemLibraryItem *element)
{
m_itemList.append(element);
element->setVisible(true);
element->setVisible(element->isUsable());
}
const QList<QPointer<ItemLibraryItem>> &ItemLibraryItemsModel::items() const

View File

@@ -167,7 +167,7 @@ void ItemLibraryModel::setSearchText(const QString &searchText)
}
}
Import entryToImport(const ItemLibraryEntry &entry)
Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry)
{
if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
return Import::createFileImport(entry.requiredImport());
@@ -177,6 +177,28 @@ Import entryToImport(const ItemLibraryEntry &entry)
}
// Returns true if first import version is higher or equal to second import version
static bool compareVersions(const QString &version1, const QString &version2)
{
if (version2.isEmpty() || version1 == version2)
return true;
const QStringList version1List = version1.split(QLatin1Char('.'));
const QStringList version2List = version2.split(QLatin1Char('.'));
if (version1List.count() == 2 && version2List.count() == 2) {
int major1 = version1List.constFirst().toInt();
int major2 = version2List.constFirst().toInt();
if (major1 > major2) {
return true;
} else if (major1 == major2) {
int minor1 = version1List.constLast().toInt();
int minor2 = version2List.constLast().toInt();
if (minor1 >= minor2)
return true;
}
}
return false;
}
void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
{
if (!model)
@@ -190,60 +212,91 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
QString projectName = project ? project->displayName() : "";
// create import sections
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
if (import.isLibraryImport() && import.url() != projectName) {
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this);
m_importList.append(itemLibImport);
itemLibImport->setImportExpanded(loadExpandedState(import.url()));
bool addNew = true;
ItemLibraryImport *oldImport = importHash.value(import.url());
if (oldImport && oldImport->importEntry().url() == import.url()) {
// Retain the higher version if multiples exist
if (compareVersions(oldImport->importEntry().version(), import.version()))
addNew = false;
else
delete oldImport;
}
if (addNew) {
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this);
importHash.insert(import.url(), itemLibImport);
}
}
}
for (const auto itemLibImport : qAsConst(importHash)) {
m_importList.append(itemLibImport);
itemLibImport->setImportExpanded(loadExpandedState(itemLibImport->importEntry().url()));
}
const QList<ItemLibraryEntry> itemLibEntries = itemLibraryInfo->entries();
for (const ItemLibraryEntry &entry : itemLibEntries) {
NodeMetaInfo metaInfo = model->metaInfo(entry.typeName());
bool valid = metaInfo.isValid() && metaInfo.majorVersion() == entry.majorVersion();
bool isItem = valid && metaInfo.isSubclassOf("QtQuick.Item");
bool forceVisiblity = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary();
bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary();
if (m_flowMode && metaInfo.isValid()) {
isItem = metaInfo.isSubclassOf("FlowView.FlowItem")
|| metaInfo.isSubclassOf("FlowView.FlowWildcard")
|| metaInfo.isSubclassOf("FlowView.FlowDecision");
forceVisiblity = isItem;
forceVisibility = isItem;
}
bool blocked = false;
const DesignerMcuManager &mcuManager = DesignerMcuManager::instance();
if (mcuManager.isMCUProject()) {
const QSet<QString> blockTypes = mcuManager.bannedItems();
if (blockTypes.contains(QString::fromUtf8(entry.typeName())))
valid = false;
blocked = true;
}
if (valid && (isItem || forceVisiblity) // We can change if the navigator does support pure QObjects
&& (entry.requiredImport().isEmpty()
|| model->hasImport(entryToImport(entry), true, true))) {
Import import = entryToImport(entry);
bool hasImport = model->hasImport(import, true, true);
bool isImportPossible = false;
if (!hasImport)
isImportPossible = model->isImportPossible(import, true, true);
bool isUsable = (valid && (isItem || forceVisibility))
&& (entry.requiredImport().isEmpty() || hasImport);
if (!blocked && (isUsable || isImportPossible)) {
ItemLibraryImport *importSection = nullptr;
QString catName = entry.category();
if (catName == ItemLibraryImport::userComponentsTitle()) {
// create an import section for user components
importSection = importByUrl(ItemLibraryImport::userComponentsTitle());
if (isUsable) {
if (catName == ItemLibraryImport::userComponentsTitle()) {
// create an import section for user components
importSection = importByUrl(ItemLibraryImport::userComponentsTitle());
if (!importSection) {
importSection = new ItemLibraryImport(
{}, this, ItemLibraryImport::SectionType::User);
m_importList.append(importSection);
importSection->setImportExpanded(loadExpandedState(catName));
}
} else {
if (catName.startsWith("Qt Quick - "))
catName = catName.mid(11); // remove "Qt Quick - "
importSection = importByUrl(entry.requiredImport());
}
} else {
catName = ItemLibraryImport::unimportedComponentsTitle();
importSection = importByUrl(catName);
if (!importSection) {
importSection = new ItemLibraryImport({}, this, true);
importSection = new ItemLibraryImport(
{}, this, ItemLibraryImport::SectionType::Unimported);
m_importList.append(importSection);
importSection->setImportExpanded(loadExpandedState(catName));
}
} else {
if (catName.startsWith("Qt Quick - "))
catName = catName.mid(11); // remove "Qt Quick - "
importSection = importByUrl(entry.requiredImport());
}
if (!importSection) { // should not happen, but just in case
if (!importSection) {
qWarning() << __FUNCTION__ << "No import section found! skipping entry: " << entry.name();
continue;
}
@@ -253,12 +306,12 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
if (!categorySection) {
categorySection = new ItemLibraryCategory(catName, importSection);
importSection->addCategory(categorySection);
if (!importSection->isUserSection())
if (importSection->sectionType() == ItemLibraryImport::SectionType::Default)
categorySection->setExpanded(loadExpandedState(categorySection->categoryName()));
}
// create item
auto item = new ItemLibraryItem(entry, categorySection);
auto item = new ItemLibraryItem(entry, isUsable, categorySection);
categorySection->addItem(item);
}
}
@@ -300,7 +353,9 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const
if (itemLibraryImport->importUrl() == importUrl
|| (importUrl.isEmpty() && itemLibraryImport->importUrl() == "QtQuick")
|| (importUrl == ItemLibraryImport::userComponentsTitle()
&& itemLibraryImport->isUserSection())) {
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::User)
|| (importUrl == ItemLibraryImport::unimportedComponentsTitle()
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::Unimported)) {
return itemLibraryImport;
}
}
@@ -324,9 +379,11 @@ void ItemLibraryModel::updateVisibility(bool *changed)
for (ItemLibraryImport *import : std::as_const(m_importList)) {
bool categoryChanged = false;
bool hasVisibleItems = import->updateCategoryVisibility(m_searchText, &categoryChanged);
*changed |= categoryChanged;
if (import->sectionType() == ItemLibraryImport::SectionType::Unimported)
*changed |= import->setVisible(!m_searchText.isEmpty());
// expand import if it has an item matching search criteria
if (hasVisibleItems && !import->importExpanded())
import->setImportExpanded();

View File

@@ -69,6 +69,8 @@ public:
Q_INVOKABLE void expandAll();
Q_INVOKABLE void collapseAll();
Import entryToImport(const ItemLibraryEntry &entry);
private:
void updateVisibility(bool *changed);
void addRoleNames();

View File

@@ -86,15 +86,35 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
QMetaObject::invokeMethod(m_itemViewQuickWidget->rootObject(), "closeContextMenu");
} else if (event->type() == QMouseEvent::MouseMove) {
if (m_itemToDrag.isValid()) {
ItemLibraryEntry entry = m_itemToDrag.value<ItemLibraryEntry>();
auto drag = new QDrag(this);
drag->setPixmap(Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath()));
drag->setMimeData(m_itemLibraryModel->getMimeData(entry));
drag->exec();
drag->deleteLater();
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) {
ItemLibraryEntry entry = m_itemToDrag.value<ItemLibraryEntry>();
// For drag to be handled correctly, we must have the component properly imported
// beforehand, so we import the module immediately when the drag starts
if (!entry.requiredImport().isEmpty()) {
Import import = Import::createLibraryImport(entry.requiredImport());
if (!m_model->hasImport(import, true, true)) {
const QList<Import> possImports = m_model->possibleImports();
for (const auto &possImport : possImports) {
if (possImport.url() == import.url()) {
m_model->changeImports({possImport}, {});
break;
}
}
}
}
m_itemToDrag = {};
auto drag = new QDrag(this);
drag->setPixmap(Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath()));
drag->setMimeData(m_itemLibraryModel->getMimeData(entry));
drag->exec();
drag->deleteLater();
m_itemToDrag = {};
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_itemToDrag = {};
}
return QObject::eventFilter(obj, event);
@@ -229,11 +249,16 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo)
if (m_itemLibraryInfo) {
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::priorityImportsChanged,
this, &ItemLibraryWidget::handlePriorityImportsChanged);
}
m_itemLibraryInfo = itemLibraryInfo;
if (itemLibraryInfo) {
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::priorityImportsChanged,
this, &ItemLibraryWidget::handlePriorityImportsChanged);
m_itemLibraryAddImportModel->setPriorityImports(m_itemLibraryInfo->priorityImports());
}
delayedUpdateModel();
}
@@ -344,6 +369,7 @@ void ItemLibraryWidget::updateModel()
void ItemLibraryWidget::updatePossibleImports(const QList<Import> &possibleImports)
{
m_itemLibraryAddImportModel->update(possibleImports);
delayedUpdateModel();
}
void ItemLibraryWidget::updateUsedImports(const QList<Import> &usedImports)
@@ -365,6 +391,14 @@ void ItemLibraryWidget::updateSearch()
}
}
void ItemLibraryWidget::handlePriorityImportsChanged()
{
if (!m_itemLibraryInfo.isNull()) {
m_itemLibraryAddImportModel->setPriorityImports(m_itemLibraryInfo->priorityImports());
m_itemLibraryAddImportModel->update(m_model->possibleImports());
}
}
void ItemLibraryWidget::setResourcePath(const QString &resourcePath)
{
if (m_resourcesView->model() == m_resourcesFileSystemModel.data()) {
@@ -374,12 +408,13 @@ void ItemLibraryWidget::setResourcePath(const QString &resourcePath)
updateSearch();
}
void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry)
void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos)
{
// Actual drag is created after mouse has moved to avoid a QDrag bug that causes drag to stay
// active (and blocks mouse release) if mouse is released at the same spot of the drag start.
// This doesn't completely eliminate the bug but makes it significantly harder to produce.
m_itemToDrag = itemLibEntry;
m_dragStartPoint = mousePos.toPoint();
}
void ItemLibraryWidget::setFlowMode(bool b)
@@ -396,6 +431,15 @@ void ItemLibraryWidget::removeImport(const QString &importUrl)
m_model->changeImports({}, {importSection->importEntry()});
}
void ItemLibraryWidget::addImportForItem(const QVariant &entry)
{
QTC_ASSERT(m_itemLibraryModel, return);
QTC_ASSERT(m_model, return);
Import import = m_itemLibraryModel->entryToImport(entry.value<ItemLibraryEntry>());
m_model->changeImports({import}, {});
}
void ItemLibraryWidget::addResources(const QStringList &files)
{
auto document = QmlDesignerPlugin::instance()->currentDesignDocument();

View File

@@ -39,6 +39,7 @@
#include <QQuickWidget>
#include <QQmlPropertyMap>
#include <QTimer>
#include <QPointF>
#include <memory>
@@ -87,8 +88,9 @@ public:
void setModel(Model *model);
void setFlowMode(bool b);
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry);
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos);
Q_INVOKABLE void removeImport(const QString &importUrl);
Q_INVOKABLE void addImportForItem(const QVariant &entry);
signals:
void itemActivated(const QString& itemName);
@@ -102,6 +104,7 @@ private:
void addResources(const QStringList &files);
void importDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files);
void updateSearch();
void handlePriorityImportsChanged();
QTimer m_compressionTimer;
QSize m_itemIconSize;
@@ -126,6 +129,7 @@ private:
QVariant m_itemToDrag;
bool m_updateRetry = false;
QString m_filterText;
QPoint m_dragStartPoint;
private slots:
void handleTabChanged(int index);

View File

@@ -57,9 +57,11 @@ Column {
delegate: Rectangle {
width: listView.width
height: 25
color: mouseArea.containsMouse ? Qt.lighter(Theme.qmlDesignerButtonColor(), 1.3)
: Theme.qmlDesignerButtonColor()
height: isSeparator ? 4 : 25
color: isSeparator ? Theme.color(Theme.BackgroundColorNormal)
: mouseArea.containsMouse
? Qt.lighter(Theme.qmlDesignerButtonColor(), 1.3)
: Theme.qmlDesignerButtonColor()
visible: importVisible
Text {
@@ -77,6 +79,7 @@ Column {
anchors.fill: parent
hoverEnabled: true
onClicked: addImport(index)
enabled: !isSeparator
}
}
}

View File

@@ -24,23 +24,25 @@
****************************************************************************/
#include "richtexteditorproxy.h"
#include "richtexteditor.h"
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
#include <coreplugin/icore.h>
#include <QDialog>
#include <QDialogButtonBox>
#include <QGridLayout>
#include "richtexteditor.h"
namespace QmlDesigner {
RichTextEditorProxy::RichTextEditorProxy(QObject *parent)
: QObject(parent)
, m_dialog(new QDialog{})
, m_dialog(new QDialog(Core::ICore::dialogParent()))
, m_widget(new RichTextEditor{})
{
m_dialog->setModal(true);
QGridLayout *layout = new QGridLayout{};
layout->addWidget(m_widget);

View File

@@ -110,7 +110,7 @@ void TransitionEditorView::nodeReparented(const ModelNode &node,
const ModelNode parent = newPropertyParent.parentModelNode();
qDebug() << Q_FUNC_INFO << parent;
// qDebug() << Q_FUNC_INFO << parent;
if (parent.isValid() && parent.metaInfo().isValid()
&& parent.metaInfo().isSubclassOf("QtQuick.Transition")) {
asyncUpdate(parent);

View File

@@ -29,6 +29,7 @@
#include "propertycontainer.h"
#include <QPointer>
#include <QSet>
#include <memory>
namespace QmlDesigner {
@@ -108,11 +109,14 @@ public:
void clearEntries();
QStringList blacklistImports() const;
QSet<QString> priorityImports() const;
void addBlacklistImports(const QStringList &list);
void addPriorityImports(const QSet<QString> &set);
signals:
void entriesChanged();
void priorityImportsChanged();
private: // functions
ItemLibraryInfo(QObject *parent = nullptr);
@@ -123,6 +127,7 @@ private: // variables
QPointer<ItemLibraryInfo> m_baseInfo;
QStringList m_blacklistImports;
QSet<QString> m_priorityImports;
};
} // namespace QmlDesigner

View File

@@ -344,11 +344,27 @@ QStringList ItemLibraryInfo::blacklistImports() const
return list;
}
QSet<QString> ItemLibraryInfo::priorityImports() const
{
QSet<QString> set = m_priorityImports;
if (m_baseInfo)
set.unite(m_baseInfo->m_priorityImports);
return set;
}
void ItemLibraryInfo::addBlacklistImports(const QStringList &list)
{
m_blacklistImports.append(list);
}
void ItemLibraryInfo::addPriorityImports(const QSet<QString> &set)
{
if (!set.isEmpty()) {
m_priorityImports.unite(set);
emit priorityImportsChanged();
}
}
void ItemLibraryInfo::setBaseInfo(ItemLibraryInfo *baseInfo)
{
m_baseInfo = baseInfo;

View File

@@ -26,6 +26,8 @@
#include "metainforeader.h"
#include "metainfo.h"
#include <utils/algorithm.h>
#include <QString>
#include <QFileInfo>
#include <QDebug>
@@ -230,8 +232,10 @@ void MetaInfoReader::readImportsProperty(const QString &name, const QVariant &va
if (name == "blacklistImports" && !values.isEmpty()) {
m_metaInfo.itemLibraryInfo()->addBlacklistImports(values);
} else if (name == "showTagsForImports" && !values.isEmpty()) {
// Flow tags removed, but keeping this for now to avoid errors parsing old metadata files
} else if ((name == "priorityImports" || name == "showTagsForImports") && !values.isEmpty()) {
// Flow tags are no longer shown, but the old property is still supported for prioritizing
// imports to keep compatibility with old metainfo files.
m_metaInfo.itemLibraryInfo()->addPriorityImports(Utils::toSet(values));
} else {
addError(tr("Unknown property for Imports %1").arg(name), currentSourceLocation());
setParserState(Error);

View File

@@ -45,8 +45,6 @@
#include <transitioneditor/transitioneditorview.h>
#include <pathtool/pathtool.h>
#include <app/app_version.h>
#include <qmljseditor/qmljseditor.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljseditor/qmljseditordocument.h>
@@ -62,6 +60,7 @@
#include <coreplugin/messagebox.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -71,6 +70,7 @@
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/algorithm.h>
#include <QAction>
#include <QTimer>
@@ -232,12 +232,13 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
bool QmlDesignerPlugin::delayedInitialize()
{
// adding default path to item library plugins
const QString pluginPath = Utils::HostOsInfo::isMacHost()
? QString(QCoreApplication::applicationDirPath() + "/../PlugIns/QmlDesigner")
: QString(QCoreApplication::applicationDirPath() + "/../"
+ QLatin1String(IDE_LIBRARY_BASENAME) + "/" + Core::Constants::IDE_ID
+ "/plugins/qmldesigner");
MetaInfo::setPluginPaths(QStringList(pluginPath));
const QString postfix = Utils::HostOsInfo::isMacHost() ? QString("/QmlDesigner")
: QString("/qmldesigner");
const QStringList pluginPaths =
Utils::transform(ExtensionSystem::PluginManager::pluginPaths(), [postfix](const QString &p) {
return QString(p + postfix);
});
MetaInfo::setPluginPaths(pluginPaths);
d->settings.fromSettings(Core::ICore::settings());

View File

@@ -1012,6 +1012,7 @@ void FindReferences::displayResults(int first, int last)
item.setFilePath(Utils::FilePath::fromString(result.path));
item.setLineText(result.lineText);
item.setMainRange(result.line, result.col, result.len);
item.setUseTextEditorFont(true);
m_currentSearch->addResult(item);
}
}

View File

@@ -1,4 +1,5 @@
add_qtc_plugin(QmlProfiler
CONDITION TARGET Tracing
DEPENDS QmlDebug QmlJS Tracing Qt5::QuickWidgets
PLUGIN_DEPENDS Core Debugger ProjectExplorer QtSupport TextEditor
SOURCES

View File

@@ -287,6 +287,10 @@ public:
m_searcher->setPlaceholderText(ExamplesWelcomePage::tr("Search in Examples..."));
auto exampleSetSelector = new QComboBox(this);
QPalette pal = exampleSetSelector->palette();
// for macOS dark mode
pal.setColor(QPalette::Text, Utils::creatorTheme()->color(Theme::Welcome_TextColor));
exampleSetSelector->setPalette(pal);
exampleSetSelector->setMinimumWidth(GridProxyModel::GridItemWidth);
exampleSetSelector->setMaximumWidth(GridProxyModel::GridItemWidth);
ExampleSetModel *exampleSetModel = m_examplesModel->exampleSetModel();

View File

@@ -159,8 +159,19 @@ void RsyncDeployService::deployNextFile()
}
const DeployableFile file = m_deployableFiles.takeFirst();
const RsyncCommandLine cmdLine = RsyncDeployStep::rsyncCommand(*connection(), m_flags);
QString localFilePath = file.localFilePath().toString();
// On Windows, rsync is either from msys or cygwin. Neither work with the other's ssh.exe.
if (HostOsInfo::isWindowsHost()) {
localFilePath = '/' + localFilePath.at(0) + localFilePath.mid(2);
if (anyOf(cmdLine.options, [](const QString &opt) {
return opt.contains("cygwin", Qt::CaseInsensitive); })) {
localFilePath.prepend("/cygdrive");
}
}
const QStringList args = QStringList(cmdLine.options)
<< (file.localFilePath().toString() + (file.localFilePath().isDir() ? "/" : QString()))
<< (localFilePath + (file.localFilePath().isDir() ? "/" : QString()))
<< (cmdLine.remoteHostSpec + ':' + file.remoteFilePath());
m_rsync.start("rsync", args); // TODO: Get rsync location from settings?
}
@@ -228,7 +239,7 @@ RsyncCommandLine RsyncDeployStep::rsyncCommand(const SshConnection &sshConnectio
{
const QString sshCmdLine = QtcProcess::joinArgs(
QStringList{SshSettings::sshFilePath().toUserOutput()}
<< sshConnection.connectionOptions(SshSettings::sshFilePath()));
<< sshConnection.connectionOptions(SshSettings::sshFilePath()), OsTypeLinux);
const SshConnectionParameters sshParams = sshConnection.connectionParameters();
return RsyncCommandLine(QStringList{"-e", sshCmdLine, flags},
sshParams.userName() + '@' + sshParams.host());

View File

@@ -4,6 +4,7 @@ add_qtc_plugin(StudioWelcome
DEFINES STUDIO_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/qml/"
SOURCES
studiowelcomeplugin.cpp studiowelcomeplugin.h
examplecheckout.cpp examplecheckout.h
studiowelcome_global.h
studiowelcome.qrc
"${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc"

View File

@@ -0,0 +1,347 @@
/****************************************************************************
**
** 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 "examplecheckout.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <utils/archive.h>
#include <utils/networkaccessmanager.h>
#include <utils/qtcassert.h>
#include <QDialog>
#include <QFileDialog>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QQuickItem>
#include <QQuickWidget>
ExampleCheckout::ExampleCheckout(QObject *) {}
void ExampleCheckout::checkoutExample(const QUrl &url)
{
FileDownloader::registerQmlType();
static bool once = []() {
FileDownloader::registerQmlType();
FileExtractor::registerQmlType();
return true;
}();
QTC_ASSERT(once, ;);
m_dialog.reset(new QDialog(Core::ICore::dialogParent()));
m_dialog->setModal(true);
m_dialog->setFixedSize(620, 300);
QHBoxLayout *layout = new QHBoxLayout(m_dialog.get());
layout->setContentsMargins(2, 2, 2, 2);
auto widget = new QQuickWidget(m_dialog.get());
layout->addWidget(widget);
widget->engine()->addImportPath("qrc:/studiofonts");
widget->engine()->addImportPath(Core::ICore::resourcePath()
+ "/qmldesigner/propertyEditorQmlSources/imports");
widget->setSource(QUrl("qrc:/qml/downloaddialog/main.qml"));
m_dialog->setWindowFlag(Qt::Tool, true);
widget->setResizeMode(QQuickWidget::SizeRootObjectToView);
rootObject = widget->rootObject();
QTC_ASSERT(rootObject, qWarning() << "QML error"; return );
rootObject->setProperty("url", url);
m_dialog->show();
rootObject = widget->rootObject();
connect(rootObject, SIGNAL(canceled()), this, SLOT(handleCancel()));
connect(rootObject, SIGNAL(accepted()), this, SLOT(handleAccepted()));
}
QString ExampleCheckout::extractionFolder() const
{
return m_extrationFolder;
}
ExampleCheckout::~ExampleCheckout() {}
void ExampleCheckout::handleCancel()
{
m_dialog->close();
m_dialog.release()->deleteLater();
deleteLater();
}
void ExampleCheckout::handleAccepted()
{
QQmlProperty property(rootObject, "path");
m_extrationFolder = property.read().toString();
m_dialog->close();
emit finishedSucessfully();
m_dialog.release()->deleteLater();
deleteLater();
}
void FileDownloader::registerQmlType()
{
qmlRegisterType<FileDownloader>("ExampleCheckout", 1, 0, "FileDownloader");
}
FileDownloader::FileDownloader(QObject *parent)
: QObject(parent)
{}
FileDownloader::~FileDownloader()
{
m_tempFile.remove();
}
void FileDownloader::start()
{
m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip");
m_tempFile.open(QIODevice::WriteOnly);
auto request = QNetworkRequest(m_url);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request);
QNetworkReply::connect(reply, &QNetworkReply::readyRead, [this, reply]() {
m_tempFile.write(reply->readAll());
});
QNetworkReply::connect(reply,
&QNetworkReply::downloadProgress,
this,
[this](qint64 current, qint64 max) {
if (max == 0)
return;
m_progress = current * 100 / max;
emit progressChanged();
});
QNetworkReply::connect(reply, &QNetworkReply::finished, [this, reply]() {
if (reply->error()) {
m_tempFile.remove();
if (m_url != reply->url()) {
m_url = reply->url();
start();
} else {
emit downloadFailed();
}
} else {
m_tempFile.flush();
m_tempFile.close();
m_finished = true;
emit finishedChanged();
}
});
}
void FileDownloader::setUrl(const QUrl &url)
{
m_url = url;
emit nameChanged();
}
QUrl FileDownloader::url() const
{
return m_url;
}
bool FileDownloader::finished() const
{
return m_finished;
}
bool FileDownloader::error() const
{
return m_error;
}
QString FileDownloader::name() const
{
const QFileInfo fileInfo(m_url.path());
return fileInfo.baseName();
}
QString FileDownloader::completeBaseName() const
{
const QFileInfo fileInfo(m_url.path());
return fileInfo.completeBaseName();
}
int FileDownloader::progress() const
{
return m_progress;
}
QString FileDownloader::tempFile() const
{
return QFileInfo(m_tempFile).canonicalFilePath();
}
FileExtractor::FileExtractor(QObject *parent)
: QObject(parent)
{
if (Core::DocumentManager::instance())
m_targetPath = Core::DocumentManager::projectsDirectory();
else
m_targetPath = Utils::FilePath::fromString("/temp/");
m_timer.setInterval(500);
m_timer.setSingleShot(false);
}
FileExtractor::~FileExtractor() {}
void FileExtractor::registerQmlType()
{
qmlRegisterType<FileExtractor>("ExampleCheckout", 1, 0, "FileExtractor");
}
QString FileExtractor::targetPath() const
{
return m_targetPath.toUserOutput();
}
void FileExtractor::browse()
{
const QString path = QFileDialog::getExistingDirectory(Core::ICore::dialogParent(),
(tr("Choose Directory")),
m_targetPath.toString());
if (!path.isEmpty())
m_targetPath = Utils::FilePath::fromString(path);
emit targetPathChanged();
emit targetFolderExistsChanged();
}
void FileExtractor::setSourceFile(QString &sourceFilePath)
{
m_sourceFile = Utils::FilePath::fromString(sourceFilePath);
emit targetFolderExistsChanged();
}
void FileExtractor::setArchiveName(QString &filePath)
{
m_archiveName = filePath;
}
const QString FileExtractor::detailedText()
{
return m_detailedText;
}
bool FileExtractor::finished() const
{
return m_finished;
}
QString FileExtractor::currentFile() const
{
return m_currentFile;
}
QString FileExtractor::size() const
{
return m_size;
}
QString FileExtractor::count() const
{
return m_count;
}
bool FileExtractor::targetFolderExists() const
{
const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName;
return QFileInfo(targetFolder).exists();
}
QString FileExtractor::archiveName() const
{
return m_archiveName;
}
QString FileExtractor::sourceFile() const
{
return m_sourceFile.toString();
}
void FileExtractor::extract()
{
Utils::Archive *archive = Utils::Archive::unarchive(m_sourceFile, m_targetPath);
archive->setParent(this);
QTC_ASSERT(archive, return );
m_timer.start();
const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName;
qint64 bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable();
QTimer::connect(&m_timer, &QTimer::timeout, [this, bytesBefore, targetFolder]() {
static QHash<QString, int> hash;
QDirIterator it(targetFolder, {"*.*"}, QDir::Files, QDirIterator::Subdirectories);
int count = 0;
while (it.hasNext()) {
if (!hash.contains(it.fileName())) {
m_currentFile = it.fileName();
hash.insert(m_currentFile, 0);
emit currentFileChanged();
}
it.next();
count++;
}
m_size = QString::number(bytesBefore
- QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable());
m_count = QString::number(count);
emit sizeChanged();
});
QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) {
m_detailedText += output;
emit detailedTextChanged();
});
QObject::connect(archive, &Utils::Archive::finished, [this](bool ret) {
m_finished = ret;
m_timer.stop();
emit finishedChanged();
QTC_ASSERT(ret, ;);
});
}

View File

@@ -0,0 +1,163 @@
/****************************************************************************
**
** 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 <utils/fileutils.h>
#include <QObject>
#include <QTimer>
#include <QtQml>
#include <memory>
QT_BEGIN_NAMESPACE
class QDialog;
QT_END_NAMESPACE
class ExampleCheckout : public QObject
{
Q_OBJECT
public:
explicit ExampleCheckout(QObject *parent = nullptr);
Q_INVOKABLE void checkoutExample(const QUrl &url);
QString extractionFolder() const;
~ExampleCheckout();
public slots:
void handleCancel();
void handleAccepted();
signals:
void finishedSucessfully();
private:
std::unique_ptr<QDialog> m_dialog;
QObject *rootObject = nullptr;
QString m_extrationFolder;
};
class FileExtractor : public QObject
{
Q_OBJECT
Q_PROPERTY(QString targetPath READ targetPath NOTIFY targetPathChanged)
Q_PROPERTY(QString archiveName READ archiveName WRITE setArchiveName)
Q_PROPERTY(QString detailedText READ detailedText NOTIFY detailedTextChanged)
Q_PROPERTY(QString currentFile READ currentFile NOTIFY currentFileChanged)
Q_PROPERTY(QString size READ size NOTIFY sizeChanged)
Q_PROPERTY(QString count READ count NOTIFY sizeChanged)
Q_PROPERTY(QString sourceFile READ sourceFile WRITE setSourceFile)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
Q_PROPERTY(bool targetFolderExists READ targetFolderExists NOTIFY targetFolderExistsChanged)
public:
explicit FileExtractor(QObject *parent = nullptr);
~FileExtractor();
static void registerQmlType();
QString targetPath() const;
void setSourceFile(QString &sourceFilePath);
void setArchiveName(QString &filePath);
const QString detailedText();
bool finished() const;
QString currentFile() const;
QString size() const;
QString count() const;
bool targetFolderExists() const;
QString sourceFile() const;
QString archiveName() const;
Q_INVOKABLE void browse();
Q_INVOKABLE void extract();
signals:
void targetPathChanged();
void detailedTextChanged();
void finishedChanged();
void currentFileChanged();
void sizeChanged();
void targetFolderExistsChanged();
private:
Utils::FilePath m_targetPath;
Utils::FilePath m_sourceFile;
QString m_detailedText;
bool m_finished = false;
QTimer m_timer;
QString m_currentFile;
QString m_size;
QString m_count;
QString m_archiveName;
};
class FileDownloader : public QObject
{
Q_OBJECT
Q_PROPERTY(QUrl url READ url WRITE setUrl)
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
Q_PROPERTY(bool error READ error NOTIFY errorChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
Q_PROPERTY(QString tempFile READ tempFile NOTIFY finishedChanged)
public:
explicit FileDownloader(QObject *parent = nullptr);
~FileDownloader();
void setUrl(const QUrl &url);
QUrl url() const;
bool finished() const;
bool error() const;
static void registerQmlType();
QString name() const;
QString completeBaseName() const;
int progress() const;
QString tempFile() const;
Q_INVOKABLE void start();
signals:
void finishedChanged();
void errorChanged();
void nameChanged();
void progressChanged();
void downloadFailed();
private:
QUrl m_url;
bool m_finished = false;
bool m_error = false;
int m_progress = 0;
QFile m_tempFile;
};

View File

@@ -0,0 +1,249 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.9
import QtQuick.Shapes 1.0
Shape {
id: root
implicitWidth: 100
implicitHeight: 100
property alias gradient: path.fillGradient
property alias strokeStyle: path.strokeStyle
property alias strokeWidth: path.strokeWidth
property alias strokeColor: path.strokeColor
property alias dashPattern: path.dashPattern
property alias joinStyle: path.joinStyle
property alias fillColor: path.fillColor
property alias capStyle: path.capStyle
property alias dashOffset: path.dashOffset
property real begin: 0
property real end: 90
property real arcWidth: 10
property real arcWidthBegin: arcWidth
property real arcWidthEnd: arcWidth
property real radiusInnerAdjust: 0
property real radiusOuterAdjust: 0
property real alpha: clamp(sortedEnd() - sortedBegin(),0, 359.9)
layer.enabled: antialiasing
layer.smooth: antialiasing
layer.textureSize: Qt.size(width * 2, height * 2)
property bool outlineArc: false
property bool round: false
property bool roundEnd: round
property bool roundBegin: round
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
function myCos(angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180.0;
return Math.cos(angleInRadians)
}
function mySin(angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180.0;
return Math.sin(angleInRadians)
}
function polarToCartesianX(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180.0;
var x = centerX + radius * Math.cos(angleInRadians)
return x
}
function polarToCartesianY(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180.0;
var y = centerY + radius * Math.sin(angleInRadians);
return y
}
function calc()
{
path.__xRadius = root.width / 2 - root.strokeWidth / 2
path.__yRadius = root.height / 2 - root.strokeWidth / 2
path.__Xcenter = root.width / 2
path.__Ycenter = root.height / 2
path.startX = root.polarToCartesianX(path.__Xcenter, path.__Ycenter, path.__xRadius, root.sortedBegin() - 90) + root.__beginOff * myCos(root.sortedBegin() + 90)
path.startY = root.polarToCartesianY(path.__Xcenter, path.__Ycenter, path.__yRadius, root.sortedBegin() - 90) + root.__beginOff * mySin(root.sortedBegin() + 90)
arc1.x = root.polarToCartesianX(path.__Xcenter, path.__Ycenter, path.__xRadius, root.sortedEnd() - 90) + root.__endOff * myCos(root.sortedEnd() + 90)
arc1.y = root.polarToCartesianY(path.__Xcenter, path.__Ycenter, path.__yRadius, root.sortedEnd() - 90) + root.__endOff * mySin(root.sortedEnd() + 90)
arc1.radiusX = path.__xRadius - root.__endOff / 2 -root.__beginOff / 2 + root.radiusOuterAdjust
arc1.radiusY = path.__yRadius - root.__endOff / 2 -root.__beginOff / 2 + root.radiusOuterAdjust
arc1.useLargeArc = root.alpha > 180
}
function sortedBegin()
{
return(Math.min(root.begin, root.end))
}
function sortedEnd()
{
return(Math.max(root.begin, root.end))
}
onWidthChanged: calc()
onHeightChanged: calc()
onBeginChanged: calc()
onEndChanged: calc()
onAlphaChanged: calc()
ShapePath {
id: path
property real __xRadius
property real __yRadius
property real __Xcenter
property real __Ycenter
strokeColor: "red"
strokeWidth: 4
capStyle: ShapePath.FlatCap
}
property real __beginOff: {
if (root.arcWidthEnd > root.arcWidthBegin)
return (root.arcWidthEnd - root.arcWidthBegin) / 2
return 0;
}
property real __endOff: {
if (root.arcWidthBegin > root.arcWidthEnd)
return (root.arcWidthBegin - root.arcWidthEnd) / 2
return 0;
}
property real __startP: root.arcWidthBegin + __beginOff
property real __endP: root.arcWidthEnd + __endOff
Item {
id: shapes
PathArc {
id: arc1
property bool add: true
}
PathLine {
relativeX: root.arcWidthEnd * myCos(root.sortedEnd())
relativeY: root.arcWidthEnd * mySin(root.sortedEnd())
property bool add: !root.roundEnd && (root.outlineArc && root.alpha < 359.8)
}
PathArc {
relativeX: root.arcWidthEnd * myCos(root.sortedEnd())
relativeY: root.arcWidthEnd * mySin(root.sortedEnd())
radiusX: root.arcWidthEnd /2
radiusY: root.arcWidthEnd /2
property bool add: root.roundEnd && (root.outlineArc && root.alpha < 359.8)
}
PathMove {
relativeX: root.arcWidthEnd * myCos(root.sortedEnd())
relativeY: root.arcWidthEnd * mySin(root.sortedEnd())
property bool add: root.outlineArc && root.alpha > 359.7
}
PathArc {
id: arc2
useLargeArc: arc1.useLargeArc
radiusX: path.__xRadius - root.arcWidthBegin + root.__beginOff / 2 + root.__endOff / 2 + root.radiusInnerAdjust
radiusY:path.__yRadius - root.arcWidthBegin + root.__beginOff / 2 + root.__endOff / 2 + root.radiusInnerAdjust
x: path.startX + root.arcWidthBegin * myCos(root.sortedBegin())
y: path.startY + root.arcWidthBegin * mySin(root.sortedBegin())
direction: PathArc.Counterclockwise
property bool add: root.outlineArc
}
PathLine {
x: path.startX
y: path.startY
property bool add: !root.roundBegin && root.outlineArc && root.alpha < 359.8
}
PathArc {
x: path.startX
y: path.startY
radiusX: root.arcWidthEnd /2
radiusY: root.arcWidthEnd /2
property bool add: root.roundBegin && root.outlineArc && root.alpha < 359.8
}
PathMove {
x: path.startX
y: path.startY
property bool add: root.outlineArc && root.alpha == 360
}
}
function invalidatePaths() {
if (!root.__completed)
return
for (var i = 0; i < shapes.resources.length; i++) {
var s = shapes.resources[i];
if (s.add)
path.pathElements.push(s)
}
}
property bool __completed: false
Component.onCompleted: {
root.__completed = true
invalidatePaths()
calc()
}
}

View File

@@ -0,0 +1,129 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.12
import QtQuick.Timeline 1.0
Rectangle {
id: rectangle
width: 60
height: 60
color: "#8c8c8c"
radius: 50
property alias inputMax: rangeMapper.inputMax
property alias inputMin: rangeMapper.inputMin
property alias value: minMaxMapper.input
ArcItem {
id: arc
x: -1
y: -1
width: 62
height: 62
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
end: rangeMapper.value
antialiasing: true
strokeWidth: 8
strokeColor: "#ffffff"
capStyle: 32
fillColor: "#00000000"
}
RangeMapper {
id: rangeMapper
outputMax: 358
input: minMaxMapper.value
}
MinMaxMapper {
id: minMaxMapper
input: 95
max: rangeMapper.inputMax
min: rangeMapper.inputMin
}
Rectangle {
id: rectangle1
width: 60
height: 60
color: "#ffffff"
radius: 40
anchors.verticalCenter: parent.verticalCenter
scale: 1
anchors.horizontalCenter: parent.horizontalCenter
}
Timeline {
id: timeline
currentFrame: rangeMapper.value
enabled: true
endFrame: 360
startFrame: 0
KeyframeGroup {
target: rectangle1
property: "opacity"
Keyframe {
frame: 0
value: 0
}
Keyframe {
frame: 360
value: 1
}
}
KeyframeGroup {
target: rectangle1
property: "scale"
Keyframe {
frame: 360
value: 1
}
Keyframe {
frame: 0
value: 0.1
}
}
KeyframeGroup {
target: arc
property: "opacity"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 1
frame: 40
}
}
}
}

View File

@@ -0,0 +1,193 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.12
import QtQuick.Timeline 1.0
Item {
id: coolProgressBar
width: 605
height: 16
property alias value: timeline.currentFrame
clip: true
Timeline {
id: timeline
enabled: true
endFrame: 100
startFrame: 0
KeyframeGroup {
target: rectangle
property: "width"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 150
frame: 25
}
Keyframe {
value: 300
frame: 50
}
Keyframe {
value: 450
frame: 75
}
Keyframe {
value: 600
frame: 100
}
}
KeyframeGroup {
target: rectangle1
property: "width"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 300
frame: 25
}
Keyframe {
value: 450
frame: 50
}
Keyframe {
value: 600
frame: 75
}
}
KeyframeGroup {
target: rectangle2
property: "width"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 450
frame: 25
}
Keyframe {
value: 600
frame: 50
}
}
KeyframeGroup {
target: rectangle3
property: "width"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 600
frame: 25
}
}
KeyframeGroup {
target: content
property: "opacity"
Keyframe {
value: 0
frame: 0
}
Keyframe {
value: 1
frame: 50
}
}
}
Item {
id: content
anchors.fill: parent
Rectangle {
id: rectangle
y: 0
width: 80
height: 16
color: "#ffffff"
radius: 12
}
Rectangle {
id: rectangle1
y: 0
width: 80
height: 16
opacity: 0.6
color: "#ffffff"
radius: 12
}
Rectangle {
id: rectangle2
y: 0
width: 80
height: 16
opacity: 0.4
color: "#ffffff"
radius: 12
}
Rectangle {
id: rectangle3
y: 0
width: 80
height: 16
opacity: 0.2
color: "#ffffff"
radius: 12
}
}
}
/*##^##
Designer {
D{i:0;height:16;width:590}D{i:1}
}
##^##*/

View File

@@ -0,0 +1,91 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
T.Button {
id: control
implicitWidth: Math.max(buttonBackground ? buttonBackground.implicitWidth : 0,
textItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(buttonBackground ? buttonBackground.implicitHeight : 0,
textItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
text: "My Button"
property color defaultColor: "#b9b9ba"
property color checkedColor: "#ffffff"
background: buttonBackground
Rectangle {
id: buttonBackground
color: control.defaultColor
implicitWidth: 100
implicitHeight: 40
opacity: enabled ? 1 : 0.3
radius: 0
border.width: 1
}
contentItem: textItem
Text {
id: textItem
text: control.text
opacity: enabled ? 1.0 : 0.3
color: "#bababa"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
states: [
State {
name: "normal"
when: !control.down && !control.checked
PropertyChanges {
target: buttonBackground
color: "#2d2e30"
}
},
State {
name: "down"
when: control.down || control.checked
PropertyChanges {
target: textItem
color: control.checkedColor
}
PropertyChanges {
target: buttonBackground
color: "#545456"
border.color: "#70a2f5"
border.width: 2
}
}
]
}

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.15
import ExampleCheckout 1.0
import QtQuick.Layouts 1.11
import StudioFonts 1.0
Text {
font.family: StudioFonts.titilliumWeb_light
color: root.textColor
}

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick Designer Components.
**
** $QT_BEGIN_LICENSE:GPL$
** 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 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** 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.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.10
QtObject {
id: object
property real input
property bool minClipped: object.input < object.min
property bool maxClipped: object.input > object.max
property bool outOfRage: object.maxClipped ||object.minClipped
property real value: {
if (object.maxClipped)
return object.max
if (object.minClipped)
return object.min
return object.input
}
property real min: 0
property real max: 100
}

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick Designer Components.
**
** $QT_BEGIN_LICENSE:GPL$
** 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 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** 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.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.10
QtObject {
id: object
property real input
property real value: {
var slope = (object.outputMax - object.outputMin) / (object.inputMax - object.inputMin)
return object.outputMin + slope * (object.input - object.inputMin)
}
property real inputMin: 0
property real inputMax: 100
property real outputMin: 0
property real outputMax: 100
}

View File

@@ -0,0 +1,47 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QmlProject 1.1
Project {
mainFile: "main.qml"
/* Include .qml, .js, and image files from current directory and subdirectories */
QmlFiles {
directory: "."
}
JavaScriptFiles {
directory: "."
}
ImageFiles {
directory: "."
}
/* List of plugin directories passed to QML runtime */
importPaths: [ "mockData", "../../../../share/3rdparty/studiofonts", "../../../../../share/qtcreator/qmldesigner/propertyEditorQmlSources/imports" ]
Environment {
QT_AUTO_SCREEN_SCALE_FACTOR: "1"
}
}

View File

@@ -0,0 +1,312 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 2.15
import ExampleCheckout 1.0
import QtQuick.Layouts 1.11
import StudioFonts 1.0
Rectangle {
id: root
property alias url: downloader.url
property string path: fileExtractor.targetPath
width: 620
height: 300
color: "#2d2e30"
property color textColor: "#b9b9ba"
signal canceled
signal accepted
StackLayout {
id: stackLayout
anchors.fill: parent
currentIndex: 0
FileExtractor {
id: fileExtractor
sourceFile: downloader.tempFile
archiveName: downloader.completeBaseName
}
FileDownloader {
id: downloader
//onNameChanged: start()
onFinishedChanged: {
button.enabled = downloader.finished
if (!downloader.finished)
stackLayout.currentIndex = 3
}
onDownloadFailed: stackLayout.currentIndex = 3
}
Item {
id: download
Layout.fillHeight: true
Layout.fillWidth: true
DialogButton {
id: button
x: 532
y: 432
text: qsTr("Continue")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.rightMargin: 20
enabled: false
onClicked: stackLayout.currentIndex = 1
}
CoolProgressBar {
id: coolProgressBar
width: 605
anchors.top: parent.top
value: downloader.progress
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 69
}
DialogLabel {
x: 201
text: "Downloading Example " + downloader.completeBaseName
anchors.top: parent.top
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
}
DialogButton {
id: downloadbutton
y: 420
enabled: !button.enabled
text: qsTr("Start Download")
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 20
anchors.bottomMargin: 20
onClicked: {
downloadbutton.enabled = false
downloader.start()
}
}
CircularIndicator {
id: circularIndicator
x: 304
anchors.top: parent.top
anchors.horizontalCenterOffset: 0
value: downloader.progress
anchors.topMargin: 120
anchors.horizontalCenter: parent.horizontalCenter
}
}
Item {
id: destiationfolder
Layout.fillHeight: true
Layout.fillWidth: true
DialogButton {
id: nextPageDestination
x: 532
y: 432
text: qsTr("Continue")
anchors.right: parent.right
anchors.bottom: parent.bottom
enabled: !fileExtractor.targetFolderExists
anchors.bottomMargin: 20
anchors.rightMargin: 20
onClicked: {
stackLayout.currentIndex = 2
fileExtractor.extract()
}
}
RowLayout {
y: 114
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 104
anchors.leftMargin: 67
TextField {
id: textField
text: fileExtractor.targetPath
Layout.fillWidth: true
font.family: StudioFonts.titilliumWeb_light
wrapMode: Text.WordWrap
selectByMouse: true
readOnly: true
}
DialogButton {
id: browse
text: qsTr("Browse")
onClicked: fileExtractor.browse()
}
}
DialogLabel {
id: label
y: 436
text: qsTr("Folder ") + downloader.completeBaseName + (" already exists")
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 20
anchors.bottomMargin: 20
visible: !nextPageDestination.enabled
}
DialogButton {
id: button5
x: 400
y: 420
text: qsTr("Cancel")
anchors.right: nextPageDestination.left
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.rightMargin: 20
onClicked: root.canceled()
}
DialogLabel {
text: "Choose installation folder"
anchors.top: parent.top
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
x: 8
}
}
Item {
id: extraction
Layout.fillHeight: true
Layout.fillWidth: true
DialogButton {
id: done
x: 532
y: 432
text: qsTr("Open")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.rightMargin: 20
enabled: fileExtractor.finished
onClicked: root.accepted()
}
DialogLabel {
id: text2
text: fileExtractor.count + " files " + (fileExtractor.size / 1024 / 1024).toFixed(2) + " MB "+ fileExtractor.currentFile
anchors.left: parent.left
anchors.bottom: parent.bottom
font.pixelSize: 12
wrapMode: Text.WrapAnywhere
anchors.leftMargin: 20
anchors.bottomMargin: 20
}
DialogButton {
id: details
x: 8
text: qsTr("Details")
anchors.top: parent.top
anchors.topMargin: 66
anchors.horizontalCenter: parent.horizontalCenter
checkable: true
}
DialogLabel {
x: 8
text: "Extracting Example " + downloader.completeBaseName
anchors.top: parent.top
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
}
Flickable {
visible: details.checked
clip: true
anchors.bottomMargin: 60
anchors.rightMargin: 20
anchors.leftMargin: 20
anchors.topMargin: 120
anchors.fill: parent
id: flickable
interactive: false
DialogLabel {
onHeightChanged: flickable.contentY = text1.implicitHeight - flickable.height
id: text1
text: fileExtractor.detailedText
font.pixelSize: 12
wrapMode: Text.WrapAnywhere
width: flickable.width
}
}
}
Item {
id: failed
Layout.fillHeight: true
Layout.fillWidth: true
DialogButton {
id: finish
x: 532
y: 432
text: qsTr("Finish")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.rightMargin: 20
onClicked: root.canceled()
}
DialogLabel {
x: 8
text: qsTr("Download failed")
anchors.top: parent.top
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.0
QtObject {
id: root
signal downloadFailed
property bool finished: false
property bool url
property int progress: 55
Behavior on progress { PropertyAnimation {
duration: 2000
}
}
function start() {
timer.start()
root.progress = 100
}
property Timer timer: Timer {
interval: 2000
repeat: false
onTriggered: {
root.finished
root.progress = 1000
finished = true
}
}
}

View File

@@ -0,0 +1,41 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.0
QtObject {
signal finished
property string sourceFile: "SomeExample.zip"
property string archiveName: "SomeExample"
property string targetPath: "/extract/here"
property string detailedText: "Start" +
"\n1 Some detailed info about extraction\nSome detailed info about extraction\nSome detailed info about extractionSome detailed info about extractionSome detailed info about extraction" +
"\n2 Some detailed info about extraction\nSome detailed info about extraction\nSome detailed info about extractionSome detailed info about extractionSome detailed info about extraction" +
"\n3 Some detailed info about extraction\nSome detailed info about extraction\nSome detailed info about extractionSome detailed info about extractionSome detailed info about extraction" +
"\nend"
}

View File

@@ -0,0 +1,2 @@
FileDownloader 1.0 FileDownloader.qml
FileExtractor 1.0 FileExtractor.qml

View File

@@ -74,4 +74,13 @@ ListModel {
thumbnail: "images/washingmachinedemo_thumbnail.png"
displayName: "Washing Machine"
}
ListElement {
projectName: "highendivisystem"
qmlFileName: "Screen01.ui.qml"
thumbnail: "images/highendivi_thumbnail.png"
displayName: "Highend IVI System"
url: "https://download.qt.io/learning/examples/qtdesignstudio/highendivisystem.zip"
showDownload: true
}
}

View File

@@ -35,6 +35,8 @@ Item {
property alias imageSource: image.source
property alias labelText: label.text
property alias downloadIcon: downloadCloud.visible
onVisibleChanged: {
animateOpacity.start()
animateScale.start()
@@ -89,6 +91,19 @@ Item {
rectangle.color = "#262728"
label.color = "#686868"
}
Image {
id: downloadCloud
x: 210
y: 118
width: 60
height: 60
source: "images/downloadCloud.svg"
sourceSize.height: 60
sourceSize.width: 60
fillMode: Image.PreserveAspectFit
visible: false
}
}
}
@@ -187,3 +202,9 @@ Item {
font.family: StudioFonts.titilliumWeb_regular
}
}
/*##^##
Designer {
D{i:0;formeditorZoom:1.3300000429153442}D{i:8}
}
##^##*/

View File

@@ -39,6 +39,7 @@ GridView {
id: hoverOverDesaturate
imageSource: typeof(thumbnail) === "undefined" ? "images/thumbnail_test.png" : thumbnail;
labelText: displayName
downloadIcon: typeof(showDownload) === "undefined" ? false : showDownload;
SequentialAnimation {
id: animation

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" viewBox="0 0 128 128" overflow="visible" xml:space="preserve">
<g transform="translate(0.000000,2000.000000) scale(0.100000,-0.100000)">
<path fill="#686868" d="M764,19815.1c-12.5-0.6-28.9-2.1-38.4-3.5c-30-4.3-73-22.5-107.4-45.4c-19.9-13.2-34.6-25.2-51.1-41.6
c-18.6-18.6-30.1-33.4-43.2-55.8l-5.8-9.9l-6.7,3.2c-44.8,21.1-105.6,27.1-154.5,15.3c-35.4-8.5-75.1-30-104.5-56.5
c-3.4-3.1-6.8-6-7.5-6.5c-2.2-1.6-17.1-17.9-22.3-24.5c-7.4-9.3-16.5-23.4-21.7-33.8c-13.5-26.7-25.2-66.5-26.7-91l-0.4-7
l-15.9-7.9c-26.7-13.3-44.8-26.6-66.8-49.2c-18.9-19.3-31.4-36.4-41.9-57c-22.4-44.3-29.9-95.3-20.8-142.8
c8.4-44.1,29.1-83.5,61.5-117.1c35.2-36.5,62.1-52.4,108.2-64c24-6,47.3-7.8,115.4-8.9c12.4-0.2,29.7-0.4,38.4-0.6
c78.9-1.6,573.8-2.5,615.8-1.1c54,1.7,79,3.6,101.9,7.4c21.4,3.6,36.6,8.4,59.7,18.8c31.7,14.3,49.3,26.2,71,48
c22.6,22.7,38,46.4,49.6,76.8c14.6,38.2,19.8,83.7,13.7,121.3c-5.2,31.9-19.8,67.7-38.7,94.4c-10.5,14.7-28.8,34.7-42.7,46.2
c-28.8,24.1-58.1,39.7-90.8,48.4c-7.5,2-9.1,2.7-9.1,3.6c0,0.6,0.8,6.8,1.7,13.7c1.6,11.5,1.7,14.2,1.7,32.8
c0,21.9-0.9,31.7-4.4,49.8c-9.2,46.3-30,91.8-59.8,130.6c-21.3,27.8-49.5,53.4-77,69.9c-31.4,18.8-83.2,39.1-108.8,42.6
C819.9,19815.7,790.2,19816.3,764,19815.1z M828,19778.4c13-1.8,27.2-6.1,48.6-14.6c24-9.6,42.8-19,56.8-28.3
c57.9-38.7,101.4-106.8,113.6-178c3.9-22.6,4.1-43.6,0.6-69.7c-1.1-8.4-2.2-19-2.3-23.5c-0.3-7.6-0.3-8.5,1.1-11.6
c0.9-2.1,2.6-4.4,4.6-6.1c3.6-3.2,4.9-3.7,23.6-8.6c19.7-5.2,28.8-8.4,42.2-15.2c32.3-16.2,62.4-42.8,82-72.5
c14.5-22,26.1-52.4,29.9-78.6c0.8-5.6,1.1-11.6,1.1-22.8c-0.1-55-19.5-104.3-55.1-140.1c-18.5-18.6-39.9-32.1-71-44.9
c-32.4-13.4-63.1-16.9-162.9-18.8c-36.3-0.7-402.6-0.4-440.8,0.4c-8.2,0.2-43.7,0.4-78.9,0.6c-93.9,0.5-151.4,1.6-178.1,3.5
c-14.9,1.1-24.2,2.5-36.8,5.7c-39,9.9-59.1,21.9-89.9,53.6c-26,26.7-44.1,60-51.4,94.6c-8.9,42.1-3.2,84.4,16.8,124.7
c11.5,23.2,33.3,48.7,58.8,69c10.2,8.2,20.3,14.4,34.3,21.2c21.2,10.4,27.7,15.3,32,24.5c2.4,5,3.5,10.5,3.5,17.2
c0,12.4,5.9,37,14,59.1c4.4,11.9,12.1,27.5,18.3,36.7c18.9,28.3,49.9,55,83.7,71.9c29.5,14.8,54.5,20.2,89.4,19.4
c15-0.3,24.8-1.4,38-4c18.7-3.7,30.7-8,51.1-18.1c17-8.6,19.2-9.1,25.8-7.2c6.1,1.8,8.7,4.9,18.6,22.2c10.8,19,14.4,24.8,20.7,33.2
c28.7,38.1,74.9,72.8,121.8,91.3c23.8,9.4,36.1,12.1,62.8,13.9c5.6,0.4,11.6,0.8,13.3,0.9C774.9,19779.8,823.3,19779.1,828,19778.4
z"/>
</g>
<path fill="#686868" d="M82.4,65.7L70.2,78.7c0,0-0.1,0-0.1,0l0-33.5c0-1.6-1.2-3.1-2.9-3.2c-1.7-0.1-3.1,1.3-3.1,3v33.7c0,0,0,0-0.1,0L52,65.7
c-0.9-1-2.5-1.1-3.6-0.1h0c-1,0.9-1.1,2.5-0.1,3.6l16.7,18c1.2,1.3,3.2,1.3,4.3,0l16.7-18c0.9-1,0.9-2.6-0.1-3.6h0
C84.9,64.6,83.3,64.6,82.4,65.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -60,7 +60,7 @@ Item {
ScrollView {
ProjectsGrid {
model: ExamplesModel {}
onItemSelected: projectModel.openExample(item.projectName, item.qmlFileName)
onItemSelected: projectModel.openExample(item.projectName, item.qmlFileName, item.url)
}
}

View File

@@ -12,9 +12,11 @@ DEFINES += STUDIO_QML_PATH=\\\"$$PWD/qml/\\\"
HEADERS += \
studiowelcome_global.h \
studiowelcomeplugin.h \
examplecheckout.h
SOURCES += \
studiowelcomeplugin.cpp
studiowelcomeplugin.cpp \
examplecheckout.cpp
OTHER_FILES += \
StudioWelcome.json.in

View File

@@ -17,6 +17,8 @@ QtcPlugin {
"studiowelcome_global.h",
"studiowelcomeplugin.h",
"studiowelcomeplugin.cpp",
"examplecheckout.h",
"examplecheckout.cpp",
"studiowelcome.qrc",
]

View File

@@ -24,6 +24,7 @@
****************************************************************************/
#include "studiowelcomeplugin.h"
#include "examplecheckout.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/dialogs/restartdialog.h>
@@ -175,9 +176,29 @@ public:
QDesktopServices::openUrl(QUrl("qthelp://org.qt-project.qtcreator/doc/index.html"));
}
Q_INVOKABLE void openExample(const QString &example, const QString &formFile)
Q_INVOKABLE void openExample(const QString &example, const QString &formFile, const QString &url)
{
const QString projectFile = Core::ICore::resourcePath() + "/examples/" + example + "/" + example + ".qmlproject";
if (!url.isEmpty()) {
ExampleCheckout *checkout = new ExampleCheckout;
checkout->checkoutExample(QUrl::fromUserInput(url));
connect(checkout,
&ExampleCheckout::finishedSucessfully,
this,
[checkout, this, formFile, example]() {
const QString projectFile = checkout->extractionFolder() + "/" + example
+ "/" + example + ".qmlproject";
ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
const QString qmlFile = checkout->extractionFolder() + "/" + example + "/"
+ formFile;
Core::EditorManager::openEditor(qmlFile);
});
return;
}
const QString projectFile = Core::ICore::resourcePath() + "/examples/" + example + "/"
+ example + ".qmlproject";
ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
const QString qmlFile = Core::ICore::resourcePath() + "/examples/" + example + "/" + formFile;
Core::EditorManager::openEditor(qmlFile);

View File

@@ -30,6 +30,8 @@
#include "textmark.h"
#include "textdocument.h"
#include <utils/id.h>
#include <State>
#include <QTextBlockUserData>
@@ -48,6 +50,7 @@ struct TEXTEDITOR_EXPORT Parenthesis
: pos(position), chr(c), type(t) {}
int pos = -1;
QChar chr;
Utils::Id source;
Type type = Opened;
};