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) 2023 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 "measurenumberlayout.h"
24 : #include "measurelayout.h"
25 : #include "tlayout.h"
26 : #include "textlayout.h"
27 :
28 : #include "dom/measure.h"
29 : #include "dom/score.h"
30 : #include "dom/segment.h"
31 : #include "dom/staff.h"
32 : #include "dom/system.h"
33 :
34 : using namespace mu::engraving;
35 : using namespace mu::engraving::rendering::score;
36 :
37 3851 : void MeasureNumberLayout::layoutMeasureNumber(MeasureNumber* item, MeasureNumber::LayoutData* ldata, const LayoutContext& ctx)
38 : {
39 3851 : MeasureNumberPlacement placementMode = ctx.conf().styleV(Sid::measureNumberPlacementMode).value<MeasureNumberPlacement>();
40 3851 : if (placementMode != MeasureNumberPlacement::ON_ALL_STAVES) {
41 3851 : item->setPlacement(PlacementV::ABOVE);
42 : }
43 :
44 3851 : layoutMeasureNumberBase(item, ldata);
45 :
46 3851 : const RectF& itemBBox = ldata->bbox();
47 :
48 3851 : bool alignToBarline = ctx.conf().styleB(Sid::measureNumberAlignToBarline);
49 3851 : AlignH hPlacement = ctx.conf().styleV(Sid::measureNumberHPlacement).value<AlignH>();
50 3851 : const Segment* barlineSeg = refBarlineSegment(item, alignToBarline, hPlacement);
51 3851 : const BarLine* refBarline = barlineSeg ? toBarLine(barlineSeg->element(item->track())) : nullptr;
52 :
53 3851 : const Measure* measure = item->measure();
54 :
55 3851 : if (alignToBarline) {
56 3851 : if (refBarline) {
57 1525 : double xRef = refBarline->pageX() - measure->pageX();
58 1525 : xRef += (hPlacement == AlignH::RIGHT ? 1.0 : hPlacement == AlignH::HCENTER ? 0.5 : 0.0) * refBarline->width();
59 1525 : ldata->setPosX(xRef);
60 : } else {
61 2326 : ldata->setPosX(0.0);
62 : }
63 : } else {
64 0 : Segment* firstCRSeg = measure->first(SegmentType::ChordRest);
65 : MeasureLayout::MeasureStartEndPos measureStartEndPos
66 0 : = MeasureLayout::getMeasureStartEndPos(measure, firstCRSeg, item->staffIdx(), false, false, ctx);
67 :
68 0 : if (hPlacement == AlignH::LEFT) {
69 0 : if (measure->header()) {
70 0 : ldata->setPosX(measure->firstNoteRestSegmentX(true) - measure->systemPos().x());
71 : } else {
72 0 : double xRef = refBarline ? refBarline->pageX() - measure->pageX() : 0.0;
73 0 : ldata->setPosX(xRef);
74 : }
75 0 : } else if (hPlacement == AlignH::HCENTER) {
76 0 : ldata->setPosX(0.5 * (measureStartEndPos.x1 + measureStartEndPos.x2));
77 : } else {
78 0 : if (refBarline) {
79 0 : double xRef = refBarline->pageX() - measure->pageX() + refBarline->width();
80 0 : ldata->setPosX(xRef);
81 : } else {
82 0 : ldata->setPosX(measureStartEndPos.x2);
83 : }
84 : }
85 : }
86 :
87 3851 : if (hPlacement == AlignH::LEFT) {
88 2327 : ldata->moveX(-itemBBox.left());
89 1524 : } else if (hPlacement == AlignH::HCENTER) {
90 1524 : ldata->moveX(-0.5 * (itemBBox.right() + itemBBox.left()));
91 : } else {
92 0 : ldata->moveX(-itemBBox.right());
93 : }
94 :
95 3851 : Staff* staff = item->staff();
96 3851 : PointF defaultPos = item->defaultPos() * (staff ? staff->staffMag(item->tick()) : 1.0);
97 :
98 3851 : ldata->move(defaultPos);
99 :
100 3851 : checkBarlineCollisions(item, barlineSeg, hPlacement, ldata);
101 3851 : }
102 :
103 0 : void MeasureNumberLayout::layoutMMRestRange(MMRestRange* item, MMRestRange::LayoutData* ldata, const LayoutContext& ctx)
104 : {
105 0 : layoutMeasureNumberBase(item, ldata);
106 :
107 0 : const Measure* measure = item->measure();
108 0 : const Segment* crSeg = measure->first(SegmentType::ChordRest);
109 : const MeasureLayout::MeasureStartEndPos measureStartEnd
110 0 : = MeasureLayout::getMeasureStartEndPos(measure, crSeg, item->staffIdx(), true, false, ctx);
111 :
112 0 : const RectF& itemBBox = ldata->bbox();
113 0 : AlignH hPlacement = ctx.conf().styleV(Sid::mmRestRangeHPlacement).value<AlignH>();
114 0 : if (hPlacement == AlignH::HCENTER) {
115 0 : const double x1 = measureStartEnd.x1;
116 0 : const double x2 = measureStartEnd.x2;
117 0 : ldata->setPosX(0.5 * (x1 + x2) - 0.5 * (itemBBox.right() + itemBBox.left()));
118 0 : } else if (hPlacement == AlignH::RIGHT) {
119 0 : ldata->setPosX(item->measure()->ldata()->bbox().width() - itemBBox.right());
120 : } else {
121 0 : ldata->setPosX(measureStartEnd.x1 - itemBBox.left());
122 : }
123 :
124 0 : Staff* staff = item->staff();
125 0 : PointF defaultPos = item->defaultPos() * (staff ? staff->staffMag(item->tick()) : 1.0);
126 :
127 0 : ldata->move(defaultPos);
128 0 : }
129 :
130 3851 : void MeasureNumberLayout::layoutMeasureNumberBase(MeasureNumberBase* item, MeasureNumberBase::LayoutData* ldata)
131 : {
132 3851 : ldata->setPos(PointF());
133 :
134 3851 : TextLayout::layoutBaseTextBase1(item, ldata);
135 :
136 3851 : staff_idx_t effectiveStaffIdx = item->effectiveStaffIdx();
137 3851 : Staff* staff = item->score()->staff(effectiveStaffIdx);
138 :
139 3851 : if (item->placeBelow()) {
140 0 : double yoff = ldata->bbox().height();
141 :
142 : // If there is only one line, the barline spans outside the staff lines, so the default position is not correct.
143 0 : if (staff && staff->lines(item->tick()) == 1) {
144 0 : yoff += 2.0 * item->spatium();
145 : } else {
146 0 : yoff += item->staff() ? item->staff()->staffHeight() : 0.0;
147 : }
148 :
149 0 : ldata->setPosY(yoff);
150 : } else {
151 3851 : double yoff = 0.0;
152 :
153 : // If there is only one line, the barline spans outside the staff lines, so the default position is not correct.
154 3851 : if (staff && staff->lines(item->tick()) == 1) {
155 3 : yoff -= 2.0 * item->spatium();
156 : }
157 :
158 3851 : ldata->setPosY(yoff);
159 : }
160 3851 : }
161 :
162 3851 : const Segment* MeasureNumberLayout::refBarlineSegment(const MeasureNumber* item, bool alignToBarline, AlignH hPlacement)
163 : {
164 3851 : Segment* barlineSeg = nullptr;
165 3851 : Measure* measure = item->measure();
166 3851 : if (alignToBarline || hPlacement == AlignH::LEFT) {
167 6559 : for (Segment* seg = measure->first(); seg && seg->tick() == measure->tick() && seg->system() == measure->system();
168 2708 : seg = seg->prev1MMenabled()) {
169 4233 : if (seg->isType(SegmentType::BarLineTypes) && seg->isActive()) {
170 1525 : barlineSeg = seg;
171 1525 : break;
172 : }
173 : }
174 3851 : } else {
175 0 : for (Segment* seg = measure->first(SegmentType::ChordRest);
176 0 : seg && seg->tick() <= measure->endTick() && seg->system() == measure->system(); seg = seg->next1enabled()) {
177 0 : if (seg->isType(SegmentType::BarLineTypes) && seg->isActive()) {
178 0 : barlineSeg = seg;
179 0 : break;
180 : }
181 : }
182 : }
183 :
184 3851 : return barlineSeg;
185 : }
186 :
187 3851 : void MeasureNumberLayout::checkBarlineCollisions(const MeasureNumber* item, const Segment* barlineSeg, AlignH hPlacement,
188 : MeasureNumber::LayoutData* ldata)
189 : {
190 3851 : if (hPlacement == AlignH::HCENTER || !item->autoplace()) {
191 1524 : return;
192 : }
193 :
194 2327 : const RectF& itemBBox = ldata->bbox();
195 2327 : staff_idx_t staffIdx = item->staffIdx();
196 2327 : staff_idx_t barlineStaff = muse::nidx;
197 2327 : System* system = item->measure()->system();
198 2327 : if (item->getProperty(Pid::PLACEMENT).value<PlacementV>() == PlacementV::ABOVE) {
199 2327 : barlineStaff = system->prevVisibleStaff(staffIdx);
200 : } else {
201 0 : barlineStaff = staffIdx;
202 : }
203 2327 : if (barlineStaff == muse::nidx) {
204 2327 : return;
205 : }
206 :
207 0 : const double minBarLineDistance = 0.25 * item->spatium();
208 0 : if (!barlineSeg || (barlineSeg->segmentType() != SegmentType::BeginBarLine && !item->score()->staff(barlineStaff)->barLineSpan())) {
209 0 : return;
210 : }
211 :
212 0 : BarLine* barline = toBarLine(barlineSeg->element(staff2track(barlineStaff)));
213 0 : if (!barline) {
214 0 : return;
215 : }
216 :
217 0 : double xCur = item->pageX();
218 0 : if (hPlacement == AlignH::LEFT) {
219 0 : double xMin = barline->pageX() + barline->width() - itemBBox.left() + minBarLineDistance;
220 0 : if (xMin > xCur) {
221 0 : ldata->moveX(xMin - xCur);
222 : }
223 : } else {
224 0 : double xMax = barline->pageX() - itemBBox.right() - minBarLineDistance;
225 0 : if (xMax < xCur) {
226 0 : ldata->moveX(xMax - xCur);
227 : }
228 : }
229 : }
|