added drag notes

This commit is contained in:
Igor Korsukov 2020-06-15 10:59:21 +02:00
parent 4013ef9243
commit 4e828b0980
14 changed files with 317 additions and 31 deletions

View file

@ -1269,8 +1269,9 @@ QVariant Element::getProperty(Pid propertyId) const
case Pid::SIZE_SPATIUM_DEPENDENT:
return sizeIsSpatiumDependent();
default:
if (parent())
if (parent()) {
return parent()->getProperty(propertyId);
}
return QVariant();
}
@ -1327,7 +1328,8 @@ bool Element::setProperty(Pid propertyId, const QVariant& v)
return parent()->setProperty(propertyId, v);
}
qDebug("%s unknown <%s>(%d), data <%s>", name(), propertyName(propertyId), int(propertyId), qPrintable(v.toString()));
qDebug("%s unknown <%s>(%d), data <%s>", name(), propertyName(propertyId), int(propertyId),
qPrintable(v.toString()));
return false;
}
triggerLayout();
@ -1943,6 +1945,15 @@ void Element::triggerLayoutAll() const
}
}
//---------------------------------------------------------
// ~EditData
//---------------------------------------------------------
EditData::~EditData()
{
clearData();
}
//---------------------------------------------------------
// control
//---------------------------------------------------------

View file

@ -143,6 +143,7 @@ public:
EditData(MuseScoreView* v = nullptr)
: view(v) {}
~EditData();
void clearData();
ElementEditData* getData(const Element*) const;
@ -203,14 +204,14 @@ public:
Element* findAncestor(ElementType t);
const Element* findAncestor(ElementType t) const;
Measure* findMeasure();
const Measure* findMeasure() const;
MeasureBase* findMeasureBase();
const MeasureBase* findMeasureBase() const;
Measure* findMeasure();
const Measure* findMeasure() const;
MeasureBase* findMeasureBase();
const MeasureBase* findMeasureBase() const;
//!Note Returns basic representative for the current element.
//! For example: notes->chord, chords->beam, etc.
virtual Element* elementBase() const { return const_cast<Element*>(this); }
//!Note Returns basic representative for the current element.
//! For example: notes->chord, chords->beam, etc.
virtual Element* elementBase() const { return const_cast<Element*>(this); }
virtual bool isElement() const override { return true; }
@ -532,7 +533,7 @@ public:
return QString(); // and passed only to the screen-reader
}
virtual void triggerLayout() const ;
virtual void triggerLayout() const;
virtual void triggerLayoutAll() const;
virtual void drawEditMode(QPainter*, EditData&);

View file

@ -57,13 +57,13 @@ public:
virtual void hideShadowNote() = 0;
virtual void paintShadowNote(QPainter* p) = 0;
// Select
// input (mouse)
virtual INotationInputController* inputController() const = 0;
// select
virtual INotationSelection* selection() const = 0;
virtual void select(Element* e, SelectType type, int staffIdx = 0) = 0;
// Input (mouse)
virtual INotationInputController* inputController() const = 0;
// notify
virtual async::Notification notationChanged() const = 0;
virtual async::Notification inputStateChanged() const = 0;

View file

@ -20,6 +20,10 @@
#define MU_DOMAIN_INOTATIONINPUTCONTROLLER_H
#include <QPointF>
#include <functional>
#include "async/notification.h"
#include "notationtypes.h"
namespace mu {
@ -31,6 +35,15 @@ public:
virtual ~INotationInputController() = default;
virtual Element* hitElement(const QPointF& pos, float width) const = 0;
// drag
using IsDraggable = std::function<bool (const Element*)>;
virtual bool isDragStarted() const = 0;
virtual void startDrag(const std::vector<Element*>& elems, const QPointF& eoffset, const IsDraggable& isDrag) = 0;
virtual void drag(const QPointF& fromPos, const QPointF& toPos, DragMode mode) = 0;
virtual void endDrag() = 0;
virtual async::Notification dragChanged() = 0;
};
}
}

View file

@ -19,8 +19,9 @@
#ifndef MU_DOMAIN_INOTATIONSELECTION_H
#define MU_DOMAIN_INOTATIONSELECTION_H
#include <vector>
#include <QRectF>
#include "async/notification.h"
#include "notationtypes.h"
namespace mu {
namespace domain {
@ -31,6 +32,9 @@ public:
virtual ~INotationSelection() = default;
virtual bool isNone() const = 0;
virtual bool isRange() const = 0;
virtual std::vector<Element*> elements() const = 0;
virtual QRectF canvasBoundingRect() const = 0;
};

View file

@ -33,6 +33,7 @@
#include "libmscore/slur.h"
#include "libmscore/system.h"
#include "libmscore/chord.h"
#include "libmscore/elementgroup.h"
#include "scorecallbacks.h"
@ -64,6 +65,10 @@ Notation::Notation()
m_inputState = new NotationInputState(this);
m_selection = new NotationSelection(this);
m_inputController = new NotationInputController(this);
m_inputController->dragChanged().onNotify(this, [this]() {
notifyAboutNotationChanged();
});
}
Notation::~Notation()
@ -334,6 +339,11 @@ void Notation::putNote(const QPointF& pos, bool replace, bool insert)
score()->startCmd();
score()->putNote(pos, replace, insert);
score()->endCmd();
notifyAboutNotationChanged();
}
void Notation::notifyAboutNotationChanged()
{
m_notationChanged.notify();
}

