LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - tlayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 71.7 % 4362 3126
Test Date: 2026-11-05 04:07:02

            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              : #include <cfloat>
      23              : 
      24              : #include "tlayout.h"
      25              : 
      26              : #include "global/realfn.h"
      27              : #include "global/types/number.h"
      28              : #include "draw/fontmetrics.h"
      29              : 
      30              : #include "iengravingconfiguration.h" // IWYU pragma: keep
      31              : #include "iengravingfont.h"
      32              : 
      33              : #include "infrastructure/rtti.h"
      34              : #include "infrastructure/ld_access.h"
      35              : 
      36              : #include "types/typesconv.h"
      37              : #include "types/symnames.h"
      38              : #include "dom/score.h"
      39              : #include "dom/utils.h"
      40              : 
      41              : #include "dom/accidental.h"
      42              : #include "dom/ambitus.h"
      43              : #include "dom/arpeggio.h"
      44              : #include "dom/articulation.h"
      45              : 
      46              : #include "dom/barline.h"
      47              : #include "dom/beam.h"
      48              : #include "dom/bend.h"
      49              : #include "dom/box.h"
      50              : #include "dom/bracket.h"
      51              : #include "dom/breath.h"
      52              : 
      53              : #include "dom/chord.h"
      54              : #include "dom/chordline.h"
      55              : #include "dom/clef.h"
      56              : #include "dom/capo.h"
      57              : 
      58              : #include "dom/deadslapped.h"
      59              : #include "dom/dynamic.h"
      60              : 
      61              : #include "dom/expression.h"
      62              : 
      63              : #include "dom/fermata.h"
      64              : #include "dom/figuredbass.h"
      65              : #include "dom/fingering.h"
      66              : #include "dom/fret.h"
      67              : 
      68              : #include "dom/glissando.h"
      69              : #include "dom/gradualtempochange.h"
      70              : #include "dom/guitarbend.h"
      71              : 
      72              : #include "dom/hairpin.h"
      73              : #include "dom/hammeronpulloff.h"
      74              : #include "dom/harppedaldiagram.h"
      75              : #include "dom/harmonicmark.h"
      76              : #include "dom/harmony.h"
      77              : #include "dom/hook.h"
      78              : 
      79              : #include "dom/image.h"
      80              : #include "dom/instrchange.h"
      81              : #include "dom/instrumentname.h"
      82              : 
      83              : #include "dom/jump.h"
      84              : 
      85              : #include "dom/keysig.h"
      86              : 
      87              : #include "dom/layoutbreak.h"
      88              : #include "dom/ledgerline.h"
      89              : #include "dom/letring.h"
      90              : #include "dom/line.h"
      91              : #include "dom/linkedobjects.h"
      92              : #include "dom/lyrics.h"
      93              : 
      94              : #include "dom/marker.h"
      95              : #include "dom/measure.h"
      96              : #include "dom/measurebase.h"
      97              : #include "dom/measurenumber.h"
      98              : #include "dom/measurenumberbase.h"
      99              : #include "dom/measurerepeat.h"
     100              : #include "dom/mmrest.h"
     101              : #include "dom/mmrestrange.h"
     102              : 
     103              : #include "dom/note.h"
     104              : #include "dom/notedot.h"
     105              : #include "dom/noteline.h"
     106              : 
     107              : #include "dom/ornament.h"
     108              : #include "dom/ottava.h"
     109              : 
     110              : #include "dom/page.h"
     111              : #include "dom/palmmute.h"
     112              : #include "dom/parenthesis.h"
     113              : #include "dom/part.h"
     114              : #include "dom/pedal.h"
     115              : #include "dom/pickscrape.h"
     116              : #include "dom/playtechannotation.h"
     117              : 
     118              : #include "dom/rasgueado.h"
     119              : #include "dom/rehearsalmark.h"
     120              : #include "dom/rest.h"
     121              : 
     122              : #include "dom/shadownote.h"
     123              : #include "dom/slur.h"
     124              : #include "dom/spacer.h"
     125              : #include "dom/staff.h"
     126              : #include "dom/stafflines.h"
     127              : #include "dom/staffstate.h"
     128              : #include "dom/stafftext.h"
     129              : #include "dom/stafftype.h"
     130              : #include "dom/stafftypechange.h"
     131              : #include "dom/stem.h"
     132              : #include "dom/stemslash.h"
     133              : #include "dom/sticking.h"
     134              : #include "dom/stringtunings.h"
     135              : #include "dom/symbol.h"
     136              : #include "dom/system.h"
     137              : #include "dom/systemdivider.h"
     138              : #include "dom/systemlockindicator.h"
     139              : #include "dom/systemtext.h"
     140              : #include "dom/soundflag.h"
     141              : 
     142              : #include "dom/tapping.h"
     143              : #include "dom/tempotext.h"
     144              : #include "dom/text.h"
     145              : #include "dom/textline.h"
     146              : #include "dom/tie.h"
     147              : #include "dom/timesig.h"
     148              : #include "dom/tremolosinglechord.h"
     149              : #include "dom/tremolotwochord.h"
     150              : #include "dom/tremolobar.h"
     151              : #include "dom/trill.h"
     152              : #include "dom/tripletfeel.h"
     153              : #include "dom/tuplet.h"
     154              : 
     155              : #include "dom/vibrato.h"
     156              : #include "dom/volta.h"
     157              : 
     158              : #include "dom/whammybar.h"
     159              : 
     160              : #include "dom/factory.h"
     161              : 
     162              : #include "editing/editchord.h"
     163              : #include "editing/navigation.h"
     164              : 
     165              : #include "accidentalslayout.h"
     166              : #include "arpeggiolayout.h"
     167              : #include "autoplace.h"
     168              : #include "beamlayout.h"
     169              : #include "boxlayout.h"
     170              : #include "chordlayout.h"
     171              : #include "dynamicslayout.h"
     172              : #include "guitarbendlayout.h"
     173              : #include "lyricslayout.h"
     174              : #include "masklayout.h"
     175              : #include "slurtielayout.h"
     176              : #include "tremololayout.h"
     177              : #include "tupletlayout.h"
     178              : #include "horizontalspacing.h"
     179              : #include "measurelayout.h"
     180              : #include "tappinglayout.h"
     181              : #include "harmonylayout.h"
     182              : #include "markerlayout.h"
     183              : #include "measurenumberlayout.h"
     184              : #include "parenthesislayout.h"
     185              : #include "restlayout.h"
     186              : #include "textlayout.h"
     187              : 
     188              : using namespace muse;
     189              : using namespace muse::draw;
     190              : using namespace mu::engraving;
     191              : using namespace mu::engraving::rtti;
     192              : using namespace mu::engraving::rendering::score;
     193              : 
     194              : #define LAYOUT_CALL_ITEM(item) LAYOUT_CALL() << LAYOUT_ITEM_INFO(item);
     195              : 
     196       147115 : void TLayout::layoutItem(EngravingItem* item, LayoutContext& ctx)
     197              : {
     198              :     //DO_ASSERT(!ctx.conf().isPaletteMode());
     199              : 
     200       147115 :     EngravingItem::LayoutData* ldata = item->mutldata();
     201              : 
     202       147115 :     switch (item->type()) {
     203            0 :     case ElementType::ACCIDENTAL:
     204            0 :         layoutAccidental(item_cast<const Accidental*>(item), static_cast<Accidental::LayoutData*>(ldata), ctx.conf());
     205            0 :         break;
     206            0 :     case ElementType::ACTION_ICON:
     207            0 :         layoutActionIcon(item_cast<const ActionIcon*>(item), static_cast<ActionIcon::LayoutData*>(ldata));
     208            0 :         break;
     209            0 :     case ElementType::AMBITUS:
     210            0 :         layoutAmbitus(item_cast<const Ambitus*>(item), static_cast<Ambitus::LayoutData*>(ldata), ctx);
     211            0 :         break;
     212          445 :     case ElementType::ARPEGGIO:
     213          445 :         layoutArpeggio(item_cast<const Arpeggio*>(item), static_cast<Arpeggio::LayoutData*>(ldata), ctx.conf());
     214          445 :         break;
     215            0 :     case ElementType::CHORD_BRACKET:
     216            0 :         layoutChordBracket(item_cast<const ChordBracket*>(item), static_cast<ChordBracket::LayoutData*>(ldata), ctx.conf());
     217            0 :         break;
     218         2684 :     case ElementType::ARTICULATION:
     219         2684 :         layoutArticulation(item_cast<Articulation*>(item), static_cast<Articulation::LayoutData*>(ldata));
     220         2684 :         break;
     221            0 :     case ElementType::BAR_LINE:
     222            0 :         layoutBarLine(item_cast<const BarLine*>(item), static_cast<BarLine::LayoutData*>(ldata), ctx);
     223            0 :         break;
     224            0 :     case ElementType::BEAM:             layoutBeam(item_cast<Beam*>(item), ctx);
     225            0 :         break;
     226            4 :     case ElementType::BEND:
     227            4 :         layoutBend(item_cast<const Bend*>(item), static_cast<Bend::LayoutData*>(ldata));
     228            4 :         break;
     229          147 :     case ElementType::HBOX:
     230          147 :         layoutHBox(item_cast<const HBox*>(item), static_cast<HBox::LayoutData*>(ldata), ctx);
     231          147 :         break;
     232            0 :     case ElementType::VBOX:
     233            0 :         layoutVBox(item_cast<const VBox*>(item), static_cast<VBox::LayoutData*>(ldata), ctx);
     234            0 :         break;
     235            0 :     case ElementType::FBOX:
     236            0 :         layoutFBox(item_cast<const FBox*>(item), static_cast<FBox::LayoutData*>(ldata), ctx);
     237            0 :         break;
     238            0 :     case ElementType::TBOX:
     239            0 :         layoutTBox(item_cast<const TBox*>(item), static_cast<TBox::LayoutData*>(ldata), ctx);
     240            0 :         break;
     241            0 :     case ElementType::BRACKET:
     242            0 :         layoutBracket(item_cast<const Bracket*>(item), static_cast<Bracket::LayoutData*>(ldata), ctx.conf());
     243            0 :         break;
     244          168 :     case ElementType::BREATH:
     245          168 :         layoutBreath(item_cast<const Breath*>(item), static_cast<Breath::LayoutData*>(ldata), ctx.conf());
     246          168 :         break;
     247        49663 :     case ElementType::CHORD:            layoutChord(item_cast<Chord*>(item), ctx);
     248        49663 :         break;
     249          168 :     case ElementType::CHORDLINE:
     250          168 :         layoutChordLine(item_cast<const ChordLine*>(item), static_cast<ChordLine::LayoutData*>(ldata), ctx.conf());
     251          168 :         break;
     252            0 :     case ElementType::CLEF:
     253            0 :         layoutClef(item_cast<const Clef*>(item), static_cast<Clef::LayoutData*>(ldata), ctx.conf());
     254            0 :         break;
     255           44 :     case ElementType::CAPO:
     256           44 :         layoutCapo(item_cast<const Capo*>(item), static_cast<Capo::LayoutData*>(ldata), ctx);
     257           44 :         break;
     258            0 :     case ElementType::DEAD_SLAPPED:
     259            0 :         layoutDeadSlapped(item_cast<const DeadSlapped*>(item), static_cast<DeadSlapped::LayoutData*>(ldata));
     260            0 :         break;
     261         1145 :     case ElementType::DYNAMIC:
     262         1145 :         layoutDynamic(item_cast<Dynamic*>(item), static_cast<Dynamic::LayoutData*>(ldata), ctx.conf());
     263         1145 :         break;
     264           34 :     case ElementType::EXPRESSION:
     265           34 :         layoutExpression(item_cast<const Expression*>(item), static_cast<Expression::LayoutData*>(ldata));
     266           34 :         break;
     267          566 :     case ElementType::FERMATA:
     268          566 :         layoutFermata(item_cast<const Fermata*>(item), static_cast<Fermata::LayoutData*>(ldata));
     269          566 :         break;
     270          156 :     case ElementType::FIGURED_BASS:
     271          156 :         layoutFiguredBass(item_cast<const FiguredBass*>(item), static_cast<FiguredBass::LayoutData*>(ldata), ctx);
     272          156 :         break;
     273            0 :     case ElementType::FINGERING:
     274            0 :         layoutFingering(item_cast<const Fingering*>(item), static_cast<Fingering::LayoutData*>(ldata));
     275            0 :         break;
     276          412 :     case ElementType::FRET_DIAGRAM:
     277          412 :         layoutFretDiagram(item_cast<const FretDiagram*>(item), static_cast<FretDiagram::LayoutData*>(ldata), ctx);
     278          412 :         break;
     279          234 :     case ElementType::GLISSANDO:        layoutGlissando(item_cast<Glissando*>(item), ctx);
     280          234 :         break;
     281          910 :     case ElementType::GLISSANDO_SEGMENT: layoutGlissandoSegment(item_cast<GlissandoSegment*>(item), ctx);
     282          910 :         break;
     283            0 :     case ElementType::GRADUAL_TEMPO_CHANGE: layoutGradualTempoChange(item_cast<GradualTempoChange*>(item), ctx);
     284            0 :         break;
     285           22 :     case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT: layoutGradualTempoChangeSegment(item_cast<GradualTempoChangeSegment*>(item), ctx);
     286           22 :         break;
     287          409 :     case ElementType::GUITAR_BEND: layoutGuitarBend(item_cast<GuitarBend*>(item), ctx);
     288          409 :         break;
     289         1250 :     case ElementType::GUITAR_BEND_SEGMENT: layoutGuitarBendSegment(item_cast<GuitarBendSegment*>(item), ctx);
     290         1250 :         break;
     291            6 :     case ElementType::GUITAR_BEND_HOLD_SEGMENT: GuitarBendLayout::layoutHoldLine(item_cast<GuitarBendHoldSegment*>(item));
     292            6 :         break;
     293            0 :     case ElementType::HAIRPIN:          layoutHairpin(item_cast<Hairpin*>(item), ctx);
     294            0 :         break;
     295          472 :     case ElementType::HAIRPIN_SEGMENT:  layoutHairpinSegment(item_cast<HairpinSegment*>(item), ctx);
     296          472 :         break;
     297            0 :     case ElementType::HAMMER_ON_PULL_OFF: layoutHammerOnPullOff(item_cast<HammerOnPullOff*>(item), ctx);
     298            0 :         break;
     299            0 :     case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: layoutHammerOnPullOffSegment(item_cast<HammerOnPullOffSegment*>(item), ctx);
     300            0 :         break;
     301           75 :     case ElementType::HAMMER_ON_PULL_OFF_TEXT: layoutHammerOnPullOffText(item_cast<HammerOnPullOffText*>(item), ctx);
     302           75 :         break;
     303           32 :     case ElementType::HARP_DIAGRAM:
     304           32 :         layoutHarpPedalDiagram(item_cast<const HarpPedalDiagram*>(item), static_cast<HarpPedalDiagram::LayoutData*>(ldata));
     305           32 :         break;
     306         2276 :     case ElementType::HARMONY:
     307         2276 :         layoutHarmony(item_cast<Harmony*>(item), static_cast<Harmony::LayoutData*>(ldata), ctx);
     308         2276 :         break;
     309           29 :     case ElementType::HARMONIC_MARK_SEGMENT: layoutHarmonicMarkSegment(item_cast<HarmonicMarkSegment*>(item), ctx);
     310           29 :         break;
     311            0 :     case ElementType::HOOK:
     312            0 :         layoutHook(item_cast<const Hook*>(item), static_cast<Hook::LayoutData*>(ldata));
     313            0 :         break;
     314           60 :     case ElementType::IMAGE:
     315           60 :         layoutImage(item_cast<const Image*>(item), static_cast<Image::LayoutData*>(ldata));
     316           60 :         break;
     317           91 :     case ElementType::INSTRUMENT_CHANGE:
     318           91 :         layoutInstrumentChange(item_cast<const InstrumentChange*>(item), static_cast<InstrumentChange::LayoutData*>(ldata));
     319           91 :         break;
     320            0 :     case ElementType::INSTRUMENT_NAME:
     321            0 :         layoutInstrumentName(item_cast<const InstrumentName*>(item), static_cast<InstrumentName::LayoutData*>(ldata));
     322            0 :         break;
     323          212 :     case ElementType::JUMP:
     324          212 :         layoutJump(item_cast<const Jump*>(item), static_cast<Jump::LayoutData*>(ldata));
     325          212 :         break;
     326            0 :     case ElementType::KEYSIG:
     327            0 :         layoutKeySig(item_cast<const KeySig*>(item), static_cast<KeySig::LayoutData*>(ldata), ctx.conf());
     328            0 :         break;
     329            0 :     case ElementType::LAISSEZ_VIB:
     330            0 :         layoutLaissezVib(item_cast<LaissezVib*>(item));
     331            0 :         break;
     332         2168 :     case ElementType::LAYOUT_BREAK:
     333         2168 :         layoutLayoutBreak(item_cast<const LayoutBreak*>(item), static_cast<LayoutBreak::LayoutData*>(ldata));
     334         2168 :         break;
     335              :     // TODO: case ElementType::STAFF_VISIBILTY_INDICATOR:
     336            0 :     case ElementType::SYSTEM_LOCK_INDICATOR:
     337            0 :         layoutIndicatorIcon(item_cast<const IndicatorIcon*>(item), static_cast<IndicatorIcon::LayoutData*>(ldata));
     338            0 :         break;
     339            0 :     case ElementType::LET_RING:         layoutLetRing(item_cast<LetRing*>(item), ctx);
     340            0 :         break;
     341           31 :     case ElementType::LET_RING_SEGMENT: layoutLetRingSegment(item_cast<LetRingSegment*>(item), ctx);
     342           31 :         break;
     343            0 :     case ElementType::LEDGER_LINE:      layoutLedgerLine(item_cast<LedgerLine*>(item), ctx);
     344            0 :         break;
     345            0 :     case ElementType::LYRICS:           layoutLyrics(item_cast<Lyrics*>(item), ctx);
     346            0 :         break;
     347            0 :     case ElementType::LYRICSLINE_SEGMENT: layoutLyricsLineSegment(item_cast<LyricsLineSegment*>(item), ctx);
     348            0 :         break;
     349            6 :     case ElementType::PARTIAL_LYRICSLINE_SEGMENT: layoutLyricsLineSegment(item_cast<LyricsLineSegment*>(item), ctx);
     350            6 :         break;
     351          309 :     case ElementType::MARKER:
     352          309 :         layoutMarker(item_cast<Marker*>(item), static_cast<Marker::LayoutData*>(ldata), ctx);
     353          309 :         break;
     354            0 :     case ElementType::MEASURE_NUMBER:
     355            0 :         layoutMeasureNumber(item_cast<MeasureNumber*>(item), static_cast<MeasureNumber::LayoutData*>(ldata), ctx);
     356            0 :         break;
     357          365 :     case ElementType::MEASURE_REPEAT:
     358          365 :         layoutMeasureRepeat(item_cast<const MeasureRepeat*>(item), static_cast<MeasureRepeat::LayoutData*>(ldata), ctx);
     359          365 :         break;
     360          175 :     case ElementType::MMREST:
     361          175 :         layoutMMRest(item_cast<const MMRest*>(item), static_cast<MMRest::LayoutData*>(ldata), ctx);
     362          175 :         break;
     363            0 :     case ElementType::MMREST_RANGE:
     364            0 :         layoutMMRestRange(item_cast<MMRestRange*>(item), static_cast<MMRestRange::LayoutData*>(ldata), ctx);
     365            0 :         break;
     366            0 :     case ElementType::NOTE:
     367            0 :         layoutNote(item_cast<const Note*>(item), static_cast<Note::LayoutData*>(ldata));
     368            0 :         break;
     369            0 :     case ElementType::NOTEDOT:
     370            0 :         layoutNoteDot(item_cast<const NoteDot*>(item), static_cast<NoteDot::LayoutData*>(ldata));
     371            0 :         break;
     372            0 :     case ElementType::NOTEHEAD:
     373            0 :         layoutSymbol(item_cast<const NoteHead*>(item), static_cast<NoteHead::LayoutData*>(ldata), ctx);
     374            0 :         break;
     375            3 :     case ElementType::NOTELINE:
     376            3 :         layoutNoteLine(item_cast<NoteLine*>(item), ctx);
     377            3 :         break;
     378           12 :     case ElementType::NOTELINE_SEGMENT:
     379           12 :         layoutNoteLineSegment(item_cast<NoteLineSegment*>(item), ctx);
     380           12 :         break;
     381          636 :     case ElementType::ORNAMENT:
     382          636 :         layoutOrnament(item_cast<const Ornament*>(item), static_cast<Ornament::LayoutData*>(ldata), ctx.conf());
     383          636 :         break;
     384            0 :     case ElementType::OTTAVA:           layoutOttava(item_cast<Ottava*>(item), ctx);
     385            0 :         break;
     386          161 :     case ElementType::OTTAVA_SEGMENT:   layoutOttavaSegment(item_cast<OttavaSegment*>(item), ctx);
     387          161 :         break;
     388            0 :     case ElementType::PALM_MUTE:        layoutPalmMute(item_cast<PalmMute*>(item), ctx);
     389            0 :         break;
     390           14 :     case ElementType::PALM_MUTE_SEGMENT: layoutPalmMuteSegment(item_cast<PalmMuteSegment*>(item), ctx);
     391           14 :         break;
     392            0 :     case ElementType::PAGE_LOCK_INDICATOR:
     393            0 :         layoutPageLockIndicator(item_cast<const PageLockIndicator*>(item), static_cast<PageLockIndicator::LayoutData*>(ldata));
     394            0 :         break;
     395            0 :     case ElementType::PARENTHESIS:      layoutParenthesis(item_cast<Parenthesis*>(item), static_cast<Parenthesis::LayoutData*>(ldata), ctx);
     396            0 :         break;
     397            0 :     case ElementType::PEDAL:            layoutPedal(item_cast<Pedal*>(item), ctx);
     398            0 :         break;
     399          119 :     case ElementType::PEDAL_SEGMENT:    layoutPedalSegment(item_cast<PedalSegment*>(item), ctx);
     400          119 :         break;
     401            2 :     case ElementType::PICK_SCRAPE_SEGMENT: layoutPickScrapeSegment(item_cast<PickScrapeSegment*>(item), ctx);
     402            2 :         break;
     403            0 :     case ElementType::PLAY_COUNT_TEXT:
     404            0 :         layoutPlayCountText(item_cast<PlayCountText*>(item), static_cast<PlayCountText::LayoutData*>(ldata));
     405            0 :         break;
     406           87 :     case ElementType::PLAYTECH_ANNOTATION:
     407           87 :         layoutPlayTechAnnotation(item_cast<const PlayTechAnnotation*>(item), static_cast<PlayTechAnnotation::LayoutData*>(ldata));
     408           87 :         break;
     409            4 :     case ElementType::RASGUEADO_SEGMENT: layoutRasgueadoSegment(item_cast<RasgueadoSegment*>(item), ctx);
     410            4 :         break;
     411          127 :     case ElementType::REHEARSAL_MARK:
     412          127 :         layoutRehearsalMark(item_cast<const RehearsalMark*>(item), static_cast<RehearsalMark::LayoutData*>(ldata));
     413          127 :         break;
     414        66760 :     case ElementType::REST:
     415        66760 :         layoutRest(item_cast<const Rest*>(item), static_cast<Rest::LayoutData*>(ldata), ctx);
     416        66760 :         break;
     417            0 :     case ElementType::SHADOW_NOTE:      layoutShadowNote(item_cast<ShadowNote*>(item), ctx);
     418            0 :         break;
     419            0 :     case ElementType::SLUR:             layoutSlur(item_cast<Slur*>(item), ctx);
     420            0 :         break;
     421            0 :     case ElementType::SPACER:           layoutSpacer(item_cast<Spacer*>(item), ctx);
     422            0 :         break;
     423            0 :     case ElementType::STAFF_STATE:
     424            0 :         layoutStaffState(item_cast<const StaffState*>(item), static_cast<StaffState::LayoutData*>(ldata));
     425            0 :         break;
     426         1133 :     case ElementType::STAFF_TEXT:
     427         1133 :         layoutStaffText(item_cast<const StaffText*>(item), static_cast<StaffText::LayoutData*>(ldata));
     428         1133 :         break;
     429            6 :     case ElementType::STAVE_SHARING_LABEL:
     430            6 :         layoutStaveSharingLabel(item_cast<const StaveSharingLabel*>(item), static_cast<StaffText::LayoutData*>(ldata));
     431            6 :         break;
     432           19 :     case ElementType::STAFFTYPE_CHANGE:
     433           19 :         layoutStaffTypeChange(item_cast<const StaffTypeChange*>(item), static_cast<StaffTypeChange::LayoutData*>(ldata), ctx.conf());
     434           19 :         break;
     435            0 :     case ElementType::SOUND_FLAG:
     436            0 :         layoutSoundFlag(item_cast<const SoundFlag*>(item), static_cast<SoundFlag::LayoutData*>(ldata));
     437            0 :         break;
     438            0 :     case ElementType::STEM:
     439            0 :         layoutStem(item_cast<const Stem*>(item), static_cast<Stem::LayoutData*>(ldata), ctx.conf());
     440            0 :         break;
     441            0 :     case ElementType::STEM_SLASH:
     442            0 :         layoutStemSlash(item_cast<const StemSlash*>(item), static_cast<StemSlash::LayoutData*>(ldata), ctx.conf());
     443            0 :         break;
     444          131 :     case ElementType::STICKING:
     445          131 :         layoutSticking(item_cast<const Sticking*>(item), static_cast<Sticking::LayoutData*>(ldata));
     446          131 :         break;
     447           19 :     case ElementType::STRING_TUNINGS:
     448           19 :         layoutStringTunings(item_cast<StringTunings*>(item), ctx);
     449           19 :         break;
     450           50 :     case ElementType::SYMBOL:
     451           50 :         layoutSymbol(item_cast<const Symbol*>(item), static_cast<Symbol::LayoutData*>(ldata), ctx);
     452           50 :         break;
     453            0 :     case ElementType::FSYMBOL:
     454            0 :         layoutFSymbol(item_cast<const FSymbol*>(item), static_cast<FSymbol::LayoutData*>(ldata));
     455            0 :         break;
     456            0 :     case ElementType::SYSTEM_DIVIDER:
     457            0 :         layoutSystemDivider(item_cast<const SystemDivider*>(item), static_cast<SystemDivider::LayoutData*>(ldata), ctx);
     458            0 :         break;
     459            0 :     case ElementType::SYSTEM_TEXT:
     460            0 :         layoutSystemText(item_cast<const SystemText*>(item), static_cast<SystemText::LayoutData*>(ldata));
     461            0 :         break;
     462          134 :     case ElementType::TAPPING:
     463          134 :         layoutTapping(toTapping(item), static_cast<Tapping::LayoutData*>(ldata), ctx);
     464          134 :         break;
     465            0 :     case ElementType::TAPPING_HALF_SLUR:
     466            0 :         layoutTappingHalfSlur(toTappingHalfSlur(item));
     467            0 :         break;
     468         1136 :     case ElementType::TEMPO_TEXT:
     469         1136 :         layoutTempoText(item_cast<const TempoText*>(item), static_cast<TempoText::LayoutData*>(ldata));
     470         1136 :         break;
     471        10780 :     case ElementType::TEXT:
     472        10780 :         layoutText(item_cast<const Text*>(item), static_cast<Text::LayoutData*>(ldata));
     473        10780 :         break;
     474            0 :     case ElementType::TEXTLINE:         layoutTextLine(item_cast<TextLine*>(item), ctx);
     475            0 :         break;
     476           55 :     case ElementType::TEXTLINE_SEGMENT: layoutTextLineSegment(item_cast<TextLineSegment*>(item), ctx);
     477           55 :         break;
     478            0 :     case ElementType::TIE:              layoutTie(item_cast<Tie*>(item), ctx);
     479            0 :         break;
     480            0 :     case ElementType::TIMESIG:
     481            0 :         layoutTimeSig(item_cast<const TimeSig*>(item), static_cast<TimeSig::LayoutData*>(ldata), ctx);
     482            0 :         break;
     483          498 :     case ElementType::TIME_TICK_ANCHOR: layoutTimeTickAnchor(static_cast<TimeTickAnchor*>(item), ctx);
     484          498 :         break;
     485            0 :     case ElementType::TREMOLO_SINGLECHORD: layoutTremoloSingle(item_cast<TremoloSingleChord*>(item), ctx);
     486            0 :         break;
     487            0 :     case ElementType::TREMOLO_TWOCHORD:    layoutTremoloTwo(item_cast<TremoloTwoChord*>(item), ctx);
     488            0 :         break;
     489           38 :     case ElementType::TREMOLOBAR:
     490           38 :         layoutTremoloBar(item_cast<const TremoloBar*>(item), static_cast<TremoloBar::LayoutData*>(ldata));
     491           38 :         break;
     492            0 :     case ElementType::TRILL:            layoutTrill(item_cast<Trill*>(item), ctx);
     493            0 :         break;
     494          116 :     case ElementType::TRILL_SEGMENT:    layoutTrillSegment(item_cast<TrillSegment*>(item), ctx);
     495          116 :         break;
     496            0 :     case ElementType::TRIPLET_FEEL:
     497            0 :         layoutTripletFeel(item_cast<const TripletFeel*>(item), static_cast<TripletFeel::LayoutData*>(ldata));
     498            0 :         break;
     499            0 :     case ElementType::TUPLET:           layoutTuplet(item_cast<Tuplet*>(item), ctx);
     500            0 :         break;
     501            0 :     case ElementType::VIBRATO:          layoutVibrato(item_cast<Vibrato*>(item), ctx);
     502            0 :         break;
     503           28 :     case ElementType::VIBRATO_SEGMENT:  layoutVibratoSegment(item_cast<VibratoSegment*>(item), ctx);
     504           28 :         break;
     505            0 :     case ElementType::VOLTA:            layoutVolta(item_cast<Volta*>(item), ctx);
     506            0 :         break;
     507          279 :     case ElementType::VOLTA_SEGMENT:    layoutVoltaSegment(item_cast<VoltaSegment*>(item), ctx);
     508          279 :         break;
     509            0 :     case ElementType::WHAMMY_BAR:       layoutLine(item_cast<WhammyBar*>(item), ctx);
     510            0 :         break;
     511            0 :     case ElementType::WHAMMY_BAR_SEGMENT: layoutWhammyBarSegment(item_cast<WhammyBarSegment*>(item), ctx);
     512            0 :         break;
     513            0 :     default:
     514            0 :         LOGE() << "not found in layout types item: " << item->typeName();
     515            0 :         DO_ASSERT(false);
     516              :     }
     517       147115 : }
     518              : 
     519         4086 : void TLayout::layoutAccidental(const Accidental* item, Accidental::LayoutData* ldata, const LayoutConfiguration& conf)
     520              : {
     521              :     LAYOUT_CALL_ITEM(item);
     522              :     LD_INDEPENDENT;
     523              : 
     524         4086 :     if (ldata->isValid()) {
     525           95 :         return;
     526              :     }
     527              : 
     528         4001 :     ldata->syms.clear();
     529              : 
     530              :     // TODO: remove Accidental in layout
     531              :     // don't show accidentals for tab or slash notation
     532         4001 :     if (item->onTabStaff() || (item->note() && item->note()->fixed())) {
     533            5 :         ldata->setIsSkipDraw(true);
     534            5 :         return;
     535              :     }
     536         3996 :     ldata->setIsSkipDraw(false);
     537         3996 :     ldata->setPos(PointF());
     538              : 
     539         3996 :     if (item->accidentalType() == AccidentalType::NONE) {
     540            5 :         return;
     541              :     }
     542              : 
     543         3991 :     ldata->column = 0;
     544              : 
     545         3991 :     auto accidentalSingleSym = [](const Accidental* item) -> SymId
     546              :     {
     547              :         // if the accidental is standard (doubleflat, flat, natural, sharp or double sharp)
     548              :         // and it has either no bracket or parentheses, then we have glyphs straight from smufl.
     549              : 
     550         3991 :         if (item->bracket() == AccidentalBracket::PARENTHESIS && !item->parentNoteHasParentheses()) {
     551           57 :             switch (item->accidentalType()) {
     552            4 :             case AccidentalType::FLAT:      return SymId::accidentalFlatParens;
     553            4 :             case AccidentalType::FLAT2:     return SymId::accidentalDoubleFlatParens;
     554            9 :             case AccidentalType::NATURAL:   return SymId::accidentalNaturalParens;
     555            4 :             case AccidentalType::SHARP:     return SymId::accidentalSharpParens;
     556            4 :             case AccidentalType::SHARP2:    return SymId::accidentalDoubleSharpParens;
     557           32 :             default:
     558           32 :                 break;
     559              :             }
     560              :         }
     561         3966 :         return SymId::noSym;
     562              :     };
     563              : 
     564           57 :     auto accidentalBracketSyms = [](AccidentalBracket type) -> std::pair<SymId, SymId>
     565              :     {
     566           57 :         switch (type) {
     567           57 :         case AccidentalBracket::PARENTHESIS: return { SymId::accidentalParensLeft, SymId::accidentalParensRight };
     568            0 :         case AccidentalBracket::BRACKET: return { SymId::accidentalBracketLeft, SymId::accidentalBracketRight };
     569            0 :         case AccidentalBracket::BRACE: return { SymId::accidentalCombiningOpenCurlyBrace, SymId::accidentalCombiningCloseCurlyBrace };
     570            0 :         case AccidentalBracket::NONE: return { SymId::noSym, SymId::noSym };
     571              :         }
     572            0 :         return { SymId::noSym, SymId::noSym };
     573              :     };
     574              : 
     575         3991 :     Shape shape;
     576              : 
     577              :     // Single?
     578         3991 :     SymId singleSym = accidentalSingleSym(item);
     579         3991 :     if (singleSym != SymId::noSym && conf.engravingFont()->isValid(singleSym)) {
     580            0 :         Accidental::LayoutData::Sym s(singleSym, 0.0, 0.0);
     581            0 :         ldata->syms.push_back(s);
     582              : 
     583            0 :         shape.add(item->symShapeWithCutouts(singleSym));
     584              :     }
     585              :     // Multi
     586              :     else {
     587         3991 :         double margin = conf.styleAbsolute(Sid::bracketedAccidentalPadding);
     588         3991 :         double x = 0.0;
     589              : 
     590         3991 :         std::pair<SymId, SymId> bracketSyms;
     591         3991 :         bool isNeedBracket = item->bracket() != AccidentalBracket::NONE && !item->parentNoteHasParentheses();
     592         3991 :         if (isNeedBracket) {
     593           57 :             bracketSyms = accidentalBracketSyms(item->bracket());
     594              :         }
     595              : 
     596              :         // Left
     597         3991 :         if (bracketSyms.first != SymId::noSym) {
     598              :             Accidental::LayoutData::Sym ls(bracketSyms.first, 0.0,
     599           57 :                                            item->bracket() == AccidentalBracket::BRACE ? item->spatium() * 0.4 : 0.0);
     600           57 :             ldata->syms.push_back(ls);
     601           57 :             shape.add(item->symBbox(bracketSyms.first), item);
     602              : 
     603           57 :             x += item->symAdvance(bracketSyms.first) + margin;
     604              :         }
     605              : 
     606              :         // Main
     607         3991 :         SymId mainSym = item->symId();
     608         3991 :         Accidental::LayoutData::Sym ms(mainSym, x, 0.0);
     609         3991 :         ldata->syms.push_back(ms);
     610         3991 :         shape.add(item->symShapeWithCutouts(mainSym).translated(PointF(x, 0.0)));
     611              : 
     612              :         // Right
     613         3991 :         if (bracketSyms.second != SymId::noSym) {
     614           57 :             x += item->symAdvance(mainSym) + margin;
     615              : 
     616              :             Accidental::LayoutData::Sym rs(bracketSyms.second, x,
     617           57 :                                            item->bracket() == AccidentalBracket::BRACE ? item->spatium() * 0.4 : 0.0);
     618           57 :             ldata->syms.push_back(rs);
     619           57 :             shape.add(item->symBbox(bracketSyms.second).translated(x, 0.0), item);
     620              :         }
     621              :     }
     622              : 
     623         3991 :     ldata->setShape(shape);
     624         3991 : }
     625              : 
     626            0 : void TLayout::layoutActionIcon(const ActionIcon* item, ActionIcon::LayoutData* ldata)
     627              : {
     628              :     LAYOUT_CALL_ITEM(item);
     629              :     LD_INDEPENDENT;
     630              : 
     631            0 :     if (ldata->isValid()) {
     632            0 :         return;
     633              :     }
     634              : 
     635            0 :     FontMetrics fontMetrics(item->iconFont());
     636            0 :     ldata->setBbox(fontMetrics.boundingRect(Char(item->icon())));
     637            0 :     ldata->setPos(PointF());
     638            0 : }
     639              : 
     640            2 : void TLayout::layoutAmbitus(const Ambitus* item, Ambitus::LayoutData* ldata, const LayoutContext& ctx)
     641              : {
     642              :     LAYOUT_CALL_ITEM(item);
     643              :     LD_INDEPENDENT;
     644              : 
     645            2 :     if (ldata->isValid()) {
     646            0 :         return;
     647              :     }
     648              : 
     649            2 :     const double spatium = item->spatium();
     650            2 :     const double headWdt = item->headWidth();
     651              : 
     652            2 :     Accidental::LayoutData* topAccData = item->topAccidental()->mutldata();
     653            2 :     Accidental::LayoutData* bottomAccData = item->bottomAccidental()->mutldata();
     654              : 
     655            2 :     ldata->setPos(PointF());
     656              : 
     657              :     //
     658              :     // NOTEHEADS Y POS
     659              :     //
     660              :     {
     661            2 :         const Fraction tick = item->segment()->tick();
     662            2 :         const Staff* stf = ctx.dom().staff(item->staffIdx());
     663            2 :         const ClefType clf = stf->clef(tick);
     664            2 :         const Key key = stf->key(tick);
     665            2 :         const double lineDist = stf->lineDistance(tick) * spatium;
     666              : 
     667              :         // top notehead
     668            2 :         if (!pitchIsValid(item->topPitch()) || !tpcIsValid(item->topTpc())) {
     669            0 :             ldata->topPos.setY(0.0); // if uninitialized, set to top staff line
     670              :         } else {
     671            2 :             int topLine = Ambitus::staffLine(item->topTpc(), item->topPitch(), clf);
     672            2 :             ldata->topPos.setY(topLine * lineDist * 0.5);
     673              :             // compute accidental
     674            2 :             AccidentalType accidType = Ambitus::accidentalType(item->topTpc(), key);
     675            2 :             item->topAccidental()->setAccidentalType(accidType);
     676            2 :             TLayout::layoutAccidental(item->topAccidental(), topAccData, ctx.conf());
     677            2 :             topAccData->setPosY(ldata->topPos.y());
     678              :         }
     679              : 
     680              :         // bottom notehead
     681            2 :         if (!pitchIsValid(item->bottomPitch()) || !tpcIsValid(item->bottomTpc())) {
     682            0 :             const int numOfLines = stf->lines(tick);
     683            0 :             ldata->bottomPos.setY((numOfLines - 1) * lineDist);         // if uninitialized, set to last staff line
     684              :         } else {
     685            2 :             int bottomLine = Ambitus::staffLine(item->bottomTpc(), item->bottomPitch(), clf);
     686            2 :             ldata->bottomPos.setY(bottomLine * lineDist * 0.5);
     687              :             // compute accidental
     688            2 :             AccidentalType accidType = Ambitus::accidentalType(item->bottomTpc(), key);
     689            2 :             item->bottomAccidental()->setAccidentalType(accidType);
     690            2 :             TLayout::layoutAccidental(item->bottomAccidental(), bottomAccData, ctx.conf());
     691            2 :             bottomAccData->setPosY(ldata->bottomPos.y());
     692              :         }
     693              :     }
     694              : 
     695              :     //
     696              :     // NOTEHEAD X POS
     697              :     //
     698              :     // Note: manages colliding accidentals
     699              :     //
     700              :     {
     701            2 :         double accNoteDist = item->absoluteFromSpatium(ctx.conf().styleS(Sid::accidentalNoteDistance));
     702            2 :         double xAccidOffTop = topAccData->bbox().width() + accNoteDist;
     703            2 :         double xAccidOffBottom = bottomAccData->bbox().width() + accNoteDist;
     704              : 
     705              :         // if top accidental extends down more than bottom accidental extends up,
     706              :         // AND ambitus is not leaning right, bottom accidental needs to be displaced
     707            2 :         bool collision = (item->direction() != DirectionH::RIGHT)
     708            3 :                          && (topAccData->pos().y() + topAccData->bbox().y() + topAccData->bbox().height()
     709            1 :                              > bottomAccData->pos().y() + bottomAccData->bbox().y());
     710            2 :         if (collision) {
     711              :             // displace bottom accidental (also attempting to 'undercut' flats)
     712            0 :             AccidentalType bottomAccType = item->bottomAccidental()->accidentalType();
     713            0 :             xAccidOffBottom = xAccidOffTop + ((bottomAccType == AccidentalType::FLAT
     714            0 :                                                || bottomAccType == AccidentalType::FLAT2
     715            0 :                                                || bottomAccType == AccidentalType::NATURAL)
     716            0 :                                               ? bottomAccData->bbox().width() * 0.5 : bottomAccData->bbox().width());
     717              :         }
     718              : 
     719            2 :         switch (item->direction()) {
     720            1 :         case DirectionH::AUTO:                   // noteheads one above the other
     721              :             // left align noteheads and right align accidentals 'hanging' on the left
     722            1 :             ldata->topPos.setX(0.0);
     723            1 :             ldata->bottomPos.setX(0.0);
     724            1 :             topAccData->setPosX(-xAccidOffTop);
     725            1 :             bottomAccData->setPosX(-xAccidOffBottom);
     726            1 :             break;
     727            0 :         case DirectionH::LEFT:                   // top notehead at the left of bottom notehead
     728              :             // place top notehead at left margin; bottom notehead at right of top head;
     729              :             // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
     730            0 :             ldata->topPos.setX(0.0);
     731            0 :             ldata->bottomPos.setX(headWdt);
     732            0 :             topAccData->setPosX(-xAccidOffTop);
     733            0 :             bottomAccData->setPosX(collision ? -xAccidOffBottom : headWdt - xAccidOffBottom);
     734            0 :             break;
     735            1 :         case DirectionH::RIGHT:                  // top notehead at the right of bottom notehead
     736              :             // bottom notehead at left margin; top notehead at right of bottomnotehead
     737              :             // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
     738            1 :             ldata->topPos.setX(headWdt);
     739            1 :             ldata->bottomPos.setX(0.0);
     740            1 :             topAccData->setPosX(headWdt - xAccidOffTop);
     741            1 :             bottomAccData->setPosX(-xAccidOffBottom);
     742            1 :             break;
     743              :         }
     744              :     }
     745              : 
     746              :     // LINE
     747              :     // compute line from top note centre to bottom note centre
     748              :     {
     749            2 :         LineF fullLine(ldata->topPos.x() + headWdt * 0.5,
     750              :                        ldata->topPos.y(),
     751            2 :                        ldata->bottomPos.x() + headWdt * 0.5,
     752            6 :                        ldata->bottomPos.y());
     753              :         // shorten line on each side by offsets
     754            2 :         double yDelta = ldata->bottomPos.y() - ldata->topPos.y();
     755            2 :         if (!muse::RealIsNull(yDelta)) {
     756            2 :             double off = Ambitus::LINEOFFSET_DEFAULT.toAbsolute(spatium);
     757            2 :             PointF p1 = fullLine.pointAt(off / yDelta);
     758            2 :             PointF p2 = fullLine.pointAt(1 - (off / yDelta));
     759            2 :             ldata->line = LineF(p1, p2);
     760              :         } else {
     761            0 :             ldata->line = fullLine;
     762              :         }
     763              :     }
     764              : 
     765              :     // BBOX
     766              :     {
     767            2 :         RectF headRect(0, -0.5 * spatium, headWdt, 1 * spatium);
     768            4 :         ldata->setBbox(headRect.translated(ldata->topPos).united(headRect.translated(ldata->bottomPos))
     769            2 :                        .united(topAccData->bbox().translated(topAccData->pos()))
     770            4 :                        .united(bottomAccData->bbox().translated(bottomAccData->pos())));
     771              :     }
     772              : }
     773              : 
     774          466 : void TLayout::layoutArpeggio(const Arpeggio* item, Arpeggio::LayoutData* ldata, const LayoutConfiguration& conf)
     775              : {
     776              :     LAYOUT_CALL_ITEM(item);
     777              : 
     778              :     //! NOTE Can be edited and relayout,
     779              :     //! in this case the reset layout data has not yet been done
     780              : //    if (ldata->isValid()) {
     781              : //        return;
     782              : //    }
     783              : 
     784          466 :     if (conf.styleB(Sid::arpeggioHiddenInStdIfTab)) {
     785            0 :         if (item->staff() && item->staff()->isPitchedStaff(item->tick())) {
     786            0 :             for (Staff* s : item->staff()->staffList()) {
     787            0 :                 if (s->onSameScore(item) && s->isTabStaff(item->tick()) && s->visible()) {
     788            0 :                     ldata->setIsSkipDraw(true);
     789              :                 }
     790            0 :             }
     791              :         }
     792              :     }
     793          466 :     ldata->setIsSkipDraw(false);
     794          466 :     if (!ldata->isSetPos()) {
     795          165 :         ldata->setPos(PointF());
     796              :     }
     797              : 
     798          466 :     IF_ASSERT_FAILED(item->ownershipParent()) {
     799            0 :         return;
     800              :     }
     801              : 
     802              :     //! NOTE Must already be set previously
     803          466 :     Chord* parentChord = item->chord();
     804          466 :     LD_CONDITION(parentChord->upNote()->ldata()->isSetPos());
     805          466 :     LD_CONDITION(parentChord->downNote()->ldata()->isSetPos());
     806              : 
     807          394 :     auto symbolLine = [](const std::shared_ptr<const IEngravingFont>& f, Arpeggio::LayoutData* data, SymId end, SymId fill)
     808              :     {
     809          394 :         data->symbols.clear();
     810              : 
     811          394 :         double w = data->bottom - data->top;
     812          394 :         double w1 = f->advance(end, data->magS);
     813          394 :         double w2 = f->advance(fill, data->magS);
     814          394 :         int n = lrint((w - w1) / w2);
     815         1803 :         for (int i = 0; i < n; ++i) {
     816         1409 :             data->symbols.push_back(fill);
     817              :         }
     818          394 :         data->symbols.push_back(end);
     819          394 :     };
     820              : 
     821          466 :     ldata->arpeggioHeight = ArpeggioLayout::computeHeight(item);
     822          466 :     ldata->top = ArpeggioLayout::calcTop(item, conf);
     823          466 :     ldata->bottom = ArpeggioLayout::calcBottom(item, ldata->arpeggioHeight, conf);
     824              : 
     825          466 :     ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
     826          466 :     ldata->magS = conf.magS(ldata->mag());
     827              : 
     828          466 :     std::shared_ptr<const IEngravingFont> font = conf.engravingFont();
     829          466 :     switch (item->arpeggioType()) {
     830          294 :     case ArpeggioType::NORMAL: {
     831          294 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUp, SymId::wiggleArpeggiatoUp);
     832              :         // string is rotated -90 degrees
     833          294 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     834          294 :         ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     835          294 :     } break;
     836              : 
     837           46 :     case ArpeggioType::UP: {
     838           46 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
     839              :         // string is rotated -90 degrees
     840           46 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     841           46 :         ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     842           46 :     } break;
     843              : 
     844           54 :     case ArpeggioType::DOWN: {
     845           54 :         symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
     846              :         // string is rotated +90 degrees (so that UpArrow turns into a DownArrow)
     847           54 :         ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
     848           54 :         ldata->setBbox(RectF(0.0, ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
     849           54 :     } break;
     850              : 
     851           10 :     case ArpeggioType::UP_STRAIGHT: {
     852           10 :         double x1 = item->spatium() * 0.5;
     853           10 :         ldata->symsBBox = font->bbox(SymId::arrowheadBlackUp, ldata->magS);
     854           10 :         double w = ldata->symsBBox.width();
     855           10 :         ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
     856           10 :     } break;
     857              : 
     858           10 :     case ArpeggioType::DOWN_STRAIGHT: {
     859           10 :         double x1 = item->spatium() * 0.5;
     860           10 :         ldata->symsBBox = font->bbox(SymId::arrowheadBlackDown, ldata->magS);
     861           10 :         double w = ldata->symsBBox.width();
     862           10 :         ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
     863           10 :     } break;
     864              : 
     865           52 :     case ArpeggioType::BRACKET: {
     866           52 :         double w  = conf.styleS(Sid::arpeggioHookLen).val() * item->spatium();
     867           52 :         ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
     868           52 :     } break;
     869              :     }
     870              : 
     871              :     // Loop through staves spanned & regenerate chord shape
     872              :     // This makes sure the arpeggio's shape is added to the shape of each chord it spans
     873          466 :     Chord* chord = item->chord();
     874          466 :     Segment* seg = chord->segment();
     875          466 :     staff_idx_t staveSpan = (item->track() + item->span() - 1) / VOICES;
     876          980 :     for (staff_idx_t staffIdx = item->staffIdx(); staffIdx <= staveSpan; staffIdx++) {
     877          514 :         seg->createShape(staffIdx);
     878              :     }
     879          466 : }
     880              : 
     881            0 : static Shape getChordsShape(const ChordBracket* item)
     882              : {
     883            0 :     Chord* owner = item->chord();
     884            0 :     Shape result = owner->shape();
     885            0 :     result.removeTypes({ ElementType::CHORD_BRACKET, ElementType::ARPEGGIO });
     886            0 :     if (!owner->segment() || !owner->staff()) {
     887            0 :         return result;
     888              :     }
     889              : 
     890            0 :     const track_idx_t startTrack = staff2track(owner->staffIdx());
     891            0 :     const track_idx_t endTrack = startTrack + VOICES;
     892              : 
     893            0 :     for (track_idx_t track = startTrack; track < endTrack; ++track) {
     894            0 :         EngravingItem* e = owner->segment()->element(track);
     895            0 :         if (track == owner->track()) {
     896            0 :             continue;
     897              :         }
     898              : 
     899            0 :         if (!e || !e->isChord()) {
     900            0 :             continue;
     901              :         }
     902              : 
     903            0 :         Chord* chord = toChord(e);
     904            0 :         if (chord->vStaffIdx() != owner->vStaffIdx()) {
     905            0 :             continue;
     906              :         }
     907              : 
     908            0 :         Shape chordShape = chord->shape();
     909            0 :         chordShape.removeTypes({ ElementType::CHORD_BRACKET, ElementType::ARPEGGIO });
     910            0 :         chordShape.translate(chord->pos() - owner->pos());
     911            0 :         result.add(chordShape);
     912            0 :     }
     913              : 
     914            0 :     return result;
     915            0 : }
     916              : 
     917            0 : void TLayout::layoutChordBracket(const ChordBracket* item, Arpeggio::LayoutData* ldata, const LayoutConfiguration& conf)
     918              : {
     919            0 :     double spatium = item->spatium();
     920            0 :     ldata->arpeggioHeight = ArpeggioLayout::computeHeight(item);
     921            0 :     ldata->top = ArpeggioLayout::calcTop(item, conf) - 0.25 * spatium;
     922            0 :     ldata->bottom = ArpeggioLayout::calcBottom(item, ldata->arpeggioHeight, conf) + 0.5 * spatium;
     923              : 
     924            0 :     const double minLen = 2 * item->spatium();
     925            0 :     double diff = minLen - std::abs(ldata->bottom);
     926            0 :     if (diff > 0) {
     927            0 :         ldata->bottom += diff;
     928            0 :         if (item->hookPos() == DirectionV::DOWN) {
     929            0 :             ldata->top -= diff;
     930            0 :         } else if (item->hookPos() == DirectionV::AUTO) {
     931            0 :             ldata->top -= 0.5 * diff;
     932              :         }
     933              :     }
     934              : 
     935            0 :     ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
     936            0 :     ldata->magS = conf.magS(ldata->mag());
     937              : 
     938            0 :     ldata->setShape(Shape(RectF(0.0, ldata->top, item->absoluteFromSpatium(item->hookLength()), ldata->bottom).normalized(), item));
     939              : 
     940            0 :     const Note* upnote = item->chord()->upNote();
     941            0 :     ldata->setPosY(upnote->y() + upnote->ldata()->bbox().top());
     942              : 
     943            0 :     Shape chordShape = getChordsShape(item);
     944            0 :     Shape itemShape = item->shape().translated(PointF(0.0, item->ldata()->pos().y()));
     945            0 :     if (item->rightSide()) {
     946            0 :         double x = HorizontalSpacing::minHorizontalDistance(chordShape, itemShape, spatium);
     947            0 :         x = std::max(x, 0.0);
     948            0 :         ldata->setPosX(x);
     949              :     } else {
     950            0 :         double x = HorizontalSpacing::minHorizontalDistance(itemShape, chordShape, spatium);
     951            0 :         x = std::max(x, 0.0);
     952            0 :         ldata->setPosX(-x);
     953              :     }
     954              : 
     955              :     // Loop through staves spanned & regenerate chord shape
     956              :     // This makes sure the arpeggio's shape is added to the shape of each chord it spans
     957            0 :     Chord* chord = item->chord();
     958            0 :     Segment* seg = chord->segment();
     959            0 :     staff_idx_t staveSpan = (item->track() + item->span() - 1) / VOICES;
     960            0 :     for (staff_idx_t staffIdx = item->staffIdx(); staffIdx <= staveSpan; staffIdx++) {
     961            0 :         seg->createShape(staffIdx);
     962              :     }
     963            0 : }
     964              : 
     965         3435 : void TLayout::layoutArticulation(Articulation* item, Articulation::LayoutData* ldata)
     966              : {
     967              :     LAYOUT_CALL_ITEM(item);
     968         3435 :     if (item->isHiddenOnTabStaff()) {
     969            0 :         ldata->setIsSkipDraw(true);
     970            0 :         return;
     971              :     }
     972         3435 :     ldata->setIsSkipDraw(false);
     973         3435 :     ldata->setPos(PointF());
     974              : 
     975              :     //! NOTE Must already be set previously
     976         3435 :     LD_CONDITION(ldata->symId.has_value());
     977              : 
     978         3435 :     if (item->textType() != ArticulationTextType::NO_TEXT) {
     979           68 :         if (!item->text()) {
     980           28 :             Text* text = new Text(item, TextStyleType::ARTICULATION);
     981           28 :             static const ElementStyle elementStyle;
     982           28 :             text->initElementStyle(&elementStyle);
     983           28 :             item->setText(text);
     984              :         }
     985              : 
     986           68 :         Text* text = item->text();
     987           68 :         text->setXmlText(TConv::text(item->textType()));
     988           68 :         text->setTrack(item->track());
     989           68 :         text->setOwnershipParent(item);
     990           68 :         text->setSelected(item->selected());
     991              : 
     992           68 :         TextLayout::layoutBaseTextBase(item->text(), item->text()->mutldata());
     993              :     }
     994              : 
     995         3435 :     fillArticulationShape(item, ldata);
     996              : }
     997              : 
     998         3435 : void TLayout::fillArticulationShape(const Articulation* item, Articulation::LayoutData* ldata)
     999              : {
    1000              :     LAYOUT_CALL_ITEM(item);
    1001              : 
    1002         3435 :     SymId sym = item->symId();
    1003         3435 :     if (sym == SymId::articAccentAbove || sym == SymId::articAccentBelow) {
    1004          424 :         RectF symBbox = item->symBbox(sym);
    1005          424 :         double width = symBbox.width();
    1006          424 :         double height = symBbox.height();
    1007          424 :         double thirdWidth = width / 3;
    1008          424 :         double thirdHeight = height / 3;
    1009          424 :         RectF base = RectF(0.0, 0.0, thirdWidth, height);
    1010          424 :         RectF center = RectF(thirdWidth, thirdHeight, thirdWidth, thirdHeight);
    1011          424 :         RectF tip = RectF(2 * thirdWidth, 1.25 * thirdHeight, thirdWidth, 0.5 * thirdHeight);
    1012          424 :         Shape shape;
    1013          424 :         shape.add(base, item);
    1014          424 :         shape.add(center, item);
    1015          424 :         shape.add(tip, item);
    1016          424 :         PointF translate(0.0, sym == SymId::articAccentAbove ? -height : 0.0);
    1017          424 :         ldata->setShape(shape.translate(translate));
    1018         3435 :     } else if (item->textType() == ArticulationTextType::NO_TEXT) {
    1019         2943 :         ldata->setShape(Shape(item->symBbox(sym), item));
    1020              :     } else {
    1021           68 :         ldata->setShape(Shape(item->text()->ldata()->bbox(), item));
    1022              :     }
    1023         3435 : }
    1024              : 
    1025       112638 : static double barLineWidth(const BarLine* item, const MStyle& style, double dotWidth)
    1026              : {
    1027       112638 :     double w = 0.0;
    1028       112638 :     switch (item->barLineType()) {
    1029          963 :     case BarLineType::DOUBLE:
    1030          963 :         w = style.styleAbsolute(Sid::doubleBarWidth) * 2.0
    1031          963 :             + style.styleAbsolute(Sid::doubleBarDistance);
    1032          963 :         break;
    1033           32 :     case BarLineType::DOUBLE_HEAVY:
    1034           32 :         w = style.styleAbsolute(Sid::endBarWidth) * 2.0
    1035           32 :             + style.styleAbsolute(Sid::endBarDistance);
    1036           32 :         break;
    1037            0 :     case BarLineType::END_START_REPEAT:
    1038            0 :         w = style.styleAbsolute(Sid::endBarWidth)
    1039            0 :             + style.styleAbsolute(Sid::barWidth) * 2.0
    1040            0 :             + style.styleAbsolute(Sid::endBarDistance) * 2.0
    1041            0 :             + style.styleAbsolute(Sid::repeatBarlineDotSeparation) * 2.0
    1042            0 :             + dotWidth * 2;
    1043            0 :         break;
    1044         1385 :     case BarLineType::START_REPEAT:
    1045              :     case BarLineType::END_REPEAT:
    1046         1385 :         w = style.styleAbsolute(Sid::endBarWidth)
    1047         1385 :             + style.styleAbsolute(Sid::barWidth)
    1048         1385 :             + style.styleAbsolute(Sid::endBarDistance)
    1049         1385 :             + style.styleAbsolute(Sid::repeatBarlineDotSeparation)
    1050              :             + dotWidth;
    1051         1385 :         break;
    1052        11649 :     case BarLineType::END:
    1053              :     case BarLineType::REVERSE_END:
    1054        11649 :         w = style.styleAbsolute(Sid::endBarWidth)
    1055        11649 :             + style.styleAbsolute(Sid::barWidth)
    1056        11649 :             + style.styleAbsolute(Sid::endBarDistance);
    1057        11649 :         break;
    1058           82 :     case BarLineType::BROKEN:
    1059           82 :         w = style.styleAbsolute(Sid::dashBarWidth);
    1060           82 :         break;
    1061        98487 :     case BarLineType::NORMAL:
    1062              :     case BarLineType::DOTTED:
    1063        98487 :         w = style.styleAbsolute(Sid::barWidth);
    1064        98487 :         break;
    1065           40 :     case BarLineType::HEAVY:
    1066           40 :         w = style.styleAbsolute(Sid::endBarWidth);
    1067           40 :         break;
    1068              :     }
    1069       112638 :     return w;
    1070              : }
    1071              : 
    1072       112638 : void TLayout::layoutBarLine(const BarLine* item, BarLine::LayoutData* ldata, const LayoutContext& ctx)
    1073              : {
    1074              :     LAYOUT_CALL_ITEM(item);
    1075              : 
    1076              :     // barlines hidden on this staff
    1077       112638 :     if (item->staff() && item->segment()) {
    1078       112638 :         if ((!item->staff()->staffTypeForElement(item)->showBarlines() && item->segment()->segmentType() == SegmentType::EndBarLine)
    1079       112638 :             || (item->staff()->hideSystemBarLine() && item->segment()->segmentType() == SegmentType::BeginBarLine)) {
    1080            0 :             ldata->setPos(PointF());
    1081            0 :             ldata->setBbox(RectF());
    1082            0 :             ldata->setIsSkipDraw(true);
    1083            0 :             return;
    1084              :         }
    1085              :     }
    1086              : 
    1087       112638 :     ldata->setIsSkipDraw(false);
    1088       112638 :     ldata->setPos(PointF());
    1089              : 
    1090              :     // Check conditions
    1091              : 
    1092              :     //! NOTE Here the goal is not just to check whether the conditions are met,
    1093              :     //! but the goal is to clearly show these conditions to the developer.
    1094       112638 :     for (const EngravingItem* e : *item->el()) {
    1095            0 :         switch (e->type()) {
    1096            0 :         case ElementType::ARTICULATION:
    1097              :             // form Articulation layout
    1098            0 :             LD_CONDITION(item_cast<const Articulation*>(e)->ldata()->symId.has_value());
    1099            0 :             break;
    1100            0 :         case ElementType::SYMBOL:
    1101              :             LD_INDEPENDENT;
    1102            0 :             break;
    1103            0 :         case ElementType::IMAGE:
    1104              :             // will be done
    1105              :             // LD_CONDITION(parentLD->isSetBbox());
    1106              :             LD_INDEPENDENT;
    1107            0 :             break;
    1108            0 :         default:
    1109            0 :             UNREACHABLE;
    1110              :         }
    1111              :     }
    1112              : 
    1113       112638 :     ldata->setMag(ctx.conf().styleB(Sid::scaleBarlines) && item->staff() ? item->staff()->staffMag(item->tick()) : 1.0);
    1114              :     // Note: the true values of y1 and y2 are computed in layout2() (can be done only
    1115              :     // after staff distances are known). This is a temporary layout.
    1116       112638 :     const double spatium = item->spatium();
    1117              : 
    1118       112638 :     ldata->y1 = spatium * .5 * item->spanFrom();
    1119       112638 :     ldata->y2 = spatium * .5 * (8.0 + item->spanTo());
    1120              : 
    1121       112638 :     const IEngravingFontPtr font = ctx.engravingFont();
    1122       112638 :     const double magS = ctx.conf().magS(ldata->mag());
    1123              : 
    1124       112638 :     double w = barLineWidth(item, ctx.conf().style(), font->width(SymId::repeatDot, magS)) * ldata->mag();
    1125       112638 :     RectF r(0.0, ldata->y1, w, ldata->y2 - ldata->y1);
    1126              : 
    1127       112638 :     if (ctx.conf().styleB(Sid::repeatBarTips)) {
    1128          157 :         switch (item->barLineType()) {
    1129            4 :         case BarLineType::START_REPEAT: {
    1130            4 :             r.unite(font->bbox(SymId::bracketTop, magS).translated(0, ldata->y1));
    1131            4 :         } break;
    1132            8 :         case BarLineType::END_REPEAT: {
    1133            8 :             double w1 = font->width(SymId::reversedBracketTop, magS) - w;
    1134            8 :             r.unite(font->bbox(SymId::reversedBracketTop, magS).translated(-w1, ldata->y1));
    1135            8 :         } break;
    1136            0 :         case BarLineType::END_START_REPEAT: {
    1137            0 :             double w1 = font->width(SymId::reversedBracketTop, magS) - w;
    1138            0 :             r.unite(font->bbox(SymId::reversedBracketTop, magS).translated(-w1, ldata->y1));
    1139            0 :             r.unite(font->bbox(SymId::bracketTop, magS).translated(0, ldata->y1));
    1140            0 :         } break;
    1141          145 :         default:
    1142          145 :             break;
    1143              :         }
    1144              :     }
    1145              : 
    1146       112638 :     ldata->setBbox(r);
    1147              : 
    1148       112638 :     updateBarlineShape(item, ldata, ctx);
    1149              : 
    1150              :     //! NOTE The types are listed here explicitly to show what types there are (see add method)
    1151              :     //! and accordingly show what the barline layout depends on.
    1152       112638 :     for (EngravingItem* e : *item->el()) {
    1153            0 :         switch (e->type()) {
    1154            0 :         case ElementType::ARTICULATION: {
    1155            0 :             Articulation* a = item_cast<Articulation*>(e);
    1156            0 :             Articulation::LayoutData* aldata = a->mutldata();
    1157            0 :             TLayout::layoutArticulation(a, aldata);
    1158            0 :             DirectionV dir = a->direction();
    1159            0 :             double distance = 0.5 * spatium;
    1160            0 :             double x = /*barline*/ ldata->bbox().width() * 0.5;
    1161            0 :             if (dir == DirectionV::DOWN) {
    1162            0 :                 double botY = ldata->y2 + distance;
    1163            0 :                 aldata->setPos(PointF(x, botY));
    1164              :             } else {
    1165            0 :                 double topY = ldata->y1 - distance;
    1166            0 :                 aldata->setPos(PointF(x, topY));
    1167              :             }
    1168            0 :         } break;
    1169            0 :         case ElementType::SYMBOL: {
    1170            0 :             Symbol* sb = item_cast<Symbol*>(e);
    1171            0 :             TLayout::layoutSymbol(sb, sb->mutldata(), ctx);
    1172            0 :         } break;
    1173            0 :         case ElementType::IMAGE: {
    1174            0 :             Image* im = item_cast<Image*>(e);
    1175            0 :             TLayout::layoutImage(im, im->mutldata());
    1176            0 :         } break;
    1177            0 :         default:
    1178            0 :             UNREACHABLE;
    1179              :         }
    1180              :     }
    1181              : 
    1182       112638 :     if (!item->segment()) {
    1183            0 :         return;
    1184              :     }
    1185              : 
    1186       112638 :     if (Fermata* fermata = toFermata(item->segment()->findAnnotation(ElementType::FERMATA, item->track(), item->track() + VOICES))) {
    1187           52 :         layoutFermata(fermata, fermata->mutldata());
    1188              :     }
    1189              : }
    1190              : 
    1191       164959 : void TLayout::updateBarlineShape(const BarLine* item, BarLine::LayoutData* ldata, const LayoutContext& ctx)
    1192              : {
    1193              :     LAYOUT_CALL_ITEM(item);
    1194              : 
    1195       164959 :     RectF bb = ldata->bbox();
    1196       164959 :     if (item->staff()) {
    1197              :         // actual height may include span to next staff
    1198              :         // but this should not be included in shapes or skylines
    1199       164959 :         double sp = item->spatium();
    1200       164959 :         int span = item->staff()->lines(item->tick()) - 1;
    1201              :         int sFrom;
    1202              :         int sTo;
    1203       164959 :         if (span == 0 && item->spanTo() == 0) {
    1204          177 :             sFrom = BARLINE_SPAN_1LINESTAFF_FROM;
    1205          177 :             sTo = item->spanStaff() ? 0 : BARLINE_SPAN_1LINESTAFF_TO;
    1206              :         } else {
    1207       164782 :             sFrom = item->spanFrom();
    1208       164782 :             sTo = item->spanStaff() ? 0 : item->spanTo();
    1209              :         }
    1210       164959 :         double y = sp * sFrom * 0.5;
    1211       164959 :         double h = sp * (span + (sTo - sFrom) * 0.5);
    1212       164959 :         if (ctx.conf().styleB(Sid::repeatBarTips)) {
    1213          232 :             switch (item->barLineType()) {
    1214           20 :             case BarLineType::START_REPEAT:
    1215              :             case BarLineType::END_REPEAT:
    1216              :             case BarLineType::END_START_REPEAT: {
    1217           20 :                 if (item->isTop()) {
    1218           10 :                     double top = item->symBbox(SymId::bracketTop).height();
    1219           10 :                     y -= top;
    1220           10 :                     h += top;
    1221              :                 }
    1222           20 :                 if (item->isBottom()) {
    1223           10 :                     double bottom = item->symBbox(SymId::bracketBottom).height();
    1224           10 :                     h += bottom;
    1225              :                 }
    1226              :             }
    1227              :             default:
    1228          232 :                 break;
    1229              :             }
    1230              :         }
    1231       164959 :         bb.setTop(y);
    1232       164959 :         bb.setHeight(h);
    1233              :     }
    1234              : 
    1235       164959 :     ldata->setShape(Shape(bb, item));
    1236       164959 : }
    1237              : 
    1238              : //---------------------------------------------------------
    1239              : //    called after system layout; set vertical dimensions
    1240              : //---------------------------------------------------------
    1241        55635 : void TLayout::layoutBarLine2(BarLine* item, LayoutContext& ctx)
    1242              : {
    1243              :     LAYOUT_CALL_ITEM(item);
    1244              : 
    1245        55635 :     BarLine::LayoutData* ldata = item->mutldata();
    1246              : 
    1247        55635 :     if (ldata->isSkipDraw()) {
    1248            0 :         return;
    1249              :     }
    1250              : 
    1251        55635 :     item->calcY();
    1252        55635 :     RectF bbox = ldata->bbox();
    1253        55635 :     bbox.setTop(ldata->y1);
    1254        55635 :     bbox.setBottom(ldata->y2);
    1255              : 
    1256        55635 :     if (ctx.conf().styleB(Sid::repeatBarTips)) {
    1257           75 :         switch (item->barLineType()) {
    1258            4 :         case BarLineType::START_REPEAT:
    1259            4 :             bbox.unite(item->symBbox(SymId::bracketTop).translated(0, ldata->y1));
    1260            4 :             bbox.unite(item->symBbox(SymId::bracketBottom).translated(0, ldata->y2));
    1261            4 :             break;
    1262            4 :         case BarLineType::END_REPEAT:
    1263              :         {
    1264            4 :             double w1 = item->symBbox(SymId::reversedBracketTop).x() - bbox.x();
    1265            4 :             bbox.unite(item->symBbox(SymId::reversedBracketTop).translated(-w1, ldata->y1));
    1266            4 :             bbox.unite(item->symBbox(SymId::reversedBracketBottom).translated(-w1, ldata->y2));
    1267            4 :             break;
    1268              :         }
    1269            0 :         case BarLineType::END_START_REPEAT:
    1270              :         {
    1271            0 :             double w1 = item->symBbox(SymId::reversedBracketTop).x() - bbox.x();
    1272            0 :             bbox.unite(item->symBbox(SymId::reversedBracketTop).translated(-w1, ldata->y1));
    1273            0 :             bbox.unite(item->symBbox(SymId::reversedBracketBottom).translated(-w1, ldata->y2));
    1274            0 :             bbox.unite(item->symBbox(SymId::bracketTop).translated(0, ldata->y1));
    1275            0 :             bbox.unite(item->symBbox(SymId::bracketBottom).translated(0, ldata->y2));
    1276            0 :             break;
    1277              :         }
    1278           67 :         default:
    1279           67 :             break;
    1280              :         }
    1281              :     }
    1282              : 
    1283        55635 :     ldata->setBbox(bbox);
    1284              : }
    1285              : 
    1286         8751 : void TLayout::layoutBeam(Beam* item, const LayoutContext& ctx)
    1287              : {
    1288              :     LAYOUT_CALL_ITEM(item);
    1289         8751 :     BeamLayout::layout(item, ctx);
    1290         8751 : }
    1291              : 
    1292          109 : void TLayout::layoutBeam1(Beam* item, LayoutContext& ctx)
    1293              : {
    1294              :     LAYOUT_CALL_ITEM(item);
    1295          109 :     BeamLayout::layout1(item, ctx);
    1296          109 : }
    1297              : 
    1298            4 : void TLayout::layoutBend(const Bend* item, Bend::LayoutData* ldata)
    1299              : {
    1300              :     LAYOUT_CALL_ITEM(item);
    1301            4 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    1302            0 :         return;
    1303              :     }
    1304              : 
    1305            4 :     LD_CONDITION(item->note()->ldata()->isSetPos());
    1306            4 :     LD_CONDITION(item->note()->ldata()->isSetBbox());
    1307              : 
    1308            4 :     double spatium = item->spatium();
    1309            4 :     double lw = item->absoluteFromSpatium(item->lineWidth());
    1310              : 
    1311            4 :     const Note::LayoutData* noteLD = item->note()->ldata();
    1312            4 :     PointF notePos = noteLD->pos();
    1313            4 :     notePos.ry() = std::max(notePos.y(), 0.0);
    1314              : 
    1315            4 :     ldata->noteWidth = noteLD->bbox().width();
    1316            4 :     ldata->notePos = notePos;
    1317              : 
    1318            4 :     RectF bb;
    1319              : 
    1320            4 :     FontMetrics fm(item->font(spatium));
    1321              : 
    1322            4 :     size_t n = item->points().size();
    1323            4 :     double x = ldata->noteWidth;
    1324            4 :     double y = -spatium * .8;
    1325            4 :     double x2 = 0.0, y2 = 0.0;
    1326              : 
    1327            4 :     double aw = spatium * .5;
    1328            4 :     PolygonF arrowUp;
    1329            4 :     arrowUp << PointF(0, 0) << PointF(aw * .5, aw) << PointF(-aw * .5, aw);
    1330            4 :     PolygonF arrowDown;
    1331            4 :     arrowDown << PointF(0, 0) << PointF(aw * .5, -aw) << PointF(-aw * .5, -aw);
    1332              : 
    1333            8 :     for (size_t pt = 0; pt < n; ++pt) {
    1334            8 :         if (pt == (n - 1)) {
    1335            0 :             break;
    1336              :         }
    1337            8 :         int pitch = item->points().at(pt).pitch;
    1338            8 :         if (pt == 0 && pitch) {
    1339            0 :             y2 = -ldata->notePos.y() - spatium * 2;
    1340            0 :             x2 = x;
    1341            0 :             bb.unite(RectF(x, y, x2 - x, y2 - y));
    1342              : 
    1343            0 :             bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
    1344              : 
    1345            0 :             int idx = (pitch + 12) / 25;
    1346            0 :             const char* l = Bend::label[idx];
    1347            0 :             bb.unite(fm.boundingRect(String::fromAscii(l)));
    1348            0 :             y = y2;
    1349              :         }
    1350            8 :         if (pitch == item->points().at(pt + 1).pitch) {
    1351            4 :             if (pt == (n - 2)) {
    1352            4 :                 break;
    1353              :             }
    1354            0 :             x2 = x + spatium;
    1355            0 :             y2 = y;
    1356            0 :             bb.unite(RectF(x, y, x2 - x, y2 - y));
    1357            4 :         } else if (pitch < item->points().at(pt + 1).pitch) {
    1358              :             // up
    1359            4 :             x2 = x + spatium * .5;
    1360            4 :             y2 = -ldata->notePos.y() - spatium * 2;
    1361            4 :             double dx = x2 - x;
    1362            4 :             double dy = y2 - y;
    1363              : 
    1364            4 :             PainterPath path;
    1365            4 :             path.moveTo(x, y);
    1366            4 :             path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
    1367            4 :             bb.unite(path.boundingRect());
    1368            4 :             bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
    1369              : 
    1370            4 :             int idx = (item->points().at(pt + 1).pitch + 12) / 25;
    1371            4 :             const char* l = Bend::label[idx];
    1372            4 :             bb.unite(fm.boundingRect(String::fromAscii(l)));
    1373            4 :         } else {
    1374              :             // down
    1375            0 :             x2 = x + spatium * .5;
    1376            0 :             y2 = y + spatium * 3;
    1377            0 :             double dx = x2 - x;
    1378            0 :             double dy = y2 - y;
    1379              : 
    1380            0 :             PainterPath path;
    1381            0 :             path.moveTo(x, y);
    1382            0 :             path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
    1383            0 :             bb.unite(path.boundingRect());
    1384              : 
    1385            0 :             bb.unite(arrowDown.translated(x2, y2 - spatium * .2).boundingRect());
    1386            0 :         }
    1387            4 :         x = x2;
    1388            4 :         y = y2;
    1389              :     }
    1390            4 :     bb.adjust(-lw, -lw, lw, lw);
    1391              : 
    1392            4 :     ldata->setBbox(bb);
    1393            4 :     ldata->setPos(PointF());
    1394            4 : }
    1395              : 
    1396          147 : void TLayout::layoutHBox(const HBox* item, HBox::LayoutData* ldata, const LayoutContext& ctx)
    1397              : {
    1398              :     LAYOUT_CALL_ITEM(item);
    1399          147 :     BoxLayout::layoutHBox(item, ldata, ctx);
    1400          147 : }
    1401              : 
    1402            0 : void TLayout::layoutVBox(const VBox* item, VBox::LayoutData* ldata, const LayoutContext& ctx)
    1403              : {
    1404              :     LAYOUT_CALL_ITEM(item);
    1405            0 :     BoxLayout::layoutVBox(item, ldata, ctx);
    1406            0 : }
    1407              : 
    1408            0 : void TLayout::layoutFBox(const FBox* item, FBox::LayoutData* ldata, const LayoutContext& ctx)
    1409              : {
    1410              :     LAYOUT_CALL_ITEM(item);
    1411              : 
    1412            0 :     BoxLayout::layoutFBox(item, ldata, ctx);
    1413            0 : }
    1414              : 
    1415            0 : void TLayout::layoutTBox(const TBox* item, TBox::LayoutData* ldata, const LayoutContext& ctx)
    1416              : {
    1417              :     LAYOUT_CALL_ITEM(item);
    1418            0 :     BoxLayout::layoutTBox(item, ldata, ctx);
    1419            0 : }
    1420              : 
    1421         7436 : void TLayout::layoutBracket(const Bracket* item, Bracket::LayoutData* ldata, const LayoutConfiguration& conf)
    1422              : {
    1423              :     LAYOUT_CALL_ITEM(item);
    1424         7436 :     LD_CONDITION(ldata->bracketHeight.has_value());
    1425         7436 :     if (!ldata->bracketHeight.has_value()) {
    1426            0 :         return;
    1427              :     }
    1428              : 
    1429         7436 :     const_cast<Bracket*>(item)->setVisible(item->bi()->visible());
    1430              : 
    1431         7436 :     ldata->braceSymbol = item->braceSymbol();
    1432              : 
    1433         7436 :     switch (item->bracketType()) {
    1434         4532 :     case BracketType::BRACE: {
    1435         4532 :         String musicalSymbolFont = conf.styleSt(Sid::musicalSymbolFont);
    1436         4532 :         if (musicalSymbolFont == "Emmentaler" || musicalSymbolFont == "Gonville") {
    1437          759 :             ldata->braceSymbol = SymId::noSym;
    1438          759 :             double w = conf.styleAbsolute(Sid::akkoladeWidth);
    1439          759 :             double h2 = ldata->bracketHeight * 0.5;
    1440              : 
    1441              : #define XM(a) (a + 700) * w / 700
    1442              : #define YM(a) (a + 7100) * h2 / 7100
    1443              : 
    1444          759 :             PainterPath path;
    1445          759 :             path.moveTo(XM(-8), YM(-2048));
    1446          759 :             path.cubicTo(XM(-8), YM(-3192), XM(-360), YM(-4304), XM(-360), YM(-5400));                 // c 0
    1447          759 :             path.cubicTo(XM(-360), YM(-5952), XM(-264), YM(-6488), XM(32), YM(-6968));                 // c 1
    1448          759 :             path.cubicTo(XM(36), YM(-6974), XM(38), YM(-6984), XM(38), YM(-6990));                     // c 0
    1449          759 :             path.cubicTo(XM(38), YM(-7008), XM(16), YM(-7024), XM(0), YM(-7024));                      // c 0
    1450          759 :             path.cubicTo(XM(-8), YM(-7024), XM(-22), YM(-7022), XM(-32), YM(-7008));                   // c 1
    1451          759 :             path.cubicTo(XM(-416), YM(-6392), XM(-544), YM(-5680), XM(-544), YM(-4960));               // c 0
    1452          759 :             path.cubicTo(XM(-544), YM(-3800), XM(-168), YM(-2680), XM(-168), YM(-1568));               // c 0
    1453          759 :             path.cubicTo(XM(-168), YM(-1016), XM(-264), YM(-496), XM(-560), YM(-16));                  // c 1
    1454          759 :             path.lineTo(XM(-560), YM(0));                    //  l 1
    1455          759 :             path.lineTo(XM(-560), YM(16));                   //  l 1
    1456          759 :             path.cubicTo(XM(-264), YM(496), XM(-168), YM(1016), XM(-168), YM(1568));                   // c 0
    1457          759 :             path.cubicTo(XM(-168), YM(2680), XM(-544), YM(3800), XM(-544), YM(4960));                  // c 0
    1458          759 :             path.cubicTo(XM(-544), YM(5680), XM(-416), YM(6392), XM(-32), YM(7008));                   // c 1
    1459          759 :             path.cubicTo(XM(-22), YM(7022), XM(-8), YM(7024), XM(0), YM(7024));                        // c 0
    1460          759 :             path.cubicTo(XM(16), YM(7024), XM(38), YM(7008), XM(38), YM(6990));                        // c 0
    1461          759 :             path.cubicTo(XM(38), YM(6984), XM(36), YM(6974), XM(32), YM(6968));                        // c 1
    1462          759 :             path.cubicTo(XM(-264), YM(6488), XM(-360), YM(5952), XM(-360), YM(5400));                  // c 0
    1463          759 :             path.cubicTo(XM(-360), YM(4304), XM(-8), YM(3192), XM(-8), YM(2048));                      // c 0
    1464          759 :             path.cubicTo(XM(-8), YM(1320), XM(-136), YM(624), XM(-512), YM(0));                        // c 1
    1465          759 :             path.cubicTo(XM(-136), YM(-624), XM(-8), YM(-1320), XM(-8), YM(-2048));                    // c 0*/
    1466          759 :             ldata->path = path;
    1467          759 :             ldata->setBbox(path.boundingRect());
    1468          759 :             ldata->shape.add(ldata->bbox());
    1469          759 :             ldata->bracketWidth = w + conf.styleAbsolute(Sid::akkoladeBarDistance);
    1470          759 :         } else {
    1471         3773 :             if (item->braceSymbol() == SymId::noSym) {
    1472            0 :                 ldata->braceSymbol = SymId::brace;
    1473              :             }
    1474         3773 :             double h = ldata->bracketHeight;
    1475         3773 :             double w = item->symWidth(ldata->braceSymbol) * item->magx();
    1476         3773 :             ldata->setBbox(RectF(0, 0, w, h));
    1477         3773 :             ldata->shape.add(ldata->bbox());
    1478         3773 :             ldata->bracketWidth = w + conf.styleAbsolute(Sid::akkoladeBarDistance);
    1479              :         }
    1480         4532 :     }
    1481         4532 :     break;
    1482         2269 :     case BracketType::NORMAL: {
    1483         2269 :         double w = conf.styleAbsolute(Sid::bracketWidth) * .5;
    1484         2269 :         double bd = item->spatium() * (conf.styleSt(Sid::musicalSymbolFont) == "Leland" ? .5 : .25);
    1485              : 
    1486         2269 :         Shape shape = RectF(-w, -bd, w * 2, 2 * (ldata->bracketHeight * 0.5 + bd));
    1487         2269 :         shape.add(item->symBbox(SymId::bracketTop).translated(PointF(-w, -bd)));
    1488         2269 :         shape.add(item->symBbox(SymId::bracketBottom).translated(PointF(-w, bd + ldata->bracketHeight)));
    1489         2269 :         ldata->setShape(shape);
    1490              : 
    1491         2269 :         ldata->bracketWidth = conf.styleAbsolute(Sid::bracketWidth) + conf.styleAbsolute(Sid::bracketDistance);
    1492         2269 :     }
    1493         2269 :     break;
    1494          593 :     case BracketType::SQUARE:
    1495              :     {
    1496          593 :         double w = conf.styleAbsolute(Sid::staffLineWidth) * .5;
    1497          593 :         double x = -w;
    1498          593 :         double y = -w;
    1499          593 :         double h = (ldata->bracketHeight * 0.5 + w) * 2;
    1500          593 :         w += (.5 * item->spatium() + 2 * w);
    1501          593 :         ldata->setBbox(RectF(x, y, w, h));
    1502          593 :         ldata->shape.add(ldata->bbox());
    1503              : 
    1504          593 :         ldata->bracketWidth = conf.styleAbsolute(Sid::staffLineWidth) / 2 + 0.5 * item->spatium();
    1505              :     }
    1506          593 :     break;
    1507           42 :     case BracketType::LINE: {
    1508           42 :         double _spatium = item->spatium();
    1509           42 :         double w = 0.67 * conf.styleAbsolute(Sid::bracketWidth) * .5;
    1510           42 :         double x = -w;
    1511           42 :         double bd = _spatium * .25;
    1512           42 :         double y = -bd;
    1513           42 :         double h = (-y + ldata->bracketHeight * 0.5) * 2;
    1514           42 :         ldata->setBbox(RectF(x, y, w, h));
    1515           42 :         ldata->shape.add(ldata->bbox());
    1516              : 
    1517           42 :         ldata->bracketWidth = 0.67 * conf.styleAbsolute(Sid::bracketWidth) + conf.styleAbsolute(Sid::bracketDistance);
    1518              :     }
    1519           42 :     break;
    1520            0 :     case BracketType::GROUP:
    1521            0 :         layoutGroupBracket(item, ldata, conf);
    1522            0 :         break;
    1523            0 :     case BracketType::NO_BRACKET:
    1524            0 :         break;
    1525              :     }
    1526              : }
    1527              : 
    1528            0 : void TLayout::layoutGroupBracket(const Bracket* item, Bracket::LayoutData* ldata, const LayoutConfiguration& conf)
    1529              : {
    1530            0 :     BracketItem* bracketItem = item->bracketItem();
    1531              : 
    1532            0 :     double w = conf.styleAbsolute(Sid::groupBracketLineWidth) * 0.5;
    1533            0 :     double x = 0.0;
    1534            0 :     double y = -w;
    1535            0 :     double h = (ldata->bracketHeight * 0.5 + w) * 2;
    1536            0 :     double width = conf.styleAbsolute(Sid::groupBracketHookLen);
    1537              : 
    1538            0 :     ldata->bracketWidth = std::max(width, 2 * w);
    1539              : 
    1540            0 :     Shape shape;
    1541            0 :     shape.add(RectF(x, y, width, h), item);
    1542            0 :     ldata->setShape(shape);
    1543              : 
    1544            0 :     if (!item->text()) {
    1545            0 :         Text* bracketText = new Text(const_cast<Bracket*>(item), TextStyleType::GROUP_BRACKET);
    1546            0 :         bracketText->setSelected(item->selected());
    1547            0 :         bracketText->setGenerated(true);
    1548            0 :         const_cast<Bracket*>(item)->setText(bracketText);
    1549              :     }
    1550              : 
    1551            0 :     Text* text = item->text();
    1552            0 :     text->setVisible(bracketItem->visible() && bracketItem->showText());
    1553            0 :     text->setAlign(Align(text->align().horizontal, AlignV::VCENTER));
    1554            0 :     text->setPosition(AlignH::HCENTER);
    1555              : 
    1556            0 :     Orientation textOrientation = conf.styleV(Sid::groupBracketTextOrientation).value<Orientation>();
    1557            0 :     text->setTextAngle(textOrientation == Orientation::VERTICAL ? -90.0 : 0.0);
    1558              : 
    1559            0 :     double textPadding = 0.5 * item->spatium();
    1560            0 :     double bracketHeight = item->ldata()->bbox().height();
    1561            0 :     double minHeight = bracketHeight - 6 * textPadding;
    1562              : 
    1563            0 :     if (textOrientation == Orientation::HORIZONTAL) {
    1564            0 :         text->setXmlText(item->labelType() == InstrumentNameType::LONG ? bracketItem->longName() : bracketItem->shortName());
    1565            0 :         layoutText(text, text->mutldata());
    1566              :     } else {
    1567              :         // Vertical labels ignore labelType(): use the long name if it fits, otherwise the short name
    1568            0 :         text->setXmlText(bracketItem->longName());
    1569            0 :         layoutText(text, text->mutldata());
    1570            0 :         if (text->ldata()->bbox().height() > minHeight) {
    1571            0 :             text->setXmlText(bracketItem->shortName());
    1572            0 :             layoutText(text, text->mutldata());
    1573              :         }
    1574              :     }
    1575              : 
    1576            0 :     text->mutldata()->setPosY(bracketHeight / 2);
    1577              : 
    1578            0 :     switch (conf.styleV(Sid::groupBracketTextAlign).value<DirectionH>()) {
    1579            0 :     case DirectionH::AUTO:
    1580              :     {
    1581            0 :         text->mutldata()->setPosX(0.0);
    1582            0 :         if (text->visible()) {
    1583            0 :             RectF mask = text->ldata()->bbox().translated(text->pos()).padded(textPadding);
    1584            0 :             ldata->setMask(mask);
    1585              :         }
    1586            0 :         break;
    1587              :     }
    1588            0 :     case DirectionH::LEFT:
    1589            0 :         text->mutldata()->setPosX(-text->mutldata()->bbox().right() - textPadding);
    1590            0 :         break;
    1591            0 :     case DirectionH::RIGHT:
    1592            0 :         text->mutldata()->setPosX(-text->mutldata()->bbox().left() + textPadding);
    1593              :     }
    1594              : 
    1595            0 :     shape.add(text->ldata()->shape().translated(text->pos()));
    1596            0 :     ldata->setShape(shape);
    1597            0 : }
    1598              : 
    1599          168 : void TLayout::layoutBreath(const Breath* item, Breath::LayoutData* ldata, const LayoutConfiguration& conf)
    1600              : {
    1601              :     LAYOUT_CALL_ITEM(item);
    1602              :     LD_INDEPENDENT;
    1603              : 
    1604              :     // TODO review with all other instances of this
    1605              :     // if (ldata->isValid()) {
    1606              :     // return;
    1607              :     // }
    1608              : 
    1609          168 :     const double voiceOffset = item->placeBelow() ? item->staff()->staffHeight(item->tick()) : 0.0;
    1610          168 :     if (item->isCaesura()) {
    1611           42 :         if (item->symId() == SymId::chantCaesura) {
    1612              :             // special handling for chant caesura, which is higher than the normal one
    1613            2 :             ldata->setPosY(1.5 * item->spatium() + voiceOffset);
    1614              :         } else {
    1615           40 :             ldata->setPosY(item->symHeight(item->symId()) / 2 + voiceOffset);
    1616              :         }
    1617          126 :     } else if ((conf.styleSt(Sid::musicalSymbolFont) == "Emmentaler") && (item->symId() == SymId::breathMarkComma)) {
    1618           15 :         const double shift = item->placeBelow() ? -0.5 * item->spatium() + item->symHeight(item->symId()) : 0.5 * item->spatium();
    1619           15 :         ldata->setPosY(shift + voiceOffset);
    1620              :     } else {
    1621          111 :         const double shift = item->placeBelow() ? 0.5 * item->spatium() + item->symHeight(item->symId()) : -0.5 * item->spatium();
    1622          111 :         ldata->setPosY(shift + voiceOffset);
    1623              :     }
    1624              : 
    1625          168 :     ldata->setBbox(item->symBbox(item->symId()));
    1626              : 
    1627          168 :     ldata->setPosX(-ldata->bbox().right());
    1628          168 : }
    1629              : 
    1630        49746 : void TLayout::layoutChord(Chord* item, LayoutContext& ctx)
    1631              : {
    1632              :     LAYOUT_CALL_ITEM(item);
    1633        49746 :     ChordLayout::layout(item, ctx);
    1634        49746 : }
    1635              : 
    1636          168 : void TLayout::layoutChordLine(const ChordLine* item, ChordLine::LayoutData* ldata, const LayoutConfiguration& conf)
    1637              : {
    1638              :     LAYOUT_CALL_ITEM(item);
    1639          168 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    1640            0 :         return;
    1641              :     }
    1642              : 
    1643          168 :     Note* note = nullptr;
    1644          168 :     const double spatium = item->spatium();
    1645              : 
    1646          168 :     if (item->note()) {
    1647          115 :         note = item->chord()->findNote(item->note()->pitch());
    1648              :     }
    1649              : 
    1650          168 :     if (!note) {
    1651           53 :         note = item->chord()->upNote();
    1652              :     }
    1653              : 
    1654          168 :     const Note::LayoutData* noteLD = note->ldata();
    1655              : 
    1656              :     //! NOTE Temporary, instead of item->chord()->shape()
    1657          168 :     LD_CONDITION(item->chord()->ldata()->isSetBbox());
    1658              : 
    1659          168 :     LD_CONDITION(noteLD->isSetPos());
    1660          168 :     LD_CONDITION(noteLD->isSetBbox());
    1661              : 
    1662          168 :     ldata->setMag(item->chord()->mag());
    1663              : 
    1664          168 :     layoutChordLinePath(item, ldata, spatium, item->chordMag());
    1665              : 
    1666          168 :     double x = 0.0;
    1667          168 :     double y = noteLD->pos().y();
    1668          168 :     double horOffset = 0.33 * spatium;         // one third of a space away from the note
    1669          168 :     double vertOffset = 0.25 * spatium;         // one quarter of a space from the center line
    1670              :     // Get chord shape
    1671          168 :     Shape chordShape = item->chord()->shape();
    1672              :     // ...but remove from the shape items that the chordline shouldn't try to avoid
    1673              :     // (especially the chordline itself)
    1674          168 :     chordShape.remove_if([](ShapeElement& shapeEl){
    1675          378 :         if (!shapeEl.item()) {
    1676            0 :             return true;
    1677              :         }
    1678          378 :         const EngravingItem* item = shapeEl.item();
    1679          378 :         if (item->isChordLine() || item->isHarmony() || item->isLyrics()) {
    1680            1 :             return true;
    1681              :         }
    1682          377 :         return false;
    1683              :     });
    1684              : 
    1685          168 :     const RectF r = item->isWavy() ? conf.engravingFont()->bbox(item->waveSym(), item->magS()) : ldata->path.boundingRect();
    1686              : 
    1687          168 :     y += item->isBelow() ? vertOffset : -vertOffset;
    1688              : 
    1689          168 :     const double yBelow = y + r.height();
    1690              :     double chordEdge
    1691          168 :         = item->isToTheLeft() ? chordShape.leftMostEdgeAtHeight(y, yBelow) : chordShape.rightMostEdgeAtHeight(y, yBelow);
    1692              : 
    1693          168 :     if (!item->isToTheLeft()) {
    1694              :         // Always place lines to the right of dots
    1695           91 :         double xMin = chordShape.left();
    1696          286 :         for (ShapeElement& shapeEl : chordShape.elements()) {
    1697          195 :             if (!shapeEl.item() || !shapeEl.item()->isNoteDot()) {
    1698          195 :                 continue;
    1699              :             }
    1700              : 
    1701            0 :             xMin = std::max(xMin, shapeEl.right());
    1702              :         }
    1703           91 :         chordEdge = std::max(chordEdge, xMin);
    1704              :     }
    1705              : 
    1706          168 :     x += item->isToTheLeft() ? chordEdge - horOffset : chordEdge + horOffset;
    1707          168 :     ldata->setPos(x, y);
    1708              : 
    1709          168 :     if (!item->isWavy()) {
    1710          162 :         int x1 = 0, y1 = 0, width = 0, height = 0;
    1711              : 
    1712          162 :         x1 = r.x();
    1713          162 :         y1 = r.y();
    1714          162 :         width = r.width();
    1715          162 :         height = r.height();
    1716          162 :         ldata->setBbox(x1, y1, width, height);
    1717              :     } else {
    1718              :         // Align an eighth space above/below the centre of the note
    1719            6 :         if (item->isBelow()) {
    1720            4 :             ldata->moveY(r.height() - spatium * 0.125);
    1721              :         } else {
    1722            2 :             ldata->moveY(spatium * 0.075);
    1723              :         }
    1724              : 
    1725            6 :         if (item->isToTheLeft()) {
    1726            2 :             ldata->moveX(-r.width());
    1727              :         }
    1728              : 
    1729            6 :         ldata->setBbox(r);
    1730              :     }
    1731          168 : }
    1732              : 
    1733          174 : void TLayout::layoutChordLinePath(const ChordLine* item, ChordLine::LayoutData* ldata, double spatium, double mag)
    1734              : {
    1735          174 :     ldata->path = PainterPath();
    1736          174 :     if (item->chordLineType() == ChordLineType::NOTYPE || item->isWavy()) {
    1737            7 :         return;
    1738              :     }
    1739              : 
    1740          167 :     const ChordLine::Points defaultPoints = item->defaultPoints();
    1741          167 :     const ChordLine::Points& offsets = item->bezierOffsets();
    1742          167 :     ChordLine::Points points;
    1743         1002 :     for (size_t i = 0; i < points.size(); ++i) {
    1744          668 :         points[i] = (defaultPoints[i] * spatium + offsets[i]) * mag;
    1745              :     }
    1746              : 
    1747          167 :     PainterPath path;
    1748          167 :     path.moveTo(points[size_t(ChordLine::Point::START)]);
    1749          167 :     if (item->isStraight()) {
    1750           95 :         path.lineTo(points[size_t(ChordLine::Point::END)]);
    1751              :     } else {
    1752           72 :         path.cubicTo(points[size_t(ChordLine::Point::BEZIER1)], points[size_t(ChordLine::Point::BEZIER2)],
    1753           72 :                      points[size_t(ChordLine::Point::END)]);
    1754              :     }
    1755          167 :     ldata->path = path;
    1756          167 : }
    1757              : 
    1758        24448 : void TLayout::layoutClef(const Clef* item, Clef::LayoutData* ldata, const LayoutConfiguration& conf)
    1759              : {
    1760              :     LAYOUT_CALL_ITEM(item);
    1761              :     LD_INDEPENDENT;
    1762              : 
    1763              :     // determine current number of lines and line distance
    1764        24448 :     int lines = 5;
    1765        24448 :     Spatium lineDist = 1.0_sp;
    1766        24448 :     int stepOffset = 0;
    1767        24448 :     Spatium staffOffsetY = 0_sp;
    1768              : 
    1769        24448 :     Segment* clefSeg  = item->segment();
    1770              : 
    1771              :     // check clef visibility and type compatibility
    1772        24448 :     if (clefSeg && item->staff()) {
    1773        24448 :         StaffType* stVisibility = item->staff()->staffType(item->tick());
    1774        24448 :         bool show = stVisibility->genClef();            // check staff type allows clef display
    1775              : 
    1776        24448 :         const bool endOfMeasureClef = clefSeg->rtick() == clefSeg->measure()->ticks();
    1777        24448 :         const Fraction tick = endOfMeasureClef && !item->isTrailer() ? item->tick() : clefSeg->measure()->tick();
    1778              : 
    1779        24448 :         const Fraction tickPrev = tick - Fraction::eps();
    1780        24448 :         const StaffType* st = item->staff()->staffType(tick);
    1781        24448 :         const StaffType* stPrev = !tickPrev.negative() ? item->staff()->staffType(tickPrev) : nullptr;
    1782        24448 :         StaffGroup staffGroup = st->group();
    1783        24448 :         const bool hideClef = st->isTabStaff() ? conf.styleB(Sid::hideTabClefAfterFirst) : !conf.styleB(Sid::genClef);
    1784              : 
    1785              :         // if not tab, use instrument->useDrumset to set staffGroup (to allow pitched to unpitched in same staff)
    1786        24448 :         if (staffGroup != StaffGroup::TAB) {
    1787        23853 :             staffGroup = item->originPart()->instrument(item->tick())->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
    1788              :         }
    1789              : 
    1790              :         // check clef is compatible with staff type group:
    1791        24448 :         if (ClefInfo::staffGroup(item->clefType()) != staffGroup) {
    1792          448 :             if (tick > Fraction(0, 1) && (!item->generated() || hideClef)) {     // if clef is not generated, hide it
    1793            0 :                 show = false;
    1794              :             } else {                            // if generated, replace with initial clef type
    1795              :                 // TODO : instead of initial staff clef (which is assumed to be compatible)
    1796              :                 // use the last compatible clef previously found in staff
    1797          448 :                 const_cast<Clef*>(item)->setClefType(item->staff()->clefType(Fraction(0, 1)));
    1798              :             }
    1799              :         }
    1800              : 
    1801              :         // if clef not to show or not compatible with staff group
    1802        24448 :         if (!show) {
    1803            0 :             ldata->setBbox(RectF());
    1804            0 :             ldata->symId = SymId::noSym;
    1805            0 :             LOGD("invisible clef at tick %d(%d) staff %zu",
    1806            0 :                  item->segment()->tick().ticks(), item->segment()->tick().ticks() / 1920, item->staffIdx());
    1807            0 :             return;
    1808              :         }
    1809        24448 :         lines      = st->lines();             // init values from staff type
    1810        24448 :         lineDist   = st->lineDistance();
    1811        24448 :         stepOffset = st->stepOffset();
    1812              : 
    1813        24448 :         const Spatium stOffset = st->yoffset();
    1814        24448 :         const Spatium stPrevOffset = stPrev && clefSeg->rtick() != Fraction(0, 1) ? stPrev->yoffset() : 0.0_sp;
    1815        24448 :         staffOffsetY = item->isHeader() ? 0.0_sp : stOffset - stPrevOffset;
    1816              :     }
    1817              : 
    1818        24448 :     double _spatium = item->spatium();
    1819        24448 :     Spatium yoff     = 0.0_sp;
    1820        24448 :     if (item->clefType() != ClefType::INVALID && item->clefType() != ClefType::MAX) {
    1821        24448 :         ldata->symId = ClefInfo::symId(item->clefType());
    1822        24448 :         yoff = lineDist * (5 - ClefInfo::line(item->clefType()));
    1823              :     } else {
    1824            0 :         ldata->symId = SymId::noSym;
    1825              :     }
    1826              : 
    1827        24448 :     switch (item->clefType()) {
    1828            3 :     case ClefType::C_19C:                                    // 19th C clef is like a G clef
    1829            3 :         yoff = lineDist * 1.5;
    1830            3 :         break;
    1831          595 :     case ClefType::TAB:                                     // TAB clef
    1832              :     case ClefType::TAB4:                                    // TAB clef 4 strings
    1833              :     case ClefType::TAB_SERIF:                               // TAB clef alternate style
    1834              :     case ClefType::TAB4_SERIF:                              // TAB clef alternate style
    1835              :         // on tablature, position clef at half the number of spaces * line distance
    1836          595 :         yoff = lineDist * (lines - 1) * .5;
    1837          595 :         stepOffset = 0;           //  ignore stepOffset for TAB and percussion clefs
    1838          595 :         break;
    1839         1109 :     case ClefType::PERC:                                   // percussion clefs
    1840              :     case ClefType::PERC2:
    1841         1109 :         yoff = lineDist * (lines - 1) * 0.5;
    1842         1109 :         stepOffset = 0;
    1843         1109 :         break;
    1844            0 :     case ClefType::INVALID:
    1845              :     case ClefType::MAX:
    1846            0 :         LOGD("Clef::layout: invalid type");
    1847            0 :         return;
    1848        22741 :     default:
    1849        22741 :         break;
    1850              :     }
    1851              :     // clefs on palette or at start of system/measure are left aligned
    1852              :     // other clefs are right aligned
    1853        48896 :     Shape shape(item->symShapeWithCutouts(ldata->symId));
    1854        24448 :     bool isMidMeasureClef = item->isMidMeasureClef();
    1855        24448 :     double x = isMidMeasureClef ? -shape.right() : 0.0;
    1856        24448 :     ldata->setPos(PointF(x, (yoff + (Spatium(stepOffset) * 0.5) + staffOffsetY).toAbsolute(_spatium)));
    1857        24448 :     if (item->isMidMeasureClef()) {
    1858          744 :         ldata->setShape(shape);
    1859              :     } else {
    1860        23704 :         ldata->setBbox(item->symBbox(ldata->symId));
    1861              :     }
    1862              : }
    1863              : 
    1864           44 : void TLayout::layoutCapo(const Capo* item, Capo::LayoutData* ldata, const LayoutContext&)
    1865              : {
    1866              :     LAYOUT_CALL_ITEM(item);
    1867              :     //! NOTE Looks like it doesn't belong here
    1868           44 :     if (item->shouldAutomaticallyGenerateText() || item->empty()) {
    1869           44 :         if (const Part* part = item->originPart()) {
    1870           44 :             if (const StringData* stringData = part->stringData(item->tick(), item->staff()->idx())) {
    1871           44 :                 String text = item->generateText(stringData->strings());
    1872           44 :                 const_cast<Capo*>(item)->setXmlText(text);
    1873           44 :             }
    1874              :         }
    1875              :     }
    1876              : 
    1877           44 :     TextLayout::layoutBaseTextBase(item, ldata);
    1878              : 
    1879           44 :     if (item->autoplace()) {
    1880           44 :         const Segment* s = item->segment();
    1881           44 :         const Measure* m = s->measure();
    1882           44 :         LD_CONDITION(ldata->isSetPos());
    1883           44 :         LD_CONDITION(m->ldata()->isSetPos());
    1884           44 :         LD_CONDITION(s->ldata()->isSetPos());
    1885              :     }
    1886              : 
    1887           44 :     Autoplace::autoplaceSegmentElement(item, ldata);
    1888           44 : }
    1889              : 
    1890            0 : void TLayout::layoutDeadSlapped(const DeadSlapped* item, DeadSlapped::LayoutData* ldata)
    1891              : {
    1892              :     LAYOUT_CALL_ITEM(item);
    1893              :     LD_INDEPENDENT;
    1894              : 
    1895            0 :     if (ldata->isValid()) {
    1896            0 :         return;
    1897              :     }
    1898              : 
    1899            0 :     const double deadSlappedWidth = item->spatium() * 2;
    1900            0 :     RectF rect = RectF(0, 0, deadSlappedWidth, item->staff()->staffHeight());
    1901            0 :     ldata->setBbox(rect);
    1902            0 :     ldata->setPos(PointF());
    1903              : 
    1904              :     // fillPath
    1905              :     {
    1906            0 :         constexpr double crossThinknessPercentage = 0.1;
    1907            0 :         double height = rect.height();
    1908            0 :         double width = rect.width();
    1909            0 :         double crossThickness = width * crossThinknessPercentage;
    1910              : 
    1911            0 :         PointF topLeft = PointF(rect.x(), rect.y());
    1912            0 :         PointF bottomRight = topLeft + PointF(width, height);
    1913            0 :         PointF topRight = topLeft + PointF(width, 0);
    1914            0 :         PointF bottomLeft = topLeft + PointF(0, height);
    1915            0 :         PointF offsetX = PointF(crossThickness, 0);
    1916              : 
    1917            0 :         PainterPath path1;
    1918            0 :         path1.moveTo(topLeft);
    1919            0 :         path1.lineTo(topLeft + offsetX);
    1920            0 :         path1.lineTo(bottomRight);
    1921            0 :         path1.lineTo(bottomRight - offsetX);
    1922            0 :         path1.lineTo(topLeft);
    1923              : 
    1924            0 :         PainterPath path2;
    1925            0 :         path2.moveTo(topRight);
    1926            0 :         path2.lineTo(topRight - offsetX);
    1927            0 :         path2.lineTo(bottomLeft);
    1928            0 :         path2.lineTo(bottomLeft + offsetX);
    1929            0 :         path2.lineTo(topRight);
    1930              : 
    1931            0 :         ldata->path1 = path1;
    1932            0 :         ldata->path2 = path2;
    1933            0 :     }
    1934              : }
    1935              : 
    1936         1145 : void TLayout::layoutDynamic(Dynamic* item, Dynamic::LayoutData* ldata, const LayoutConfiguration& conf)
    1937              : {
    1938         1145 :     DynamicsLayout::layoutDynamic(item, ldata, conf);
    1939         1145 : }
    1940              : 
    1941           34 : void TLayout::layoutExpression(const Expression* item, Expression::LayoutData* ldata)
    1942              : {
    1943              :     LAYOUT_CALL_ITEM(item);
    1944           34 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    1945            0 :         return;
    1946              :     }
    1947              : 
    1948           34 :     const_cast<Expression*>(item)->setPlacementBasedOnVoiceAssignment(item->style().styleV(
    1949              :                                                                           Sid::dynamicsHairpinVoiceBasedPlacement).value<DirectionV>());
    1950              : 
    1951           34 :     TextLayout::layoutBaseTextBase(item, ldata);
    1952              : 
    1953           34 :     const Segment* segment = item->segment();
    1954              : 
    1955           34 :     if (item->align().horizontal != AlignH::LEFT) {
    1956            0 :         Chord* chordToAlign = nullptr;
    1957              :         // Look for chord in this staff
    1958            0 :         track_idx_t startTrack = track2staff(item->staffIdx());
    1959            0 :         track_idx_t endTrack = startTrack + VOICES;
    1960            0 :         for (track_idx_t track = startTrack; track < endTrack; ++track) {
    1961            0 :             EngravingItem* e = segment->element(track);
    1962            0 :             if (e && e->isChord()) {
    1963            0 :                 chordToAlign = toChord(e);
    1964            0 :                 break;
    1965              :             }
    1966              :         }
    1967              : 
    1968            0 :         if (chordToAlign && !chordToAlign->notes().empty()) {
    1969            0 :             const Note* note = chordToAlign->notes().at(0);
    1970            0 :             double headWidth = note->headWidth();
    1971            0 :             bool center = item->align().horizontal == AlignH::HCENTER;
    1972            0 :             ldata->moveX(headWidth * (center ? 0.5 : 1));
    1973              :         }
    1974              :     }
    1975              : 
    1976           34 :     ldata->disconnectSnappedItems();
    1977              : 
    1978           34 :     if (!item->autoplace()) {
    1979            0 :         return;
    1980              :     }
    1981              : 
    1982           34 :     const Segment* s = item->segment();
    1983           34 :     const Measure* m = s->measure();
    1984           34 :     LD_CONDITION(ldata->isSetPos());
    1985           34 :     LD_CONDITION(m->ldata()->isSetPos());
    1986           34 :     LD_CONDITION(s->ldata()->isSetPos());
    1987              : 
    1988           34 :     Dynamic* dynamic = toDynamic(segment->findAnnotation(ElementType::DYNAMIC, item->track(), item->track()));
    1989           34 :     if (!dynamic) {
    1990           32 :         Segment* timeTickSeg = m->findSegmentR(SegmentType::TimeTick, s->rtick());
    1991           32 :         dynamic = timeTickSeg ? toDynamic(timeTickSeg->findAnnotation(ElementType::DYNAMIC, item->track(), item->track())) : nullptr;
    1992              :     }
    1993            2 :     if (!dynamic || dynamic->placeAbove() != item->placeAbove() || dynamic->voiceAssignment() != item->voiceAssignment()
    1994           36 :         || !dynamic->visible()) {
    1995           32 :         Autoplace::autoplaceSegmentElement(item, ldata);
    1996           32 :         return;
    1997              :     }
    1998              : 
    1999            2 :     LD_CONDITION(dynamic->ldata()->isSetBbox()); // dynamic->shape()
    2000            2 :     LD_CONDITION(dynamic->ldata()->isSetPos());
    2001              : 
    2002              :     // If there is a dynamic on same segment and track make space for it horizontally
    2003            2 :     double padding = item->computeDynamicExpressionDistance(dynamic);
    2004            2 :     double dynamicRight = dynamic->shape().translate(dynamic->pos()).right();
    2005            2 :     double expressionLeft = ldata->bbox().translated(item->pos()).left();
    2006            2 :     double difference = expressionLeft - dynamicRight - padding;
    2007            2 :     ldata->moveX(-difference);
    2008              : 
    2009            2 :     if (item->snapToDynamics()) {
    2010            2 :         ldata->connectItemSnappedBefore(dynamic);
    2011              :     }
    2012              : 
    2013            2 :     Autoplace::autoplaceSegmentElement(item, ldata);
    2014              : }
    2015              : 
    2016          618 : void TLayout::layoutFermata(const Fermata* item, Fermata::LayoutData* ldata)
    2017              : {
    2018              :     LAYOUT_CALL_ITEM(item);
    2019          618 :     const StaffType* stType = item->staffType();
    2020          618 :     if (stType && stType->isHiddenElementOnTab(Sid::fermataShowTabCommon, Sid::fermataShowTabSimple)) {
    2021            0 :         ldata->setIsSkipDraw(true);
    2022            0 :         return;
    2023              :     }
    2024          618 :     ldata->setIsSkipDraw(false);
    2025          618 :     ldata->setPos(PointF());
    2026              : 
    2027          618 :     double x = 0.0;
    2028          618 :     double y = item->placeAbove() ? 0.0 : item->staff()->staffHeight(item->tick());
    2029          618 :     const Segment* s = item->segment();
    2030          618 :     const track_idx_t layoutParentTrack = s->isType(SegmentType::BarLineTypes) ? item->staffIdx() * VOICES : item->track();
    2031          618 :     const EngravingItem* e = s->element(layoutParentTrack);
    2032              : 
    2033          618 :     if (e) {
    2034          618 :         LD_CONDITION(e->ldata()->isSetBbox()); // e->shape()
    2035          618 :         LD_CONDITION(e->ldata()->isSetPos());
    2036              : 
    2037          618 :         if (e->isChord()) {
    2038          481 :             const Chord* chord = toChord(e);
    2039          481 :             x = chord->x() + ChordLayout::centerX(chord);
    2040          137 :         } else if (e->isRest()) {
    2041           56 :             const Rest* rest = toRest(e);
    2042           56 :             x = rest->x() + rest->centerX();
    2043              :         } else {
    2044           81 :             x = e->x() + e->shape().left() + e->width() * item->staff()->staffMag(Fraction(0, 1)) * .5;
    2045              :         }
    2046              :     }
    2047              : 
    2048          618 :     String name = String::fromAscii(SymNames::nameForSymId(item->symId()).ascii());
    2049          618 :     if (item->placeAbove()) {
    2050          517 :         if (name.endsWith(u"Below")) {
    2051              :             //! NOTE It is not clear whether SymId is layout data or given data.
    2052            0 :             const_cast<Fermata*>(item)->setSymId(SymNames::symIdByName(name.left(name.size() - 5) + u"Above"));
    2053              :         }
    2054              :     } else {
    2055          101 :         if (name.endsWith(u"Above")) {
    2056              :             //! NOTE It is not clear whether SymId is layout data or given data.
    2057           19 :             const_cast<Fermata*>(item)->setSymId(SymNames::symIdByName(name.left(name.size() - 5) + u"Below"));
    2058              :         }
    2059              :     }
    2060              : 
    2061          618 :     ldata->setShape(Shape(item->symBbox(item->symId()), item));
    2062          618 :     x -= 0.5 * ldata->bbox().width();
    2063              : 
    2064          618 :     ldata->setPos(x, y);
    2065              : 
    2066          618 :     PointF defaultPos = item->defaultPos();
    2067          618 :     ldata->move(defaultPos);
    2068              : 
    2069          618 :     if (item->autoplace()) {
    2070          618 :         const Segment* s2 = item->segment();
    2071          618 :         const Measure* m = s2->measure();
    2072          618 :         LD_CONDITION(ldata->isSetPos());
    2073          618 :         LD_CONDITION(m->ldata()->isSetPos());
    2074          618 :         LD_CONDITION(s2->ldata()->isSetPos());
    2075              :     }
    2076              : 
    2077          618 :     Autoplace::autoplaceSegmentElement(item, ldata);
    2078          618 : }
    2079              : 
    2080              : //---------------------------------------------------------
    2081              : //   FiguredBassItem layout
    2082              : //    creates the display text (set as element text) and computes
    2083              : //    the horiz. offset needed to align the right part as well as the vert. offset
    2084              : //---------------------------------------------------------
    2085          211 : void TLayout::layoutFiguredBassItem(const FiguredBassItem* item, FiguredBassItem::LayoutData* ldata, const LayoutContext& ctx)
    2086              : {
    2087              :     LAYOUT_CALL_ITEM(item);
    2088              :     // construct font metrics
    2089          211 :     int fontIdx = 0;
    2090          211 :     Font f(FiguredBass::FBFonts().at(fontIdx).family, Font::Type::Tablature);
    2091              : 
    2092              :     // font size in pixels, scaled according to spatium()
    2093              :     // (use the same font selection as used in draw() below)
    2094          211 :     double m = ctx.conf().styleD(Sid::figuredBassFontSize) * item->spatium() / item->defaultSpatium();
    2095          211 :     f.setPointSizeF(m);
    2096          211 :     FontMetrics fm(f);
    2097              : 
    2098          211 :     String str;
    2099          211 :     double x = item->symWidth(SymId::noteheadBlack) * .5;
    2100          211 :     double x1 = 0.0;
    2101          211 :     double x2 = 0.0;
    2102              : 
    2103              :     // create display text
    2104          211 :     int font = 0;
    2105          211 :     int style = ctx.conf().styleI(Sid::figuredBassStyle);
    2106              : 
    2107          211 :     if (item->parenth1() != FiguredBassItem::Parenthesis::NONE) {
    2108            0 :         str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth1())]);
    2109              :     }
    2110              : 
    2111              :     // prefix
    2112          211 :     if (item->prefix() != FiguredBassItem::Modifier::NONE) {
    2113              :         // if no digit, the string created so far 'hangs' to the left of the note
    2114           18 :         if (item->digit() == FBIDigitNone) {
    2115            8 :             x1 = fm.width(str);
    2116              :         }
    2117           18 :         str.append(FiguredBass::FBFonts().at(font).displayAccidental[int(item->prefix())]);
    2118              :         // if no digit, the string from here onward 'hangs' to the right of the note
    2119           18 :         if (item->digit() == FBIDigitNone) {
    2120            8 :             x2 = fm.width(str);
    2121              :         }
    2122              :     }
    2123              : 
    2124          211 :     if (item->parenth2() != FiguredBassItem::Parenthesis::NONE) {
    2125            4 :         str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth2())]);
    2126              :     }
    2127              : 
    2128              :     // digit
    2129          211 :     if (item->digit() != FBIDigitNone) {
    2130              :         // if some digit, the string created so far 'hangs' to the left of the note
    2131          183 :         x1 = fm.width(str);
    2132              :         // if suffix is a combining shape, combine it with digit (multi-digit numbers cannot be combined)
    2133              :         // unless there is a parenthesis in between
    2134          183 :         if ((item->digit() < 10)
    2135          183 :             && (item->suffix() == FiguredBassItem::Modifier::CROSS
    2136          178 :                 || item->suffix() == FiguredBassItem::Modifier::BACKSLASH
    2137          176 :                 || item->suffix() == FiguredBassItem::Modifier::SLASH)
    2138          366 :             && item->parenth3() == FiguredBassItem::Parenthesis::NONE) {
    2139           11 :             str.append(FiguredBass::FBFonts().at(font).displayDigit[style][item->digit()][int(item->suffix())
    2140           11 :                                                                                           - (int(FiguredBassItem::Modifier::CROSS)
    2141              :                                                                                              - 1)]);
    2142              :         }
    2143              :         // if several digits or no shape combination, convert _digit to font styled chars
    2144              :         else {
    2145          172 :             String digits;
    2146          172 :             int digit = item->digit();
    2147              :             while (true) {
    2148          172 :                 digits.prepend(FiguredBass::FBFonts().at(font).displayDigit[style][(digit % 10)][0]);
    2149          172 :                 digit /= 10;
    2150          172 :                 if (digit == 0) {
    2151          172 :                     break;
    2152              :                 }
    2153              :             }
    2154          172 :             str.append(digits);
    2155          172 :         }
    2156              :         // if some digit, the string from here onward 'hangs' to the right of the note
    2157          183 :         x2 = fm.width(str);
    2158              :     }
    2159              : 
    2160          211 :     if (item->parenth3() != FiguredBassItem::Parenthesis::NONE) {
    2161            4 :         str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth3())]);
    2162              :     }
    2163              : 
    2164              :     // suffix
    2165              :     // append only if non-combining shape or cannot combine (no digit or parenthesis in between)
    2166          211 :     if (item->suffix() != FiguredBassItem::Modifier::NONE
    2167          243 :         && ((item->suffix() != FiguredBassItem::Modifier::CROSS
    2168           27 :              && item->suffix() != FiguredBassItem::Modifier::BACKSLASH
    2169           25 :              && item->suffix() != FiguredBassItem::Modifier::SLASH)
    2170           11 :             || item->digit() == FBIDigitNone
    2171           11 :             || item->parenth3() != FiguredBassItem::Parenthesis::NONE)) {
    2172           21 :         str.append(FiguredBass::FBFonts().at(font).displayAccidental[int(item->suffix())]);
    2173              :     }
    2174              : 
    2175          211 :     if (item->parenth4() != FiguredBassItem::Parenthesis::NONE) {
    2176            0 :         str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth4())]);
    2177              :     }
    2178              : 
    2179              :     //! NOTE Look like, this text not layout data (not depends by position)
    2180          211 :     ldata->displayText = str;             // this text will be displayed
    2181              : 
    2182          211 :     if (str.size()) {                     // if some text
    2183          191 :         x = x - (x1 + x2) * 0.5;          // position the text so that [x1<-->x2] is centered below the note
    2184              :     } else {                              // if no text (but possibly a line)
    2185           20 :         x = 0;                            // start at note left margin
    2186              :     }
    2187              :     // vertical position
    2188          211 :     double h = fm.lineSpacing();
    2189          211 :     h *= ctx.conf().styleD(Sid::figuredBassLineHeight);
    2190          211 :     double y = 0.0;
    2191          211 :     if (ctx.conf().styleI(Sid::figuredBassAlignment) == 0) {          // top alignment: stack down from first item
    2192          211 :         y = h * item->ord();
    2193              :     } else {                                                      // bottom alignment: stack up from last item
    2194            0 :         y = -h * (item->figuredBass()->itemsCount() - item->ord());
    2195              :     }
    2196          211 :     ldata->setPos(x, y);
    2197              :     // determine bbox from text width
    2198              : //      w = fm.width(str);
    2199          211 :     double w = fm.width(str);
    2200          211 :     ldata->textWidth = w;
    2201              :     // if there is a cont.line, extend width to cover the whole FB element duration line
    2202          211 :     int lineLen = 0;
    2203          211 :     if (item->contLine() != FiguredBassItem::ContLine::NONE
    2204          211 :         && (lineLen = item->figuredBass()->ldata()->lineLength(0)) > w) {
    2205           32 :         w = lineLen;
    2206              :     }
    2207          211 :     ldata->setBbox(0, 0, w, h);
    2208          211 : }
    2209              : 
    2210              : //    lays out the duration indicator line(s), filling the _lineLengths array
    2211              : //    and the length of printed lines (used by continuation lines)
    2212              : 
    2213          136 : static void layoutLines(const FiguredBass* item, FiguredBass::LayoutData* ldata, const LayoutContext& ctx)
    2214              : {
    2215          136 :     std::vector<double> lineLengths = ldata->lineLengths;
    2216          136 :     if (item->ticks() <= Fraction(0, 1) || !item->segment()) {
    2217            7 :         lineLengths.resize(1);                             // be sure to always have
    2218            7 :         lineLengths[0] = 0;                                // at least 1 item in array
    2219            7 :         ldata->lineLengths = lineLengths;
    2220            7 :         return;
    2221              :     }
    2222              : 
    2223          129 :     ChordRest* lastCR  = nullptr;                         // the last ChordRest of this
    2224          129 :     Segment* nextSegm = nullptr;                          // the Segment beyond this' segment
    2225          129 :     Fraction nextTick = item->segment()->tick() + item->ticks();       // the tick beyond this' duration
    2226              : 
    2227              :     // locate the measure containing the last tick of this; it is either:
    2228              :     // the same measure containing nextTick, if nextTick is not the first tick of a measure
    2229              :     //    (and line should stop right before it)
    2230              :     // or the previous measure, if nextTick is the first tick of a measure
    2231              :     //    (and line should stop before any measure terminal segment (bar, clef, ...) )
    2232              : 
    2233          129 :     const Measure* m = ctx.dom().tick2measure(nextTick - Fraction::eps());
    2234          129 :     if (m) {
    2235              :         // locate the first segment (of ANY type) right after this' last tick
    2236          625 :         for (nextSegm = m->first(SegmentType::All); nextSegm; nextSegm = nextSegm->next()) {
    2237          625 :             if (nextSegm->tick() >= nextTick) {
    2238          129 :                 break;
    2239              :             }
    2240              :         }
    2241              :         // locate the last ChordRest of this
    2242          129 :         if (nextSegm) {
    2243          129 :             track_idx_t startTrack = trackZeroVoice(item->track());
    2244          129 :             track_idx_t endTrack = startTrack + VOICES;
    2245          143 :             for (const Segment* seg = nextSegm->prev1(); seg; seg = seg->prev1()) {
    2246          199 :                 for (track_idx_t t = startTrack; t < endTrack; ++t) {
    2247          185 :                     EngravingItem* el = seg->element(t);
    2248          185 :                     if (el && el->isChordRest()) {
    2249          129 :                         lastCR = toChordRest(el);
    2250          129 :                         break;
    2251              :                     }
    2252              :                 }
    2253          143 :                 if (lastCR) {
    2254          129 :                     break;
    2255              :                 }
    2256              :             }
    2257              :         }
    2258              :     }
    2259          129 :     if (!m || !nextSegm) {
    2260            0 :         LOGD("FiguredBass layout: no segment found for tick %d", nextTick.ticks());
    2261            0 :         lineLengths.resize(1);                             // be sure to always have
    2262            0 :         lineLengths[0] = 0;                                // at least 1 item in array
    2263            0 :         ldata->lineLengths = lineLengths;
    2264            0 :         return;
    2265              :     }
    2266              : 
    2267              :     // get length of printed lines from horiz. page position of lastCR
    2268              :     // (enter a bit 'into' the ChordRest for clarity)
    2269          129 :     double printedLineLength = lastCR ? lastCR->pageX() - item->pageX() + 1.5 * item->spatium() : 3 * item->spatium();
    2270          129 :     ldata->printedLineLength = printedLineLength;
    2271              : 
    2272              :     // get duration indicator line(s) from page position of nextSegm
    2273          129 :     const std::vector<System*>& systems = ctx.dom().systems();
    2274          129 :     System* s1  = item->segment()->measure()->system();
    2275          129 :     System* s2  = nextSegm->measure()->system();
    2276          129 :     system_idx_t sysIdx1 = muse::indexOf(systems, s1);
    2277          129 :     system_idx_t sysIdx2 = muse::indexOf(systems, s2);
    2278              : 
    2279          129 :     if (sysIdx2 == muse::nidx || sysIdx2 < sysIdx1) {
    2280            0 :         sysIdx2 = sysIdx1;
    2281            0 :         nextSegm = item->segment()->next1();
    2282              :         // TODO
    2283              :         // During layout of figured bass next systems' numbers may be still
    2284              :         // undefined (then sysIdx2 == muse::nidx) or change in the future.
    2285              :         // A layoutSystem() approach similar to that for spanners should
    2286              :         // probably be implemented.
    2287              :     }
    2288              : 
    2289              :     system_idx_t i;
    2290              :     int len;
    2291          129 :     size_t segIdx = 0;
    2292          258 :     for (i = sysIdx1, segIdx = 0; i <= sysIdx2; ++i, ++segIdx) {
    2293          129 :         len = 0;
    2294          129 :         if (sysIdx1 == sysIdx2 || i == sysIdx1) {
    2295              :             // single line
    2296          129 :             len = nextSegm->pageX() - item->pageX() - 4;               // stop 4 raster units before next segm
    2297            0 :         } else if (i == sysIdx1) {
    2298              :             // initial line
    2299            0 :             double w   = s1->staff(item->staffIdx())->bbox().right();
    2300            0 :             double x   = s1->pageX() + w;
    2301            0 :             len = x - item->pageX();
    2302            0 :         } else if (i > 0 && i != sysIdx2) {
    2303              :             // middle line
    2304            0 :             LOGD("FiguredBass: duration indicator middle line not implemented");
    2305            0 :         } else if (i == sysIdx2) {
    2306              :             // end line
    2307            0 :             LOGD("FiguredBass: duration indicator end line not implemented");
    2308              :         }
    2309              :         // store length item, reusing array items if already present
    2310          129 :         if (lineLengths.size() <= segIdx) {
    2311           74 :             lineLengths.push_back(len);
    2312              :         } else {
    2313           55 :             lineLengths[segIdx] = len;
    2314              :         }
    2315              :     }
    2316              :     // if more array items than needed, truncate array
    2317          129 :     if (lineLengths.size() > segIdx) {
    2318            0 :         lineLengths.resize(segIdx);
    2319              :     }
    2320              : 
    2321          129 :     ldata->lineLengths = lineLengths;
    2322          136 : }
    2323              : 
    2324          156 : void TLayout::layoutFiguredBass(const FiguredBass* item, FiguredBass::LayoutData* ldata, const LayoutContext& ctx)
    2325              : {
    2326              :     LAYOUT_CALL_ITEM(item);
    2327              :     // VERTICAL POSITION:
    2328          156 :     const double y = ctx.conf().styleD(Sid::figuredBassYOffset) * item->spatium();
    2329          156 :     ldata->setPos(PointF(0.0, y));
    2330              : 
    2331              :     // BOUNDING BOX and individual item layout (if required)
    2332          156 :     TextLayout::layoutBaseTextBase1(item, ldata);  // prepare structs and data expected by Text methods
    2333              :     // if element could be parsed into items, layout each element
    2334              :     // Items list will be empty in edit mode (see FiguredBass::startEdit).
    2335              :     // TODO: consider disabling specific layout in case text style is changed (tid() != TextStyleName::FIGURED_BASS).
    2336          156 :     if (item->items().size() > 0) {
    2337          136 :         Shape shape;
    2338          136 :         layoutLines(item, ldata, ctx);
    2339              :         // layout each item and enlarge bbox to include items bboxes
    2340          347 :         for (FiguredBassItem* fit : item->items()) {
    2341          211 :             FiguredBassItem::LayoutData* fildata = fit->mutldata();
    2342          211 :             layoutFiguredBassItem(fit, fildata, ctx);
    2343          211 :             shape.add(fildata->bbox().translated(fit->pos()), fit);
    2344              :         }
    2345          136 :         ldata->setShape(shape);
    2346          136 :     }
    2347          156 : }
    2348              : 
    2349          408 : void TLayout::layoutFingering(const Fingering* item, Fingering::LayoutData* ldata)
    2350              : {
    2351              :     LAYOUT_CALL_ITEM(item);
    2352          408 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    2353           10 :         return;
    2354              :     }
    2355              : 
    2356          408 :     Fraction tick = item->ownershipParentItem()->tick();
    2357          408 :     const Staff* st = item->staff();
    2358          408 :     if (st && st->isTabStaff(tick)
    2359          816 :         && (!st->staffType(tick)->showTabFingering() || item->textStyleType() == TextStyleType::STRING_NUMBER)) {
    2360           10 :         ldata->setIsSkipDraw(true);
    2361           10 :         return;
    2362              :     }
    2363          398 :     ldata->setIsSkipDraw(false);
    2364              : 
    2365          398 :     TextLayout::layoutBaseTextBase(item, ldata);
    2366          398 :     ldata->setPosY(0.0); // handle placement below
    2367              : 
    2368          398 :     if (item->autoplace()) {
    2369          398 :         const Note* note  = item->note();
    2370          398 :         const Chord* chord = note->chord();
    2371              : 
    2372          398 :         bool voices  = chord->measure()->hasVoices(chord->staffIdx(), chord->tick(), chord->actualTicks());
    2373          398 :         bool tight   = voices && chord->notes().size() == 1 && !chord->beam() && item->textStyleType() != TextStyleType::STRING_NUMBER;
    2374              : 
    2375          398 :         double headWidth = note->bboxRightPos();
    2376              : 
    2377              :         // temporarily exclude self from chord shape
    2378          398 :         const_cast<Fingering*>(item)->setAutoplace(false);
    2379              : 
    2380          398 :         if (item->layoutType() == ElementType::CHORD) {
    2381          390 :             bool above = item->placeAbove();
    2382          390 :             const Stem* stem = chord->stem();
    2383          390 :             const Segment* segment = chord->segment();
    2384          390 :             const Measure* measure = segment->measure();
    2385          390 :             double sp = item->spatium();
    2386          390 :             double md = item->minDistance().val() * sp;
    2387          390 :             const SysStaff* ss = measure->system()->staff(chord->vStaffIdx());
    2388          390 :             const Staff* vStaff = chord->staff();           // TODO: use current height at tick
    2389              : 
    2390          390 :             LD_CONDITION(measure->ldata()->isSetPos());
    2391          390 :             LD_CONDITION(segment->ldata()->isSetPos());
    2392          390 :             LD_CONDITION(chord->ldata()->isSetPos());
    2393          390 :             LD_CONDITION(note->ldata()->isSetPos());
    2394          390 :             LD_CONDITION(note->ldata()->isSetBbox());
    2395              :             //! NOTE A lot of spam
    2396              :             // LD_CONDITION(note->ldata()->mirror.has_value());
    2397          390 :             if (stem) {
    2398          390 :                 LD_CONDITION(stem->ldata()->isSetPos());
    2399          390 :                 LD_CONDITION(stem->ldata()->isSetBbox());
    2400              :             }
    2401          390 :             LD_CONDITION(chord->upNote()->ldata()->isSetBbox());
    2402          390 :             LD_CONDITION(chord->upNote()->ldata()->isSetPos());
    2403          390 :             LD_CONDITION(chord->downNote()->ldata()->isSetBbox());
    2404          390 :             LD_CONDITION(chord->downNote()->ldata()->isSetPos());
    2405              : 
    2406          390 :             if (note->ldata()->mirror.value(LD_ACCESS::BAD)) {
    2407            0 :                 ldata->moveX(-note->ldata()->pos().x());
    2408              :             }
    2409          390 :             ldata->moveX(headWidth * .5);
    2410          390 :             if (above) {
    2411          295 :                 if (tight) {
    2412           20 :                     if (chord->stem()) {
    2413           20 :                         ldata->moveX(-0.8 * sp);
    2414              :                     }
    2415           20 :                     ldata->moveY(-1.5 * sp);
    2416              :                 } else {
    2417          275 :                     RectF r = ldata->bbox().translated(measure->pos() + segment->pos() + chord->pos() + note->pos() + item->pos());
    2418          275 :                     SkylineLine sk(false);
    2419          275 :                     sk.add(r, const_cast<Fingering*>(item));
    2420          275 :                     double d = sk.minDistance(ss->skyline().north());
    2421          275 :                     double yd = 0.0;
    2422          275 :                     if (d > 0.0 && item->isStyled(Pid::MIN_DISTANCE)) {
    2423          259 :                         yd -= d + item->ldata()->bbox().height() * .25;
    2424              :                     }
    2425              :                     // force extra space above staff & chord (but not other fingerings)
    2426          275 :                     double top = 0.0;
    2427          275 :                     if (chord->up() && chord->beam() && stem) {
    2428           53 :                         top = stem->y() + stem->ldata()->bbox().top();
    2429              :                     } else {
    2430          222 :                         const Note* un = chord->upNote();
    2431          222 :                         top = std::min(0.0, un->y() + un->ldata()->bbox().top());
    2432              :                     }
    2433          275 :                     top -= md;
    2434          275 :                     double diff = (ldata->bbox().bottom() + ldata->pos().y() + yd + note->y()) - top;
    2435          275 :                     if (diff > 0.0) {
    2436          202 :                         yd -= diff;
    2437              :                     }
    2438          275 :                     ldata->moveY(yd);
    2439          275 :                 }
    2440              :             } else {
    2441           95 :                 if (tight) {
    2442           10 :                     if (chord->stem()) {
    2443           10 :                         ldata->moveX(0.8 * sp);
    2444              :                     }
    2445           10 :                     ldata->moveY(1.5 * sp);
    2446              :                 } else {
    2447           85 :                     RectF r = ldata->bbox().translated(measure->pos() + segment->pos() + chord->pos() + note->pos() + item->pos());
    2448           85 :                     SkylineLine sk(true);
    2449           85 :                     sk.add(r, const_cast<Fingering*>(item));
    2450           85 :                     double d = ss->skyline().south().minDistance(sk);
    2451           85 :                     double yd = 0.0;
    2452           85 :                     if (d > 0.0 && item->isStyled(Pid::MIN_DISTANCE)) {
    2453           85 :                         yd += d + item->ldata()->bbox().height() * .25;
    2454              :                     }
    2455              :                     // force extra space below staff & chord (but not other fingerings)
    2456              :                     double bottom;
    2457           85 :                     if (!chord->up() && chord->beam() && stem) {
    2458           10 :                         bottom = stem->y() + stem->ldata()->bbox().bottom();
    2459              :                     } else {
    2460           75 :                         const Note* dn = chord->downNote();
    2461           75 :                         bottom = std::max(vStaff->staffHeight(item->tick()), dn->y() + dn->ldata()->bbox().bottom());
    2462              :                     }
    2463           85 :                     bottom += md;
    2464           85 :                     double diff = bottom - (ldata->bbox().top() + ldata->pos().y() + yd + note->y());
    2465           85 :                     if (diff > 0.0) {
    2466           83 :                         yd += diff;
    2467              :                     }
    2468           85 :                     ldata->moveY(yd);
    2469           85 :                 }
    2470              :             }
    2471            8 :         } else if (item->textStyleType() == TextStyleType::LH_GUITAR_FINGERING) {
    2472              :             // place to left of note
    2473            8 :             double left = note->shape().left();
    2474            8 :             if (left + note->x() < 0.0) {
    2475            2 :                 ldata->moveX(left);
    2476              :             } else {
    2477            6 :                 ldata->moveX(-note->x());
    2478              :             }
    2479              :         }
    2480              :         // for other fingering styles, do not autoplace
    2481              : 
    2482              :         // restore autoplace
    2483          398 :         const_cast<Fingering*>(item)->setAutoplace(true);
    2484              :     }
    2485              : }
    2486              : 
    2487          701 : void TLayout::layoutFretDiagram(const FretDiagram* item, FretDiagram::LayoutData* ldata, const LayoutContext& ctx)
    2488              : {
    2489              :     LAYOUT_CALL_ITEM(item);
    2490          701 :     double spatium  = item->spatium() * item->userMag();
    2491          701 :     ldata->stringLineWidth = spatium * 0.08;
    2492         1358 :     ldata->nutLineWidth = ((item->fretOffset() || !item->showNut()) ? ldata->stringLineWidth
    2493          657 :                            : ctx.conf().styleAbsolute(Sid::fretNutThickness) * item->userMag());
    2494          701 :     ldata->nutY = -0.5 * (ldata->nutLineWidth - ldata->stringLineWidth);
    2495          701 :     ldata->stringDist = ctx.conf().styleAbsolute(Sid::fretStringSpacing) * item->userMag();
    2496          701 :     ldata->fretDist = ctx.conf().styleAbsolute(Sid::fretFretSpacing) * item->userMag();
    2497          701 :     ldata->markerSize = ldata->stringDist * .8;
    2498          701 :     ldata->markerY = ldata->nutY - 0.5 * ldata->nutLineWidth - ldata->markerSize - 0.20 * spatium;
    2499          701 :     bool extendedStyle = item->style().styleB(Sid::fretStyleExtended);
    2500          701 :     ldata->stringExtendTop = item->fretOffset() && extendedStyle ? -spatium * .2 : 0.0;
    2501          701 :     ldata->stringExtendBottom = extendedStyle ? 0.5 * ldata->fretDist : 0.0;
    2502          701 :     ldata->dotDiameter = ctx.conf().styleAbsolute(Sid::fretDotSpatiumSize) * item->userMag();
    2503          701 :     ldata->gridHeight = 0.0;
    2504              : 
    2505          701 :     double shapeMarginAboveDiagram = ldata->fretDist * 1.5;
    2506          701 :     double w = ldata->stringDist * (item->strings() - 1) + ldata->markerSize;
    2507          701 :     double h = item->frets() * ldata->fretDist + ldata->stringExtendBottom + 0.5 * ldata->stringLineWidth + shapeMarginAboveDiagram;
    2508          701 :     double y = -shapeMarginAboveDiagram;
    2509          701 :     double x = -(ldata->markerSize * .5);
    2510              : 
    2511          701 :     bool isVertical = item->orientation() == Orientation::VERTICAL;
    2512              : 
    2513              :     // Allocate space for fret offset number
    2514          701 :     if (item->fretOffset() > 0) {
    2515           44 :         double padding = 0.4 * ldata->stringDist;
    2516           48 :         for (auto i : item->barres()) {
    2517           36 :             FretItem::Barre barre = i.second;
    2518           36 :             if (!barre.exists()) {
    2519            0 :                 continue;
    2520              :             }
    2521           32 :             if ((barre.startString == 0 && item->numPos() == 0)
    2522           68 :                 || ((barre.endString == -1 || barre.endString == item->strings() - 1) && item->numPos() == 1)) {
    2523           32 :                 padding += 0.20 * ldata->dotDiameter * ctx.conf().styleD(Sid::barreLineWidth);
    2524           32 :                 break;
    2525              :             }
    2526              :         }
    2527           44 :         Font scaledFont(item->fretNumFont());
    2528           44 :         FontMetrics fm2(scaledFont);
    2529           44 :         String fretText = String::number(item->fretOffset() + 1);
    2530           44 :         if (ctx.conf().styleB(Sid::fretUseCustomSuffix)) {
    2531            0 :             fretText += ctx.conf().styleSt(Sid::fretCustomSuffix);
    2532              :         }
    2533           44 :         ldata->fretText = fretText;
    2534           44 :         double numw = isVertical ? fm2.width(fretText) : fm2.tightBoundingRect(fretText).height();
    2535           44 :         double xdiff = numw + padding;
    2536           44 :         w += xdiff;
    2537           44 :         x += (item->numPos() == 0) == isVertical ? -xdiff : 0;
    2538           44 :         ldata->fretNumPadding = padding;
    2539           44 :     }
    2540              : 
    2541          701 :     if (!isVertical) {
    2542            0 :         double tempW = w,
    2543            0 :                tempX = x;
    2544            0 :         w = h;
    2545            0 :         h = tempW;
    2546            0 :         x = y;
    2547            0 :         y = tempX;
    2548              :     }
    2549              : 
    2550              :     // When changing how bbox is calculated, don't forget to update the centerX and rightX methods too.
    2551          701 :     Shape shape;
    2552          701 :     shape.add(RectF(x, y, w, h), item);
    2553              : 
    2554          701 :     ldata->gridHeight = h;
    2555              : 
    2556          701 :     ldata->fingeringItems.clear();
    2557          701 :     if (item->showFingering()) {
    2558            0 :         const double padding = 0.2 * spatium;
    2559            0 :         Font fingeringFont = item->fingeringFont();
    2560            0 :         FontMetrics fontMetrics(fingeringFont);
    2561            0 :         double fontHeight = fontMetrics.capHeight();
    2562            0 :         for (size_t i = 0; i < item->fingering().size(); ++i) {
    2563            0 :             int finger = item->fingering()[i];
    2564            0 :             if (finger == 0) {
    2565            0 :                 continue;
    2566              :             }
    2567            0 :             String fingerS = String::number(finger);
    2568            0 :             double width = fontMetrics.width(fingerS);
    2569            0 :             double digitHeight = fontMetrics.tightBoundingRect(fingerS).height();
    2570            0 :             double xOff = -0.5 * width;
    2571            0 :             double fingerX = isVertical ? (item->strings() - i - 1) * ldata->stringDist + xOff : x + w + padding;
    2572            0 :             double fingerY = isVertical ? y + h + fontHeight + padding : i * ldata->stringDist + 0.5 * digitHeight;
    2573            0 :             ldata->fingeringItems.push_back(FretDiagram::FingeringItem(fingerS, PointF(fingerX, fingerY)));
    2574            0 :             shape.add(ShapeElement(fontMetrics.tightBoundingRect(fingerS).translated(PointF(fingerX, fingerY)), item));
    2575            0 :         }
    2576            0 :     }
    2577              : 
    2578          701 :     ldata->setShape(shape);
    2579              : 
    2580          701 :     if (item->ownershipParent()->isSegment()) {
    2581              :         // We need to get the width of the notehead/rest in order to position the fret diagram correctly
    2582          631 :         Segment* pSeg = item->segment();
    2583          631 :         double noteheadWidth = 0.0;
    2584          631 :         if (pSeg->isChordRestType()) {
    2585          631 :             staff_idx_t idx = item->staff()->idx();
    2586          631 :             for (EngravingItem* e = pSeg->firstElementOfSegment(idx); e; e = pSeg->nextElementOfSegment(e, idx)) {
    2587          497 :                 if (e->isRest()) {
    2588          284 :                     const Rest* r = toRest(e);
    2589          284 :                     LD_CONDITION(r->ldata()->sym.has_value());
    2590          284 :                     noteheadWidth = item->symWidth(r->ldata()->sym());
    2591          284 :                     break;
    2592          213 :                 } else if (e->isNote()) {
    2593          213 :                     Note* n = toNote(e);
    2594          213 :                     noteheadWidth = n->headWidth();
    2595          213 :                     break;
    2596              :                 }
    2597              :             }
    2598              :         }
    2599          631 :         if (RealIsNull(noteheadWidth)) {
    2600              :             // If note or rest not found, use standard notehead width
    2601          134 :             noteheadWidth = item->symWidth(SymId::noteheadBlack);
    2602              :         }
    2603              : 
    2604          631 :         ldata->setPos((noteheadWidth - item->mainWidth()) / 2, -(ldata->shape().bottom() + item->styleP(Sid::fretY)));
    2605              :     }
    2606              : 
    2607          701 :     Harmony* harmony = item->harmony();
    2608          701 :     if (harmony) {
    2609          530 :         TLayout::layoutHarmony(harmony, harmony->mutldata(), ctx);
    2610          530 :         if (item->visible() && harmony->autoplace()) {
    2611          530 :             double vertDist = ldata->bbox().top() - harmony->ldata()->bbox().translated(harmony->pos()).bottom();
    2612          530 :             double diff = vertDist - harmony->minDistance().val() * item->spatium();
    2613          530 :             if (diff < 0) {
    2614          509 :                 harmony->mutldata()->moveY(diff);
    2615              :             }
    2616              :         }
    2617              :     }
    2618              : 
    2619          701 :     ldata->slurPaths.clear();
    2620          906 :     for (auto i : item->barres()) {
    2621          205 :         FretItem::Barre barre = i.second;
    2622          205 :         if (!barre.exists()) {
    2623            0 :             continue;
    2624              :         }
    2625          205 :         int startString = barre.startString;
    2626          205 :         int endString = barre.endString != -1 ? barre.endString : item->strings() - 1;
    2627          205 :         int fret = i.first;
    2628          205 :         bool slurStyleBarre = ctx.conf().styleB(Sid::barreAppearanceSlur);
    2629         1344 :         for (int string = startString; string <= endString; ++string) {
    2630         1139 :             if (slurStyleBarre) {
    2631            0 :                 const_cast<FretDiagram*>(item)->addDotForDotStyleBarre(string, fret);
    2632              :             } else {
    2633         1139 :                 const_cast<FretDiagram*>(item)->removeDotForDotStyleBarre(string, fret);
    2634              :             }
    2635              :         }
    2636          205 :         int length = endString - startString;
    2637          205 :         double insetX = (length < 2 ? 1.25 : 2) * ldata->stringLineWidth;
    2638          205 :         double insetY = fret == 1 ? ldata->nutLineWidth + ldata->stringLineWidth : length < 2 ? 2 * ldata->stringLineWidth : insetX;
    2639          205 :         double startX = startString * ldata->stringDist + insetX;
    2640          205 :         double endX = (endString) * ldata->stringDist - insetX;
    2641          205 :         double shoulderXoffset = 0.2 * (endX - startX);
    2642          205 :         double startEndY = (fret - 1) * ldata->fretDist - insetY;
    2643          205 :         double shoulderY = startEndY - (length < 2 ? 0.3 : length < 3 ? 0.4 : 0.5) * ldata->fretDist;
    2644          205 :         double slurThickness = (length < 2 ? 0.08 : 0.1) * item->spatium() * item->userMag();
    2645          205 :         double shoulderYfor = shoulderY - slurThickness;
    2646          205 :         double shoulderYback = shoulderY + slurThickness;
    2647          205 :         PointF bezier1for = PointF(startX + shoulderXoffset, shoulderYfor);
    2648          205 :         PointF bezier2for = PointF(endX - shoulderXoffset, shoulderYfor);
    2649          205 :         PointF bezier1back = PointF(startX + shoulderXoffset, shoulderYback);
    2650          205 :         PointF bezier2back = PointF(endX - shoulderXoffset, shoulderYback);
    2651          205 :         PainterPath slurPath = PainterPath();
    2652          205 :         slurPath.moveTo(startX, startEndY);
    2653          205 :         slurPath.cubicTo(bezier1for, bezier2for, PointF(endX, startEndY));
    2654          205 :         slurPath.cubicTo(bezier2back, bezier1back, PointF(startX, startEndY));
    2655          205 :         ldata->slurPaths.push_back(slurPath);
    2656          205 :     }
    2657          701 : }
    2658              : 
    2659          454 : void TLayout::layoutGlissando(Glissando* item, LayoutContext& ctx)
    2660              : {
    2661              :     LAYOUT_CALL_ITEM(item);
    2662              : 
    2663          454 :     if (item->startElement()) {
    2664          454 :         item->setTick(item->startElement()->tick());
    2665              :     }
    2666              : 
    2667          454 :     if (item->endElement()) {
    2668          454 :         item->setTick2(item->endElement()->tick());
    2669              :     }
    2670              : 
    2671          454 :     TLayout::layoutLine(const_cast<Glissando*>(item), ctx);
    2672              : 
    2673          454 :     if (item->spannerSegments().empty()) {
    2674            0 :         LOGD("no segments");
    2675            0 :         return;
    2676              :     }
    2677          454 :     Glissando::LayoutData* ldata = item->mutldata();
    2678          454 :     ldata->setPos(0.0, 0.0);
    2679              : 
    2680          454 :     String instrId = item->staff()->part()->instrumentId(item->tick());
    2681          454 :     bool harpStaff = instrId == "harp";
    2682          454 :     if (!item->isHarpGliss().has_value()) {
    2683           68 :         item->setIsHarpGliss(harpStaff);
    2684              :     } else {
    2685          386 :         if (harpStaff != item->isHarpGliss().value()) {
    2686              :             // Preserve whether this gliss has its default playback style
    2687            0 :             bool defaultStyle = false;
    2688            0 :             if (item->isStyled(Pid::GLISS_STYLE)) {
    2689            0 :                 defaultStyle = true;
    2690              :             }
    2691            0 :             item->setIsHarpGliss(harpStaff);
    2692            0 :             if (defaultStyle) {
    2693            0 :                 item->resetProperty(Pid::GLISS_STYLE);
    2694              :             }
    2695              : 
    2696              :             // Make sure harp glisses can only be diatonic and chromatic
    2697            0 :             GlissandoStyle glissStyle = item->glissandoStyle();
    2698            0 :             if (item->isHarpGliss().value()
    2699            0 :                 && (glissStyle != GlissandoStyle::DIATONIC
    2700            0 :                     && glissStyle != GlissandoStyle::CHROMATIC)) {
    2701            0 :                 item->setGlissandoStyle(GlissandoStyle::DIATONIC);
    2702              :             }
    2703              :         }
    2704              :     }
    2705              : 
    2706          454 :     layoutNoteAnchoredLine(item, ldata, ctx);
    2707              : 
    2708          454 :     ChordLayout::addLineAttachPoints(item);
    2709          454 : }
    2710              : 
    2711          910 : void TLayout::layoutGlissandoSegment(GlissandoSegment* item, LayoutContext&)
    2712              : {
    2713              :     LAYOUT_CALL_ITEM(item);
    2714              : 
    2715          910 :     GlissandoSegment::LayoutData* ldata = item->mutldata();
    2716          910 :     if (item->pos2().x() <= 0) {
    2717          287 :         ldata->setBbox(RectF());
    2718          287 :         return;
    2719              :     }
    2720              : 
    2721          623 :     if (item->staff()) {
    2722          623 :         ldata->setMag(item->staff()->staffMag(item->tick()));
    2723              :     }
    2724          623 :     RectF r = RectF(0.0, 0.0, item->pos2().x(), item->pos2().y()).normalized();
    2725          623 :     double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
    2726          623 :     item->setbbox(r.adjusted(-lw, -lw, lw, lw));
    2727              : }
    2728              : 
    2729         1028 : void TLayout::layoutGraceNotesGroup(GraceNotesGroup* item, LayoutContext& ctx)
    2730              : {
    2731         1028 :     const StaffType* staffType = item->parent()->staffType();
    2732         1028 :     if (staffType->isTabStaff() && !item->empty()) {
    2733           45 :         const Staff* staff = item->parent()->staff();
    2734           45 :         if (staff->links()) {
    2735           33 :             for (EngravingObject* linkedSt : *staff->links()) {
    2736           33 :                 Staff* linkedStaff = toStaff(linkedSt);
    2737           66 :                 if (linkedStaff->score() == staff->score() && !linkedStaff->staffType()->isTabStaff()
    2738           66 :                     && linkedStaff->idx() < staff->idx()) {
    2739              :                     // HACK: align grace notes in TAB to their linked notation staff.
    2740              :                     // CAUTION: Works only if notation staff is above the TAB.
    2741              :                     // TODO: refactor grace notes to allow doing this properly.
    2742           33 :                     alignTabGraceNotesToMainStave(item, linkedStaff);
    2743           33 :                     return;
    2744              :                 }
    2745              :             }
    2746              :         }
    2747              :     }
    2748              : 
    2749              :     LAYOUT_CALL_ITEM(item);
    2750          995 :     Shape _shape;
    2751         2322 :     for (size_t i = item->size() - 1; i != muse::nidx; --i) {
    2752         1327 :         Chord* grace = item->at(i);
    2753         1327 :         Shape graceShape = grace->shape();
    2754         1327 :         Shape groupShape = _shape;
    2755         1327 :         groupShape.remove_if([grace](ShapeElement& s) {
    2756         1695 :             if (!s.item() || (s.item()->isStem() && s.item()->vStaffIdx() != grace->vStaffIdx())) {
    2757            0 :                 return true;
    2758              :             }
    2759         1695 :             return false;
    2760              :         });
    2761              :         double offset;
    2762         1327 :         offset = -std::max(HorizontalSpacing::minHorizontalDistance(graceShape, groupShape, grace->spatium()), 0.0);
    2763              :         // Adjust spacing for cross-beam situations
    2764         1327 :         if (i < item->size() - 1 && grace->stem()) {
    2765          268 :             Chord* prevGrace = item->at(i + 1);
    2766          268 :             if (prevGrace->up() != grace->up()) {
    2767            0 :                 double crossCorrection = grace->notes().front()->headWidth() - grace->stem()->width();
    2768            0 :                 if (prevGrace->up() && !grace->up()) {
    2769            0 :                     offset += crossCorrection;
    2770              :                 } else {
    2771            0 :                     offset -= crossCorrection;
    2772              :                 }
    2773              :             }
    2774              :         }
    2775         1327 :         _shape.add(graceShape.translate(PointF(offset, 0.0)));
    2776         1327 :         double xpos = offset - item->parent()->rxoffset() - item->parent()->ldata()->pos().x();
    2777         1327 :         grace->setPos(xpos, 0.0);
    2778         1327 :     }
    2779              : 
    2780          995 :     const Segment* appendedSeg = item->appendedSegment();
    2781          995 :     Chord* parentChord = toChord(item->parent());
    2782          995 :     Shape staffShape = appendedSeg->staffShape(parentChord->staffIdx());
    2783          995 :     bool isTabStaff = parentChord->staffType() && parentChord->staffType()->isTabStaff();
    2784          995 :     if (isTabStaff) {
    2785           12 :         staffShape.remove_if([](ShapeElement& el) {
    2786              :             // Ignore stems on tab staves
    2787           18 :             return el.item() && el.item()->isStem();
    2788              :         });
    2789              :     }
    2790          995 :     double _shapeSpatium = HorizontalSpacing::shapeSpatium(_shape);
    2791          995 :     double xPos = -HorizontalSpacing::minHorizontalDistance(_shape, staffShape, _shapeSpatium);
    2792              : 
    2793              :     // If the parent chord is cross-staff, also check against shape in the other staff and take the minimum
    2794          995 :     if (item->parent()->staffMove() != 0) {
    2795            0 :         double xPosCross = -HorizontalSpacing::minHorizontalDistance(_shape,
    2796            0 :                                                                      appendedSeg->staffShape(item->parent()->vStaffIdx()),
    2797            0 :                                                                      _shapeSpatium);
    2798            0 :         xPos = std::min(xPos, xPosCross);
    2799              :     }
    2800              :     // Same if the grace note itself is cross-staff
    2801          995 :     Chord* firstGN = item->back();
    2802          995 :     if (firstGN->staffMove() != 0) {
    2803            0 :         double xPosCross = -HorizontalSpacing::minHorizontalDistance(_shape,
    2804              :                                                                      appendedSeg->staffShape(firstGN->vStaffIdx()),
    2805            0 :                                                                      _shapeSpatium);
    2806            0 :         xPos = std::min(xPos, xPosCross);
    2807              :     }
    2808              :     // Safety net in case the shape checks don't succeed
    2809          995 :     xPos = std::min(xPos, -double(ctx.conf().styleAbsolute(Sid::graceToMainNoteDist) + firstGN->notes().front()->headWidth() / 2));
    2810              : 
    2811          995 :     item->setPos(xPos, 0.0);
    2812              : 
    2813          995 :     if (isTabStaff) {
    2814           12 :         ChordLayout::layoutStem(parentChord, ctx);
    2815              :     }
    2816          995 : }
    2817              : 
    2818        96438 : void TLayout::layoutGraceNotesGroup2(const GraceNotesGroup* item, GraceNotesGroup::LayoutData* ldata)
    2819              : {
    2820              :     LAYOUT_CALL_ITEM(item);
    2821              :     //! NOTE A lot of spam
    2822              :     // LD_CONDITION(ldata->isSetPos());
    2823              : 
    2824        96438 :     Shape shape(Shape::Type::Composite);
    2825        97343 :     for (const Chord* grace : *item) {
    2826          905 :         LD_CONDITION(grace->ldata()->isSetShape());
    2827          905 :         LD_CONDITION(grace->ldata()->isSetPos());
    2828              : 
    2829          905 :         shape.add(grace->shape(LD_ACCESS::PASS).translate(grace->pos() - item->pos()));
    2830              :     }
    2831        96438 :     ldata->setShape(shape);
    2832        96438 : }
    2833              : 
    2834           33 : void TLayout::alignTabGraceNotesToMainStave(GraceNotesGroup* graceNotes, const Staff* notationStaff)
    2835              : {
    2836              :     // HACK: align grace notes in TAB to their linked notation staff.
    2837              :     // CAUTION: Works only if notation staff is above the TAB.
    2838              :     // TODO: refactor grace notes to allow doing this properly.
    2839              : 
    2840           70 :     for (Chord* grace : *graceNotes) {
    2841           37 :         Chord* linkedGrace = toChord(grace->findLinkedInStaff(notationStaff));
    2842           37 :         if (!linkedGrace) {
    2843            0 :             continue;
    2844              :         }
    2845           37 :         PointF linkedGracePos = linkedGrace->ldata()->pos();
    2846           37 :         grace->setPos(linkedGracePos);
    2847              :     }
    2848           33 : }
    2849              : 
    2850           22 : void TLayout::manageTempoChangeSnapping(GradualTempoChangeSegment* item, LayoutContext& ctx)
    2851              : {
    2852           22 :     GradualTempoChangeSegment::LayoutData* ldata = item->mutldata();
    2853              : 
    2854           22 :     ldata->disconnectSnappedItems();
    2855           22 :     GradualTempoChangeSegment* tempoChangeSegmentSnappedBefore = item->findElementToSnapBefore();
    2856           22 :     if (tempoChangeSegmentSnappedBefore) {
    2857            0 :         ldata->connectItemSnappedBefore(tempoChangeSegmentSnappedBefore);
    2858            0 :         doLayoutGradualTempoChangeSegment(tempoChangeSegmentSnappedBefore, ctx);
    2859              :     }
    2860              : 
    2861           22 :     TempoText* tempoTextSnappedAfter = item->findElementToSnapAfter();
    2862           22 :     if (tempoTextSnappedAfter) {
    2863            4 :         ldata->connectItemSnappedAfter(tempoTextSnappedAfter);
    2864              :     }
    2865           22 : }
    2866              : 
    2867           22 : void TLayout::doLayoutGradualTempoChangeSegment(GradualTempoChangeSegment* item, LayoutContext& ctx)
    2868              : {
    2869           22 :     GradualTempoChangeSegment::LayoutData* ldata = item->mutldata();
    2870           22 :     ldata->setPosY(0.0);
    2871              : 
    2872            4 :     auto extendLineToSnappedItemAfter = [item](EngravingItem* itemAfter) {
    2873            4 :         assert(itemAfter->isGradualTempoChangeSegment() || itemAfter->isTempoText());
    2874            4 :         if (item->tempoChange()->adjustForRehearsalMark(false)
    2875            4 :             || itemAfter->findAncestor(ElementType::SYSTEM) != item->system()) {
    2876            0 :             return;
    2877              :         }
    2878              : 
    2879            4 :         double xItemPos = itemAfter->pageX() - item->system()->pageX();
    2880            4 :         double itemLeftEdge = xItemPos + itemAfter->ldata()->bbox().left();
    2881              : 
    2882            4 :         double padding = item->spatium();
    2883            4 :         if (itemAfter->isTempoText()) {
    2884            4 :             const double fontSizeScaleFactor = toTempoText(itemAfter)->size() / 10.0;
    2885            4 :             padding = item->spatium() * fontSizeScaleFactor;
    2886            0 :         } else if (itemAfter->isGradualTempoChangeSegment()) {
    2887            0 :             Text* startText = toGradualTempoChangeSegment(itemAfter)->text();
    2888            0 :             if (startText) {
    2889            0 :                 const double fontSizeScaleFactor = startText->size() / 10.0;
    2890            0 :                 padding = item->spatium() * fontSizeScaleFactor;
    2891              :             }
    2892              :         }
    2893              : 
    2894            4 :         double maxTempoLineEnd = itemLeftEdge - padding;
    2895            4 :         double xEndDiff = maxTempoLineEnd - (item->pos().x() + item->pos2().x());
    2896            4 :         item->rxpos2() += xEndDiff;
    2897           22 :     };
    2898              : 
    2899           22 :     if (ldata->itemSnappedAfter() && ldata->itemSnappedAfter()->isTempoText()) {
    2900            4 :         TempoText* tempoTextSnappedAfter = toTempoText(ldata->itemSnappedAfter());
    2901            4 :         extendLineToSnappedItemAfter(tempoTextSnappedAfter);
    2902           18 :     } else if (ldata->itemSnappedAfter() && ldata->itemSnappedAfter()->isGradualTempoChangeSegment()) {
    2903            0 :         GradualTempoChangeSegment* tempoChangeSegmentSnappedAfter = toGradualTempoChangeSegment(ldata->itemSnappedAfter());
    2904            0 :         extendLineToSnappedItemAfter(tempoChangeSegmentSnappedAfter);
    2905              :     }
    2906              : 
    2907           22 :     layoutTextLineBaseSegment(item, ctx);
    2908              : 
    2909           22 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    2910           22 : }
    2911              : 
    2912           22 : void TLayout::layoutGradualTempoChangeSegment(GradualTempoChangeSegment* item, LayoutContext& ctx)
    2913              : {
    2914              :     LAYOUT_CALL_ITEM(item);
    2915              : 
    2916           22 :     manageTempoChangeSnapping(item, ctx);
    2917              : 
    2918           22 :     doLayoutGradualTempoChangeSegment(item, ctx);
    2919           22 : }
    2920              : 
    2921            0 : void TLayout::layoutGradualTempoChange(GradualTempoChange* item, LayoutContext& ctx)
    2922              : {
    2923              :     LAYOUT_CALL_ITEM(item);
    2924            0 :     layoutLine(item, ctx);
    2925            0 : }
    2926              : 
    2927         1250 : void TLayout::layoutGuitarBend(GuitarBend* item, LayoutContext& ctx, System* system)
    2928              : {
    2929              :     LAYOUT_CALL_ITEM(item);
    2930         1250 :     item->computeBendAmount();
    2931              : 
    2932         1250 :     GuitarBendLayout::updateSegmentsAndLayout(item, ctx, system);
    2933              : 
    2934         1250 :     item->updateHoldLine();
    2935         1250 :     if (item->holdLine()) {
    2936            6 :         GuitarBendLayout::updateSegmentsAndLayout(item->holdLine(), ctx, system);
    2937              :     }
    2938         1250 : }
    2939              : 
    2940         1250 : void TLayout::layoutGuitarBendSegment(GuitarBendSegment* item, LayoutContext& ctx)
    2941              : {
    2942              :     LAYOUT_CALL_ITEM(item);
    2943         1250 :     bool tabStaff = item->staffType()->isTabStaff();
    2944         1250 :     if (tabStaff) {
    2945           94 :         GuitarBendLayout::layoutTabStaff(item, ctx);
    2946              :     } else {
    2947         1156 :         GuitarBendLayout::layoutStandardStaff(item, ctx);
    2948              :     }
    2949              : 
    2950         1250 :     SysStaff* staff = item->system()->staff(item->staffIdx());
    2951         1250 :     Skyline& skyline = staff->skyline();
    2952         1250 :     SkylineLine& skylineLine = tabStaff ? skyline.north() : (item->guitarBend()->ldata()->up() ? skyline.north() : skyline.south());
    2953         1250 :     skylineLine.add(item->shape().translate(item->pos()));
    2954              : 
    2955         1250 :     fillGuitarBendSegmentShape(item, item->mutldata());
    2956         1250 : }
    2957              : 
    2958         2573 : void TLayout::fillGuitarBendSegmentShape(const GuitarBendSegment* item, GuitarBendSegment::LayoutData* ldata)
    2959              : {
    2960              :     LAYOUT_CALL_ITEM(item);
    2961         2573 :     Shape shape;
    2962         2573 :     shape.add(ldata->bbox(), item);
    2963         2573 :     if (!item->bendText()->empty()) {
    2964          204 :         shape.add(item->bendText()->shape().translate(item->bendText()->pos()));
    2965              :     }
    2966         2573 :     ldata->setShape(shape);
    2967         2573 : }
    2968              : 
    2969          472 : void TLayout::layoutHairpinSegment(HairpinSegment* item, LayoutContext& ctx)
    2970              : {
    2971              :     LAYOUT_CALL_ITEM(item);
    2972          472 :     HairpinSegment::LayoutData* ldata = item->mutldata();
    2973              : 
    2974          472 :     const StaffType* stType = item->staffType();
    2975              : 
    2976          472 :     if (stType && stType->isHiddenElementOnTab(Sid::hairpinShowTabCommon, Sid::hairpinShowTabSimple)) {
    2977            0 :         ldata->setIsSkipDraw(true);
    2978            0 :         return;
    2979              :     }
    2980          472 :     ldata->setIsSkipDraw(false);
    2981              : 
    2982          472 :     const double _spatium = item->spatium();
    2983              : 
    2984          472 :     ldata->disconnectSnappedItems();
    2985              : 
    2986          472 :     manageHairpinSnapping(item, ctx);
    2987              : 
    2988          472 :     HairpinType type = item->hairpin()->hairpinType();
    2989          472 :     if (item->hairpin()->isLineType()) {
    2990           26 :         item->setTwoLines(false);
    2991           26 :         layoutTextLineBaseSegment(item, ctx);
    2992           26 :         item->setDrawCircledTip(false);
    2993           26 :         item->setCircledTipRadius(0.0);
    2994              :     } else {
    2995          446 :         item->setTwoLines(true);
    2996              : 
    2997          446 :         item->hairpin()->setBeginTextAlign({ AlignH::LEFT, AlignV::VCENTER });
    2998          446 :         item->hairpin()->setEndTextAlign({ AlignH::RIGHT, AlignV::VCENTER });
    2999          446 :         item->hairpin()->setBeginTextPosition(AlignH::LEFT);
    3000          446 :         item->hairpin()->setEndTextPosition(AlignH::RIGHT);
    3001              : 
    3002          446 :         double x1 = 0.0;
    3003          446 :         layoutTextLineBaseSegment(item, ctx);
    3004          446 :         if (!item->text()->empty()) {
    3005           42 :             x1 = item->text()->width() + _spatium * .5;
    3006              :         }
    3007              : 
    3008          446 :         Transform t;
    3009          446 :         double h1 = item->hairpin()->hairpinHeight().val() * _spatium * .5;
    3010          446 :         double h2 = item->hairpin()->hairpinContHeight().val() * _spatium * .5;
    3011              : 
    3012          446 :         double x = item->pos2().x();
    3013          446 :         if (!item->endText()->empty()) {
    3014            4 :             x -= (item->endText()->width() + _spatium * .5);             // 0.5 spatium distance
    3015              :         }
    3016          446 :         if (x < _spatium) {               // minimum size of hairpin
    3017            4 :             x = _spatium;
    3018              :         }
    3019          446 :         double y = item->pos2().y();
    3020          446 :         double len = sqrt(x * x + y * y);
    3021          446 :         t.rotateRadians(asin(y / len));
    3022              : 
    3023          446 :         item->setDrawCircledTip(item->hairpin()->hairpinCircledTip());
    3024          446 :         item->setCircledTipRadius(item->drawCircledTip() ? 0.6 * _spatium * .5 : 0.0);
    3025              : 
    3026          446 :         LineF l1, l2;
    3027              : 
    3028          446 :         switch (type) {
    3029          358 :         case HairpinType::CRESC_HAIRPIN: {
    3030          358 :             switch (item->spannerSegmentType()) {
    3031          356 :             case SpannerSegmentType::SINGLE:
    3032              :             case SpannerSegmentType::BEGIN: {
    3033          356 :                 l1.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, h1);
    3034          356 :                 l2.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, -h1);
    3035          356 :                 PointF circledTip;
    3036          356 :                 circledTip.setX(x1 + item->circledTipRadius());
    3037          356 :                 circledTip.setY(0.0);
    3038          356 :                 item->setCircledTip(circledTip);
    3039          356 :             } break;
    3040              : 
    3041            2 :             case SpannerSegmentType::MIDDLE:
    3042              :             case SpannerSegmentType::END:
    3043            2 :                 item->setDrawCircledTip(false);
    3044            2 :                 l1.setLine(x1,  h2, len, h1);
    3045            2 :                 l2.setLine(x1, -h2, len, -h1);
    3046            2 :                 break;
    3047              :             }
    3048              :         }
    3049          358 :         break;
    3050           88 :         case HairpinType::DIM_HAIRPIN: {
    3051           88 :             switch (item->spannerSegmentType()) {
    3052           88 :             case SpannerSegmentType::SINGLE:
    3053              :             case SpannerSegmentType::END: {
    3054           88 :                 l1.setLine(x1,  h1, len - item->circledTipRadius() * 2, 0.0);
    3055           88 :                 l2.setLine(x1, -h1, len - item->circledTipRadius() * 2, 0.0);
    3056           88 :                 PointF circledTip;
    3057           88 :                 circledTip.setX(len - item->circledTipRadius());
    3058           88 :                 circledTip.setY(0.0);
    3059           88 :                 item->setCircledTip(circledTip);
    3060           88 :             } break;
    3061            0 :             case SpannerSegmentType::BEGIN:
    3062              :             case SpannerSegmentType::MIDDLE:
    3063            0 :                 item->setDrawCircledTip(false);
    3064            0 :                 l1.setLine(x1,  h1, len, +h2);
    3065            0 :                 l2.setLine(x1, -h1, len, -h2);
    3066            0 :                 break;
    3067              :             }
    3068              :         }
    3069           88 :         break;
    3070            0 :         default:
    3071            0 :             break;
    3072              :         }
    3073              : 
    3074              :         // Do Coord rotation
    3075          446 :         l1 = t.map(l1);
    3076          446 :         l2 = t.map(l2);
    3077          446 :         if (item->drawCircledTip()) {
    3078            3 :             item->setCircledTip(t.map(item->circledTip()));
    3079              :         }
    3080              : 
    3081          446 :         ldata->points[0] = l1.p1();
    3082          446 :         ldata->points[1] = l1.p2();
    3083          446 :         ldata->points[2] = l2.p1();
    3084          446 :         ldata->points[3] = l2.p2();
    3085          446 :         ldata->npoints   = 4;
    3086              : 
    3087          446 :         ldata->joinedHairpin.clear();
    3088          446 :         if (item->spannerSegmentType() != SpannerSegmentType::MIDDLE) {
    3089          445 :             if (type == HairpinType::DIM_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::BEGIN) {
    3090           88 :                 ldata->joinedHairpin << ldata->points[0] << ldata->points[1] << ldata->points[2]; // [top-left, joint, bottom-left]
    3091          357 :             } else if (type == HairpinType::CRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::END) {
    3092          356 :                 ldata->joinedHairpin << ldata->points[1] << ldata->points[0] << ldata->points[3]; // [top-right, joint, bottom-right]
    3093              :             }
    3094              :         }
    3095              : 
    3096          446 :         RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
    3097          446 :         if (!item->text()->empty()) {
    3098           42 :             r.unite(item->text()->ldata()->bbox());
    3099              :         }
    3100          446 :         if (!item->endText()->empty()) {
    3101            4 :             r.unite(item->endText()->ldata()->bbox().translated(x + item->endText()->ldata()->bbox().width(), 0.0));
    3102              :         }
    3103          446 :         double w  = item->absoluteFromSpatium(ctx.conf().styleS(Sid::hairpinLineWidth));
    3104          446 :         item->setbbox(r.adjusted(-w * .5, -w * .5, w, w));
    3105              :     }
    3106              : 
    3107          472 :     if (!item->ownershipParent()) {
    3108            0 :         item->setPos(PointF());
    3109            0 :         item->roffset() = PointF();
    3110            0 :         return;
    3111              :     }
    3112              : 
    3113          472 :     if (item->autoplace()) {
    3114          472 :         Autoplace::autoplaceSpannerSegment(item, ldata);
    3115              :     }
    3116              : }
    3117              : 
    3118          472 : void TLayout::manageHairpinSnapping(HairpinSegment* item, LayoutContext& ctx)
    3119              : {
    3120          472 :     HairpinSegment::LayoutData* ldata = item->mutldata();
    3121              : 
    3122          472 :     EngravingItem* possibleSnapBeforeElement = nullptr;
    3123          472 :     EngravingItem* possibleSnapAfterElement = nullptr;
    3124          472 :     if (item->isSingleBeginType()) {
    3125          467 :         possibleSnapBeforeElement = item->findElementToSnapBefore();
    3126              :     }
    3127          472 :     if (item->isSingleEndType() && item->hairpin()->ticks().isNotZero()) {
    3128          463 :         possibleSnapAfterElement = item->findElementToSnapAfter();
    3129              :     }
    3130              : 
    3131          472 :     bool doSnapBefore = possibleSnapBeforeElement && item->hairpin()->snapToItemBefore();
    3132          472 :     if (doSnapBefore && possibleSnapBeforeElement->isHairpinSegment()) {
    3133           58 :         doSnapBefore = doSnapBefore && toHairpinSegment(possibleSnapBeforeElement)->hairpin()->snapToItemAfter()
    3134           58 :                        && possibleSnapBeforeElement->ldata()->itemSnappedBefore() != item;
    3135              :     }
    3136              : 
    3137          472 :     if (doSnapBefore) {
    3138          135 :         ldata->connectItemSnappedBefore(possibleSnapBeforeElement);
    3139              :     }
    3140              : 
    3141          472 :     bool doSnapAfter = possibleSnapAfterElement && item->hairpin()->snapToItemAfter();
    3142          472 :     if (doSnapAfter && possibleSnapAfterElement->isHairpinSegment()) {
    3143            0 :         doSnapAfter = doSnapAfter && toHairpinSegment(possibleSnapAfterElement)->hairpin()->snapToItemBefore()
    3144            0 :                       && possibleSnapAfterElement->ldata()->itemSnappedAfter() != item;
    3145              :     }
    3146              : 
    3147          472 :     if (doSnapAfter) {
    3148           69 :         ldata->connectItemSnappedAfter(possibleSnapAfterElement);
    3149              :     }
    3150              : 
    3151              :     // In case of dynamics/expressions before or after, make space for them horizontally
    3152          472 :     double mag = item->staff()->staffMag(item);
    3153          472 :     double hairpinDistToDynOrExpr = ctx.conf().style().styleAbsolute(Sid::autoplaceHairpinDynamicsDistance) * mag;
    3154              : 
    3155          135 :     bool makeSpaceBefore = (doSnapBefore && possibleSnapBeforeElement->isTextBase())
    3156          607 :                            || (possibleSnapBeforeElement && possibleSnapBeforeElement->isDynamic());
    3157              : 
    3158          472 :     if (makeSpaceBefore && possibleSnapBeforeElement->findAncestor(ElementType::SYSTEM) == item->system()) {
    3159          106 :         double xItemPos = possibleSnapBeforeElement->pageX() - item->system()->pageX();
    3160          106 :         double itemRightEdge = xItemPos + possibleSnapBeforeElement->ldata()->bbox().right();
    3161          106 :         double xMinHairpinStart = itemRightEdge + hairpinDistToDynOrExpr;
    3162          106 :         double xStartDiff = ldata->pos().x() - xMinHairpinStart;
    3163          106 :         if (xStartDiff < 0) {
    3164          106 :             ldata->setPosX(xMinHairpinStart);
    3165          106 :             item->rxpos2() += xStartDiff;
    3166              :         }
    3167              :     }
    3168              : 
    3169           69 :     bool makeSpaceAfter = (doSnapAfter && possibleSnapAfterElement->isTextBase())
    3170          541 :                           || (possibleSnapAfterElement && possibleSnapAfterElement->isDynamic());
    3171              : 
    3172          472 :     if (makeSpaceAfter && possibleSnapAfterElement->findAncestor(ElementType::SYSTEM) == item->system()) {
    3173           69 :         double xItemPos = possibleSnapAfterElement->pageX() - item->system()->pageX();
    3174           69 :         double itemLeftEdge = xItemPos + possibleSnapAfterElement->ldata()->bbox().left();
    3175           69 :         double xMaxHairpinEnd = itemLeftEdge - hairpinDistToDynOrExpr;
    3176           69 :         double xEndDiff = xMaxHairpinEnd - (item->pos().x() + item->pos2().x());
    3177           69 :         const double EXTEND_THRESHOLD = 3.0 * item->spatium();
    3178           69 :         if (xEndDiff < 0) {
    3179           27 :             item->rxpos2() += xEndDiff;
    3180           42 :         } else if (item->hairpin()->snapToItemAfter() && xEndDiff > EXTEND_THRESHOLD) {
    3181            0 :             item->rxpos2() += xEndDiff;
    3182              :         }
    3183              :     }
    3184          472 : }
    3185              : 
    3186            0 : void TLayout::layoutHairpin(Hairpin* item, LayoutContext& ctx)
    3187              : {
    3188              :     LAYOUT_CALL_ITEM(item);
    3189            0 :     item->setPos(0.0, 0.0);
    3190            0 :     layoutTextLineBase(item, ctx);
    3191            0 : }
    3192              : 
    3193            0 : void TLayout::layoutHammerOnPullOff(HammerOnPullOff* item, LayoutContext& ctx)
    3194              : {
    3195            0 :     layoutSlur(toSlur(item), ctx);
    3196            0 : }
    3197              : 
    3198           61 : void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutContext& ctx)
    3199              : {
    3200              :     // The layout of the slur has already been done. Here we layout the H/P letters.
    3201           61 :     item->updateHopoText();
    3202              : 
    3203           61 :     System* system = item->system();
    3204           61 :     Fraction systemEndTick = system->endTick();
    3205           61 :     Skyline& sk = system->staff(item->staffIdx())->skyline();
    3206              : 
    3207          136 :     for (HammerOnPullOffText* hopoText : item->hopoText()) {
    3208           75 :         bool above = hopoText->placeAbove();
    3209              : 
    3210           75 :         Align align;
    3211           75 :         align.vertical = AlignV::BASELINE;
    3212           75 :         align.horizontal = AlignH::HCENTER;
    3213           75 :         hopoText->setPosition(AlignH::HCENTER);
    3214           75 :         hopoText->setAlign(align);
    3215           75 :         layoutItem(hopoText, ctx);
    3216              : 
    3217           75 :         const Chord* startChord = hopoText->startChord();
    3218           75 :         const Chord* endChord = hopoText->endChord();
    3219           75 :         double startX = startChord->systemPos().x() + startChord->upNote()->headWidth();
    3220           75 :         double endX = startX;
    3221           75 :         if (endChord->tick() < systemEndTick) {
    3222           75 :             endX = endChord->systemPos().x();
    3223              :         } else {
    3224              :             // The last endChord of this segment is in next system. Use end barline instead.
    3225            0 :             Segment* endSeg = system->lastMeasure()->last(SegmentType::BarLineTypes);
    3226            0 :             endX = endSeg ? endSeg->systemPos().x() : endX;
    3227              :         }
    3228           75 :         if (startChord->stem() && endChord->stem() && startChord->up() == above && endChord->up() == above) {
    3229              :             // Mid-way between centered on the notes and centered on the stems
    3230           19 :             endX += (above ? 0.5 : -0.5) * endChord->upNote()->headWidth();
    3231              :         }
    3232           75 :         double centerX = 0.5 * (startX + endX);
    3233              : 
    3234           75 :         double vertPadding = 0.5 * item->spatium();
    3235           75 :         Shape hopoTextShape = hopoText->ldata()->shape().translated(PointF(centerX, 0.0));
    3236           75 :         SkylineLine& skl = above ? sk.north() : sk.south();
    3237          150 :         SkylineLine skyline = skl.getFilteredCopy([](const ShapeElement& el) {
    3238         2499 :             return el.item() && el.item()->isArticulationFamily();
    3239           75 :         });
    3240              : 
    3241           75 :         double y = above ? -skyline.minDistanceToShapeAbove(hopoTextShape) : skyline.minDistanceToShapeBelow(hopoTextShape);
    3242           75 :         y += above ? -vertPadding : vertPadding;
    3243           75 :         y = above ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding);
    3244              : 
    3245           75 :         Note* startNote = above ? startChord->upNote() : startChord->downNote();
    3246           75 :         Note* endNote = above ? endChord->upNote() : endChord->downNote();
    3247           75 :         double yNoteLimit = above ? std::min(startNote->y(), endNote->y()) - 2 * vertPadding
    3248            0 :                             : std::max(startNote->y(), endNote->y()) + 2 * vertPadding;
    3249           75 :         y = above ? std::min(y, yNoteLimit) : std::max(y, yNoteLimit);
    3250              : 
    3251           75 :         hopoText->mutldata()->setPos(centerX, y);
    3252              : 
    3253           75 :         hopoTextShape.translateY(y);
    3254           75 :         skl.add(hopoTextShape);
    3255           75 :     }
    3256              : 
    3257           61 :     Shape hopoSegmentShape = item->mutldata()->shape();
    3258          136 :     for (HammerOnPullOffText* hopoText : item->hopoText()) {
    3259           75 :         hopoSegmentShape.add(hopoText->ldata()->shape().translated(hopoText->pos()));
    3260              :     }
    3261              : 
    3262           61 :     item->mutldata()->setShape(hopoSegmentShape);
    3263           61 : }
    3264              : 
    3265           75 : void TLayout::layoutHammerOnPullOffText(HammerOnPullOffText* item, LayoutContext& ctx)
    3266              : {
    3267           75 :     TextLayout::layoutBaseTextBase(item, ctx);
    3268           75 : }
    3269              : 
    3270           32 : void TLayout::layoutHarpPedalDiagram(const HarpPedalDiagram* item, HarpPedalDiagram::LayoutData* ldata)
    3271              : {
    3272              :     LAYOUT_CALL_ITEM(item);
    3273           32 :     const_cast<HarpPedalDiagram*>(item)->updateDiagramText();
    3274              : 
    3275           32 :     TextLayout::layoutBaseTextBase(item, ldata);
    3276              : 
    3277           32 :     if (item->autoplace()) {
    3278           32 :         const Segment* s = toSegment(item->ownershipParent());
    3279           32 :         const Measure* m = s->measure();
    3280              : 
    3281           32 :         LD_CONDITION(ldata->isSetPos());
    3282           32 :         LD_CONDITION(m->ldata()->isSetPos());
    3283           32 :         LD_CONDITION(s->ldata()->isSetPos());
    3284              :     }
    3285              : 
    3286           32 :     Autoplace::autoplaceSegmentElement(item, ldata);
    3287           32 : }
    3288              : 
    3289           29 : void TLayout::layoutHarmonicMarkSegment(HarmonicMarkSegment* item, LayoutContext& ctx)
    3290              : {
    3291              :     LAYOUT_CALL_ITEM(item);
    3292           29 :     HarmonicMarkSegment::LayoutData* ldata = item->mutldata();
    3293           29 :     const StaffType* stType = item->staffType();
    3294           29 :     if (stType
    3295           29 :         && (!stType->isTabStaff()
    3296            0 :             || stType->isHiddenElementOnTab(Sid::harmonicMarkShowTabCommon, Sid::harmonicMarkShowTabSimple))) {
    3297           29 :         ldata->setIsSkipDraw(true);
    3298           29 :         return;
    3299              :     }
    3300            0 :     ldata->setIsSkipDraw(false);
    3301              : 
    3302            0 :     layoutTextLineBaseSegment(item, ctx);
    3303              : 
    3304            0 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    3305              : }
    3306              : 
    3307         3654 : void TLayout::layoutHarmony(Harmony* item, Harmony::LayoutData* ldata, const LayoutContext& ctx)
    3308              : {
    3309              :     LAYOUT_CALL_ITEM(item);
    3310              :     LD_INDEPENDENT;
    3311              : 
    3312         3654 :     HarmonyLayout::layoutHarmony(item, ldata, ctx);
    3313         3654 : }
    3314              : 
    3315         5787 : void TLayout::layoutHook(const Hook* item, Hook::LayoutData* ldata)
    3316              : {
    3317              :     LAYOUT_CALL_ITEM(item);
    3318              :     LD_INDEPENDENT;
    3319              : 
    3320         5787 :     if (ldata->isValid()) {
    3321         3495 :         return;
    3322              :     }
    3323              : 
    3324         2292 :     ldata->setBbox(item->symBbox(item->sym()));
    3325              : }
    3326              : 
    3327           60 : void TLayout::layoutImage(const Image* item, Image::LayoutData* ldata)
    3328              : {
    3329              :     LAYOUT_CALL_ITEM(item);
    3330           60 :     IF_ASSERT_FAILED(item->layoutParent()) {
    3331            0 :         return;
    3332              :     }
    3333              : 
    3334           60 :     ldata->setPos(0.0, 0.0);
    3335           60 :     const_cast<Image*>(item)->init();
    3336              : 
    3337           60 :     SizeF imageSize = item->size();
    3338              : 
    3339              :     // if autoscale && inside a box, scale to box relevant size
    3340           60 :     if (item->autoScale()
    3341           60 :         && ((item->layoutParent()->isHBox() || item->layoutParent()->isVBox()))) {
    3342            0 :         const EngravingItem::LayoutData* parentLD = item->layoutParent()->ldata();
    3343              : 
    3344            0 :         LD_CONDITION(parentLD->isSetBbox());
    3345              : 
    3346            0 :         if (item->lockAspectRatio()) {
    3347            0 :             double f = item->sizeIsSpatium() ? item->spatium() : DPMM;
    3348            0 :             SizeF size(item->imageSize());
    3349            0 :             double ratio = size.width() / size.height();
    3350            0 :             double w = parentLD->bbox().width();
    3351            0 :             double h = parentLD->bbox().height();
    3352            0 :             if ((w / h) < ratio) {
    3353            0 :                 imageSize.setWidth(w / f);
    3354            0 :                 imageSize.setHeight((w / ratio) / f);
    3355              :             } else {
    3356            0 :                 imageSize.setHeight(h / f);
    3357            0 :                 imageSize.setWidth(h * ratio / f);
    3358              :             }
    3359              :         } else {
    3360            0 :             imageSize = item->pixel2size(parentLD->bbox().size());
    3361              :         }
    3362              :     }
    3363              : 
    3364           60 :     const_cast<Image*>(item)->setSize(imageSize);
    3365              : 
    3366              :     // in any case, adjust position relative to parent
    3367           60 :     ldata->setBbox(RectF(PointF(), item->size2pixel(imageSize)));
    3368              : }
    3369              : 
    3370           91 : void TLayout::layoutInstrumentChange(const InstrumentChange* item, InstrumentChange::LayoutData* ldata)
    3371              : {
    3372              :     LAYOUT_CALL_ITEM(item);
    3373           91 :     TextLayout::layoutBaseTextBase(item, ldata);
    3374              : 
    3375           91 :     if (item->autoplace()) {
    3376           91 :         const Segment* s = toSegment(item->ownershipParent());
    3377           91 :         const Measure* m = s->measure();
    3378           91 :         LD_CONDITION(ldata->isSetPos());
    3379           91 :         LD_CONDITION(m->ldata()->isSetPos());
    3380           91 :         LD_CONDITION(s->ldata()->isSetPos());
    3381              :     }
    3382              : 
    3383           91 :     Autoplace::autoplaceSegmentElement(item, ldata);
    3384           91 : }
    3385              : 
    3386         7975 : void TLayout::layoutInstrumentName(const InstrumentName* item, InstrumentName::LayoutData* ldata)
    3387              : {
    3388              :     LAYOUT_CALL_ITEM(item);
    3389              :     LD_INDEPENDENT;
    3390              :     //! NOTE There are problems, an investigation is required
    3391              : //    if (ldata->isValid()) {
    3392              : //        return;
    3393              : //    }
    3394              : 
    3395         7975 :     TextLayout::layoutBaseTextBase(item, ldata);
    3396         7975 : }
    3397              : 
    3398          212 : void TLayout::layoutJump(const Jump* item, Jump::LayoutData* ldata)
    3399              : {
    3400              :     LAYOUT_CALL_ITEM(item);
    3401          212 :     LD_CONDITION(item->layoutParent()->ldata()->isSetBbox());
    3402              : 
    3403          212 :     TextLayout::layoutBaseTextBase(item, ldata);
    3404              : 
    3405          212 :     AlignH position = item->position();
    3406              : 
    3407              :     // ignore courtesy keysig, timesig, but fall back if needed
    3408          212 :     Measure* measure = toMeasure(item->parent());
    3409          212 :     const BarLine* bl = measure->endBarLine();
    3410          212 :     double xAdj = bl ? bl->segment()->x() + bl->ldata()->bbox().width() : measure->width();
    3411              : 
    3412          212 :     bool avoidBarline = item->staffIdx() != 0 && position != AlignH::HCENTER;
    3413          212 :     if (avoidBarline) {
    3414            1 :         bool startRepeat = measure->repeatStart();
    3415            1 :         bool endRepeat = measure->repeatEnd();
    3416            1 :         staff_idx_t blAboveIdx = item->staffIdx() - 1;
    3417              : 
    3418            1 :         const double fontSizeScaleFactor = item->size() / 10.0;
    3419            1 :         double padding = (startRepeat || endRepeat) ? 0.0 : 0.5 * item->spatium() * fontSizeScaleFactor;
    3420              : 
    3421            1 :         if (position == AlignH::LEFT) {
    3422            0 :             const BarLine* blAbove = startRepeat || !measure->prevMeasure()
    3423            0 :                                      ? measure->startBarLine(blAboveIdx) : measure->prevMeasure()->endBarLine(blAboveIdx);
    3424            0 :             double blWidth = (startRepeat && blAbove) ? blAbove->width() : 0.0;
    3425            0 :             xAdj += padding + blWidth;
    3426            1 :         } else if (position == AlignH::RIGHT) {
    3427            1 :             const BarLine* blAbove = measure->endBarLine(blAboveIdx);
    3428            1 :             double blWidth = blAbove ? blAbove->width() : 0.0;
    3429            1 :             xAdj -= blWidth + padding;
    3430              :         }
    3431              :     }
    3432          212 :     ldata->moveX(xAdj);
    3433              : 
    3434          212 :     if (item->autoplace()) {
    3435          212 :         LD_CONDITION(ldata->isSetPos());
    3436          212 :         LD_CONDITION(ldata->isSetBbox());
    3437          212 :         LD_CONDITION(measure->ldata()->isSetPos());
    3438              :     }
    3439              : 
    3440          212 :     Autoplace::autoplaceMeasureElement(item, ldata);
    3441          212 : }
    3442              : 
    3443         8376 : static void keySigAddLayout(const KeySig* item, const LayoutConfiguration& conf, SymId sym, int line, KeySig::LayoutData* ldata)
    3444              : {
    3445         8376 :     double _spatium = item->spatium();
    3446         8376 :     double step = _spatium * (item->staff() ? item->staff()->staffTypeForElement(item)->lineDistance().val() * 0.5 : 0.5);
    3447         8376 :     KeySym ks;
    3448         8376 :     ks.sym = sym;
    3449         8376 :     Spatium x = 0.0_sp;
    3450         8376 :     if (!ldata->keySymbols.empty()) {
    3451         5224 :         const KeySym& previous = ldata->keySymbols.back();
    3452         5224 :         Spatium accidentalGap = conf.styleS(Sid::keysigAccidentalDistance);
    3453         5224 :         if (previous.sym != sym) {
    3454            2 :             accidentalGap *= 2;
    3455         5222 :         } else if (previous.sym == SymId::accidentalNatural && sym == SymId::accidentalNatural) {
    3456          248 :             accidentalGap = conf.styleS(Sid::keysigNaturalDistance);
    3457              :         }
    3458         5224 :         Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), _spatium);
    3459         5224 :         x = previous.xPos + previousWidth + accidentalGap;
    3460         5224 :         bool isAscending = line < previous.line;
    3461         5224 :         SmuflAnchorId currentCutout = isAscending ? SmuflAnchorId::cutOutSW : SmuflAnchorId::cutOutNW;
    3462         5224 :         SmuflAnchorId previousCutout = isAscending ? SmuflAnchorId::cutOutNE : SmuflAnchorId::cutOutSE;
    3463         5224 :         PointF cutout = item->symSmuflAnchor(sym, currentCutout);
    3464         5224 :         double currentCutoutY = line * step + cutout.y();
    3465         5224 :         double previousCutoutY = previous.line * step + item->symSmuflAnchor(previous.sym, previousCutout).y();
    3466         5224 :         if ((isAscending && currentCutoutY < previousCutoutY) || (!isAscending && currentCutoutY > previousCutoutY)) {
    3467         5136 :             x -= Spatium::fromAbsolute(cutout.x(), _spatium);
    3468              :         }
    3469              :     }
    3470         8376 :     ks.xPos = x;
    3471         8376 :     ks.line = line;
    3472         8376 :     ldata->keySymbols.push_back(ks);
    3473         8376 : }
    3474              : 
    3475        23295 : void TLayout::layoutKeySig(const KeySig* item, KeySig::LayoutData* ldata, const LayoutConfiguration& conf)
    3476              : {
    3477              :     LAYOUT_CALL_ITEM(item);
    3478              :     LD_INDEPENDENT;
    3479              : 
    3480              :     //! NOTE There are problems, an investigation is required
    3481              : //    if (ldata->isValid()) {
    3482              : //        return;
    3483              : //    }
    3484              : 
    3485        23295 :     ldata->setBbox(RectF());
    3486        23295 :     ldata->keySymbols.clear();
    3487              : 
    3488        23295 :     const Staff* staff = item->staff();
    3489        23295 :     const StaffType* stVisibility = staff ? staff->staffType(item->tick()) : nullptr;
    3490        23295 :     if (stVisibility && !stVisibility->genKeysig()) {
    3491          216 :         return;
    3492              :     }
    3493              : 
    3494        23079 :     const StaffType* st = item->staffType();
    3495        23079 :     const Segment* s = item->segment();
    3496        23079 :     track_idx_t track = item->track();
    3497        23079 :     double spatium = item->spatium();
    3498        23079 :     double step = spatium * (st ? st->lineDistance().val() * 0.5 : 0.5);
    3499              : 
    3500              :     // determine current clef for this staff
    3501        23079 :     ClefType clef = ClefType::G;
    3502        23079 :     if (item->staff()) {
    3503              :         // Look for a clef before the key signature at the same tick
    3504        23079 :         Clef* c = nullptr;
    3505        23079 :         if (s) {
    3506        46169 :             for (Segment* seg = s->prev1(); !c && seg && seg->tick() == item->tick(); seg = seg->prev1()) {
    3507        46027 :                 const bool isClefSeg = seg->isClefType() || seg->isHeaderClefType()
    3508        46027 :                                        || (seg->isClefRepeatAnnounceType() && s->isKeySigRepeatAnnounceType());
    3509        23090 :                 if (seg->enabled() && isClefSeg) {
    3510        21022 :                     c = toClef(seg->element(track));
    3511              :                 }
    3512              :             }
    3513              :         }
    3514              :         // If no clef found, get the clef type from the clefs list (using the previous tick)
    3515        23079 :         clef = c ? c->clefType() : item->staff()->clef(item->tick() - Fraction::eps());
    3516              :     }
    3517              : 
    3518        23079 :     int t1 = int(item->key());
    3519        23079 :     const signed char* lines = ClefInfo::lines(clef);
    3520              : 
    3521        23079 :     if (item->isCustom() && !item->isAtonal()) {
    3522           50 :         Spatium accidentalGap = conf.styleS(Sid::keysigAccidentalDistance);
    3523              :         // add standard key accidentals first, if necessary
    3524           50 :         for (int i = 1; i <= std::abs(t1) && std::abs(t1) <= 7; ++i) {
    3525            0 :             bool drop = false;
    3526            0 :             for (const CustDef& cd: item->customKeyDefs()) {
    3527            0 :                 int degree = item->degInKey(cd.degree);
    3528              :                 // if custom keysig accidental takes place, don't create tonal accidental
    3529            0 :                 if ((degree * 2 + 2) % 7 == (t1 < 0 ? 8 - i : i) % 7) {
    3530            0 :                     drop = true;
    3531            0 :                     break;
    3532              :                 }
    3533              :             }
    3534            0 :             if (!drop) {
    3535            0 :                 KeySym ks;
    3536            0 :                 int lineIndexOffset = t1 > 0 ? -1 : 6;
    3537            0 :                 ks.sym = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    3538            0 :                 ks.line = lines[lineIndexOffset + i];
    3539            0 :                 if (!ldata->keySymbols.empty()) {
    3540            0 :                     const KeySym& previous = ldata->keySymbols.back();
    3541            0 :                     Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
    3542            0 :                     ks.xPos = previous.xPos + previousWidth + accidentalGap;
    3543              :                 } else {
    3544            0 :                     ks.xPos = 0_sp;
    3545              :                 }
    3546              :                 // TODO octave metters?
    3547            0 :                 ldata->keySymbols.push_back(ks);
    3548              :             }
    3549              :         }
    3550          148 :         for (const CustDef& cd : item->customKeyDefs()) {
    3551           98 :             SymId sym = item->symInKey(cd.sym, cd.degree);
    3552           98 :             int degree = item->degInKey(cd.degree);
    3553           98 :             bool flat = std::string(SymNames::nameForSymId(sym).ascii()).find("Flat") != std::string::npos;
    3554           98 :             int accIdx = (degree * 2 + 1) % 7; // C D E F ... index to F C G D index
    3555           98 :             accIdx = flat ? 13 - accIdx : accIdx;
    3556           98 :             int line = lines[accIdx] + cd.octAlt * 7;
    3557           98 :             Spatium xpos = cd.xAlt;
    3558           98 :             if (!ldata->keySymbols.empty()) {
    3559           48 :                 const KeySym& previous = ldata->keySymbols.back();
    3560           48 :                 Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
    3561           48 :                 xpos += previous.xPos + previousWidth + accidentalGap;
    3562              :             }
    3563              :             // if translated symbol if out of range, add key accidental followed by untranslated symbol
    3564           98 :             if (sym == SymId::noSym) {
    3565            0 :                 KeySym ks;
    3566            0 :                 ks.line = line;
    3567            0 :                 ks.xPos = xpos;
    3568              :                 // for quadruple sharp use two double sharps
    3569            0 :                 if (cd.sym == SymId::accidentalTripleSharp) {
    3570            0 :                     ks.sym = SymId::accidentalDoubleSharp;
    3571            0 :                     sym = SymId::accidentalDoubleSharp;
    3572              :                 } else {
    3573            0 :                     ks.sym = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    3574            0 :                     sym = cd.sym;
    3575              :                 }
    3576            0 :                 ldata->keySymbols.push_back(ks);
    3577            0 :                 xpos += Spatium(t1 < 0 ? 0.7 : 1); // flats closer
    3578              :             }
    3579              :             // create symbol; natural only if is user defined
    3580           98 :             if (sym != SymId::accidentalNatural || sym == cd.sym) {
    3581           98 :                 KeySym ks;
    3582           98 :                 ks.sym = sym;
    3583           98 :                 ks.line = line;
    3584           98 :                 ks.xPos = xpos;
    3585           98 :                 ldata->keySymbols.push_back(ks);
    3586              :             }
    3587              :         }
    3588              :     } else {
    3589        23029 :         auto layoutSharpsFlats = [&]() {
    3590        23029 :             if (std::abs(t1) > 7) {
    3591            0 :                 LOGD("illegal t1 key %d", t1);
    3592            0 :                 return;
    3593              :             }
    3594        23029 :             SymId symbol = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
    3595        23029 :             int lineIndexOffset = t1 > 0 ? 0 : 7;
    3596        31091 :             for (int i = 0; i < std::abs(t1); ++i) {
    3597         8062 :                 keySigAddLayout(item, conf, symbol, lines[lineIndexOffset + i], ldata);
    3598              :             }
    3599        23029 :         };
    3600              : 
    3601              :         // Naturals are shown if:
    3602              :         // Key signature is courtesy, mid-measure or prev. measure has no section break and no courtesy keysig.
    3603              :         // AND we're not force hiding naturals (continuous mode)
    3604              :         // AND key sig is CMaj/Amin OR style says they are on
    3605        23029 :         const Measure* pm = item->measure() ? item->measure()->prevMeasureMM() : nullptr;
    3606        23029 :         const bool isCourtesy = s && (s->isType(SegmentType::CourtesyKeySigTypes) || !s->rtick().isZero());
    3607        23029 :         const bool prevTrailerCourtesy = pm && !pm->sectionBreak() && (!pm->trailer() || !pm->hasCourtesyKeySig());
    3608        46058 :         if (!item->hideNaturals() && track != muse::nidx
    3609        23029 :             && (conf.styleI(Sid::keySigNaturals) != int(KeySigNatural::NONE) || (t1 == 0))
    3610        46058 :             && (isCourtesy || prevTrailerCourtesy)) {
    3611         7963 :             KeySigEvent prevKsEvent = item->staff() ? item->staff()->keySigEvent(item->tick() - Fraction::eps()) : KeySigEvent();
    3612         7963 :             int t2 = int(prevKsEvent.key());
    3613              : 
    3614              :             // Handle naturals in continuation courtesy
    3615         7963 :             if (pm && s && s->isType(SegmentType::KeySigStartRepeatAnnounce)) {
    3616            0 :                 Segment* prevCourtesySeg = pm->findSegmentR(SegmentType::KeySigRepeatAnnounce, pm->ticks());
    3617            0 :                 if (prevCourtesySeg && prevCourtesySeg->element(track)) {
    3618            0 :                     t2 = int(toKeySig(prevCourtesySeg->element(track))->key());
    3619              :                 }
    3620              :             }
    3621              :             // Don't show naturals when going from sharps to flats, if style says so
    3622         7963 :             const bool sameAccidentals = t1 * t2 >= 0;
    3623         7963 :             if (t2 != 0 && (sameAccidentals || conf.styleB(Sid::keySigShowNaturalsChangingSharpsFlats))) {
    3624          110 :                 auto key2accidentals = [](int key) -> int {
    3625          110 :                     switch (std::abs(key)) {
    3626           28 :                     case 7: return 0x7f;
    3627            0 :                     case 6: return 0x3f;
    3628            8 :                     case 5: return 0x1f;
    3629            8 :                     case 4: return 0xf;
    3630           12 :                     case 3: return 0x7;
    3631           18 :                     case 2: return 0x3;
    3632            4 :                     case 1: return 0x1;
    3633           32 :                     case 0: return 0;
    3634            0 :                     default:
    3635            0 :                         LOGD("illegal key %d", key);
    3636            0 :                         return 0;
    3637              :                     }
    3638              :                 };
    3639              : 
    3640           72 :                 int naturals = key2accidentals(t2);
    3641              :                 // remove redundant naturals
    3642           72 :                 if (!((t1 > 0) ^ (t2 > 0))) {
    3643           38 :                     naturals &= ~key2accidentals(t1);
    3644              :                 }
    3645           72 :                 auto layoutNaturals = [&]() {
    3646           72 :                     int lineIndexOffset = t2 > 0 ? 0 : 7;
    3647          576 :                     for (int i = 0; i < 7; ++i) {
    3648          504 :                         if (naturals & (1 << i)) {
    3649          314 :                             keySigAddLayout(item, conf, SymId::accidentalNatural, lines[i + lineIndexOffset], ldata);
    3650              :                         }
    3651              :                     }
    3652          144 :                 };
    3653              :                 // Naturals should go BEFORE accidentals if style says so
    3654              :                 // or going from sharps to flats or vice versa (i.e. t1 & t2 have opposite signs)
    3655           72 :                 if (conf.styleI(Sid::keySigNaturals) == int(KeySigNatural::BEFORE) || !sameAccidentals) {
    3656            8 :                     layoutNaturals();
    3657            8 :                     layoutSharpsFlats();
    3658              :                 } else {
    3659           64 :                     layoutSharpsFlats();
    3660           64 :                     layoutNaturals();
    3661              :                 }
    3662         7891 :             } else if (prevKsEvent.custom()) {
    3663            0 :                 Fraction prevKeyTick = Fraction::fromTicks(item->staff()->keyList()->currentKeyTick(
    3664            0 :                                                                (item->tick() - Fraction::eps()).ticks()));
    3665            0 :                 Segment* prevKsSeg = item->score()->tick2segment(prevKeyTick, true, SegmentType::KeySig);
    3666            0 :                 if (prevKsSeg) {
    3667            0 :                     KeySig* prevCustomKeySig = toKeySig(prevKsSeg->element(item->track()));
    3668            0 :                     if (prevCustomKeySig) {
    3669            0 :                         for (KeySym keySym : prevCustomKeySig->ldata()->keySymbols) {
    3670            0 :                             if (keySym.sym != SymId::accidentalNatural) {
    3671            0 :                                 keySigAddLayout(item, conf, SymId::accidentalNatural, keySym.line, ldata);
    3672              :                             }
    3673              :                         }
    3674              :                     }
    3675              :                 }
    3676              :             }
    3677         7963 :         }
    3678              : 
    3679              :         // No naturals were added, so just create a regular keysig
    3680        23029 :         if (ldata->keySymbols.empty()) {
    3681        22957 :             layoutSharpsFlats();
    3682              :         }
    3683              :     }
    3684              : 
    3685        23079 :     ldata->setPosY((st ? step * st->stepOffset() : 0.0) + item->staffOffsetY());
    3686              : 
    3687        23079 :     Shape keySigShape;
    3688        31553 :     for (const KeySym& ks : ldata->keySymbols) {
    3689         8474 :         double x = ks.xPos.toAbsolute(spatium);
    3690         8474 :         double y = ks.line * step;
    3691         8474 :         Shape ksShape = item->symShapeWithCutouts(ks.sym);
    3692        38665 :         for (const ShapeElement& ksElem : ksShape.elements()) {
    3693        30191 :             keySigShape.add(ksElem.translated(x, y), item);
    3694              :         }
    3695         8474 :     }
    3696        23079 :     ldata->setShape(keySigShape);
    3697        23079 : }
    3698              : 
    3699            0 : void TLayout::layoutLaissezVib(LaissezVib* item)
    3700              : {
    3701              :     UNUSED(item);
    3702            0 : }
    3703              : 
    3704         2168 : void TLayout::layoutLayoutBreak(const LayoutBreak* item, LayoutBreak::LayoutData* ldata)
    3705              : {
    3706              :     LAYOUT_CALL_ITEM(item);
    3707              :     LD_INDEPENDENT;
    3708              : 
    3709         2168 :     if (ldata->isValid() || !item->configuration()->canLayoutIcons()) {
    3710         2168 :         return;
    3711              :     }
    3712              : 
    3713            0 :     FontMetrics metrics(item->font());
    3714            0 :     RectF bbox = metrics.boundingRect(item->iconCode());
    3715            0 :     ldata->setShape(Shape(bbox, item));
    3716            0 : }
    3717              : 
    3718          147 : void TLayout::layoutIndicatorIcon(const IndicatorIcon* item, IndicatorIcon::LayoutData* ldata)
    3719              : {
    3720          147 :     if (!item->configuration()->canLayoutIcons()) {
    3721          147 :         return;
    3722              :     }
    3723              : 
    3724            0 :     const MeasureBase* endMB = nullptr;
    3725              : 
    3726            0 :     Shape shape;
    3727              : 
    3728            0 :     const FontMetrics metrics(item->font());
    3729            0 :     const RectF iconBox = metrics.boundingRect(item->iconCode());
    3730            0 :     shape.add(iconBox, item);
    3731              : 
    3732            0 :     if (item->isSystemLockIndicator()) {
    3733            0 :         const SystemLockIndicator* sli = toSystemLockIndicator(item);
    3734            0 :         endMB = sli->systemLock()->endMB();
    3735            0 :         if (item->selected()) {
    3736              :             // Draw the range rect...
    3737            0 :             const RangeLock* lock = sli->systemLock();
    3738              : 
    3739            0 :             double xStart = lock->startMB()->x();
    3740            0 :             double xEnd = lock->endMB()->x() + lock->endMB()->width();
    3741              : 
    3742            0 :             double width = xEnd - xStart;
    3743            0 :             double height = iconBox.height() + sli->spatium();
    3744              : 
    3745            0 :             double y = iconBox.top() - 0.5 * sli->spatium();
    3746              : 
    3747            0 :             ldata->rangeRect = RectF(xStart, y, width, height).translated(-sli->x(), 0.0);
    3748            0 :             shape.add(ldata->rangeRect);
    3749              :         }
    3750              :     } else {
    3751            0 :         endMB = item->system()->last();
    3752              :     }
    3753              : 
    3754            0 :     ldata->setShape(shape);
    3755              : 
    3756            0 :     const double spatium = item->spatium();
    3757              : 
    3758            0 :     double x = endMB->x() + endMB->width();
    3759            0 :     x -= iconBox.right() + 0.5 * spatium;
    3760              : 
    3761            0 :     double xOffset = endMB->x() + endMB->width();
    3762            0 :     for (EngravingItem* el : endMB->el()) {
    3763            0 :         if (el->isLayoutBreak()) {
    3764            0 :             xOffset = std::min(xOffset, endMB->x() + el->x() + el->ldata()->bbox().left() - spatium);
    3765              :         }
    3766              :     }
    3767              : 
    3768            0 :     for (const SystemLockIndicator* sli : item->system()->systemLockIndicators()) {
    3769            0 :         if (sli != item && sli->systemLock()->endMB() == endMB) {
    3770            0 :             const double sliWidth = FontMetrics(sli->font()).boundingRect(sli->iconCode()).width();
    3771              :             // TODO: Rough spacing here
    3772            0 :             xOffset -= (sliWidth * 2) - spatium;
    3773              :         }
    3774            0 :     }
    3775              : 
    3776            0 :     x = std::min(x, xOffset - iconBox.right());
    3777              : 
    3778            0 :     ldata->setPos(PointF(x, -2.5 * spatium));
    3779              : 
    3780              :     // Ensure it goes behind notation and LayoutBreak
    3781            0 :     const_cast<IndicatorIcon*>(item)->setZ(-100);
    3782            0 : }
    3783              : 
    3784       206540 : static void _layoutLedgerLine(const LedgerLine* item, const LayoutContext& ctx, LedgerLine::LayoutData* ldata)
    3785              : {
    3786       206540 :     double chordMag = item->chord()->mag();
    3787       206540 :     ldata->setMag(chordMag);
    3788       206540 :     ldata->lineWidth = ctx.conf().styleAbsolute(Sid::ledgerLineWidth) * chordMag;
    3789       206540 :     if (item->staff()) {
    3790       206540 :         const_cast<LedgerLine*>(item)->setColor(item->staff()->staffType(item->tick())->color());
    3791              :     }
    3792       206540 :     double w2 = ldata->lineWidth * .5;
    3793              : 
    3794       206540 :     if (item->vertical()) {
    3795            0 :         ldata->setBbox(-w2, 0, w2, item->len());
    3796              :     } else {
    3797       206540 :         ldata->setBbox(0, -w2, item->len(), 2 * w2);
    3798              :     }
    3799       206540 : }
    3800              : 
    3801       206540 : void TLayout::layoutLedgerLine(LedgerLine* item, LayoutContext& ctx)
    3802              : {
    3803              :     LAYOUT_CALL_ITEM(item);
    3804       206540 :     _layoutLedgerLine(item, ctx, item->mutldata());
    3805       206540 : }
    3806              : 
    3807            0 : void TLayout::layoutLetRing(LetRing* item, LayoutContext& ctx)
    3808              : {
    3809              :     LAYOUT_CALL_ITEM(item);
    3810            0 :     layoutLine(item, ctx);
    3811            0 : }
    3812              : 
    3813           31 : void TLayout::layoutLetRingSegment(LetRingSegment* item, LayoutContext& ctx)
    3814              : {
    3815              :     LAYOUT_CALL_ITEM(item);
    3816           31 :     HarmonicMarkSegment::LayoutData* ldata = item->mutldata();
    3817              : 
    3818           31 :     const StaffType* stType = item->staffType();
    3819              : 
    3820           31 :     if (stType && stType->isHiddenElementOnTab(Sid::letRingShowTabCommon, Sid::letRingShowTabSimple)) {
    3821            0 :         ldata->setIsSkipDraw(true);
    3822            0 :         return;
    3823              :     }
    3824           31 :     ldata->setIsSkipDraw(false);
    3825              : 
    3826           31 :     layoutTextLineBaseSegment(item, ctx);
    3827              : 
    3828           31 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    3829              : }
    3830              : 
    3831         3055 : void TLayout::layoutLineSegment(LineSegment* item, LayoutContext& ctx)
    3832              : {
    3833              :     LAYOUT_CALL_ITEM(item);
    3834         3055 :     layoutItem(item, ctx);
    3835         3055 : }
    3836              : 
    3837         2180 : void TLayout::layoutLyrics(Lyrics* item, LayoutContext& ctx)
    3838              : {
    3839              :     LAYOUT_CALL_ITEM(item);
    3840         2180 :     LyricsLayout::layout(item, ctx);
    3841         2180 : }
    3842              : 
    3843          328 : void TLayout::layoutLyricsLine(LyricsLine* item)
    3844              : {
    3845              :     LAYOUT_CALL_ITEM(item);
    3846          328 :     LyricsLayout::layout(item);
    3847          328 : }
    3848              : 
    3849          348 : void TLayout::layoutLyricsLineSegment(LyricsLineSegment* item, LayoutContext& ctx)
    3850              : {
    3851              :     LAYOUT_CALL_ITEM(item);
    3852          348 :     LyricsLayout::layout(item, ctx);
    3853          348 : }
    3854              : 
    3855          309 : void TLayout::layoutMarker(Marker* item, Marker::LayoutData* ldata, LayoutContext& ctx)
    3856              : {
    3857              :     LAYOUT_CALL_ITEM(item);
    3858              : 
    3859          309 :     MarkerLayout::layoutMarker(item, ldata, ctx);
    3860          309 : }
    3861              : 
    3862          147 : void TLayout::layoutMeasureBase(MeasureBase* item, LayoutContext& ctx)
    3863              : {
    3864          147 :     layoutItem(item, ctx);
    3865          147 : }
    3866              : 
    3867        36303 : void TLayout::layoutBaseMeasureBase(const MeasureBase* item, MeasureBase::LayoutData* ldata, const LayoutContext& ctx)
    3868              : {
    3869        36303 :     LD_CONDITION(ldata->isSetBbox());
    3870              : 
    3871        36303 :     double x = ldata->bbox().width();
    3872        43016 :     for (EngravingItem* e : item->el()) {
    3873         6713 :         if (e->isLayoutBreak()) {
    3874         2162 :             TLayout::layoutItem(e, const_cast<LayoutContext&>(ctx));
    3875         2162 :             EngravingItem::LayoutData* eldata = e->mutldata();
    3876         2162 :             double spatium = item->spatium();
    3877         2162 :             double y = 0.0;
    3878         2162 :             if (toLayoutBreak(e)->isNoBreak()) {
    3879           83 :                 x -= eldata->bbox().right() * .5;
    3880              :             } else {
    3881         2079 :                 x -= eldata->bbox().right() + 0.5 * spatium;
    3882              :             }
    3883         2162 :             y = -2.5 * spatium;
    3884         2162 :             eldata->setPos(x, y);
    3885         2162 :             x += eldata->bbox().left() - 0.5 * spatium;
    3886         4551 :         } else if (e->isMarker() || e->isJump()) {
    3887              :         } else {
    3888         4018 :             layoutItem(e, const_cast<LayoutContext&>(ctx));
    3889              :         }
    3890              :     }
    3891        36303 : }
    3892              : 
    3893         3851 : void TLayout::layoutMeasureNumber(MeasureNumber* item, MeasureNumber::LayoutData* ldata, const LayoutContext& ctx)
    3894              : {
    3895         3851 :     MeasureNumberLayout::layoutMeasureNumber(item,  ldata, ctx);
    3896         3851 : }
    3897              : 
    3898          365 : void TLayout::layoutMeasureRepeat(const MeasureRepeat* item, MeasureRepeat::LayoutData* ldata, const LayoutContext& ctx)
    3899              : {
    3900              :     LAYOUT_CALL_ITEM(item);
    3901              : 
    3902              :     //! NOTE The types are listed here explicitly to show what types there are (see Rest::add method)
    3903              :     //! and accordingly show what depends on.
    3904          365 :     for (EngravingItem* e : item->el()) {
    3905            0 :         switch (e->type()) {
    3906            0 :         case ElementType::DEAD_SLAPPED: {
    3907            0 :             DeadSlapped* ds = item_cast<DeadSlapped*>(e);
    3908              :             LD_INDEPENDENT;
    3909            0 :             layoutDeadSlapped(ds, ds->mutldata());
    3910            0 :         } break;
    3911            0 :         case ElementType::SYMBOL: {
    3912            0 :             Symbol* s = item_cast<Symbol*>(e);
    3913              :             // LD_X not clear yet
    3914            0 :             layoutSymbol(s, s->mutldata(), ctx);
    3915            0 :         } break;
    3916            0 :         case ElementType::IMAGE: {
    3917            0 :             Image* im = item_cast<Image*>(e);
    3918              :             LD_INDEPENDENT;
    3919            0 :             layoutImage(im, im->mutldata());
    3920            0 :         } break;
    3921            0 :         default:
    3922            0 :             UNREACHABLE;
    3923              :         }
    3924              :     }
    3925              : 
    3926          365 :     switch (item->numMeasures()) {
    3927          276 :     case 1:
    3928              :     {
    3929          276 :         ldata->setSymId(SymId::repeat1Bar);
    3930          276 :         if (ctx.conf().styleB(Sid::mrNumberSeries) && item->track() != muse::nidx) {
    3931            0 :             int placeInSeries = 2; // "1" would be the measure actually being repeated
    3932            0 :             staff_idx_t staffIdx = item->staffIdx();
    3933            0 :             const Measure* m = item->measure();
    3934            0 :             while (m && m->isOneMeasureRepeat(staffIdx) && m->prevIsOneMeasureRepeat(staffIdx)) {
    3935            0 :                 placeInSeries++;
    3936            0 :                 m = m->prevMeasure();
    3937              :             }
    3938            0 :             if (placeInSeries % ctx.conf().styleI(Sid::mrNumberEveryXMeasures) == 0) {
    3939            0 :                 if (ctx.conf().styleB(Sid::mrNumberSeriesWithParentheses)) {
    3940            0 :                     ldata->setNumberSym(String(u"(%1)").arg(placeInSeries));
    3941              :                 } else {
    3942            0 :                     ldata->setNumberSym(placeInSeries);
    3943              :                 }
    3944              :             } else {
    3945            0 :                 ldata->clearNumberSym();
    3946              :             }
    3947          276 :         } else if (ctx.conf().styleB(Sid::oneMeasureRepeatShow1)) {
    3948            0 :             ldata->setNumberSym(1);
    3949              :         } else {
    3950          276 :             ldata->clearNumberSym();
    3951              :         }
    3952          276 :         break;
    3953              :     }
    3954           46 :     case 2:
    3955           46 :         ldata->setSymId(SymId::repeat2Bars);
    3956           46 :         ldata->setNumberSym(item->numMeasures());
    3957           46 :         break;
    3958           43 :     case 4:
    3959           43 :         ldata->setSymId(SymId::repeat4Bars);
    3960           43 :         ldata->setNumberSym(item->numMeasures());
    3961           43 :         break;
    3962            0 :     default:
    3963            0 :         ldata->setSymId(SymId::noSym); // should never happen
    3964            0 :         ldata->clearNumberSym();
    3965            0 :         break;
    3966              :     }
    3967              : 
    3968          365 :     RectF bbox = item->symBbox(ldata->symId);
    3969              : 
    3970          365 :     if (item->track() != muse::nidx) { // if this is in score rather than a palette cell
    3971              :         // For unknown reasons, the symbol has some offset in almost all SMuFL fonts
    3972              :         // We compensate for it, to make sure the symbol is visually centered around the staff line
    3973          365 :         double offset = (-bbox.top() - bbox.bottom()) / 2.0;
    3974              : 
    3975          365 :         const StaffType* staffType = item->staffType();
    3976              : 
    3977              :         // Only need to set y position here; x position is handled in MeasureLayout::layoutMeasureElements()
    3978          365 :         ldata->setPos(0, std::floor(staffType->middleLine() / 2.0) * staffType->lineDistance().val() * item->spatium() + offset);
    3979              :     }
    3980              : 
    3981          365 :     ChordLayout::fillShape(item, ldata, ctx.conf());
    3982          365 : }
    3983              : 
    3984            0 : void TLayout::layoutMeasureRepeatExtender(const MeasureRepeat* item, MeasureRepeat::LayoutData* ldata, const LayoutContext& ctx)
    3985              : {
    3986            0 :     ldata->extenderLineLeft = LineF();
    3987            0 :     ldata->extenderLineRight = LineF();
    3988              : 
    3989            0 :     Measure* parentMeasure = item->measure();
    3990            0 :     if (!parentMeasure) {
    3991            0 :         return;
    3992              :     }
    3993              : 
    3994            0 :     Measure* startMeasure = parentMeasure->prevMeasure();
    3995            0 :     if (!startMeasure || startMeasure->system() != parentMeasure->system()) {
    3996            0 :         return;
    3997              :     }
    3998              : 
    3999            0 :     Measure* endMeasure = parentMeasure->nextMeasure();
    4000            0 :     if (endMeasure) {
    4001            0 :         endMeasure = endMeasure->nextMeasure();
    4002              :     }
    4003            0 :     if (!endMeasure || endMeasure->system() != parentMeasure->system()) {
    4004            0 :         return;
    4005              :     }
    4006              : 
    4007              :     // Reuse the function that calculates start and end pos for mmRests,
    4008              :     // except start and end point are on different measures
    4009            0 :     staff_idx_t staffIdx = item->staffIdx();
    4010            0 :     double xStart = MeasureLayout::getMeasureStartEndPos(startMeasure, startMeasure->first(SegmentType::ChordRest), staffIdx,
    4011            0 :                                                          /*needHeaderException*/ startMeasure->header(), /*modernMMrest*/ true, ctx).x1;
    4012            0 :     double xEnd = MeasureLayout::getMeasureStartEndPos(endMeasure, endMeasure->first(SegmentType::ChordRest), staffIdx,
    4013            0 :                                                        /*needHeaderException*/ false, /*modernMRest*/ true, ctx).x2;
    4014              : 
    4015            0 :     double margin = ctx.conf().styleAbsolute(Sid::multiMeasureRestMargin);
    4016            0 :     xStart += margin;
    4017            0 :     xEnd -= margin;
    4018              : 
    4019            0 :     double xItemInSysCoords = item->x() + item->segment()->x() + parentMeasure->x();
    4020              : 
    4021            0 :     PointF startPoint(xStart + startMeasure->x() - xItemInSysCoords, 0.0);
    4022            0 :     PointF midPointL(-0.5 * item->spatium(), 0.0);
    4023            0 :     ldata->extenderLineLeft = LineF(startPoint, midPointL);
    4024              : 
    4025            0 :     PointF midPointR(item->symWidth(ldata->symId) + 0.5 * item->spatium(), 0.0);
    4026            0 :     PointF endPoint(xEnd + endMeasure->x() - xItemInSysCoords, 0.0);
    4027            0 :     ldata->extenderLineRight = LineF(midPointR, endPoint);
    4028              : }
    4029              : 
    4030          356 : void TLayout::layoutMMRest(const MMRest* item, MMRest::LayoutData* ldata, const LayoutContext& ctx)
    4031              : {
    4032              :     LAYOUT_CALL_ITEM(item);
    4033              : 
    4034              :     //! NOTE The types are listed here explicitly to show what types there are (see Rest::add method)
    4035              :     //! and accordingly show what depends on.
    4036          356 :     for (EngravingItem* e : item->el()) {
    4037            0 :         switch (e->type()) {
    4038            0 :         case ElementType::DEAD_SLAPPED: {
    4039            0 :             DeadSlapped* ds = item_cast<DeadSlapped*>(e);
    4040              :             LD_INDEPENDENT;
    4041            0 :             layoutDeadSlapped(ds, ds->mutldata());
    4042            0 :         } break;
    4043            0 :         case ElementType::SYMBOL: {
    4044            0 :             Symbol* s = item_cast<Symbol*>(e);
    4045              :             // LD_X not clear yet
    4046            0 :             layoutSymbol(s, s->mutldata(), ctx);
    4047            0 :         } break;
    4048            0 :         case ElementType::IMAGE: {
    4049            0 :             Image* im = item_cast<Image*>(e);
    4050              :             LD_INDEPENDENT;
    4051            0 :             layoutImage(im, im->mutldata());
    4052            0 :         } break;
    4053            0 :         default:
    4054            0 :             UNREACHABLE;
    4055              :         }
    4056              :     }
    4057              : 
    4058          356 :     LD_CONDITION(ldata->restWidth.has_value());
    4059              : 
    4060              :     //! NOTE This is not look like layout data, perhaps this is should be set not here
    4061          356 :     ldata->number = item->measure()->mmRestCount();
    4062          356 :     ldata->setNumberSym(ldata->number);
    4063          356 :     ldata->yNumberPos = ctx.conf().styleAbsolute(Sid::mmRestNumberPos);
    4064              : 
    4065          356 :     const Staff* staff = item->staff();
    4066          356 :     if (staff->lines(item->tick()) == 1) {
    4067            0 :         ldata->yNumberPos = std::min(ldata->yNumberPos, -item->spatium());
    4068              :     }
    4069              : 
    4070          356 :     if (item->isOldStyle()) {
    4071           66 :         SymIdList restSyms;
    4072           66 :         double symsWidth = 0.0;
    4073              : 
    4074           66 :         int remaining = ldata->number;
    4075           66 :         double spacing = ctx.conf().styleAbsolute(Sid::mmRestOldStyleSpacing);
    4076              :         SymId sym;
    4077              : 
    4078          132 :         while (remaining > 0) {
    4079           66 :             if (remaining >= 4) {
    4080            0 :                 sym = SymId::restLonga;
    4081            0 :                 remaining -= 4;
    4082           66 :             } else if (remaining >= 2) {
    4083            0 :                 sym = SymId::restDoubleWhole;
    4084            0 :                 remaining -= 2;
    4085              :             } else {
    4086           66 :                 sym = SymId::restWhole;
    4087           66 :                 remaining -= 1;
    4088              :             }
    4089              : 
    4090           66 :             restSyms.push_back(sym);
    4091           66 :             symsWidth += item->symBbox(sym).width();
    4092              : 
    4093           66 :             if (remaining > 0) { // do not add spacing after last symbol
    4094            0 :                 symsWidth += spacing;
    4095              :             }
    4096              :         }
    4097              : 
    4098           66 :         ldata->restSyms = restSyms;
    4099           66 :         ldata->symsWidth = symsWidth;
    4100           66 :     }
    4101              : 
    4102              :     // Only need to set y position here; x position is handled in MeasureLayout::layoutMeasureElements()
    4103          356 :     const StaffType* staffType = item->staffType();
    4104          356 :     double midline = staffType->middleLine() / 2.0;
    4105          356 :     if (item->isOldStyle()) {
    4106           66 :         midline = std::max(1.0, midline);
    4107              :     }
    4108          356 :     ldata->setPos(0, midline * staffType->lineDistance().val() * item->spatium());
    4109              : 
    4110          356 :     ChordLayout::fillShape(item, ldata, ctx.conf());
    4111          356 : }
    4112              : 
    4113            0 : void TLayout::layoutMMRestRange(MMRestRange* item, MMRestRange::LayoutData* ldata, const LayoutContext& ctx)
    4114              : {
    4115            0 :     MeasureNumberLayout::layoutMMRestRange(item, ldata, ctx);
    4116            0 : }
    4117              : 
    4118        60053 : void TLayout::layoutNote(const Note* item, Note::LayoutData* ldata)
    4119              : {
    4120              :     LAYOUT_CALL_ITEM(item);
    4121              :     LD_INDEPENDENT;
    4122              : 
    4123        60053 :     if (!ldata->isSetPos()) {
    4124          734 :         ldata->setPos(PointF());
    4125              :     }
    4126              : 
    4127        60053 :     bool useTablature = item->staff() && item->staff()->isTabStaff(item->chord()->tick());
    4128        60053 :     ldata->useTablature.set_value(useTablature);
    4129              : 
    4130        60053 :     RectF noteBBox;
    4131        60053 :     if (useTablature) {
    4132         1355 :         if (item->displayFret() == Note::DisplayFretOption::Hide) {
    4133            0 :             return;
    4134              :         }
    4135              : 
    4136         1355 :         const Staff* st = item->staff();
    4137         1355 :         const StaffType* tab = st->staffTypeForElement(item);
    4138              :         // not complete but we need systems to be laid out to add parenthesis
    4139         1355 :         if (item->fixed()) {
    4140            0 :             const_cast<Note*>(item)->setFretString(u"/");
    4141              :         } else {
    4142         1355 :             const_cast<Note*>(item)->setFretString(tab->fretString(std::fabs(item->fret()), item->string(), item->deadNote()));
    4143              : 
    4144         1355 :             if (item->negativeFretUsed() && !item->deadNote()) {
    4145           40 :                 const_cast<Note*>(item)->setFretString(u"-" + item->fretString());
    4146              :             }
    4147              : 
    4148         1355 :             if (item->displayFret() == Note::DisplayFretOption::ArtificialHarmonic) {
    4149            0 :                 const_cast<Note*>(item)->setFretString(String(u"%1 <%2>").arg(item->fretString(), String::number(item->harmonicFret())));
    4150         1355 :             } else if (item->displayFret() == Note::DisplayFretOption::NaturalHarmonic) {
    4151            0 :                 const_cast<Note*>(item)->setFretString(String(u"<%1>").arg(String::number(item->harmonicFret())));
    4152              :             }
    4153              :         }
    4154              : 
    4155         1355 :         double w = item->tabHeadWidth(tab);
    4156         1355 :         double mags = item->magS();
    4157         1355 :         double y = item->deadNote() ? tab->deadFretBoxY() : tab->fretBoxY();
    4158         1355 :         double height = item->deadNote() ? tab->deadFretBoxH() : tab->fretBoxH();
    4159              : 
    4160         1355 :         noteBBox = RectF(0, y * mags, w, height * mags);
    4161              :     } else {
    4162        58698 :         if (item->deadNote()) {
    4163          218 :             const_cast<Note*>(item)->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
    4164        58480 :         } else if (item->harmonic()) {
    4165           74 :             const_cast<Note*>(item)->setHeadGroup(NoteHeadGroup::HEAD_DIAMOND);
    4166              :         }
    4167              : 
    4168        58698 :         SymId nh = item->noteHead();
    4169        58698 :         ldata->cachedNoteheadSym.set_value(nh);
    4170              : 
    4171        58698 :         if (item->isNoteName()) {
    4172          137 :             ldata->cachedSymNull.set_value(SymId::noteEmptyBlack);
    4173          137 :             NoteHeadType ht = item->headType() == NoteHeadType::HEAD_AUTO ? item->chord()->durationType().headType() : item->headType();
    4174          137 :             if (ht == NoteHeadType::HEAD_WHOLE) {
    4175            8 :                 ldata->cachedSymNull.set_value(SymId::noteEmptyWhole);
    4176          129 :             } else if (ht == NoteHeadType::HEAD_HALF) {
    4177            0 :                 ldata->cachedSymNull.set_value(SymId::noteEmptyHalf);
    4178              :             }
    4179              :         } else {
    4180        58561 :             ldata->cachedSymNull.set_value(SymId::noSym);
    4181              :         }
    4182        58698 :         noteBBox = item->symBbox(nh);
    4183              :     }
    4184              : 
    4185        60053 :     ldata->setBbox(noteBBox);
    4186              : 
    4187        60053 :     fillNoteShape(item, ldata);
    4188              : }
    4189              : 
    4190       121109 : void TLayout::fillNoteShape(const Note* item, Note::LayoutData* ldata)
    4191              : {
    4192              :     LAYOUT_CALL_ITEM(item);
    4193       121109 :     RectF noteBBox = ldata->bbox();
    4194              : 
    4195       121109 :     Shape shape(Shape::Type::Composite);
    4196       121109 :     shape.add(noteBBox, item);
    4197              : 
    4198       123216 :     for (const NoteDot* dot : item->dots()) {
    4199         2107 :         if (!dot->addToSkyline()) {
    4200            4 :             continue;
    4201              :         }
    4202         2103 :         shape.add(item->symBbox(SymId::augmentationDot).translated(dot->pos()), dot);
    4203              :     }
    4204              : 
    4205       121109 :     Accidental* acc = item->accidental();
    4206       121109 :     if (acc && acc->addToSkyline()) {
    4207         8151 :         shape.add(acc->ldata()->shape().translated(acc->pos()));
    4208              :     }
    4209       121109 :     const LaissezVib* lv = item->laissezVib();
    4210       121109 :     const LaissezVibSegment* lvSeg = lv && !lv->segmentsEmpty() ? lv->frontSegment() : nullptr;
    4211       121109 :     if (lvSeg && lvSeg->addToSkyline()) {
    4212          108 :         const PointF cornerClosestToNote = lv->up() ? lvSeg->shape().bbox().bottomLeft() : lvSeg->shape().bbox().topLeft();
    4213          108 :         const PointF pos = lvSeg->ldata()->posRelativeToNote.value() - cornerClosestToNote;
    4214          108 :         shape.add(lvSeg->shape().translate(pos));
    4215              :     }
    4216       122040 :     for (auto e : item->el()) {
    4217          931 :         if (e->addToSkyline()) {
    4218          896 :             if (e->isFingering() && toFingering(e)->layoutType() != ElementType::NOTE) {
    4219          783 :                 continue;
    4220              :             }
    4221          113 :             shape.add(e->ldata()->bbox().translated(e->pos()), e);
    4222              :         }
    4223              :     }
    4224              : 
    4225              :     // This method is also called from SingleLayout, where `part` may be nullptr
    4226       121109 :     Part* part = item->part();
    4227       121109 :     if (part && part->instrument()->hasStrings()) {
    4228        14459 :         GuitarBend* bend = item->bendFor();
    4229        14459 :         if (bend && bend->addToSkyline() && bend->bendType() == GuitarBendType::SLIGHT_BEND && !bend->segmentsEmpty()) {
    4230           70 :             GuitarBendSegment* bendSeg = toGuitarBendSegment(bend->frontSegment());
    4231              :             // Semi-hack: the relative position of note and bend
    4232              :             // isn't fully known yet, so we use an approximation
    4233           70 :             double sp = item->spatium();
    4234           70 :             PointF approxRelPos(noteBBox.width() + 0.25 * sp, -0.25 * sp);
    4235           70 :             shape.add(bendSeg->shape().translate(approxRelPos));
    4236              :         }
    4237        14459 :         GuitarBend* bendBack = item->bendBack();
    4238         1228 :         if (bendBack && bendBack->bendType() == GuitarBendType::SCOOP && !bendBack->segmentsEmpty()
    4239        15687 :             && item->findAncestor(ElementType::SYSTEM)) {
    4240            3 :             GuitarBendSegment* seg = toGuitarBendSegment(bendBack->frontSegment());
    4241            3 :             shape.add(seg->shape().translated(seg->pos() - item->systemPos()));
    4242              :         }
    4243              :     }
    4244              : 
    4245       121109 :     ldata->setShape(shape);
    4246       121109 : }
    4247              : 
    4248         1215 : void TLayout::layoutNoteDot(const NoteDot* item, NoteDot::LayoutData* ldata)
    4249              : {
    4250              :     LAYOUT_CALL_ITEM(item);
    4251              :     LD_INDEPENDENT;
    4252              : 
    4253         1215 :     if (ldata->isValid()) {
    4254           59 :         return;
    4255              :     }
    4256              : 
    4257         1156 :     ldata->setBbox(item->symBbox(SymId::augmentationDot));
    4258              : }
    4259              : 
    4260          751 : void TLayout::layoutOrnament(const Ornament* item, Ornament::LayoutData* ldata, const LayoutConfiguration& conf)
    4261              : {
    4262              :     LAYOUT_CALL_ITEM(item);
    4263          751 :     LD_CONDITION(ldata->symId.has_value());
    4264              : 
    4265          751 :     ldata->setShape(Shape());
    4266              : 
    4267          751 :     layoutArticulation(const_cast<Ornament*>(item), ldata);
    4268              : 
    4269          751 :     double _spatium = item->spatium();
    4270          751 :     double vertMargin = 0.35 * _spatium;
    4271              :     static constexpr double ornamentAccidentalMag = 0.6; // TODO: style?
    4272              : 
    4273         4506 :     for (size_t i = 0; i < item->accidentalsAboveAndBelow().size(); ++i) {
    4274         1502 :         bool above = (i == 0);
    4275         1502 :         Accidental* accidental = item->accidentalsAboveAndBelow()[i];
    4276         1502 :         if (!accidental) {
    4277         1434 :             continue;
    4278              :         }
    4279           68 :         Accidental::LayoutData* accLData = accidental->mutldata();
    4280              : 
    4281           68 :         LD_CONDITION(accLData->isSetBbox());
    4282              : 
    4283           68 :         accidental->computeMag();
    4284              : 
    4285           68 :         accLData->setMag(accLData->mag() * ornamentAccidentalMag);
    4286           68 :         accLData->syms.clear(); // Invalidate
    4287           68 :         layoutAccidental(accidental, accLData, conf);
    4288           68 :         Shape accidentalShape = accidental->shape();
    4289              :         double minVertDist = above
    4290          108 :                              ? accidentalShape.minVerticalDistance(Shape(ldata->bbox()))
    4291           68 :                              : Shape(ldata->bbox()).minVerticalDistance(accidentalShape);
    4292              : 
    4293          108 :         accLData->setPos(-0.5 * accLData->bbox().width() + 0.5 * ldata->bbox().width(),
    4294           40 :                          above ? (-minVertDist - vertMargin) : (minVertDist + vertMargin));
    4295           68 :     }
    4296              : 
    4297          751 :     Shape sh(Shape::Type::Composite);
    4298          751 :     sh.add(ldata->bbox(), item); // from Articulation
    4299         2253 :     for (const Accidental* accidental : item->accidentalsAboveAndBelow()) {
    4300         1502 :         if (accidental && accidental->visible()) {
    4301           68 :             LD_CONDITION(accidental->ldata()->isSetShape());
    4302           68 :             LD_CONDITION(accidental->ldata()->isSetPos());
    4303           68 :             sh.add(accidental->shape().translate(accidental->pos()));
    4304              :         }
    4305              :     }
    4306              : 
    4307          751 :     ldata->setShape(sh);
    4308          751 : }
    4309              : 
    4310           16 : void TLayout::layoutOrnamentCueNote(Ornament* item, LayoutContext& ctx)
    4311              : {
    4312              :     LAYOUT_CALL_ITEM(item);
    4313           16 :     if (!item->ownershipParent()) {
    4314            0 :         return;
    4315              :     }
    4316              : 
    4317           16 :     Chord* parentChord = toChord(item->chordRest());
    4318           16 :     Chord* cueNoteChord = item->cueNoteChord();
    4319              : 
    4320           16 :     if (!cueNoteChord) {
    4321            0 :         return;
    4322              :     }
    4323              : 
    4324           16 :     const std::vector<Note*>& notes = cueNoteChord->notes();
    4325           16 :     Note* cueNote = notes.empty() ? nullptr : notes.front();
    4326              : 
    4327           16 :     if (!cueNote) {
    4328            0 :         return;
    4329              :     }
    4330              : 
    4331           64 :     ChordLayout::layoutChords3({ cueNoteChord }, { cueNote }, item->staff(), ctx);
    4332           32 :     ChordLayout::layoutLedgerLines({ cueNoteChord }, ctx);
    4333           32 :     AccidentalsLayout::layoutAccidentals({ cueNoteChord }, ctx);
    4334           16 :     layoutChord(cueNoteChord, ctx);
    4335              : 
    4336           16 :     Shape noteShape = cueNoteChord->shape();
    4337           16 :     Shape parentChordShape = parentChord->shape();
    4338           16 :     double minDist = HorizontalSpacing::minHorizontalDistance(parentChordShape, noteShape, parentChord->spatium());
    4339              :     // Check for possible other chords in same segment
    4340           16 :     staff_idx_t startStaff = staff2track(parentChord->staffIdx());
    4341           80 :     for (staff_idx_t staff = startStaff; staff < startStaff + VOICES; ++staff) {
    4342           64 :         Segment* segment = parentChord->segment();
    4343           64 :         ChordRest* cr = segment->element(staff) ? toChordRest(segment->element(staff)) : nullptr;
    4344           64 :         if (cr) {
    4345           16 :             minDist = std::max(minDist, HorizontalSpacing::minHorizontalDistance(cr->shape(), noteShape, cr->spatium()));
    4346              :         }
    4347              :     }
    4348           16 :     cueNoteChord->mutldata()->setPosX(minDist);
    4349           16 : }
    4350              : 
    4351            0 : void TLayout::layoutOttava(Ottava* item, LayoutContext& ctx)
    4352              : {
    4353              :     LAYOUT_CALL_ITEM(item);
    4354            0 :     layoutLine(item, ctx);
    4355            0 : }
    4356              : 
    4357          161 : void TLayout::layoutOttavaSegment(OttavaSegment* item, LayoutContext& ctx)
    4358              : {
    4359              :     LAYOUT_CALL_ITEM(item);
    4360          161 :     OttavaSegment::LayoutData* ldata = item->mutldata();
    4361              : 
    4362          161 :     layoutTextLineBaseSegment(item, ctx);
    4363              : 
    4364          161 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    4365          161 : }
    4366              : 
    4367           56 : void TLayout::layoutPageLockIndicator(const PageLockIndicator* item, PageLockIndicator::LayoutData* ldata)
    4368              : {
    4369           56 :     if (!item->configuration()->canLayoutIcons()) {
    4370           56 :         return;
    4371              :     }
    4372            0 :     const Page* page = item->page();
    4373            0 :     const System* sys = item->system();
    4374              : 
    4375            0 :     IF_ASSERT_FAILED(page && sys) {
    4376            0 :         return;
    4377              :     }
    4378              : 
    4379            0 :     Shape shape;
    4380              : 
    4381            0 :     const double spatium = item->spatium();
    4382              : 
    4383            0 :     const FontMetrics metrics(item->font());
    4384            0 :     const RectF iconBox = metrics.boundingRect(item->iconCode());
    4385            0 :     shape.add(iconBox, item);
    4386              : 
    4387            0 :     const double borderWidth = iconBox.height() + spatium;
    4388            0 :     const double borderPadding = 2 * spatium;
    4389            0 :     const double iconInset = (borderWidth - iconBox.width()) / 2;
    4390            0 :     PointF posOnPage = PointF(page->width() - borderPadding - iconBox.width() - iconBox.left() - iconInset,
    4391            0 :                               page->height() - borderPadding - iconInset);
    4392            0 :     PointF posOnSystem = posOnPage - sys->pos();
    4393            0 :     ldata->setPos(posOnSystem);
    4394              : 
    4395            0 :     if (item->selected()) {
    4396              :         // Outer range rectangle
    4397            0 :         RectF rangeRect = page->shape().bbox();
    4398            0 :         PointF rectPagePos = rangeRect.topLeft() - ldata->pos() - sys->pos();
    4399            0 :         rangeRect.translate(rectPagePos);
    4400            0 :         rangeRect.adjust(borderPadding, borderPadding, -borderPadding, -borderPadding);
    4401            0 :         ldata->rangeRect = rangeRect;
    4402              : 
    4403              :         // Inner rectangle cutout
    4404              :         // Leaves border with rounded corners when painted
    4405            0 :         RectF innerRect = rangeRect.adjusted(borderWidth, borderWidth, -borderWidth, -borderWidth);
    4406            0 :         ldata->innerRangeRect = innerRect;
    4407            0 :         shape.add(RectF(PointF(rangeRect.left(),  rangeRect.top()),     PointF(rangeRect.right(),   innerRect.top())));       // top
    4408            0 :         shape.add(RectF(PointF(rangeRect.left(),  innerRect.bottom()),  PointF(rangeRect.right(),   rangeRect.bottom())));    // bottom
    4409            0 :         shape.add(RectF(PointF(rangeRect.left(),  innerRect.top()),     PointF(innerRect.left(),    innerRect.bottom())));    // left
    4410            0 :         shape.add(RectF(PointF(innerRect.right(), innerRect.top()),     PointF(rangeRect.right(),   innerRect.bottom())));     // right
    4411              :     }
    4412              : 
    4413            0 :     ldata->setShape(shape);
    4414              : 
    4415              :     // Ensure it goes behind notation and LayoutBreak
    4416            0 :     const_cast<PageLockIndicator*>(item)->setZ(-100);
    4417            0 : }
    4418              : 
    4419            0 : void TLayout::layoutPalmMute(PalmMute* item, LayoutContext& ctx)
    4420              : {
    4421              :     LAYOUT_CALL_ITEM(item);
    4422            0 :     layoutLine(item, ctx);
    4423            0 : }
    4424              : 
    4425           14 : void TLayout::layoutPalmMuteSegment(PalmMuteSegment* item, LayoutContext& ctx)
    4426              : {
    4427              :     LAYOUT_CALL_ITEM(item);
    4428           14 :     PalmMuteSegment::LayoutData* ldata = item->mutldata();
    4429              : 
    4430           14 :     const StaffType* stType = item->staffType();
    4431              : 
    4432           14 :     if (stType && stType->isHiddenElementOnTab(Sid::palmMuteShowTabCommon, Sid::palmMuteShowTabSimple)) {
    4433            0 :         ldata->setIsSkipDraw(true);
    4434            0 :         return;
    4435              :     }
    4436           14 :     ldata->setIsSkipDraw(false);
    4437              : 
    4438           14 :     layoutTextLineBaseSegment(item, ctx);
    4439              : 
    4440           14 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    4441              : }
    4442              : 
    4443            0 : void TLayout::layoutParenthesis(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
    4444              : {
    4445            0 :     ParenthesisLayout::layoutParenthesis(item, ldata, ctx);
    4446            0 : }
    4447              : 
    4448            0 : void TLayout::layoutPedal(Pedal* item, LayoutContext& ctx)
    4449              : {
    4450              :     LAYOUT_CALL_ITEM(item);
    4451            0 :     layoutLine(item, ctx);
    4452            0 : }
    4453              : 
    4454          119 : void TLayout::layoutPedalSegment(PedalSegment* item, LayoutContext& ctx)
    4455              : {
    4456              :     LAYOUT_CALL_ITEM(item);
    4457          119 :     PedalSegment::LayoutData* ldata = item->mutldata();
    4458              : 
    4459          119 :     layoutTextLineBaseSegment(item, ctx);
    4460              : 
    4461          119 :     Text* endText = item->endText();
    4462          119 :     if (endText && !endText->empty() && ldata->npoints > 0) { // Rosette
    4463           11 :         PointF endPoint = item->pos2();
    4464           11 :         double endTextWidth = endText->ldata()->bbox().width();
    4465           11 :         double xEndText = endPoint.x() - endTextWidth;
    4466           11 :         if (const Text* startText = item->text()) {
    4467           11 :             xEndText = std::max(xEndText, startText->ldata()->bbox().width());
    4468              :         }
    4469           11 :         endText->mutldata()->setPosX(xEndText);
    4470              : 
    4471           11 :         double lineTextGap = item->absoluteFromSpatium(item->getProperty(Pid::GAP_BETWEEN_TEXT_AND_LINE).value<Spatium>());
    4472           11 :         PointF& endOfLine = ldata->points.at(ldata->npoints - 1);
    4473           11 :         endOfLine.setX(xEndText - lineTextGap);
    4474              : 
    4475              :         // Recalculate shape after moving end text
    4476           11 :         Shape sh = recalculateTextLineBaseSegmentShape(item);
    4477           11 :         ldata->setShape(sh);
    4478           11 :     }
    4479              : 
    4480          119 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    4481          119 : }
    4482              : 
    4483            2 : void TLayout::layoutPickScrapeSegment(PickScrapeSegment* item, LayoutContext& ctx)
    4484              : {
    4485              :     LAYOUT_CALL_ITEM(item);
    4486            2 :     PickScrapeSegment::LayoutData* ldata = item->mutldata();
    4487            2 :     layoutTextLineBaseSegment(item, ctx);
    4488            2 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    4489            2 : }
    4490              : 
    4491            6 : void TLayout::layoutPlayCountText(PlayCountText* item, TextBase::LayoutData* ldata)
    4492              : {
    4493              :     LAYOUT_CALL_ITEM(item);
    4494            6 :     BarLine* bl = item->barline();
    4495            6 :     Segment* seg = item->segment();
    4496              : 
    4497            6 :     TextLayout::layoutBaseTextBase(item, ldata);
    4498              : 
    4499              :     // Avoid incoming barlines from above
    4500            6 :     double xAdj = 0.0;
    4501            6 :     BarLine* blAbove = item->staffIdx() != 0 ? toBarLine(seg->element(staff2track(item->staffIdx() - 1))) : nullptr;
    4502            6 :     if (blAbove && blAbove->spanStaff()) {
    4503            0 :         xAdj = blAbove->width();
    4504              :     }
    4505              : 
    4506            6 :     const double barlineWidth = bl->width();
    4507            6 :     const double diff = item->width() - barlineWidth + xAdj;
    4508            6 :     ldata->moveX(-diff);
    4509            6 : }
    4510              : 
    4511           87 : void TLayout::layoutPlayTechAnnotation(const PlayTechAnnotation* item, PlayTechAnnotation::LayoutData* ldata)
    4512              : {
    4513              :     LAYOUT_CALL_ITEM(item);
    4514           87 :     TextLayout::layoutBaseTextBase(item, ldata);
    4515              : 
    4516           87 :     if (item->isHandbellsSymbol()) {
    4517           25 :         Chord* chord = nullptr;
    4518           25 :         Segment* seg = item->segment();
    4519           25 :         track_idx_t sTrack = staff2track(item->staffIdx());
    4520           25 :         track_idx_t eTrack = sTrack + VOICES;
    4521           25 :         for (track_idx_t track = sTrack; track < eTrack; ++track) {
    4522           25 :             if (seg->element(track) && seg->element(track)->isChord()) {
    4523           25 :                 chord = toChord(seg->element(track));
    4524           25 :                 break;
    4525              :             }
    4526              :         }
    4527           25 :         double center = 0.0;
    4528           25 :         if (chord) {
    4529           25 :             Note* refNote = item->placeAbove() ? chord->upNote() : chord->downNote();
    4530           25 :             center = 0.5 * refNote->width() + refNote->x() + chord->x();
    4531              :         } else {
    4532            0 :             center = 0.5 * item->score()->noteHeadWidth();
    4533              :         }
    4534           25 :         ldata->setPosX(center - 0.5 * (ldata->bbox().right() - ldata->bbox().left()));
    4535              :     }
    4536              : 
    4537           87 :     if (item->autoplace()) {
    4538           87 :         const Segment* s = toSegment(item->ownershipParent());
    4539           87 :         const Measure* m = s->measure();
    4540           87 :         LD_CONDITION(ldata->isSetPos());
    4541           87 :         LD_CONDITION(m->ldata()->isSetPos());
    4542           87 :         LD_CONDITION(s->ldata()->isSetPos());
    4543              :     }
    4544              : 
    4545           87 :     Autoplace::autoplaceSegmentElement(item, ldata);
    4546           87 : }
    4547              : 
    4548            4 : void TLayout::layoutRasgueadoSegment(RasgueadoSegment* item, LayoutContext& ctx)
    4549              : {
    4550              :     LAYOUT_CALL_ITEM(item);
    4551            4 :     RasgueadoSegment::LayoutData* ldata = item->mutldata();
    4552            4 :     const StaffType* stType = item->staffType();
    4553              : 
    4554            4 :     if (stType && stType->isHiddenElementOnTab(Sid::rasgueadoShowTabCommon, Sid::rasgueadoShowTabSimple)) {
    4555            0 :         ldata->setIsSkipDraw(true);
    4556            0 :         return;
    4557              :     }
    4558            4 :     ldata->setIsSkipDraw(false);
    4559              : 
    4560            4 :     layoutTextLineBaseSegment(item, ctx);
    4561              : 
    4562            4 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    4563              : }
    4564              : 
    4565          127 : void TLayout::layoutRehearsalMark(const RehearsalMark* item, RehearsalMark::LayoutData* ldata)
    4566              : {
    4567              :     LAYOUT_CALL_ITEM(item);
    4568          127 :     TextLayout::layoutBaseTextBase(item, ldata);
    4569              : 
    4570          127 :     const Segment* s = item->segment();
    4571              : 
    4572          127 :     LD_CONDITION(s->ldata()->isSetPos());
    4573              : 
    4574          127 :     if (!s->rtick().isZero()) {
    4575           27 :         return;
    4576              :     }
    4577              : 
    4578              :     // first CR of measure, alignment is hcenter or right (the usual cases)
    4579              :     // align with barline, point just after header, or start of measure depending on context
    4580          100 :     const Measure* m = s->measure();
    4581          100 :     const Segment* header = s->prev();                // possibly just a start repeat
    4582          100 :     while (header && header->isType(SegmentType::Duration)) {
    4583            0 :         header = header->prev();
    4584              :     }
    4585          100 :     double measureX = -s->x();
    4586          100 :     const Segment* repeat = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0, 1));
    4587              : 
    4588          100 :     if (repeat) {
    4589            0 :         LD_CONDITION(repeat->ldata()->isSetPos());
    4590              :     }
    4591              : 
    4592          100 :     double barlineX = repeat ? repeat->x() - s->x() : measureX;
    4593          100 :     const System* sys = m->system();
    4594          100 :     bool systemFirst = (sys && m->isFirstInSystem());
    4595              : 
    4596          100 :     if (!header || repeat || !systemFirst) {
    4597              :         // no header, or header with repeat, or header mid-system - align with barline
    4598           32 :         ldata->setPosX(barlineX);
    4599           32 :         checkRehearsalMarkVSBigTimeSig(item, ldata);
    4600           32 :         return;
    4601              :     }
    4602              : 
    4603              :     // header at start of system
    4604              :     // align to a point just after the header
    4605           68 :     EngravingItem* e = header->element(item->track());
    4606              : 
    4607           68 :     if (e) {
    4608           68 :         LD_CONDITION(e->ldata()->isSetBbox());
    4609              :     }
    4610              : 
    4611           68 :     LD_CONDITION(header->ldata()->isSetBbox());
    4612              : 
    4613           68 :     double w = e ? e->ldata()->bbox().width() : header->ldata()->bbox().width();
    4614           68 :     ldata->setPosX(header->x() + w - s->x());
    4615              : 
    4616              :     // special case for right aligned rehearsal marks at start of system
    4617              :     // left align with start of measure if that is further left
    4618           68 :     if (item->position() == AlignH::RIGHT) {
    4619            0 :         ldata->setPosX(std::min(ldata->pos().x(), measureX + ldata->bbox().width()));
    4620              :     }
    4621              : 
    4622           68 :     checkRehearsalMarkVSBigTimeSig(item, ldata);
    4623              : }
    4624              : 
    4625          100 : void TLayout::checkRehearsalMarkVSBigTimeSig(const RehearsalMark* item, TextBase::LayoutData* ldata)
    4626              : {
    4627          100 :     if (item->score()->style().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>() != TimeSigPlacement::ABOVE_STAVES) {
    4628          100 :         return;
    4629              :     }
    4630              : 
    4631            0 :     const Segment* s = item->segment();
    4632            0 :     TimeSig* bigTimeSig = nullptr;
    4633            0 :     for (const Segment* segment = s; segment && segment->tick() == s->tick(); segment = segment->prevActive()) {
    4634            0 :         if (!segment->isType(SegmentType::TimeSigTypes)) {
    4635            0 :             continue;
    4636              :         }
    4637            0 :         TimeSig* timeSig = toTimeSig(segment->element(item->track()));
    4638            0 :         if (timeSig && timeSig->visible()) {
    4639            0 :             bigTimeSig = timeSig;
    4640            0 :             break;
    4641              :         }
    4642              :     }
    4643              : 
    4644            0 :     if (!bigTimeSig) {
    4645            0 :         return;
    4646              :     }
    4647              : 
    4648            0 :     double timeSigRightEdge = bigTimeSig->ldata()->bbox().translated(bigTimeSig->ldata()->pos()).right()
    4649            0 :                               + bigTimeSig->segment()->x() - s->x();
    4650            0 :     double rehMarkLeftEdge = ldata->bbox().translated(item->ldata()->pos()).left();
    4651            0 :     double distance = rehMarkLeftEdge - timeSigRightEdge;
    4652            0 :     const double margin = 0.5 * item->fontMetrics().xHeight();
    4653            0 :     if (distance < margin) {
    4654            0 :         ldata->moveX(margin - distance);
    4655              :     }
    4656              : }
    4657              : 
    4658        66760 : void TLayout::layoutRest(const Rest* item, Rest::LayoutData* ldata, const LayoutContext& ctx)
    4659              : {
    4660        66760 :     RestLayout::layoutRest(item, ldata, ctx);
    4661        66760 : }
    4662              : 
    4663            0 : void TLayout::layoutShadowNote(ShadowNote* item, LayoutContext& ctx)
    4664              : {
    4665              :     LAYOUT_CALL_ITEM(item);
    4666            0 :     if (!item->isValid()) {
    4667            0 :         item->setbbox(RectF());
    4668            0 :         return;
    4669              :     }
    4670              : 
    4671            0 :     double _spatium = item->spatium();
    4672            0 :     double mag = item->mag();
    4673            0 :     RectF noteheadBbox = item->symBbox(item->noteheadSymbol());
    4674            0 :     bool up = item->computeUp();
    4675              : 
    4676              :     // Layout dots
    4677            0 :     double dotWidth = 0.0;
    4678            0 :     if (item->duration().dots() > 0) {
    4679            0 :         double noteheadWidth = noteheadBbox.width();
    4680            0 :         double d  = ctx.conf().styleAbsolute(Sid::dotNoteDistance) * mag;
    4681            0 :         double dd = ctx.conf().styleAbsolute(Sid::dotDotDistance) * mag;
    4682            0 :         dotWidth = (noteheadWidth + d);
    4683            0 :         if (item->hasFlag() && up) {
    4684            0 :             dotWidth = std::max(dotWidth, noteheadWidth + item->symBbox(item->flagSym()).right());
    4685              :         }
    4686            0 :         for (int i = 0; i < item->duration().dots(); i++) {
    4687            0 :             dotWidth += dd * i;
    4688              :         }
    4689              :     }
    4690              : 
    4691            0 :     RectF newBbox;
    4692            0 :     newBbox.setRect(noteheadBbox.x(), noteheadBbox.y(), noteheadBbox.width() + dotWidth, noteheadBbox.height());
    4693              : 
    4694              :     // Layout stem and flag
    4695            0 :     if (item->hasStem()) {
    4696            0 :         double x = noteheadBbox.x();
    4697            0 :         double w = noteheadBbox.width();
    4698              : 
    4699            0 :         double stemWidth = ctx.conf().styleAbsolute(Sid::stemWidth);
    4700            0 :         double stemLength = (up ? -3.5 : 3.5) * _spatium;
    4701            0 :         double stemAnchor = item->symSmuflAnchor(item->noteheadSymbol(), up ? SmuflAnchorId::stemUpSE : SmuflAnchorId::stemDownNW).y();
    4702              : 
    4703            0 :         newBbox |= RectF(up ? x + w - stemWidth : x,
    4704              :                          stemAnchor,
    4705              :                          stemWidth,
    4706            0 :                          stemLength - stemAnchor);
    4707              : 
    4708            0 :         if (item->hasFlag()) {
    4709            0 :             RectF flagBbox = item->symBbox(item->flagSym());
    4710            0 :             newBbox |= RectF(up ? x + w - stemWidth : x,
    4711            0 :                              stemAnchor + stemLength + flagBbox.y(),
    4712              :                              flagBbox.width(),
    4713            0 :                              flagBbox.height());
    4714              :         }
    4715              :     }
    4716              : 
    4717            0 :     int lineIdx = item->lineIndex();
    4718              : 
    4719              :     // Layout ledger lines if needed
    4720            0 :     if (item->ledgerLinesVisible()) {
    4721            0 :         double extraLen = ctx.conf().styleAbsolute(Sid::ledgerLineLength) * mag;
    4722            0 :         double step = 0.5 * _spatium * item->staffType()->lineDistance().val();
    4723            0 :         double yOffset = item->staffOffsetY();
    4724            0 :         double x = noteheadBbox.x() - extraLen;
    4725            0 :         double w = noteheadBbox.width() + 2 * extraLen;
    4726              : 
    4727            0 :         double lw = ctx.conf().styleAbsolute(Sid::ledgerLineWidth);
    4728              : 
    4729            0 :         RectF r(x, -lw * .5, w, lw);
    4730            0 :         const int topLine = -2 + yOffset / step;
    4731            0 :         for (int i = topLine; i >= lineIdx; i -= 2) {
    4732            0 :             newBbox |= r.translated(PointF(0, step * (i - lineIdx)));
    4733              :         }
    4734            0 :         int l = item->staffType()->lines() * 2 + yOffset / step; // first ledger line below staff
    4735            0 :         for (int i = l; i <= lineIdx; i += 2) {
    4736            0 :             newBbox |= r.translated(PointF(0, step * (i - lineIdx)));
    4737              :         }
    4738              :     }
    4739              : 
    4740              :     // Layout accidental
    4741            0 :     SymId acc = Accidental::subtype2symbol(item->accidentalType());
    4742            0 :     if (acc != SymId::noSym) {
    4743            0 :         RectF symRect = item->symBbox(acc);
    4744            0 :         double accWidth = symRect.width() + ctx.conf().styleAbsolute(Sid::accidentalNoteDistance) * mag;
    4745            0 :         double dh = 0.0;
    4746              : 
    4747            0 :         if (symRect.y() < newBbox.y()) {
    4748            0 :             dh = symRect.height() - (newBbox.y() - symRect.y());
    4749            0 :             newBbox.setY(symRect.y());
    4750            0 :         } else if (symRect.y() > newBbox.y()) {
    4751            0 :             dh = newBbox.height() - (symRect.y() - newBbox.y());
    4752              :         }
    4753              : 
    4754            0 :         newBbox.setX(newBbox.x() - accWidth);
    4755            0 :         newBbox.setWidth(newBbox.width() + accWidth);
    4756            0 :         newBbox.setHeight(newBbox.height() + dh);
    4757              :     }
    4758              : 
    4759            0 :     const std::set<SymId>& articulationIds = item->articulationIds();
    4760            0 :     if (articulationIds.empty()) {
    4761            0 :         item->setbbox(newBbox);
    4762            0 :         return;
    4763              :     }
    4764              : 
    4765              :     // Layout articulations
    4766            0 :     bool distantArticulations = false;
    4767            0 :     double minDist = 0.4 * _spatium;
    4768            0 :     if ((!up && lineIdx > 1) || (up && lineIdx < item->staffType()->bottomLine() - 1)) {
    4769            0 :         minDist = _spatium;
    4770              :     }
    4771              : 
    4772              :     // Extend bounding box for nearby articulations
    4773            0 :     for (const SymId& artic: item->articulationIds()) {
    4774            0 :         String articName = Articulation::symId2ArticulationName(artic);
    4775            0 :         const double symH = item->symHeight(artic);
    4776            0 :         if (articName == u"staccato" || articName == u"tenuto") {
    4777            0 :             if (!up) {
    4778            0 :                 newBbox.setTop(newBbox.y() - minDist - symH);
    4779              :             } else {
    4780            0 :                 newBbox.setBottom(newBbox.bottom() + minDist + symH);
    4781              :             }
    4782              :         } else {
    4783            0 :             distantArticulations = true;
    4784              :         }
    4785            0 :     }
    4786              : 
    4787              :     // If there are any other articulations, extend bounding box to include whole staff
    4788              :     // And extend further to include those articulations
    4789            0 :     if (distantArticulations) {
    4790            0 :         minDist = 0.4 * _spatium;
    4791            0 :         const double staffTop = -_spatium * .5 * lineIdx;
    4792            0 :         const double staffBottom = -_spatium * .5 * (item->staffType()->bottomLine() - lineIdx);
    4793            0 :         if (newBbox.y() > staffTop) {
    4794            0 :             newBbox.setTop(staffTop);
    4795              :         }
    4796            0 :         if (newBbox.bottom() < staffBottom) {
    4797            0 :             newBbox.setBottom(staffBottom);
    4798              :         }
    4799              : 
    4800            0 :         for (const SymId& artic: item->articulationIds()) {
    4801            0 :             String articName = Articulation::symId2ArticulationName(artic);
    4802            0 :             const double symH = item->symHeight(artic);
    4803            0 :             bool isMarcato = Articulation::symId2ArticulationName(artic).contains(u"marcato");
    4804            0 :             bool isLv = artic == SymId::articLaissezVibrerAbove || artic == SymId::articLaissezVibrerBelow;
    4805            0 :             if (articName == u"staccato" || articName == u"tenuto") {
    4806            0 :                 continue;
    4807              :             } else {
    4808            0 :                 if (!up || isMarcato) {
    4809            0 :                     newBbox.setTop(newBbox.y() - minDist - symH);
    4810              :                 } else {
    4811            0 :                     newBbox.setBottom(newBbox.bottom() + minDist + symH);
    4812              :                 }
    4813              :             }
    4814            0 :             if (isLv) {
    4815            0 :                 const double width = item->symWidth(artic) - item->symWidth(item->noteheadSymbol()) / 2;
    4816            0 :                 newBbox.setWidth(newBbox.width() + width);
    4817              :             }
    4818            0 :         }
    4819              :     }
    4820              : 
    4821            0 :     item->setbbox(newBbox);
    4822              : }
    4823              : 
    4824          460 : void TLayout::layoutLine(SLine* item, LayoutContext& ctx)
    4825              : {
    4826              :     LAYOUT_CALL_ITEM(item);
    4827          460 :     item->computeStartElement();
    4828          460 :     item->computeEndElement();
    4829              : 
    4830              :     System* s1;
    4831              :     System* s2;
    4832          460 :     PointF p1(item->linePos(Grip::START, &s1));
    4833          460 :     PointF p2(item->linePos(Grip::END,   &s2));
    4834              : 
    4835          460 :     const std::vector<System*>& systems = ctx.dom().systems();
    4836          460 :     system_idx_t sysIdx1 = muse::indexOf(systems, s1);
    4837          460 :     system_idx_t sysIdx2 = muse::indexOf(systems, s2);
    4838          460 :     int segmentsNeeded = 0;
    4839              : 
    4840          460 :     if (sysIdx1 == muse::nidx || sysIdx2 == muse::nidx) {
    4841            0 :         return;
    4842              :     }
    4843              : 
    4844          921 :     for (system_idx_t i = sysIdx1; i <= sysIdx2; ++i) {
    4845          461 :         if (systems.at(i)->vbox()) {
    4846            0 :             continue;
    4847              :         }
    4848          461 :         ++segmentsNeeded;
    4849              :     }
    4850              : 
    4851          460 :     int segCount = int(item->spannerSegments().size());
    4852              : 
    4853          460 :     if (segmentsNeeded != segCount) {
    4854          319 :         item->fixupSegments(segmentsNeeded, [item]() { return item->createLineSegment(); });
    4855          159 :         if (segmentsNeeded > segCount) {
    4856          319 :             for (int i = segCount; i < segmentsNeeded; ++i) {
    4857          160 :                 LineSegment* lineSegm = item->segmentAt(i);
    4858              :                 // set user offset to previous segment's offset
    4859          160 :                 if (segCount > 0) {
    4860            0 :                     lineSegm->setOffset(PointF(0, item->segmentAt(i - 1)->offset().y()));
    4861              :                 } else {
    4862          160 :                     lineSegm->setOffset(PointF(0, item->offset().y()));
    4863              :                 }
    4864              :             }
    4865              :         }
    4866              :     }
    4867              : 
    4868          460 :     int segIdx = 0;
    4869          921 :     for (system_idx_t i = sysIdx1; i <= sysIdx2; ++i) {
    4870          461 :         System* system = systems.at(i);
    4871          461 :         if (system->vbox()) {
    4872            0 :             continue;
    4873              :         }
    4874          461 :         LineSegment* lineSegm = item->segmentAt(segIdx++);
    4875          461 :         lineSegm->setTrack(item->track());           // DEBUG
    4876          461 :         lineSegm->moveToSystem(system);
    4877              : 
    4878          461 :         if (sysIdx1 == sysIdx2) {
    4879              :             // single segment
    4880          459 :             lineSegm->setSpannerSegmentType(SpannerSegmentType::SINGLE);
    4881          459 :             double len = p2.x() - p1.x();
    4882              :             // enforcing a minimum length would be possible but inadvisable
    4883              :             // the line length calculations are tuned well enough that this should not be needed
    4884              :             //if (anchor() == Anchor::SEGMENT && !isPedal())
    4885              :             //      len = std::max(1.0 * spatium(), len);
    4886          459 :             lineSegm->setPos(p1);
    4887          459 :             lineSegm->setPos2(PointF(len, p2.y() - p1.y()));
    4888            2 :         } else if (i == sysIdx1) {
    4889              :             // start segment
    4890            1 :             lineSegm->setSpannerSegmentType(SpannerSegmentType::BEGIN);
    4891            1 :             lineSegm->setPos(p1);
    4892            1 :             double x2 = system->endingXForOpenEndedLines();
    4893            1 :             lineSegm->setPos2(PointF(x2 - p1.x(), 0.0));
    4894            1 :         } else if (i > 0 && i != sysIdx2) {
    4895              :             // middle segment
    4896            0 :             lineSegm->setSpannerSegmentType(SpannerSegmentType::MIDDLE);
    4897            0 :             double x1 = system->firstNoteRestSegmentX(true);
    4898            0 :             double x2 = system->endingXForOpenEndedLines();
    4899            0 :             lineSegm->setPos(PointF(x1, p1.y()));
    4900            0 :             lineSegm->setPos2(PointF(x2 - x1, 0.0));
    4901            1 :         } else if (i == sysIdx2) {
    4902              :             // end segment
    4903            1 :             double minLen = 0.0;
    4904            1 :             double x1 = system->firstNoteRestSegmentX(true);
    4905            1 :             double len = std::max(minLen, p2.x() - x1);
    4906            1 :             lineSegm->setSpannerSegmentType(SpannerSegmentType::END);
    4907            1 :             lineSegm->setPos(PointF(p2.x() - len, p2.y()));
    4908            1 :             lineSegm->setPos2(PointF(len, 0.0));
    4909              :         }
    4910          461 :         layoutLineSegment(lineSegm, ctx);
    4911              :     }
    4912              : }
    4913              : 
    4914          460 : void TLayout::layoutNoteAnchoredLine(SLine* item, EngravingItem::LayoutData* ldata, LayoutContext& ctx)
    4915              : {
    4916          460 :     double _spatium = item->spatium();
    4917          460 :     Note* startAnchor = toNote(item->startElement());
    4918          460 :     Note* endAnchor = toNote(item->endElement());
    4919          460 :     Chord* startChord = startAnchor->chord();
    4920          460 :     Chord* endChord = endAnchor->chord();
    4921          460 :     LineSegment* startSeg = toLineSegment(item->frontSegment());
    4922          460 :     LineSegment* endSeg = toLineSegment(item->backSegment());
    4923              : 
    4924              :     // Note: line segments are defined by
    4925              :     // initial point: ipos() (relative to system origin)
    4926              :     // ending point:  pos2() (relative to initial point)
    4927              : 
    4928              :     // LINE ENDING POINTS TO NOTEHEAD CENTRES
    4929              : 
    4930              :     // assume line goes from centre of initial note centre to centre of ending note:
    4931              :     // move first segment origin and last segment ending point from notehead origin to notehead centre
    4932              :     // For TAB: begin at the right-edge of initial note rather than centre
    4933          460 :     PointF startOffset = (startChord->staff()->isTabStaff(startChord->tick()))
    4934          460 :                          ? PointF(startAnchor->ldata()->bbox().right(), 0.0)
    4935          460 :                          : PointF(startAnchor->headWidth() * 0.5, 0.0);
    4936              : 
    4937          460 :     PointF endOffset = PointF(endAnchor->headWidth() * 0.5, 0.0);
    4938              : 
    4939              :     // AVOID HORIZONTAL LINES
    4940              : 
    4941              :     // for microtonality read tuning, or check note accidental
    4942          460 :     double startTune = startAnchor->tuning();
    4943          460 :     double endTune = endAnchor->tuning();
    4944          460 :     AccidentalType startAcc = startAnchor->accidentalType();
    4945          460 :     AccidentalType endAcc = endAnchor->accidentalType();
    4946          460 :     if (muse::RealIsNull(startTune) && Accidental::isMicrotonal(startAcc)) {
    4947            0 :         startTune = Accidental::subtype2centOffset(startAcc);
    4948              :     }
    4949          460 :     if (muse::RealIsNull(endTune) && Accidental::isMicrotonal(endAcc)) {
    4950            0 :         endTune = Accidental::subtype2centOffset(endAcc);
    4951              :     }
    4952              : 
    4953          460 :     int upDown = (0 < (endAnchor->ppitch() - startAnchor->ppitch())) - ((endAnchor->ppitch() - startAnchor->ppitch()) < 0);
    4954              :     // same note, so compare tunings
    4955          460 :     if (upDown == 0) {
    4956            3 :         upDown = (0 < (endTune - startTune)) - ((endTune - startTune) < 0);
    4957              :     }
    4958              : 
    4959              :     // on TAB's, glissando are by necessity on the same string, this gives an horizontal glissando line;
    4960              :     // make bottom end point lower and top ending point higher
    4961          460 :     if (startChord->staff()->isTabStaff(startChord->tick())) {
    4962           15 :         double yOff = startChord->staff()->lineDistance(startChord->tick()) * 0.4 * _spatium;
    4963           15 :         startOffset.ry() += yOff * upDown;
    4964           15 :         endOffset.ry() -= yOff * upDown;
    4965              :     }
    4966              :     // if not TAB, angle glissando between notes on the same line
    4967              :     else {
    4968          445 :         if (startAnchor->line() == endAnchor->line()) {
    4969            3 :             startOffset.ry() += _spatium * 0.25 * upDown;
    4970            3 :             endOffset.ry() -= _spatium * 0.25 * upDown;
    4971              :         }
    4972              :     }
    4973              : 
    4974              :     // move initial point of first segment and adjust its length accordingly
    4975          460 :     startSeg->setPos(startSeg->ldata()->pos() + startOffset);
    4976          460 :     startSeg->setPos2(startSeg->ipos2() - startOffset);
    4977              :     // adjust ending point of last segment
    4978          460 :     endSeg->setPos2(endSeg->ipos2() + endOffset);
    4979              : 
    4980              :     // INTERPOLATION OF INTERMEDIATE POINTS
    4981              : 
    4982              :     // get total x-width and total y-height of all segments
    4983          460 :     double xTot = 0.0;
    4984          921 :     for (SpannerSegment* segm : item->spannerSegments()) {
    4985          461 :         xTot += segm->ipos2().x();
    4986              :     }
    4987          460 :     double startY   = startSeg->ldata()->pos().y();
    4988          460 :     double yTot = endSeg->ldata()->pos().y() + endSeg->ipos2().y() - startY;
    4989          460 :     yTot -= yStaffDifference(endSeg->system(), startSeg->system(), track2staff(item->track2()));
    4990          460 :     double ratio = muse::divide(yTot, xTot, 1.0);
    4991              :     // interpolate y-coord of intermediate points across total width and height
    4992          460 :     double xCurr = 0.0;
    4993              :     double yCurr;
    4994          461 :     for (unsigned i = 0; i + 1 < item->spannerSegments().size(); i++) {
    4995            1 :         SpannerSegment* segm = item->segmentAt(i);
    4996            1 :         xCurr += segm->ipos2().x();
    4997            1 :         yCurr = startY + ratio * xCurr;
    4998            1 :         segm->rypos2() = yCurr - segm->ldata()->pos().y();           // position segm. end point at yCurr
    4999              :         // next segment shall start where this segment stopped
    5000            1 :         SpannerSegment* nextSeg = item->segmentAt(i + 1);
    5001            1 :         yCurr += yStaffDifference(nextSeg->system(), segm->system(), track2staff(item->track2()));
    5002            1 :         segm = nextSeg;
    5003            1 :         segm->rypos2() += segm->ldata()->pos().y() - yCurr;          // adjust next segm. vertical length
    5004            1 :         segm->mutldata()->setPosY(yCurr);                                // position next segm. start point at yCurr
    5005              :     }
    5006              : 
    5007              :     // KEEP CLEAR OF ALL ELEMENTS OF THE CHORD
    5008              :     // Remove offset already applied
    5009          460 :     startOffset  *= -1.0;
    5010          460 :     endOffset *= -1.0;
    5011              :     // Look at chord shapes (but don't consider lyrics)
    5012          460 :     Shape startCRShape = startChord->shape();
    5013          460 :     startCRShape.remove_if([](ShapeElement& s) {
    5014         1099 :         if (!s.item() || s.item()->isLyrics()) {
    5015            6 :             return true;
    5016              :         } else {
    5017         1093 :             return false;
    5018              :         }
    5019              :     });
    5020              : 
    5021          460 :     double startYAbove = startAnchor->ldata()->pos().y() + startAnchor->ldata()->bbox().topRight().y();
    5022          460 :     double startYBelow = startYAbove + startAnchor->ldata()->bbox().height();
    5023          460 :     startOffset.rx() += startCRShape.rightMostEdgeAtHeight(startYAbove, startYBelow) - startAnchor->pos().x();
    5024          460 :     if (!endChord->staff()->isTabStaff(endChord->tick())) {
    5025          445 :         double endYAbove = endAnchor->ldata()->pos().y() + endAnchor->ldata()->bbox().topLeft().y();
    5026          445 :         double endYBelow = endYAbove + endAnchor->ldata()->bbox().height();
    5027          445 :         double noteMiddle = endYAbove + endAnchor->ldata()->bbox().height() / 2;
    5028          445 :         if (upDown != 0) {
    5029          442 :             int llWidth = ctx.conf().styleS(Sid::ledgerLineWidth).val() * _spatium;
    5030              :             // Only check top/bottom half of note depending on line approach direction
    5031              :             // to avoid clearing acidentals the line won't collide with
    5032          442 :             endYAbove = upDown == 1 ? noteMiddle - llWidth : endYAbove;
    5033          442 :             endYBelow = upDown == 1 ? endYBelow : noteMiddle + llWidth;
    5034              :         }
    5035              : 
    5036          445 :         endOffset.rx() -= endAnchor->pos().x() - endChord->shape().leftMostEdgeAtHeight(endYAbove, endYBelow);
    5037              :     }
    5038              :     // Add note distance
    5039          460 :     const double lineNoteDist = 0.25 * item->spatium(); // TODO: style
    5040          460 :     startOffset.rx() += lineNoteDist;
    5041          460 :     endOffset.rx() -= lineNoteDist;
    5042              : 
    5043              :     // apply offsets: shorten first segment by x1 (and proportionally y) and adjust its length accordingly
    5044          460 :     startOffset.ry() = startSeg->ipos2().y() * muse::divide(startOffset.x(), startSeg->ipos2().x(), 1.0);
    5045          460 :     startSeg->setPos(startSeg->ldata()->pos() + startOffset);
    5046          460 :     startSeg->setPos2(startSeg->ipos2() - startOffset);
    5047              :     // adjust last segment length by x2 (and proportionally y)
    5048          460 :     endOffset.ry() = endSeg->ipos2().y() * muse::divide(endOffset.x(), endSeg->ipos2().x(), 1.0);
    5049          460 :     endSeg->setPos2(endSeg->ipos2() + endOffset);
    5050              : 
    5051          921 :     for (SpannerSegment* segm : item->spannerSegments()) {
    5052          461 :         TLayout::layoutItem(segm, ctx);
    5053              :     }
    5054              : 
    5055              :     // compute line bbox as the bbox of the last segment, relative to the end anchor note
    5056          460 :     PointF endAnchorPagePos = endAnchor->pagePos();
    5057          460 :     PointF endSystemPagePos;
    5058          460 :     IF_ASSERT_FAILED(endChord->segment()->system()) {
    5059            0 :         endSystemPagePos = endSeg->pos();
    5060              :     } else {
    5061          460 :         endSystemPagePos = endChord->segment()->system()->pagePos();
    5062              :     }
    5063              : 
    5064          460 :     PointF endAnchorSystPos = endAnchorPagePos - endSystemPagePos;
    5065          460 :     RectF r = RectF(endAnchorSystPos - endSeg->pos(), endAnchorSystPos - endSeg->pos() - endSeg->pos2()).normalized();
    5066          460 :     double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
    5067          460 :     ldata->setBbox(r.adjusted(-lw, -lw, lw, lw));
    5068          460 : }
    5069              : 
    5070            6 : void TLayout::layoutNoteLine(NoteLine* item, LayoutContext& ctx)
    5071              : {
    5072              :     LAYOUT_CALL_ITEM(item);
    5073              : 
    5074            6 :     if (item->startElement()) {
    5075            6 :         item->setTick(item->startElement()->tick());
    5076              :     }
    5077              : 
    5078            6 :     if (item->endElement()) {
    5079            6 :         item->setTick2(item->endElement()->tick());
    5080              :     }
    5081              : 
    5082            6 :     TLayout::layoutLine(item, ctx);
    5083              : 
    5084            6 :     if (item->lineEndPlacement() == NoteLineEndPlacement::OFFSET_ENDS) {
    5085            6 :         layoutNoteAnchoredLine(item, item->mutldata(), ctx);
    5086            6 :         ChordLayout::addLineAttachPoints(item);
    5087              :     }
    5088            6 : }
    5089              : 
    5090           12 : void TLayout::layoutNoteLineSegment(NoteLineSegment* item, LayoutContext& ctx)
    5091              : {
    5092              :     LAYOUT_CALL_ITEM(item);
    5093              : 
    5094           12 :     NoteLineSegment::LayoutData* ldata = item->mutldata();
    5095           12 :     layoutTextLineBaseSegment(item, ctx);
    5096              : 
    5097           12 :     if (item->staff()) {
    5098           12 :         ldata->setMag(item->staff()->staffMag(item->tick()));
    5099              :     }
    5100           12 : }
    5101              : 
    5102            0 : void TLayout::layoutSlur(Slur* item, LayoutContext& ctx)
    5103              : {
    5104            0 :     SlurTieLayout::createSlurSegments(item, ctx);
    5105            0 : }
    5106              : 
    5107            2 : void TLayout::layoutSpacer(Spacer* item, LayoutContext&)
    5108              : {
    5109              :     LAYOUT_CALL_ITEM(item);
    5110            2 :     Spacer::LayoutData* ldata = item->mutldata();
    5111              : 
    5112            2 :     double spatium = item->spatium();
    5113              : 
    5114            2 :     PainterPath path = PainterPath();
    5115            2 :     double w = spatium;
    5116            2 :     double b = w * .5;
    5117            2 :     double h = item->ownershipParent() ? item->absoluteGap() : item->absoluteFromSpatium(std::min(item->gap(), 4.0_sp));        // limit length for palette
    5118              : 
    5119            2 :     switch (item->spacerType()) {
    5120            0 :     case SpacerType::DOWN:
    5121            0 :         path.lineTo(w, 0.0);
    5122            0 :         path.moveTo(b, 0.0);
    5123            0 :         path.lineTo(b, h);
    5124            0 :         path.lineTo(0.0, h - b);
    5125            0 :         path.moveTo(b, h);
    5126            0 :         path.lineTo(w, h - b);
    5127            0 :         break;
    5128            0 :     case SpacerType::UP:
    5129            0 :         path.moveTo(b, 0.0);
    5130            0 :         path.lineTo(0.0, b);
    5131            0 :         path.moveTo(b, 0.0);
    5132            0 :         path.lineTo(w, b);
    5133            0 :         path.moveTo(b, 0.0);
    5134            0 :         path.lineTo(b, h);
    5135            0 :         path.moveTo(0.0, h);
    5136            0 :         path.lineTo(w, h);
    5137            0 :         break;
    5138            2 :     case SpacerType::FIXED:
    5139            2 :         path.lineTo(w, 0.0);
    5140            2 :         path.moveTo(b, 0.0);
    5141            2 :         path.lineTo(b, h);
    5142            2 :         path.moveTo(0.0, h);
    5143            2 :         path.lineTo(w, h);
    5144            2 :         break;
    5145              :     }
    5146            2 :     ldata->path = path;
    5147            2 :     double lw = spatium * 0.4;
    5148            2 :     RectF bb(0, 0, w, h);
    5149            2 :     bb.adjust(-lw, -lw, lw, lw);
    5150            2 :     ldata->setBbox(bb);
    5151              : 
    5152            2 :     item->setZ(0.0);
    5153            2 : }
    5154              : 
    5155          646 : void TLayout::layoutSpanner(Spanner* item, LayoutContext& ctx)
    5156              : {
    5157              :     LAYOUT_CALL_ITEM(item);
    5158          646 :     layoutItem(item, ctx);
    5159          646 : }
    5160              : 
    5161        44190 : void TLayout::layoutStaffLines(StaffLines* item, LayoutContext& ctx)
    5162              : {
    5163              :     LAYOUT_CALL_ITEM(item);
    5164        44190 :     layoutForWidth(item, item->measure()->width(), ctx);
    5165        44190 : }
    5166              : 
    5167        44190 : void TLayout::layoutForWidth(StaffLines* item, double w, LayoutContext& ctx)
    5168              : {
    5169              :     LAYOUT_CALL_ITEM(item);
    5170        44190 :     StaffLines::LayoutData* ldata = item->mutldata();
    5171        44190 :     const Staff* s = item->staff();
    5172        44190 :     double _spatium = item->spatium();
    5173        44190 :     double dist     = _spatium;
    5174        44190 :     item->setPos(PointF(0.0, 0.0));
    5175              :     int _lines;
    5176        44190 :     if (s) {
    5177        44190 :         ldata->setMag(s->staffMag(item->measure()->tick()));
    5178        44190 :         item->setVisible(!s->isLinesInvisible(item->measure()->tick()));
    5179        44190 :         item->setColor(s->color(item->measure()->tick()));
    5180        44190 :         const StaffType* st = s->staffType(item->measure()->tick());
    5181        44190 :         dist *= st->lineDistance().val();
    5182        44190 :         _lines = st->lines();
    5183        44190 :         ldata->setPosY(st->yoffset().val() * _spatium);
    5184              : //            if (_lines == 1)
    5185              : //                  rypos() = 2 * _spatium;
    5186              :     } else {
    5187            0 :         _lines = 5;
    5188            0 :         item->setColor(item->configuration()->defaultColor());
    5189              :     }
    5190        44190 :     item->setLw(ctx.conf().styleS(Sid::staffLineWidth).val() * _spatium);
    5191        44190 :     double x1 = item->pos().x();
    5192        44190 :     double x2 = x1 + w;
    5193        44190 :     double y  = item->pos().y();
    5194        44190 :     ldata->setBbox(x1, -item->lw() * .5 + y, w, (_lines - 1) * dist + item->lw());
    5195              : 
    5196        44190 :     std::vector<LineF> ll;
    5197       265722 :     for (int i = 0; i < _lines; ++i) {
    5198       221532 :         ll.push_back(LineF(x1, y, x2, y));
    5199       221532 :         y += dist;
    5200              :     }
    5201        44190 :     item->setLines(ll);
    5202        44190 : }
    5203              : 
    5204            0 : void TLayout::layoutStaffState(const StaffState* item, StaffState::LayoutData* ldata)
    5205              : {
    5206              :     LAYOUT_CALL_ITEM(item);
    5207              :     LD_INDEPENDENT;
    5208              : 
    5209            0 :     if (ldata->isValid()) {
    5210            0 :         return;
    5211              :     }
    5212              : 
    5213            0 :     double _spatium = item->spatium();
    5214            0 :     ldata->lw = (_spatium * 0.3);
    5215            0 :     double h  = _spatium * 4;
    5216            0 :     double w  = _spatium * 2.5;
    5217              : //      double w1 = w * .6;
    5218              : 
    5219            0 :     PainterPath path;
    5220            0 :     switch (item->staffStateType()) {
    5221            0 :     case StaffStateType::INSTRUMENT:
    5222            0 :         path.lineTo(w, 0.0);
    5223            0 :         path.lineTo(w, h);
    5224            0 :         path.lineTo(0.0, h);
    5225            0 :         path.lineTo(0.0, 0.0);
    5226            0 :         path.moveTo(w * .5, h - _spatium * .5);
    5227            0 :         path.lineTo(w * .5, _spatium * 2);
    5228            0 :         path.moveTo(w * .5, _spatium * .8);
    5229            0 :         path.lineTo(w * .5, _spatium * 1.0);
    5230            0 :         break;
    5231              : 
    5232            0 :     case StaffStateType::TYPE:
    5233            0 :         path.lineTo(w, 0.0);
    5234            0 :         path.lineTo(w, h);
    5235            0 :         path.lineTo(0.0, h);
    5236            0 :         path.lineTo(0.0, 0.0);
    5237            0 :         break;
    5238              : 
    5239            0 :     case StaffStateType::VISIBLE:
    5240            0 :         path.lineTo(w, 0.0);
    5241            0 :         path.lineTo(w, h);
    5242            0 :         path.lineTo(0.0, h);
    5243            0 :         path.lineTo(0.0, 0.0);
    5244            0 :         break;
    5245              : 
    5246            0 :     case StaffStateType::INVISIBLE:
    5247            0 :         path.lineTo(w, 0.0);
    5248            0 :         path.lineTo(w, h);
    5249            0 :         path.lineTo(0.0, h);
    5250            0 :         path.lineTo(0.0, 0.0);
    5251            0 :         break;
    5252              : 
    5253            0 :     default:
    5254            0 :         LOGD("unknown layout break symbol");
    5255            0 :         break;
    5256              :     }
    5257              : 
    5258            0 :     ldata->path = path;
    5259              : 
    5260            0 :     RectF bb(0, 0, w, h);
    5261            0 :     bb.adjust(-ldata->lw, -ldata->lw, ldata->lw, ldata->lw);
    5262            0 :     ldata->setBbox(bb);
    5263            0 :     ldata->setPos(0.0, _spatium * -6.0);
    5264            0 : }
    5265              : 
    5266         1133 : void TLayout::layoutStaffText(const StaffText* item, StaffText::LayoutData* ldata)
    5267              : {
    5268              :     LAYOUT_CALL_ITEM(item);
    5269         1133 :     TextLayout::layoutBaseTextBase(item, ldata);
    5270              : 
    5271         1133 :     if (item->autoplace()) {
    5272         1133 :         const Segment* s = toSegment(item->ownershipParent());
    5273         1133 :         const Measure* m = s->measure();
    5274         1133 :         LD_CONDITION(ldata->isSetPos());
    5275         1133 :         LD_CONDITION(m->ldata()->isSetPos());
    5276         1133 :         LD_CONDITION(s->ldata()->isSetPos());
    5277              :     }
    5278              : 
    5279         1133 :     Autoplace::autoplaceSegmentElement(item, ldata);
    5280              : 
    5281         1133 :     if (SoundFlag* flag = item->soundFlag()) {
    5282            9 :         layoutSoundFlag(flag, flag->mutldata());
    5283              :     }
    5284         1133 : }
    5285              : 
    5286            6 : void TLayout::layoutStaveSharingLabel(const StaveSharingLabel* item, TextBase::LayoutData* ldata)
    5287              : {
    5288              :     LAYOUT_CALL_ITEM(item);
    5289            6 :     TextLayout::layoutBaseTextBase(item, ldata);
    5290            6 :     Autoplace::autoplaceSegmentElement(item, ldata);
    5291            6 : }
    5292              : 
    5293           19 : void TLayout::layoutStaffTypeChange(const StaffTypeChange* item, StaffTypeChange::LayoutData* ldata, const LayoutConfiguration& conf)
    5294              : {
    5295              :     LAYOUT_CALL_ITEM(item);
    5296              :     LD_INDEPENDENT;
    5297              : 
    5298           19 :     double spatium = conf.spatium();
    5299           19 :     ldata->setBbox(RectF(-item->lw() * .5, -item->lw() * .5, spatium * 2.5 + item->lw(), spatium * 2.5 + item->lw()));
    5300           19 :     if (item->measure()) {
    5301           19 :         double y = -1.5 * spatium - ldata->bbox().height() + item->measure()->system()->staff(item->staffIdx())->y();
    5302           19 :         ldata->setPos(spatium * .8, y);
    5303              :     } else {
    5304            0 :         ldata->setPos(0.0, 0.0);
    5305              :     }
    5306           19 : }
    5307              : 
    5308       216469 : void TLayout::layoutStem(const Stem* item, Stem::LayoutData* ldata, const LayoutConfiguration& conf)
    5309              : {
    5310              :     LAYOUT_CALL_ITEM(item);
    5311              : 
    5312       216469 :     const bool up = item->up();
    5313       216469 :     const double _up = up ? -1.0 : 1.0;
    5314              : 
    5315              :     //Note* note = up ? item->chord()->downNote() : item->chord()->upNote();
    5316              :     //! NOTE A lot of spam
    5317              :     // LD_CONDITION(note->ldata()->mirror.has_value());
    5318              : 
    5319       216469 :     double y1 = 0.0; // vertical displacement to match note attach point
    5320       216469 :     double y2 = _up * (item->length());
    5321              : 
    5322       216469 :     bool isTabStaff = false;
    5323       216469 :     if (item->chord()) {
    5324       216469 :         ldata->setMag(item->chord()->mag());
    5325              : 
    5326       216469 :         const Staff* staff = item->staff();
    5327       216469 :         const StaffType* staffType = staff ? staff->staffTypeForElement(item->chord()) : nullptr;
    5328       216469 :         isTabStaff = staffType && staffType->isTabStaff();
    5329              : 
    5330       216469 :         if (isTabStaff) {
    5331          493 :             if (staffType->stemThrough()) {
    5332              :                 // if stems through staves, gets Y pos. of stem-side note relative to chord other side
    5333          105 :                 const double staffLinesDistance = staffType->lineDistance().val() * item->spatium();
    5334          105 :                 y1 = (item->chord()->downString() - item->chord()->upString()) * _up * staffLinesDistance;
    5335              : 
    5336              :                 // if fret marks above lines, raise stem beginning by 1/2 line distance
    5337          105 :                 if (!staffType->onLines()) {
    5338            0 :                     y1 -= staffLinesDistance * 0.5;
    5339              :                 }
    5340              : 
    5341              :                 // shorten stem by 1/2 lineDist to clear the note and a little more to keep 'air' between stem and note
    5342          105 :                 y1 += _up * staffLinesDistance * 0.7;
    5343              :             }
    5344              :             // in other TAB types, no correction
    5345              :         } else { // non-TAB
    5346              :             // move stem start to note attach point
    5347       215976 :             Note* note = up ? item->chord()->downNote() : item->chord()->upNote();
    5348       215976 :             if ((up && !note->ldata()->mirror.value(LD_ACCESS::BAD)) || (!up && note->ldata()->mirror.value(LD_ACCESS::BAD))) {
    5349       112753 :                 y1 = note->stemUpSE().y();
    5350              :             } else {
    5351       103223 :                 y1 = note->stemDownNW().y();
    5352              :             }
    5353              : 
    5354       215976 :             ldata->setPosY(note->ldata()->pos().y());
    5355              :         }
    5356              : 
    5357       216469 :         const Hook* hook = item->chord()->hook();
    5358       216469 :         const Beam* beam = item->chord()->beam();
    5359       216469 :         if (hook && !beam) {
    5360         6676 :             y2 += hook->smuflAnchor().y();
    5361              :         }
    5362              : 
    5363       216469 :         if (beam) {
    5364       115255 :             y2 -= _up * beam->absoluteFromSpatium(conf.styleS(Sid::beamWidth)) * .5 * beam->mag();
    5365              :         }
    5366              :     }
    5367              : 
    5368       216469 :     double lineWidthCorrection = item->lineWidthMag() * 0.5;
    5369       216469 :     double lineX = isTabStaff ? 0.0 : _up * lineWidthCorrection;
    5370              : 
    5371       216469 :     LineF line = LineF(lineX, y1, lineX, y2);
    5372       216469 :     ldata->line = line;
    5373              : 
    5374              :     // compute line and bounding rectangle
    5375       216469 :     RectF rect(line.p1(), line.p2() + PointF(0.0, ldata->beamCorrection));
    5376       216469 :     ldata->setBbox(rect.normalized().adjusted(-lineWidthCorrection, 0, lineWidthCorrection, 0));
    5377       216469 : }
    5378              : 
    5379          841 : void TLayout::layoutStemSlash(const StemSlash* item, StemSlash::LayoutData* ldata, const LayoutConfiguration& conf)
    5380              : {
    5381              :     LAYOUT_CALL_ITEM(item);
    5382          841 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    5383            0 :         return;
    5384              :     }
    5385              : 
    5386          841 :     const Chord* c = item->chord();
    5387          841 :     const Stem* stem = c->stem();
    5388          841 :     const Hook* hook = c->hook();
    5389          841 :     const Beam* beam = c->beam();
    5390              : 
    5391          841 :     LD_CONDITION(stem->ldata()->isSetPos());
    5392          841 :     LD_CONDITION(stem->ldata()->isSetBbox());
    5393              :     //  Notes without a stem (including whole notes) don't draw a slash.
    5394              : 
    5395              :     static constexpr double heightReduction = 0.66;
    5396              :     static constexpr double angleIncrease = 1.2;
    5397              :     static constexpr double lengthIncrease = 1.1;
    5398          841 :     const double mag = c->mag();
    5399              : 
    5400          841 :     double up = c->up() ? -1 : 1;
    5401          841 :     double stemTipY = c->up()
    5402          841 :                       ? stem->ldata()->bbox().translated(stem->pos()).top()
    5403            9 :                       : stem->ldata()->bbox().translated(stem->pos()).bottom();
    5404          841 :     double leftHang = conf.noteHeadWidth() * mag / 2;
    5405          841 :     double angle = conf.styleD(Sid::stemSlashAngle) * M_PI / 180; // converting to radians
    5406          841 :     bool straight = conf.styleB(Sid::useStraightNoteFlags);
    5407          841 :     double graceNoteMag = mag;
    5408              : 
    5409          841 :     double startX = stem->ldata()->bbox().translated(stem->pos()).right() - leftHang;
    5410          841 :     double startY = stemTipY - up * graceNoteMag * conf.styleAbsolute(Sid::stemSlashPosition)
    5411          841 :                     * (straight || !hook ? heightReduction : 1);
    5412              : 
    5413          841 :     double endX = 0;
    5414          841 :     double endY = 0;
    5415              : 
    5416          841 :     if (hook) {
    5417          472 :         auto musicFont = conf.styleSt(Sid::musicalSymbolFont);
    5418              :         // HACK: adjust slash angle for fonts with "fat" hooks. In future, we must use smufl cutOut
    5419          507 :         if (c->beams() >= 2 && !straight
    5420          507 :             && (musicFont == "Bravura" || musicFont == "Finale Maestro" || musicFont == "Gonville")) {
    5421            0 :             angle *= angleIncrease;
    5422              :         }
    5423          472 :         endX = hook->ldata()->bbox().translated(hook->ldata()->pos()).right(); // always ends at the right bbox margin of the hook
    5424          472 :         endY = startY + up * (endX - startX) * tan(angle);
    5425          841 :     } else if (beam) {
    5426          225 :         PointF p1 = beam->startAnchor();
    5427          225 :         PointF p2 = beam->endAnchor();
    5428          225 :         double beamAngle = p2.x() > p1.x() ? atan((p2.y() - p1.y()) / (p2.x() - p1.x())) : 0;
    5429          225 :         angle += up * beamAngle / 2;
    5430          225 :         double length = 2 * item->spatium();
    5431          225 :         bool obtuseAngle = (c->up() && beamAngle < 0) || (!c->up() && beamAngle > 0);
    5432          225 :         if (obtuseAngle) {
    5433          120 :             length *= lengthIncrease; // needs to be slightly longer
    5434              :         }
    5435          225 :         endX = startX + length * cos(angle);
    5436          225 :         endY = startY + up * length * sin(angle);
    5437              :     } else {
    5438          144 :         double rightHang = (conf.noteHeadWidth() * mag / 2) - stem->width(); //  subtract the stem width so the slash is optically centered on the stem
    5439          144 :         endX = stem->ldata()->bbox().translated(stem->pos()).right() + rightHang;
    5440          144 :         endY = startY + up * (endX - startX) * tan(angle);
    5441              :     }
    5442              : 
    5443          841 :     ldata->line = LineF(PointF(startX, startY), PointF(endX, endY));
    5444          841 :     ldata->stemWidth = conf.styleAbsolute(Sid::stemSlashThickness) * graceNoteMag;
    5445              : 
    5446          841 :     RectF bbox = RectF(ldata->line.p1(), ldata->line.p2()).normalized();
    5447          841 :     bbox = bbox.adjusted(-ldata->stemWidth / 2, -ldata->stemWidth / 2, ldata->stemWidth, ldata->stemWidth);
    5448          841 :     ldata->setBbox(bbox);
    5449              : }
    5450              : 
    5451          131 : void TLayout::layoutSticking(const Sticking* item, Sticking::LayoutData* ldata)
    5452              : {
    5453              :     LAYOUT_CALL_ITEM(item);
    5454          131 :     TextLayout::layoutBaseTextBase(item, ldata);
    5455              : 
    5456          131 :     if (item->autoplace() && item->ownershipParent()) {
    5457          131 :         const Segment* s = toSegment(item->ownershipParent());
    5458          131 :         const Measure* m = s->measure();
    5459          131 :         LD_CONDITION(ldata->isSetPos());
    5460          131 :         LD_CONDITION(m->ldata()->isSetPos());
    5461          131 :         LD_CONDITION(s->ldata()->isSetPos());
    5462              :     }
    5463              : 
    5464          131 :     Autoplace::autoplaceSegmentElement(item, ldata);
    5465          131 : }
    5466              : 
    5467           19 : void TLayout::layoutStringTunings(StringTunings* item, LayoutContext& ctx)
    5468              : {
    5469              :     LAYOUT_CALL_ITEM(item);
    5470           19 :     item->updateText();
    5471              : 
    5472           19 :     TextLayout::layoutBaseTextBase(item, ctx);
    5473              : 
    5474           19 :     double spatium = item->spatium();
    5475              : 
    5476           19 :     if (item->noStringVisible()) {
    5477           19 :         Font font(item->font());
    5478              : 
    5479           19 :         RectF rect;
    5480           19 :         rect.setTopLeft({ 0, item->ldata()->bbox().y() - 2.5 * spatium });
    5481           19 :         rect.setSize({ spatium, 2.5 * spatium });
    5482              : 
    5483           19 :         item->setbbox(rect);
    5484           19 :     }
    5485              : 
    5486           38 :     for (TextBlock& block : item->mutldata()->blocks) {
    5487           19 :         for (TextFragment& fragment : block.fragments()) {
    5488            0 :             Font font = fragment.font(item);
    5489            0 :             if (font.type() == Font::Type::MusicSymbol) {
    5490              :                 // HACK: the music symbol doesn't have a good baseline
    5491              :                 // to go with text so we correct it here
    5492            0 :                 const double baselineAdjustment = 0.35 * spatium * item->symbolScale();
    5493            0 :                 fragment.pos.setY(fragment.pos.y() + baselineAdjustment);
    5494              :             }
    5495            0 :         }
    5496              :     }
    5497              : 
    5498           19 :     double secondStringXAlign = 0.0;
    5499           19 :     for (const TextFragment& fragment : item->fragmentList()) {
    5500            0 :         if (fragment.font(item).type() == Font::Type::MusicSymbol) {
    5501            0 :             secondStringXAlign = std::max(secondStringXAlign, fragment.pos.x());
    5502              :         }
    5503           19 :     }
    5504              : 
    5505           38 :     for (TextBlock& block : item->mutldata()->blocks) {
    5506           19 :         double xMove = 0.0;
    5507           19 :         for (TextFragment& fragment : block.fragments()) {
    5508            0 :             if (block.fragments().front() == fragment) {  // skip first
    5509            0 :                 continue;
    5510              :             }
    5511              : 
    5512            0 :             if (fragment.font(item).type() == Font::Type::MusicSymbol) {
    5513            0 :                 xMove = secondStringXAlign - fragment.pos.x();
    5514              :             }
    5515            0 :             fragment.pos.setX(fragment.pos.x() + xMove);
    5516              :         }
    5517              :     }
    5518              : 
    5519           19 :     Segment* parentSegment = item->segment();
    5520           19 :     item->move(PointF(-parentSegment->x() + item->spatium(), 0.0));
    5521              : 
    5522           19 :     Autoplace::autoplaceSegmentElement(item, item->mutldata());
    5523           19 : }
    5524              : 
    5525            9 : void TLayout::layoutSoundFlag(const SoundFlag* item, SoundFlag::LayoutData* ldata)
    5526              : {
    5527              :     LAYOUT_CALL_ITEM(item);
    5528              : 
    5529            9 :     const Score* score = item->score();
    5530            9 :     if (score && !score->showSoundFlags()) {
    5531            0 :         return;
    5532              :     }
    5533              : 
    5534            9 :     const EngravingItem* parent = item->ownershipParentItem();
    5535            9 :     if (!parent) {
    5536            0 :         return;
    5537              :     }
    5538              : 
    5539            9 :     double spatium = item->spatium();
    5540            9 :     double iconHeight = spatium * 3.0;
    5541              : 
    5542            9 :     RectF parentBbox = parent->ldata()->bbox();
    5543            9 :     RectF iconBBox = RectF(parentBbox.x(), parentBbox.y(), iconHeight, iconHeight);
    5544              : 
    5545            9 :     iconBBox.moveCenter(parentBbox.center());
    5546              : 
    5547              :     // <icon><space><text>
    5548            9 :     double space = spatium / 2.0;
    5549            9 :     iconBBox.setX(parentBbox.x() - iconBBox.width() - space);
    5550              : 
    5551            9 :     ldata->setBbox(iconBBox);
    5552              : }
    5553              : 
    5554           72 : void TLayout::layoutSymbol(const Symbol* item, Symbol::LayoutData* ldata, const LayoutContext& ctx)
    5555              : {
    5556              :     LAYOUT_CALL_ITEM(item);
    5557           72 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    5558            0 :         return;
    5559              :     }
    5560              : 
    5561              :     LD_INDEPENDENT;
    5562              : 
    5563           72 :     double expectedMag = item->staff() ? item->staff()->staffMag(item->tick()) : ldata->mag();
    5564              : 
    5565           72 :     if (ldata->isValid() && RealIsEqual(ldata->mag(), expectedMag)) {
    5566            0 :         return;
    5567              :     }
    5568              : 
    5569           72 :     if (item->staff()) {
    5570           50 :         ldata->setMag(expectedMag);
    5571              :     }
    5572          144 :     ldata->setBbox(item->scoreFont()
    5573           72 :                    ? item->scoreFont()->bbox(item->sym(), item->magS() * item->symbolsSize())
    5574           31 :                    : item->symBbox(item->sym()));
    5575           72 :     double w = ldata->bbox().width();
    5576           72 :     PointF p;
    5577           72 :     if (item->align() == AlignV::BOTTOM) {
    5578            0 :         p.setY(-ldata->bbox().height());
    5579           72 :     } else if (item->align() == AlignV::VCENTER) {
    5580            0 :         p.setY((-ldata->bbox().height()) * .5);
    5581           72 :     } else if (item->align() == AlignV::BASELINE) {
    5582           72 :         p.setY(-item->baseLine());
    5583              :     }
    5584           72 :     if (item->align() == AlignH::RIGHT) {
    5585            0 :         p.setX(-w);
    5586           72 :     } else if (item->align() == AlignH::HCENTER) {
    5587            0 :         p.setX(-(w * .5));
    5588              :     }
    5589           72 :     ldata->setPos(p);
    5590              : 
    5591              :     // see BSymbol::add
    5592           72 :     for (EngravingItem* e : item->leafs()) {
    5593            0 :         switch (e->type()) {
    5594            0 :         case ElementType::SYMBOL: {
    5595            0 :             Symbol* s = item_cast<Symbol*>(e);
    5596            0 :             layoutSymbol(s, s->mutldata(), ctx);
    5597            0 :         } break;
    5598            0 :         case ElementType::IMAGE: {
    5599            0 :             Image* im = item_cast<Image*>(e);
    5600            0 :             layoutImage(im, im->mutldata());
    5601            0 :         } break;
    5602            0 :         default:
    5603            0 :             UNREACHABLE;
    5604              :             break;
    5605              :         }
    5606              :     }
    5607              : }
    5608              : 
    5609            0 : void TLayout::layoutFSymbol(const FSymbol* item, FSymbol::LayoutData* ldata)
    5610              : {
    5611              :     LAYOUT_CALL_ITEM(item);
    5612              :     LD_INDEPENDENT;
    5613            0 :     if (ldata->isValid()) {
    5614            0 :         return;
    5615              :     }
    5616              : 
    5617            0 :     ldata->setBbox(FontMetrics::boundingRect(item->font(), item->toString()));
    5618              : }
    5619              : 
    5620           28 : void TLayout::layoutSystemDivider(const SystemDivider* item, SystemDivider::LayoutData* ldata, const LayoutContext& ctx)
    5621              : {
    5622              :     LAYOUT_CALL_ITEM(item);
    5623              :     LD_INDEPENDENT;
    5624           28 :     if (ldata->isValid()) {
    5625            6 :         return;
    5626              :     }
    5627              : 
    5628           22 :     layoutSymbol(item, ldata, ctx);
    5629              : }
    5630              : 
    5631          136 : void TLayout::layoutSystemText(const SystemText* item, SystemText::LayoutData* ldata)
    5632              : {
    5633              :     LAYOUT_CALL_ITEM(item);
    5634          136 :     TextLayout::layoutBaseTextBase(item, ldata);
    5635              : 
    5636          136 :     if (item->autoplace() && item->ownershipParent()) {
    5637          136 :         const Segment* s = toSegment(item->ownershipParent());
    5638          136 :         const Measure* m = s->measure();
    5639          136 :         LD_CONDITION(ldata->isSetPos());
    5640          136 :         LD_CONDITION(m->ldata()->isSetPos());
    5641          136 :         LD_CONDITION(s->ldata()->isSetPos());
    5642              :     }
    5643              : 
    5644          136 :     Autoplace::autoplaceSegmentElement(item, ldata);
    5645          136 : }
    5646              : 
    5647           24 : void TLayout::layoutTabDurationSymbol(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata)
    5648              : {
    5649              :     LAYOUT_CALL_ITEM(item);
    5650              :     LD_INDEPENDENT;
    5651              : 
    5652              :     static constexpr Spatium TAB_RESTSYMBDISPL = 2.0_sp;
    5653              : 
    5654           24 :     if (!item->tab()) {
    5655            0 :         ldata->setBbox(RectF());
    5656            0 :         return;
    5657              :     }
    5658           24 :     double spatium    = item->spatium();
    5659              :     double hbb, wbb, xbb, ybb;     // bbox sizes
    5660              :     double xpos, ypos;           // position coords
    5661              : 
    5662           24 :     ldata->beamGrid = TabBeamGrid::NONE;
    5663           24 :     ldata->beamLength = 0.0;
    5664           24 :     ldata->beamLevel = 0;
    5665           24 :     Chord* chord = item->ownershipParent() && item->ownershipParent()->isChord() ? toChord(item->ownershipParent()) : nullptr;
    5666              :     // A grid replaces the individual duration signs. Only connect equal, undotted
    5667              :     // values; otherwise retain the signs rather than lose their rhythm information.
    5668           15 :     auto connects = [](const ChordRest* left, const ChordRest* right) {
    5669           15 :         if (!left || !right || !left->isChord() || !right->isChord()) {
    5670            4 :             return false;
    5671              :         }
    5672           22 :         const bool regularChords = !left->isGrace() && !right->isGrace()
    5673           22 :                                    && !toChord(left)->noStem() && !toChord(right)->noStem();
    5674           22 :         const bool sameContext = left->measure() == right->measure() && left->staffType() == right->staffType()
    5675           22 :                                  && left->tuplet() == right->tuplet();
    5676           11 :         const bool equalUndottedDurations = left->durationType() == right->durationType() && !left->dots() && !right->dots();
    5677           11 :         const bool leftContinues = left->beamMode() == BeamMode::BEGIN || left->beamMode() == BeamMode::MID;
    5678           11 :         const bool rightConnects = right->beamMode() == BeamMode::MID || right->beamMode() == BeamMode::END;
    5679           11 :         return regularChords && sameContext && equalUndottedDurations && leftContinues && rightConnects;
    5680              :     };
    5681           24 :     const int beamLevel = chord ? static_cast<int>(chord->durationType().type())
    5682           24 :                           - static_cast<int>(item->tab()->tabDurationFont().zeroBeamLevel) : 0;
    5683           22 :     const bool joinsPrevious = beamLevel > 0 && (chord->beamMode() == BeamMode::MID || chord->beamMode() == BeamMode::END)
    5684           46 :                                && connects(Navigation::prevChordRest(chord), chord);
    5685           22 :     const bool joinsNext = beamLevel > 0 && (chord->beamMode() == BeamMode::BEGIN || chord->beamMode() == BeamMode::MID)
    5686           46 :                            && connects(chord, Navigation::nextChordRest(chord));
    5687              : // if no chord (shouldn't happens...) or not a special beam mode, layout regular symbol
    5688           24 :     if (beamLevel < 1 || (!joinsPrevious && !joinsNext)) {
    5689           16 :         FontMetrics fm(item->tab()->durationFont());
    5690           16 :         hbb   = item->tab()->durationBoxH();
    5691           16 :         wbb   = fm.width(item->text());
    5692           16 :         xbb   = 0.0;
    5693           16 :         xpos  = 0.0;
    5694           16 :         ypos  = item->tab()->durationFontYOffset();
    5695           16 :         ybb   = item->tab()->durationBoxY() - ypos;
    5696              :         // with rests, move symbol down by half its displacement from staff
    5697           16 :         if (item->ownershipParent() && item->ownershipParent()->isRest()) {
    5698            0 :             ybb  += TAB_RESTSYMBDISPL.toAbsolute(spatium);
    5699            0 :             ypos += TAB_RESTSYMBDISPL.toAbsolute(spatium);
    5700              :         }
    5701           16 :     }
    5702              : // if on a chord with special beam mode, layout an 'English'-style duration grid
    5703              :     else {
    5704            8 :         const TablatureDurationFont& font = item->tab()->tabDurationFont();
    5705            8 :         hbb   = font.gridStemHeight.toAbsolute(spatium);         // bbox height is stem height
    5706            8 :         wbb   = font.gridStemWidth.toAbsolute(spatium);          // bbox width is stem width
    5707            8 :         xbb   = -wbb * 0.5;                             // bbox is half at left and half at right of stem centre
    5708            8 :         ybb   = -hbb;                                   // bbox top is at top of stem height
    5709            8 :         xpos  = 0.75 * spatium;                        // conventional centring of stem on fret marks
    5710            8 :         ypos  = item->tab()->durationGridYOffset();      // stem start is at bottom
    5711            8 :         ldata->beamLevel = beamLevel;
    5712            8 :         ldata->beamGrid = joinsPrevious ? TabBeamGrid::MEDIALFINAL : TabBeamGrid::INITIAL;
    5713              :     }
    5714              : // set this' mag from parent chord mag (include staff mag)
    5715           24 :     double mag = chord != nullptr ? chord->mag() : 1.0;
    5716           24 :     ldata->setMag(mag);
    5717           24 :     mag = item->magS();         // local mag * score mag
    5718              : // set magnified bbox and position
    5719           24 :     ldata->setBbox(xbb * mag, ybb * mag, wbb * mag, hbb * mag);
    5720           24 :     ldata->setPos(xpos * mag, ypos * mag);
    5721              : }
    5722              : 
    5723           24 : void TLayout::layoutTabDurationSymbol2(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata)
    5724              : {
    5725           24 :     if (ldata->beamGrid != TabBeamGrid::MEDIALFINAL) {
    5726           20 :         return;
    5727              :     }
    5728            4 :     const ChordRest* previous = Navigation::prevChordRest(toChord(item->ownershipParent()));
    5729            4 :     if (!previous || !previous->tabDur()) {
    5730            0 :         return;
    5731              :     }
    5732              :     // Horizontal spacing is now final. Drawing applies the symbol magnification again.
    5733            4 :     const double length = previous->tabDur()->pagePos().x() - item->pagePos().x();
    5734            4 :     ldata->beamLength = length / item->magS();
    5735            4 :     RectF bbox = ldata->bbox();
    5736            4 :     bbox.setLeft(std::min(bbox.left(), length));
    5737            4 :     ldata->setBbox(bbox);
    5738              : }
    5739              : 
    5740          134 : void TLayout::layoutTapping(Tapping* item, Tapping::LayoutData* ldata, LayoutContext& ctx)
    5741              : {
    5742          134 :     TappingLayout::layoutTapping(item, ldata, ctx);
    5743          134 : }
    5744              : 
    5745            0 : void TLayout::layoutTappingHalfSlur(TappingHalfSlur* item)
    5746              : {
    5747              :     UNUSED(item)
    5748            0 : }
    5749              : 
    5750         1136 : void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldata)
    5751              : {
    5752              :     LAYOUT_CALL_ITEM(item);
    5753         1136 :     IF_ASSERT_FAILED(item->ownershipParent()) {
    5754            0 :         return;
    5755              :     }
    5756              : 
    5757         1136 :     ldata->disconnectSnappedItems();
    5758              : 
    5759         1136 :     TextLayout::layoutBaseTextBase(item, ldata);
    5760              : 
    5761         1136 :     if (!item->autoplace()) {
    5762            0 :         return;
    5763              :     }
    5764              : 
    5765              :     // tempo text on first chordrest of measure should align over time sig if present, unless time sig is above staff
    5766         1136 :     Segment* s = item->segment();
    5767              : 
    5768         1136 :     RehearsalMark* rehearsalMark = toRehearsalMark(s->findAnnotation(ElementType::REHEARSAL_MARK, item->track(), item->track()));
    5769         1136 :     RectF rehearsalMarkBbox = rehearsalMark ? rehearsalMark->ldata()->bbox().translated(rehearsalMark->pos()) : RectF();
    5770         1136 :     RectF thisBbox = ldata->bbox().translated(item->pos());
    5771              : 
    5772         1136 :     if (s->rtick().isZero()) {
    5773         1116 :         Segment* p = item->segment()->prev(SegmentType::TimeSig);
    5774         1116 :         if (p && !p->allElementsInvisible()) {
    5775          874 :             ldata->moveX(-(s->x() - p->x()));
    5776          874 :             EngravingItem* e = p->element(item->staffIdx() * VOICES);
    5777          874 :             if (e) {
    5778          874 :                 ldata->moveX(p->hasTimeSigAboveStaves() ? e->x() + e->width() + e->spatium() : e->x());
    5779              :             }
    5780              :         }
    5781              :     }
    5782              : 
    5783         1136 :     if (rehearsalMark) {
    5784           10 :         const bool sameSide = item->placeAbove() == rehearsalMark->placeAbove();
    5785              :         const bool collision
    5786           10 :             = item->placeAbove() ? muse::RealIsEqualOrMore(rehearsalMarkBbox.bottom(), thisBbox.top()) : muse::RealIsEqualOrLess(
    5787           10 :                   rehearsalMarkBbox.top(), thisBbox.bottom());
    5788              : 
    5789           10 :         if (sameSide && collision && item->getProperty(Pid::TEMPO_ALIGN_RIGHT_OF_REHEARSAL_MARK).toBool()) {
    5790           10 :             double rightEdge = rehearsalMarkBbox.right();
    5791           10 :             const double fontSizeScaleFactor = item->size() / 10.0;
    5792           10 :             const double padding = 0.5 * item->spatium() * fontSizeScaleFactor;
    5793           10 :             double curX = ldata->pos().x();
    5794           10 :             ldata->setPosX(std::max(curX, rightEdge + padding));
    5795              :         }
    5796              :     }
    5797         1136 :     Autoplace::autoplaceSegmentElement(item, ldata);
    5798              : }
    5799              : 
    5800           11 : Shape TLayout::recalculateTextLineBaseSegmentShape(const TextLineBaseSegment* item)
    5801              : {
    5802              :     LAYOUT_CALL_ITEM(item);
    5803           11 :     const TextLineBaseSegment::LayoutData* ldata = item->ldata();
    5804           11 :     Shape shape;
    5805           11 :     if (!item->text()->empty()) {
    5806           11 :         shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item->text());
    5807              :     }
    5808           11 :     if (!item->endText()->empty()) {
    5809           11 :         shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item->endText());
    5810              :     }
    5811           11 :     double lw2 = 0.5 * item->absoluteFromSpatium(item->lineWidth());
    5812           11 :     bool isDottedLine = item->textLineBase()->lineStyle() == LineType::DOTTED;
    5813           11 :     if (item->twoLines()) {     // hairpins
    5814            0 :         shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[0], ldata->points[1], lw2, isDottedLine), item);
    5815            0 :         shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[2], ldata->points[3], lw2, isDottedLine), item);
    5816              :     } else {
    5817           22 :         for (int i = 0; i < ldata->npoints - 1; ++i) {
    5818           11 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lw2, isDottedLine), item,
    5819           11 :                       !item->textLineBase()->lineVisible());
    5820              :         }
    5821              :     }
    5822           11 :     return shape;
    5823            0 : }
    5824              : 
    5825        15377 : void TLayout::layoutText(const Text* item, Text::LayoutData* ldata)
    5826              : {
    5827              :     LAYOUT_CALL_ITEM(item);
    5828        15377 :     if (item->layoutParent() && item->layoutToParentWidth()) {
    5829        11152 :         LD_CONDITION(item->layoutParent()->ldata()->isSetBbox());
    5830              :     }
    5831              : 
    5832        15377 :     TextLayout::layoutBaseTextBase(item, ldata);
    5833        15377 : }
    5834              : 
    5835            0 : void TLayout::layoutTextLine(TextLine* item, LayoutContext& ctx)
    5836              : {
    5837              :     LAYOUT_CALL_ITEM(item);
    5838            0 :     layoutLine(item, ctx);
    5839            0 : }
    5840              : 
    5841           55 : void TLayout::layoutTextLineSegment(TextLineSegment* item, LayoutContext& ctx)
    5842              : {
    5843              :     LAYOUT_CALL_ITEM(item);
    5844           55 :     TextLineSegment::LayoutData* ldata = item->mutldata();
    5845           55 :     layoutTextLineBaseSegment(item, ctx);
    5846              : 
    5847           55 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    5848           55 : }
    5849              : 
    5850              : // Extends lines to fill the corner between them.
    5851              : // Assumes that l1p2 == l2p1 is the intersection between the lines.
    5852              : // If checkAngle is false, assumes that the lines are perpendicular,
    5853              : // and some calculations are saved.
    5854          147 : static inline void extendLines(const PointF& l1p1, PointF& l1p2, PointF& l2p1, const PointF& l2p2, double lineWidth, bool checkAngle)
    5855              : {
    5856          147 :     PointF l1UnitVector = (l1p2 - l1p1).normalized();
    5857          147 :     PointF l2UnitVector = (l2p1 - l2p2).normalized();
    5858              : 
    5859          147 :     double addedLength = lineWidth * 0.5;
    5860              : 
    5861          147 :     if (checkAngle) {
    5862            0 :         double angle = M_PI - acos(PointF::dotProduct(l1UnitVector, l2UnitVector));
    5863              : 
    5864            0 :         if (angle <= M_PI_2) {
    5865            0 :             addedLength *= tan(0.5 * angle);
    5866              :         }
    5867              :     }
    5868              : 
    5869          147 :     l1p2 += l1UnitVector * addedLength;
    5870          147 :     l2p1 += l2UnitVector * addedLength;
    5871          147 : }
    5872              : 
    5873            0 : static PolygonF createArrow(bool start, bool filled, PointF& startPoint, PointF& endPoint, const TextLineBase* tl)
    5874              : {
    5875            0 :     double arrowWidth = 0.0;
    5876            0 :     double arrowHeight = 0.0;
    5877            0 :     if (filled) {
    5878            0 :         arrowWidth = tl->absoluteFromSpatium(start ? tl->beginFilledArrowWidth() : tl->endFilledArrowWidth());
    5879            0 :         arrowHeight = tl->absoluteFromSpatium(start ? tl->beginFilledArrowHeight() : tl->endFilledArrowHeight());
    5880              :     } else {
    5881            0 :         arrowWidth = tl->absoluteFromSpatium(start ? tl->beginLineArrowWidth() : tl->endLineArrowWidth());
    5882            0 :         arrowHeight = tl->absoluteFromSpatium(start ? tl->beginLineArrowHeight() : tl->endLineArrowHeight());
    5883              :     }
    5884              : 
    5885            0 :     PolygonF arrow;
    5886            0 :     if (start) {
    5887            0 :         arrow << PointF(0.0, -arrowHeight / 2) << PointF(-arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2);  // left
    5888              :     } else {
    5889            0 :         arrow << PointF(0.0, -arrowHeight / 2) << PointF(arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2);  // right
    5890              :     }
    5891              : 
    5892            0 :     PointF arrowAdjust = PointF(arrowWidth, 0.0);
    5893            0 :     arrowAdjust = (start ? 1.0 : -1.0) * arrowAdjust;
    5894            0 :     arrow.translate(arrowAdjust);
    5895              : 
    5896            0 :     const double yDiff = endPoint.y() - startPoint.y();
    5897            0 :     const double xDiff = endPoint.x() - startPoint.x();
    5898            0 :     const double rotate = atan(yDiff / xDiff) + (endPoint.x() < startPoint.x() ? M_PI : 0.0);
    5899              : 
    5900            0 :     Transform t;
    5901            0 :     t.rotateRadians(rotate);
    5902              : 
    5903            0 :     for (PointF& p : arrow) {
    5904            0 :         p = t.map(p);
    5905              :     }
    5906              : 
    5907            0 :     if (start) {
    5908            0 :         arrow.translate(startPoint);
    5909              :     } else {
    5910            0 :         arrow.translate(endPoint);
    5911              :     }
    5912              : 
    5913            0 :     const PointF lineVector = endPoint - startPoint;
    5914            0 :     const PointF unitVector = lineVector.normalized();
    5915            0 :     const double reduction = filled ? arrowWidth : tl->absoluteFromSpatium(tl->lineWidth()) / 2;
    5916            0 :     if (start) {
    5917            0 :         startPoint += reduction * unitVector;
    5918              :     } else {
    5919            0 :         endPoint -= reduction * unitVector;
    5920              :     }
    5921              : 
    5922            0 :     return arrow;
    5923            0 : }
    5924              : 
    5925            0 : void TLayout::layoutTextLineBase(TextLineBase* item, LayoutContext& ctx)
    5926              : {
    5927            0 :     layoutLine(item, ctx);
    5928            0 : }
    5929              : 
    5930         1269 : void TLayout::layoutTextLineBaseSegment(TextLineBaseSegment* item, LayoutContext& ctx)
    5931              : {
    5932         1269 :     TextLineBaseSegment::LayoutData* ldata = item->mutldata();
    5933         1269 :     ldata->npoints = 0;
    5934         1269 :     const TextLineBase* tl = item->textLineBase();
    5935         1269 :     const double spatium = tl->spatium();
    5936         1269 :     const bool isSingleOrBegin = item->isSingleBeginType();
    5937         1269 :     const bool isSingleOrEnd = item->isSingleEndType();
    5938         1269 :     const bool isDottedLine = tl->lineStyle() == LineType::DOTTED;
    5939         1269 :     const double lineWidth = tl->absoluteFromSpatium(tl->lineWidth());
    5940              : 
    5941         1269 :     if (item->spanner()->placeBelow()) {
    5942          594 :         ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
    5943              :     }
    5944              : 
    5945              :     // adjust Y pos to staffType offset
    5946         1269 :     ldata->moveY(item->staffOffsetY());
    5947              : 
    5948         1269 :     if (!tl->diagonal()) {
    5949         1252 :         item->setUserYoffset2(0);
    5950              :     }
    5951              : 
    5952          438 :     auto alignBaseLine = [lineWidth](Text* text, PointF& pp1, PointF& pp2) {
    5953          438 :         PointF widthCorrection(0.0, lineWidth / 2);
    5954          438 :         switch (text->align().vertical) {
    5955          117 :         case AlignV::TOP:
    5956          117 :             pp1 += widthCorrection;
    5957          117 :             pp2 += widthCorrection;
    5958          117 :             break;
    5959          136 :         case AlignV::VCENTER:
    5960          136 :             break;
    5961            0 :         case AlignV::BOTTOM:
    5962            0 :             pp1 -= widthCorrection;
    5963            0 :             pp2 -= widthCorrection;
    5964            0 :             break;
    5965          185 :         case AlignV::BASELINE:
    5966          185 :             pp1 -= widthCorrection;
    5967          185 :             pp2 -= widthCorrection;
    5968          185 :             break;
    5969              :         }
    5970          438 :     };
    5971              : 
    5972         1269 :     if (isSingleOrBegin) {
    5973         1246 :         item->text()->setXmlText(tl->beginText());
    5974         1246 :         item->text()->setFamily(tl->beginFontFamily());
    5975         1246 :         item->text()->setSize(tl->beginFontSize());
    5976         1246 :         item->text()->setSymbolScale(tl->beginTextMusicalSymbolsScale());
    5977         1246 :         item->text()->setSymbolSize(tl->beginTextMusicSymbolsSize());
    5978         1246 :         item->text()->setOffset(tl->beginTextOffset() * item->mag());
    5979         1246 :         item->text()->setAlign(tl->beginTextAlign());
    5980         1246 :         item->text()->setPosition(tl->beginTextPosition());
    5981         1246 :         item->text()->setFontStyle(tl->beginFontStyle());
    5982              :     } else {
    5983           23 :         item->text()->setXmlText(tl->continueText());
    5984           23 :         item->text()->setFamily(tl->continueFontFamily());
    5985           23 :         item->text()->setSize(tl->continueFontSize());
    5986           23 :         item->text()->setSymbolScale(tl->continueTextMusicalSymbolsScale());
    5987           23 :         item->text()->setSymbolSize(tl->continueTextMusicSymbolsSize());
    5988           23 :         item->text()->setOffset(tl->continueTextOffset() * item->mag());
    5989           23 :         item->text()->setAlign(tl->continueTextAlign());
    5990           23 :         item->text()->setPosition(tl->continueTextPosition());
    5991           23 :         item->text()->setFontStyle(tl->continueFontStyle());
    5992              :     }
    5993         1269 :     item->text()->setPlacement(PlacementV::ABOVE);
    5994         1269 :     item->text()->setTrack(item->track());
    5995         1269 :     item->text()->setColor(tl->lineColor());
    5996         1269 :     layoutText(item->text(), item->text()->mutldata());
    5997              : 
    5998         1269 :     if (isSingleOrEnd) {
    5999         1244 :         item->endText()->setXmlText(tl->endText());
    6000         1244 :         item->endText()->setFamily(tl->endFontFamily());
    6001         1244 :         item->endText()->setSize(tl->endFontSize());
    6002         1244 :         item->endText()->setSymbolScale(tl->endTextMusicalSymbolsScale());
    6003         1244 :         item->endText()->setSymbolSize(tl->endTextMusicSymbolsSize());
    6004         1244 :         item->endText()->setOffset(tl->endTextOffset() * item->mag());
    6005         1244 :         item->endText()->setAlign(tl->endTextAlign());
    6006         1244 :         item->endText()->setPosition(tl->endTextPosition());
    6007         1244 :         item->endText()->setFontStyle(tl->endFontStyle());
    6008         1244 :         item->endText()->setPlacement(PlacementV::ABOVE);
    6009         1244 :         item->endText()->setTrack(item->track());
    6010         1244 :         item->endText()->setColor(tl->lineColor());
    6011         1244 :         layoutText(item->endText(), item->endText()->mutldata());
    6012              :     } else {
    6013           25 :         item->endText()->setXmlText(u"");
    6014              :     }
    6015              : 
    6016         1269 :     if (!item->textLineBase()->textSizeSpatiumDependent()) {
    6017            0 :         item->text()->setSize(item->text()->size() * item->defaultSpatium() / item->spatium());
    6018            0 :         item->endText()->setSize(item->endText()->size() * item->defaultSpatium() / item->spatium());
    6019              :     }
    6020              : 
    6021         1269 :     PointF defaultPos = tl->defaultPos();
    6022         1269 :     ldata->move(defaultPos);
    6023              : 
    6024         1269 :     PointF pp1;
    6025         1269 :     PointF pp2(item->pos2());
    6026              : 
    6027              :     // line with no text or hooks - just use the basic rectangle for line
    6028         1763 :     if (item->text()->empty() && item->endText()->empty()
    6029          483 :         && (!isSingleOrBegin || tl->beginHookType() == HookType::NONE)
    6030         1763 :         && (!isSingleOrEnd || tl->endHookType() == HookType::NONE)) {
    6031          444 :         ldata->npoints = 2;
    6032          444 :         ldata->points[0] = pp1;
    6033          444 :         ldata->points[1] = pp2;
    6034          444 :         ldata->lineLength = sqrt(PointF::dotProduct(pp2 - pp1, pp2 - pp1));
    6035              : 
    6036          444 :         item->setbbox(TextLineBaseSegment::boundingBoxOfLine(pp1, pp2, lineWidth / 2, isDottedLine));
    6037          493 :         return;
    6038              :     }
    6039              : 
    6040          825 :     double l1 = 0.0;
    6041          825 :     double l2 = 0.0;
    6042          825 :     const double gapBetweenTextAndLine = spatium * tl->gapBetweenTextAndLine().val();
    6043              : 
    6044          825 :     const bool alignBeginText = tl->beginTextPlace() == TextPlace::LEFT || tl->beginTextPlace() == TextPlace::AUTO;
    6045          825 :     const bool alignContinueText = tl->continueTextPlace() == TextPlace::LEFT || tl->continueTextPlace() == TextPlace::AUTO;
    6046          825 :     const bool alignEndText = tl->endTextPlace() == TextPlace::LEFT || tl->endTextPlace() == TextPlace::AUTO;
    6047          825 :     const bool hasBeginText = !item->text()->empty() && isSingleOrBegin;
    6048          825 :     const bool hasContinueText = !item->text()->empty() && !isSingleOrBegin;
    6049          825 :     const bool hasEndText = !item->endText()->empty() && isSingleOrEnd;
    6050              : 
    6051          825 :     Shape shape;
    6052              : 
    6053          825 :     if (!item->text()->empty()) {
    6054          775 :         if ((isSingleOrBegin && alignBeginText) || (!isSingleOrBegin && alignContinueText)) {
    6055          427 :             l1 = gapBetweenTextAndLine;
    6056          427 :             switch (item->text()->position()) {
    6057          427 :             case AlignH::LEFT:
    6058          427 :                 l1 += item->text()->ldata()->bbox().width();
    6059          427 :                 break;
    6060            0 :             case AlignH::HCENTER:
    6061            0 :                 l1 += item->text()->ldata()->bbox().width() / 2;
    6062            0 :                 break;
    6063            0 :             default:
    6064            0 :                 break;
    6065              :             }
    6066              :         }
    6067          775 :         shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item);
    6068              :     }
    6069              :     // set end text position and extend bbox
    6070          825 :     if (!item->endText()->empty()) {
    6071           30 :         if (alignEndText) {
    6072           30 :             l2 = gapBetweenTextAndLine;
    6073           30 :             switch (item->endText()->position()) {
    6074            4 :             case AlignH::RIGHT:
    6075            4 :                 l2 += item->endText()->ldata()->bbox().width();
    6076            4 :                 break;
    6077           11 :             case AlignH::HCENTER:
    6078           11 :                 l2 += item->endText()->ldata()->bbox().width() / 2;
    6079           11 :                 break;
    6080           15 :             default:
    6081           15 :                 break;
    6082              :             }
    6083              :         }
    6084           30 :         const double endTextX = std::max(pp2.x(), shape.bbox().right() + l2); // prevent end text from overlapping begin text
    6085           30 :         item->endText()->mutldata()->setPosX(endTextX);
    6086           30 :         shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item);
    6087              :     }
    6088              : 
    6089          825 :     bool beginHookDrawnSeparately = false;
    6090          825 :     bool endHookDrawnSeparately = false;
    6091              : 
    6092          825 :     if (tl->lineVisible() || !ctx.conf().isPrintingMode()) {
    6093          825 :         pp1.rx() += l1;
    6094          825 :         pp2.rx() -= l2;
    6095              : 
    6096              :         // Make sure baseline of text and line are properly aligned (accounting for line thickness)
    6097          825 :         if ((hasBeginText && alignBeginText) || (hasContinueText && alignContinueText)) {
    6098          427 :             alignBaseLine(item->text(), pp1, pp2);
    6099          398 :         } else if (hasEndText && alignEndText) {
    6100           11 :             alignBaseLine(item->endText(), pp1, pp2);
    6101              :         }
    6102              : 
    6103          825 :         const double beginHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->beginHookHeight().val() * spatium;
    6104          825 :         const double endHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->endHookHeight().val() * spatium;
    6105              : 
    6106          825 :         double beginHookWidth = 0.0;
    6107          825 :         double endHookWidth = 0.0;
    6108              : 
    6109          825 :         if (tl->beginHookType() == HookType::HOOK_45) {
    6110           10 :             beginHookWidth = std::fabs(beginHookHeight * .4);
    6111           10 :             pp1.rx() += beginHookWidth;
    6112              :         }
    6113              : 
    6114          825 :         if (tl->endHookType() == HookType::HOOK_45) {
    6115            8 :             endHookWidth = std::fabs(endHookHeight * .4);
    6116            8 :             pp2.rx() -= endHookWidth;
    6117              :         }
    6118              : 
    6119              :         // don't draw backwards lines (or hooks) if text is longer than nominal line length
    6120          825 :         if (!item->text()->empty() && pp1.x() > pp2.x() && !tl->diagonal()) {
    6121           49 :             ldata->setShape(shape);
    6122           49 :             return;
    6123              :         }
    6124              : 
    6125              :         const bool beginArrow = isSingleOrBegin
    6126          776 :                                 && (tl->beginHookType() == HookType::ARROW_FILLED || tl->beginHookType() == HookType::ARROW);
    6127          776 :         const bool endArrow = isSingleOrEnd && (tl->endHookType() == HookType::ARROW_FILLED || tl->endHookType() == HookType::ARROW);
    6128              : 
    6129          776 :         if (beginArrow) {
    6130            0 :             const bool filled = tl->beginHookType() == HookType::ARROW_FILLED;
    6131            0 :             ldata->beginArrow = createArrow(true, filled, pp1, pp2, tl);
    6132            0 :             shape.add(ldata->beginArrow.boundingRect(), item);
    6133              :         }
    6134          776 :         if (endArrow) {
    6135            0 :             const bool filled = tl->endHookType() == HookType::ARROW_FILLED;
    6136            0 :             ldata->endArrow = createArrow(false, filled, pp1, pp2, tl);
    6137            0 :             shape.add(ldata->endArrow.boundingRect(), item);
    6138              :         }
    6139              : 
    6140         1511 :         auto hasHook = [](HookType type) -> bool {
    6141         1511 :             switch (type) {
    6142          897 :             case HookType::HOOK_45:
    6143              :             case HookType::HOOK_90:
    6144              :             case HookType::HOOK_90T:
    6145          897 :                 return true;
    6146          614 :             case HookType::NONE:
    6147              :             case HookType::ARROW:
    6148              :             case HookType::ARROW_FILLED:
    6149              :             case HookType::ROSETTE:
    6150          614 :                 return false;
    6151            0 :             default:
    6152            0 :                 break;
    6153              :             }
    6154            0 :             return false;
    6155              :         };
    6156              : 
    6157          776 :         const bool beginHook = isSingleOrBegin && hasHook(tl->beginHookType());
    6158          776 :         const bool endHook = isSingleOrEnd && hasHook(tl->endHookType());
    6159              : 
    6160          776 :         if (beginHook) {
    6161              :             // We use the term "endpoint" for the point that does not touch the main line.
    6162          374 :             const PointF& beginHookEndpoint = ldata->points[ldata->npoints++]
    6163          374 :                                                   = PointF(pp1.x() - beginHookWidth, pp1.y() + beginHookHeight);
    6164          374 :             if (tl->beginHookType() == HookType::HOOK_90T) {
    6165              :                 // A T-hook needs to be drawn separately, so we add an extra point
    6166            0 :                 beginHookDrawnSeparately = true;
    6167            0 :                 ldata->points[ldata->npoints++] = PointF(pp1.x() - beginHookWidth, pp1.y() - beginHookHeight);
    6168          374 :             } else if (tl->lineStyle() != LineType::SOLID) {
    6169              :                 // For non-solid lines, we also draw the hook separately,
    6170              :                 // so that we can distribute the dashes/dots for each linepiece individually
    6171            2 :                 beginHookDrawnSeparately = true;
    6172            2 :                 PointF& beginHookStartpoint = ldata->points[ldata->npoints++] = pp1;
    6173              : 
    6174            2 :                 if (tl->lineStyle() == LineType::DASHED) {
    6175              :                     // For dashes lines, we extend the lines somewhat,
    6176              :                     // so that the corner between them gets filled
    6177            1 :                     bool checkAngle = tl->beginHookType() == HookType::HOOK_45 || tl->diagonal();
    6178            1 :                     extendLines(beginHookEndpoint, beginHookStartpoint, pp1, pp2, lineWidth, checkAngle);
    6179              :                 }
    6180              :             }
    6181              :         }
    6182              : 
    6183          776 :         ldata->points[ldata->npoints++] = pp1;
    6184          776 :         PointF& pp22 = ldata->points[ldata->npoints++] = pp2; // Keep a reference so that we can modify later
    6185              : 
    6186          776 :         if (endHook) {
    6187          523 :             const PointF endHookEndpoint = PointF(pp2.x() + endHookWidth, pp2.y() + endHookHeight);
    6188          523 :             if (tl->endHookType() == HookType::HOOK_90T) {
    6189              :                 // A T-hook needs to be drawn separately, so we add an extra point
    6190           45 :                 endHookDrawnSeparately = true;
    6191           45 :                 ldata->points[ldata->npoints++] = PointF(pp2.x() + endHookWidth, pp2.y() - endHookHeight);
    6192          478 :             } else if (tl->lineStyle() != LineType::SOLID) {
    6193              :                 // For non-solid lines, we also draw the hook separately,
    6194              :                 // so that we can distribute the dashes/dots for each linepiece individually
    6195          148 :                 endHookDrawnSeparately = true;
    6196          148 :                 PointF& endHookStartpoint = ldata->points[ldata->npoints++] = pp2;
    6197              : 
    6198          148 :                 if (tl->lineStyle() == LineType::DASHED) {
    6199              :                     // For dashes lines, we extend the lines somewhat,
    6200              :                     // so that the corner between them gets filled
    6201          146 :                     bool checkAngle = tl->endHookType() == HookType::HOOK_45 || tl->diagonal();
    6202          146 :                     extendLines(pp1, pp22, endHookStartpoint, endHookEndpoint, lineWidth, checkAngle);
    6203              :                 }
    6204              :             }
    6205              : 
    6206          523 :             ldata->points[ldata->npoints++] = endHookEndpoint;
    6207              :         }
    6208              : 
    6209          776 :         ldata->lineLength = sqrt(PointF::dotProduct(pp22 - pp1, pp22 - pp1));
    6210              :     }
    6211              : 
    6212              :     // Calculate shape of line
    6213              :     {
    6214          776 :         int start = 0;
    6215          776 :         if (beginHookDrawnSeparately) {
    6216            2 :             PointF& p1 = ldata->points[start++];
    6217            2 :             PointF& p2 = ldata->points[start++];
    6218            2 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
    6219              :         }
    6220              : 
    6221          776 :         int end = ldata->npoints;
    6222          776 :         if (endHookDrawnSeparately) {
    6223          193 :             PointF& p1 = ldata->points[--end];
    6224          193 :             PointF& p2 = ldata->points[--end];
    6225          193 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
    6226              :         }
    6227              : 
    6228         2254 :         for (int i = start; i < end - 1; ++i) {
    6229         1478 :             shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lineWidth / 2,
    6230         1478 :                                                              isDottedLine), item, !tl->lineVisible());
    6231              :         }
    6232              :     }
    6233              : 
    6234          776 :     ldata->setShape(shape);
    6235          825 : }
    6236              : 
    6237         2280 : void TLayout::layoutTie(Tie* item, LayoutContext&)
    6238              : {
    6239              :     UNUSED(item);
    6240         2280 : }
    6241              : 
    6242         6530 : void TLayout::layoutTimeSig(const TimeSig* item, TimeSig::LayoutData* ldata, const LayoutContext& ctx)
    6243              : {
    6244              :     LAYOUT_CALL_ITEM(item);
    6245              :     LD_INDEPENDENT;
    6246              : 
    6247         6530 :     ldata->setPos(0.0, 0.0);
    6248         6530 :     double spatium = item->spatium();
    6249              : 
    6250         6530 :     ldata->setBbox(RectF());                    // prepare for an empty time signature
    6251              : 
    6252         6530 :     ldata->pointLargeLeftParen = PointF();
    6253         6530 :     ldata->pz = PointF();
    6254         6530 :     ldata->pn = PointF();
    6255         6530 :     ldata->pointLargeRightParen = PointF();
    6256              : 
    6257         6530 :     const MStyle& style = item->style();
    6258         6530 :     TimeSigPlacement timeSigPlacement = style.styleV(Sid::timeSigPlacement).value<TimeSigPlacement>();
    6259         6530 :     if (timeSigPlacement == TimeSigPlacement::NORMAL) {
    6260         6496 :         const_cast<TimeSig*>(item)->setSystemFlag(false);
    6261              :     } else {
    6262           34 :         const_cast<TimeSig*>(item)->setSystemFlag(true);
    6263              :     }
    6264              : 
    6265         6530 :     double lineDist = 0.0;
    6266         6530 :     int numOfLines = 0;
    6267         6530 :     TimeSigType sigType = item->timeSigType();
    6268         6530 :     const Staff* staff = item->staff();
    6269         6530 :     const Segment* seg = item->segment();
    6270         6530 :     const Measure* meas = seg ? seg->measure() : nullptr;
    6271         6530 :     const Fraction tick = meas ? meas->tick() : item->tick();
    6272              : 
    6273         6530 :     if (staff) {
    6274              :         // if staff is without time sig, format as if no text at all
    6275         6530 :         const StaffType* stVisibility = staff->staffType(item->tick());
    6276         6530 :         if (!stVisibility->genTimesig()) {
    6277              :             // reset position and box sizes to 0
    6278              :             // LOGD("staff: no time sig");
    6279          111 :             ldata->pointLargeLeftParen.rx() = 0.0;
    6280          111 :             ldata->pn.rx() = 0.0;
    6281          111 :             ldata->pz.rx() = 0.0;
    6282          111 :             ldata->pointLargeRightParen.rx() = 0.0;
    6283          111 :             ldata->setBbox(RectF());
    6284              :             // leave everything else as it is:
    6285              :             // draw() will anyway skip any drawing if staff type has no time sigs
    6286          111 :             return;
    6287              :         }
    6288         6419 :         numOfLines  = staff->lines(tick);
    6289         6419 :         lineDist    = staff->lineDistance(tick);
    6290              :     } else {
    6291              :         // assume dimensions of a standard staff
    6292            0 :         lineDist = 1.0;
    6293            0 :         numOfLines = 5;
    6294              :     }
    6295              : 
    6296              :     // if some symbol
    6297              :     // compute vert. displacement to center in the staff height
    6298              :     // determine middle staff position:
    6299              : 
    6300         6419 :     double yoff = spatium * (numOfLines - 1) * .5 * lineDist;
    6301              : 
    6302              :     // C and Ccut are placed at the middle of the staff: use yoff directly
    6303         6419 :     IEngravingFontPtr font = ctx.engravingFont();
    6304         6419 :     ScaleF scale = item->scale();
    6305         6419 :     SizeF mag(item->magS() * scale);
    6306         6419 :     TimeSigStyle timeSigStyle = item->timeSigStyle();
    6307              : 
    6308         6419 :     double numDist = item->numDist() * scale.height();
    6309              : 
    6310         6419 :     Shape shape;
    6311         6419 :     if (sigType == TimeSigType::FOUR_FOUR) {
    6312           76 :         ldata->pz = PointF(0.0, yoff);
    6313          152 :         SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCommonLarge
    6314           76 :                     : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCommonNarrow
    6315              :                     : SymId::timeSigCommon;
    6316           76 :         RectF bbox = font->bbox(sym, mag);
    6317           76 :         shape.add(bbox.translated(ldata->pz), item);
    6318           76 :         ldata->ns.clear();
    6319           76 :         ldata->ns.push_back(sym);
    6320           76 :         ldata->ds.clear();
    6321         6343 :     } else if (sigType == TimeSigType::ALLA_BREVE) {
    6322           56 :         ldata->pz = PointF(0.0, yoff);
    6323          112 :         SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCutCommonLarge
    6324           56 :                     : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCutCommonNarrow
    6325              :                     : SymId::timeSigCutCommon;
    6326           56 :         RectF bbox = font->bbox(sym, mag);
    6327           56 :         shape.add(bbox.translated(ldata->pz), item);
    6328           56 :         ldata->ns.clear();
    6329           56 :         ldata->ns.push_back(sym);
    6330           56 :         ldata->ds.clear();
    6331         6287 :     } else if (sigType == TimeSigType::CUT_BACH) {
    6332            0 :         ldata->pz = PointF(0.0, yoff);
    6333            0 :         SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCut2Large
    6334            0 :                     : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCut2Narrow
    6335              :                     : SymId::timeSigCut2;
    6336            0 :         RectF bbox = font->bbox(sym, mag);
    6337            0 :         shape.add(bbox.translated(ldata->pz), item);
    6338            0 :         ldata->ns.clear();
    6339            0 :         ldata->ns.push_back(sym);
    6340            0 :         ldata->ds.clear();
    6341         6287 :     } else if (sigType == TimeSigType::CUT_TRIPLE) {
    6342            0 :         ldata->pz = PointF(0.0, yoff);
    6343            0 :         SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCut3Large
    6344            0 :                     : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCut3Narrow
    6345              :                     : SymId::timeSigCut3;
    6346            0 :         RectF bbox = font->bbox(sym, mag);
    6347            0 :         shape.add(bbox.translated(ldata->pz), item);
    6348            0 :         ldata->ns.clear();
    6349            0 :         ldata->ns.push_back(sym);
    6350            0 :         ldata->ds.clear();
    6351              :     } else {
    6352         6287 :         if (item->numeratorString().isEmpty()) {
    6353        18795 :             ldata->ns = timeSigSymIdsFromString(item->numeratorString().isEmpty()
    6354        12530 :                                                 ? String::number(item->sig().numerator())
    6355            0 :                                                 : item->numeratorString(),
    6356         6265 :                                                 timeSigStyle);
    6357              : 
    6358        18795 :             ldata->ds = timeSigSymIdsFromString(item->denominatorString().isEmpty()
    6359        12530 :                                                 ? String::number(item->sig().denominator())
    6360            0 :                                                 : item->denominatorString(),
    6361         6265 :                                                 timeSigStyle);
    6362              :         } else {
    6363           22 :             ldata->ns = timeSigSymIdsFromString(item->numeratorString(), timeSigStyle);
    6364           22 :             ldata->ds = timeSigSymIdsFromString(item->denominatorString(), timeSigStyle);
    6365              :         }
    6366              : 
    6367         6287 :         RectF numRect = font->bbox(ldata->ns, mag);
    6368         6287 :         RectF denRect = font->bbox(ldata->ds, mag);
    6369              : 
    6370         6287 :         double pzY = yoff - (denRect.width() < 0.01 ? 0.0 : 0.5 * (numRect.height() + numDist));
    6371         6287 :         double pnY = yoff + 0.5 * (denRect.height() + numDist);
    6372              : 
    6373         6287 :         if (numRect.width() >= denRect.width()) {
    6374              :             // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
    6375         5298 :             ldata->pz = PointF(0.0, pzY);
    6376              :             // denominator: horiz: centred around centre of numerator | vert: one space below centre line
    6377         5298 :             ldata->pn = PointF((numRect.width() - denRect.width()) * .5, pnY);
    6378              :         } else {
    6379              :             // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
    6380          989 :             ldata->pz = PointF((denRect.width() - numRect.width()) * .5, pzY);
    6381              :             // denominator: horiz: centred around centre of numerator | vert: one space below centre line
    6382          989 :             ldata->pn = PointF(0.0, pnY);
    6383              :         }
    6384              : 
    6385              :         // centering of parenthesis so the middle of the parenthesis is at the divisor marking level
    6386         6287 :         int centerY = yoff / 2 + spatium;
    6387         6287 :         int widestPortion = numRect.width() > denRect.width() ? numRect.width() : denRect.width();
    6388         6287 :         ldata->pointLargeLeftParen = PointF(-spatium, centerY);
    6389         6287 :         ldata->pointLargeRightParen = PointF(widestPortion + spatium, centerY);
    6390              : 
    6391         6287 :         shape.add(numRect.translated(ldata->pz), item);       // translate bounding boxes to actual string positions
    6392         6287 :         shape.add(denRect.translated(ldata->pn), item);
    6393         6287 :         if (item->largeParentheses()) {
    6394           18 :             shape.add(RectF(ldata->pointLargeLeftParen.x(), ldata->pointLargeLeftParen.y() - denRect.height(), spatium / 2,
    6395           18 :                             numRect.height() + denRect.height()), item);
    6396           18 :             shape.add(RectF(ldata->pointLargeRightParen.x(), ldata->pointLargeRightParen.y() - denRect.height(),  spatium / 2,
    6397           18 :                             numRect.height() + denRect.height()), item);
    6398              :         }
    6399              :     }
    6400              : 
    6401         6419 :     ldata->setShape(shape);
    6402              : 
    6403         6419 :     ldata->setPosX(-shape.bbox().left());
    6404              : 
    6405         6419 :     if (item->isAboveStaves()) {
    6406            0 :         if (staff->hasSystemObjectsBelowBottomStaff()) {
    6407            0 :             double staffHeight = spatium * (numOfLines - 1) * lineDist;
    6408            0 :             ldata->setPosY(staffHeight - ldata->bbox().top());
    6409              :         } else {
    6410            0 :             ldata->setPosY(-ldata->bbox().bottom());
    6411              :         }
    6412         6419 :     } else if (item->isAcrossStaves()) {
    6413           34 :         double top = ldata->bbox().top();
    6414           34 :         ldata->setPosY(-top);
    6415              :     }
    6416              : 
    6417         6419 :     ldata->moveY(item->yPos());
    6418         6419 : }
    6419              : 
    6420          498 : void TLayout::layoutTimeTickAnchor(TimeTickAnchor* item, LayoutContext&)
    6421              : {
    6422          498 :     TimeTickAnchor::LayoutData* ldata = item->mutldata();
    6423          498 :     const StaffType* st = item->staffType();
    6424          498 :     ldata->setPos(0.0, 0.0);
    6425          498 :     double width = item->segment()->width();
    6426          498 :     double height = item->staff()->staffHeight();
    6427          498 :     if (st && st->lines() == 1) {
    6428              :         // Special case for single line staves
    6429            0 :         const double lineDist = st->lineDistance().val() * item->spatium();
    6430            0 :         ldata->setPosY(-lineDist);
    6431            0 :         height = lineDist * 2;
    6432              :     }
    6433          498 :     ldata->setBbox(0.0, 0.0, width, height);
    6434          498 : }
    6435              : 
    6436            0 : void TLayout::layoutTremoloSingle(TremoloSingleChord* item, LayoutContext& ctx)
    6437              : {
    6438              :     LAYOUT_CALL_ITEM(item);
    6439            0 :     TremoloLayout::layout(item, ctx);
    6440            0 : }
    6441              : 
    6442            0 : void TLayout::layoutTremoloTwo(TremoloTwoChord* item, LayoutContext& ctx)
    6443              : {
    6444              :     LAYOUT_CALL_ITEM(item);
    6445            0 :     TremoloLayout::layout(item, ctx);
    6446            0 : }
    6447              : 
    6448           38 : void TLayout::layoutTremoloBar(const TremoloBar* item, TremoloBar::LayoutData* ldata)
    6449              : {
    6450              :     LAYOUT_CALL_ITEM(item);
    6451              :     LD_INDEPENDENT;
    6452              : 
    6453           38 :     double spatium = item->spatium();
    6454              : 
    6455           38 :     ldata->setPos(0.0, -spatium * 3.0);
    6456              : 
    6457              :     /* we place the tremolo bars starting slightly before the
    6458              :      *  notehead, and end it slightly after, drawing above the
    6459              :      *  note. The values specified in Guitar Pro are very large, too
    6460              :      *  large for the scale used in Musescore. We used the
    6461              :      *  timeFactor and pitchFactor below to reduce these values down
    6462              :      *  consistently to values that make sense to draw with the
    6463              :      *  Musescore scale. */
    6464              : 
    6465           38 :     double timeFactor  = item->userMag() / 1.0;
    6466           38 :     double pitchFactor = -spatium * .02;
    6467              : 
    6468           38 :     PolygonF polygon;
    6469          142 :     for (const PitchValue& v : item->points()) {
    6470          104 :         polygon << PointF(v.time * timeFactor, v.pitch * pitchFactor);
    6471              :     }
    6472           38 :     ldata->polygon = polygon;
    6473              : 
    6474           38 :     const double w = item->absoluteFromSpatium(item->lineWidth());
    6475           38 :     ldata->setBbox(ldata->polygon.boundingRect().adjusted(-w, -w, w, w));
    6476           38 : }
    6477              : 
    6478          116 : void TLayout::layoutTrillSegment(TrillSegment* item, LayoutContext& ctx)
    6479              : {
    6480              :     LAYOUT_CALL_ITEM(item);
    6481          116 :     TrillSegment::LayoutData* ldata = item->mutldata();
    6482          116 :     Trill* trill = item->trill();
    6483          116 :     EngravingItem* startItem = trill->startElement();
    6484          116 :     Chord* startChord = startItem && startItem->isChord() ? toChord(startItem) : nullptr;
    6485          116 :     if (startChord) {
    6486              :         // Semi-hack: spanners don't have staffMove property, so we change
    6487              :         // the staffIdx itself to follow cross-staff chords if needed
    6488          114 :         item->setStaffIdx(startChord->vStaffIdx());
    6489              :     }
    6490              : 
    6491          116 :     if (item->staff()) {
    6492          116 :         ldata->setMag(item->staff()->staffMag(item->tick()));
    6493              :     }
    6494          116 :     if (item->spanner()->placeBelow()) {
    6495            1 :         ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
    6496              :     }
    6497          116 :     const double yOff = item->staffOffsetY();
    6498          116 :     ldata->moveY(yOff);
    6499              : 
    6500          116 :     PointF defaultPos = item->defaultPos();
    6501          116 :     ldata->move(defaultPos);
    6502              : 
    6503          116 :     bool accidentalGoesBelow = trill->trillType() == TrillType::DOWNPRALL_LINE;
    6504          116 :     Ornament* ornament = trill->ornament();
    6505          116 :     if (ornament) {
    6506          116 :         if (item->isSingleBeginType()) {
    6507          115 :             TLayout::layoutOrnament(ornament, ornament->mutldata(), ctx.conf());
    6508              :         }
    6509          116 :         trill->setAccidental(accidentalGoesBelow ? ornament->accidentalBelow() : ornament->accidentalAbove());
    6510          116 :         trill->setCueNoteChord(ornament->cueNoteChord());
    6511          116 :         ArticulationAnchor anchor = ornament->anchor();
    6512          116 :         if (anchor == ArticulationAnchor::AUTO) {
    6513          113 :             trill->setPlacement(trill->track() % 2 ? PlacementV::BELOW : PlacementV::ABOVE);
    6514              :         } else {
    6515            3 :             trill->setPlacement(anchor == ArticulationAnchor::TOP ? PlacementV::ABOVE : PlacementV::BELOW);
    6516              :         }
    6517          116 :         trill->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::STYLED); // Ensures that the property isn't written (it is written by the ornamnent)
    6518              :     }
    6519              : 
    6520          116 :     if (item->isSingleType() || item->isBeginType()) {
    6521          115 :         switch (trill->trillType()) {
    6522          101 :         case TrillType::TRILL_LINE:
    6523          101 :             item->symbolLine(SymId::ornamentTrill, SymId::wiggleTrill);
    6524          101 :             break;
    6525            6 :         case TrillType::PRALLPRALL_LINE:
    6526            6 :             item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
    6527            6 :             break;
    6528            4 :         case TrillType::UPPRALL_LINE:
    6529            4 :             item->symbolLine(SymId::ornamentBottomLeftConcaveStroke,
    6530              :                              SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
    6531            4 :             break;
    6532            4 :         case TrillType::DOWNPRALL_LINE:
    6533            4 :             item->symbolLine(SymId::ornamentLeftVerticalStroke,
    6534              :                              SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
    6535            4 :             break;
    6536              :         }
    6537          115 :         Accidental* a = trill->accidental();
    6538          115 :         if (a) {
    6539           10 :             double vertMargin = 0.35 * item->spatium();
    6540           10 :             RectF box = item->symBbox(item->symbols().front());
    6541           10 :             double x = 0;
    6542           10 :             double y = 0;
    6543           10 :             x = 0.5 * (box.width() - a->width());
    6544              :             double minVertDist = accidentalGoesBelow
    6545           20 :                                  ? Shape(box).minVerticalDistance(a->shape())
    6546           10 :                                  : a->shape().minVerticalDistance(Shape(box));
    6547           10 :             y = (accidentalGoesBelow ? minVertDist + vertMargin : -minVertDist - vertMargin) + yOff;
    6548           10 :             a->setPos(x, y);
    6549           10 :             a->setOwnershipParent(item);
    6550              :         }
    6551              :     } else {
    6552            1 :         switch (trill->trillType()) {
    6553            1 :         case TrillType::TRILL_LINE:
    6554              :         case TrillType::PRALLPRALL_LINE:
    6555            1 :             item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
    6556            1 :             break;
    6557            0 :         case TrillType::UPPRALL_LINE:
    6558              :         case TrillType::DOWNPRALL_LINE:
    6559            0 :             item->symbolLine(SymId::ornamentZigZagLineNoRightEnd,
    6560              :                              SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
    6561            0 :             break;
    6562              :         }
    6563              :     }
    6564              : 
    6565          116 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    6566          116 : }
    6567              : 
    6568          232 : void TLayout::fillTrillSegmentShape(const TrillSegment* item, TrillSegment::LayoutData* ldata, const LayoutConfiguration& conf)
    6569              : {
    6570              :     LAYOUT_CALL_ITEM(item);
    6571          232 :     std::shared_ptr<const IEngravingFont> font = conf.engravingFont();
    6572          232 :     Shape s = font->shape(item->symbols(), item->magS());
    6573          232 :     Accidental* accidental = item->trill()->accidental();
    6574          232 :     if (accidental && accidental->visible() && item->isSingleBeginType()) {
    6575           20 :         s.add(accidental->shape().translate(accidental->pos()));
    6576              :     }
    6577              : 
    6578          232 :     ldata->setShape(s);
    6579          232 : }
    6580              : 
    6581            0 : void TLayout::layoutTripletFeel(const TripletFeel* item, TripletFeel::LayoutData* ldata)
    6582              : {
    6583              :     LAYOUT_CALL_ITEM(item);
    6584            0 :     layoutSystemText(item, ldata);
    6585            0 : }
    6586              : 
    6587            0 : void TLayout::layoutTrill(Trill* item, LayoutContext& ctx)
    6588              : {
    6589              :     LAYOUT_CALL_ITEM(item);
    6590            0 :     layoutLine(static_cast<SLine*>(item), ctx);
    6591            0 : }
    6592              : 
    6593           41 : void TLayout::layoutTuplet(Tuplet* item, LayoutContext& ctx)
    6594              : {
    6595              :     LAYOUT_CALL_ITEM(item);
    6596           41 :     TupletLayout::layout(item, ctx);
    6597           41 : }
    6598              : 
    6599         1941 : void TLayout::fillTupletShape(const Tuplet* item, Tuplet::LayoutData* ldata)
    6600              : {
    6601              :     LAYOUT_CALL_ITEM(item);
    6602         1941 :     Shape s;
    6603         1941 :     if (item->hasBracket()) {
    6604         4107 :         auto tupletRect = [](const PointF& p1, const PointF& p2, double height, const Tuplet* item) {
    6605         4107 :             Shape shape;
    6606         4107 :             double xLength = p2.x() - p1.x();
    6607         4107 :             int nSteps = 2 * std::ceil(xLength / item->spatium());
    6608         4107 :             double xStep = xLength / nSteps;
    6609         4107 :             double yStep = (p2.y() - p1.y()) / nSteps;
    6610         4107 :             double rectHeight = (item->isUp() ? height : -height) - yStep;
    6611         4107 :             double xCur = p1.x();
    6612         4107 :             double yCur = p1.y() + yStep;
    6613        28095 :             for (int i = 0; i < nSteps; ++i) {
    6614        23988 :                 shape.add(RectF(xCur, yCur, xStep, rectHeight).normalized(), item);
    6615        23988 :                 xCur += xStep;
    6616        23988 :                 yCur += yStep;
    6617              :             }
    6618         4107 :             return shape;
    6619            0 :         };
    6620              : 
    6621         1027 :         double height = item->style().styleAbsolute(Sid::tupletBracketHookHeight);
    6622         1027 :         s.add(tupletRect(item->bracketL[0], item->bracketL[1], height, item));
    6623         1027 :         s.add(tupletRect(item->bracketL[1], item->bracketL[2], height, item));
    6624         1027 :         if (item->number()) {
    6625         1026 :             s.add(tupletRect(item->bracketR[0], item->bracketR[1], height, item));
    6626         1026 :             s.add(tupletRect(item->bracketR[1], item->bracketR[2], height, item));
    6627              :         } else {
    6628            1 :             s.add(tupletRect(item->bracketL[2], item->bracketL[3], height, item));
    6629              :         }
    6630              :     }
    6631         1941 :     if (item->number()) {
    6632         1694 :         s.add(item->number()->ldata()->bbox().translated(item->number()->pos()));
    6633              :     }
    6634              : 
    6635         1941 :     ldata->setShape(s);
    6636         1941 : }
    6637              : 
    6638           28 : void TLayout::layoutVibratoSegment(VibratoSegment* item, LayoutContext&)
    6639              : {
    6640              :     LAYOUT_CALL_ITEM(item);
    6641           28 :     VibratoSegment::LayoutData* ldata = item->mutldata();
    6642           28 :     if (item->staff()) {
    6643           28 :         ldata->setMag(item->staff()->staffMag(item->tick()));
    6644              :     }
    6645           28 :     if (item->spanner()->placeBelow()) {
    6646            0 :         ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
    6647              :     }
    6648              : 
    6649           28 :     PointF defaultPos = item->defaultPos();
    6650           28 :     ldata->move(defaultPos);
    6651              : 
    6652           28 :     ldata->moveY(item->staffOffsetY());
    6653              : 
    6654           28 :     switch (item->vibrato()->vibratoType()) {
    6655           10 :     case VibratoType::GUITAR_VIBRATO:
    6656           10 :         item->symbolLine(SymId::guitarVibratoStroke, SymId::guitarVibratoStroke);
    6657           10 :         break;
    6658            4 :     case VibratoType::GUITAR_VIBRATO_WIDE:
    6659            4 :         item->symbolLine(SymId::guitarWideVibratoStroke, SymId::guitarWideVibratoStroke);
    6660            4 :         break;
    6661           11 :     case VibratoType::VIBRATO_SAWTOOTH:
    6662           11 :         item->symbolLine(SymId::wiggleSawtooth, SymId::wiggleSawtooth);
    6663           11 :         break;
    6664            3 :     case VibratoType::VIBRATO_SAWTOOTH_WIDE:
    6665            3 :         item->symbolLine(SymId::wiggleSawtoothWide, SymId::wiggleSawtoothWide);
    6666            3 :         break;
    6667            0 :     default:
    6668            0 :         break;
    6669              :     }
    6670              : 
    6671           28 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    6672           28 : }
    6673              : 
    6674            0 : void TLayout::layoutVibrato(Vibrato* item, LayoutContext& ctx)
    6675              : {
    6676              :     LAYOUT_CALL_ITEM(item);
    6677            0 :     layoutLine(static_cast<SLine*>(item), ctx);
    6678              : 
    6679            0 :     if (ctx.conf().isPaletteMode()) {
    6680            0 :         return;
    6681              :     }
    6682            0 :     if (item->spannerSegments().empty()) {
    6683            0 :         LOGD("Vibrato: no segments");
    6684            0 :         return;
    6685              :     }
    6686              : }
    6687              : 
    6688            0 : void TLayout::layoutVolta(Volta* item, LayoutContext& ctx)
    6689              : {
    6690              :     LAYOUT_CALL_ITEM(item);
    6691            0 :     layoutLine(item, ctx);
    6692            0 : }
    6693              : 
    6694          279 : void TLayout::layoutVoltaSegment(VoltaSegment* item, LayoutContext& ctx)
    6695              : {
    6696              :     LAYOUT_CALL_ITEM(item);
    6697          279 :     VoltaSegment::LayoutData* ldata = item->mutldata();
    6698          279 :     layoutTextLineBaseSegment(item, ctx);
    6699          279 :     if (item->placeBelow()) {
    6700            0 :         if (TextBase* beginText = item->text()) {
    6701            0 :             PointF offset = beginText->offset();
    6702            0 :             offset.setY(-offset.y() + beginText->ldata()->bbox().height());
    6703            0 :             beginText->setOffset(offset);
    6704              :         }
    6705            0 :         if (TextBase* endText = item->endText()) {
    6706            0 :             PointF offset = endText->offset();
    6707            0 :             offset.setY(-offset.y() + endText->ldata()->bbox().height());
    6708            0 :             endText->setOffset(offset);
    6709              :         }
    6710              :         // recalculate shape after moving texts
    6711            0 :         Shape sh = recalculateTextLineBaseSegmentShape(item);
    6712            0 :         ldata->setShape(sh);
    6713            0 :     }
    6714          279 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    6715          279 : }
    6716              : 
    6717            0 : void TLayout::layoutWhammyBarSegment(WhammyBarSegment* item, LayoutContext& ctx)
    6718              : {
    6719              :     LAYOUT_CALL_ITEM(item);
    6720            0 :     WhammyBarSegment::LayoutData* ldata = item->mutldata();
    6721            0 :     layoutTextLineBaseSegment(item, ctx);
    6722              : 
    6723            0 :     Autoplace::autoplaceSpannerSegment(item, ldata);
    6724            0 : }
    6725              : 
    6726              : using LayoutSystemTypes = rtti::TypeList<LyricsLine, Slur, HammerOnPullOff, TappingHalfSlur, Volta>;
    6727              : 
    6728              : class LayoutSystemVisitor : public rtti::Visitor<LayoutSystemVisitor>
    6729              : {
    6730              : public:
    6731              :     template<typename T>
    6732         8839 :     static bool doVisit(Spanner* item, System* system, LayoutContext& ctx, SpannerSegment** segOut)
    6733              :     {
    6734         8839 :         if (T::classof(item)) {
    6735         1494 :             *segOut = TLayout::layoutSystem(static_cast<T*>(item), system, ctx);
    6736         1494 :             return true;
    6737              :         }
    6738         7345 :         return false;
    6739              :     }
    6740              : };
    6741              : 
    6742         2553 : SpannerSegment* TLayout::layoutSystem(Spanner* item, System* system, LayoutContext& ctx)
    6743              : {
    6744              :     LAYOUT_CALL_ITEM(item);
    6745         2553 :     SpannerSegment* seg = nullptr;
    6746         2553 :     bool found = LayoutSystemVisitor::visit(LayoutSystemTypes {}, item, system, ctx, &seg);
    6747         2553 :     if (!found) {
    6748         1059 :         SLine* line = dynamic_cast<SLine*>(item);
    6749         1059 :         if (line) {
    6750         1059 :             seg = layoutSystemSLine(line, system, ctx);
    6751              :         } else {
    6752            0 :             DO_ASSERT(found);
    6753              :         }
    6754              :     }
    6755         2553 :     return seg;
    6756              : }
    6757              : 
    6758         2609 : SpannerSegment* TLayout::getNextLayoutSystemSegment(Spanner* spanner, System* system,
    6759              :                                                     std::function<SpannerSegment* ()> createSegment)
    6760              : {
    6761              :     // Prefer a segment which has already been added to the system
    6762         2609 :     SpannerSegment* seg = nullptr;
    6763         2609 :     SpannerSegment* detached = nullptr;
    6764         3765 :     for (SpannerSegment* ss : spanner->spannerSegments()) {
    6765         1156 :         if (ss->system() == system) {
    6766            0 :             seg = ss;
    6767            0 :             break;
    6768              :         }
    6769         1156 :         if (!detached && !ss->system()) {
    6770         1065 :             detached = ss;
    6771              :         }
    6772              :     }
    6773         2609 :     if (!seg) {
    6774         2609 :         seg = detached;
    6775              :     }
    6776              : 
    6777         2609 :     if (!seg) {
    6778         1544 :         if ((seg = spanner->popUnusedSegment())) {
    6779            0 :             spanner->reuse(seg);
    6780              :         } else {
    6781         1544 :             seg = createSegment();
    6782         1544 :             assert(seg);
    6783         1544 :             spanner->add(seg);
    6784              :         }
    6785              :     }
    6786         2609 :     seg->moveToSystem(system);
    6787         2609 :     seg->setSpanner(spanner);
    6788         2609 :     seg->setTrack(spanner->track());
    6789         2609 :     seg->setVisible(spanner->visible());
    6790         2609 :     return seg;
    6791              : }
    6792              : 
    6793              : // layout spannersegment for system
    6794         1338 : SpannerSegment* TLayout::layoutSystemSLine(SLine* line, System* system, LayoutContext& ctx)
    6795              : {
    6796              :     LAYOUT_CALL_ITEM(line);
    6797         1338 :     Fraction stick = system->firstMeasure()->tick();
    6798         1338 :     Fraction etick = system->lastMeasure()->endTick();
    6799              : 
    6800         1338 :     LineSegment* lineSegm = toLineSegment(TLayout::getNextLayoutSystemSegment(line, system, [line]() {
    6801          823 :         return line->createLineSegment();
    6802              :     }));
    6803              : 
    6804              :     SpannerSegmentType sst;
    6805         1338 :     if (line->tick() >= stick) {
    6806              :         //
    6807              :         // this is the first call to layoutSystem,
    6808              :         // processing the first line segment
    6809              :         //
    6810         1314 :         line->computeStartElement();
    6811         1314 :         line->computeEndElement();
    6812         1314 :         if (line->tick2() < etick) {
    6813         1150 :             sst = SpannerSegmentType::SINGLE;
    6814          164 :         } else if (line->tick2() == etick) {
    6815          143 :             if (line->isPedal() && toPedal(line)->connect45HookToNext()) {
    6816            0 :                 sst = SpannerSegmentType::BEGIN;
    6817              :             } else {
    6818          143 :                 sst = SpannerSegmentType::SINGLE;
    6819              :             }
    6820              :         } else {
    6821           21 :             sst = SpannerSegmentType::BEGIN;
    6822              :         }
    6823         1314 :         if (line->hasVoiceAssignmentProperties()) {
    6824          467 :             line->setPlacementBasedOnVoiceAssignment(ctx.conf().styleV(Sid::dynamicsHairpinVoiceBasedPlacement).value<DirectionV>());
    6825              :         }
    6826           24 :     } else if (line->tick() < stick
    6827           24 :                && (line->tick2() > etick || (line->tick2() == etick && line->isPedal() && toPedal(line)->connect45HookToNext()))) {
    6828            3 :         sst = SpannerSegmentType::MIDDLE;
    6829              :     } else {
    6830              :         //
    6831              :         // this is the last call to layoutSystem
    6832              :         // processing the last line segment
    6833              :         //
    6834           21 :         sst = SpannerSegmentType::END;
    6835              :     }
    6836         1338 :     lineSegm->setSpannerSegmentType(sst);
    6837         1338 :     lineSegm->setPlacement(line->placement());
    6838              : 
    6839         1338 :     switch (sst) {
    6840         1293 :     case SpannerSegmentType::SINGLE: {
    6841              :         System* s;
    6842         1293 :         PointF p1 = line->linePos(Grip::START, &s);
    6843         1293 :         PointF p2 = line->linePos(Grip::END,   &s);
    6844         1293 :         double len = p2.x() - p1.x();
    6845         1293 :         lineSegm->setPos(p1);
    6846         1293 :         lineSegm->setPos2(PointF(len, p2.y() - p1.y()));
    6847              :     }
    6848         1293 :     break;
    6849           21 :     case SpannerSegmentType::BEGIN: {
    6850              :         System* s;
    6851           21 :         PointF p1 = line->linePos(Grip::START, &s);
    6852           21 :         lineSegm->setPos(p1);
    6853           21 :         double x2 = system->endingXForOpenEndedLines();
    6854           21 :         lineSegm->setPos2(PointF(x2 - p1.x(), 0.0));
    6855              :     }
    6856           21 :     break;
    6857            3 :     case SpannerSegmentType::MIDDLE: {
    6858            3 :         double x1 = line->isVolta() ? voltaMidEndSegmentStartX(toVolta(line), system, ctx)
    6859            3 :                     : system->firstNoteRestSegmentX(true);
    6860            3 :         double x2 = system->endingXForOpenEndedLines();
    6861              :         System* s;
    6862            3 :         PointF p1 = line->linePos(Grip::START, &s);
    6863            3 :         lineSegm->setPos(PointF(x1, p1.y()));
    6864            3 :         lineSegm->setPos2(PointF(x2 - x1, 0.0));
    6865              :     }
    6866            3 :     break;
    6867           21 :     case SpannerSegmentType::END: {
    6868              :         System* s;
    6869           21 :         PointF p2 = line->linePos(Grip::END,   &s);
    6870           21 :         double x1 = line->isVolta() ? voltaMidEndSegmentStartX(toVolta(line), system, ctx)
    6871           15 :                     : system->firstNoteRestSegmentX(true);
    6872           21 :         double len = p2.x() - x1;
    6873           21 :         lineSegm->setPos(PointF(p2.x() - len, p2.y()));
    6874           21 :         lineSegm->setPos2(PointF(len, 0.0));
    6875              :     }
    6876           21 :     break;
    6877              :     }
    6878              : 
    6879         1338 :     layoutLineSegment(lineSegm, ctx);
    6880              : 
    6881         1338 :     return lineSegm;
    6882              : }
    6883              : 
    6884            6 : double TLayout::voltaMidEndSegmentStartX(Volta* volta, System* system, LayoutContext& ctx)
    6885              : {
    6886            6 :     Measure* firstMeasure = system->firstMeasure();
    6887            6 :     if (!firstMeasure) {
    6888            0 :         return 0.0;
    6889              :     }
    6890              : 
    6891            6 :     Segment* refSeg = firstMeasure->repeatStart()
    6892           12 :                       ? firstMeasure->first(SegmentType::StartRepeatBarLine)
    6893            6 :                       : ctx.conf().styleB(Sid::voltaAlignStartBeforeKeySig)
    6894            6 :                       ? firstMeasure->first(SegmentType::KeySig)
    6895            6 :                       : nullptr;
    6896              : 
    6897            6 :     if (!refSeg) {
    6898            6 :         return system->firstNoteRestSegmentX(true);
    6899              :     }
    6900              : 
    6901            0 :     if (refSeg->isStartRepeatBarLineType()) {
    6902            0 :         return refSeg->x() + firstMeasure->x();
    6903              :     }
    6904              : 
    6905            0 :     KeySig* keySig = toKeySig(refSeg->element(volta->track()));
    6906            0 :     if (!keySig || keySig->ldata()->keySymbols.empty()) {
    6907            0 :         return 0.0;
    6908              :     }
    6909              : 
    6910            0 :     KeySym keySym = keySig->ldata()->keySymbols.front();
    6911            0 :     double xCutout = ctx.engravingFont()->smuflAnchor(keySym.sym, SmuflAnchorId::cutOutNW, 1.0).x();
    6912            0 :     return keySig->x() + keySym.xPos.val() + xCutout + refSeg->x() + firstMeasure->x();
    6913              : }
    6914              : 
    6915          342 : SpannerSegment* TLayout::layoutSystem(LyricsLine* line, System* system, LayoutContext& ctx)
    6916              : {
    6917              :     LAYOUT_CALL_ITEM(line);
    6918          342 :     if (!line->isPartialLyricsLine() && !line->lyrics()) {
    6919            0 :         return nullptr;
    6920              :     }
    6921              : 
    6922          342 :     Fraction stick = system->firstMeasure()->tick();
    6923          342 :     Fraction etick = system->lastMeasure()->endTick();
    6924              : 
    6925          342 :     LyricsLineSegment* lineSegm = toLyricsLineSegment(TLayout::getNextLayoutSystemSegment(line, system, [line]() {
    6926          250 :         return line->createLineSegment();
    6927              :     }));
    6928              : 
    6929              :     SpannerSegmentType sst;
    6930          342 :     if (line->tick() >= stick) {
    6931          328 :         TLayout::layoutLyricsLine(line);
    6932          328 :         if (line->ticks().isZero() && line->isEndMelisma()) {
    6933            0 :             return nullptr;
    6934              :         }
    6935          328 :         line->computeStartElement();
    6936          328 :         line->computeEndElement();
    6937          328 :         sst = line->tick2() <= etick ? SpannerSegmentType::SINGLE : SpannerSegmentType::BEGIN;
    6938           14 :     } else if (line->tick() < stick && line->tick2() > etick) {
    6939            4 :         sst = SpannerSegmentType::MIDDLE;
    6940              :     } else {
    6941           10 :         sst = SpannerSegmentType::END;
    6942              :     }
    6943          342 :     lineSegm->setSpannerSegmentType(sst);
    6944              : 
    6945          342 :     TLayout::layoutLyricsLineSegment(lineSegm, ctx);
    6946          342 :     if (!line->isPartialLyricsLine() && !line->lyrics()) {
    6947              :         // this line could have been removed in the process of laying out surrounding lyrics
    6948            0 :         return nullptr;
    6949              :     }
    6950              : 
    6951          342 :     return lineSegm;
    6952              : }
    6953              : 
    6954          279 : SpannerSegment* TLayout::layoutSystem(Volta* line, System* system, LayoutContext& ctx)
    6955              : {
    6956              :     LAYOUT_CALL_ITEM(line);
    6957          279 :     return layoutSystemSLine(line, system, ctx);
    6958              : }
    6959              : 
    6960          873 : SpannerSegment* TLayout::layoutSystem(Slur* line, System* system, LayoutContext& ctx)
    6961              : {
    6962              :     LAYOUT_CALL_ITEM(line);
    6963          873 :     return SlurTieLayout::layoutSystem(line, system, ctx);
    6964              : }
        

Generated by: LCOV version 2.0-1