@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.
@@ -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
  *
@@ -203,9 +203,9 @@ class AlphaTabError extends Error {
203
203
  * @internal
204
204
  */
205
205
  class VersionInfo {
206
- static version = '1.8.0-alpha.1647';
207
- static date = '2025-12-17T02:10:13.730Z';
208
- static commit = 'a1bd11d4a3eef22409f1f49a5dd172ace16108cc';
206
+ static version = '1.8.0-alpha.1650';
207
+ static date = '2025-12-20T02:07:19.755Z';
208
+ static commit = '5789b59da2b7f8897debe003c9b75e4d48b5e9b3';
209
209
  static print(print) {
210
210
  print(`alphaTab ${VersionInfo.version}`);
211
211
  print(`commit: ${VersionInfo.commit}`);
@@ -2744,6 +2744,23 @@ class RenderStylesheet {
2744
2744
  * Whether barlines should be drawn across staves within the same system.
2745
2745
  */
2746
2746
  extendBarLines = false;
2747
+ /**
2748
+ * Whether to hide empty staves.
2749
+ */
2750
+ hideEmptyStaves = false;
2751
+ /**
2752
+ * Whether to also hide empty staves in the first system.
2753
+ * @remarks
2754
+ * Only has an effect when activating {@link hideEmptyStaves}.
2755
+ */
2756
+ hideEmptyStavesInFirstSystem = false;
2757
+ /**
2758
+ * Whether to show brackets and braces across single staves.
2759
+ * @remarks
2760
+ * This allows a more consistent view for identifying staves when using
2761
+ * {@link hideEmptyStaves}
2762
+ */
2763
+ showSingleStaffBrackets = false;
2747
2764
  }
2748
2765
 
2749
2766
  /**
@@ -8673,7 +8690,10 @@ class AlphaTex1LanguageDefinitions {
8673
8690
  ['firstsystemtracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
8674
8691
  ['othersystemstracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
8675
8692
  ['extendbarlines', null],
8676
- ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]]
8693
+ ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]],
8694
+ ['hideemptystaves', null],
8695
+ ['hideemptystavesinfirstsystem', null],
8696
+ ['showsinglestaffbrackets', null]
8677
8697
  ]);
8678
8698
  static staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([
8679
8699
  ['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]],
@@ -9013,6 +9033,9 @@ class AlphaTex1LanguageDefinitions {
9013
9033
  ['othersystemstracknameorientation', null],
9014
9034
  ['extendbarlines', null],
9015
9035
  ['chorddiagramsinscore', null],
9036
+ ['hideemptystaves', null],
9037
+ ['hideemptystavesinfirstsystem', null],
9038
+ ['showsinglestaffbrackets', null],
9016
9039
  [
9017
9040
  'tuning',
9018
9041
  [
@@ -13887,6 +13910,15 @@ class AlphaTex1LanguageHandler {
13887
13910
  ? AlphaTex1LanguageHandler._booleanLikeValue(metaData.arguments.arguments, 0)
13888
13911
  : true;
13889
13912
  return ApplyNodeResult.Applied;
13913
+ case 'hideemptystaves':
13914
+ score.stylesheet.hideEmptyStaves = true;
13915
+ return ApplyNodeResult.Applied;
13916
+ case 'hideemptystavesinfirstsystem':
13917
+ score.stylesheet.hideEmptyStavesInFirstSystem = true;
13918
+ return ApplyNodeResult.Applied;
13919
+ case 'showsinglestaffbrackets':
13920
+ score.stylesheet.showSingleStaffBrackets = true;
13921
+ return ApplyNodeResult.Applied;
13890
13922
  default:
13891
13923
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
13892
13924
  }
@@ -15718,6 +15750,15 @@ class AlphaTex1LanguageHandler {
15718
15750
  if (stylesheet.globalDisplayChordDiagramsInScore) {
15719
15751
  nodes.push(Atnf.meta('chordDiagramsInScore'));
15720
15752
  }
15753
+ if (stylesheet.hideEmptyStaves) {
15754
+ nodes.push(Atnf.meta('hideEmptyStaves'));
15755
+ }
15756
+ if (stylesheet.hideEmptyStavesInFirstSystem) {
15757
+ nodes.push(Atnf.meta('hideEmptyStavesInFirstSystem'));
15758
+ }
15759
+ if (stylesheet.showSingleStaffBrackets) {
15760
+ nodes.push(Atnf.meta('showSingleStaffBrackets'));
15761
+ }
15721
15762
  // Unsupported:
15722
15763
  // 'globaldisplaychorddiagramsontop',
15723
15764
  // 'pertrackchorddiagramsontop',
@@ -38804,6 +38845,9 @@ class RenderStylesheetSerializer {
38804
38845
  }
38805
38846
  }
38806
38847
  o.set("extendbarlines", obj.extendBarLines);
38848
+ o.set("hideemptystaves", obj.hideEmptyStaves);
38849
+ o.set("hideemptystavesinfirstsystem", obj.hideEmptyStavesInFirstSystem);
38850
+ o.set("showsinglestaffbrackets", obj.showSingleStaffBrackets);
38807
38851
  return o;
38808
38852
  }
38809
38853
  static setProperty(obj, property, v) {
@@ -38865,6 +38909,15 @@ class RenderStylesheetSerializer {
38865
38909
  case "extendbarlines":
38866
38910
  obj.extendBarLines = v;
38867
38911
  return true;
38912
+ case "hideemptystaves":
38913
+ obj.hideEmptyStaves = v;
38914
+ return true;
38915
+ case "hideemptystavesinfirstsystem":
38916
+ obj.hideEmptyStavesInFirstSystem = v;
38917
+ return true;
38918
+ case "showsinglestaffbrackets":
38919
+ obj.showSingleStaffBrackets = v;
38920
+ return true;
38868
38921
  }
38869
38922
  return false;
38870
38923
  }
@@ -45892,6 +45945,19 @@ class BeatTickLookup {
45892
45945
  }
45893
45946
  return null;
45894
45947
  }
45948
+ /**
45949
+ * Looks for the first visible beat which starts at this lookup so it can be used for cursor placement.
45950
+ * @param checker The custom checker to see if a beat is visible.
45951
+ * @returns The first beat which is visible according to the given tracks or null.
45952
+ */
45953
+ getVisibleBeatAtStartWithChecker(checker) {
45954
+ for (const b of this.highlightedBeats) {
45955
+ if (b.playbackStart === this.start && checker.isVisible(b.beat)) {
45956
+ return b.beat;
45957
+ }
45958
+ }
45959
+ return null;
45960
+ }
45895
45961
  }
45896
45962
 
45897
45963
  /**
@@ -46331,6 +46397,18 @@ class MidiTickLookupFindBeatResult {
46331
46397
  }
46332
46398
  }
46333
46399
  }
46400
+ /**
46401
+ * @internal
46402
+ */
46403
+ class TrackLookupBeatVisibilityChecker {
46404
+ _lookup;
46405
+ constructor(lookup) {
46406
+ this._lookup = lookup;
46407
+ }
46408
+ isVisible(beat) {
46409
+ return this._lookup.has(beat.voice.bar.staff.track.index);
46410
+ }
46411
+ }
46334
46412
  /**
46335
46413
  * This class holds all information about when {@link MasterBar}s and {@link Beat}s are played.
46336
46414
  *
@@ -46391,31 +46469,44 @@ class MidiTickLookup {
46391
46469
  * @returns The information about the current beat or null if no beat could be found.
46392
46470
  */
46393
46471
  findBeat(trackLookup, tick, currentBeatHint = null) {
46472
+ return this.findBeatWithChecker(new TrackLookupBeatVisibilityChecker(trackLookup), tick, currentBeatHint);
46473
+ }
46474
+ /**
46475
+ * Finds the currently played beat given a list of tracks and the current time.
46476
+ * @param checker The checker to ask whether a beat is visible and should be considered for result.
46477
+ * @param tick The current time in midi ticks.
46478
+ * @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).
46479
+ * @returns The information about the current beat or null if no beat could be found.
46480
+ */
46481
+ findBeatWithChecker(checker, tick, currentBeatHint = null) {
46394
46482
  let result = null;
46395
46483
  if (currentBeatHint) {
46396
- result = this._findBeatFast(trackLookup, currentBeatHint, tick);
46484
+ result = this._findBeatFast(checker, currentBeatHint, tick);
46397
46485
  }
46398
46486
  if (!result) {
46399
- result = this._findBeatSlow(trackLookup, currentBeatHint, tick, false);
46487
+ result = this._findBeatSlow(checker, currentBeatHint, tick, false);
46400
46488
  }
46401
46489
  return result;
46402
46490
  }
46403
- _findBeatFast(trackLookup, currentBeatHint, tick) {
46491
+ _findBeatFast(checker, currentBeatHint, tick) {
46404
46492
  // still within current lookup.
46405
46493
  if (tick >= currentBeatHint.start && tick < currentBeatHint.end) {
46406
46494
  return currentBeatHint;
46407
46495
  }
46408
46496
  // already on the next beat?
46409
- if (currentBeatHint.nextBeat && tick >= currentBeatHint.nextBeat.start && tick < currentBeatHint.nextBeat.end) {
46497
+ if (currentBeatHint.nextBeat &&
46498
+ tick >= currentBeatHint.nextBeat.start &&
46499
+ tick < currentBeatHint.nextBeat.end &&
46500
+ (checker === undefined || checker.isVisible(currentBeatHint.nextBeat.beat))) {
46410
46501
  const next = currentBeatHint.nextBeat;
46411
46502
  // fill next in chain
46412
- this._fillNextBeat(next, trackLookup);
46503
+ this._fillNextBeat(next, checker);
46413
46504
  return next;
46414
46505
  }
46415
46506
  // likely a loop or manual seek, need to fallback to slow path
46416
46507
  return null;
46417
46508
  }
46418
- _fillNextBeatMultiBarRest(current, trackLookup) {
46509
+ _fillNextBeatMultiBarRest(current, checker) {
46419
46510
  const group = this.multiBarRestInfo.get(current.masterBar.masterBar.index);
46420
46511
  // this is a bit sensitive. we assume that the sequence of multi-rest bars and the
46421
46512
  // chained nextMasterBar equal. so we just jump over X bars
@@ -46429,7 +46520,7 @@ class MidiTickLookup {
46429
46520
  if (endMasterBar) {
46430
46521
  // one more following -> use start of next
46431
46522
  if (endMasterBar.nextMasterBar) {
46432
- current.nextBeat = this._firstBeatInMasterBar(trackLookup, endMasterBar.nextMasterBar, endMasterBar.nextMasterBar.start, true);
46523
+ current.nextBeat = this._firstBeatInMasterBar(checker, endMasterBar.nextMasterBar, endMasterBar.nextMasterBar.start, true);
46433
46524
  // if we have the next beat take the difference between the times as duration
46434
46525
  if (current.nextBeat) {
46435
46526
  current.tickDuration = current.nextBeat.start - current.start;
@@ -46459,19 +46550,19 @@ class MidiTickLookup {
46459
46550
  }
46460
46551
  current.calculateDuration();
46461
46552
  }
46462
- _fillNextBeat(current, trackLookup) {
46553
+ _fillNextBeat(current, checker) {
46463
46554
  // on multibar rests take the duration until the end.
46464
46555
  if (this._isMultiBarRestResult(current)) {
46465
- this._fillNextBeatMultiBarRest(current, trackLookup);
46556
+ this._fillNextBeatMultiBarRest(current, checker);
46466
46557
  }
46467
46558
  else {
46468
- this._fillNextBeatDefault(current, trackLookup);
46559
+ this._fillNextBeatDefault(current, checker);
46469
46560
  }
46470
46561
  }
46471
- _fillNextBeatDefault(current, trackLookup) {
46472
- current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end, trackLookup, true);
46562
+ _fillNextBeatDefault(current, checker) {
46563
+ current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end, checker, true);
46473
46564
  if (current.nextBeat == null) {
46474
- current.nextBeat = this._findBeatSlow(trackLookup, current, current.end, true);
46565
+ current.nextBeat = this._findBeatSlow(checker, current, current.end, true);
46475
46566
  }
46476
46567
  // if we have the next beat take the difference between the times as duration
46477
46568
  if (current.nextBeat) {
@@ -46502,7 +46593,7 @@ class MidiTickLookup {
46502
46593
  beat.isRest &&
46503
46594
  beat.voice.bar.isRestOnly);
46504
46595
  }
46505
- _findBeatSlow(trackLookup, currentBeatHint, tick, isNextSearch) {
46596
+ _findBeatSlow(checker, currentBeatHint, tick, isNextSearch) {
46506
46597
  // get all beats within the masterbar
46507
46598
  let masterBar = null;
46508
46599
  if (currentBeatHint != null) {
@@ -46524,14 +46615,14 @@ class MidiTickLookup {
46524
46615
  if (!masterBar) {
46525
46616
  return null;
46526
46617
  }
46527
- return this._firstBeatInMasterBar(trackLookup, masterBar, tick, isNextSearch);
46618
+ return this._firstBeatInMasterBar(checker, masterBar, tick, isNextSearch);
46528
46619
  }
46529
- _firstBeatInMasterBar(trackLookup, startMasterBar, tick, isNextSearch) {
46620
+ _firstBeatInMasterBar(checker, startMasterBar, tick, isNextSearch) {
46530
46621
  let masterBar = startMasterBar;
46531
46622
  // scan through beats and find first one which has a beat visible
46532
46623
  while (masterBar) {
46533
46624
  if (masterBar.firstBeat) {
46534
- const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick, trackLookup, isNextSearch);
46625
+ const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick, checker, isNextSearch);
46535
46626
  if (beat) {
46536
46627
  return beat;
46537
46628
  }
@@ -46549,7 +46640,7 @@ class MidiTickLookup {
46549
46640
  * @param isNextSearch
46550
46641
  * @returns
46551
46642
  */
46552
- _findBeatInMasterBar(masterBar, currentStartLookup, tick, visibleTracks, isNextSearch) {
46643
+ _findBeatInMasterBar(masterBar, currentStartLookup, tick, checker, isNextSearch) {
46553
46644
  if (!currentStartLookup) {
46554
46645
  return null;
46555
46646
  }
@@ -46563,7 +46654,7 @@ class MidiTickLookup {
46563
46654
  (currentStartLookup.start <= relativeTick || (isNextSearch && relativeTick < 0)) &&
46564
46655
  relativeTick < currentStartLookup.end) {
46565
46656
  startBeatLookup = currentStartLookup;
46566
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46657
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46567
46658
  // found the matching beat lookup but none of the beats are visible
46568
46659
  // in this case scan further to the next lookup which has any visible beat
46569
46660
  if (!startBeat) {
@@ -46571,7 +46662,7 @@ class MidiTickLookup {
46571
46662
  let currentMasterBar = masterBar;
46572
46663
  while (currentMasterBar != null && startBeat == null) {
46573
46664
  while (currentStartLookup != null) {
46574
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46665
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46575
46666
  if (startBeat) {
46576
46667
  startBeatLookup = currentStartLookup;
46577
46668
  masterBar = currentMasterBar;
@@ -46589,7 +46680,7 @@ class MidiTickLookup {
46589
46680
  let currentMasterBar = masterBar;
46590
46681
  while (currentMasterBar != null && startBeat == null) {
46591
46682
  while (currentStartLookup != null) {
46592
- startBeat = currentStartLookup.getVisibleBeatAtStart(visibleTracks);
46683
+ startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
46593
46684
  if (startBeat) {
46594
46685
  startBeatLookup = currentStartLookup;
46595
46686
  masterBar = currentMasterBar;
@@ -46613,17 +46704,17 @@ class MidiTickLookup {
46613
46704
  if (startBeat == null) {
46614
46705
  return null;
46615
46706
  }
46616
- const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch, visibleTracks);
46707
+ const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch, checker);
46617
46708
  return result;
46618
46709
  }
46619
- _createResult(masterBar, beatLookup, beat, isNextSearch, visibleTracks) {
46710
+ _createResult(masterBar, beatLookup, beat, isNextSearch, checker) {
46620
46711
  const result = new MidiTickLookupFindBeatResult(masterBar);
46621
46712
  result.beat = beat;
46622
46713
  result.beatLookup = beatLookup;
46623
46714
  result.tickDuration = beatLookup.end - beatLookup.start;
46624
46715
  if (!isNextSearch) {
46625
46716
  // the next beat filling will adjust this result with the respective durations
46626
- this._fillNextBeat(result, visibleTracks);
46717
+ this._fillNextBeat(result, checker);
46627
46718
  }
46628
46719
  else if (this._isMultiBarRestResult(result)) {
46629
46720
  const multiRest = this.multiBarRestInfo.get(masterBar.masterBar.index);
@@ -49550,6 +49641,19 @@ class ExternalMediaPlayer extends BackingTrackPlayer {
49550
49641
  }
49551
49642
  }
49552
49643
 
49644
+ /**
49645
+ * @internal
49646
+ */
49647
+ class BoundsLookupVisibilityChecker {
49648
+ bounds = null;
49649
+ isVisible(beat) {
49650
+ const bounds = this.bounds;
49651
+ if (!bounds) {
49652
+ return false;
49653
+ }
49654
+ return bounds.findBeat(beat) !== null;
49655
+ }
49656
+ }
49553
49657
  /**
49554
49658
  * This class represents the public API of alphaTab and provides all logic to display
49555
49659
  * a music sheet in any UI using the given {@link IUiFacade}
@@ -49560,6 +49664,7 @@ class AlphaTabApiBase {
49560
49664
  _startTime = 0;
49561
49665
  _trackIndexes = null;
49562
49666
  _trackIndexLookup = null;
49667
+ _beatVisibilityChecker = new BoundsLookupVisibilityChecker();
49563
49668
  _isDestroyed = false;
49564
49669
  _score = null;
49565
49670
  _tracks = [];
@@ -51356,12 +51461,9 @@ class AlphaTabApiBase {
51356
51461
  this._previousTick = tick;
51357
51462
  const cache = this._tickCache;
51358
51463
  if (cache) {
51359
- const tracks = this._trackIndexLookup;
51360
- if (tracks != null && tracks.size > 0) {
51361
- const beat = cache.findBeat(tracks, tick, this._currentBeat);
51362
- if (beat) {
51363
- this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
51364
- }
51464
+ const beat = cache.findBeatWithChecker(this._beatVisibilityChecker, tick, this._currentBeat);
51465
+ if (beat) {
51466
+ this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
51365
51467
  }
51366
51468
  }
51367
51469
  }
@@ -52563,6 +52665,7 @@ class AlphaTabApiBase {
52563
52665
  if (this._isDestroyed) {
52564
52666
  return;
52565
52667
  }
52668
+ this._beatVisibilityChecker.bounds = this.boundsLookup;
52566
52669
  this._currentBeat = null;
52567
52670
  this._cursorUpdateTick(this._previousTick, false, 1, true, true);
52568
52671
  this.postRenderFinished.trigger();
@@ -60570,8 +60673,10 @@ class RenderStaff {
60570
60673
  height = 0;
60571
60674
  index = 0;
60572
60675
  staffIndex = 0;
60676
+ isVisible = false;
60677
+ _emptyBarCount = 0;
60573
60678
  get isFirstInSystem() {
60574
- return this.index === 0;
60679
+ return this.system.firstVisibleStaff === this;
60575
60680
  }
60576
60681
  topEffectInfos = [];
60577
60682
  bottomEffectInfos = [];
@@ -60606,10 +60711,11 @@ class RenderStaff {
60606
60711
  get contentBottom() {
60607
60712
  return this.y + this.topPadding + this.topOverflow + this.staffBottom;
60608
60713
  }
60609
- constructor(trackIndex, staff, factory) {
60714
+ constructor(system, trackIndex, staff, factory) {
60610
60715
  this._factory = factory;
60611
60716
  this.trackIndex = trackIndex;
60612
60717
  this.modelStaff = staff;
60718
+ this.system = system;
60613
60719
  for (const b of factory.effectBands) {
60614
60720
  if (b.shouldCreate && !b.shouldCreate(staff)) {
60615
60721
  continue;
@@ -60625,6 +60731,7 @@ class RenderStaff {
60625
60731
  break;
60626
60732
  }
60627
60733
  }
60734
+ this._updateVisibility();
60628
60735
  }
60629
60736
  getSharedLayoutData(key, def) {
60630
60737
  if (this._sharedLayoutData.has(key)) {
@@ -60651,6 +60758,20 @@ class RenderStaff {
60651
60758
  renderer.reLayout();
60652
60759
  this.barRenderers.push(renderer);
60653
60760
  this.system.layout.registerBarRenderer(this.staffId, renderer);
60761
+ if (renderer.bar.isEmpty || renderer.bar.isRestOnly) {
60762
+ this._emptyBarCount++;
60763
+ }
60764
+ this._updateVisibility();
60765
+ }
60766
+ _updateVisibility() {
60767
+ const stylesheet = this.modelStaff.track.score.stylesheet;
60768
+ const canHideEmptyStaves = stylesheet.hideEmptyStaves && (stylesheet.hideEmptyStavesInFirstSystem || this.system.index > 0);
60769
+ if (canHideEmptyStaves) {
60770
+ this.isVisible = this._emptyBarCount < this.barRenderers.length;
60771
+ }
60772
+ else {
60773
+ this.isVisible = true;
60774
+ }
60654
60775
  }
60655
60776
  addBar(bar, layoutingInfo, additionalMultiBarsRestBars) {
60656
60777
  const renderer = this._factory.create(this.system.layout.renderer, bar);
@@ -60670,6 +60791,10 @@ class RenderStaff {
60670
60791
  }
60671
60792
  this.barRenderers.push(renderer);
60672
60793
  this.system.layout.registerBarRenderer(this.staffId, renderer);
60794
+ if (bar.isEmpty || bar.isRestOnly) {
60795
+ this._emptyBarCount++;
60796
+ }
60797
+ this._updateVisibility();
60673
60798
  }
60674
60799
  revertLastBar() {
60675
60800
  this._sharedLayoutData = new Map();
@@ -60680,6 +60805,10 @@ class RenderStaff {
60680
60805
  for (const r of this.barRenderers) {
60681
60806
  r.afterStaffBarReverted();
60682
60807
  }
60808
+ if (lastBar.bar.isEmpty || lastBar.bar.isRestOnly) {
60809
+ this._emptyBarCount--;
60810
+ }
60811
+ this._updateVisibility();
60683
60812
  return lastBar;
60684
60813
  }
60685
60814
  scaleToWidth(width) {
@@ -60790,6 +60919,7 @@ class RenderStaff {
60790
60919
  this.height += this.topPadding + topOverflow + this.bottomOverflow + this.bottomPadding;
60791
60920
  }
60792
60921
  this.height = Math.ceil(this.height);
60922
+ this._updateVisibility();
60793
60923
  }
60794
60924
  paint(cx, cy, canvas, startIndex, count) {
60795
60925
  if (this.height === 0 || count === 0) {
@@ -61190,6 +61320,8 @@ class StaffTrackGroup {
61190
61320
  track;
61191
61321
  staffSystem;
61192
61322
  staves = [];
61323
+ firstVisibleStaff;
61324
+ lastVisibleStaff;
61193
61325
  bracket = null;
61194
61326
  constructor(staffSystem, track) {
61195
61327
  this.staffSystem = staffSystem;
@@ -61204,18 +61336,47 @@ class StaffTrackGroup {
61204
61336
  * @internal
61205
61337
  */
61206
61338
  class SystemBracket {
61207
- firstStaffInBracket = null;
61208
- lastStaffInBracket = null;
61339
+ _system;
61340
+ firstStaffInBracket;
61341
+ lastStaffInBracket;
61342
+ firstVisibleStaffInBracket;
61343
+ lastVisibleStaffInBracket;
61209
61344
  drawAsBrace = false;
61210
61345
  braceScale = 1;
61211
61346
  width = 0;
61212
61347
  index = 0;
61213
- get canPaint() {
61214
- return this.firstStaffInBracket !== null && this.lastStaffInBracket !== null;
61348
+ canPaint = false;
61349
+ constructor(system) {
61350
+ this._system = system;
61351
+ }
61352
+ updateCanPaint() {
61353
+ let firstVisibleStaff = undefined;
61354
+ let lastVisibleStaff = undefined;
61355
+ for (let i = this.firstStaffInBracket.index; i <= this.lastStaffInBracket.index; i++) {
61356
+ const staff = this._system.allStaves[i];
61357
+ if (staff.isVisible) {
61358
+ if (!firstVisibleStaff) {
61359
+ firstVisibleStaff = staff;
61360
+ }
61361
+ lastVisibleStaff = staff;
61362
+ }
61363
+ }
61364
+ this.firstVisibleStaffInBracket = firstVisibleStaff;
61365
+ this.lastVisibleStaffInBracket = lastVisibleStaff;
61366
+ if (!firstVisibleStaff || !lastVisibleStaff) {
61367
+ this.canPaint = false;
61368
+ return;
61369
+ }
61370
+ // single staff brackets?
61371
+ const singleStaffBrackets = this._system.layout.renderer.score.stylesheet.showSingleStaffBrackets;
61372
+ if (!singleStaffBrackets && firstVisibleStaff === lastVisibleStaff) {
61373
+ this.canPaint = false;
61374
+ return;
61375
+ }
61376
+ this.canPaint = true;
61215
61377
  }
61216
61378
  finalizeBracket(smuflMetrics) {
61217
- // systems with just a single staff do not have a bracket
61218
- if (this.firstStaffInBracket === this.lastStaffInBracket) {
61379
+ if (!this.canPaint) {
61219
61380
  this.width = 0;
61220
61381
  return;
61221
61382
  }
@@ -61229,11 +61390,11 @@ class SystemBracket {
61229
61390
  else {
61230
61391
  this.width = smuflMetrics.bracketThickness;
61231
61392
  }
61232
- if (!this.drawAsBrace || !this.firstStaffInBracket || !this.lastStaffInBracket) {
61393
+ if (!this.drawAsBrace) {
61233
61394
  return;
61234
61395
  }
61235
- const firstStart = this.firstStaffInBracket.contentTop;
61236
- const lastEnd = this.lastStaffInBracket.contentBottom;
61396
+ const firstStart = this.firstVisibleStaffInBracket.contentTop;
61397
+ const lastEnd = this.lastVisibleStaffInBracket.contentBottom;
61237
61398
  const requiredHeight = lastEnd - firstStart;
61238
61399
  const requiredScaleForBracket = requiredHeight / bravuraBraceHeightAtMusicFontSize;
61239
61400
  this.braceScale = requiredScaleForBracket;
@@ -61245,8 +61406,8 @@ class SystemBracket {
61245
61406
  */
61246
61407
  class SingleTrackSystemBracket extends SystemBracket {
61247
61408
  track;
61248
- constructor(track) {
61249
- super();
61409
+ constructor(system, track) {
61410
+ super(system);
61250
61411
  this.track = track;
61251
61412
  this.drawAsBrace = SingleTrackSystemBracket.isTrackDrawAsBrace(track);
61252
61413
  }
@@ -61311,6 +61472,7 @@ class StaffSystem {
61311
61472
  topPadding;
61312
61473
  bottomPadding;
61313
61474
  allStaves = [];
61475
+ firstVisibleStaff;
61314
61476
  constructor(layout) {
61315
61477
  this.layout = layout;
61316
61478
  this.topPadding = layout.renderer.settings.display.systemPaddingTop;
@@ -61329,14 +61491,40 @@ class StaffSystem {
61329
61491
  this.masterBarsRenderers.push(renderers);
61330
61492
  renderers.layoutingInfo.preBeatSize = 0;
61331
61493
  let src = 0;
61332
- for (let i = 0, j = this.staves.length; i < j; i++) {
61333
- const g = this.staves[i];
61334
- for (let k = 0, l = g.staves.length; k < l; k++) {
61335
- const s = g.staves[k];
61494
+ let firstVisibleStaff = undefined;
61495
+ let anyStaffVisible = false;
61496
+ for (const g of this.staves) {
61497
+ let firstVisibleStaffInGroup = undefined;
61498
+ let lastVisibleStaffInGroup = undefined;
61499
+ for (const s of g.staves) {
61336
61500
  const renderer = renderers.renderers[src++];
61337
61501
  s.addBarRenderer(renderer);
61502
+ if (s.isVisible) {
61503
+ anyStaffVisible = true;
61504
+ if (!firstVisibleStaffInGroup) {
61505
+ firstVisibleStaffInGroup = s;
61506
+ }
61507
+ if (!firstVisibleStaff) {
61508
+ firstVisibleStaff = s;
61509
+ }
61510
+ lastVisibleStaffInGroup = s;
61511
+ }
61338
61512
  }
61513
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61514
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61515
+ if (!firstVisibleStaff) {
61516
+ firstVisibleStaff = firstVisibleStaffInGroup;
61517
+ }
61518
+ }
61519
+ if (!anyStaffVisible) {
61520
+ const group = this.staves[0];
61521
+ const firstStaff = group.staves[0];
61522
+ firstStaff.isVisible = true;
61523
+ group.firstVisibleStaff = firstStaff;
61524
+ group.lastVisibleStaff = firstStaff;
61525
+ firstVisibleStaff = firstStaff;
61339
61526
  }
61527
+ this.firstVisibleStaff = firstVisibleStaff;
61340
61528
  this._calculateAccoladeSpacing(tracks);
61341
61529
  this._updateWidthFromLastBar();
61342
61530
  return renderers;
@@ -61347,15 +61535,26 @@ class StaffSystem {
61347
61535
  result.layoutingInfo = new BarLayoutingInfo();
61348
61536
  result.masterBar = tracks[0].score.masterBars[barIndex];
61349
61537
  this.masterBarsRenderers.push(result);
61538
+ let firstVisibleStaff = undefined;
61539
+ let anyStaffVisible = false;
61350
61540
  // add renderers
61351
61541
  const barLayoutingInfo = result.layoutingInfo;
61352
61542
  for (const g of this.staves) {
61543
+ let firstVisibleStaffInGroup = undefined;
61544
+ let lastVisibleStaffInGroup = undefined;
61353
61545
  for (const s of g.staves) {
61354
61546
  const bar = g.track.staves[s.modelStaff.index].bars[barIndex];
61355
61547
  const additionalMultiBarsRestBars = additionalMultiBarRestIndexes == null
61356
61548
  ? null
61357
61549
  : additionalMultiBarRestIndexes.map(b => g.track.staves[s.modelStaff.index].bars[b]);
61358
61550
  s.addBar(bar, barLayoutingInfo, additionalMultiBarsRestBars);
61551
+ if (s.isVisible) {
61552
+ anyStaffVisible = true;
61553
+ if (!firstVisibleStaffInGroup) {
61554
+ firstVisibleStaffInGroup = s;
61555
+ }
61556
+ lastVisibleStaffInGroup = s;
61557
+ }
61359
61558
  const renderer = s.barRenderers[s.barRenderers.length - 1];
61360
61559
  result.renderers.push(renderer);
61361
61560
  if (renderer.isLinkedToPrevious) {
@@ -61365,7 +61564,21 @@ class StaffSystem {
61365
61564
  result.canWrap = false;
61366
61565
  }
61367
61566
  }
61567
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61568
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61569
+ if (!firstVisibleStaff) {
61570
+ firstVisibleStaff = firstVisibleStaffInGroup;
61571
+ }
61572
+ }
61573
+ if (!anyStaffVisible) {
61574
+ const group = this.staves[0];
61575
+ const firstStaff = group.staves[0];
61576
+ firstStaff.isVisible = true;
61577
+ group.firstVisibleStaff = firstStaff;
61578
+ group.lastVisibleStaff = firstStaff;
61579
+ firstVisibleStaff = firstStaff;
61368
61580
  }
61581
+ this.firstVisibleStaff = firstVisibleStaff;
61369
61582
  this._calculateAccoladeSpacing(tracks);
61370
61583
  barLayoutingInfo.finish();
61371
61584
  // ensure same widths of new renderer
@@ -61378,19 +61591,35 @@ class StaffSystem {
61378
61591
  this.masterBarsRenderers.splice(this.masterBarsRenderers.length - 1, 1);
61379
61592
  let width = 0;
61380
61593
  let barDisplayScale = 0;
61381
- for (let i = 0, j = this.allStaves.length; i < j; i++) {
61382
- const s = this.allStaves[i];
61383
- const lastBar = s.revertLastBar();
61384
- const computedWidth = lastBar.computedWidth;
61385
- if (computedWidth > width) {
61386
- width = computedWidth;
61594
+ let firstVisibleStaff = undefined;
61595
+ for (const g of this.staves) {
61596
+ let firstVisibleStaffInGroup = undefined;
61597
+ let lastVisibleStaffInGroup = undefined;
61598
+ for (const s of g.staves) {
61599
+ const lastBar = s.revertLastBar();
61600
+ const computedWidth = lastBar.computedWidth;
61601
+ if (computedWidth > width) {
61602
+ width = computedWidth;
61603
+ }
61604
+ const newBarDisplayScale = lastBar.barDisplayScale;
61605
+ if (newBarDisplayScale > barDisplayScale) {
61606
+ barDisplayScale = newBarDisplayScale;
61607
+ }
61608
+ lastBar.afterReverted();
61609
+ if (s.isVisible) {
61610
+ if (!firstVisibleStaffInGroup) {
61611
+ firstVisibleStaffInGroup = s;
61612
+ }
61613
+ lastVisibleStaffInGroup = s;
61614
+ }
61387
61615
  }
61388
- const newBarDisplayScale = lastBar.barDisplayScale;
61389
- if (newBarDisplayScale > barDisplayScale) {
61390
- barDisplayScale = newBarDisplayScale;
61616
+ g.firstVisibleStaff = firstVisibleStaffInGroup;
61617
+ g.lastVisibleStaff = lastVisibleStaffInGroup;
61618
+ if (!firstVisibleStaff) {
61619
+ firstVisibleStaff = firstVisibleStaffInGroup;
61391
61620
  }
61392
- lastBar.afterReverted();
61393
61621
  }
61622
+ this.firstVisibleStaff = firstVisibleStaff;
61394
61623
  this.width -= width;
61395
61624
  this.computedWidth -= width;
61396
61625
  this.totalBarDisplayScale -= barDisplayScale;
@@ -61497,6 +61726,7 @@ class StaffSystem {
61497
61726
  }
61498
61727
  let braceWidth = 0;
61499
61728
  for (const b of this._brackets) {
61729
+ b.updateCanPaint();
61500
61730
  b.finalizeBracket(settings.display.resources.engravingSettings);
61501
61731
  braceWidth = Math.max(braceWidth, b.width);
61502
61732
  }
@@ -61504,6 +61734,12 @@ class StaffSystem {
61504
61734
  this.width += this.accoladeWidth;
61505
61735
  this.computedWidth += this.accoladeWidth;
61506
61736
  }
61737
+ else {
61738
+ for (const b of this._brackets) {
61739
+ b.updateCanPaint();
61740
+ b.finalizeBracket(settings.display.resources.engravingSettings);
61741
+ }
61742
+ }
61507
61743
  }
61508
61744
  _getStaffTrackGroup(track) {
61509
61745
  for (let i = 0, j = this.staves.length; i < j; i++) {
@@ -61533,12 +61769,12 @@ class StaffSystem {
61533
61769
  break;
61534
61770
  case BracketExtendMode.GroupStaves:
61535
61771
  // when grouping staves, we create one bracket for the whole track across all staves
61536
- bracket = new SingleTrackSystemBracket(track);
61772
+ bracket = new SingleTrackSystemBracket(this, track);
61537
61773
  bracket.index = this._brackets.length;
61538
61774
  this._brackets.push(bracket);
61539
61775
  break;
61540
61776
  case BracketExtendMode.GroupSimilarInstruments:
61541
- bracket = new SimilarInstrumentSystemBracket(track);
61777
+ bracket = new SimilarInstrumentSystemBracket(this, track);
61542
61778
  bracket.index = this._brackets.length;
61543
61779
  this._brackets.push(bracket);
61544
61780
  break;
@@ -61585,8 +61821,10 @@ class StaffSystem {
61585
61821
  }
61586
61822
  }
61587
61823
  paintPartial(cx, cy, canvas, startIndex, count) {
61588
- for (let i = 0, j = this.allStaves.length; i < j; i++) {
61589
- this.allStaves[i].paint(cx, cy, canvas, startIndex, count);
61824
+ for (const s of this.allStaves) {
61825
+ if (s.isVisible) {
61826
+ s.paint(cx, cy, canvas, startIndex, count);
61827
+ }
61590
61828
  }
61591
61829
  const res = this.layout.renderer.settings.display.resources;
61592
61830
  if (this.staves.length > 0 && startIndex === 0) {
@@ -61623,9 +61861,9 @@ class StaffSystem {
61623
61861
  const oldBaseLine = canvas.textBaseline;
61624
61862
  const oldTextAlign = canvas.textAlign;
61625
61863
  for (const g of this.staves) {
61626
- if (g.staves.length > 0) {
61627
- const firstStart = cy + g.staves[0].contentTop;
61628
- const lastEnd = cy + g.staves[g.staves.length - 1].contentBottom;
61864
+ if (g.firstVisibleStaff) {
61865
+ const firstStart = cy + g.firstVisibleStaff.contentTop;
61866
+ const lastEnd = cy + g.lastVisibleStaff.contentBottom;
61629
61867
  let trackNameText = '';
61630
61868
  switch (trackNameMode) {
61631
61869
  case TrackNameMode.FullName:
@@ -61679,6 +61917,9 @@ class StaffSystem {
61679
61917
  if (this.allStaves.length > 0 && needsSystemBarLine) {
61680
61918
  let previousStaffInBracket = null;
61681
61919
  for (const s of this.allStaves) {
61920
+ if (!s.isVisible) {
61921
+ continue;
61922
+ }
61682
61923
  if (previousStaffInBracket !== null) {
61683
61924
  const previousBottom = previousStaffInBracket.contentBottom;
61684
61925
  const thisTop = s.contentTop;
@@ -61705,17 +61946,17 @@ class StaffSystem {
61705
61946
  const settings = this.layout.renderer.settings;
61706
61947
  for (const bracket of this._brackets) {
61707
61948
  if (bracket.canPaint) {
61708
- const barStartX = cx + bracket.firstStaffInBracket.x;
61949
+ const barStartX = cx + bracket.firstVisibleStaffInBracket.x;
61709
61950
  const barSize = bracket.width;
61710
61951
  const barOffset = settings.display.accoladeBarPaddingRight;
61711
- const firstStart = cy + bracket.firstStaffInBracket.contentTop;
61712
- const lastEnd = cy + bracket.lastStaffInBracket.contentBottom;
61952
+ const firstStart = cy + bracket.firstVisibleStaffInBracket.contentTop;
61953
+ const lastEnd = cy + bracket.lastVisibleStaffInBracket.contentBottom;
61713
61954
  let accoladeStart = firstStart;
61714
61955
  let accoladeEnd = lastEnd;
61715
61956
  if (bracket.drawAsBrace) {
61716
61957
  CanvasHelper.fillMusicFontSymbolSafe(canvas, barStartX - barOffset - barSize, accoladeEnd, bracket.braceScale, MusicFontSymbol.Brace);
61717
61958
  }
61718
- else if (bracket.firstStaffInBracket !== bracket.lastStaffInBracket) {
61959
+ else if (bracket.firstVisibleStaffInBracket !== bracket.lastVisibleStaffInBracket) {
61719
61960
  // brackets typically overflow by 1/4 staff-space
61720
61961
  const smuflMetrics = settings.display.resources.engravingSettings;
61721
61962
  const bracketOverflow = smuflMetrics.oneStaffSpace * 0.25;
@@ -61747,40 +61988,86 @@ class StaffSystem {
61747
61988
  this.bottomPadding = Math.max(this.bottomPadding, neededHeight);
61748
61989
  this._hasSystemSeparator = true;
61749
61990
  }
61991
+ const anyStaffVisible = this._finalizeTrackGroups();
61992
+ // for now we always force one staff to be visible.
61993
+ // making also whole systems invisible needs separate attention (also on player cursor handling)
61994
+ if (!anyStaffVisible) {
61995
+ const group = this.staves[0];
61996
+ const firstStaff = group.staves[0];
61997
+ firstStaff.isVisible = true;
61998
+ this._finalizeTrackGroups(true);
61999
+ }
62000
+ for (const b of this._brackets) {
62001
+ b.finalizeBracket(settings.display.resources.engravingSettings);
62002
+ }
62003
+ }
62004
+ _finalizeTrackGroups(onlyFirstGroup = false) {
61750
62005
  let currentY = 0;
62006
+ const settings = this.layout.renderer.settings;
61751
62007
  const smufl = settings.display.resources.engravingSettings;
61752
62008
  const topBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketTop);
61753
62009
  const bottomBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketBottom);
61754
62010
  let previousStaff = undefined;
61755
- for (const staff of this.allStaves) {
61756
- // check if we need "in-between padding"
61757
- if (previousStaff !== undefined && previousStaff.trackIndex !== staff.trackIndex) {
61758
- currentY += settings.display.trackStaffPaddingBetween;
61759
- }
61760
- const bracket = this._staffToBracket.has(staff) ? this._staffToBracket.get(staff) : undefined;
61761
- const hasBracket = bracket && !bracket.drawAsBrace && bracket.canPaint;
61762
- if (hasBracket && bracket.firstStaffInBracket === staff) {
61763
- const spikeOverflow = topBracketSpikeHeight - staff.topOverflow;
61764
- if (spikeOverflow > 0) {
61765
- currentY += spikeOverflow;
62011
+ let endSpikeOverflow = 0;
62012
+ let anyStaffVisible = false;
62013
+ for (const group of this.staves) {
62014
+ let firstVisibleStaffInGroup = undefined;
62015
+ let lastVisibleStaffInGroup = undefined;
62016
+ for (const staff of group.staves) {
62017
+ // check if we need "in-between padding"
62018
+ if (previousStaff !== undefined && previousStaff.trackIndex !== staff.trackIndex) {
62019
+ currentY += settings.display.trackStaffPaddingBetween;
62020
+ }
62021
+ const bracket = this._staffToBracket.has(staff) ? this._staffToBracket.get(staff) : undefined;
62022
+ const hasBracket = bracket && !bracket.drawAsBrace && bracket.canPaint;
62023
+ if (hasBracket && bracket.firstStaffInBracket === staff) {
62024
+ const spikeOverflow = topBracketSpikeHeight - staff.topOverflow;
62025
+ if (spikeOverflow > 0) {
62026
+ currentY += spikeOverflow;
62027
+ }
62028
+ }
62029
+ staff.x = this.accoladeWidth;
62030
+ staff.y = currentY;
62031
+ if (!onlyFirstGroup) {
62032
+ staff.finalizeStaff();
62033
+ }
62034
+ if (staff.isVisible) {
62035
+ currentY += staff.height;
62036
+ anyStaffVisible = true;
62037
+ previousStaff = staff;
62038
+ if (!firstVisibleStaffInGroup) {
62039
+ firstVisibleStaffInGroup = staff;
62040
+ }
62041
+ lastVisibleStaffInGroup = staff;
62042
+ }
62043
+ endSpikeOverflow = 0;
62044
+ if (hasBracket && bracket.lastStaffInBracket === staff) {
62045
+ const spikeOverflow = bottomBracketSpikeHeight - staff.bottomOverflow;
62046
+ if (spikeOverflow > 0) {
62047
+ if (staff.isVisible) {
62048
+ currentY += spikeOverflow;
62049
+ }
62050
+ else {
62051
+ endSpikeOverflow = spikeOverflow;
62052
+ }
62053
+ }
61766
62054
  }
61767
62055
  }
61768
- staff.x = this.accoladeWidth;
61769
- staff.y = currentY;
61770
- staff.finalizeStaff();
61771
- currentY += staff.height;
61772
- if (hasBracket && bracket.lastStaffInBracket === staff) {
61773
- const spikeOverflow = bottomBracketSpikeHeight - staff.bottomOverflow;
61774
- if (spikeOverflow > 0) {
61775
- currentY += spikeOverflow;
61776
- }
62056
+ group.firstVisibleStaff = firstVisibleStaffInGroup;
62057
+ group.lastVisibleStaff = lastVisibleStaffInGroup;
62058
+ if (!this.firstVisibleStaff) {
62059
+ this.firstVisibleStaff = firstVisibleStaffInGroup;
62060
+ }
62061
+ if (onlyFirstGroup) {
62062
+ break;
61777
62063
  }
61778
- previousStaff = staff;
61779
62064
  }
61780
- this._contentHeight = currentY;
61781
- for (const b of this._brackets) {
61782
- b.finalizeBracket(smufl);
62065
+ // ensure we add overflow if last bracket is hidden
62066
+ if (endSpikeOverflow) {
62067
+ currentY += endSpikeOverflow;
61783
62068
  }
62069
+ this._contentHeight = currentY;
62070
+ return anyStaffVisible;
61784
62071
  }
61785
62072
  buildBoundingsLookup(cx, cy) {
61786
62073
  if (this.layout.renderer.boundsLookup.isFinished) {
@@ -61814,6 +62101,9 @@ class StaffSystem {
61814
62101
  const masterBarBoundsLookup = new Map();
61815
62102
  for (let i = 0; i < this.staves.length; i++) {
61816
62103
  for (const staff of this.staves[i].staves) {
62104
+ if (!staff.isVisible) {
62105
+ continue;
62106
+ }
61817
62107
  for (const renderer of staff.barRenderers) {
61818
62108
  let masterBarBounds;
61819
62109
  if (!masterBarBoundsLookup.has(renderer.bar.masterBar.index)) {
@@ -62124,8 +62414,9 @@ class ScoreLayout {
62124
62414
  }
62125
62415
  firstBarIndex = 0;
62126
62416
  lastBarIndex = 0;
62127
- createEmptyStaffSystem() {
62417
+ createEmptyStaffSystem(index) {
62128
62418
  const system = new StaffSystem(this);
62419
+ system.index = index;
62129
62420
  const allFactories = Environment.defaultRenderers;
62130
62421
  const renderStaves = [];
62131
62422
  for (let trackIndex = 0; trackIndex < this.renderer.tracks.length; trackIndex++) {
@@ -62137,7 +62428,7 @@ class ScoreLayout {
62137
62428
  let previousStaff = undefined;
62138
62429
  for (const factory of allFactories) {
62139
62430
  if (this.profile.has(factory.staffId) && factory.canCreate(track, staff)) {
62140
- const renderStaff = new RenderStaff(trackIndex, staff, factory);
62431
+ const renderStaff = new RenderStaff(system, trackIndex, staff, factory);
62141
62432
  // insert shared effect bands at front
62142
62433
  renderStaff.topEffectInfos.splice(0, 0, ...sharedTopEffects);
62143
62434
  renderStaff.bottomEffectInfos.push(...sharedBottomEffects);
@@ -65295,7 +65586,7 @@ class HorizontalScreenLayout extends ScoreLayout {
65295
65586
  }
65296
65587
  endBarIndex = startIndex + endBarIndex - 1; // map count to array index
65297
65588
  endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex));
65298
- this._system = this.createEmptyStaffSystem();
65589
+ this._system = this.createEmptyStaffSystem(0);
65299
65590
  this._system.isLast = true;
65300
65591
  this._system.x = this.pagePadding[0];
65301
65592
  this._system.y = this.pagePadding[1];
@@ -65568,8 +65859,7 @@ class PageViewLayout extends ScoreLayout {
65568
65859
  this._systems = [];
65569
65860
  let currentIndex = 0;
65570
65861
  const maxWidth = this._maxWidth;
65571
- let system = this.createEmptyStaffSystem();
65572
- system.index = this._systems.length;
65862
+ let system = this.createEmptyStaffSystem(this._systems.length);
65573
65863
  system.x = this.pagePadding[0];
65574
65864
  system.y = y;
65575
65865
  while (currentIndex < this._allMasterBarRenderers.length) {
@@ -65600,8 +65890,7 @@ class PageViewLayout extends ScoreLayout {
65600
65890
  this._fitSystem(system);
65601
65891
  y += this._paintSystem(system, oldHeight);
65602
65892
  // note: we do not increase currentIndex here to have it added to the next system
65603
- system = this.createEmptyStaffSystem();
65604
- system.index = this._systems.length;
65893
+ system = this.createEmptyStaffSystem(this._systems.length);
65605
65894
  system.x = this.pagePadding[0];
65606
65895
  system.y = y;
65607
65896
  }
@@ -65686,8 +65975,7 @@ class PageViewLayout extends ScoreLayout {
65686
65975
  return barsPerRow;
65687
65976
  }
65688
65977
  _createStaffSystem(currentBarIndex, endIndex) {
65689
- const system = this.createEmptyStaffSystem();
65690
- system.index = this._systems.length;
65978
+ const system = this.createEmptyStaffSystem(this._systems.length);
65691
65979
  const barsPerRow = this._getBarsPerSystem(system.index);
65692
65980
  const maxWidth = this._maxWidth;
65693
65981
  const end = endIndex + 1;
@@ -68367,16 +68655,77 @@ class NumberedBarRendererFactory extends BarRendererFactory {
68367
68655
  class ClefGlyph extends MusicFontGlyph {
68368
68656
  _clef;
68369
68657
  _clefOttava;
68658
+ _ottavaGlyph;
68370
68659
  constructor(x, y, clef, clefOttava) {
68371
68660
  super(x, y, 1, ClefGlyph._getSymbol(clef, clefOttava));
68372
68661
  this._clef = clef;
68373
68662
  this._clefOttava = clefOttava;
68374
68663
  }
68664
+ getBoundingBoxTop() {
68665
+ let top = super.getBoundingBoxTop();
68666
+ const ottava = this._ottavaGlyph;
68667
+ if (ottava) {
68668
+ const ottavaTop = this.y + ottava.getBoundingBoxTop();
68669
+ top = ModelUtils.minBoundingBox(top, ottavaTop);
68670
+ }
68671
+ return top;
68672
+ }
68673
+ getBoundingBoxBottom() {
68674
+ let bottom = super.getBoundingBoxBottom();
68675
+ const ottava = this._ottavaGlyph;
68676
+ if (ottava) {
68677
+ const ottavaBottom = this.y + ottava.getBoundingBoxBottom();
68678
+ bottom = ModelUtils.maxBoundingBox(bottom, ottavaBottom);
68679
+ }
68680
+ return bottom;
68681
+ }
68375
68682
  doLayout() {
68376
68683
  this.center = true;
68377
68684
  super.doLayout();
68378
68685
  this.width = this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.GClef);
68379
68686
  this.offsetX = this.width / 2;
68687
+ this._ottavaGlyph = undefined;
68688
+ switch (this._clef) {
68689
+ case Clef.C3:
68690
+ case Clef.C4:
68691
+ switch (this._clefOttava) {
68692
+ case Ottavia._8vb:
68693
+ return;
68694
+ }
68695
+ break;
68696
+ case Clef.F4:
68697
+ case Clef.G2:
68698
+ return;
68699
+ }
68700
+ let ottavaSymbol;
68701
+ let top = false;
68702
+ switch (this._clefOttava) {
68703
+ case Ottavia._15ma:
68704
+ ottavaSymbol = MusicFontSymbol.Clef15;
68705
+ top = true;
68706
+ break;
68707
+ case Ottavia._8va:
68708
+ ottavaSymbol = MusicFontSymbol.Clef8;
68709
+ top = true;
68710
+ break;
68711
+ case Ottavia._8vb:
68712
+ ottavaSymbol = MusicFontSymbol.Clef8;
68713
+ break;
68714
+ case Ottavia._15mb:
68715
+ ottavaSymbol = MusicFontSymbol.Clef15;
68716
+ break;
68717
+ default:
68718
+ return;
68719
+ }
68720
+ const ottavaX = this.width / 2;
68721
+ const ottavaY = top
68722
+ ? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
68723
+ : this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
68724
+ this.renderer.smuflMetrics.glyphHeights.get(ottavaSymbol);
68725
+ this._ottavaGlyph = new MusicFontGlyph(ottavaX, -ottavaY, 1, ottavaSymbol);
68726
+ this._ottavaGlyph.center = true;
68727
+ this._ottavaGlyph.renderer = this.renderer;
68728
+ this._ottavaGlyph.doLayout();
68380
68729
  }
68381
68730
  static _getSymbol(clef, clefOttava) {
68382
68731
  switch (clef) {
@@ -68424,44 +68773,10 @@ class ClefGlyph extends MusicFontGlyph {
68424
68773
  const _ = ElementStyleHelper.bar(canvas, BarSubElement.StandardNotationClef, this.renderer.bar);
68425
68774
  try {
68426
68775
  super.paint(cx, cy, canvas);
68427
- switch (this._clef) {
68428
- case Clef.C3:
68429
- case Clef.C4:
68430
- switch (this._clefOttava) {
68431
- case Ottavia._8vb:
68432
- return;
68433
- }
68434
- break;
68435
- case Clef.F4:
68436
- case Clef.G2:
68437
- return;
68438
- }
68439
- let ottavaGlyph;
68440
- let top = false;
68441
- switch (this._clefOttava) {
68442
- case Ottavia._15ma:
68443
- ottavaGlyph = MusicFontSymbol.Clef15;
68444
- top = true;
68445
- break;
68446
- case Ottavia._8va:
68447
- ottavaGlyph = MusicFontSymbol.Clef8;
68448
- top = true;
68449
- break;
68450
- case Ottavia._8vb:
68451
- ottavaGlyph = MusicFontSymbol.Clef8;
68452
- break;
68453
- case Ottavia._15mb:
68454
- ottavaGlyph = MusicFontSymbol.Clef15;
68455
- break;
68456
- default:
68457
- return;
68776
+ const ottava = this._ottavaGlyph;
68777
+ if (ottava) {
68778
+ ottava.paint(cx + this.x, cy + this.y, canvas);
68458
68779
  }
68459
- const ottavaX = this.width / 2;
68460
- const ottavaY = top
68461
- ? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
68462
- : this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
68463
- this.renderer.smuflMetrics.glyphHeights.get(ottavaGlyph);
68464
- CanvasHelper.fillMusicFontSymbolSafe(canvas, cx + this.x + ottavaX, cy + this.y - ottavaY, 1, ottavaGlyph, true);
68465
68780
  }
68466
68781
  finally {
68467
68782
  _?.[Symbol.dispose]?.();