Launcher: UI feedback for hangar migrations
Add pop-up notification when we migrate to a new version of a hangar. Convert the ‘new version available’ message to use a pop-up notification as well.
This commit is contained in:
parent
c71f281a58
commit
df42c6f026
18 changed files with 580 additions and 65 deletions
|
@ -132,6 +132,10 @@ if (HAVE_QT)
|
|||
FavouriteAircraftData.hxx
|
||||
UpdateChecker.cxx
|
||||
UpdateChecker.hxx
|
||||
LauncherPackageDelegate.hxx
|
||||
LauncherPackageDelegate.cxx
|
||||
LauncherNotificationsController.hxx
|
||||
LauncherNotificationsController.cxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
#include <QQmlFileSelector>
|
||||
|
||||
// launcher headers
|
||||
#include "QtLauncher.hxx"
|
||||
#include "AddOnsController.hxx"
|
||||
#include "AircraftItemModel.hxx"
|
||||
#include "AircraftModel.hxx"
|
||||
#include "DefaultAircraftLocator.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
#include "LocalAircraftCache.hxx"
|
||||
#include "LauncherController.hxx"
|
||||
#include "LauncherNotificationsController.hxx"
|
||||
#include "LauncherPackageDelegate.hxx"
|
||||
#include "LocalAircraftCache.hxx"
|
||||
#include "LocationController.hxx"
|
||||
#include "QtLauncher.hxx"
|
||||
#include "UpdateChecker.hxx"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -80,6 +82,8 @@ LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
|
|||
connect(qa, &QAction::triggered, m_controller, &LauncherController::quit);
|
||||
}
|
||||
|
||||
connect(this, &QQuickView::statusChanged, this, &LauncherMainWindow::onQuickStatusChanged);
|
||||
|
||||
m_controller->initialRestoreSettings();
|
||||
|
||||
////////////
|
||||
|
@ -103,6 +107,12 @@ LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
|
|||
auto updater = new UpdateChecker(this);
|
||||
ctx->setContextProperty("_updates", updater);
|
||||
|
||||
auto packageDelegate = new LauncherPackageDelegate(this);
|
||||
ctx->setContextProperty("_packages", packageDelegate);
|
||||
|
||||
auto notifications = new LauncherNotificationsController{this, engine()};
|
||||
ctx->setContextProperty("_notifications", notifications);
|
||||
|
||||
if (!inSimMode) {
|
||||
auto addOnsCtl = new AddOnsController(this, m_controller->config());
|
||||
ctx->setContextProperty("_addOns", addOnsCtl);
|
||||
|
@ -120,25 +130,22 @@ LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
|
|||
setSource(QUrl("qrc:///qml/Launcher.qml"));
|
||||
}
|
||||
|
||||
#if 0
|
||||
void LauncherMainWindow::onQuickStatusChanged(QQuickWidget::Status status)
|
||||
void LauncherMainWindow::onQuickStatusChanged(QQuickView::Status status)
|
||||
{
|
||||
if (status == QQuickWidget::Error) {
|
||||
QQuickWidget* qw = qobject_cast<QQuickWidget*>(sender());
|
||||
if (status == QQuickView::Error) {
|
||||
QString errorString;
|
||||
|
||||
Q_FOREACH(auto err, qw->errors()) {
|
||||
Q_FOREACH (auto err, errors()) {
|
||||
errorString.append("\n" + err.toString());
|
||||
}
|
||||
|
||||
QMessageBox::critical(this, "UI loading failures.",
|
||||
tr("Problems occurred loading the user interface. This is often due to missing modules on your system. "
|
||||
QMessageBox::critical(nullptr, "UI loading failures.",
|
||||
tr("Problems occurred loading the user interface. This is usually due to missing modules on your system. "
|
||||
"Please report this error to the FlightGear developer list or forum, and take care to mention your system "
|
||||
"distribution, etc. Please also include the information provided below.\n")
|
||||
+ errorString);
|
||||
"distribution, etc. Please also include the information provided below.\n") +
|
||||
errorString);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LauncherMainWindow::~LauncherMainWindow()
|
||||
{
|
||||
|
|
|
@ -48,6 +48,10 @@ public:
|
|||
bool wasRejected();
|
||||
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void onQuickStatusChanged(QQuickView::Status status);
|
||||
|
||||
private:
|
||||
LauncherController* m_controller;
|
||||
};
|
||||
|
|
167
src/GUI/LauncherNotificationsController.cxx
Normal file
167
src/GUI/LauncherNotificationsController.cxx
Normal file
|
@ -0,0 +1,167 @@
|
|||
// Written by James Turner, started October 2020
|
||||
//
|
||||
// Copyright (C) 2020 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
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "LauncherNotificationsController.hxx"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QDebug>
|
||||
#include <QQmlEngine>
|
||||
#include <QSettings>
|
||||
|
||||
static LauncherNotificationsController* static_instance = nullptr;
|
||||
|
||||
namespace {
|
||||
|
||||
const int IdRole = Qt::UserRole + 1;
|
||||
const int SourceRole = Qt::UserRole + 2;
|
||||
const int ArgsRole = Qt::UserRole + 3;
|
||||
|
||||
} // namespace
|
||||
|
||||
class LauncherNotificationsController::NotificationsModel : public QAbstractListModel
|
||||
{
|
||||
public:
|
||||
int rowCount(const QModelIndex&) const override
|
||||
{
|
||||
return static_cast<int>(_data.size());
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override
|
||||
{
|
||||
const int row = index.row();
|
||||
if ((row < 0) || (row >= static_cast<int>(_data.size()))) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& d = _data.at(row);
|
||||
switch (role) {
|
||||
case IdRole: return d.id;
|
||||
case SourceRole: return d.source;
|
||||
case ArgsRole: return QVariant::fromValue(d.args);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override
|
||||
{
|
||||
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
||||
result[IdRole] = "id";
|
||||
result[SourceRole] = "source";
|
||||
result[ArgsRole] = "args";
|
||||
return result;
|
||||
}
|
||||
|
||||
void removeIndex(int index)
|
||||
{
|
||||
beginRemoveRows({}, index, index);
|
||||
_data.erase(_data.begin() + index);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void append(QString id, QUrl source, QJSValue args)
|
||||
{
|
||||
const int newRow = static_cast<int>(_data.size());
|
||||
beginInsertRows({}, newRow, newRow);
|
||||
_data.push_back({id, source, args});
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
struct Data {
|
||||
QString id;
|
||||
QUrl source;
|
||||
QJSValue args;
|
||||
};
|
||||
|
||||
std::vector<Data> _data;
|
||||
};
|
||||
|
||||
LauncherNotificationsController::LauncherNotificationsController(QObject* pr, QQmlEngine* engine) : QObject(pr)
|
||||
{
|
||||
Q_ASSERT(static_instance == nullptr);
|
||||
static_instance = this;
|
||||
|
||||
_model = new NotificationsModel;
|
||||
|
||||
_qmlEngine = engine;
|
||||
}
|
||||
|
||||
LauncherNotificationsController::~LauncherNotificationsController()
|
||||
{
|
||||
static_instance = nullptr;
|
||||
}
|
||||
|
||||
LauncherNotificationsController* LauncherNotificationsController::instance()
|
||||
{
|
||||
return static_instance;
|
||||
}
|
||||
|
||||
QAbstractItemModel* LauncherNotificationsController::notifications() const
|
||||
{
|
||||
return _model;
|
||||
}
|
||||
|
||||
QJSValue LauncherNotificationsController::argsForIndex(int index) const
|
||||
{
|
||||
if ((index < 0) || (index >= static_cast<int>(_model->_data.size()))) {
|
||||
return {};
|
||||
}
|
||||
const auto& d = _model->_data.at(index);
|
||||
qDebug() << Q_FUNC_INFO << index;
|
||||
return d.args;
|
||||
}
|
||||
|
||||
QJSEngine* LauncherNotificationsController::jsEngine()
|
||||
{
|
||||
return _qmlEngine;
|
||||
}
|
||||
|
||||
void LauncherNotificationsController::dismissIndex(int index)
|
||||
{
|
||||
const auto& d = _model->_data.at(index);
|
||||
|
||||
// if the notificsation supports persistent dismissal, then record this
|
||||
// fact in the global settings, so we don't show it again.
|
||||
// restore defaults will of course clear these settings, but that's
|
||||
// desirable anyway.
|
||||
if (d.args.property("persistent-dismiss").toBool()) {
|
||||
QSettings settings;
|
||||
settings.beginGroup("dismissed-notifications");
|
||||
settings.setValue(d.id, true);
|
||||
}
|
||||
|
||||
_model->removeIndex(index);
|
||||
}
|
||||
|
||||
void LauncherNotificationsController::postNotification(QString id, QUrl source, QJSValue args)
|
||||
{
|
||||
const bool supportsPersistentDismiss = args.property("persistent-dismiss").toBool();
|
||||
if (supportsPersistentDismiss) {
|
||||
QSettings settings;
|
||||
settings.beginGroup("dismissed-notifications");
|
||||
bool alreadyDimissed = settings.value(id).toBool();
|
||||
if (alreadyDimissed) {
|
||||
qWarning() << "Skipping notification" << id << ", was previousl dimissed by user";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_model->append(id, source, args);
|
||||
}
|
59
src/GUI/LauncherNotificationsController.hxx
Normal file
59
src/GUI/LauncherNotificationsController.hxx
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Written by James Turner, started October 2020
|
||||
//
|
||||
// Copyright (C) 2020 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
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJSValue>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
// forward decls
|
||||
class QAbstractItemModel;
|
||||
class QJSEngine;
|
||||
class QQmlEngine;
|
||||
|
||||
class LauncherNotificationsController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QAbstractItemModel* active READ notifications CONSTANT)
|
||||
public:
|
||||
LauncherNotificationsController(QObject* pr, QQmlEngine* qmlEngine);
|
||||
~LauncherNotificationsController();
|
||||
|
||||
static LauncherNotificationsController* instance();
|
||||
|
||||
QAbstractItemModel* notifications() const;
|
||||
|
||||
Q_INVOKABLE QJSValue argsForIndex(int index) const;
|
||||
|
||||
QJSEngine* jsEngine();
|
||||
public slots:
|
||||
void dismissIndex(int index);
|
||||
|
||||
void postNotification(QString id, QUrl source, QJSValue args = {});
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
private:
|
||||
class NotificationsModel;
|
||||
|
||||
NotificationsModel* _model = nullptr;
|
||||
QQmlEngine* _qmlEngine = nullptr;
|
||||
};
|
73
src/GUI/LauncherPackageDelegate.cxx
Normal file
73
src/GUI/LauncherPackageDelegate.cxx
Normal file
|
@ -0,0 +1,73 @@
|
|||
|
||||
// Copyright (C) 2020 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
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "LauncherPackageDelegate.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJSEngine>
|
||||
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Network/HTTPClient.hxx>
|
||||
|
||||
#include "LauncherNotificationsController.hxx"
|
||||
|
||||
LauncherPackageDelegate::LauncherPackageDelegate(QObject* parent) : QObject(parent)
|
||||
{
|
||||
globals->packageRoot()->addDelegate(this);
|
||||
const auto http = globals->get_subsystem<FGHTTPClient>();
|
||||
_defaultCatalogId = http->getDefaultCatalogId();
|
||||
}
|
||||
|
||||
LauncherPackageDelegate::~LauncherPackageDelegate()
|
||||
{
|
||||
globals->packageRoot()->removeDelegate(this);
|
||||
}
|
||||
|
||||
void LauncherPackageDelegate::catalogRefreshed(simgear::pkg::CatalogRef aCatalog, simgear::pkg::Delegate::StatusCode aReason)
|
||||
{
|
||||
if ((aReason != Delegate::STATUS_REFRESHED) || !aCatalog) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto nc = LauncherNotificationsController::instance();
|
||||
|
||||
if (aCatalog->migratedFrom() != simgear::pkg::CatalogRef{}) {
|
||||
QJSValue args = nc->jsEngine()->newObject();
|
||||
|
||||
args.setProperty("newCatalogName", QString::fromStdString(aCatalog->name()));
|
||||
|
||||
if (aCatalog->id() == _defaultCatalogId) {
|
||||
nc->postNotification("did-migrate-official-catalog-to-" + QString::fromStdString(_defaultCatalogId),
|
||||
QUrl{"qrc:///qml/DidMigrateOfficialCatalogNotification.qml"},
|
||||
args);
|
||||
} else {
|
||||
nc->postNotification("did-migrate-catalog-to-" + QString::fromStdString(aCatalog->id()),
|
||||
QUrl{"qrc:///qml/DidMigrateOtherCatalogNotification.qml"},
|
||||
args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherPackageDelegate::finishInstall(simgear::pkg::InstallRef ref, simgear::pkg::Delegate::StatusCode status)
|
||||
{
|
||||
Q_UNUSED(ref)
|
||||
Q_UNUSED(status)
|
||||
}
|
37
src/GUI/LauncherPackageDelegate.hxx
Normal file
37
src/GUI/LauncherPackageDelegate.hxx
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef LAUNCHERPACKAGEDELEGATE_HXX
|
||||
#define LAUNCHERPACKAGEDELEGATE_HXX
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
class LauncherPackageDelegate : public QObject,
|
||||
public simgear::pkg::Delegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LauncherPackageDelegate(QObject* parent = nullptr);
|
||||
~LauncherPackageDelegate();
|
||||
|
||||
protected:
|
||||
void catalogRefreshed(simgear::pkg::CatalogRef aCatalog, StatusCode aReason) override;
|
||||
|
||||
// mandatory overrides, not actually needed here.
|
||||
void startInstall(simgear::pkg::InstallRef) override {}
|
||||
void installProgress(simgear::pkg::InstallRef, unsigned int, unsigned int) override{};
|
||||
void finishInstall(simgear::pkg::InstallRef ref, StatusCode status) override;
|
||||
|
||||
signals:
|
||||
void didMigrateOfficialHangarChanged();
|
||||
|
||||
private:
|
||||
std::string _defaultCatalogId;
|
||||
};
|
||||
|
||||
#endif // LAUNCHERPACKAGEDELEGATE_HXX
|
|
@ -135,6 +135,7 @@ void UpdateChecker::receivedUpdateXML(QByteArray body)
|
|||
const auto s = body.toStdString();
|
||||
|
||||
QSettings settings;
|
||||
auto nc = LauncherNotificationsController::instance();
|
||||
|
||||
try {
|
||||
const char* buffer = s.c_str();
|
||||
|
@ -156,6 +157,8 @@ void UpdateChecker::receivedUpdateXML(QByteArray body)
|
|||
m_updateUri = QUrl(QString::fromStdString(newVersionUri));
|
||||
emit statusChanged(m_status);
|
||||
|
||||
nc->postNotification("flightgear-update-major", QUrl{"qrc:///qml/NewVersionNotification.qml"});
|
||||
|
||||
return; // don't consider minor updates
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +175,8 @@ void UpdateChecker::receivedUpdateXML(QByteArray body)
|
|||
const std::string newVersionUri = props->getStringValue("download-uri");
|
||||
m_updateUri = QUrl(QString::fromStdString(newVersionUri));
|
||||
emit statusChanged(m_status);
|
||||
|
||||
nc->postNotification("flightgear-update-point", QUrl{"qrc:///qml/NewVersionNotification.qml"});
|
||||
}
|
||||
}
|
||||
} catch (const sg_exception &e) {
|
||||
|
|
BIN
src/GUI/assets/white-cross-icon.png
Normal file
BIN
src/GUI/assets/white-cross-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 290 B |
21
src/GUI/qml/DidMigrateOfficialCatalogNotification.qml
Normal file
21
src/GUI/qml/DidMigrateOfficialCatalogNotification.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Text {
|
||||
signal dismiss();
|
||||
|
||||
readonly property string updateAllLink: "\"launcher:update-all\"";
|
||||
readonly property string newName: _notifications.argsForIndex(model.index).newCatalogName
|
||||
|
||||
text: qsTr("An updated version of the official aircraft hangar '%2' was automatically installed. " +
|
||||
"Existing aircraft have been marked for update, <a href=%1>click here to update them all</a>").arg(updateAllLink).arg(newName)
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
color: "white"
|
||||
|
||||
onLinkActivated: {
|
||||
_launcher.requestUpdateAllAircraft();
|
||||
dismiss(); // request our dismissal
|
||||
}
|
||||
}
|
21
src/GUI/qml/DidMigrateOtherCatalogNotification.qml
Normal file
21
src/GUI/qml/DidMigrateOtherCatalogNotification.qml
Normal file
|
@ -0,0 +1,21 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Text {
|
||||
signal dismiss();
|
||||
|
||||
readonly property string updateAllLink: "\"launcher:update-all\"";
|
||||
readonly property string newName: _notifications.argsForIndex(model.index).newCatalogName
|
||||
|
||||
text: qsTr("An updated version of the hangar '%2' was automatically installed. " +
|
||||
"Existing aircraft have been marked for update, <a href=%1>click here to update them all</a>").arg(updateAllLink).arg(newName)
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
color: "white"
|
||||
|
||||
onLinkActivated: {
|
||||
_launcher.requestUpdateAllAircraft();
|
||||
dismiss(); // request our dismissal
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import QtQuick 2.4
|
||||
import QtQml 2.4
|
||||
import FlightGear 1.0
|
||||
import "."
|
||||
|
||||
|
@ -149,6 +150,18 @@ Item {
|
|||
source: "qrc:///qml/Summary.qml"
|
||||
}
|
||||
|
||||
NotificationArea {
|
||||
id: notifications
|
||||
// only show on the summary page
|
||||
visible: sidebar.selectedPage === 0
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
function selectPage(index)
|
||||
{
|
||||
sidebar.setSelectedPage(index);
|
||||
|
|
28
src/GUI/qml/NewVersionNotification.qml
Normal file
28
src/GUI/qml/NewVersionNotification.qml
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import "."
|
||||
|
||||
ClickableText {
|
||||
signal dismiss();
|
||||
|
||||
text: msg.arg(_updates.updateVersion)
|
||||
readonly property string msg: (_updates.status == UpdateChecker.MajorUpdate) ?
|
||||
qsTr("A new release of FlightGear is available (%1): click for more information")
|
||||
: qsTr("Updated version %1 is available: click here to download")
|
||||
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Style.subHeadingFontPixelSize
|
||||
color: "white"
|
||||
|
||||
onClicked: {
|
||||
_launcher.launchUrl(_updates.updateUri);
|
||||
}
|
||||
|
||||
function dismissed()
|
||||
{
|
||||
_updates.ignoreUpdate();
|
||||
}
|
||||
}
|
||||
|
111
src/GUI/qml/NotificationArea.qml
Normal file
111
src/GUI/qml/NotificationArea.qml
Normal file
|
@ -0,0 +1,111 @@
|
|||
import QtQuick 2.4
|
||||
import QtQml 2.4
|
||||
|
||||
import FlightGear.Launcher 1.0
|
||||
import ".."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 500
|
||||
|
||||
readonly property int ourMargin: Style.margin * 2
|
||||
|
||||
|
||||
Component {
|
||||
id: notificationBox
|
||||
|
||||
Rectangle {
|
||||
id: boxRoot
|
||||
|
||||
property alias content: contentLoader.sourceComponent
|
||||
|
||||
width: notificationsColumn.width
|
||||
height: contentLoader.height + (ourMargin * 2)
|
||||
|
||||
clip: true
|
||||
color: Style.themeColor
|
||||
border.width: 1
|
||||
border.color: Qt.darker(Style.themeColor)
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
opacity: Style.panelOpacity
|
||||
color: "white"
|
||||
}
|
||||
|
||||
Loader {
|
||||
// height is not anchored, can float
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: closeButton.left
|
||||
margins: ourMargin
|
||||
}
|
||||
|
||||
id: contentLoader
|
||||
source: model.source
|
||||
|
||||
// don't set height, comes from content
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: contentLoader.item
|
||||
onDismiss: {
|
||||
_notifications.dismissIndex(model.index)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: closeButton
|
||||
source: "qrc:///white-delete-icon"
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
margins: ourMargin
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
if (contentLoader.item.dismissed) {
|
||||
contentLoader.item.dismissed();
|
||||
}
|
||||
|
||||
_notifications.dismissIndex(model.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of notification box
|
||||
}
|
||||
|
||||
// ensure clicks 'near' the notifications don't go to other UI
|
||||
MouseArea {
|
||||
width: parent.width
|
||||
height: notificationsColumn.height
|
||||
}
|
||||
|
||||
Column {
|
||||
id: notificationsColumn
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
margins: Style.strutSize
|
||||
}
|
||||
|
||||
spacing: Style.strutSize
|
||||
|
||||
Repeater {
|
||||
model: _notifications.active
|
||||
delegate: notificationBox
|
||||
}
|
||||
} // of boxes column
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import QtQuick 2.4
|
||||
import QtQml 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import "."
|
||||
|
||||
|
@ -77,54 +78,7 @@ Item {
|
|||
styleColor: "black"
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.margin
|
||||
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
|
||||
|
||||
id: updateMessage
|
||||
|
||||
visible: _updates.status != UpdateChecker.NoUpdate
|
||||
|
||||
readonly property string msg: (_updates.status == UpdateChecker.MajorUpdate) ?
|
||||
qsTr("A new release of FlightGear is available (%1): click for more information")
|
||||
: qsTr("Updated version %1 is available: click here to download")
|
||||
|
||||
ClickableText {
|
||||
text: parent.msg.arg(_updates.updateVersion)
|
||||
baseTextColor: "white"
|
||||
style: Text.Outline
|
||||
styleColor: "black"
|
||||
font.bold: true
|
||||
font.pixelSize: Style.headingFontPixelSize
|
||||
|
||||
onClicked: {
|
||||
_launcher.launchUrl(_updates.updateUri);
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText {
|
||||
text: qsTr("(or click to ignore this)")
|
||||
baseTextColor: "white"
|
||||
style: Text.Outline
|
||||
styleColor: "black"
|
||||
font.bold: true
|
||||
font.pixelSize: Style.headingFontPixelSize
|
||||
|
||||
onClicked: {
|
||||
_updates.ignoreUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText {
|
||||
visible: !updateMessage.visible
|
||||
anchors {
|
||||
left: logoText.left
|
||||
right: logoText.right
|
||||
|
@ -360,5 +314,5 @@ Item {
|
|||
width: 1; height: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of summary box
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
<file>qml/LocationAltitudeRow.qml</file>
|
||||
<file>qml/CatalogDelegate.qml</file>
|
||||
<file alias="clear-text-icon">assets/icons8-clear-symbol-26.png</file>
|
||||
<file alias="white-delete-icon">assets/white-cross-icon.png</file>
|
||||
<file>qml/FlightPlan.qml</file>
|
||||
<file>qml/PlainTextEditBox.qml</file>
|
||||
<file>qml/HeaderBox.qml</file>
|
||||
|
@ -136,6 +137,11 @@
|
|||
<file alias="favourite-icon-outline">assets/icons8-christmas-star-outline.png</file>
|
||||
<file>qml/FirstRun.qml</file>
|
||||
<file>qml/HelpSupport.qml</file>
|
||||
<file>qml/NotificationArea.qml</file>
|
||||
<file>qml/NewVersionNotification.qml</file>
|
||||
<file>qml/DidMigrateOfficialCatalogNotification.qml</file>
|
||||
<file>qml/DidMigrateOtherCatalogNotification.qml</file>
|
||||
|
||||
</qresource>
|
||||
<qresource prefix="/preview">
|
||||
<file alias="close-icon">assets/preview-close.png</file>
|
||||
|
|
|
@ -124,12 +124,15 @@ bool FGHTTPClient::isDefaultCatalogInstalled() const
|
|||
return getDefaultCatalog().valid();
|
||||
}
|
||||
|
||||
void FGHTTPClient::addDefaultCatalog()
|
||||
pkg::CatalogRef FGHTTPClient::addDefaultCatalog()
|
||||
{
|
||||
pkg::CatalogRef defaultCatalog = getDefaultCatalog();
|
||||
if (!defaultCatalog.valid()) {
|
||||
pkg::Catalog::createFromUrl(globals->packageRoot(), getDefaultCatalogUrl());
|
||||
auto cat = pkg::Catalog::createFromUrl(globals->packageRoot(), getDefaultCatalogUrl());
|
||||
return cat;
|
||||
}
|
||||
|
||||
return defaultCatalog;
|
||||
}
|
||||
|
||||
std::string FGHTTPClient::getDefaultCatalogId() const
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
#ifndef FG_HTTP_CLIENT_HXX
|
||||
#define FG_HTTP_CLIENT_HXX
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class FGHTTPClient : public SGSubsystem
|
||||
|
@ -46,7 +48,7 @@ public:
|
|||
simgear::HTTP::Client const* client() const { return _http.get(); }
|
||||
|
||||
bool isDefaultCatalogInstalled() const;
|
||||
void addDefaultCatalog();
|
||||
simgear::pkg::CatalogRef addDefaultCatalog();
|
||||
|
||||
std::string getDefaultCatalogId() const;
|
||||
std::string getDefaultCatalogUrl() const;
|
||||
|
|
Loading…
Reference in a new issue