Basic implementation of single-click access to editing elements with grips
This commit is contained in:
parent
a6f4b33d6d
commit
f84f889d8e
16 changed files with 148 additions and 67 deletions
|
@ -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; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -443,8 +443,7 @@ void InspectorBase::valueChanged(int idx, bool reset)
|
|||
recursion = false;
|
||||
|
||||
ScoreView* cv = mscore->currentScoreView();
|
||||
if (cv->editMode())
|
||||
cv->updateGrips();
|
||||
cv->updateGrips();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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(); }
|
||||
|
|
Loading…
Reference in a new issue