Skip to content

Commit

Permalink
hovering piano keyboard shows shadownote (for staves that are not tab…
Browse files Browse the repository at this point in the history
… or drum)
  • Loading branch information
22justinl committed Jul 14, 2024
1 parent b7be435 commit 39bcf51
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/notation/inotationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ class INotationInteraction

// Shadow note
virtual bool showShadowNote(const muse::PointF& pos) = 0;
virtual void showShadowNoteFromPianoKeyboard(const uint8_t note) = 0;
virtual void hideShadowNote() = 0;
virtual muse::RectF shadowNoteRect() const = 0;
virtual muse::async::Channel<bool> pianoKeyboardShadowNoteChanged() const = 0;

// Visibility
virtual void toggleVisible() = 0;
Expand Down
67 changes: 65 additions & 2 deletions src/notation/internal/notationinteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#include "engraving/dom/textedit.h"
#include "engraving/dom/tuplet.h"
#include "engraving/dom/undo.h"
#include "engraving/dom/utils.h"
#include "engraving/compat/dummyelement.h"

#include "engraving/rw/xmlreader.h"
Expand Down Expand Up @@ -338,6 +339,65 @@ bool NotationInteraction::showShadowNote(const PointF& pos)
return false;
}

updateShadowNoteProperties(position);

return true;
}

void NotationInteraction::showShadowNoteFromPianoKeyboard(const uint8_t key)
{
const mu::engraving::InputState& inputState = score()->inputState();
mu::engraving::ShadowNote& shadowNote = *score()->shadowNote();

Segment* inputSegment = inputState.segment();
if (!inputSegment) {
return;
}
staff_idx_t staffIdx = inputState.track() / VOICES;
const Staff* inputStaff = score()->staff(staffIdx);
const Fraction tick = inputSegment->tick();
const double mag = inputStaff->staffMag(tick);
double lineDist = inputStaff->staffType(tick)->lineDistance().val()
* (inputStaff->isTabStaff(tick) ? 1 : .5)
* mag
* score()->style().spatium();
int rLine = 0;
if (inputStaff->isDrumStaff(tick)) {
// different behavior? ignoring for now
hideShadowNote();
m_pianoKeyboardShadowNoteChanged.send(false);
return;
} else if (inputStaff->isTabStaff(tick)) {
// different behavior? ignoring for now
hideShadowNote();
m_pianoKeyboardShadowNoteChanged.send(false);
return;
} else {
int octave = key / 12;
int line = octave * 7 + mu::engraving::pitch2step(key);
ClefType clef = inputStaff->clef(tick);
rLine = mu::engraving::relStep(line, clef);

// temp
shadowNote.setStaffIdx(staffIdx);
shadowNote.setLineIndex(rLine);
}

double xPos = inputSegment->canvasPos().x();
double yPos = inputSegment->system()->staffCanvasYpage(staffIdx) + rLine * lineDist;

PointF p = { xPos, yPos };

Position position = { inputSegment, staffIdx, rLine, mu::engraving::INVALID_FRET_INDEX, p };
updateShadowNoteProperties(position);

m_pianoKeyboardShadowNoteChanged.send(true);
}

void NotationInteraction::updateShadowNoteProperties(Position& position)
{
const mu::engraving::InputState& inputState = score()->inputState();
mu::engraving::ShadowNote& shadowNote = *score()->shadowNote();
Staff* staff = score()->staff(position.staffIdx);
const mu::engraving::Instrument* instr = staff->part()->instrument();

Expand Down Expand Up @@ -410,8 +470,6 @@ bool NotationInteraction::showShadowNote(const PointF& pos)
score()->renderer()->layoutItem(&shadowNote);

shadowNote.setPos(position.pos);

return true;
}

void NotationInteraction::hideShadowNote()
Expand Down Expand Up @@ -440,6 +498,11 @@ RectF NotationInteraction::shadowNoteRect() const
return rect;
}

muse::async::Channel<bool> NotationInteraction::pianoKeyboardShadowNoteChanged() const
{
return m_pianoKeyboardShadowNoteChanged;
}

