worldhopper/lib/screens/reader/page_grouping.dart

109 lines
3.1 KiB
Dart

import 'package:worldhopper/providers/page_dimensions_provider.dart';
/// Represents a spread (one or two pages displayed together).
class PageSpread {
/// Page indices in display order (left to right on screen).
final List<int> pages;
const PageSpread(this.pages);
bool get isSingle => pages.length == 1;
bool get isPair => pages.length == 2;
/// The lowest page index in this spread (used for progress tracking).
int get primaryPage => pages.reduce((a, b) => a < b ? a : b);
/// The highest page index in this spread.
int get lastPage => pages.reduce((a, b) => a > b ? a : b);
}
/// Computes page spreads based on known dimensions.
///
/// Rules:
/// - If [firstPageIsCover], page 0 is shown alone
/// - Landscape images (aspect ratio >= threshold) shown alone
/// - Two consecutive portrait pages paired side by side
/// - Look-ahead: if current is portrait but next is landscape, show current alone
/// - Last page shown alone if unpaired
/// - [rtl]: pages within a pair are reversed (higher index on left)
List<PageSpread> computePageSpreads({
required int pageCount,
required Map<int, PageDimension> dimensions,
required bool rtl,
bool firstPageIsCover = true,
}) {
if (pageCount == 0) return [];
final spreads = <PageSpread>[];
var i = 0;
while (i < pageCount) {
// Cover page shown alone
if (i == 0 && firstPageIsCover) {
spreads.add(const PageSpread([0]));
i++;
continue;
}
final currentDim = dimensions[i];
final isCurrentDouble = currentDim?.isDoublePage ?? false;
// Landscape/double-page images shown alone
if (isCurrentDouble) {
spreads.add(PageSpread([i]));
i++;
continue;
}
// Last page with no partner
if (i + 1 >= pageCount) {
spreads.add(PageSpread([i]));
i++;
continue;
}
// Look ahead: if next page is landscape, show current alone
final nextDim = dimensions[i + 1];
final isNextDouble = nextDim?.isDoublePage ?? false;
if (isNextDouble) {
spreads.add(PageSpread([i]));
i++;
continue;
}
// Pair two portrait pages
if (rtl) {
// RTL: higher-index page on the left
spreads.add(PageSpread([i + 1, i]));
} else {
spreads.add(PageSpread([i, i + 1]));
}
i += 2;
}
return spreads;
}
/// Returns the spread index that contains the given page index.
int spreadIndexForPage(List<PageSpread> spreads, int pageIndex) {
for (var i = 0; i < spreads.length; i++) {
if (spreads[i].pages.contains(pageIndex)) {
return i;
}
}
// Fallback: return last spread
return spreads.isEmpty ? 0 : spreads.length - 1;
}
/// Returns the primary (lowest) page index for a given spread index.
int primaryPageForSpread(List<PageSpread> spreads, int spreadIndex) {
if (spreadIndex < 0 || spreadIndex >= spreads.length) return 0;
return spreads[spreadIndex].primaryPage;
}
/// Returns the last (highest) page index for a given spread index.
int lastPageInSpread(List<PageSpread> spreads, int spreadIndex) {
if (spreadIndex < 0 || spreadIndex >= spreads.length) return 0;
return spreads[spreadIndex].lastPage;
}