116 lines
5.4 KiB
Dart
116 lines
5.4 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../app/transfer_job.dart';
|
|
import '../../core/file_ops.dart' show formatSize;
|
|
import '../../core/vfs.dart' show vBasename;
|
|
import '../../l10n/tr.dart';
|
|
import '../theme.dart';
|
|
|
|
/// 약속 (E5d-4): 복사 · 이동 작업 하나의 진행 - 제목 (복사 · 옮기는 중 · 완료 · 멈춤 + 방법) · 대상 · [취소] (도는 동안).
|
|
/// 파일 탐색기: 위 = 전체 항목 중 · 아래 = 지금 폴더의 파일 중. [rsync] (폴더 하나를 맞춤): 위 = 지금 파일 · 아래 = 폴더 전체.
|
|
/// 그리기만 한다 (작업이 바뀌면 부르는 쪽이 다시 그림)
|
|
class TransferJobView extends StatelessWidget {
|
|
const TransferJobView({super.key, required this.job, required this.rsync});
|
|
|
|
final TransferJob job;
|
|
final bool rsync;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
String pct(double v) => '${(v * 100).round()}%';
|
|
final cur = job.index < job.total ? job.index : job.total - 1;
|
|
final curFiles = job.total == 0 ? 0 : job.filesTotal[cur.clamp(0, job.total - 1)];
|
|
final curDone = job.total == 0 ? 0 : job.filesDone[cur.clamp(0, job.total - 1)];
|
|
final title = job.finished
|
|
? (job.error == null ? tr('완료') : tr('멈춤'))
|
|
: job.move
|
|
? tr('옮기는 중')
|
|
: tr('복사하는 중');
|
|
final labelMax = MediaQuery.sizeOf(context).width * 0.45;
|
|
Widget line(String label, double value, String right) => Padding(
|
|
padding: const EdgeInsets.only(top: 6),
|
|
child: Row(children: [
|
|
SizedBox(
|
|
width: math.min(300, labelMax), // 좁은 화면 (폰 세로) 은 폭의 45% 까지
|
|
child: Text(label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12.5)),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(3),
|
|
child: LinearProgressIndicator(
|
|
minHeight: 8,
|
|
value: job.counting ? null : value,
|
|
// 빈 곳과 찬 곳이 분명히 다르게
|
|
color: JjColors.accent,
|
|
backgroundColor: JjColors.border,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
SizedBox(
|
|
width: 150,
|
|
child: Text(right, textAlign: TextAlign.end, style: TextStyle(fontSize: 12, color: JjColors.textDim)),
|
|
),
|
|
]),
|
|
);
|
|
return Column(mainAxisSize: MainAxisSize.min, children: [
|
|
Row(children: [
|
|
Icon(job.move ? Icons.drive_file_move_outline : Icons.copy_outlined, size: 18, color: JjColors.accent),
|
|
const SizedBox(width: 8),
|
|
Text('$title · ${tr(job.method.label)}', style: const TextStyle(fontWeight: FontWeight.w600)),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
// Rsync 화면: 원본 → 대상 (⇄ 는 두 줄)
|
|
child: Text(rsync ? '${job.sources.join(', ')}/ → ${job.dest}/' : job.dest,
|
|
maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: JjColors.textDim)),
|
|
),
|
|
if (!job.finished) TextButton(onPressed: job.cancel, child: Text(tr('취소'))),
|
|
]),
|
|
// Rsync 화면 (폴더 하나를 맞춤): 위 = 지금 복사하는 파일 (그 파일의 진행률 · 속도), 아래 = 폴더 전체 (파일 수)
|
|
if (rsync) ...[
|
|
line(
|
|
job.counting
|
|
? tr('파일 세는 중…')
|
|
: job.pruning
|
|
? (job.finished ? trf('빈 폴더 {0}개 지움', [job.pruned]) : tr('원본의 빈 폴더 정리 중…'))
|
|
: job.finished
|
|
? tr('끝')
|
|
: job.currentFile.isEmpty
|
|
? tr('시작하는 중…')
|
|
: job.currentFile.split('/').last,
|
|
job.finished ? 1.0 : (job.filePercent ?? 0),
|
|
[
|
|
if (job.filePercent != null && !job.finished) pct(job.filePercent!),
|
|
if (!job.finished && job.speed != null) '${formatSize(job.speed!.round())}/s',
|
|
].join(' · '),
|
|
),
|
|
line(
|
|
trf('{0} 전체', [job.currentName.isEmpty ? vBasename(job.sources.first) : job.currentName]),
|
|
job.folderProgress,
|
|
'${pct(job.folderProgress)} · ${job.checkTotal > 0 && !job.finished ? trf('항목 {0}/{1}', [job.checked, job.checkTotal]) : trf('파일 {0}/{1}', [job.allDone, job.allFiles])}',
|
|
),
|
|
] else ...[
|
|
// 파일 탐색기 (여러 항목): 위 = 전체 항목 중 · 아래 = 지금 폴더의 파일 중
|
|
line(
|
|
trf('전체 {0}개 중 {1}번째', [job.total, (cur + 1).clamp(1, job.total)]),
|
|
job.overall,
|
|
'${pct(job.overall)} · ${trf('파일 {0}/{1}', [job.allDone, job.allFiles])}'
|
|
'${!job.finished && job.speed != null ? ' · ${formatSize(job.speed!.round())}/s' : ''}',
|
|
),
|
|
line(
|
|
job.counting
|
|
? tr('파일 세는 중…')
|
|
: job.pruning
|
|
? (job.finished ? trf('빈 폴더 {0}개 지움', [job.pruned]) : tr('원본의 빈 폴더 정리 중…'))
|
|
: trf('{0} 안의 파일', [job.currentName]),
|
|
job.current,
|
|
'${pct(job.current)} · ${trf('파일 {0}/{1}', [curDone, curFiles])}',
|
|
),
|
|
],
|
|
]);
|
|
}
|
|
}
|