forked from qt-creator/qt-creator
iOS: Remove simulator management from settings page
Simulators can be managed via Xcode, which must be installed anyway. Re- implementing this functionality is not useful, error-prone, and a maintenance burden. Point users to the corresponding Xcode documentation instead. Add a button for updating the list of simulators in the run configuration settings (which didn't update when simulators were changed in Xcode). Change-Id: I5a861f21851bb866d45a703f46bb20ed5df960e8 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -2,7 +2,6 @@ add_qtc_plugin(Ios
|
|||||||
DEPENDS QmlDebug Qt::Xml
|
DEPENDS QmlDebug Qt::Xml
|
||||||
PLUGIN_DEPENDS Core Debugger ProjectExplorer QmakeProjectManager CMakeProjectManager
|
PLUGIN_DEPENDS Core Debugger ProjectExplorer QmakeProjectManager CMakeProjectManager
|
||||||
SOURCES
|
SOURCES
|
||||||
createsimulatordialog.cpp createsimulatordialog.h
|
|
||||||
devicectlutils.cpp
|
devicectlutils.cpp
|
||||||
devicectlutils.h
|
devicectlutils.h
|
||||||
ios.qrc
|
ios.qrc
|
||||||
@@ -23,8 +22,6 @@ add_qtc_plugin(Ios
|
|||||||
iostoolhandler.cpp iostoolhandler.h
|
iostoolhandler.cpp iostoolhandler.h
|
||||||
iostr.h
|
iostr.h
|
||||||
simulatorcontrol.cpp simulatorcontrol.h
|
simulatorcontrol.cpp simulatorcontrol.h
|
||||||
simulatorinfomodel.cpp simulatorinfomodel.h
|
|
||||||
simulatoroperationdialog.cpp simulatoroperationdialog.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(Ios
|
extend_qtc_plugin(Ios
|
||||||
|
@@ -1,172 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "createsimulatordialog.h"
|
|
||||||
|
|
||||||
#include "iostr.h"
|
|
||||||
#include "simulatorcontrol.h"
|
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
|
||||||
#include <utils/async.h>
|
|
||||||
#include <utils/layoutbuilder.h>
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent)
|
|
||||||
: QDialog(parent)
|
|
||||||
{
|
|
||||||
resize(320, 160);
|
|
||||||
setWindowTitle(Tr::tr("Create Simulator"));
|
|
||||||
|
|
||||||
m_nameEdit = new QLineEdit(this);
|
|
||||||
m_deviceTypeCombo = new QComboBox(this);
|
|
||||||
m_runtimeCombo = new QComboBox(this);
|
|
||||||
|
|
||||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
|
||||||
buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
|
||||||
|
|
||||||
using namespace Layouting;
|
|
||||||
|
|
||||||
Column {
|
|
||||||
Form {
|
|
||||||
Tr::tr("Simulator name:"), m_nameEdit, br,
|
|
||||||
Tr::tr("Device type:"), m_deviceTypeCombo, br,
|
|
||||||
Tr::tr("OS version:"), m_runtimeCombo, br,
|
|
||||||
},
|
|
||||||
buttonBox
|
|
||||||
}.attachTo(this);
|
|
||||||
|
|
||||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
||||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
|
|
||||||
const auto enableOk = [this, buttonBox] {
|
|
||||||
buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
|
|
||||||
!m_nameEdit->text().isEmpty() &&
|
|
||||||
m_deviceTypeCombo->currentIndex() > 0 &&
|
|
||||||
m_runtimeCombo->currentIndex() > 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
connect(m_nameEdit, &QLineEdit::textChanged, this, enableOk);
|
|
||||||
connect(m_runtimeCombo, &QComboBox::currentIndexChanged, this, enableOk);
|
|
||||||
connect(m_deviceTypeCombo, &QComboBox::currentIndexChanged, this, [this, enableOk] {
|
|
||||||
populateRuntimes(m_deviceTypeCombo->currentData().value<DeviceTypeInfo>());
|
|
||||||
enableOk();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_futureSync.addFuture(Utils::onResultReady(SimulatorControl::updateDeviceTypes(), this,
|
|
||||||
&CreateSimulatorDialog::populateDeviceTypes));
|
|
||||||
|
|
||||||
QFuture<QList<RuntimeInfo>> runtimesfuture = SimulatorControl::updateRuntimes();
|
|
||||||
Utils::onResultReady(runtimesfuture, this, [this](const QList<RuntimeInfo> &runtimes) {
|
|
||||||
m_runtimes = runtimes;
|
|
||||||
});
|
|
||||||
m_futureSync.addFuture(runtimesfuture);
|
|
||||||
populateRuntimes(DeviceTypeInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateSimulatorDialog::~CreateSimulatorDialog() = default;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the simulator name entered by user.
|
|
||||||
*/
|
|
||||||
QString CreateSimulatorDialog::name() const
|
|
||||||
{
|
|
||||||
return m_nameEdit->text();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the simulator runtime (OS version) selected by user.
|
|
||||||
Though the runtimes are filtered by the selected device type but the runtime camppatibility is
|
|
||||||
not checked. i.e. User can select the Runtime iOS 10.2 for iPhone 4 but the combination is not
|
|
||||||
possible as iOS 10.2 is not compatible with iPhone 4. In this case the command to create
|
|
||||||
simulator shall fail with an error message describing the compatibility.
|
|
||||||
*/
|
|
||||||
RuntimeInfo CreateSimulatorDialog::runtime() const
|
|
||||||
{
|
|
||||||
return m_runtimeCombo->currentData().value<RuntimeInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the selected device type.
|
|
||||||
*/
|
|
||||||
DeviceTypeInfo CreateSimulatorDialog::deviceType() const
|
|
||||||
{
|
|
||||||
return m_deviceTypeCombo->currentData().value<DeviceTypeInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Populates the devices types. Similar device types are grouped together.
|
|
||||||
*/
|
|
||||||
void CreateSimulatorDialog::populateDeviceTypes(const QList<DeviceTypeInfo> &deviceTypes)
|
|
||||||
{
|
|
||||||
m_deviceTypeCombo->clear();
|
|
||||||
m_deviceTypeCombo->addItem(Tr::tr("None"));
|
|
||||||
|
|
||||||
if (deviceTypes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_deviceTypeCombo->insertSeparator(1);
|
|
||||||
|
|
||||||
auto addItems = [this, deviceTypes](const QString &filter) {
|
|
||||||
const auto filteredTypes = Utils::filtered(deviceTypes, [filter](const DeviceTypeInfo &type){
|
|
||||||
return type.name.contains(filter, Qt::CaseInsensitive);
|
|
||||||
});
|
|
||||||
for (auto type : filteredTypes) {
|
|
||||||
m_deviceTypeCombo->addItem(type.name, QVariant::fromValue<DeviceTypeInfo>(type));
|
|
||||||
}
|
|
||||||
return filteredTypes.count();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (addItems(QStringLiteral("iPhone")) > 0)
|
|
||||||
m_deviceTypeCombo->insertSeparator(m_deviceTypeCombo->count());
|
|
||||||
if (addItems(QStringLiteral("iPad")) > 0)
|
|
||||||
m_deviceTypeCombo->insertSeparator(m_deviceTypeCombo->count());
|
|
||||||
if (addItems(QStringLiteral("TV")) > 0)
|
|
||||||
m_deviceTypeCombo->insertSeparator(m_deviceTypeCombo->count());
|
|
||||||
addItems(QStringLiteral("Watch"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Populates the available runtimes. Though the runtimes are filtered by the selected device type
|
|
||||||
but the runtime camppatibility is not checked. i.e. User can select the Runtime iOS 10.2 for
|
|
||||||
iPhone 4 but the combination is not possible as iOS 10.2 is not compatible with iPhone 4. In
|
|
||||||
this case the command to create simulator shall fail with an error message describing the
|
|
||||||
compatibility issue.
|
|
||||||
*/
|
|
||||||
void CreateSimulatorDialog::populateRuntimes(const DeviceTypeInfo &deviceType)
|
|
||||||
{
|
|
||||||
m_runtimeCombo->clear();
|
|
||||||
m_runtimeCombo->addItem(Tr::tr("None"));
|
|
||||||
|
|
||||||
if (deviceType.name.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_runtimeCombo->insertSeparator(1);
|
|
||||||
|
|
||||||
auto addItems = [this](const QString &filter) {
|
|
||||||
const auto filteredTypes = Utils::filtered(m_runtimes, [filter](const RuntimeInfo &runtime){
|
|
||||||
return runtime.name.contains(filter, Qt::CaseInsensitive);
|
|
||||||
});
|
|
||||||
for (auto runtime : filteredTypes) {
|
|
||||||
m_runtimeCombo->addItem(runtime.name, QVariant::fromValue<RuntimeInfo>(runtime));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (deviceType.name.contains(QStringLiteral("iPhone")))
|
|
||||||
addItems(QStringLiteral("iOS"));
|
|
||||||
else if (deviceType.name.contains(QStringLiteral("iPad")))
|
|
||||||
addItems(QStringLiteral("iOS"));
|
|
||||||
else if (deviceType.name.contains(QStringLiteral("TV")))
|
|
||||||
addItems(QStringLiteral("tvOS"));
|
|
||||||
else if (deviceType.name.contains(QStringLiteral("Watch")))
|
|
||||||
addItems(QStringLiteral("watchOS"));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
@@ -1,46 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utils/futuresynchronizer.h>
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QComboBox;
|
|
||||||
class QLineEdit;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
class DeviceTypeInfo;
|
|
||||||
class RuntimeInfo;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
A dialog to select the iOS Device type and the runtime for a new
|
|
||||||
iOS simulator device.
|
|
||||||
*/
|
|
||||||
class CreateSimulatorDialog : public QDialog
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit CreateSimulatorDialog(QWidget *parent = nullptr);
|
|
||||||
~CreateSimulatorDialog() override;
|
|
||||||
|
|
||||||
QString name() const;
|
|
||||||
RuntimeInfo runtime() const;
|
|
||||||
DeviceTypeInfo deviceType() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void populateDeviceTypes(const QList<DeviceTypeInfo> &deviceTypes);
|
|
||||||
void populateRuntimes(const DeviceTypeInfo &deviceType);
|
|
||||||
|
|
||||||
QList<RuntimeInfo> m_runtimes;
|
|
||||||
|
|
||||||
QLineEdit *m_nameEdit;
|
|
||||||
QComboBox *m_deviceTypeCombo;
|
|
||||||
QComboBox *m_runtimeCombo;
|
|
||||||
Utils::FutureSynchronizer m_futureSync; // Keep me last
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
@@ -69,7 +69,6 @@ const bool IgnoreAllDevicesDefault = false;
|
|||||||
|
|
||||||
const char SettingsGroup[] = "IosConfigurations";
|
const char SettingsGroup[] = "IosConfigurations";
|
||||||
const char ignoreAllDevicesKey[] = "IgnoreAllDevices";
|
const char ignoreAllDevicesKey[] = "IgnoreAllDevices";
|
||||||
const char screenshotDirPathKey[] = "ScreeshotDirPath";
|
|
||||||
|
|
||||||
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
||||||
const char freeTeamTag[] = "isFreeProvisioningTeam";
|
const char freeTeamTag[] = "isFreeProvisioningTeam";
|
||||||
@@ -343,19 +342,6 @@ void IosConfigurations::setIgnoreAllDevices(bool ignoreDevices)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IosConfigurations::setScreenshotDir(const FilePath &path)
|
|
||||||
{
|
|
||||||
if (m_instance->m_screenshotDir != path) {
|
|
||||||
m_instance->m_screenshotDir = path;
|
|
||||||
m_instance->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FilePath IosConfigurations::screenshotDir()
|
|
||||||
{
|
|
||||||
return m_instance->m_screenshotDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
FilePath IosConfigurations::developerPath()
|
FilePath IosConfigurations::developerPath()
|
||||||
{
|
{
|
||||||
return m_instance->m_developerPath;
|
return m_instance->m_developerPath;
|
||||||
@@ -366,20 +352,11 @@ QVersionNumber IosConfigurations::xcodeVersion()
|
|||||||
return m_instance->m_xcodeVersion;
|
return m_instance->m_xcodeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FilePath defaultScreenshotDirPath()
|
|
||||||
{
|
|
||||||
return FilePath::fromUserInput(
|
|
||||||
QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).constFirst());
|
|
||||||
}
|
|
||||||
|
|
||||||
void IosConfigurations::save()
|
void IosConfigurations::save()
|
||||||
{
|
{
|
||||||
QtcSettings *settings = Core::ICore::settings();
|
QtcSettings *settings = Core::ICore::settings();
|
||||||
settings->beginGroup(SettingsGroup);
|
settings->beginGroup(SettingsGroup);
|
||||||
settings->setValueWithDefault(ignoreAllDevicesKey, m_ignoreAllDevices, IgnoreAllDevicesDefault);
|
settings->setValueWithDefault(ignoreAllDevicesKey, m_ignoreAllDevices, IgnoreAllDevicesDefault);
|
||||||
settings->setValueWithDefault(screenshotDirPathKey,
|
|
||||||
m_screenshotDir.toSettings(),
|
|
||||||
defaultScreenshotDirPath().toSettings());
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,11 +373,6 @@ void IosConfigurations::load()
|
|||||||
QtcSettings *settings = Core::ICore::settings();
|
QtcSettings *settings = Core::ICore::settings();
|
||||||
settings->beginGroup(SettingsGroup);
|
settings->beginGroup(SettingsGroup);
|
||||||
m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, IgnoreAllDevicesDefault).toBool();
|
m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, IgnoreAllDevicesDefault).toBool();
|
||||||
m_screenshotDir = FilePath::fromSettings(settings->value(screenshotDirPathKey));
|
|
||||||
|
|
||||||
if (!m_screenshotDir.isWritableDir())
|
|
||||||
m_screenshotDir = defaultScreenshotDirPath();
|
|
||||||
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -78,8 +78,6 @@ public:
|
|||||||
static void initialize();
|
static void initialize();
|
||||||
static bool ignoreAllDevices();
|
static bool ignoreAllDevices();
|
||||||
static void setIgnoreAllDevices(bool ignoreDevices);
|
static void setIgnoreAllDevices(bool ignoreDevices);
|
||||||
static void setScreenshotDir(const Utils::FilePath &path);
|
|
||||||
static Utils::FilePath screenshotDir();
|
|
||||||
static Utils::FilePath developerPath();
|
static Utils::FilePath developerPath();
|
||||||
static QVersionNumber xcodeVersion();
|
static QVersionNumber xcodeVersion();
|
||||||
static Utils::FilePath lldbPath();
|
static Utils::FilePath lldbPath();
|
||||||
@@ -103,7 +101,6 @@ private:
|
|||||||
void loadProvisioningData(bool notify = true);
|
void loadProvisioningData(bool notify = true);
|
||||||
|
|
||||||
Utils::FilePath m_developerPath;
|
Utils::FilePath m_developerPath;
|
||||||
Utils::FilePath m_screenshotDir;
|
|
||||||
QVersionNumber m_xcodeVersion;
|
QVersionNumber m_xcodeVersion;
|
||||||
bool m_ignoreAllDevices;
|
bool m_ignoreAllDevices;
|
||||||
QFileSystemWatcher *m_provisioningDataWatcher = nullptr;
|
QFileSystemWatcher *m_provisioningDataWatcher = nullptr;
|
||||||
|
@@ -21,10 +21,11 @@
|
|||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/qtcprocess.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
@@ -342,12 +344,25 @@ void IosDeviceTypeAspect::addToLayout(Layouting::LayoutItem &parent)
|
|||||||
|
|
||||||
m_deviceTypeLabel = new QLabel(Tr::tr("Device type:"));
|
m_deviceTypeLabel = new QLabel(Tr::tr("Device type:"));
|
||||||
|
|
||||||
parent.addItems({m_deviceTypeLabel, m_deviceTypeComboBox});
|
m_updateButton = new QPushButton(Tr::tr("Update"));
|
||||||
|
|
||||||
|
parent.addItems({m_deviceTypeLabel, m_deviceTypeComboBox, m_updateButton, Layouting::st});
|
||||||
|
|
||||||
updateValues();
|
updateValues();
|
||||||
|
|
||||||
connect(m_deviceTypeComboBox, &QComboBox::currentIndexChanged,
|
connect(m_deviceTypeComboBox, &QComboBox::currentIndexChanged,
|
||||||
this, &IosDeviceTypeAspect::setDeviceTypeIndex);
|
this, &IosDeviceTypeAspect::setDeviceTypeIndex);
|
||||||
|
connect(m_updateButton, &QPushButton::clicked, this, [this] {
|
||||||
|
m_updateButton->setEnabled(false);
|
||||||
|
Utils::onFinished(
|
||||||
|
QFuture<void>(SimulatorControl::updateAvailableSimulators(this)),
|
||||||
|
this,
|
||||||
|
[this](QFuture<void>) {
|
||||||
|
m_updateButton->setEnabled(true);
|
||||||
|
m_deviceTypeModel.clear();
|
||||||
|
updateValues();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IosDeviceTypeAspect::setDeviceTypeIndex(int devIndex)
|
void IosDeviceTypeAspect::setDeviceTypeIndex(int devIndex)
|
||||||
@@ -363,6 +378,7 @@ void IosDeviceTypeAspect::updateValues()
|
|||||||
bool showDeviceSelector = deviceType().type != IosDeviceType::IosDevice;
|
bool showDeviceSelector = deviceType().type != IosDeviceType::IosDevice;
|
||||||
m_deviceTypeLabel->setVisible(showDeviceSelector);
|
m_deviceTypeLabel->setVisible(showDeviceSelector);
|
||||||
m_deviceTypeComboBox->setVisible(showDeviceSelector);
|
m_deviceTypeComboBox->setVisible(showDeviceSelector);
|
||||||
|
m_updateButton->setVisible(showDeviceSelector);
|
||||||
if (showDeviceSelector && m_deviceTypeModel.rowCount() == 0) {
|
if (showDeviceSelector && m_deviceTypeModel.rowCount() == 0) {
|
||||||
const QList<SimulatorInfo> devices = SimulatorControl::availableSimulators();
|
const QList<SimulatorInfo> devices = SimulatorControl::availableSimulators();
|
||||||
for (const SimulatorInfo &device : devices) {
|
for (const SimulatorInfo &device : devices) {
|
||||||
|
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "iosconstants.h"
|
|
||||||
#include "iosconfigurations.h"
|
|
||||||
#include "iossimulator.h"
|
#include "iossimulator.h"
|
||||||
|
|
||||||
#include <projectexplorer/runconfiguration.h>
|
#include <projectexplorer/runconfiguration.h>
|
||||||
@@ -12,9 +10,13 @@
|
|||||||
|
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QComboBox;
|
||||||
|
class QPushButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Ios::Internal {
|
namespace Ios::Internal {
|
||||||
|
|
||||||
class IosRunConfiguration;
|
class IosRunConfiguration;
|
||||||
@@ -58,6 +60,7 @@ private:
|
|||||||
QStandardItemModel m_deviceTypeModel;
|
QStandardItemModel m_deviceTypeModel;
|
||||||
QLabel *m_deviceTypeLabel = nullptr;
|
QLabel *m_deviceTypeLabel = nullptr;
|
||||||
QComboBox *m_deviceTypeComboBox = nullptr;
|
QComboBox *m_deviceTypeComboBox = nullptr;
|
||||||
|
QPushButton *m_updateButton = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IosRunConfiguration : public ProjectExplorer::RunConfiguration
|
class IosRunConfiguration : public ProjectExplorer::RunConfiguration
|
||||||
|
@@ -3,34 +3,17 @@
|
|||||||
|
|
||||||
#include "iossettingspage.h"
|
#include "iossettingspage.h"
|
||||||
|
|
||||||
#include "createsimulatordialog.h"
|
|
||||||
#include "iosconfigurations.h"
|
#include "iosconfigurations.h"
|
||||||
#include "iosconstants.h"
|
#include "iosconstants.h"
|
||||||
#include "iostr.h"
|
#include "iostr.h"
|
||||||
#include "simulatorcontrol.h"
|
|
||||||
#include "simulatorinfomodel.h"
|
|
||||||
#include "simulatoroperationdialog.h"
|
|
||||||
|
|
||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
|
||||||
#include <utils/async.h>
|
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/pathchooser.h>
|
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDateTime>
|
|
||||||
#include <QGroupBox>
|
|
||||||
#include <QHeaderView>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QTreeView>
|
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
@@ -47,43 +30,10 @@ private:
|
|||||||
|
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
|
|
||||||
void onStart();
|
|
||||||
void onCreate();
|
|
||||||
void onReset();
|
|
||||||
void onRename();
|
|
||||||
void onDelete();
|
|
||||||
void onScreenshot();
|
|
||||||
void onSelectionChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Utils::PathChooser *m_pathWidget;
|
|
||||||
QPushButton *m_startButton;
|
|
||||||
QPushButton *m_renameButton;
|
|
||||||
QPushButton *m_deleteButton;
|
|
||||||
QPushButton *m_resetButton;
|
|
||||||
QTreeView *m_deviceView;
|
|
||||||
QCheckBox *m_deviceAskCheckBox;
|
QCheckBox *m_deviceAskCheckBox;
|
||||||
};
|
};
|
||||||
|
|
||||||
const int simStartWarnCount = 4;
|
|
||||||
|
|
||||||
static SimulatorInfoList selectedSimulators(const QTreeView *deviceTreeView)
|
|
||||||
{
|
|
||||||
SimulatorInfoList list;
|
|
||||||
QItemSelectionModel *selectionModel = deviceTreeView->selectionModel();
|
|
||||||
for (QModelIndex index: selectionModel->selectedRows())
|
|
||||||
list << deviceTreeView->model()->data(index, Qt::UserRole).value<SimulatorInfo>();
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void onSimOperation(const SimulatorInfo &simInfo,
|
|
||||||
SimulatorOperationDialog *dlg,
|
|
||||||
const QString &contextStr,
|
|
||||||
const SimulatorControl::Response &response)
|
|
||||||
{
|
|
||||||
dlg->addMessage(simInfo, response, contextStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
IosSettingsWidget::IosSettingsWidget()
|
IosSettingsWidget::IosSettingsWidget()
|
||||||
{
|
{
|
||||||
setWindowTitle(Tr::tr("iOS Configuration"));
|
setWindowTitle(Tr::tr("iOS Configuration"));
|
||||||
@@ -91,43 +41,14 @@ IosSettingsWidget::IosSettingsWidget()
|
|||||||
m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode"));
|
m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode"));
|
||||||
m_deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices());
|
m_deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices());
|
||||||
|
|
||||||
m_renameButton = new QPushButton(Tr::tr("Rename"));
|
auto xcodeLabel = new QLabel(
|
||||||
m_renameButton->setEnabled(false);
|
Tr::tr("Configure available simulator devices in <a href=\"%1\">Xcode</a>.")
|
||||||
m_renameButton->setToolTip(Tr::tr("Rename a simulator device."));
|
.arg("https://developer.apple.com/documentation/xcode/"
|
||||||
|
"running-your-app-in-simulator-or-on-a-device/"
|
||||||
m_deleteButton = new QPushButton(Tr::tr("Delete"));
|
"#Configure-the-list-of-simulated-devices"));
|
||||||
m_deleteButton->setEnabled(false);
|
xcodeLabel->setOpenExternalLinks(true);
|
||||||
m_deleteButton->setToolTip(Tr::tr("Delete simulator devices."));
|
|
||||||
|
|
||||||
m_resetButton = new QPushButton(Tr::tr("Reset"));
|
|
||||||
m_resetButton->setEnabled(false);
|
|
||||||
m_resetButton->setToolTip(Tr::tr("Reset contents and settings of simulator devices."));
|
|
||||||
|
|
||||||
auto createButton = new QPushButton(Tr::tr("Create"));
|
|
||||||
createButton->setToolTip(Tr::tr("Create a new simulator device."));
|
|
||||||
|
|
||||||
m_startButton = new QPushButton(Tr::tr("Start"));
|
|
||||||
m_startButton->setEnabled(false);
|
|
||||||
m_startButton->setToolTip(Tr::tr("Start simulator devices."));
|
|
||||||
|
|
||||||
auto proxyModel = new QSortFilterProxyModel(this);
|
|
||||||
proxyModel->setSourceModel(new SimulatorInfoModel(this));
|
|
||||||
|
|
||||||
m_deviceView = new QTreeView;
|
|
||||||
m_deviceView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
|
|
||||||
m_deviceView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
m_deviceView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
m_deviceView->setSortingEnabled(true);
|
|
||||||
m_deviceView->setModel(proxyModel);
|
|
||||||
m_deviceView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
|
||||||
|
|
||||||
m_pathWidget = new Utils::PathChooser;
|
|
||||||
m_pathWidget->setExpectedKind(Utils::PathChooser::ExistingDirectory);
|
|
||||||
m_pathWidget->lineEdit()->setReadOnly(true);
|
|
||||||
m_pathWidget->setFilePath(IosConfigurations::screenshotDir());
|
|
||||||
m_pathWidget->addButton(Tr::tr("Screenshot"), this,
|
|
||||||
std::bind(&IosSettingsWidget::onScreenshot, this));
|
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
Column {
|
Column {
|
||||||
Group {
|
Group {
|
||||||
@@ -136,33 +57,11 @@ IosSettingsWidget::IosSettingsWidget()
|
|||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
title(Tr::tr("Simulator")),
|
title(Tr::tr("Simulator")),
|
||||||
Column {
|
Row { xcodeLabel }
|
||||||
Row {
|
},
|
||||||
m_deviceView,
|
|
||||||
Column {
|
|
||||||
createButton,
|
|
||||||
st, // FIXME: Better some fixed space?
|
|
||||||
m_startButton,
|
|
||||||
m_renameButton,
|
|
||||||
m_resetButton,
|
|
||||||
m_deleteButton,
|
|
||||||
st
|
st
|
||||||
},
|
|
||||||
},
|
|
||||||
hr,
|
|
||||||
Row { Tr::tr("Screenshot directory:"), m_pathWidget }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
|
// clang-format on
|
||||||
connect(m_startButton, &QPushButton::clicked, this, &IosSettingsWidget::onStart);
|
|
||||||
connect(createButton, &QPushButton::clicked, this, &IosSettingsWidget::onCreate);
|
|
||||||
connect(m_renameButton, &QPushButton::clicked, this, &IosSettingsWidget::onRename);
|
|
||||||
connect(m_resetButton, &QPushButton::clicked, this, &IosSettingsWidget::onReset);
|
|
||||||
connect(m_deleteButton, &QPushButton::clicked, this, &IosSettingsWidget::onDelete);
|
|
||||||
|
|
||||||
connect(m_deviceView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
||||||
this, &IosSettingsWidget::onSelectionChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IosSettingsWidget::~IosSettingsWidget() = default;
|
IosSettingsWidget::~IosSettingsWidget() = default;
|
||||||
@@ -173,229 +72,9 @@ void IosSettingsWidget::apply()
|
|||||||
IosConfigurations::updateAutomaticKitList();
|
IosConfigurations::updateAutomaticKitList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on start button click. Selected simulator devices are started. Multiple devices can be
|
|
||||||
started simultaneously provided they in shutdown state.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onStart()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView);
|
|
||||||
if (simulatorInfoList.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (simulatorInfoList.count() > simStartWarnCount) {
|
|
||||||
const QString message =
|
|
||||||
Tr::tr("You are trying to launch %n simulators simultaneously. This "
|
|
||||||
"will take significant system resources. Do you really want to "
|
|
||||||
"continue?", "", simulatorInfoList.count());
|
|
||||||
const int buttonCode = QMessageBox::warning(this, Tr::tr("Simulator Start"), message,
|
|
||||||
QMessageBox::Ok | QMessageBox::Abort,
|
|
||||||
QMessageBox::Abort);
|
|
||||||
|
|
||||||
if (buttonCode == QMessageBox::Abort)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Starting %n simulator device(s)...", "", simulatorInfoList.count()),
|
|
||||||
Utils::NormalMessageFormat);
|
|
||||||
|
|
||||||
QList<QFuture<void>> futureList;
|
|
||||||
for (const SimulatorInfo &info : simulatorInfoList) {
|
|
||||||
if (!info.isShutdown()) {
|
|
||||||
statusDialog->addMessage(Tr::tr("Cannot start simulator (%1, %2) in current state: %3.")
|
|
||||||
.arg(info.name)
|
|
||||||
.arg(info.runtimeName)
|
|
||||||
.arg(info.state),
|
|
||||||
Utils::StdErrFormat);
|
|
||||||
} else {
|
|
||||||
futureList << QFuture<void>(Utils::onResultReady(
|
|
||||||
SimulatorControl::startSimulator(info.identifier), this,
|
|
||||||
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator start"), _1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
statusDialog->addFutures(futureList);
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on create button click. User is presented with the create simulator dialog and with the
|
|
||||||
selected options a new device is created.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onCreate()
|
|
||||||
{
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Creating simulator device..."), Utils::NormalMessageFormat);
|
|
||||||
const auto onSimulatorCreate = [statusDialog](const QString &name,
|
|
||||||
const SimulatorControl::Response &response) {
|
|
||||||
if (response) {
|
|
||||||
statusDialog->addMessage(Tr::tr("Simulator device (%1) created.\nUDID: %2")
|
|
||||||
.arg(name)
|
|
||||||
.arg(response->simUdid),
|
|
||||||
Utils::StdOutFormat);
|
|
||||||
} else {
|
|
||||||
statusDialog->addMessage(Tr::tr("Simulator device (%1) creation failed.\nError: %2")
|
|
||||||
.arg(name)
|
|
||||||
.arg(response.error()),
|
|
||||||
Utils::StdErrFormat);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CreateSimulatorDialog createDialog(this);
|
|
||||||
if (createDialog.exec() == QDialog::Accepted) {
|
|
||||||
QFuture<void> f = QFuture<void>(Utils::onResultReady(SimulatorControl::createSimulator(
|
|
||||||
createDialog.name(), createDialog.deviceType(), createDialog.runtime()),
|
|
||||||
this, std::bind(onSimulatorCreate, createDialog.name(), _1)));
|
|
||||||
statusDialog->addFutures({ f });
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on reset button click. Contents and settings of the selected devices are erased. Multiple
|
|
||||||
devices can be erased simultaneously provided they in shutdown state.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onReset()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView);
|
|
||||||
if (simulatorInfoList.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int userInput = QMessageBox::question(this, Tr::tr("Reset"),
|
|
||||||
Tr::tr("Do you really want to reset the contents and settings"
|
|
||||||
" of the %n selected device(s)?", "",
|
|
||||||
simulatorInfoList.count()));
|
|
||||||
if (userInput == QMessageBox::No)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Resetting contents and settings..."),
|
|
||||||
Utils::NormalMessageFormat);
|
|
||||||
|
|
||||||
QList<QFuture<void>> futureList;
|
|
||||||
for (const SimulatorInfo &info : simulatorInfoList) {
|
|
||||||
futureList << QFuture<void>(Utils::onResultReady(
|
|
||||||
SimulatorControl::resetSimulator(info.identifier), this,
|
|
||||||
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator reset"), _1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
statusDialog->addFutures(futureList);
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on rename button click. Selected device is renamed. Only one device can be renamed at a
|
|
||||||
time. Rename button is disabled on multi-selection.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onRename()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView);
|
|
||||||
if (simulatorInfoList.isEmpty() || simulatorInfoList.count() > 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const SimulatorInfo &simInfo = simulatorInfoList.at(0);
|
|
||||||
const QString newName = QInputDialog::getText(this, Tr::tr("Rename %1").arg(simInfo.name),
|
|
||||||
Tr::tr("Enter new name:"));
|
|
||||||
if (newName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Renaming simulator device..."), Utils::NormalMessageFormat);
|
|
||||||
QFuture<void> f = QFuture<void>(Utils::onResultReady(
|
|
||||||
SimulatorControl::renameSimulator(simInfo.identifier, newName), this,
|
|
||||||
std::bind(onSimOperation, simInfo, statusDialog, Tr::tr("simulator rename"), _1)));
|
|
||||||
statusDialog->addFutures({f});
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on delete button click. Selected devices are deleted. Multiple devices can be deleted
|
|
||||||
simultaneously provided they in shutdown state.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onDelete()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView);
|
|
||||||
if (simulatorInfoList.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int userInput =
|
|
||||||
QMessageBox::question(this, Tr::tr("Delete Device"),
|
|
||||||
Tr::tr("Do you really want to delete the %n selected "
|
|
||||||
"device(s)?", "", simulatorInfoList.count()));
|
|
||||||
if (userInput == QMessageBox::No)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Deleting %n simulator device(s)...", "", simulatorInfoList.count()),
|
|
||||||
Utils::NormalMessageFormat);
|
|
||||||
QList<QFuture<void>> futureList;
|
|
||||||
for (const SimulatorInfo &info : simulatorInfoList) {
|
|
||||||
futureList << QFuture<void>(Utils::onResultReady(
|
|
||||||
SimulatorControl::deleteSimulator(info.identifier), this,
|
|
||||||
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator delete"), _1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
statusDialog->addFutures(futureList);
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Called on screenshot button click. Screenshot of the selected devices are saved to the selected
|
|
||||||
path. Screenshot from multiple devices can be taken simultaneously provided they in booted state.
|
|
||||||
*/
|
|
||||||
void IosSettingsWidget::onScreenshot()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView);
|
|
||||||
if (simulatorInfoList.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto generatePath = [this](const SimulatorInfo &info) {
|
|
||||||
const QString fileName = QString("%1_%2_%3.png").arg(info.name).arg(info.runtimeName)
|
|
||||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss-z")).replace(' ', '_');
|
|
||||||
return m_pathWidget->filePath().pathAppended(fileName).toString();
|
|
||||||
};
|
|
||||||
|
|
||||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
|
||||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
statusDialog->addMessage(Tr::tr("Capturing screenshots from %n device(s)...", "",
|
|
||||||
simulatorInfoList.count()), Utils::NormalMessageFormat);
|
|
||||||
QList<QFuture<void>> futureList;
|
|
||||||
for (const SimulatorInfo &info : simulatorInfoList) {
|
|
||||||
futureList << QFuture<void>(Utils::onResultReady(
|
|
||||||
SimulatorControl::takeSceenshot(info.identifier, generatePath(info)), this,
|
|
||||||
std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator screenshot"), _1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
statusDialog->addFutures(futureList);
|
|
||||||
statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled.
|
|
||||||
}
|
|
||||||
|
|
||||||
void IosSettingsWidget::onSelectionChanged()
|
|
||||||
{
|
|
||||||
const SimulatorInfoList infoList = selectedSimulators(m_deviceView);
|
|
||||||
const bool hasRunning = Utils::anyOf(infoList, [](const SimulatorInfo &info) {
|
|
||||||
return info.isBooted();
|
|
||||||
});
|
|
||||||
const bool hasShutdown = Utils::anyOf(infoList, [](const SimulatorInfo &info) {
|
|
||||||
return info.isShutdown();
|
|
||||||
});
|
|
||||||
m_startButton->setEnabled(hasShutdown);
|
|
||||||
m_deleteButton->setEnabled(hasShutdown);
|
|
||||||
m_resetButton->setEnabled(hasShutdown);
|
|
||||||
m_renameButton->setEnabled(infoList.count() == 1 && hasShutdown);
|
|
||||||
m_pathWidget->buttonAtIndex(1)->setEnabled(hasRunning); // Screenshot button
|
|
||||||
}
|
|
||||||
|
|
||||||
void IosSettingsWidget::saveSettings()
|
void IosSettingsWidget::saveSettings()
|
||||||
{
|
{
|
||||||
IosConfigurations::setIgnoreAllDevices(!m_deviceAskCheckBox->isChecked());
|
IosConfigurations::setIgnoreAllDevices(!m_deviceAskCheckBox->isChecked());
|
||||||
IosConfigurations::setScreenshotDir(m_pathWidget->filePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IosSettingsPage
|
// IosSettingsPage
|
||||||
|
@@ -1,142 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "simulatorinfomodel.h"
|
|
||||||
|
|
||||||
#include "iostr.h"
|
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
|
||||||
#include <utils/async.h>
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
using namespace std::placeholders;
|
|
||||||
|
|
||||||
const int colCount = 3;
|
|
||||||
const int nameCol = 0;
|
|
||||||
const int runtimeCol = 1;
|
|
||||||
const int stateCol = 2;
|
|
||||||
const int deviceUpdateInterval = 1000; // Update simulator state every 1 sec.
|
|
||||||
|
|
||||||
SimulatorInfoModel::SimulatorInfoModel(QObject *parent) :
|
|
||||||
QAbstractItemModel(parent)
|
|
||||||
{
|
|
||||||
requestSimulatorInfo();
|
|
||||||
|
|
||||||
auto updateTimer = new QTimer(this);
|
|
||||||
connect(updateTimer, &QTimer::timeout, this, &SimulatorInfoModel::requestSimulatorInfo);
|
|
||||||
updateTimer->setInterval(deviceUpdateInterval);
|
|
||||||
updateTimer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant SimulatorInfoModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const SimulatorInfo &simInfo = m_simList[index.row()];
|
|
||||||
if (role == Qt::EditRole || role == Qt::DisplayRole) {
|
|
||||||
switch (index.column()) {
|
|
||||||
case nameCol:
|
|
||||||
return simInfo.name;
|
|
||||||
case runtimeCol:
|
|
||||||
return simInfo.runtimeName;
|
|
||||||
case stateCol:
|
|
||||||
return simInfo.state;
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
} else if (role == Qt::ToolTipRole) {
|
|
||||||
return Tr::tr("UDID: %1").arg(simInfo.identifier);
|
|
||||||
} else if (role == Qt::UserRole) {
|
|
||||||
return QVariant::fromValue<SimulatorInfo>(simInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int SimulatorInfoModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
if (!parent.isValid())
|
|
||||||
return m_simList.count();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SimulatorInfoModel::columnCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent)
|
|
||||||
return colCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant SimulatorInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
||||||
{
|
|
||||||
if (orientation == Qt::Vertical || section > colCount)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
|
||||||
switch (section) {
|
|
||||||
case nameCol:
|
|
||||||
return Tr::tr("Simulator Name");
|
|
||||||
case runtimeCol:
|
|
||||||
return Tr::tr("Runtime");
|
|
||||||
case stateCol:
|
|
||||||
return Tr::tr("Current State");
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex SimulatorInfoModel::index(int row, int column, const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex SimulatorInfoModel::parent(const QModelIndex &) const
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorInfoModel::requestSimulatorInfo()
|
|
||||||
{
|
|
||||||
m_fetchFuture.flushFinishedFutures();
|
|
||||||
if (!m_fetchFuture.isEmpty())
|
|
||||||
return; // Ignore the request if the last request is still pending.
|
|
||||||
|
|
||||||
m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(this),
|
|
||||||
this, &SimulatorInfoModel::populateSimulators));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorInfoModel::populateSimulators(const SimulatorInfoList &simulatorList)
|
|
||||||
{
|
|
||||||
if (m_simList.isEmpty() || m_simList.count() != simulatorList.count()) {
|
|
||||||
// Reset the model in case of addition or deletion.
|
|
||||||
beginResetModel();
|
|
||||||
m_simList = simulatorList;
|
|
||||||
endResetModel();
|
|
||||||
} else {
|
|
||||||
// update the rows with data chagne. e.g. state changes.
|
|
||||||
auto newItr = simulatorList.cbegin();
|
|
||||||
int start = -1, end = -1;
|
|
||||||
std::list<std::pair<int, int>> updatedIndexes;
|
|
||||||
for (auto itr = m_simList.cbegin(); itr < m_simList.cend(); ++itr, ++newItr) {
|
|
||||||
if (*itr == *newItr) {
|
|
||||||
if (end != -1)
|
|
||||||
updatedIndexes.emplace_back(start, end - 1);
|
|
||||||
start = std::distance(m_simList.cbegin(), itr);
|
|
||||||
end = -1;
|
|
||||||
} else {
|
|
||||||
end = std::distance(m_simList.cbegin(), itr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_simList = simulatorList;
|
|
||||||
for (auto pair: updatedIndexes)
|
|
||||||
emit dataChanged(index(pair.first,0), index(pair.second, colCount - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
@@ -1,37 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "simulatorcontrol.h"
|
|
||||||
|
|
||||||
#include <utils/futuresynchronizer.h>
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
using SimulatorInfoList = QList<SimulatorInfo>;
|
|
||||||
|
|
||||||
class SimulatorInfoModel : public QAbstractItemModel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SimulatorInfoModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
int columnCount(const QModelIndex &parent) const override;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation,
|
|
||||||
int role = Qt::DisplayRole) const override;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
QModelIndex parent(const QModelIndex &) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void requestSimulatorInfo();
|
|
||||||
void populateSimulators(const SimulatorInfoList &simulatorList);
|
|
||||||
|
|
||||||
Utils::FutureSynchronizer m_fetchFuture;
|
|
||||||
SimulatorInfoList m_simList;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
@@ -1,125 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "simulatoroperationdialog.h"
|
|
||||||
|
|
||||||
#include "iostr.h"
|
|
||||||
|
|
||||||
#include <utils/layoutbuilder.h>
|
|
||||||
#include <utils/outputformatter.h>
|
|
||||||
#include <utils/qtcassert.h>
|
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QPlainTextEdit>
|
|
||||||
#include <QProgressBar>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
static Q_LOGGING_CATEGORY(iosCommon, "qtc.ios.common", QtWarningMsg)
|
|
||||||
|
|
||||||
SimulatorOperationDialog::SimulatorOperationDialog(QWidget *parent) :
|
|
||||||
// TODO: Maximize buttong only because of QTBUG-41932
|
|
||||||
QDialog(parent,Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint)
|
|
||||||
{
|
|
||||||
resize(580, 320);
|
|
||||||
setModal(true);
|
|
||||||
setWindowTitle(Tr::tr("Simulator Operation Status"));
|
|
||||||
|
|
||||||
auto messageEdit = new QPlainTextEdit;
|
|
||||||
messageEdit->setReadOnly(true);
|
|
||||||
|
|
||||||
m_progressBar = new QProgressBar;
|
|
||||||
m_progressBar->setMaximum(0);
|
|
||||||
m_progressBar->setValue(-1);
|
|
||||||
|
|
||||||
m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
|
||||||
|
|
||||||
m_formatter = new Utils::OutputFormatter;
|
|
||||||
m_formatter->setPlainTextEdit(messageEdit);
|
|
||||||
|
|
||||||
using namespace Layouting;
|
|
||||||
|
|
||||||
Column {
|
|
||||||
messageEdit,
|
|
||||||
m_progressBar,
|
|
||||||
m_buttonBox
|
|
||||||
}.attachTo(this);
|
|
||||||
|
|
||||||
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
||||||
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
}
|
|
||||||
|
|
||||||
SimulatorOperationDialog::~SimulatorOperationDialog()
|
|
||||||
{
|
|
||||||
// Cancel all pending futures.
|
|
||||||
const auto futureWatchList = m_futureWatchList;
|
|
||||||
for (auto watcher : futureWatchList) {
|
|
||||||
if (!watcher->isFinished())
|
|
||||||
watcher->cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for futures to finish
|
|
||||||
for (auto watcher : futureWatchList) {
|
|
||||||
if (!watcher->isFinished())
|
|
||||||
watcher->waitForFinished();
|
|
||||||
delete watcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete m_formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorOperationDialog::addFutures(const QList<QFuture<void> > &futureList)
|
|
||||||
{
|
|
||||||
for (auto future : futureList) {
|
|
||||||
if (!future.isFinished() || !future.isCanceled()) {
|
|
||||||
auto watcher = new QFutureWatcher<void>;
|
|
||||||
connect(watcher, &QFutureWatcherBase::finished, this, [this, watcher] {
|
|
||||||
m_futureWatchList.removeAll(watcher);
|
|
||||||
watcher->deleteLater();
|
|
||||||
updateInputs();
|
|
||||||
});
|
|
||||||
watcher->setFuture(future);
|
|
||||||
m_futureWatchList << watcher;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateInputs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorOperationDialog::addMessage(const QString &message, Utils::OutputFormat format)
|
|
||||||
{
|
|
||||||
m_formatter->appendMessage(message + "\n\n", format);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorOperationDialog::addMessage(const SimulatorInfo &siminfo,
|
|
||||||
const SimulatorControl::Response &response,
|
|
||||||
const QString &context)
|
|
||||||
{
|
|
||||||
if (response) {
|
|
||||||
QTC_CHECK(siminfo.identifier == response->simUdid);
|
|
||||||
addMessage(Tr::tr("%1, %2\nOperation %3 completed successfully.").arg(siminfo.name)
|
|
||||||
.arg(siminfo.runtimeName).arg(context), Utils::StdOutFormat);
|
|
||||||
} else {
|
|
||||||
QString erroMsg = response.error();
|
|
||||||
QString message = Tr::tr("%1, %2\nOperation %3 failed.\nUDID: %4\nError: %5").arg(siminfo.name)
|
|
||||||
.arg(siminfo.runtimeName).arg(context).arg(siminfo.identifier)
|
|
||||||
.arg(erroMsg.isEmpty() ? Tr::tr("Unknown") : erroMsg);
|
|
||||||
addMessage(message, Utils::StdErrFormat);
|
|
||||||
qCDebug(iosCommon) << message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulatorOperationDialog::updateInputs()
|
|
||||||
{
|
|
||||||
bool enableOk = m_futureWatchList.isEmpty();
|
|
||||||
m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(!enableOk);
|
|
||||||
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enableOk);
|
|
||||||
if (enableOk) {
|
|
||||||
addMessage(Tr::tr("Done."), Utils::NormalMessageFormat);
|
|
||||||
m_progressBar->setMaximum(1); // Stop progress bar.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
@@ -1,47 +0,0 @@
|
|||||||
// Copyright (C) 2017 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "simulatorcontrol.h"
|
|
||||||
|
|
||||||
#include <utils/outputformat.h>
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QDialogButtonBox;
|
|
||||||
class QProgressBar;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Utils { class OutputFormatter; }
|
|
||||||
|
|
||||||
namespace Ios::Internal {
|
|
||||||
|
|
||||||
class SimulatorOperationDialog : public QDialog
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SimulatorOperationDialog(QWidget *parent = nullptr);
|
|
||||||
~SimulatorOperationDialog() override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void addFutures(const QList<QFuture<void> > &futureList);
|
|
||||||
void addMessage(const QString &message, Utils::OutputFormat format);
|
|
||||||
void addMessage(const SimulatorInfo &siminfo,
|
|
||||||
const SimulatorControl::Response &response,
|
|
||||||
const QString &context);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void updateInputs();
|
|
||||||
|
|
||||||
Utils::OutputFormatter *m_formatter = nullptr;
|
|
||||||
|
|
||||||
QList<QFutureWatcher<void> *> m_futureWatchList;
|
|
||||||
QProgressBar *m_progressBar;
|
|
||||||
QDialogButtonBox *m_buttonBox;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Ios::Internal
|
|
Reference in New Issue
Block a user