musescore / musescore/MuseScore

We must stop using the "Offset" property to determine the default position of items

Open
#22,622 3 comments 10 reactions 4 assignees View on GitHub

Nobody has claimed this yet.

engraving P1 UX/interaction
Dominant language
C++
Stars
15.1k
Forks
3.3k
Avg merge
2d 2h
Merged PRs (30d)
91

Description

Issue type

A disastrous combination of UX and Engraving jank.

Summary

What follows is just an explanation and a collection of the most obvious issues which arise from this. I am sure parts of this have been reported before. All of these problems come back to one single original design error:

The "Offset" property must be reserved for the user's offset. It must not be used for the default positions of the program.

This is something which I've encountered over and over again. It has now surfaced yet again as a blocker for the new "Center between staves" feature, unless I find I way to circumvent the issue. I'm logging this now as an issue of its own, so it's there for when we can get to it. It will be a big project, not only because we will need to rewrite all the default positions of all the items without using offsets, but also because we have years and years of scores that were generated this way, so we'll have to do migrations etc.

Explanation and examples

In Musescore, the default position of many items is determined by pre-applying an offset to them. See for instance:
image

Essentially, we have a concept of "default offset". This is a disaster, and it keeps causing an insane amount of engraving issues and general UX jank (which corresponds to code jank and hacks and workarounds underneath).

  1. The concept of "default offset" is in itself contradictory. You offset something with respect to a default, or a reference point. A "default offset" is... not an offset. It is just the default position of the item on the score.

  2. From a UX point of view, it is confusing and inconsistent. If I click on other items, like notes, I find their default position to have zero offset. So I (correctly) interpret the "Offset" property to represent the user's chosen position with respect to the default one. But when I select the hairpin, like above, I'm contradicted, and I have to guess that in that case the offset is meant with respect to the bottom of the staff. So which is it?
    image

  3. The "default offset" is meant to determine the default position. But the default position is also determined by the autoplace system. Then consider this: what does a 2sp offset mean in this case? (Spoiler: nothing, it's absolutely meaningless).
    image

  4. The "Offset" property is still supposed to hold the user's customized movement, so we have one single variable (the offset) representing both a default position and a user position. Let's say I want that hairpin to be slightly further away from the note. So I enter 3sp in the vertical offset field and... nothing happens. Why? Because the autoplace has already moved that hairpin to a point that's further than 3sp from the staff.
    image

  5. When mouse-dragging an item, the dragging operation has to move the item with respect to the position that the user sees on screen. But that position was calculated by autoplace. But autoplace already took an offset into account (the default one). But now I'm dragging so I'm also adding new offset. This can (and does, often) cause things to "explode" when dragging items, cause it's easy to run into infinite loops. To avoid this, we do a hack of "rebasing" the offset. "Rebasing" means that we add to the offset value the movement that had already been done by the autoplace. So, as soon as I start draggins this hairpin, the offset value jumps from 2sp to 5sp, even though I haven't even moved it yet.
    image
    Then, say, I want to put the hairpin back to the default position. I would imagine that that's what I'd get if I'd reset the offset.
    image
    But no: because we're now in a "offset rebased" state, the offset will indeed go back to 2sp, but meant as the original "2sp below the staff", which means we get this.
    image

  6. Notice from the previous example that, when the hairpin ends up overlapping the note, the "Minimum distance" property has suddenly been updated to an inexplicable negative value. That's not a bug, it's done intentionally. Why? Because, since the offset is also used to determine default positions, the autoplace system must take offsets into account, ending up with a situation where the offset of an item affects the autoplace of itself. Say, in the next example, I need to save space, so I move that dynamic inside the staff. I do that by dragging (i.e. applying an offset to) it. If we left "Minimum distance" as normal, the autoplace would take my offset into account and therefore "offset my offset" in order to prevent the collision with the staff, effectively disallowing me to move the dynamic inside the staff. To circumvent this problem, we hack the minimum distance and set it to a large negative value, so that the autoplace will not interpret that as a collision and let us do what we want.
    image

Steps to reproduce

Just try using the offset property on basically any item for more than 30 seconds.

Screenshots/Screen recordings

No response

MuseScore Version

Current master

Regression

No.

Operating system

Any

Additional context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.