E6 1: 탐색기 모양 처음 값을 기기마다 (Windows = Total Commander) · 예전 판이 저장한 X-plore 는 한 번만 묻기

- ExplorerSettings: 처음 값 defaultStyle (Windows totalcmd · Android xplore). 예전 파일에 모양 키가 없으면 그때의 X-plore.
  Windows 에서 저장된 xplore 이고 물었다는 표시 (explorerStyleV2) 가 없으면 styleAskPending (저장 안 함)
- style_ask: 탐색기를 열 때 한 번 [바꾸기] (Total Commander) / [그대로] (X-plore) - 창 밖으로 닫지 못함, 어느 쪽이든 다시 묻지 않음.
  환경 설정에서 직접 고르면 묻지 않음
- 약속 시험 explorer_style_contract_test: 새 설치 · 저장된 xplore (키 없던 예전 파일 포함) · windows/totalcmd 안 물음 · [그대로] · [바꾸기]
- X-plore 모양 (트리) 을 보는 기존 시험 14개 파일 + 공용 하네스에 style = 'xplore' 를 밝힘 (기대값 그대로, 관리자 승인 10/23)
- 설정 JSON 골든: 새 키 explorerStyleV2 한 줄 · 처음 값 출력의 Windows 모양 totalcmd 만 바꿈. 사전 4개 (en · ja · zh-Hans)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
modular
RyuJongJin 2026-10-23 05:39:39 +09:00
parent 8c561a6133
commit 20f9d083b2
25 changed files with 171 additions and 6 deletions

View File

@ -423,6 +423,7 @@
"Windows 에서 이 앱의 알림이 꺼져 있어 보이지 않습니다": "Notifications for this app are turned off in Windows, so they won't appear",
"Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "Available on Windows only. On this device, documents open in another app.",
"Windows 오류 0x{0}": "Windows error 0x{0}",
"Windows 의 처음 모양이 Total Commander 로 바뀌었습니다 (목록 · \"..\" · 아래 F3~F8 단추 줄). 지금 쓰는 X-plore 모양을 바꿀까요? 환경 설정 > 파일 탐색기 > 모양에서 언제든 바꿀 수 있습니다.": "The default look on Windows is now Total Commander (list · \"..\" · F3–F8 buttons at the bottom). Change the X-plore look you are using now? You can change it any time in Settings > File explorer > Look.",
"Windows 탐색기": "Windows Explorer",
"X-plore 스타일: 선택 표시 늘 보이기": "X-plore style: always show selection marks",
"X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore style: swipe sideways to switch panes",
@ -827,6 +828,7 @@
"규칙상 허용되지 않음": "not allowed by ruleset",
"그 자리로": "Go to location",
"그 파일 하나만": "Only that file",
"그대로": "Keep",
"그대로 두기": "Keep",
"그래도 Chrome 엔진 쓰기…": "Use the Chrome engine anyway…",
"그래도 맞추기 ({0}개 지움)": "Sync anyway (delete {0})",
@ -2884,6 +2886,7 @@
"파일 탐색기 누르기: 한 번 누르면 선택 → 한 번 누르면 바로 실행. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "File explorer tap: single tap selects → single tap opens. To revert: Settings > File explorer > Tap",
"파일 탐색기 단축키": "File explorer shortcuts",
"파일 탐색기 두 창 배치: 화면 모양 따라 → 좌우. 되돌리기: 파일 탐색기의 [좌우 ⇆ / 위아래 ⇅] 버튼 또는 창 배치": "File explorer two-pane layout: follow screen shape → side by side. To revert: the [Columns ⇆ / Rows ⇅] button in the file explorer, or Pane layout",
"파일 탐색기 모양": "File explorer look",
"파일 탐색기 목록의 이름 · 크기 글자만. 앱 화면 크기 · 기기 글자 크기와 곱해집니다": "Only the name and size text in the file explorer list. Multiplied by the app screen size and the device text size",
"파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "Row height in the file explorer list. Compact rows may be hard to tap with a finger",
"파일 탐색기 위쪽 ⋮ 메뉴에서도 바꿀 수 있습니다 (좌우 · 위아래 · 버튼 줄 위치 · 버튼 구성)": "You can also change it from the ⋮ menu at the top of the file explorer (side by side · top and bottom · button bar position · buttons)",
@ -2918,6 +2921,7 @@
"페이지 열기": "Open page",
"페이지 지우기": "Delete page",
"페이지를 바꾼 것을 저장할까요?": "Save the page changes?",
"편집": "Edit",
"편집 목록 {0}개": "{0} in the edit list",
"편집 목록에 있음": "In the edit list",
"편집 목록에 추가": "Add to the edit list",

