1
0
Fork 0

Summary page converted to QtQuick

This commit is contained in:
James Turner 2018-03-11 11:49:38 +00:00
parent eaf89ab23b
commit 19ae26164a
18 changed files with 939 additions and 448 deletions

View file

@ -140,6 +140,10 @@ if (HAVE_QT)
MPServersModel.h MPServersModel.h
PathUrlHelper.cxx PathUrlHelper.cxx
PathUrlHelper.hxx PathUrlHelper.hxx
RecentAircraftModel.cxx
RecentAircraftModel.hxx
RecentLocationsModel.cxx
RecentLocationsModel.hxx
${uic_sources} ${uic_sources}
${qrc_sources} ${qrc_sources}
${qml_sources}) ${qml_sources})

View file

@ -34,6 +34,20 @@ std::string defaultAirportICAO()
return airportCode; 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() DefaultAircraftLocator::DefaultAircraftLocator()
{ {
SGPropertyNode_ptr root = loadXMLDefaults(); SGPropertyNode_ptr root = loadXMLDefaults();

View file

@ -13,6 +13,8 @@ namespace flightgear
std::string defaultAirportICAO(); std::string defaultAirportICAO();
string_list defaultSplashScreenPaths();
/** /**
* we don't want to rely on the main AircraftModel threaded scan, to find the * 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 * default aircraft, so we do a synchronous scan here, on the assumption that

View file

@ -250,231 +250,31 @@
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="summaryPage">
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0,0,0,0,0,0,0" columnstretch="0,0,0,1,0"> <widget class="QWidget" name="summaryPage">
<item row="0" column="3" colspan="2"> <layout class="QVBoxLayout" name="verticalLayout_3">
<widget class="QLabel" name="appTitleLabel"> <property name="spacing">
<property name="font"> <number>4</number>
<font> </property>
<pointsize>48</pointsize> <property name="leftMargin">
<weight>75</weight> <number>4</number>
<bold>true</bold> </property>
</font> <property name="topMargin">
</property> <number>4</number>
<property name="text"> </property>
<string>FlightGear 2017.1.0</string> <property name="rightMargin">
</property> <number>4</number>
</widget> </property>
</item> <property name="bottomMargin">
<item row="3" column="4"> <number>4</number>
<widget class="QPushButton" name="aircraftHistory"> </property>
<property name="autoDefault"> <item>
<bool>false</bool> <widget class="QQuickWidget" name="summary"/>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="stateLabel">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>State:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="aircraftName">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>aircraft</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Settings:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Aircraft:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
</widget>
</item>
<item row="7" column="3">
<widget class="QLabel" name="locationDescription">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>location</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="3" colspan="2">
<widget class="QLabel" name="thumbnail">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>171</width>
<height>128</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="11" column="3" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>294</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="3" colspan="2">
<widget class="QLabel" name="aircraftDescription">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="3" colspan="2">
<widget class="QComboBox" name="stateCombo"/>
</item>
<item row="1" column="3" colspan="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;©2017 FlightGear contributors. Licensed under the GNU Public License. See &lt;a href=&quot;http://www.flightgear.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;here&lt;/span&gt;&lt;/a&gt; for more information&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>Location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<widget class="QLabel" name="logoIcon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources.qrc">:/app-icon-large</pixmap>
</property>
</widget>
</item>
<item row="7" column="4">
<widget class="QPushButton" name="locationHistory">
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="3" colspan="2">
<widget class="QLabel" name="settingsDescription">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text">
<string>settings</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="3" colspan="2">
<widget class="QLabel" name="stateDescription">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="aircraftPage"> <widget class="QWidget" name="aircraftPage">
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing"> <property name="spacing">

View file

@ -9,6 +9,9 @@
#include <QMenu> #include <QMenu>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkDiskCache> #include <QNetworkDiskCache>
#include <QPushButton>
#include <QStandardItemModel>
#include <QDesktopServices>
#include <QQuickItem> #include <QQuickItem>
#include <QQmlEngine> #include <QQmlEngine>
@ -49,11 +52,11 @@
#include "LauncherArgumentTokenizer.hxx" #include "LauncherArgumentTokenizer.hxx"
#include "PathUrlHelper.hxx" #include "PathUrlHelper.hxx"
#include "PopupWindowTracker.hxx" #include "PopupWindowTracker.hxx"
#include "RecentAircraftModel.hxx"
#include "RecentLocationsModel.hxx"
#include "ui_Launcher.h" #include "ui_Launcher.h"
const int MAX_RECENT_AIRCRAFT = 20;
using namespace simgear::pkg; using namespace simgear::pkg;
extern void restartTheApp(QStringList fgArgs); extern void restartTheApp(QStringList fgArgs);
@ -75,14 +78,11 @@ QQmlPrivate::AutoParentResult launcher_autoParent(QObject* thing, QObject* paren
LauncherMainWindow::LauncherMainWindow() : LauncherMainWindow::LauncherMainWindow() :
QMainWindow(), QMainWindow(),
m_ui(NULL),
m_subsystemIdleTimer(NULL) m_subsystemIdleTimer(NULL)
{ {
m_ui.reset(new Ui::Launcher); m_ui.reset(new Ui::Launcher);
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->appTitleLabel->setText(tr("FlightGear %1").arg(FLIGHTGEAR_VERSION));
QMenuBar* mb = menuBar(); QMenuBar* mb = menuBar();
#if !defined(Q_OS_MAC) #if !defined(Q_OS_MAC)
@ -108,15 +108,7 @@ LauncherMainWindow::LauncherMainWindow() :
m_serversModel = new MPServersModel(this); m_serversModel = new MPServersModel(this);
// keep the description QLabel in sync as the current item changes m_locationHistory = new RecentLocationsModel(this);
connect(m_ui->stateCombo,
static_cast<void (QComboBox::*)(int)>(&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_selectedAircraftInfo = new QmlAircraftInfo(this); m_selectedAircraftInfo = new QmlAircraftInfo(this);
initQML(); initQML();
@ -135,23 +127,14 @@ LauncherMainWindow::LauncherMainWindow() :
connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton); connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->addOnsButton, &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, connect(m_ui->location, &LocationWidget::descriptionChanged,
m_ui->locationDescription, &QLabel::setText); this, &LauncherMainWindow::summaryChanged);
QAction* qa = new QAction(this); QAction* qa = new QAction(this);
qa->setShortcut(QKeySequence("Ctrl+Q")); qa->setShortcut(QKeySequence("Ctrl+Q"));
connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit); connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit);
addAction(qa); addAction(qa);
QIcon historyIcon(":/history-icon");
m_ui->aircraftHistory->setIcon(historyIcon);
m_ui->locationHistory->setIcon(historyIcon);
m_aircraftModel = new AircraftItemModel(this); m_aircraftModel = new AircraftItemModel(this);
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel); m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_installedAircraftModel->setInstalledFilterEnabled(true); m_installedAircraftModel->setInstalledFilterEnabled(true);
@ -161,40 +144,13 @@ LauncherMainWindow::LauncherMainWindow() :
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel); m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView); m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
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"));
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted, connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
this, &LauncherMainWindow::onAircraftInstalledCompleted); this, &LauncherMainWindow::onAircraftInstalledCompleted);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed, connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
this, &LauncherMainWindow::onAircraftInstallFailed); this, &LauncherMainWindow::onAircraftInstallFailed);
connect(LocalAircraftCache::instance(), connect(LocalAircraftCache::instance(),
&LocalAircraftCache::scanCompleted, &LocalAircraftCache::scanCompleted,
this, &LauncherMainWindow::updateSelectedAircraft); this, &LauncherMainWindow::updateSelectedAircraft);
@ -219,8 +175,50 @@ LauncherMainWindow::LauncherMainWindow() :
m_ui->stack->addWidget(m_viewCommandLinePage); m_ui->stack->addWidget(m_viewCommandLinePage);
restoreSettings(); restoreSettings();
updateSettingsSummary();
emit summaryChanged();
emit showNoOfficialHangarChanged(); 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() void LauncherMainWindow::initQML()
@ -240,6 +238,9 @@ void LauncherMainWindow::initQML()
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no"); qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no"); qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
qmlRegisterUncreatableType<RecentAircraftModel>("FlightGear.Launcher", 1, 0, "RecentAircraftModel", "no");
qmlRegisterUncreatableType<RecentLocationsModel>("FlightGear.Launcher", 1, 0, "RecentLocationsModel", "no");
qmlRegisterUncreatableType<SettingsControl>("FlightGear.Launcher", 1, 0, "Control", "Base class"); qmlRegisterUncreatableType<SettingsControl>("FlightGear.Launcher", 1, 0, "Control", "Base class");
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API"); qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
qmlRegisterType<FileDialogWrapper>("FlightGear.Launcher", 1, 0, "FileDialog"); qmlRegisterType<FileDialogWrapper>("FlightGear.Launcher", 1, 0, "FileDialog");
@ -265,6 +266,9 @@ void LauncherMainWindow::initQML()
settingsContext->setContextProperty("_config", m_config); settingsContext->setContextProperty("_config", m_config);
settingsContext->setContextProperty("_osName", osName); settingsContext->setContextProperty("_osName", osName);
QQmlContext* summaryContext = m_ui->summary->engine()->rootContext();
summaryContext->setContextProperty("_config", m_config);
qmlRegisterUncreatableType<LocalAircraftCache>("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache"); qmlRegisterUncreatableType<LocalAircraftCache>("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache");
qmlRegisterUncreatableType<AircraftItemModel>("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model"); qmlRegisterUncreatableType<AircraftItemModel>("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model");
qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage"); qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage");
@ -311,12 +315,8 @@ void LauncherMainWindow::restoreSettings()
restoreGeometry(settings.value("window-geometry").toByteArray()); restoreGeometry(settings.value("window-geometry").toByteArray());
// full paths to -set.xml files m_selectedAircraft = m_aircraftHistory->mostRecent();
m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList()); if (m_selectedAircraft.isEmpty()) {
if (!m_recentAircraft.empty()) {
m_selectedAircraft = m_recentAircraft.front();
} else {
// select the default aircraft specified in defaults.xml // select the default aircraft specified in defaults.xml
flightgear::DefaultAircraftLocator da; flightgear::DefaultAircraftLocator da;
if (da.foundPath().exists()) { if (da.foundPath().exists()) {
@ -332,22 +332,16 @@ void LauncherMainWindow::restoreSettings()
} }
m_ui->location->restoreSettings(); m_ui->location->restoreSettings();
m_recentLocations = settings.value("recent-location-sets").toList(); QVariantMap currentLocation = m_locationHistory->mostRecent();
QVariantMap currentLocation; if (currentLocation.isEmpty()) {
if (m_recentLocations.isEmpty()) {
// use the default // use the default
std::string defaultAirport = flightgear::defaultAirportICAO(); std::string defaultAirport = flightgear::defaultAirportICAO();
FGAirportRef apt = FGAirport::findByIdent(defaultAirport); FGAirportRef apt = FGAirport::findByIdent(defaultAirport);
if (apt) { if (apt) {
currentLocation["location-id"] = static_cast<qlonglong>(apt->guid()); currentLocation["location-id"] = static_cast<qlonglong>(apt->guid());
currentLocation["location-apt-runway"] = "active"; currentLocation["location-apt-runway"] = "active";
qDebug() << "restored default airport:" << QString::fromStdString(defaultAirport);
} // otherwise we failed to find the default airport in the nav-db :( } // 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); m_ui->location->restoreLocation(currentLocation);
updateSelectedAircraft(); updateSelectedAircraft();
@ -363,9 +357,10 @@ void LauncherMainWindow::saveSettings()
{ {
emit requestSaveState(); emit requestSaveState();
m_aircraftHistory->saveToSettings();
m_locationHistory->saveToSettings();
QSettings settings; QSettings settings;
settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
settings.setValue("recent-location-sets", m_recentLocations);
settings.setValue("window-geometry", saveGeometry()); settings.setValue("window-geometry", saveGeometry());
Q_FOREACH(SettingsSection* ss, findChildren<SettingsSection*>()) { Q_FOREACH(SettingsSection* ss, findChildren<SettingsSection*>()) {
@ -416,41 +411,13 @@ void LauncherMainWindow::onRun()
m_config->reset(); m_config->reset();
m_config->collect(); m_config->collect();
// aircraft m_aircraftHistory->insert(m_selectedAircraft);
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();
}
if (m_ui->stateCombo->isVisible()) { QVariant locSet = m_ui->location->saveLocation();
// apply state setting m_locationHistory->insert(locSet);
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
// aircraft paths // aircraft paths
QSettings settings; QSettings settings;
updateLocationHistory();
QString downloadDir = settings.value("downloadSettings/downloadDir").toString(); QString downloadDir = settings.value("downloadSettings/downloadDir").toString();
if (!downloadDir.isEmpty()) { if (!downloadDir.isEmpty()) {
QDir d(downloadDir); QDir d(downloadDir);
@ -480,7 +447,7 @@ void LauncherMainWindow::onRun()
qApp->exit(1); qApp->exit(1);
} }
std::string LauncherMainWindow::selectStateAutomatically() QString LauncherMainWindow::selectAircraftStateAutomatically()
{ {
if (m_ui->location->isAirborneLocation()) { if (m_ui->location->isAirborneLocation()) {
return "approach"; return "approach";
@ -517,13 +484,7 @@ void LauncherMainWindow::onApply()
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft; qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
} }
// manage aircraft history m_aircraftHistory->insert(m_selectedAircraft);
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();
globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue); globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue);
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir); globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
} }
@ -536,23 +497,6 @@ void LauncherMainWindow::onApply()
m_runInApp = false; 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() void LauncherMainWindow::onQuit()
{ {
if (m_inAppMode) { if (m_inAppMode) {
@ -628,6 +572,7 @@ void LauncherMainWindow::updateSelectedAircraft()
m_selectedAircraftInfo->setUri(m_selectedAircraft); m_selectedAircraftInfo->setUri(m_selectedAircraft);
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft); QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (index.isValid()) { if (index.isValid()) {
#if 0
QPixmap pm = index.data(Qt::DecorationRole).value<QPixmap>(); QPixmap pm = index.data(Qt::DecorationRole).value<QPixmap>();
m_ui->thumbnail->setPixmap(pm); m_ui->thumbnail->setPixmap(pm);
@ -640,7 +585,7 @@ void LauncherMainWindow::updateSelectedAircraft()
QVariant longDesc = index.data(AircraftLongDescriptionRole); QVariant longDesc = index.data(AircraftLongDescriptionRole);
m_ui->aircraftDescription->setVisible(!longDesc.isNull()); m_ui->aircraftDescription->setVisible(!longDesc.isNull());
m_ui->aircraftDescription->setText(longDesc.toString()); m_ui->aircraftDescription->setText(longDesc.toString());
#endif
int status = index.data(AircraftPackageStatusRole).toInt(); int status = index.data(AircraftPackageStatusRole).toInt();
bool canRun = (status == LocalAircraftCache::PackageInstalled); bool canRun = (status == LocalAircraftCache::PackageInstalled);
m_ui->flyButton->setEnabled(canRun); m_ui->flyButton->setEnabled(canRun);
@ -655,6 +600,7 @@ void LauncherMainWindow::updateSelectedAircraft()
m_ui->location->setAircraftType(aircraftType); m_ui->location->setAircraftType(aircraftType);
const bool hasStates = m_selectedAircraftInfo->hasStates(); const bool hasStates = m_selectedAircraftInfo->hasStates();
#if 0
m_ui->stateCombo->setVisible(hasStates); m_ui->stateCombo->setVisible(hasStates);
m_ui->stateLabel->setVisible(hasStates); m_ui->stateLabel->setVisible(hasStates);
m_ui->stateDescription->setVisible(false); m_ui->stateDescription->setVisible(false);
@ -664,13 +610,16 @@ void LauncherMainWindow::updateSelectedAircraft()
// hiden when no description is present // hiden when no description is present
m_ui->stateDescription->setVisible(!m_ui->stateDescription->text().isEmpty()); m_ui->stateDescription->setVisible(!m_ui->stateDescription->text().isEmpty());
} }
#endif
} else { } else {
#if 0
m_ui->thumbnail->setPixmap(QPixmap()); m_ui->thumbnail->setPixmap(QPixmap());
m_ui->aircraftName->setText(""); m_ui->aircraftName->setText("");
m_ui->aircraftDescription->hide(); m_ui->aircraftDescription->hide();
m_ui->stateCombo->hide(); m_ui->stateCombo->hide();
m_ui->stateLabel->hide(); m_ui->stateLabel->hide();
m_ui->stateDescription->hide(); m_ui->stateDescription->hide();
#endif
m_ui->flyButton->setEnabled(false); m_ui->flyButton->setEnabled(false);
} }
} }
@ -690,58 +639,6 @@ void LauncherMainWindow::setSceneryPaths()
flightgear::launcherSetSceneryPaths(); 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() void LauncherMainWindow::onSubsytemIdleTimeout()
{ {
globals->get_subsystem_mgr()->update(0.0); globals->get_subsystem_mgr()->update(0.0);
@ -836,6 +733,11 @@ QmlAircraftInfo *LauncherMainWindow::selectedAircraftInfo() const
return m_selectedAircraftInfo; return m_selectedAircraftInfo;
} }
void LauncherMainWindow::restoreLocation(QVariant var)
{
m_ui->location->restoreLocation(var.toMap());
}
bool LauncherMainWindow::matchesSearch(QString term, QStringList keywords) const bool LauncherMainWindow::matchesSearch(QString term, QStringList keywords) const
{ {
Q_FOREACH(QString s, keywords) { Q_FOREACH(QString s, keywords) {
@ -888,7 +790,6 @@ void LauncherMainWindow::setSettingsSummary(QStringList settingsSummary)
m_settingsSummary = settingsSummary; m_settingsSummary = settingsSummary;
emit summaryChanged(); emit summaryChanged();
updateSettingsSummary();
} }
void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary) void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary)
@ -898,7 +799,16 @@ void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary)
m_environmentSummary = environmentSummary; m_environmentSummary = environmentSummary;
emit summaryChanged(); 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 simgear::pkg::PackageRef LauncherMainWindow::packageForAircraftURI(QUrl uri) const
@ -1040,3 +950,34 @@ bool LauncherMainWindow::showNoOfficialHanger() const
return shouldShowOfficialCatalogMessage(); 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;
}

View file

@ -48,6 +48,9 @@ class ViewCommandLinePage;
class MPServersModel; class MPServersModel;
class QQuickItem; class QQuickItem;
class QmlAircraftInfo; class QmlAircraftInfo;
class QStandardItemModel;
class RecentAircraftModel;
class RecentLocationsModel;
class LauncherMainWindow : public QMainWindow 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 settingsSummary READ settingsSummary WRITE setSettingsSummary NOTIFY summaryChanged)
Q_PROPERTY(QStringList environmentSummary READ environmentSummary WRITE setEnvironmentSummary 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: public:
LauncherMainWindow(); LauncherMainWindow();
virtual ~LauncherMainWindow(); virtual ~LauncherMainWindow();
@ -105,6 +116,8 @@ public:
QmlAircraftInfo* selectedAircraftInfo() const; QmlAircraftInfo* selectedAircraftInfo() const;
Q_INVOKABLE void restoreLocation(QVariant var);
Q_INVOKABLE bool matchesSearch(QString term, QStringList keywords) const; Q_INVOKABLE bool matchesSearch(QString term, QStringList keywords) const;
bool isSearchActive() const; bool isSearchActive() const;
@ -118,6 +131,25 @@ public:
QStringList environmentSummary() const; 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: public slots:
void setSelectedAircraft(QUrl selectedAircraft); void setSelectedAircraft(QUrl selectedAircraft);
@ -155,11 +187,6 @@ private slots:
void onQuit(); void onQuit();
void onPopupAircraftHistory();
void onPopupLocationHistory();
void updateSettingsSummary();
void onSubsytemIdleTimeout(); void onSubsytemIdleTimeout();
void onAircraftInstalledCompleted(QModelIndex index); void onAircraftInstalledCompleted(QModelIndex index);
@ -192,13 +219,11 @@ private:
// scrolling, to give the view time it seems. // scrolling, to give the view time it seems.
void delayedAircraftModelReset(); void delayedAircraftModelReset();
void updateLocationHistory();
bool shouldShowOfficialCatalogMessage() const; bool shouldShowOfficialCatalogMessage() const;
void collectAircraftArgs(); void collectAircraftArgs();
void initQML(); void initQML();
std::string selectStateAutomatically();
QScopedPointer<Ui::Launcher> m_ui; QScopedPointer<Ui::Launcher> m_ui;
AircraftProxyModel* m_installedAircraftModel; AircraftProxyModel* m_installedAircraftModel;
@ -208,21 +233,20 @@ private:
MPServersModel* m_serversModel = nullptr; MPServersModel* m_serversModel = nullptr;
QUrl m_selectedAircraft; QUrl m_selectedAircraft;
QList<QUrl> m_recentAircraft;
QTimer* m_subsystemIdleTimer; QTimer* m_subsystemIdleTimer;
bool m_inAppMode = false; bool m_inAppMode = false;
bool m_runInApp = false; bool m_runInApp = false;
bool m_accepted = false; bool m_accepted = false;
int m_ratingFilters[4] = {3, 3, 3, 3}; int m_ratingFilters[4] = {3, 3, 3, 3};
QVariantList m_recentLocations;
QQmlEngine* m_qmlEngine = nullptr; QQmlEngine* m_qmlEngine = nullptr;
LaunchConfig* m_config = nullptr; LaunchConfig* m_config = nullptr;
ExtraSettingsSection* m_extraSettings = nullptr;
ViewCommandLinePage* m_viewCommandLinePage = nullptr; ViewCommandLinePage* m_viewCommandLinePage = nullptr;
QmlAircraftInfo* m_selectedAircraftInfo = nullptr; QmlAircraftInfo* m_selectedAircraftInfo = nullptr;
QString m_settingsSearchTerm; QString m_settingsSearchTerm;
QStringList m_settingsSummary, QStringList m_settingsSummary,
m_environmentSummary; m_environmentSummary;
RecentAircraftModel* m_aircraftHistory = nullptr;
RecentLocationsModel* m_locationHistory = nullptr;
}; };
#endif // of LAUNCHER_MAIN_WINDOW_HXX #endif // of LAUNCHER_MAIN_WINDOW_HXX

View file

@ -2,6 +2,7 @@
#include <QVariant> #include <QVariant>
#include <QDebug> #include <QDebug>
#include <QQmlEngine>
#include <simgear/package/Install.hxx> #include <simgear/package/Install.hxx>
#include <simgear/package/Root.hxx> #include <simgear/package/Root.hxx>
@ -86,9 +87,12 @@ private:
struct StateInfo struct StateInfo
{ {
std::string tag; // internal XML name string_list tags;
QString name; // human-readable name, or blank if we auto-generate this QString name; // human-readable name, or blank if we auto-generate this
QString description; // human-readable description QString description; // human-readable description
std::string primaryTag() const
{ return tags.front(); }
}; };
using AircraftStateVec = std::vector<StateInfo>; using AircraftStateVec = std::vector<StateInfo>;
@ -111,12 +115,20 @@ static AircraftStateVec readAircraftStates(const SGPath& setXMLPath)
AircraftStateVec result; AircraftStateVec result;
result.reserve(nodes.size()); result.reserve(nodes.size());
for (auto cn : nodes) { 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("readable-name")),
QString::fromStdString(cn->getStringValue("description")) QString::fromStdString(cn->getStringValue("description"))
}); });
qInfo() << QString::fromStdString(result.back().tag) << result.back().description;
} }
return result; return result;
@ -141,35 +153,34 @@ QString humanNameFromStateTag(const std::string& tag)
class StatesModel : public QAbstractListModel class StatesModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool hasExplicitAuto READ hasExplicitAuto CONSTANT)
public: public:
StatesModel(const AircraftStateVec& states) : StatesModel(const AircraftStateVec& states) :
_data(states) _data(states)
{ {
// sort which places 'auto' item at the front if it exists // sort which places 'auto' item at the front if it exists
std::sort(_data.begin(), _data.end(), [](const StateInfo& a, const StateInfo& b) { std::sort(_data.begin(), _data.end(), [](const StateInfo& a, const StateInfo& b) {
if (a.tag == "auto") return true; if (a.primaryTag() == "auto") return true;
if (b.tag == "auto") return false; if (b.primaryTag() == "auto") return false;
return a.tag < b.tag; 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 // track if the aircraft supplied an 'auto' state, in which case
// we will not run our own selection logic // we will not run our own selection logic
_explicitAutoState = true; _explicitAutoState = true;
} else { } else {
// disabling this code for 2018.1, since it needs more testing // 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.")});
_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
} }
} }
int indexForTag(const std::string &tag) const int indexForTag(const std::string &tag) const
{ {
auto it = std::find_if(_data.begin(), _data.end(), [tag](const StateInfo& i) { 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()) if (it == _data.end())
@ -186,18 +197,17 @@ public:
QVariant data(const QModelIndex &index, int role) const override QVariant data(const QModelIndex &index, int role) const override
{ {
const StateInfo& s = _data.at(index.row()); const StateInfo& s = _data.at(index.row());
// qInfo() << index.row() << s.name << QString::fromStdString(s.tag);
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (s.name.isEmpty()) { if (s.name.isEmpty()) {
return humanNameFromStateTag(s.tag); return humanNameFromStateTag(s.primaryTag());
} }
return s.name; return s.name;
} else if (role == QmlAircraftInfo::StateTagRole) { } else if (role == QmlAircraftInfo::StateTagRole) {
return QString::fromStdString(s.tag); return QString::fromStdString(s.primaryTag());
} else if (role == QmlAircraftInfo::StateDescriptionRole) { } else if (role == QmlAircraftInfo::StateDescriptionRole) {
return s.description; return s.description;
} else if (role == QmlAircraftInfo::StateExplicitRole) { } else if (role == QmlAircraftInfo::StateExplicitRole) {
if (s.tag == "auto") if (s.primaryTag() == "auto")
return _explicitAutoState; return _explicitAutoState;
return true; return true;
} }
@ -213,6 +223,28 @@ public:
result[QmlAircraftInfo::StateDescriptionRole] = "description"; result[QmlAircraftInfo::StateDescriptionRole] = "description";
return result; 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: private:
AircraftStateVec _data; AircraftStateVec _data;
bool _explicitAutoState = false; bool _explicitAutoState = false;
@ -224,6 +256,7 @@ QmlAircraftInfo::QmlAircraftInfo(QObject *parent)
: QObject(parent) : QObject(parent)
, _delegate(new Delegate(this)) , _delegate(new Delegate(this))
{ {
qmlRegisterUncreatableType<StatesModel>("FlightGear.Launcher", 1, 0, "StatesModel", "no");
} }
QmlAircraftInfo::~QmlAircraftInfo() QmlAircraftInfo::~QmlAircraftInfo()
@ -621,7 +654,7 @@ bool QmlAircraftInfo::isPackaged() const
return _package != PackageRef(); return _package != PackageRef();
} }
QAbstractListModel *QmlAircraftInfo::statesModel() StatesModel *QmlAircraftInfo::statesModel()
{ {
if (!hasStates()) if (!hasStates())
return nullptr; return nullptr;

View file

@ -14,6 +14,8 @@
struct AircraftItem; struct AircraftItem;
typedef QSharedPointer<AircraftItem> AircraftItemPtr; typedef QSharedPointer<AircraftItem> AircraftItemPtr;
class StatesModel;
class QmlAircraftInfo : public QObject class QmlAircraftInfo : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -57,7 +59,7 @@ class QmlAircraftInfo : public QObject
Q_PROPERTY(bool hasStates READ hasStates NOTIFY infoChanged) 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: public:
explicit QmlAircraftInfo(QObject *parent = nullptr); explicit QmlAircraftInfo(QObject *parent = nullptr);
@ -110,7 +112,7 @@ public:
static const int StateDescriptionRole; static const int StateDescriptionRole;
static const int StateExplicitRole; static const int StateExplicitRole;
QAbstractListModel* statesModel(); StatesModel* statesModel();
signals: signals:
void uriChanged(); void uriChanged();
void infoChanged(); void infoChanged();
@ -134,7 +136,7 @@ private:
AircraftItemPtr _item; AircraftItemPtr _item;
int _variant = 0; int _variant = 0;
int _downloadBytes = 0; int _downloadBytes = 0;
QScopedPointer<QAbstractListModel> _statesModel; QScopedPointer<StatesModel> _statesModel;
}; };
#endif // QMLAIRCRAFTINFO_HXX #endif // QMLAIRCRAFTINFO_HXX

View file

@ -0,0 +1,92 @@
#include "RecentAircraftModel.hxx"
#include <QSettings>
#include <QDebug>
#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<int, QByteArray> RecentAircraftModel::roleNames() const
{
QHash<int, QByteArray> 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();
}
}

View file

@ -0,0 +1,34 @@
#ifndef RECENTAIRCRAFTMODEL_HXX
#define RECENTAIRCRAFTMODEL_HXX
#include <QAbstractListModel>
#include <QUrl>
// 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<int, QByteArray> roleNames() const override;
QUrl mostRecent() const;
void insert(QUrl aircraftUrl);
void saveToSettings();
Q_INVOKABLE QUrl uriAt(int index) const;
private:
AircraftItemModel* m_aircraftModel;
QList<QUrl> m_data;
};
#endif // RECENTAIRCRAFTMODEL_HXX

View file

@ -0,0 +1,91 @@
#include "RecentLocationsModel.hxx"
#include <QSettings>
#include <QDebug>
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<int, QByteArray> RecentLocationsModel::roleNames() const
{
QHash<int, QByteArray> 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();
}
}

View file

@ -0,0 +1,31 @@
#ifndef RECENTLOCATIONSMODEL_HXX
#define RECENTLOCATIONSMODEL_HXX
#include <QAbstractListModel>
#include <QvariantList>
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<int, QByteArray> 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

View file

@ -4,7 +4,8 @@ import "."
Text { Text {
signal clicked(); signal clicked();
color: mouse.containsMouse ? Style.themeColor : Style.baseTextColor property color baseTextColor: Style.baseTextColor
color: mouse.containsMouse ? Style.themeColor : baseTextColor
MouseArea { MouseArea {
id: mouse id: mouse
@ -12,5 +13,7 @@ Text {
anchors.fill: parent anchors.fill: parent
onClicked: parent.clicked(); onClicked: parent.clicked();
cursorShape: Qt.PointingHandCursor
} }
} }

View file

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

View file

@ -14,7 +14,7 @@ Item {
property int currentIndex: 0 property int currentIndex: 0
property bool __dummy: false property bool __dummy: false
implicitHeight: label.implicitHeight implicitHeight: Math.max(label.implicitHeight, currentChoiceFrame.height)
Item { Item {
Repeater { Repeater {
@ -40,6 +40,10 @@ Item {
__dummy = !__dummy __dummy = !__dummy
} }
onModelChanged: {
__dummy = !__dummy // force update of currentText
}
function currentText() function currentText()
{ {
var foo = __dummy; // fake propery dependency to update this var foo = __dummy; // fake propery dependency to update this

View file

@ -21,6 +21,10 @@ QtObject
readonly property string baseTextColor: "#3f3f3f" readonly property string baseTextColor: "#3f3f3f"
readonly property int baseFontPixelSize: 12 readonly property int baseFontPixelSize: 12
readonly property int headingFontPixelSize: 18
readonly property string disabledTextColor: "#5f5f5f" readonly property string disabledTextColor: "#5f5f5f"
readonly property double panelOpacity: 0.8
} }

321
src/GUI/qml/Summary.qml Normal file
View file

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

View file

@ -71,21 +71,12 @@
<file>qml/DateTimeEdit.qml</file> <file>qml/DateTimeEdit.qml</file>
<file>qml/DateTimeValueEdit.qml</file> <file>qml/DateTimeValueEdit.qml</file>
<file>qml/SettingsDateTimePicker.qml</file> <file>qml/SettingsDateTimePicker.qml</file>
<file>qml/Summary.qml</file>
<file>qml/HistoryPopup.qml</file>
</qresource> </qresource>
<qresource prefix="/preview"> <qresource prefix="/preview">
<file alias="close-icon">preview-close.png</file> <file alias="close-icon">preview-close.png</file>
<file alias="left-arrow-icon">preview-left-arrow.png</file> <file alias="left-arrow-icon">preview-left-arrow.png</file>
<file alias="right-arrow-icon">preview-right-arrow.png</file> <file alias="right-arrow-icon">preview-right-arrow.png</file>
</qresource> </qresource>
<qresource prefix="/settings">
<file alias="downloads">DownloadSettings.qml</file>
<file alias="general">GeneralSettings.qml</file>
<file alias="mp">MPSettings.qml</file>
<file alias="render">RenderSettings.qml</file>
<file alias="view">ViewSettings.qml</file>
</qresource>
<qresource prefix="/environment">
<file alias="time">TimeSettings.qml</file>
<file alias="weather">Weather.qml</file>
</qresource>
</RCC> </RCC>