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 \
|
iosdeploystepwidget.h \
|
||||||
simulatorcontrol.h \
|
simulatorcontrol.h \
|
||||||
iosbuildconfiguration.h \
|
iosbuildconfiguration.h \
|
||||||
iosbuildsettingswidget.h
|
iosbuildsettingswidget.h \
|
||||||
|
createsimulatordialog.h \
|
||||||
|
simulatoroperationdialog.h \
|
||||||
|
simulatorinfomodel.h
|
||||||
|
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@@ -61,14 +64,19 @@ SOURCES += \
|
|||||||
iosdeploystepwidget.cpp \
|
iosdeploystepwidget.cpp \
|
||||||
simulatorcontrol.cpp \
|
simulatorcontrol.cpp \
|
||||||
iosbuildconfiguration.cpp \
|
iosbuildconfiguration.cpp \
|
||||||
iosbuildsettingswidget.cpp
|
iosbuildsettingswidget.cpp \
|
||||||
|
createsimulatordialog.cpp \
|
||||||
|
simulatoroperationdialog.cpp \
|
||||||
|
simulatorinfomodel.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
iossettingswidget.ui \
|
iossettingswidget.ui \
|
||||||
iosbuildstep.ui \
|
iosbuildstep.ui \
|
||||||
iosdeploystepwidget.ui \
|
iosdeploystepwidget.ui \
|
||||||
iospresetbuildstep.ui \
|
iospresetbuildstep.ui \
|
||||||
iosbuildsettingswidget.ui
|
iosbuildsettingswidget.ui \
|
||||||
|
createsimulatordialog.ui \
|
||||||
|
simulatoroperationdialog.ui
|
||||||
|
|
||||||
DEFINES += IOS_LIBRARY
|
DEFINES += IOS_LIBRARY
|
||||||
|
|
||||||
|
@@ -14,6 +14,9 @@ QtcPlugin {
|
|||||||
cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["CoreFoundation", "IOKit"] : [])
|
cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["CoreFoundation", "IOKit"] : [])
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
"createsimulatordialog.cpp",
|
||||||
|
"createsimulatordialog.h",
|
||||||
|
"createsimulatordialog.ui",
|
||||||
"ios.qrc",
|
"ios.qrc",
|
||||||
"iosbuildconfiguration.cpp",
|
"iosbuildconfiguration.cpp",
|
||||||
"iosbuildconfiguration.h",
|
"iosbuildconfiguration.h",
|
||||||
@@ -70,6 +73,11 @@ QtcPlugin {
|
|||||||
"iostoolhandler.cpp",
|
"iostoolhandler.cpp",
|
||||||
"iostoolhandler.h",
|
"iostoolhandler.h",
|
||||||
"simulatorcontrol.cpp",
|
"simulatorcontrol.cpp",
|
||||||
"simulatorcontrol.h"
|
"simulatorcontrol.h",
|
||||||
|
"simulatorinfomodel.cpp",
|
||||||
|
"simulatorinfomodel.h",
|
||||||
|
"simulatoroperationdialog.cpp",
|
||||||
|
"simulatoroperationdialog.h",
|
||||||
|
"simulatoroperationdialog.ui"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,8 @@
|
|||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
@@ -77,6 +79,7 @@ namespace Internal {
|
|||||||
|
|
||||||
const QLatin1String SettingsGroup("IosConfigurations");
|
const QLatin1String SettingsGroup("IosConfigurations");
|
||||||
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
|
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
|
||||||
|
const char screenshotDirPathKey[] = "ScreeshotDirPath";
|
||||||
|
|
||||||
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
||||||
const char freeTeamTag[] = "isFreeProvisioningTeam";
|
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()
|
FileName IosConfigurations::developerPath()
|
||||||
{
|
{
|
||||||
return m_instance->m_developerPath;
|
return m_instance->m_developerPath;
|
||||||
@@ -370,6 +386,7 @@ void IosConfigurations::save()
|
|||||||
QSettings *settings = Core::ICore::settings();
|
QSettings *settings = Core::ICore::settings();
|
||||||
settings->beginGroup(SettingsGroup);
|
settings->beginGroup(SettingsGroup);
|
||||||
settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices);
|
settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices);
|
||||||
|
settings->setValue(screenshotDirPathKey, m_screenshotDir.toString());
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +401,12 @@ void IosConfigurations::load()
|
|||||||
QSettings *settings = Core::ICore::settings();
|
QSettings *settings = Core::ICore::settings();
|
||||||
settings->beginGroup(SettingsGroup);
|
settings->beginGroup(SettingsGroup);
|
||||||
m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, false).toBool();
|
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();
|
settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -113,6 +113,8 @@ public:
|
|||||||
static void initialize();
|
static void initialize();
|
||||||
static bool ignoreAllDevices();
|
static bool ignoreAllDevices();
|
||||||
static void setIgnoreAllDevices(bool ignoreDevices);
|
static void setIgnoreAllDevices(bool ignoreDevices);
|
||||||
|
static void setScreenshotDir(const Utils::FileName &path);
|
||||||
|
static Utils::FileName screenshotDir();
|
||||||
static Utils::FileName developerPath();
|
static Utils::FileName developerPath();
|
||||||
static QVersionNumber xcodeVersion();
|
static QVersionNumber xcodeVersion();
|
||||||
static Utils::FileName lldbPath();
|
static Utils::FileName lldbPath();
|
||||||
@@ -135,6 +137,7 @@ private:
|
|||||||
void loadProvisioningData(bool notify = true);
|
void loadProvisioningData(bool notify = true);
|
||||||
|
|
||||||
Utils::FileName m_developerPath;
|
Utils::FileName m_developerPath;
|
||||||
|
Utils::FileName m_screenshotDir;
|
||||||
QVersionNumber m_xcodeVersion;
|
QVersionNumber m_xcodeVersion;
|
||||||
bool m_ignoreAllDevices;
|
bool m_ignoreAllDevices;
|
||||||
QFileSystemWatcher *m_provisioningDataWatcher = nullptr;
|
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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -24,36 +24,68 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "iossettingswidget.h"
|
#include "iossettingswidget.h"
|
||||||
|
|
||||||
#include "ui_iossettingswidget.h"
|
#include "ui_iossettingswidget.h"
|
||||||
|
|
||||||
|
#include "createsimulatordialog.h"
|
||||||
#include "iosconfigurations.h"
|
#include "iosconfigurations.h"
|
||||||
#include "iosconstants.h"
|
#include "simulatorinfomodel.h"
|
||||||
|
#include "simulatoroperationdialog.h"
|
||||||
|
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/algorithm.h>
|
||||||
#include <projectexplorer/toolchainmanager.h>
|
#include <utils/runextensions.h>
|
||||||
#include <projectexplorer/kitmanager.h>
|
|
||||||
#include <projectexplorer/kitinformation.h>
|
|
||||||
#include <qtsupport/qtkitinformation.h>
|
|
||||||
#include <qtsupport/qtversionmanager.h>
|
|
||||||
|
|
||||||
#include <QFile>
|
#include <QDateTime>
|
||||||
#include <QTextStream>
|
#include <QInputDialog>
|
||||||
#include <QProcess>
|
|
||||||
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QModelIndex>
|
#include <QPointer>
|
||||||
|
|
||||||
|
static const int simStartWarnCount = 4;
|
||||||
|
|
||||||
namespace Ios {
|
namespace Ios {
|
||||||
namespace Internal {
|
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)
|
IosSettingsWidget::IosSettingsWidget(QWidget *parent)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
m_ui(new Ui::IosSettingsWidget),
|
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()
|
IosSettingsWidget::~IosSettingsWidget()
|
||||||
@@ -61,16 +93,225 @@ IosSettingsWidget::~IosSettingsWidget()
|
|||||||
delete m_ui;
|
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);
|
const SimulatorInfoList simulatorInfoList = selectedSimulators(m_ui->deviceView);
|
||||||
m_ui->deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices());
|
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()
|
void IosSettingsWidget::saveSettings()
|
||||||
{
|
{
|
||||||
IosConfigurations::setIgnoreAllDevices(!m_ui->deviceAskCheckBox->isChecked());
|
IosConfigurations::setIgnoreAllDevices(!m_ui->deviceAskCheckBox->isChecked());
|
||||||
|
IosConfigurations::setScreenshotDir(m_ui->pathWidget->fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Ios
|
} // 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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -26,32 +26,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "iosconfigurations.h"
|
#include "iosconfigurations.h"
|
||||||
|
#include "simulatorcontrol.h"
|
||||||
#include <QList>
|
|
||||||
#include <QString>
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QAbstractTableModel>
|
|
||||||
|
|
||||||
namespace Ios {
|
namespace Ios {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
namespace Ui { class IosSettingsWidget; }
|
namespace Ui { class IosSettingsWidget; }
|
||||||
|
class SimulatorInfoModel;
|
||||||
|
using SimulatorInfoList = QList<SimulatorInfo>;
|
||||||
|
|
||||||
class IosSettingsWidget : public QWidget
|
class IosSettingsWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Todo: This would be so much simpler if it just used Utils::PathChooser!!!
|
|
||||||
IosSettingsWidget(QWidget *parent = 0);
|
IosSettingsWidget(QWidget *parent = 0);
|
||||||
~IosSettingsWidget();
|
~IosSettingsWidget();
|
||||||
|
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initGui();
|
void onStart();
|
||||||
|
void onCreate();
|
||||||
|
void onReset();
|
||||||
|
void onRename();
|
||||||
|
void onDelete();
|
||||||
|
void onScreenshot();
|
||||||
|
void onSelectionChanged();
|
||||||
|
|
||||||
Ui::IosSettingsWidget *m_ui;
|
private:
|
||||||
bool m_saveSettingsRequested;
|
Ui::IosSettingsWidget *m_ui = nullptr;
|
||||||
|
bool m_saveSettingsRequested = false;
|
||||||
|
SimulatorControl *m_simControl = nullptr;
|
||||||
|
SimulatorInfoModel *m_simInfoModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -1,54 +1,210 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>Ios::Internal::IosSettingsWidget</class>
|
<class>Ios::Internal::IosSettingsWidget</class>
|
||||||
<widget class="QWidget" name="IosSettingsWidget">
|
<widget class="QWidget" name="Ios::Internal::IosSettingsWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>679</width>
|
<width>622</width>
|
||||||
<height>184</height>
|
<height>456</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>iOS Configuration</string>
|
<string>iOS Configuration</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<item row="0" column="0">
|
<property name="title">
|
||||||
<widget class="QCheckBox" name="deviceAskCheckBox">
|
<string>Devices</string>
|
||||||
<property name="sizePolicy">
|
</property>
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<horstretch>0</horstretch>
|
<item>
|
||||||
<verstretch>0</verstretch>
|
<widget class="QCheckBox" name="deviceAskCheckBox">
|
||||||
</sizepolicy>
|
<property name="sizePolicy">
|
||||||
</property>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<property name="text">
|
<horstretch>0</horstretch>
|
||||||
<string>Ask about devices not in developer mode</string>
|
<verstretch>0</verstretch>
|
||||||
</property>
|
</sizepolicy>
|
||||||
<property name="checked">
|
</property>
|
||||||
<bool>true</bool>
|
<property name="text">
|
||||||
</property>
|
<string>Ask about devices not in developer mode</string>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<property name="checked">
|
||||||
</layout>
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="orientation">
|
<property name="title">
|
||||||
<enum>Qt::Vertical</enum>
|
<string>Simulator</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<size>
|
<item row="3" column="1">
|
||||||
<width>20</width>
|
<widget class="QPushButton" name="renameButton">
|
||||||
<height>40</height>
|
<property name="toolTip">
|
||||||
</size>
|
<string>Rename a simulator device.</string>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
<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>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>Utils::PathChooser</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header location="global">utils/pathchooser.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@@ -574,5 +574,14 @@ QDebug &operator<<(QDebug &stream, const SimulatorInfo &info)
|
|||||||
return stream;
|
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 Internal
|
||||||
} // namespace Ios
|
} // namespace Ios
|
||||||
|
@@ -58,6 +58,8 @@ public:
|
|||||||
bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; }
|
bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; }
|
||||||
bool isShutdown() const { return state.compare(QStringLiteral("Shutdown")) == 0; }
|
bool isShutdown() const { return state.compare(QStringLiteral("Shutdown")) == 0; }
|
||||||
bool isShuttingDown() const { return state == "Shutting Down"; }
|
bool isShuttingDown() const { return state == "Shutting Down"; }
|
||||||
|
bool operator==(const SimulatorInfo &other) const;
|
||||||
|
bool operator!=(const SimulatorInfo &other) const { return !(*this == other); }
|
||||||
bool available;
|
bool available;
|
||||||
QString state;
|
QString state;
|
||||||
QString runtimeName;
|
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