Basic implementation of single-click access to editing elements with grips

This commit is contained in:
Dmitri Ovodok 2019-12-12 13:28:36 +02:00
parent a6f4b33d6d
commit f84f889d8e
16 changed files with 148 additions and 67 deletions

View file

@ -90,6 +90,10 @@ class Arpeggio final : public Element {
virtual bool setProperty(Pid propertyId, const QVariant&) override;
virtual QVariant propertyDefault(Pid propertyId) const override;
virtual Pid propertyId(const QStringRef& xmlName) const override;
// TODO: add a grip for moving the entire arpeggio
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
};

View file

@ -143,6 +143,9 @@ class BarLine final : public Element {
virtual QString accessibleInfo() const override;
virtual QString accessibleExtraInfo() const override;
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
static const std::vector<BarLineTableItem> barLineTable;
};
} // namespace Ms

View file

@ -157,6 +157,9 @@ class Beam final : public Element {
virtual void triggerLayout() const override;
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
static IconType iconType(Mode);
};

View file

@ -87,6 +87,10 @@ class Box : public MeasureBase {
virtual bool setProperty(Pid propertyId, const QVariant&) override;
virtual QVariant propertyDefault(Pid) const override;
virtual QString accessibleExtraInfo() const override;
// TODO: add a grip for moving the entire box
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
};
//---------------------------------------------------------

View file

