docker: Add dockercli setting

Change-Id: I46fada555d697007042d823ef1cad0658be98e22
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2022-07-04 11:09:11 +02:00
parent 95c1f37a31
commit 8a7a4fce6c
9 changed files with 86 additions and 130 deletions

View File

@@ -1027,6 +1027,7 @@ QtcProcess::QtcProcess(QObject *parent)
: QObject(parent), : QObject(parent),
d(new QtcProcessPrivate(this)) d(new QtcProcessPrivate(this))
{ {
qRegisterMetaType<ProcessResultData>("ProcessResultData");
static int qProcessExitStatusMeta = qRegisterMetaType<QProcess::ExitStatus>(); static int qProcessExitStatusMeta = qRegisterMetaType<QProcess::ExitStatus>();
static int qProcessProcessErrorMeta = qRegisterMetaType<QProcess::ProcessError>(); static int qProcessProcessErrorMeta = qRegisterMetaType<QProcess::ProcessError>();
Q_UNUSED(qProcessExitStatusMeta) Q_UNUSED(qProcessExitStatusMeta)

View File

@@ -43,7 +43,8 @@ using namespace Utils;
DockerApi *s_instance{nullptr}; DockerApi *s_instance{nullptr};
DockerApi::DockerApi() DockerApi::DockerApi(QSharedPointer<DockerSettings> settings)
: m_settings(settings)
{ {
s_instance = this; s_instance = this;
} }
@@ -56,7 +57,7 @@ DockerApi *DockerApi::instance()
bool DockerApi::canConnect() bool DockerApi::canConnect()
{ {
QtcProcess process; QtcProcess process;
FilePath dockerExe = findDockerClient(); FilePath dockerExe = dockerClient();
if (dockerExe.isEmpty() || !dockerExe.isExecutableFile()) if (dockerExe.isEmpty() || !dockerExe.isExecutableFile())
return false; return false;
@@ -83,11 +84,11 @@ void DockerApi::checkCanConnect(bool async)
return; return;
m_dockerDaemonAvailable = nullopt; m_dockerDaemonAvailable = nullopt;
dockerDaemonAvailableChanged(); emit dockerDaemonAvailableChanged();
auto future = Utils::runAsync([lk = std::move(lk), this] { auto future = Utils::runAsync([lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect(); m_dockerDaemonAvailable = canConnect();
dockerDaemonAvailableChanged(); emit dockerDaemonAvailableChanged();
}); });
Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin"); Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin");
@@ -98,7 +99,7 @@ void DockerApi::checkCanConnect(bool async)
bool isAvailable = canConnect(); bool isAvailable = canConnect();
if (!m_dockerDaemonAvailable.has_value() || isAvailable != m_dockerDaemonAvailable) { if (!m_dockerDaemonAvailable.has_value() || isAvailable != m_dockerDaemonAvailable) {
m_dockerDaemonAvailable = isAvailable; m_dockerDaemonAvailable = isAvailable;
dockerDaemonAvailableChanged(); emit dockerDaemonAvailableChanged();
} }
} }
@@ -121,11 +122,9 @@ Utils::optional<bool> DockerApi::isDockerDaemonAvailable(bool async)
return s_instance->dockerDaemonAvailable(async); return s_instance->dockerDaemonAvailable(async);
} }
FilePath DockerApi::findDockerClient() FilePath DockerApi::dockerClient()
{ {
if (m_dockerExecutable.isEmpty() || m_dockerExecutable.isExecutableFile()) return FilePath::fromString(m_settings->dockerBinaryPath.value());
m_dockerExecutable = FilePath::fromString("docker").searchInPath();
return m_dockerExecutable;
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include "dockersettings.h"
#include <QMutex> #include <QMutex>
#include <QObject> #include <QObject>
@@ -40,7 +42,7 @@ class DockerApi : public QObject
Q_OBJECT Q_OBJECT
public: public:
DockerApi(); DockerApi(QSharedPointer<DockerSettings> settings);
static DockerApi *instance(); static DockerApi *instance();
@@ -56,12 +58,12 @@ public:
static Utils::optional<bool> isDockerDaemonAvailable(bool async = true); static Utils::optional<bool> isDockerDaemonAvailable(bool async = true);
private: private:
Utils::FilePath findDockerClient(); Utils::FilePath dockerClient();
private: private:
Utils::FilePath m_dockerExecutable;
Utils::optional<bool> m_dockerDaemonAvailable; Utils::optional<bool> m_dockerDaemonAvailable;
QMutex m_daemonCheckGuard; QMutex m_daemonCheckGuard;
QSharedPointer<DockerSettings> m_settings;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -29,6 +29,8 @@
namespace Docker { namespace Docker {
namespace Constants { namespace Constants {
const char DOCKER[] = "docker";
const char DOCKER_SETTINGS_ID[] = "Docker.Settings"; const char DOCKER_SETTINGS_ID[] = "Docker.Settings";
const char DOCKER_DEVICE_TYPE[] = "DockerDeviceType"; const char DOCKER_DEVICE_TYPE[] = "DockerDeviceType";

View File

@@ -106,18 +106,20 @@ static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg);
class ContainerShell : public Utils::DeviceShell class ContainerShell : public Utils::DeviceShell
{ {
public: public:
ContainerShell(const QString &containerId) ContainerShell(QSharedPointer<DockerSettings> settings, const QString &containerId)
: m_containerId(containerId) : m_settings(settings)
, m_containerId(containerId)
{ {
} }
private: private:
void setupShellProcess(QtcProcess *shellProcess) final void setupShellProcess(QtcProcess *shellProcess) final
{ {
shellProcess->setCommand({"docker", {"container", "start", "-i", "-a", m_containerId}}); shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "start", "-i", "-a", m_containerId}});
} }
private: private:
QSharedPointer<DockerSettings> m_settings;
QString m_containerId; QString m_containerId;
}; };
@@ -126,8 +128,9 @@ class DockerDevicePrivate : public QObject
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice) Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice)
public: public:
DockerDevicePrivate(DockerDevice *parent) DockerDevicePrivate(DockerDevice *parent, QSharedPointer<DockerSettings> settings)
: q(parent) : q(parent)
, m_settings(settings)
{} {}
~DockerDevicePrivate() { stopCurrentContainer(); } ~DockerDevicePrivate() { stopCurrentContainer(); }
@@ -144,6 +147,7 @@ public:
DockerDevice *q; DockerDevice *q;
DockerDeviceData m_data; DockerDeviceData m_data;
QSharedPointer<DockerSettings> m_settings;
// For local file access // For local file access
@@ -316,8 +320,8 @@ QString DockerDeviceData::repoAndTag() const
// DockerDevice // DockerDevice
DockerDevice::DockerDevice(const DockerDeviceData &data) DockerDevice::DockerDevice(QSharedPointer<DockerSettings> settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this)) : d(new DockerDevicePrivate(this, settings))
{ {
d->m_data = data; d->m_data = data;
@@ -327,7 +331,7 @@ DockerDevice::DockerDevice(const DockerDeviceData &data)
setDisplayName(tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId)); setDisplayName(tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId));
setAllowEmptyCommand(true); setAllowEmptyCommand(true);
setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { setOpenTerminal([this, settings](const Environment &env, const FilePath &workingDir) {
Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below. Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below.
updateContainerAccess(); updateContainerAccess();
if (d->m_container.isEmpty()) { if (d->m_container.isEmpty()) {
@@ -345,7 +349,7 @@ DockerDevice::DockerDevice(const DockerDeviceData &data)
}); });
const QString wd = workingDir.isEmpty() ? "/" : workingDir.path(); const QString wd = workingDir.isEmpty() ? "/" : workingDir.path();
proc->setCommand({"docker", {"exec", "-it", "-w", wd, d->m_container, "/bin/sh"}}); proc->setCommand({settings->dockerBinaryPath.filePath(), {"exec", "-it", "-w", wd, d->m_container, "/bin/sh"}});
proc->setEnvironment(Environment::systemEnvironment()); // The host system env. Intentional. proc->setEnvironment(Environment::systemEnvironment()); // The host system env. Intentional.
proc->start(); proc->start();
}); });
@@ -385,7 +389,7 @@ void DockerDevicePrivate::stopCurrentContainer()
m_shell.reset(); m_shell.reset();
QtcProcess proc; QtcProcess proc;
proc.setCommand({"docker", {"container", "stop", m_container}}); proc.setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "stop", m_container}});
m_container.clear(); m_container.clear();
@@ -410,7 +414,7 @@ void DockerDevicePrivate::startContainer()
{ {
const QString display = HostOsInfo::isLinuxHost() ? QString(":0") const QString display = HostOsInfo::isLinuxHost() ? QString(":0")
: QString(getLocalIPv4Address() + ":0.0"); : QString(getLocalIPv4Address() + ":0.0");
CommandLine dockerCreate{"docker", {"create", CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(), {"create",
"-i", "-i",
"--rm", "--rm",
"-e", QString("DISPLAY=%1").arg(display), "-e", QString("DISPLAY=%1").arg(display),
@@ -448,7 +452,7 @@ void DockerDevicePrivate::startContainer()
return; return;
LOG("Container via process: " << m_container); LOG("Container via process: " << m_container);
m_shell = std::make_unique<ContainerShell>(m_container); m_shell = std::make_unique<ContainerShell>(m_settings, m_container);
connect(m_shell.get(), &DeviceShell::done, this, [this] (const ProcessResultData &resultData) { connect(m_shell.get(), &DeviceShell::done, this, [this] (const ProcessResultData &resultData) {
if (resultData.m_error != QProcess::UnknownError) if (resultData.m_error != QProcess::UnknownError)
return; return;
@@ -497,7 +501,7 @@ CommandLine DockerDevice::withDockerExecCmd(const Utils::CommandLine &cmd, bool
args << "-i"; args << "-i";
args << d->m_container; args << d->m_container;
CommandLine dcmd{"docker", args}; CommandLine dcmd{d->m_settings->dockerBinaryPath.filePath(), args};
dcmd.addCommandLineAsArgs(cmd); dcmd.addCommandLineAsArgs(cmd);
return dcmd; return dcmd;
} }
@@ -1001,7 +1005,7 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
{ {
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false)) if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return false; return false;
CommandLine dcmd{"docker", {"exec", m_container}}; CommandLine dcmd{m_settings->dockerBinaryPath.filePath(), {"exec", m_container}};
dcmd.addCommandLineAsArgs(cmd); dcmd.addCommandLineAsArgs(cmd);
QtcProcess proc; QtcProcess proc;
@@ -1062,8 +1066,9 @@ public:
class DockerDeviceSetupWizard final : public QDialog class DockerDeviceSetupWizard final : public QDialog
{ {
public: public:
DockerDeviceSetupWizard() DockerDeviceSetupWizard(QSharedPointer<DockerSettings> settings)
: QDialog(ICore::dialogParent()) : QDialog(ICore::dialogParent())
, m_settings(settings)
{ {
setWindowTitle(DockerDevice::tr("Docker Image Selection")); setWindowTitle(DockerDevice::tr("Docker Image Selection"));
resize(800, 600); resize(800, 600);
@@ -1100,7 +1105,7 @@ public:
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
CommandLine cmd{"docker", {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}}; CommandLine cmd{m_settings->dockerBinaryPath.filePath(), {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
m_log->append(DockerDevice::tr("Running \"%1\"\n").arg(cmd.toUserOutput())); m_log->append(DockerDevice::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
m_process = new QtcProcess(this); m_process = new QtcProcess(this);
@@ -1150,7 +1155,7 @@ public:
DockerImageItem *item = m_model.itemForIndex(selectedRows.front()); DockerImageItem *item = m_model.itemForIndex(selectedRows.front());
QTC_ASSERT(item, return {}); QTC_ASSERT(item, return {});
auto device = DockerDevice::create(*item); auto device = DockerDevice::create(m_settings, *item);
device->setupId(IDevice::ManuallyAdded); device->setupId(IDevice::ManuallyAdded);
device->setType(Constants::DOCKER_DEVICE_TYPE); device->setType(Constants::DOCKER_DEVICE_TYPE);
device->setMachineType(IDevice::Hardware); device->setMachineType(IDevice::Hardware);
@@ -1163,6 +1168,7 @@ public:
TreeView *m_view = nullptr; TreeView *m_view = nullptr;
QTextBrowser *m_log = nullptr; QTextBrowser *m_log = nullptr;
QDialogButtonBox *m_buttons; QDialogButtonBox *m_buttons;
QSharedPointer<DockerSettings> m_settings;
QtcProcess *m_process = nullptr; QtcProcess *m_process = nullptr;
QString m_selectedId; QString m_selectedId;
@@ -1170,18 +1176,18 @@ public:
// Factory // Factory
DockerDeviceFactory::DockerDeviceFactory() DockerDeviceFactory::DockerDeviceFactory(QSharedPointer<DockerSettings> settings)
: IDeviceFactory(Constants::DOCKER_DEVICE_TYPE) : IDeviceFactory(Constants::DOCKER_DEVICE_TYPE)
{ {
setDisplayName(DockerDevice::tr("Docker Device")); setDisplayName(DockerDevice::tr("Docker Device"));
setIcon(QIcon()); setIcon(QIcon());
setCreator([] { setCreator([settings] {
DockerDeviceSetupWizard wizard; DockerDeviceSetupWizard wizard(settings);
if (wizard.exec() != QDialog::Accepted) if (wizard.exec() != QDialog::Accepted)
return IDevice::Ptr(); return IDevice::Ptr();
return wizard.device(); return wizard.device();
}); });
setConstructionFunction([] { return DockerDevice::create({}); }); setConstructionFunction([settings] { return DockerDevice::create(settings, {}); });
} }
} // Internal } // Internal

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include "dockersettings.h"
#include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/idevicefactory.h> #include <projectexplorer/devicesupport/idevicefactory.h>
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
@@ -56,10 +58,10 @@ public:
using Ptr = QSharedPointer<DockerDevice>; using Ptr = QSharedPointer<DockerDevice>;
using ConstPtr = QSharedPointer<const DockerDevice>; using ConstPtr = QSharedPointer<const DockerDevice>;
explicit DockerDevice(const DockerDeviceData &data); explicit DockerDevice(QSharedPointer<DockerSettings> settings, const DockerDeviceData &data);
~DockerDevice(); ~DockerDevice();
static Ptr create(const DockerDeviceData &data) { return Ptr(new DockerDevice(data)); } static Ptr create(QSharedPointer<DockerSettings> settings, const DockerDeviceData &data) { return Ptr(new DockerDevice(settings, data)); }
ProjectExplorer::IDeviceWidget *createWidget() override; ProjectExplorer::IDeviceWidget *createWidget() override;
QList<ProjectExplorer::Task> validate() const override; QList<ProjectExplorer::Task> validate() const override;
@@ -133,7 +135,7 @@ private:
class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory
{ {
public: public:
DockerDeviceFactory(); DockerDeviceFactory(QSharedPointer<DockerSettings> settings);
}; };
} // Internal } // Internal

View File

@@ -25,8 +25,6 @@
#include "dockerplugin.h" #include "dockerplugin.h"
#include "dockerconstants.h"
#include "dockerapi.h" #include "dockerapi.h"
#include "dockerdevice.h" #include "dockerdevice.h"
#include "dockersettings.h" #include "dockersettings.h"
@@ -45,15 +43,14 @@ namespace Internal {
class DockerPluginPrivate class DockerPluginPrivate
{ {
public: public:
// DockerSettings settings; QSharedPointer<DockerSettings> m_settings{new DockerSettings};
// DockerOptionsPage optionsPage{&settings}; DockerDeviceFactory deviceFactory{m_settings};
DockerSettingsPage m_settingPage{m_settings};
DockerDeviceFactory deviceFactory;
// DockerBuildStepFactory buildStepFactory; // DockerBuildStepFactory buildStepFactory;
Utils::optional<bool> daemonRunning; Utils::optional<bool> daemonRunning;
DockerApi dockerApi; DockerApi dockerApi{m_settings};
}; };
static DockerPlugin *s_instance = nullptr; static DockerPlugin *s_instance = nullptr;

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 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.
@@ -23,107 +23,63 @@
** **
****************************************************************************/ ****************************************************************************/
#include "dockerconstants.h"
#include "dockersettings.h" #include "dockersettings.h"
#include "dockerconstants.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <utils/filepath.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
using namespace Utils; using namespace Utils;
namespace Docker { namespace Docker {
namespace Internal { namespace Internal {
// DockerSettings
const char SETTINGS_KEY[] = "Docker";
static DockerSettings *theSettings = nullptr;
DockerSettings::DockerSettings() DockerSettings::DockerSettings()
{ {
theSettings = this; setSettingsGroup(Constants::DOCKER);
setAutoApply(false); setAutoApply(false);
registerAspect(&dockerBinaryPath);
dockerBinaryPath.setDisplayStyle(StringAspect::PathChooserDisplay);
dockerBinaryPath.setExpectedKind(PathChooser::ExistingCommand);
dockerBinaryPath.setDefaultValue(FilePath::fromString("docker").searchInPath().toString());
dockerBinaryPath.setDisplayName(tr("Docker CLI"));
dockerBinaryPath.setHistoryCompleter("Docker.Command.History");
dockerBinaryPath.setLabelText(tr("Command:"));
dockerBinaryPath.setSettingsKey("cli");
readSettings(Core::ICore::settings()); readSettings(Core::ICore::settings());
imageListFilter.setSettingsKey("DockerListFilter");
imageListFilter.setPlaceHolderText(tr("<filter>"));
imageListFilter.setDisplayStyle(StringAspect::LineEditDisplay);
imageListFilter.setLabelText(tr("Filter:"));
imageList.setDisplayStyle(StringAspect::TextEditDisplay);
imageList.setLabelText(tr("Images:"));
connect(&imageListFilter, &BaseAspect::changed, this, &DockerSettings::updateImageList);
} }
DockerSettings *DockerSettings::instance() // DockerSettingsPage
{
return theSettings;
}
void DockerSettings::writeSettings(QSettings *settings) const DockerSettingsPage::DockerSettingsPage(QSharedPointer<DockerSettings> settings)
{ {
settings->remove(SETTINGS_KEY); setId(Docker::Constants::DOCKER_SETTINGS_ID);
settings->beginGroup(SETTINGS_KEY);
forEachAspect([settings](BaseAspect *aspect) {
QtcSettings::setValueWithDefault(settings, aspect->settingsKey(),
aspect->value(), aspect->defaultValue());
});
settings->endGroup();
}
void DockerSettings::updateImageList()
{
QtcProcess process;
process.setCommand({"docker", {"search", imageListFilter.value()}});
process.start();
process.waitForFinished();
imageList.setValue(process.cleanedStdOut());
}
void DockerSettings::readSettings(const QSettings *settings)
{
const QString keyRoot = QString(SETTINGS_KEY) + '/';
forEachAspect([settings, keyRoot](BaseAspect *aspect) {
QString key = aspect->settingsKey();
const QVariant value = settings->value(keyRoot + key, aspect->defaultValue());
aspect->setValue(value);
});
}
// DockerOptionsPage
DockerOptionsPage::DockerOptionsPage(DockerSettings *settings)
{
setId(Constants::DOCKER_SETTINGS_ID);
setDisplayName(DockerSettings::tr("Docker")); setDisplayName(DockerSettings::tr("Docker"));
setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
setDisplayCategory(QCoreApplication::translate("ProjectExplorer", "Devices")); setSettings(settings.get());
setCategoryIconPath(":/projectexplorer/images/settingscategory_devices.png");
setSettings(settings);
setLayouter([settings](QWidget *widget) { setLayouter([settings = settings.get()](QWidget *widget) {
using namespace Layouting;
DockerSettings &s = *settings; DockerSettings &s = *settings;
using namespace Layouting;
// clang-format off
Column { Column {
Group { Group {
Title(DockerSettings::tr("Search Images on Docker Hub")), Title(DockerSettings::tr("Configuration")),
Form { Row { s.dockerBinaryPath }
s.imageListFilter,
s.imageList
},
}, },
Stretch() Stretch()
}.attachTo(widget); }.attachTo(widget);
// clang-format on
}); });
} }
} // Internal } // namespace Internal
} // Docker } // namespace Docker

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2022 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.
@@ -25,36 +25,27 @@
#pragma once #pragma once
#include "coreplugin/dialogs/ioptionspage.h"
#include <utils/aspects.h> #include <utils/aspects.h>
#include <utils/fileutils.h>
#include <coreplugin/dialogs/ioptionspage.h>
namespace Docker { namespace Docker {
namespace Internal { namespace Internal {
class DockerSettings : public Utils::AspectContainer class DockerSettings final : public Utils::AspectContainer
{ {
Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerSettings) Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerSettings)
public: public:
DockerSettings(); DockerSettings();
static DockerSettings *instance();
void readSettings(const QSettings *settings); Utils::StringAspect dockerBinaryPath;
void writeSettings(QSettings *settings) const;
void updateImageList();
Utils::StringAspect imageListFilter;
Utils::StringAspect imageList;
}; };
class DockerOptionsPage final : public Core::IOptionsPage class DockerSettingsPage final : public Core::IOptionsPage
{ {
public: public:
explicit DockerOptionsPage(DockerSettings *settings); explicit DockerSettingsPage(QSharedPointer<DockerSettings> settings);
}; };
} // Internal } // namespace Internal
} // Docker } // namespace Docker