Skip to content

Commit

Permalink
Add shortcut functionality and automatic switching of hairpin type
Browse files Browse the repository at this point in the history
  • Loading branch information
ketgg committed Aug 22, 2024
1 parent 5b13c6f commit 5c1cb87
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/app/configs/data/shortcuts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,10 @@
<key>add-slur</key>
<seq>S</seq>
</SC>
<SC>
<key>add-dynamic</key>
<seq>Ctrl+D</seq>
</SC>
<SC>
<key>add-hairpin</key>
<seq>Shift+,</seq>
Expand Down
4 changes: 4 additions & 0 deletions src/app/configs/data/shortcuts_azerty.xml
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,10 @@
<key>add-slur</key>
<seq>S</seq>
</SC>
<SC>
<key>add-dynamic</key>
<seq>Ctrl+D</seq>
</SC>
<SC>
<key>add-hairpin</key>
<seq>Shift+,</seq>
Expand Down
4 changes: 4 additions & 0 deletions src/app/configs/data/shortcuts_mac.xml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,10 @@
<key>add-slur</key>
<seq>S</seq>
</SC>
<SC>
<key>add-dynamic</key>
<seq>Ctrl+D</seq>
</SC>
<SC>
<key>add-hairpin</key>
<seq>Shift+,</seq>
Expand Down
9 changes: 9 additions & 0 deletions src/engraving/dom/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,15 @@ TextBase* Score::addText(TextStyleType type, EngravingItem* destinationElement)
chordRest->undoAddAnnotation(textBox);
break;
}
case TextStyleType::DYNAMICS: {
ChordRest* chordRest = chordOrRest(destinationElement);
if (!chordRest) {
break;
}
textBox = Factory::createDynamic(dummy()->segment());
chordRest->undoAddAnnotation(textBox);
break;
}
case TextStyleType::INSTRUMENT_CHANGE: {
ChordRest* chordRest = chordOrRest(destinationElement);
if (!chordRest) {
Expand Down
30 changes: 30 additions & 0 deletions src/engraving/dom/hairpin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,36 @@ DynamicType Hairpin::dynamicTypeTo() const
return TConv::dynamicType(ba.constChar());
}

const Dynamic* Hairpin::dynamicSnappedBefore() const
{
const LineSegment* seg = frontSegment();
if (!seg) {
return nullptr;
}

const EngravingItem* item = seg->ldata()->itemSnappedBefore();
if (!item || !item->isDynamic()) {
return nullptr;
}

return toDynamic(item);
}

const Dynamic* Hairpin::dynamicSnappedAfter() const
{
const LineSegment* seg = backSegment();
if (!seg) {
return nullptr;
}

const EngravingItem* item = seg->ldata()->itemSnappedAfter();
if (!item || !item->isDynamic()) {
return nullptr;
}

return toDynamic(item);
}

//---------------------------------------------------------
// setHairpinType
//---------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/engraving/dom/hairpin.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class Hairpin final : public TextLineBase
DynamicType dynamicTypeFrom() const;
DynamicType dynamicTypeTo() const;

const Dynamic* dynamicSnappedBefore() const;
const Dynamic* dynamicSnappedAfter() const;

HairpinType hairpinType() const { return m_hairpinType; }
void setHairpinType(HairpinType val);

Expand Down
3 changes: 3 additions & 0 deletions src/notation/inotationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ class INotationInteraction

virtual void increaseDecreaseDuration(int steps, bool stepByDots) = 0;

virtual void flipHairpinsType(engraving::Dynamic* selDyn) = 0;

virtual void toggleDynamicPopup() = 0;
virtual bool toggleLayoutBreakAvailable() const = 0;
virtual void toggleLayoutBreak(LayoutBreakType breakType) = 0;

Expand Down
1 change: 1 addition & 0 deletions src/notation/internal/notationactioncontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ void NotationActionController::init()

registerAction("add-8va", &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VA);
registerAction("add-8vb", &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VB);
registerAction("add-dynamic", &Interaction::toggleDynamicPopup, &Controller::noteOrRestSelected);
registerAction("add-hairpin", &Interaction::addHairpinsToSelection, HairpinType::CRESC_HAIRPIN, &Controller::noteOrRestSelected);
registerAction("add-hairpin-reverse", &Interaction::addHairpinsToSelection, HairpinType::DECRESC_HAIRPIN,
&Controller::noteOrRestSelected);
Expand Down
94 changes: 94 additions & 0 deletions src/notation/internal/notationinteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4416,6 +4416,100 @@ void NotationInteraction::increaseDecreaseDuration(int steps, bool stepByDots)
apply();
}

