File indexing completed on 2024-04-21 04:05:24

0001 /*
0002     This file is part of the KDE games lskat program
0003     SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "gameview.h"
0009 
0010 // General includes
0011 #include <cmath>
0012 
0013 // Qt includes
0014 #include <QTimer>
0015 
0016 // KF includes
0017 
0018 // Local includes
0019 #include "lskat_debug.h"
0020 #include "lskatglobal.h"
0021 #include "thememanager.h"
0022 
0023 // Constructor for the view
0024 GameView::GameView(const QSize &size, int advancePeriod, QGraphicsScene *scene, ThemeManager *theme, QWidget *parent)
0025         : QGraphicsView(scene, parent)
0026 {
0027     // Store attributes
0028     mTheme  = theme;
0029 
0030     // We do not need scrolling so switch it off
0031     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0032     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0033 
0034     // Frame off
0035     setFrameStyle(QFrame::NoFrame);
0036 
0037     // Cache on
0038     setCacheMode(QGraphicsView::CacheBackground);
0039     setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
0040     setOptimizationFlags(
0041                          QGraphicsView::DontSavePainterState |
0042                          QGraphicsView::DontAdjustForAntialiasing);
0043 
0044     // Debug
0045     mDisplayUpdateTime = 0;
0046     mFrameSprite = new QGraphicsTextItem(nullptr);
0047     scene->addItem(mFrameSprite);
0048     mFrameSprite->setPos(QPointF(0.0, 0.0));
0049     mFrameSprite->setZValue(1000.0);
0050     if (global_debug > 0) mFrameSprite->show();
0051     else mFrameSprite->hide();
0052 
0053     // Update/advance in [ms]
0054     QTimer *timer = new QTimer(this);
0055     connect(timer, &QTimer::timeout, this, &GameView::updateAndAdvance);
0056     timer->start(advancePeriod);
0057 
0058     // Set size and position of the view and the canvas:
0059     // they are resized once a level is loaded
0060     resize(size);
0061     scene->setSceneRect(0, 0, this->width(), this->height());
0062     adjustSize();
0063 
0064     // Enable mouse
0065     setInteractive(true);
0066 
0067     // Scale theme
0068     //mTheme->rescale(this->width());
0069     mThemeQueue.clear();
0070     mThemeOffset.clear();
0071     mTimeStart.start();
0072 }
0073 
0074 GameView::~GameView()
0075 {
0076     delete mFrameSprite;
0077 }
0078 
0079 // Advance and update canvas
0080 void GameView::updateAndAdvance()
0081 {
0082     static int elapsed = -1;
0083     static QElapsedTimer timer;
0084     if (elapsed < 0)
0085     {
0086         timer.start();
0087         elapsed = 0;
0088     }
0089     else
0090     {
0091         elapsed = timer.elapsed();
0092         timer.start();
0093         mDisplayUpdateTime = elapsed;
0094     }
0095 
0096     scene()->advance();
0097     //NOTE regarding QGV porting
0098     //QGV will handle dirty rects for us
0099     //Calling update will just dirty the view and cause a full redraw, killing performance
0100     //scene()->update();
0101 }
0102 
0103 // Slot called by the framework when the window is resized.
0104 void GameView::resizeEvent(QResizeEvent *e)
0105 {
0106     QElapsedTimer t;
0107     t.start();
0108     if (global_debug > 2) qCDebug(LSKAT_LOG) << "RESIZE EVENT " << e->size() << " oldSize="<< e->oldSize() << " at " << t.msecsTo(mTimeStart);
0109     double diffW = double(e->oldSize().width() - e->size().width());
0110     double diffH = double(e->oldSize().height() - e->size().height());
0111     double delta = fabs(diffW) + fabs(diffH);
0112 
0113     // Adapt the canvas size to the window size
0114     if (scene())
0115     {
0116         scene()->setSceneRect(0, 0, e->size().width(), e->size().height());
0117     }
0118 
0119     QSizeF size = QSizeF(e->size());
0120     // Rescale on minimum fitting aspect ratio either width or height limiting
0121     double aspect = size.width() / size.height();
0122     QPoint offset;
0123     double width = 0.0;
0124 
0125     // Scale width:
0126     // Ideal size would be: 'width'*'height'
0127     // Offset in width is (e->size().width() - width) / 2, offset in height is zero
0128     if (aspect > mTheme->aspectRatio())
0129     {
0130         width  = e->size().height() * mTheme->aspectRatio();
0131         offset = QPoint(int((e->size().width() - width) / 2.0), 0);
0132     }
0133     // Scale height:
0134     // 'height' = width / mTheme->aspectRatio()
0135     // Ideal size would be: 'width'*'height':
0136     // Offset in height is (e->size().height() - width / mTheme->aspectRatio()) / 2, offset in width is zero
0137     else
0138     {
0139         width = e->size().width();
0140         offset = QPoint(0, int((e->size().height() - width / mTheme->aspectRatio()) / 2.0));
0141     }
0142 
0143     // Pixel rescale
0144     double oldScale = mTheme->getScale();
0145     resetTransform();
0146     if (width > oldScale)
0147     {
0148         scale(double(width / oldScale), double(width / oldScale));
0149     }
0150     mThemeQueue.prepend(int(width));
0151     mThemeOffset.prepend(offset);
0152     if (global_debug > 2) qCDebug(LSKAT_LOG) << "Queued resize, aspect=" << aspect << " theme aspect=" << mTheme->aspectRatio();
0153 
0154     long queueDelay = 0;
0155     if (delta < 15) queueDelay = 750;
0156     else if (delta < 35) queueDelay = 500;
0157 
0158     QTimer::singleShot(queueDelay, this, &GameView::rescaleTheme);
0159 }
0160 
0161 // Rescale the theme (update theme SVG graphics) from the theme list
0162 void GameView::rescaleTheme()
0163 {
0164     if (mThemeQueue.size() == 0)
0165     {
0166         if (global_debug > 2) qCDebug(LSKAT_LOG) << "***************** Swallowing rescale event ***********************";
0167         return;
0168     }
0169 
0170     QElapsedTimer t;
0171     t.start();
0172 
0173     if (global_debug > 2) qCDebug(LSKAT_LOG) << "Theme queue rescale start at " << t.msecsTo(mTimeStart);
0174     resetTransform();
0175     int width = mThemeQueue.first();
0176     mInputOffset = mThemeOffset.first();
0177     if (global_debug > 2) qCDebug(LSKAT_LOG) << "Theme queue size=" << mThemeQueue.size() << " Rescale width to " << width;
0178 
0179     mThemeQueue.clear();
0180     mThemeOffset.clear();
0181     mTheme->rescale(width, mInputOffset);
0182 
0183     if (global_debug > 2) qCDebug(LSKAT_LOG) << "Time elapsed: " << t.elapsed() << "ms";
0184 }
0185 
0186 // Our subclassed (temporary) QGraphicsView paintEvent, see header file
0187 void GameView::paintEvent(QPaintEvent *event)
0188 {
0189     QPaintEvent *newEvent = new QPaintEvent(event->region().boundingRect());
0190     QGraphicsView::paintEvent(newEvent);
0191     delete newEvent;
0192 }
0193 
0194 // Mouse click event
0195 void GameView::mouseReleaseEvent(QMouseEvent *ev)
0196 {
0197     if (ev->button() != Qt::LeftButton) return;
0198 
0199     QPointF point = ev->pos()-mInputOffset;
0200     Q_EMIT signalLeftMousePress(point.toPoint());
0201 }
0202 
0203 void GameView::drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[])
0204 {
0205     QElapsedTimer time;
0206     time.start();
0207     QGraphicsView::drawItems(painter, numItems, items, options);
0208 
0209     // Time display
0210     int elapsed = time.elapsed();
0211     mDrawTimes.append(elapsed);
0212     if (mDrawTimes.size() > 50) mDrawTimes.removeFirst();
0213     double avg = 0.0;
0214     for (int drawTime : std::as_const(mDrawTimes))
0215         avg += drawTime;
0216     avg /= mDrawTimes.size();
0217 
0218     if (global_debug > 0)
0219         mFrameSprite->setPlainText(QStringLiteral("Draw: %1 ms  Average %2 ms  Update: %3 ms").arg(elapsed).arg(int(avg)).arg(mDisplayUpdateTime));
0220 }
0221 
0222 #include "moc_gameview.cpp"