@coderline/alphatab 1.8.0-alpha.1647 → 1.8.0-alpha.1650

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/alphaTab.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.8.0-alpha.1647 (develop, build 1647)
2
+ * alphaTab v1.8.0-alpha.1650 (develop, build 1650)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -209,9 +209,9 @@
209
209
  * @internal
210
210
  */
211
211
  class VersionInfo {
212
- static version = '1.8.0-alpha.1647';
213
- static date = '2025-12-17T02:10:13.730Z';
214
- static commit = 'a1bd11d4a3eef22409f1f49a5dd172ace16108cc';
212
+ static version = '1.8.0-alpha.1650';
213
+ static date = '2025-12-20T02:07:19.755Z';
214
+ static commit = '5789b59da2b7f8897debe003c9b75e4d48b5e9b3';
215
215
  static print(print) {
216
216
  print(`alphaTab ${VersionInfo.version}`);
217
217
  print(`commit: ${VersionInfo.commit}`);
@@ -2750,6 +2750,23 @@
2750
2750
  * Whether barlines should be drawn across staves within the same system.
2751
2751
  */
2752
2752
  extendBarLines = false;
2753
+ /**
2754
+ * Whether to hide empty staves.
2755
+ */
2756
+ hideEmptyStaves = false;
2757
+ /**
2758
+ * Whether to also hide empty staves in the first system.
2759
+ * @remarks
2760
+ * Only has an effect when activating {@link hideEmptyStaves}.
2761
+ */
2762
+ hideEmptyStavesInFirstSystem = false;
2763
+ /**
2764
+ * Whether to show brackets and braces across single staves.
2765
+ * @remarks
2766
+ * This allows a more consistent view for identifying staves when using
2767
+ * {@link hideEmptyStaves}
2768
+ */
2769
+ showSingleStaffBrackets = false;
2753
2770
  }
2754
2771
 
2755
2772
  /**
@@ -8679,7 +8696,10 @@
8679
8696
  ['firstsystemtracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
8680
8697
  ['othersystemstracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
8681
8698
  ['extendbarlines', null],
8682
- ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]]
8699
+ ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]],
8700
+ ['hideemptystaves', null],
8701
+ ['hideemptystavesinfirstsystem', null],
8702
+ ['showsinglestaffbrackets', null]
8683
8703
  ]);
8684
8704
  static staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([
8685
8705
  ['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]],
@@ -9019,6 +9039,9 @@
9019
9039
  ['othersystemstracknameorientation', null],
9020
9040
  ['extendbarlines', null],
9021
9041
  ['chorddiagramsinscore', null],
9042
+ ['hideemptystaves', null],
9043
+ ['hideemptystavesinfirstsystem', null],
9044
+ ['showsinglestaffbrackets', null],
9022
9045
  [
9023
9046
  'tuning',
9024
9047
  [
@@ -13893,6 +13916,15 @@
13893
13916
  ? AlphaTex1LanguageHandler._booleanLikeValue(metaData.arguments.arguments, 0)
13894
13917
  : true;
13895
13918
  return ApplyNodeResult.Applied;
13919
+ case 'hideemptystaves':
13920
+ score.stylesheet.hideEmptyStaves = true;
13921
+ return ApplyNodeResult.Applied;
13922
+ case 'hideemptystavesinfirstsystem':
13923
+ score.stylesheet.hideEmptyStavesInFirstSystem = true;
13924
+ return ApplyNodeResult.Applied;
13925
+ case 'showsinglestaffbrackets':
13926
+ score.stylesheet.showSingleStaffBrackets = true;
13927
+ return ApplyNodeResult.Applied;
13896
13928
  default:
13897
13929
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
13898
13930
  }
@@ -15724,6 +15756,15 @@
15724
15756
  if (stylesheet.globalDisplayChordDiagramsInScore) {
15725
15757
  nodes.push(Atnf.meta('chordDiagramsInScore'));
15726
15758
  }
15759
+ if (stylesheet.hideEmptyStaves) {
15760
+ nodes.push(Atnf.meta('hideEmptyStaves'));
15761
+ }
15762
+ if (stylesheet.hideEmptyStavesInFirstSystem) {
15763
+ nodes.push(Atnf.meta('hideEmptyStavesInFirstSystem'));
15764
+ }
15765
+ if (stylesheet.showSingleStaffBrackets) {
15766
+ nodes.push(Atnf.meta('showSingleStaffBrackets'));
15767
+ }
15727
15768
  // Unsupported:
15728
15769
  // 'globaldisplaychorddiagramsontop',
15729
15770
  // 'pertrackchorddiagramsontop',
@@ -38810,6 +38851,9 @@
38810
38851
  }
38811
38852
  }
38812
38853
  o.set("extendbarlines", obj.extendBarLines);
38854
+ o.set("hideemptystaves", obj.hideEmptyStaves);
38855
+ o.set("hideemptystavesinfirstsystem", obj.hideEmptyStavesInFirstSystem);
38856
+ o.set("showsinglestaffbrackets", obj.showSingleStaffBrackets);
38813
38857
  return o;
38814
38858
  }
38815
38859
  static setProperty(obj, property, v) {
@@ -38871,6 +38915,15 @@
38871
38915
  case "extendbarlines":
38872
38916
  obj.extendBarLines = v;
38873
38917
  return true;
38918
+ case "hideemptystaves":
38919
+ obj.hideEmptyStaves = v;
38920
+ return true;
38921
+ case "hideemptystavesinfirstsystem":
38922
+ obj.hideEmptyStavesInFirstSystem = v;
38923
+ return true;
38924
+ case "showsinglestaffbrackets":
38925
+ obj.showSingleStaffBrackets = v;
38926
+ return true;
38874
38927
  }
38875
38928
  return false;
38876
38929
  }
@@ -45898,6 +45951,19 @@
45898
45951
  }
45899
45952
  return null;
45900
45953
  }
45954
+ /**
45955
+ * Looks for the first visible beat which starts at this lookup so it can be used for cursor placement.
45956
+ * @param checker The custom checker to see if a beat is visible.
45957
+ * @returns The first beat which is visible according to the given tracks or null.
45958
+ */
45959
+ getVisibleBeatAtStartWithChecker(checker) {
45960
+ for (const b of this.highlightedBeats) {
45961
+ if (b.playbackStart === this.start && checker.isVisible(b.beat)) {
45962
+ return b.beat;
45963
+ }
45964
+ }
45965
+ return null;
45966
+ }
45901
45967
  }
45902
45968
 
