Previews displayed in the launcher.
This commit is contained in:
parent
38554f629f
commit
17fe0460a9
16 changed files with 1004 additions and 786 deletions
|
@ -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}")
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -432,7 +439,7 @@ private:
|
||||||
AircraftItemModel* m_model;
|
AircraftItemModel* m_model;
|
||||||
};
|
};
|
||||||
|
|
||||||
AircraftItemModel::AircraftItemModel(QObject* pr ) :
|
AircraftItemModel::AircraftItemModel(QObject* pr) :
|
||||||
QAbstractListModel(pr)
|
QAbstractListModel(pr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -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());
|
QPixmap pix;
|
||||||
path.append(p->thumbnails()[ds.thumbnail]);
|
pix.load(QString::fromStdString(thumbPath.utf8Str()));
|
||||||
if (path.exists()) {
|
// resize to the standard size
|
||||||
QPixmap pix;
|
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||||
pix.load(QString::fromStdString(path.utf8Str()));
|
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||||
// resize to the standard size
|
|
||||||
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
|
||||||
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
|
||||||
}
|
|
||||||
m_thumbnailPixmapCache[urlQString] = pix;
|
|
||||||
return pix;
|
|
||||||
}
|
}
|
||||||
} // of have thumbnail file names
|
m_downloadedPixmapCache[urlQString] = pix;
|
||||||
|
return pix;
|
||||||
|
}
|
||||||
} // 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
1380
src/GUI/Launcher.ui
1380
src/GUI/Launcher.ui
File diff suppressed because it is too large
Load diff
|
@ -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();
|
||||||
|
|
|
@ -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
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
BIN
src/GUI/preview-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 771 B |
BIN
src/GUI/preview-left-arrow.png
Normal file
BIN
src/GUI/preview-left-arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,016 B |
BIN
src/GUI/preview-right-arrow.png
Normal file
BIN
src/GUI/preview-right-arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 964 B |
108
src/GUI/previewwindow.cpp
Normal file
108
src/GUI/previewwindow.cpp
Normal 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
39
src/GUI/previewwindow.h
Normal 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
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue