WIP 85 파일 탐색기 진행 패널 숨기기 · 작은 표시 · Android 알림 진행률 · 끝나면 [폴더 열기] (검토 대기)
- 관리자 지시 (F14): 복사 진행 패널을 숨길 수 있게 (Android 는 알림줄에 진행), 끝나면 [폴더 열기]. - 패널 오른쪽 위 [숨기기] → 오른쪽 아래 작은 표시 "복사 · 이동 n개 · 45%" (누르면 다시 펼침). 숨김은 설정으로 기억 (처음 값: 보임). 관리자 판단: 숨겨도 묻는 창 (확인 · 같은 이름) 과 실패 알림은 늘 뜬다 (패널과 따로). 멈춘 (실패한) 것이 있으면 작은 표시가 빨갛게 "복사 · 이동 멈춤 - 눌러서 보기". - Android 작업 알림: "복사 · 이동 n개 · 45%" (CopyCenter.activeProgress), 다운로드가 없으면 진행 막대도 복사 · 이동 진행률. - 끝난 알림에 [폴더 열기] - 앱 탐색기에서 넣은 폴더로 (그 폴더를 보는 창, 없으면 지금 창). - 시험: keep_alive_test (복사 진행률 글 · 막대 · 다운로드가 먼저), explorer_test 85 ([폴더 열기] · [숨기기] → 작은 표시 → 다시). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음6
parent
5cdf57d7ff
commit
53a1b47a2c
|
|
@ -1209,7 +1209,10 @@
|
|||
"복사 · rsync 목록으로 옮겼습니다.": "Moved to the copy · rsync list.",
|
||||
"복사 · 동기화가 중간에 끊겨 남은 임시 파일입니다 (.jjpart). 완성된 파일이 아니므로 지워도 됩니다. 같은 파일을 다시 복사하면 새로 만듭니다.": "Temporary files left behind when a copy or sync was interrupted (.jjpart). They are not complete files, so you can delete them. Copying the same file again creates it anew.",
|
||||
"복사 · 이동 {0}개": "{0} copy · move",
|
||||
"복사 · 이동 {0}개 · {1}%": "Copy · move: {0} · {1}%",
|
||||
"복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "{0} copy/move tasks have not finished. Exiting stops them, and the file being copied is removed so no incomplete file is left.",
|
||||
"복사 · 이동 끝": "Copy · move finished",
|
||||
"복사 · 이동 멈춤 - 눌러서 보기": "Copy · move stopped - tap to see",
|
||||
"복사 · 이동 중입니다": "Copying · moving in progress",
|
||||
"복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "Copy · move: files are skipped when the receiving side is newer (before, the older source overwrote them, and a move also deleted the source). Skipped files stay in the source and their number appears in the finish notice. To change back: Settings > File explorer (or Rsync) > Skip when the receiving side is newer",
|
||||
"복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "Copy · move works with two panes (Pane layout > Two panes).",
|
||||
|
|
@ -1377,6 +1380,7 @@
|
|||
"수정 · 이동": "Edit · move",
|
||||
"수정한 날짜": "Date modified",
|
||||
"숨겨도 위쪽 ⋮ 메뉴에서 창 배치 · 버튼 구성을 바꿀 수 있습니다.": "Even when hidden, you can change the pane layout · buttons from the ⋮ menu at the top.",
|
||||
"숨기기": "Hide",
|
||||
"숨긴 항목 (. 으로 시작) 보이기": "Show hidden items (starting with .)",
|
||||
"숨긴 항목 숨기기": "Hide hidden items",
|
||||
"숨김": "Hidden",
|
||||
|
|
@ -2115,6 +2119,7 @@
|
|||
"직접 지정 (기본)": "Manual (default)",
|
||||
"진행 중인 작업 없음": "No jobs in progress",
|
||||
"진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "A job is in progress: {0}\nStop it and quit?",
|
||||
"진행 패널 보기": "Show progress panel",
|
||||
"집 NAS 처럼 자체 서명 인증서면 [서버 설정 고치기] 에서 \"인증서 확인 안 함\" 을 켜세요.": "If it uses a self-signed certificate (like a home NAS), turn on \"Don't verify certificate\" in [Edit server settings].",
|
||||
"짧을수록 빨리 맞추지만 배터리를 더 씁니다.": "Shorter means faster syncing but more battery use.",
|
||||
"차례 기다림": "Waiting in queue",
|
||||
|
|
|
|||
|
|
@ -1209,7 +1209,10 @@
|
|||
"복사 · rsync 목록으로 옮겼습니다.": "コピー · rsync の一覧へ移しました。",
|
||||
"복사 · 동기화가 중간에 끊겨 남은 임시 파일입니다 (.jjpart). 완성된 파일이 아니므로 지워도 됩니다. 같은 파일을 다시 복사하면 새로 만듭니다.": "コピー・同期が途中で中断されて残った一時ファイルです (.jjpart)。完成したファイルではないので削除しても構いません。同じファイルをもう一度コピーすると新しく作ります。",
|
||||
"복사 · 이동 {0}개": "コピー・移動 {0} 件",
|
||||
"복사 · 이동 {0}개 · {1}%": "コピー・移動 {0} 件 · {1}%",
|
||||
"복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "コピー・移動 {0} 件がまだ終わっていません。終了すると止まり、コピー中のファイルは未完成のまま残らないよう削除します。",
|
||||
"복사 · 이동 끝": "コピー・移動 完了",
|
||||
"복사 · 이동 멈춤 - 눌러서 보기": "コピー・移動が停止 - タップして表示",
|
||||
"복사 · 이동 중입니다": "コピー・移動中です",
|
||||
"복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "コピー · 移動: 受け取る側のファイルが新しければスキップします (以前は古い元で上書きし、移動なら元も削除していました)。スキップしたファイルは元に残り、終了の通知にその数が出ます。戻すには: 環境設定 > ファイルエクスプローラー (または Rsync) > 受け取る側が新しければスキップ",
|
||||
"복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "コピー · 移動は 2 ペインで使います (ペインの配置 > 2 ペイン)。",
|
||||
|
|
@ -1377,6 +1380,7 @@
|
|||
"수정 · 이동": "編集・移動",
|
||||
"수정한 날짜": "更新日時",
|
||||
"숨겨도 위쪽 ⋮ 메뉴에서 창 배치 · 버튼 구성을 바꿀 수 있습니다.": "非表示にしても、上の ⋮ メニューからペインの配置 · ボタン構成を変えられます。",
|
||||
"숨기기": "隠す",
|
||||
"숨긴 항목 (. 으로 시작) 보이기": "隠し項目 (. で始まる) を表示",
|
||||
"숨긴 항목 숨기기": "隠し項目を隠す",
|
||||
"숨김": "非表示",
|
||||
|
|
@ -2115,6 +2119,7 @@
|
|||
"직접 지정 (기본)": "手動指定 (既定)",
|
||||
"진행 중인 작업 없음": "進行中の作業なし",
|
||||
"진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "進行中の作業があります: {0}\n中止して終了しますか?",
|
||||
"진행 패널 보기": "進行パネルを表示",
|
||||
"집 NAS 처럼 자체 서명 인증서면 [서버 설정 고치기] 에서 \"인증서 확인 안 함\" 을 켜세요.": "自宅 NAS のように自己署名証明書なら、[サーバー設定を修正] で「証明書を検証しない」をオンにしてください。",
|
||||
"짧을수록 빨리 맞추지만 배터리를 더 씁니다.": "短いほど早く同期しますが、バッテリーをより使います。",
|
||||
"차례 기다림": "順番待ち",
|
||||
|
|
|
|||
|
|
@ -1209,7 +1209,10 @@
|
|||
"복사 · rsync 목록으로 옮겼습니다.": "已移到 复制 · rsync 列表。",
|
||||
"복사 · 동기화가 중간에 끊겨 남은 임시 파일입니다 (.jjpart). 완성된 파일이 아니므로 지워도 됩니다. 같은 파일을 다시 복사하면 새로 만듭니다.": "这是复制 · 同步中途中断后留下的临时文件 (.jjpart)。它们不是完整的文件,可以删除。再次复制同一文件会重新创建。",
|
||||
"복사 · 이동 {0}개": "复制 · 移动 {0} 个",
|
||||
"복사 · 이동 {0}개 · {1}%": "复制 · 移动 {0} 个 · {1}%",
|
||||
"복사 · 이동 {0}개가 아직 끝나지 않았습니다. 끝내면 멈추고, 복사하던 파일은 완성되지 않은 채로 남지 않게 지웁니다.": "{0} 个复制 · 移动尚未完成。退出会停止它们,并删除正在复制的文件,以免留下不完整的文件。",
|
||||
"복사 · 이동 끝": "复制 · 移动 已完成",
|
||||
"복사 · 이동 멈춤 - 눌러서 보기": "复制 · 移动已停止 - 点按查看",
|
||||
"복사 · 이동 중입니다": "正在复制 · 移动",
|
||||
"복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). 건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기": "复制 · 移动:接收端文件较新时跳过(以前会用旧的源文件覆盖,移动时还会删除源)。跳过的文件留在源中,完成通知中会显示数量。恢复:设置 > 文件浏览器(或 Rsync)> 接收端较新时跳过",
|
||||
"복사 · 이동은 두 창에서 씁니다 (창 배치 > 두 창).": "复制 · 移动需在双窗格中使用 (窗格布局 > 双窗格)。",
|
||||
|
|
@ -1377,6 +1380,7 @@
|
|||
"수정 · 이동": "编辑 · 移动",
|
||||
"수정한 날짜": "修改日期",
|
||||
"숨겨도 위쪽 ⋮ 메뉴에서 창 배치 · 버튼 구성을 바꿀 수 있습니다.": "即使隐藏,也可以在顶部 ⋮ 菜单中更改窗格布局 · 按钮配置。",
|
||||
"숨기기": "隐藏",
|
||||
"숨긴 항목 (. 으로 시작) 보이기": "显示隐藏项目 (以 . 开头)",
|
||||
"숨긴 항목 숨기기": "隐藏隐藏项目",
|
||||
"숨김": "隐藏",
|
||||
|
|
@ -2115,6 +2119,7 @@
|
|||
"직접 지정 (기본)": "手动指定 (默认)",
|
||||
"진행 중인 작업 없음": "没有进行中的任务",
|
||||
"진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "有正在进行的任务: {0}\n要停止并退出吗?",
|
||||
"진행 패널 보기": "显示进度面板",
|
||||
"집 NAS 처럼 자체 서명 인증서면 [서버 설정 고치기] 에서 \"인증서 확인 안 함\" 을 켜세요.": "如果是自签名证书 (如家用 NAS),请在 [修改服务器设置] 中开启“不验证证书”。",
|
||||
"짧을수록 빨리 맞추지만 배터리를 더 씁니다.": "间隔越短同步越快,但更耗电。",
|
||||
"차례 기다림": "排队等待",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ class CopyCenter extends ChangeNotifier {
|
|||
/// 지금 도는 복사 · 이동 수 (끝낼 때 확인 · Android 작업 알림에)
|
||||
int get activeCount => jobs.values.where((j) => !j.finished).length;
|
||||
|
||||
/// 85: 지금 도는 복사 · 이동 전체의 진행률 (0~1, 도는 것이 없거나 아직 세는 중이면 null) - Android 알림 · 숨긴 패널의 작은 표시
|
||||
double? get activeProgress {
|
||||
final run = [for (final j in jobs.values) if (!j.finished) j];
|
||||
if (run.isEmpty || run.any((j) => j.counting)) return null;
|
||||
return run.fold<double>(0, (a, j) => a + j.overall) / run.length;
|
||||
}
|
||||
|
||||
/// 이미 만들어진 것 (없으면 null - 새로 만들지 않음)
|
||||
static CopyCenter? peekOf(AppController c) => _instance != null && _instance!.c == c ? _instance : null;
|
||||
bool get anyRunning => jobs.values.any((j) => !j.finished);
|
||||
|
|
|
|||
|
|
@ -425,6 +425,9 @@ class AppSettings {
|
|||
|
||||
/// 82: 창 사이 끌어 놓기 - 'ask' 묻기 (처음 값, 관리자 판단) · 'copy' 복사 · 'move' 이동
|
||||
String explorerDropAction = 'ask';
|
||||
|
||||
/// 85: 탐색기의 복사 · 이동 진행 패널을 숨김 (아래 작은 표시만). 처음 값: 보임
|
||||
bool explorerProgressHidden = false;
|
||||
bool explorerShowHidden = false;
|
||||
|
||||
// ── 복사 · 이동 (파일 탐색기) · rsync · lsync (Rsync 화면) ──
|
||||
|
|
@ -745,6 +748,7 @@ class AppSettings {
|
|||
'explorerSortDesc': explorerSortDesc,
|
||||
'explorerFolderSize': explorerFolderSize,
|
||||
'explorerDropAction': explorerDropAction,
|
||||
'explorerProgressHidden': explorerProgressHidden,
|
||||
'explorerSortPerFolder': explorerSortPerFolder,
|
||||
'explorerFolderSorts': explorerFolderSorts,
|
||||
'explorerShowHidden': explorerShowHidden,
|
||||
|
|
@ -962,6 +966,7 @@ class AppSettings {
|
|||
..explorerSortDesc = j['explorerSortDesc'] as bool? ?? false
|
||||
..explorerFolderSize = const ['sort', 'always', 'off'].contains(j['explorerFolderSize']) ? j['explorerFolderSize'] as String : 'sort'
|
||||
..explorerSortPerFolder = j['explorerSortPerFolder'] == true
|
||||
..explorerProgressHidden = j['explorerProgressHidden'] == true
|
||||
..explorerDropAction = const ['ask', 'copy', 'move'].contains(j['explorerDropAction']) ? j['explorerDropAction'] as String : 'ask'
|
||||
..explorerFolderSorts = {
|
||||
if (j['explorerFolderSorts'] is Map)
|
||||
|
|
|
|||
|
|
@ -95,9 +95,12 @@ class AndroidKeepAlive {
|
|||
_ai = store?..addListener(_changed);
|
||||
}
|
||||
final ai = [AiJobs.instance.statusLine, store?.statusLine].nonNulls.join(' · ').trim();
|
||||
var (text, progress) =
|
||||
status(c.currentJob, c.busy, running, downloads.overallProgress,
|
||||
syncs: syncs, syncing: live?.anyRunning ?? false, copies: copying, stopped: live?.problems.length ?? 0);
|
||||
var (text, progress) = status(c.currentJob, c.busy, running, downloads.overallProgress,
|
||||
syncs: syncs,
|
||||
syncing: live?.anyRunning ?? false,
|
||||
copies: copying,
|
||||
copyProgress: copies?.activeProgress,
|
||||
stopped: live?.problems.length ?? 0);
|
||||
if (ai.isNotEmpty) {
|
||||
// 진행 알림에 남은 장 수와 예상 시간 (발열 · 배터리 때문에 쉬어 가지는 않는다 - 사용자 결정)
|
||||
text = text == null ? ai : '$ai · $text';
|
||||
|
|
@ -124,18 +127,25 @@ class AndroidKeepAlive {
|
|||
/// 알림 글과 진행률 (0~100, 모르면 -1). 진행 중인 일이 없으면 글이 null.
|
||||
/// [syncs] 백그라운드로 지켜보는 실시간 동기화 수 ([syncing] 지금 맞추는 중)
|
||||
static (String?, int) status(String? job, bool busy, int downloads, double? downloadProgress,
|
||||
{int syncs = 0, bool syncing = false, int copies = 0, int stopped = 0}) {
|
||||
{int syncs = 0, bool syncing = false, int copies = 0, double? copyProgress, int stopped = 0}) {
|
||||
final parts = [
|
||||
// 68: 원본을 읽지 못해 멈춘 동기화 (화면을 보지 않아도 알 수 있게 맨 앞에)
|
||||
if (stopped > 0) trf('⚠ 동기화 멈춤 {0}개 · 원본 확인 필요', [stopped]),
|
||||
if (busy) job ?? tr('작업 중'),
|
||||
if (copies > 0) trf('복사 · 이동 {0}개', [copies]),
|
||||
// 85: 복사 · 이동 진행률도 알림에 (탐색기의 진행 패널을 숨겨도 보이게)
|
||||
if (copies > 0)
|
||||
copyProgress == null ? trf('복사 · 이동 {0}개', [copies]) : trf('복사 · 이동 {0}개 · {1}%', [copies, (copyProgress * 100).round()]),
|
||||
if (downloads > 0) trf('다운로드 {0}개{1}', [downloads, downloadProgress == null ? '' : ' · ${(downloadProgress * 100).round()}%']),
|
||||
if (syncs > 0) syncing ? trf('동기화 {0}개 · 맞추는 중', [syncs]) : trf('동기화 {0}개 지켜보는 중', [syncs]),
|
||||
];
|
||||
if (parts.isEmpty) return (null, -1);
|
||||
// 진행률 막대는 다운로드만 있을 때 (작업 진행률은 동영상마다 따로라 하나로 합치기 어렵다)
|
||||
final p = !busy && downloadProgress != null ? (downloadProgress * 100).round().clamp(0, 100) : -1;
|
||||
// 진행률 막대는 다운로드만 있을 때 (작업 진행률은 동영상마다 따로라 하나로 합치기 어렵다).
|
||||
// 85: 다운로드가 없으면 복사 · 이동 진행률
|
||||
final p = !busy && downloadProgress != null
|
||||
? (downloadProgress * 100).round().clamp(0, 100)
|
||||
: !busy && downloads == 0 && copyProgress != null
|
||||
? (copyProgress * 100).round().clamp(0, 100)
|
||||
: -1;
|
||||
return (parts.join(' · '), p);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2320,7 +2320,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
}
|
||||
x.changed();
|
||||
}
|
||||
_snack(error is FileOpCancelled
|
||||
final doneText = error is FileOpCancelled
|
||||
? tr('취소했습니다.')
|
||||
: error != null
|
||||
? trf('끝나지 못했습니다: {0}', [error])
|
||||
|
|
@ -2331,7 +2331,23 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
if (!move && job.keptNewer > 0) trf('받는 쪽이 더 새것 {0}개 건너뜀', [job.keptNewer]),
|
||||
if (job.skipped.isNotEmpty) trf('같은 이름이라 건너뜀: {0}', [_namesOf(job.skipped)]),
|
||||
if (skippedHere.isNotEmpty) trf('이미 이 폴더에 있어 건너뜀: {0}', [_namesOf(skippedHere)]),
|
||||
].join(' · '));
|
||||
].join(' · ');
|
||||
// 85: 끝나면 [폴더 열기] - 앱 탐색기에서 넣은 폴더로 (그 폴더를 보고 있는 창, 없으면 지금 창)
|
||||
if (error == null && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(doneText),
|
||||
action: SnackBarAction(
|
||||
label: tr('폴더 열기'),
|
||||
onPressed: () {
|
||||
final show = _panes.where((x) => samePath(x.current, dest)).firstOrNull ?? _pane;
|
||||
setState(() => _active = _panes.indexOf(show));
|
||||
unawaited(_goTo(show, dest, fresh: true));
|
||||
},
|
||||
),
|
||||
));
|
||||
} else {
|
||||
_snack(doneText);
|
||||
}
|
||||
// 82: 끌어 놓기의 이동 되돌리기 (끝난 알림 바로 뒤에 - 진행 막대가 내려가기를 기다리지 않게)
|
||||
if (error == null && mounted) onFinished?.call(job);
|
||||
// 다 되면 진행 막대가 잠깐 100% 를 보인 뒤 내려간다
|
||||
|
|
@ -2624,6 +2640,58 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
/// 아래에서 올라오는 진행 막대 (위: 전체 항목 중 · 아래: 지금 폴더의 파일 중)
|
||||
Widget _transferPanel() {
|
||||
final jobs = _jobs;
|
||||
// 85: 숨겼으면 오른쪽 아래 작은 표시 "복사 · 이동 n개 · 45%" (누르면 다시 펼침)
|
||||
if (jobs.isNotEmpty && c.settings.explorerProgressHidden) {
|
||||
return Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: ListenableBuilder(
|
||||
listenable: Listenable.merge(jobs),
|
||||
builder: (context, _) {
|
||||
final run = [for (final j in jobs) if (!j.finished) j];
|
||||
// 관리자 판단: 숨겨도 멈춘 (실패한) 것은 분명히 - 빨간 표시 (끝난 알림 · 묻는 창은 숨김과 상관없이 늘 뜬다)
|
||||
final failed = jobs.any((j) => j.finished && j.error != null && j.error is! FileOpCancelled);
|
||||
final pct = run.isEmpty || run.any((j) => j.counting)
|
||||
? null
|
||||
: (run.fold<double>(0, (a, j) => a + j.overall) / run.length * 100).round();
|
||||
return Material(
|
||||
elevation: 6,
|
||||
color: failed ? JjColors.danger.withValues(alpha: 0.25) : JjColors.panelHigh,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
onTap: () => c.updateSettings((x) => x.explorerProgressHidden = false),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(strokeWidth: 2, value: run.isEmpty ? 1 : (pct == null ? null : pct / 100)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
failed
|
||||
? tr('복사 · 이동 멈춤 - 눌러서 보기')
|
||||
: run.isEmpty
|
||||
? tr('복사 · 이동 끝')
|
||||
: pct == null
|
||||
? trf('복사 · 이동 {0}개', [run.length])
|
||||
: trf('복사 · 이동 {0}개 · {1}%', [run.length, pct]),
|
||||
style: const TextStyle(fontSize: 12.5),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Tooltip(message: tr('진행 패널 보기'), child: const Icon(Icons.expand_less, size: 18)),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return AnimatedSlide(
|
||||
offset: jobs.isEmpty ? const Offset(0, 1.2) : Offset.zero,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
|
|
@ -2641,6 +2709,19 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
constraints: BoxConstraints(maxHeight: MediaQuery.sizeOf(context).height * 0.45),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
// 85: 숨기기 (아래 작은 표시로) - 다른 파일을 보면서 기다릴 수 있게. Android 는 알림줄에도 진행률
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: 28,
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(visualDensity: VisualDensity.compact),
|
||||
onPressed: () => c.updateSettings((x) => x.explorerProgressHidden = true),
|
||||
icon: const Icon(Icons.expand_more, size: 18),
|
||||
label: Text(tr('숨기기'), style: const TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final (k, job) in jobs.indexed) ...[
|
||||
if (k > 0) const Divider(height: 18),
|
||||
ListenableBuilder(listenable: job, builder: (context, _) => _jobView(job)),
|
||||
|
|
|
|||
|
|
@ -1102,6 +1102,32 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
testWidgets('85: 진행 패널 [숨기기] → 아래 작은 표시 (누르면 다시) · 끝난 알림의 [폴더 열기]', (tester) async {
|
||||
await open(tester);
|
||||
await act(tester, () => tester.tap(find.text('doc.txt')));
|
||||
await settle(tester, () => false, rounds: 15);
|
||||
await act(tester, () => tester.tap(find.text('복사').first));
|
||||
await calm(tester);
|
||||
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
|
||||
await settle(tester, () => File(p.join(right, 'doc.txt')).existsSync() && find.text('폴더 열기').evaluate().isNotEmpty);
|
||||
expect(find.text('폴더 열기'), findsOneWidget, reason: '끝난 알림에 [폴더 열기]');
|
||||
// 패널은 끝난 뒤 잠깐 남는다 - [숨기기]
|
||||
expect(find.text('숨기기'), findsOneWidget);
|
||||
await act(tester, () => tester.tap(find.text('숨기기')));
|
||||
await calm(tester);
|
||||
expect(c.settings.explorerProgressHidden, isTrue, reason: '숨김은 기억');
|
||||
expect(find.text('숨기기'), findsNothing);
|
||||
expect(find.text('복사 · 이동 끝'), findsOneWidget, reason: '아래 작은 표시');
|
||||
// (아래 알림이 잠깐 작은 표시를 덮는다 - 알림을 닫고 누름)
|
||||
ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars();
|
||||
await calm(tester);
|
||||
await act(tester, () => tester.tap(find.text('복사 · 이동 끝')));
|
||||
await calm(tester);
|
||||
expect(c.settings.explorerProgressHidden, isFalse, reason: '누르면 다시 펼침');
|
||||
ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars();
|
||||
await calm(tester);
|
||||
});
|
||||
|
||||
testWidgets('81: Space 로 빠른 보기 (글 파일 앞부분) · 필터에 치는 중에는 그냥 빈칸 · TC 는 F3', (tester) async {
|
||||
c.settings.explorerStyle = 'windows';
|
||||
await open(tester);
|
||||
|
|
|
|||
|
|
@ -14,5 +14,10 @@ void main() {
|
|||
// 백그라운드로 실행: 실시간 동기화를 지켜보는 동안에도
|
||||
expect(AndroidKeepAlive.status(null, false, 0, null, syncs: 2), ('동기화 2개 지켜보는 중', -1));
|
||||
expect(AndroidKeepAlive.status('MKV 만들기', true, 0, null, syncs: 1, syncing: true), ('MKV 만들기 · 동기화 1개 · 맞추는 중', -1));
|
||||
// 85: 복사 · 이동 진행률 - 다운로드가 없으면 막대도
|
||||
expect(AndroidKeepAlive.status(null, false, 0, null, copies: 1, copyProgress: 0.4), ('복사 · 이동 1개 · 40%', 40));
|
||||
expect(AndroidKeepAlive.status(null, false, 0, null, copies: 2), ('복사 · 이동 2개', -1), reason: '세는 중 (모름)');
|
||||
expect(AndroidKeepAlive.status(null, false, 1, 0.5, copies: 1, copyProgress: 0.1), ('복사 · 이동 1개 · 10% · 다운로드 1개 · 50%', 50),
|
||||
reason: '막대는 다운로드가 먼저');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue