forked from qt-creator/qt-creator
iOS: Add UI for simulator device management
UI under devices tab to enable iOS simulator device management Task-number: QTCREATORBUG-17602 Change-Id: I66dbf57f07dac107c253518ded5ffd78b8ce4555 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
179
src/plugins/ios/createsimulatordialog.cpp
Normal file
179
src/plugins/ios/createsimulatordialog.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "createsimulatordialog.h"
|
||||
#include "ui_createsimulatordialog.h"
|
||||
#include "simulatorcontrol.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QVariant>
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
CreateSimulatorDialog::CreateSimulatorDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
m_ui(new Ui::CreateSimulatorDialog),
|
||||
m_simControl(new SimulatorControl(this))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
const auto enableOk = [this]() {
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
|
||||
!m_ui->nameEdit->text().isEmpty() &&
|
||||
m_ui->deviceTypeCombo->currentIndex() > 0 &&
|
||||
m_ui->runtimeCombo->currentIndex() > 0);
|
||||
};
|
||||
|
||||
const auto indexChanged = static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged);
|
||||
connect(m_ui->nameEdit, &QLineEdit::textChanged, enableOk);
|
||||
connect(m_ui->runtimeCombo, indexChanged, enableOk);
|
||||
connect(m_ui->deviceTypeCombo, indexChanged, [this, enableOk]() {
|
||||
populateRuntimes(m_ui->deviceTypeCombo->currentData().value<DeviceTypeInfo>());
|
||||
enableOk();
|
||||
});
|
||||
|
||||
m_futureSync.setCancelOnWait(true);
|
||||
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()
|
||||
{
|
||||
m_futureSync.waitForFinished();
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the the simulator name entered by user.
|
||||
*/
|
||||
QString CreateSimulatorDialog::name() const
|
||||
{
|
||||
return m_ui->nameEdit->text();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the 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_ui->runtimeCombo->currentData().value<RuntimeInfo>();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the the selected device type.
|
||||
*/
|
||||
DeviceTypeInfo CreateSimulatorDialog::deviceType() const
|
||||
{
|
||||
return m_ui->deviceTypeCombo->currentData().value<DeviceTypeInfo>();
|
||||
}
|
||||
|
||||
/*!
|
||||
Populates the devices types. Similar device types are grouped together.
|
||||
*/
|
||||
void CreateSimulatorDialog::populateDeviceTypes(const QList<DeviceTypeInfo> &deviceTypes)
|
||||
{
|
||||
m_ui->deviceTypeCombo->clear();
|
||||
m_ui->deviceTypeCombo->addItem(tr("None"));
|
||||
|
||||
if (deviceTypes.isEmpty())
|
||||
return;
|
||||
|
||||
m_ui->deviceTypeCombo->insertSeparator(1);
|
||||
|
||||
auto addItems = [this, deviceTypes](const QString &filter) {
|
||||
auto filteredTypes = Utils::filtered(deviceTypes, [filter](const DeviceTypeInfo &type){
|
||||
return type.name.contains(filter, Qt::CaseInsensitive);
|
||||
});
|
||||
foreach (auto type, filteredTypes) {
|
||||
m_ui->deviceTypeCombo->addItem(type.name, QVariant::fromValue<DeviceTypeInfo>(type));
|
||||
}
|
||||
return filteredTypes.count();
|
||||
};
|
||||
|
||||
if (addItems(QStringLiteral("iPhone")) > 0)
|
||||
m_ui->deviceTypeCombo->insertSeparator(m_ui->deviceTypeCombo->count());
|
||||
if (addItems(QStringLiteral("iPad")) > 0)
|
||||
m_ui->deviceTypeCombo->insertSeparator(m_ui->deviceTypeCombo->count());
|
||||
if (addItems(QStringLiteral("TV")) > 0)
|
||||
m_ui->deviceTypeCombo->insertSeparator(m_ui->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_ui->runtimeCombo->clear();
|
||||
m_ui->runtimeCombo->addItem(tr("None"));
|
||||
|
||||
if (deviceType.name.isEmpty())
|
||||
return;
|
||||
|
||||
m_ui->runtimeCombo->insertSeparator(1);
|
||||
|
||||
auto addItems = [this](const QString &filter) {
|
||||
auto filteredTypes = Utils::filtered(m_runtimes, [filter](const RuntimeInfo &runtime){
|
||||
return runtime.name.contains(filter, Qt::CaseInsensitive);
|
||||
});
|
||||
foreach (auto runtime, filteredTypes) {
|
||||
m_ui->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"));
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
67
src/plugins/ios/createsimulatordialog.h
Normal file
67
src/plugins/ios/createsimulatordialog.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 <QDialog>
|
||||
#include <QFutureSynchronizer>
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
namespace Ui { class CreateSimulatorDialog; }
|
||||
class SimulatorControl;
|
||||
class RuntimeInfo;
|
||||
class DeviceTypeInfo;
|
||||
|
||||
/*!
|
||||
A dialog to select the iOS Device type and the runtime for a new
|
||||
iOS simulator device.
|
||||
*/
|
||||
class CreateSimulatorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CreateSimulatorDialog(QWidget *parent = nullptr);
|
||||
~CreateSimulatorDialog();
|
||||
|
||||
QString name() const;
|
||||
RuntimeInfo runtime() const;
|
||||
DeviceTypeInfo deviceType() const;
|
||||
|
||||
private:
|
||||
void populateDeviceTypes(const QList<DeviceTypeInfo> &deviceTypes);
|
||||
void populateRuntimes(const DeviceTypeInfo &deviceType);
|
||||
|
||||
private:
|
||||
QFutureSynchronizer<void> m_futureSync;
|
||||
Ui::CreateSimulatorDialog *m_ui = nullptr;
|
||||
SimulatorControl *m_simControl = nullptr;
|
||||
QList<RuntimeInfo> m_runtimes;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
104
src/plugins/ios/createsimulatordialog.ui
Normal file
104
src/plugins/ios/createsimulatordialog.ui
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Ios::Internal::CreateSimulatorDialog</class>
|
||||
<widget class="QDialog" name="Ios::Internal::CreateSimulatorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>160</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Simulator</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Simulator name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="nameEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Device type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="deviceTypeCombo"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>OS version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="runtimeCombo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Ios::Internal::CreateSimulatorDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Ios::Internal::CreateSimulatorDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@@ -33,7 +33,10 @@ HEADERS += \
|
||||
iosdeploystepwidget.h \
|
||||
simulatorcontrol.h \
|
||||
iosbuildconfiguration.h \
|
||||
iosbuildsettingswidget.h
|
||||
iosbuildsettingswidget.h \
|
||||
createsimulatordialog.h \
|
||||
simulatoroperationdialog.h \
|
||||
simulatorinfomodel.h
|
||||
|
||||
|
||||
SOURCES += \
|
||||
@@ -61,14 +64,19 @@ SOURCES += \
|
||||
iosdeploystepwidget.cpp \
|
||||
simulatorcontrol.cpp \
|
||||
iosbuildconfiguration.cpp \
|
||||
iosbuildsettingswidget.cpp
|
||||
iosbuildsettingswidget.cpp \
|
||||
createsimulatordialog.cpp \
|
||||
simulatoroperationdialog.cpp \
|
||||
simulatorinfomodel.cpp
|
||||
|
||||
FORMS += \
|
||||
iossettingswidget.ui \
|
||||
iosbuildstep.ui \
|
||||
iosdeploystepwidget.ui \
|
||||
iospresetbuildstep.ui \
|
||||
iosbuildsettingswidget.ui
|
||||
iosbuildsettingswidget.ui \
|
||||
createsimulatordialog.ui \
|
||||
simulatoroperationdialog.ui
|
||||
|
||||
DEFINES += IOS_LIBRARY
|
||||
|
||||
|
@@ -14,6 +14,9 @@ QtcPlugin {
|
||||
cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["CoreFoundation", "IOKit"] : [])
|
||||
|
||||
files: [
|
||||
"createsimulatordialog.cpp",
|
||||
"createsimulatordialog.h",
|
||||
"createsimulatordialog.ui",
|
||||
"ios.qrc",
|
||||
"iosbuildconfiguration.cpp",
|
||||
"iosbuildconfiguration.h",
|
||||
@@ -70,6 +73,11 @@ QtcPlugin {
|
||||
"iostoolhandler.cpp",
|
||||
"iostoolhandler.h",
|
||||
"simulatorcontrol.cpp",
|
||||
"simulatorcontrol.h"
|
||||
"simulatorcontrol.h",
|
||||
"simulatorinfomodel.cpp",
|
||||
"simulatorinfomodel.h",
|
||||
"simulatoroperationdialog.cpp",
|
||||
"simulatoroperationdialog.h",
|
||||
"simulatoroperationdialog.ui"
|
||||
]
|
||||
}
|
||||
|
@@ -59,6 +59,8 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
@@ -77,6 +79,7 @@ namespace Internal {
|
||||
|
||||
const QLatin1String SettingsGroup("IosConfigurations");
|
||||
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
|
||||
const char screenshotDirPathKey[] = "ScreeshotDirPath";
|
||||
|
||||
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
||||
const char freeTeamTag[] = "isFreeProvisioningTeam";
|
||||
@@ -355,6 +358,19 @@ void IosConfigurations::setIgnoreAllDevices(bool ignoreDevices)
|
||||
}
|
||||
}
|
||||
|
||||
void IosConfigurations::setScreenshotDir(const FileName &path)
|
||||
{
|
||||
if (m_instance->m_screenshotDir != path) {
|
||||
m_instance->m_screenshotDir = path;
|
||||
m_instance->save();
|
||||
}
|
||||
}
|
||||
|
||||
FileName IosConfigurations::screenshotDir()
|
||||
{
|
||||
return m_instance->m_screenshotDir;
|
||||
}
|
||||
|
||||
FileName IosConfigurations::developerPath()
|
||||
{
|
||||
return m_instance->m_developerPath;
|
||||
@@ -370,6 +386,7 @@ void IosConfigurations::save()
|
||||
QSettings *settings = Core::ICore::settings();
|
||||
settings->beginGroup(SettingsGroup);
|
||||
settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices);
|
||||
settings->setValue(screenshotDirPathKey, m_screenshotDir.toString());
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
@@ -384,6 +401,12 @@ void IosConfigurations::load()
|
||||
QSettings *settings = Core::ICore::settings();
|
||||
settings->beginGroup(SettingsGroup);
|
||||
m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, false).toBool();
|
||||
m_screenshotDir = FileName::fromString(settings->value(screenshotDirPathKey).toString());
|
||||
if (!m_screenshotDir.exists()) {
|
||||
QString defaultDir = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first();
|
||||
m_screenshotDir = FileName::fromString(defaultDir);
|
||||
}
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
|
@@ -113,6 +113,8 @@ public:
|
||||
static void initialize();
|
||||
static bool ignoreAllDevices();
|
||||
static void setIgnoreAllDevices(bool ignoreDevices);
|
||||
static void setScreenshotDir(const Utils::FileName &path);
|
||||
static Utils::FileName screenshotDir();
|
||||
static Utils::FileName developerPath();
|
||||
static QVersionNumber xcodeVersion();
|
||||
static Utils::FileName lldbPath();
|
||||
@@ -135,6 +137,7 @@ private:
|
||||
void loadProvisioningData(bool notify = true);
|
||||
|
||||
Utils::FileName m_developerPath;
|
||||
Utils::FileName m_screenshotDir;
|
||||
QVersionNumber m_xcodeVersion;
|
||||
bool m_ignoreAllDevices;
|
||||
QFileSystemWatcher *m_provisioningDataWatcher = nullptr;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -24,36 +24,68 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "iossettingswidget.h"
|
||||
|
||||
#include "ui_iossettingswidget.h"
|
||||
|
||||
#include "createsimulatordialog.h"
|
||||
#include "iosconfigurations.h"
|
||||
#include "iosconstants.h"
|
||||
#include "simulatorinfomodel.h"
|
||||
#include "simulatoroperationdialog.h"
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <projectexplorer/toolchainmanager.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <qtsupport/qtkitinformation.h>
|
||||
#include <qtsupport/qtversionmanager.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QProcess>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QDateTime>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QModelIndex>
|
||||
#include <QPointer>
|
||||
|
||||
static const int simStartWarnCount = 4;
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
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::ResponseData &response)
|
||||
{
|
||||
dlg->addMessage(simInfo, response, contextStr);
|
||||
}
|
||||
|
||||
IosSettingsWidget::IosSettingsWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
m_ui(new Ui::IosSettingsWidget),
|
||||
m_saveSettingsRequested(false)
|
||||
m_simControl(new SimulatorControl(this)),
|
||||
m_simInfoModel( new SimulatorInfoModel(this))
|
||||
{
|
||||
initGui();
|
||||
m_ui->setupUi(this);
|
||||
m_ui->deviceView->setModel(m_simInfoModel);
|
||||
m_ui->deviceView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
m_ui->pathWidget->setExpectedKind(Utils::PathChooser::ExistingDirectory);
|
||||
m_ui->pathWidget->lineEdit()->setReadOnly(true);
|
||||
m_ui->pathWidget->setFileName(IosConfigurations::screenshotDir());
|
||||
m_ui->pathWidget->addButton(tr("Screenshot"), this,
|
||||
std::bind(&IosSettingsWidget::onScreenshot, this));
|
||||
|
||||
m_ui->deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices());
|
||||
|
||||
connect(m_ui->startButton, &QPushButton::clicked, this, &IosSettingsWidget::onStart);
|
||||
connect(m_ui->createButton, &QPushButton::clicked, this, &IosSettingsWidget::onCreate);
|
||||
connect(m_ui->renameButton, &QPushButton::clicked, this, &IosSettingsWidget::onRename);
|
||||
connect(m_ui->resetButton, &QPushButton::clicked, this, &IosSettingsWidget::onReset);
|
||||
connect(m_ui->deleteButton, &QPushButton::clicked, this, &IosSettingsWidget::onDelete);
|
||||
|
||||
connect(m_ui->deviceView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
&IosSettingsWidget::onSelectionChanged);
|
||||
}
|
||||
|
||||
IosSettingsWidget::~IosSettingsWidget()
|
||||
@@ -61,16 +93,225 @@ IosSettingsWidget::~IosSettingsWidget()
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void IosSettingsWidget::initGui()
|
||||
/*!
|
||||
Called on start button click. Selected simulator devices are started. Multiple devices can be
|
||||
started simultaneously provided they in shutdown state.
|
||||
*/
|
||||
void IosSettingsWidget::onStart()
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices());
|
||||
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_ui->deviceView);
|
||||
if (simulatorInfoList.isEmpty())
|
||||
return;
|
||||
|
||||
if (simulatorInfoList.count() > simStartWarnCount) {
|
||||
const QString message = 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("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("Starting simulator devices...", "", simulatorInfoList.count()),
|
||||
Utils::NormalMessageFormat);
|
||||
|
||||
QList<QFuture<void>> futureList;
|
||||
foreach (const SimulatorInfo &info, simulatorInfoList) {
|
||||
if (!info.isShutdown()) {
|
||||
statusDialog->addMessage(tr("Cannot start simulator(%1, %2) in current state: %3")
|
||||
.arg(info.name).arg(info.runtimeName).arg(info.state),
|
||||
Utils::StdErrFormat);
|
||||
} else {
|
||||
futureList << Utils::onResultReady(m_simControl->startSimulator(info.identifier),
|
||||
std::bind(onSimOperation, info, statusDialog,
|
||||
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("Creating simulator device..."), Utils::NormalMessageFormat);
|
||||
const auto onSimulatorCreate = [this, statusDialog](const QString &name,
|
||||
const SimulatorControl::ResponseData &response) {
|
||||
if (response.success) {
|
||||
statusDialog->addMessage(tr("Simulator device(%1) created.\nUDID: %2")
|
||||
.arg(name).arg(response.simUdid), Utils::StdOutFormat);
|
||||
} else {
|
||||
statusDialog->addMessage(tr("Simulator device(%1) creation failed.\nError: %2").
|
||||
arg(name).arg(QString::fromUtf8(response.commandOutput)),
|
||||
Utils::StdErrFormat);
|
||||
}
|
||||
};
|
||||
|
||||
CreateSimulatorDialog createDialog(this);
|
||||
if (createDialog.exec() == QDialog::Accepted) {
|
||||
QFuture<void> f = Utils::onResultReady(
|
||||
m_simControl->createSimulator(
|
||||
createDialog.name(),
|
||||
createDialog.deviceType(),
|
||||
createDialog.runtime()),
|
||||
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_ui->deviceView);
|
||||
if (simulatorInfoList.isEmpty())
|
||||
return;
|
||||
|
||||
const int userInput = QMessageBox::question(this, tr("Reset"),
|
||||
tr("Do you really want to reset the contents and settings"
|
||||
" of the selected devices", "",
|
||||
simulatorInfoList.count()));
|
||||
if (userInput == QMessageBox::No)
|
||||
return;
|
||||
|
||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
statusDialog->addMessage(tr("Resetting contents and settings..."), Utils::NormalMessageFormat);
|
||||
|
||||
QList<QFuture<void>> futureList;
|
||||
foreach (const SimulatorInfo &info, simulatorInfoList) {
|
||||
futureList << Utils::onResultReady(m_simControl->resetSimulator(info.identifier),
|
||||
std::bind(onSimOperation, info, statusDialog,
|
||||
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_ui->deviceView);
|
||||
if (simulatorInfoList.isEmpty() || simulatorInfoList.count() > 1)
|
||||
return;
|
||||
|
||||
const SimulatorInfo &simInfo = simulatorInfoList.at(0);
|
||||
const QString newName = QInputDialog::getText(this, tr("Rename %1").arg(simInfo.name),
|
||||
tr("Enter new name:"));
|
||||
|
||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
statusDialog->addMessage(tr("Renaming simulator device..."), Utils::NormalMessageFormat);
|
||||
QFuture<void> f = Utils::onResultReady(m_simControl->renameSimulator(simInfo.identifier, newName),
|
||||
std::bind(onSimOperation, simInfo, statusDialog,
|
||||
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_ui->deviceView);
|
||||
if (simulatorInfoList.isEmpty())
|
||||
return;
|
||||
|
||||
const int userInput = QMessageBox::question(this, tr("Delete Device"),
|
||||
tr("Do you really want to delete the selected "
|
||||
"devices", "", simulatorInfoList.count()));
|
||||
if (userInput == QMessageBox::No)
|
||||
return;
|
||||
|
||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
statusDialog->addMessage(tr("Deleting simulator devices...", "", simulatorInfoList.count()),
|
||||
Utils::NormalMessageFormat);
|
||||
QList<QFuture<void>> futureList;
|
||||
foreach (const SimulatorInfo &info, simulatorInfoList) {
|
||||
futureList << Utils::onResultReady(m_simControl->deleteSimulator(info.identifier),
|
||||
std::bind(onSimOperation, info, statusDialog,
|
||||
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_ui->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_ui->pathWidget->fileName().appendPath(fileName).toString();
|
||||
};
|
||||
|
||||
QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this);
|
||||
statusDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
statusDialog->addMessage(tr("Capturing screenshots from devices...", "",
|
||||
simulatorInfoList.count()), Utils::NormalMessageFormat);
|
||||
QList<QFuture<void>> futureList;
|
||||
foreach (const SimulatorInfo &info, simulatorInfoList) {
|
||||
futureList << Utils::onResultReady(m_simControl->takeSceenshot(info.identifier,
|
||||
generatePath(info)),
|
||||
std::bind(onSimOperation, info, statusDialog,
|
||||
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_ui->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_ui->startButton->setEnabled(hasShutdown);
|
||||
m_ui->deleteButton->setEnabled(hasShutdown);
|
||||
m_ui->resetButton->setEnabled(hasShutdown);
|
||||
m_ui->renameButton->setEnabled(infoList.count() == 1 && hasShutdown);
|
||||
m_ui->pathWidget->buttonAtIndex(1)->setEnabled(hasRunning); // Screenshot button
|
||||
}
|
||||
|
||||
void IosSettingsWidget::saveSettings()
|
||||
{
|
||||
IosConfigurations::setIgnoreAllDevices(!m_ui->deviceAskCheckBox->isChecked());
|
||||
IosConfigurations::setScreenshotDir(m_ui->pathWidget->fileName());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -26,32 +26,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "iosconfigurations.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include "simulatorcontrol.h"
|
||||
#include <QWidget>
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
namespace Ui { class IosSettingsWidget; }
|
||||
class SimulatorInfoModel;
|
||||
using SimulatorInfoList = QList<SimulatorInfo>;
|
||||
|
||||
class IosSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Todo: This would be so much simpler if it just used Utils::PathChooser!!!
|
||||
IosSettingsWidget(QWidget *parent = 0);
|
||||
~IosSettingsWidget();
|
||||
|
||||
void saveSettings();
|
||||
|
||||
private:
|
||||
void initGui();
|
||||
void onStart();
|
||||
void onCreate();
|
||||
void onReset();
|
||||
void onRename();
|
||||
void onDelete();
|
||||
void onScreenshot();
|
||||
void onSelectionChanged();
|
||||
|
||||
Ui::IosSettingsWidget *m_ui;
|
||||
bool m_saveSettingsRequested;
|
||||
private:
|
||||
Ui::IosSettingsWidget *m_ui = nullptr;
|
||||
bool m_saveSettingsRequested = false;
|
||||
SimulatorControl *m_simControl = nullptr;
|
||||
SimulatorInfoModel *m_simInfoModel = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -1,54 +1,210 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Ios::Internal::IosSettingsWidget</class>
|
||||
<widget class="QWidget" name="IosSettingsWidget">
|
||||
<widget class="QWidget" name="Ios::Internal::IosSettingsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>679</width>
|
||||
<height>184</height>
|
||||
<width>622</width>
|
||||
<height>456</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>iOS Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="deviceAskCheckBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ask about devices not in developer mode</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Devices</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="deviceAskCheckBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ask about devices not in developer mode</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Simulator</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="renameButton">
|
||||
<property name="toolTip">
|
||||
<string>Rename a simulator device.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete simulator devices.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="resetButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reset contents and settings of simulator devices.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="7">
|
||||
<widget class="QTreeView" name="deviceView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Screenshot directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Utils::PathChooser" name="pathWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="createButton">
|
||||
<property name="toolTip">
|
||||
<string>Create a new simulator device.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="startButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start simulator devices.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Utils::PathChooser</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">utils/pathchooser.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@@ -574,5 +574,14 @@ QDebug &operator<<(QDebug &stream, const SimulatorInfo &info)
|
||||
return stream;
|
||||
}
|
||||
|
||||
bool SimulatorInfo::operator==(const SimulatorInfo &other) const
|
||||
{
|
||||
return identifier == other.identifier
|
||||
&& state == other.state
|
||||
&& name == other.name
|
||||
&& available == other.available
|
||||
&& runtimeName == other.runtimeName;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
||||
|
@@ -58,6 +58,8 @@ public:
|
||||
bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; }
|
||||
bool isShutdown() const { return state.compare(QStringLiteral("Shutdown")) == 0; }
|
||||
bool isShuttingDown() const { return state == "Shutting Down"; }
|
||||
bool operator==(const SimulatorInfo &other) const;
|
||||
bool operator!=(const SimulatorInfo &other) const { return !(*this == other); }
|
||||
bool available;
|
||||
QString state;
|
||||
QString runtimeName;
|
||||
|
166
src/plugins/ios/simulatorinfomodel.cpp
Normal file
166
src/plugins/ios/simulatorinfomodel.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "simulatorinfomodel.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
const int colCount = 3;
|
||||
const int nameCol = 0;
|
||||
const int runtimeCol = 1;
|
||||
const int stateCol = 2;
|
||||
static const int deviceUpdateInterval = 1000; // Update simulator state every 1 sec.
|
||||
|
||||
SimulatorInfoModel::SimulatorInfoModel(QObject *parent) :
|
||||
QAbstractItemModel(parent)
|
||||
{
|
||||
m_fetchFuture.setCancelOnWait(true);
|
||||
|
||||
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 QVariant();
|
||||
|
||||
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("UDID: %1").arg(simInfo.identifier);
|
||||
} else if (role == Qt::UserRole) {
|
||||
return QVariant::fromValue<SimulatorInfo>(simInfo);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
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 QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case nameCol:
|
||||
return tr("Simulator Name");
|
||||
case runtimeCol:
|
||||
return tr("Runtime");
|
||||
case stateCol:
|
||||
return tr("Current State");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
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 QModelIndex();
|
||||
}
|
||||
|
||||
void SimulatorInfoModel::requestSimulatorInfo()
|
||||
{
|
||||
if (!m_fetchFuture.futures().isEmpty() && !m_fetchFuture.futures().at(0).isFinished())
|
||||
return; // Ignore the request if the last request is still pending.
|
||||
|
||||
m_fetchFuture.clearFutures();
|
||||
m_fetchFuture.addFuture(Utils::onResultReady(SimulatorControl::updateAvailableSimulators(),
|
||||
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.push_back(std::make_pair(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));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
63
src/plugins/ios/simulatorinfomodel.h
Normal file
63
src/plugins/ios/simulatorinfomodel.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "simulatorcontrol.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QFutureSynchronizer>
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
using SimulatorInfoList = QList<SimulatorInfo>;
|
||||
|
||||
class SimulatorInfoModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
QFutureSynchronizer<void> m_fetchFuture;
|
||||
SimulatorInfoList m_simList;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
130
src/plugins/ios/simulatoroperationdialog.cpp
Normal file
130
src/plugins/ios/simulatoroperationdialog.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "simulatoroperationdialog.h"
|
||||
#include "ui_simulatoroperationdialog.h"
|
||||
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace {
|
||||
Q_LOGGING_CATEGORY(iosCommon, "qtc.ios.common")
|
||||
}
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
SimulatorOperationDialog::SimulatorOperationDialog(QWidget *parent) :
|
||||
// TODO: Maximize buttong only because of QTBUG-41932
|
||||
QDialog(parent,Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint),
|
||||
m_ui(new Ui::SimulatorOperationDialog)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_formatter = new Utils::OutputFormatter;
|
||||
m_formatter->setPlainTextEdit(m_ui->messageEdit);
|
||||
}
|
||||
|
||||
SimulatorOperationDialog::~SimulatorOperationDialog()
|
||||
{
|
||||
// Cancel all pending futures.
|
||||
foreach (auto watcher, m_futureWatchList) {
|
||||
if (!watcher->isFinished())
|
||||
watcher->cancel();
|
||||
}
|
||||
|
||||
// wait for futures to finish
|
||||
foreach (auto watcher, m_futureWatchList) {
|
||||
if (!watcher->isFinished())
|
||||
watcher->waitForFinished();
|
||||
delete watcher;
|
||||
}
|
||||
|
||||
delete m_formatter;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void SimulatorOperationDialog::addFutures(const QList<QFuture<void> > &futureList)
|
||||
{
|
||||
foreach (auto future, futureList) {
|
||||
if (!future.isFinished() || !future.isCanceled()) {
|
||||
auto watcher = new QFutureWatcher<void>;
|
||||
watcher->setFuture(future);
|
||||
connect(watcher, &QFutureWatcher<void>::finished,
|
||||
this, &SimulatorOperationDialog::futureFinished);
|
||||
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::ResponseData &response,
|
||||
const QString &context)
|
||||
{
|
||||
QTC_CHECK(siminfo.identifier == response.simUdid);
|
||||
if (response.success) {
|
||||
addMessage(tr("%1, %2\nOperation %3 completed successfully.").arg(siminfo.name)
|
||||
.arg(siminfo.runtimeName).arg(context), Utils::StdOutFormat);
|
||||
} else {
|
||||
QByteArray erroMsg = response.commandOutput.trimmed();
|
||||
QString message = 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("Unknown") : QString::fromUtf8(erroMsg));
|
||||
addMessage(message, Utils::StdErrFormat);
|
||||
qCDebug(iosCommon) << message;
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorOperationDialog::futureFinished()
|
||||
{
|
||||
auto watcher = static_cast<QFutureWatcher<void> *>(sender());
|
||||
m_futureWatchList.removeAll(watcher);
|
||||
watcher->deleteLater();
|
||||
updateInputs();
|
||||
}
|
||||
|
||||
void SimulatorOperationDialog::updateInputs()
|
||||
{
|
||||
bool enableOk = m_futureWatchList.isEmpty();
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(!enableOk);
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enableOk);
|
||||
if (enableOk) {
|
||||
addMessage(tr("Done."), Utils::NormalMessageFormat);
|
||||
m_ui->progressBar->setMaximum(1); // Stop progress bar.
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
67
src/plugins/ios/simulatoroperationdialog.h
Normal file
67
src/plugins/ios/simulatoroperationdialog.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "simulatorcontrol.h"
|
||||
|
||||
#include <utils/outputformat.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFuture>
|
||||
#include <QList>
|
||||
|
||||
namespace Utils { class OutputFormatter; }
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
namespace Ui { class SimulatorOperationDialog; }
|
||||
|
||||
class SimulatorOperationDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SimulatorOperationDialog(QWidget *parent = nullptr);
|
||||
~SimulatorOperationDialog();
|
||||
|
||||
public:
|
||||
void addFutures(const QList<QFuture<void> > &futureList);
|
||||
void addMessage(const QString &message, Utils::OutputFormat format);
|
||||
void addMessage(const SimulatorInfo &siminfo, const SimulatorControl::ResponseData &response,
|
||||
const QString &context);
|
||||
|
||||
private:
|
||||
void futureFinished();
|
||||
void updateInputs();
|
||||
|
||||
private:
|
||||
Ui::SimulatorOperationDialog *m_ui = nullptr;
|
||||
Utils::OutputFormatter *m_formatter = nullptr;
|
||||
QList<QFutureWatcher<void> *> m_futureWatchList;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
87
src/plugins/ios/simulatoroperationdialog.ui
Normal file
87
src/plugins/ios/simulatoroperationdialog.ui
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Ios::Internal::SimulatorOperationDialog</class>
|
||||
<widget class="QDialog" name="Ios::Internal::SimulatorOperationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>580</width>
|
||||
<height>320</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Simulator Operation Status</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="messageEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Ios::Internal::SimulatorOperationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Ios::Internal::SimulatorOperationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Reference in New Issue
Block a user