void NotationInteraction::flipHairpinsType(Dynamic* selDyn)
{
if (!selDyn) {
return;
}

if (selDyn->dynamicType() == DynamicType::OTHER || selDyn->dynamicType() >= DynamicType::FP) {
return;
}

selDyn->findAdjacentHairpins();

startEdit();

if (selDyn->leftHairpin()) {
Hairpin* leftHp = selDyn->leftHairpin();
const Dynamic* startDyn = leftHp->dynamicSnappedBefore();

if (!(startDyn->dynamicType() == DynamicType::OTHER || startDyn->dynamicType() >= DynamicType::FP) && !leftHp->isLineType()) {
if (int(startDyn->dynamicType()) > int(selDyn->dynamicType())) {
leftHp->undoChangeProperty(Pid::HAIRPIN_TYPE, int(HairpinType::DECRESC_HAIRPIN));
} else {
leftHp->undoChangeProperty(Pid::HAIRPIN_TYPE, int(HairpinType::CRESC_HAIRPIN));
}
}
}

if (selDyn->rightHairpin()) {
Hairpin* rightHp = selDyn->rightHairpin();
const Dynamic* endDyn = rightHp->dynamicSnappedAfter();

if (!(endDyn->dynamicType() == DynamicType::OTHER || endDyn->dynamicType() >= DynamicType::FP) && !rightHp->isLineType()) {
if (int(endDyn->dynamicType()) > int(selDyn->dynamicType())) {
rightHp->undoChangeProperty(Pid::HAIRPIN_TYPE, int(HairpinType::CRESC_HAIRPIN));
} else {
rightHp->undoChangeProperty(Pid::HAIRPIN_TYPE, int(HairpinType::DECRESC_HAIRPIN));
}
}
}

apply();
}

void NotationInteraction::toggleDynamicPopup()
{
if (selection()->isNone()) {
return;
}

// If multiple selected selection()->element() returns null
if (!selection()->element()) {
return;
}

EngravingItem* el = selection()->element();

if (el->isHairpinSegment()) {
HairpinSegment* hairpinSeg = toHairpinSegment(el);

switch (m_editData.curGrip) {
case Grip::START: {
EngravingItem* startDynOrExp = hairpinSeg->findElementToSnapBefore();
if (startDynOrExp != nullptr) {
select({ startDynOrExp }); // If there is already a dynamic select it instead of opening an empty popup
if (startDynOrExp->isDynamic()) {
startEditElement(startDynOrExp, false);
flipHairpinsType(toDynamic(startDynOrExp));
}
} else {
addTextToItem(TextStyleType::DYNAMICS, hairpinSeg->spanner()->startCR());
}
}
return;
case Grip::END: {
EngravingItem* endDynOrExp = hairpinSeg->findElementToSnapAfter();
if (endDynOrExp != nullptr) {
select({ endDynOrExp }); // If there is already a dynamic select it instead of opening an empty popup
if (endDynOrExp->isDynamic()) {
startEditElement(endDynOrExp, false);
flipHairpinsType(toDynamic(endDynOrExp));
}
} else {
addTextToItem(TextStyleType::DYNAMICS, hairpinSeg->spanner()->endCR());
}
}
return;
default:
return;
}
} else {
addTextToItem(TextStyleType::DYNAMICS, el);
}
}

bool NotationInteraction::toggleLayoutBreakAvailable() const
{
return !selection()->isNone() && !isTextEditingStarted();
Expand Down
3 changes: 3 additions & 0 deletions src/notation/internal/notationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable

void increaseDecreaseDuration(int steps, bool stepByDots) override;

void flipHairpinsType(engraving::Dynamic* selDyn) override;

void toggleDynamicPopup() override;
bool toggleLayoutBreakAvailable() const override;
void toggleLayoutBreak(LayoutBreakType breakType) override;

Expand Down
6 changes: 6 additions & 0 deletions src/notation/internal/notationuiactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,12 @@ const UiActionList NotationUiActions::m_actions = {
TranslatableString("action", "Ottava 8va &bassa"),
TranslatableString("action", "Add ottava 8va bassa")
),
UiAction("add-dynamic",
mu::context::UiCtxNotationOpened,
mu::context::CTX_ANY,
TranslatableString("action", "&Dynamic"),
TranslatableString("action", "Add dynamic")
),
UiAction("add-hairpin",
mu::context::UiCtxNotationOpened,
mu::context::CTX_ANY,
Expand Down
3 changes: 3 additions & 0 deletions src/notation/tests/mocks/notationinteractionmock.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class NotationInteractionMock : public INotationInteraction

MOCK_METHOD(void, increaseDecreaseDuration, (int, bool), (override));

MOCK_METHOD(void, flipHairpinsType, (engraving::Dynamic * selDyn), (override));

MOCK_METHOD(void, toggleDynamicPopup, (), (override));
MOCK_METHOD(bool, toggleLayoutBreakAvailable, (), (const, override));
MOCK_METHOD(void, toggleLayoutBreak, (LayoutBreakType), (override));

Expand Down
10 changes: 10 additions & 0 deletions src/notation/view/internal/dynamicpopupmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ void DynamicPopupModel::addOrChangeDynamic(int page, int index)
m_item->undoChangeProperty(Pid::DYNAMIC_TYPE, DYN_POPUP_PAGES[page][index].dynType);
endCommand();

INotationInteractionPtr interaction = currentNotation()->interaction();

interaction->flipHairpinsType(toDynamic(m_item));

// Hide the bounding box which appears when called using Ctrl+D shortcut
if (interaction->isTextEditingStarted()) {
interaction->endEditText();
interaction->startEditGrip(m_item, Grip::DRAG);
}

updateNotation();
}

Expand Down
5 changes: 5 additions & 0 deletions src/notation/view/notationviewinputcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,11 @@ void NotationViewInputController::mouseReleaseEvent(QMouseEvent* event)

if (interaction->isDragStarted()) {
interaction->endDrag();
// When dragging of hairpin ends on a note or rest, open dynamic popup
// Check for note or rest happens in Score::addText which is called through addTextToItem in toggleDynamicPopup
if (interaction->selection()->element()->isHairpinSegment()) {
interaction->toggleDynamicPopup();
}
}

if (interaction->isDragCopyStarted()) {
Expand Down

0 comments on commit 5c1cb87

Please sign in to comment.