void NotationInteraction::toggleVisible()
{
startEdit();
Expand Down
5 changes: 5 additions & 0 deletions src/notation/internal/notationinteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable

// Shadow note
bool showShadowNote(const muse::PointF& pos) override;
void showShadowNoteFromPianoKeyboard(const uint8_t note) override;
void hideShadowNote() override;
muse::RectF shadowNoteRect() const override;
muse::async::Channel<bool> pianoKeyboardShadowNoteChanged() const override;

// Visibility
void toggleVisible() override;
Expand Down Expand Up @@ -393,6 +395,8 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable
template<typename P>
void execute(void (mu::engraving::Score::* function)(P), P param);

void updateShadowNoteProperties(mu::engraving::Position& position);

struct HitMeasureData
{
Measure* measure = nullptr;
Expand Down Expand Up @@ -443,6 +447,7 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable
DropData m_dropData;
muse::async::Notification m_dropChanged;

muse::async::Channel<bool> m_pianoKeyboardShadowNoteChanged;
muse::async::Channel<ScoreConfigType> m_scoreConfigChanged;

engraving::BspTree m_droppableTree;
Expand Down
2 changes: 2 additions & 0 deletions src/notation/tests/mocks/notationinteractionmock.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ class NotationInteractionMock : public INotationInteraction
MOCK_METHOD(INotationNoteInputPtr, noteInput, (), (const, override));

MOCK_METHOD(bool, showShadowNote, (const muse::PointF&), (override));
MOCK_METHOD(void, showShadowNoteFromPianoKeyboard, (const uint8_t), (override));
MOCK_METHOD(void, hideShadowNote, (), (override));
MOCK_METHOD(muse::RectF, shadowNoteRect, (), (const, override));
MOCK_METHOD(muse::async::Channel<bool>, pianoKeyboardShadowNoteChanged, (), (const, override));

MOCK_METHOD(void, toggleVisible, (), (override));

Expand Down
24 changes: 24 additions & 0 deletions src/notation/view/abstractnotationpaintview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,30 @@ void AbstractNotationPaintView::onLoadNotation(INotationPtr)
}
});

// interaction->pianoKeyboardShadowNoteChanged().onReceive(this, [this](const PointF p) {
// showShadowNote(p);
// });
interaction->pianoKeyboardShadowNoteChanged().onReceive(this, [this](bool visible) {
// extract into function? redrawShadowNote(bool visible)
if (m_shadowNoteRect.isValid()) {
scheduleRedraw(m_shadowNoteRect);

if (!visible) {
m_shadowNoteRect = RectF();
return;
}
}

RectF shadowNoteRect = fromLogical(notationInteraction()->shadowNoteRect());

if (shadowNoteRect.isValid()) {
compensateFloatPart(shadowNoteRect);
scheduleRedraw(shadowNoteRect);
}

m_shadowNoteRect = shadowNoteRect;
});

updateLoopMarkers();
notationPlayback()->loopBoundariesChanged().onNotify(this, [this]() {
updateLoopMarkers();
Expand Down
14 changes: 14 additions & 0 deletions src/notation/view/pianokeyboard/pianokeyboardcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ void PianoKeyboardController::setPressedKey(std::optional<piano_key_t> key)
m_keyStatesChanged.notify();
}

void PianoKeyboardController::setHoveredKey(std::optional<piano_key_t> key)
{
auto notation = currentNotation();
if (!notation || m_hoveredKey == key || !notation->interaction()->noteInput()->isNoteInputMode()) {
return;
}

if (key.has_value()) {
notation->interaction()->showShadowNoteFromPianoKeyboard(key.value());
}

m_hoveredKey = key;
}

void PianoKeyboardController::onNotationChanged()
{
if (auto notation = currentNotation()) {
Expand Down
2 changes: 2 additions & 0 deletions src/notation/view/pianokeyboard/pianokeyboardcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class PianoKeyboardController : public muse::Injectable, public muse::async::Asy

std::optional<piano_key_t> pressedKey() const;
void setPressedKey(std::optional<piano_key_t> key);
void setHoveredKey(std::optional<piano_key_t> key);

KeyState keyState(piano_key_t key) const;
muse::async::Notification keyStatesChanged() const;
Expand All @@ -55,6 +56,7 @@ class PianoKeyboardController : public muse::Injectable, public muse::async::Asy
void sendNoteOff(piano_key_t key);

std::optional<piano_key_t> m_pressedKey = std::nullopt;
std::optional<piano_key_t> m_hoveredKey = std::nullopt;
std::unordered_set<piano_key_t> m_keys;
std::unordered_set<piano_key_t> m_otherNotesInChord;

Expand Down
13 changes: 13 additions & 0 deletions src/notation/view/pianokeyboard/pianokeyboardview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ PianoKeyboardView::PianoKeyboardView(QQuickItem* parent)
: muse::uicomponents::QuickPaintedView(parent), muse::Injectable(muse::iocCtxForQmlObject(this))
{
setAcceptedMouseButtons(Qt::LeftButton);
setAcceptHoverEvents(true);
}

PianoKeyboardView::~PianoKeyboardView()
Expand Down Expand Up @@ -552,3 +553,15 @@ void PianoKeyboardView::mouseReleaseEvent(QMouseEvent*)
{
m_controller->setPressedKey(std::nullopt);
}

void PianoKeyboardView::hoverMoveEvent(QHoverEvent* event)
{
QPointF oldPos = event->oldPosF();
QPointF pos = event->position();

if (oldPos == pos) {
return;
}

m_controller->setHoveredKey(keyAt(event->position()));
}
2 changes: 2 additions & 0 deletions src/notation/view/pianokeyboard/pianokeyboardview.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class PianoKeyboardView : public muse::uicomponents::QuickPaintedView, public mu
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;

void hoverMoveEvent(QHoverEvent* event) override;

std::optional<piano_key_t> keyAt(const QPointF& position) const;

static constexpr piano_key_t MIN_KEY = 0;
Expand Down

0 comments on commit 39bcf51

Please sign in to comment.