View file

@ -21,6 +21,7 @@
#include "../inotation.h"
#include "actions/action.h"
#include "async/asyncable.h"
#include "igetscore.h"
#include "notationinputstate.h"
@ -32,13 +33,14 @@ class MScore;
class MasterScore;
class ShadowNote;
class Page;
class ElementGroup;
}
namespace mu {
namespace domain {
namespace notation {
class ScoreCallbacks;
class Notation : public INotation, public IGetScore
class Notation : public INotation, public IGetScore, public async::Asyncable
{
public:
Notation();
@ -62,11 +64,13 @@ public:
void hideShadowNote() override;
void paintShadowNote(QPainter* p) override;
// Input (mouse)
INotationInputController* inputController() const override;
// select
INotationSelection* selection() const override;
void select(Element* e, SelectType type, int staffIdx = 0) override;
INotationInputController* inputController() const override;
// notify
async::Notification notationChanged() const override;
async::Notification inputStateChanged() const override;
@ -77,6 +81,7 @@ public:
private:
void notifyAboutNotationChanged();
void selectFirstTopLeftOrLast();
QSizeF m_viewSize;

View file

@ -18,7 +18,10 @@
//=============================================================================
#include "notationinputcontroller.h"
#include "log.h"
#include <memory>
#include <QRectF>
#include "libmscore/score.h"
#include "libmscore/page.h"
#include "libmscore/notedot.h"
@ -27,6 +30,7 @@ using namespace mu::domain::notation;
NotationInputController::NotationInputController(IGetScore* getScore)
: m_getScore(getScore)
{
m_dragData.editData.view = new ScoreCallbacks();
}
Ms::Score* NotationInputController::score() const
@ -43,6 +47,139 @@ Element* NotationInputController::hitElement(const QPointF& pos, float width) co
return ll.first();
}
bool NotationInputController::isDragStarted() const
{
return m_dragData.dragGroups.size() > 0;
}
void NotationInputController::DragData::reset()
{
beginMove = QPointF();
elementOffset = QPointF();
editData = Ms::EditData();
dragGroups.clear();
}
void NotationInputController::startDrag(const std::vector<Element*>& elems,
const QPointF& eoffset,
const IsDraggable& isDraggable)
{
m_dragData.reset();
m_dragData.elementOffset = eoffset;
for (Element* e : elems) {
if (!isDraggable(e)) {
continue;
}
std::unique_ptr<Ms::ElementGroup> g = e->getDragGroup(isDraggable);
if (g && g->enabled()) {
m_dragData.dragGroups.push_back(std::move(g));
}
}
score()->startCmd();
for (auto& g : m_dragData.dragGroups) {
g->startDrag(m_dragData.editData);
}
}
void NotationInputController::drag(const QPointF& fromPos, const QPointF& toPos, DragMode mode)
{
if (m_dragData.beginMove.isNull()) {
m_dragData.beginMove = fromPos;
m_dragData.editData.pos = fromPos;
}
QPointF normalizedBegin = m_dragData.beginMove - m_dragData.elementOffset;
QPointF delta = toPos - normalizedBegin;
QPointF evtDelta = toPos - m_dragData.editData.pos;
switch (mode) {
case DragMode::BothXY:
break;
case DragMode::OnlyX:
delta.setY(m_dragData.editData.delta.y());
evtDelta.setY(0.0);
break;
case DragMode::OnlyY:
delta.setX(m_dragData.editData.delta.x());
evtDelta.setX(0.0);
break;
}
m_dragData.editData.lastPos = m_dragData.editData.pos;
m_dragData.editData.hRaster = false; //mscore->hRaster();
m_dragData.editData.vRaster = false; //mscore->vRaster();
m_dragData.editData.delta = delta;
m_dragData.editData.moveDelta = delta - m_dragData.elementOffset;
m_dragData.editData.evtDelta = evtDelta;
m_dragData.editData.pos = toPos;
for (auto& g : m_dragData.dragGroups) {
score()->addRefresh(g->drag(m_dragData.editData));
}
score()->update();
m_dragChanged.notify();
// QVector<QLineF> anchorLines;
// const Selection& sel = score()->selection();
// for (Element* e : sel.elements()) {
// QVector<QLineF> elAnchorLines = e->dragAnchorLines();
// Element* const page = e->findAncestor(ElementType::PAGE);
// const QPointF pageOffset((page ? page : e)->pos());
// if (!elAnchorLines.isEmpty()) {
// for (QLineF& l : elAnchorLines) {
// l.translate(pageOffset);
// }
// anchorLines.append(elAnchorLines);
// }
// }
// if (anchorLines.isEmpty()) {
// setDropTarget(0); // this also resets dropAnchor
// } else {
// setDropAnchorLines(anchorLines);
// }
// Element* e = _score->getSelectedElement();
// if (e) {
// if (_score->playNote()) {
// mscore->play(e);
// _score->setPlayNote(false);
// }
// }
// updateGrips();
// _score->update();
}
void NotationInputController::endDrag()
{
for (auto& g : m_dragData.dragGroups) {
g->endDrag(m_dragData.editData);
}
m_dragData.reset();
//score->selection().unlock("drag");
//setDropTarget(0); // this also resets dropAnchor
score()->endCmd();
// updateGrips();
// if (editData.element->normalModeEditBehavior() == Element::EditBehavior::Edit
// && _score->selection().element() == editData.element) {
// startEdit(/* editMode */ false);
// }
}
mu::async::Notification NotationInputController::dragChanged()
{
return m_dragChanged;
}
Ms::Page* NotationInputController::point2page(const QPointF& p) const
{
if (score()->layoutMode() == Ms::LayoutMode::LINE) {

View file

@ -19,8 +19,16 @@
#ifndef MU_DOMAIN_NOTATIONINPUTCONTROLLER_H
#define MU_DOMAIN_NOTATIONINPUTCONTROLLER_H
#include <memory>
#include <vector>
#include <QPointF>
#include "../inotationinputcontroller.h"
#include "igetscore.h"
#include "scorecallbacks.h"
#include "libmscore/element.h"
#include "libmscore/elementgroup.h"
namespace mu {
namespace domain {
@ -32,6 +40,12 @@ public:
Element* hitElement(const QPointF& pos, float width) const override;
bool isDragStarted() const override;
void startDrag(const std::vector<Element*>& elems, const QPointF& eoffset, const IsDraggable& isDraggable) override;
void drag(const QPointF& fromPos, const QPointF& toPos, DragMode mode) override;
void endDrag() override;
async::Notification dragChanged() override;
Ms::Page* point2page(const QPointF& p) const;
private:
@ -41,7 +55,19 @@ private:
static bool elementIsLess(const Ms::Element* e1, const Ms::Element* e2);
IGetScore* m_getScore;
struct DragData
{
QPointF beginMove;
QPointF elementOffset;
Ms::EditData editData;
std::vector<std::unique_ptr<Ms::ElementGroup> > dragGroups;
void reset();
};
IGetScore* m_getScore = nullptr;
DragData m_dragData;
ScoreCallbacks* m_scoreCallbacks = nullptr;
async::Notification m_dragChanged;
};
}
}

View file

@ -38,6 +38,22 @@ bool NotationSelection::isNone() const
return score()->selection().isNone();
}
bool NotationSelection::isRange() const
{
return score()->selection().isRange();
}
std::vector<Element*> NotationSelection::elements() const
{
std::vector<Element*> els;
QList<Ms::Element*> list = score()->selection().elements();
els.reserve(list.count());
for (Ms::Element* e : list) {
els.push_back(e);
}
return els;
}
QRectF NotationSelection::canvasBoundingRect() const
{
if (isNone()) {

View file

@ -37,6 +37,9 @@ public:
NotationSelection(IGetScore* getScore);
bool isNone() const override;
bool isRange() const override;
std::vector<Element*> elements() const override;
QRectF canvasBoundingRect() const override;
private:

View file

@ -28,9 +28,16 @@ namespace mu {
namespace domain {
namespace notation {
using Element = Ms::Element;
using ElementType = Ms::ElementType;
using DurationType = Ms::TDuration::DurationType;
using SelectType = Ms::SelectType;
using Pad = Ms::Pad;
enum class DragMode {
BothXY = 0,
OnlyX,
OnlyY
};
}
}
}

View file

@ -52,10 +52,12 @@ void NotationViewInputController::wheelEvent(QWheelEvent* ev)
steps = static_cast<qreal>(stepsScrolled.y()) / 120.0;
}
Qt::KeyboardModifiers keyState = ev->modifiers();
// Windows touch pad pinches also execute this
if (ev->modifiers() & Qt::ControlModifier) {
if (keyState & Qt::ControlModifier) {
m_view->zoomStep(steps, m_view->toLogical(ev->pos()));
} else if (ev->modifiers() & Qt::ShiftModifier && dx == 0) {
} else if (keyState & Qt::ShiftModifier && dx == 0) {
dx = dy;
m_view->scrollHorizontal(dx);
} else {
@ -70,8 +72,8 @@ void NotationViewInputController::mousePressEvent(QMouseEvent* ev)
// note enter mode
if (m_view->isNoteEnterMode()) {
bool replace = ev->modifiers() & Qt::ShiftModifier;
bool insert = ev->modifiers() & Qt::ControlModifier;
bool replace = keyState & Qt::ShiftModifier;
bool insert = keyState & Qt::ControlModifier;
dispatcher()->dispatch("domain/notation/put-note",
ActionData::make_arg3<QPoint, bool, bool>(logicPos, replace, insert));
@ -79,9 +81,9 @@ void NotationViewInputController::mousePressEvent(QMouseEvent* ev)
}
m_interactData.beginPoint = logicPos;
m_interactData.element = notationInputController()->hitElement(logicPos, hitWidth());
m_interactData.hitElement = notationInputController()->hitElement(logicPos, hitWidth());
if (m_interactData.element) {
if (m_interactData.hitElement && !m_interactData.hitElement->selected()) {
SelectType st = SelectType::SINGLE;
if (keyState == Qt::NoModifier) {
st = SelectType::SINGLE;
@ -91,8 +93,7 @@ void NotationViewInputController::mousePressEvent(QMouseEvent* ev)
st = SelectType::ADD;
}
//! NOTE Maybe a better action?
m_view->notation()->select(m_interactData.element, st);
m_view->notation()->select(m_interactData.hitElement, st);
}
}
@ -102,12 +103,33 @@ void NotationViewInputController::mouseMoveEvent(QMouseEvent* ev)
return;
}
if (m_interactData.element) {
QPoint logicPos = m_view->toLogical(ev->pos());
Qt::KeyboardModifiers keyState = ev->modifiers();
// start some drag operations after a minimum of movement:
bool isDrag = (logicPos - m_interactData.beginPoint).manhattanLength() > 4;
if (!isDrag) {
return;
}
QPoint pos = m_view->toLogical(ev->pos());
QPoint d = pos - m_interactData.beginPoint;
// hit element
if (m_interactData.hitElement && m_interactData.hitElement->isMovable()) {
if (!notationInputController()->isDragStarted()) {
startDragElements(m_interactData.hitElement->type(), m_interactData.hitElement->offset());
}
DragMode mode = DragMode::BothXY;
if (keyState & Qt::ShiftModifier) {
mode = DragMode::OnlyY;
} else if (keyState & Qt::ControlModifier) {
mode = DragMode::OnlyX;
}
notationInputController()->drag(m_interactData.beginPoint, logicPos, mode);
return;
}
QPoint d = logicPos - m_interactData.beginPoint;
int dx = d.x();
int dy = d.y();
@ -118,8 +140,26 @@ void NotationViewInputController::mouseMoveEvent(QMouseEvent* ev)
m_view->moveCanvas(dx, dy);
}
void NotationViewInputController::startDragElements(ElementType etype, const QPointF& eoffset)
{
std::vector<Element*> els = notationSelection()->elements();
IF_ASSERT_FAILED(els.size() > 0) {
return;
}
const bool isFilterType = notationSelection()->isRange();
const auto isDraggable = [isFilterType, etype](const Element* e) {
return e && e->selected() && (!isFilterType || etype == e->type());
};
notationInputController()->startDrag(els, eoffset, isDraggable);
}
void NotationViewInputController::mouseReleaseEvent(QMouseEvent* /*ev*/)
{
if (notationInputController()->isDragStarted()) {
notationInputController()->endDrag();
}
}
void NotationViewInputController::hoverMoveEvent(QHoverEvent* ev)
@ -139,6 +179,15 @@ INotationInputController* NotationViewInputController::notationInputController()
return notation->inputController();
}
INotationSelection* NotationViewInputController::notationSelection() const
{
auto notation = m_view->notation();
if (!notation) {
return nullptr;
}
return notation->selection();
}
float NotationViewInputController::hitWidth() const
{
return configuration()->selectionProximity() * 0.5 / m_view->scale();

View file

@ -47,10 +47,14 @@ private:
struct InteractData {
QPoint beginPoint;
domain::notation::Element* element = nullptr;
domain::notation::Element* hitElement = nullptr;
};
domain::notation::INotationInputController* notationInputController() const;
domain::notation::INotationSelection* notationSelection() const;
void startDragElements(domain::notation::ElementType etype, const QPointF& eoffset);
float hitWidth() const;
NotationPaintView* m_view = nullptr;