1 // AircraftItemDelegate.cxx - part of GUI launcher using Qt5
3 // Written by James Turner, started March 2015.
5 // Copyright (C) 2014 James Turner <zakalawe@mac.com>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "AircraftItemDelegate.hxx"
26 #include <QLinearGradient>
28 #include <QMouseEvent>
29 #include <QFontMetrics>
31 #include "AircraftModel.hxx"
33 AircraftItemDelegate::AircraftItemDelegate(QListView* view) :
36 view->viewport()->installEventFilter(this);
37 view->viewport()->setMouseTracking(true);
39 m_leftArrowIcon.load(":/left-arrow-icon");
40 m_rightArrowIcon.load(":/right-arrow-icon");
43 void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option,
44 const QModelIndex & index) const
46 QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
48 QVariant v = index.data(AircraftPackageStatusRole);
49 AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
50 if (status == NoOfficialCatalogMessage) {
51 painter->setPen(QColor(0x7f, 0x7f, 0x7f));
52 painter->setBrush(Qt::NoBrush);
54 // draw bottom dividing line
55 painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
56 contentRect.right(), contentRect.bottom() + MARGIN);
61 // selection feedback rendering
62 if (option.state & QStyle::State_Selected) {
63 QLinearGradient grad(option.rect.topLeft(), option.rect.bottomLeft());
64 grad.setColorAt(0.0, QColor(152, 163, 180));
65 grad.setColorAt(1.0, QColor(90, 107, 131));
67 QBrush backgroundBrush(grad);
68 painter->fillRect(option.rect, backgroundBrush);
70 painter->setPen(QColor(90, 107, 131));
71 painter->drawLine(option.rect.topLeft(), option.rect.topRight());
75 QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
76 quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
77 painter->drawPixmap(contentRect.left(), yPos, thumbnail);
80 painter->setPen(QColor(0x7f, 0x7f, 0x7f));
81 painter->setBrush(Qt::NoBrush);
82 painter->drawRect(contentRect.left(), yPos, thumbnail.width(), thumbnail.height());
84 // draw bottom dividing line
85 painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
86 contentRect.right(), contentRect.bottom() + MARGIN);
88 int variantCount = index.data(AircraftVariantCountRole).toInt();
89 int currentVariant = index.data(AircraftVariantRole).toInt();
90 QString description = index.data(Qt::DisplayRole).toString();
91 contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
93 painter->setPen(Qt::black);
98 QRect descriptionRect = contentRect.adjusted(ARROW_SIZE, 0, -ARROW_SIZE, 0),
101 if (variantCount > 0) {
102 bool canLeft = (currentVariant > 0);
103 bool canRight = (currentVariant < variantCount );
105 QRect leftArrowRect = leftCycleArrowRect(option.rect, index);
107 painter->drawPixmap(leftArrowRect.topLeft() + QPoint(2, 2), m_leftArrowIcon);
110 QRect rightArrowRect = rightCycleArrowRect(option.rect, index);
112 painter->drawPixmap(rightArrowRect.topLeft() + QPoint(2, 2), m_rightArrowIcon);
116 painter->drawText(descriptionRect, Qt::TextWordWrap, description, &actualBounds);
117 QString authors = index.data(AircraftAuthorsRole).toString();
120 QFontMetrics smallMetrics(f);
124 if (!authors.isEmpty()) {
125 QRect authorsRect = descriptionRect;
126 authorsRect.moveTop(actualBounds.bottom() + MARGIN);
127 painter->drawText(authorsRect, Qt::TextWordWrap,
128 QString("by: %1").arg(authors),
132 QString longDescription = index.data(AircraftLongDescriptionRole).toString();
133 if (!longDescription.isEmpty()) {
134 QRect longDescriptionRect = descriptionRect;
135 longDescriptionRect.moveTop(actualBounds.bottom() + MARGIN);
136 painter->drawText(longDescriptionRect, Qt::TextWordWrap,
137 longDescription, &actualBounds);
140 QRect r = contentRect;
141 r.setWidth(contentRect.width() / 3);
142 r.moveTop(actualBounds.bottom() + MARGIN);
143 r.moveLeft(r.right());
144 r.setHeight(qMax(24, smallMetrics.height() + MARGIN));
146 painter->setRenderHint(QPainter::Antialiasing, true);
148 if (index.data(AircraftHasRatingsRole).toBool()) {
149 drawRating(painter, "Flight model:", r, index.data(AircraftRatingRole).toInt());
150 r.moveTop(r.bottom());
151 drawRating(painter, "Systems:", r, index.data(AircraftRatingRole + 1).toInt());
153 r.moveTop(actualBounds.bottom() + MARGIN);
154 r.moveLeft(r.right());
155 drawRating(painter, "Cockpit:", r, index.data(AircraftRatingRole + 2).toInt());
156 r.moveTop(r.bottom());
157 drawRating(painter, "Exterior:", r, index.data(AircraftRatingRole + 3).toInt());
160 double downloadFraction = 0.0;
162 if (status != PackageInstalled) {
163 QString buttonText, infoText;
164 QColor buttonColor(27, 122, 211);
166 double sizeInMBytes = index.data(AircraftPackageSizeRole).toInt();
167 sizeInMBytes /= 0x100000;
169 if (status == PackageDownloading) {
170 buttonText = tr("Cancel");
171 double downloadedMB = index.data(AircraftInstallDownloadedSizeRole).toInt();
172 downloadedMB /= 0x100000;
173 infoText = QStringLiteral("%1 MB of %2 MB").arg(downloadedMB, 0, 'f', 1).arg(sizeInMBytes, 0, 'f', 1);
174 buttonColor = QColor(0xcf, 0xcf, 0xcf);
175 downloadFraction = downloadedMB / sizeInMBytes;
176 } else if (status == PackageQueued) {
177 buttonText = tr("Cancel");
178 infoText = tr("Waiting to download %1 MB").arg(sizeInMBytes, 0, 'f', 1);
179 buttonColor = QColor(0xcf, 0xcf, 0xcf);
181 infoText = QStringLiteral("%1MB").arg(sizeInMBytes, 0, 'f', 1);
182 if (status == PackageNotInstalled) {
183 buttonText = "Install";
184 } else if (status == PackageUpdateAvailable) {
185 buttonText = "Update";
189 painter->setBrush(Qt::NoBrush);
190 QRect buttonRect = packageButtonRect(option.rect, index);
191 painter->setPen(Qt::NoPen);
192 painter->setBrush(buttonColor);
193 painter->drawRoundedRect(buttonRect, 5, 5);
194 painter->setPen(Qt::white);
195 painter->drawText(buttonRect, Qt::AlignCenter, buttonText);
197 QRect infoTextRect = buttonRect;
198 infoTextRect.setLeft(buttonRect.right() + MARGIN);
199 infoTextRect.setWidth(200);
201 if (status == PackageDownloading) {
202 QRect progressRect = infoTextRect;
203 progressRect.setHeight(6);
204 painter->setPen(QPen(QColor(0xcf, 0xcf, 0xcf), 0));
205 painter->setBrush(Qt::NoBrush);
206 painter->drawRoundedRect(progressRect, 3, 3);
207 infoTextRect.setTop(progressRect.bottom() + 1);
209 QRect progressBarRect = progressRect.marginsRemoved(QMargins(2, 2, 2, 2));
211 progressBarRect.setWidth(static_cast<int>(progressBarRect.width() * downloadFraction));
213 painter->setBrush(QColor(27, 122, 211));
214 painter->setPen(Qt::NoPen);
215 painter->drawRoundedRect(progressBarRect, 2, 2);
218 painter->setPen(Qt::black);
219 painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
220 } // of update / install / download status
222 painter->setRenderHint(QPainter::Antialiasing, false);
226 QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
228 QVariant v = index.data(AircraftPackageStatusRole);
229 AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
231 if (status == NoOfficialCatalogMessage) {
232 QSize r = option.rect.size();
237 QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
239 QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize();
240 contentRect.setLeft(contentRect.left() + MARGIN + thumbnailSize.width());
241 contentRect.setBottom(9999); // large value to avoid clipping
242 contentRect.adjust(ARROW_SIZE, 0, -ARROW_SIZE, 0);
246 QFontMetrics metrics(f);
248 int textHeight = metrics.boundingRect(contentRect, Qt::TextWordWrap,
249 index.data().toString()).height();
252 QFontMetrics smallMetrics(f);
254 QString authors = QString("by: %1").arg(index.data(AircraftAuthorsRole).toString());
256 if (!authors.isEmpty()) {
257 textHeight += MARGIN;
258 textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, authors).height();
261 QString desc = index.data(AircraftLongDescriptionRole).toString();
262 if (!desc.isEmpty()) {
263 textHeight += MARGIN;
264 textHeight += smallMetrics.boundingRect(contentRect, Qt::TextWordWrap, desc).height();
267 if (index.data(AircraftHasRatingsRole).toBool()) {
269 int ratingHeight = qMax(24, smallMetrics.height() + MARGIN);
270 textHeight += ratingHeight * 2;
272 // just the button height
273 textHeight += BUTTON_HEIGHT;
276 textHeight = qMax(textHeight, thumbnailSize.height());
277 return QSize(option.rect.width(), textHeight + (MARGIN * 2));
280 bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
282 if ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
284 QMouseEvent* me = static_cast< QMouseEvent* >( event );
285 QModelIndex index = m_view->indexAt( me->pos() );
286 int variantCount = index.data(AircraftVariantCountRole).toInt();
287 int variantIndex = index.data(AircraftVariantRole).toInt();
288 QRect vr = m_view->visualRect(index);
290 if ( (event->type() == QEvent::MouseButtonRelease) && (variantCount > 0) )
292 QRect leftCycleRect = leftCycleArrowRect(vr, index),
293 rightCycleRect = rightCycleArrowRect(vr, index);
295 if ((variantIndex > 0) && leftCycleRect.contains(me->pos())) {
296 m_view->model()->setData(index, variantIndex - 1, AircraftVariantRole);
297 emit variantChanged(index);
299 } else if ((variantIndex < variantCount) && rightCycleRect.contains(me->pos())) {
300 m_view->model()->setData(index, variantIndex + 1, AircraftVariantRole);
301 emit variantChanged(index);
306 if ((event->type() == QEvent::MouseButtonRelease) &&
307 packageButtonRect(vr, index).contains(me->pos()))
309 QVariant v = index.data(AircraftPackageStatusRole);
310 AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
311 if (status == PackageNotInstalled) {
312 emit requestInstall(index);
313 } else if ((status == PackageDownloading) || (status == PackageQueued)) {
314 emit cancelDownload(index);
315 } else if (status == PackageUpdateAvailable) {
316 emit requestInstall(index);
321 } else if ( event->type() == QEvent::MouseMove ) {
323 QMouseEvent* me = static_cast< QMouseEvent* >( event );
324 QModelIndex index = m_view->indexAt( me->pos() );
325 QRect vr = m_view->visualRect(index);
327 if (packageButtonRect(vr, index).contains(me->pos())) {
328 qDebug() << "mouse inside button";
337 QRect AircraftItemDelegate::leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
339 QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
340 QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
341 contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
343 QRect r = contentRect;
344 r.setRight(r.left() + ARROW_SIZE);
345 r.setBottom(r.top() + ARROW_SIZE);
350 QRect AircraftItemDelegate::rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const
352 QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
353 QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
354 contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
356 QRect r = contentRect;
357 r.setLeft(r.right() - ARROW_SIZE);
358 r.setBottom(r.top() + ARROW_SIZE);
363 QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QModelIndex& index) const
365 QRect contentRect = visualRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
366 QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
367 contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
369 return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24,
370 BUTTON_WIDTH, BUTTON_HEIGHT);
373 void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
375 const int DOT_SIZE = 10;
376 const int DOT_MARGIN = 2;
379 dotBox.setLeft(box.right() - (DOT_MARGIN * 6 + DOT_SIZE * 5));
381 painter->setPen(Qt::black);
383 textBox.setRight(dotBox.left() - DOT_MARGIN);
384 painter->drawText(textBox, Qt::AlignVCenter | Qt::AlignRight, label);
386 painter->setPen(Qt::NoPen);
387 QRect dot(dotBox.left() + DOT_MARGIN,
388 dotBox.center().y() - (DOT_SIZE / 2),
391 for (int i=0; i<5; ++i) {
392 painter->setBrush((i < value) ? QColor(0x3f, 0x3f, 0x3f) : QColor(0xaf, 0xaf, 0xaf));
393 painter->drawEllipse(dot);
394 dot.moveLeft(dot.right() + DOT_MARGIN);