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 "exampleview.h"
24 :
25 : #include <QMimeData>
26 :
27 : #include "draw/painter.h"
28 :
29 : #include "engraving/dom/engravingitem.h"
30 : #include "engraving/dom/page.h"
31 : #include "engraving/dom/score.h"
32 : #include "engraving/dom/system.h"
33 : #include "engraving/rendering/iscorerenderer.h"
34 :
35 : #include "notation/inotationcontextconfiguration.h"
36 :
37 : #include "log.h"
38 :
39 : using namespace mu;
40 : using namespace muse::draw;
41 : using namespace mu::notation;
42 : using namespace mu::engraving;
43 :
44 0 : ExampleView::ExampleView(QWidget* parent)
45 0 : : QFrame(parent)
46 : {
47 0 : m_score = nullptr;
48 0 : setFocusPolicy(Qt::StrongFocus);
49 0 : resetMatrix();
50 0 : m_backgroundPixmap = nullptr;
51 0 : m_backgroundColor = Qt::white;
52 :
53 0 : if (notationConfiguration()->foregroundUseColor()) {
54 0 : m_backgroundColor = notationConfiguration()->foregroundColor();
55 : } else {
56 0 : QString wallpaperPath = notationConfiguration()->foregroundWallpaperPath().toQString();
57 :
58 0 : m_backgroundPixmap = new QPixmap(wallpaperPath);
59 0 : if (m_backgroundPixmap == 0 || m_backgroundPixmap->isNull()) {
60 0 : LOGD("no valid pixmap %s", qPrintable(wallpaperPath));
61 : }
62 0 : }
63 0 : }
64 :
65 0 : ExampleView::~ExampleView()
66 : {
67 0 : if (m_backgroundPixmap) {
68 0 : delete m_backgroundPixmap;
69 : }
70 0 : }
71 :
72 0 : void ExampleView::resetMatrix()
73 : {
74 0 : qreal _spatium = score()->style().defaultSpatium() * m_defaultScaling;
75 : // example would normally be 10sp from top of page; this leaves 3sp margin above
76 0 : m_matrix = Transform(m_defaultScaling, 0.0, 0.0, m_defaultScaling, _spatium, -_spatium * 7.0);
77 0 : }
78 :
79 0 : void ExampleView::layoutChanged()
80 : {
81 0 : }
82 :
83 0 : void ExampleView::dataChanged(const RectF&)
84 : {
85 0 : }
86 :
87 0 : void ExampleView::updateAll()
88 : {
89 0 : update();
90 0 : }
91 :
92 0 : void ExampleView::adjustCanvasPosition(const EngravingItem* /*el*/, int)
93 : {
94 0 : }
95 :
96 0 : void ExampleView::setScore(Score* s)
97 : {
98 0 : delete m_score;
99 0 : m_score = s;
100 0 : m_score->addViewer(this);
101 0 : m_score->setLayoutMode(LayoutMode::LINE);
102 :
103 : //! HACK
104 0 : muse::ContextInject<INotationContextConfiguration> contextConfiguration = { s->iocContext() };
105 :
106 0 : m_defaultScaling = 0.9 * contextConfiguration()->notationScaling();
107 :
108 0 : ScoreLoad sl;
109 0 : m_score->doLayout();
110 0 : resetMatrix();
111 0 : updateGeometry(); // notify the widget's layout system that the sizeHint has changed
112 0 : update();
113 0 : }
114 :
115 0 : void ExampleView::removeScore()
116 : {
117 0 : }
118 :
119 0 : void ExampleView::changeEditElement(EngravingItem*)
120 : {
121 0 : }
122 :
123 0 : void ExampleView::drawBackground(Painter* p, const RectF& r) const
124 : {
125 0 : if (m_backgroundPixmap == 0 || m_backgroundPixmap->isNull()) {
126 0 : p->fillRect(r, m_backgroundColor);
127 : } else {
128 0 : p->drawTiledPixmap(r, *m_backgroundPixmap, r.topLeft() - PointF(m_matrix.dx(), m_matrix.dy()));
129 : }
130 0 : }
131 :
132 0 : void ExampleView::drawElements(Painter& painter, const std::vector<EngravingItem*>& el)
133 : {
134 0 : engraving::rendering::PaintOptions opt;
135 0 : opt.invertColors = notationConfiguration()->shouldInvertScore();
136 :
137 0 : for (EngravingItem* e : el) {
138 0 : e->itemDiscovered = 0;
139 0 : PointF pos(e->pagePos());
140 0 : painter.translate(pos);
141 0 : e->renderer()->drawItem(e, &painter, opt);
142 0 : painter.translate(-pos);
143 : }
144 0 : }
145 :
146 0 : void ExampleView::paintEvent(QPaintEvent* event)
147 : {
148 0 : QFrame::paintEvent(event);
149 :
150 0 : if (!m_score) {
151 0 : return;
152 : }
153 :
154 0 : Painter painter(this, "exampleview");
155 0 : painter.setAntialiasing(true);
156 0 : const RectF rect = RectF::fromQRectF(event->rect());
157 :
158 0 : drawBackground(&painter, rect);
159 :
160 0 : painter.setWorldTransform(m_matrix);
161 :
162 0 : Page* page = m_score->pages().front();
163 0 : std::vector<EngravingItem*> ell = page->items(m_matrix.inverted().map(rect));
164 0 : std::sort(ell.begin(), ell.end(), elementLessThan);
165 0 : drawElements(painter, ell);
166 0 : }
167 :
168 0 : void ExampleView::mousePressEvent(QMouseEvent* event)
169 : {
170 0 : if (event->button() == Qt::LeftButton) {
171 0 : m_isDragging = true;
172 0 : m_moveStartPoint = toLogical(event->position());
173 0 : setCursor(Qt::SizeAllCursor);
174 : }
175 0 : }
176 :
177 0 : PointF ExampleView::toLogical(const QPointF& point)
178 : {
179 0 : return m_matrix.inverted().map(PointF::fromQPointF(point));
180 : }
181 :
182 0 : void ExampleView::mouseMoveEvent(QMouseEvent* event)
183 : {
184 0 : if (m_isDragging) {
185 0 : dragExampleView(event);
186 : }
187 0 : }
188 :
189 0 : void ExampleView::mouseReleaseEvent(QMouseEvent* event)
190 : {
191 0 : if (event->button() == Qt::LeftButton && m_isDragging) {
192 0 : m_isDragging = false;
193 0 : setCursor(Qt::ArrowCursor);
194 : }
195 0 : }
196 :
197 0 : QSize ExampleView::sizeHint() const
198 : {
199 0 : qreal mag = m_defaultScaling;
200 0 : qreal _spatium = score()->style().defaultSpatium() * mag;
201 : // staff is 4sp tall with 3sp margin above; this leaves 3sp margin below
202 0 : qreal height = 10.0 * _spatium;
203 0 : if (score() && score()->pages().size() > 0) {
204 0 : height = score()->pages()[0]->tbbox().height() * mag + (6 * _spatium);
205 : }
206 0 : return QSize(1000 * mag, height);
207 : }
208 :
209 : //---------------------------------------------------------
210 : // dragExampleView
211 : // constrained scrolling ensuring that this ExampleView won't be moved past the borders of its QFrame
212 : //---------------------------------------------------------
213 :
214 0 : void ExampleView::dragExampleView(QMouseEvent* event)
215 : {
216 0 : PointF delta = PointF::fromQPointF(event->position()) - m_matrix.map(m_moveStartPoint);
217 0 : int dx = delta.x();
218 0 : if (dx == 0) {
219 0 : return;
220 : }
221 :
222 0 : constraintCanvas(&dx);
223 :
224 : // Perform the actual scrolling
225 0 : m_matrix.translate(dx, 0);
226 0 : scroll(dx, 0);
227 : }
228 :
229 0 : void ExampleView::wheelEvent(QWheelEvent* event)
230 : {
231 0 : QPoint pixelsScrolled = event->pixelDelta();
232 0 : QPoint stepsScrolled = event->angleDelta();
233 0 : int dx = 0, dy = 0;
234 0 : if (!pixelsScrolled.isNull()) {
235 0 : dx = pixelsScrolled.x();
236 0 : dy = pixelsScrolled.y();
237 0 : } else if (!stepsScrolled.isNull()) {
238 0 : dx = static_cast<qreal>(stepsScrolled.x()) * qMax(2, width() / 10) / 120;
239 0 : dy = static_cast<qreal>(stepsScrolled.y()) * qMax(2, height() / 10) / 120;
240 : }
241 :
242 0 : if (dx == 0) {
243 0 : if (dy == 0) {
244 0 : return;
245 : } else {
246 0 : dx = dy;
247 : }
248 : }
249 :
250 0 : constraintCanvas(&dx);
251 :
252 0 : m_matrix.translate(dx, 0);
253 0 : scroll(dx, 0);
254 : }
255 :
256 : //-----------------------------------------------------------------------------
257 : // constraintCanvas
258 : //-----------------------------------------------------------------------------
259 :
260 0 : void ExampleView::constraintCanvas(int* dxx)
261 : {
262 0 : int dx = *dxx;
263 :
264 0 : Q_ASSERT(m_score->pages().front()->system(0)); // should exist if doLayout ran
265 :
266 : // form rectangle bounding the system with a spatium margin and translate relative to view space
267 0 : qreal xstart = m_score->pages().front()->system(0)->ldata()->bbox().left() - score()->style().defaultSpatium();
268 0 : qreal xend = m_score->pages().front()->system(0)->ldata()->bbox().right() + 2.0 * score()->style().defaultSpatium();
269 0 : QRectF systemScaledViewRect(xstart * m_matrix.m11(), 0, xend * m_matrix.m11(), 0);
270 0 : systemScaledViewRect.translate(m_matrix.dx(), 0);
271 :
272 0 : qreal frameWidth = static_cast<QFrame*>(this)->frameRect().width();
273 :
274 : // constrain the dx of scrolling so that this ExampleView won't be moved past the borders of its QFrame
275 0 : if (dx > 0) {
276 : // when moving right, ensure the left edge of systemScaledViewRect won't be right of frame's left edge
277 0 : if (systemScaledViewRect.left() + dx > 0) {
278 0 : dx = -systemScaledViewRect.left();
279 : }
280 : } else {
281 : // never move left if entire system already fits entirely within the frame
282 0 : if (systemScaledViewRect.width() < frameWidth) {
283 0 : dx = 0;
284 : }
285 : // when moving left, ensure the right edge of systemScaledViewRect won't be left of frame's right edge
286 0 : else if (systemScaledViewRect.right() + dx < frameWidth) {
287 0 : dx = frameWidth - systemScaledViewRect.right();
288 : }
289 : }
290 :
291 0 : *dxx = dx;
292 0 : }
|