#pragma once #include class MonostepAudioProcessor; class SequencerMatrix final : public juce::Component { public: explicit SequencerMatrix (MonostepAudioProcessor& processor); ~SequencerMatrix() override = default; std::function onStepSelected; std::function onGateChanged; std::function onNoteChanged; std::function onFineChanged; std::function onSlideChanged; std::function onAccentChanged; void setSelectedStep (int stepIdx); int getSelectedStep() const { return selectedStep; } void setPlayPosition (int stepIdx); void paint (juce::Graphics&) override; void mouseDown (const juce::MouseEvent&) override; void mouseDrag (const juce::MouseEvent&) override; void mouseUp (const juce::MouseEvent&) override; private: struct CellRect { int step; int row; bool hit; }; CellRect cellAt (const juce::MouseEvent&) const; juce::Rectangle matrixBounds() const; int rowForSemitone (int semitone) const; int semitoneForRow (int row) const; MonostepAudioProcessor& processor; float labelW = 40.0f; float headerH = 24.0f; float headerPad = 10.0f; mutable float cellW = 24.0f; mutable float cellH = 17.0f; int selectedStep = 0; int playPos = -1; bool dragging = false; bool startedActive = false; bool moved = false; int lastRow = 0; int dragStep = 0; bool fineDragging = false; int fineRefRow = 0; float fineStartCents = 0.0f; float fineLastCents = -1000.0f; bool slideDragging = false; bool startedSlideActive = false; int slideLastStep = -1; bool accentDragging = false; bool startedAccentActive = false; int accentLastStep = -1; };