Launcher: favourite aircraft support
This commit is contained in:
parent
3060cf10c6
commit
2904321959
16 changed files with 248 additions and 15 deletions
|
@ -23,6 +23,7 @@
|
|||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
#include <QSharedPointer>
|
||||
#include <QSettings>
|
||||
|
||||
// Simgear
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
@ -157,6 +158,8 @@ AircraftItemModel::AircraftItemModel(QObject* pr) :
|
|||
this, &AircraftItemModel::onScanAddedItems);
|
||||
connect(cache, &LocalAircraftCache::cleared,
|
||||
this, &AircraftItemModel::onLocalCacheCleared);
|
||||
|
||||
loadFavourites();
|
||||
}
|
||||
|
||||
AircraftItemModel::~AircraftItemModel()
|
||||
|
@ -238,6 +241,12 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
|||
return m_delegateStates.at(row).variant;
|
||||
}
|
||||
|
||||
if (role == AircraftIsFavouriteRole) {
|
||||
// recursive call here, hope that's okay
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
return m_favourites.contains(uri);
|
||||
}
|
||||
|
||||
if (row >= m_cachedLocalAircraftCount) {
|
||||
quint32 packageIndex = static_cast<quint32>(row - m_cachedLocalAircraftCount);
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
|
@ -421,6 +430,18 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
m_delegateStates[row].variant = newValue;
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
} else if (role == AircraftIsFavouriteRole) {
|
||||
bool f = value.toBool();
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
const auto cur = m_favourites.contains(uri);
|
||||
if (f && !cur) {
|
||||
m_favourites.append(uri);
|
||||
} else if (!f && cur) {
|
||||
m_favourites.removeOne(uri);
|
||||
}
|
||||
|
||||
saveFavourites();
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -441,6 +462,7 @@ QHash<int, QByteArray> AircraftItemModel::roleNames() const
|
|||
|
||||
result[AircraftInstallDownloadedSizeRole] = "downloadedBytes";
|
||||
result[AircraftVariantRole] = "activeVariant";
|
||||
result[AircraftIsFavouriteRole] = "favourite";
|
||||
|
||||
result[AircraftStatusRole] = "aircraftStatus";
|
||||
result[AircraftMinVersionRole] = "requiredFGVersion";
|
||||
|
@ -560,7 +582,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
QString ident = uri.path();
|
||||
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
|
||||
if (pkg) {
|
||||
int variantIndex = pkg->indexOfVariant(ident.toStdString());
|
||||
const auto variantIndex = pkg->indexOfVariant(ident.toStdString());
|
||||
return QString::fromStdString(pkg->nameForVariant(variantIndex));
|
||||
}
|
||||
} else {
|
||||
|
@ -572,7 +594,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
|
||||
void AircraftItemModel::onScanAddedItems(int addedCount)
|
||||
{
|
||||
Q_UNUSED(addedCount);
|
||||
Q_UNUSED(addedCount)
|
||||
const auto items = LocalAircraftCache::instance()->allItems();
|
||||
const int newItemCount = items.size() - m_cachedLocalAircraftCount;
|
||||
const int firstRow = m_cachedLocalAircraftCount;
|
||||
|
@ -631,7 +653,7 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
|||
return true; // local file, always runnable
|
||||
}
|
||||
|
||||
quint32 packageIndex = index.row() - m_cachedLocalAircraftCount;
|
||||
quint32 packageIndex = static_cast<quint32>(index.row() - m_cachedLocalAircraftCount);
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
if (!ex.valid()) {
|
||||
|
@ -641,4 +663,21 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
|||
return !ex->isDownloading();
|
||||
}
|
||||
|
||||
void AircraftItemModel::loadFavourites()
|
||||
{
|
||||
m_favourites.clear();
|
||||
QSettings settings;
|
||||
Q_FOREACH(auto v, settings.value("favourite-aircraft").toList()) {
|
||||
m_favourites.append(v.toUrl());
|
||||
}
|
||||
}
|
||||
|
||||
void AircraftItemModel::saveFavourites()
|
||||
{
|
||||
QVariantList favs;
|
||||
Q_FOREACH(auto u, m_favourites) {
|
||||
favs.append(u);
|
||||
}
|
||||
QSettings settings;
|
||||
settings.setValue("favourite-aircraft", favs);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ const int AircraftURIRole = Qt::UserRole + 14;
|
|||
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
|
||||
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
|
||||
const int AircraftPackageRefRole = Qt::UserRole + 19;
|
||||
const int AircraftIsFavouriteRole = Qt::UserRole + 20;
|
||||
|
||||
const int AircraftStatusRole = Qt::UserRole + 22;
|
||||
const int AircraftMinVersionRole = Qt::UserRole + 23;
|
||||
|
@ -145,14 +146,20 @@ private:
|
|||
void installSucceeded(QModelIndex index);
|
||||
void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
|
||||
|
||||
void loadFavourites();
|
||||
void saveFavourites();
|
||||
|
||||
private:
|
||||
PackageDelegate* m_delegate = nullptr;
|
||||
|
||||
QVector<DelegateState> m_delegateStates;
|
||||
|
||||
simgear::pkg::RootRef m_packageRoot;
|
||||
simgear::pkg::PackageList m_packages;
|
||||
|
||||
|
||||
QVector<QUrl> m_favourites;
|
||||
int m_cachedLocalAircraftCount = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // of FG_GUI_AIRCRAFT_MODEL
|
||||
|
|
|
@ -14,6 +14,8 @@ AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source)
|
|||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
|
||||
|
||||
// important we sort on the primary name role and not Qt::DisplayRole
|
||||
// otherwise the aircraft jump when switching variant
|
||||
setSortRole(AircraftVariantDescriptionRole);
|
||||
|
@ -113,6 +115,15 @@ void AircraftProxyModel::setHaveUpdateFilterEnabled(bool e)
|
|||
invalidate();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setShowFavourites(bool e)
|
||||
{
|
||||
if (e == m_onlyShowFavourites)
|
||||
return;
|
||||
|
||||
m_onlyShowFavourites = e;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
@ -145,6 +156,11 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||
}
|
||||
}
|
||||
|
||||
if (m_onlyShowFavourites) {
|
||||
if (!index.data(AircraftIsFavouriteRole).toBool())
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there is no search active, i.e we are browsing, we might apply the
|
||||
// ratings filter.
|
||||
if (m_filterString.isEmpty() && !m_onlyShowInstalled && m_ratingsFilter) {
|
||||
|
|
|
@ -60,6 +60,8 @@ public slots:
|
|||
void setInstalledFilterEnabled(bool e);
|
||||
|
||||
void setHaveUpdateFilterEnabled(bool e);
|
||||
|
||||
void setShowFavourites(bool e);
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
|
@ -69,6 +71,7 @@ private:
|
|||
bool m_ratingsFilter = true;
|
||||
bool m_onlyShowInstalled = false;
|
||||
bool m_onlyShowWithUpdate = false;
|
||||
bool m_onlyShowFavourites = false;
|
||||
|
||||
QList<int> m_ratings;
|
||||
QString m_filterString;
|
||||
|
|
|
@ -172,6 +172,8 @@ if (HAVE_QT)
|
|||
RouteDiagram.hxx
|
||||
ModelDataExtractor.cxx
|
||||
ModelDataExtractor.hxx
|
||||
HoverArea.cxx
|
||||
HoverArea.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
|
|
33
src/GUI/HoverArea.cxx
Normal file
33
src/GUI/HoverArea.cxx
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "HoverArea.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QQuickWindow>
|
||||
|
||||
HoverArea::HoverArea()
|
||||
{
|
||||
connect(this, &QQuickItem::windowChanged, [this](QQuickWindow* win) {
|
||||
if (win) {
|
||||
win->installEventFilter(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool HoverArea::eventFilter(QObject *sender, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(sender)
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
QMouseEvent* me = static_cast<QMouseEvent*>(event);
|
||||
const auto local = mapFromScene(me->pos());
|
||||
const bool con = contains(local);
|
||||
if (con != m_containsMouse) {
|
||||
m_containsMouse = con;
|
||||
emit containsMouseChanged(con);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
31
src/GUI/HoverArea.hxx
Normal file
31
src/GUI/HoverArea.hxx
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef HOVERAREA_HXX
|
||||
#define HOVERAREA_HXX
|
||||
|
||||
#include <QQuickItem>
|
||||
|
||||
class HoverArea : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged);
|
||||
|
||||
public:
|
||||
HoverArea();
|
||||
|
||||
bool containsMouse() const
|
||||
{
|
||||
return m_containsMouse;
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
void containsMouseChanged(bool containsMouse);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* sender, QEvent* event) override;
|
||||
|
||||
private:
|
||||
bool m_containsMouse = false;
|
||||
};
|
||||
|
||||
#endif // HOVERAREA_HXX
|
|
@ -54,6 +54,7 @@
|
|||
#include "ModelDataExtractor.hxx"
|
||||
#include "CarriersLocationModel.hxx"
|
||||
#include "SetupRootDialog.hxx"
|
||||
#include "HoverArea.hxx"
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
|
@ -92,6 +93,9 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
|
|||
|
||||
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
|
||||
m_favouriteAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
m_favouriteAircraftModel->setShowFavourites(true);
|
||||
|
||||
m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
|
||||
|
||||
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
|
||||
|
@ -167,6 +171,7 @@ void LauncherController::initQML()
|
|||
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
|
||||
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
|
||||
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
||||
qmlRegisterType<HoverArea>("FlightGear", 1, 0, "HoverArea");
|
||||
|
||||
qmlRegisterType<ModelDataExtractor>("FlightGear", 1, 0, "ModelDataExtractor");
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ class LauncherController : public QObject
|
|||
Q_PROPERTY(AircraftProxyModel* aircraftWithUpdatesModel MEMBER m_aircraftWithUpdatesModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* favouriteAircraftModel MEMBER m_favouriteAircraftModel CONSTANT)
|
||||
|
||||
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
|
||||
|
||||
|
@ -269,6 +270,8 @@ private:
|
|||
AircraftProxyModel* m_aircraftSearchModel;
|
||||
AircraftProxyModel* m_browseAircraftModel;
|
||||
AircraftProxyModel* m_aircraftWithUpdatesModel;
|
||||
AircraftProxyModel* m_favouriteAircraftModel;
|
||||
|
||||
MPServersModel* m_serversModel = nullptr;
|
||||
LocationController* m_location = nullptr;
|
||||
FlightPlanController* m_flightPlan = nullptr;
|
||||
|
|
BIN
src/GUI/assets/icons8-christmas-star-filled.png
Normal file
BIN
src/GUI/assets/icons8-christmas-star-filled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 612 B |
BIN
src/GUI/assets/icons8-christmas-star-outline.png
Normal file
BIN
src/GUI/assets/icons8-christmas-star-outline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 497 B |
|
@ -1,5 +1,7 @@
|
|||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import FlightGear 1.0
|
||||
|
||||
import "."
|
||||
|
||||
Item {
|
||||
|
@ -77,19 +79,39 @@ Item {
|
|||
|
||||
spacing: Style.margin
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: titleBox
|
||||
|
||||
Item {
|
||||
height: titleBox.height
|
||||
width: parent.width
|
||||
|
||||
aircraft: model.uri;
|
||||
currentIndex: model.activeVariant
|
||||
onSelected: {
|
||||
model.activeVariant = index
|
||||
root.select(model.uri)
|
||||
FavouriteToggleButton {
|
||||
id: favourite
|
||||
checked: model.favourite
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onToggle: {
|
||||
model.favourite = on;
|
||||
}
|
||||
}
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: titleBox
|
||||
|
||||
anchors {
|
||||
left: favourite.right
|
||||
leftMargin: Style.margin
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
aircraft: model.uri;
|
||||
currentIndex: model.activeVariant
|
||||
onSelected: {
|
||||
model.activeVariant = index
|
||||
root.select(model.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
StyledText {
|
||||
id: description
|
||||
width: parent.width
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.4
|
||||
import FlightGear.Launcher 1.0
|
||||
import FlightGear 1.0
|
||||
import "."
|
||||
|
||||
Item {
|
||||
|
@ -87,10 +88,21 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
FavouriteToggleButton {
|
||||
id: favourite
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
margins: Style.margin
|
||||
}
|
||||
|
||||
visible: hover.containsMouse || model.favourite
|
||||
checked: model.favourite
|
||||
onToggle: { model.favourite = on; }
|
||||
}
|
||||
|
||||
HoverArea {
|
||||
id: hover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
} // of root item
|
||||
|
|
|
@ -50,6 +50,16 @@ FocusScope
|
|||
active: root.state == "installed"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
id: favouritesButton
|
||||
text: qsTr("Favourites")
|
||||
onClicked: {
|
||||
root.state = "favourites"
|
||||
root.updateSelectionFromLauncher();
|
||||
}
|
||||
active: root.state == "favourites"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
id: browseButton
|
||||
text: qsTr("Browse")
|
||||
|
@ -231,7 +241,22 @@ FocusScope
|
|||
PropertyChanges {
|
||||
target: gridModeToggle; visible: false
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
name: "favourites"
|
||||
|
||||
PropertyChanges {
|
||||
target: root
|
||||
__model: _launcher.favouriteAircraftModel
|
||||
__header: emptyHeader
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: gridModeToggle; visible: true
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
function showDetails(uri)
|
||||
|
|
32
src/GUI/qml/FavouriteToggleButton.qml
Normal file
32
src/GUI/qml/FavouriteToggleButton.qml
Normal file
|
@ -0,0 +1,32 @@
|
|||
import QtQuick 2.4
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool checked: false
|
||||
|
||||
implicitWidth: icon.width + Style.margin
|
||||
implicitHeight: icon.height + Style.margin
|
||||
|
||||
signal toggle(var on);
|
||||
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
source: {
|
||||
var b = mouse.containsMouse ? !root.checked : root.checked;
|
||||
return b ? "qrc:///favourite-icon-filled" : "qrc:///favourite-icon-outline";
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: root.toggle(!root.checked);
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
|
@ -132,6 +132,9 @@
|
|||
<file>qml/GridToggleButton.qml</file>
|
||||
<file>qml/EnableDisableButton.qml</file>
|
||||
<file>qml/IconButton.qml</file>
|
||||
<file>qml/FavouriteToggleButton.qml</file>
|
||||
<file alias="favourite-icon-filled">assets/icons8-christmas-star-filled.png</file>
|
||||
<file alias="favourite-icon-outline">assets/icons8-christmas-star-outline.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/preview">
|
||||
<file alias="close-icon">preview-close.png</file>
|
||||
|
|
Loading…
Add table
Reference in a new issue