Skip to content

Commit

Permalink
Improvements around hairpins and dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-spa committed May 31, 2024
1 parent 306cc5d commit 6a83e75
Show file tree
Hide file tree
Showing 31 changed files with 741 additions and 325 deletions.
7 changes: 7 additions & 0 deletions src/engraving/dom/anchors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "score.h"
#include "spanner.h"
#include "system.h"
#include "page.h"

#include "rendering/dev/measurelayout.h"

Expand Down Expand Up @@ -79,6 +80,7 @@ void EditTimeTickAnchors::updateAnchors(Measure* measure, staff_idx_t staffIdx)
for (const Fraction& anchorTick : anchorTicks) {
createTimeTickAnchor(measure, anchorTick, staffIdx);
}
measure->computeTicks();

Score* score = measure->score();

Expand All @@ -100,6 +102,11 @@ TimeTickAnchor* EditTimeTickAnchors::createTimeTickAnchor(Measure* measure, Frac
anchor->setParent(segment);
anchor->setTrack(track);
segment->add(anchor);
if (System* system = measure->system()) {
if (Page* page = system->page()) {
page->invalidateBspTree();
}
}
}

return anchor;
Expand Down
30 changes: 17 additions & 13 deletions src/engraving/dom/dynamic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ String Dynamic::dynamicText(DynamicType t)
return String::fromUtf8(DYN_LIST[int(t)].text);
}

Expression* Dynamic::snappedExpression() const
{
EngravingItem* item = ldata()->itemSnappedAfter();
return item && item->isExpression() ? toExpression(item) : nullptr;
}

bool Dynamic::acceptDrop(EditData& ed) const
{
ElementType droppedType = ed.dropElement->type();
Expand Down Expand Up @@ -624,11 +630,16 @@ void Dynamic::editDrag(EditData& ed)
if (km != (ShiftModifier | ControlModifier)) {
staff_idx_t si = staffIdx();
Segment* seg = segment();
score()->dragPosition(canvasPos(), &si, &seg, allowTimeAnchor());
static constexpr double spacingFactor = 1.0;
score()->dragPosition(canvasPos(), &si, &seg, spacingFactor, allowTimeAnchor());
if (seg != segment() || staffIdx() != si) {
const PointF oldOffset = offset();
PointF pos1(canvasPos());
score()->undoChangeParent(this, seg, si);
score()->undoChangeParent(this, seg, staffIdx());
Expression* snappedExpr = snappedExpression();
if (snappedExpr) {
score()->undoChangeParent(snappedExpr, seg, staffIdx());
}
setOffset(PointF());

renderer()->layoutItem(this);
Expand Down Expand Up @@ -666,6 +677,10 @@ void Dynamic::reset()
undoResetProperty(Pid::DIRECTION);
undoResetProperty(Pid::CENTER_BETWEEN_STAVES);
TextBase::reset();
Expression* snappedExp = snappedExpression();
if (snappedExp && snappedExp->getProperty(Pid::OFFSET) != snappedExp->propertyDefault(Pid::OFFSET)) {
snappedExp->reset();
}
}

//---------------------------------------------------------
Expand Down Expand Up @@ -815,17 +830,6 @@ PropertyValue Dynamic::propertyDefault(Pid id) const
}
}

void Dynamic::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
{
TextBase::undoChangeProperty(id, v, ps);
if (m_snappedExpression) {
if ((id == Pid::OFFSET && m_snappedExpression->offset() != v.value<PointF>())
|| (id == Pid::PLACEMENT && m_snappedExpression->placement() != v.value<PlacementV>())) {
m_snappedExpression->undoChangeProperty(id, v, ps);
}
}
}

//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
Expand Down
7 changes: 2 additions & 5 deletions src/engraving/dom/dynamic.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,18 @@ class Dynamic final : public TextBase
PropertyValue getProperty(Pid propertyId) const override;
bool setProperty(Pid propertyId, const PropertyValue&) override;
PropertyValue propertyDefault(Pid id) const override;
void undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps) override;

std::unique_ptr<ElementGroup> getDragGroup(std::function<bool(const EngravingItem*)> isDragged) override;

String accessibleInfo() const override;
String screenReaderInfo() const override;
// void doAutoplace();

void manageBarlineCollisions();

static String dynamicText(DynamicType t);
bool hasCustomText() const { return dynamicText(m_dynamicType) != xmlText(); }

void setSnappedExpression(Expression* e) { m_snappedExpression = e; }
Expression* snappedExpression() const { return m_snappedExpression; }
Expression* snappedExpression() const;

bool playDynamic() const { return m_playDynamic; }
void setPlayDynamic(bool v) { m_playDynamic = v; }
Expand All @@ -140,7 +138,6 @@ class Dynamic final : public TextBase
bool nudge(const EditData& ed);

DynamicType m_dynamicType = DynamicType::OTHER;
Expression* m_snappedExpression = nullptr;
bool m_playDynamic = true;

