1
0
Fork 0

Previews displayed in the launcher.

This commit is contained in:
James Turner 2017-02-06 14:09:07 +00:00
parent 38554f629f
commit 17fe0460a9
16 changed files with 1004 additions and 786 deletions

View file

@ -305,7 +305,7 @@ endif (USE_DBUS)
## Qt5 setup setup ## Qt5 setup setup
if (ENABLE_QT) if (ENABLE_QT)
message(STATUS "Qt launcher enabled, checking for Qt 5.1 / qmake") message(STATUS "Qt launcher enabled, checking for Qt 5.1 / qmake")
find_package(Qt5 5.1 COMPONENTS Widgets) find_package(Qt5 5.1 COMPONENTS Widgets Network)
if (Qt5Widgets_FOUND) if (Qt5Widgets_FOUND)
message(STATUS "Will enable Qt launcher GUI") message(STATUS "Will enable Qt launcher GUI")
message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}") message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}")

View file

@ -46,6 +46,8 @@ AircraftItemDelegate::AircraftItemDelegate(QListView* view) :
m_leftArrowIcon.load(":/left-arrow-icon"); m_leftArrowIcon.load(":/left-arrow-icon");
m_rightArrowIcon.load(":/right-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, void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option,
@ -79,15 +81,22 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
painter->drawLine(option.rect.topLeft(), option.rect.topRight()); painter->drawLine(option.rect.topLeft(), option.rect.topRight());
} }
// thumbnail
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>(); QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2); quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
painter->drawPixmap(contentRect.left(), yPos, thumbnail); painter->drawPixmap(contentRect.left(), yPos, thumbnail);
// draw 1px frame // draw 1px frame
QRect thumbFrame(contentRect.left(), yPos, thumbnail.width(), thumbnail.height());
painter->setPen(QColor(0x7f, 0x7f, 0x7f)); painter->setPen(QColor(0x7f, 0x7f, 0x7f));
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
painter->drawRect(contentRect.left(), yPos, thumbnail.width(), thumbnail.height()); 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 // draw bottom dividing line
painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN, painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
@ -337,6 +346,13 @@ bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
return true; 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 ) { } else if ( event->type() == QEvent::MouseMove ) {
} }
@ -381,6 +397,18 @@ QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QMo
BUTTON_WIDTH, 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 void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
{ {
QRect dotBox = box; QRect dotBox = box;

View file

@ -50,17 +50,23 @@ Q_SIGNALS:
void requestUninstall(const QModelIndex& index); void requestUninstall(const QModelIndex& index);
void cancelDownload(const QModelIndex& index); void cancelDownload(const QModelIndex& index);
void showPreviews(const QModelIndex& index);
private: private:
QRect leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const; QRect leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
QRect rightCycleArrowRect(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 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; void drawRating(QPainter* painter, QString label, const QRect& box, int value) const;
QListView* m_view; QListView* m_view;
QPixmap m_leftArrowIcon, QPixmap m_leftArrowIcon,
m_rightArrowIcon; m_rightArrowIcon,
m_openPreviewsIcon;
}; };
#endif #endif

View file

@ -40,28 +40,18 @@
// FlightGear // FlightGear
#include <Main/globals.hxx> #include <Main/globals.hxx>
const int STANDARD_THUMBNAIL_HEIGHT = 128; const int STANDARD_THUMBNAIL_HEIGHT = 128;
const int STANDARD_THUMBNAIL_WIDTH = 172; const int STANDARD_THUMBNAIL_WIDTH = 172;
static quint32 CACHE_VERSION = 6;
using namespace simgear::pkg; using namespace simgear::pkg;
AircraftItem::AircraftItem() : AircraftItem::AircraftItem()
excluded(false),
usesHeliports(false),
usesSeaports(false)
{ {
// oh for C++11 initialisers
for (int i=0; i<4; ++i) ratings[i] = 0;
} }
AircraftItem::AircraftItem(QDir dir, QString filePath) : AircraftItem::AircraftItem(QDir dir, QString filePath)
excluded(false),
usesHeliports(false),
usesSeaports(false)
{ {
for (int i=0; i<4; ++i) ratings[i] = 0;
SGPropertyNode root; SGPropertyNode root;
readProperties(filePath.toStdString(), &root); readProperties(filePath.toStdString(), &root);
@ -113,6 +103,16 @@ AircraftItem::AircraftItem(QDir dir, QString filePath) :
} }
} // of tags iteration } // of tags iteration
} // of set-xml has tags } // of set-xml has tags
if (sim->hasChild("previews")) {
SGPropertyNode_ptr previewsNode = sim->getChild("previews");
for (auto previewNode : previewsNode->getChildren("preview")) {
// add file path as url
QString pathInXml = QString::fromStdString(previewNode->getStringValue("path"));
QString previewPath = dir.absoluteFilePath(pathInXml);
previews.append(QUrl::fromLocalFile(previewPath));
}
}
} }
QString AircraftItem::baseName() const QString AircraftItem::baseName() const
@ -131,6 +131,7 @@ void AircraftItem::fromDataStream(QDataStream& ds)
ds >> description >> longDescription >> authors >> variantOf; ds >> description >> longDescription >> authors >> variantOf;
for (int i=0; i<4; ++i) ds >> ratings[i]; for (int i=0; i<4; ++i) ds >> ratings[i];
ds >> previews;
} }
void AircraftItem::toDataStream(QDataStream& ds) const void AircraftItem::toDataStream(QDataStream& ds) const
@ -142,6 +143,7 @@ void AircraftItem::toDataStream(QDataStream& ds) const
ds << description << longDescription << authors << variantOf; ds << description << longDescription << authors << variantOf;
for (int i=0; i<4; ++i) ds << ratings[i]; for (int i=0; i<4; ++i) ds << ratings[i];
ds << previews;
} }
QPixmap AircraftItem::thumbnail(bool loadIfRequired) const QPixmap AircraftItem::thumbnail(bool loadIfRequired) const
@ -161,9 +163,6 @@ QPixmap AircraftItem::thumbnail(bool loadIfRequired) const
return m_thumbnail; return m_thumbnail;
} }
static quint32 CACHE_VERSION = 5;
class AircraftScanThread : public QThread class AircraftScanThread : public QThread
{ {
Q_OBJECT Q_OBJECT
@ -402,14 +401,22 @@ protected:
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation); pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation);
} }
m_model->m_thumbnailPixmapCache.insert(QString::fromStdString(aThumbnailUrl), pix);
// notify any affected items. Linear scan here avoids another map/dict m_model->m_downloadedPixmapCache.insert(QString::fromStdString(aThumbnailUrl), pix);
// structure.
// notify any affected items. Linear scan here avoids another map/dict structure.
for (auto pkg : m_model->m_packages) { for (auto pkg : m_model->m_packages) {
const string_list& urls(pkg->thumbnailUrls()); const int variantCount = pkg->variants().size();
auto cit = std::find(urls.begin(), urls.end(), aThumbnailUrl); bool notifyChanged = false;
if (cit != urls.end()) {
for (int v=0; v < variantCount; ++v) {
const Package::Thumbnail& thumb(pkg->thumbnailForVariant(v));
if (thumb.url == aThumbnailUrl) {
notifyChanged = true;
}
}
if (notifyChanged) {
QModelIndex mi = indexForPackage(pkg); QModelIndex mi = indexForPackage(pkg);
m_model->dataChanged(mi, mi); m_model->dataChanged(mi, mi);
} }
@ -593,10 +600,6 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
return m_delegateStates.at(row).variant; return m_delegateStates.at(row).variant;
} }
if (role == AircraftCurrentThumbnailRole) {
return m_delegateStates.at(row).thumbnail;
}
if (row >= m_items.size()) { if (row >= m_items.size()) {
quint32 packageIndex = row - m_items.size(); quint32 packageIndex = row - m_items.size();
const PackageRef& pkg(m_packages[packageIndex]); const PackageRef& pkg(m_packages[packageIndex]);
@ -623,20 +626,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
return item->variants.count(); return item->variants.count();
} }
if (role == AircraftThumbnailCountRole) { if (role >= AircraftVariantDescriptionRole) {
QPixmap p = item->thumbnail();
return p.isNull() ? 0 : 1;
}
if (role == AircraftThumbnailSizeRole) {
QPixmap pm = item->thumbnail(false);
if (pm.isNull()) {
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
}
return pm.size();
}
if ((role >= AircraftVariantDescriptionRole) && (role < AircraftThumbnailRole)) {
int variantIndex = role - AircraftVariantDescriptionRole; int variantIndex = role - AircraftVariantDescriptionRole;
return item->variants.at(variantIndex)->description; return item->variants.at(variantIndex)->description;
} }
@ -648,6 +638,14 @@ 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 (role == Qt::DisplayRole) {
if (item->description.isEmpty()) { if (item->description.isEmpty()) {
return tr("Missing description for: %1").arg(item->baseName()); return tr("Missing description for: %1").arg(item->baseName());
@ -662,7 +660,13 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
return item->authors; return item->authors;
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) { } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
return item->ratings[role - AircraftRatingRole]; return item->ratings[role - AircraftRatingRole];
} else if (role >= AircraftThumbnailRole) { } else if (role == AircraftPreviewsRole) {
QVariantList result;
Q_FOREACH(QUrl u, item->previews) {
result.append(u);
}
return result;
} else if (role == AircraftThumbnailRole) {
return item->thumbnail(); return item->thumbnail();
} else if (role == AircraftPackageIdRole) { } else if (role == AircraftPackageIdRole) {
// can we fake an ID? otherwise fall through to a null variant // can we fake an ID? otherwise fall through to a null variant
@ -692,10 +696,10 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const
{ {
if (role == Qt::DecorationRole) { if (role == Qt::DecorationRole) {
role = AircraftThumbnailRole; // use first thumbnail role = AircraftThumbnailRole;
} }
if ((role >= AircraftVariantDescriptionRole) && (role < AircraftThumbnailRole)) { if (role >= AircraftVariantDescriptionRole) {
int variantIndex = role - AircraftVariantDescriptionRole; int variantIndex = role - AircraftVariantDescriptionRole;
QString desc = QString::fromStdString(item->nameForVariant(variantIndex)); QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
if (desc.isEmpty()) { if (desc.isEmpty()) {
@ -743,11 +747,10 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
if (pm.isNull()) if (pm.isNull())
return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT); return QSize(STANDARD_THUMBNAIL_WIDTH, STANDARD_THUMBNAIL_HEIGHT);
return pm.size(); return pm.size();
} else if (role >= AircraftThumbnailRole) { } else if (role == AircraftThumbnailRole) {
DelegateState changedState(state); return packageThumbnail(item, state);
// override the current thumbnail as required } else if (role == AircraftPreviewsRole) {
changedState.thumbnail = (role - AircraftThumbnailRole); return packagePreviews(item, state);
return packageThumbnail(item, changedState);
} else if (role == AircraftAuthorsRole) { } else if (role == AircraftAuthorsRole) {
std::string authors = item->getLocalisedProp("author", state.variant); std::string authors = item->getLocalisedProp("author", state.variant);
if (!authors.empty()) { if (!authors.empty()) {
@ -776,46 +779,69 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega
QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const
{ {
const string_list& thumbnails(p->thumbnailUrls()); const Package::Thumbnail& thumb(p->thumbnailForVariant(ds.variant));
if (ds.thumbnail >= static_cast<int>(thumbnails.size())) { if (thumb.url.empty()) {
return QVariant(); return QVariant();
} }
std::string thumbnailUrl = thumbnails.at(ds.thumbnail); QString urlQString(QString::fromStdString(thumb.url));
QString urlQString(QString::fromStdString(thumbnailUrl)); if (m_downloadedPixmapCache.contains(urlQString)) {
if (m_thumbnailPixmapCache.contains(urlQString)) {
// cache hit, easy // cache hit, easy
return m_thumbnailPixmapCache.value(urlQString); return m_downloadedPixmapCache.value(urlQString);
} }
// check the on-disk store. This relies on the order of thumbnails in the // check the on-disk store.
// results of thumbnailUrls and thumbnails corresponding
InstallRef ex = p->existingInstall(); InstallRef ex = p->existingInstall();
if (ex.valid()) { if (ex.valid()) {
const string_list& thumbNames(p->thumbnails()); SGPath thumbPath = ex->path() / thumb.path;
if (!thumbNames.empty()) { if (thumbPath.exists()) {
SGPath path(ex->path());
path.append(p->thumbnails()[ds.thumbnail]);
if (path.exists()) {
QPixmap pix; QPixmap pix;
pix.load(QString::fromStdString(path.utf8Str())); pix.load(QString::fromStdString(thumbPath.utf8Str()));
// resize to the standard size // resize to the standard size
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT); pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
} }
m_thumbnailPixmapCache[urlQString] = pix; m_downloadedPixmapCache[urlQString] = pix;
return pix; return pix;
} }
} // of have thumbnail file names
} // of have existing install } // of have existing install
if (download) { if (download) {
m_packageRoot->requestThumbnailData(thumbnailUrl); m_packageRoot->requestThumbnailData(thumb.url);
} }
return QVariant(); 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) bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
int row = index.row(); int row = index.row();
@ -831,16 +857,6 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
return true; return true;
} }
if (role == AircraftCurrentThumbnailRole) {
if (m_delegateStates[row].thumbnail == newValue) {
return true;
}
m_delegateStates[row].thumbnail = newValue;
emit dataChanged(index, index);
return true;
}
return false; return false;
} }

View file

@ -38,7 +38,6 @@ const int AircraftPathRole = Qt::UserRole + 1;
const int AircraftAuthorsRole = Qt::UserRole + 2; const int AircraftAuthorsRole = Qt::UserRole + 2;
const int AircraftVariantRole = Qt::UserRole + 3; const int AircraftVariantRole = Qt::UserRole + 3;
const int AircraftVariantCountRole = Qt::UserRole + 4; const int AircraftVariantCountRole = Qt::UserRole + 4;
const int AircraftThumbnailCountRole = Qt::UserRole + 5;
const int AircraftPackageIdRole = Qt::UserRole + 6; const int AircraftPackageIdRole = Qt::UserRole + 6;
const int AircraftPackageStatusRole = Qt::UserRole + 7; const int AircraftPackageStatusRole = Qt::UserRole + 7;
const int AircraftPackageProgressRole = Qt::UserRole + 8; const int AircraftPackageProgressRole = Qt::UserRole + 8;
@ -51,12 +50,12 @@ const int AircraftURIRole = Qt::UserRole + 14;
const int AircraftThumbnailSizeRole = Qt::UserRole + 15; const int AircraftThumbnailSizeRole = Qt::UserRole + 15;
const int AircraftIsHelicopterRole = Qt::UserRole + 16; const int AircraftIsHelicopterRole = Qt::UserRole + 16;
const int AircraftIsSeaplaneRole = Qt::UserRole + 17; const int AircraftIsSeaplaneRole = Qt::UserRole + 17;
const int AircraftCurrentThumbnailRole = Qt::UserRole + 18;
const int AircraftPackageRefRole = Qt::UserRole + 19; const int AircraftPackageRefRole = Qt::UserRole + 19;
const int AircraftThumbnailRole = Qt::UserRole + 20;
const int AircraftPreviewsRole = Qt::UserRole + 21;
const int AircraftRatingRole = Qt::UserRole + 100; const int AircraftRatingRole = Qt::UserRole + 100;
const int AircraftVariantDescriptionRole = Qt::UserRole + 200; const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
const int AircraftThumbnailRole = Qt::UserRole + 300;
class AircraftScanThread; class AircraftScanThread;
class QDataStream; class QDataStream;
@ -81,17 +80,18 @@ struct AircraftItem
QPixmap thumbnail(bool loadIfRequired = true) const; QPixmap thumbnail(bool loadIfRequired = true) const;
bool excluded; bool excluded = false;
QString path; QString path;
QString description; QString description;
QString longDescription; QString longDescription;
QString authors; QString authors;
int ratings[4]; int ratings[4] = {0, 0, 0, 0};
QString variantOf; QString variantOf;
QDateTime pathModTime; QDateTime pathModTime;
QList<AircraftItemPtr> variants; QList<AircraftItemPtr> variants;
bool usesHeliports; bool usesHeliports = false;
bool usesSeaports; bool usesSeaports = false;
QList<QUrl> previews;
private: private:
mutable QPixmap m_thumbnail; mutable QPixmap m_thumbnail;
}; };
@ -199,6 +199,8 @@ private:
QVariant packageThumbnail(simgear::pkg::PackageRef p, QVariant packageThumbnail(simgear::pkg::PackageRef p,
const DelegateState& state, bool download = true) const; const DelegateState& state, bool download = true) const;
QVariant packagePreviews(simgear::pkg::PackageRef p, const DelegateState &ds) const;
void abandonCurrentScan(); void abandonCurrentScan();
void refreshPackages(); void refreshPackages();
@ -217,7 +219,7 @@ private:
simgear::pkg::RootRef m_packageRoot; simgear::pkg::RootRef m_packageRoot;
simgear::pkg::PackageList m_packages; simgear::pkg::PackageList m_packages;
mutable QHash<QString, QPixmap> m_thumbnailPixmapCache; mutable QHash<QString, QPixmap> m_downloadedPixmapCache;
}; };
#endif // of FG_GUI_AIRCRAFT_MODEL #endif // of FG_GUI_AIRCRAFT_MODEL

