Skip to content

Commit

Permalink
Create new inspector section for "Linked to score" option and impleme…
Browse files Browse the repository at this point in the history
…nt related property

Introduce PropertyPropagate

Implement property prapagate on normal property edit

Implement property propagate on drag edit

Implement property propagate on text edit

Introduce relink all properties to master

Introduce read/write for LINKED_TO_MASTER property

Introduce read/write for individually unlinked properties

Consider "linked to master" property undefined if there are undividually unlinked properties

When indeterminate, the first click on the checkBox results in uncheck, instead of check

Change selection color to orange when an item has one or more properties unlinked from master

correction

Let TEXT property be selectively unlinked

Introduce linkListForProperty() to have better control of linked items when propagatin properties

Implement linkListForProperty for SpannerSegment

Refactor the system with the double toggle for property linking

Correct property linking on undo operations

Note head group property correction

Implement smart property propagation for spanners and spanner segments

Surface refactoring

Partial rewording

Introduce "Exclude from parts" inspector option

Introduce "Exclude from other parts" property

Introduce inspector UI for "Exclude from parts/score" property

Basic implementation of removing items upon checking "Exclude from score/part"

Basic implementation of reinserting items upon un-checking "Exclude from parts"

Correct written and playedback ottavas in parts when it has been excluded

Exclude more properties from the "Appearance" group

Add properties to POSITION set

Simplified logic for property link of spanners
  • Loading branch information
mike-spa committed Aug 16, 2023
1 parent a31e3ca commit 70df530
Show file tree
Hide file tree
Showing 29 changed files with 904 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/engraving/iengravingconfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class IEngravingConfiguration : MODULE_EXPORT_INTERFACE

virtual double guiScaling() const = 0;

virtual draw::Color selectionColor(voice_idx_t voiceIndex = 0, bool itemVisible = true) const = 0;
virtual draw::Color selectionColor(voice_idx_t voiceIndex = 0, bool itemVisible = true, bool itemIsUnlinkedFromScore = false) const = 0;
virtual void setSelectionColor(voice_idx_t voiceIndex, draw::Color color) = 0;
virtual async::Channel<voice_idx_t, draw::Color> selectionColorChanged() const = 0;

