finishLayout method
Implementation
void finishLayout(ChantContext ctxt) {
final theme = ctxt.theme;
ledgerLines = [];
final notations = score.notations;
final lastIndex = notationsStartIndex + numNotationsOnLine;
void processElementForLedgerLine(
ChantLayoutElement element, {
Note? endElem,
int? staffPosition,
double? offsetX,
}) {
final sp = staffPosition ?? (element as dynamic).staffPosition;
final ox = offsetX ?? (element is Note ? element.neume!.bounds.x : 0);
final ee = endElem ?? element;
final ledgerLinePositionAbove = ctxt.staffLineCount * 2 + 1;
if (sp >= ledgerLinePositionAbove || sp <= -1) {
var x1 = ox + element.bounds.x - ctxt.intraNeumeSpacing;
final x2 = ox + ee.bounds.x + ee.bounds.width + ctxt.intraNeumeSpacing;
int roundedStaffPosition = sp;
if (sp > 0) {
roundedStaffPosition = sp - ((sp - 1) % 2);
} else {
roundedStaffPosition = sp - ((sp + 1) % 2);
}
final minLedgerSeparation =
ctxt.staffInterval * theme.minLedgerSeparation;
if (ledgerLines.isNotEmpty &&
ledgerLines.last.x2 + minLedgerSeparation >= x1) {
final half = (x1 - ledgerLines.last.x2) / 2;
ledgerLines.last.x2 += half;
x1 -= half;
}
double finalX2 = x2;
if (finalX2 > staffRight) finalX2 = staffRight;
ledgerLines.add(LedgerLinePos(x1, finalX2, roundedStaffPosition));
}
}
List<HorizontalEpisema> episemata = [];
BracePoint? startBrace;
double minY = double.infinity;
double maxY = double.negativeInfinity;
void positionNonLyricText(
TextElement text,
ChantNotationElement neume, {
double? rightX,
}) {
final initialX = text.bounds.x;
text.setMaxWidth(ctxt, staffRight);
text.bounds = text.bounds.copyWith(x: initialX);
if (rightX != null) {
text.bounds = text.bounds.copyWith(
x: (text.bounds.x + rightX - text.bounds.width) / 2,
);
}
final beyondStaffRight = neume.bounds.x + text.bounds.right - staffRight;
if (beyondStaffRight > 0) {
text.bounds = text.bounds.copyWith(x: text.bounds.x - beyondStaffRight);
}
if (neume.bounds.x + text.bounds.x < 0) {
text.bounds = text.bounds.copyWith(x: -neume.bounds.x);
}
}
int i; // TODO: clean up if(startBrace...) section
for (i = notationsStartIndex; i < lastIndex; i++) {
final neume = notations[i];
minY = (minY < neume.bounds.y) ? minY : neume.bounds.y;
maxY = (maxY > neume.bounds.bottom) ? maxY : neume.bounds.bottom;
if (neume is Custos) {
processElementForLedgerLine(neume);
continue;
}
if (neume.alText.isNotEmpty) {
for (int j = 0; j < neume.alText.length; j++) {
positionNonLyricText(neume.alText[j], neume);
}
}
if (neume.translationText.isNotEmpty) {
for (int j = 0; j < neume.translationText.length; j++) {
final text = neume.translationText[j];
if (text.endNeume != null) {
double rightX = (text.endNeume!.hasLyrics)
? text.endNeume!.bounds.x +
text.endNeume!.lyrics
.map((l) => l.bounds.right)
.reduce(math.max)
: text.endNeume!.bounds.right;
rightX -= neume.bounds.x;
positionNonLyricText(text, neume, rightX: rightX);
} else {
positionNonLyricText(text, neume);
}
}
}
if (neume is! Neume) continue;
for (int j = 0; j < neume.ledgerLines.length; j++) {
final ll = neume.ledgerLines[j];
processElementForLedgerLine(
ll.element,
endElem: ll.endElem,
staffPosition: ll.staffPosition,
);
}
for (int j = 0; j < neume.notes.length; j++) {
final note = neume.notes[j];
if (note.episemata.isEmpty) episemata = [];
for (int k = 0; k < note.episemata.length; k++) {
final episema = note.episemata[k];
double spaceBetweenEpisemata = 0;
if (episemata.isNotEmpty) {
spaceBetweenEpisemata =
neume.bounds.x +
episema.bounds.x -
(episemata.last.note.neume!.bounds.x +
episemata.last.bounds.right);
}
if (episemata.isEmpty ||
episemata.last.positionHint != episema.positionHint ||
episemata.last.terminating == true ||
episemata.last.alignment == HorizontalEpisemaAlignment.left ||
episemata.last.alignment == HorizontalEpisemaAlignment.center ||
episema.alignment == HorizontalEpisemaAlignment.right ||
episema.alignment == HorizontalEpisemaAlignment.center ||
(spaceBetweenEpisemata > ctxt.intraNeumeSpacing * 2 &&
note.glyphVisualizer!.glyphCode != GlyphCode.none)) {
episemata = [episema];
} else {
double newY;
if (episema.positionHint == MarkingPositionHint.below) {
newY = (episema.bounds.y > episemata.last.bounds.y)
? episema.bounds.y
: episemata.last.bounds.y;
} else {
newY = (episema.bounds.y < episemata.last.bounds.y)
? episema.bounds.y
: episemata.last.bounds.y;
}
if (episema.bounds.y != newY) {
episema.bounds = episema.bounds.copyWith(y: newY);
} else {
for (int l = 0; l < episemata.length; l++) {
episemata[l].bounds = episemata[l].bounds.copyWith(y: newY);
}
}
double newWidth =
neume.bounds.x +
episema.bounds.x -
(episemata.last.note.neume!.bounds.x + episemata.last.bounds.x);
if (newWidth < 0) {
newWidth *= -1;
episemata.last.bounds = episemata.last.bounds.copyWith(
x: episemata.last.bounds.x - newWidth,
);
}
episemata.last.bounds = episemata.last.bounds.copyWith(
width: newWidth,
);
episemata.add(episema);
}
}
if (note.braceEnd != null) handleEndBrace(ctxt, note, i);
if (note.braceStart != null) {
ctxt.lastStartBrace = startBrace = note.braceStart;
startBrace!.notationIndex = i;
}
}
}
if (startBrace != null) {
if (custos != null) {
final nextNotation = notations[lastIndex];
final nextNote = (nextNotation is Neume) ? nextNotation.notes[0] : null;
final nextNotationButOne = (lastIndex + 1 < notations.length)
? notations[lastIndex + 1]
: null;
final nextNoteButOne = (nextNotationButOne is Neume)
? nextNotationButOne.notes[0]
: null;
final braceEnd =
(nextNote?.braceEnd) ??
(nextNotation is Accidental ? nextNoteButOne?.braceEnd : null);
if (braceEnd != null) {
custos!.braceEnd = braceEnd;
handleEndBrace(ctxt, custos!, i);
} else {
custos!.braceEnd = BracePoint(
custos!,
startBrace.isAbove,
startBrace.shape,
BraceAttachment.right,
);
handleEndBrace(ctxt, custos!, i - 1);
ctxt.lastStartBrace = BracePoint(
null,
startBrace.isAbove,
startBrace.shape,
BraceAttachment.left,
);
ctxt.lastStartBrace!.notationIndex = i;
}
}
}
if (custos != null) processElementForLedgerLine(custos!);
}