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) 2025 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 : #include "editmoderenderer.h"
23 :
24 : #include "draw/painter.h"
25 :
26 : #include "iengravingconfiguration.h" // IWYU pragma: keep
27 : #include "dom/engravingitem.h"
28 : #include "dom/barline.h"
29 : #include "dom/dynamic.h"
30 : #include "dom/slurtie.h"
31 : #include "dom/textbase.h"
32 : #include "rendering/score/tdraw.h"
33 :
34 : using namespace mu::engraving::rendering::editmode;
35 : using namespace mu::engraving;
36 : using namespace muse::draw;
37 :
38 0 : void EditModeRenderer::drawItem(const EngravingItem* item, muse::draw::Painter* painter, const EditData& ed, double currentViewScaling,
39 : const PaintOptions& opt)
40 : {
41 0 : switch (item->type()) {
42 0 : case ElementType::BAR_LINE:
43 0 : drawBarline(item_cast<const BarLine*>(item), painter, ed, currentViewScaling, opt);
44 0 : break;
45 0 : case ElementType::CAPO:
46 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
47 0 : break;
48 0 : case ElementType::DYNAMIC:
49 0 : drawDynamic(item_cast<const Dynamic*>(item), painter, ed, currentViewScaling, opt);
50 0 : break;
51 0 : case ElementType::EXPRESSION:
52 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
53 0 : break;
54 0 : case ElementType::FIGURED_BASS:
55 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
56 0 : break;
57 0 : case ElementType::FINGERING:
58 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
59 0 : break;
60 0 : case ElementType::GUITAR_BEND_TEXT:
61 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
62 0 : break;
63 0 : case ElementType::HARMONY:
64 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
65 0 : break;
66 0 : case ElementType::HARP_DIAGRAM:
67 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
68 0 : break;
69 0 : case ElementType::INSTRUMENT_CHANGE:
70 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
71 0 : break;
72 0 : case ElementType::INSTRUMENT_NAME:
73 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
74 0 : break;
75 0 : case ElementType::JUMP:
76 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
77 0 : break;
78 0 : case ElementType::LAISSEZ_VIB_SEGMENT:
79 0 : drawSlurTieSegment(item_cast<const SlurTieSegment*>(item), painter, ed, currentViewScaling, opt);
80 0 : break;
81 0 : case ElementType::LYRICS:
82 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
83 0 : break;
84 0 : case ElementType::MMREST_RANGE:
85 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
86 0 : break;
87 0 : case ElementType::MARKER:
88 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
89 0 : break;
90 0 : case ElementType::MEASURE_NUMBER:
91 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
92 0 : break;
93 0 : case ElementType::PARTIAL_TIE_SEGMENT:
94 0 : drawSlurTieSegment(item_cast<const SlurTieSegment*>(item), painter, ed, currentViewScaling, opt);
95 0 : break;
96 0 : case ElementType::PLAY_COUNT_TEXT:
97 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
98 0 : break;
99 0 : case ElementType::PLAYTECH_ANNOTATION:
100 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
101 0 : break;
102 0 : case ElementType::REHEARSAL_MARK:
103 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
104 0 : break;
105 0 : case ElementType::SLUR_SEGMENT:
106 : case ElementType::TIE_SEGMENT:
107 : case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
108 : case ElementType::TAPPING_HALF_SLUR_SEGMENT:
109 0 : drawSlurTieSegment(item_cast<const SlurTieSegment*>(item), painter, ed, currentViewScaling, opt);
110 0 : break;
111 0 : case ElementType::STAFF_TEXT:
112 : case ElementType::STAVE_SHARING_LABEL:
113 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
114 0 : break;
115 0 : case ElementType::STICKING:
116 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
117 0 : break;
118 0 : case ElementType::STRING_TUNINGS:
119 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
120 0 : break;
121 0 : case ElementType::SYSTEM_TEXT:
122 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
123 0 : break;
124 0 : case ElementType::TEMPO_TEXT:
125 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
126 0 : break;
127 0 : case ElementType::TEXT:
128 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
129 0 : break;
130 0 : case ElementType::TRIPLET_FEEL:
131 0 : drawTextBase(item_cast<const TextBase*>(item), painter, currentViewScaling, opt);
132 0 : break;
133 0 : default:
134 0 : drawEngravingItem(item, painter, ed, currentViewScaling, opt);
135 : }
136 0 : }
137 :
138 0 : void EditModeRenderer::drawEngravingItem(const EngravingItem* item, muse::draw::Painter* painter, const EditData& ed,
139 : double currentViewScaling, const PaintOptions& opt)
140 : {
141 : UNUSED(currentViewScaling);
142 :
143 0 : Pen pen(item->configuration()->displayedDefaultColor(opt.invertColors), 0.0);
144 0 : painter->setPen(pen);
145 0 : for (int i = 0; i < ed.grips; ++i) {
146 0 : if (Grip(i) == ed.curGrip) {
147 0 : painter->setBrush(item->configuration()->scoreGreyColor());
148 : } else {
149 0 : painter->setBrush(BrushStyle::NoBrush);
150 : }
151 0 : painter->drawRect(ed.grip[i]);
152 : }
153 0 : }
154 :
155 0 : void EditModeRenderer::drawBarline(const BarLine* item, muse::draw::Painter* painter, const EditData& ed, double currentViewScaling,
156 : const PaintOptions& opt)
157 : {
158 0 : drawEngravingItem(item, painter, ed, currentViewScaling, opt);
159 :
160 0 : const BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(item).get());
161 0 : BarLine::LayoutData* ldata = const_cast<BarLine::LayoutData*>(item->ldata());
162 0 : ldata->y1 += bed->yoff1;
163 0 : ldata->y2 += bed->yoff2;
164 0 : PointF pos(item->canvasPos());
165 0 : painter->translate(pos);
166 :
167 0 : score::TDraw::drawItem(item, painter, opt);
168 :
169 0 : ldata->y1 -= bed->yoff1;
170 0 : ldata->y2 -= bed->yoff2;
171 0 : painter->translate(-pos);
172 0 : }
173 :
174 0 : void EditModeRenderer::drawDynamic(const Dynamic* item, muse::draw::Painter* painter, const EditData& ed, double currentViewScaling,
175 : const PaintOptions& opt)
176 : {
177 0 : if (item->cursor() && item->cursor()->editing()) {
178 0 : drawTextBase(item, painter, currentViewScaling, opt);
179 0 : return;
180 : }
181 :
182 0 : drawEngravingItem(item, painter, ed, currentViewScaling, opt);
183 : }
184 :
185 0 : void EditModeRenderer::drawSlurTieSegment(const SlurTieSegment* item, muse::draw::Painter* painter, const EditData& ed,
186 : double currentViewScaling, const PaintOptions& opt)
187 : {
188 0 : PolygonF polygon(7);
189 0 : polygon[0] = PointF(ed.grip[int(Grip::START)].center());
190 0 : polygon[1] = PointF(ed.grip[int(Grip::BEZIER1)].center());
191 0 : polygon[2] = PointF(ed.grip[int(Grip::SHOULDER)].center());
192 0 : polygon[3] = PointF(ed.grip[int(Grip::BEZIER2)].center());
193 0 : polygon[4] = PointF(ed.grip[int(Grip::END)].center());
194 0 : polygon[5] = PointF(ed.grip[int(Grip::DRAG)].center());
195 0 : polygon[6] = PointF(ed.grip[int(Grip::START)].center());
196 0 : painter->setPen(Pen(item->configuration()->scoreGreyColor(), 0.0));
197 0 : painter->drawPolyline(polygon);
198 :
199 0 : drawEngravingItem(item, painter, ed, currentViewScaling, opt);
200 0 : }
201 :
202 0 : static void drawTextBaseSelection(const TextBase* item, muse::draw::Painter* painter, const RectF& r)
203 : {
204 0 : painter->save();
205 0 : Brush bg(item->configuration()->selectionColor());
206 0 : painter->setCompositionMode(CompositionMode::HardLight);
207 0 : painter->setBrush(bg);
208 0 : painter->setNoPen();
209 0 : painter->drawRect(r);
210 0 : painter->restore();
211 0 : }
212 :
213 0 : void EditModeRenderer::drawTextBase(const TextBase* item, muse::draw::Painter* painter, double currentViewScaling,
214 : const PaintOptions& opt)
215 : {
216 0 : PointF pos(item->canvasPos());
217 0 : painter->translate(pos);
218 :
219 0 : const TextCursor* cursor = item->cursor();
220 0 : const TextBase::LayoutData* ldata = item->ldata();
221 0 : IF_ASSERT_FAILED(ldata) {
222 0 : return;
223 : }
224 :
225 0 : if (cursor->hasSelection()) {
226 0 : painter->setBrush(BrushStyle::NoBrush);
227 0 : painter->setPen(item->textColor(opt));
228 0 : size_t r1 = cursor->selectLine();
229 0 : size_t r2 = cursor->row();
230 0 : size_t c1 = cursor->selectColumn();
231 0 : size_t c2 = cursor->column();
232 :
233 0 : TextBase::sort(r1, c1, r2, c2);
234 0 : size_t row = 0;
235 0 : for (const TextBlock& t : ldata->blocks) {
236 0 : if (row >= r1 && row <= r2) {
237 0 : RectF br;
238 0 : if (row == r1 && r1 == r2) {
239 0 : br = t.boundingRect(static_cast<int>(c1), static_cast<int>(c2), item);
240 0 : } else if (row == r1) {
241 0 : br = t.boundingRect(static_cast<int>(c1), static_cast<int>(t.columns()), item);
242 0 : } else if (row == r2) {
243 0 : br = t.boundingRect(0, static_cast<int>(c2), item);
244 : } else {
245 0 : br = t.boundingRect();
246 : }
247 0 : br.translate(0.0, t.y());
248 0 : drawTextBaseSelection(item, painter, br);
249 : }
250 0 : ++row;
251 : }
252 : }
253 0 : painter->setBrush(item->curColor(opt));
254 0 : Pen pen(item->curColor(opt));
255 0 : pen.setJoinStyle(PenJoinStyle::MiterJoin);
256 0 : painter->setPen(pen);
257 :
258 0 : if (cursor->visible()) {
259 0 : painter->drawRect(cursor->cursorRect());
260 : }
261 :
262 0 : painter->translate(-pos);
263 0 : painter->setPen(Pen(item->configuration()->frameColor(), 2.0 / currentViewScaling)); // 2 pixel pen size
264 0 : painter->setBrush(BrushStyle::NoBrush);
265 :
266 0 : double m = item->spatium();
267 0 : RectF r = item->canvasBoundingRect().adjusted(-m, -m, m, m);
268 :
269 0 : painter->drawRect(r);
270 0 : }
271 :
272 0 : void EditModeRenderer::draw(const TextBlock& textBlock, const TextBase* item, muse::draw::Painter* painter)
273 : {
274 0 : painter->translate(0.0, textBlock.y());
275 0 : for (const TextFragment& f : textBlock.fragments()) {
276 0 : draw(f, item, painter);
277 : }
278 0 : painter->translate(0.0, -textBlock.y());
279 0 : }
280 :
281 0 : void EditModeRenderer::draw(const TextFragment& textFragment, const TextBase* item, muse::draw::Painter* painter)
282 : {
283 0 : painter->setFont(textFragment.font(item));
284 0 : painter->drawText(textFragment.pos, textFragment.text);
285 0 : }
|