45903
45969
  /**
@@ -46337,6 +46403,18 @@
46337
46403
  }
46338
46404
  }
46339
46405
  }
46406
+ /**
46407
+ * @internal
46408
+ */
46409
+ class TrackLookupBeatVisibilityChecker {
46410
+ _lookup;
46411
+ constructor(lookup) {
46412
+ this._lookup = lookup;
46413
+ }
46414
+ isVisible(beat) {
46415
+ return this._lookup.has(beat.voice.bar.staff.track.index);
46416
+ }
46417
+ }
46340
46418
  /**
46341
46419
  * This class holds all information about when {@link MasterBar}s and {@link Beat}s are played.
46342
46420
  *
@@ -46397,31 +46475,44 @@
46397
46475
  * @returns The information about the current beat or null if no beat could be found.
46398
46476
  */
46399
46477
  findBeat(trackLookup, tick, currentBeatHint = null) {
46478
+ return this.findBeatWithChecker(new TrackLookupBeatVisibilityChecker(trackLookup), tick, currentBeatHint);
46479
+ }
46480
+ /**
46481
+ * Finds the currently played beat given a list of tracks and the current time.
46482
+ * @param checker The checker to ask whether a beat is visible and should be considered for result.
46483
+ * @param tick The current time in midi ticks.
46484
+ * @param currentBeatHint Used for optimized lookup during playback. By passing in a previous result lookup of the next one can be optimized using heuristics. (optional).
46485
+ * @returns The information about the current beat or null if no beat could be found.
46486
+ */
46487
+ findBeatWithChecker(checker, tick, currentBeatHint = null) {
46400
46488
  let result = null;
46401
46489
  if (currentBeatHint) {
46402
- result = this._findBeatFast(trackLookup, currentBeatHint, tick);
46490
+ result = this._findBeatFast(checker, currentBeatHint, tick);
46403
46491
  }
46404
46492
  if (!result) {
46405
- result = this._findBeatSlow(trackLookup, currentBeatHint, tick, false);
46493
+ result = this._findBeatSlow(checker, currentBeatHint, tick, false);
46406
46494
  }
46407
46495
  return result;
46408
46496
  }
