-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GSoC '24 - Dynamics Popup #23038
base: master
Are you sure you want to change the base?
GSoC '24 - Dynamics Popup #23038
Conversation
5904da1
to
0565ec7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First round of code review! I see a lot of good things, which is nice. And I also have a few suggestions to make it even better.
src/notation/qml/MuseScore/NotationScene/internal/DynamicPopup.qml
Outdated
Show resolved
Hide resolved
src/notation/qml/MuseScore/NotationScene/internal/DynamicPopup.qml
Outdated
Show resolved
Hide resolved
src/notation/qml/MuseScore/NotationScene/internal/DynamicPopup.qml
Outdated
Show resolved
Hide resolved
0565ec7
to
fed4c93
Compare
1e6a203
to
81579c2
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
cf50bc6
to
e081307
Compare
…nality to add hairpin from the popup
…e end of hairpin drag
e081307
to
d47c49d
Compare
…mics (for SFF dynamic as it doesnot have a SymId)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a mostly complete review of the changes in src/engraving. @mike-spa May I ask you to check the remaining methods?
// Right grip (when two grips) | ||
if (int(ed.curGrip) == 1 && hasLeftGrip() && hasRightGrip()) { | ||
m_rightDragOffset += ed.evtDelta.x(); | ||
if (rightDragOffset() < 0) { | ||
m_rightDragOffset = 0; | ||
} | ||
return; | ||
} | ||
|
||
// Right grip (when single grip) | ||
if (int(ed.curGrip) == 0 && !hasLeftGrip() && hasRightGrip()) { | ||
m_rightDragOffset += ed.evtDelta.x(); | ||
if (rightDragOffset() < 0) { | ||
m_rightDragOffset = 0; | ||
} | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like these can be consolidated into one if
statement, like
if ((int(ed.curGrip) == 1 && hasLeftGrip() && hasRightGrip())
|| (int(ed.curGrip) == 0 && !hasLeftGrip() && hasRightGrip())) {
Also, instead of calling has…Grip
again and again, you might want to call them once and use a variable, like this:
const bool hasLeftGrip = this->hasLeftGrip();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, will do that
// findAdjacentHairpins | ||
//--------------------------------------------------------- | ||
|
||
void Dynamic::findAdjacentHairpins() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike-spa Please check this method
} | ||
} | ||
|
||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant return statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I forgot to remove it
|
||
if (hasLeftGrip() && hasRightGrip()) { | ||
return 2; | ||
} else if (hasLeftGrip() ^ hasRightGrip()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The &&
case has already been checked, so no need to use xor
here. The use of ^
is a bit questionable because officially it's a bitwise operator and not a boolean operator.
} else if (hasLeftGrip() ^ hasRightGrip()) { | |
} else if (hasLeftGrip() || hasRightGrip()) { |
Also, the same applies about not calling has…Grip
again and again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, will use OR here
PointF leftOffset(-ldata->bbox().width() / 2 - md + m_leftDragOffset, -11.408); | ||
PointF rightOffset(ldata->bbox().width() / 2 + md + m_rightDragOffset, -11.408); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The number -11.408
is what we call a "magic number". When a cryptic hardcoded number is necessary, it is usually a good idea to extract it into a named constant, sometimes accompanied by a comment about how this number was found.
But in this case, I think it should not be a hardcoded number, but computed based on the height of the Dynamic, or perhaps some other parameter like the y-value of the visual center. It should namely also work well with different fonts, font sizes, spatium values.
@mike-spa Could you suggest a good way to compute the vertical grip positions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbjeukendrup I calculated it by subtracting the pagePos
of the dynamic from the position of Grip::START from HairpinSegment::gripsPositions
and it turned out to be a constant value. I wasn't sure how this could be computed, so I hardcoded it.
@@ -25,6 +25,8 @@ | |||
|
|||
#include "textbase.h" | |||
|
|||
#include "draw/painter.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of including the header here, you could include it in the cpp file, and here in the header file only write
namespace muse::draw {
class Painter;
}
} | ||
textBox = Factory::createDynamic(dummy()->segment()); | ||
chordRest->undoAddAnnotation(textBox); | ||
textBox->setFlag(ElementFlag::IS_PREVIEW, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to set this flag in the popup code. The popup is namely the only thing that will create a preview dynamic, so it would be good to keep the code that sets the flag close to the code that creates the preview dynamic.
(Also, when splitting this PR into smaller PRs, you'll need to do that anyway, because in the PR about the popup, this code won't exist yet 🙂)
@@ -3870,6 +3880,33 @@ void Score::addHairpinToDynamic(Hairpin* hairpin, Dynamic* dynamic) | |||
undoAddElement(hairpin); | |||
} | |||
|
|||
void Score::addHairpinOnGripDrag(Hairpin* hairpin, Dynamic* dynamic, const PointF& pos, Dynamic::Grip grip) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike-spa Please check this method
// Don't draw cursor if it's dynamic or if there is a selection | ||
if (!ed.element->isDynamic() && !cursor->hasSelection()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is questionable; for existing scores, dynamics may also contain normal text besides the dynamic symbol. So the user should also be able to edit that normal text, so a text cursor would be required.
RectF r; | ||
|
||
if (ed.element->isDynamic()) { | ||
r = toDynamic(ed.element)->adjustedBoundingRect(); | ||
} else { | ||
double m = spatium(); | ||
r = canvasBoundingRect().adjusted(-m, -m, m, m); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here applies what I said about checking whether we really need the adjustedBoundingRect
special case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is everything for notation/view
@@ -138,7 +138,11 @@ void NotationViewInputController::onNotationChanged() | |||
m_view->hideElementPopup(); | |||
|
|||
if (AbstractElementPopupModel::supportsPopup(type)) { | |||
m_view->showElementPopup(type, selectedItem->canvasBoundingRect()); | |||
if (selectedItem->isDynamic()) { | |||
m_view->showElementPopup(type, toDynamic(selectedItem)->adjustedBoundingRect()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same doubt again about adjustedBoundingRect
. You could decide to move all changes related to the addition of this method into yet another separate PR, so that the initial popup functionality PR will be easy to merge.
@@ -877,6 +881,10 @@ void NotationViewInputController::mouseReleaseEvent(QMouseEvent* event) | |||
|
|||
if (interaction->isDragStarted()) { | |||
interaction->endDrag(); | |||
// When dragging of hairpin ends on a note or rest, open dynamic popup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on a note or rest
I don't see this reflected in the actual condition in the code. Is it a requirement at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the check for note or rest happens here in Score::addText
which is called through addTextToItem
in toggleDynamicPopup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah okay. Still it might be better to adjust the comment, because it's a bit prone to causing confusion.
@@ -877,6 +881,10 @@ void NotationViewInputController::mouseReleaseEvent(QMouseEvent* event) | |||
|
|||
if (interaction->isDragStarted()) { | |||
interaction->endDrag(); | |||
// When dragging of hairpin ends on a note or rest, open dynamic popup | |||
if (interaction->selection()->element()->isHairpinSegment()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the one hand, this feels a bit like an ad-hoc special case. On the other hand, I don't quite see a better way for this, currently. @mike-spa do you have ideas?
@@ -546,7 +546,11 @@ void AbstractNotationPaintView::showElementPopup(const ElementType& elementType, | |||
|
|||
PopupModelType modelType = AbstractElementPopupModel::modelTypeFromElement(elementType); | |||
|
|||
emit showElementPopupRequested(modelType, fromLogical(elementRect).toQRectF()); | |||
if (elementType == ElementType::DYNAMIC) { | |||
emit showElementPopupRequested(modelType, fromLogical(elementRect).toQRect()); // toQRect() is used in AbstractElementPopupModel::updateItemRect() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand this change. Is it necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbjeukendrup Initially when we click on dynamic showElementPopupRequested
is emitted which if you notice uses toQRectF() meaning that the y
value will be a decimal which will be used to position the popup. But, the updateItemRect
method (which is called on changing the dynamic) returns a QRect and not QRectF so the value after the decimal will be lost, meaning that it will shift the popup a little higher than initially positioned as seen in below video. (It's not very noticeable but still...)
2024-08-19.08-57-17.mp4
I was thinking if we can change updateItemRect
to use QRectF (or, in general use QRect in showElementPopupRequested), so its in sync?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see! Yes, updating updateItemRect
and m_itemRect
to use QRectF seems good to me.
rect = QRect(); | ||
} else { | ||
if (m_item->isDynamic()) { | ||
rect = fromLogical(toDynamic(m_item)->adjustedBoundingRect()).toQRect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, same thing again...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used to place the popup below the adjustedBoundingRectangle
of dynamic like you can see in the in the below image, even when we zoom in it is placed consistently below the rectangle.
If we use the canvasBoundingRect
the popup will be inside the bounding rectangle on zooming (even if I add some value to root.y of updatePosition in Qml file it was not getting placed consistently on zooming, so thats why I used adjustedBoudingRect
instead of canvasBoundingRect
as the elementRect
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see... @mike-spa do you have ideas about how this can be generalised? A bit like hitShape()
, but different...
Also, since all TextBase elements have this grey box around them while editing their text, it might make sense to apply this to all of them. But only during actual text editing.
Pull request for Dynamics Popup Project.
Current Status