Files
BadNote/lib/editor/layout/double_page_layout.dart

76 lines
2.6 KiB
Dart
Raw Normal View History

// lib/editor/layout/double_page_layout.dart
//
// Two-up (spread) layout foundation for book-like reading (F2/F3 — the user's
// explicit "two-page spread" ask). Pages are grouped into spread ROWS; each row
// then stacks vertically exactly like continuous-single, so windowing reuses
// PageStackMetrics on the row heights.
//
// Pure geometry (no widgets / pdfrx): the row-mounting widget is device-gated;
// the pairing + row-height math is here, fully unit-tested. Building it ahead of
// its phase is zero-rework-risk (it is not rendering and the P0.5 perf gate
// can't invalidate pure geometry).
import 'page_viewport.dart';
import '../pdf/pdf_document_source.dart';
/// Groups page indices into two-up spread rows. With [coverAlone], page 0 sits
/// on its own row (a book cover / title page) and the rest pair 1-2, 3-4, …;
/// otherwise pages pair from 0. A trailing odd page occupies a single-page row.
List<List<int>> pairIntoRows(int pageCount, {bool coverAlone = false}) {
assert(pageCount >= 0);
final rows = <List<int>>[];
var i = 0;
if (coverAlone && pageCount > 0) {
rows.add(<int>[0]);
i = 1;
}
while (i < pageCount) {
if (i + 1 < pageCount) {
rows.add(<int>[i, i + 1]);
i += 2;
} else {
rows.add(<int>[i]);
i += 1;
}
}
return rows;
}
/// Height of each spread row when each page is fit to HALF of [columnWidth]
/// (two pages share the column). A row's height is the tallest of its pages so
/// both pages sit on a common baseline. Non-positive page widths contribute 0.
List<double> spreadRowHeights(
PageDocumentSource source,
double columnWidth,
List<List<int>> rows,
) {
assert(columnWidth >= 0);
final half = columnWidth / 2;
return rows.map((row) {
var tallest = 0.0;
for (final pageIndex in row) {
final size = source.pageSize(pageIndex);
if (size.width <= 0) continue;
final fit = half * (size.height / size.width);
if (fit > tallest) tallest = fit;
}
return tallest;
}).toList(growable: false);
}
/// Continuous-DOUBLE stacking metrics: pair pages into spread rows, then stack
/// the rows vertically. [PageStackMetrics.visibleRange] over the result yields
/// the visible ROW range; map rows back to pages via the [pairIntoRows] result.
PageStackMetrics spreadStackMetrics(
PageDocumentSource source,
double columnWidth, {
bool coverAlone = false,
double gap = 0.0,
}) {
final rows = pairIntoRows(source.pageCount, coverAlone: coverAlone);
return PageStackMetrics(
pageHeights: spreadRowHeights(source, columnWidth, rows),
gap: gap,
);
}