View File

@ -423,6 +423,7 @@
"Windows 에서 이 앱의 알림이 꺼져 있어 보이지 않습니다": "Windows でこのアプリの通知がオフのため表示されません",
"Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "Windows でのみ使えます。この端末では文書を他のアプリで開きます。",
"Windows 오류 0x{0}": "Windows エラー 0x{0}",
"Windows 의 처음 모양이 Total Commander 로 바뀌었습니다 (목록 · \"..\" · 아래 F3~F8 단추 줄). 지금 쓰는 X-plore 모양을 바꿀까요? 환경 설정 > 파일 탐색기 > 모양에서 언제든 바꿀 수 있습니다.": "Windows の既定の見た目が Total Commander になりました (一覧 · \"..\" · 下の F3~F8 ボタン列)。今使っている X-plore の見た目を変えますか? 設定 > ファイルエクスプローラー > 見た目でいつでも変えられます。",
"Windows 탐색기": "Windows エクスプローラー",
"X-plore 스타일: 선택 표시 늘 보이기": "X-plore スタイル: 選択マークを常に表示",
"X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore スタイル: 横にスワイプでペイン切り替え",
@ -827,6 +828,7 @@
"규칙상 허용되지 않음": "ルールで許可されていません",
"그 자리로": "その場所へ",
"그 파일 하나만": "そのファイルだけ",
"그대로": "このまま",
"그대로 두기": "そのままにする",
"그래도 Chrome 엔진 쓰기…": "それでも Chrome エンジンを使う…",
"그래도 맞추기 ({0}개 지움)": "それでも同期 ({0} 個を削除)",
@ -2884,6 +2886,7 @@
"파일 탐색기 누르기: 한 번 누르면 선택 → 한 번 누르면 바로 실행. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "ファイルエクスプローラーのタップ: 1 回で選択 → 1 回ですぐ開く。元に戻す: 環境設定 > ファイルエクスプローラー > タップ",
"파일 탐색기 단축키": "ファイルエクスプローラーのショートカット",
"파일 탐색기 두 창 배치: 화면 모양 따라 → 좌우. 되돌리기: 파일 탐색기의 [좌우 ⇆ / 위아래 ⇅] 버튼 또는 창 배치": "ファイルエクスプローラーの 2 画面配置: 画面の形に合わせる → 左右。元に戻す: ファイルエクスプローラーの [左右 ⇆ / 上下 ⇅] ボタンまたは画面配置",
"파일 탐색기 모양": "ファイルエクスプローラーの見た目",
"파일 탐색기 목록의 이름 · 크기 글자만. 앱 화면 크기 · 기기 글자 크기와 곱해집니다": "ファイルエクスプローラー一覧の名前・サイズの文字のみ。アプリの画面サイズ・端末の文字サイズと掛け合わされます",
"파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "ファイルエクスプローラー一覧の行の高さ。狭くすると指でタップしにくい場合があります",
"파일 탐색기 위쪽 ⋮ 메뉴에서도 바꿀 수 있습니다 (좌우 · 위아래 · 버튼 줄 위치 · 버튼 구성)": "ファイルエクスプローラー上部の ⋮ メニューからも変えられます (左右 · 上下 · ボタン列の位置 · ボタン構成)",
@ -2918,6 +2921,7 @@
"페이지 열기": "ページを開く",
"페이지 지우기": "ページを削除",
"페이지를 바꾼 것을 저장할까요?": "ページの変更を保存しますか?",
"편집": "編集",
"편집 목록 {0}개": "編集リスト {0}個",
"편집 목록에 있음": "編集リストにあり",
"편집 목록에 추가": "編集リストに追加",

View File

