Pure QtQuick aircraft-UI
Moves previews, searching, rating handling and extended aircraft information entirely into QQ. Many cosmetic tweaks and improvements still to come.
This commit is contained in:
parent
041b9527d3
commit
d34edaa569
43 changed files with 2146 additions and 1532 deletions
112
src/GUI/AircraftCompactDelegate.qml
Normal file
112
src/GUI/AircraftCompactDelegate.qml
Normal file
|
@ -0,0 +1,112 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int margin: 8
|
||||
|
||||
signal select(var uri);
|
||||
signal showDetails(var uri)
|
||||
|
||||
implicitHeight: Math.max(contentBox.childrenRect.height, thumbnailBox.height) + footer.height
|
||||
implicitWidth: ListView.view.width
|
||||
|
||||
readonly property bool __isSelected: ListView.isCurrentItem
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (__isSelected) {
|
||||
root.showDetails(model.uri)
|
||||
} else {
|
||||
root.select(model.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumbnailBox
|
||||
// thumbnail border
|
||||
|
||||
y: Math.max(0, Math.round((contentBox.childrenRect.height - height) * 0.5))
|
||||
|
||||
border.width: 1
|
||||
border.color: "#7f7f7f"
|
||||
|
||||
width: thumbnail.width
|
||||
height: thumbnail.height
|
||||
|
||||
ThumbnailImage {
|
||||
id: thumbnail
|
||||
|
||||
aircraftUri: model.uri
|
||||
maximumSize.width: 172
|
||||
maximumSize.height: 128
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentBox
|
||||
|
||||
anchors {
|
||||
margins: root.margin
|
||||
left: thumbnailBox.right
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
spacing: root.margin
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: titleBox
|
||||
|
||||
width: parent.width
|
||||
|
||||
aircraft: model.uri;
|
||||
currentIndex: model.activeVariant
|
||||
onSelected: {
|
||||
model.activeVariant = index
|
||||
root.select(model.uri)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: description
|
||||
width: parent.width
|
||||
text: model.description
|
||||
maximumLineCount: 3
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
height: implicitHeight
|
||||
visible: model.description != ""
|
||||
}
|
||||
|
||||
AircraftDownloadPanel
|
||||
{
|
||||
id: downloadPanel
|
||||
visible: (model.package != undefined)
|
||||
packageSize: model.packageSizeBytes
|
||||
installStatus: model.packageStatus
|
||||
downloadedBytes: model.downloadedBytes
|
||||
uri: model.uri
|
||||
width: parent.width
|
||||
compact: true
|
||||
}
|
||||
|
||||
} // of content column
|
||||
|
||||
Item {
|
||||
id: footer
|
||||
height: 12
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Rectangle {
|
||||
color: "#68A6E1"
|
||||
height: 1
|
||||
width: parent.width - 60
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
} // of root item
|
|
@ -1,10 +1,17 @@
|
|||
import QtQuick 2.0
|
||||
import FGLauncher 1.0
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: root
|
||||
property alias aircraftURI: aircraft.uri
|
||||
|
||||
property alias aurcradftURI: aircraft.uri
|
||||
color: "white"
|
||||
readonly property int margin: 16
|
||||
|
||||
MouseArea {
|
||||
// consume all mouse-clicks on the detail view
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
AircraftInfo
|
||||
{
|
||||
|
@ -12,40 +19,151 @@ Item {
|
|||
}
|
||||
|
||||
Column {
|
||||
Text {
|
||||
id: aircraftName
|
||||
width: root.width - (margin * 2)
|
||||
spacing: root.margin
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
AircraftVariantChoice {
|
||||
id: headingBox
|
||||
fontPixelSize: 30
|
||||
popupFontPixelSize: 18
|
||||
|
||||
anchors {
|
||||
margins: 100 // space for back button
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
aircraft: aircraftURI
|
||||
currentIndex: aircraft.variant
|
||||
|
||||
onSelected: {
|
||||
aircraft.variant = index
|
||||
_launcher.selectedAircraft = aircraft.uri;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: preview
|
||||
|
||||
|
||||
// selector overlay
|
||||
|
||||
// left / right arrows
|
||||
// this element normally hides itself unless needed
|
||||
AircraftWarningPanel {
|
||||
id: warningBox
|
||||
aircraftStatus: aircraft.status
|
||||
requiredFGVersion: aircraft.minimumFGVersion
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: previewCycleTimer
|
||||
// thumbnails + description + authors container
|
||||
Item {
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
Rectangle {
|
||||
id: thumbnailBox
|
||||
// thumbnail border
|
||||
|
||||
border.width: 1
|
||||
border.color: "#7f7f7f"
|
||||
|
||||
width: thumbnail.width
|
||||
height: thumbnail.height
|
||||
|
||||
ThumbnailImage {
|
||||
id: thumbnail
|
||||
|
||||
aircraftUri: root.aircraftURI
|
||||
maximumSize.width: 172
|
||||
maximumSize.height: 128
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.left: thumbnailBox.right
|
||||
anchors.leftMargin: root.margin
|
||||
anchors.right: parent.right
|
||||
spacing: root.margin
|
||||
|
||||
Text {
|
||||
id: aircraftDescription
|
||||
text: aircraft.description
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
visible: aircraft.description != ""
|
||||
font.pixelSize: 14
|
||||
}
|
||||
|
||||
Text {
|
||||
id: aircraftAuthors
|
||||
text: qsTr("by %1").arg(aircraft.authors)
|
||||
width: parent.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: Text.WordWrap
|
||||
visible: (aircraft.authors != undefined)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AircraftDownloadPanel {
|
||||
width: parent.width
|
||||
uri: aircraft.uri
|
||||
installStatus: aircraft.installStatus
|
||||
packageSize: aircraft.packageSize
|
||||
downloadedBytes: aircraft.downloadedBytes
|
||||
}
|
||||
|
||||
Text {
|
||||
id: aircraftDescription
|
||||
AircraftPreviewPanel {
|
||||
id: previews
|
||||
width: parent.width
|
||||
previews: aircraft.previews
|
||||
visible: aircraft.previews.length > 0
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: ratingGrid
|
||||
anchors.left: parent.left
|
||||
|
||||
visible: aircraft.ratings != undefined
|
||||
|
||||
rows: 2
|
||||
columns: 3
|
||||
rowSpacing: root.margin
|
||||
columnSpacing: root.margin
|
||||
|
||||
Text {
|
||||
id: ratingsLabel
|
||||
text: qsTr("Ratings:")
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Flight model")
|
||||
value: aircraft.ratings[0]
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Systems")
|
||||
value: aircraft.ratings[1]
|
||||
}
|
||||
|
||||
Item {
|
||||
width: ratingsLabel.width
|
||||
height: 1
|
||||
} // placeholder
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Cockpit")
|
||||
value: aircraft.ratings[2]
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Exterior")
|
||||
value: aircraft.ratings[3]
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: aircraftAuthors
|
||||
text: qsTr("Local file location: %1").arg(aircraft.pathOnDisk);
|
||||
visible: aircraft.pathOnDisk != undefined
|
||||
}
|
||||
|
||||
// info button
|
||||
|
||||
// version warning!
|
||||
|
||||
// ratings box
|
||||
|
||||
// package size
|
||||
|
||||
// install / download / update button
|
||||
} // main layout column
|
||||
}
|
||||
|
|
|
@ -5,28 +5,34 @@ Item {
|
|||
id: root
|
||||
|
||||
property url uri
|
||||
property int installStatus: AircraftModel.PackageNotInstalled
|
||||
property int installStatus: LocalAircraftCache.PackageNotInstalled
|
||||
property int packageSize: 0
|
||||
|
||||
property int downloadedBytes: 0
|
||||
|
||||
readonly property bool active: (installStatus == AircraftModel.PackageQueued) ||
|
||||
(installStatus == AircraftModel.PackageDownloading)
|
||||
readonly property bool active: (installStatus == LocalAircraftCache.PackageQueued) ||
|
||||
(installStatus == LocalAircraftCache.PackageDownloading)
|
||||
|
||||
readonly property int compactWidth: button.width + sizeText.width
|
||||
|
||||
property bool compact: false
|
||||
|
||||
implicitWidth: childrenRect.width
|
||||
implicitHeight: childrenRect.height
|
||||
|
||||
|
||||
state: "not-installed"
|
||||
|
||||
onInstallStatusChanged: {
|
||||
if (installStatus == AircraftModel.PackageInstalled) {
|
||||
if (installStatus == LocalAircraftCache.PackageInstalled) {
|
||||
state = "installed";
|
||||
} else if (installStatus == AircraftModel.PackageNotInstalled) {
|
||||
} else if (installStatus == LocalAircraftCache.PackageNotInstalled) {
|
||||
state = "not-installed"
|
||||
} else if (installStatus == AircraftModel.PackageUpdateAvailable) {
|
||||
} else if (installStatus == LocalAircraftCache.PackageUpdateAvailable) {
|
||||
state = "has-update"
|
||||
} else if (installStatus == AircraftModel.PackageQueued) {
|
||||
} else if (installStatus == LocalAircraftCache.PackageQueued) {
|
||||
state = "queued"
|
||||
} else if (installStatus == AircraftModel.PackageDownloading) {
|
||||
} else if (installStatus == LocalAircraftCache.PackageDownloading) {
|
||||
state = "downloading"
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +183,7 @@ Item {
|
|||
Text {
|
||||
id: statusText
|
||||
visible: false
|
||||
text: "Downloaded " + (root.downloadedBytes / 0x100000).toFixed(1) +
|
||||
text: (compact ? "" : "Downloaded ") + (root.downloadedBytes / 0x100000).toFixed(1) +
|
||||
"MB of " + (root.packageSize / 0x100000).toFixed(1) + "MB";
|
||||
}
|
||||
} // item container for progress bar and text
|
||||
|
|
225
src/GUI/AircraftFullDelegate.qml
Normal file
225
src/GUI/AircraftFullDelegate.qml
Normal file
|
@ -0,0 +1,225 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int margin: 8
|
||||
|
||||
// background
|
||||
|
||||
height: Math.max(contentBox.childrenRect.height, thumbnailBox.height) + footer.height
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.ListView.view.currentIndex = model.index
|
||||
_launcher.selectAircraft(model.uri);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumbnailBox
|
||||
// thumbnail border
|
||||
|
||||
y: Math.max(0, Math.round((contentBox.childrenRect.height - height) * 0.5))
|
||||
|
||||
border.width: 1
|
||||
border.color: "#7f7f7f"
|
||||
|
||||
width: thumbnail.width
|
||||
height: thumbnail.height
|
||||
|
||||
ThumbnailImage {
|
||||
id: thumbnail
|
||||
|
||||
aircraftUri: model.uri
|
||||
maximumSize.width: 300
|
||||
maximumSize.height: 200
|
||||
}
|
||||
|
||||
Image {
|
||||
id: previewIcon
|
||||
visible: model.hasPreviews
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
source: "qrc:///preview-icon"
|
||||
opacity: showPreviewsMouse.containsMouse ? 1.0 : 0.5
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 100 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: showPreviewsMouse
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
visible: model.hasPreviews
|
||||
|
||||
onClicked: {
|
||||
_launcher.showPreviewsFor(model.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentBox
|
||||
|
||||
anchors.leftMargin: root.margin
|
||||
anchors.left: thumbnailBox.right
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.margin
|
||||
|
||||
spacing: root.margin
|
||||
|
||||
Item
|
||||
{
|
||||
// box to contain the aircraft title and the variant
|
||||
// selection button arrows
|
||||
|
||||
id: headingBox
|
||||
width: parent.width
|
||||
height: title.height
|
||||
|
||||
ArrowButton {
|
||||
id: previousVariantButton
|
||||
|
||||
visible: (model.variantCount > 0) && (model.activeVariant > 0)
|
||||
arrow: "qrc:///left-arrow-icon"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
onClicked: {
|
||||
model.activeVariant = (model.activeVariant - 1)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: title
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: previousVariantButton.right
|
||||
anchors.right: nextVariantButton.left
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 24
|
||||
text: model.title
|
||||
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
ArrowButton {
|
||||
id: nextVariantButton
|
||||
|
||||
arrow: "qrc:///right-arrow-icon"
|
||||
visible: (model.variantCount > 0) && (model.activeVariant < model.variantCount - 1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
onClicked: {
|
||||
model.activeVariant = (model.activeVariant + 1)
|
||||
}
|
||||
}
|
||||
|
||||
} // top header box (title + variant arrows)
|
||||
|
||||
// this element normally hides itself unless needed
|
||||
AircraftWarningPanel {
|
||||
id: warningBox
|
||||
aircraftStatus: model.aircraftStatus
|
||||
requiredFGVersion: model.requiredFGVersion
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Text {
|
||||
id: authors
|
||||
visible: (model.authors != undefined)
|
||||
width: parent.width
|
||||
text: "by: " + model.authors
|
||||
maximumLineCount: 3
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
id: description
|
||||
width: parent.width
|
||||
text: model.description
|
||||
maximumLineCount: 10
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item {
|
||||
// this area holds the install/update/remove button,
|
||||
// and the ratings grid. when a download / update is
|
||||
// happening, the content is re-arranged to give more room
|
||||
// for the progress bar and feedback
|
||||
id: bottomContent
|
||||
|
||||
readonly property int minimumWidthForBottomContent: ratingGrid.width + downloadPanel.compactWidth + root.margin
|
||||
|
||||
width: Math.max(parent.width, minimumWidthForBottomContent)
|
||||
height: ratingGrid.height
|
||||
|
||||
AircraftDownloadPanel
|
||||
{
|
||||
id: downloadPanel
|
||||
visible: (model.package != undefined)
|
||||
packageSize: model.packageSizeBytes
|
||||
installStatus: model.packageStatus
|
||||
downloadedBytes: model.downloadedBytes
|
||||
uri: model.uri
|
||||
width: parent.width // full width, grid sits on top
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: ratingGrid
|
||||
anchors.right: parent.right
|
||||
|
||||
// hide ratings when the panel is doing something, to
|
||||
// make more room for the progress bar and text
|
||||
visible: model.hasRatings && !downloadPanel.active
|
||||
|
||||
rows: 2
|
||||
columns: 2
|
||||
rowSpacing: root.margin
|
||||
columnSpacing: root.margin
|
||||
|
||||
AircraftRating {
|
||||
title: "Flight model"
|
||||
value: model.ratingFDM;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Systems"
|
||||
value: model.ratingSystems;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Cockpit"
|
||||
value: model.ratingCockpit;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Exterior"
|
||||
value: model.ratingExterior;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of content column
|
||||
|
||||
Item {
|
||||
id: footer
|
||||
height: 12
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Rectangle {
|
||||
color: "#68A6E1"
|
||||
height: 2
|
||||
width: parent.width - 60
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
} // of root item
|
|
@ -1,434 +0,0 @@
|
|||
// AircraftItemDelegate.cxx - part of GUI launcher using Qt5
|
||||
//
|
||||
// Written by James Turner, started March 2015.
|
||||
//
|
||||
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// 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 "AircraftItemDelegate.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QLinearGradient>
|
||||
#include <QListView>
|
||||
#include <QMouseEvent>
|
||||
#include <QFontMetrics>
|
||||
|
||||
#include "AircraftModel.hxx"
|
||||
|
||||
const int DOT_SIZE = 11;
|
||||
const int DOT_MARGIN = 2;
|
||||
|
||||
int dotBoxWidth()
|
||||
{
|
||||
return (DOT_MARGIN * 6 + DOT_SIZE * 5);
|
||||
}
|
||||
|
||||
AircraftItemDelegate::AircraftItemDelegate(QListView* view) :
|
||||
m_view(view)
|
||||
{
|
||||
view->viewport()->installEventFilter(this);
|
||||
view->viewport()->setMouseTracking(true);
|
||||
|
||||
m_leftArrowIcon.load(":/left-arrow-icon");
|
||||
m_rightArrowIcon.load(":/right-arrow-icon");
|
||||
m_openPreviewsIcon.load(":/preview-icon");
|
||||
m_openPreviewsIcon = m_openPreviewsIcon.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option,
|
||||
const QModelIndex & index) const
|
||||
{
|
||||
QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
const AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
||||
if (status == MessageWidget) {
|
||||
painter->setPen(QColor(0x7f, 0x7f, 0x7f));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
// draw bottom dividing line
|
||||
painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
|
||||
contentRect.right(), contentRect.bottom() + MARGIN);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// selection feedback rendering
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
QLinearGradient grad(option.rect.topLeft(), option.rect.bottomLeft());
|
||||
grad.setColorAt(0.0, QColor(152, 163, 180));
|
||||
grad.setColorAt(1.0, QColor(90, 107, 131));
|
||||
|
||||
QBrush backgroundBrush(grad);
|
||||
painter->fillRect(option.rect, backgroundBrush);
|
||||
|
||||
painter->setPen(QColor(90, 107, 131));
|
||||
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
|
||||
}
|
||||
|
||||
// thumbnail
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
|
||||
painter->drawPixmap(contentRect.left(), yPos, thumbnail);
|
||||
|
||||
// draw 1px frame
|
||||
QRect thumbFrame(contentRect.left(), yPos, thumbnail.width(), thumbnail.height());
|
||||
painter->setPen(QColor(0x7f, 0x7f, 0x7f));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRect(thumbFrame);
|
||||
|
||||
if (!index.data(AircraftPreviewsRole).toList().empty()) {
|
||||
QRect previewIconRect = m_openPreviewsIcon.rect();
|
||||
previewIconRect.moveBottomLeft(thumbFrame.bottomLeft());
|
||||
painter->drawPixmap(previewIconRect, m_openPreviewsIcon);
|
||||
}
|
||||
|
||||
// draw bottom dividing line
|
||||
painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
|
||||
contentRect.right(), contentRect.bottom() + MARGIN);
|
||||
|
||||
int variantCount = index.data(AircraftVariantCountRole).toInt();
|
||||
int currentVariant = index.data(AircraftVariantRole).toInt();
|
||||
QString description = index.data(Qt::DisplayRole).toString();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
QFont f;
|
||||
f.setPointSize(18);
|
||||
painter->setFont(f);
|
||||
|
||||
QRect descriptionRect = contentRect.adjusted(ARROW_SIZE, 0, -ARROW_SIZE, 0),
|
||||
actualBounds;
|
||||
|
||||
if (variantCount > 0) {
|
||||
bool canLeft = (currentVariant > 0);
|
||||
bool canRight = (currentVariant < variantCount );
|
||||
|
||||
QRect leftArrowRect = leftCycleArrowRect(option.rect, index);
|
||||
if (canLeft) {
|
||||
painter->drawPixmap(leftArrowRect.topLeft() + QPoint(2, 2), m_leftArrowIcon);
|
||||
}
|
||||
|
||||
QRect rightArrowRect = rightCycleArrowRect(option.rect, index);
|
||||
if (canRight) {
|
||||
painter->drawPixmap(rightArrowRect.topLeft() + QPoint(2, 2), m_rightArrowIcon);
|
||||
}
|
||||
}
|
||||
|
||||
painter->drawText(descriptionRect, Qt::TextWordWrap, description, &actualBounds);
|
||||
QString authors = index.data(AircraftAuthorsRole).toString();
|
||||
|
||||
f.setPointSize(12);
|
||||
QFontMetrics smallMetrics(f);
|
||||
|
||||
painter->setFont(f);
|
||||
|
||||
if (!authors.isEmpty()) {
|
||||
// ellide this beyond some maximum size, with a click to expand?
|
||||
QRect authorsRect = descriptionRect;
|
||||
authorsRect.moveTop(actualBounds.bottom() + MARGIN);
|
||||
painter->drawText(authorsRect, Qt::TextWordWrap,
|
||||
tr("by: %1").arg(authors),
|
||||
&actualBounds);
|
||||
}
|
||||
|
||||
QString longDescription = index.data(AircraftLongDescriptionRole).toString();
|
||||
if (!longDescription.isEmpty()) {
|
||||
QRect longDescriptionRect = descriptionRect;
|
||||
longDescriptionRect.moveTop(actualBounds.bottom() + MARGIN);
|
||||
painter->drawText(longDescriptionRect, Qt::TextWordWrap,
|
||||
longDescription, &actualBounds);
|
||||
}
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
if (index.data(AircraftHasRatingsRole).toBool()) {
|
||||
int ratingsWidth = smallMetrics.width("Flight model:") + dotBoxWidth();
|
||||
|
||||
QRect r = contentRect;
|
||||
r.setWidth(ratingsWidth);
|
||||
r.moveLeft(contentRect.right() - (ratingsWidth * 2));
|
||||
r.moveTop(actualBounds.bottom() + MARGIN);
|
||||
r.setHeight(qMax(24, smallMetrics.height() + MARGIN));
|
||||
|
||||
drawRating(painter, tr("Flight model:"), r, index.data(AircraftRatingRole).toInt());
|
||||
r.moveTop(r.bottom());
|
||||
drawRating(painter, tr("Systems:"), r, index.data(AircraftRatingRole + 1).toInt());
|
||||
|
||||
r.moveTop(actualBounds.bottom() + MARGIN);
|
||||
r.moveLeft(r.right());
|
||||
drawRating(painter, tr("Cockpit:"), r, index.data(AircraftRatingRole + 2).toInt());
|
||||
r.moveTop(r.bottom());
|
||||
drawRating(painter, tr("Exterior:"), r, index.data(AircraftRatingRole + 3).toInt());
|
||||
}
|
||||
|
||||
double downloadFraction = 0.0;
|
||||
QString buttonText, infoText;
|
||||
QColor buttonColor(27, 122, 211);
|
||||
|
||||
double sizeInMBytes = index.data(AircraftPackageSizeRole).toInt();
|
||||
sizeInMBytes /= 0x100000;
|
||||
|
||||
if (status == PackageDownloading) {
|
||||
buttonText = tr("Cancel");
|
||||
double downloadedMB = index.data(AircraftInstallDownloadedSizeRole).toInt();
|
||||
downloadedMB /= 0x100000;
|
||||
infoText = tr("%1 MB of %2 MB").arg(downloadedMB, 0, 'f', 1).arg(sizeInMBytes, 0, 'f', 1);
|
||||
buttonColor = QColor(0xcf, 0xcf, 0xcf);
|
||||
downloadFraction = downloadedMB / sizeInMBytes;
|
||||
} else if (status == PackageQueued) {
|
||||
buttonText = tr("Cancel");
|
||||
infoText = tr("Waiting to download %1 MB").arg(sizeInMBytes, 0, 'f', 1);
|
||||
buttonColor = QColor(0xcf, 0xcf, 0xcf);
|
||||
} else {
|
||||
infoText = QStringLiteral("%1MB").arg(sizeInMBytes, 0, 'f', 1);
|
||||
if (status == PackageNotInstalled) {
|
||||
buttonText = tr("Install");
|
||||
} else if (status == PackageUpdateAvailable) {
|
||||
buttonText = tr("Update");
|
||||
} else if (status == PackageInstalled) {
|
||||
bool canUninstall = index.data(AircraftPackageIdRole).isValid(); // local aircraft have no package ID
|
||||
if (canUninstall) {
|
||||
buttonText = tr("Uninstall");
|
||||
} else {
|
||||
infoText.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QRect buttonRect = packageButtonRect(option.rect, index);
|
||||
if (!buttonText.isEmpty()) {
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(buttonColor);
|
||||
painter->drawRoundedRect(buttonRect, 5, 5);
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(buttonRect, Qt::AlignCenter, buttonText);
|
||||
}
|
||||
|
||||
QRect infoTextRect = buttonRect;
|
||||
infoTextRect.setLeft(buttonRect.right() + MARGIN);
|
||||
infoTextRect.setWidth(200);
|
||||
|
||||
if (status == PackageDownloading) {
|
||||
QRect progressRect = infoTextRect;
|
||||
progressRect.setHeight(6);
|
||||
painter->setPen(QPen(QColor(0xcf, 0xcf, 0xcf), 0));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRoundedRect(progressRect, 3, 3);
|
||||
infoTextRect.setTop(progressRect.bottom() + 1);
|
||||
|
||||
QRect progressBarRect = progressRect.marginsRemoved(QMargins(2, 2, 2, 2));
|
||||
|
||||
progressBarRect.setWidth(static_cast<int>(progressBarRect.width() * downloadFraction));
|
||||
|
||||
painter->setBrush(QColor(27, 122, 211));
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(progressBarRect, 2, 2);
|
||||
}
|
||||
|
||||
if (!infoText.isEmpty()) {
|
||||
painter->setPen(Qt::black);
|
||||
painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
|
||||
}
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
}
|
||||
|
||||
QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||
{
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
const AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
||||
|
||||
if (status == MessageWidget) {
|
||||
QSize r = option.rect.size();
|
||||
r.setHeight(100);
|
||||
return r;
|
||||
}
|
||||
|
||||
QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
|
||||
QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnailSize.width());
|
||||
contentRect.setBottom(9999); // large value to avoid clipping
|
||||
contentRect.adjust(ARROW_SIZE, 0, -ARROW_SIZE, 0);
|
||||
|
||||
QFont f;
|
||||
f.setPointSize(18);
|
||||
QFontMetrics metrics(f);
|
||||
|
||||
int textHeight = metrics.boundingRect(contentRect, Qt::TextWordWrap,
|
||||
index.data().toString()).height();
|
||||
|
||||
f.setPointSize(12);
|
||||
QFontMetrics smallMetrics(f);
|
||||
|
||||
QString authors = tr("by: %1").arg(index.data(AircraftAuthorsRole).toString());
|
||||
|
||||
if (!authors.isEmpty()) {
|
||||
textHeight += MARGIN;
|
||||
textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, authors).height();
|
||||
}
|
||||
|
||||
QString desc = index.data(AircraftLongDescriptionRole).toString();
|
||||
if (!desc.isEmpty()) {
|
||||
textHeight += MARGIN;
|
||||
textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, desc).height();
|
||||
}
|
||||
|
||||
if (index.data(AircraftHasRatingsRole).toBool()) {
|
||||
// ratings
|
||||
int ratingHeight = qMax(24, smallMetrics.height() + MARGIN);
|
||||
textHeight += ratingHeight * 2;
|
||||
}
|
||||
|
||||
textHeight += BUTTON_HEIGHT;
|
||||
|
||||
textHeight = qMax(textHeight, thumbnailSize.height());
|
||||
return QSize(option.rect.width(), textHeight + (MARGIN * 2));
|
||||
}
|
||||
|
||||
bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
|
||||
{
|
||||
if ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
|
||||
{
|
||||
QMouseEvent* me = static_cast< QMouseEvent* >( event );
|
||||
QModelIndex index = m_view->indexAt( me->pos() );
|
||||
int variantCount = index.data(AircraftVariantCountRole).toInt();
|
||||
int variantIndex = index.data(AircraftVariantRole).toInt();
|
||||
QRect vr = m_view->visualRect(index);
|
||||
|
||||
if ( (event->type() == QEvent::MouseButtonRelease) && (variantCount > 0) )
|
||||
{
|
||||
QRect leftCycleRect = leftCycleArrowRect(vr, index),
|
||||
rightCycleRect = rightCycleArrowRect(vr, index);
|
||||
|
||||
if ((variantIndex > 0) && leftCycleRect.contains(me->pos())) {
|
||||
m_view->model()->setData(index, variantIndex - 1, AircraftVariantRole);
|
||||
emit variantChanged(index);
|
||||
return true;
|
||||
} else if ((variantIndex < variantCount) && rightCycleRect.contains(me->pos())) {
|
||||
m_view->model()->setData(index, variantIndex + 1, AircraftVariantRole);
|
||||
emit variantChanged(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((event->type() == QEvent::MouseButtonRelease) &&
|
||||
packageButtonRect(vr, index).contains(me->pos()))
|
||||
{
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
const AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
||||
if (status == PackageNotInstalled) {
|
||||
emit requestInstall(index);
|
||||
} else if ((status == PackageDownloading) || (status == PackageQueued)) {
|
||||
emit cancelDownload(index);
|
||||
} else if (status == PackageUpdateAvailable) {
|
||||
emit requestInstall(index);
|
||||
} else if (status == PackageInstalled) {
|
||||
emit requestUninstall(index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((event->type() == QEvent::MouseButtonRelease) &&
|
||||
!index.data(AircraftPreviewsRole).toList().empty() &&
|
||||
showPreviewsRect(vr, index).contains(me->pos()))
|
||||
{
|
||||
emit showPreviews(index);
|
||||
}
|
||||
} else if ( event->type() == QEvent::MouseMove ) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QRect AircraftItemDelegate::leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
|
||||
{
|
||||
QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
QRect r = contentRect;
|
||||
r.setRight(r.left() + ARROW_SIZE);
|
||||
r.setBottom(r.top() + ARROW_SIZE);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
QRect AircraftItemDelegate::rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
|
||||
{
|
||||
QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
QRect r = contentRect;
|
||||
r.setLeft(r.right() - ARROW_SIZE);
|
||||
r.setBottom(r.top() + ARROW_SIZE);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QModelIndex& index) const
|
||||
{
|
||||
QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - BUTTON_HEIGHT,
|
||||
BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
}
|
||||
|
||||
QRect AircraftItemDelegate::showPreviewsRect(const QRect& visualRect, const QModelIndex& index) const
|
||||
{
|
||||
QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
const quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
|
||||
QRect thumbFrame(contentRect.left(), yPos, thumbnail.width(), thumbnail.height());
|
||||
|
||||
QRect previewIconRect = m_openPreviewsIcon.rect();
|
||||
previewIconRect.moveBottomLeft(thumbFrame.bottomLeft());
|
||||
return previewIconRect;
|
||||
}
|
||||
|
||||
void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
|
||||
{
|
||||
QRect dotBox = box;
|
||||
dotBox.setLeft(box.right() - dotBoxWidth());
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
QRect textBox = box;
|
||||
textBox.setRight(dotBox.left());
|
||||
painter->drawText(textBox, Qt::AlignVCenter | Qt::AlignRight, label);
|
||||
|
||||
painter->setPen(Qt::NoPen);
|
||||
// magic +1 offset in to account for fonts having more empty ascent
|
||||
// space than descent space
|
||||
QRect dot(dotBox.left() + DOT_MARGIN,
|
||||
dotBox.center().y() - (DOT_SIZE / 2) + 1,
|
||||
DOT_SIZE,
|
||||
DOT_SIZE);
|
||||
for (int i=0; i<5; ++i) {
|
||||
painter->setBrush((i < value) ? QColor(0x3f, 0x3f, 0x3f) : QColor(0xaf, 0xaf, 0xaf));
|
||||
painter->drawEllipse(dot);
|
||||
dot.moveLeft(dot.right() + DOT_MARGIN);
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// AircraftItemDelegate.hxx - part of GUI launcher using Qt5
|
||||
//
|
||||
// Written by James Turner, started March 2015.
|
||||
//
|
||||
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef FG_GUI_AIRCRAFT_ITEM_DELEGATE
|
||||
#define FG_GUI_AIRCRAFT_ITEM_DELEGATE
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class QListView;
|
||||
|
||||
class AircraftItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const int MARGIN = 4;
|
||||
static const int ARROW_SIZE = 20;
|
||||
static const int BUTTON_HEIGHT = 24;
|
||||
static const int BUTTON_WIDTH = 80;
|
||||
|
||||
AircraftItemDelegate(QListView* view);
|
||||
|
||||
virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE;
|
||||
|
||||
virtual QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE;
|
||||
|
||||
virtual bool eventFilter( QObject*, QEvent* event ) Q_DECL_OVERRIDE;
|
||||
|
||||
Q_SIGNALS:
|
||||
void variantChanged(const QModelIndex& index);
|
||||
|
||||
void requestInstall(const QModelIndex& index);
|
||||
|
||||
void requestUninstall(const QModelIndex& index);
|
||||
|
||||
void cancelDownload(const QModelIndex& index);
|
||||
|
||||
void showPreviews(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
QRect leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
QRect rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
|
||||
QRect packageButtonRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
|
||||
QRect showPreviewsRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
|
||||
void drawRating(QPainter* painter, QString label, const QRect& box, int value) const;
|
||||
|
||||
QListView* m_view;
|
||||
QPixmap m_leftArrowIcon,
|
||||
m_rightArrowIcon,
|
||||
m_openPreviewsIcon;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear.Launcher 1.0
|
||||
import QtQuick 2.2
|
||||
import FlightGear.Launcher 1.0 as FG
|
||||
|
||||
Item
|
||||
{
|
||||
|
@ -7,230 +7,62 @@ Item
|
|||
|
||||
readonly property int margin: 8
|
||||
|
||||
Component
|
||||
Rectangle
|
||||
{
|
||||
id: aircraftDelegate
|
||||
id: tabBar
|
||||
height: installedAircraftButton.height + margin
|
||||
width: parent.width
|
||||
|
||||
Item {
|
||||
// background
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: root.margin
|
||||
|
||||
height: Math.max(contentBox.childrenRect.height, thumbnailBox.height) + footer.height
|
||||
width: root.width
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
TabButton {
|
||||
id: installedAircraftButton
|
||||
text: qsTr("Installed Aircraft")
|
||||
onClicked: {
|
||||
list.currentIndex = model.index
|
||||
_launcher.selectAircraft(model.uri);
|
||||
root.state = "installed"
|
||||
}
|
||||
active: root.state == "installed"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumbnailBox
|
||||
// thumbnail border
|
||||
|
||||
y: Math.max(0, Math.round((contentBox.childrenRect.height - height) * 0.5))
|
||||
|
||||
border.width: 1
|
||||
border.color: "#7f7f7f"
|
||||
|
||||
width: thumbnail.width
|
||||
height: thumbnail.height
|
||||
|
||||
ThumbnailImage {
|
||||
id: thumbnail
|
||||
|
||||
aircraftUri: model.uri
|
||||
maximumSize.width: 300
|
||||
maximumSize.height: 200
|
||||
TabButton {
|
||||
id: browseButton
|
||||
text: qsTr("Browse")
|
||||
onClicked: {
|
||||
root.state = "browse"
|
||||
}
|
||||
active: root.state == "browse"
|
||||
}
|
||||
} // of header row
|
||||
|
||||
Image {
|
||||
id: previewIcon
|
||||
visible: model.hasPreviews
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
source: "qrc:///preview-icon"
|
||||
opacity: showPreviewsMouse.containsMouse ? 1.0 : 0.5
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 100 }
|
||||
}
|
||||
}
|
||||
SearchButton {
|
||||
id: searchButton
|
||||
|
||||
MouseArea {
|
||||
id: showPreviewsMouse
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
visible: model.hasPreviews
|
||||
width: 180
|
||||
height: installedAircraftButton.height
|
||||
|
||||
onClicked: {
|
||||
_launcher.showPreviewsFor(model.uri);
|
||||
}
|
||||
}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: margin
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onSearch: {
|
||||
_launcher.searchAircraftModel.setAircraftFilterString(term)
|
||||
root.state = "search"
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentBox
|
||||
active: root.state == "search"
|
||||
}
|
||||
|
||||
anchors.leftMargin: root.margin
|
||||
anchors.left: thumbnailBox.right
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.margin
|
||||
Rectangle {
|
||||
color: "#68A6E1"
|
||||
height: 1
|
||||
width: parent.width - 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
spacing: root.margin
|
||||
|
||||
Item
|
||||
{
|
||||
// box to contain the aircraft title and the variant
|
||||
// selection button arrows
|
||||
|
||||
id: headingBox
|
||||
width: parent.width
|
||||
height: title.height
|
||||
|
||||
ArrowButton {
|
||||
id: previousVariantButton
|
||||
|
||||
visible: (model.variantCount > 0) && (model.activeVariant > 0)
|
||||
arrow: "qrc:///left-arrow-icon"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
onClicked: {
|
||||
model.activeVariant = (model.activeVariant - 1)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: title
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: previousVariantButton.right
|
||||
anchors.right: nextVariantButton.left
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 24
|
||||
text: model.title
|
||||
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
ArrowButton {
|
||||
id: nextVariantButton
|
||||
|
||||
arrow: "qrc:///right-arrow-icon"
|
||||
visible: (model.variantCount > 0) && (model.activeVariant < model.variantCount - 1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
onClicked: {
|
||||
model.activeVariant = (model.activeVariant + 1)
|
||||
}
|
||||
}
|
||||
|
||||
} // top header box (title + variant arrows)
|
||||
|
||||
// this element normally hides itself unless needed
|
||||
AircraftWarningPanel {
|
||||
id: warningBox
|
||||
aircraftStatus: model.aircraftStatus
|
||||
requiredFGVersion: model.requiredFGVersion
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Text {
|
||||
id: authors
|
||||
visible: (model.authors != undefined)
|
||||
width: parent.width
|
||||
text: "by: " + model.authors
|
||||
maximumLineCount: 3
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
id: description
|
||||
width: parent.width
|
||||
text: model.description
|
||||
maximumLineCount: 10
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item {
|
||||
// this area holds the install/update/remove button,
|
||||
// and the ratings grid. when a download / update is
|
||||
// happening, the content is re-arranged to give more room
|
||||
// for the progress bar and feedback
|
||||
id: bottomContent
|
||||
|
||||
readonly property int minimumWidthForBottomContent: ratingGrid.width + downloadPanel.compactWidth + root.margin
|
||||
|
||||
width: Math.max(parent.width, minimumWidthForBottomContent)
|
||||
height: ratingGrid.height
|
||||
|
||||
AircraftDownloadPanel
|
||||
{
|
||||
id: downloadPanel
|
||||
visible: (model.package != undefined)
|
||||
packageSize: model.packageSizeBytes
|
||||
installStatus: model.packageStatus
|
||||
downloadedBytes: model.downloadedBytes
|
||||
uri: model.uri
|
||||
width: parent.width // full width, grid sits on top
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: ratingGrid
|
||||
anchors.right: parent.right
|
||||
|
||||
// hide ratings when the panel is doing something, to
|
||||
// make more room for the progress bar and text
|
||||
visible: model.hasRatings && !downloadPanel.active
|
||||
|
||||
rows: 2
|
||||
columns: 2
|
||||
rowSpacing: root.margin
|
||||
columnSpacing: root.margin
|
||||
|
||||
AircraftRating {
|
||||
title: "Flight model"
|
||||
value: model.ratingFDM;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Systems"
|
||||
value: model.ratingSystems;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Cockpit"
|
||||
value: model.ratingCockpit;
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: "Exterior"
|
||||
value: model.ratingExterior;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // of content column
|
||||
|
||||
Item {
|
||||
id: footer
|
||||
height: 12
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Rectangle {
|
||||
color: "#68A6E1"
|
||||
height: 2
|
||||
width: parent.width - 60
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
} // of Component root item
|
||||
} // of Component
|
||||
|
||||
Component {
|
||||
id: highlight
|
||||
|
@ -242,11 +74,51 @@ Item
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: ratingsHeader
|
||||
AircraftRatingsPanel {
|
||||
width: aircraftList.width - 80
|
||||
x: (aircraftList.width - width) / 2
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: noDefaultCatalogHeader
|
||||
NoDefaultCatalogPanel {
|
||||
width: aircraftList.width - 80
|
||||
x: (aircraftList.width - width) / 2
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: updateAllHeader
|
||||
UpdateAllPanel {
|
||||
width: aircraftList.width - 80
|
||||
x: (aircraftList.width - width) / 2
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
model: _filteredModel
|
||||
anchors.fill: parent
|
||||
delegate: aircraftDelegate
|
||||
id: aircraftList
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: tabBar.bottom
|
||||
bottom: parent.bottom
|
||||
right: scrollbar.left
|
||||
}
|
||||
|
||||
delegate: AircraftCompactDelegate {
|
||||
onSelect: {
|
||||
aircraftList.currentIndex = model.index;
|
||||
_launcher.selectedAircraft = uri;
|
||||
}
|
||||
|
||||
onShowDetails: root.showDetails(uri)
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
highlight: highlight
|
||||
highlightMoveDuration: 100
|
||||
|
@ -259,102 +131,100 @@ Item
|
|||
|
||||
}
|
||||
|
||||
Scrollbar {
|
||||
anchors.right: parent.right
|
||||
height: parent.height
|
||||
function updateSelectionFromLauncher()
|
||||
{
|
||||
model.selectVariantForAircraftURI(_launcher.selectedAircraft);
|
||||
var row = model.indexForURI(_launcher.selectedAircraft);
|
||||
if (row >= 0) {
|
||||
currentIndex = row;
|
||||
} else {
|
||||
// clear selection in view, so we don't show something
|
||||
// erroneous such as the previous value
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scrollbar {
|
||||
id: scrollbar
|
||||
anchors.right: parent.right
|
||||
anchors.top: tabBar.bottom
|
||||
height: aircraftList.height
|
||||
flickable: aircraftList
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: _launcher
|
||||
onSelectAircraftIndex: {
|
||||
console.warn("Selecting aircraft:" + index);
|
||||
list.currentIndex = index;
|
||||
aircraftList.currentIndex = index;
|
||||
aircraftList.model.select
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: updateAllBox
|
||||
state: "installed"
|
||||
|
||||
visible: _aircraftModel.showUpdateAll
|
||||
width: parent.width
|
||||
height: updateAllRow.childrenRect.height + root.margin * 2
|
||||
|
||||
Row {
|
||||
y: root.margin
|
||||
id: updateAllRow
|
||||
spacing: root.margin
|
||||
|
||||
Text {
|
||||
text: _aircraftModel.aircraftNeedingUpdated + " aircraft have updates available - download and install them now?"
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
states: [
|
||||
State {
|
||||
name: "installed"
|
||||
PropertyChanges {
|
||||
target: aircraftList
|
||||
model: _launcher.installedAircraftModel
|
||||
header: _launcher.baseAircraftModel.showUpdateAll ? updateAllHeader : null
|
||||
}
|
||||
},
|
||||
|
||||
Button {
|
||||
text: "Update all"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: {
|
||||
_launcher.requestUpdateAllAircraft();
|
||||
_launcher.showUpdateAll = false
|
||||
}
|
||||
State {
|
||||
name: "search"
|
||||
PropertyChanges {
|
||||
target: aircraftList
|
||||
model: _launcher.searchAircraftModel
|
||||
header: null
|
||||
}
|
||||
},
|
||||
|
||||
Button {
|
||||
text: "Not now"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: {
|
||||
_launcher.showUpdateAll = false
|
||||
}
|
||||
State {
|
||||
name: "browse"
|
||||
PropertyChanges {
|
||||
target: aircraftList
|
||||
model: _launcher.browseAircraftModel
|
||||
header: _launcher.showNoOfficialHanger ? noDefaultCatalogHeader : ratingsHeader
|
||||
}
|
||||
}
|
||||
} // of update-all prompt
|
||||
]
|
||||
|
||||
Rectangle {
|
||||
id: noDefaultCatalogBox
|
||||
visible: _launcher.showNoOfficialHanger
|
||||
width: parent.width
|
||||
height: noDefaultCatalogRow.childrenRect.height + root.margin * 2
|
||||
function showDetails(uri)
|
||||
{
|
||||
// set URI, start animation
|
||||
// change state
|
||||
detailsView.aircraftURI = uri;
|
||||
detailsView.visible = true
|
||||
}
|
||||
|
||||
Row {
|
||||
y: root.margin
|
||||
id: noDefaultCatalogRow
|
||||
spacing: root.margin
|
||||
function goBack()
|
||||
{
|
||||
detailsView.visible = false;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "The official FlightGear aircraft hangar is not added, so many standard "
|
||||
+ "aircraft will not be available. You can add the hangar now, or hide "
|
||||
+ "this message. The offical hangar can always be restored from the 'Add-Ons' page."
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: noDefaultCatalogBox.width - (addDefaultButton.width + hideButton.width + root.margin * 3)
|
||||
}
|
||||
AircraftDetailsView {
|
||||
id: detailsView
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
|
||||
Button {
|
||||
id: addDefaultButton
|
||||
text: qsTr("Add default hangar")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Button {
|
||||
anchors { left: parent.left; top: parent.top; margins: root.margin }
|
||||
width: 60
|
||||
|
||||
onClicked: {
|
||||
_launcher.officialCatalogAction("add-official");
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: hideButton
|
||||
text: qsTr("Hide")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: {
|
||||
_launcher.officialCatalogAction("hide");
|
||||
}
|
||||
id: backButton
|
||||
text: "< Back"
|
||||
onClicked: {
|
||||
// ensure that if the variant was changed inside the detailsView,
|
||||
// that we update our selection correctly
|
||||
aircraftList.updateSelectionFromLauncher();
|
||||
root.goBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no default catalog pop-over
|
||||
}
|
||||
|
||||
|
|
|
@ -298,14 +298,6 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
}
|
||||
}
|
||||
|
||||
if (role == AircraftThumbnailSizeRole) {
|
||||
QPixmap pm = item->thumbnail(false);
|
||||
if (pm.isNull()) {
|
||||
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
|
||||
}
|
||||
return pm.size();
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (item->description.isEmpty()) {
|
||||
return tr("Missing description for: %1").arg(item->baseName());
|
||||
|
@ -320,20 +312,12 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
return item->authors;
|
||||
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
|
||||
return item->ratings[role - AircraftRatingRole];
|
||||
} else if (role == AircraftPreviewsRole) {
|
||||
QVariantList result;
|
||||
Q_FOREACH(QUrl u, item->previews) {
|
||||
result.append(u);
|
||||
}
|
||||
return result;
|
||||
} else if (role == AircraftHasPreviewsRole) {
|
||||
return !item->previews.empty();
|
||||
} else if (role == AircraftThumbnailRole) {
|
||||
return item->thumbnail();
|
||||
} else if (role == AircraftPackageIdRole) {
|
||||
// can we fake an ID? otherwise fall through to a null variant
|
||||
} else if (role == AircraftPackageStatusRole) {
|
||||
return PackageInstalled; // always the case
|
||||
return LocalAircraftCache::PackageInstalled; // always the case
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
return item->path;
|
||||
} else if (role == AircraftURIRole) {
|
||||
|
@ -395,34 +379,25 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
|
|||
InstallRef i = item->existingInstall();
|
||||
if (i.valid()) {
|
||||
if (i->isDownloading()) {
|
||||
return PackageDownloading;
|
||||
return LocalAircraftCache::PackageDownloading;
|
||||
}
|
||||
if (i->isQueued()) {
|
||||
return PackageQueued;
|
||||
return LocalAircraftCache::PackageQueued;
|
||||
}
|
||||
if (i->hasUpdate()) {
|
||||
return PackageUpdateAvailable;
|
||||
return LocalAircraftCache::PackageUpdateAvailable;
|
||||
}
|
||||
|
||||
return PackageInstalled;
|
||||
return LocalAircraftCache::PackageInstalled;
|
||||
} else {
|
||||
return PackageNotInstalled;
|
||||
return LocalAircraftCache::PackageNotInstalled;
|
||||
}
|
||||
} else if (role == AircraftVariantCountRole) {
|
||||
// this value wants the number of aditional variants, i.e not
|
||||
// including the primary. Hence the -1 term.
|
||||
return static_cast<quint32>(item->variants().size() - 1);
|
||||
} else if (role == AircraftThumbnailSizeRole) {
|
||||
QPixmap pm = packageThumbnail(item, state, false).value<QPixmap>();
|
||||
if (pm.isNull())
|
||||
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
|
||||
return pm.size();
|
||||
} else if (role == AircraftThumbnailRole) {
|
||||
return packageThumbnail(item, state);
|
||||
} else if (role == AircraftPreviewsRole) {
|
||||
return packagePreviews(item, state);
|
||||
} else if (role == AircraftHasPreviewsRole) {
|
||||
return !item->previewsForVariant(state.variant).empty();
|
||||
} else if (role == AircraftAuthorsRole) {
|
||||
std::string authors = item->getLocalisedProp("author", state.variant);
|
||||
if (!authors.empty()) {
|
||||
|
@ -492,35 +467,6 @@ QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState&
|
|||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::packagePreviews(PackageRef p, const DelegateState& ds) const
|
||||
{
|
||||
const Package::PreviewVec& previews = p->previewsForVariant(ds.variant);
|
||||
if (previews.empty()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariantList result;
|
||||
// if we have an install, return file URLs, not remote (http) ones
|
||||
InstallRef ex = p->existingInstall();
|
||||
if (ex.valid()) {
|
||||
for (auto p : previews) {
|
||||
SGPath localPreviewPath = ex->path() / p.path;
|
||||
if (!localPreviewPath.exists()) {
|
||||
qWarning() << "missing local preview" << QString::fromStdString(localPreviewPath.utf8Str());
|
||||
continue;
|
||||
}
|
||||
result.append(QUrl::fromLocalFile(QString::fromStdString(localPreviewPath.utf8Str())));
|
||||
}
|
||||
}
|
||||
|
||||
// return remote urls
|
||||
for (auto p : previews) {
|
||||
result.append(QUrl(QString::fromStdString(p.url)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
int row = index.row();
|
||||
|
@ -553,7 +499,6 @@ QHash<int, QByteArray> AircraftItemModel::roleNames() const
|
|||
result[AircraftPackageSizeRole] = "packageSizeBytes";
|
||||
result[AircraftPackageStatusRole] = "packageStatus";
|
||||
|
||||
result[AircraftHasPreviewsRole] = "hasPreviews";
|
||||
result[AircraftInstallDownloadedSizeRole] = "downloadedBytes";
|
||||
result[AircraftVariantRole] = "activeVariant";
|
||||
|
||||
|
@ -655,6 +600,10 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
{
|
||||
if (uri.isLocalFile()) {
|
||||
AircraftItemPtr item = LocalAircraftCache::instance()->findItemWithUri(uri);
|
||||
if (!item) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const QString path = uri.toLocalFile();
|
||||
if (item->path == path) {
|
||||
return item->description;
|
||||
|
@ -675,10 +624,9 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
}
|
||||
} else {
|
||||
qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QString();
|
||||
return {};
|
||||
}
|
||||
|
||||
void AircraftItemModel::onScanAddedItems(int count)
|
||||
|
|
|
@ -47,18 +47,14 @@ const int AircraftInstallPercentRole = Qt::UserRole + 11;
|
|||
const int AircraftPackageSizeRole = Qt::UserRole + 12;
|
||||
const int AircraftInstallDownloadedSizeRole = Qt::UserRole + 13;
|
||||
const int AircraftURIRole = Qt::UserRole + 14;
|
||||
const int AircraftThumbnailSizeRole = Qt::UserRole + 15;
|
||||
const int AircraftIsHelicopterRole = Qt::UserRole + 16;
|
||||
const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
|
||||
const int AircraftPackageRefRole = Qt::UserRole + 19;
|
||||
const int AircraftThumbnailRole = Qt::UserRole + 20;
|
||||
const int AircraftPreviewsRole = Qt::UserRole + 21;
|
||||
|
||||
const int AircraftStatusRole = Qt::UserRole + 22;
|
||||
const int AircraftMinVersionRole = Qt::UserRole + 23;
|
||||
|
||||
const int AircraftHasPreviewsRole = Qt::UserRole + 24;
|
||||
|
||||
const int AircraftRatingRole = Qt::UserRole + 100;
|
||||
const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
|
||||
|
||||
|
@ -73,16 +69,7 @@ class AircraftItemModel : public QAbstractListModel
|
|||
Q_PROPERTY(int aircraftNeedingUpdated READ aircraftNeedingUpdated NOTIFY aircraftNeedingUpdatedChanged)
|
||||
Q_PROPERTY(bool showUpdateAll READ showUpdateAll WRITE setShowUpdateAll NOTIFY aircraftNeedingUpdatedChanged)
|
||||
|
||||
Q_ENUMS(AircraftItemStatus)
|
||||
Q_ENUMS(AircraftStatus)
|
||||
public:
|
||||
enum AircraftItemStatus {
|
||||
PackageNotInstalled = 0,
|
||||
PackageInstalled,
|
||||
PackageUpdateAvailable,
|
||||
PackageQueued,
|
||||
PackageDownloading
|
||||
};
|
||||
|
||||
AircraftItemModel(QObject* pr);
|
||||
|
||||
|
|
87
src/GUI/AircraftPreviewPanel.qml
Normal file
87
src/GUI/AircraftPreviewPanel.qml
Normal file
|
@ -0,0 +1,87 @@
|
|||
import QtQuick 2.0
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var previews: []
|
||||
property int activePreview: 0
|
||||
|
||||
readonly property bool __havePreviews: (previews.length > 0)
|
||||
onPreviewsChanged: {
|
||||
activePreview = 0
|
||||
}
|
||||
|
||||
height: width / (16/9)
|
||||
color: "#7f7f7f"
|
||||
|
||||
border.width: 1
|
||||
border.color: "#3f3f3f"
|
||||
|
||||
Timer {
|
||||
id: previewCycleTimer
|
||||
}
|
||||
|
||||
PreviewImage {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
imageUrl: __havePreviews ? root.previews[root.activePreview] : ""
|
||||
}
|
||||
|
||||
Row {
|
||||
visible: (previews.length > 1)
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 8
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: root.previews
|
||||
Rectangle {
|
||||
height: 8
|
||||
width: 8
|
||||
radius: 4
|
||||
color: (model.index == root.activePreview) ? "white" : "#cfcfcf"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: leftArrow
|
||||
|
||||
source: "qrc:///preview/left-arrow-icon"
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: leftMouseArea.containsMouse ? 1.0 : 0.3
|
||||
visible: root.activePreview > 0
|
||||
|
||||
MouseArea {
|
||||
id: leftMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
root.activePreview = Math.max(root.activePreview - 1, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: rightArrow
|
||||
|
||||
source: "qrc:///preview/right-arrow-icon"
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: rightMouseArea.containsMouse ? 1.0 : 0.3
|
||||
visible: root.activePreview < (root.previews.length - 1)
|
||||
|
||||
MouseArea {
|
||||
id: rightMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
root.activePreview = Math.min(root.activePreview + 1, root.previews.length - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
src/GUI/AircraftRatingsPanel.qml
Normal file
97
src/GUI/AircraftRatingsPanel.qml
Normal file
|
@ -0,0 +1,97 @@
|
|||
import QtQuick 2.2
|
||||
import FlightGear.Launcher 1.0 as FG
|
||||
|
||||
ListHeaderBox
|
||||
{
|
||||
contents: [
|
||||
|
||||
ToggleSwitch {
|
||||
id: doFilterCheck
|
||||
checked: _launcher.browseAircraftModel.ratingsFilterEnabled
|
||||
|
||||
onCheckedChanged: {
|
||||
_launcher.browseAircraftModel.ratingsFilterEnabled = checked
|
||||
}
|
||||
|
||||
label: qsTr("Filter aircraft based on rating")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
},
|
||||
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.margin
|
||||
text: qsTr("Adjust minimum ratings")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
editRatingsPanel.visible = true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// mouse are behind panel to consume clicks
|
||||
MouseArea {
|
||||
width: 10000 // deliberately huge values here
|
||||
height: 10000
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
visible: editRatingsPanel.visible
|
||||
|
||||
onClicked: {
|
||||
editRatingsPanel.visible = false
|
||||
}
|
||||
},
|
||||
|
||||
Rectangle {
|
||||
id: editRatingsPanel
|
||||
visible: false
|
||||
width: parent.width
|
||||
y: parent.height - 1
|
||||
height: childrenRect.height + 24
|
||||
|
||||
border.width: 1
|
||||
border.color: "#9f9f9f"
|
||||
|
||||
Column {
|
||||
y: 12
|
||||
spacing: 24
|
||||
|
||||
Text {
|
||||
text: qsTr("Aircraft are rated by the community based on four critiera, on a scale from " +
|
||||
"one to five. The ratings are designed to help make an informed guess how "+
|
||||
"complete and functional an aircraft is.")
|
||||
width: editRatingsPanel.width - 100
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
RatingSlider {
|
||||
label: qsTr("Minimum flight-model (FDM) rating:")
|
||||
ratings: _launcher.browseAircraftModel.ratings
|
||||
ratingIndex: 0
|
||||
}
|
||||
|
||||
RatingSlider {
|
||||
label: qsTr("Minimum visual model rating")
|
||||
ratings: _launcher.browseAircraftModel.ratings
|
||||
ratingIndex: 1
|
||||
}
|
||||
|
||||
RatingSlider {
|
||||
label: qsTr("Minimum systems rating")
|
||||
ratings: _launcher.browseAircraftModel.ratings
|
||||
ratingIndex: 2
|
||||
}
|
||||
|
||||
RatingSlider {
|
||||
label: qsTr("Minimum FDM rating")
|
||||
ratings: _launcher.browseAircraftModel.ratings
|
||||
ratingIndex: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
|
@ -3,15 +3,24 @@
|
|||
#include "AircraftModel.hxx"
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
AircraftProxyModel::AircraftProxyModel(QObject *pr) :
|
||||
AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) :
|
||||
QSortFilterProxyModel(pr)
|
||||
{
|
||||
m_ratings = {3, 3, 3, 3};
|
||||
setSourceModel(source);
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
setSortRole(Qt::DisplayRole);
|
||||
setDynamicSortFilter(true);
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setRatings(int *ratings)
|
||||
void AircraftProxyModel::setRatings(QList<int> ratings)
|
||||
{
|
||||
::memcpy(m_ratings, ratings, sizeof(int) * 4);
|
||||
if (ratings == m_ratings)
|
||||
return;
|
||||
m_ratings = ratings;
|
||||
invalidate();
|
||||
emit ratingsChanged();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setAircraftFilterString(QString s)
|
||||
|
@ -27,6 +36,22 @@ void AircraftProxyModel::setAircraftFilterString(QString s)
|
|||
invalidate();
|
||||
}
|
||||
|
||||
int AircraftProxyModel::indexForURI(QUrl uri) const
|
||||
{
|
||||
auto sourceIndex = qobject_cast<AircraftItemModel*>(sourceModel())->indexOfAircraftURI(uri);
|
||||
auto ourIndex = mapFromSource(sourceIndex);
|
||||
if (!sourceIndex.isValid() || !ourIndex.isValid()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ourIndex.row();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::selectVariantForAircraftURI(QUrl uri)
|
||||
{
|
||||
qobject_cast<AircraftItemModel*>(sourceModel())->selectVariantForAircraftURI(uri);
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setRatingFilterEnabled(bool e)
|
||||
{
|
||||
if (e == m_ratingsFilter) {
|
||||
|
@ -35,6 +60,7 @@ void AircraftProxyModel::setRatingFilterEnabled(bool e)
|
|||
|
||||
m_ratingsFilter = e;
|
||||
invalidate();
|
||||
emit ratingsFilterEnabledChanged();
|
||||
}
|
||||
|
||||
void AircraftProxyModel::setInstalledFilterEnabled(bool e)
|
||||
|
@ -58,8 +84,8 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||
|
||||
if (m_onlyShowInstalled) {
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
const auto status = static_cast<AircraftItemModel::AircraftItemStatus>(v.toInt());
|
||||
if (status == AircraftItemModel::PackageNotInstalled) {
|
||||
const auto status = static_cast<LocalAircraftCache::PackageStatus>(v.toInt());
|
||||
if (status == LocalAircraftCache::PackageNotInstalled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -67,8 +93,8 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||
// 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) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
if (m_ratings[i] > index.data(AircraftRatingRole + i).toInt()) {
|
||||
for (int i=0; i<m_ratings.size(); ++i) {
|
||||
if (m_ratings.at(i) > index.data(AircraftRatingRole + i).toInt()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,39 @@ class AircraftProxyModel : public QSortFilterProxyModel
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AircraftProxyModel(QObject* pr);
|
||||
AircraftProxyModel(QObject* pr, QAbstractItemModel * source);
|
||||
|
||||
void setRatings(int* ratings);
|
||||
Q_PROPERTY(QList<int> ratings READ ratings WRITE setRatings NOTIFY ratingsChanged)
|
||||
|
||||
void setAircraftFilterString(QString s);
|
||||
Q_PROPERTY(bool ratingsFilterEnabled READ ratingsFilterEnabled WRITE setRatingFilterEnabled NOTIFY ratingsFilterEnabledChanged)
|
||||
|
||||
Q_INVOKABLE void setAircraftFilterString(QString s);
|
||||
|
||||
/**
|
||||
* Compute the row (index in QML / ListView speak) based on an aircraft URI.
|
||||
* Return -1 if the UIR is not present in the (filtered) model
|
||||
**/
|
||||
Q_INVOKABLE int indexForURI(QUrl uri) const;
|
||||
|
||||
Q_INVOKABLE void selectVariantForAircraftURI(QUrl uri);
|
||||
|
||||
QList<int> ratings() const
|
||||
{
|
||||
return m_ratings;
|
||||
}
|
||||
|
||||
bool ratingsFilterEnabled() const
|
||||
{
|
||||
return m_ratingsFilter;
|
||||
}
|
||||
|
||||
void setRatings(QList<int> ratings);
|
||||
void setRatingFilterEnabled(bool e);
|
||||
signals:
|
||||
void ratingsChanged();
|
||||
void ratingsFilterEnabledChanged();
|
||||
|
||||
public slots:
|
||||
void setRatingFilterEnabled(bool e);
|
||||
|
||||
void setInstalledFilterEnabled(bool e);
|
||||
|
||||
|
@ -28,7 +53,7 @@ private:
|
|||
|
||||
bool m_ratingsFilter = true;
|
||||
bool m_onlyShowInstalled = false;
|
||||
int m_ratings[4] = {3, 3, 3, 3};
|
||||
QList<int> m_ratings;
|
||||
QString m_filterString;
|
||||
SGPropertyNode_ptr m_filterProps;
|
||||
};
|
||||
|
|
131
src/GUI/AircraftVariantChoice.qml
Normal file
131
src/GUI/AircraftVariantChoice.qml
Normal file
|
@ -0,0 +1,131 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Window 2.0
|
||||
|
||||
import FlightGear.Launcher 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
implicitHeight: title.implicitHeight
|
||||
|
||||
radius: 4
|
||||
border.color: "#68A6E1"
|
||||
border.width: headingMouseArea.containsMouse ? 1 : 0
|
||||
color: headingMouseArea.containsMouse ? "#7fffffff" : "transparent"
|
||||
|
||||
readonly property bool __enabled: aircraftInfo.numVariants > 1
|
||||
|
||||
property alias aircraft: aircraftInfo.uri
|
||||
property alias currentIndex: aircraftInfo.variant
|
||||
property alias fontPixelSize: title.font.pixelSize
|
||||
property int popupFontPixelSize: 0
|
||||
|
||||
signal selected(var index)
|
||||
|
||||
Text {
|
||||
id: title
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: upDownIcon.left
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.rightMargin: 4
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 24
|
||||
text: aircraftInfo.name
|
||||
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
color: "black"
|
||||
}
|
||||
|
||||
|
||||
Image {
|
||||
id: upDownIcon
|
||||
source: "qrc:///up-down-arrow"
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: __enabled
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headingMouseArea
|
||||
enabled: __enabled
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
var screenPos = _launcher.mapToGlobal(title, Qt.point(0, -title.height * currentIndex))
|
||||
if (screenPos.y < 0) {
|
||||
// if the popup would appear off the screen, use the first item
|
||||
// position instead
|
||||
screenPos = _launcher.mapToGlobal(title, Qt.point(0, 0))
|
||||
}
|
||||
|
||||
popupFrame.x = screenPos.x;
|
||||
popupFrame.y = screenPos.y;
|
||||
popupFrame.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
AircraftInfo
|
||||
{
|
||||
id: aircraftInfo
|
||||
}
|
||||
|
||||
Window {
|
||||
id: popupFrame
|
||||
|
||||
modality: Qt.WindowModal
|
||||
width: root.width
|
||||
flags: Qt.Popup
|
||||
height: choicesColumn.childrenRect.height
|
||||
visible: false
|
||||
color: "white"
|
||||
|
||||
Rectangle {
|
||||
border.width: 1
|
||||
border.color: "#afafaf"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Column {
|
||||
id: choicesColumn
|
||||
|
||||
Repeater {
|
||||
model: popupFrame.visible ? aircraftInfo.variantNames : 0
|
||||
delegate: Item {
|
||||
width: popupFrame.width
|
||||
height: choiceText.implicitHeight
|
||||
|
||||
Text {
|
||||
id: choiceText
|
||||
text: modelData
|
||||
|
||||
// allow override the size in case the title size is enormous
|
||||
font.pixelSize: (popupFontPixelSize > 0) ? popupFontPixelSize : title.font.pixelSize
|
||||
|
||||
color: choiceArea.containsMouse ? "#68A6E1" : "black"
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 4
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: choiceArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.selected(model.index)
|
||||
popupFrame.visible = false
|
||||
}
|
||||
}
|
||||
} // of delegate Item
|
||||
} // of Repeater
|
||||
}
|
||||
} // of popup frame
|
||||
}
|
|
@ -6,7 +6,7 @@ Rectangle {
|
|||
property int aircraftStatus
|
||||
property var requiredFGVersion
|
||||
|
||||
visible: (model.aircraftStatus != AircraftModel.AircraftOk)
|
||||
visible: (aircraftStatus != LocalAircraftCache.AircraftOk)
|
||||
|
||||
implicitHeight: warningText.height + 8
|
||||
|
||||
|
@ -23,7 +23,7 @@ Rectangle {
|
|||
|
||||
State {
|
||||
name: "sim-version-too-low"
|
||||
when: aircraftStatus == AircraftModel.AircraftNeedsNewerSimulator
|
||||
when: aircraftStatus == LocalAircraftCache.AircraftNeedsNewerSimulator
|
||||
|
||||
PropertyChanges {
|
||||
target: warningText
|
||||
|
@ -33,7 +33,7 @@ Rectangle {
|
|||
|
||||
State {
|
||||
name: "unmaintained"
|
||||
when: aircraftStatus == AircraftModel.AircraftUnmaintained
|
||||
when: aircraftStatus == LocalAircraftCache.AircraftUnmaintained
|
||||
|
||||
PropertyChanges {
|
||||
target: warningText
|
||||
|
|
|
@ -19,7 +19,6 @@ Item {
|
|||
|
||||
Image {
|
||||
id: img
|
||||
visible: (model.variantCount > 0)
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Rectangle {
|
|||
signal clicked
|
||||
|
||||
width: 120
|
||||
height: 30
|
||||
height: buttonText.implicitHeight + (radius * 2)
|
||||
radius: 6
|
||||
|
||||
color: mouse.containsMouse ? "#064989" : "#1b7ad3"
|
||||
|
|
|
@ -68,12 +68,10 @@ endif()
|
|||
|
||||
if (HAVE_QT)
|
||||
qt5_wrap_ui(uic_sources Launcher.ui
|
||||
EditRatingsFilterDialog.ui
|
||||
SetupRootDialog.ui
|
||||
AddCatalogDialog.ui
|
||||
PathsDialog.ui
|
||||
LocationWidget.ui
|
||||
NoOfficialHangar.ui
|
||||
InstallSceneryDialog.ui
|
||||
)
|
||||
qt5_add_resources(qrc_sources resources.qrc)
|
||||
|
@ -100,8 +98,6 @@ if (HAVE_QT)
|
|||
AirportDiagram.hxx
|
||||
NavaidDiagram.cxx
|
||||
NavaidDiagram.hxx
|
||||
EditRatingsFilterDialog.cxx
|
||||
EditRatingsFilterDialog.hxx
|
||||
ExtraSettingsSection.cxx
|
||||
ExtraSettingsSection.hxx
|
||||
SetupRootDialog.cxx
|
||||
|
@ -122,8 +118,6 @@ if (HAVE_QT)
|
|||
QtFileDialog.hxx
|
||||
InstallSceneryDialog.hxx
|
||||
InstallSceneryDialog.cxx
|
||||
PreviewWindow.cxx
|
||||
PreviewWindow.hxx
|
||||
SettingsSection.cxx
|
||||
SettingsSection.hxx
|
||||
SettingsSectionQML.cxx
|
||||
|
@ -146,18 +140,13 @@ if (HAVE_QT)
|
|||
ViewCommandLinePage.hxx
|
||||
MPServersModel.cpp
|
||||
MPServersModel.h
|
||||
ThumbnailImageItem.cxx
|
||||
ThumbnailImageItem.hxx
|
||||
FlickableExtentQuery.cxx
|
||||
FlickableExtentQuery.hxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
||||
set_property(TARGET fglauncher PROPERTY AUTOMOC ON)
|
||||
target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network Qt5::Qml Qt5::Quick Qt5::QuickWidgets SimGearCore)
|
||||
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI
|
||||
${Qt5Quick_PRIVATE_INCLUDE_DIRS})
|
||||
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
|
||||
|
||||
add_library(fgqmlui QQuickDrawable.cxx
|
||||
QQuickDrawable.hxx
|
||||
|
@ -173,12 +162,17 @@ if (HAVE_QT)
|
|||
QmlAircraftInfo.hxx
|
||||
LocalAircraftCache.cxx
|
||||
LocalAircraftCache.hxx
|
||||
|
||||
PreviewImageItem.cxx
|
||||
PreviewImageItem.hxx
|
||||
ThumbnailImageItem.cxx
|
||||
ThumbnailImageItem.hxx
|
||||
FlickableExtentQuery.cxx
|
||||
FlickableExtentQuery.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
target_link_libraries(fgqmlui Qt5::Quick Qt5::Network Qt5::Qml SimGearCore)
|
||||
target_include_directories(fgqmlui PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
|
||||
target_include_directories(fgqmlui PRIVATE ${PROJECT_BINARY_DIR}/src/GUI ${Qt5Quick_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
|
||||
|
|
45
src/GUI/Checkbox.qml
Normal file
45
src/GUI/Checkbox.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property bool checked: false
|
||||
property alias label: label.text
|
||||
|
||||
implicitWidth: checkBox.width + label.width + 16
|
||||
implicitHeight: label.height
|
||||
|
||||
Rectangle {
|
||||
id: checkBox
|
||||
width: 18
|
||||
height: 18
|
||||
border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f"
|
||||
border.width: 1
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
anchors.centerIn: parent
|
||||
id: checkMark
|
||||
color: "#9f9f9f"
|
||||
visible: checked
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.left: checkBox.right
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
checked = !checked
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// EditEatingsFilterDialog.cxx - part of GUI launcher using Qt5
|
||||
//
|
||||
// Written by James Turner, started December 2014.
|
||||
//
|
||||
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// 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 "EditRatingsFilterDialog.hxx"
|
||||
#include "ui_EditRatingsFilterDialog.h"
|
||||
|
||||
EditRatingsFilterDialog::EditRatingsFilterDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::EditRatingsFilterDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
EditRatingsFilterDialog::~EditRatingsFilterDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void EditRatingsFilterDialog::setRatings(int *ratings)
|
||||
{
|
||||
for (int i=0; i<4; ++i) {
|
||||
QAbstractSlider* s = sliderForIndex(i);
|
||||
s->setValue(ratings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int EditRatingsFilterDialog::getRating(int index) const
|
||||
{
|
||||
return sliderForIndex(index)->value();
|
||||
}
|
||||
|
||||
QAbstractSlider* EditRatingsFilterDialog::sliderForIndex(int index) const
|
||||
{
|
||||
switch (index) {
|
||||
case 0: return ui->fdmSlider;
|
||||
case 1: return ui->systemsSlider;
|
||||
case 2: return ui->cockpitSlider;
|
||||
case 3: return ui->modelSlider;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// EditEatingsFilterDialog.hxx - part of GUI launcher using Qt5
|
||||
//
|
||||
// Written by James Turner, started December 2014.
|
||||
//
|
||||
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef EDITRATINGSFILTERDIALOG_HXX
|
||||
#define EDITRATINGSFILTERDIALOG_HXX
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class EditRatingsFilterDialog;
|
||||
}
|
||||
|
||||
class QAbstractSlider;
|
||||
|
||||
class EditRatingsFilterDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EditRatingsFilterDialog(QWidget *parent = 0);
|
||||
~EditRatingsFilterDialog();
|
||||
|
||||
void setRatings(int* ratings);
|
||||
|
||||
int getRating(int index) const;
|
||||
private:
|
||||
Ui::EditRatingsFilterDialog *ui;
|
||||
|
||||
QAbstractSlider* sliderForIndex(int index) const;
|
||||
};
|
||||
|
||||
#endif // EDITRATINGSFILTERDIALOG_HXX
|
|
@ -1,245 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EditRatingsFilterDialog</class>
|
||||
<widget class="QDialog" name="EditRatingsFilterDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>623</width>
|
||||
<height>594</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Specify the minimum required completeness of aircraft in each area for it to be shown in the aircraft list.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Cockpit:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QSlider" name="cockpitSlider">
|
||||
<property name="maximum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>3D panel, cockpit and instrumentation status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Flight model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QSlider" name="fdmSlider">
|
||||
<property name="maximum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Accuracy of the flight (aerodynamic) model compared to available data and testing/</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Exterior model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QSlider" name="modelSlider">
|
||||
<property name="maximum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Quality and detail of exterior model, including animated moving parts such as control surfaces and under-carriage</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Systems:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QSlider" name="systemsSlider">
|
||||
<property name="maximum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Completeness of systems modellings, including fuel handling, autopilot operation, startup & failure procedures.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>EditRatingsFilterDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>EditRatingsFilterDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -446,7 +446,7 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="aircraftPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0,1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
|
@ -462,80 +462,6 @@
|
|||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Search:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="aircraftFilter">
|
||||
<property name="placeholderText">
|
||||
<string>Search aircraft (press Enter to search)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="onlyShowInstalledCheck">
|
||||
<property name="text">
|
||||
<string>Only show installed aircraft</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ratingsFilterCheck">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hide aircraft based on completeness (rating)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editRatingFilter">
|
||||
<property name="text">
|
||||
<string>Edit...</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QQuickWidget" name="aircraftList"/>
|
||||
</item>
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkDiskCache>
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlContext>
|
||||
|
@ -28,21 +31,21 @@
|
|||
|
||||
// launcher headers
|
||||
#include "QtLauncher.hxx"
|
||||
#include "EditRatingsFilterDialog.hxx"
|
||||
#include "AircraftModel.hxx"
|
||||
#include "PathsDialog.hxx"
|
||||
#include "AircraftSearchFilterModel.hxx"
|
||||
#include "DefaultAircraftLocator.hxx"
|
||||
#include "SettingsWidgets.hxx"
|
||||
#include "PreviewWindow.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
#include "SettingsSectionQML.hxx"
|
||||
#include "ExtraSettingsSection.hxx"
|
||||
#include "ViewCommandLinePage.hxx"
|
||||
#include "MPServersModel.h"
|
||||
#include "ThumbnailImageItem.hxx"
|
||||
#include "PreviewImageItem.hxx"
|
||||
#include "FlickableExtentQuery.hxx"
|
||||
#include "LocalAircraftCache.hxx"
|
||||
#include "QmlAircraftInfo.hxx"
|
||||
|
||||
#include "ui_Launcher.h"
|
||||
|
||||
|
@ -100,7 +103,6 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
connect(viewCommandLineAction, &QAction::triggered,
|
||||
this, &LauncherMainWindow::onViewCommandLine);
|
||||
|
||||
m_ui->aircraftFilter->setClearButtonEnabled(true);
|
||||
m_serversModel = new MPServersModel(this);
|
||||
m_serversModel->refresh();
|
||||
|
||||
|
@ -112,18 +114,6 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
this, &LauncherMainWindow::onSubsytemIdleTimeout);
|
||||
m_subsystemIdleTimer->start();
|
||||
|
||||
// create and configure the proxy model
|
||||
m_aircraftProxy = new AircraftProxyModel(this);
|
||||
connect(m_ui->ratingsFilterCheck, &QAbstractButton::toggled,
|
||||
m_aircraftProxy, &AircraftProxyModel::setRatingFilterEnabled);
|
||||
connect(m_ui->ratingsFilterCheck, &QAbstractButton::toggled,
|
||||
this, &LauncherMainWindow::maybeRestoreAircraftSelection);
|
||||
|
||||
connect(m_ui->onlyShowInstalledCheck, &QAbstractButton::toggled,
|
||||
m_aircraftProxy, &AircraftProxyModel::setInstalledFilterEnabled);
|
||||
connect(m_ui->aircraftFilter, &QLineEdit::textChanged,
|
||||
m_aircraftProxy, &AircraftProxyModel::setAircraftFilterString);
|
||||
|
||||
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);
|
||||
|
@ -145,26 +135,22 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit);
|
||||
addAction(qa);
|
||||
|
||||
connect(m_ui->editRatingFilter, &QPushButton::clicked,
|
||||
this, &LauncherMainWindow::onEditRatingsFilter);
|
||||
connect(m_ui->onlyShowInstalledCheck, &QCheckBox::toggled,
|
||||
this, &LauncherMainWindow::onShowInstalledAircraftToggled);
|
||||
|
||||
QIcon historyIcon(":/history-icon");
|
||||
m_ui->aircraftHistory->setIcon(historyIcon);
|
||||
m_ui->locationHistory->setIcon(historyIcon);
|
||||
|
||||
m_aircraftModel = new AircraftItemModel(this);
|
||||
m_aircraftProxy->setSourceModel(m_aircraftModel);
|
||||
|
||||
m_aircraftProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_aircraftProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_aircraftProxy->setSortRole(Qt::DisplayRole);
|
||||
m_aircraftProxy->setDynamicSortFilter(true);
|
||||
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
m_installedAircraftModel->setInstalledFilterEnabled(true);
|
||||
|
||||
m_browseAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
m_browseAircraftModel->setRatingFilterEnabled(true);
|
||||
|
||||
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
|
||||
|
||||
m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
|
||||
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_filteredModel", m_aircraftProxy);
|
||||
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_aircraftModel", m_aircraftModel);
|
||||
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", this);
|
||||
m_ui->aircraftList->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
|
||||
|
@ -187,13 +173,6 @@ LauncherMainWindow::LauncherMainWindow() :
|
|||
this, &LauncherMainWindow::onAircraftPathsChanged);
|
||||
m_ui->stack->addWidget(addOnsPage);
|
||||
|
||||
// after any kind of reset, try to restore selection and scroll
|
||||
// to match the m_selectedAircraft. This needs to be delayed
|
||||
// fractionally otherwise the scrollTo seems to be ignored,
|
||||
// unfortunately.
|
||||
connect(m_aircraftProxy, &AircraftProxyModel::modelReset,
|
||||
this, &LauncherMainWindow::delayedAircraftModelReset);
|
||||
|
||||
QSettings settings;
|
||||
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
|
||||
LocalAircraftCache::instance()->scanDirs();
|
||||
|
@ -224,12 +203,15 @@ void LauncherMainWindow::initQML()
|
|||
qmlRegisterType<SettingsDateTime>("FlightGear.Launcher", 1, 0, "DateTime");
|
||||
qmlRegisterType<SettingsPath>("FlightGear.Launcher", 1, 0, "PathChooser");
|
||||
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
|
||||
qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
|
||||
|
||||
qmlRegisterUncreatableType<SettingsControl>("FlightGear.Launcher", 1, 0, "Control", "Base class");
|
||||
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
|
||||
|
||||
qmlRegisterType<FlickableExtentQuery>("FlightGear.Launcher", 1, 0, "FlickableExtentQuery");
|
||||
|
||||
qmlRegisterType<QmlAircraftInfo>("FlightGear.Launcher", 1, 0, "AircraftInfo");
|
||||
|
||||
m_config = new LaunchConfig(this);
|
||||
connect(m_config, &LaunchConfig::collect, this, &LauncherMainWindow::collectAircraftArgs);
|
||||
m_ui->location->setLaunchConfig(m_config);
|
||||
|
@ -251,8 +233,19 @@ void LauncherMainWindow::initQML()
|
|||
flightgear::WeatherScenariosModel* weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
|
||||
m_qmlEngine->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel);
|
||||
|
||||
qmlRegisterUncreatableType<LocalAircraftCache>("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache");
|
||||
qmlRegisterUncreatableType<AircraftItemModel>("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model");
|
||||
qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage");
|
||||
qmlRegisterType<PreviewImageItem>("FlightGear.Launcher", 1, 0, "PreviewImage");
|
||||
|
||||
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
|
||||
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
|
||||
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
|
||||
|
||||
QNetworkAccessManager* netAccess = new QNetworkAccessManager(this);
|
||||
netAccess->setCache(diskCache);
|
||||
PreviewImageItem::setGlobalNetworkAccess(netAccess);
|
||||
|
||||
}
|
||||
|
||||
void LauncherMainWindow::buildSettingsSections()
|
||||
|
@ -389,23 +382,7 @@ void LauncherMainWindow::restoreSettings()
|
|||
|
||||
m_ui->location->restoreLocation(currentLocation);
|
||||
|
||||
// rating filters
|
||||
m_ui->onlyShowInstalledCheck->setChecked(settings.value("only-show-installed", false).toBool());
|
||||
if (m_ui->onlyShowInstalledCheck->isChecked()) {
|
||||
m_ui->ratingsFilterCheck->setEnabled(false);
|
||||
}
|
||||
|
||||
m_ui->ratingsFilterCheck->setChecked(settings.value("ratings-filter", true).toBool());
|
||||
int index = 0;
|
||||
Q_FOREACH(QVariant v, settings.value("min-ratings").toList()) {
|
||||
m_ratingFilters[index++] = v.toInt();
|
||||
}
|
||||
|
||||
m_aircraftProxy->setRatingFilterEnabled(m_ui->ratingsFilterCheck->isChecked());
|
||||
m_aircraftProxy->setRatings(m_ratingFilters);
|
||||
|
||||
updateSelectedAircraft();
|
||||
maybeRestoreAircraftSelection();
|
||||
|
||||
Q_FOREACH(SettingsSection* ss, findChildren<SettingsSection*>()) {
|
||||
ss->restoreState(settings);
|
||||
|
@ -414,31 +391,11 @@ void LauncherMainWindow::restoreSettings()
|
|||
m_serversModel->requestRestore();
|
||||
}
|
||||
|
||||
void LauncherMainWindow::delayedAircraftModelReset()
|
||||
{
|
||||
QTimer::singleShot(1, this, SLOT(maybeRestoreAircraftSelection()));
|
||||
}
|
||||
|
||||
void LauncherMainWindow::maybeRestoreAircraftSelection()
|
||||
{
|
||||
QModelIndex aircraftIndex = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
||||
QModelIndex proxyIndex = m_aircraftProxy->mapFromSource(aircraftIndex);
|
||||
if (proxyIndex.isValid()) {
|
||||
emit selectAircraftIndex(proxyIndex.row());
|
||||
// and also select the correct variant on the model
|
||||
m_aircraftModel->selectVariantForAircraftURI(m_selectedAircraft);
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherMainWindow::saveSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked());
|
||||
settings.setValue("only-show-installed", m_ui->onlyShowInstalledCheck->isChecked());
|
||||
settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
|
||||
settings.setValue("recent-location-sets", m_recentLocations);
|
||||
|
||||
settings.setValue("window-geometry", saveGeometry());
|
||||
|
||||
Q_FOREACH(SettingsSection* ss, findChildren<SettingsSection*>()) {
|
||||
|
@ -605,15 +562,6 @@ void LauncherMainWindow::onAircraftInstalledCompleted(QModelIndex index)
|
|||
maybeUpdateSelectedAircraft(index);
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onRatingsFilterToggled()
|
||||
{
|
||||
QModelIndex aircraftIndex = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
||||
QModelIndex proxyIndex = m_aircraftProxy->mapFromSource(aircraftIndex);
|
||||
if (proxyIndex.isValid()) {
|
||||
emit selectAircraftIndex(proxyIndex.row());
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
|
||||
|
@ -628,12 +576,6 @@ void LauncherMainWindow::onAircraftInstallFailed(QModelIndex index, QString erro
|
|||
maybeUpdateSelectedAircraft(index);
|
||||
}
|
||||
|
||||
void LauncherMainWindow::selectAircraft(QUrl aircraftUri)
|
||||
{
|
||||
m_selectedAircraft = aircraftUri;
|
||||
updateSelectedAircraft();
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onRestoreDefaults()
|
||||
{
|
||||
QMessageBox mbox(this);
|
||||
|
@ -694,7 +636,7 @@ void LauncherMainWindow::updateSelectedAircraft()
|
|||
m_ui->aircraftDescription->setText(longDesc.toString());
|
||||
|
||||
int status = index.data(AircraftPackageStatusRole).toInt();
|
||||
bool canRun = (status == AircraftItemModel::PackageInstalled);
|
||||
bool canRun = (status == LocalAircraftCache::PackageInstalled);
|
||||
m_ui->flyButton->setEnabled(canRun);
|
||||
|
||||
LauncherAircraftType aircraftType = Airplane;
|
||||
|
@ -728,18 +670,6 @@ void LauncherMainWindow::setSceneryPaths()
|
|||
flightgear::launcherSetSceneryPaths();
|
||||
}
|
||||
|
||||
QModelIndex LauncherMainWindow::proxyIndexForAircraftURI(QUrl uri) const
|
||||
{
|
||||
return m_aircraftProxy->mapFromSource(sourceIndexForAircraftURI(uri));
|
||||
}
|
||||
|
||||
QModelIndex LauncherMainWindow::sourceIndexForAircraftURI(QUrl uri) const
|
||||
{
|
||||
AircraftItemModel* sourceModel = qobject_cast<AircraftItemModel*>(m_aircraftProxy->sourceModel());
|
||||
Q_ASSERT(sourceModel);
|
||||
return sourceModel->indexOfAircraftURI(uri);
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onPopupAircraftHistory()
|
||||
{
|
||||
if (m_recentAircraft.isEmpty()) {
|
||||
|
@ -760,13 +690,7 @@ void LauncherMainWindow::onPopupAircraftHistory()
|
|||
QPoint popupPos = m_ui->aircraftHistory->mapToGlobal(m_ui->aircraftHistory->rect().bottomLeft());
|
||||
QAction* triggered = m.exec(popupPos);
|
||||
if (triggered) {
|
||||
const QUrl uri = triggered->data().toUrl();
|
||||
m_selectedAircraft = uri;
|
||||
QModelIndex index = proxyIndexForAircraftURI(m_selectedAircraft);
|
||||
emit selectAircraftIndex(index.row());
|
||||
m_ui->aircraftFilter->clear();
|
||||
m_aircraftModel->selectVariantForAircraftURI(uri);
|
||||
updateSelectedAircraft();
|
||||
setSelectedAircraft(triggered->data().toUrl());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -790,25 +714,6 @@ void LauncherMainWindow::onPopupLocationHistory()
|
|||
}
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onEditRatingsFilter()
|
||||
{
|
||||
EditRatingsFilterDialog dialog(this);
|
||||
dialog.setRatings(m_ratingFilters);
|
||||
|
||||
dialog.exec();
|
||||
if (dialog.result() == QDialog::Accepted) {
|
||||
QVariantList vl;
|
||||
for (int i=0; i<4; ++i) {
|
||||
m_ratingFilters[i] = dialog.getRating(i);
|
||||
vl.append(m_ratingFilters[i]);
|
||||
}
|
||||
m_aircraftProxy->setRatings(m_ratingFilters);
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("min-ratings", vl);
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherMainWindow::updateSettingsSummary()
|
||||
{
|
||||
QStringList summary;
|
||||
|
@ -826,12 +731,6 @@ void LauncherMainWindow::updateSettingsSummary()
|
|||
m_ui->settingsDescription->setText(s);
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onShowInstalledAircraftToggled(bool b)
|
||||
{
|
||||
m_ui->ratingsFilterCheck->setEnabled(!b);
|
||||
maybeRestoreAircraftSelection();
|
||||
}
|
||||
|
||||
void LauncherMainWindow::onSubsytemIdleTimeout()
|
||||
{
|
||||
globals->get_subsystem_mgr()->update(0.0);
|
||||
|
@ -901,6 +800,27 @@ void LauncherMainWindow::officialCatalogAction(QString s)
|
|||
emit showNoOfficialHangarChanged();
|
||||
}
|
||||
|
||||
QUrl LauncherMainWindow::selectedAircraft() const
|
||||
{
|
||||
return m_selectedAircraft;
|
||||
}
|
||||
|
||||
QPointF LauncherMainWindow::mapToGlobal(QQuickItem *item, const QPointF &pos) const
|
||||
{
|
||||
QPointF scenePos = item->mapToScene(pos);
|
||||
return m_ui->aircraftList->mapToGlobal(scenePos.toPoint());
|
||||
}
|
||||
|
||||
void LauncherMainWindow::setSelectedAircraft(QUrl selectedAircraft)
|
||||
{
|
||||
if (m_selectedAircraft == selectedAircraft)
|
||||
return;
|
||||
|
||||
m_selectedAircraft = selectedAircraft;
|
||||
updateSelectedAircraft();
|
||||
emit selectedAircraftChanged(m_selectedAircraft);
|
||||
}
|
||||
|
||||
simgear::pkg::PackageRef LauncherMainWindow::packageForAircraftURI(QUrl uri) const
|
||||
{
|
||||
if (uri.scheme() != "package") {
|
||||
|
@ -979,18 +899,6 @@ bool LauncherMainWindow::validateMetarString(QString metar)
|
|||
return true;
|
||||
}
|
||||
|
||||
void LauncherMainWindow::showPreviewsFor(QUrl aircraftUri) const
|
||||
{
|
||||
PreviewWindow* previewWindow = new PreviewWindow;
|
||||
QModelIndex index = m_aircraftModel->indexOfAircraftURI(aircraftUri);
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant urls = index.data(AircraftPreviewsRole);
|
||||
previewWindow->setUrls(urls.toList());
|
||||
}
|
||||
|
||||
void LauncherMainWindow::requestInstallUpdate(QUrl aircraftUri)
|
||||
{
|
||||
// also select, otherwise UI is confusing
|
||||
|
|
|
@ -46,6 +46,7 @@ class LaunchConfig;
|
|||
class ExtraSettingsSection;
|
||||
class ViewCommandLinePage;
|
||||
class MPServersModel;
|
||||
class QQuickItem;
|
||||
|
||||
class LauncherMainWindow : public QMainWindow
|
||||
{
|
||||
|
@ -53,6 +54,13 @@ class LauncherMainWindow : public QMainWindow
|
|||
|
||||
Q_PROPERTY(bool showNoOfficialHanger READ showNoOfficialHanger NOTIFY showNoOfficialHangarChanged)
|
||||
|
||||
Q_PROPERTY(AircraftProxyModel* installedAircraftModel MEMBER m_installedAircraftModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT)
|
||||
Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT)
|
||||
|
||||
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
|
||||
|
||||
Q_PROPERTY(QUrl selectedAircraft READ selectedAircraft WRITE setSelectedAircraft NOTIFY selectedAircraftChanged)
|
||||
public:
|
||||
LauncherMainWindow();
|
||||
virtual ~LauncherMainWindow();
|
||||
|
@ -62,7 +70,6 @@ public:
|
|||
bool wasRejected();
|
||||
|
||||
Q_INVOKABLE bool validateMetarString(QString metar);
|
||||
Q_INVOKABLE void showPreviewsFor(QUrl aircraftUri) const;
|
||||
|
||||
Q_INVOKABLE void requestInstallUpdate(QUrl aircraftUri);
|
||||
|
||||
|
@ -78,11 +85,19 @@ public:
|
|||
|
||||
Q_INVOKABLE void officialCatalogAction(QString s);
|
||||
|
||||
Q_INVOKABLE void selectAircraft(QUrl aircraftUri);
|
||||
QUrl selectedAircraft() const;
|
||||
|
||||
// work around the fact, that this is not available on QQuickItem until 5.7
|
||||
Q_INVOKABLE QPointF mapToGlobal(QQuickItem* item, const QPointF& pos) const;
|
||||
public slots:
|
||||
void setSelectedAircraft(QUrl selectedAircraft);
|
||||
|
||||
signals:
|
||||
void showNoOfficialHangarChanged();
|
||||
|
||||
void selectAircraftIndex(int index);
|
||||
void selectedAircraftChanged(QUrl selectedAircraft);
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
|
@ -100,8 +115,6 @@ private slots:
|
|||
void onPopupAircraftHistory();
|
||||
void onPopupLocationHistory();
|
||||
|
||||
void onEditRatingsFilter();
|
||||
|
||||
void updateSettingsSummary();
|
||||
|
||||
void onSubsytemIdleTimeout();
|
||||
|
@ -109,10 +122,6 @@ private slots:
|
|||
void onAircraftInstalledCompleted(QModelIndex index);
|
||||
void onAircraftInstallFailed(QModelIndex index, QString errorMessage);
|
||||
|
||||
void onShowInstalledAircraftToggled(bool b);
|
||||
|
||||
void maybeRestoreAircraftSelection();
|
||||
|
||||
void onRestoreDefaults();
|
||||
void onViewCommandLine();
|
||||
|
||||
|
@ -134,15 +143,11 @@ private:
|
|||
void restoreSettings();
|
||||
void saveSettings();
|
||||
|
||||
QModelIndex proxyIndexForAircraftURI(QUrl uri) const;
|
||||
QModelIndex sourceIndexForAircraftURI(QUrl uri) const;
|
||||
|
||||
simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
|
||||
|
||||
// need to wait after a model reset before restoring selection and
|
||||
// scrolling, to give the view time it seems.
|
||||
void delayedAircraftModelReset();
|
||||
void onRatingsFilterToggled();
|
||||
|
||||
void updateLocationHistory();
|
||||
bool shouldShowOfficialCatalogMessage() const;
|
||||
|
@ -153,8 +158,10 @@ private:
|
|||
void initQML();
|
||||
|
||||
QScopedPointer<Ui::Launcher> m_ui;
|
||||
AircraftProxyModel* m_aircraftProxy;
|
||||
AircraftProxyModel* m_installedAircraftModel;
|
||||
AircraftItemModel* m_aircraftModel;
|
||||
AircraftProxyModel* m_aircraftSearchModel;
|
||||
AircraftProxyModel* m_browseAircraftModel;
|
||||
MPServersModel* m_serversModel = nullptr;
|
||||
|
||||
QUrl m_selectedAircraft;
|
||||
|
|
26
src/GUI/ListHeaderBox.qml
Normal file
26
src/GUI/ListHeaderBox.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick 2.2
|
||||
import FlightGear.Launcher 1.0 as FG
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property int margin: 8
|
||||
|
||||
height: visible ? 48 : 0
|
||||
z: 100
|
||||
|
||||
property alias contents: contentBox.children
|
||||
|
||||
Rectangle {
|
||||
id: contentBox
|
||||
width: parent.width
|
||||
height: 40
|
||||
y: 4
|
||||
color: "white"
|
||||
border.width: 1
|
||||
border.color: "#9f9f9f"
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -181,6 +181,18 @@ QPixmap AircraftItem::thumbnail(bool loadIfRequired) const
|
|||
return m_thumbnail;
|
||||
}
|
||||
|
||||
int AircraftItem::indexOfVariant(QUrl uri) const
|
||||
{
|
||||
const QString path = uri.toLocalFile();
|
||||
for (int i=0; i< variants.size(); ++i) {
|
||||
if (variants.at(i)->path == path) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QVariant AircraftItem::status(int variant)
|
||||
{
|
||||
if (needsMaintenance) {
|
||||
|
@ -443,6 +455,21 @@ int LocalAircraftCache::findIndexWithUri(QUrl aircraftUri) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
AircraftItemPtr LocalAircraftCache::primaryItemFor(AircraftItemPtr item) const
|
||||
{
|
||||
if (!item || item->variantOf.isEmpty())
|
||||
return item;
|
||||
|
||||
for (int row=0; row < m_items.size(); ++row) {
|
||||
const AircraftItemPtr p(m_items.at(row));
|
||||
if (p->baseName() == item->variantOf) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QVector<AircraftItemPtr> LocalAircraftCache::newestItems(int count)
|
||||
{
|
||||
QVector<AircraftItemPtr> r;
|
||||
|
|
|
@ -49,6 +49,8 @@ struct AircraftItem
|
|||
|
||||
QPixmap thumbnail(bool loadIfRequired = true) const;
|
||||
|
||||
int indexOfVariant(QUrl uri) const;
|
||||
|
||||
bool excluded = false;
|
||||
QString path;
|
||||
QString description;
|
||||
|
@ -99,6 +101,8 @@ public:
|
|||
AircraftItemPtr findItemWithUri(QUrl aircraftUri) const;
|
||||
int findIndexWithUri(QUrl aircraftUri) const;
|
||||
|
||||
AircraftItemPtr primaryItemFor(AircraftItemPtr item) const;
|
||||
|
||||
QVector<AircraftItemPtr> newestItems(int count);
|
||||
|
||||
QVariant aircraftStatus(AircraftItemPtr item) const;
|
||||
|
@ -111,6 +115,16 @@ public:
|
|||
AircraftNeedsOlderSimulator // won't ever occur for the moment
|
||||
};
|
||||
|
||||
enum PackageStatus {
|
||||
PackageNotInstalled = 0,
|
||||
PackageInstalled,
|
||||
PackageUpdateAvailable,
|
||||
PackageQueued,
|
||||
PackageDownloading
|
||||
};
|
||||
|
||||
Q_ENUMS(PackageStatus)
|
||||
Q_ENUMS(AircraftStatus)
|
||||
signals:
|
||||
|
||||
void scanStarted();
|
||||
|
|
48
src/GUI/NoDefaultCatalogPanel.qml
Normal file
48
src/GUI/NoDefaultCatalogPanel.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
import QtQuick 2.2
|
||||
import FlightGear.Launcher 1.0 as FG
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property int margin: 6
|
||||
|
||||
height: noDefaultCatalogRow.childrenRect.height + margin * 2;
|
||||
z: 100
|
||||
color: "white"
|
||||
border.width: 1
|
||||
border.color: "#9f9f9f"
|
||||
|
||||
Row {
|
||||
y: root.margin
|
||||
id: noDefaultCatalogRow
|
||||
spacing: root.margin
|
||||
|
||||
Text {
|
||||
text: "The official FlightGear aircraft hangar is not added, so many standard "
|
||||
+ "aircraft will not be available. You can add the hangar now, or hide "
|
||||
+ "this message. The offical hangar can always be restored from the 'Add-Ons' page."
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: root.width - (addDefaultButton.width + hideButton.width + root.margin * 3)
|
||||
}
|
||||
|
||||
Button {
|
||||
id: addDefaultButton
|
||||
text: qsTr("Add default hangar")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: {
|
||||
_launcher.officialCatalogAction("add-official");
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: hideButton
|
||||
text: qsTr("Hide")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
onClicked: {
|
||||
_launcher.officialCatalogAction("hide");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
src/GUI/PopupChoice.qml
Normal file
95
src/GUI/PopupChoice.qml
Normal file
|
@ -0,0 +1,95 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias label: label.text
|
||||
|
||||
property var choices: []
|
||||
property string displayRole: ""
|
||||
|
||||
property int currentIndex: 0
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: mouseArea.containsMouse ? "#68A6E1" : "black"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: currentChoiceFrame
|
||||
radius: 4
|
||||
border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f"
|
||||
border.width: 1
|
||||
height: root.height
|
||||
width: parent.width / 2
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
id: currentChoiceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
text: choices[currentIndex][displayRole]
|
||||
color: mouseArea.containsMouse ? "#68A6E1" : "#7F7F7F"
|
||||
}
|
||||
|
||||
Image {
|
||||
id: upDownIcon
|
||||
source: "qrc:///up-down-arrow"
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
popupFrame.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: popupFrame
|
||||
|
||||
width: currentChoiceFrame.width
|
||||
anchors.left: currentChoiceFrame.left
|
||||
|
||||
// todo - position so current item lies on top
|
||||
anchors.top: currentChoiceFrame.bottom
|
||||
|
||||
height: choicesColumn.childrenRect.height
|
||||
|
||||
visible: false
|
||||
|
||||
border.color: "#9f9f9f"
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
id: choicesColumn
|
||||
|
||||
Repeater {
|
||||
model: choices
|
||||
delegate: Text {
|
||||
text: choices[model.index][root.displayRole]
|
||||
width: popupFrame.width
|
||||
height: 40
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.currentIndex = model.index
|
||||
popupFrame.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
127
src/GUI/PreviewImageItem.cxx
Normal file
127
src/GUI/PreviewImageItem.cxx
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include "PreviewImageItem.hxx"
|
||||
|
||||
#include <QSGSimpleTextureNode>
|
||||
#include <QQuickWindow>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
namespace {
|
||||
|
||||
QNetworkAccessManager* global_previewNetAccess = nullptr;
|
||||
|
||||
}
|
||||
|
||||
PreviewImageItem::PreviewImageItem(QQuickItem* parent) :
|
||||
QQuickItem(parent)
|
||||
{
|
||||
setFlag(ItemHasContents);
|
||||
// setImplicitWidth(STANDARD_THUMBNAIL_WIDTH);
|
||||
// setImplicitHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||
|
||||
Q_ASSERT(global_previewNetAccess);
|
||||
}
|
||||
|
||||
PreviewImageItem::~PreviewImageItem()
|
||||
{
|
||||
}
|
||||
|
||||
QSGNode *PreviewImageItem::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *)
|
||||
{
|
||||
if (m_image.isNull()) {
|
||||
delete oldNode;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSGSimpleTextureNode* textureNode = static_cast<QSGSimpleTextureNode*>(oldNode);
|
||||
if (m_imageDirty || !textureNode) {
|
||||
if (!textureNode) {
|
||||
textureNode = new QSGSimpleTextureNode;
|
||||
textureNode->setOwnsTexture(true);
|
||||
}
|
||||
|
||||
QSGTexture* tex = window()->createTextureFromImage(m_image);
|
||||
textureNode->setTexture(tex);
|
||||
textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial);
|
||||
m_imageDirty = false;
|
||||
}
|
||||
|
||||
textureNode->setRect(QRectF(0, 0, width(), height()));
|
||||
return textureNode;
|
||||
}
|
||||
|
||||
QUrl PreviewImageItem::imageUrl() const
|
||||
{
|
||||
return m_imageUrl;
|
||||
}
|
||||
|
||||
QSize PreviewImageItem::sourceSize() const
|
||||
{
|
||||
return m_image.size();
|
||||
}
|
||||
|
||||
void PreviewImageItem::setGlobalNetworkAccess(QNetworkAccessManager *netAccess)
|
||||
{
|
||||
global_previewNetAccess = netAccess;
|
||||
}
|
||||
|
||||
void PreviewImageItem::setImageUrl( QUrl url)
|
||||
{
|
||||
if (m_imageUrl == url)
|
||||
return;
|
||||
|
||||
m_imageUrl = url;
|
||||
m_downloadRetryCount = 0;
|
||||
startDownload();
|
||||
emit imageUrlChanged();
|
||||
}
|
||||
|
||||
void PreviewImageItem::startDownload()
|
||||
{
|
||||
if (m_imageUrl.isEmpty())
|
||||
return;
|
||||
|
||||
QNetworkRequest request(m_imageUrl);
|
||||
QNetworkReply* reply = global_previewNetAccess->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &PreviewImageItem::onFinished);
|
||||
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(onDownloadError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
void PreviewImageItem::setImage(QImage image)
|
||||
{
|
||||
m_image = image;
|
||||
m_imageDirty = true;
|
||||
setImplicitSize(m_image.width(), m_image.height());
|
||||
emit sourceSizeChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
void PreviewImageItem::onFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
QImage img;
|
||||
if (!img.load(reply, nullptr)) {
|
||||
qWarning() << Q_FUNC_INFO << "failed to read image data from" << reply->url();
|
||||
return;
|
||||
}
|
||||
setImage(img);
|
||||
}
|
||||
|
||||
void PreviewImageItem::onDownloadError(QNetworkReply::NetworkError errorCode)
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if (errorCode == 403) {
|
||||
if (m_downloadRetryCount++ < 4) {
|
||||
startDownload(); // retry
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "failed to download:" << reply->url();
|
||||
qWarning() << reply->errorString();
|
||||
}
|
58
src/GUI/PreviewImageItem.hxx
Normal file
58
src/GUI/PreviewImageItem.hxx
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef PREVIEW_IMAGEITEM_HXX
|
||||
#define PREVIEW_IMAGEITEM_HXX
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class PreviewImageItem : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QUrl imageUrl READ imageUrl WRITE setImageUrl NOTIFY imageUrlChanged)
|
||||
|
||||
Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged)
|
||||
|
||||
// Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged)
|
||||
public:
|
||||
PreviewImageItem(QQuickItem* parent = nullptr);
|
||||
~PreviewImageItem();
|
||||
|
||||
QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
|
||||
|
||||
QUrl imageUrl() const;
|
||||
|
||||
QSize sourceSize() const;
|
||||
|
||||
static void setGlobalNetworkAccess(QNetworkAccessManager* netAccess);
|
||||
|
||||
signals:
|
||||
void imageUrlChanged();
|
||||
void sourceSizeChanged();
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
void setImageUrl(QUrl url);
|
||||
|
||||
private slots:
|
||||
void onDownloadError(QNetworkReply::NetworkError errorCode);
|
||||
|
||||
void onFinished();
|
||||
private:
|
||||
void setImage(QImage image);
|
||||
void startDownload();
|
||||
|
||||
QUrl m_imageUrl;
|
||||
|
||||
bool m_imageDirty = false;
|
||||
QImage m_image;
|
||||
unsigned int m_downloadRetryCount = 0;
|
||||
};
|
||||
|
||||
#endif // PREVIEW_IMAGEITEM_HXX
|
|
@ -4,14 +4,81 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include <simgear/package/Install.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <Include/version.h>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "LocalAircraftCache.hxx"
|
||||
|
||||
QmlAircraftInfo::QmlAircraftInfo(QObject *parent) : QObject(parent)
|
||||
{
|
||||
using namespace simgear::pkg;
|
||||
|
||||
class QmlAircraftInfo::Delegate : public simgear::pkg::Delegate
|
||||
{
|
||||
public:
|
||||
Delegate(QmlAircraftInfo* info) :
|
||||
p(info)
|
||||
{
|
||||
globals->packageRoot()->addDelegate(this);
|
||||
}
|
||||
|
||||
~Delegate()
|
||||
{
|
||||
globals->packageRoot()->removeDelegate(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
void catalogRefreshed(CatalogRef, StatusCode) override
|
||||
{
|
||||
}
|
||||
|
||||
void startInstall(InstallRef aInstall) override
|
||||
{
|
||||
if (aInstall->package() == p->packageRef()) {
|
||||
p->setDownloadBytes(0);
|
||||
}
|
||||
}
|
||||
|
||||
void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override
|
||||
{
|
||||
if (aInstall->package() == p->packageRef()) {
|
||||
p->setDownloadBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void finishInstall(InstallRef aInstall, StatusCode aReason) override
|
||||
{
|
||||
Q_UNUSED(aReason);
|
||||
if (aInstall->package() == p->packageRef()) {
|
||||
p->infoChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
|
||||
{
|
||||
Q_UNUSED(aReason);
|
||||
if (aInstall->package() == p->packageRef()) {
|
||||
p->downloadChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void finishUninstall(const PackageRef& pkg) override
|
||||
{
|
||||
if (pkg == p->packageRef()) {
|
||||
p->downloadChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QmlAircraftInfo* p;
|
||||
};
|
||||
|
||||
QmlAircraftInfo::QmlAircraftInfo(QObject *parent)
|
||||
: QObject(parent)
|
||||
, _delegate(new Delegate(this))
|
||||
{
|
||||
}
|
||||
|
||||
QmlAircraftInfo::~QmlAircraftInfo()
|
||||
|
@ -19,9 +86,15 @@ QmlAircraftInfo::~QmlAircraftInfo()
|
|||
|
||||
}
|
||||
|
||||
int QmlAircraftInfo::numPreviews() const
|
||||
QUrl QmlAircraftInfo::uri() const
|
||||
{
|
||||
return 0;
|
||||
if (_item) {
|
||||
QUrl::fromLocalFile(resolveItem()->path);
|
||||
} else if (_package) {
|
||||
return QUrl("package:" + QString::fromStdString(_package->qualifiedVariantId(_variant)));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int QmlAircraftInfo::numVariants() const
|
||||
|
@ -94,6 +167,47 @@ QVariantList QmlAircraftInfo::ratings() const
|
|||
return {};
|
||||
}
|
||||
|
||||
QVariantList QmlAircraftInfo::previews() const
|
||||
{
|
||||
if (_item) {
|
||||
QVariantList result;
|
||||
Q_FOREACH(QUrl u, _item->previews) {
|
||||
result.append(u);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_package) {
|
||||
const auto& previews = _package->previewsForVariant(_variant);
|
||||
if (previews.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariantList result;
|
||||
// if we have an install, return file URLs, not remote (http) ones
|
||||
auto ex = _package->existingInstall();
|
||||
if (ex.valid()) {
|
||||
for (auto p : previews) {
|
||||
SGPath localPreviewPath = ex->path() / p.path;
|
||||
if (!localPreviewPath.exists()) {
|
||||
qWarning() << "missing local preview" << QString::fromStdString(localPreviewPath.utf8Str());
|
||||
continue;
|
||||
}
|
||||
result.append(QUrl::fromLocalFile(QString::fromStdString(localPreviewPath.utf8Str())));
|
||||
}
|
||||
}
|
||||
|
||||
// return remote urls
|
||||
for (auto p : previews) {
|
||||
result.append(QUrl(QString::fromStdString(p.url)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QUrl QmlAircraftInfo::thumbnail() const
|
||||
{
|
||||
if (_item) {
|
||||
|
@ -143,7 +257,7 @@ int QmlAircraftInfo::packageSize() const
|
|||
|
||||
int QmlAircraftInfo::downloadedBytes() const
|
||||
{
|
||||
return 0;
|
||||
return _downloadBytes;
|
||||
}
|
||||
|
||||
QVariant QmlAircraftInfo::status() const
|
||||
|
@ -180,16 +294,45 @@ AircraftItemPtr QmlAircraftInfo::resolveItem() const
|
|||
return _item;
|
||||
}
|
||||
|
||||
void QmlAircraftInfo::setUri(QUrl uri)
|
||||
void QmlAircraftInfo::setUri(QUrl u)
|
||||
{
|
||||
if (_uri == uri)
|
||||
if (uri() == u)
|
||||
return;
|
||||
|
||||
_uri = uri;
|
||||
_item.clear();
|
||||
_package.clear();
|
||||
|
||||
if (u.isLocalFile()) {
|
||||
_item = LocalAircraftCache::instance()->findItemWithUri(u);
|
||||
if (_item->variantOf.isEmpty()) {
|
||||
_variant = 0;
|
||||
} else {
|
||||
_item = LocalAircraftCache::instance()->primaryItemFor(_item);
|
||||
_variant = _item->indexOfVariant(u);
|
||||
}
|
||||
} else if (u.scheme() == "package") {
|
||||
auto ident = u.path().toStdString();
|
||||
try {
|
||||
_package = globals->packageRoot()->getPackageById(ident);
|
||||
_variant = _package->indexOfVariant(ident);
|
||||
} catch (sg_exception&) {
|
||||
qWarning() << "couldn't find package/variant for " << u;
|
||||
}
|
||||
}
|
||||
|
||||
emit uriChanged();
|
||||
emit infoChanged();
|
||||
emit downloadChanged();
|
||||
}
|
||||
|
||||
void QmlAircraftInfo::setVariant(int variant)
|
||||
{
|
||||
if (_variant == variant)
|
||||
return;
|
||||
|
||||
_variant = variant;
|
||||
emit infoChanged();
|
||||
emit variantChanged(_variant);
|
||||
}
|
||||
|
||||
void QmlAircraftInfo::requestInstallUpdate()
|
||||
|
@ -222,3 +365,63 @@ QVariant QmlAircraftInfo::packageAircraftStatus(simgear::pkg::PackageRef p)
|
|||
return (c < 0) ? LocalAircraftCache::AircraftNeedsNewerSimulator :
|
||||
LocalAircraftCache::AircraftOk;
|
||||
}
|
||||
|
||||
QVariant QmlAircraftInfo::installStatus() const
|
||||
{
|
||||
if (_item) {
|
||||
return LocalAircraftCache::PackageInstalled;
|
||||
}
|
||||
|
||||
if (_package) {
|
||||
auto i = _package->existingInstall();
|
||||
if (i.valid()) {
|
||||
if (i->isDownloading()) {
|
||||
return LocalAircraftCache::PackageDownloading;
|
||||
}
|
||||
if (i->isQueued()) {
|
||||
return LocalAircraftCache::PackageQueued;
|
||||
}
|
||||
if (i->hasUpdate()) {
|
||||
return LocalAircraftCache::PackageUpdateAvailable;
|
||||
}
|
||||
|
||||
return LocalAircraftCache::PackageInstalled;
|
||||
} else {
|
||||
return LocalAircraftCache::PackageNotInstalled;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
PackageRef QmlAircraftInfo::packageRef() const
|
||||
{
|
||||
return _package;
|
||||
}
|
||||
|
||||
void QmlAircraftInfo::setDownloadBytes(int bytes)
|
||||
{
|
||||
_downloadBytes = bytes;
|
||||
emit downloadChanged();;
|
||||
}
|
||||
|
||||
QStringList QmlAircraftInfo::variantNames() const
|
||||
{
|
||||
QStringList result;
|
||||
if (_item) {
|
||||
Q_FOREACH(auto v, _item->variants) {
|
||||
if (v->description.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "missing description for " << v->path;
|
||||
}
|
||||
result.append(v->description);
|
||||
}
|
||||
} else if (_package) {
|
||||
for (int vindex = 0; vindex < _package->variants().size(); ++vindex) {
|
||||
if (_package->nameForVariant(vindex).empty()) {
|
||||
qWarning() << Q_FUNC_INFO << "missing description for variant" << vindex;
|
||||
}
|
||||
result.append(QString::fromStdString(_package->nameForVariant(vindex)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef QMLAIRCRAFTINFO_HXX
|
||||
#define QMLAIRCRAFTINFO_HXX
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QSharedPointer>
|
||||
|
@ -16,8 +18,9 @@ class QmlAircraftInfo : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QUrl uri READ uri WRITE setUri NOTIFY uriChanged)
|
||||
Q_PROPERTY(int variant READ variant WRITE setVariant NOTIFY variantChanged)
|
||||
|
||||
Q_PROPERTY(int numPreviews READ numPreviews NOTIFY infoChanged)
|
||||
Q_PROPERTY(QVariantList previews READ previews NOTIFY infoChanged)
|
||||
Q_PROPERTY(int numVariants READ numVariants NOTIFY infoChanged)
|
||||
|
||||
Q_PROPERTY(QString name READ name NOTIFY infoChanged)
|
||||
|
@ -35,6 +38,7 @@ class QmlAircraftInfo : public QObject
|
|||
Q_PROPERTY(int downloadedBytes READ downloadedBytes NOTIFY downloadChanged)
|
||||
|
||||
Q_PROPERTY(QVariant status READ status NOTIFY infoChanged)
|
||||
Q_PROPERTY(QVariant installStatus READ installStatus NOTIFY downloadChanged)
|
||||
|
||||
Q_PROPERTY(QString minimumFGVersion READ minimumFGVersion NOTIFY infoChanged)
|
||||
|
||||
|
@ -46,16 +50,15 @@ class QmlAircraftInfo : public QObject
|
|||
|
||||
Q_PROPERTY(QVariantList ratings READ ratings NOTIFY infoChanged)
|
||||
|
||||
Q_PROPERTY(QStringList variantNames READ variantNames NOTIFY infoChanged)
|
||||
|
||||
|
||||
public:
|
||||
explicit QmlAircraftInfo(QObject *parent = nullptr);
|
||||
virtual ~QmlAircraftInfo();
|
||||
|
||||
QUrl uri() const
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
QUrl uri() const;
|
||||
|
||||
int numPreviews() const;
|
||||
int numVariants() const;
|
||||
|
||||
QString name() const;
|
||||
|
@ -63,6 +66,8 @@ public:
|
|||
QString authors() const;
|
||||
QVariantList ratings() const;
|
||||
|
||||
QVariantList previews() const;
|
||||
|
||||
QUrl thumbnail() const;
|
||||
QString pathOnDisk() const;
|
||||
|
||||
|
@ -74,21 +79,42 @@ public:
|
|||
QString minimumFGVersion() const;
|
||||
|
||||
static QVariant packageAircraftStatus(simgear::pkg::PackageRef p);
|
||||
|
||||
int variant() const
|
||||
{
|
||||
return _variant;
|
||||
}
|
||||
|
||||
QVariant installStatus() const;
|
||||
|
||||
simgear::pkg::PackageRef packageRef() const;
|
||||
|
||||
void setDownloadBytes(int bytes);
|
||||
|
||||
QStringList variantNames() const;
|
||||
signals:
|
||||
void uriChanged();
|
||||
void infoChanged();
|
||||
void downloadChanged();
|
||||
void variantChanged(int variant);
|
||||
|
||||
public slots:
|
||||
|
||||
void setUri(QUrl uri);
|
||||
|
||||
void setVariant(int variant);
|
||||
|
||||
private:
|
||||
QUrl _uri;
|
||||
class Delegate;
|
||||
std::unique_ptr<Delegate> _delegate;
|
||||
|
||||
simgear::pkg::PackageRef _package;
|
||||
AircraftItemPtr _item;
|
||||
int _variant = 0;
|
||||
int _downloadBytes = 0;
|
||||
|
||||
AircraftItemPtr resolveItem() const;
|
||||
int m_variant;
|
||||
};
|
||||
|
||||
#endif // QMLAIRCRAFTINFO_HXX
|
||||
|
|
18
src/GUI/RatingSlider.qml
Normal file
18
src/GUI/RatingSlider.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Slider {
|
||||
property var ratings: []
|
||||
property int ratingIndex: 0
|
||||
|
||||
min: 0
|
||||
max: 5
|
||||
|
||||
value: ratings[ratingIndex]
|
||||
width: editRatingsPanel.width - 30
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
sliderWidth: width / 2
|
||||
|
||||
onValueChanged: {
|
||||
ratings[ratingIndex] = value
|
||||
}
|
||||
}
|
75
src/GUI/SearchButton.qml
Normal file
75
src/GUI/SearchButton.qml
Normal file
|
@ -0,0 +1,75 @@
|
|||
import QtQuick 2.2
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// property string text
|
||||
property bool active: false
|
||||
|
||||
signal search(string term)
|
||||
|
||||
radius: 6
|
||||
|
||||
border.width: 1
|
||||
border.color: (mouse.containsMouse | active) ? "#1b7ad3" : "#cfcfcf"
|
||||
clip: true
|
||||
|
||||
TextInput {
|
||||
id: buttonText
|
||||
anchors.left: parent.left
|
||||
anchors.right: searchIcon.left
|
||||
anchors.margins: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: "#3f3f3f"
|
||||
|
||||
onTextChanged: {
|
||||
searchTimer.restart();
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
root.search(text);
|
||||
focus = false;
|
||||
}
|
||||
|
||||
text: "Search"
|
||||
}
|
||||
|
||||
Image {
|
||||
id: searchIcon
|
||||
source: "qrc:///search-icon-small"
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
buttonText.forceActiveFocus();
|
||||
buttonText.text = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
// rest search text when we deactive
|
||||
buttonText.text = "Search"
|
||||
searchTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: searchTimer
|
||||
interval: 800
|
||||
onTriggered: {
|
||||
if (buttonText.text.length > 2) {
|
||||
root.search(buttonText.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
src/GUI/Slider.qml
Normal file
90
src/GUI/Slider.qml
Normal file
|
@ -0,0 +1,90 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property alias label: labelText.text
|
||||
|
||||
property int min: 0
|
||||
property int max: 100
|
||||
property int value: 50
|
||||
|
||||
property int sliderWidth: 0
|
||||
|
||||
readonly property real __percentFull: value / (max - min)
|
||||
|
||||
implicitHeight: labelText.height
|
||||
implicitWidth: labelText.width * 2
|
||||
|
||||
Text {
|
||||
id: labelText
|
||||
width: parent.width - (emptyTrack.width + 8)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: emptyTrack
|
||||
|
||||
width: sliderWidth > 0 ? sliderWidth : parent.width - (labelText.implicitWidth + 8)
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
height: 2
|
||||
color: "#9f9f9f"
|
||||
|
||||
Rectangle {
|
||||
id: fullTrack
|
||||
height: parent.height
|
||||
color:"#68A6E1"
|
||||
width: parent.width * __percentFull
|
||||
}
|
||||
|
||||
// invisble item that moves directly with the mouse
|
||||
Item {
|
||||
id: dragThumb
|
||||
height: 2
|
||||
width: 2
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Drag.active: thumbMouse.drag.active
|
||||
|
||||
onXChanged: {
|
||||
if (thumbMouse.drag.active) {
|
||||
var frac = x / emptyTrack.width;
|
||||
value = min + (max - min) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumb
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: "#68A6E1"
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: parent.width * __percentFull
|
||||
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: thumbMouse
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
|
||||
drag.axis: Drag.XAxis
|
||||
drag.minimumX: 0
|
||||
drag.maximumX: emptyTrack.width
|
||||
drag.target: dragThumb
|
||||
|
||||
onPressed: {
|
||||
dragThumb.x = emptyTrack.width * __percentFull
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // of base track rectangle
|
||||
|
||||
}
|
33
src/GUI/TabButton.qml
Normal file
33
src/GUI/TabButton.qml
Normal file
|
@ -0,0 +1,33 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property alias text: buttonText.text
|
||||
property bool active: false
|
||||
|
||||
signal clicked
|
||||
|
||||
width: buttonText.width + (radius * 2)
|
||||
height: buttonText.height + (radius * 2)
|
||||
radius: 6
|
||||
|
||||
color: mouse.containsMouse ? "#064989" : (active ? "#1b7ad3" : "white")
|
||||
|
||||
Text {
|
||||
id: buttonText
|
||||
anchors.centerIn: parent
|
||||
color: (active | mouse.containsMouse) ? "white" : "#3f3f3f"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
57
src/GUI/ToggleSwitch.qml
Normal file
57
src/GUI/ToggleSwitch.qml
Normal file
|
@ -0,0 +1,57 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property bool checked: false
|
||||
property alias label: label.text
|
||||
|
||||
implicitWidth: track.width + label.width + 16
|
||||
implicitHeight: label.height
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
width: height * 2
|
||||
height: 12
|
||||
radius: 6
|
||||
|
||||
color: checked ? "#68A6E1" :"#9f9f9f"
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle {
|
||||
id: thumb
|
||||
width: 18
|
||||
height: 18
|
||||
radius: 9
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: checked ? "#68A6E1" : "white"
|
||||
border.width: 1
|
||||
border.color: "#9f9f9f"
|
||||
|
||||
x: checked ? parent.width - (6 + radius) : -3
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.left: track.right
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
checked = !checked
|
||||
}
|
||||
}
|
||||
}
|
48
src/GUI/UpdateAllPanel.qml
Normal file
48
src/GUI/UpdateAllPanel.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
import QtQuick 2.2
|
||||
import FlightGear.Launcher 1.0 as FG
|
||||
|
||||
ListHeaderBox {
|
||||
id: root
|
||||
property int margin: 6
|
||||
|
||||
contents: [
|
||||
Text {
|
||||
id: updateAllText
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: updateAllButton.left
|
||||
margins: root.margin
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
text: qsTr("%1 aircraft have updates available - download and install them now?").
|
||||
arg( _launcher.baseAircraftModel.aircraftNeedingUpdated);
|
||||
wrapMode: Text.WordWrap
|
||||
},
|
||||
|
||||
Button {
|
||||
id: updateAllButton
|
||||
text: qsTr("Update all")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: notNowButton.left
|
||||
anchors.rightMargin: root.margin
|
||||
|
||||
onClicked: {
|
||||
_launcher.requestUpdateAllAircraft();
|
||||
_launcher.baseAircraftModel.showUpdateAll = false
|
||||
}
|
||||
},
|
||||
|
||||
Button {
|
||||
id: notNowButton
|
||||
text: qsTr("Not now")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.margin
|
||||
|
||||
onClicked: {
|
||||
_launcher.baseAircraftModel.showUpdateAll = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -36,6 +36,23 @@
|
|||
<file>AircraftWarningPanel.qml</file>
|
||||
<file>Scrollbar.qml</file>
|
||||
<file>AircraftDetailsView.qml</file>
|
||||
<file>AircraftFullDelegate.qml</file>
|
||||
<file>AircraftCompactDelegate.qml</file>
|
||||
<file>SearchButton.qml</file>
|
||||
<file>AircraftPreviewPanel.qml</file>
|
||||
<file>TabButton.qml</file>
|
||||
<file>Checkbox.qml</file>
|
||||
<file>Slider.qml</file>
|
||||
<file>ToggleSwitch.qml</file>
|
||||
<file>RatingSlider.qml</file>
|
||||
<file>PopupChoice.qml</file>
|
||||
<file alias="up-down-arrow">up-down-arrow.png</file>
|
||||
<file>AircraftVariantChoice.qml</file>
|
||||
<file alias="search-icon-small">search-icon-small.png</file>
|
||||
<file>AircraftRatingsPanel.qml</file>
|
||||
<file>NoDefaultCatalogPanel.qml</file>
|
||||
<file>ListHeaderBox.qml</file>
|
||||
<file>UpdateAllPanel.qml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/preview">
|
||||
<file alias="close-icon">preview-close.png</file>
|
||||
|
|
BIN
src/GUI/search-icon-small.png
Normal file
BIN
src/GUI/search-icon-small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 904 B |
BIN
src/GUI/up-down-arrow.png
Normal file
BIN
src/GUI/up-down-arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 768 B |
Loading…
Reference in a new issue