Launcher: enable/disable all add-on types
Allow enable/disable in the UI of everything in the ‘add-ons’ page of the launcher.
This commit is contained in:
parent
32d6f75305
commit
410c249ba8
20 changed files with 467 additions and 160 deletions
|
@ -22,12 +22,15 @@
|
|||
#include "AddonsModel.hxx"
|
||||
#include "InstallSceneryDialog.hxx"
|
||||
#include "QtLauncher.hxx"
|
||||
#include "PathListModel.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
||||
AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* config) :
|
||||
QObject(parent),
|
||||
m_launcher(parent)
|
||||
m_launcher(parent),
|
||||
m_config(config)
|
||||
{
|
||||
m_catalogs = new CatalogListModel(this,
|
||||
simgear::pkg::RootRef(globals->packageRoot()));
|
||||
|
@ -38,10 +41,25 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
|||
connect(m_addonsModuleModel, &AddonsModel::modulesChanged, this, &AddOnsController::onAddonsChanged);
|
||||
|
||||
using namespace flightgear::addons;
|
||||
QSettings settings;
|
||||
m_sceneryPaths = settings.value("scenery-paths").toStringList();
|
||||
m_aircraftPaths = settings.value("aircraft-paths").toStringList();
|
||||
|
||||
m_sceneryPaths = new PathListModel(this);
|
||||
connect(m_sceneryPaths, &PathListModel::enabledPathsChanged, [this] () {
|
||||
m_sceneryPaths->saveToSettings("scenery-paths");
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
});
|
||||
m_sceneryPaths->loadFromSettings("scenery-paths");
|
||||
|
||||
m_aircraftPaths = new PathListModel(this);
|
||||
connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () {
|
||||
m_aircraftPaths->saveToSettings("aircraft-paths");
|
||||
|
||||
auto aircraftCache = LocalAircraftCache::instance();
|
||||
aircraftCache->setPaths(m_aircraftPaths->enabledPaths());
|
||||
aircraftCache->scanDirs();
|
||||
});
|
||||
m_aircraftPaths->loadFromSettings("aircraft-paths");
|
||||
|
||||
QSettings settings;
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
@ -66,14 +84,18 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) :
|
|||
qmlRegisterUncreatableType<AddOnsController>("FlightGear.Launcher", 1, 0, "AddOnsControllers", "no");
|
||||
qmlRegisterUncreatableType<CatalogListModel>("FlightGear.Launcher", 1, 0, "CatalogListModel", "no");
|
||||
qmlRegisterUncreatableType<AddonsModel>("FlightGear.Launcher", 1, 0, "AddonsModel", "no");
|
||||
qmlRegisterUncreatableType<PathListModel>("FlightGear.Launcher", 1, 0, "PathListMode", "no");
|
||||
|
||||
connect(m_config, &LaunchConfig::collect,
|
||||
this, &AddOnsController::collectArgs);
|
||||
}
|
||||
|
||||
QStringList AddOnsController::aircraftPaths() const
|
||||
PathListModel* AddOnsController::aircraftPaths() const
|
||||
{
|
||||
return m_aircraftPaths;
|
||||
}
|
||||
|
||||
QStringList AddOnsController::sceneryPaths() const
|
||||
PathListModel* AddOnsController::sceneryPaths() const
|
||||
{
|
||||
return m_sceneryPaths;
|
||||
}
|
||||
|
@ -119,6 +141,7 @@ QString AddOnsController::addAircraftPath() const
|
|||
}
|
||||
}
|
||||
|
||||
m_aircraftPaths->appendPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -206,6 +229,7 @@ QString AddOnsController::addSceneryPath() const
|
|||
}
|
||||
}
|
||||
|
||||
m_sceneryPaths->appendPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -227,37 +251,6 @@ void AddOnsController::openDirectory(QString path)
|
|||
QDesktopServices::openUrl(u);
|
||||
}
|
||||
|
||||
void AddOnsController::setAircraftPaths(QStringList aircraftPaths)
|
||||
{
|
||||
if (m_aircraftPaths == aircraftPaths)
|
||||
return;
|
||||
|
||||
m_aircraftPaths = aircraftPaths;
|
||||
emit aircraftPathsChanged(m_aircraftPaths);
|
||||
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("aircraft-paths", m_aircraftPaths);
|
||||
auto aircraftCache = LocalAircraftCache::instance();
|
||||
aircraftCache->setPaths(m_aircraftPaths);
|
||||
aircraftCache->scanDirs();
|
||||
}
|
||||
|
||||
void AddOnsController::setSceneryPaths(QStringList sceneryPaths)
|
||||
{
|
||||
if (m_sceneryPaths == sceneryPaths)
|
||||
return;
|
||||
|
||||
m_sceneryPaths = sceneryPaths;
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("scenery-paths", m_sceneryPaths);
|
||||
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
|
||||
emit sceneryPathsChanged(m_sceneryPaths);
|
||||
}
|
||||
|
||||
void AddOnsController::setAddons(AddonsModel* addons)
|
||||
{
|
||||
|
||||
|
@ -342,3 +335,31 @@ void AddOnsController::onAddonsChanged()
|
|||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
void AddOnsController::collectArgs()
|
||||
{
|
||||
// scenery paths
|
||||
Q_FOREACH(QString path, m_sceneryPaths->enabledPaths()) {
|
||||
m_config->setArg("fg-scenery", path);
|
||||
}
|
||||
|
||||
// aircraft paths
|
||||
Q_FOREACH(QString path, m_aircraftPaths->enabledPaths()) {
|
||||
m_config->setArg("fg-aircraft", path);
|
||||
}
|
||||
|
||||
// add-on module paths
|
||||
// we could query this directly from AddonsModel, but this is simpler right now
|
||||
QSettings settings;
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
||||
QString path = settings.value("path").toString();
|
||||
bool enable = settings.value("enable").toBool();
|
||||
if (enable) {
|
||||
m_config->setArg("addon", path);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
class CatalogListModel;
|
||||
class AddonsModel;
|
||||
class LauncherMainWindow;
|
||||
class PathListModel;
|
||||
class LaunchConfig;
|
||||
|
||||
class AddOnsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList aircraftPaths READ aircraftPaths WRITE setAircraftPaths NOTIFY aircraftPathsChanged)
|
||||
Q_PROPERTY(QStringList sceneryPaths READ sceneryPaths WRITE setSceneryPaths NOTIFY sceneryPathsChanged)
|
||||
Q_PROPERTY(PathListModel* aircraftPaths READ aircraftPaths CONSTANT)
|
||||
Q_PROPERTY(PathListModel* sceneryPaths READ sceneryPaths CONSTANT)
|
||||
Q_PROPERTY(QStringList modulePaths READ modulePaths WRITE setModulePaths NOTIFY modulePathsChanged)
|
||||
|
||||
Q_PROPERTY(CatalogListModel* catalogs READ catalogs CONSTANT)
|
||||
|
@ -22,10 +24,10 @@ class AddOnsController : public QObject
|
|||
Q_PROPERTY(bool showNoOfficialHangar READ showNoOfficialHangar NOTIFY showNoOfficialHangarChanged)
|
||||
|
||||
public:
|
||||
explicit AddOnsController(LauncherMainWindow *parent = nullptr);
|
||||
explicit AddOnsController(LauncherMainWindow *parent, LaunchConfig* config);
|
||||
|
||||
QStringList aircraftPaths() const;
|
||||
QStringList sceneryPaths() const;
|
||||
PathListModel* aircraftPaths() const;
|
||||
PathListModel* sceneryPaths() const;
|
||||
QStringList modulePaths() const;
|
||||
|
||||
CatalogListModel* catalogs() const
|
||||
|
@ -50,8 +52,6 @@ public:
|
|||
Q_INVOKABLE void officialCatalogAction(QString s);
|
||||
|
||||
signals:
|
||||
void aircraftPathsChanged(QStringList aircraftPaths);
|
||||
void sceneryPathsChanged(QStringList sceneryPaths);
|
||||
void modulePathsChanged(QStringList modulePaths);
|
||||
void modulesChanged();
|
||||
|
||||
|
@ -59,12 +59,12 @@ signals:
|
|||
void showNoOfficialHangarChanged();
|
||||
|
||||
public slots:
|
||||
void setAircraftPaths(QStringList aircraftPaths);
|
||||
void setSceneryPaths(QStringList sceneryPaths);
|
||||
void setModulePaths(QStringList modulePaths);
|
||||
void setAddons(AddonsModel* addons);
|
||||
void onAddonsChanged(void);
|
||||
|
||||
void collectArgs();
|
||||
|
||||
private:
|
||||
bool shouldShowOfficialCatalogMessage() const;
|
||||
void onCatalogsChanged();
|
||||
|
@ -72,9 +72,10 @@ private:
|
|||
LauncherMainWindow* m_launcher;
|
||||
CatalogListModel* m_catalogs = nullptr;
|
||||
AddonsModel* m_addonsModuleModel = nullptr;
|
||||
LaunchConfig* m_config = nullptr;
|
||||
|
||||
QStringList m_aircraftPaths;
|
||||
QStringList m_sceneryPaths;
|
||||
PathListModel* m_aircraftPaths = nullptr;
|
||||
PathListModel* m_sceneryPaths = nullptr;
|
||||
QStringList m_addonModulePaths;
|
||||
};
|
||||
|
||||
|
|
|
@ -42,6 +42,19 @@ const int STANDARD_THUMBNAIL_HEIGHT = 128;
|
|||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
bool isPackageFailure(Delegate::StatusCode status)
|
||||
{
|
||||
switch (status) {
|
||||
case Delegate::STATUS_SUCCESS:
|
||||
case Delegate::STATUS_REFRESHED:
|
||||
case Delegate::STATUS_IN_PROGRESS:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class PackageDelegate : public simgear::pkg::Delegate
|
||||
{
|
||||
public:
|
||||
|
@ -77,8 +90,8 @@ protected:
|
|||
|
||||
void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override
|
||||
{
|
||||
Q_UNUSED(bytes);
|
||||
Q_UNUSED(total);
|
||||
Q_UNUSED(bytes)
|
||||
Q_UNUSED(total)
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
@ -104,7 +117,7 @@ protected:
|
|||
|
||||
void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
|
||||
{
|
||||
Q_UNUSED(aReason);
|
||||
Q_UNUSED(aReason)
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
@ -261,7 +274,7 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
|||
}
|
||||
|
||||
if (row >= m_cachedLocalAircraftCount) {
|
||||
quint32 packageIndex = row - m_cachedLocalAircraftCount;
|
||||
quint32 packageIndex = static_cast<quint32>(row - m_cachedLocalAircraftCount);
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
|
||||
|
@ -393,6 +406,10 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
|
|||
return LocalAircraftCache::PackageUpdateAvailable;
|
||||
}
|
||||
|
||||
const auto status = i->status();
|
||||
if (isPackageFailure(status))
|
||||
return LocalAircraftCache::PackageInstallFailed;
|
||||
|
||||
return LocalAircraftCache::PackageInstalled;
|
||||
} else {
|
||||
return LocalAircraftCache::PackageNotInstalled;
|
||||
|
|
|
@ -124,6 +124,8 @@ if (HAVE_QT)
|
|||
AddonsModel.hxx
|
||||
PixmapImageItem.cxx
|
||||
PixmapImageItem.hxx
|
||||
PathListModel.cxx
|
||||
PathListModel.hxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
|
|
@ -93,13 +93,13 @@ void CatalogListModel::resetData()
|
|||
|
||||
int CatalogListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return m_catalogs.size();
|
||||
Q_UNUSED(parent)
|
||||
return static_cast<int>(m_catalogs.size());
|
||||
}
|
||||
|
||||
QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
simgear::pkg::CatalogRef cat = m_catalogs.at(index.row());
|
||||
|
||||
const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (role == Qt::DisplayRole) {
|
||||
QString name = QString::fromStdString(cat->name());
|
||||
QString desc;
|
||||
|
@ -139,6 +139,8 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
|||
return translateStatusForCatalog(cat);
|
||||
} else if (role == CatalogIsNewlyAdded) {
|
||||
return (cat == m_newlyAddedCatalog);
|
||||
} else if (role == CatalogEnabled) {
|
||||
return cat->isUserEnabled();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -146,13 +148,18 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
|||
|
||||
bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (role == CatalogEnabled) {
|
||||
cat->setUserEnabled(value.toBool());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags r = Qt::ItemIsSelectable;
|
||||
const auto cat = m_catalogs.at(index.row());
|
||||
const auto cat = m_catalogs.at(static_cast<size_t>(index.row()));
|
||||
if (cat->isEnabled()) {
|
||||
r |= Qt::ItemIsEnabled;
|
||||
}
|
||||
|
@ -168,28 +175,27 @@ QHash<int, QByteArray> CatalogListModel::roleNames() const
|
|||
result[CatalogNameRole] = "name";
|
||||
result[CatalogStatusRole] = "status";
|
||||
result[CatalogIsNewlyAdded] = "isNewlyAdded";
|
||||
|
||||
result[CatalogEnabled] = "enabled";
|
||||
return result;
|
||||
}
|
||||
|
||||
void CatalogListModel::removeCatalog(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= m_catalogs.size())) {
|
||||
if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string removeId = m_catalogs.at(index)->id();
|
||||
const std::string removeId = m_catalogs.at(static_cast<size_t>(index))->id();
|
||||
m_packageRoot->removeCatalogById(removeId);
|
||||
resetData();
|
||||
}
|
||||
|
||||
void CatalogListModel::refreshCatalog(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= m_catalogs.size())) {
|
||||
if ((index < 0) || (index >= static_cast<int>(m_catalogs.size()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_catalogs.at(index)->refresh();
|
||||
m_catalogs.at(static_cast<size_t>(index))->refresh();
|
||||
}
|
||||
|
||||
void CatalogListModel::installDefaultCatalog()
|
||||
|
@ -221,7 +227,7 @@ int CatalogListModel::indexOf(QUrl url)
|
|||
if (it == m_catalogs.end())
|
||||
return -1;
|
||||
|
||||
return std::distance(m_catalogs.begin(), it);
|
||||
return static_cast<int>(std::distance(m_catalogs.begin(), it));
|
||||
}
|
||||
|
||||
void CatalogListModel::finalizeAddCatalog()
|
||||
|
@ -237,7 +243,7 @@ void CatalogListModel::finalizeAddCatalog()
|
|||
return;
|
||||
}
|
||||
|
||||
const int row = std::distance(m_catalogs.begin(), it);
|
||||
const int row = static_cast<int>(std::distance(m_catalogs.begin(), it));
|
||||
m_newlyAddedCatalog.clear();
|
||||
emit isAddingCatalogChanged();
|
||||
emit statusOfAddingCatalogChanged();
|
||||
|
|
|
@ -38,6 +38,7 @@ const int CatalogStatusRole = Qt::UserRole + 5;
|
|||
const int CatalogDescriptionRole = Qt::UserRole + 6;
|
||||
const int CatalogNameRole = Qt::UserRole + 7;
|
||||
const int CatalogIsNewlyAdded = Qt::UserRole + 8;
|
||||
const int CatalogEnabled = Qt::UserRole + 9;
|
||||
|
||||
class CatalogDelegate;
|
||||
|
||||
|
|
|
@ -102,8 +102,6 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
|
|||
this, &LauncherController::updateSelectedAircraft);
|
||||
|
||||
QSettings settings;
|
||||
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
|
||||
LocalAircraftCache::instance()->scanDirs();
|
||||
m_aircraftModel->setPackageRoot(globals->packageRoot());
|
||||
|
||||
m_aircraftGridMode = settings.value("aircraftGridMode").toBool();
|
||||
|
@ -280,31 +278,6 @@ void LauncherController::collectAircraftArgs()
|
|||
m_config->setArg("state", state);
|
||||
}
|
||||
}
|
||||
|
||||
// scenery paths
|
||||
QSettings settings;
|
||||
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
||||
m_config->setArg("fg-scenery", path);
|
||||
}
|
||||
|
||||
// aircraft paths
|
||||
Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) {
|
||||
m_config->setArg("fg-aircraft", path);
|
||||
}
|
||||
|
||||
// add-on module paths
|
||||
int size = settings.beginReadArray("addon-modules");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
|
||||
QString path = settings.value("path").toString();
|
||||
bool enable = settings.value("enable").toBool();
|
||||
if (enable) {
|
||||
m_config->setArg("addon", path);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
}
|
||||
|
||||
void LauncherController::saveAircraft()
|
||||
|
|
|
@ -68,9 +68,8 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
connect(qa, &QAction::triggered, m_controller, &LauncherController::quit);
|
||||
|
||||
m_controller->initialRestoreSettings();
|
||||
flightgear::launcherSetSceneryPaths();
|
||||
|
||||
auto addOnsCtl = new AddOnsController(this);
|
||||
auto addOnsCtl = new AddOnsController(this, m_controller->config());
|
||||
|
||||
////////////
|
||||
#if defined(Q_OS_WIN)
|
||||
|
|
|
@ -126,7 +126,8 @@ public:
|
|||
PackageUpdateAvailable,
|
||||
PackageQueued,
|
||||
PackageDownloading,
|
||||
NotPackaged
|
||||
NotPackaged,
|
||||
PackageInstallFailed
|
||||
};
|
||||
|
||||
Q_ENUMS(PackageStatus)
|
||||
|
|
188
src/GUI/PathListModel.cxx
Normal file
188
src/GUI/PathListModel.cxx
Normal file
|
@ -0,0 +1,188 @@
|
|||
#include "PathListModel.hxx"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
PathListModel::PathListModel(QObject *pr) :
|
||||
QAbstractListModel(pr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PathListModel::~PathListModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PathListModel::loadFromSettings(QString key)
|
||||
{
|
||||
QSettings settings;
|
||||
if (!settings.contains(key))
|
||||
return;
|
||||
|
||||
QVariantList vl = settings.value(key).toList();
|
||||
mPaths.clear();
|
||||
mPaths.reserve(static_cast<size_t>(vl.size()));
|
||||
|
||||
beginResetModel();
|
||||
|
||||
Q_FOREACH(QVariant v, vl) {
|
||||
QVariantMap m = v.toMap();
|
||||
PathEntry entry;
|
||||
entry.path = m.value("path").toString();
|
||||
if (entry.path.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.enabled = m.value("enabled", QVariant{true}).toBool();
|
||||
mPaths.push_back(entry);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::saveToSettings(QString key) const
|
||||
{
|
||||
QVariantList vl;
|
||||
for (const auto &e : mPaths) {
|
||||
QVariantMap v;
|
||||
v["path"] = e.path;
|
||||
v["enabled"] = e.enabled;
|
||||
vl.append(v);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue(key, vl);
|
||||
}
|
||||
|
||||
QStringList PathListModel::readEnabledPaths(QString settingsKey)
|
||||
{
|
||||
QSettings settings;
|
||||
if (!settings.contains(settingsKey))
|
||||
return {};
|
||||
|
||||
QStringList result;
|
||||
QVariantList vl = settings.value(settingsKey).toList();
|
||||
Q_FOREACH(QVariant v, vl) {
|
||||
QVariantMap m = v.toMap();
|
||||
if (!m.value("enabled").toBool())
|
||||
continue;
|
||||
|
||||
result.append(m.value("path").toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList PathListModel::enabledPaths() const
|
||||
{
|
||||
QStringList result;
|
||||
for (const auto& e : mPaths) {
|
||||
if (e.enabled) {
|
||||
result.append(e.path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PathListModel::count()
|
||||
{
|
||||
return static_cast<int>(mPaths.size());
|
||||
}
|
||||
|
||||
int PathListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return static_cast<int>(mPaths.size());
|
||||
}
|
||||
|
||||
QVariant PathListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
int row = index.row();
|
||||
const auto& entry = mPaths.at(static_cast<size_t>(row));
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case PathRole:
|
||||
return entry.path;
|
||||
|
||||
case PathEnabledRole:
|
||||
return entry.enabled;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool PathListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
auto& entry = mPaths.at(static_cast<size_t>(row));
|
||||
if (role == PathEnabledRole) {
|
||||
entry.enabled = value.toBool();
|
||||
emit dataChanged(index, index, {PathEnabledRole});
|
||||
emit enabledPathsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PathListModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
||||
result[Qt::DisplayRole] = "path";
|
||||
result[PathEnabledRole] = "enabled";
|
||||
return result;
|
||||
}
|
||||
|
||||
void PathListModel::removePath(int index)
|
||||
{
|
||||
if ((index < 0) || (index >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
beginRemoveRows({}, index, index);
|
||||
auto it = mPaths.begin() + index;
|
||||
mPaths.erase(it);
|
||||
endRemoveRows();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::appendPath(QString path)
|
||||
{
|
||||
PathEntry entry;
|
||||
entry.path = path;
|
||||
entry.enabled = true; // enable by default
|
||||
const int newRow = static_cast<int>(mPaths.size());
|
||||
beginInsertRows({}, newRow, newRow);
|
||||
mPaths.push_back(entry);
|
||||
endInsertRows();
|
||||
emit enabledPathsChanged();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
void PathListModel::sawpIndices(int indexA, int indexB)
|
||||
{
|
||||
if ((indexA < 0) || (indexA >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << indexA;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((indexB < 0) || (indexB >= static_cast<int>(mPaths.size()))) {
|
||||
qWarning() << Q_FUNC_INFO << "index invalid:" << indexB;
|
||||
return;
|
||||
}
|
||||
|
||||
std::swap(mPaths[static_cast<size_t>(indexA)],
|
||||
mPaths[static_cast<size_t>(indexB)]);
|
||||
emit dataChanged(index(indexA), index(indexA));
|
||||
emit dataChanged(index(indexB), index(indexB));
|
||||
emit enabledPathsChanged();
|
||||
}
|
||||
|
56
src/GUI/PathListModel.hxx
Normal file
56
src/GUI/PathListModel.hxx
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef PATHLISTMODEL_HXX
|
||||
#define PATHLISTMODEL_HXX
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
const int PathRole = Qt::UserRole + 1;
|
||||
const int PathEnabledRole = Qt::UserRole + 2;
|
||||
|
||||
class PathListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
public:
|
||||
|
||||
PathListModel(QObject* pr);
|
||||
~PathListModel() override;
|
||||
|
||||
void loadFromSettings(QString key);
|
||||
void saveToSettings(QString key) const;
|
||||
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
static QStringList readEnabledPaths(QString settingsKey);
|
||||
|
||||
QStringList enabledPaths() const;
|
||||
|
||||
int count();
|
||||
signals:
|
||||
void enabledPathsChanged();
|
||||
void countChanged();
|
||||
|
||||
public slots:
|
||||
void removePath(int index);
|
||||
void appendPath(QString path);
|
||||
|
||||
void sawpIndices(int indexA, int indexB);
|
||||
|
||||
private:
|
||||
struct PathEntry {
|
||||
QString path;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
std::vector<PathEntry> mPaths;
|
||||
};
|
||||
|
||||
#endif // PATHLISTMODEL_HXX
|
|
@ -74,6 +74,7 @@
|
|||
#include "LauncherMainWindow.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
#include "UnitsModel.hxx"
|
||||
#include "PathListModel.hxx"
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear::pkg;
|
||||
|
@ -403,7 +404,7 @@ void launcherSetSceneryPaths()
|
|||
// positions
|
||||
QSettings settings;
|
||||
// append explicit scenery paths
|
||||
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
||||
Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths")) {
|
||||
globals->append_fg_scenery(path.toStdString());
|
||||
}
|
||||
|
||||
|
|
BIN
src/GUI/assets/icons8-hide-50.png
Normal file
BIN
src/GUI/assets/icons8-hide-50.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -102,12 +102,7 @@ Item {
|
|||
description: qsTr("To use aircraft you download yourself, FlightGear needs to " +
|
||||
"know the folder(s) containing the aircraft data.")
|
||||
showAddButton: true
|
||||
onAdd: {
|
||||
var newPath =_addOns.addAircraftPath();
|
||||
if (newPath !== "") {
|
||||
_addOns.aircraftPaths.push(newPath)
|
||||
}
|
||||
}
|
||||
onAdd: _addOns.addAircraftPath();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -127,21 +122,10 @@ Item {
|
|||
model: _addOns.aircraftPaths
|
||||
delegate: PathListDelegate {
|
||||
width: aircraftPathsColumn.width
|
||||
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
|
||||
modelCount: _addOns.aircraftPaths.length
|
||||
|
||||
onPerformDelete: {
|
||||
var modifiedPaths = _addOns.aircraftPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
_addOns.aircraftPaths = modifiedPaths;
|
||||
}
|
||||
|
||||
onPerformMove: {
|
||||
var modifiedPaths = _addOns.aircraftPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
modifiedPaths.splice(newIndex, 0, modelData)
|
||||
_addOns.aircraftPaths = modifiedPaths;
|
||||
}
|
||||
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path);
|
||||
modelCount: _addOns.aircraftPaths.count
|
||||
onPerformDelete: _addOns.aircraftPaths.removePath(model.index)
|
||||
onPerformMove: _addOns.aircraftPaths.sawpIndices(model.index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,12 +225,7 @@ Item {
|
|||
"to know the folders containing the scenery data. " +
|
||||
"Adjust the order of the list to control which scenery is used in a region.");
|
||||
showAddButton: true
|
||||
onAdd: {
|
||||
var newPath =_addOns.addSceneryPath();
|
||||
if (newPath !== "") {
|
||||
_addOns.sceneryPaths.push(newPath)
|
||||
}
|
||||
}
|
||||
onAdd: _addOns.addSceneryPath();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -267,21 +246,10 @@ Item {
|
|||
|
||||
delegate: PathListDelegate {
|
||||
width: sceneryPathsColumn.width
|
||||
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
|
||||
modelCount: _addOns.sceneryPaths.length
|
||||
|
||||
onPerformDelete: {
|
||||
var modifiedPaths = _addOns.sceneryPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
_addOns.sceneryPaths = modifiedPaths;
|
||||
}
|
||||
|
||||
onPerformMove: {
|
||||
var modifiedPaths = _addOns.sceneryPaths.slice()
|
||||
modifiedPaths.splice(model.index, 1);
|
||||
modifiedPaths.splice(newIndex, 0, modelData)
|
||||
_addOns.sceneryPaths = modifiedPaths;
|
||||
}
|
||||
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path);
|
||||
modelCount: _addOns.sceneryPaths.count
|
||||
onPerformDelete: _addOns.sceneryPaths.removePath(model.index)
|
||||
onPerformMove: _addOns.sceneryPaths.swapIndices(model.index, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,14 +272,10 @@ Item {
|
|||
var path = _addOns.installCustomScenery();
|
||||
if (path !== "") {
|
||||
// insert into scenery paths if not already present
|
||||
var sceneryPaths = _addOns.sceneryPaths
|
||||
for (var i = 0; i < sceneryPaths.length; i++) {
|
||||
if (sceneryPaths[i] === path)
|
||||
return; // found, we are are done
|
||||
}
|
||||
|
||||
|
||||
// not found, add it
|
||||
_addOns.sceneryPaths.push(path);
|
||||
_addOns.sceneryPaths.appendPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,9 @@ Item {
|
|||
width: delegateRoot.width
|
||||
|
||||
Checkbox {
|
||||
id: chkbox
|
||||
id: enableCheckbox
|
||||
checked: model.enable
|
||||
width: 30
|
||||
anchors.left: parent.left
|
||||
anchors.right: addonsDelegateHover.left
|
||||
anchors.rightMargin: Style.margin
|
||||
height: contentRect.height
|
||||
onCheckedChanged: {
|
||||
_addOns.modules.enable(model.index, checked)
|
||||
|
@ -59,7 +56,7 @@ Item {
|
|||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: chkbox.right
|
||||
anchors.left: enableCheckbox.right
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
|
|
@ -26,12 +26,19 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function isDisabled()
|
||||
{
|
||||
return (model.status !== CatalogListModel.Ok) ||
|
||||
(enableCheckbox.checked === false);
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
anchors.top: divider.bottom
|
||||
height: catalogTextColumn.childrenRect.height + Style.margin * 2
|
||||
width: parent.width
|
||||
|
||||
|
||||
Column {
|
||||
id: catalogTextColumn
|
||||
|
||||
|
@ -42,11 +49,28 @@ Item {
|
|||
anchors.rightMargin: Style.margin
|
||||
spacing: Style.margin
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
font.bold: true
|
||||
width: parent.width
|
||||
text: model.name
|
||||
Row {
|
||||
spacing: Style.margin
|
||||
height: headingText.height
|
||||
|
||||
Checkbox {
|
||||
id: enableCheckbox
|
||||
checked: model.enabled
|
||||
height: parent.height
|
||||
onCheckedChanged: model.enable = checked;
|
||||
// only allow the user to toggle enable/disable if
|
||||
// the catalog is valid
|
||||
visible: (model.status === CatalogListModel.Ok)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: headingText
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
font.bold: true
|
||||
width: catalogTextColumn.width - enableCheckbox.width
|
||||
text: model.name
|
||||
font.strikeout: delegateRoot.isDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Item {
|
||||
property bool checked: false
|
||||
|
@ -11,7 +12,7 @@ Item {
|
|||
id: checkBox
|
||||
width: 18
|
||||
height: 18
|
||||
border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f"
|
||||
border.color: mouseArea.containsMouse ? Style.frameColor : Style.inactiveThemeColor
|
||||
border.width: 1
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
|
@ -22,7 +23,7 @@ Item {
|
|||
height: 12
|
||||
anchors.centerIn: parent
|
||||
id: checkMark
|
||||
color: "#9f9f9f"
|
||||
color: Style.themeColor
|
||||
visible: checked
|
||||
}
|
||||
}
|
||||
|
@ -41,5 +42,6 @@ Item {
|
|||
onClicked: {
|
||||
checked = !checked
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
|
36
src/GUI/qml/EnableDisableButton.qml
Normal file
36
src/GUI/qml/EnableDisableButton.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.0
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
width: height
|
||||
height: icon.implicitHeight + 1
|
||||
|
||||
property bool enable: true
|
||||
|
||||
signal clicked();
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
source: "qrc:///svg/icon-hide"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
// hoverEnabled: true
|
||||
onClicked: root.clicked();
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// Text {
|
||||
// anchors.right: root.left
|
||||
// anchors.rightMargin: Style.margin
|
||||
// anchors.verticalCenter: root.verticalCenter
|
||||
// visible: mouse.containsMouse
|
||||
// color: Style.baseTextColor
|
||||
// font.pixelSize: Style.baseFontPixelSize
|
||||
// text: root.enable ? qsTr("Click_to_disable")
|
||||
// : qsTr("Click_to_enable")
|
||||
// }
|
||||
}
|
|
@ -36,9 +36,23 @@ Item {
|
|||
height: Math.max(label.implicitHeight, pathDeleteButton.height)
|
||||
width: delegateRoot.width
|
||||
|
||||
Checkbox {
|
||||
id: enableCheckbox
|
||||
checked: model.enabled
|
||||
anchors.left: parent.left
|
||||
height: parent.height
|
||||
onCheckedChanged: {
|
||||
model.enabled = checked;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pathDelegateHover
|
||||
anchors.fill: parent
|
||||
anchors.left: enableCheckbox.right
|
||||
anchors.leftMargin: Style.margin
|
||||
anchors.right: parent.right
|
||||
height: parent.height
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
|
@ -46,10 +60,10 @@ Item {
|
|||
// MouseArea, so nested containsMouse logic works
|
||||
ClickableText {
|
||||
id: label
|
||||
text: modelData
|
||||
text: model.path
|
||||
onClicked: {
|
||||
// open the location
|
||||
_addOns.openDirectory(modelData)
|
||||
_addOns.openDirectory(model.path)
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: reorderButton.left
|
||||
|
@ -58,6 +72,8 @@ Item {
|
|||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
font.strikeout: enableCheckbox.checked === false
|
||||
}
|
||||
|
||||
DeleteButton {
|
||||
|
@ -88,7 +104,7 @@ Item {
|
|||
delegateRoot.performMove(model.index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of MouseArea for hover
|
||||
|
||||
YesNoPanel {
|
||||
id: confirmDeletePath
|
||||
|
|
|
@ -129,6 +129,7 @@
|
|||
<file>qml/AircraftGridView.qml</file>
|
||||
<file>qml/AircraftListView.qml</file>
|
||||
<file>qml/GridToggleButton.qml</file>
|
||||
<file>qml/EnableDisableButton.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/preview">
|
||||
<file alias="close-icon">preview-close.png</file>
|
||||
|
@ -147,5 +148,6 @@
|
|||
<file alias="toolbox-fly-heli">assets/icons8-helicopter.svg</file>
|
||||
<file alias="icon-grid-view">assets/icons8-grid-view.svg</file>
|
||||
<file alias="icon-list-view">assets/icons8-menu.svg</file>
|
||||
<file alias="icon-hide">assets/icons8-hide-50.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
Loading…
Add table
Reference in a new issue