@coderline/alphatab 1.9.0-alpha.1768 → 1.9.0-alpha.1785
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/README.md +1 -3
- package/dist/alphaTab.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +454 -66
- package/dist/alphaTab.d.ts +35 -61
- package/dist/alphaTab.js +454 -66
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +9 -14
package/dist/alphaTab.core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.9.0-alpha.
|
|
2
|
+
* alphaTab v1.9.0-alpha.1785 (develop, build 1785)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, 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.9.0-alpha.
|
|
207
|
-
static date = '2026-04-
|
|
208
|
-
static commit = '
|
|
206
|
+
static version = '1.9.0-alpha.1785';
|
|
207
|
+
static date = '2026-04-27T03:55:02.662Z';
|
|
208
|
+
static commit = '760ed909a3d8dc36b159d23b4ff6780e95a3daf1';
|
|
209
209
|
static print(print) {
|
|
210
210
|
print(`alphaTab ${VersionInfo.version}`);
|
|
211
211
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -32628,9 +32628,16 @@ class DisplaySettings {
|
|
|
32628
32628
|
*
|
|
32629
32629
|
* The page layout does not use `displayWidth`. The use of absolute widths would break the proper alignments needed for this kind of display.
|
|
32630
32630
|
*
|
|
32631
|
-
*
|
|
32632
|
-
*
|
|
32633
|
-
*
|
|
32631
|
+
* In both modes, prefix and postfix glyphs (clef, key signature, time signature, barlines) are treated as fixed overhead: they keep their
|
|
32632
|
+
* natural size and the remaining staff width is distributed across bars by a per-bar weight. This matches the convention used by
|
|
32633
|
+
* Guitar Pro, Dorico, Finale, Sibelius and MuseScore. Bars that carry a system-start prefix or a mid-line clef/key/time-signature change
|
|
32634
|
+
* are therefore visibly wider than plain bars with the same weight. The weight source depends on the mode:
|
|
32635
|
+
*
|
|
32636
|
+
* * `Automatic` (default for `page` layout): weights come from the built-in spacing engine (the natural content width of each bar).
|
|
32637
|
+
* `displayScale` on the model is ignored.
|
|
32638
|
+
* * `UseModelLayout` (and the `parchment` layout): weights come from `bar.displayScale` / `masterBar.displayScale`. An unset
|
|
32639
|
+
* `displayScale` defaults to `1` and behaves identically to an explicit `1`, matching Guitar Pro (which omits the value when the
|
|
32640
|
+
* author hasn't customized it).
|
|
32634
32641
|
*
|
|
32635
32642
|
* ### Horizontal Layout
|
|
32636
32643
|
*
|
|
@@ -38829,14 +38836,25 @@ class StaffSystemBounds {
|
|
|
38829
38836
|
*/
|
|
38830
38837
|
boundsLookup;
|
|
38831
38838
|
/**
|
|
38832
|
-
*
|
|
38839
|
+
* Whether this system's bounds have already been scaled via `finish`. Prevents double-scaling
|
|
38840
|
+
* when the parent `BoundsLookup` is preserved across partial renders and `finish` is invoked
|
|
38841
|
+
* again on a mix of already-scaled (preserved) and newly-registered (natural-coordinate) systems.
|
|
38842
|
+
*/
|
|
38843
|
+
isFinished = false;
|
|
38844
|
+
/**
|
|
38845
|
+
* Finished the lookup for optimized access. Idempotent: once finished, further calls are no-ops
|
|
38846
|
+
* so preserved systems survive partial renders without being re-scaled.
|
|
38833
38847
|
*/
|
|
38834
38848
|
finish(scale = 1) {
|
|
38849
|
+
if (this.isFinished) {
|
|
38850
|
+
return;
|
|
38851
|
+
}
|
|
38835
38852
|
this.realBounds.scaleWith(scale);
|
|
38836
38853
|
this.visualBounds.scaleWith(scale);
|
|
38837
38854
|
for (const t of this.bars) {
|
|
38838
38855
|
t.finish(scale);
|
|
38839
38856
|
}
|
|
38857
|
+
this.isFinished = true;
|
|
38840
38858
|
}
|
|
38841
38859
|
/**
|
|
38842
38860
|
* Adds a new master bar to this lookup.
|
|
@@ -39009,6 +39027,58 @@ class BoundsLookup {
|
|
|
39009
39027
|
}
|
|
39010
39028
|
this.isFinished = true;
|
|
39011
39029
|
}
|
|
39030
|
+
/**
|
|
39031
|
+
* Re-opens the lookup for registrations without discarding previously registered bounds.
|
|
39032
|
+
* Used by the renderer when it preserves this lookup across a partial render so that new
|
|
39033
|
+
* bounds for the re-layouted range can be added while preserved systems stay intact.
|
|
39034
|
+
* @internal
|
|
39035
|
+
*/
|
|
39036
|
+
resetForPartialUpdate() {
|
|
39037
|
+
this.isFinished = false;
|
|
39038
|
+
}
|
|
39039
|
+
/**
|
|
39040
|
+
* Removes all entries belonging to the given master bar index and any bars after it.
|
|
39041
|
+
* Used before a partial render re-registers bounds for the re-layouted range, so the
|
|
39042
|
+
* preserved lookup ends up with only the unchanged entries when registration begins.
|
|
39043
|
+
*
|
|
39044
|
+
* Assumes the layout aligns its re-layouted range to system boundaries - i.e. the first
|
|
39045
|
+
* system to clear starts exactly at `masterBarIndex`. Caller is responsible for passing
|
|
39046
|
+
* the first master-bar-index of the first re-layouted system.
|
|
39047
|
+
* @internal
|
|
39048
|
+
*/
|
|
39049
|
+
clearFromMasterBar(masterBarIndex) {
|
|
39050
|
+
// drop staff systems whose bars start at or after the cleared range.
|
|
39051
|
+
let firstRemovedSystem = -1;
|
|
39052
|
+
for (let i = 0; i < this.staffSystems.length; i++) {
|
|
39053
|
+
const systemBars = this.staffSystems[i].bars;
|
|
39054
|
+
if (systemBars.length > 0 && systemBars[0].index >= masterBarIndex) {
|
|
39055
|
+
firstRemovedSystem = i;
|
|
39056
|
+
break;
|
|
39057
|
+
}
|
|
39058
|
+
}
|
|
39059
|
+
if (firstRemovedSystem !== -1) {
|
|
39060
|
+
this.staffSystems.splice(firstRemovedSystem, this.staffSystems.length - firstRemovedSystem);
|
|
39061
|
+
}
|
|
39062
|
+
// drop master bar entries at or beyond the cleared range.
|
|
39063
|
+
for (const key of Array.from(this._masterBarLookup.keys())) {
|
|
39064
|
+
if (key >= masterBarIndex) {
|
|
39065
|
+
this._masterBarLookup.delete(key);
|
|
39066
|
+
}
|
|
39067
|
+
}
|
|
39068
|
+
// drop beat entries whose beats belong to cleared bars.
|
|
39069
|
+
for (const key of Array.from(this._beatLookup.keys())) {
|
|
39070
|
+
const list = this._beatLookup.get(key);
|
|
39071
|
+
const filtered = list.filter(b => b.beat.voice.bar.index < masterBarIndex);
|
|
39072
|
+
if (filtered.length === 0) {
|
|
39073
|
+
this._beatLookup.delete(key);
|
|
39074
|
+
}
|
|
39075
|
+
else if (filtered.length !== list.length) {
|
|
39076
|
+
this._beatLookup.set(key, filtered);
|
|
39077
|
+
}
|
|
39078
|
+
}
|
|
39079
|
+
// drop the in-progress pointer - the next addStaffSystem call will replace it.
|
|
39080
|
+
this._currentStaffSystem = null;
|
|
39081
|
+
}
|
|
39012
39082
|
/**
|
|
39013
39083
|
* Adds a new staff sytem to the lookup.
|
|
39014
39084
|
* @param bounds The staff system bounds to add.
|
|
@@ -46840,7 +46910,15 @@ class ScoreRenderer {
|
|
|
46840
46910
|
Logger.warning('Rendering', 'AlphaTab skipped rendering because of width=0 (element invisible)', null);
|
|
46841
46911
|
return;
|
|
46842
46912
|
}
|
|
46843
|
-
|
|
46913
|
+
// For partial renders we preserve the existing lookup so bars outside the re-layouted
|
|
46914
|
+
// range keep their already-scaled bounds - the layout will clear the changed range
|
|
46915
|
+
// before the paint pass re-registers fresh entries for it.
|
|
46916
|
+
if (renderHints?.firstChangedMasterBar !== undefined && this.boundsLookup) {
|
|
46917
|
+
this.boundsLookup.resetForPartialUpdate();
|
|
46918
|
+
}
|
|
46919
|
+
else {
|
|
46920
|
+
this.boundsLookup = new BoundsLookup();
|
|
46921
|
+
}
|
|
46844
46922
|
this._recreateCanvas();
|
|
46845
46923
|
this.canvas.lineWidth = 1;
|
|
46846
46924
|
this.canvas.settings = this.settings;
|
|
@@ -48714,9 +48792,6 @@ class BeatTickLookup {
|
|
|
48714
48792
|
* @param beat The beat to add.
|
|
48715
48793
|
*/
|
|
48716
48794
|
highlightBeat(beat, playbackStart) {
|
|
48717
|
-
if (beat.isEmpty && !beat.voice.isEmpty) {
|
|
48718
|
-
return;
|
|
48719
|
-
}
|
|
48720
48795
|
if (!this._highlightedBeats.has(beat.id)) {
|
|
48721
48796
|
this._highlightedBeats.set(beat.id, true);
|
|
48722
48797
|
this.highlightedBeats.push(new BeatTickLookupItem(beat, playbackStart));
|
|
@@ -50184,7 +50259,13 @@ class MidiFileGenerator {
|
|
|
50184
50259
|
let beatStart = beat.playbackStart;
|
|
50185
50260
|
let audioDuration = beat.playbackDuration;
|
|
50186
50261
|
const masterBarDuration = beat.voice.bar.masterBar.calculateDuration();
|
|
50187
|
-
|
|
50262
|
+
// For a bar whose voice contains a single empty beat (the typical "whole-bar rest"
|
|
50263
|
+
// placeholder inserted during score.finish), extend the beat's audio duration to cover
|
|
50264
|
+
// the full bar so cursor navigation has a beat to follow across the whole bar. Don't
|
|
50265
|
+
// apply this when the voice has multiple beats: those represent explicit rhythmic
|
|
50266
|
+
// subdivisions even when each beat is empty (e.g. a recording grid of placeholder
|
|
50267
|
+
// slots), and overriding would make every beat overlap the whole bar.
|
|
50268
|
+
if (beat.voice.bar.isEmpty && beat.voice.beats.length === 1) {
|
|
50188
50269
|
audioDuration = masterBarDuration;
|
|
50189
50270
|
}
|
|
50190
50271
|
else if (beat.voice.bar.masterBar.tripletFeel !== TripletFeel.NoTripletFeel &&
|
|
@@ -54805,7 +54886,8 @@ class AlphaTabApiBase {
|
|
|
54805
54886
|
this._isInitialBeatCursorUpdate ||
|
|
54806
54887
|
barBounds.y !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.y ||
|
|
54807
54888
|
startBeatX < previousBeatBounds.onNotesX ||
|
|
54808
|
-
barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1
|
|
54889
|
+
barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1 ||
|
|
54890
|
+
barBounds.h !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.h;
|
|
54809
54891
|
if (jumpCursor) {
|
|
54810
54892
|
cursorHandler.placeBeatCursor(beatCursor, beatBoundings, startBeatX);
|
|
54811
54893
|
}
|
|
@@ -58045,8 +58127,9 @@ class AlphaTabWebWorker {
|
|
|
58045
58127
|
break;
|
|
58046
58128
|
case 'alphaTab.renderScore':
|
|
58047
58129
|
this._updateFontSizes(data.fontSizes);
|
|
58130
|
+
const renderHints = data.renderHints;
|
|
58048
58131
|
const score = data.score == null ? null : JsonConverter.jsObjectToScore(data.score, this._renderer.settings);
|
|
58049
|
-
this._renderMultiple(score, data.trackIndexes);
|
|
58132
|
+
this._renderMultiple(score, data.trackIndexes, renderHints);
|
|
58050
58133
|
break;
|
|
58051
58134
|
case 'alphaTab.updateSettings':
|
|
58052
58135
|
this._updateSettings(data.settings);
|
|
@@ -62919,6 +63002,15 @@ class BarRendererBase {
|
|
|
62919
63002
|
}
|
|
62920
63003
|
return false;
|
|
62921
63004
|
}
|
|
63005
|
+
/**
|
|
63006
|
+
* The fixed-overhead width of this renderer: glyphs that do not stretch when
|
|
63007
|
+
* the bar is scaled (clef, key signature, time signature, barlines, courtesy
|
|
63008
|
+
* accidentals, etc). Treated as a fixed allocation by the system-level layout
|
|
63009
|
+
* before distributing remaining width across bars by {@link Bar.displayScale}.
|
|
63010
|
+
*/
|
|
63011
|
+
get fixedOverhead() {
|
|
63012
|
+
return this._preBeatGlyphs.width + this._postBeatGlyphs.width;
|
|
63013
|
+
}
|
|
62922
63014
|
scaleToWidth(width) {
|
|
62923
63015
|
// preBeat and postBeat glyphs do not get resized
|
|
62924
63016
|
const containerWidth = width - this._preBeatGlyphs.width - this._postBeatGlyphs.width;
|
|
@@ -65477,6 +65569,22 @@ class BarLayoutingInfo {
|
|
|
65477
65569
|
postBeatSize = 0;
|
|
65478
65570
|
minStretchForce = 0;
|
|
65479
65571
|
totalSpringConstant = 0;
|
|
65572
|
+
/**
|
|
65573
|
+
* The smallest note duration encountered within this bar's springs, used as the reference in
|
|
65574
|
+
* the Gourlay stretch formula. Read by the owning {@link StaffSystem} so that the system can
|
|
65575
|
+
* aggregate a shared minimum across all bars and trigger a reconcile if an added bar introduces
|
|
65576
|
+
* a shorter duration than previously seen.
|
|
65577
|
+
*/
|
|
65578
|
+
get localMinDuration() {
|
|
65579
|
+
return this._minDuration;
|
|
65580
|
+
}
|
|
65581
|
+
/**
|
|
65582
|
+
* The minimum-duration reference against which the spring constants currently held by this info
|
|
65583
|
+
* were computed. Set by {@link finish} and {@link recomputeSpringConstants}. The owning
|
|
65584
|
+
* StaffSystem compares this against its system-wide minimum to decide whether spring constants
|
|
65585
|
+
* need re-derivation.
|
|
65586
|
+
*/
|
|
65587
|
+
computedWithMinDuration = 0;
|
|
65480
65588
|
_updateMinStretchForce(force) {
|
|
65481
65589
|
if (this.minStretchForce < force) {
|
|
65482
65590
|
this.minStretchForce = force;
|
|
@@ -65646,10 +65754,26 @@ class BarLayoutingInfo {
|
|
|
65646
65754
|
this._incompleteGraceRodsWidth += sp.preBeatWidth + sp.postSpringWidth;
|
|
65647
65755
|
}
|
|
65648
65756
|
}
|
|
65649
|
-
this._calculateSpringConstants();
|
|
65757
|
+
this._calculateSpringConstants(this._minDuration);
|
|
65758
|
+
this.computedWithMinDuration = this._minDuration;
|
|
65650
65759
|
this.version++;
|
|
65651
65760
|
}
|
|
65652
|
-
|
|
65761
|
+
/**
|
|
65762
|
+
* Re-derives the spring constants (and {@link minStretchForce} / {@link totalSpringConstant})
|
|
65763
|
+
* using a caller-supplied minimum-duration reference rather than this bar's local minimum.
|
|
65764
|
+
*
|
|
65765
|
+
* Called by {@link StaffSystem.reconcileMinDurationIfDirty} when a bar added later to the
|
|
65766
|
+
* system introduced a shorter note than previously seen, invalidating this bar's spring
|
|
65767
|
+
* constants. Grace-rod data is not recomputed — it is independent of the minimum-duration
|
|
65768
|
+
* reference. The internal {@link version} is bumped so downstream consumers (e.g.
|
|
65769
|
+
* {@link BarRendererBase.applyLayoutingInfo}) pick up the refreshed positions.
|
|
65770
|
+
*/
|
|
65771
|
+
recomputeSpringConstants(minDuration) {
|
|
65772
|
+
this._calculateSpringConstants(minDuration);
|
|
65773
|
+
this.computedWithMinDuration = minDuration;
|
|
65774
|
+
this.version++;
|
|
65775
|
+
}
|
|
65776
|
+
_calculateSpringConstants(minDuration) {
|
|
65653
65777
|
let totalSpringConstant = 0;
|
|
65654
65778
|
const sortedSprings = this._timeSortedSprings;
|
|
65655
65779
|
if (sortedSprings.length === 0) {
|
|
@@ -65667,7 +65791,7 @@ class BarLayoutingInfo {
|
|
|
65667
65791
|
const nextSpring = sortedSprings[i + 1];
|
|
65668
65792
|
duration = Math.abs(nextSpring.timePosition - currentSpring.timePosition);
|
|
65669
65793
|
}
|
|
65670
|
-
currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration);
|
|
65794
|
+
currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration, minDuration);
|
|
65671
65795
|
totalSpringConstant += 1 / currentSpring.springConstant;
|
|
65672
65796
|
}
|
|
65673
65797
|
this.totalSpringConstant = 1 / totalSpringConstant;
|
|
@@ -65727,7 +65851,7 @@ class BarLayoutingInfo {
|
|
|
65727
65851
|
// springX += this.calculateWidth(force, spring.springConstant);
|
|
65728
65852
|
// }
|
|
65729
65853
|
// }
|
|
65730
|
-
_calculateSpringConstant(spring, duration) {
|
|
65854
|
+
_calculateSpringConstant(spring, duration, minDuration) {
|
|
65731
65855
|
if (duration <= 0) {
|
|
65732
65856
|
duration = MidiUtils.toTicks(Duration.TwoHundredFiftySixth);
|
|
65733
65857
|
}
|
|
@@ -65735,7 +65859,6 @@ class BarLayoutingInfo {
|
|
|
65735
65859
|
spring.smallestDuration = duration;
|
|
65736
65860
|
}
|
|
65737
65861
|
const smallestDuration = spring.smallestDuration;
|
|
65738
|
-
const minDuration = this._minDuration;
|
|
65739
65862
|
const minDurationWidth = BarLayoutingInfo._defaultMinDurationWidth;
|
|
65740
65863
|
const phi = 1 + 0.85 * Math.log2(duration / minDuration);
|
|
65741
65864
|
return (smallestDuration / duration) * (1 / (phi * minDurationWidth));
|
|
@@ -65798,6 +65921,18 @@ class MasterBarsRenderers {
|
|
|
65798
65921
|
canWrap = true;
|
|
65799
65922
|
masterBar;
|
|
65800
65923
|
additionalMultiBarRestIndexes = null;
|
|
65924
|
+
/**
|
|
65925
|
+
* Max fixed overhead (prefix + postfix glyph width) across all staves of this bar.
|
|
65926
|
+
* Used by the layout-mode horizontal scaling pass to carve out the fixed-overhead bucket
|
|
65927
|
+
* before distributing staff width across bars.
|
|
65928
|
+
*/
|
|
65929
|
+
maxFixedOverhead = 0;
|
|
65930
|
+
/**
|
|
65931
|
+
* Max natural content width (computedWidth - fixedOverhead) across all staves of this bar.
|
|
65932
|
+
* Used as the bar weight when the layout ignores {@link MasterBar.displayScale} (e.g.
|
|
65933
|
+
* Page layout with `SystemsLayoutMode.Automatic`).
|
|
65934
|
+
*/
|
|
65935
|
+
maxContentWidth = 0;
|
|
65801
65936
|
get lastMasterBarIndex() {
|
|
65802
65937
|
if (this.additionalMultiBarRestIndexes) {
|
|
65803
65938
|
return this.additionalMultiBarRestIndexes[this.additionalMultiBarRestIndexes.length - 1];
|
|
@@ -65979,6 +66114,45 @@ class StaffSystem {
|
|
|
65979
66114
|
* This value is mainly used in the parchment style layout for correct scaling of the bars.
|
|
65980
66115
|
*/
|
|
65981
66116
|
totalBarDisplayScale = 0;
|
|
66117
|
+
/**
|
|
66118
|
+
* Sum of per-bar {@link MasterBarsRenderers.maxFixedOverhead} across the system. The layout-mode
|
|
66119
|
+
* horizontal scaling pass subtracts this from the available staff width before distributing the
|
|
66120
|
+
* remainder across bars.
|
|
66121
|
+
*/
|
|
66122
|
+
totalFixedOverhead = 0;
|
|
66123
|
+
/**
|
|
66124
|
+
* Sum of per-bar {@link MasterBarsRenderers.maxContentWidth} across the system. Used as the
|
|
66125
|
+
* denominator when distributing staff width in modes that weight bars by natural content width
|
|
66126
|
+
* (Page layout with `SystemsLayoutMode.Automatic`).
|
|
66127
|
+
*/
|
|
66128
|
+
totalContentWidth = 0;
|
|
66129
|
+
/**
|
|
66130
|
+
* Shortest note duration (in ticks) across every bar that has been added to this system, used
|
|
66131
|
+
* as the common reference in the Gourlay stretch formula so that rhythmically-equivalent beats
|
|
66132
|
+
* in different bars of the same system align column-wise.
|
|
66133
|
+
*
|
|
66134
|
+
* `-1` means "no bar added yet". The value only moves downward during system assembly; when a
|
|
66135
|
+
* new bar introduces a shorter minimum, {@link isMinDurationDirty} is set so that
|
|
66136
|
+
* {@link reconcileMinDurationIfDirty} can re-derive spring constants on the previously-added
|
|
66137
|
+
* bars before layout distribution runs.
|
|
66138
|
+
*/
|
|
66139
|
+
minDuration = -1;
|
|
66140
|
+
/**
|
|
66141
|
+
* Set when a bar added to this system introduced a shorter {@link minDuration} than previously
|
|
66142
|
+
* seen, leaving earlier bars' spring constants stale. Consumed by
|
|
66143
|
+
* {@link reconcileMinDurationIfDirty} which is called from `VerticalLayoutBase._fitSystem`
|
|
66144
|
+
* once the system is fully assembled.
|
|
66145
|
+
*/
|
|
66146
|
+
isMinDurationDirty = false;
|
|
66147
|
+
/**
|
|
66148
|
+
* Whether this system coordinates a shared minimum-duration reference across its bars for the
|
|
66149
|
+
* Gourlay stretch formula. Defaults to `true` for page-style and parchment layouts where bars
|
|
66150
|
+
* of a system fight for a common staff width. Set to `false` for horizontal layouts where each
|
|
66151
|
+
* bar is sized independently (by `bar.displayWidth` or its intrinsic width) and there is no
|
|
66152
|
+
* column-alignment concern - each bar keeps its local minimum so pre-existing rendering is
|
|
66153
|
+
* preserved.
|
|
66154
|
+
*/
|
|
66155
|
+
shareMinDurationAcrossBars = true;
|
|
65982
66156
|
isLast = false;
|
|
65983
66157
|
masterBarsRenderers = [];
|
|
65984
66158
|
staves = [];
|
|
@@ -66040,6 +66214,9 @@ class StaffSystem {
|
|
|
66040
66214
|
}
|
|
66041
66215
|
this.firstVisibleStaff = firstVisibleStaff;
|
|
66042
66216
|
this._calculateAccoladeSpacing(tracks);
|
|
66217
|
+
// On the resize path the layoutingInfo was finalized in a previous layout pass, so we
|
|
66218
|
+
// only need to check whether its min-duration reference still matches the new system's.
|
|
66219
|
+
this._trackSystemMinDuration(renderers.layoutingInfo);
|
|
66043
66220
|
this._applyLayoutAndUpdateWidth();
|
|
66044
66221
|
return renderers;
|
|
66045
66222
|
}
|
|
@@ -66095,10 +66272,89 @@ class StaffSystem {
|
|
|
66095
66272
|
this.firstVisibleStaff = firstVisibleStaff;
|
|
66096
66273
|
this._calculateAccoladeSpacing(tracks);
|
|
66097
66274
|
barLayoutingInfo.finish();
|
|
66275
|
+
// Reconcile against the system-wide minimum-duration reference now that springs are
|
|
66276
|
+
// finalized. If this bar introduced a shorter note, earlier bars become stale (flagged
|
|
66277
|
+
// for bulk reconcile at fit time). If the system already had a shorter min than this
|
|
66278
|
+
// bar's local one, this bar's spring constants are recomputed immediately so the width
|
|
66279
|
+
// we return below reflects the shared reference.
|
|
66280
|
+
this._trackSystemMinDuration(barLayoutingInfo);
|
|
66098
66281
|
// ensure same widths of new renderer
|
|
66099
66282
|
result.width = this._applyLayoutAndUpdateWidth();
|
|
66100
66283
|
return result;
|
|
66101
66284
|
}
|
|
66285
|
+
/**
|
|
66286
|
+
* Updates {@link minDuration} and {@link isMinDurationDirty} when a bar is added, and brings
|
|
66287
|
+
* the just-added bar's {@link BarLayoutingInfo} in line with the current system minimum if the
|
|
66288
|
+
* system already saw a shorter reference. The bulk reconcile over previously-added bars is
|
|
66289
|
+
* deferred to {@link reconcileMinDurationIfDirty} (called from `_fitSystem`) to avoid
|
|
66290
|
+
* re-iterating the system every time a bar is appended.
|
|
66291
|
+
*/
|
|
66292
|
+
_trackSystemMinDuration(info) {
|
|
66293
|
+
if (!this.shareMinDurationAcrossBars) {
|
|
66294
|
+
return;
|
|
66295
|
+
}
|
|
66296
|
+
const localMin = info.localMinDuration;
|
|
66297
|
+
if (this.minDuration === -1 || localMin < this.minDuration) {
|
|
66298
|
+
// this bar shortens the system minimum; earlier bars (if any) are now stale
|
|
66299
|
+
if (this.masterBarsRenderers.length > 1 && localMin !== this.minDuration) {
|
|
66300
|
+
this.isMinDurationDirty = true;
|
|
66301
|
+
}
|
|
66302
|
+
this.minDuration = localMin;
|
|
66303
|
+
}
|
|
66304
|
+
if (info.computedWithMinDuration > this.minDuration) {
|
|
66305
|
+
// this bar was initialized against a larger (local) min than the system carries; pull
|
|
66306
|
+
// it down to the system reference so its computedWidth reflects the shared spacing.
|
|
66307
|
+
info.recomputeSpringConstants(this.minDuration);
|
|
66308
|
+
}
|
|
66309
|
+
}
|
|
66310
|
+
/**
|
|
66311
|
+
* Re-derives spring constants on bars whose {@link BarLayoutingInfo.computedWithMinDuration}
|
|
66312
|
+
* is out of sync with the current {@link minDuration}, and rebuilds the cached system totals
|
|
66313
|
+
* (widths, {@link totalFixedOverhead}, {@link totalContentWidth}) from the refreshed bar
|
|
66314
|
+
* widths. Called from `VerticalLayoutBase._fitSystem` after the system is fully assembled and
|
|
66315
|
+
* before distribution runs. No-op when {@link isMinDurationDirty} is false.
|
|
66316
|
+
*/
|
|
66317
|
+
reconcileMinDurationIfDirty() {
|
|
66318
|
+
if (!this.isMinDurationDirty) {
|
|
66319
|
+
return;
|
|
66320
|
+
}
|
|
66321
|
+
let systemWidth = this.accoladeWidth;
|
|
66322
|
+
let totalFixedOverhead = 0;
|
|
66323
|
+
let totalContentWidth = 0;
|
|
66324
|
+
for (const mb of this.masterBarsRenderers) {
|
|
66325
|
+
if (mb.layoutingInfo.computedWithMinDuration > this.minDuration) {
|
|
66326
|
+
mb.layoutingInfo.recomputeSpringConstants(this.minDuration);
|
|
66327
|
+
}
|
|
66328
|
+
let maxPrefix = 0;
|
|
66329
|
+
let maxContent = 0;
|
|
66330
|
+
let realWidth = 0;
|
|
66331
|
+
for (const r of mb.renderers) {
|
|
66332
|
+
r.applyLayoutingInfo();
|
|
66333
|
+
if (r.computedWidth > realWidth) {
|
|
66334
|
+
realWidth = r.computedWidth;
|
|
66335
|
+
}
|
|
66336
|
+
const overhead = r.fixedOverhead;
|
|
66337
|
+
if (overhead > maxPrefix) {
|
|
66338
|
+
maxPrefix = overhead;
|
|
66339
|
+
}
|
|
66340
|
+
const content = Math.max(0, r.computedWidth - overhead);
|
|
66341
|
+
if (content > maxContent) {
|
|
66342
|
+
maxContent = content;
|
|
66343
|
+
}
|
|
66344
|
+
}
|
|
66345
|
+
mb.maxFixedOverhead = maxPrefix;
|
|
66346
|
+
mb.maxContentWidth = maxContent;
|
|
66347
|
+
mb.width = realWidth;
|
|
66348
|
+
systemWidth += realWidth;
|
|
66349
|
+
totalFixedOverhead += maxPrefix;
|
|
66350
|
+
totalContentWidth += maxContent;
|
|
66351
|
+
}
|
|
66352
|
+
this.width = systemWidth;
|
|
66353
|
+
this.computedWidth = systemWidth;
|
|
66354
|
+
this.totalFixedOverhead = totalFixedOverhead;
|
|
66355
|
+
this.totalContentWidth = totalContentWidth;
|
|
66356
|
+
this.isMinDurationDirty = false;
|
|
66357
|
+
}
|
|
66102
66358
|
getBarDisplayScale(renderer) {
|
|
66103
66359
|
return this.staves.length > 1 ? renderer.bar.masterBar.displayScale : renderer.bar.displayScale;
|
|
66104
66360
|
}
|
|
@@ -66137,12 +66393,16 @@ class StaffSystem {
|
|
|
66137
66393
|
this.width -= width;
|
|
66138
66394
|
this.computedWidth -= width;
|
|
66139
66395
|
this.totalBarDisplayScale -= barDisplayScale;
|
|
66396
|
+
this.totalFixedOverhead -= toRemove.maxFixedOverhead;
|
|
66397
|
+
this.totalContentWidth -= toRemove.maxContentWidth;
|
|
66140
66398
|
return toRemove;
|
|
66141
66399
|
}
|
|
66142
66400
|
return null;
|
|
66143
66401
|
}
|
|
66144
66402
|
_applyLayoutAndUpdateWidth() {
|
|
66145
66403
|
let realWidth = 0;
|
|
66404
|
+
let maxFixedOverhead = 0;
|
|
66405
|
+
let maxContentWidth = 0;
|
|
66146
66406
|
let barDisplayScale = 0;
|
|
66147
66407
|
for (const s of this.allStaves) {
|
|
66148
66408
|
const last = s.barRenderers[s.barRenderers.length - 1];
|
|
@@ -66151,8 +66411,21 @@ class StaffSystem {
|
|
|
66151
66411
|
if (last.computedWidth > realWidth) {
|
|
66152
66412
|
realWidth = last.computedWidth;
|
|
66153
66413
|
}
|
|
66414
|
+
const overhead = last.fixedOverhead;
|
|
66415
|
+
if (overhead > maxFixedOverhead) {
|
|
66416
|
+
maxFixedOverhead = overhead;
|
|
66417
|
+
}
|
|
66418
|
+
const content = Math.max(0, last.computedWidth - overhead);
|
|
66419
|
+
if (content > maxContentWidth) {
|
|
66420
|
+
maxContentWidth = content;
|
|
66421
|
+
}
|
|
66154
66422
|
}
|
|
66423
|
+
const renderers = this.masterBarsRenderers[this.masterBarsRenderers.length - 1];
|
|
66424
|
+
renderers.maxFixedOverhead = maxFixedOverhead;
|
|
66425
|
+
renderers.maxContentWidth = maxContentWidth;
|
|
66155
66426
|
this.totalBarDisplayScale += barDisplayScale;
|
|
66427
|
+
this.totalFixedOverhead += maxFixedOverhead;
|
|
66428
|
+
this.totalContentWidth += maxContentWidth;
|
|
66156
66429
|
this.width += realWidth;
|
|
66157
66430
|
this.computedWidth += realWidth;
|
|
66158
66431
|
return realWidth;
|
|
@@ -66650,17 +66923,6 @@ class StaffSystem {
|
|
|
66650
66923
|
}
|
|
66651
66924
|
}
|
|
66652
66925
|
|
|
66653
|
-
/**
|
|
66654
|
-
* @internal
|
|
66655
|
-
*/
|
|
66656
|
-
class LazyPartial {
|
|
66657
|
-
args;
|
|
66658
|
-
renderCallback;
|
|
66659
|
-
constructor(args, renderCallback) {
|
|
66660
|
-
this.args = args;
|
|
66661
|
-
this.renderCallback = renderCallback;
|
|
66662
|
-
}
|
|
66663
|
-
}
|
|
66664
66926
|
/**
|
|
66665
66927
|
* This is the base class for creating new layouting engines for the score renderer.
|
|
66666
66928
|
* @internal
|
|
@@ -66691,15 +66953,21 @@ class ScoreLayout {
|
|
|
66691
66953
|
this.doResize();
|
|
66692
66954
|
}
|
|
66693
66955
|
layoutAndRender(renderHints) {
|
|
66694
|
-
this._lazyPartials.clear();
|
|
66695
66956
|
this.slurRegistry.clear();
|
|
66696
|
-
this.beamingRuleLookups.clear();
|
|
66697
|
-
this._barRendererLookup.clear();
|
|
66698
|
-
this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
|
|
66699
66957
|
const score = this.renderer.score;
|
|
66700
66958
|
this.firstBarIndex = ModelUtils.computeFirstDisplayedBarIndex(score, this.renderer.settings);
|
|
66701
66959
|
this.lastBarIndex = ModelUtils.computeLastDisplayedBarIndex(score, this.renderer.settings, this.firstBarIndex);
|
|
66702
66960
|
this.multiBarRestInfo = ModelUtils.buildMultiBarRestInfo(this.renderer.tracks, this.firstBarIndex, this.lastBarIndex);
|
|
66961
|
+
const firstChangedMasterBar = renderHints?.firstChangedMasterBar;
|
|
66962
|
+
if (firstChangedMasterBar !== undefined) {
|
|
66963
|
+
if (this.doUpdateForBars(renderHints)) {
|
|
66964
|
+
return;
|
|
66965
|
+
}
|
|
66966
|
+
}
|
|
66967
|
+
this._lazyPartials.clear();
|
|
66968
|
+
this.beamingRuleLookups.clear();
|
|
66969
|
+
this._barRendererLookup.clear();
|
|
66970
|
+
this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
|
|
66703
66971
|
this.pagePadding = this.renderer.settings.display.padding.map(p => p / this.renderer.settings.display.scale);
|
|
66704
66972
|
if (!this.pagePadding) {
|
|
66705
66973
|
this.pagePadding = [0, 0, 0, 0];
|
|
@@ -66714,6 +66982,9 @@ class ScoreLayout {
|
|
|
66714
66982
|
this.doLayoutAndRender(renderHints);
|
|
66715
66983
|
}
|
|
66716
66984
|
_lazyPartials = new Map();
|
|
66985
|
+
getExistingPartialArgs(id) {
|
|
66986
|
+
return this._lazyPartials.has(id) ? this._lazyPartials.get(id).args : undefined;
|
|
66987
|
+
}
|
|
66717
66988
|
registerPartial(args, callback) {
|
|
66718
66989
|
if (args.height === 0) {
|
|
66719
66990
|
return;
|
|
@@ -66732,7 +67003,11 @@ class ScoreLayout {
|
|
|
66732
67003
|
}
|
|
66733
67004
|
else {
|
|
66734
67005
|
// in case of lazy loading -> first register lazy, then notify
|
|
66735
|
-
|
|
67006
|
+
const partial = {
|
|
67007
|
+
args,
|
|
67008
|
+
renderCallback: callback
|
|
67009
|
+
};
|
|
67010
|
+
this._lazyPartials.set(args.id, partial);
|
|
66736
67011
|
this.renderer.partialLayoutFinished.trigger(args);
|
|
66737
67012
|
}
|
|
66738
67013
|
}
|
|
@@ -67027,7 +67302,7 @@ class ScoreLayout {
|
|
|
67027
67302
|
glyph.textAlign = TextAlign.Left;
|
|
67028
67303
|
}
|
|
67029
67304
|
}
|
|
67030
|
-
|
|
67305
|
+
_layoutAndRenderAnnotation(y) {
|
|
67031
67306
|
// attention, you are not allowed to remove change this notice within any version of this library without permission!
|
|
67032
67307
|
const msg = 'rendered by alphaTab';
|
|
67033
67308
|
const resources = this.renderer.settings.display.resources;
|
|
@@ -67091,6 +67366,12 @@ class HorizontalScreenLayout extends ScoreLayout {
|
|
|
67091
67366
|
}
|
|
67092
67367
|
doResize() {
|
|
67093
67368
|
}
|
|
67369
|
+
doUpdateForBars(_renderHints) {
|
|
67370
|
+
// not supported yet, modifications likely cause anyhow full updates
|
|
67371
|
+
// as we do not optimize effect bands yet. with effect bands being more
|
|
67372
|
+
// isolated in bars we could try updating dynamically
|
|
67373
|
+
return false;
|
|
67374
|
+
}
|
|
67094
67375
|
doLayoutAndRender(renderHints) {
|
|
67095
67376
|
const score = this.renderer.score;
|
|
67096
67377
|
let startIndex = this.renderer.settings.display.startBar;
|
|
@@ -67104,6 +67385,11 @@ class HorizontalScreenLayout extends ScoreLayout {
|
|
|
67104
67385
|
endBarIndex = startIndex + endBarIndex - 1; // map count to array index
|
|
67105
67386
|
endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex));
|
|
67106
67387
|
this._system = this.createEmptyStaffSystem(0);
|
|
67388
|
+
// Each bar in horizontal layout is sized independently (by bar.displayWidth or the bar's
|
|
67389
|
+
// intrinsic width), so there is no shared staff width to distribute across bars. Keep each
|
|
67390
|
+
// bar's spring constants referenced against its own local minimum-duration so rendering
|
|
67391
|
+
// matches the historical per-bar behaviour.
|
|
67392
|
+
this._system.shareMinDurationAcrossBars = false;
|
|
67107
67393
|
this._system.isLast = true;
|
|
67108
67394
|
this._system.x = this.pagePadding[0];
|
|
67109
67395
|
this._system.y = this.pagePadding[1];
|
|
@@ -67165,7 +67451,7 @@ class HorizontalScreenLayout extends ScoreLayout {
|
|
|
67165
67451
|
currentBarIndex += partial.masterBars.length;
|
|
67166
67452
|
}
|
|
67167
67453
|
this.height = this.layoutAndRenderBottomScoreInfo(this.height);
|
|
67168
|
-
this.height = this.
|
|
67454
|
+
this.height = this._layoutAndRenderAnnotation(this.height);
|
|
67169
67455
|
this.height += this.pagePadding[3];
|
|
67170
67456
|
this.height *= this.renderer.settings.display.scale;
|
|
67171
67457
|
}
|
|
@@ -67232,11 +67518,16 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67232
67518
|
_allMasterBarRenderers = [];
|
|
67233
67519
|
_barsFromPreviousSystem = [];
|
|
67234
67520
|
_reuseViewPort = false;
|
|
67521
|
+
_preSystemPartialIds = [];
|
|
67522
|
+
_systemPartialIds = [];
|
|
67235
67523
|
doLayoutAndRender(renderHints) {
|
|
67236
67524
|
let y = this.pagePadding[1];
|
|
67237
67525
|
this.width = this.renderer.width;
|
|
67238
67526
|
this._allMasterBarRenderers = [];
|
|
67527
|
+
this._preSystemPartialIds = [];
|
|
67528
|
+
this._systemPartialIds = [];
|
|
67239
67529
|
this._reuseViewPort = renderHints?.reuseViewport ?? false;
|
|
67530
|
+
this._systems = [];
|
|
67240
67531
|
//
|
|
67241
67532
|
// 1. Score Info
|
|
67242
67533
|
y = this._layoutAndRenderScoreInfo(y, -1);
|
|
@@ -67248,15 +67539,23 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67248
67539
|
y = this._layoutAndRenderChordDiagrams(y, -1);
|
|
67249
67540
|
//
|
|
67250
67541
|
// 4. One result per StaffSystem
|
|
67251
|
-
y = this._layoutAndRenderScore(y);
|
|
67542
|
+
y = this._layoutAndRenderScore(y, this.firstBarIndex);
|
|
67252
67543
|
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67253
|
-
y = this.
|
|
67544
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67254
67545
|
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67255
67546
|
}
|
|
67256
67547
|
registerPartial(args, callback) {
|
|
67257
67548
|
args.reuseViewport = this._reuseViewPort;
|
|
67258
67549
|
super.registerPartial(args, callback);
|
|
67259
67550
|
}
|
|
67551
|
+
reregisterPartial(id) {
|
|
67552
|
+
const args = this.getExistingPartialArgs(id);
|
|
67553
|
+
if (!args) {
|
|
67554
|
+
return;
|
|
67555
|
+
}
|
|
67556
|
+
args.reuseViewport = this._reuseViewPort;
|
|
67557
|
+
this.renderer.partialLayoutFinished.trigger(args);
|
|
67558
|
+
}
|
|
67260
67559
|
get supportsResize() {
|
|
67261
67560
|
return true;
|
|
67262
67561
|
}
|
|
@@ -67267,6 +67566,47 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67267
67566
|
}
|
|
67268
67567
|
return x;
|
|
67269
67568
|
}
|
|
67569
|
+
doUpdateForBars(renderHints) {
|
|
67570
|
+
this._reuseViewPort = renderHints.reuseViewport ?? false;
|
|
67571
|
+
const firstModifiedMasterBar = renderHints.firstChangedMasterBar;
|
|
67572
|
+
// first update existing systems as needed
|
|
67573
|
+
const systemIndex = this._systems.findIndex(s => {
|
|
67574
|
+
const first = s.masterBarsRenderers[0].masterBar.index;
|
|
67575
|
+
const last = s.masterBarsRenderers[s.masterBarsRenderers.length - 1].masterBar.index;
|
|
67576
|
+
return first <= firstModifiedMasterBar && firstModifiedMasterBar <= last;
|
|
67577
|
+
});
|
|
67578
|
+
if (systemIndex === -1 || !this.renderer.settings.core.enableLazyLoading) {
|
|
67579
|
+
return false;
|
|
67580
|
+
}
|
|
67581
|
+
// Bars from the start of the re-layouted system onward will be re-registered during the
|
|
67582
|
+
// paint pass. Clear their old entries from the preserved BoundsLookup so registration
|
|
67583
|
+
// produces a clean, complete lookup after this render finishes.
|
|
67584
|
+
const firstRebuiltBarIndex = this._systems[systemIndex].masterBarsRenderers[0].masterBar.index;
|
|
67585
|
+
this.renderer.boundsLookup.clearFromMasterBar(firstRebuiltBarIndex);
|
|
67586
|
+
// for now we do a full relayout from the first modified masterbar
|
|
67587
|
+
// there is a lot of room for even more performant updates, but they come
|
|
67588
|
+
// at a risk that features break.
|
|
67589
|
+
// e.g. we could only shift systems where the content didn't change,
|
|
67590
|
+
// but we might still have ties/slurs which have to be updated.
|
|
67591
|
+
const removeSystems = this._systems.splice(systemIndex, this._systems.length - systemIndex);
|
|
67592
|
+
this._systemPartialIds.splice(systemIndex, this._systemPartialIds.length - systemIndex);
|
|
67593
|
+
const system = removeSystems[0];
|
|
67594
|
+
let y = system.y;
|
|
67595
|
+
const firstBarIndex = system.masterBarsRenderers[0].masterBar.index;
|
|
67596
|
+
// signal all partials which didn't change
|
|
67597
|
+
for (const preSystemPartial of this._preSystemPartialIds) {
|
|
67598
|
+
this.reregisterPartial(preSystemPartial);
|
|
67599
|
+
}
|
|
67600
|
+
for (let i = 0; i < systemIndex; i++) {
|
|
67601
|
+
this.reregisterPartial(this._systemPartialIds[i]);
|
|
67602
|
+
}
|
|
67603
|
+
// new partials for all other prats
|
|
67604
|
+
y = this._layoutAndRenderScore(y, firstBarIndex);
|
|
67605
|
+
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67606
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67607
|
+
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67608
|
+
return true;
|
|
67609
|
+
}
|
|
67270
67610
|
doResize() {
|
|
67271
67611
|
let y = this.pagePadding[1];
|
|
67272
67612
|
this.width = this.renderer.width;
|
|
@@ -67285,7 +67625,7 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67285
67625
|
// 4. One result per StaffSystem
|
|
67286
67626
|
y = this._resizeAndRenderScore(y, oldHeight);
|
|
67287
67627
|
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67288
|
-
y = this.
|
|
67628
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67289
67629
|
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67290
67630
|
}
|
|
67291
67631
|
_layoutAndRenderTunings(y, totalHeight = -1) {
|
|
@@ -67309,6 +67649,7 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67309
67649
|
canvas.textAlign = TextAlign.Center;
|
|
67310
67650
|
this.tuningGlyph.paint(0, 0, canvas);
|
|
67311
67651
|
});
|
|
67652
|
+
this._preSystemPartialIds.push(e.id);
|
|
67312
67653
|
return y + tuningHeight;
|
|
67313
67654
|
}
|
|
67314
67655
|
_layoutAndRenderChordDiagrams(y, totalHeight = -1) {
|
|
@@ -67332,6 +67673,7 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67332
67673
|
canvas.textAlign = TextAlign.Center;
|
|
67333
67674
|
this.chordDiagrams.paint(0, 0, canvas);
|
|
67334
67675
|
});
|
|
67676
|
+
this._preSystemPartialIds.push(e.id);
|
|
67335
67677
|
return y + diagramHeight;
|
|
67336
67678
|
}
|
|
67337
67679
|
_layoutAndRenderScoreInfo(y, totalHeight = -1) {
|
|
@@ -67374,12 +67716,14 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67374
67716
|
g.paint(0, 0, canvas);
|
|
67375
67717
|
}
|
|
67376
67718
|
});
|
|
67719
|
+
this._preSystemPartialIds.push(e.id);
|
|
67377
67720
|
}
|
|
67378
67721
|
return y + infoHeight;
|
|
67379
67722
|
}
|
|
67380
67723
|
_resizeAndRenderScore(y, oldHeight) {
|
|
67381
67724
|
// if we have a fixed number of bars per row, we only need to refit them.
|
|
67382
67725
|
const barsPerRowActive = this.getBarsPerSystem(0) > 0;
|
|
67726
|
+
this._systemPartialIds = [];
|
|
67383
67727
|
if (barsPerRowActive) {
|
|
67384
67728
|
for (let i = 0; i < this._systems.length; i++) {
|
|
67385
67729
|
const system = this._systems[i];
|
|
@@ -67442,11 +67786,9 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67442
67786
|
}
|
|
67443
67787
|
return y;
|
|
67444
67788
|
}
|
|
67445
|
-
_layoutAndRenderScore(y) {
|
|
67446
|
-
const startIndex = this.firstBarIndex;
|
|
67789
|
+
_layoutAndRenderScore(y, startIndex) {
|
|
67447
67790
|
let currentBarIndex = startIndex;
|
|
67448
67791
|
const endBarIndex = this.lastBarIndex;
|
|
67449
|
-
this._systems = [];
|
|
67450
67792
|
while (currentBarIndex <= endBarIndex) {
|
|
67451
67793
|
// create system and align set proper coordinates
|
|
67452
67794
|
const system = this._createStaffSystem(currentBarIndex, endBarIndex);
|
|
@@ -67481,6 +67823,7 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67481
67823
|
// since we use partial drawing
|
|
67482
67824
|
system.paint(0, -(args.y / this.renderer.settings.display.scale), canvas);
|
|
67483
67825
|
});
|
|
67826
|
+
this._systemPartialIds.push(args.id);
|
|
67484
67827
|
// calculate coordinates for next system
|
|
67485
67828
|
return height;
|
|
67486
67829
|
}
|
|
@@ -67488,6 +67831,10 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67488
67831
|
* Realignes the bars in this line according to the available space
|
|
67489
67832
|
*/
|
|
67490
67833
|
_fitSystem(system) {
|
|
67834
|
+
// If a bar added late in the assembly introduced a shorter note than earlier bars, the
|
|
67835
|
+
// earlier bars' spring constants (and the cached system widths / totals) are stale.
|
|
67836
|
+
// Reconcile now - it's a no-op when nothing changed.
|
|
67837
|
+
system.reconcileMinDurationIfDirty();
|
|
67491
67838
|
if (system.isFull || system.width > this._maxWidth || this.renderer.settings.display.justifyLastSystem) {
|
|
67492
67839
|
this._scaleToWidth(system, this._maxWidth);
|
|
67493
67840
|
}
|
|
@@ -67499,29 +67846,35 @@ class VerticalLayoutBase extends ScoreLayout {
|
|
|
67499
67846
|
_scaleToWidth(system, width) {
|
|
67500
67847
|
const staffWidth = width - system.accoladeWidth;
|
|
67501
67848
|
const shouldApplyBarScale = this.shouldApplyBarScale;
|
|
67502
|
-
|
|
67503
|
-
//
|
|
67504
|
-
//
|
|
67505
|
-
//
|
|
67506
|
-
//
|
|
67507
|
-
//
|
|
67508
|
-
|
|
67509
|
-
|
|
67849
|
+
// Industry fixed-overhead model (Behind Bars, Dorico, Finale, Sibelius, MuseScore, Guitar Pro):
|
|
67850
|
+
// prefix/postfix glyphs (clef, key sig, time sig, barlines) are treated as fixed overhead and the
|
|
67851
|
+
// remaining staff width is distributed across bars by a per-bar weight.
|
|
67852
|
+
//
|
|
67853
|
+
// distributable = staffWidth - totalFixedOverhead
|
|
67854
|
+
// contentShare = distributable / sum(weight)
|
|
67855
|
+
// bar.width = bar.maxFixedOverhead + weight * contentShare
|
|
67856
|
+
//
|
|
67857
|
+
// The weight depends on the layout mode:
|
|
67858
|
+
// - shouldApplyBarScale=true -> weight = bar.displayScale (model-driven, matches Guitar Pro)
|
|
67859
|
+
// displayScale defaults to 1, so an unset value behaves identically
|
|
67860
|
+
// to an explicit 1 (GP omits the property when not customized).
|
|
67861
|
+
// - shouldApplyBarScale=false -> weight = natural content width (automatic, ignores displayScale)
|
|
67862
|
+
//
|
|
67863
|
+
// Per-bar maxFixedOverhead / maxContentWidth and the system-wide totals are maintained incrementally
|
|
67864
|
+
// in StaffSystem._applyLayoutAndUpdateWidth / revertLastBar so this pass can apply directly.
|
|
67865
|
+
const weightTotal = shouldApplyBarScale ? system.totalBarDisplayScale : system.totalContentWidth;
|
|
67866
|
+
const distributable = Math.max(0, staffWidth - system.totalFixedOverhead);
|
|
67867
|
+
const contentShare = weightTotal > 0 ? distributable / weightTotal : 0;
|
|
67510
67868
|
for (const s of system.allStaves) {
|
|
67511
67869
|
s.resetSharedLayoutData();
|
|
67512
|
-
// scale the bars by keeping their respective ratio size
|
|
67513
67870
|
let w = 0;
|
|
67514
|
-
for (
|
|
67871
|
+
for (let i = 0; i < s.barRenderers.length; i++) {
|
|
67872
|
+
const renderer = s.barRenderers[i];
|
|
67873
|
+
const mb = system.masterBarsRenderers[i];
|
|
67515
67874
|
renderer.x = w;
|
|
67516
67875
|
renderer.y = s.topPadding + s.topOverflow;
|
|
67517
|
-
|
|
67518
|
-
|
|
67519
|
-
const barDisplayScale = system.getBarDisplayScale(renderer);
|
|
67520
|
-
actualBarWidth = (barDisplayScale * staffWidth) / totalScale;
|
|
67521
|
-
}
|
|
67522
|
-
else {
|
|
67523
|
-
actualBarWidth = renderer.computedWidth + spacePerBar;
|
|
67524
|
-
}
|
|
67876
|
+
const weight = shouldApplyBarScale ? system.getBarDisplayScale(renderer) : mb.maxContentWidth;
|
|
67877
|
+
const actualBarWidth = mb.maxFixedOverhead + weight * contentShare;
|
|
67525
67878
|
renderer.scaleToWidth(actualBarWidth);
|
|
67526
67879
|
w += renderer.width;
|
|
67527
67880
|
}
|
|
@@ -69345,12 +69698,24 @@ class LineBarRenderer extends BarRendererBase {
|
|
|
69345
69698
|
const firstNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(firstNonRestBeat);
|
|
69346
69699
|
const lastNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(lastNonRestBeat);
|
|
69347
69700
|
const direction = this.getTupletBeamDirection(firstNonRestBeamingHelper);
|
|
69348
|
-
let startY
|
|
69349
|
-
let endY
|
|
69701
|
+
let startY;
|
|
69702
|
+
let endY;
|
|
69350
69703
|
if (isRestOnly) {
|
|
69351
|
-
|
|
69704
|
+
// rests have no stems, so anchor to the actual rest glyph bounds
|
|
69705
|
+
// instead of a stem-adjusted flag position (which would place the bracket
|
|
69706
|
+
// a full quarter-stem length away from the rests).
|
|
69707
|
+
if (direction === BeamDirection.Up) {
|
|
69708
|
+
startY = Math.min(this.getRestY(firstNonRestBeat, NoteYPosition.Top), this.getRestY(lastNonRestBeat, NoteYPosition.Top));
|
|
69709
|
+
}
|
|
69710
|
+
else {
|
|
69711
|
+
startY = Math.max(this.getRestY(firstNonRestBeat, NoteYPosition.Bottom), this.getRestY(lastNonRestBeat, NoteYPosition.Bottom));
|
|
69712
|
+
}
|
|
69352
69713
|
endY = startY;
|
|
69353
69714
|
}
|
|
69715
|
+
else {
|
|
69716
|
+
startY = this.calculateBeamYWithDirection(firstNonRestBeamingHelper, startX, direction);
|
|
69717
|
+
endY = this.calculateBeamYWithDirection(lastNonRestBeamingHelper, endX, direction);
|
|
69718
|
+
}
|
|
69354
69719
|
// align line centered in available space
|
|
69355
69720
|
if (direction === BeamDirection.Down) {
|
|
69356
69721
|
startY += shift;
|
|
@@ -69720,7 +70085,30 @@ class LineBarRenderer extends BarRendererBase {
|
|
|
69720
70085
|
let minNoteY = 0;
|
|
69721
70086
|
for (const v of this.helpers.beamHelpers) {
|
|
69722
70087
|
for (const h of v) {
|
|
69723
|
-
if (!this.shouldPaintBeamingHelper(h))
|
|
70088
|
+
if (!this.shouldPaintBeamingHelper(h)) {
|
|
70089
|
+
// beam is not drawn, but a rest-only tuplet still draws a bracket
|
|
70090
|
+
// anchored to the rest glyph bounds and needs overflow reserved.
|
|
70091
|
+
if (h.hasTuplet && h.isRestBeamHelper) {
|
|
70092
|
+
const tupletGroup = h.beats[0].tupletGroup;
|
|
70093
|
+
const tupletFirst = tupletGroup.beats[0];
|
|
70094
|
+
const tupletLast = tupletGroup.beats[tupletGroup.beats.length - 1];
|
|
70095
|
+
const tupletDirection = this.getTupletBeamDirection(h);
|
|
70096
|
+
if (tupletDirection === BeamDirection.Up) {
|
|
70097
|
+
const restTop = Math.min(this.getRestY(tupletFirst, NoteYPosition.Top), this.getRestY(tupletLast, NoteYPosition.Top));
|
|
70098
|
+
const topY = restTop - this.tupletSize - this.tupletOffset;
|
|
70099
|
+
if (topY < maxNoteY) {
|
|
70100
|
+
maxNoteY = topY;
|
|
70101
|
+
}
|
|
70102
|
+
}
|
|
70103
|
+
else {
|
|
70104
|
+
const restBottom = Math.max(this.getRestY(tupletFirst, NoteYPosition.Bottom), this.getRestY(tupletLast, NoteYPosition.Bottom));
|
|
70105
|
+
const bottomY = restBottom + this.tupletSize + this.tupletOffset;
|
|
70106
|
+
if (bottomY > minNoteY) {
|
|
70107
|
+
minNoteY = bottomY;
|
|
70108
|
+
}
|
|
70109
|
+
}
|
|
70110
|
+
}
|
|
70111
|
+
}
|
|
69724
70112
|
else if (h.beats.length === 1 && h.beats[0].duration >= Duration.Half) {
|
|
69725
70113
|
const tupletDirection = this.getTupletBeamDirection(h);
|
|
69726
70114
|
const direction = this.getBeamDirection(h);
|