From 58c7c8722b11da93ff675359e4429e9e18f6baf0 Mon Sep 17 00:00:00 2001 From: SeeLook <945374+SeeLook@users.noreply.github.com> Date: Sat, 1 Apr 2017 21:14:26 +0200 Subject: [PATCH] Implemented touch handling for score, by mouse events (press, move, release) overriding. Mouse doesn't work under mobile. --- src/libs/core/score/tnoteobject.cpp | 70 ++++++++++++++++++++++++++-- src/libs/core/score/tnoteobject.h | 6 +++ src/libs/core/score/tscoreobject.cpp | 10 +++- src/libs/core/score/tscoreobject.h | 30 ++++++++++-- 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/libs/core/score/tnoteobject.cpp b/src/libs/core/score/tnoteobject.cpp index 085a38ecd..e20693abc 100644 --- a/src/libs/core/score/tnoteobject.cpp +++ b/src/libs/core/score/tnoteobject.cpp @@ -27,6 +27,8 @@ #include <QtQml/qqmlengine.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpalette.h> +#include <QtCore/qtimer.h> +#include <QtCore/qelapsedtimer.h> #include <QtCore/qdebug.h> @@ -119,6 +121,7 @@ TnoteObject::TnoteObject(TstaffObject* staffObj, TnotePair* wrapper) : setHeight(staffObj->staffItem()->height()); setAcceptHoverEvents(true); setZ(10); + setAcceptedMouseButtons(Qt::LeftButton); } @@ -405,19 +408,81 @@ void TnoteObject::keySignatureChanged() { } +/** + * Due to some bug invoking hover events after touch, mouse is not supported under mobile for now + * That bug makes it messy + */ +#if !defined (Q_OS_ANDROID) void TnoteObject::hoverEnterEvent(QHoverEvent*) { + m_measure->score()->setHoveredNote(this); m_measure->score()->changeActiveNote(this); } void TnoteObject::hoverLeaveEvent(QHoverEvent*) { + m_measure->score()->setHoveredNote(nullptr); m_measure->score()->changeActiveNote(nullptr); } void TnoteObject::hoverMoveEvent(QHoverEvent* event) { - if (static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) - m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + if (!m_measure->score()->pressedNote() && m_measure->score()->hoveredNote() + && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); +} +#endif + +static QElapsedTimer m_touchDuration; +/** + * Mouse events are used to handle touch when supported (they are ignored when enter event occurred before invoked by mouse) + * Press event displays note cursor and locks grabbing the mouse - so moving a finger doesn't scroll entire score + */ +void TnoteObject::mousePressEvent(QMouseEvent* event) { + setKeepMouseGrab(true); + m_measure->score()->setPressedNote(this); + m_measure->score()->touchHideTimer()->stop(); + if (m_measure->score()->activeNote() != this) { + if (event->buttons() & Qt::LeftButton) { + m_measure->score()->changeActiveNote(this); + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); + } + } + m_touchDuration.restart(); +} + + +/** + * @p touchHideTimer() of score becomes active after first release of a finger, + * note cursor remains visible for 2.5 seconds. + * Second touch will set the note and hide cursor. + * + * Release event is also used to set (confirm) a note by mouse press + */ +void TnoteObject::mouseReleaseEvent(QMouseEvent*) { + if (keepMouseGrab()) + setKeepMouseGrab(false); + if (m_measure->score()->hoveredNote()) { // mouse + if (m_measure->score()->hoveredNote() == this) + m_measure->score()->noteClicked(m_measure->score()->activeYpos()); + m_measure->score()->setPressedNote(nullptr); + } else { // touch + if (m_touchDuration.elapsed() < 150) { // confirm note + m_measure->score()->touchHideTimer()->stop(); + if (m_measure->score()->activeNote() == this) // set note only when it was touched second time + m_measure->score()->noteClicked(m_measure->score()->activeYpos()); + m_measure->score()->setPressedNote(nullptr); + m_measure->score()->changeActiveNote(nullptr); + } else { // keep cursor visible + m_measure->score()->touchHideTimer()->start(2500); + } + } +} + + +void TnoteObject::mouseMoveEvent(QMouseEvent* event) { + if (m_measure->score()->pressedNote() && !m_measure->score()->touchHideTimer()->isActive() + && static_cast<int>(m_measure->score()->activeYpos()) != static_cast<int>(event->pos().y())) + m_measure->score()->setActiveNotePos(qFloor(event->pos().y())); } @@ -425,7 +490,6 @@ void TnoteObject::hoverMoveEvent(QHoverEvent* event) { //################### PRIVATE ############################################ //################################################################################################# - QQuickItem* TnoteObject::createAddLine() { auto line = qobject_cast<QQuickItem*>(m_staff->score()->component()->create()); line->setParentItem(this); diff --git a/src/libs/core/score/tnoteobject.h b/src/libs/core/score/tnoteobject.h index 0afb9bdeb..e86a22983 100644 --- a/src/libs/core/score/tnoteobject.h +++ b/src/libs/core/score/tnoteobject.h @@ -138,9 +138,15 @@ protected: */ void checkTie(); +#if !defined (Q_OS_ANDROID) void hoverEnterEvent(QHoverEvent*) override; void hoverLeaveEvent(QHoverEvent*) override; void hoverMoveEvent(QHoverEvent* event) override; +#endif + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent*) override; + void mouseMoveEvent(QMouseEvent* event) override; private: diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index d5a2e53ae..b57420263 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -56,6 +56,13 @@ TscoreObject::TscoreObject(QObject* parent) : for (int i = 0; i < 7; i++) // reset accidentals array m_accidInKeyArray[i] = 0; + + m_touchHideTimer = new QTimer(this); + connect(m_touchHideTimer, &QTimer::timeout, [=]{ + changeActiveNote(nullptr); + setPressedNote(nullptr); + m_touchHideTimer->stop(); + }); } @@ -224,7 +231,7 @@ TnoteObject * TscoreObject::note(int noteId) { void TscoreObject::noteClicked(qreal yPos) { - if (activeNote()->note()->isRest()) + if (!activeNote() || activeNote()->note()->isRest()) return; CHECKTIME( int globalNr = m_clefOffset.octave * 7 - (yPos - static_cast<int>(upperLine()) - m_clefOffset.note); @@ -286,6 +293,7 @@ CHECKTIME( if (m_segments[notesForAlterCheck.y()]->item()->staff() != m_segments[notesForAlterCheck.x()]->item()->staff()) m_segments[notesForAlterCheck.y()]->item()->staff()->fit(); } + emit clicked(); qDebug() << "[TscoreObject] clicked note" << activeNote() << yPos << newNote.toText() << newNote.rtm.string(); ) } diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index 027747cac..3d59eb8ce 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -188,6 +188,11 @@ signals: void activeYposChanged(); void upperLineChanged(); + /** + * When active note was clicked + */ + void clicked(); + protected: QQmlComponent* component() { return m_qmlComponent; } QQmlEngine* qmlEngine() { return m_qmlEngine; } @@ -224,15 +229,31 @@ protected: */ qint8 accidInKey(int k) const { return m_accidInKeyArray[k]; } - void changeActiveNote(TnoteObject* aNote); - void setActiveNotePos(qreal yPos); - /** * Returns number of note that starts a tie through @p x() * and number of note that ends the tie through @p y() */ QPoint tieRange(TnoteObject* n); +/* ------------------ Note cursor ------------------ */ + + /** + * Sets note over which one cursor appears or null if none + */ + void changeActiveNote(TnoteObject* aNote); + void setActiveNotePos(qreal yPos); + + /** + * Delays hiding note cursor when touch was released + */ + QTimer* touchHideTimer() { return m_touchHideTimer; } + + TnoteObject* hoveredNote() { return m_hoveredNote; } + void setHoveredNote(TnoteObject* hn) { m_hoveredNote = hn; } + + TnoteObject* pressedNote() { return m_presseddNote; } + void setPressedNote(TnoteObject* pn) { m_presseddNote = pn; } + private: /** * Appends notes to @p m_notes list, creates corresponding @p TnotePair @@ -269,6 +290,9 @@ private: /* Note cursor */ TnoteObject *m_activeNote = nullptr; qreal m_activeYpos = 0.0; + QTimer *m_touchHideTimer; + TnoteObject *m_hoveredNote = nullptr; + TnoteObject *m_presseddNote = nullptr; }; -- GitLab