@ -423,6 +423,7 @@
"Windows 에서 이 앱의 알림이 꺼져 있어 보이지 않습니다": "Windows 中此应用的通知已关闭,因此不会显示",
"Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "仅限 Windows。在本设备上会用其他应用打开文档。",
"Windows 오류 0x{0}": "Windows 错误 0x{0}",
"Windows 의 처음 모양이 Total Commander 로 바뀌었습니다 (목록 · \"..\" · 아래 F3~F8 단추 줄). 지금 쓰는 X-plore 모양을 바꿀까요? 환경 설정 > 파일 탐색기 > 모양에서 언제든 바꿀 수 있습니다.": "Windows 的默认外观已改为 Total Commander (列表 · \"..\" · 底部 F3~F8 按钮栏)。要更改当前使用的 X-plore 外观吗? 可随时在 设置 > 文件浏览器 > 外观 中更改。",
"Windows 탐색기": "Windows 资源管理器",
"X-plore 스타일: 선택 표시 늘 보이기": "X-plore 样式: 始终显示选择标记",
"X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore 样式: 左右滑动切换窗格",
@ -827,6 +828,7 @@
"규칙상 허용되지 않음": "规则不允许",
"그 자리로": "转到所在位置",
"그 파일 하나만": "仅该文件",
"그대로": "保持不变",
"그대로 두기": "保留",
"그래도 Chrome 엔진 쓰기…": "仍然使用 Chrome 引擎…",
"그래도 맞추기 ({0}개 지움)": "仍然同步 (删除 {0} 个)",
@ -2884,6 +2886,7 @@
"파일 탐색기 누르기: 한 번 누르면 선택 → 한 번 누르면 바로 실행. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "文件浏览器点击: 单击选择 → 单击直接打开。恢复: 设置 > 文件浏览器 > 点击",
"파일 탐색기 단축키": "文件管理器快捷键",
"파일 탐색기 두 창 배치: 화면 모양 따라 → 좌우. 되돌리기: 파일 탐색기의 [좌우 ⇆ / 위아래 ⇅] 버튼 또는 창 배치": "文件浏览器双窗格布局: 跟随屏幕形状 → 左右。恢复: 文件浏览器中的 [左右 ⇆ / 上下 ⇅] 按钮或窗格布局",
"파일 탐색기 모양": "文件浏览器外观",
"파일 탐색기 목록의 이름 · 크기 글자만. 앱 화면 크기 · 기기 글자 크기와 곱해집니다": "仅文件管理器列表中的名称和大小文字。会与应用界面大小及设备文字大小相乘",
"파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "文件管理器列表的行高。紧凑时可能难以用手指点按",
"파일 탐색기 위쪽 ⋮ 메뉴에서도 바꿀 수 있습니다 (좌우 · 위아래 · 버튼 줄 위치 · 버튼 구성)": "也可以在文件浏览器顶部的 ⋮ 菜单中更改 (左右 · 上下 · 按钮栏位置 · 按钮配置)",
@ -2918,6 +2921,7 @@
"페이지 열기": "打开页面",
"페이지 지우기": "删除页面",
"페이지를 바꾼 것을 저장할까요?": "保存页面的更改吗?",
"편집": "编辑",
"편집 목록 {0}개": "编辑列表 {0} 个",
"편집 목록에 있음": "已在编辑列表中",
"편집 목록에 추가": "加入编辑列表",

View File

