1
0
Fork 0

Launcher converted to QQ2

This simplifies the launcher's rendering since the widget-based
code is gone, various things get hooked up as a result. Styling
fixes everywhere as well.

Menubar on Linux/Windows needs to be re-added.
This commit is contained in:
James Turner 2018-06-22 12:09:35 +01:00
parent 78882621e9
commit 1bf52662ae
29 changed files with 613 additions and 393 deletions

View file

@ -51,7 +51,7 @@ QStringList AddOnsController::sceneryPaths() const
QString AddOnsController::addAircraftPath() const
{
QString path = QFileDialog::getExistingDirectory(m_launcher, tr("Choose aircraft folder"));
QString path = QFileDialog::getExistingDirectory(nullptr, tr("Choose aircraft folder"));
if (path.isEmpty()) {
return {};
}
@ -90,7 +90,7 @@ QString AddOnsController::addAircraftPath() const
QString AddOnsController::addSceneryPath() const
{
QString path = QFileDialog::getExistingDirectory(m_launcher, tr("Choose scenery folder"));
QString path = QFileDialog::getExistingDirectory(nullptr, tr("Choose scenery folder"));
if (path.isEmpty()) {
return {};
@ -128,7 +128,7 @@ QString AddOnsController::installCustomScenery()
{
QSettings settings;
QString downloadDir = settings.value("download-dir").toString();
InstallSceneryDialog dlg(m_launcher, downloadDir);
InstallSceneryDialog dlg(nullptr, downloadDir);
if (dlg.exec() == QDialog::Accepted) {
return dlg.sceneryPath();
}

View file

@ -47,6 +47,45 @@ void LaunchConfig::setEnableDisableOption(QString name, bool value)
m_values.push_back(Arg((value ? "enable-" : "disable-") + name));
}
QString LaunchConfig::htmlForCommandLine()
{
QString html;
string_list commandLineOpts = flightgear::Options::sharedInstance()->extractOptions();
if (!commandLineOpts.empty()) {
html += tr("<p>Options passed on the command line:</p>\n");
html += "<ul>\n";
for (auto opt : commandLineOpts) {
html += QString("<li>--") + QString::fromStdString(opt) + "</li>\n";
}
html += "</ul>\n";
}
#if 0
if (m_extraSettings) {
LauncherArgumentTokenizer tk;
Q_FOREACH(auto arg, tk.tokenize(m_extraSettings->argsText())) {
// m_config->setArg(arg.arg, arg.value);
}
}
#endif
reset();
collect();
html += tr("<p>Options set in the launcher:</p>\n");
html += "<ul>\n";
for (auto arg : values()) {
if (arg.value.isEmpty()) {
html += QString("<li>--") + arg.arg + "</li>\n";
} else if (arg.arg == "prop") {
html += QString("<li>--") + arg.arg + ":" + arg.value + "</li>\n";
} else {
html += QString("<li>--") + arg.arg + "=" + arg.value + "</li>\n";
}
}
html += "</ul>\n";
return html;
}
QVariant LaunchConfig::getValueForKey(QString group, QString key, QVariant defaultValue) const
{
QSettings settings;

View file

@ -38,6 +38,7 @@ public:
Q_INVOKABLE void setEnableDisableOption(QString name, bool value);
Q_INVOKABLE QString htmlForCommandLine();
// ensure a property is /not/ set?

View file

@ -49,8 +49,9 @@
using namespace simgear::pkg;
LauncherController::LauncherController(QObject *parent) :
QObject(parent)
LauncherController::LauncherController(QObject *parent, QWindow* window) :
QObject(parent),
m_window(window)
{
m_serversModel = new MPServersModel(this);
m_location = new LocationController(this);
@ -91,6 +92,19 @@ LauncherController::LauncherController(QObject *parent) :
LocalAircraftCache::instance()->scanDirs();
m_aircraftModel->setPackageRoot(globals->packageRoot());
m_subsystemIdleTimer = new QTimer(this);
m_subsystemIdleTimer->setInterval(0);
connect(m_subsystemIdleTimer, &QTimer::timeout, []()
{globals->get_subsystem_mgr()->update(0.0);});
m_subsystemIdleTimer->start();
QRect winRect= settings.value("window-geometry").toRect();
if (winRect.isValid()) {
m_window->setGeometry(winRect);
} else {
m_window->setWidth(600);
m_window->setHeight(800);
}
}
void LauncherController::initQML()
@ -171,6 +185,9 @@ void LauncherController::saveSettings()
{
emit requestSaveState();
QSettings settings;
settings.setValue("window-geometry", m_window->geometry());
m_aircraftHistory->saveToSettings();
m_locationHistory->saveToSettings();
}
@ -448,6 +465,12 @@ void LauncherController::setEnvironmentSummary(QStringList environmentSummary)
emit summaryChanged();
}
void LauncherController::fly()
{
doRun();
qApp->exit(1);
}
QStringList LauncherController::combinedSummary() const
{
return m_settingsSummary + m_environmentSummary;

View file

@ -29,6 +29,8 @@
#include <simgear/package/Catalog.hxx>
// forward decls
class QTimer;
class QWindow;
class AircraftProxyModel;
class QmlAircraftInfo;
class RecentAircraftModel;
@ -74,7 +76,7 @@ class LauncherController : public QObject
Q_PROPERTY(AircraftType aircraftType READ aircraftType NOTIFY selectedAircraftChanged)
public:
explicit LauncherController(QObject *parent);
explicit LauncherController(QObject *parent, QWindow* win);
void initQML();
@ -174,6 +176,9 @@ signals:
* state to persistent storage
*/
void requestSaveState();
void viewCommandLine();
public slots:
void setSelectedAircraft(QUrl selectedAircraft);
@ -183,6 +188,8 @@ public slots:
void setEnvironmentSummary(QStringList environmentSummary);
void fly();
private slots:
void onAircraftInstalledCompleted(QModelIndex index);
@ -206,6 +213,8 @@ private:
void collectAircraftArgs();
private:
QWindow* m_window = nullptr;
AircraftProxyModel* m_installedAircraftModel;
AircraftItemModel* m_aircraftModel;
AircraftProxyModel* m_aircraftSearchModel;
@ -222,6 +231,8 @@ private:
QStringList m_settingsSummary, m_environmentSummary;
RecentAircraftModel* m_aircraftHistory = nullptr;
RecentLocationsModel* m_locationHistory = nullptr;
QTimer* m_subsystemIdleTimer = nullptr;
};
#endif // LAUNCHERCONTROLLER_HXX

View file

@ -6,10 +6,7 @@
#include <QDebug>
#include <QMenu>
#include <QMenuBar>
#include <QMenu>
#include <QPushButton>
#include <QDesktopServices>
#include <QQuickItem>
#include <QQmlEngine>
@ -17,104 +14,51 @@
#include <QQmlContext>
#include <QQmlError>
// simgear headers
#include <simgear/package/Install.hxx>
#include <simgear/environment/metar.hxx>
#include <simgear/structure/exception.hxx>
// FlightGear headers
#include <Network/HTTPClient.hxx>
#include <Main/globals.hxx>
#include <Airports/airport.hxx>
#include <Main/options.hxx>
#include <Main/fg_init.hxx>
#include <Main/fg_props.hxx>
#include "version.h"
// launcher headers
#include "QtLauncher.hxx"
#include "AircraftModel.hxx"
#include "AircraftSearchFilterModel.hxx"
#include "DefaultAircraftLocator.hxx"
#include "LaunchConfig.hxx"
#include "ViewCommandLinePage.hxx"
#include "AircraftModel.hxx"
#include "LocalAircraftCache.hxx"
#include "LauncherController.hxx"
#include "DefaultAircraftLocator.hxx"
#include "AddOnsController.hxx"
#include "CatalogListModel.hxx"
#include "LocationController.hxx"
#include "ui_Launcher.h"
extern void restartTheApp(QStringList fgArgs);
//////////////////////////////////////////////////////////////////////////////
LauncherMainWindow::LauncherMainWindow() :
QMainWindow(),
m_subsystemIdleTimer(NULL)
QQuickView()
{
m_ui.reset(new Ui::Launcher);
m_ui->setupUi(this);
setTitle("FlightGear " FLIGHTGEAR_VERSION);
QMenuBar* mb = menuBar();
#if !defined(Q_OS_MAC)
QMenu* fileMenu = mb->addMenu(tr("File"));
QAction* quitAction = fileMenu->addAction(tr("Exit"));
connect(quitAction, &QAction::triggered,
this, &LauncherMainWindow::onQuit);
#endif
m_controller = new LauncherController(this);
m_controller = new LauncherController(this, this);
m_controller->initQML();
connect(m_controller, &LauncherController::canFlyChanged,
this, &LauncherMainWindow::onCanFlyChanged);
#if defined(Q_OS_MAC)
QMenuBar* mb = new QMenuBar();
QMenu* toolsMenu = mb->addMenu(tr("Tools"));
QAction* restoreDefaultsAction = toolsMenu->addAction(tr("Restore defaults..."));
connect(restoreDefaultsAction, &QAction::triggered,
this, &LauncherMainWindow::onRestoreDefaults);
QMenu* toolsMenu = mb->addMenu(tr("Tools"));
QAction* restoreDefaultsAction = toolsMenu->addAction(tr("Restore defaults..."));
connect(restoreDefaultsAction, &QAction::triggered,
this, &LauncherMainWindow::onRestoreDefaults);
QAction* changeDataAction = toolsMenu->addAction(tr("Select data files location..."));
connect(changeDataAction, &QAction::triggered,
this, &LauncherMainWindow::onChangeDataDir);
QAction* changeDataAction = toolsMenu->addAction(tr("Select data files location..."));
connect(changeDataAction, &QAction::triggered,
this, &LauncherMainWindow::onChangeDataDir);
QAction* viewCommandLineAction = toolsMenu->addAction(tr("View command-line"));
connect(viewCommandLineAction, &QAction::triggered,
this, &LauncherMainWindow::onViewCommandLine);
m_subsystemIdleTimer = new QTimer(this);
m_subsystemIdleTimer->setInterval(0);
connect(m_subsystemIdleTimer, &QTimer::timeout,
this, &LauncherMainWindow::onSubsytemIdleTimeout);
m_subsystemIdleTimer->start();
connect(m_ui->flyButton, SIGNAL(clicked()), this, SLOT(onRun()));
connect(m_ui->summaryButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->aircraftButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->locationButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->environmentButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->addOnsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
QAction* viewCommandLineAction = toolsMenu->addAction(tr("View command-line"));
connect(viewCommandLineAction, &QAction::triggered,
m_controller, &LauncherController::viewCommandLine);
#endif
QAction* qa = new QAction(this);
qa->setShortcut(QKeySequence("Ctrl+Q"));
connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit);
addAction(qa);
m_viewCommandLinePage = new ViewCommandLinePage;
m_viewCommandLinePage->setLaunchConfig(m_controller->config());
m_ui->stack->addWidget(m_viewCommandLinePage);
QSettings settings;
restoreGeometry(settings.value("window-geometry").toByteArray());
m_controller->restoreSettings();
flightgear::launcherSetSceneryPaths();
@ -130,83 +74,23 @@ LauncherMainWindow::LauncherMainWindow() :
const QString osName("unix");
#endif
/////////////
// aircraft
m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView);
setResizeMode(QQuickView::SizeRootObjectToView);
engine()->addImportPath("qrc:///");
m_ui->aircraftList->engine()->addImportPath("qrc:///");
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_addOns", addOnsCtl);
QQmlContext* ctx = rootContext();
ctx->setContextProperty("_launcher", m_controller);
ctx->setContextProperty("_addOns", addOnsCtl);
ctx->setContextProperty("_config", m_controller->config());
ctx->setContextProperty("_location", m_controller->location());
ctx->setContextProperty("_osName", osName);
connect( m_ui->aircraftList, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->aircraftList->setSource(QUrl("qrc:///qml/AircraftList.qml"));
/////////////
// location
m_ui->location->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_ui->location->engine()->addImportPath("qrc:///");
m_ui->location->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->location->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->location->engine()->rootContext()->setContextProperty("_location", m_controller->location());
connect( m_ui->location, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->location->setSource(QUrl("qrc:///qml/Location.qml"));
/////////////
// settings
m_ui->settings->engine()->addImportPath("qrc:///");
QQmlContext* settingsContext = m_ui->settings->engine()->rootContext();
settingsContext->setContextProperty("_launcher", m_controller);
settingsContext->setContextProperty("_osName", osName);
settingsContext->setContextProperty("_config", m_controller->config());
m_ui->settings->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect( m_ui->settings, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->settings->setSource(QUrl("qrc:///qml/Settings.qml"));
// environemnt
m_ui->environmentPage->engine()->addImportPath("qrc:///");
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_launcher", m_controller);
auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->environmentPage->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect( m_ui->environmentPage, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->environmentPage->setSource(QUrl("qrc:///qml/Environment.qml"));
// summary
m_ui->summary->engine()->addImportPath("qrc:///");
m_ui->summary->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->summary->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->summary->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect( m_ui->summary, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
m_ui->summary->setSource(QUrl("qrc:///qml/Summary.qml"));
// addOns
m_ui->addOns->engine()->addImportPath("qrc:///");
m_ui->addOns->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->addOns->engine()->rootContext()->setContextProperty("_addOns", addOnsCtl);
m_ui->addOns->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_ui->addOns->setSource(QUrl("qrc:///qml/AddOns.qml"));
//////////////////////////
ctx->setContextProperty("_weatherScenarios", weatherScenariosModel);
setSource(QUrl("qrc:///qml/Launcher.qml"));
}
void LauncherMainWindow::saveSettings()
{
QSettings settings;
settings.setValue("window-geometry", saveGeometry());
}
#if 0
void LauncherMainWindow::onQuickStatusChanged(QQuickWidget::Status status)
{
if (status == QQuickWidget::Error) {
@ -224,11 +108,7 @@ void LauncherMainWindow::onQuickStatusChanged(QQuickWidget::Status status)
+ errorString);
}
}
void LauncherMainWindow::onCanFlyChanged()
{
m_ui->flyButton->setEnabled(m_controller->canFly());
}
#endif
LauncherMainWindow::~LauncherMainWindow()
{
@ -236,6 +116,7 @@ LauncherMainWindow::~LauncherMainWindow()
bool LauncherMainWindow::execInApp()
{
#if 0
m_inAppMode = true;
m_ui->addOnsButton->hide();
m_ui->settingsButton->hide();
@ -250,22 +131,10 @@ bool LauncherMainWindow::execInApp()
}
return m_accepted;
#endif
}
void LauncherMainWindow::closeEvent(QCloseEvent *event)
{
qApp->exit(-1);
}
void LauncherMainWindow::onRun()
{
m_controller->doRun();
saveSettings();
qApp->exit(1);
}
#if 0
void LauncherMainWindow::onApply()
{
@ -274,6 +143,7 @@ void LauncherMainWindow::onApply()
m_accepted = true;
m_runInApp = false;
}
#endif
void LauncherMainWindow::onQuit()
{
@ -286,7 +156,7 @@ void LauncherMainWindow::onQuit()
void LauncherMainWindow::onRestoreDefaults()
{
QMessageBox mbox(this);
QMessageBox mbox;
mbox.setText(tr("Restore all settings to defaults?"));
mbox.setInformativeText(tr("Restoring settings to their defaults may affect available add-ons such as scenery or aircraft."));
QPushButton* quitButton = mbox.addButton(tr("Restore and restart now"), QMessageBox::YesRole);
@ -308,30 +178,6 @@ void LauncherMainWindow::onRestoreDefaults()
flightgear::restartTheApp();
}
void LauncherMainWindow::onViewCommandLine()
{
m_ui->stack->setCurrentIndex(6);
Q_FOREACH (ToolboxButton* tb, findChildren<ToolboxButton*>()) {
tb->setChecked(false);
}
m_viewCommandLinePage->update();
}
void LauncherMainWindow::onClickToolboxButton()
{
int pageIndex = sender()->property("pageIndex").toInt();
m_ui->stack->setCurrentIndex(pageIndex);
Q_FOREACH (ToolboxButton* tb, findChildren<ToolboxButton*>()) {
tb->setChecked(tb->property("pageIndex").toInt() == pageIndex);
}
saveSettings();
}
void LauncherMainWindow::onSubsytemIdleTimeout()
{
globals->get_subsystem_mgr()->update(0.0);
}
void LauncherMainWindow::onChangeDataDir()
{
@ -344,7 +190,7 @@ void LauncherMainWindow::onChangeDataDir()
currentLocText = tr("Currently using location: %1").arg(root);
}
QMessageBox mbox(this);
QMessageBox mbox;
mbox.setText(tr("Change the data files used by FlightGear?"));
mbox.setInformativeText(tr("FlightGear requires additional files to operate. "
"(Also called the base package, or fg-data) "

View file

@ -2,7 +2,7 @@
//
// Written by James Turner, started October 2015.
//
// Copyright (C) 2015 James Turner <zakalawe@mac.com>
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
@ -21,18 +21,13 @@
#ifndef LAUNCHER_MAIN_WINDOW_HXX
#define LAUNCHER_MAIN_WINDOW_HXX
#include <QMainWindow>
#include <QScopedPointer>
#include <QStringList>
#include <QModelIndex>
#include <QTimer>
#include <QUrl>
#include <QQuickWidget>
#include <QQuickView>
namespace Ui
{
class Launcher;
}
class QModelIndex;
class QQmlEngine;
@ -41,7 +36,7 @@ class ViewCommandLinePage;
class QQuickItem;
class LauncherController;
class LauncherMainWindow : public QMainWindow
class LauncherMainWindow : public QQuickView
{
Q_OBJECT
public:
@ -54,47 +49,25 @@ public:
protected:
virtual void closeEvent(QCloseEvent *event) override;
private slots:
// run is used when the launcher is invoked before the main app is
// started
void onRun();
// apply is used in-app, where we must set properties and trigger
// a reset; setting command line options won't help us.
void onApply();
void onQuit();
void onSubsytemIdleTimeout();
void onRestoreDefaults();
void onViewCommandLine();
// void onViewCommandLine();
void onClickToolboxButton();
void onQuit();
void onQuickStatusChanged(QQuickWidget::Status status);
void onCanFlyChanged();
// void onQuickStatusChanged(QQuickWidget::Status status);
void onChangeDataDir();
private:
LauncherController* m_controller;
QScopedPointer<Ui::Launcher> m_ui;
QTimer* m_subsystemIdleTimer;
bool m_inAppMode = false;
bool m_runInApp = false;
bool m_accepted = false;
ViewCommandLinePage* m_viewCommandLinePage = nullptr;
void restoreSettings();
void saveSettings();
};
#endif // of LAUNCHER_MAIN_WINDOW_HXX

View file

@ -370,6 +370,8 @@ LocationController::LocationController(QObject *parent) :
m_searchModel = new NavSearchModel;
m_detailQml = new QmlPositioned(this);
m_baseQml = new QmlPositioned(this);
// chain location and offset updated to description
connect(this, &LocationController::baseLocationChanged,
this, &LocationController::descriptionChanged);
@ -453,11 +455,23 @@ void LocationController::setBaseGeod(QmlGeod geod)
void LocationController::setBaseLocation(QmlPositioned* pos)
{
if (!pos) {
m_location.clear();
m_detailLocation.clear();
m_detailQml->setGuid(0);
m_baseQml->setGuid(0);
m_airportLocation.clear();
m_locationIsLatLon = false;
emit baseLocationChanged();
return;
}
if (pos->inner() == m_location)
return;
m_locationIsLatLon = false;
m_location = pos->inner();
m_baseQml->setGuid(pos->guid());
m_detailLocation.clear();
m_detailQml->setGuid(0);
@ -610,6 +624,11 @@ QmlPositioned *LocationController::detail() const
return m_detailQml;
}
QmlPositioned *LocationController::baseLocation() const
{
return m_baseQml;
}
void LocationController::setOffsetRadial(int offsetRadial)
{
if (m_offsetRadial == offsetRadial)
@ -679,6 +698,7 @@ void LocationController::restoreLocation(QVariantMap l)
if (FGPositioned::isAirportType(m_location.ptr())) {
m_airportLocation = static_cast<FGAirport*>(m_location.ptr());
}
m_baseQml->setInner(m_location);
}
if (l.contains("altitude-type")) {
@ -716,6 +736,10 @@ void LocationController::restoreLocation(QVariantMap l)
m_detailLocation = m_airportLocation->groundNetwork()->findParkingByName(parking.toStdString());
}
if (m_detailLocation) {
m_detailQml->setInner(m_detailLocation);
}
m_onFinal = l.value("location-on-final").toBool();
m_offsetNm = l.value("location-apt-final-distance").toInt();
} // of location is an airport

View file

@ -67,7 +67,9 @@ class LocationController : public QObject
Q_PROPERTY(bool tuneNAV1 READ tuneNAV1 WRITE setTuneNAV1 NOTIFY configChanged)
Q_PROPERTY(QmlGeod baseGeod READ baseGeod WRITE setBaseGeod NOTIFY baseLocationChanged)
Q_PROPERTY(QmlPositioned* base READ baseLocation CONSTANT)
Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT)
Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged)
public:
explicit LocationController(QObject *parent = nullptr);
~LocationController();
@ -152,11 +154,18 @@ public:
QmlPositioned* detail() const;
QmlPositioned* baseLocation() const;
bool useAvailableParking() const
{
return m_useAvailableParking;
}
bool isBaseLatLon() const
{
return m_locationIsLatLon;
}
public slots:
void setOffsetRadial(int offsetRadial);
@ -203,6 +212,7 @@ private:
FGPositionedList m_recentLocations;
LaunchConfig* m_config = nullptr;
QmlPositioned* m_detailQml = nullptr;
QmlPositioned* m_baseQml = nullptr;
bool m_offsetEnabled = false;
int m_offsetRadial = 0;

View file

@ -10,9 +10,17 @@ Rectangle {
readonly property bool isActive: (state != "start")
Behavior on height {
enabled: false // disable to prevent animation on load
id: heightBehaviour
NumberAnimation { duration: 200; }
}
Timer { // ugly: timer to enable the animation after loading is done
onTriggered: heightBehaviour.enabled = true;
running: true
interval: 5
}
Column {
id: column
width: parent.width

View file

@ -36,14 +36,14 @@ Item {
anchors.rightMargin: Style.margin
spacing: Style.margin
Text {
StyledText {
font.pixelSize: Style.subHeadingFontPixelSize
font.bold: true
width: parent.width
text: model.name
}
Text {
StyledText {
visible: model.status === CatalogListModel.Ok
width: parent.width
text: model.description
@ -62,7 +62,7 @@ Item {
}
}
Text {
StyledText {
width: parent.width
text: model.url
}
@ -119,33 +119,12 @@ Item {
//////////////////////////////////////////////////////////////////
// catalogs //////////////////////////////////////////////////////
Item {
id: catalogHeaderItem
width: parent.width
height: catalogHeadingText.height + catalogDescriptionText.height + Style.margin
Text {
id: catalogHeadingText
text: qsTr("Aircraft hangars")
font.pixelSize: Style.headingFontPixelSize
width: parent.width
}
Text {
id: catalogDescriptionText
text: qsTr("Aircraft hangars are managed collections of aircraft, which can be " +
"downloaded, installed and updated inside FlightGear.")
anchors {
top: catalogHeadingText.bottom
topMargin: Style.margin
}
width: parent.width
wrapMode: Text.WordWrap
}
} // of catalogs header item
AddOnsHeader {
id: catalogHeader
title: qsTr("Aircraft hangars")
description: qsTr("Aircraft hangars are managed collections of aircraft, which can be " +
"downloaded, installed and updated inside FlightGear.")
}
Rectangle {
width: parent.width
@ -191,49 +170,19 @@ Item {
height: Style.margin * 2
}
Item {
id: aircraftHeaderItem
width: parent.width
height: aircraftHeading.height + aircraftDescriptionText.height + Style.margin
Text {
id: aircraftHeading
text: qsTr("Additional aircraft folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
AddOnsHeader {
id: aircraftHeader
title: qsTr("Additional aircraft folders")
description: qsTr("To use aircraft you download yourself, FlightGear needs to " +
"know the folder(s) containing the aircraft data.")
showAddButton: true
onAdd: {
var newPath =_addOns.addAircraftPath();
if (newPath !== "") {
_addOns.aircraftPaths.push(newPath)
}
}
Text {
id: aircraftDescriptionText
text: qsTr("To use aircraft you download yourself, FlightGear needs to " +
"know the folder(s) containing the aircraft data.")
anchors {
top: aircraftHeading.bottom
topMargin: Style.margin
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addAircraftPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addAircraftPath();
if (newPath !== "") {
_addOns.aircraftPaths.push(newPath)
}
}
}
} // of aircraft header item
}
Rectangle {
width: parent.width
@ -270,7 +219,7 @@ Item {
}
}
Text {
StyledText {
visible: (aircraftPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom aircraft paths are configured.");
@ -288,51 +237,20 @@ Item {
height: Style.margin * 2
}
Item {
id: sceneryHeaderItem
width: parent.width
height: sceneryHeading.height + sceneryDescriptionText.height + Style.margin
Text {
id: sceneryHeading
text: qsTr("Additional scenery folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
AddOnsHeader {
id: sceneryHeader
title: qsTr("Additional scenery folders")
description: qsTr("To use scenery you download yourself, FlightGear needs " +
"to know the folders containing the scenery data. " +
"Adjust the order of the list to control which scenery is used in a region.");
showAddButton: true
onAdd: {
var newPath =_addOns.addSceneryPath();
if (newPath !== "") {
_addOns.sceneryPaths.push(newPath)
}
}
Text {
id: sceneryDescriptionText
text: qsTr("To use scenery you download yourself, FlightGear needs " +
"to know the folders containing the scenery data. " +
"Adjust the order of the list to control which scenery is used in a region.");
anchors {
top: sceneryHeading.bottom
topMargin: Style.margin
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addSceneryPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addSceneryPath();
if (newPath !== "") {
_addOns.sceneryPaths.push(newPath)
}
}
}
} // of aircraft header item
}
Rectangle {
width: parent.width
@ -370,7 +288,7 @@ Item {
}
}
Text {
StyledText {
visible: (sceneryPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom scenery paths are configured.");
@ -400,7 +318,7 @@ Item {
}
}
Text {
StyledText {
id: installTarballText
anchors {
left: installTarballButton.right

View file

@ -0,0 +1,53 @@
import QtQuick 2.4
import "."
Item {
id: root
property bool showAddButton: false
property alias title: headerTitle.text
property alias description: description.text
signal add();
implicitWidth: parent.width
implicitHeight: headerRect.height + Style.margin + description.height
Rectangle {
id: headerRect
width: parent.width
height: headerTitle.height + (Style.margin * 2)
color: Style.themeColor
border.width: 1
border.color: Style.frameColor
Text {
id: headerTitle
color: "white"
anchors.verticalCenter: parent.verticalCenter
font.bold: true
font.pixelSize: Style.subHeadingFontPixelSize
anchors.left: parent.left
anchors.leftMargin: Style.inset
}
AddButton {
id: addButton
visible: root.showAddButton
anchors.right: parent.right
anchors.rightMargin: Style.margin
anchors.verticalCenter: parent.verticalCenter
onClicked: root.add()
}
}
StyledText {
id: description
width: parent.width
anchors.top: headerRect.bottom
anchors.topMargin: Style.margin
wrapMode: Text.WordWrap
}
}

View file

@ -6,6 +6,10 @@ Item
{
id: root
Component.onCompleted: {
aircraftList.updateSelectionFromLauncher();
}
Rectangle
{
id: tabBar

View file

@ -8,6 +8,7 @@ Text {
property bool clickable: true
property color baseTextColor: Style.baseTextColor
color: mouse.containsMouse ? Style.themeColor : baseTextColor
font.pixelSize: Style.baseFontPixelSize
MouseArea {
id: mouse

117
src/GUI/qml/Launcher.qml Normal file
View file

@ -0,0 +1,117 @@
import QtQuick 2.4
import "."
Item {
id: root
// order of this model sets the order of buttons in the sidebar
ListModel {
id: pagesModel
ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///toolbox-summary"; state:"loader" }
ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///toolbox-aircraft"; state:"loader" }
ListElement { title: qsTr("Location"); pageSource: "qrc:///qml/Location.qml"; iconPath: "qrc:///toolbox-location"; state:"loader" }
// due to some design stupidity by James, we can't use the Loader mechanism for these pages; they need to exist
// permanently so that collecting args works. So we instantiate them down below, and toggle the visiblity
// of them and the loader using a state.
ListElement { title: qsTr("Environment"); pageSource: ""; iconPath: "qrc:///toolbox-environment"; state:"environment" }
ListElement { title: qsTr("Settings"); pageSource: ""; iconPath: "qrc:///toolbox-settings"; state:"settings" }
ListElement { title: qsTr("Add-ons"); pageSource: "qrc:///qml/AddOns.qml"; iconPath: "qrc:///toolbox-addons"; state:"loader" }
}
states: [
State {
name: "loader"
PropertyChanges { target: pageLoader; visible: true }
PropertyChanges { target: settings; visible: false }
PropertyChanges { target: environment; visible: false }
},
State {
name: "settings"
PropertyChanges { target: pageLoader; visible: false }
PropertyChanges { target: settings; visible: true }
PropertyChanges { target: environment; visible: false }
},
State {
name: "environment"
PropertyChanges { target: pageLoader; visible: false }
PropertyChanges { target: settings; visible: false }
PropertyChanges { target: environment; visible: true }
}
]
Connections {
target: _launcher
onViewCommandLine: {
sidebar.selectedPage = -1;
pageLoader.source = "qrc:///qml/ViewCommandLine.qml"
root.state = "loader";
}
}
Sidebar {
id: sidebar
width: Style.strutSize * 2
height: parent.height
z: 1
pagesModel: pagesModel
selectedPage: 0 // open on the summary page
onSelectPage: {
pageLoader.source = pageSource
root.state = pagesModel.get(selectedPage).state
}
}
Settings {
id: settings
height: parent.height
anchors {
left: sidebar.right
right: parent.right
}
}
Environment {
id: environment
height: parent.height
anchors {
left: sidebar.right
right: parent.right
}
}
Loader {
id: pageLoader
height: parent.height
anchors {
left: sidebar.right
right: parent.right
}
source: "qrc:///qml/Summary.qml"
}
function selectPage(index)
{
sidebar.setSelectedPage(index);
var page = pagesModel.get(index);
pageLoader.source = page.pageSource
root.state = page.state
}
Connections {
target: pageLoader.item
ignoreUnknownSignals: true
onShowSelectedAircraft: root.selectPage(1)
onShowSelectedLocation: root.selectPage(2)
}
}

View file

@ -12,6 +12,7 @@ Item {
function backToSearch()
{
detailLoader.sourceComponent = null
_location.setBaseLocation(null)
}
function selectLocation(guid, type)
@ -28,7 +29,21 @@ Item {
}
Component.onCompleted: {
_location.showHistoryInSearchModel()
// important so we can leave the location page and return to it,
// preserving the state
if (_location.base.valid) {
selectedLocation.guid = _location.base.guid;
if (selectedLocation.isAirportType) {
detailLoader.sourceComponent = airportDetails
} else {
detailLoader.sourceComponent = navaidDetails
}
} else if (_location.isBaseLatLon) {
detailLoader.sourceComponent = navaidDetails
} else {
_location.showHistoryInSearchModel();
}
}
Positioned {
@ -42,7 +57,6 @@ Item {
}
}
Component {
id: navaidDetails
LocationNavaidView {
@ -94,7 +108,7 @@ Item {
image: model.icon
}
Text {
StyledText {
id: delegateText
anchors.right: parent.right
anchors.left: delegateIcon.right
@ -131,11 +145,21 @@ Item {
}
}
Text {
id: headerText
text: qsTr("Location")
font.pixelSize: Style.headingFontPixelSize
anchors.left: parent.left
anchors.leftMargin: Style.inset
anchors.top: parent.top
anchors.topMargin: Style.margin
}
SearchButton {
id: searchButton
anchors.right: parent.right
anchors.top: parent.top
anchors.top: headerText.bottom
anchors.left: parent.left
anchors.margins: Style.margin
@ -151,7 +175,6 @@ Item {
var geod = _location.parseStringAsGeod(term)
if (geod.valid) {
console.info("REMOVE-ME: Setting lat-lon location")
_location.baseGeod = geod
selectedLocation.guid = 0;
detailLoader.sourceComponent = navaidDetails
@ -163,7 +186,7 @@ Item {
}
}
Text {
StyledText {
id: searchHelpText
anchors.right: parent.right
anchors.top: searchButton.bottom
@ -248,14 +271,12 @@ Item {
}
Button {
id: backButton
anchors { left: parent.left; top: parent.top; margins: Style.margin }
width: Style.strutSize
visible: detailLoader.visible
id: backButton
text: "< Back"
onClicked: {
root.backToSearch();
}
onClicked: root.backToSearch();
}
}

View file

@ -77,6 +77,7 @@ Item {
color: "white"
anchors.verticalCenter: parent.verticalCenter
font.bold: true
font.pixelSize: Style.subHeadingFontPixelSize
anchors.left: parent.left
anchors.leftMargin: Style.inset
}

View file

@ -13,6 +13,8 @@ SettingControl {
// value
property bool setIfDefault: false
implicitHeight: toggle.height + Style.margin + description.height
ToggleSwitch {
id: toggle
label: root.label
@ -37,4 +39,9 @@ SettingControl {
_config.setEnableDisableOption(option, checked)
}
}
function setValue(newValue)
{
toggle.setValue(newValue)
}
}

View file

@ -15,8 +15,8 @@ Item {
// define a new visiblity property so we can control built-in 'visible' ourselves
property bool hidden: false
implicitHeight: childrenRect.height
implicitWidth: parent.width // which is assumed to be the section
visible: {
// override so advanced items show up in searches
if (_launcher.isSearchActive && _launcher.matchesSearch(_launcher.settingsSearchTerm, keywords)) {
@ -86,8 +86,13 @@ Item {
var rawValue = _config.getValueForKey("", root.setting, defaultValue);
// console.warn("restoring state for " + root.setting + ", got raw value " + rawValue + " with type " + typeof(rawValue))
if (rawValue !== undefined) {
// root["value"] = rawValue
this.value = rawValue
setValue(rawValue);
}
}
function setValue(newValue)
{
// hook method so controls can override
this.value = newValue
}
}

View file

@ -3,7 +3,8 @@ import "."
SettingControl {
id: root
implicitHeight: childrenRect.height
implicitHeight: edit.height + Style.margin + description.height
property alias placeholder: edit.placeholder
property alias validation: edit.validator

View file

@ -5,7 +5,7 @@ import "."
SettingControl {
id: root
implicitHeight: childrenRect.height
implicitHeight: defaultButton.height + Style.margin + description.height
property alias label: label.text
property string path

View file

@ -3,6 +3,7 @@ import FlightGear.Launcher 1.0
import "."
Item {
id: settings
Rectangle {
// search 'dimming' rectangle
visible: _launcher.isSearchActive
@ -21,14 +22,21 @@ Item {
Connections {
target: _launcher
onRequestSaveState: {
mpSettings.saveState();
downloadSettings.saveState();
generalSettings.saveState();
renderSection.saveState();
extraArgsSection.saveState();
windowSettings.saveState();
}
onRequestSaveState: settings.saveState();
}
Component.onDestruction: {
settings.saveState();
}
function saveState()
{
mpSettings.saveState();
downloadSettings.saveState();
generalSettings.saveState();
renderSection.saveState();
extraArgsSection.saveState();
windowSettings.saveState();
}
Flickable {
@ -57,7 +65,7 @@ Item {
Text {
id: headerText
text: qsTr("Settings")
font.pixelSize: Style.strutSize / 2
font.pixelSize: Style.headingFontPixelSize
anchors.left: parent.left
anchors.leftMargin: Style.inset
}

View file

@ -13,7 +13,7 @@ SettingControl {
property alias value: popup.currentIndex
property alias defaultValue: root.defaultIndex
implicitHeight: childrenRect.height
implicitHeight: popup.height + Style.margin + description.height
PopupChoice {
id: popup

50
src/GUI/qml/Sidebar.qml Normal file
View file

@ -0,0 +1,50 @@
import QtQuick 2.4
import "."
Rectangle {
id: root
color: Style.themeColor
property alias pagesModel: buttonRepeater.model
property int selectedPage: 0
signal selectPage(var pageSource);
function setSelectedPage(index)
{
selectedPage = index
}
Column {
width: parent.width
anchors.top: parent.top
anchors.topMargin: Style.margin
anchors.bottom: flyButton.top
Repeater {
id: buttonRepeater
delegate: SidebarButton {
icon: model.iconPath
label: model.title
onClicked: {
root.selectedPage = model.index
root.selectPage(model.pageSource);
}
selected: (model.index === root.selectedPage)
}
}
}
SidebarButton {
id: flyButton
label: qsTr("Fly!")
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.margin
enabled: _launcher.canFly
icon: "qrc:///toolbox-fly"
onClicked: _launcher.fly();
}
}

View file

@ -0,0 +1,49 @@
import QtQuick 2.4
import "."
Item {
id: root
width: Style.strutSize * 2
height: Style.strutSize * 2
property alias icon: iconImage.source
property alias label: label.text
signal clicked()
property bool selected: false
property bool enabled: true
Rectangle {
anchors.fill: parent
visible: root.enabled & (root.selected | mouse.containsMouse)
color: Style.activeColor
}
Image {
id: iconImage
anchors.centerIn: parent
opacity: root.enabled ? 1.0 : 0.5
}
Text {
id: label
color: "white"
// enabled appearance is done via opacity to match the icon
opacity: root.enabled ? 1.0 : 0.5
width: parent.width
horizontalAlignment: Text.AlignHCenter
anchors.top: iconImage.bottom
anchors.topMargin: Style.margin
}
MouseArea {
id: mouse
enabled: root.enabled
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked();
}
}

View file

@ -5,6 +5,9 @@ import "."
Item {
id: root
signal showSelectedAircraft();
signal showSelectedLocation();
Rectangle {
anchors.fill: parent
color: "#7f7f7f"
@ -12,19 +15,6 @@ Item {
readonly property string __aircraftDescription: _launcher.selectedAircraftInfo.description
// base image when preview not available
Rectangle {
anchors.fill: parent
color: "magenta"
}
Connections {
target: _launcher
onAircraftTypeChanged: {
console.info("Aircraft type is now:" + _launcher.aircraftType)
}
}
PreviewImage {
id: preview
anchors.centerIn: parent
@ -101,6 +91,7 @@ Item {
style: Text.Outline
styleColor: "black"
font.bold: true
font.pixelSize: Style.subHeadingFontPixelSize
onClicked: {
_launcher.launchUrl("http://home.flightgear.org/about/");
@ -114,6 +105,7 @@ Item {
color: "transparent"
border.width: 1
border.color: Style.frameColor
clip: true
anchors {
left: parent.left
@ -149,7 +141,7 @@ Item {
}
// aircraft name row
Text {
StyledText {
text: qsTr("Aircraft:")
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.headingFontPixelSize
@ -157,10 +149,13 @@ Item {
// TODO - make clickable, jump to to the aircraft in the installed
// aircraft list
Text {
ClickableText {
text: _launcher.selectedAircraftInfo.name === "" ?
qsTr("No aircraft selected") : _launcher.selectedAircraftInfo.name
enabled: _launcher.selectedAircraftInfo.name !== ""
font.pixelSize: Style.headingFontPixelSize
onClicked: root.showSelectedAircraft();
}
HistoryPopup {
@ -189,7 +184,7 @@ Item {
maximumSize.height: 128
}
Text {
StyledText {
id: aircraftDescriptionText
anchors {
left: thumbnail.right
@ -229,7 +224,7 @@ Item {
width: parent.width
}
Text {
StyledText {
id: stateDescriptionText
wrapMode: Text.WordWrap
maximumLineCount: 5
@ -264,18 +259,18 @@ Item {
}
// location summary row
Text {
StyledText {
id: locationLabel
text: qsTr("Location:")
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.headingFontPixelSize
}
// TODO - make clickable, jump to the location page
Text {
ClickableText {
text: _launcher.location.description
font.pixelSize: Style.headingFontPixelSize
width: summaryGrid.middleColumnWidth
onClicked: root.showSelectedLocation()
}
HistoryPopup {
@ -288,13 +283,13 @@ Item {
}
// settings summary row
Text {
StyledText {
text: qsTr("Settings:")
horizontalAlignment: Text.AlignRight
font.pixelSize: Style.headingFontPixelSize
}
Text {
StyledText {
text: _launcher.combinedSummary.join(", ")
font.pixelSize: Style.headingFontPixelSize
wrapMode: Text.WordWrap

View file

@ -9,6 +9,14 @@ Item {
implicitWidth: track.width + label.width + 16
implicitHeight: Math.max(label.height, thumb.height)
// helepr to set the value without an animation
function setValue(newCheck)
{
sliderBehaviour.enabled = false;
checked = newCheck
sliderBehaviour.enabled = true;
}
Rectangle {
id: track
width: height * 2
@ -34,6 +42,7 @@ Item {
x: checked ? parent.width - (track.radius + radius) : (track.radius - radius)
Behavior on x {
id: sliderBehaviour
NumberAnimation {
duration: 250
}

View file

@ -0,0 +1,41 @@
import QtQuick 2.4
import FlightGear.Launcher 1.0
import "."
Item {
Flickable {
id: flick
anchors {
left: parent.left
right: scrollbar.right
top: parent.top
bottom: parent.bottom
margins: Style.margin
}
contentHeight: contents.implicitHeight
TextEdit {
id: contents
width: parent.width
selectByMouse: true
textFormat: TextEdit.RichText
readOnly: true
wrapMode: TextEdit.Wrap
text: _config.htmlForCommandLine();
}
}
Scrollbar {
id: scrollbar
anchors.right: parent.right
height: flick.height
flickable: flick
visible: flick.contentHeight > flick.height
}
}

View file

@ -94,6 +94,11 @@
<file>qml/IntegerSpinbox.qml</file>
<file>qml/DoubleSpinbox.qml</file>
<file>qml/StyledText.qml</file>
<file>qml/Launcher.qml</file>
<file>qml/Sidebar.qml</file>
<file>qml/SidebarButton.qml</file>
<file>qml/ViewCommandLine.qml</file>
<file>qml/AddOnsHeader.qml</file>
</qresource>
<qresource prefix="/preview">
<file alias="close-icon">preview-close.png</file>