mutable PointF m_dragOffset;
Expand Down
81 changes: 81 additions & 0 deletions src/engraving/dom/engravingitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,51 @@ void EngravingItem::setPlacementBasedOnVoiceApplication(DirectionV styledDirecti
}
}

bool EngravingItem::shouldBeCenteredBetweenStaves(const System* system) const
{
if (!isStyled(Pid::OFFSET)) {
// NOTE: because of current limitations of the offset system, we can't center an element that's been manually moved.
return false;
}

const Part* itemPart = part();
bool centerStyle = style().styleB(Sid::dynamicsHairpinsAutoCenterOnGrandStaff);
AutoOnOff centerProperty = getProperty(Pid::CENTER_BETWEEN_STAVES).value<AutoOnOff>();
if (itemPart->nstaves() <= 1 || centerProperty == AutoOnOff::OFF || (!centerStyle && centerProperty != AutoOnOff::ON)) {
return false;
}

if (centerProperty != AutoOnOff::ON && !itemPart->instrument()->isNormallyMultiStaveInstrument()) {
return false;
}

const Staff* thisStaff = staff();
const std::vector<Staff*>& partStaves = itemPart->staves();
IF_ASSERT_FAILED(partStaves.size() > 0) {
return false;
}

if ((thisStaff == partStaves.front() && placeAbove()) || (thisStaff == partStaves.back() && placeBelow())) {
return false;
}

staff_idx_t thisIdx = thisStaff->idx();
if (placeAbove()) {
IF_ASSERT_FAILED(thisIdx > 0) {
return false;
}
}
staff_idx_t nextIdx = placeAbove() ? thisIdx - 1 : thisIdx + 1;

const SysStaff* thisSystemStaff = system->staff(thisIdx);
const SysStaff* nextSystemStaff = system->staff(nextIdx);
if (!thisSystemStaff->show() || !nextSystemStaff->show()) {
return false;
}

return centerProperty == AutoOnOff::ON || appliesToAllVoicesInInstrument();
}

void EngravingItem::relinkPropertiesToMaster(PropertyGroup propGroup)
{
assert(!score()->isMaster());
Expand Down Expand Up @@ -2557,6 +2602,42 @@ void EngravingItem::LayoutData::setBbox(const RectF& r)
m_shape.set_value(Shape(r, m_item, Shape::Type::Fixed));
}

void EngravingItem::LayoutData::connectItemSnappedBefore(EngravingItem* itemBefore)
{
IF_ASSERT_FAILED(itemBefore) {
return;
}
m_itemSnappedBefore = itemBefore;
itemBefore->mutldata()->m_itemSnappedAfter = const_cast<EngravingItem*>(m_item);
}

void EngravingItem::LayoutData::disconnectItemSnappedBefore()
{
if (!m_itemSnappedBefore) {
return;
}
m_itemSnappedBefore->mutldata()->m_itemSnappedAfter = nullptr;
m_itemSnappedBefore = nullptr;
}

void EngravingItem::LayoutData::connectItemSnappedAfter(EngravingItem* itemAfter)
{
IF_ASSERT_FAILED(itemAfter) {
return;
}
m_itemSnappedAfter = itemAfter;
itemAfter->mutldata()->m_itemSnappedBefore = const_cast<EngravingItem*>(m_item);
}

void EngravingItem::LayoutData::disconnectItemSnappedAfter()
{
if (!m_itemSnappedAfter) {
return;
}
m_itemSnappedAfter->mutldata()->m_itemSnappedBefore = nullptr;
m_itemSnappedAfter = nullptr;
}

const RectF& EngravingItem::LayoutData::bbox(LD_ACCESS mode) const
{
//! NOTE Temporary disabled CHECK - a lot of messages
Expand Down
25 changes: 25 additions & 0 deletions src/engraving/dom/engravingitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,24 @@ class EngravingItem : public EngravingObject

OffsetChange offsetChanged() const { return autoplace.offsetChanged; }

void connectItemSnappedBefore(EngravingItem* itemBefore);
void disconnectItemSnappedBefore();
void connectItemSnappedAfter(EngravingItem* itemAfter);
void disconnectItemSnappedAfter();
EngravingItem* itemSnappedBefore() const { return m_itemSnappedBefore; }
EngravingItem* itemSnappedAfter() const { return m_itemSnappedAfter; }

struct StaffCenteringInfo {
double availableVertSpaceAbove = 0.0;
double availableVertSpaceBelow = 0.0;
};
const StaffCenteringInfo& staffCenteringInfo() const { return m_staffCenteringInfo; }
void setStaffCenteringInfo(double availSpaceAbove, double availSpaceBelow)
{
m_staffCenteringInfo.availableVertSpaceAbove = availSpaceAbove;
m_staffCenteringInfo.availableVertSpaceBelow = availSpaceBelow;
}

void dump(std::stringstream& ss) const;

protected:
Expand Down Expand Up @@ -604,6 +622,11 @@ class EngravingItem : public EngravingObject
double m_mag = 1.0; // standard magnification (derived value)
ld_field<PointF> m_pos = "pos"; // Reference position, relative to _parent, set by autoplace
ld_field<Shape> m_shape = "shape";

EngravingItem* m_itemSnappedBefore = nullptr;
EngravingItem* m_itemSnappedAfter = nullptr;

StaffCenteringInfo m_staffCenteringInfo;
};

