From 4ffa1a67722435d98303d896f862a6be9388e2d7 Mon Sep 17 00:00:00 2001 From: Eism Date: Thu, 10 Dec 2020 13:22:54 +0200 Subject: [PATCH] Implemented note input cursor --- src/framework/midi/internal/fluidsynth.cpp | 4 +- src/libmscore/segment.cpp | 30 +++++++ src/libmscore/segment.h | 2 + src/notation/CMakeLists.txt | 4 +- src/notation/inotationconfiguration.h | 2 +- src/notation/inotationnoteinput.h | 3 + .../internal/notationconfiguration.cpp | 19 +++-- src/notation/internal/notationconfiguration.h | 2 +- src/notation/internal/notationnoteinput.cpp | 60 +++++++++++++ src/notation/internal/notationnoteinput.h | 7 ++ src/notation/view/notationpaintview.cpp | 4 + src/notation/view/notationpaintview.h | 2 + src/notation/view/noteinputcursor.cpp | 84 +++++++++++++++++++ src/notation/view/noteinputcursor.h | 53 ++++++++++++ 14 files changed, 264 insertions(+), 12 deletions(-) create mode 100644 src/notation/view/noteinputcursor.cpp create mode 100644 src/notation/view/noteinputcursor.h diff --git a/src/framework/midi/internal/fluidsynth.cpp b/src/framework/midi/internal/fluidsynth.cpp index 174177c8bb..963ec4b5cb 100644 --- a/src/framework/midi/internal/fluidsynth.cpp +++ b/src/framework/midi/internal/fluidsynth.cpp @@ -185,7 +185,7 @@ Ret FluidSynth::removeSoundFonts() Ret FluidSynth::setupChannels(const std::vector& events) { - IF_ASSERT_FAILED(m_fluid->synth) { + if (!m_fluid->synth) { return make_ret(Err::SynthNotInited); } @@ -271,7 +271,7 @@ void FluidSynth::allSoundsOff() void FluidSynth::flushSound() { - IF_ASSERT_FAILED(m_fluid->synth) { + if (!m_fluid->synth) { return; } diff --git a/src/libmscore/segment.cpp b/src/libmscore/segment.cpp index 90b57f1e3d..8461b0d1e7 100644 --- a/src/libmscore/segment.cpp +++ b/src/libmscore/segment.cpp @@ -37,6 +37,7 @@ #include "xml.h" #include "undo.h" #include "harmony.h" +#include "hook.h" namespace Ms { //--------------------------------------------------------- @@ -1217,6 +1218,35 @@ void Segment::scanElements(void* data, void (* func)(void*, Element*), bool all) } } +QRectF Segment::contentRect() const +{ + QRectF result; + for (const Element* element: elist()) { + if (!element) { + continue; + } + + if (element->isChord()) { + const Chord* chord = dynamic_cast(element); + for (const Note* note: chord->notes()) { + result = result.united(note->bbox()); + } + + Hook* hook = chord->hook(); + if (hook) { + QRectF rect = QRectF(hook->pos().x(), hook->pos().y(), hook->width(), hook->height()); + result = result.united(rect); + } + + continue; + } + + result = result.united(element->bbox()); + } + + return result; +} + //--------------------------------------------------------- // firstElement // This function returns the first main element from a diff --git a/src/libmscore/segment.h b/src/libmscore/segment.h index cb10f59ea2..06bbf044e4 100644 --- a/src/libmscore/segment.h +++ b/src/libmscore/segment.h @@ -136,6 +136,8 @@ public: qreal x() const override { return ipos().x(); } void setX(qreal v) { rxpos() = v; } + QRectF contentRect() const; + void insertStaff(int staff); void removeStaff(int staff); diff --git a/src/notation/CMakeLists.txt b/src/notation/CMakeLists.txt index df56661902..077da7ee1b 100644 --- a/src/notation/CMakeLists.txt +++ b/src/notation/CMakeLists.txt @@ -128,8 +128,10 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/view/viewmodecontrolmodel.h ${CMAKE_CURRENT_LIST_DIR}/view/notationaccessibilitymodel.cpp ${CMAKE_CURRENT_LIST_DIR}/view/notationaccessibilitymodel.h - ${CMAKE_CURRENT_LIST_DIR}/view/playbackcursor.h ${CMAKE_CURRENT_LIST_DIR}/view/playbackcursor.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/playbackcursor.h + ${CMAKE_CURRENT_LIST_DIR}/view/noteinputcursor.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/noteinputcursor.h ${CMAKE_CURRENT_LIST_DIR}/view/notationswitchlistmodel.cpp ${CMAKE_CURRENT_LIST_DIR}/view/notationswitchlistmodel.h ${CMAKE_CURRENT_LIST_DIR}/view/partlistmodel.cpp diff --git a/src/notation/inotationconfiguration.h b/src/notation/inotationconfiguration.h index ba1c97da1a..3bf0075073 100644 --- a/src/notation/inotationconfiguration.h +++ b/src/notation/inotationconfiguration.h @@ -49,7 +49,7 @@ public: virtual QColor anchorLineColor() const = 0; virtual QColor playbackCursorColor() const = 0; - virtual QColor selectionColor() const = 0; + virtual QColor selectionColor(int voice = 0) const = 0; virtual int selectionProximity() const = 0; diff --git a/src/notation/inotationnoteinput.h b/src/notation/inotationnoteinput.h index 0fd6d79e15..2b9ebfd58a 100644 --- a/src/notation/inotationnoteinput.h +++ b/src/notation/inotationnoteinput.h @@ -48,6 +48,9 @@ public: virtual void setCurrentVoiceIndex(int voiceIndex) = 0; + virtual QRectF cursorRect() const = 0; + virtual QColor cursorColor() const = 0; + virtual async::Notification noteAdded() const = 0; virtual async::Notification stateChanged() const = 0; }; diff --git a/src/notation/internal/notationconfiguration.cpp b/src/notation/internal/notationconfiguration.cpp index c09abe39fa..2ba14a31c2 100644 --- a/src/notation/internal/notationconfiguration.cpp +++ b/src/notation/internal/notationconfiguration.cpp @@ -18,14 +18,15 @@ //============================================================================= #include "notationconfiguration.h" -#include "log.h" -#include "settings.h" - -#include "io/path.h" - #include "libmscore/preferences.h" #include "libmscore/mscore.h" +#include "log.h" +#include "settings.h" +#include "io/path.h" + +#include "notationtypes.h" + using namespace mu; using namespace mu::notation; using namespace mu::framework; @@ -160,9 +161,13 @@ QColor NotationConfiguration::playbackCursorColor() const return c; } -QColor NotationConfiguration::selectionColor() const +QColor NotationConfiguration::selectionColor(int voice) const { - return Ms::MScore::selectColor[0]; + if (!isVoiceIndexValid(voice)) { + return QColor(); + } + + return Ms::MScore::selectColor[voice]; } int NotationConfiguration::selectionProximity() const diff --git a/src/notation/internal/notationconfiguration.h b/src/notation/internal/notationconfiguration.h index 19bcc42e28..326bb003fc 100644 --- a/src/notation/internal/notationconfiguration.h +++ b/src/notation/internal/notationconfiguration.h @@ -50,7 +50,7 @@ public: io::path foregroundWallpaper() const override; QColor playbackCursorColor() const override; - QColor selectionColor() const override; + QColor selectionColor(int voice) const override; int selectionProximity() const override; diff --git a/src/notation/internal/notationnoteinput.cpp b/src/notation/internal/notationnoteinput.cpp index 63b1bffb47..80be4b0e73 100644 --- a/src/notation/internal/notationnoteinput.cpp +++ b/src/notation/internal/notationnoteinput.cpp @@ -25,6 +25,9 @@ #include "libmscore/chord.h" #include "libmscore/slur.h" #include "libmscore/articulation.h" +#include "libmscore/system.h" +#include "libmscore/page.h" +#include "libmscore/stafftype.h" #include "scorecallbacks.h" @@ -255,6 +258,63 @@ void NotationNoteInput::putTuplet(const TupletOptions& options) m_stateChanged.notify(); } +QRectF NotationNoteInput::cursorRect() const +{ + if (!isNoteInputMode()) { + return QRectF(); + } + + Ms::InputState& inputState = score()->inputState(); + Ms::Segment* segment = inputState.segment(); + if (!segment) { + return QRectF(); + } + + Ms::System* system = segment->measure()->system(); + if (!system) { + return QRectF(); + } + + int track = inputState.track() == -1 ? 0 : inputState.track(); + int staffIdx = track / VOICES; + + QRectF segmentContentRect = segment->contentRect(); + double x = segmentContentRect.translated(segment->pagePos()).x() - 6; + double y = system->staffYpage(staffIdx) + system->page()->pos().y(); + double w = segmentContentRect.width() + 12; + + double h; + + Staff* staff = score()->staff(staffIdx); + const Ms::StaffType* staffType = staff->staffType(inputState.tick()); + double spatium = score()->spatium(); + double lineDist = staffType->lineDistance().val() * spatium; + int lines = staffType->lines(); + int strg = inputState.string(); + + int instrStrgs = staff->part()->instrument()->stringData()->strings(); + if (staff->isTabStaff(inputState.tick()) && strg >= 0 && strg <= instrStrgs) { + h = lineDist; + y += staffType->physStringToYOffset(strg) * spatium; + y -= (staffType->onLines() ? lineDist * 0.5 : lineDist); + } else { + h = (lines - 1) * lineDist + 4 * spatium; + y -= 2.0 * spatium; + } + + QRectF result = QRectF(x, y, w, h); + return result; +} + +QColor NotationNoteInput::cursorColor() const +{ + Ms::InputState& inputState = score()->inputState(); + int track = inputState.track() == -1 ? 0 : inputState.track(); + int voice = track % VOICES; + + return configuration()->selectionColor(voice); +} + void NotationNoteInput::setSlur(Ms::Slur* slur) { Ms::InputState& inputState = score()->inputState(); diff --git a/src/notation/internal/notationnoteinput.h b/src/notation/internal/notationnoteinput.h index b4465952a9..b7badeb398 100644 --- a/src/notation/internal/notationnoteinput.h +++ b/src/notation/internal/notationnoteinput.h @@ -20,7 +20,9 @@ #define MU_NOTATION_NOTATIONNOTEINPUT_H #include "../inotationnoteinput.h" +#include "modularity/ioc.h" #include "async/asyncable.h" +#include "inotationconfiguration.h" #include "igetscore.h" #include "inotationinteraction.h" #include "inotationundostack.h" @@ -33,6 +35,8 @@ namespace mu::notation { class ScoreCallbacks; class NotationNoteInput : public INotationNoteInput, public async::Asyncable { + INJECT(notation, INotationConfiguration, configuration) + public: NotationNoteInput(const IGetScore* getScore, INotationInteraction* interaction, INotationUndoStackPtr undoStack); ~NotationNoteInput() override; @@ -56,6 +60,9 @@ public: void setCurrentVoiceIndex(int voiceIndex) override; + QRectF cursorRect() const override; + QColor cursorColor() const override; + async::Notification noteAdded() const override; async::Notification stateChanged() const override; diff --git a/src/notation/view/notationpaintview.cpp b/src/notation/view/notationpaintview.cpp index 270dbceb03..229ef62fe0 100644 --- a/src/notation/view/notationpaintview.cpp +++ b/src/notation/view/notationpaintview.cpp @@ -47,6 +47,8 @@ NotationPaintView::NotationPaintView(QQuickItem* parent) m_playbackCursor->setColor(configuration()->playbackCursorColor()); m_playbackCursor->setVisible(false); + m_noteInputCursor = new NoteInputCursor(); + playbackController()->isPlayingChanged().onNotify(this, [this]() { onPlayingChanged(); }); @@ -73,6 +75,7 @@ NotationPaintView::~NotationPaintView() { delete m_inputController; delete m_playbackCursor; + delete m_noteInputCursor; } void NotationPaintView::handleAction(const QString& actionName) @@ -208,6 +211,7 @@ void NotationPaintView::paint(QPainter* painter) m_notation->paint(painter, toLogical(rect)); m_playbackCursor->paint(painter); + m_noteInputCursor->paint(painter); } else { painter->drawText(10, 10, "no notation"); } diff --git a/src/notation/view/notationpaintview.h b/src/notation/view/notationpaintview.h index 6c3de0eb85..d3658337a2 100644 --- a/src/notation/view/notationpaintview.h +++ b/src/notation/view/notationpaintview.h @@ -36,6 +36,7 @@ #include "notationviewinputcontroller.h" #include "playbackcursor.h" +#include "noteinputcursor.h" namespace mu { namespace notation { @@ -124,6 +125,7 @@ private: QTransform m_matrix; NotationViewInputController* m_inputController = nullptr; PlaybackCursor* m_playbackCursor = nullptr; + NoteInputCursor* m_noteInputCursor = nullptr; }; } } diff --git a/src/notation/view/noteinputcursor.cpp b/src/notation/view/noteinputcursor.cpp new file mode 100644 index 0000000000..ec752501e2 --- /dev/null +++ b/src/notation/view/noteinputcursor.cpp @@ -0,0 +1,84 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= +#include "noteinputcursor.h" + +#include + +using namespace mu::notation; + +void NoteInputCursor::paint(QPainter* painter) +{ + if (!isNoteInputMode()) { + return; + } + + QRectF cursorRect = rect(); + QColor fillColor = color(); + fillColor.setAlpha(50); + painter->fillRect(cursorRect, fillColor); + + QRectF leftLine = QRectF(cursorRect.topLeft().x(), cursorRect.topLeft().y(), 3, cursorRect.height()); + QColor lineColor = color(); + painter->fillRect(leftLine, lineColor); +} + +INotationNoteInputPtr NoteInputCursor::currentNoteInput() const +{ + auto notation = globalContext()->currentNotation(); + if (!notation) { + return nullptr; + } + + auto interaction = notation->interaction(); + if (!interaction) { + return nullptr; + } + + return interaction->noteInput(); +} + +bool NoteInputCursor::isNoteInputMode() const +{ + auto noteInput = currentNoteInput(); + if (!noteInput) { + return false; + } + + return noteInput->isNoteInputMode(); +} + +QRectF NoteInputCursor::rect() const +{ + auto noteInput = currentNoteInput(); + if (!noteInput) { + return QRectF(); + } + + return noteInput->cursorRect(); +} + +QColor NoteInputCursor::color() const +{ + auto noteInput = currentNoteInput(); + if (!noteInput) { + return QColor(); + } + + return noteInput->cursorColor(); +} diff --git a/src/notation/view/noteinputcursor.h b/src/notation/view/noteinputcursor.h new file mode 100644 index 0000000000..b709d09e7c --- /dev/null +++ b/src/notation/view/noteinputcursor.h @@ -0,0 +1,53 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= +#ifndef MU_NOTATION_NOTEINPUTCURSOR_H +#define MU_NOTATION_NOTEINPUTCURSOR_H + +#include +#include + +#include "modularity/ioc.h" +#include "context/iglobalcontext.h" +#include "async/asyncable.h" +#include "inotationconfiguration.h" + +class QPainter; + +namespace mu::notation { +class NoteInputCursor : public async::Asyncable +{ + INJECT(notation, context::IGlobalContext, globalContext) + INJECT(notation, INotationConfiguration, configuration) + +public: + + NoteInputCursor() = default; + + void paint(QPainter* painter); + +private: + INotationNoteInputPtr currentNoteInput() const; + + bool isNoteInputMode() const; + QRectF rect() const; + QColor color() const; +}; +} + +#endif // MU_NOTATION_NOTEINPUTCURSOR_H