diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt
index b93440a4c..534c4682f 100644
--- a/src/GUI/CMakeLists.txt
+++ b/src/GUI/CMakeLists.txt
@@ -140,6 +140,10 @@ if (HAVE_QT)
MPServersModel.h
PathUrlHelper.cxx
PathUrlHelper.hxx
+ RecentAircraftModel.cxx
+ RecentAircraftModel.hxx
+ RecentLocationsModel.cxx
+ RecentLocationsModel.hxx
${uic_sources}
${qrc_sources}
${qml_sources})
diff --git a/src/GUI/DefaultAircraftLocator.cxx b/src/GUI/DefaultAircraftLocator.cxx
index 3986339b0..b14021a9d 100644
--- a/src/GUI/DefaultAircraftLocator.cxx
+++ b/src/GUI/DefaultAircraftLocator.cxx
@@ -34,6 +34,20 @@ std::string defaultAirportICAO()
return airportCode;
}
+string_list defaultSplashScreenPaths()
+{
+ string_list result;
+ SGPath tpath = globals->get_fg_root() / "Textures";
+ simgear::Dir d(tpath);
+ for (auto c : d.children(simgear::Dir::TYPE_FILE, ".png")) {
+ if (c.file_base().find("Splash") == 0) {
+ result.push_back(c.utf8Str());
+ }
+ }
+
+ return result;
+}
+
DefaultAircraftLocator::DefaultAircraftLocator()
{
SGPropertyNode_ptr root = loadXMLDefaults();
diff --git a/src/GUI/DefaultAircraftLocator.hxx b/src/GUI/DefaultAircraftLocator.hxx
index 0cc350fe2..b68373d29 100644
--- a/src/GUI/DefaultAircraftLocator.hxx
+++ b/src/GUI/DefaultAircraftLocator.hxx
@@ -13,6 +13,8 @@ namespace flightgear
std::string defaultAirportICAO();
+string_list defaultSplashScreenPaths();
+
/**
* we don't want to rely on the main AircraftModel threaded scan, to find the
* default aircraft, so we do a synchronous scan here, on the assumption that
diff --git a/src/GUI/Launcher.ui b/src/GUI/Launcher.ui
index 9d80698e4..b909f4ecb 100644
--- a/src/GUI/Launcher.ui
+++ b/src/GUI/Launcher.ui
@@ -250,231 +250,31 @@
0
-
-
- -
-
-
-
- 48
- 75
- true
-
-
-
- FlightGear 2017.1.0
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
-
- 16
-
-
-
- State:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- 16
-
-
-
- aircraft
-
-
- true
-
-
-
- -
-
-
-
- 16
-
-
-
- Settings:
-
-
- Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
-
-
-
- -
-
-
-
- 16
-
-
-
- Aircraft:
-
-
- Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
-
-
-
- -
-
-
-
- 16
-
-
-
- location
-
-
- true
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 171
- 128
-
-
-
- TextLabel
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 294
-
-
-
-
- -
-
-
-
- 11
-
-
-
- TextLabel
-
-
- true
-
-
-
- -
-
-
- -
-
-
- <html><head/><body><p>©2017 FlightGear contributors. Licensed under the GNU Public License. See <a href="http://www.flightgear.org"><span style=" text-decoration: underline; color:#0000ff;">here</span></a> for more information</p></body></html>
-
-
- true
-
-
- true
-
-
-
- -
-
-
-
- 16
-
-
-
- Location:
-
-
- Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
-
-
-
- -
-
-
-
-
-
- :/app-icon-large
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
-
- 16
-
-
-
- settings
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
-
-
- true
-
-
-
- -
-
-
-
- 11
-
-
-
- TextLabel
-
-
+
+
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+
diff --git a/src/GUI/LauncherMainWindow.cxx b/src/GUI/LauncherMainWindow.cxx
index 24097b3d7..733b36cb3 100644
--- a/src/GUI/LauncherMainWindow.cxx
+++ b/src/GUI/LauncherMainWindow.cxx
@@ -9,6 +9,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include
@@ -49,11 +52,11 @@
#include "LauncherArgumentTokenizer.hxx"
#include "PathUrlHelper.hxx"
#include "PopupWindowTracker.hxx"
+#include "RecentAircraftModel.hxx"
+#include "RecentLocationsModel.hxx"
#include "ui_Launcher.h"
-const int MAX_RECENT_AIRCRAFT = 20;
-
using namespace simgear::pkg;
extern void restartTheApp(QStringList fgArgs);
@@ -75,14 +78,11 @@ QQmlPrivate::AutoParentResult launcher_autoParent(QObject* thing, QObject* paren
LauncherMainWindow::LauncherMainWindow() :
QMainWindow(),
- m_ui(NULL),
m_subsystemIdleTimer(NULL)
{
m_ui.reset(new Ui::Launcher);
m_ui->setupUi(this);
- m_ui->appTitleLabel->setText(tr("FlightGear %1").arg(FLIGHTGEAR_VERSION));
-
QMenuBar* mb = menuBar();
#if !defined(Q_OS_MAC)
@@ -108,15 +108,7 @@ LauncherMainWindow::LauncherMainWindow() :
m_serversModel = new MPServersModel(this);
- // keep the description QLabel in sync as the current item changes
- connect(m_ui->stateCombo,
- static_cast(&QComboBox::currentIndexChanged),
- [this](int)
- {
- auto v = m_ui->stateCombo->currentData(QmlAircraftInfo::StateDescriptionRole);
- m_ui->stateDescription->setText(v.toString());
- m_ui->stateDescription->setVisible(!v.toString().isEmpty());
- });
+ m_locationHistory = new RecentLocationsModel(this);
m_selectedAircraftInfo = new QmlAircraftInfo(this);
initQML();
@@ -135,23 +127,14 @@ LauncherMainWindow::LauncherMainWindow() :
connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->addOnsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
- connect(m_ui->aircraftHistory, &QPushButton::clicked,
- this, &LauncherMainWindow::onPopupAircraftHistory);
- connect(m_ui->locationHistory, &QPushButton::clicked,
- this, &LauncherMainWindow::onPopupLocationHistory);
-
connect(m_ui->location, &LocationWidget::descriptionChanged,
- m_ui->locationDescription, &QLabel::setText);
+ this, &LauncherMainWindow::summaryChanged);
QAction* qa = new QAction(this);
qa->setShortcut(QKeySequence("Ctrl+Q"));
connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit);
addAction(qa);
- QIcon historyIcon(":/history-icon");
- m_ui->aircraftHistory->setIcon(historyIcon);
- m_ui->locationHistory->setIcon(historyIcon);
-
m_aircraftModel = new AircraftItemModel(this);
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_installedAircraftModel->setInstalledFilterEnabled(true);
@@ -161,40 +144,13 @@ LauncherMainWindow::LauncherMainWindow() :
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
- m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView);
-
- m_ui->aircraftList->engine()->addImportPath("qrc:///");
- m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", this);
- m_ui->aircraftList->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
-
- m_ui->aircraftList->setSource(QUrl("qrc:///qml/AircraftList.qml"));
-
- m_ui->settings->engine()->addImportPath("qrc:///");
- m_ui->settings->engine()->rootContext()->setContextProperty("_launcher", this);
- m_ui->settings->engine()->rootContext()->setContextProperty("_mpServers", m_serversModel);
-
- m_ui->settings->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
-
- m_ui->settings->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_ui->settings->setSource(QUrl("qrc:///qml/Settings.qml"));
-
- m_ui->environmentPage->engine()->addImportPath("qrc:///");
- m_ui->environmentPage->engine()->rootContext()->setContextProperty("_launcher", this);
- auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
- m_ui->environmentPage->engine()->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel);
-
- m_ui->environmentPage->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
- m_ui->environmentPage->engine()->rootContext()->setContextProperty("_config", m_config);
-
- m_ui->environmentPage->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_ui->environmentPage->setSource(QUrl("qrc:///qml/Environment.qml"));
+ m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
this, &LauncherMainWindow::onAircraftInstalledCompleted);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
this, &LauncherMainWindow::onAircraftInstallFailed);
-
connect(LocalAircraftCache::instance(),
&LocalAircraftCache::scanCompleted,
this, &LauncherMainWindow::updateSelectedAircraft);
@@ -219,8 +175,50 @@ LauncherMainWindow::LauncherMainWindow() :
m_ui->stack->addWidget(m_viewCommandLinePage);
restoreSettings();
- updateSettingsSummary();
+
+ emit summaryChanged();
emit showNoOfficialHangarChanged();
+
+ /////////////
+ // aircraft
+ m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView);
+
+ m_ui->aircraftList->engine()->addImportPath("qrc:///");
+ m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", this);
+ m_ui->aircraftList->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
+
+ m_ui->aircraftList->setSource(QUrl("qrc:///qml/AircraftList.qml"));
+
+ // settings
+ m_ui->settings->engine()->addImportPath("qrc:///");
+ m_ui->settings->engine()->rootContext()->setContextProperty("_launcher", this);
+ m_ui->settings->engine()->rootContext()->setContextProperty("_mpServers", m_serversModel);
+
+ m_ui->settings->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
+
+ m_ui->settings->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_ui->settings->setSource(QUrl("qrc:///qml/Settings.qml"));
+
+ // environemnt
+ m_ui->environmentPage->engine()->addImportPath("qrc:///");
+ m_ui->environmentPage->engine()->rootContext()->setContextProperty("_launcher", this);
+ auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
+ m_ui->environmentPage->engine()->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel);
+
+ m_ui->environmentPage->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
+ m_ui->environmentPage->engine()->rootContext()->setContextProperty("_config", m_config);
+
+ m_ui->environmentPage->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_ui->environmentPage->setSource(QUrl("qrc:///qml/Environment.qml"));
+
+ // summary
+ m_ui->summary->engine()->addImportPath("qrc:///");
+ m_ui->summary->engine()->rootContext()->setContextProperty("_launcher", this);
+ m_ui->summary->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
+ m_ui->summary->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_ui->summary->setSource(QUrl("qrc:///qml/Summary.qml"));
+ //////////////////////////
+
}
void LauncherMainWindow::initQML()
@@ -240,6 +238,9 @@ void LauncherMainWindow::initQML()
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "QAIM", "no");
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
+ qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "RecentAircraftModel", "no");
+ qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "RecentLocationsModel", "no");
+
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "Control", "Base class");
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
qmlRegisterType("FlightGear.Launcher", 1, 0, "FileDialog");
@@ -265,6 +266,9 @@ void LauncherMainWindow::initQML()
settingsContext->setContextProperty("_config", m_config);
settingsContext->setContextProperty("_osName", osName);
+ QQmlContext* summaryContext = m_ui->summary->engine()->rootContext();
+ summaryContext->setContextProperty("_config", m_config);
+
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache");
qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model");
qmlRegisterType("FlightGear.Launcher", 1, 0, "ThumbnailImage");
@@ -311,12 +315,8 @@ void LauncherMainWindow::restoreSettings()
restoreGeometry(settings.value("window-geometry").toByteArray());
- // full paths to -set.xml files
- m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList());
-
- if (!m_recentAircraft.empty()) {
- m_selectedAircraft = m_recentAircraft.front();
- } else {
+ m_selectedAircraft = m_aircraftHistory->mostRecent();
+ if (m_selectedAircraft.isEmpty()) {
// select the default aircraft specified in defaults.xml
flightgear::DefaultAircraftLocator da;
if (da.foundPath().exists()) {
@@ -332,22 +332,16 @@ void LauncherMainWindow::restoreSettings()
}
m_ui->location->restoreSettings();
- m_recentLocations = settings.value("recent-location-sets").toList();
- QVariantMap currentLocation;
- if (m_recentLocations.isEmpty()) {
+ QVariantMap currentLocation = m_locationHistory->mostRecent();
+ if (currentLocation.isEmpty()) {
// use the default
std::string defaultAirport = flightgear::defaultAirportICAO();
FGAirportRef apt = FGAirport::findByIdent(defaultAirport);
if (apt) {
currentLocation["location-id"] = static_cast(apt->guid());
currentLocation["location-apt-runway"] = "active";
- qDebug() << "restored default airport:" << QString::fromStdString(defaultAirport);
} // otherwise we failed to find the default airport in the nav-db :(
- } else {
- // we have a valid current location
- currentLocation = m_recentLocations.front().toMap();
}
-
m_ui->location->restoreLocation(currentLocation);
updateSelectedAircraft();
@@ -363,9 +357,10 @@ void LauncherMainWindow::saveSettings()
{
emit requestSaveState();
+ m_aircraftHistory->saveToSettings();
+ m_locationHistory->saveToSettings();
+
QSettings settings;
- settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
- settings.setValue("recent-location-sets", m_recentLocations);
settings.setValue("window-geometry", saveGeometry());
Q_FOREACH(SettingsSection* ss, findChildren()) {
@@ -416,41 +411,13 @@ void LauncherMainWindow::onRun()
m_config->reset();
m_config->collect();
- // aircraft
- if (!m_selectedAircraft.isEmpty()) {
- // manage aircraft history
- if (m_recentAircraft.contains(m_selectedAircraft))
- m_recentAircraft.removeOne(m_selectedAircraft);
- m_recentAircraft.prepend(m_selectedAircraft);
- if (m_recentAircraft.size() > MAX_RECENT_AIRCRAFT)
- m_recentAircraft.pop_back();
- }
+ m_aircraftHistory->insert(m_selectedAircraft);
- if (m_ui->stateCombo->isVisible()) {
- // apply state setting
- std::string tag = m_ui->stateCombo->currentData(QmlAircraftInfo::StateTagRole).
- toString().toStdString();
-
- // implicit auto behaviour disabled for 2018.1, since it
- // needs a bit more work
-#if 0
- if (tag == "auto") {
- bool isExplictAuto = m_ui->stateCombo->currentData(QmlAircraftInfo::StateExplicitRole).toBool();
- if (!isExplictAuto) {
- tag = selectStateAutomatically();
- qInfo() << "automatic state selection: picked:" << QString::fromStdString(tag);
- }
- }
-#endif
- if (!tag.empty() && (tag != "__default__")) {
- m_config->setArg("state", tag);
- }
- } // of applying state selection
+ QVariant locSet = m_ui->location->saveLocation();
+ m_locationHistory->insert(locSet);
// aircraft paths
QSettings settings;
- updateLocationHistory();
-
QString downloadDir = settings.value("downloadSettings/downloadDir").toString();
if (!downloadDir.isEmpty()) {
QDir d(downloadDir);
@@ -480,7 +447,7 @@ void LauncherMainWindow::onRun()
qApp->exit(1);
}
-std::string LauncherMainWindow::selectStateAutomatically()
+QString LauncherMainWindow::selectAircraftStateAutomatically()
{
if (m_ui->location->isAirborneLocation()) {
return "approach";
@@ -517,13 +484,7 @@ void LauncherMainWindow::onApply()
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
}
- // manage aircraft history
- if (m_recentAircraft.contains(m_selectedAircraft))
- m_recentAircraft.removeOne(m_selectedAircraft);
- m_recentAircraft.prepend(m_selectedAircraft);
- if (m_recentAircraft.size() > MAX_RECENT_AIRCRAFT)
- m_recentAircraft.pop_back();
-
+ m_aircraftHistory->insert(m_selectedAircraft);
globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue);
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
}
@@ -536,23 +497,6 @@ void LauncherMainWindow::onApply()
m_runInApp = false;
}
-void LauncherMainWindow::updateLocationHistory()
-{
- QVariant locSet = m_ui->location->saveLocation();
-
- // check for existing; let's use description to imply uniqueness. This means
- // 'A1111' parkings get merged but I prefer that to keep the menu usable
- QVariant locDesc = locSet.toMap().value("text");
- auto it = std::remove_if(m_recentLocations.begin(), m_recentLocations.end(),
- [locDesc](QVariant v) { return v.toMap().value("text") == locDesc; });
- m_recentLocations.erase(it, m_recentLocations.end());
-
- // now we can always prepend
- m_recentLocations.prepend(locSet);
- if (m_recentLocations.size() > MAX_RECENT_AIRCRAFT)
- m_recentLocations.pop_back();
-}
-
void LauncherMainWindow::onQuit()
{
if (m_inAppMode) {
@@ -628,6 +572,7 @@ void LauncherMainWindow::updateSelectedAircraft()
m_selectedAircraftInfo->setUri(m_selectedAircraft);
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (index.isValid()) {
+#if 0
QPixmap pm = index.data(Qt::DecorationRole).value();
m_ui->thumbnail->setPixmap(pm);
@@ -640,7 +585,7 @@ void LauncherMainWindow::updateSelectedAircraft()
QVariant longDesc = index.data(AircraftLongDescriptionRole);
m_ui->aircraftDescription->setVisible(!longDesc.isNull());
m_ui->aircraftDescription->setText(longDesc.toString());
-
+#endif
int status = index.data(AircraftPackageStatusRole).toInt();
bool canRun = (status == LocalAircraftCache::PackageInstalled);
m_ui->flyButton->setEnabled(canRun);
@@ -655,6 +600,7 @@ void LauncherMainWindow::updateSelectedAircraft()
m_ui->location->setAircraftType(aircraftType);
const bool hasStates = m_selectedAircraftInfo->hasStates();
+#if 0
m_ui->stateCombo->setVisible(hasStates);
m_ui->stateLabel->setVisible(hasStates);
m_ui->stateDescription->setVisible(false);
@@ -664,13 +610,16 @@ void LauncherMainWindow::updateSelectedAircraft()
// hiden when no description is present
m_ui->stateDescription->setVisible(!m_ui->stateDescription->text().isEmpty());
}
+#endif
} else {
+#if 0
m_ui->thumbnail->setPixmap(QPixmap());
m_ui->aircraftName->setText("");
m_ui->aircraftDescription->hide();
m_ui->stateCombo->hide();
m_ui->stateLabel->hide();
m_ui->stateDescription->hide();
+#endif
m_ui->flyButton->setEnabled(false);
}
}
@@ -690,58 +639,6 @@ void LauncherMainWindow::setSceneryPaths()
flightgear::launcherSetSceneryPaths();
}
-void LauncherMainWindow::onPopupAircraftHistory()
-{
- if (m_recentAircraft.isEmpty()) {
- return;
- }
-
- QMenu m;
- Q_FOREACH(QUrl uri, m_recentAircraft) {
- QString nm = m_aircraftModel->nameForAircraftURI(uri);
- if (nm.isEmpty()) {
- continue;
- }
-
- QAction* act = m.addAction(nm);
- act->setData(uri);
- }
-
- QPoint popupPos = m_ui->aircraftHistory->mapToGlobal(m_ui->aircraftHistory->rect().bottomLeft());
- QAction* triggered = m.exec(popupPos);
- if (triggered) {
- setSelectedAircraft(triggered->data().toUrl());
- }
-}
-
-void LauncherMainWindow::onPopupLocationHistory()
-{
- if (m_recentLocations.isEmpty()) {
- return;
- }
-
- QMenu m;
- Q_FOREACH(QVariant loc, m_recentLocations) {
- QString summary = loc.toMap().value("text").toString();
- QAction* act = m.addAction(summary);
- act->setData(loc);
- }
-
- QPoint popupPos = m_ui->locationHistory->mapToGlobal(m_ui->locationHistory->rect().bottomLeft());
- QAction* triggered = m.exec(popupPos);
- if (triggered) {
- m_ui->location->restoreLocation(triggered->data().toMap());
- }
-}
-
-void LauncherMainWindow::updateSettingsSummary()
-{
- const QStringList summary = m_settingsSummary + m_environmentSummary;
- QString s = summary.join(", ");
- s[0] = s[0].toUpper();
- m_ui->settingsDescription->setText(s);
-}
-
void LauncherMainWindow::onSubsytemIdleTimeout()
{
globals->get_subsystem_mgr()->update(0.0);
@@ -836,6 +733,11 @@ QmlAircraftInfo *LauncherMainWindow::selectedAircraftInfo() const
return m_selectedAircraftInfo;
}
+void LauncherMainWindow::restoreLocation(QVariant var)
+{
+ m_ui->location->restoreLocation(var.toMap());
+}
+
bool LauncherMainWindow::matchesSearch(QString term, QStringList keywords) const
{
Q_FOREACH(QString s, keywords) {
@@ -888,7 +790,6 @@ void LauncherMainWindow::setSettingsSummary(QStringList settingsSummary)
m_settingsSummary = settingsSummary;
emit summaryChanged();
- updateSettingsSummary();
}
void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary)
@@ -898,7 +799,16 @@ void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary)
m_environmentSummary = environmentSummary;
emit summaryChanged();
- updateSettingsSummary();
+}
+
+QStringList LauncherMainWindow::combinedSummary() const
+{
+ return m_settingsSummary + m_environmentSummary;
+}
+
+QString LauncherMainWindow::locationDescription() const
+{
+ return m_ui->location->locationDescription();
}
simgear::pkg::PackageRef LauncherMainWindow::packageForAircraftURI(QUrl uri) const
@@ -1040,3 +950,34 @@ bool LauncherMainWindow::showNoOfficialHanger() const
return shouldShowOfficialCatalogMessage();
}
+QString LauncherMainWindow::versionString() const
+{
+ return FLIGHTGEAR_VERSION;
+}
+
+RecentAircraftModel *LauncherMainWindow::aircraftHistory()
+{
+ return m_aircraftHistory;
+}
+
+RecentLocationsModel *LauncherMainWindow::locationHistory()
+{
+ return m_locationHistory;
+}
+
+void LauncherMainWindow::launchUrl(QUrl url)
+{
+ QDesktopServices::openUrl(url);
+}
+
+QVariantList LauncherMainWindow::defaultSplashUrls() const
+{
+ QVariantList urls;
+
+ for (auto path : flightgear::defaultSplashScreenPaths()) {
+ QUrl url = QUrl::fromLocalFile(QString::fromStdString(path));
+ urls.append(url);
+ }
+
+ return urls;
+}
diff --git a/src/GUI/LauncherMainWindow.hxx b/src/GUI/LauncherMainWindow.hxx
index 5ab9bd5a4..a12bdb7b1 100644
--- a/src/GUI/LauncherMainWindow.hxx
+++ b/src/GUI/LauncherMainWindow.hxx
@@ -48,6 +48,9 @@ class ViewCommandLinePage;
class MPServersModel;
class QQuickItem;
class QmlAircraftInfo;
+class QStandardItemModel;
+class RecentAircraftModel;
+class RecentLocationsModel;
class LauncherMainWindow : public QMainWindow
{
@@ -72,6 +75,14 @@ class LauncherMainWindow : public QMainWindow
Q_PROPERTY(QStringList settingsSummary READ settingsSummary WRITE setSettingsSummary NOTIFY summaryChanged)
Q_PROPERTY(QStringList environmentSummary READ environmentSummary WRITE setEnvironmentSummary NOTIFY summaryChanged)
+ Q_PROPERTY(QString locationDescription READ locationDescription NOTIFY summaryChanged)
+ Q_PROPERTY(QStringList combinedSummary READ combinedSummary NOTIFY summaryChanged)
+
+ Q_PROPERTY(QString versionString READ versionString CONSTANT)
+
+ Q_PROPERTY(RecentAircraftModel* aircraftHistory READ aircraftHistory CONSTANT)
+ Q_PROPERTY(RecentLocationsModel* locationHistory READ locationHistory CONSTANT)
+
public:
LauncherMainWindow();
virtual ~LauncherMainWindow();
@@ -105,6 +116,8 @@ public:
QmlAircraftInfo* selectedAircraftInfo() const;
+ Q_INVOKABLE void restoreLocation(QVariant var);
+
Q_INVOKABLE bool matchesSearch(QString term, QStringList keywords) const;
bool isSearchActive() const;
@@ -118,6 +131,25 @@ public:
QStringList environmentSummary() const;
+ QStringList combinedSummary() const;
+
+ QString locationDescription() const;
+
+ QString versionString() const;
+
+ RecentAircraftModel* aircraftHistory();
+
+ RecentLocationsModel* locationHistory();
+
+ Q_INVOKABLE void launchUrl(QUrl url);
+
+ // list of QUrls containing the default splash images from FGData.
+ // used on the summary screen
+ Q_INVOKABLE QVariantList defaultSplashUrls() const;
+
+
+ Q_INVOKABLE QString selectAircraftStateAutomatically();
+
public slots:
void setSelectedAircraft(QUrl selectedAircraft);
@@ -155,11 +187,6 @@ private slots:
void onQuit();
- void onPopupAircraftHistory();
- void onPopupLocationHistory();
-
- void updateSettingsSummary();
-
void onSubsytemIdleTimeout();
void onAircraftInstalledCompleted(QModelIndex index);
@@ -192,13 +219,11 @@ private:
// scrolling, to give the view time it seems.
void delayedAircraftModelReset();
- void updateLocationHistory();
bool shouldShowOfficialCatalogMessage() const;
void collectAircraftArgs();
void initQML();
- std::string selectStateAutomatically();
QScopedPointer m_ui;
AircraftProxyModel* m_installedAircraftModel;
@@ -208,21 +233,20 @@ private:
MPServersModel* m_serversModel = nullptr;
QUrl m_selectedAircraft;
- QList m_recentAircraft;
QTimer* m_subsystemIdleTimer;
bool m_inAppMode = false;
bool m_runInApp = false;
bool m_accepted = false;
int m_ratingFilters[4] = {3, 3, 3, 3};
- QVariantList m_recentLocations;
QQmlEngine* m_qmlEngine = nullptr;
LaunchConfig* m_config = nullptr;
- ExtraSettingsSection* m_extraSettings = nullptr;
ViewCommandLinePage* m_viewCommandLinePage = nullptr;
QmlAircraftInfo* m_selectedAircraftInfo = nullptr;
QString m_settingsSearchTerm;
QStringList m_settingsSummary,
- m_environmentSummary;
+ m_environmentSummary;
+ RecentAircraftModel* m_aircraftHistory = nullptr;
+ RecentLocationsModel* m_locationHistory = nullptr;
};
#endif // of LAUNCHER_MAIN_WINDOW_HXX
diff --git a/src/GUI/QmlAircraftInfo.cxx b/src/GUI/QmlAircraftInfo.cxx
index c449204e0..2c4b1ba2e 100644
--- a/src/GUI/QmlAircraftInfo.cxx
+++ b/src/GUI/QmlAircraftInfo.cxx
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
#include
@@ -86,9 +87,12 @@ private:
struct StateInfo
{
- std::string tag; // internal XML name
+ string_list tags;
QString name; // human-readable name, or blank if we auto-generate this
QString description; // human-readable description
+
+ std::string primaryTag() const
+ { return tags.front(); }
};
using AircraftStateVec = std::vector;
@@ -111,12 +115,20 @@ static AircraftStateVec readAircraftStates(const SGPath& setXMLPath)
AircraftStateVec result;
result.reserve(nodes.size());
for (auto cn : nodes) {
- result.push_back({cn->getStringValue("name"),
+ string_list stateNames;
+ for (auto nameNode : cn->getChildren("name")) {
+ stateNames.push_back(nameNode->getStringValue());
+ }
+
+ if (stateNames.empty()) {
+ qWarning() << "state with no names defined, skipping";
+ continue;
+ }
+
+ result.push_back({stateNames,
QString::fromStdString(cn->getStringValue("readable-name")),
QString::fromStdString(cn->getStringValue("description"))
});
-
- qInfo() << QString::fromStdString(result.back().tag) << result.back().description;
}
return result;
@@ -141,35 +153,34 @@ QString humanNameFromStateTag(const std::string& tag)
class StatesModel : public QAbstractListModel
{
Q_OBJECT
+
+ Q_PROPERTY(bool hasExplicitAuto READ hasExplicitAuto CONSTANT)
public:
StatesModel(const AircraftStateVec& states) :
_data(states)
{
// sort which places 'auto' item at the front if it exists
std::sort(_data.begin(), _data.end(), [](const StateInfo& a, const StateInfo& b) {
- if (a.tag == "auto") return true;
- if (b.tag == "auto") return false;
- return a.tag < b.tag;
+ if (a.primaryTag() == "auto") return true;
+ if (b.primaryTag() == "auto") return false;
+ return a.primaryTag() < b.primaryTag();
});
- if (_data.front().tag == "auto") {
+ if (_data.front().primaryTag() == "auto") {
// track if the aircraft supplied an 'auto' state, in which case
// we will not run our own selection logic
_explicitAutoState = true;
} else {
// disabling this code for 2018.1, since it needs more testing
-#if 0
- _data.insert(_data.begin(), {"auto", {}, tr("Select state based on startup position.")});
-#else
- _data.insert(_data.begin(), {"__default__", tr("Parked"), tr("Default state for the aircraft (usually cold and dark)")});
-#endif
+ _data.insert(_data.begin(), {{"auto"}, {}, tr("Select state based on startup position.")});
}
}
int indexForTag(const std::string &tag) const
{
auto it = std::find_if(_data.begin(), _data.end(), [tag](const StateInfo& i) {
- return i.tag == tag;
+ auto tagIt = std::find(i.tags.begin(), i.tags.end(), tag);
+ return (tagIt != i.tags.end());
});
if (it == _data.end())
@@ -186,18 +197,17 @@ public:
QVariant data(const QModelIndex &index, int role) const override
{
const StateInfo& s = _data.at(index.row());
- // qInfo() << index.row() << s.name << QString::fromStdString(s.tag);
if (role == Qt::DisplayRole) {
if (s.name.isEmpty()) {
- return humanNameFromStateTag(s.tag);
+ return humanNameFromStateTag(s.primaryTag());
}
return s.name;
} else if (role == QmlAircraftInfo::StateTagRole) {
- return QString::fromStdString(s.tag);
+ return QString::fromStdString(s.primaryTag());
} else if (role == QmlAircraftInfo::StateDescriptionRole) {
return s.description;
} else if (role == QmlAircraftInfo::StateExplicitRole) {
- if (s.tag == "auto")
+ if (s.primaryTag() == "auto")
return _explicitAutoState;
return true;
}
@@ -213,6 +223,28 @@ public:
result[QmlAircraftInfo::StateDescriptionRole] = "description";
return result;
}
+
+ Q_INVOKABLE QString descriptionForState(int row) const
+ {
+ if ((row < 0) || (row >= _data.size()))
+ return {};
+
+ const StateInfo& s = _data.at(row);
+ return s.description;
+ }
+
+ Q_INVOKABLE QString tagForState(int row) const
+ {
+ if ((row < 0) || (row >= _data.size()))
+ return {};
+
+ return QString::fromStdString(_data.at(row).primaryTag());
+ }
+
+ bool hasExplicitAuto() const
+ {
+ return _explicitAutoState;
+ }
private:
AircraftStateVec _data;
bool _explicitAutoState = false;
@@ -224,6 +256,7 @@ QmlAircraftInfo::QmlAircraftInfo(QObject *parent)
: QObject(parent)
, _delegate(new Delegate(this))
{
+ qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "StatesModel", "no");
}
QmlAircraftInfo::~QmlAircraftInfo()
@@ -621,7 +654,7 @@ bool QmlAircraftInfo::isPackaged() const
return _package != PackageRef();
}
-QAbstractListModel *QmlAircraftInfo::statesModel()
+StatesModel *QmlAircraftInfo::statesModel()
{
if (!hasStates())
return nullptr;
diff --git a/src/GUI/QmlAircraftInfo.hxx b/src/GUI/QmlAircraftInfo.hxx
index e767cc144..8a1755240 100644
--- a/src/GUI/QmlAircraftInfo.hxx
+++ b/src/GUI/QmlAircraftInfo.hxx
@@ -14,6 +14,8 @@
struct AircraftItem;
typedef QSharedPointer AircraftItemPtr;
+class StatesModel;
+
class QmlAircraftInfo : public QObject
{
Q_OBJECT
@@ -57,7 +59,7 @@ class QmlAircraftInfo : public QObject
Q_PROPERTY(bool hasStates READ hasStates NOTIFY infoChanged)
- Q_PROPERTY(QAbstractListModel* statesModel READ statesModel NOTIFY infoChanged)
+ Q_PROPERTY(StatesModel* statesModel READ statesModel NOTIFY infoChanged)
public:
explicit QmlAircraftInfo(QObject *parent = nullptr);
@@ -110,7 +112,7 @@ public:
static const int StateDescriptionRole;
static const int StateExplicitRole;
- QAbstractListModel* statesModel();
+ StatesModel* statesModel();
signals:
void uriChanged();
void infoChanged();
@@ -134,7 +136,7 @@ private:
AircraftItemPtr _item;
int _variant = 0;
int _downloadBytes = 0;
- QScopedPointer _statesModel;
+ QScopedPointer _statesModel;
};
#endif // QMLAIRCRAFTINFO_HXX
diff --git a/src/GUI/RecentAircraftModel.cxx b/src/GUI/RecentAircraftModel.cxx
new file mode 100644
index 000000000..ff5fd9ec5
--- /dev/null
+++ b/src/GUI/RecentAircraftModel.cxx
@@ -0,0 +1,92 @@
+#include "RecentAircraftModel.hxx"
+
+#include
+#include
+
+#include "AircraftModel.hxx"
+
+const int MAX_RECENT_AIRCRAFT = 20;
+
+
+RecentAircraftModel::RecentAircraftModel(AircraftItemModel* acModel, QObject* pr) :
+ QAbstractListModel(pr),
+ m_aircraftModel(acModel)
+{
+ QSettings settings;
+ const QStringList urls = settings.value("recent-aircraft").toStringList();
+ m_data = QUrl::fromStringList(urls);
+}
+
+void RecentAircraftModel::saveToSettings()
+{
+ QSettings settings;
+ settings.setValue("recent-aircraft", QUrl::toStringList(m_data));
+}
+
+QUrl RecentAircraftModel::uriAt(int index) const
+{
+ return m_data.at(index);
+}
+
+QVariant RecentAircraftModel::data(const QModelIndex &index, int role) const
+{
+ const QUrl uri = m_data.at(index.row());
+ if (role == Qt::DisplayRole) {
+ return m_aircraftModel->nameForAircraftURI(uri);
+ } else if (role == Qt::UserRole) {
+ return uri;
+ }
+
+ return {};
+}
+
+int RecentAircraftModel::rowCount(const QModelIndex &parent) const
+{
+ return m_data.size();
+}
+
+QHash RecentAircraftModel::roleNames() const
+{
+ QHash result = QAbstractListModel::roleNames();
+ result[Qt::DisplayRole] = "display";
+ result[Qt::UserRole] = "uri";
+ return result;
+}
+
+QUrl RecentAircraftModel::mostRecent() const
+{
+ if (m_data.empty()) {
+ return {};
+ }
+
+ return m_data.front();
+}
+
+void RecentAircraftModel::insert(QUrl aircraftUrl)
+{
+ if (aircraftUrl.isEmpty())
+ return;
+
+ int existingIndex = m_data.indexOf(aircraftUrl);
+ if (existingIndex == 0) {
+ // special, common case - nothing to do
+ return;
+ }
+
+ if (existingIndex >= 0) {
+ beginRemoveRows(QModelIndex(), existingIndex, existingIndex);
+ m_data.removeAt(existingIndex);
+ endRemoveRows();
+ }
+
+ beginInsertRows(QModelIndex(), 0, 0);
+ m_data.push_front(aircraftUrl);
+ endInsertRows();
+
+ if (m_data.size() > MAX_RECENT_AIRCRAFT) {
+ beginRemoveRows(QModelIndex(), MAX_RECENT_AIRCRAFT, m_data.size() - 1);
+ // truncate the data at the correct size
+ m_data = m_data.mid(0, MAX_RECENT_AIRCRAFT);
+ endRemoveRows();
+ }
+}
diff --git a/src/GUI/RecentAircraftModel.hxx b/src/GUI/RecentAircraftModel.hxx
new file mode 100644
index 000000000..24af11275
--- /dev/null
+++ b/src/GUI/RecentAircraftModel.hxx
@@ -0,0 +1,34 @@
+#ifndef RECENTAIRCRAFTMODEL_HXX
+#define RECENTAIRCRAFTMODEL_HXX
+
+#include
+#include
+
+// forward decls
+class AircraftItemModel;
+
+class RecentAircraftModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ RecentAircraftModel(AircraftItemModel *acModel, QObject* pr = nullptr);
+
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ int rowCount(const QModelIndex &parent) const override;
+
+ QHash roleNames() const override;
+
+ QUrl mostRecent() const;
+
+ void insert(QUrl aircraftUrl);
+
+ void saveToSettings();
+
+ Q_INVOKABLE QUrl uriAt(int index) const;
+private:
+ AircraftItemModel* m_aircraftModel;
+ QList m_data;
+};
+
+#endif // RECENTAIRCRAFTMODEL_HXX
diff --git a/src/GUI/RecentLocationsModel.cxx b/src/GUI/RecentLocationsModel.cxx
new file mode 100644
index 000000000..517e602c6
--- /dev/null
+++ b/src/GUI/RecentLocationsModel.cxx
@@ -0,0 +1,91 @@
+#include "RecentLocationsModel.hxx"
+
+#include
+#include
+
+
+const int MAX_RECENT_LOCATIONS = 20;
+
+RecentLocationsModel::RecentLocationsModel(QObject* pr) :
+ QAbstractListModel(pr)
+{
+ QSettings settings;
+ m_data = settings.value("recent-location-sets").toList();
+}
+
+void RecentLocationsModel::saveToSettings()
+{
+ QSettings settings;
+ settings.setValue("recent-location-sets", m_data);
+}
+
+QVariantMap RecentLocationsModel::locationAt(int index) const
+{
+ return m_data.at(index).toMap();
+}
+
+QVariant RecentLocationsModel::data(const QModelIndex &index, int role) const
+{
+ const QVariantMap loc = m_data.at(index.row()).toMap();
+ if (role == Qt::DisplayRole) {
+ return loc.value("text");
+ } else if (role == Qt::UserRole) {
+ return loc;
+ }
+
+ return {};
+}
+
+int RecentLocationsModel::rowCount(const QModelIndex &parent) const
+{
+ return m_data.size();
+}
+
+QHash RecentLocationsModel::roleNames() const
+{
+ QHash result = QAbstractListModel::roleNames();
+ result[Qt::DisplayRole] = "display";
+ // result[Qt::UserRole] = "uri";
+ return result;
+}
+
+QVariantMap RecentLocationsModel::mostRecent() const
+{
+ if (m_data.empty()) {
+ return {};
+ }
+
+ return m_data.front().toMap();
+}
+
+void RecentLocationsModel::insert(QVariant location)
+{
+ if (location.toMap().isEmpty())
+ return;
+
+ QVariant locDesc = location.toMap().value("text");
+ auto it = std::find_if(m_data.begin(), m_data.end(),
+ [locDesc](QVariant v) { return v.toMap().value("text") == locDesc; });
+ if (it == m_data.begin()) {
+ // special, common case - nothing to do
+ return;
+ }
+
+ if (it != m_data.end()) {
+ int existingIndex = std::distance(m_data.begin(), it);
+ beginRemoveRows(QModelIndex(), existingIndex, existingIndex);
+ m_data.erase(it);
+ endRemoveRows();
+ }
+
+ beginInsertRows(QModelIndex(), 0, 0);
+ m_data.push_front(location);
+ endInsertRows();
+
+ if (m_data.size() > MAX_RECENT_LOCATIONS) {
+ beginRemoveRows(QModelIndex(), MAX_RECENT_LOCATIONS, m_data.size() - 1);
+ // truncate the data at the correct size
+ m_data = m_data.mid(0, MAX_RECENT_LOCATIONS);
+ endRemoveRows();
+ }
+}
diff --git a/src/GUI/RecentLocationsModel.hxx b/src/GUI/RecentLocationsModel.hxx
new file mode 100644
index 000000000..552bbc6b9
--- /dev/null
+++ b/src/GUI/RecentLocationsModel.hxx
@@ -0,0 +1,31 @@
+#ifndef RECENTLOCATIONSMODEL_HXX
+#define RECENTLOCATIONSMODEL_HXX
+
+#include
+#include
+
+
+class RecentLocationsModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ RecentLocationsModel(QObject* pr = nullptr);
+
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ int rowCount(const QModelIndex &parent) const override;
+
+ QHash roleNames() const override;
+
+ QVariantMap mostRecent() const;
+
+ void insert(QVariant location);
+
+ void saveToSettings();
+
+ Q_INVOKABLE QVariantMap locationAt(int index) const;
+private:
+ QVariantList m_data;
+};
+
+#endif // RECENTLOCATIONSMODEL_HXX
diff --git a/src/GUI/qml/ClickableText.qml b/src/GUI/qml/ClickableText.qml
index a203247b3..c817c3f54 100644
--- a/src/GUI/qml/ClickableText.qml
+++ b/src/GUI/qml/ClickableText.qml
@@ -4,7 +4,8 @@ import "."
Text {
signal clicked();
- color: mouse.containsMouse ? Style.themeColor : Style.baseTextColor
+ property color baseTextColor: Style.baseTextColor
+ color: mouse.containsMouse ? Style.themeColor : baseTextColor
MouseArea {
id: mouse
@@ -12,5 +13,7 @@ Text {
anchors.fill: parent
onClicked: parent.clicked();
+
+ cursorShape: Qt.PointingHandCursor
}
}
diff --git a/src/GUI/qml/HistoryPopup.qml b/src/GUI/qml/HistoryPopup.qml
new file mode 100644
index 000000000..97e62b247
--- /dev/null
+++ b/src/GUI/qml/HistoryPopup.qml
@@ -0,0 +1,100 @@
+import QtQuick 2.0
+import QtQuick.Window 2.0
+import FlightGear.Launcher 1.0
+
+import "."
+
+Item {
+ id: root
+
+ property var model: undefined
+ property string displayRole: "display"
+ property bool enabled: true
+
+ implicitHeight: button.height
+ implicitWidth: button.width
+
+ signal selected(var index);
+
+ Rectangle {
+ id: button
+
+ width: icon.width
+ height: icon.height
+ radius: Style.roundRadius
+
+ color: enabled ? (mouse.containsMouse ? Style.activeColor : Style.themeColor) : Style.disabledThemeColor
+
+ Image {
+ id: icon
+ source: "qrc:///history-icon"
+ anchors.centerIn: parent
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ id: mouse
+ hoverEnabled: true
+ enabled: root.enabled
+ onClicked: {
+ var screenPos = _launcher.mapToGlobal(button, Qt.point(-popupFrame.width, 0))
+ popupFrame.x = screenPos.x;
+ popupFrame.y = screenPos.y;
+ popupFrame.visible = true
+ tracker.window = popupFrame
+ }
+ }
+ }
+
+ PopupWindowTracker {
+ id: tracker
+ }
+
+ Window {
+ id: popupFrame
+
+ flags: Qt.Popup
+ height: choicesColumn.childrenRect.height + Style.margin * 2
+ width: choicesColumn.childrenRect.width + Style.margin * 2
+ visible: false
+ color: "white"
+
+ Rectangle {
+ border.width: 1
+ border.color: Style.minorFrameColor
+ anchors.fill: parent
+ }
+
+ // text repeater
+ Column {
+ id: choicesColumn
+ spacing: Style.margin
+ x: Style.margin
+ y: Style.margin
+
+ Repeater {
+ id: choicesRepeater
+ model: root.model
+ delegate:
+ Text {
+ id: choiceText
+
+ // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions
+ text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel
+ : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject
+ : modelData != undefined ? modelData : "" // Models without role
+ height: implicitHeight + Style.margin
+
+ MouseArea {
+ width: popupFrame.width // full width of the popup
+ height: parent.height
+ onClicked: {
+ popupFrame.visible = false
+ root.selected(model.index);
+ }
+ }
+ } // of Text delegate
+ } // text repeater
+ } // text column
+ } // of popup Window
+}
diff --git a/src/GUI/qml/PopupChoice.qml b/src/GUI/qml/PopupChoice.qml
index b77d78170..b94031a12 100644
--- a/src/GUI/qml/PopupChoice.qml
+++ b/src/GUI/qml/PopupChoice.qml
@@ -14,7 +14,7 @@ Item {
property int currentIndex: 0
property bool __dummy: false
- implicitHeight: label.implicitHeight
+ implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
Item {
Repeater {
@@ -40,6 +40,10 @@ Item {
__dummy = !__dummy
}
+ onModelChanged: {
+ __dummy = !__dummy // force update of currentText
+ }
+
function currentText()
{
var foo = __dummy; // fake propery dependency to update this
diff --git a/src/GUI/qml/Style.qml b/src/GUI/qml/Style.qml
index 782069b9a..5869e0419 100644
--- a/src/GUI/qml/Style.qml
+++ b/src/GUI/qml/Style.qml
@@ -21,6 +21,10 @@ QtObject
readonly property string baseTextColor: "#3f3f3f"
readonly property int baseFontPixelSize: 12
+ readonly property int headingFontPixelSize: 18
+
readonly property string disabledTextColor: "#5f5f5f"
+
+ readonly property double panelOpacity: 0.8
}
diff --git a/src/GUI/qml/Summary.qml b/src/GUI/qml/Summary.qml
new file mode 100644
index 000000000..e22d00072
--- /dev/null
+++ b/src/GUI/qml/Summary.qml
@@ -0,0 +1,321 @@
+import QtQuick 2.0
+import FlightGear.Launcher 1.0
+import "."
+
+Item {
+ id: root
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#7f7f7f"
+ }
+
+ property string __aircraftDescription
+
+ // conditional bindings on aircraft description
+ Binding {
+ when: _launcher.selectedAircraftInfo != undefined
+ target: root
+ property: "__aircraftDescription"
+ value: _launcher.selectedAircraftInfo.description
+ }
+
+ Binding {
+ when: _launcher.selectedAircraftInfo == undefined
+ target: root
+ property: "__aircraftDescription"
+ value: ""
+ }
+
+ // base image when preview not available
+ Rectangle {
+ anchors.fill: parent
+ color: "magenta"
+ }
+
+ PreviewImage {
+ id: preview
+ anchors.centerIn: parent
+
+ // over-zoom the preview to fill the entire space available
+ readonly property double scale: Math.max(root.width / sourceSize.width,
+ root.height / sourceSize.height)
+
+ width: height * aspectRatio
+ height: scale * sourceSize.height
+
+ property var urlsList: []
+ property int __currentUrl: 0
+
+ onUrlsListChanged: {
+ __currentUrl = 0;
+ }
+
+ Timer {
+ running: true
+ interval: 8000
+ repeat: true
+ onTriggered: {
+ var len = preview.urlsList.length
+ preview.__currentUrl = (preview.__currentUrl + 1) % len
+ }
+ }
+
+ visible: imageUrl != ""
+ imageUrl: urlsList[__currentUrl]
+
+// conditional binding when we have valid previews
+ Binding {
+ when: _launcher.selectedAircraftInfo != undefined &&
+ (_launcher.selectedAircraftInfo.previews.length > 0)
+ target: preview
+ property: "urlsList"
+ value: _launcher.selectedAircraftInfo.previews
+ }
+
+ Binding {
+ when: _launcher.selectedAircraftInfo == undefined ||
+ _launcher.selectedAircraftInfo.previews.length == 0
+ target: preview
+ property: "urlsList"
+ value: _launcher.defaultSplashUrls()
+ }
+ }
+
+ Text {
+ id: logoText
+ font.pointSize: Style.strutSize * 2
+ font.italic: true
+ font.bold: true
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ margins: Style.strutSize
+ }
+ fontSizeMode: Text.Fit
+ text: "FlightGear " + _launcher.versionString
+ color: "white"
+ style: Text.Outline
+ styleColor: "black"
+ }
+
+ ClickableText {
+ anchors {
+ left: logoText.left
+ right: logoText.right
+ }
+
+ // anchoring to logoText bottom doesn't work as expected because of
+ // dynamic text sizing, so bind it manually
+ y: logoText.y + Style.margin + logoText.contentHeight
+ wrapMode: Text.WordWrap
+ text: "Licenced under the GNU Public License (GPL) version 2.0 - click for more info"
+ baseTextColor: "white"
+ style: Text.Outline
+ styleColor: "black"
+
+ onClicked: {
+ _launcher.launchUrl("http://flightgear.org/license.html");
+ }
+ }
+
+ Rectangle {
+ id: summaryPanel
+
+ color: "white"
+ opacity: Style.panelOpacity
+ // radius: Style.roundRadius
+ border.width: 1
+ border.color: Style.frameColor
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ margins: Style.strutSize
+ }
+
+ height: summaryGrid.height + Style.margin * 2
+
+ Grid {
+ id: summaryGrid
+ columns: 3
+ columnSpacing: Style.margin
+ rowSpacing: Style.margin
+
+ readonly property int middleColumnWidth: summaryGrid.width - (locationLabel.width + Style.margin * 2 + aircraftHistoryPopup.width)
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ margins: Style.margin
+ }
+
+ // aircraft name row
+ Text {
+ text: qsTr("Aircraft:")
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: Style.headingFontPixelSize
+ }
+
+ // TODO - make clickable, jump to to the aircraft in the installed
+ // aircraft list
+ Text {
+ text: _launcher.selectedAircraftInfo.name
+ font.pixelSize: Style.headingFontPixelSize
+ }
+
+ HistoryPopup {
+ id: aircraftHistoryPopup
+ model: _launcher.aircraftHistory
+ onSelected: {
+ _launcher.selectedAircraft = _launcher.aircraftHistory.uriAt(index)
+ }
+ }
+
+ // empty space in next row (thumbnail, long aircraft description)
+ Item {
+ width: 1; height: 1
+ }
+
+ Item {
+ id: aircraftDetailsRow
+ width: summaryGrid.middleColumnWidth
+ height: Math.max(thumbnail.height, aircraftDescriptionText.height)
+
+ ThumbnailImage {
+ id: thumbnail
+ aircraftUri: _launcher.selectedAircraft
+ maximumSize.width: 172
+ maximumSize.height: 128
+ }
+
+ Text {
+ id: aircraftDescriptionText
+ anchors {
+ left: thumbnail.right
+ leftMargin: Style.margin
+ right: parent.right
+ }
+
+ text: root.__aircraftDescription
+ visible: root.__aircraftDescription != ""
+ wrapMode: Text.WordWrap
+ maximumLineCount: 5
+ elide: Text.ElideRight
+ }
+ }
+
+
+
+ Item {
+ width: 1; height: 1
+ }
+
+ // aircraft state row, if enabled
+
+ Item {
+ width: 1; height: 1
+ visible: stateSelectionGroup.visible
+ }
+
+ Column {
+ id: stateSelectionGroup
+ visible: _launcher.selectedAircraftInfo.hasStates
+ width: summaryGrid.middleColumnWidth
+ spacing: Style.margin
+
+ PopupChoice {
+ id: stateSelectionCombo
+ model: _launcher.selectedAircraftInfo.statesModel
+ displayRole: "name"
+ label: qsTr("State:")
+ width: parent.width
+ }
+
+ Text {
+ id: stateDescriptionText
+ wrapMode: Text.WordWrap
+ maximumLineCount: 5
+ elide: Text.ElideRight
+ width: parent.width
+
+ Binding {
+ when: _launcher.selectedAircraftInfo.statesModel != null
+ target: stateDescriptionText
+ property: "text"
+ value: _launcher.selectedAircraftInfo.statesModel.descriptionForState(stateSelectionCombo.currentIndex)
+ }
+ }
+
+ Connections {
+ target: _config
+ onCollect: {
+ if (!_launcher.selectedAircraftInfo.hasStates)
+ return;
+
+ var state = _launcher.selectedAircraftInfo.statesModel.tagForState(stateSelectionCombo.currentIndex);
+ if (state === "auto" && !_launcher.selectedAircraftInfo.statesModel.hasExplicitAuto) {
+ // auto state selection if not handled by aircraft
+ state = _launcher.selectAircraftStateAutomatically();
+ console.info("launcher auto state selection, picked:" + state)
+ }
+
+ if (state !== "__default__") { // don't set arg in default case
+ _config.setArg("state", state);
+ }
+ }
+ } // of connections
+ }
+
+ Item {
+ width: 1; height: 1
+ visible: stateSelectionGroup.visible
+ }
+
+ // location summary row
+ Text {
+ id: locationLabel
+ text: qsTr("Location:")
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: Style.headingFontPixelSize
+ }
+
+ // TODO - make clickable, jump to the location page
+ Text {
+ text: _launcher.locationDescription
+ font.pixelSize: Style.headingFontPixelSize
+ }
+
+ HistoryPopup {
+ id: locationHistoryPopup
+ model: _launcher.locationHistory
+ onSelected: {
+ _launcher.restoreLocation(_launcher.locationHistory.locationAt(index))
+ }
+ }
+
+ // settings summary row
+ Text {
+ text: qsTr("Settings:")
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: Style.headingFontPixelSize
+ }
+
+ Text {
+ text: _launcher.combinedSummary.join(", ")
+ font.pixelSize: Style.headingFontPixelSize
+ wrapMode: Text.WordWrap
+ maximumLineCount: 2
+ elide: Text.ElideRight
+ width: summaryGrid.middleColumnWidth
+ }
+
+ Item {
+ width: 1; height: 1
+ }
+ }
+ }
+}
diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc
index 73c3e8883..cb6b8b12e 100644
--- a/src/GUI/resources.qrc
+++ b/src/GUI/resources.qrc
@@ -71,21 +71,12 @@
qml/DateTimeEdit.qml
qml/DateTimeValueEdit.qml
qml/SettingsDateTimePicker.qml
+ qml/Summary.qml
+ qml/HistoryPopup.qml
preview-close.png
preview-left-arrow.png
preview-right-arrow.png
-
- DownloadSettings.qml
- GeneralSettings.qml
- MPSettings.qml
- RenderSettings.qml
- ViewSettings.qml
-
-
- TimeSettings.qml
- Weather.qml
-