const LayoutData* ldata() const;
Expand Down Expand Up @@ -650,6 +673,8 @@ class EngravingItem : public EngravingObject
void checkVoiceApplicationCompatibleWithTrack();
void setPlacementBasedOnVoiceApplication(DirectionV styledDirection);

bool shouldBeCenteredBetweenStaves(const System* system) const;

void setOffsetChanged(bool val, bool absolute = true, const PointF& diff = PointF());
//! ---------------------

Expand Down
41 changes: 24 additions & 17 deletions src/engraving/dom/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ PropertyValue Expression::propertyDefault(Pid id) const
}
}

double Expression::computeDynamicExpressionDistance() const
double Expression::computeDynamicExpressionDistance(const Dynamic* snappedDyn) const
{
if (!m_snappedDynamic) {
IF_ASSERT_FAILED(snappedDyn) {
return 0.0;
}
// We are essentially faking the kerning behaviour of dynamic VS expression text
// There's no other way to do this because the dynamic is a different font.
String dynamicTextString = m_snappedDynamic->xmlText();
String dynamicTextString = snappedDyn->xmlText();
String f = String::fromStdString("<sym>dynamicForte</sym>");
double distance = (dynamicTextString.endsWith(f) ? 0.2 : 0.5) * spatium();
distance *= 0.5 * (m_snappedDynamic->dynamicsSize() + (size() / 10));
distance *= 0.5 * (snappedDyn->dynamicsSize() + (size() / 10));
return distance;
}

Expand All @@ -80,17 +80,6 @@ std::unique_ptr<ElementGroup> Expression::getDragGroup(std::function<bool(const
return TextBase::getDragGroup(isDragged);
}

void Expression::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps)
{
TextBase::undoChangeProperty(id, v, ps);
if (m_snappedDynamic) {
if ((id == Pid::OFFSET && m_snappedDynamic->offset() != v.value<PointF>())
|| (id == Pid::PLACEMENT && m_snappedDynamic->placement() != v.value<PlacementV>())) {
m_snappedDynamic->undoChangeProperty(id, v, ps);
}
}
}

bool Expression::acceptDrop(EditData& ed) const
{
return ed.dropElement->type() == ElementType::DYNAMIC || TextBase::acceptDrop(ed);
Expand All @@ -100,8 +89,9 @@ EngravingItem* Expression::drop(EditData& ed)
{
EngravingItem* item = ed.dropElement;
if (item->isDynamic()) {
if (m_snappedDynamic) {
return m_snappedDynamic->drop(ed);
Dynamic* snappedDyn = snappedDynamic();
if (snappedDyn) {
return snappedDyn->drop(ed);
}

item->setTrack(track());
Expand Down Expand Up @@ -167,4 +157,21 @@ void Expression::mapPropertiesFromOldExpressions(StaffText* staffText)
setFrameRound(staffText->frameRound());
}
}

Dynamic* Expression::snappedDynamic() const
{
EngravingItem* item = ldata()->itemSnappedBefore();
return item && item->isDynamic() ? toDynamic(item) : nullptr;
}

void Expression::reset()
{
undoResetProperty(Pid::DIRECTION);
undoResetProperty(Pid::CENTER_BETWEEN_STAVES);
TextBase::reset();
Dynamic* snappedDyn = snappedDynamic();
if (snappedDyn && snappedDyn->getProperty(Pid::OFFSET) != snappedDyn->propertyDefault(Pid::OFFSET)) {
snappedDyn->reset();
}
}
} // namespace mu::engraving
11 changes: 4 additions & 7 deletions src/engraving/dom/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,22 @@ class Expression final : public TextBase

PropertyValue propertyDefault(Pid id) const override;

double computeDynamicExpressionDistance() const;
double computeDynamicExpressionDistance(const Dynamic* snappedDyn) const;

std::unique_ptr<ElementGroup> getDragGroup(std::function<bool(const EngravingItem*)> isDragged) override;

void undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps) override;

bool acceptDrop(EditData& ed) const override;
EngravingItem* drop(EditData& ed) override;

PropertyValue getProperty(Pid propertyId) const override;
bool setProperty(Pid propertyId, const PropertyValue& v) override;
void mapPropertiesFromOldExpressions(StaffText* staffText);

Dynamic* snappedDynamic() const { return m_snappedDynamic; }
void setSnappedDynamic(Dynamic* d) { m_snappedDynamic = d; }
Dynamic* snappedDynamic() const;

private:
bool hasVoiceApplicationProperties() const override { return true; }

Dynamic* m_snappedDynamic = nullptr;
void reset() override;
};
} // namespace mu::engraving
#endif // MU_ENGRAVING_EXPRESSION_H
Loading

0 comments on commit 6a83e75

Please sign in to comment.