placement and concertPitch cannot deviate from style

If my .mss defines concertPitch=1 and the default placement of hairpins to be above, I cannot make it use transposing pitch, nor can I move any hairpins to below: upon saving, these changes are simply lost.

I believe the problem also stems from this: I have the .mss set in Edit → Preferences → Score → Style, and the application cannot deviate from it (the .mss says concertPitch=1 so absence of the concertPitch XML tag in the .mscx means 1, but upon saving, 0 is saved as absence, too).

Could be, but in any case, I still can't reproduce a problem in 3.0.5, even setting my default style to your file and creating another new score as described above. Can you please list precise steps to reproduce the problem?

FWIW, the code to optimize saving of style parameters doesn't care about your own style defaults, it uses the "base" style always. At least it's supposed to, and does for me here.

Format → Load Style… and load the .mssagain (to ensure all settings are used; merely setting it as default style does not, for example, use its Spatium; this is no regression against 2.x)

in the first measure, create a hairpin in the Women

method 1

press X to move the hairpin from its default (above) position to below

in the toolbar, press Concert Pitch to make the Men use the bass clef

save

close

open

see the hairpin is back above

method 2

Format → Style… → Hairpins

change Placement to Below (note the ⟲ does not work here, perhaps because my .mss overrides the system default → bug)

hit OK ⇒ hairpin changes to below

save, close, reopen ⇒ hairpin is back above

edit the .mscx and either change the concertPitch tag from 1 to 0 or remove it entirely (bug both don’t work where both should work)

open ⇒ concert pitch is off

save, close, reopen ⇒ see it’s back on

This makes me believe that, upon reading a score, or when using the ⟲ icon, the presence of a default style in Preferences changes what MuseScore believes to be the default, whereas, upon writing it, only things changed from the built-in default are written.

This is entirely wrong and a major regression against 2.x, where the default style was merely used when creating new scores, and otherwise, the ⮢ button reverted to built-in defaults.

In 3.x AFAICT the .mss is still copied into the beginning of the .mscx (XPath //museScore/Score/Style) but it also affects (incompletely) what MuseScore thinks to be the built-in default style (as in: when I don’t write this tag to the XML / it doesn’t exist when reading from the XML, the default is used).

This even affects scores downloaded from MuseScore.com: when I load them in 3.x while my style is set as the default style, they take over some of its styles, overriding whatever the original author laid out and designed ☹

I can confirm that virtually all of this is fixed by the changes I alluded to, including the fact that your default style erroneously takes over loaded scores. There are other problems with line placement that are still pending, but this specific case with hairpins works for me in 3.1 even without my PR merged.

So, if you can reproduce a problem using 3.1, please post those steps, but the steps post above do work for me.

The behavior of the reset button within the style dialog is the only question mark here. Currently, it is true that this does a reset to default style (your customized defaults) rather than to the base style (MuseScore internal defaults). There are arguments both ways for how this should work, it's been discussed on Telegram and so far this has been the consensus. But it's inconsistent indeed that the enabled/disabled stated of the reset button itself depends on base style while the action is relative to default style. We should revisit this discussion - probably best on the forum where more users can weigh in - and come to a consensus.

Implementation-wise, right now, the isDefault() function used to decide on the state of the reset button compares against base style and is the same function used for for the optimize on save. Probably it should be renamed isBase() and then a new function isDefault() should be created that truly operates on the default.