diff --git a/changes b/changes index 5ce8ee768c72b8eab396089f58712a3a69773113..fbf2375b23552a1aa7c834f68caea3eb5ed9f3c6 100644 --- a/changes +++ b/changes @@ -1,4 +1,5 @@ ANDROID + - handle pinch gesture to zoom score in/out - application can restart itself to restore initial settings 1.2A.6 diff --git a/src/libs/main/score/tmainscore.cpp b/src/libs/main/score/tmainscore.cpp index a210a378e9008fed7865b06fc6070015e11adfad..4de08247aba4d20b9fe8eceafaf4150e969110b3 100644 --- a/src/libs/main/score/tmainscore.cpp +++ b/src/libs/main/score/tmainscore.cpp @@ -90,6 +90,13 @@ TmainScore::TmainScore(QMainWindow* mw, QWidget* parent) : createNoteName(); isExamExecuting(false); // initialize some connections setNote(0, Tnote()); // To display fake empty note properly + + connect(this, &TmultiScore::pinchZoom, [=](int dir) { + if (dir > 0) + m_acts->zoomIn()->trigger(); + else + m_acts->zoomOut()->trigger(); + }); } @@ -714,16 +721,30 @@ void TmainScore::showNamesSlot() { } +/** + * @p zoomScoreSlot() is invoked by zoom in/out actions + * triggered from menu or through pinch zoom signal, + * and represents user scale factor. + * By default preferred scale of a staff is: + * - half of screen height on desktop + * - 6 * finger size on mobile, + * but not greater than score widget height - to ensure display entire staff + * On bigger tablet screens maximum height is bounded by 8 * finger size (mobile) + * or screen height / 1.5 (desktop). + * Minimal size is bounded by user scale factor but under mobile, + * also by minimal clef width that can be smaller than half of finger + * to avoid covering clef by menu button + */ void TmainScore::zoomScoreSlot() { - qreal newScale = Tcore::gl()->S->scoreScale; - if (sender() == m_acts->zoomOut()) { - newScale = qMin(Tcore::gl()->S->scoreScale + 0.25, 2.0); - } else { - newScale = qMax(Tcore::gl()->S->scoreScale - 0.25, 1.0); - } - if (newScale != Tcore::gl()->S->scoreScale) { - Tcore::gl()->S->scoreScale = newScale; - setScoreScale(newScale); + qreal newScale = Tcore::gl()->S->scoreScale; + if (sender() == m_acts->zoomOut()) + newScale = qMin(Tcore::gl()->S->scoreScale + 0.2, 3.0); + else + newScale = qMax(Tcore::gl()->S->scoreScale - 0.2, 0.4); + + if (newScale != Tcore::gl()->S->scoreScale) { + setScoreScale(newScale); + Tcore::gl()->S->scoreScale = scoreScale(); if (m_questMark) { m_questMark->setPos(0, 0); // reset position to enable positioning again setQuestionMarkPos(); diff --git a/src/libs/score/tmultiscore.cpp b/src/libs/score/tmultiscore.cpp index 3b2b7ef87ecf890908b984749e9fcfc5ddfbca38..e94d0c71a356666f84cb0839e4160e34734154a8 100644 --- a/src/libs/score/tmultiscore.cpp +++ b/src/libs/score/tmultiscore.cpp @@ -29,7 +29,12 @@ #endif #include <QtWidgets/QtWidgets> - +/** Definition of maximum staff height depends on platform */ +#if defined (Q_OS_ANDROID) + #define MAX_STAFF_HEIGHT qMin(height(), Tmtr::fingerPixels() * 8) +#else + #define MAX_STAFF_HEIGHT qMin(qreal(height()), qMin(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 1.5) +#endif #define SENDER_TO_STAFF static_cast<TscoreStaff*>(sender()) @@ -179,7 +184,26 @@ void TmultiScore::setScoreDisabled(bool disabled) { } +/** + * @p m_scale represents scale sets by user + * - this value can be ignored: + * -- if scale is too big to display entire staff + * -- or when it is too small and clef goes under mobile menu + * By default @p m_scale = 1.0 - bigger values make score smaller + * smaller values increase the score + */ void TmultiScore::setScoreScale(qreal sc) { + qreal scoreFactor = transform().m11() * getScaleFactor(height(), sc); + if (staff()->height() * scoreFactor > MAX_STAFF_HEIGHT) { + qDebug() << "Staff height out of score! Scaling ignored"; + return; + } +#if defined (Q_OS_ANDROID) + if (CLEF_WIDTH * scoreFactor < Tmtr::fingerPixels() * 0.5) { + qDebug() << "Score will be too small! Scaling ignored!"; + return; + } +#endif if (sc != m_scale) { m_scale = sc; resizeEvent(0); @@ -303,6 +327,15 @@ void TmultiScore::ensureNoteIsVisible() { //#################################################################################################### //################################### PROTECTED ################################################## //#################################################################################################### +qreal TmultiScore::getScaleFactor(int minH, qreal sc) { +#if defined (Q_OS_ANDROID) + qreal hh = qMin(qreal(minH), Tmtr::fingerPixels() * 6.0); +#else + qreal hh = qMin(minH, qMin<int>(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 2); +#endif + return ((hh / (staff()->height() + 0.4)) / transform().m11()) / sc; + +} void TmultiScore::resizeEvent(QResizeEvent* event) { int hh = height(), ww = width(); @@ -324,12 +357,8 @@ void TmultiScore::resizeEvent(QResizeEvent* event) { qreal staffOff = 0.0; if (staff()->isPianoStaff()) staffOff = 1.1; -#if defined (Q_OS_ANDROID) - hh = qMin<int>(hh, Tmtr::fingerPixels() * 6); -#else - hh = qMin<int>(hh, qMin<int>(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 2); -#endif - qreal factor = (((qreal)hh / (staff()->height() + 0.4)) / transform().m11()) / m_scale; + + qreal factor = getScaleFactor(hh, m_scale); scoreScene()->prepareToChangeRect(); scale(factor, factor); int stavesNumber; // how many staves are needed diff --git a/src/libs/score/tmultiscore.h b/src/libs/score/tmultiscore.h index 0d1a7024a0dab408a3e7ea5a9bd279e01205a13b..b5fa2cb38bab24ecc46ba28b84f008f8981ffbc1 100644 --- a/src/libs/score/tmultiscore.h +++ b/src/libs/score/tmultiscore.h @@ -113,6 +113,10 @@ protected: void deleteFakeLines(int lastNr); /**< Deletes last @p lastNr lines from the fake list. */ + /** Calculates score view scale factor with given user scale @p sc + * and minimal score height @p minH */ + qreal getScaleFactor(int minH, qreal sc); + protected slots: void keyChangedSlot(); diff --git a/src/libs/score/tsimplescore.cpp b/src/libs/score/tsimplescore.cpp index a56490275825c1884353b319e8b6ff0fe0fae85d..5bc8e01d778a82448037705dd4f48a17f87ae8c3 100644 --- a/src/libs/score/tsimplescore.cpp +++ b/src/libs/score/tsimplescore.cpp @@ -28,6 +28,7 @@ #include <tcolor.h> #include <tnoofont.h> #include <QtWidgets/QtWidgets> +#include <tmtr.h> #define TAP_TIME (200) // ms @@ -36,10 +37,11 @@ TsimpleScore::TsimpleScore(int notesNumber, QWidget* parent) : QGraphicsView(parent), - m_notesNr(notesNumber), m_bgGlyph(0), - m_prevBGglyph(-1) -{ + m_notesNr(notesNumber), + m_prevBGglyph(-1), + m_pinchZoomEmitted(false) +{ if (TscoreNote::touchEnabled()) viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true); else { @@ -342,7 +344,7 @@ void TsimpleScore::resizeEvent(QResizeEvent* event) { * with latest touch position in scene coordinates. To use them, TscoreItem subclass has to map it first. * When second finger touches screen, last touch event is canceled by putting null point as a parameter. * So far, only: @class TscoreNote, @class TscoreKeySignature and @class TscoreClef handles those methods. - * Two fingers touch is used to scroll score + * Two fingers touch is used to scroll score and to zoom score in/out */ bool TsimpleScore::viewportEvent(QEvent* event) { if (TscoreNote::touchEnabled()) { @@ -366,6 +368,7 @@ bool TsimpleScore::viewportEvent(QEvent* event) { break; } case Qt::TouchPointReleased: { + m_pinchZoomEmitted = false; if (m_currentIt) { m_currentIt->untouched(touchScenePos); m_currentIt = 0; @@ -381,7 +384,21 @@ bool TsimpleScore::viewportEvent(QEvent* event) { m_currentIt->untouched(QPointF(0, 0)); m_currentIt = 0; } - verticalScrollBar()->setValue(verticalScrollBar()->value() + (te->touchPoints()[0].lastPos().y() - te->touchPoints()[0].pos().y())); + QLineF l1(te->touchPoints()[0].startPos(), te->touchPoints()[1].startPos()); // initial double-touch position + QLineF l2(te->touchPoints()[0].pos(), te->touchPoints()[1].pos()); // final position + qreal distance = l1.length() - l2.length(); + if (distance < -2.0 * Tmtr::fingerPixels()) { // zoom out + if (!m_pinchZoomEmitted) { + emit pinchZoom(1); + m_pinchZoomEmitted = true; + } + } else if (distance > 2.0 * Tmtr::fingerPixels()) { // zoom in + if (!m_pinchZoomEmitted) { + emit pinchZoom(-1); + m_pinchZoomEmitted = true; + } + } else if (distance < Tmtr::fingerPixels()) // just scroll + verticalScrollBar()->setValue(verticalScrollBar()->value() + int(te->touchPoints()[0].lastPos().y() - te->touchPoints()[0].pos().y())); return true; } } diff --git a/src/libs/score/tsimplescore.h b/src/libs/score/tsimplescore.h index 029434e7d5cac38dfc5b291e592e800ee1de1286..5f0fa9912b887126c899cee643377ad8b100fa96 100644 --- a/src/libs/score/tsimplescore.h +++ b/src/libs/score/tsimplescore.h @@ -77,7 +77,7 @@ public: bool isPianoStaff(); - /** Sets background color. Alpha value will be overriden. */ + /** Sets background color. Alpha value will be overridden. */ void setBGcolor(QColor bgColor); /** It disables accids buttons and locks editing, @@ -106,6 +106,13 @@ signals: /** TsimpleScore takes care about changing staves but also emits this signal when changes are done.*/ void clefChanged(Tclef); + /** Emitted when score detects pinch touch gesture. + * Emitted value is zoom factor: + * - positive (> 0): zoom out + * - negative (< 0): zoom in + * Signal is emitted once per touch (m_pinchZoomEmitted is responsible for it) */ + void pinchZoom(int); + public slots: virtual void noteWasClicked(int index); @@ -150,9 +157,10 @@ private: Tclef::Etype m_clefType; QSize m_sizeHint; QPointer<TscoreItem> m_currentIt; - QPointF m_initPos; /** In scene coordinates. */ + QPointF m_initPos; /**< In scene coordinates. */ bool m_wheelFree; QTimer *m_wheelLockTimer; + bool m_pinchZoomEmitted; }; #endif // TSIMPLESCORE_H diff --git a/src/main.cpp b/src/main.cpp index 337a3be889da034ef92b1e7822128e8e0a67bb90..08f91d2cdad2116127d29ddbfc6b68342cc399a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -130,9 +130,9 @@ int main(int argc, char *argv[]) QFile f(confFile); f.remove(); Tandroid::restartNootka(); // and call Nootka after delay - a->quit(); } resetConfig = false; // do - while loop doesn't work with Android +// a->quit(); // HACK: calling QApplication::quick() solves hang on x86 when Qt uses native (usually obsolete) SSL libraries #endif } while (resetConfig); delete a;