@ -169,6 +169,12 @@ class Element : public ScoreElement {
///< valid after call to layout()
uint _tag; ///< tag bitmask
public:
enum class EditBehavior {
SelectOnly,
Edit,
};
protected:
mutable int _z;
QColor _color; ///< element color attribute
@ -301,6 +307,9 @@ class Element : public ScoreElement {
virtual bool prevGrip(EditData&) const;
virtual QPointF gripAnchor(Grip) const { return QPointF(); }
virtual EditBehavior normalModeEditBehavior() const { return EditBehavior::SelectOnly; }
virtual Grip defaultGrip() const { return Grip::NO_GRIP; }
int track() const { return _track; }
virtual void setTrack(int val) { _track = val; }

View file

@ -55,6 +55,9 @@ class LineSegment : public SpannerSegment {
virtual Element* propertyDelegate(Pid) override;
Element::EditBehavior normalModeEditBehavior() const override { return Element::EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::MIDDLE; }
virtual QLineF dragAnchor() const override;
};

View file

@ -113,6 +113,9 @@ class SlurTieSegment : public SpannerSegment {
struct UP& ups(Grip i) { return _ups[int(i)]; }
virtual Shape shape() const override { return _shape; }
Element::EditBehavior normalModeEditBehavior() const override { return Element::EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::DRAG; }
void writeSlur(XmlWriter& xml, int no) const;
void read(XmlReader&);
virtual void drawEditMode(QPainter*, EditData&) override;

View file

@ -58,6 +58,9 @@ class Spacer final : public Element {
void setGap(qreal sp);
qreal gap() const { return _gap; }
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
QVariant getProperty(Pid propertyId) const;
bool setProperty(Pid propertyId, const QVariant&);
QVariant propertyDefault(Pid id) const;

View file

@ -72,6 +72,9 @@ class Stem final : public Element {
QPointF hookPos() const;
qreal stemLen() const;
QPointF p2() const { return line.p2(); }
EditBehavior normalModeEditBehavior() const override { return EditBehavior::Edit; }
Grip defaultGrip() const override { return Grip::START; }
};

View file

@ -44,6 +44,8 @@ class TBox : public VBox {
virtual void scanElements(void* data, void (*func)(void*, Element*), bool all=true);
virtual QString accessibleExtraInfo() const override;
Text* text() { return _text; }
EditBehavior normalModeEditBehavior() const override { return EditBehavior::SelectOnly; }
};

View file

@ -270,6 +270,10 @@ void ScoreView::mouseReleaseEvent(QMouseEvent* mouseEvent)
changeState(ViewState::NORMAL);
break;
case ViewState::DRAG_EDIT:
if (editData.element && editData.element->normalModeEditBehavior() == Element::EditBehavior::Edit) {
changeState(ViewState::NORMAL);
break;
}
changeState(ViewState::EDIT);
break;
case ViewState::FOTO_DRAG:
@ -427,8 +431,46 @@ void ScoreView::mousePressEvent(QMouseEvent* ev)
editData.buttons = ev->buttons();
editData.modifiers = qApp->keyboardModifiers();
bool gripFound = false;
if (editData.element && editData.grips && ev->button() == Qt::LeftButton) {
switch (state) {
case ViewState::NORMAL:
case ViewState::EDIT:
case ViewState::FOTO:
{
bool gripChanged = false;
const qreal a = editData.grip[0].width() * 0.5;
for (int i = 0; i < editData.grips; ++i) {
if (editData.grip[i].adjusted(-a, -a, a, a).contains(editData.startMove)) {
editData.curGrip = Grip(i);
gripChanged = true;
break;
}
}
if (!gripChanged && editData.element->canvasBoundingRect().contains(editData.startMove)) { // TODO: check shape instead?
editData.curGrip = editData.element->defaultGrip();
gripChanged = true;
}
if (gripChanged) {
updateGrips();
score()->update();
if (editData.curGrip != Grip::NO_GRIP)
gripFound = true;
}
}
break;
default:
break;
}
}
switch (state) {
case ViewState::NORMAL:
if (gripFound)
break;
if (ev->button() == Qt::RightButton) // context menu?
break;
@ -440,27 +482,16 @@ void ScoreView::mousePressEvent(QMouseEvent* ev)
toTextBase(editData.element)->setPrimed(false);
}
editData.element = elementNear(editData.startMove);
setEditElement(elementNear(editData.startMove));
mousePressEventNormal(ev);
break;
case ViewState::FOTO: {
if (ev->buttons() & Qt::RightButton)
break;
editData.element = _foto;
bool gripClicked = false;
qreal a = editData.grip[0].width() * 0.5;
for (int i = 0; i < editData.grips; ++i) {
if (editData.grip[i].adjusted(-a, -a, a, a).contains(editData.startMove)) {
editData.curGrip = Grip(i);
updateGrips();
gripClicked = true;
score()->update();
break;
}
}
setEditElement(_foto);
if (gripClicked)
if (gripFound)
changeState(ViewState::FOTO_DRAG_EDIT);
else if (_foto->canvasBoundingRect().contains(editData.startMove))
changeState(ViewState::FOTO_DRAG_OBJECT);
@ -487,41 +518,20 @@ void ScoreView::mousePressEvent(QMouseEvent* ev)
break;
case ViewState::EDIT: {
if (editData.grips) {
qreal a = editData.grip[0].width() * 0.5;
bool gripFound = false;
for (int i = 0; i < editData.grips; ++i) {
if (editData.grip[i].adjusted(-a, -a, a, a).contains(editData.startMove)) {
editData.curGrip = Grip(i);
updateGrips();
score()->update();
gripFound = true;
break;
}
}
if (!gripFound) {
changeState(ViewState::NORMAL);
// changeState may trigger layout and destroy some elements
// so we should search elementNear after changeState.
editData.element = elementNear(editData.startMove);
mousePressEventNormal(ev);
break;
}
if (gripFound)
break;
if (!editData.element->canvasBoundingRect().contains(editData.startMove)) {
changeState(ViewState::NORMAL);
// changeState may trigger layout and destroy some elements
// so we should search elementNear after changeState.
setEditElement(elementNear(editData.startMove));
mousePressEventNormal(ev);
}
else {
if (!editData.element->canvasBoundingRect().contains(editData.startMove)) {
changeState(ViewState::NORMAL);
// changeState may trigger layout and destroy some elements
// so we should search elementNear after changeState.
editData.element = elementNear(editData.startMove);
mousePressEventNormal(ev);
}
else {
editData.element->mousePress(editData);
score()->update();
if (editData.element->isTextBase() && mscore->textTools())
mscore->textTools()->updateTools(editData);
}
editData.element->mousePress(editData);
score()->update();
if (editData.element->isTextBase() && mscore->textTools())
mscore->textTools()->updateTools(editData);
}
}
break;
@ -582,12 +592,23 @@ void ScoreView::mouseMoveEvent(QMouseEvent* me)
case ViewState::NORMAL:
if (!drag)
return;
if (!editData.element && (me->modifiers() & Qt::ShiftModifier))
if (!editData.element && (me->modifiers() & Qt::ShiftModifier)) {
changeState(ViewState::LASSO);
else if (editData.element && editData.element->isMovable())
changeState(ViewState::DRAG_OBJECT);
else
changeState(ViewState::DRAG);
break;
}
if (editData.element) {
if (editData.element->normalModeEditBehavior() == Element::EditBehavior::Edit) {
score()->startCmd();
editData.element->startEditDrag(editData);
changeState(ViewState::DRAG_EDIT);
break;
}
if (editData.element->isMovable()) {
changeState(ViewState::DRAG_OBJECT);
break;
}
}
changeState(ViewState::DRAG);
break;
case ViewState::NOTE_ENTRY: {

View file

@ -99,7 +99,7 @@ void ScoreView::startFotomode()
_foto->setFlag(ElementFlag::MOVABLE, true);
_foto->setVisible(true);
_score->select(_foto);
editData.element = _foto;
setEditElement(_foto);
QAction* a = getAction("fotomode");
a->setChecked(true);
startEdit();
@ -166,7 +166,7 @@ void ScoreView::endFotoDrag()
editData.grip.resize(8);
for (int i = 0; i < 8; ++i)
editData.grip[i] = r;
editData.element = _foto;
setEditElement(_foto);
updateGrips();
_score->setUpdateAll();
_score->update();

View file

@ -443,8 +443,7 @@ void InspectorBase::valueChanged(int idx, bool reset)
recursion = false;
ScoreView* cv = mscore->currentScoreView();
if (cv->editMode())
cv->updateGrips();
cv->updateGrips();
}
//---------------------------------------------------------

View file

@ -6005,10 +6005,9 @@ void MuseScore::cmd(QAction* a, const QString& cmd)
if (_sstate != STATE_NORMAL )
undoRedo(true);
#ifdef Q_OS_MAC
else if (cs) {
cs->startCmd();
cs->cmdDeleteSelection();
cs->endCmd();
else if (cv) {
cv->cmd(getAction("delete"));
return;
}
#endif
}

View file

@ -1085,6 +1085,9 @@ void ScoreView::paint(const QRect& r, QPainter& p)
if (editData.element) {
switch (state) {
case ViewState::NORMAL:
if (editData.element->normalModeEditBehavior() == Element::EditBehavior::Edit)
editData.element->drawEditMode(&p, editData);
break;
case ViewState::DRAG:
case ViewState::DRAG_OBJECT:
case ViewState::LASSO:
@ -3520,12 +3523,12 @@ void ScoreView::addSlur(const Slur* slurTemplate)
void ScoreView::cmdAddSlur(ChordRest* cr1, ChordRest* cr2, const Slur* slurTemplate)
{
bool startEditMode = false;
bool switchToSlur = false;
if (cr2 == 0) {
cr2 = nextChordRest(cr1);
if (cr2 == 0)
cr2 = cr1;
startEditMode = true; // start slur in edit mode if last chord is not given
switchToSlur = true; // select slur for editing if last chord is not given
}
_score->startCmd();
@ -3555,9 +3558,8 @@ void ScoreView::cmdAddSlur(ChordRest* cr1, ChordRest* cr2, const Slur* slurTempl
_score->inputState().setSlur(slur);
ss->setSelected(true);
}
else if (startEditMode) {
editData.element = ss;
changeState(ViewState::EDIT);
else if (switchToSlur) {
startEditMode(ss);
}
}
@ -3602,8 +3604,7 @@ void ScoreView::cmdAddHairpin(HairpinType type)
const std::vector<SpannerSegment*>& el = pin->spannerSegments();
if (!noteEntryMode()) {
if (!el.empty()) {
editData.element = el.front();
changeState(ViewState::EDIT);
startEditMode(el.front());
}
}
}
@ -4826,6 +4827,28 @@ bool ScoreView::fotoMode() const
return false;
}
//---------------------------------------------------------
// setEditElement
//---------------------------------------------------------
void ScoreView::setEditElement(Element* e)
{
if (editData.element == e)
return;
const bool normalState = (state == ViewState::NORMAL);
if (normalState && editData.element && editData.element->normalModeEditBehavior() == Element::EditBehavior::Edit)
endEdit();
editData.element = e;
if (normalState && e && e->normalModeEditBehavior() == Element::EditBehavior::Edit)
startEdit();
else if (editData.grips)
editData.grips = 0;
}
//---------------------------------------------------------
// visibleElementInScore
//---------------------------------------------------------

View file

@ -337,6 +337,8 @@ class ScoreView : public QWidget, public MuseScoreView {
virtual void cmdAddHairpin(HairpinType);
void cmdAddNoteLine();
void setEditElement(Element*);
bool noteEntryMode() const { return state == ViewState::NOTE_ENTRY; }
bool editMode() const { return state == ViewState::EDIT; }
bool textEditMode() const { return editMode() && editData.element && editData.element->isTextBase(); }