performLayout method

void performLayout(
  1. ChantContext ctxt
)

Implementation

void performLayout(ChantContext ctxt) {
  final staffSpaces = this.staffSpaces;
  final staffLineCount = score.staffLineCount;
  final theme = ctxt.theme;
  notationBounds = Rect.fromXYWH(
    staffLeft,
    -(ctxt.staffLineWeight / 2 +
            staffLineCount * 2 -
            1 +
            theme.minSpaceAboveStaff) *
        ctxt.staffInterval,
    staffRight - staffLeft,
    (ctxt.staffLineWeight + (staffSpaces * 2) + theme.minSpaceAboveStaff) *
        ctxt.staffInterval,
  );

  var notations = score.notations;
  int lastNeumeIndex = extraTextOnlyIndex == null
      ? notationsStartIndex + numNotationsOnLine
      : extraTextOnlyIndex!;
  int lastIndex = notationsStartIndex + numNotationsOnLine;
  ChantNotationElement notation;

  notationBounds += startingClef!.bounds;

  lyricLineHeight =
      ctxt.textStyles['lyric']!['size'] *
      (ctxt.textStyles['lyric']!['lineHeight'] ?? 1.1);
  lyricLineBaseline = 0;
  numLyricLines = 0;

  altLineHeight = 0;
  altLineBaseline = 0;
  numAltLines = 0;

  translationLineHeight =
      ctxt.textStyles['translation']!['size'] *
      (ctxt.textStyles['translation']!['lineHeight'] ?? 1.1);
  translationLineBaseline = 0;
  numTranslationLines = 0;

  final aboveLinesLineHeight =
      ctxt.textStyles['al']!['size'] *
      (ctxt.textStyles['al']!['lineHeight'] ?? 1.1);

  for (int i = notationsStartIndex; i < lastNeumeIndex; i++) {
    notation = notations[i];

    if (notation.bounds.height != 0 || notation.bounds.width != 0) {
      notationBounds += notation.bounds;
    }

    if (notation.lyrics.isNotEmpty && notation.lyrics[0].text.isNotEmpty) {
      if (notation.lyrics[0].origin.y > lyricLineBaseline) {
        lyricLineBaseline = notation.lyrics[0].origin.y;
      }
      if (notation.lyrics.length > numLyricLines) {
        numLyricLines = notation.lyrics.length;
      }
    }

    if (notation.alText.isNotEmpty && numAltLines < notation.alText.length) {
      if (notation.alText[0].bounds.height > altLineHeight) {
        altLineHeight = notation.alText[0].bounds.height;
      }
      if (notation.alText[0].origin.y > altLineBaseline) {
        altLineBaseline = notation.alText[0].origin.y;
      }
      if (notation.alText.length > numAltLines) {
        numAltLines = notation.alText.length;
      }
    }

    if (notation.translationText.isNotEmpty &&
        notation.translationText[0].text.isNotEmpty) {
      if (notation.translationText[0].origin.y > translationLineBaseline) {
        translationLineBaseline = notation.translationText[0].origin.y;
      }
      if (notation.translationText.length > numTranslationLines) {
        numTranslationLines = notation.translationText.length;
      }
    }
  }

  if (custos != null) notationBounds += custos!.bounds;

  for (var brace in braces) {
    notationBounds += brace.bounds;
  }

  final notationBoundsOffset =
      notationBounds.bottom + theme.minSpaceBelowStaff * ctxt.staffInterval;
  lyricLineBaseline += notationBoundsOffset;
  translationLineBaseline += notationBoundsOffset;
  altLineBaseline +=
      notationBounds.y - altLineHeight - ctxt.staffInterval * 0.5;

  for (int i = notationsStartIndex; i < lastNeumeIndex; i++) {
    notation = notations[i];
    double offset = 0;
    for (int j = 0; j < notation.lyrics.length; j++) {
      notation.lyrics[j].bounds = notation.lyrics[j].bounds.copyWith(
        y: offset + lyricLineBaseline,
      );
      offset += lyricLineHeight;
    }

    for (int j = 0; j < notation.translationText.length; j++) {
      notation.translationText[j].bounds = notation.translationText[j].bounds
          .copyWith(y: offset + translationLineBaseline);
      offset += translationLineHeight;
    }

    offset = 0;
    for (int j = 0; j < notation.alText.length; j++) {
      notation.alText[j].bounds = notation.alText[j].bounds.copyWith(
        y: offset + altLineBaseline,
      );
      offset -= aboveLinesLineHeight;
    }
  }

  extraTextOnlyHeight = 0;
  if (ctxt.useExtraTextOnly) {
    int extraTextOnlyLyricIndex = this.extraTextOnlyLyricIndex;
    if (extraTextOnlyIndex == null) {
      var lastNotation = lastNeumeIndex > 0
          ? notations[lastNeumeIndex - 1]
          : null;
      if (lastNotation is ChantLineBreak) {
        lastNotation = (lastNeumeIndex > 1)
            ? notations[lastNeumeIndex - 2]
            : null;
      }
      if (lastNotation is TextOnly &&
          lastNotation.lyrics.length == 1 &&
          lastNotation.lyrics[0].bounds.height > lyricLineHeight) {
        extraTextOnlyHeight = lyricLineHeight;
      }
    } else {
      Lyric lastLyric;
      double xOffset = 0;
      double offset = (numLyricLines - 1) * lyricLineHeight;
      offset += numTranslationLines * translationLineHeight;
      int extraLines = 0;
      for (int i = extraTextOnlyIndex!; i < lastIndex; i++) {
        notation = notations[i];
        if (notation.lyrics.length <= extraTextOnlyLyricIndex) continue;
        lastLyric = notation.lyrics[extraTextOnlyLyricIndex];
        if (lastLyric.lineWidth != null) {
          xOffset = staffRight - lastLyric.lineWidth!;
          offset += lyricLineHeight;
          extraLines++;
        }
        extraLines += lastLyric.numLines - 1;
        lastLyric.bounds = lastLyric.bounds.copyWith(
          y: offset + lyricLineBaseline,
        );
        notation.bounds = notation.bounds.copyWith(
          x: notation.bounds.x + xOffset,
        );
      }
      extraTextOnlyHeight = lyricLineHeight * extraLines;
    }
  }

  if (startingClef!.hasLyrics) {
    double offset = 0;
    for (int j = 0; j < startingClef!.lyrics.length; j++) {
      startingClef!.lyrics[j].bounds = startingClef!.lyrics[j].bounds
          .copyWith(y: offset + lyricLineBaseline);
      offset += lyricLineHeight;
    }
  }

  if (notationsStartIndex == 0) {
    // With a multi-line drop cap, the annotation is placed above it by
    // ChantScore.layoutChantLines once the drop cap baseline is known.
    final annotationTop = layoutAnnotation(
      ctxt,
      ctxt.theme.initialLines <= 1 ? lyricLineBaseline : null,
    );
    if (annotationTop != null && annotationTop < notationBounds.y) {
      notationBounds = notationBounds.copyWith(
        y: annotationTop,
        height: notationBounds.height + notationBounds.y - annotationTop,
      );
    }

    if (score.dropCap case DropCap dc) {
      dc.bounds = dc.bounds.copyWith(x: staffLeft / 2);
      // A drop cap spanning several lines is positioned by
      // ChantScore.layoutChantLines once all those lines are known.
      if (ctxt.theme.initialLines <= 1) {
        dc.bounds = dc.bounds.copyWith(
          y: lyricLineBaseline - dc.origin.y + dc.baselineOffset(ctxt),
        );
        notationBounds += dc.bounds;
        if (ctxt.textMeasurer.align == .baseline) {
          dc.bounds = dc.bounds.copyWith(y: lyricLineBaseline);
        }
      }
    }
  }

  if (numLyricLines > 0) {
    final lyricAndTextRect = Rect.fromXYWH(
      0,
      notationBoundsOffset,
      0,
      lyricLineHeight * numLyricLines +
          extraTextOnlyHeight +
          translationLineHeight * numTranslationLines,
    );
    notationBounds += lyricAndTextRect;
  }
  if (numAltLines > 0) {
    final altLineTextRect = Rect.fromXYWH(
      0,
      notationBounds.y -
          altLineHeight -
          0.5 * ctxt.staffInterval -
          aboveLinesLineHeight * (numAltLines - 1),
      0,
      aboveLinesLineHeight * numAltLines,
    );
    notationBounds += altLineTextRect;
  }
  notationBounds += Rect.fromXYWH(
    0,
    -ctxt.staffInterval,
    0,
    (ctxt.staffLineWeight / 2 + theme.minSpaceBelowStaff) *
        ctxt.staffInterval,
  );
  double totalHeight = notationBounds.height;

  bounds = bounds.copyWith(
    x: 0,
    y: notationBounds.y,
    width: notationBounds.right,
    height: totalHeight,
  );

  origin = Point(staffLeft, -notationBounds.y);
}