View file

@ -117,11 +117,13 @@ if (HAVE_QT)
InstallSceneryDialog.cxx InstallSceneryDialog.cxx
EditCustomMPServerDialog.cxx EditCustomMPServerDialog.cxx
EditCustomMPServerDialog.hxx EditCustomMPServerDialog.hxx
PreviewWindow.cpp
PreviewWindow.h
${uic_sources} ${uic_sources}
${qrc_sources}) ${qrc_sources})
set_property(TARGET fglauncher PROPERTY AUTOMOC ON) set_property(TARGET fglauncher PROPERTY AUTOMOC ON)
target_link_libraries(fglauncher Qt5::Core Qt5::Widgets SimGearCore) target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network SimGearCore)
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI) target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)

View file

@ -34,7 +34,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="summaryTab"> <widget class="QWidget" name="summaryTab">
<attribute name="title"> <attribute name="title">

View file

@ -69,6 +69,7 @@
#include "AircraftModel.hxx" #include "AircraftModel.hxx"
#include "PathsDialog.hxx" #include "PathsDialog.hxx"
#include "EditCustomMPServerDialog.hxx" #include "EditCustomMPServerDialog.hxx"
#include "previewwindow.h"
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include <Main/fg_props.hxx> #include <Main/fg_props.hxx>
@ -854,6 +855,8 @@ QtLauncher::QtLauncher() :
this, &QtLauncher::onRequestPackageUninstall); this, &QtLauncher::onRequestPackageUninstall);
connect(delegate, &AircraftItemDelegate::cancelDownload, connect(delegate, &AircraftItemDelegate::cancelDownload,
this, &QtLauncher::onCancelDownload); this, &QtLauncher::onCancelDownload);
connect(delegate, &AircraftItemDelegate::showPreviews,
this, &QtLauncher::onShowPreviews);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted, connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
this, &QtLauncher::onAircraftInstalledCompleted); this, &QtLauncher::onAircraftInstalledCompleted);
@ -1401,6 +1404,14 @@ void QtLauncher::onRequestPackageUninstall(const QModelIndex& index)
} }
} }
void QtLauncher::onShowPreviews(const QModelIndex &index)
{
QVariant urls = index.data(AircraftPreviewsRole);
PreviewWindow* previewWindow = new PreviewWindow;
previewWindow->setUrls(urls.toList());
}
void QtLauncher::onCancelDownload(const QModelIndex& index) void QtLauncher::onCancelDownload(const QModelIndex& index)
{ {
QString pkg = index.data(AircraftPackageIdRole).toString(); QString pkg = index.data(AircraftPackageIdRole).toString();

View file

@ -75,7 +75,7 @@ private slots:
void onAircraftSelected(const QModelIndex& index); void onAircraftSelected(const QModelIndex& index);
void onRequestPackageInstall(const QModelIndex& index); void onRequestPackageInstall(const QModelIndex& index);
void onRequestPackageUninstall(const QModelIndex& index); void onRequestPackageUninstall(const QModelIndex& index);
void onShowPreviews(const QModelIndex& index);
void onCancelDownload(const QModelIndex& index); void onCancelDownload(const QModelIndex& index);
void onPopupAircraftHistory(); void onPopupAircraftHistory();

BIN
src/GUI/preview-close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
src/GUI/preview-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

108
src/GUI/previewwindow.cpp Normal file
View file

@ -0,0 +1,108 @@
#include "previewwindow.h"
#include <QPainter>
#include <QMouseEvent>
#include <QNetworkReply>
const int BORDER_SIZE = 16;
PreviewWindow::PreviewWindow(QWidget *parent)
: QDialog(parent)
, m_netAccess(new QNetworkAccessManager(this))
{
setWindowFlags(Qt::Popup);
setModal(true);
m_closeIcon.load(":/preview/close-icon");
m_leftIcon.load(":/preview/left-arrow-icon");
m_rightIcon.load(":/preview/right-arrow-icon");
}
void PreviewWindow::setUrls(QVariantList urls)
{
m_cache.clear();
Q_FOREACH (QVariant v, urls) {
QUrl url = v.toUrl();
qWarning() << v;
m_urls.append(url);
QNetworkReply* reply = m_netAccess->get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, &PreviewWindow::onDownloadFinished);
}
}
void PreviewWindow::paintEvent(QPaintEvent *pe)
{
QUrl key = m_urls.at(m_currentPreview);
QPixmap pm = m_cache.value(key.toString());
if (pm.isNull()) {
qWarning() << "null pixmap";
}
QPainter painter(this);
painter.fillRect(rect(), Qt::black);
QRect imgRect = rect().adjusted(BORDER_SIZE, BORDER_SIZE, -BORDER_SIZE, -BORDER_SIZE);
painter.drawPixmap(imgRect, pm);
QRect closeIconRect = m_closeIcon.rect();
closeIconRect.moveTopRight(rect().topRight());
painter.drawPixmap(closeIconRect, m_closeIcon);
QRect leftArrowRect = m_leftIcon.rect();
unsigned int iconTop = rect().center().y() - (m_leftIcon.size().height() / 2);
leftArrowRect.moveTopLeft(QPoint(0, iconTop));
painter.drawPixmap(leftArrowRect, m_leftIcon);
QRect rightArrowRect = m_rightIcon.rect();
rightArrowRect.moveTopRight(QPoint(width(), iconTop));
painter.drawPixmap(rightArrowRect, m_rightIcon);
}
void PreviewWindow::mouseReleaseEvent(QMouseEvent *event)
{
QRect closeIconRect = m_closeIcon.rect();
closeIconRect.moveTopRight(rect().topRight());
QRect leftArrowRect = m_leftIcon.rect();
unsigned int iconTop = rect().center().y() - (m_leftIcon.size().height() / 2);
leftArrowRect.moveTopLeft(QPoint(0, iconTop));
QRect rightArrowRect = m_rightIcon.rect();
rightArrowRect.moveTopRight(QPoint(width(), iconTop));
if (closeIconRect.contains(event->pos())) {
close();
deleteLater();
}
if (leftArrowRect.contains(event->pos())) {
m_currentPreview = (m_currentPreview - 1) % m_urls.size();
update();
}
if (rightArrowRect.contains(event->pos())) {
m_currentPreview = (m_currentPreview + 1) % m_urls.size();
update();
}
}
void PreviewWindow::onDownloadFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QImage img;
if (!img.load(reply, nullptr)) {
qWarning() << "failed to read image data from" << reply->url();
return;
}
m_cache.insert(reply->url().toString(), QPixmap::fromImage(img));
if (!isVisible()) {
QSize winSize(img.width() + BORDER_SIZE * 2, img.height() + BORDER_SIZE * 2);
resize(winSize);
show();
}
}