@ -45,9 +45,15 @@ class ExplorerSettings implements SettingsGroup {
/// 길게 누르기 · 오른쪽 클릭은 늘 기능 메뉴.
String click = 'select';
/// 모양: 'xplore' (기본) · 'windows' Windows 탐색기 · 'totalcmd' Total Commander.
/// 규칙 1 의 Windows 처음 값 (Total Commander) 과 저장된 값 묻기 (결정 1) 는 E6 (TC 모양) 과 함께 (관리자 결정 10/22)
String style = 'xplore';
/// 모양: 'xplore' X-plore · 'windows' Windows 탐색기 · 'totalcmd' Total Commander.
/// 처음 값은 기기마다 (규칙 1 · E6): Windows = Total Commander, Android = X-plore
String style = defaultStyle;
static String get defaultStyle => Platform.isWindows ? 'totalcmd' : 'xplore';
/// E6 (결정 10/22 ①): Windows 에서 예전 판이 저장한 'xplore' 를 한 번 물을지 (처음 값이었는지 사용자가 고른 것인지 알 수 없어서).
/// 읽을 때 정하고 저장하지 않는다 - 물었다는 표시 (explorerStyleV2) 는 늘 저장한다
bool styleAskPending = false;
/// 두 창 배치: 'side' 좌우 (기본, 118 - 폰 세로에서도) · 'stacked' 위아래 · 'auto' 화면 모양 따라 (가로로 넓으면 좌우, 세로로 길면 위아래)
String orientation = 'side';
@ -123,6 +129,7 @@ class ExplorerSettings implements SettingsGroup {
j['androidTrashDays'] = androidTrashDays;
j['explorerClick'] = click;
j['explorerStyle'] = style;
j['explorerStyleV2'] = true;
j['explorerOrientation'] = orientation;
j['explorerToolbar'] = toolbar;
j['explorerButtons'] = buttons;
@ -174,7 +181,13 @@ class ExplorerSettings implements SettingsGroup {
};
// 규칙 2: 두 기기 모두 한 번 = 선택. 규칙 이전 Android 의 'open' 은 예전 처음 값이라 한 번 'select' 로
click = j['explorerClick'] == 'open' && (j['explorerRulesV1'] == true || !Platform.isAndroid) ? 'open' : 'select';
style = const ['windows', 'totalcmd'].contains(j['explorerStyle']) ? j['explorerStyle'] as String : 'xplore';
// 예전 판의 파일에 모양이 없으면 그때의 처음 값 (X-plore) - 새로 설치 (빈 설정) 만 기기의 처음 값
style = const ['xplore', 'windows', 'totalcmd'].contains(j['explorerStyle'])
? j['explorerStyle'] as String
: j.isEmpty
? defaultStyle
: 'xplore';
styleAskPending = Platform.isWindows && j.isNotEmpty && j['explorerStyleV2'] != true && style == 'xplore';
// 118: 예전 기본 '화면 모양 따라' 는 한 번 좌우로 옮긴다 (사용자가 고른 위아래는 그대로)
orientation = j['explorerOrientation'] == 'stacked'
? 'stacked'

View File

@ -0,0 +1,33 @@
/// 약속 (E6, 결정 10/22 ①): Windows 에서 예전 판이 저장한 X-plore 모양을 한 번만 묻는다.
/// [ExplorerSettings.styleAskPending] 일 때만 창을 띄우고, [바꾸기] 면 Total Commander, [그대로] 면 X-plore 를 둔다.
/// 어느 쪽이든 다시 묻지 않는다 (물었다는 표시는 설정 저장에 늘 들어감). 창 밖을 눌러 닫지 못한다 (답을 골라야 함).
library;
import 'package:flutter/material.dart';
import '../../app/app_controller.dart';
import '../../l10n/tr.dart';
/// 물었으면 true (답과 상관없이). 물을 것이 없으면 false
Future<bool> askExplorerStyleOnce(BuildContext context, AppController c) async {
if (!c.settings.explorer.styleAskPending) return false;
c.settings.explorer.styleAskPending = false; // 창을 띄우는 동안 다시 부르면 또 띄우지 않게
final change = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (ctx) => AlertDialog(
title: Text(tr('파일 탐색기 모양')),
content: Text(tr('Windows 의 처음 모양이 Total Commander 로 바뀌었습니다 (목록 · ".." · 아래 F3~F8 단추 줄). '
'지금 쓰는 X-plore 모양을 바꿀까요? 환경 설정 > 파일 탐색기 > 모양에서 언제든 바꿀 수 있습니다.')),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(tr('그대로'))),
FilledButton(onPressed: () => Navigator.pop(ctx, true), child: Text(tr('바꾸기'))),
],
),
);
await c.updateSettings((x) {
x.explorer.styleAskPending = false;
if (change == true) x.explorer.style = 'totalcmd';
});
return true;
}

View File

