From 91c870b0dfd2b172ec267154d6f5675678aa3ad0 Mon Sep 17 00:00:00 2001 From: yuxshao Date: Mon, 24 Jan 2022 21:50:59 -0500 Subject: [PATCH] Left piano take 3 (#50) --- src/editor/EditState.cpp | 2 +- src/editor/EditState.h | 29 ++- src/editor/EditorScrollArea.cpp | 9 +- src/editor/EditorScrollArea.h | 2 + src/editor/EditorWindow.cpp | 2 + src/editor/StyleEditor.cpp | 2 + src/editor/StyleEditor.h | 1 + src/editor/audio/NotePreview.cpp | 22 +- src/editor/audio/NotePreview.h | 1 + src/editor/views/KeyboardView.cpp | 344 ++++++++++++++++++------------ src/editor/views/KeyboardView.h | 3 + src/editor/views/MeasureView.cpp | 65 +++--- src/editor/views/ParamView.cpp | 33 +-- src/editor/views/ViewHelper.cpp | 6 +- src/editor/views/ViewHelper.h | 1 + src/styles/ptCollage/config.ini | 5 +- 16 files changed, 340 insertions(+), 187 deletions(-) diff --git a/src/editor/EditState.cpp b/src/editor/EditState.cpp index c988ec21..d54888da 100644 --- a/src/editor/EditState.cpp +++ b/src/editor/EditState.cpp @@ -45,7 +45,7 @@ MouseEditState::MouseEditState() last_pitch(EVENTDEFAULT_KEY), start_clock(0), current_clock(0), - kind(MouseKeyboardEdit({0, 0})), + kind(MouseKeyboardEdit({MouseMainKeyboard{0}, 0})), selection(std::nullopt) {} QDataStream &operator<<(QDataStream &out, const MouseEditState &a) { diff --git a/src/editor/EditState.h b/src/editor/EditState.h index e45387fb..1386ead4 100644 --- a/src/editor/EditState.h +++ b/src/editor/EditState.h @@ -12,16 +12,39 @@ #include "protocol/SerializeVariant.h" #include "pxtone/pxtnMaster.h" -struct MouseKeyboardEdit { +struct MouseLeftKeyboard { + qint32 start_vel; +}; +inline QDataStream &operator<<(QDataStream &out, const MouseLeftKeyboard &a) { + out << a.start_vel; + return out; +} +inline QDataStream &operator>>(QDataStream &in, MouseLeftKeyboard &a) { + in >> a.start_vel; + return in; +} + +struct MouseMainKeyboard { qint32 start_pitch; +}; +inline QDataStream &operator<<(QDataStream &out, const MouseMainKeyboard &a) { + out << a.start_pitch; + return out; +} +inline QDataStream &operator>>(QDataStream &in, MouseMainKeyboard &a) { + in >> a.start_pitch; + return in; +} +struct MouseKeyboardEdit { + std::variant kind; qint32 current_pitch; }; inline QDataStream &operator<<(QDataStream &out, const MouseKeyboardEdit &a) { - out << a.start_pitch << a.current_pitch; + out << a.kind << a.current_pitch; return out; } inline QDataStream &operator>>(QDataStream &in, MouseKeyboardEdit &a) { - in >> a.start_pitch >> a.current_pitch; + in >> a.kind >> a.current_pitch; return in; } diff --git a/src/editor/EditorScrollArea.cpp b/src/editor/EditorScrollArea.cpp index f0d7ade9..9a5ca025 100644 --- a/src/editor/EditorScrollArea.cpp +++ b/src/editor/EditorScrollArea.cpp @@ -27,7 +27,8 @@ EditorScrollArea::EditorScrollArea(QWidget *parent, bool match_scale) mouseDown(false), m_match_scale(match_scale), lastPos(), - anim(new Animation(this)) { + anim(new Animation(this)), + m_scroll_with_mouse_x(true) { setWidgetResizable(true); setMouseTracking(true); setFrameStyle(QFrame::NoFrame); @@ -40,7 +41,7 @@ EditorScrollArea::EditorScrollArea(QWidget *parent, bool match_scale) // scrolling. if (!(QApplication::mouseButtons() & (Qt::LeftButton | Qt::RightButton))) mouseDown = false; - if (mouseDown) scrollWithMouseX(); + if (mouseDown && m_scroll_with_mouse_x) scrollWithMouseX(); }); } @@ -195,6 +196,10 @@ void EditorScrollArea::ensureWithinMargin(int x, qreal minDistFromLeft, } } +void EditorScrollArea::setEnableScrollWithMouseX(bool b) { + m_scroll_with_mouse_x = b; +} + constexpr int JUMP_MAX = 10; constexpr int MARGIN_MAX = 100; constexpr double MARGIN_FRAC = 0.25; diff --git a/src/editor/EditorScrollArea.h b/src/editor/EditorScrollArea.h index d6ef9642..29c9a164 100644 --- a/src/editor/EditorScrollArea.h +++ b/src/editor/EditorScrollArea.h @@ -16,6 +16,7 @@ class EditorScrollArea : public QScrollArea { void ensureWithinMargin(int x, qreal minDistFromLeft, qreal jumpMinDistFromLeft, qreal jumpMaxDistFromLeft, qreal maxDistFromLeft); + void setEnableScrollWithMouseX(bool); signals: void viewportChanged(const QRect &viewport); @@ -27,6 +28,7 @@ class EditorScrollArea : public QScrollArea { bool m_match_scale; QPoint lastPos; Animation *anim; + bool m_scroll_with_mouse_x; bool event(QEvent *e) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; diff --git a/src/editor/EditorWindow.cpp b/src/editor/EditorWindow.cpp index b48caf2f..79c50b50 100644 --- a/src/editor/EditorWindow.cpp +++ b/src/editor/EditorWindow.cpp @@ -111,6 +111,8 @@ EditorWindow::EditorWindow(QWidget *parent) m_scroll_area->setWidget(m_keyboard_view); // TODO: find a better place for this. + connect(m_keyboard_view, &KeyboardView::setScrollOnClick, m_scroll_area, + &EditorScrollArea::setEnableScrollWithMouseX); connect(m_keyboard_view, &KeyboardView::ensureVisibleX, [this](int x, bool strict) { if (!strict) diff --git a/src/editor/StyleEditor.cpp b/src/editor/StyleEditor.cpp index b637541a..6839939d 100644 --- a/src/editor/StyleEditor.cpp +++ b/src/editor/StyleEditor.cpp @@ -114,6 +114,8 @@ void loadConfig(const QString &path, Config &c) { setConfigColor(styleConfig, c.color.KeyboardBlackNote, "keyboard/BlackNote"); setConfigColor(styleConfig, c.color.KeyboardWhiteLeft, "keyboard/WhiteLeft"); setConfigColor(styleConfig, c.color.KeyboardBlackLeft, "keyboard/BlackLeft"); + setConfigColor(styleConfig, c.color.KeyboardBlackLeftInner, + "keyboard/BlackLeftInner"); setConfigColor(styleConfig, c.color.KeyboardBlack, "keyboard/Black"); setConfigColor(styleConfig, c.color.KeyboardMeasure, "keyboard/Measure"); diff --git a/src/editor/StyleEditor.h b/src/editor/StyleEditor.h index ac07b89f..6ece9610 100644 --- a/src/editor/StyleEditor.h +++ b/src/editor/StyleEditor.h @@ -33,6 +33,7 @@ struct Config { QColor KeyboardBlackNote; QColor KeyboardWhiteLeft; QColor KeyboardBlackLeft; + QColor KeyboardBlackLeftInner; QColor KeyboardBlack; QColor KeyboardMeasure; diff --git a/src/editor/audio/NotePreview.cpp b/src/editor/audio/NotePreview.cpp index b46afc55..bddc9d0f 100644 --- a/src/editor/audio/NotePreview.cpp +++ b/src/editor/audio/NotePreview.cpp @@ -63,15 +63,7 @@ NotePreview::NotePreview(const pxtnService *pxtn, const mooParams *moo_params, // We don't constantly reset because sometimes the audio engine forces // [life_count = 0] (say at the end of the sample) - if (m_unit != nullptr) { - std::shared_ptr woice = m_this_unit->get_woice(); - for (int i = 0; i < woice->get_voice_num(); ++i) { - // TODO: calculating the life count should be more automatic. - auto tone = m_this_unit->get_tone(i); - tone->on_count = duration; - tone->life_count = duration + woice->get_instance(i)->env_release; - } - } + if (m_unit != nullptr) resetOn(duration); if (m_moo_state != nullptr) { for (auto &unit : m_moo_state->units) { pxtnUnitTone *u = &unit; @@ -109,6 +101,18 @@ void NotePreview::processEvent(EVENTKIND kind, int32_t value) { m_moo_params->processNonOnEvent(m_this_unit, kind, value, m_pxtn); } +void NotePreview::resetOn(int duration) { + std::shared_ptr woice = m_this_unit->get_woice(); + for (int i = 0; i < woice->get_voice_num(); ++i) { + // TODO: calculating the life count should be more automatic. + auto tone = m_this_unit->get_tone(i); + *tone = pxtnVOICETONE(tone->env_release_clock, tone->offset_freq, + woice->get_instance(i)->env_size != 0); + tone->on_count = duration; + tone->life_count = duration + woice->get_instance(i)->env_release; + } +} + static EVERECORD ev(int32_t clock, EVENTKIND kind, int32_t value) { EVERECORD e; e.clock = clock; diff --git a/src/editor/audio/NotePreview.h b/src/editor/audio/NotePreview.h index 426ef68e..55a5b55b 100644 --- a/src/editor/audio/NotePreview.h +++ b/src/editor/audio/NotePreview.h @@ -28,6 +28,7 @@ class NotePreview : public QObject { int vel, int duration, std::shared_ptr woice, int bufferSize, QObject *parent); void processEvent(EVENTKIND kind, int32_t value); + void resetOn(int duration); ~NotePreview(); private: diff --git a/src/editor/views/KeyboardView.cpp b/src/editor/views/KeyboardView.cpp index 4dd82c76..154713d4 100644 --- a/src/editor/views/KeyboardView.cpp +++ b/src/editor/views/KeyboardView.cpp @@ -23,9 +23,9 @@ void LocalEditState::update(const pxtnService *pxtn, const EditState &s) { } QSize KeyboardView::sizeHint() const { - return QSize( - one_over_last_clock(m_pxtn) / m_client->editState().scale.clockPerPx, - m_client->editState().scale.pitchToY(EVENTMIN_KEY)); + return QSize(LEFT_LEGEND_WIDTH + one_over_last_clock(m_pxtn) / + m_client->editState().scale.clockPerPx, + m_client->editState().scale.pitchToY(EVENTMIN_KEY)); } void KeyboardView::setHoveredUnitNo(std::optional new_unit_no) { @@ -49,6 +49,7 @@ KeyboardView::KeyboardView(PxtoneClient *client, MooClock *moo_clock, m_client(client), m_moo_clock(moo_clock), m_select_unit_enabled(false), + m_last_left_kb_pitch(0), m_test_activity(false) { setFocusPolicy(Qt::StrongFocus); setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -173,9 +174,11 @@ int impliedVelocity(MouseEditState state, const Scale &scale) { if (std::holds_alternative(state.kind) && Settings::VelocityDrag::get()) { const auto &s = std::get(state.kind); - delta = (s.current_pitch - s.start_pitch) / scale.pitchPerPx; - // Apply a sigmoid so that small changes in y hardly do anything - delta = 2 * slack / (1 + exp(2 * delta / slack)) + delta - slack; + if (const auto *main_keyboard = std::get_if(&s.kind)) { + delta = (s.current_pitch - main_keyboard->start_pitch) / scale.pitchPerPx; + // Apply a sigmoid so that small changes in y hardly do anything + delta = 2 * slack / (1 + exp(2 * delta / slack)) + delta - slack; + } } return clamp(int(round(state.base_velocity + delta / pixelsPerVelocity)), 0, @@ -269,13 +272,16 @@ void drawOngoingAction(const EditState &state, const LocalEditState &localState, break; const auto &keyboard_edit_state = std::get(mouse_edit_state.kind); + if (!std::holds_alternative(keyboard_edit_state.kind)) + break; + const auto &[start_pitch] = + std::get(keyboard_edit_state.kind); int velocity = impliedVelocity(mouse_edit_state, state.scale); // TODO: maybe factor out this quantization logic Interval interval( mouse_edit_state.clock_int(localState.m_quantize_clock)); - int pitch = quantize_pitch(keyboard_edit_state.start_pitch, - localState.m_quantize_pitch); + int pitch = quantize_pitch(start_pitch, localState.m_quantize_pitch); int alpha = (mouse_edit_state.type == MouseEditState::Nothing ? 128 : 255); @@ -328,32 +334,35 @@ void drawOngoingAction(const EditState &state, const LocalEditState &localState, static void drawCursor(const EditState &state, QPainter &painter, const QColor &color, const QString &username, qint64 uid) { - if (!std::holds_alternative(state.mouse_edit_state.kind)) - return; - const auto &keyboard_edit_state = - std::get(state.mouse_edit_state.kind); + std::optional pitch; + if (std::holds_alternative(state.mouse_edit_state.kind)) + pitch = + std::get(state.mouse_edit_state.kind).current_pitch; + + if (!pitch.has_value()) return; QPoint position(state.mouse_edit_state.current_clock / state.scale.clockPerPx, - state.scale.pitchToY(keyboard_edit_state.current_pitch)); + state.scale.pitchToY(pitch.value())); drawCursor(position, painter, color, username, uid); } -void drawLeftPiano(QPainter &painter, int x, int y, int h, const QColor &b) { - painter.fillRect(x, y, LEFT_LEGEND_WIDTH, h, b); - painter.fillRect(x + LEFT_LEGEND_WIDTH, y + 1, 1, h - 2, b); +void drawLeftPiano(QPainter &painter, int y, int h, const QColor &b, + QColor *bInner) { + painter.fillRect(0, y, LEFT_LEGEND_WIDTH, h, b); + painter.fillRect(LEFT_LEGEND_WIDTH, y + 1, 1, h - 2, b); + if (bInner) painter.fillRect(0, y, LEFT_LEGEND_WIDTH * 2 / 3, h, *bInner); } double smoothDistance(double dy, double dx) { double r = dy * dy + dx * dx; return std::max(0.0, 1 / (r + 1)); } -void drawStateSegment(QPainter &painter, const DrawState &state, - const Interval &segment, +void drawStateSegment(QPainter &painter, QPainter &leftPianoPainter, + const DrawState &state, const Interval &segment, const std::optional &selection, const Interval &bounds, const Brush &brush, qint32 alpha, const Scale &scale, qint32 current_clock, const MouseEditState &mouse, bool drawTooltip, bool muted, - int width, bool playing, int viewportLeft, - int displayEdo) { + int width, bool playing, int displayEdo) { Interval on = state.ongoingOnEvent.value(); Interval interval = interval_intersect(on, segment); playing = playing && on.contains(current_clock); @@ -363,12 +372,12 @@ void drawStateSegment(QPainter &painter, const DrawState &state, 128, false, 16 * state.velocity.value / 128 * (alpha / 2 + 128) / 256); paintBlock(state.pitch.value, Interval{0, int(scale.clockPerPx * width)}, painter, c, scale, displayEdo); - c.setAlpha((32 + c.alpha() * 2 / 3) * + c.setAlpha((64 + c.alpha() * 2 / 3) * (1 - std::min(0.5, (current_clock - on.start) / 1200.0))); - drawLeftPiano(painter, viewportLeft, + drawLeftPiano(leftPianoPainter, scale.pitchToY(state.pitch.value) - int(PITCH_PER_OCTAVE / scale.pitchPerPx / displayEdo / 2), - PITCH_PER_OCTAVE / displayEdo / scale.pitchPerPx, c); + PITCH_PER_OCTAVE / displayEdo / scale.pitchPerPx, c, nullptr); } if (interval_intersect(interval, bounds).empty()) return; QColor color = brush.toQColor(state.velocity.value, playing && !muted, alpha); @@ -427,13 +436,15 @@ void KeyboardView::paintEvent(QPaintEvent *event) { ++painted; // if (painted > 10) return; QPainter painter(this); + painter.fillRect(event->rect(), Qt::black); + + painter.setTransform(worldTransform()); Interval clockBounds = { qint32(event->rect().left() * m_client->editState().scale.clockPerPx) - WINDOW_BOUND_SLACK, qint32(event->rect().right() * m_client->editState().scale.clockPerPx) + WINDOW_BOUND_SLACK}; - painter.fillRect(0, 0, size().width(), size().height(), Qt::black); // Draw white lines under background QBrush beatBrush(StyleEditor::config.color.KeyboardBeat); QBrush measureBrush(StyleEditor::config.color.KeyboardMeasure); @@ -452,6 +463,7 @@ void KeyboardView::paintEvent(QPaintEvent *event) { QColor whiteLeftBrush = StyleEditor::config.color.KeyboardWhiteLeft; QColor blackLeftBrush = StyleEditor::config.color.KeyboardBlackLeft; + QColor blackLeftInnerBrush = StyleEditor::config.color.KeyboardBlackLeftInner; QColor black = StyleEditor::config.color.KeyboardBlack; QLinearGradient gradient(0, 0, 1, 0); @@ -464,12 +476,23 @@ void KeyboardView::paintEvent(QPaintEvent *event) { const Scale &scale = m_client->editState().scale; bool octave_display_a = Settings::OctaveDisplayA::get(); - QPixmap octaveDisplayLayer(event->rect().size()); - octaveDisplayLayer.fill(Qt::transparent); - QPainter octaveDisplayPainter(&octaveDisplayLayer); - octaveDisplayPainter.translate(-event->rect().topLeft()); + QPixmap leftPianoLayer(event->rect().size()); + leftPianoLayer.fill(Qt::transparent); + QPainter leftPianoPainter(&leftPianoLayer); + leftPianoPainter.translate(-event->rect().topLeft()); + leftPianoPainter.translate(-pos().x(), 0); + leftPianoPainter.fillRect(0, 0, LEFT_LEGEND_WIDTH, height(), + blackLeftInnerBrush); + + std::optional> left_piano_pitch_and_vel; + if (auto *keyboard_edit_state = std::get_if( + &m_client->editState().mouse_edit_state.kind)) + if (auto *s = std::get_if(&keyboard_edit_state->kind)) { + left_piano_pitch_and_vel = {keyboard_edit_state->current_pitch, + s->start_vel}; + } for (int row = 0; true; ++row) { - QColor *brush, *leftBrush; + QColor *brush, *leftBrush, *leftInnerBrush = nullptr; // Start the backgrounds on an A just so that the key pattern lines up int a_above_max_key = (EVENTMAX_KEY / PITCH_PER_OCTAVE + 1) * PITCH_PER_OCTAVE; @@ -484,6 +507,7 @@ void KeyboardView::paintEvent(QPaintEvent *event) { if (displayEdoList[nonnegative_modulo(-row, displayEdo)]) { brush = &blackNoteBrush; leftBrush = &blackLeftBrush; + leftInnerBrush = &blackLeftInnerBrush; } else { brush = &whiteNoteBrush; leftBrush = &whiteLeftBrush; @@ -498,10 +522,10 @@ void KeyboardView::paintEvent(QPaintEvent *event) { if (this_y > size().height()) break; // Because of rounding error, calculate height by subbing next from this int h = next_y - this_y - 1; - if (m_dark && row % 2 == 1) h += 1; painter.fillRect(0, this_y - floor_h / 2, size().width(), h, *brush); - drawLeftPiano(painter, -pos().x(), this_y - floor_h / 2, h, *leftBrush); + drawLeftPiano(leftPianoPainter, this_y - floor_h / 2, h, *leftBrush, + leftInnerBrush); // painter.fillRect(0, this_y, 9999, 1, QColor::fromRgb(255, 255, 255, // 50)); @@ -510,9 +534,18 @@ void KeyboardView::paintEvent(QPaintEvent *event) { if (nonnegative_modulo(pitch - EVENTDEFAULT_KEY, PITCH_PER_OCTAVE) == pitch_offset) drawOctaveNumAlignBottomLeft( - &octaveDisplayPainter, -pos().x() + 4, this_y - floor_h / 2 + h - 2, + &leftPianoPainter, 4, this_y - floor_h / 2 + h - 2, (pitch - PITCH_PER_OCTAVE / 4) / PITCH_PER_OCTAVE - 3, floor_h, octave_display_a); + if (left_piano_pitch_and_vel.has_value() && m_audio_note_preview && + quantize_pitch(left_piano_pitch_and_vel.value().first, + m_edit_state.m_quantize_pitch) == pitch) { + int vel = left_piano_pitch_and_vel.value().second; + drawLeftPiano( + leftPianoPainter, this_y - floor_h / 2, h, + QColor::fromRgb(255, 255, 255, 32 + 64 * vel / EVENTMAX_VELOCITY), + nullptr); + } } { @@ -595,29 +628,31 @@ void KeyboardView::paintEvent(QPaintEvent *event) { alpha /= 2; } bool muted = !m_pxtn->Unit_Get(unit_no)->get_played(); - drawStateSegments.push_back([=, &min_segment_unit_no, - &min_segment_distance_to_mouse]( - const DrawState &state, int end) { - Interval segment{state.pitch.clock, end}; - const MouseEditState &mouse = m_client->editState().mouse_edit_state; - const Scale &scale = m_client->editState().scale; - - // Determine distance to mouse - if (m_select_unit_enabled && - std::holds_alternative(mouse.kind) && visible) { - Interval on = state.ongoingOnEvent.value(); - Interval interval = interval_intersect(on, segment); - double d = distance_to_mouse(mouse, interval, state.pitch.value, - displayEdo, scale); - if (d < min_segment_distance_to_mouse) { - min_segment_unit_no = unit_no; - min_segment_distance_to_mouse = d; - } - } - drawStateSegment(*thisPainter, state, segment, thisSelection, clockBounds, - brush, alpha, scale, clock, mouse, matchingUnit, muted, - width(), m_client->isPlaying(), -pos().x(), displayEdo); - }); + drawStateSegments.push_back( + [=, &min_segment_unit_no, &min_segment_distance_to_mouse, + &leftPianoPainter](const DrawState &state, int end) { + Interval segment{state.pitch.clock, end}; + const MouseEditState &mouse = m_client->editState().mouse_edit_state; + const Scale &scale = m_client->editState().scale; + + // Determine distance to mouse + if (m_select_unit_enabled && + std::holds_alternative(mouse.kind) && + visible) { + Interval on = state.ongoingOnEvent.value(); + Interval interval = interval_intersect(on, segment); + double d = distance_to_mouse(mouse, interval, state.pitch.value, + displayEdo, scale); + if (d < min_segment_distance_to_mouse) { + min_segment_unit_no = unit_no; + min_segment_distance_to_mouse = d; + } + } + drawStateSegment(*thisPainter, leftPianoPainter, state, segment, + thisSelection, clockBounds, brush, alpha, scale, + clock, mouse, matchingUnit, muted, width(), + m_client->isPlaying(), displayEdo); + }); }; for (const EVERECORD *e = m_pxtn->evels->get_Records(); e != nullptr; @@ -661,9 +696,6 @@ void KeyboardView::paintEvent(QPaintEvent *event) { painter.drawPixmap(event->rect(), activeLayer, activeLayer.rect()); painter.drawPixmap(event->rect(), hoverLayer, hoverLayer.rect()); - painter.drawPixmap(event->rect(), octaveDisplayLayer, - octaveDisplayLayer.rect()); - if (min_segment_distance_to_mouse < DISTANCE_THRESHOLD_SQ && min_segment_unit_no >= 0) setHoveredUnitNo(min_segment_unit_no); @@ -703,6 +735,18 @@ void KeyboardView::paintEvent(QPaintEvent *event) { displayEdo); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + drawLastSeek(painter, m_client, height(), false); + drawRepeatAndEndBars(painter, m_moo_clock, + m_client->editState().scale.clockPerPx, height()); + + painter.setOpacity(m_dark ? 0.7 : 1); + painter.drawPixmap(event->rect().translated(-LEFT_LEGEND_WIDTH, 0), + leftPianoLayer, leftPianoLayer.rect()); + painter.setOpacity(1); + + drawCurrentPlayerPosition(painter, m_moo_clock, height(), + m_client->editState().scale.clockPerPx, false); + // Draw cursors for (const auto &[uid, remote_state] : m_client->remoteEditStates()) { if (uid == m_client->following_uid() || uid == m_client->uid()) continue; @@ -729,12 +773,6 @@ void KeyboardView::paintEvent(QPaintEvent *event) { my_username, m_client->following_uid()); } - drawLastSeek(painter, m_client, height(), false); - drawCurrentPlayerPosition(painter, m_moo_clock, height(), - m_client->editState().scale.clockPerPx, false); - drawRepeatAndEndBars(painter, m_moo_clock, - m_client->editState().scale.clockPerPx, height()); - // Simulate activity on a client if (m_test_activity) { m_client->changeEditState( @@ -777,27 +815,37 @@ void KeyboardView::wheelEvent(QWheelEvent *event) { } } -static void updateStatePositions(EditState &edit_state, - const QMouseEvent *event) { +void KeyboardView::updateStatePositions(EditState &edit_state, + const QMouseEvent *event, int leftPos) { MouseEditState &state = edit_state.mouse_edit_state; + QPointF mouse_pos = worldTransform().inverted().map(event->localPos()); state.current_clock = - std::max(0., event->localPos().x() * edit_state.scale.clockPerPx); - qint32 current_pitch = edit_state.scale.pitchOfY(event->localPos().y()); - if (!std::holds_alternative(state.kind)) - state.kind.emplace( - MouseKeyboardEdit{current_pitch, current_pitch}); - auto &keyboard_edit_state = std::get(state.kind); - - keyboard_edit_state.current_pitch = current_pitch; + std::max(0., mouse_pos.x() * edit_state.scale.clockPerPx); + int current_pitch = edit_state.scale.pitchOfY(mouse_pos.y()); + int left_keyboard_current_vel = + int(EVENTMAX_VELOCITY - + EVENTMAX_VELOCITY * (leftPos - mouse_pos.x()) / LEFT_LEGEND_WIDTH); + std::variant kind; + + if (mouse_pos.x() < leftPos && state.type == MouseEditState::Type::Nothing) + kind = MouseLeftKeyboard{left_keyboard_current_vel}; + else + kind = MouseMainKeyboard{current_pitch}; if (state.type == MouseEditState::Type::Nothing || - state.type == MouseEditState::Type::Seek) { + state.type == MouseEditState::Type::Seek || + !std::holds_alternative(state.kind)) { state.type = (event->modifiers() & Qt::ShiftModifier ? MouseEditState::Type::Seek : MouseEditState::Type::Nothing); state.start_clock = state.current_clock; - keyboard_edit_state.start_pitch = current_pitch; + state.kind.emplace( + MouseKeyboardEdit{kind, current_pitch}); } + auto &keyboard_edit_state = std::get(state.kind); + keyboard_edit_state.current_pitch = current_pitch; + emit setScrollOnClick( + std::holds_alternative(keyboard_edit_state.kind)); } void KeyboardView::mousePressEvent(QMouseEvent *event) { @@ -820,7 +868,7 @@ void KeyboardView::mousePressEvent(QMouseEvent *event) { if (m_pxtn->Unit_Num() == 0) return; m_client->changeEditState( [&](EditState &s) { - updateStatePositions(s, event); + updateStatePositions(s, event, -pos().x()); bool make_note_preview = false; MouseEditState::Type type; @@ -858,8 +906,9 @@ void KeyboardView::mousePressEvent(QMouseEvent *event) { if (maybe_unit_no != std::nullopt && std::holds_alternative( s.mouse_edit_state.kind)) { - int pitch = std::get(s.mouse_edit_state.kind) - .current_pitch; + const auto &mouse_keyboard_state = + std::get(s.mouse_edit_state.kind); + int pitch = mouse_keyboard_state.current_pitch; pitch = quantize_pitch(pitch, m_edit_state.m_quantize_pitch); s.mouse_edit_state.last_pitch = pitch; @@ -870,6 +919,9 @@ void KeyboardView::mousePressEvent(QMouseEvent *event) { qint32 vel = m_pxtn->evels->get_Value(clock, unit_no, EVENTKIND_VELOCITY); s.mouse_edit_state.base_velocity = vel; + if (const auto *left_kb_state = + std::get_if(&mouse_keyboard_state.kind)) + vel = left_kb_state->start_vel; m_audio_note_preview = std::make_unique( m_pxtn, &m_client->moo()->params, unit_no, clock, pitch, vel, @@ -882,15 +934,33 @@ void KeyboardView::mousePressEvent(QMouseEvent *event) { } void KeyboardView::mouseMoveEvent(QMouseEvent *event) { - if (m_audio_note_preview != nullptr) - m_audio_note_preview->processEvent( - EVENTKIND_VELOCITY, - impliedVelocity(m_client->editState().mouse_edit_state, - m_client->editState().scale)); - if (!m_client->isFollowing()) { - m_client->changeEditState([&](auto &s) { updateStatePositions(s, event); }, - true); + m_client->changeEditState( + [&](auto &s) { updateStatePositions(s, event, -pos().x()); }, true); + } + if (m_audio_note_preview != nullptr) { + bool is_left_kb = false; + const auto *keyboard_edit_state = std::get_if( + &m_client->editState().mouse_edit_state.kind); + if (const auto *keyboard_edit_state = std::get_if( + &m_client->editState().mouse_edit_state.kind)) + is_left_kb = + std::holds_alternative(keyboard_edit_state->kind); + if (is_left_kb) { + int pitch = quantize_pitch(keyboard_edit_state->current_pitch, + m_edit_state.m_quantize_pitch); + if (pitch != m_last_left_kb_pitch) { + m_audio_note_preview->processEvent(EVENTKIND_KEY, pitch); + m_audio_note_preview->processEvent(EVENTKIND_PORTAMENT, 0); + m_audio_note_preview->resetOn(10000000); + m_last_left_kb_pitch = pitch; + } + } else { + m_audio_note_preview->processEvent( + EVENTKIND_VELOCITY, + impliedVelocity(m_client->editState().mouse_edit_state, + m_client->editState().scale)); + } } event->ignore(); } @@ -1027,8 +1097,15 @@ void KeyboardView::mouseReleaseEvent(QMouseEvent *event) { Interval clock_int(m_client->editState().mouse_edit_state.clock_int( m_client->quantizeClock())); - int start_pitch = quantize_pitch(keyboard_edit_state.start_pitch, - m_edit_state.m_quantize_pitch); + + // the optional int is a proxy for if we're actually modifying the main + // keyboard vs. previewing in the left kbd + std::optional start_pitch; + if (const auto *mouse_main_keyboard = + std::get_if(&keyboard_edit_state.kind)) { + start_pitch = quantize_pitch(mouse_main_keyboard->start_pitch, + m_edit_state.m_quantize_pitch); + } // int end_pitch = int(round(pitchOfY(event->localPos().y()))); m_client->changeEditState( @@ -1039,50 +1116,54 @@ void KeyboardView::mouseReleaseEvent(QMouseEvent *event) { switch (s.mouse_edit_state.type) { case MouseEditState::SetOn: case MouseEditState::DeleteOn: - actions.push_back({EVENTKIND_ON, - m_client->editState().m_current_unit_id, - clock_int.start, Delete{clock_int.end}}); - actions.push_back({EVENTKIND_VELOCITY, - m_client->editState().m_current_unit_id, - clock_int.start, Delete{clock_int.end}}); - actions.push_back({EVENTKIND_KEY, - m_client->editState().m_current_unit_id, - clock_int.start, Delete{clock_int.end}}); - if (m_client->editState().mouse_edit_state.type == - MouseEditState::SetOn) { + if (start_pitch.has_value()) { actions.push_back({EVENTKIND_ON, m_client->editState().m_current_unit_id, - clock_int.start, Add{clock_int.length()}}); - qint32 vel = - impliedVelocity(m_client->editState().mouse_edit_state, - m_client->editState().scale); - s.mouse_edit_state.base_velocity = vel; + clock_int.start, Delete{clock_int.end}}); actions.push_back({EVENTKIND_VELOCITY, m_client->editState().m_current_unit_id, - clock_int.start, Add{vel}}); + clock_int.start, Delete{clock_int.end}}); actions.push_back({EVENTKIND_KEY, m_client->editState().m_current_unit_id, - clock_int.start, Add{start_pitch}}); + clock_int.start, Delete{clock_int.end}}); + if (m_client->editState().mouse_edit_state.type == + MouseEditState::SetOn) { + actions.push_back({EVENTKIND_ON, + m_client->editState().m_current_unit_id, + clock_int.start, Add{clock_int.length()}}); + qint32 vel = + impliedVelocity(m_client->editState().mouse_edit_state, + m_client->editState().scale); + s.mouse_edit_state.base_velocity = vel; + actions.push_back({EVENTKIND_VELOCITY, + m_client->editState().m_current_unit_id, + clock_int.start, Add{vel}}); + actions.push_back( + {EVENTKIND_KEY, m_client->editState().m_current_unit_id, + clock_int.start, Add{start_pitch.value()}}); + } } break; case MouseEditState::SetNote: case MouseEditState::DeleteNote: - actions.push_back({EVENTKIND_KEY, - m_client->editState().m_current_unit_id, - clock_int.start, Delete{clock_int.end}}); - - if (m_client->editState().mouse_edit_state.type == - MouseEditState::SetNote) { - int current_pitch = - quantize_pitch(keyboard_edit_state.current_pitch, - m_edit_state.m_quantize_pitch); - const auto intervals = - vibratoIntervals(clock_int, m_client->quantizeClock(), - start_pitch, current_pitch); - for (const auto &[interval, pitch] : intervals) { - actions.push_back({EVENTKIND_KEY, - m_client->editState().m_current_unit_id, - interval.start, Add{pitch}}); + if (start_pitch.has_value()) { + actions.push_back({EVENTKIND_KEY, + m_client->editState().m_current_unit_id, + clock_int.start, Delete{clock_int.end}}); + + if (m_client->editState().mouse_edit_state.type == + MouseEditState::SetNote) { + int current_pitch = + quantize_pitch(keyboard_edit_state.current_pitch, + m_edit_state.m_quantize_pitch); + const auto intervals = + vibratoIntervals(clock_int, m_client->quantizeClock(), + start_pitch.value(), current_pitch); + for (const auto &[interval, pitch] : intervals) { + actions.push_back({EVENTKIND_KEY, + m_client->editState().m_current_unit_id, + interval.start, Add{pitch}}); + } } } break; @@ -1102,7 +1183,7 @@ void KeyboardView::mouseReleaseEvent(QMouseEvent *event) { } } s.mouse_edit_state.type = MouseEditState::Type::Nothing; - updateStatePositions(s, event); + updateStatePositions(s, event, -pos().x()); }, false); } @@ -1137,8 +1218,8 @@ void KeyboardView::quantizeSelectionX() { const std::set &unit_nos(m_client->selectedUnitNos()); LocalEditState localState(m_pxtn, m_client->editState()); int quantizeClock = localState.m_quantize_clock; - // Quantize with rounding, rather than flooring / ceiling is more useful for - // MIDI input. + // Quantize with rounding, rather than flooring / ceiling is more useful + // for MIDI input. auto quantize = [&](qint32 c) { return ((2 * c + quantizeClock) / (quantizeClock * 2)) * quantizeClock; }; @@ -1162,12 +1243,13 @@ void KeyboardView::quantizeSelectionX() { kindsToQuantize.find(kind) != kindsToQuantize.end()) { int unit_id = m_client->unitIdMap().noToId(e->unit_no); if (Evelist_Kind_IsTail(e->kind)) { - // We round the end time up if it's too small. Even though this might - // cause an add overlap with the next value, this should be okay in - // terms of interacting with undo, since the undos of both of these is - // 2 clears. (It takes some effort to explain) + // We round the end time up if it's too small. Even though this + // might cause an add overlap with the next value, this should be + // okay in terms of interacting with undo, since the undos of both + // of these is 2 clears. (It takes some effort to explain) - // 2021-10-13: However we don't let it go beyond the selection end. + // 2021-10-13: However we don't let it go beyond the selection + // end. int v = std::min(std::max(quantizeClock, quantize(e->value)), range.end - start_clock); actions.push_back({kind, unit_id, start_clock, Action::Add{v}}); diff --git a/src/editor/views/KeyboardView.h b/src/editor/views/KeyboardView.h index 80bbb421..ecf2d279 100644 --- a/src/editor/views/KeyboardView.h +++ b/src/editor/views/KeyboardView.h @@ -41,6 +41,7 @@ class KeyboardView : public QWidget { void ensureVisibleX(int x, bool strict); void fpsUpdated(qreal fps); void hoverUnitNoChanged(std::optional); + void setScrollOnClick(bool); public slots: void toggleTestActivity(); @@ -66,6 +67,7 @@ class KeyboardView : public QWidget { std::set selectedUnitNos(); void setHoveredUnitNo(std::optional); void updateHoverSelect(QMouseEvent *event); + void updateStatePositions(EditState &, const QMouseEvent *, int leftPos); QScrollArea *m_scrollarea; const pxtnService *m_pxtn; QElapsedTimer *m_timer; @@ -81,6 +83,7 @@ class KeyboardView : public QWidget { // m_select_unit_enabled should prob be folded into edit state. Right now it's // just a sore thumb of local state bool m_select_unit_enabled; + int m_last_left_kb_pitch; bool m_test_activity; }; diff --git a/src/editor/views/MeasureView.cpp b/src/editor/views/MeasureView.cpp index 06907589..229ec2f0 100644 --- a/src/editor/views/MeasureView.cpp +++ b/src/editor/views/MeasureView.cpp @@ -268,8 +268,8 @@ void drawOngoingAction(const EditState &state, } QSize MeasureView::sizeHint() const { - return QSize(one_over_last_clock(m_client->pxtn()) / - m_client->editState().scale.clockPerPx, + return QSize(LEFT_LEGEND_WIDTH + one_over_last_clock(m_client->pxtn()) / + m_client->editState().scale.clockPerPx, unit_edit_y(1 + m_client->editState().m_pinned_unit_ids.size())); } @@ -289,11 +289,15 @@ void MeasureView::setHoveredUnitNo(std::optional new_unit_no) { } } -void MeasureView::paintEvent(QPaintEvent *e) { +void MeasureView::paintEvent(QPaintEvent *raw_event) { const pxtnService *pxtn = m_client->pxtn(); QPainter painter(this); - painter.fillRect(0, 0, width(), height(), Qt::black); + painter.setTransform(worldTransform()); + QPaintEvent event(worldTransform().inverted().mapRect(raw_event->rect())); + QPaintEvent *e = &event; + + painter.fillRect(e->rect(), Qt::black); // Draw white lines under background // TODO: Dedup with keyboardview @@ -302,30 +306,36 @@ void MeasureView::paintEvent(QPaintEvent *e) { int activeMeas = std::max(master->get_last_meas(), master->get_meas_num()); qreal activeWidth = activeMeas * clockPerMeas / m_client->editState().scale.clockPerPx; - int lastMeasureDraw = -MEASURE_NUM_BLOCK_WIDTH - 1; + painter.fillRect(e->rect().left(), MEASURE_NUM_BLOCK_HEIGHT, + e->rect().width(), RULER_HEIGHT, + StyleEditor::config.color.MeasureExcluded); painter.fillRect(0, MEASURE_NUM_BLOCK_HEIGHT, activeWidth, RULER_HEIGHT, StyleEditor::config.color.MeasureIncluded); - painter.fillRect(activeWidth, MEASURE_NUM_BLOCK_HEIGHT, width() - activeWidth, - RULER_HEIGHT, StyleEditor::config.color.MeasureExcluded); - painter.fillRect(0, + painter.fillRect(e->rect().left(), MEASURE_NUM_BLOCK_HEIGHT + RULER_HEIGHT + SEPARATOR_OFFSET, - width(), 1, StyleEditor::config.color.MeasureBeat); - for (int beat = 0; true; ++beat) { + e->rect().width(), 1, StyleEditor::config.color.MeasureBeat); + int first_beat = e->rect().left() * m_client->editState().scale.clockPerPx / + master->get_beat_clock() - + 1; + std::optional lastMeasureDraw; + for (int beat = first_beat; true; ++beat) { int x = beat * master->get_beat_clock() / m_client->editState().scale.clockPerPx; - if (x > width()) break; + if (x > event.rect().right()) break; if (beat % master->get_beat_num() == 0) { int measure = beat / master->get_beat_num(); painter.fillRect(x, MEASURE_NUM_BLOCK_HEIGHT, 1, size().height(), StyleEditor::config.color.MeasureSeparator); - if (x - lastMeasureDraw < MEASURE_NUM_BLOCK_WIDTH) continue; + if (lastMeasureDraw.has_value() && + x - lastMeasureDraw.value() < MEASURE_NUM_BLOCK_WIDTH) + continue; lastMeasureDraw = x; painter.fillRect(x, 0, 1, MEASURE_NUM_BLOCK_HEIGHT, StyleEditor::config.color.MeasureSeparator); painter.fillRect(x + 1, 0, MEASURE_NUM_BLOCK_WIDTH, MEASURE_NUM_BLOCK_HEIGHT, StyleEditor::config.color.MeasureNumberBlock); - if (measure < activeMeas) + if (measure < activeMeas && measure >= 0) drawNumAlignTopRight(&painter, x + MEASURE_NUM_BLOCK_WIDTH, 1, beat / master->get_beat_num()); } else @@ -354,7 +364,8 @@ void MeasureView::paintEvent(QPaintEvent *e) { // Draw the unit edit rows for (uint i = 0; i < unit_draw_params_map.rows.size(); ++i) { - painter.fillRect(0, unit_edit_y(i), width(), UNIT_EDIT_HEIGHT, + painter.fillRect(e->rect().left(), unit_edit_y(i), e->rect().width(), + UNIT_EDIT_HEIGHT, StyleEditor::config.color.MeasureUnitEdit); if (!unit_draw_params_map.rows[i].pinned_unit_id.has_value()) continue; bool is_focused = @@ -372,8 +383,8 @@ void MeasureView::paintEvent(QPaintEvent *e) { m_client->pxtn()->Unit_Get(unit_no.value())->get_operated(); for (bool b : {is_hovered, is_selected, is_focused}) { if (b) - painter.fillRect(0, unit_edit_y(i), width(), UNIT_EDIT_HEIGHT, - QColor::fromRgb(255, 255, 255, 32)); + painter.fillRect(e->rect().left(), unit_edit_y(i), e->rect().width(), + UNIT_EDIT_HEIGHT, QColor::fromRgb(255, 255, 255, 32)); } } @@ -429,6 +440,9 @@ void MeasureView::paintEvent(QPaintEvent *e) { for (auto it = last_on_by_no.begin(); it != last_on_by_no.end(); ++it) drawLastOn(it->first, false); + drawLastSeek(painter, m_client, height(), true); + drawCurrentPlayerPosition(painter, m_moo_clock, height(), + m_client->editState().scale.clockPerPx, true); // Draw text labels if (Settings::PinnedUnitLabels::get()) { QPixmap textLabelLayer(e->rect().size()); @@ -446,12 +460,12 @@ void MeasureView::paintEvent(QPaintEvent *e) { constexpr int x_padding = 5; for (int x = -2; x <= 2; ++x) for (int y = -1; y <= 1; ++y) - p.drawText(-pos().x() + x_padding + x, y_base + y, 10000000, - UNIT_EDIT_HEIGHT, Qt::AlignVCenter, unit_name); + p.drawText(-pos().x() - LEFT_LEGEND_WIDTH + x_padding + x, y_base + y, + 10000000, UNIT_EDIT_HEIGHT, Qt::AlignVCenter, unit_name); p.setPen(Qt::white); QRect bbox; - p.drawText(-pos().x() + x_padding, y_base, 10000000, UNIT_EDIT_HEIGHT, - Qt::AlignVCenter, unit_name, &bbox); + p.drawText(-pos().x() - LEFT_LEGEND_WIDTH + x_padding, y_base, 10000000, + UNIT_EDIT_HEIGHT, Qt::AlignVCenter, unit_name, &bbox); if (bbox.width() > maxTextLabelWidth) maxTextLabelWidth = bbox.width(); }; @@ -480,9 +494,6 @@ void MeasureView::paintEvent(QPaintEvent *e) { painter.setOpacity(1); } - drawLastSeek(painter, m_client, height(), true); - drawCurrentPlayerPosition(painter, m_moo_clock, height(), - m_client->editState().scale.clockPerPx, true); for (const auto &[uid, remote_state] : m_client->remoteEditStates()) { if (uid == m_client->following_uid() || uid == m_client->uid()) continue; if (remote_state.state.has_value()) { @@ -549,9 +560,10 @@ static void updateStatePositions(EditState &edit_state, const UnitDrawParamsMap &unit_draw_params_map, const QMouseEvent *event) { MouseEditState &state = edit_state.mouse_edit_state; + QPointF mouse_pos = worldTransform().inverted().map(event->localPos()); state.current_clock = - std::max(0., event->localPos().x() * edit_state.scale.clockPerPx); + std::max(0., mouse_pos.x() * edit_state.scale.clockPerPx); switch (state.type) { case MouseEditState::Nothing: case MouseEditState::Seek: @@ -796,10 +808,11 @@ void MeasureView::wheelEvent(QWheelEvent *event) { } void MeasureView::mouseMoveEvent(QMouseEvent *event) { - m_mouse_x = event->localPos().x() + pos().x(); + QPointF mouse_pos = worldTransform().inverted().map(event->localPos()); + m_mouse_x = mouse_pos.x() + pos().x(); UnitDrawParamsMap draw_params_map = make_draw_params_map(m_client); - int hovered_row = (event->localPos().y() - UNIT_EDIT_Y) / UNIT_EDIT_INCREMENT; + int hovered_row = (mouse_pos.y() - UNIT_EDIT_Y) / UNIT_EDIT_INCREMENT; std::optional hovered_pinned_unit_no = std::nullopt; if (int(draw_params_map.rows.size()) > hovered_row && hovered_row >= 0) { auto unit_id = draw_params_map.rows[hovered_row].pinned_unit_id; diff --git a/src/editor/views/ParamView.cpp b/src/editor/views/ParamView.cpp index ebb86ce2..1c0f1ee3 100644 --- a/src/editor/views/ParamView.cpp +++ b/src/editor/views/ParamView.cpp @@ -62,8 +62,8 @@ ParamView::ParamView(PxtoneClient *client, MooClock *moo_clock, QWidget *parent) } QSize ParamView::sizeHint() const { - return QSize(one_over_last_clock(m_client->pxtn()) / - m_client->editState().scale.clockPerPx, + return QSize(LEFT_LEGEND_WIDTH + one_over_last_clock(m_client->pxtn()) / + m_client->editState().scale.clockPerPx, 0x20); } @@ -297,26 +297,33 @@ static void drawOngoingEdit(QPainter &painter, const MouseEditState &state, } break; } } -void ParamView::paintEvent(QPaintEvent *event) { +void ParamView::paintEvent(QPaintEvent *raw_event) { const pxtnService *pxtn = m_client->pxtn(); + QPainter painter(this); + painter.fillRect(raw_event->rect(), Qt::black); + painter.setTransform(worldTransform()); + QPaintEvent e(worldTransform().inverted().mapRect(raw_event->rect())); + QPaintEvent *event = &e; Interval clockBounds = { qint32(event->rect().left() * m_client->editState().scale.clockPerPx) - WINDOW_BOUND_SLACK, qint32(event->rect().right() * m_client->editState().scale.clockPerPx) + WINDOW_BOUND_SLACK}; - QPainter painter(this); - painter.fillRect(0, 0, size().width(), size().height(), Qt::black); // Draw white lines under background // TODO: Dedup with keyboardview QBrush beatBrush = StyleEditor::config.color.ParamBeat; QBrush measureBrush = StyleEditor::config.color.ParamMeasure; const pxtnMaster *master = pxtn->master; - for (int beat = 0; true; ++beat) { + int first_beat = (event->rect().left() - 10) * + m_client->editState().scale.clockPerPx / + master->get_beat_clock() - + 1; + for (int beat = first_beat; true; ++beat) { bool isMeasureLine = (beat % master->get_beat_num() == 0); int x = master->get_beat_clock() * beat / m_client->editState().scale.clockPerPx; - if (x > size().width()) break; + if (x > event->rect().right()) break; painter.fillRect(x, 0, 1, size().height(), (isMeasureLine ? measureBrush : beatBrush)); } @@ -325,7 +332,7 @@ void ParamView::paintEvent(QPaintEvent *event) { for (int i = 0; i < NUM_BACKGROUND_GAPS - 1; ++i) { int this_y = BACKGROUND_GAPS[i] * size().height() / 0x80; int next_y = BACKGROUND_GAPS[i + 1] * size().height() / 0x80; - painter.fillRect(0, this_y + 1, size().width(), + painter.fillRect(event->rect().left(), this_y + 1, event->rect().width(), std::max(1, next_y - this_y - 2), *GAP_COLORS[i]); } @@ -345,8 +352,10 @@ void ParamView::paintEvent(QPaintEvent *event) { colors.reserve(m_client->pxtn()->Unit_Num()); painters.reserve(m_client->pxtn()->Unit_Num()); lastEvents.reserve(m_client->pxtn()->Unit_Num()); + int first_clock = first_beat * master->get_beat_clock(); for (int i = 0; i < m_client->pxtn()->Unit_Num(); ++i) { - lastEvents.emplace_back(Event{-1000, DefaultKindValue(current_kind)}); + lastEvents.emplace_back( + Event{first_clock, DefaultKindValue(current_kind)}); int unit_id = m_client->unitIdMap().noToId(i); colors.push_back( brushes[nonnegative_modulo(unit_id, NUM_BRUSHES)].toQColor(108, false, @@ -471,11 +480,11 @@ static void updateStatePositions(EditState &edit_state, const QMouseEvent *event, EVENTKIND current_kind, int height) { MouseEditState &state = edit_state.mouse_edit_state; + QPointF mouse_pos = worldTransform().inverted().map(event->localPos()); state.current_clock = - std::max(0., event->localPos().x() * edit_state.scale.clockPerPx); + std::max(0., mouse_pos.x() * edit_state.scale.clockPerPx); bool snap = event->modifiers() & Qt::ControlModifier; - qint32 current_param = - paramOfY(event->localPos().y(), current_kind, height, snap); + qint32 current_param = paramOfY(mouse_pos.y(), current_kind, height, snap); if (!std::holds_alternative(state.kind)) state.kind = MouseParamEdit{current_param, current_param}; auto ¶m_edit_state = std::get(state.kind); diff --git a/src/editor/views/ViewHelper.cpp b/src/editor/views/ViewHelper.cpp index d201649d..248509c9 100644 --- a/src/editor/views/ViewHelper.cpp +++ b/src/editor/views/ViewHelper.cpp @@ -218,5 +218,9 @@ void drawOctaveNumAlignBottomLeft(QPainter *painter, int x, int y, int num, 6); } +const int LEFT_LEGEND_WIDTH = 40; +QTransform worldTransform() { + return QTransform().translate(LEFT_LEGEND_WIDTH, 0); +} + const int WINDOW_BOUND_SLACK = 32; -const int LEFT_LEGEND_WIDTH = 28; diff --git a/src/editor/views/ViewHelper.h b/src/editor/views/ViewHelper.h index 80fd8696..a2bd9d63 100644 --- a/src/editor/views/ViewHelper.h +++ b/src/editor/views/ViewHelper.h @@ -86,4 +86,5 @@ extern void drawNumAlignTopRight(QPainter *painter, int x, int y, int num); extern void drawOctaveNumAlignBottomLeft(QPainter *painter, int x, int y, int num, int height, bool a); extern const int LEFT_LEGEND_WIDTH; +extern QTransform worldTransform(); #endif // VIEWHELPER_H diff --git a/src/styles/ptCollage/config.ini b/src/styles/ptCollage/config.ini index de58146d..d7cce59a 100644 --- a/src/styles/ptCollage/config.ini +++ b/src/styles/ptCollage/config.ini @@ -50,8 +50,9 @@ Measure=#FFFFFF RootNote=#544C4C WhiteNote=#404040 BlackNote=#202020 -WhiteLeft=#80837E78 -BlackLeft=#804E4B61 +WhiteLeft=#837E78 +BlackLeft=#4E4B61 +BlackLeftInner=#1A1949 Black=#000000 [fonts]