109 lines
3.1 KiB
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;
|
|
}
|