Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "timelineview.h"
24 :
25 : #include <QApplication>
26 : #include <QQuickWindow>
27 : #include <QSplitter>
28 : #include <QTimer>
29 :
30 : #include "notation/inotationinteraction.h" // IWYU pragma: keep
31 : #include "notation/inotationundostack.h" // IWYU pragma: keep
32 :
33 : #include "widgets/timeline.h"
34 :
35 : namespace mu::notation {
36 : class TimelineAdapter : public QSplitter, public muse::uicomponents::IDisplayableWidget
37 : {
38 : public:
39 0 : TimelineAdapter(const muse::modularity::ContextPtr& iocCtx)
40 0 : : QSplitter(nullptr)
41 : {
42 0 : setFocusPolicy(Qt::NoFocus);
43 0 : setHandleWidth(0);
44 0 : setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
45 0 : setObjectName("TimelineAdapter");
46 :
47 0 : m_msTimeline = new Timeline(this, iocCtx);
48 0 : }
49 :
50 0 : void updateView()
51 : {
52 0 : m_msTimeline->updateGridFromCmdState();
53 0 : }
54 :
55 0 : void setNotation(INotationPtr notation)
56 : {
57 0 : m_msTimeline->setNotation(notation);
58 0 : }
59 :
60 : private:
61 0 : QWidget* qWidget() override
62 : {
63 0 : return this;
64 : }
65 :
66 0 : bool handleEvent(QEvent* e) override
67 : {
68 0 : if (QMouseEvent* me = dynamic_cast<QMouseEvent*>(e)) {
69 0 : return handleMouseEvent(me);
70 : }
71 :
72 0 : return m_msTimeline->handleEvent(e);
73 : }
74 :
75 0 : bool handleMouseEvent(QMouseEvent* event)
76 : {
77 0 : QPoint pos = event ? event->pos() : QPoint();
78 :
79 0 : if (event->type() == QEvent::MouseButtonPress) {
80 0 : m_mouseDownWidget = childAt(pos);
81 0 : } else if (event->type() == QEvent::MouseButtonRelease) {
82 0 : m_mouseDownWidget = nullptr;
83 : }
84 :
85 0 : bool result = false;
86 :
87 0 : if (QWidget* receiver = m_mouseDownWidget ? m_mouseDownWidget : childAt(pos)) {
88 0 : QMouseEvent mappedEvent(event->type(), receiver->mapFrom(this, event->position()),
89 0 : event->scenePosition(), event->globalPosition(),
90 : event->button(), event->buttons(), event->modifiers(),
91 0 : event->source(), event->pointingDevice());
92 0 : result = qApp->notify(receiver, &mappedEvent);
93 0 : event->setAccepted(mappedEvent.isAccepted());
94 0 : }
95 :
96 0 : return result;
97 : }
98 :
99 : Timeline* m_msTimeline = nullptr;
100 : QWidget* m_mouseDownWidget = nullptr;
101 : };
102 : }
103 :
104 : using namespace mu::notation;
105 :
106 0 : TimelineView::TimelineView(QQuickItem* parent)
107 0 : : WidgetView(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
108 : {
109 0 : m_drawTimer.setSingleShot(true);
110 0 : m_drawTimer.setInterval(32); // 30 fps
111 :
112 0 : connect(&m_drawTimer, &QTimer::timeout, this, &TimelineView::doDraw);
113 :
114 0 : doDraw();
115 0 : }
116 :
117 0 : void TimelineView::doDraw()
118 : {
119 0 : if (isVisible()) {
120 0 : m_dpr = window() ? window()->devicePixelRatio() : 1.0;
121 :
122 0 : m_image = QImage(width() * m_dpr, height() * m_dpr, QImage::Format_ARGB32_Premultiplied);
123 0 : m_image.setDevicePixelRatio(m_dpr);
124 0 : m_image.fill(Qt::transparent);
125 :
126 0 : if (qWidget()) {
127 0 : qWidget()->render(&m_image, QPoint(), QRegion(), QWidget::DrawWindowBackground | QWidget::DrawChildren);
128 : }
129 :
130 0 : update();
131 : }
132 :
133 0 : m_drawTimer.start();
134 0 : }
135 :
136 0 : void TimelineView::paint(QPainter* painter)
137 : {
138 0 : painter->drawImage(0, 0, m_image);
139 0 : }
140 :
141 0 : void TimelineView::componentComplete()
142 : {
143 0 : WidgetView::componentComplete();
144 :
145 0 : auto timeline = std::make_shared<TimelineAdapter>(iocContext());
146 :
147 0 : auto updateView = [this, timeline]() {
148 0 : update();
149 :
150 0 : if (timeline) {
151 0 : timeline->updateView();
152 : }
153 0 : };
154 :
155 0 : auto initTimeline = [this, updateView, timeline] {
156 0 : INotationPtr notation = projectSession()->currentNotation();
157 0 : timeline->setNotation(notation);
158 :
159 0 : updateView();
160 :
161 0 : if (!notation) {
162 0 : return;
163 : }
164 :
165 0 : notation->undoStack()->stackChanged().onNotify(this, [=] {
166 0 : updateView();
167 0 : }, Asyncable::Mode::SetReplace /* FIXME */);
168 :
169 0 : notation->interaction()->selectionChanged().onNotify(this, [=] {
170 0 : updateView();
171 0 : }, Asyncable::Mode::SetReplace /* FIXME */);
172 0 : };
173 :
174 0 : projectSession()->currentNotationChanged().onNotify(this, [initTimeline]() {
175 0 : initTimeline();
176 0 : });
177 :
178 0 : setWidget(timeline);
179 :
180 0 : initTimeline();
181 0 : }
|