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:
Vikas Pachdha
2017-01-27 11:00:20 +01:00
parent 4ab18cde0e
commit 18638d5560
17 changed files with 1386 additions and 65 deletions

View 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

View 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

View 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>

View File

@@ -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

View File

@@ -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"
]
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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;

View 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

View 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

View 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

View 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

View 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>