Expand Down
6 changes: 4 additions & 2 deletions src/engraving/internal/engravingconfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct VoiceColorKey {

static VoiceColorKey voiceColorKeys[VOICES];

static Color unlinkedItemColor = "#FF9300";

void EngravingConfiguration::init()
{
Color defaultVoiceColors[VOICES] {
Expand Down Expand Up @@ -216,9 +218,9 @@ double EngravingConfiguration::guiScaling() const
return uiConfiguration()->guiScaling();
}

Color EngravingConfiguration::selectionColor(voice_idx_t voice, bool itemVisible) const
Color EngravingConfiguration::selectionColor(voice_idx_t voice, bool itemVisible, bool itemIsUnlinkedFromScore) const
{
Color color = voiceColorKeys[voice].color;
Color color = itemIsUnlinkedFromScore ? unlinkedItemColor : voiceColorKeys[voice].color;

if (itemVisible) {
return color;
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/internal/engravingconfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class EngravingConfiguration : public IEngravingConfiguration, public async::Asy

double guiScaling() const override;

draw::Color selectionColor(voice_idx_t voiceIndex = 0, bool itemVisible = true) const override;
draw::Color selectionColor(voice_idx_t voiceIndex = 0, bool itemVisible = true, bool itemIsUnlinkedFromScore = false) const override;
void setSelectionColor(voice_idx_t voiceIndex, draw::Color color) override;
async::Channel<voice_idx_t, draw::Color> selectionColorChanged() const override;

Expand Down
71 changes: 40 additions & 31 deletions src/engraving/libmscore/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4890,34 +4890,41 @@ void Score::cloneVoice(track_idx_t strack, track_idx_t dtrack, Segment* sf, cons
// return true if an property was actually changed
//---------------------------------------------------------

bool Score::undoPropertyChanged(EngravingItem* e, Pid t, const PropertyValue& st, PropertyFlags ps)
bool Score::undoPropertyChanged(EngravingItem* item, Pid propId, const PropertyValue& propValue, PropertyFlags propFlags)
{
bool changed = false;

if (propertyLink(t) && e->links()) {
for (EngravingObject* ee : *e->links()) {
if (ee == e) {
if (ee->getProperty(t) != st) {
undoStack()->push1(new ChangeProperty(ee, t, st, ps));
changed = true;
}
} else {
// property in linked element has not changed yet
// push() calls redo() to change it
if (ee->getProperty(t) != e->getProperty(t)) {
undoStack()->push(new ChangeProperty(ee, t, e->getProperty(t), ps), 0);
changed = true;
}
}
PropertyValue currentPropValue = item->getProperty(propId);
PropertyFlags currentPropFlags = item->propertyFlags(propId);

if ((currentPropValue != propValue) || (currentPropFlags != propFlags)) {
item->setPropertyFlags(propId, propFlags);
undoStack()->push1(new ChangeProperty(item, propId, propValue, propFlags));
changed = true;
}

std::list<EngravingObject*> linkedItems = item->linkListForPropertyPropagation();

for (EngravingObject* linkedItem : linkedItems) {
if (linkedItem == item) {
continue;
}
} else {
PropertyFlags po = e->propertyFlags(t);
if ((e->getProperty(t) != st) || (ps != po)) {
e->setPropertyFlags(t, ps);
undoStack()->push1(new ChangeProperty(e, t, st, po));
changed = true;
PropertyPropagation propertyPropagate = item->propertyPropagation(linkedItem, propId);
switch (propertyPropagate) {
case PropertyPropagation::PROPAGATE:
if (linkedItem->getProperty(propId) != currentPropValue) {
undoStack()->push(new ChangeProperty(linkedItem, propId, currentPropValue, propFlags), 0);
changed = true;
}
break;
case PropertyPropagation::UNLINK:
item->unlinkPropertyFromMaster(propId);
break;
default:
break;
}
}

return changed;
}

Expand Down Expand Up @@ -6440,24 +6447,26 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
// undoRemoveElement
//---------------------------------------------------------

void Score::undoRemoveElement(EngravingItem* element)
void Score::undoRemoveElement(EngravingItem* element, bool removeLinked)
{
if (!element) {
return;
}
std::list<Segment*> segments;
for (EngravingObject* ee : element->linkList()) {
EngravingItem* e = static_cast<EngravingItem*>(ee);
undo(new RemoveElement(e));
if (e == element || removeLinked) {
undo(new RemoveElement(e));

if (e->explicitParent() && (e->explicitParent()->isSegment())) {
Segment* s = toSegment(e->explicitParent());
if (!mu::contains(segments, s)) {
segments.push_back(s);
if (e->explicitParent() && (e->explicitParent()->isSegment())) {
Segment* s = toSegment(e->explicitParent());
if (!mu::contains(segments, s)) {
segments.push_back(s);
}
}
if (e->explicitParent() && e->explicitParent()->isSystem()) {
e->setParent(0); // systems will be regenerated upon redo, so detach
}
}
if (e->explicitParent() && e->explicitParent()->isSystem()) {
e->setParent(0); // systems will be regenerated upon redo, so detach
}
}

Expand Down
128 changes: 127 additions & 1 deletion src/engraving/libmscore/engravingitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ void EngravingItem::reset()
undoResetProperty(Pid::PLACEMENT);
undoResetProperty(Pid::MIN_DISTANCE);
undoResetProperty(Pid::OFFSET);
undoResetProperty(Pid::POSITION_LINKED_TO_MASTER);
undoResetProperty(Pid::APPEARANCE_LINKED_TO_MASTER);
setOffsetChanged(false);
EngravingObject::reset();
}
Expand Down Expand Up @@ -612,7 +614,9 @@ mu::draw::Color EngravingItem::curColor(bool isVisible, Color normalColor) const
}

if (selected() || marked) {
return engravingConfiguration()->selectionColor(track() == mu::nidx ? 0 : voice(), isVisible);
bool isUnlinkedFromMaster = !(getProperty(Pid::POSITION_LINKED_TO_MASTER).toBool()
&& getProperty(Pid::APPEARANCE_LINKED_TO_MASTER).toBool());
return engravingConfiguration()->selectionColor(track() == mu::nidx ? 0 : voice(), isVisible, isUnlinkedFromMaster);
}

if (!isVisible) {
Expand Down Expand Up @@ -1121,6 +1125,12 @@ PropertyValue EngravingItem::getProperty(Pid propertyId) const
return systemFlag();
case Pid::SIZE_SPATIUM_DEPENDENT:
return sizeIsSpatiumDependent();
case Pid::POSITION_LINKED_TO_MASTER:
return _isPositionLinkedToMaster;
case Pid::APPEARANCE_LINKED_TO_MASTER:
return _isAppearanceLinkedToMaster;
case Pid::EXCLUDE_FROM_OTHER_PARTS:
return _excludeFromOtherParts;
default:
if (explicitParent()) {
return explicitParent()->getProperty(propertyId);
Expand Down Expand Up @@ -1176,6 +1186,27 @@ bool EngravingItem::setProperty(Pid propertyId, const PropertyValue& v)
case Pid::SIZE_SPATIUM_DEPENDENT:
setSizeIsSpatiumDependent(v.toBool());
break;
case Pid::POSITION_LINKED_TO_MASTER:
if (_isPositionLinkedToMaster == v.toBool()) {
break;
}
if (!_isPositionLinkedToMaster) {
relinkPropertiesToMaster(PropertyGroup::POSITION);
}
setPositionLinkedToMaster(v.toBool());
break;
case Pid::APPEARANCE_LINKED_TO_MASTER:
if (_isAppearanceLinkedToMaster == v.toBool()) {
break;
}
if (!_isAppearanceLinkedToMaster) {
relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
}
setAppearanceLinkedToMaster(v.toBool());
break;
case Pid::EXCLUDE_FROM_OTHER_PARTS:
setExcludeFromOtherParts(v.toBool());
break;
default:
if (explicitParent()) {
return explicitParent()->setProperty(propertyId, v);
Expand All @@ -1188,6 +1219,77 @@ bool EngravingItem::setProperty(Pid propertyId, const PropertyValue& v)
return true;
}

void EngravingItem::manageExclusionFromParts(bool exclude)
{
if (exclude) {
for (EngravingObject* linkedObject : linkList()) {
if (linkedObject == this) {
continue;
}
EngravingItem* linkedItem = toEngravingItem(linkedObject);
linkedItem->score()->undoRemoveElement(linkedItem, false);
linkedItem->undoUnlink();
}
} else {
// This element was exluded from parts (i.e. its clones were removed from the parts)
// Now we are asking to re-include it, i.e. re-clone it from this score to all others
// The only way to do this without duplicating cloning logic is to remove it and re-add it.
score()->undoRemoveElement(this);
if (isMeasureBase()) {
score()->insertMeasure(this->type(), toMeasureBase(this)->next());
} else if (isClef()) {
score()->undoChangeClef(this->staff(), this->nextElement(), toClef(this)->clefType());
} else {
score()->undoAddElement(this, true);
}
}
}

void EngravingItem::relinkPropertiesToMaster(PropertyGroup propertyGroup)
{
assert(!score()->isMaster());

std::list<EngravingObject*> linkedElements = linkListForPropertyPropagation();
EngravingObject* masterElement = nullptr;
for (EngravingObject* element : linkedElements) {
if (element->score()->isMaster()) {
masterElement = element;
break;
}
}

if (!masterElement) {
return;
}

const std::set<Pid>& propertiesToRelink = propertyGroup == PropertyGroup::POSITION ? positionProperties() : appearanceProperties();

for (Pid propertyId : propertiesToRelink) {
PropertyValue masterValue = masterElement->getProperty(propertyId);
PropertyFlags masterFlags = masterElement->propertyFlags(propertyId);
setProperty(propertyId, masterValue);
setPropertyFlags(propertyId, masterFlags);
}

return;
}

bool EngravingItem::canBeExcludedFromOtherParts() const
{
static const std::set<ElementType> typesExcludibleFromParts {
ElementType::STAFF_TEXT,
ElementType::SYSTEM_TEXT,
ElementType::CLEF,
ElementType::OTTAVA_SEGMENT,
ElementType::HBOX,
ElementType::VBOX,
ElementType::TBOX,
ElementType::FBOX,
};

return mu::contains(typesExcludibleFromParts, type());
}

//---------------------------------------------------------
// undoChangeProperty
//---------------------------------------------------------
Expand Down Expand Up @@ -1243,6 +1345,12 @@ PropertyValue EngravingItem::propertyDefault(Pid pid) const
return true;
case Pid::Z:
return int(type()) * 100;
case Pid::POSITION_LINKED_TO_MASTER:
return true;
case Pid::APPEARANCE_LINKED_TO_MASTER:
return true;
case Pid::EXCLUDE_FROM_OTHER_PARTS:
return false;
default: {
PropertyValue v = EngravingObject::propertyDefault(pid);

Expand Down Expand Up @@ -2413,6 +2521,24 @@ String EngravingItem::formatBarsAndBeats() const
return result;
}

bool EngravingItem::isPropertyLinkedToMaster(Pid id) const
{
if (mu::contains(positionProperties(), id)) {
return isPositionLinkedToMaster();
}

return isAppearanceLinkedToMaster();
}

void EngravingItem::unlinkPropertyFromMaster(Pid id)
{
if (mu::contains(positionProperties(), id)) {
score()->undo(new ChangeProperty(this, Pid::POSITION_LINKED_TO_MASTER, false));
} else if (mu::contains(appearanceProperties(), id)) {
score()->undo(new ChangeProperty(this, Pid::APPEARANCE_LINKED_TO_MASTER, false));
}
}

EngravingItem::LayoutData* EngravingItem::createLayoutData() const
{
return new EngravingItem::LayoutData();
Expand Down
10 changes: 10 additions & 0 deletions src/engraving/libmscore/engravingitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ class EngravingItem : public EngravingObject
INJECT_STATIC(IEngravingConfiguration, engravingConfiguration)
INJECT_STATIC(rendering::IScoreRenderer, renderer)

M_PROPERTY2(bool, isPositionLinkedToMaster, setPositionLinkedToMaster, true)
M_PROPERTY2(bool, isAppearanceLinkedToMaster, setAppearanceLinkedToMaster, true)
M_PROPERTY2(bool, excludeFromOtherParts, setExcludeFromOtherParts, false)

public:

virtual ~EngravingItem();
Expand Down Expand Up @@ -509,6 +513,12 @@ class EngravingItem : public EngravingObject
const PointF& ipos() const;
virtual double mag() const;

virtual bool isPropertyLinkedToMaster(Pid id) const;
void unlinkPropertyFromMaster(Pid id);
void relinkPropertiesToMaster(PropertyGroup propertyGroup);
bool canBeExcludedFromOtherParts() const;
void manageExclusionFromParts(bool exclude);

//! --- Old Interface ---
bool skipDraw() const { return layoutData()->isSkipDraw; }
void setSkipDraw(bool val) { mutLayoutData()->isSkipDraw = val; }
Expand Down
Loading

0 comments on commit 70df530

Please sign in to comment.