46409
- _findBeatFast(trackLookup, currentBeatHint, tick) {
46497
+ _findBeatFast(checker, currentBeatHint, tick) {
46410
46498
  // still within current lookup.
46411
46499
  if (tick >= currentBeatHint.start && tick < currentBeatHint.end) {
46412
46500
  return currentBeatHint;
46413
46501
  }
46414
46502
  // already on the next beat?
46415
- if (currentBeatHint.nextBeat && tick >= currentBeatHint.nextBeat.start && tick < currentBeatHint.nextBeat.end) {
46503
+ if (currentBeatHint.nextBeat &&
46504
+ tick >= currentBeatHint.nextBeat.start &&
46505
+ tick < currentBeatHint.nextBeat.end &&
46506
+ (checker === undefined || checker.isVisible(currentBeatHint.nextBeat.beat))) {
46416
46507
  const next = currentBeatHint.nextBeat;
46417
46508
  // fill next in chain
46418
- this._fillNextBeat(next, trackLookup);
46509
+ this._fillNextBeat(next, checker);
46419
46510
  return next;
46420
46511
  }
46421
46512
  // likely a loop or manual seek, need to fallback to slow path
46422
46513
  return null;
46423
46514
  }
46424
- _fillNextBeatMultiBarRest(current, trackLookup) {
46515
+ _fillNextBeatMultiBarRest(current, checker) {
46425
46516
  const group = this.multiBarRestInfo.get(current.masterBar.masterBar.index);
46426
46517
  // this is a bit sensitive. we assume that the sequence of multi-rest bars and the
46427
46518
  // chained nextMasterBar equal. so we just jump over X bars
@@ -46435,7 +46526,7 @@
46435
46526
  if (endMasterBar) {
46436
46527
  // one more following -> use start of next
46437
46528
  if (endMasterBar.nextMasterBar) {
46438
- current.nextBeat = this._firstBeatInMasterBar(trackLookup, endMasterBar.nextMasterBar, endMasterBar.nextMasterBar.start, true);
46529
+ current.nextBeat = this._firstBeatInMasterBar(checker, endMasterBar.nextMasterBar, endMasterBar.nextMasterBar.start, true);
46439
46530
  // if we have the next beat take the difference between the times as duration
46440
46531
  if (current.nextBeat) {
46441
46532
  current.tickDuration = current.nextBeat.start - current.start;
@@ -46465,19 +46556,19 @@
46465
46556
  }
46466
46557
  current.calculateDuration();
46467
46558
  }
46468
- _fillNextBeat(current, trackLookup) {
46559
+ _fillNextBeat(current, checker) {
46469
46560
  // on multibar rests take the duration until the end.
46470
46561
  if (this._isMultiBarRestResult(current)) {
46471
- this._fillNextBeatMultiBarRest(current, trackLookup);
46562
+ this._fillNextBeatMultiBarRest(current, checker);
46472
46563
  }
46473
46564
  else {
46474
- this._fillNextBeatDefault(current, trackLookup);
46565
+ this._fillNextBeatDefault(current, checker);
46475
46566
  }
46476
46567
  }
46477
- _fillNextBeatDefault(current, trackLookup) {
46478
- current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end, trackLookup, true);
46568
+ _fillNextBeatDefault(current, checker) {
46569
+ current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end, checker, true);
46479
46570
  if (current.nextBeat == null) {
46480
- current.nextBeat = this._findBeatSlow(trackLookup, current, current.end, true);
46571
+ current.nextBeat = this._findBeatSlow(checker, current, current.end, true);
46481
46572
  }
46482
46573
  // if we have the next beat take the difference between the times as duration
46483
46574
  if (current.nextBeat) {
@@ -46508,7 +46599,7 @@
46508
46599
  beat.isRest &&
46509
46600
  beat.voice.bar.isRestOnly);
46510
46601
  }
46511
- _findBeatSlow(trackLookup, currentBeatHint, tick, isNextSearch) {
46602
+ _findBeatSlow(checker, currentBeatHint, tick, isNextSearch) {
46512
46603
  // get all beats within the masterbar
46513
46604
  let masterBar = null;
46514
46605
  if (currentBeatHint != null) {
@@ -46530,14 +46621,14 @@
46530
46621
  if (!masterBar) {
46531
46622
  return null;
46532
46623
  }
46533
- return this._firstBeatInMasterBar(trackLookup, masterBar, tick, isNextSearch);
46624
+ return this._firstBeatInMasterBar(checker, masterBar, tick, isNextSearch);
46534
46625
  }
46535
- _firstBeatInMasterBar(trackLookup, startMasterBar, tick, isNextSearch) {
46626
+ _firstBeatInMasterBar(checker, startMasterBar, tick, isNextSearch) {
46536
46627
  let masterBar = startMasterBar;
46537
46628
  // scan through beats and find first one which has a beat visible
46538
46629
  while (masterBar) {
46539
46630
  if (masterBar.firstBeat) {
46540
- const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick, trackLookup, isNextSearch);
46631
+ const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick, checker, isNextSearch);
46541
46632
  if (beat) {
46542
46633
  return beat;
46543
46634
  }
@@ -46555,7 +46646,7 @@
46555
46646
  * @param isNextSearch
46556
46647
  * @returns
46557
46648
  */
46558
- _findBeatInMasterBar(masterBar, currentStartLookup, tick, visibleTracks, isNextSearch) {
46649
+ _findBeatInMasterBar(masterBar, currentStartLookup, tick, checker, isNextSearch) {
46559
46650
  if (!currentStartLookup) {
46560
46651
  return null;
46561
46652
  }
@@ -46569,7 +46660,7 @@
46569
46660
  (currentStartLookup.start <= relativeTick || (isNextSearch && relativeTick < 0)) &&
46570
46661
  relativeTick < currentStartLookup.end) {
46571
46662
  startBeatLookup = currentStartLookup;
46572
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46663
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46573
46664
  // found the matching beat lookup but none of the beats are visible
46574
46665
  // in this case scan further to the next lookup which has any visible beat
46575
46666
  if (!startBeat) {
@@ -46577,7 +46668,7 @@
46577
46668
  let currentMasterBar = masterBar;
46578
46669
  while (currentMasterBar != null && startBeat == null) {
46579
46670
  while (currentStartLookup != null) {
46580
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46671
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46581
46672
  if (startBeat) {
46582
46673
  startBeatLookup = currentStartLookup;
46583
46674
  masterBar = currentMasterBar;
@@ -46595,7 +46686,7 @@
46595
46686
  let currentMasterBar = masterBar;
46596
46687
  while (currentMasterBar != null && startBeat == null) {
46597
46688
  while (currentStartLookup != null) {
46598
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46689
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46599
46690
  if (startBeat) {
46600
46691
  startBeatLookup = currentStartLookup;
46601
46692
  masterBar = currentMasterBar;
@@ -46619,17 +46710,17 @@
46619
46710
  if (startBeat == null) {
46620
46711
  return null;
46621
46712
  }
46622
- const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch, visibleTracks);
46713
+ const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch, checker);
46623
46714
  return result;
46624
46715
  }
46625
- _createResult(masterBar, beatLookup, beat, isNextSearch, visibleTracks) {
46716
+ _createResult(masterBar, beatLookup, beat, isNextSearch, checker) {
46626
46717
  const result = new MidiTickLookupFindBeatResult(masterBar);
46627
46718
  result.beat = beat;
46628
46719
  result.beatLookup = beatLookup;
46629
46720
  result.tickDuration = beatLookup.end - beatLookup.start;
46630
46721
  if (!isNextSearch) {
46631
46722
  // the next beat filling will adjust this result with the respective durations
46632
- this._fillNextBeat(result, visibleTracks);
46723
+ this._fillNextBeat(result, checker);
46633
46724
  }
46634
46725
  else if (this._isMultiBarRestResult(result)) {
46635
46726
  const multiRest = this.multiBarRestInfo.get(masterBar.masterBar.index);
@@ -49556,6 +49647,19 @@
49556
49647
  }
49557
49648
  }
49558
49649
 
49650
+ /**
49651
+ * @internal
49652
+ */
49653
+ class BoundsLookupVisibilityChecker {
49654
+ bounds = null;
49655
+ isVisible(beat) {
49656
+ const bounds = this.bounds;
49657
+ if (!bounds) {
49658
+ return false;
49659
+ }
49660
+ return bounds.findBeat(beat) !== null;
49661
+ }
49662
+ }
49559
49663
  /**
49560
49664
  * This class represents the public API of alphaTab and provides all logic to display
49561
49665
  * a music sheet in any UI using the given {@link IUiFacade}
@@ -49566,6 +49670,7 @@
49566
49670
  _startTime = 0;
49567
49671
  _trackIndexes = null;
49568
49672
  _trackIndexLookup = null;
49673
+ _beatVisibilityChecker = new BoundsLookupVisibilityChecker();
49569
49674
  _isDestroyed = false;
49570
49675
  _score = null;
49571
49676
  _tracks = [];
@@ -51362,12 +51467,9 @@
51362
51467
  this._previousTick = tick;
51363
51468
  const cache = this._tickCache;
51364
51469
  if (cache) {
51365
- const tracks = this._trackIndexLookup;
51366
- if (tracks != null && tracks.size > 0) {
51367
- const beat = cache.findBeat(tracks, tick, this._currentBeat);
51368
- if (beat) {
51369
- this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
51370
- }
51470
+ const beat = cache.findBeatWithChecker(this._beatVisibilityChecker, tick, this._currentBeat);
51471
+ if (beat) {
51472
+ this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
51371
51473
  }
51372
51474
  }
51373
51475
  }
@@ -52569,6 +52671,7 @@
52569
52671
  if (this._isDestroyed) {
52570
52672
  return;
52571
52673
  }
52674
+ this._beatVisibilityChecker.bounds = this.boundsLookup;
52572
52675
  this._currentBeat = null;
52573
52676
  this._cursorUpdateTick(this._previousTick, false, 1, true, true);
52574
52677
  this.postRenderFinished.trigger();
@@ -60576,8 +60679,10 @@
60576
60679
  height = 0;
60577
60680
  index = 0;
60578
60681
  staffIndex = 0;
60682
+ isVisible = false;
60683
+ _emptyBarCount = 0;
60579
60684
  get isFirstInSystem() {
60580
- return this.index === 0;
60685
+ return this.system.firstVisibleStaff === this;
60581
60686
  }
60582
60687
  topEffectInfos = [];
60583
60688
  bottomEffectInfos = [];
@@ -60612,10 +60717,11 @@
60612
60717
  get contentBottom() {
60613
60718
  return this.y + this.topPadding + this.topOverflow + this.staffBottom;
60614
60719
  }
60615
- constructor(trackIndex, staff, factory) {
60720
+ constructor(system, trackIndex, staff, factory) {
60616
60721
  this._factory = factory;
60617
60722
  this.trackIndex = trackIndex;
60618
60723
  this.modelStaff = staff;
60724
+ this.system = system;
60619
60725
  for (const b of factory.effectBands) {
60620
60726
  if (b.shouldCreate && !b.shouldCreate(staff)) {
60621
60727
  continue;
@@ -60631,6 +60737,7 @@
60631
60737
  break;
60632
60738
  }
60633
60739
  }
60740
+ this._updateVisibility();
60634
60741
  }
60635
60742
  getSharedLayoutData(key, def) {
60636
60743
  if (this._sharedLayoutData.has(key)) {
@@ -60657,6 +60764,20 @@
60657
60764
  renderer.reLayout();
60658
60765
  this.barRenderers.push(renderer);
60659
60766
  this.system.layout.registerBarRenderer(this.staffId, renderer);
60767
+ if (renderer.bar.isEmpty || renderer.bar.isRestOnly) {
60768
+ this._emptyBarCount++;
60769
+ }
60770
+ this._updateVisibility();
60771
+ }
60772
+ _updateVisibility() {
60773
+ const stylesheet = this.modelStaff.track.score.stylesheet;
60774
+ const canHideEmptyStaves = stylesheet.hideEmptyStaves && (stylesheet.hideEmptyStavesInFirstSystem || this.system.index > 0);
60775
+ if (canHideEmptyStaves) {
60776
+ this.isVisible = this._emptyBarCount < this.barRenderers.length;
60777
+ }
60778
+ else {
60779
+ this.isVisible = true;
60780
+ }
60660
60781
  }
60661
60782
  addBar(bar, layoutingInfo, additionalMultiBarsRestBars) {
60662
60783
  const renderer = this._factory.create(this.system.layout.renderer, bar);
@@ -60676,6 +60797,10 @@
60676
60797
  }
60677
60798
  this.barRenderers.push(renderer);
60678
60799
  this.system.layout.registerBarRenderer(this.staffId, renderer);
60800
+ if (bar.isEmpty || bar.isRestOnly) {
60801
+ this._emptyBarCount++;
60802
+ }
60803
+ this._updateVisibility();
60679
60804
  }
60680
60805
  revertLastBar() {
60681
60806
  this._sharedLayoutData = new Map();
@@ -60686,6 +60811,10 @@
60686
60811
  for (const r of this.barRenderers) {
60687
60812
  r.afterStaffBarReverted();
60688
60813
  }
60814
+ if (lastBar.bar.isEmpty || lastBar.bar.isRestOnly) {
60815
+ this._emptyBarCount--;
60816
+ }
60817
+ this._updateVisibility();
60689
60818
  return lastBar;
60690
60819
  }
60691
60820
  scaleToWidth(width) {
@@ -60796,6 +60925,7 @@
60796
60925
  this.height += this.topPadding + topOverflow + this.bottomOverflow + this.bottomPadding;
60797
60926
  }
60798
60927
  this.height = Math.ceil(this.height);
60928
+ this._updateVisibility();
60799
60929
  }
60800
60930
  paint(cx, cy, canvas, startIndex, count) {
60801
60931
  if (this.height === 0 || count === 0) {
@@ -61196,6 +61326,8 @@
61196
61326
  track;
61197
61327
  staffSystem;
61198
61328
  staves = [];
61329
+ firstVisibleStaff;
61330
+ lastVisibleStaff;
61199
61331
  bracket = null;
61200
61332
  constructor(staffSystem, track) {
61201
61333
  this.staffSystem = staffSystem;
@@ -61210,18 +61342,47 @@
61210
61342
  * @internal
61211
61343
  */
61212
61344
  class SystemBracket {
61213
- firstStaffInBracket = null;
61214
- lastStaffInBracket = null;
61345
+ _system;
61346
+ firstStaffInBracket;
61347
+ lastStaffInBracket;
61348
+ firstVisibleStaffInBracket;
61349
+ lastVisibleStaffInBracket;
61215
61350
  drawAsBrace = false;
61216
61351
  braceScale = 1;
61217
61352
  width = 0;
61218
61353
  index = 0;
61219
- get canPaint() {
61220
- return this.firstStaffInBracket !== null && this.lastStaffInBracket !== null;
61354
+ canPaint = false;
61355
+ constructor(system) {
61356
+ this._system = system;
61357
+ }
61358
+ updateCanPaint() {
61359
+ let firstVisibleStaff = undefined;
61360
+ let lastVisibleStaff = undefined;
61361
+ for (let i = this.firstStaffInBracket.index; i <= this.lastStaffInBracket.index; i++) {
61362
+ const staff = this._system.allStaves[i];
61363
+ if (staff.isVisible) {
61364
+ if (!firstVisibleStaff) {
61365
+ firstVisibleStaff = staff;
61366
+ }
61367
+ lastVisibleStaff = staff;
61368
+ }
61369
+ }
61370
+ this.firstVisibleStaffInBracket = firstVisibleStaff;
61371
+ this.lastVisibleStaffInBracket = lastVisibleStaff;
61372
+ if (!firstVisibleStaff || !lastVisibleStaff) {
61373
+ this.canPaint = false;
61374
+ return;
61375
+ }
61376
+ // single staff brackets?
61377
+ const singleStaffBrackets = this._system.layout.renderer.score.stylesheet.showSingleStaffBrackets;
61378
+ if (!singleStaffBrackets && firstVisibleStaff === lastVisibleStaff) {
61379
+ this.canPaint = false;
61380
+ return;
61381
+ }
61382
+ this.canPaint = true;
61221
61383
  }
61222
61384
  finalizeBracket(smuflMetrics) {
61223
- // systems with just a single staff do not have a bracket
61224
- if (this.firstStaffInBracket === this.lastStaffInBracket) {
61385
+ if (!this.canPaint) {
61225
61386
  this.width = 0;
61226
61387
  return;
61227
61388
  }
@@ -61235,11 +61396,11 @@
61235
61396
  else {
61236
61397
  this.width = smuflMetrics.bracketThickness;
61237
61398
  }
61238
- if (!this.drawAsBrace || !this.firstStaffInBracket || !this.lastStaffInBracket) {
61399
+ if (!this.drawAsBrace) {
61239
61400
  return;
61240
61401
  }
61241
- const firstStart = this.firstStaffInBracket.contentTop;
61242
- const lastEnd = this.lastStaffInBracket.contentBottom;
61402
+ const firstStart = this.firstVisibleStaffInBracket.contentTop;
61403
+ const lastEnd = this.lastVisibleStaffInBracket.contentBottom;
61243
61404
  const requiredHeight = lastEnd - firstStart;
61244
61405
  const requiredScaleForBracket = requiredHeight / bravuraBraceHeightAtMusicFontSize;
61245
61406
  this.braceScale = requiredScaleForBracket;
@@ -61251,8 +61412,8 @@
61251
61412
  */
61252
61413
  class SingleTrackSystemBracket extends SystemBracket {
61253
61414
  track;
61254
- constructor(track) {
61255
- super();
61415
+ constructor(system, track) {
61416
+ super(system);
61256
61417
  this.track = track;
61257
61418
  this.drawAsBrace = SingleTrackSystemBracket.isTrackDrawAsBrace(track);
61258
61419
  }
@@ -61317,6 +61478,7 @@
61317
61478
  topPadding;
61318
61479
  bottomPadding;
61319
61480
  allStaves = [];
61481
+ firstVisibleStaff;
61320
61482
  constructor(layout) {
61321
61483
  this.layout = layout;
61322
61484
  this.topPadding = layout.renderer.settings.display.systemPaddingTop;
@@ -61335,14 +61497,40 @@
61335
61497
  this.masterBarsRenderers.push(renderers);
61336
61498
  renderers.layoutingInfo.preBeatSize = 0;
61337
61499
  let src = 0;
61338
- for (let i = 0, j = this.staves.length; i < j; i++) {
61339
- const g = this.staves[i];
61340
- for (let k = 0, l = g.staves.length; k < l; k++) {
61341
- const s = g.staves[k];
61500
+ let firstVisibleStaff = undefined;
61501
+ let anyStaffVisible = false;
61502
+ for (const g of this.staves) {
61503
+ let firstVisibleStaffInGroup = undefined;
61504
+ let lastVisibleStaffInGroup = undefined;
61505
+ for (const s of g.staves) {
61342
61506
  const renderer = renderers.renderers[src++];
61343
61507
  s.addBarRenderer(renderer);
61508
+ if (s.isVisible) {
61509
+ anyStaffVisible = true;
61510
+ if (!firstVisibleStaffInGroup) {
61511
+ firstVisibleStaffInGroup = s;
61512
+ }
61513
+ if (!firstVisibleStaff) {
61514
+ firstVisibleStaff = s;
61515
+ }
61516
+ lastVisibleStaffInGroup = s;
61517
+ }
61344
61518
  }
61519
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61520
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61521
+ if (!firstVisibleStaff) {
61522
+ firstVisibleStaff = firstVisibleStaffInGroup;
61523
+ }
61524
+ }
61525
+ if (!anyStaffVisible) {
61526
+ const group = this.staves[0];
61527
+ const firstStaff = group.staves[0];
61528
+ firstStaff.isVisible = true;
61529
+ group.firstVisibleStaff = firstStaff;
61530
+ group.lastVisibleStaff = firstStaff;
61531
+ firstVisibleStaff = firstStaff;
61345
61532
  }
61533
+ this.firstVisibleStaff = firstVisibleStaff;
61346
61534
  this._calculateAccoladeSpacing(tracks);
61347
61535
  this._updateWidthFromLastBar();
61348
61536
  return renderers;
@@ -61353,15 +61541,26 @@
61353
61541
  result.layoutingInfo = new BarLayoutingInfo();
61354
61542
  result.masterBar = tracks[0].score.masterBars[barIndex];
61355
61543
  this.masterBarsRenderers.push(result);
61544
+ let firstVisibleStaff = undefined;
61545
+ let anyStaffVisible = false;
61356
61546
  // add renderers
61357
61547
  const barLayoutingInfo = result.layoutingInfo;
61358
61548
  for (const g of this.staves) {
61549
+ let firstVisibleStaffInGroup = undefined;
61550
+ let lastVisibleStaffInGroup = undefined;
61359
61551
  for (const s of g.staves) {
61360
61552
  const bar = g.track.staves[s.modelStaff.index].bars[barIndex];
61361
61553
  const additionalMultiBarsRestBars = additionalMultiBarRestIndexes == null
61362
61554
  ? null
61363
61555
  : additionalMultiBarRestIndexes.map(b => g.track.staves[s.modelStaff.index].bars[b]);
61364
61556
  s.addBar(bar, barLayoutingInfo, additionalMultiBarsRestBars);
61557
+ if (s.isVisible) {
61558
+ anyStaffVisible = true;
61559
+ if (!firstVisibleStaffInGroup) {
61560
+ firstVisibleStaffInGroup = s;
61561
+ }
61562
+ lastVisibleStaffInGroup = s;
61563
+ }
61365
61564
  const renderer = s.barRenderers[s.barRenderers.length - 1];
61366
61565
  result.renderers.push(renderer);
61367
61566
  if (renderer.isLinkedToPrevious) {
@@ -61371,7 +61570,21 @@
61371
61570
  result.canWrap = false;
61372
61571
  }
61373
61572
  }
61573
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61574
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61575
+ if (!firstVisibleStaff) {
61576
+ firstVisibleStaff = firstVisibleStaffInGroup;
61577
+ }
61578
+ }
61579
+ if (!anyStaffVisible) {
61580
+ const group = this.staves[0];
61581
+ const firstStaff = group.staves[0];
61582
+ firstStaff.isVisible = true;
61583
+ group.firstVisibleStaff = firstStaff;
61584
+ group.lastVisibleStaff = firstStaff;
61585
+ firstVisibleStaff = firstStaff;
61374
61586
  }
61587
+ this.firstVisibleStaff = firstVisibleStaff;
61375
61588
  this._calculateAccoladeSpacing(tracks);
61376
61589
  barLayoutingInfo.finish();
61377
61590
  // ensure same widths of new renderer
@@ -61384,19 +61597,35 @@
61384
61597
  this.masterBarsRenderers.splice(this.masterBarsRenderers.length - 1, 1);
61385
61598
  let width = 0;
61386
61599
  let barDisplayScale = 0;
61387
- for (let i = 0, j = this.allStaves.length; i < j; i++) {
61388
- const s = this.allStaves[i];
61389
- const lastBar = s.revertLastBar();
61390
- const computedWidth = lastBar.computedWidth;
61391
- if (computedWidth > width) {
61392
- width = computedWidth;
61600
+ let firstVisibleStaff = undefined;
61601
+ for (const g of this.staves) {
61602
+ let firstVisibleStaffInGroup = undefined;
61603
+ let lastVisibleStaffInGroup = undefined;
61604
+ for (const s of g.staves) {
61605
+ const lastBar = s.revertLastBar();
61606
+ const computedWidth = lastBar.computedWidth;
61607
+ if (computedWidth > width) {
61608
+ width = computedWidth;
61609
+ }
61610
+ const newBarDisplayScale = lastBar.barDisplayScale;
61611
+ if (newBarDisplayScale > barDisplayScale) {
61612
+ barDisplayScale = newBarDisplayScale;
61613
+ }
61614
+ lastBar.afterReverted();
61615
+ if (s.isVisible) {
61616
+ if (!firstVisibleStaffInGroup) {
61617
+ firstVisibleStaffInGroup = s;
61618
+ }
61619
+ lastVisibleStaffInGroup = s;
61620
+ }
61393
61621
  }
61394
- const newBarDisplayScale = lastBar.barDisplayScale;
61395
- if (newBarDisplayScale > barDisplayScale) {
61396
- barDisplayScale = newBarDisplayScale;
61622
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61623
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61624
+ if (!firstVisibleStaff) {
61625
+ firstVisibleStaff = firstVisibleStaffInGroup;
61397
61626
  }
61398
- lastBar.afterReverted();
61399
61627
  }
61628
+ this.firstVisibleStaff = firstVisibleStaff;
61400
61629
  this.width -= width;
61401
61630
  this.computedWidth -= width;
61402
61631
  this.totalBarDisplayScale -= barDisplayScale;
@@ -61503,6 +61732,7 @@
61503
61732
  }
61504
61733
  let braceWidth = 0;
61505
61734
  for (const b of this._brackets) {
61735
+ b.updateCanPaint();
61506
61736
  b.finalizeBracket(settings.display.resources.engravingSettings);
61507
61737
  braceWidth = Math.max(braceWidth, b.width);
61508
61738
  }
@@ -61510,6 +61740,12 @@
61510
61740
  this.width += this.accoladeWidth;
61511
61741
  this.computedWidth += this.accoladeWidth;
61512
61742
  }
61743
+ else {
61744
+ for (const b of this._brackets) {
61745
+ b.updateCanPaint();
61746
+ b.finalizeBracket(settings.display.resources.engravingSettings);
61747
+ }
61748
+ }
61513
61749
  }
61514
61750
  _getStaffTrackGroup(track) {
61515
61751
  for (let i = 0, j = this.staves.length; i < j; i++) {
@@ -61539,12 +61775,12 @@
61539
61775
  break;
61540
61776
  case BracketExtendMode.GroupStaves:
61541
61777
  // when grouping staves, we create one bracket for the whole track across all staves
61542
- bracket = new SingleTrackSystemBracket(track);
61778
+ bracket = new SingleTrackSystemBracket(this, track);
61543
61779
  bracket.index = this._brackets.length;
61544
61780
  this._brackets.push(bracket);
61545
61781
  break;
61546
61782
  case BracketExtendMode.GroupSimilarInstruments:
61547
- bracket = new SimilarInstrumentSystemBracket(track);
61783
+ bracket = new SimilarInstrumentSystemBracket(this, track);
61548
61784
  bracket.index = this._brackets.length;
61549
61785
  this._brackets.push(bracket);
61550
61786
  break;
@@ -61591,8 +61827,10 @@
61591
61827
  }
61592
61828
  }
61593
61829
  paintPartial(cx, cy, canvas, startIndex, count) {
61594
- for (let i = 0, j = this.allStaves.length; i < j; i++) {
61595
- this.allStaves[i].paint(cx, cy, canvas, startIndex, count);
61830
+ for (const s of this.allStaves) {
61831
+ if (s.isVisible) {
61832
+ s.paint(cx, cy, canvas, startIndex, count);
61833
+ }
61596
61834
  }
61597
61835
  const res = this.layout.renderer.settings.display.resources;
61598
61836
  if (this.staves.length > 0 && startIndex === 0) {
@@ -61629,9 +61867,9 @@
61629
61867
  const oldBaseLine = canvas.textBaseline;
61630
61868
  const oldTextAlign = canvas.textAlign;
61631
61869
  for (const g of this.staves) {
61632
- if (g.staves.length > 0) {
61633
- const firstStart = cy + g.staves[0].contentTop;
61634
- const lastEnd = cy + g.staves[g.staves.length - 1].contentBottom;
61870
+ if (g.firstVisibleStaff) {
61871
+ const firstStart = cy + g.firstVisibleStaff.contentTop;
61872
+ const lastEnd = cy + g.lastVisibleStaff.contentBottom;
61635
61873
  let trackNameText = '';
61636
61874
  switch (trackNameMode) {
61637
61875
  case TrackNameMode.FullName:
@@ -61685,6 +61923,9 @@
61685
61923
  if (this.allStaves.length > 0 && needsSystemBarLine) {
61686
61924
  let previousStaffInBracket = null;
61687
61925
  for (const s of this.allStaves) {
61926
+ if (!s.isVisible) {
61927
+ continue;
61928
+ }
61688
61929
  if (previousStaffInBracket !== null) {
61689
61930
  const previousBottom = previousStaffInBracket.contentBottom;
61690
61931
  const thisTop = s.contentTop;
@@ -61711,17 +61952,17 @@
61711
61952
  const settings = this.layout.renderer.settings;
61712
61953
  for (const bracket of this._brackets) {
61713
61954
  if (bracket.canPaint) {
61714
- const barStartX = cx + bracket.firstStaffInBracket.x;
61955
+ const barStartX = cx + bracket.firstVisibleStaffInBracket.x;
61715
61956
  const barSize = bracket.width;
61716
61957
  const barOffset = settings.display.accoladeBarPaddingRight;
61717
- const firstStart = cy + bracket.firstStaffInBracket.contentTop;
61718
- const lastEnd = cy + bracket.lastStaffInBracket.contentBottom;
61958
+ const firstStart = cy + bracket.firstVisibleStaffInBracket.contentTop;
61959
+ const lastEnd = cy + bracket.lastVisibleStaffInBracket.contentBottom;
61719
61960
  let accoladeStart = firstStart;
61720
61961
  let accoladeEnd = lastEnd;
61721
61962
  if (bracket.drawAsBrace) {
61722
61963
  CanvasHelper.fillMusicFontSymbolSafe(canvas, barStartX - barOffset - barSize, accoladeEnd, bracket.braceScale, MusicFontSymbol.Brace);
61723
61964
  }
61724
- else if (bracket.firstStaffInBracket !== bracket.lastStaffInBracket) {
61965
+ else if (bracket.firstVisibleStaffInBracket !== bracket.lastVisibleStaffInBracket) {
61725
61966
  // brackets typically overflow by 1/4 staff-space
61726
61967
  const smuflMetrics = settings.display.resources.engravingSettings;
61727
61968
  const bracketOverflow = smuflMetrics.oneStaffSpace * 0.25;
@@ -61753,40 +61994,86 @@
61753
61994
  this.bottomPadding = Math.max(this.bottomPadding, neededHeight);
61754
61995
  this._hasSystemSeparator = true;
61755
61996
  }
61997
+ const anyStaffVisible = this._finalizeTrackGroups();
61998
+ // for now we always force one staff to be visible.
61999
+ // making also whole systems invisible needs separate attention (also on player cursor handling)
62000
+ if (!anyStaffVisible) {
62001
+ const group = this.staves[0];
62002
+ const firstStaff = group.staves[0];
62003
+ firstStaff.isVisible = true;
62004
+ this._finalizeTrackGroups(true);
62005
+ }
62006
+ for (const b of this._brackets) {
62007
+ b.finalizeBracket(settings.display.resources.engravingSettings);
62008
+ }
62009
+ }
62010
+ _finalizeTrackGroups(onlyFirstGroup = false) {
61756
62011
  let currentY = 0;
62012
+ const settings = this.layout.renderer.settings;
61757
62013
  const smufl = settings.display.resources.engravingSettings;
61758
62014
  const topBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketTop);
61759
62015
  const bottomBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketBottom);
61760
62016
  let previousStaff = undefined;
61761
- for (const staff of this.allStaves) {
61762
- // check if we need "in-between padding"
61763
- if (previousStaff !== undefined && previousStaff.trackIndex !== staff.trackIndex) {
61764
- currentY += settings.display.trackStaffPaddingBetween;
61765
- }
61766
- const bracket = this._staffToBracket.has(staff) ? this._staffToBracket.get(staff) : undefined;
61767
- const hasBracket = bracket && !bracket.drawAsBrace && bracket.canPaint;
61768
- if (hasBracket && bracket.firstStaffInBracket === staff) {
61769
- const spikeOverflow = topBracketSpikeHeight - staff.topOverflow;
61770
- if (spikeOverflow > 0) {
61771
- currentY += spikeOverflow;
62017
+ let endSpikeOverflow = 0;
62018
+ let anyStaffVisible = false;
62019
+ for (const group of this.staves) {
62020
+ let firstVisibleStaffInGroup = undefined;
62021
+ let lastVisibleStaffInGroup = undefined;
62022
+ for (const staff of group.staves) {
62023
+ // check if we need "in-between padding"
62024
+ if (previousStaff !== undefined && previousStaff.trackIndex !== staff.trackIndex) {
62025
+ currentY += settings.display.trackStaffPaddingBetween;
62026
+ }
62027
+ const bracket = this._staffToBracket.has(staff) ? this._staffToBracket.get(staff) : undefined;
62028
+ const hasBracket = bracket && !bracket.drawAsBrace && bracket.canPaint;
62029
+ if (hasBracket && bracket.firstStaffInBracket === staff) {
62030
+ const spikeOverflow = topBracketSpikeHeight - staff.topOverflow;
62031
+ if (spikeOverflow > 0) {
62032
+ currentY += spikeOverflow;
62033
+ }
62034
+ }
62035
+ staff.x = this.accoladeWidth;
62036
+ staff.y = currentY;
62037
+ if (!onlyFirstGroup) {
62038
+ staff.finalizeStaff();
62039
+ }
62040
+ if (staff.isVisible) {
62041
+ currentY += staff.height;
62042
+ anyStaffVisible = true;
62043
+ previousStaff = staff;
62044
+ if (!firstVisibleStaffInGroup) {
62045
+ firstVisibleStaffInGroup = staff;
62046
+ }
62047
+ lastVisibleStaffInGroup = staff;
62048
+ }
62049
+ endSpikeOverflow = 0;
62050
+ if (hasBracket && bracket.lastStaffInBracket === staff) {
62051
+ const spikeOverflow = bottomBracketSpikeHeight - staff.bottomOverflow;
62052
+ if (spikeOverflow > 0) {
62053
+ if (staff.isVisible) {
62054
+ currentY += spikeOverflow;
62055
+ }
62056
+ else {
62057
+ endSpikeOverflow = spikeOverflow;
62058
+ }
62059
+ }
61772
62060
  }
61773
62061
  }
61774
- staff.x = this.accoladeWidth;
61775
- staff.y = currentY;
61776
- staff.finalizeStaff();
61777
- currentY += staff.height;
61778
- if (hasBracket && bracket.lastStaffInBracket === staff) {
61779
- const spikeOverflow = bottomBracketSpikeHeight - staff.bottomOverflow;
61780
- if (spikeOverflow > 0) {
61781
- currentY += spikeOverflow;
61782
- }
62062
+ group.firstVisibleStaff = firstVisibleStaffInGroup;
62063
+ group.lastVisibleStaff = lastVisibleStaffInGroup;
62064
+ if (!this.firstVisibleStaff) {
62065
+ this.firstVisibleStaff = firstVisibleStaffInGroup;
62066
+ }
62067
+ if (onlyFirstGroup) {
62068
+ break;
61783
62069
  }
61784
- previousStaff = staff;
61785
62070
  }
61786
- this._contentHeight = currentY;
61787
- for (const b of this._brackets) {
61788
- b.finalizeBracket(smufl);
62071
+ // ensure we add overflow if last bracket is hidden
62072
+ if (endSpikeOverflow) {
62073
+ currentY += endSpikeOverflow;
61789
62074
  }
62075
+ this._contentHeight = currentY;
62076
+ return anyStaffVisible;
61790
62077
  }
61791
62078
  buildBoundingsLookup(cx, cy) {
61792
62079
  if (this.layout.renderer.boundsLookup.isFinished) {
@@ -61820,6 +62107,9 @@
61820
62107
  const masterBarBoundsLookup = new Map();
61821
62108
  for (let i = 0; i < this.staves.length; i++) {
61822
62109
  for (const staff of this.staves[i].staves) {
62110
+ if (!staff.isVisible) {
62111
+ continue;
62112
+ }
61823
62113
  for (const renderer of staff.barRenderers) {
61824
62114
  let masterBarBounds;
61825
62115
  if (!masterBarBoundsLookup.has(renderer.bar.masterBar.index)) {
@@ -62130,8 +62420,9 @@
62130
62420
  }
62131
62421
  firstBarIndex = 0;
62132
62422
  lastBarIndex = 0;
62133
- createEmptyStaffSystem() {
62423
+ createEmptyStaffSystem(index) {
62134
62424
  const system = new StaffSystem(this);
62425
+ system.index = index;
62135
62426
  const allFactories = Environment.defaultRenderers;
62136
62427
  const renderStaves = [];
62137
62428
  for (let trackIndex = 0; trackIndex < this.renderer.tracks.length; trackIndex++) {
@@ -62143,7 +62434,7 @@
62143
62434
  let previousStaff = undefined;
62144
62435
  for (const factory of allFactories) {
62145
62436
  if (this.profile.has(factory.staffId) && factory.canCreate(track, staff)) {
62146
- const renderStaff = new RenderStaff(trackIndex, staff, factory);
62437
+ const renderStaff = new RenderStaff(system, trackIndex, staff, factory);
62147
62438
  // insert shared effect bands at front
62148
62439
  renderStaff.topEffectInfos.splice(0, 0, ...sharedTopEffects);
62149
62440
  renderStaff.bottomEffectInfos.push(...sharedBottomEffects);
@@ -65301,7 +65592,7 @@
65301
65592
  }
65302
65593
  endBarIndex = startIndex + endBarIndex - 1; // map count to array index
65303
65594
  endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex));
65304
- this._system = this.createEmptyStaffSystem();
65595
+ this._system = this.createEmptyStaffSystem(0);
65305
65596
  this._system.isLast = true;
65306
65597
  this._system.x = this.pagePadding[0];
65307
65598
  this._system.y = this.pagePadding[1];
@@ -65574,8 +65865,7 @@
65574
65865
  this._systems = [];
65575
65866
  let currentIndex = 0;
65576
65867
  const maxWidth = this._maxWidth;
65577
- let system = this.createEmptyStaffSystem();
65578
- system.index = this._systems.length;
65868
+ let system = this.createEmptyStaffSystem(this._systems.length);
65579
65869
  system.x = this.pagePadding[0];
65580
65870
  system.y = y;
65581
65871
  while (currentIndex < this._allMasterBarRenderers.length) {
@@ -65606,8 +65896,7 @@
65606
65896
  this._fitSystem(system);
65607
65897
  y += this._paintSystem(system, oldHeight);
65608
65898
  // note: we do not increase currentIndex here to have it added to the next system
65609
- system = this.createEmptyStaffSystem();
65610
- system.index = this._systems.length;
65899
+ system = this.createEmptyStaffSystem(this._systems.length);
65611
65900
  system.x = this.pagePadding[0];
65612
65901
  system.y = y;
65613
65902
  }
@@ -65692,8 +65981,7 @@
65692
65981
  return barsPerRow;
65693
65982
  }
65694
65983
  _createStaffSystem(currentBarIndex, endIndex) {
65695
- const system = this.createEmptyStaffSystem();
65696
- system.index = this._systems.length;
65984
+ const system = this.createEmptyStaffSystem(this._systems.length);
65697
65985
  const barsPerRow = this._getBarsPerSystem(system.index);
65698
65986
  const maxWidth = this._maxWidth;
65699
65987
  const end = endIndex + 1;
@@ -68373,16 +68661,77 @@
68373
68661
  class ClefGlyph extends MusicFontGlyph {
68374
68662
  _clef;
68375
68663
  _clefOttava;
68664
+ _ottavaGlyph;
68376
68665
  constructor(x, y, clef, clefOttava) {
68377
68666
  super(x, y, 1, ClefGlyph._getSymbol(clef, clefOttava));
68378
68667
  this._clef = clef;
68379
68668
  this._clefOttava = clefOttava;
68380
68669
  }
68670
+ getBoundingBoxTop() {
68671
+ let top = super.getBoundingBoxTop();
68672
+ const ottava = this._ottavaGlyph;
68673
+ if (ottava) {
68674
+ const ottavaTop = this.y + ottava.getBoundingBoxTop();
68675
+ top = ModelUtils.minBoundingBox(top, ottavaTop);
68676
+ }
68677
+ return top;
68678
+ }
68679
+ getBoundingBoxBottom() {
68680
+ let bottom = super.getBoundingBoxBottom();
68681
+ const ottava = this._ottavaGlyph;
68682
+ if (ottava) {
68683
+ const ottavaBottom = this.y + ottava.getBoundingBoxBottom();
68684
+ bottom = ModelUtils.maxBoundingBox(bottom, ottavaBottom);
68685
+ }
68686
+ return bottom;
68687
+ }
68381
68688
  doLayout() {
68382
68689
  this.center = true;
68383
68690
  super.doLayout();
68384
68691
  this.width = this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.GClef);
68385
68692
  this.offsetX = this.width / 2;
68693
+ this._ottavaGlyph = undefined;
68694
+ switch (this._clef) {
68695
+ case Clef.C3:
68696
+ case Clef.C4:
68697
+ switch (this._clefOttava) {
68698
+ case Ottavia._8vb:
68699
+ return;
68700
+ }
68701
+ break;
68702
+ case Clef.F4:
68703
+ case Clef.G2:
68704
+ return;
68705
+ }
68706
+ let ottavaSymbol;
68707
+ let top = false;
68708
+ switch (this._clefOttava) {
68709
+ case Ottavia._15ma:
68710
+ ottavaSymbol = MusicFontSymbol.Clef15;
68711
+ top = true;
68712
+ break;
68713
+ case Ottavia._8va:
68714
+ ottavaSymbol = MusicFontSymbol.Clef8;
68715
+ top = true;
68716
+ break;
68717
+ case Ottavia._8vb:
68718
+ ottavaSymbol = MusicFontSymbol.Clef8;
68719
+ break;
68720
+ case Ottavia._15mb:
68721
+ ottavaSymbol = MusicFontSymbol.Clef15;
68722
+ break;
68723
+ default:
68724
+ return;
68725
+ }
68726
+ const ottavaX = this.width / 2;
68727
+ const ottavaY = top
68728
+ ? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
68729
+ : this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
68730
+ this.renderer.smuflMetrics.glyphHeights.get(ottavaSymbol);
68731
+ this._ottavaGlyph = new MusicFontGlyph(ottavaX, -ottavaY, 1, ottavaSymbol);
68732
+ this._ottavaGlyph.center = true;
68733
+ this._ottavaGlyph.renderer = this.renderer;
68734
+ this._ottavaGlyph.doLayout();
68386
68735
  }
68387
68736
  static _getSymbol(clef, clefOttava) {
68388
68737
  switch (clef) {
@@ -68430,44 +68779,10 @@
68430
68779
  const _ = ElementStyleHelper.bar(canvas, BarSubElement.StandardNotationClef, this.renderer.bar);
68431
68780
  try {
68432
68781
  super.paint(cx, cy, canvas);
68433
- switch (this._clef) {
68434
- case Clef.C3:
68435
- case Clef.C4:
68436
- switch (this._clefOttava) {
68437
- case Ottavia._8vb:
68438
- return;
68439
- }
68440
- break;
68441
- case Clef.F4:
68442
- case Clef.G2:
68443
- return;
68444
- }
68445
- let ottavaGlyph;
68446
- let top = false;
68447
- switch (this._clefOttava) {
68448
- case Ottavia._15ma:
68449
- ottavaGlyph = MusicFontSymbol.Clef15;
68450
- top = true;
68451
- break;
68452
- case Ottavia._8va:
68453
- ottavaGlyph = MusicFontSymbol.Clef8;
68454
- top = true;
68455
- break;
68456
- case Ottavia._8vb:
68457
- ottavaGlyph = MusicFontSymbol.Clef8;
68458
- break;
68459
- case Ottavia._15mb:
68460
- ottavaGlyph = MusicFontSymbol.Clef15;
68461
- break;
68462
- default:
68463
- return;
68782
+ const ottava = this._ottavaGlyph;
68783
+ if (ottava) {
68784
+ ottava.paint(cx + this.x, cy + this.y, canvas);
68464
68785
  }
68465
- const ottavaX = this.width / 2;
68466
- const ottavaY = top
68467
- ? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
68468
- : this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
68469
- this.renderer.smuflMetrics.glyphHeights.get(ottavaGlyph);
68470
- CanvasHelper.fillMusicFontSymbolSafe(canvas, cx + this.x + ottavaX, cy + this.y - ottavaY, 1, ottavaGlyph, true);
68471
68786
  }
68472
68787
  finally {
68473
68788
  _?.[Symbol.dispose]?.();