39
src/GUI/previewwindow.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef PREVIEWWINDOW_H
#define PREVIEWWINDOW_H
#include <QDialog>
#include <QVariant>
#include <QNetworkAccessManager>
#include <QMap>
class PreviewWindow : public QDialog
{
Q_OBJECT
public:
explicit PreviewWindow(QWidget *parent = 0);
void setUrls(QVariantList urls);
signals:
public slots:
protected:
virtual void paintEvent(QPaintEvent *pe) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
private:
void onDownloadFinished();
unsigned int m_currentPreview = 0;
QNetworkAccessManager* m_netAccess;
QList<QUrl> m_urls;
QMap<QUrl, QPixmap> m_cache;
QPixmap m_leftIcon, m_rightIcon, m_closeIcon;
};
#endif // PREVIEWWINDOW_H

View file

@ -19,5 +19,11 @@
<file alias="ndb-large-icon">ndb-large-icon.png</file> <file alias="ndb-large-icon">ndb-large-icon.png</file>
<file alias="airplane-icon">airplane-icon.png</file> <file alias="airplane-icon">airplane-icon.png</file>
<file alias="airport-closed-icon">airport-closed-icon.png</file> <file alias="airport-closed-icon">airport-closed-icon.png</file>
<file alias="preview-icon">preview-icon.png</file>
</qresource>
<qresource prefix="/preview">
<file alias="close-icon">preview-close.png</file>
<file alias="left-arrow-icon">preview-left-arrow.png</file>
<file alias="right-arrow-icon">preview-right-arrow.png</file>
</qresource> </qresource>
</RCC> </RCC>