JJ_MKVMaker/lib/ui/row_fit.dart

99 lines
3.9 KiB
Dart

/// 한 줄 · 두 줄 배치 판단 (관리자 지시 10/21: 화면마다 따로 재지 않고 이것 하나를 부른다 - 82a87da P2, after_1020 에서 옮김).
///
/// 약속 ([fitRow] - 판단만, 그리지 않음)
/// - 입력: [maxWidth] 쓸 수 있는 폭, [first] · [second] 앞 · 뒤 묶음 아이템의 최소 폭 (단추는 기본 누를 자리 그대로),
/// [gap] 한 줄일 때 두 묶음 사이, 줄일 수 있는 아이템 하나의 폭 범위 [flexMin] ~ [flexMax] (없으면 0 · 0).
/// - 출력: [RowFit.split] 이면 두 줄 (위 = first, 아래 = second + 줄일 수 있는 아이템), [RowFit.flexWidth] 줄일 수 있는 아이템의 폭
/// (0 이면 그 줄에 넣지 않는다 - 같은 일을 하는 다른 단추가 남아 있어야 한다).
/// - 오류: 없음. 두 줄로도 [second] 가 들지 않으면 split = true · flexWidth = 0 이고 [RowFit.overflow] 에 모자란 폭.
///
/// 약속 ([FitButtonRows] - 그림): 앞 단추들 · 뒤 앞 단추들 · 줄일 수 있는 것 (폭을 받아 그림) · 뒤 끝 단추들 → 한 줄
/// (앞 · 간격 · 뒤 앞 · 줄일 것 · 빈칸 · 뒤 끝) 또는 두 줄 (위 앞 단추 고르게, 아래 뒤 묶음). 단추 크기는 줄이지 않는다.
/// 쓰는 곳: 재생 화면 조절 줄 (P2)
library;
import 'package:flutter/material.dart';
class RowFit {
final bool split;
final double flexWidth;
final double overflow;
const RowFit(this.split, this.flexWidth, this.overflow);
}
RowFit fitRow({
required double maxWidth,
required List<double> first,
required List<double> second,
double gap = 0,
double flexMin = 0,
double flexMax = 0,
bool forceSplit = false,
}) {
double sum(List<double> xs) => xs.fold(0.0, (a, b) => a + b);
final a = sum(first), b = sum(second);
final split = forceSplit || a + gap + b + flexMax > maxWidth;
final room = maxWidth - b - (split ? 0 : a + gap);
var flex = room.clamp(0.0, flexMax);
if (flex < flexMin) flex = 0;
final over = (split ? [a, b].reduce((x, y) => x > y ? x : y) : a + gap + b) - maxWidth;
return RowFit(split, flex, over > 0 ? over : 0);
}
class FitButtonRows extends StatelessWidget {
const FitButtonRows({
super.key,
required this.first,
required this.secondLead,
required this.secondTail,
required this.buttonWidth,
this.flex,
this.flexMin = 0,
this.flexMax = 0,
this.gap = 8,
this.forceSplit = false,
});
/// 위 줄 (두 줄일 때) · 앞 (한 줄일 때) 단추들
final List<Widget> first;
/// 뒤 묶음의 앞 (줄일 것 앞) · 끝 (빈칸 뒤) 단추들
final List<Widget> secondLead, secondTail;
/// 단추 하나의 누를 자리 (판단에 쓰는 폭)
final double buttonWidth;
/// 줄일 수 있는 것 (받은 폭으로 그림 - 예: 소리 막대). 폭이 0 이면 넣지 않는다
final Widget Function(double width)? flex;
final double flexMin, flexMax, gap;
/// 늘 두 줄 (손가락 화면)
final bool forceSplit;
@override
Widget build(BuildContext context) => LayoutBuilder(builder: (context, box) {
final fit = fitRow(
maxWidth: box.maxWidth,
first: List.filled(first.length, buttonWidth),
second: List.filled(secondLead.length + secondTail.length, buttonWidth),
gap: gap,
flexMin: flex == null ? 0 : flexMin,
flexMax: flex == null ? 0 : flexMax,
forceSplit: forceSplit,
);
final rest = [
...secondLead,
if (flex != null && fit.flexWidth > 0) flex!(fit.flexWidth),
const Spacer(),
...secondTail,
];
return Column(mainAxisSize: MainAxisSize.min, children: [
if (fit.split) Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: first),
Row(children: [
if (!fit.split) ...[...first, SizedBox(width: gap)],
...rest,
]),
]);
});
}