@ -17,6 +17,7 @@ import '../core/thumb_cache.dart' show thumbMemoryForgetters;
import 'explorer_drag.dart';
import '../core/wifi_share.dart' show WifiShare;
import '../app/volumes.dart';
import 'explorer/style_ask.dart';
import 'explorer/explorer_controller.dart';
import 'explorer/explorer_host.dart';
import 'explorer/explorer_pane.dart';
@ -152,6 +153,8 @@ class _ExplorerPageState extends State<ExplorerPage> implements ExplorerHost, Ex
_kindSeen = c.settings.explorer.sortByKind;
c.addListener(_onSettings);
_init();
// E6: 예전 판이 저장한 X-plore 모양이면 한 번 묻는다 (Windows - Rsync 화면은 묻지 않음)
if (!widget.rsync) afterFrame(() => mounted ? unawaited(askExplorerStyleOnce(context, c)) : null);
}
/// 232: 앱으로 돌아오면 (다른 앱 · 탐색기에서 파일을 바꾸고 오면) 보이는 폴더를 다시 읽는다

View File

@ -205,7 +205,10 @@ class _FilesSettingsSectionState extends State<FilesSettingsSection> {
for (final st in ExplorerStyle.values)
DropdownMenuItem(value: st.name, child: Text(tr(st.label))),
],
onChanged: (v) => c.updateSettings((x) => x.explorer.style = v!),
// 직접 골랐으면 예전 모양을 묻지 않는다 (E6)
onChanged: (v) => c.updateSettings((x) => x.explorer
..style = v!
..styleAskPending = false),
),
),
SettingTile(

View File

@ -40,6 +40,7 @@ void main() {
setUp(() {
tmp = Directory.systemTemp.createTempSync('jj_dismiss_');
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
});
tearDown(() => tmp.deleteSync(recursive: true));

View File

@ -40,6 +40,7 @@ void main() {
File(p.join(left, 'doc.txt')).setLastModifiedSync(DateTime(2026, 10, 9, 12));
File(p.join(right, 'doc.txt')).setLastModifiedSync(DateTime(2026, 10, 1, 8));
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings.explorer.paths = [left, right];
final root = p.rootPrefix(tmp.path);
device.volumes = () => [(root, root.replaceAll(RegExp(r'[\\/]+$'), ''))];

View File

@ -29,6 +29,7 @@ void main() {
..writeAsStringSync('x');
Directory(p.join(tmp.path, 'right')).createSync();
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings.explorer.paths = [p.join(tmp.path, 'left'), p.join(tmp.path, 'right')];
stuck = Completer<bool>();
dead = {r'R:\'};

View File

@ -51,6 +51,7 @@ void main() {
Future<(ExplorerController, GlobalKey<_HostState>)> open(WidgetTester t, {double width = 800}) async {
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
final key = GlobalKey<_HostState>();
await t.binding.setSurfaceSize(Size(width, 600));
addTearDown(() => t.binding.setSurfaceSize(null));

View File

@ -28,6 +28,7 @@ void main() {
File(p.join(left, 'doc.txt')).writeAsStringSync('hello');
Directory(right).createSync();
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.paths = [left, right]
..copy.bandwidthKBps = 512; // 큰 파일이 6초쯤 걸리게
@ -68,6 +69,7 @@ void main() {
Directory(p.join(r, 'rsub')).createSync(recursive: true);
device.volumes = () => [(tmp.path, 'T:')];
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.orientation = 'side'
..copy.rsyncPaths = [l, r];
@ -108,6 +110,7 @@ void main() {
Directory(p.join(r, 'rsub')).createSync(recursive: true);
device.volumes = () => [(tmp.path, 'T:')];
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.orientation = 'side'
..copy.rsyncPaths = [l, r];
@ -144,6 +147,7 @@ void main() {
Directory(r).createSync(recursive: true);
device.volumes = () => [(tmp.path, 'T:')];
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.orientation = 'side'
..copy.rsyncPaths = [l, r];
@ -166,6 +170,7 @@ void main() {
Directory(p.join(sd, 'R')).createSync(recursive: true);
device.volumes = () => [(inner, '내장 저장소'), (sd, 'SD 카드')];
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.orientation = 'side'
..copy.rsyncPaths = [p.join(inner, 'L'), p.join(sd, 'R')];
@ -192,6 +197,7 @@ void main() {
Directory(p.join(l, 'sub')).createSync(recursive: true);
Directory(p.join(r, 'rsub')).createSync(recursive: true);
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.layout = 'single'
..copy.rsyncPaths = [l, r];
@ -225,6 +231,7 @@ void main() {
..writeAsStringSync('d');
File(p.join(l, 'top.txt')).writeAsStringSync('t');
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.layout = 'single'
..explorer.paths = [l, l];
@ -258,6 +265,7 @@ void main() {
..writeAsStringSync('$i');
}
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.layout = 'single'
..explorer.paths = [dl, dl];
@ -292,6 +300,7 @@ void main() {
}
File(p.join(l, 'zz_file.txt')).writeAsStringSync('t');
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.layout = 'dual'
..explorer.orientation = 'stacked'
@ -333,6 +342,7 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('a');
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.layout = 'single'
..explorer.paths = [l, l];

View File

@ -36,6 +36,7 @@ void main() {
// 저장 장치는 시험 폴더 하나 (실제 Temp 폴더의 많은 항목 사이에서 찾지 않게)
device.volumes = () => [(tmp.path, 'T:')];
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), shell: NoopShell()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..explorer.paths = [left, right]
..copy.methodFile = 'builtin'

View File

@ -0,0 +1,75 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/app_controller.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
import 'package:jj_mkvmaker/services/platform_services.dart';
import 'package:jj_mkvmaker/ui/explorer/style_ask.dart';
/// E6 약속: 파일 탐색기 모양의 처음 값은 기기마다 (Windows = Total Commander) · 예전 판이 저장한 X-plore 는 한 번만 묻는다
void main() {
final win = Platform.isWindows;
test('처음 값: 새로 설치 (빈 설정) 는 기기의 처음 값 - Windows 는 Total Commander, 묻지 않음', () {
final s = AppSettings.fromJson({});
expect(s.explorer.style, win ? 'totalcmd' : 'xplore');
expect(s.explorer.styleAskPending, isFalse);
expect(AppSettings().explorer.style, ExplorerSettings.defaultStyle);
});
test('저장된 값: 예전 판의 xplore (또는 모양 키가 없던 예전 파일) 는 Windows 에서 한 번 묻고, 고른 windows · totalcmd 는 묻지 않음', () {
final old = AppSettings.fromJson({'explorerStyle': 'xplore', 'explorerLayout': 'dual'});
expect([old.explorer.style, old.explorer.styleAskPending], ['xplore', win]);
final noKey = AppSettings.fromJson({'explorerLayout': 'dual'});
expect([noKey.explorer.style, noKey.explorer.styleAskPending], ['xplore', win], reason: '그때의 처음 값 (X-plore) 을 덮지 않음');
for (final v in ['windows', 'totalcmd']) {
final s = AppSettings.fromJson({'explorerStyle': v, 'explorerLayout': 'dual'});
expect([s.explorer.style, s.explorer.styleAskPending], [v, false]);
}
final again = AppSettings.fromJson(old.toJson());
expect([again.explorer.style, again.explorer.styleAskPending], ['xplore', false], reason: '한 번 저장하면 (물었다는 표시) 다시 묻지 않음');
});
group('묻는 창 (askExplorerStyleOnce)', () {
AppController ctl(Map<String, dynamic> saved) =>
AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()))
..settings = AppSettings.fromJson(saved);
Future<bool> ask(WidgetTester t, AppController c, {String? pick}) async {
final key = GlobalKey<NavigatorState>();
await t.pumpWidget(MaterialApp(navigatorKey: key, home: const Scaffold(body: SizedBox())));
final f = askExplorerStyleOnce(key.currentContext!, c);
await t.pumpAndSettle();
if (pick != null) {
expect(find.text('파일 탐색기 모양'), findsOneWidget);
await t.tapAt(const Offset(5, 5)); // 창 밖을 눌러도 닫히지 않음
await t.pumpAndSettle();
expect(find.text('파일 탐색기 모양'), findsOneWidget);
await t.tap(find.text(pick));
await t.pumpAndSettle();
}
return f;
}
testWidgets('[그대로] 면 X-plore 를 두고 다시 묻지 않음', (t) async {
final c = ctl({'explorerStyle': 'xplore', 'explorerLayout': 'dual'})..settings.explorer.styleAskPending = true;
expect(await ask(t, c, pick: '그대로'), isTrue);
expect([c.settings.explorer.style, c.settings.explorer.styleAskPending], ['xplore', false]);
expect(await ask(t, c), isFalse);
expect(find.text('파일 탐색기 모양'), findsNothing, reason: '두 번째는 창을 띄우지 않음');
expect(c.settings.toJson()['explorerStyleV2'], isTrue);
});
testWidgets('[바꾸기] 면 Total Commander · 물을 것이 없으면 창 없음', (t) async {
final c = ctl({'explorerStyle': 'xplore', 'explorerLayout': 'dual'})..settings.explorer.styleAskPending = true;
expect(await ask(t, c, pick: '바꾸기'), isTrue);
expect([c.settings.explorer.style, c.settings.explorer.styleAskPending], ['totalcmd', false]);
final picked = ctl({'explorerStyle': 'windows', 'explorerLayout': 'dual'});
expect(await ask(t, picked), isFalse);
expect(picked.settings.explorer.style, 'windows');
});
});
}

View File

@ -56,6 +56,7 @@ void main() {
device.volumes = () => [(root, root.replaceAll(RegExp(r'[\\/]+$'), ''))];
c = AppController(PlatformServices(
mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), shell: shell));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
});
tearDown(() async {
DavRegistry.configure([]);

View File

@ -114,7 +114,8 @@
"explorerSortByKind": false,
"explorerSortDesc": false,
"explorerSortPerFolder": false,
"explorerStyle": "xplore",
"explorerStyle": "totalcmd",
"explorerStyleV2": true,
"explorerSwipePanes": false,
"explorerTextScale": 100,
"explorerToolbar": "middle",

View File

@ -126,6 +126,7 @@
"explorerSortDesc": false,
"explorerSortPerFolder": false,
"explorerStyle": "xplore",
"explorerStyleV2": true,
"explorerSwipePanes": false,
"explorerTextScale": 100,
"explorerToolbar": "middle",

View File

@ -120,6 +120,7 @@
"explorerSortDesc": true,
"explorerSortPerFolder": true,
"explorerStyle": "xplore",
"explorerStyleV2": true,
"explorerSwipePanes": true,
"explorerTextScale": 107,
"explorerToolbar": "middle",

View File

@ -128,6 +128,7 @@ void main() {
AppController ctl({bool on = true}) {
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..tools.rootEnabled = on
..explorer.paths = [tmp.path, tmp.path]

View File

@ -36,6 +36,7 @@ void main() {
await server.start();
final s = DavServer(id: 'nas', name: '집 NAS', url: server.url, user: 'user', password: 'pass');
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..remote.webdavServers = [s]
..explorer.layout = 'single'

View File

@ -50,6 +50,7 @@ void main() {
server = s;
final local = Directory(p.join(tmp.path, 'local'))..createSync();
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
await tester.runAsync(() => c.updateSettings((x) => x
..remote.sftpServers = [SftpServer(id: 's1', name: 'NAS', host: '127.0.0.1', port: s!.port, user: 'jj', password: 'pw')]
..explorer.paths = ['sftp://s1/', local.path]));

View File

@ -111,6 +111,7 @@ void main() {
AppController ctl({bool on = true}) {
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings
..tools.shizukuEnabled = on
..explorer.paths = [tmp.path, tmp.path]

View File

@ -66,6 +66,7 @@ void useExplorerFixture() {
_c = AppController(PlatformServices(
mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), shell: shell));
c.settings.explorer.paths = [left, right];
c.settings.explorer.style = 'xplore'; // E6: 이 시험들은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
});
// Windows: 방금 닫은 파일 (65 의 잠금 시험) 이나 색인 · 백신이 잠깐 잡고 있으면 지우기가 실패한다 - 될 때까지 기다린다
tearDown(() async {

View File

@ -33,6 +33,7 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('2');
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
});
tearDown(() => tmp.deleteSync(recursive: true));

View File

@ -48,6 +48,7 @@ void main() {
AppController ctl() {
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings.explorer.style = 'xplore'; // E6: 이 시험은 X-plore 모양 (트리) 을 본다 - Windows 처음 값은 Total Commander
c.settings.vault.enabled = true;
return c;
}