From 9ba39e5ed66007791fc694050ca3f2bfc249f61b Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Thu, 22 Oct 2026 06:09:50 +0900 Subject: [PATCH] =?UTF-8?q?E3:=20=ED=83=90=EC=83=89=EA=B8=B0=20=EC=B2=98?= =?UTF-8?q?=EC=9D=8C=20=EA=B0=92=EC=9D=84=20=EC=84=A0=ED=83=9D=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=EB=8C=80=EB=A1=9C=20-=20TC=20=ED=82=A4=20(Windows)=20?= =?UTF-8?q?=C2=B7=20=ED=91=9C=EC=8B=9C=EB=8A=94=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=AA=A8=EB=93=9C=EC=97=90=EC=84=9C=EB=A7=8C=20(V)=20=C2=B7=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EB=88=84=EB=A5=B4=EB=A9=B4=20=EC=A0=91?= =?UTF-8?q?=EA=B8=B0=20=C2=B7=20Android=20=ED=95=9C=20=EB=B2=88=20=3D=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=C2=B7=20=EB=91=90=20=EC=B0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기준: wiki "파일 탐색기 선택 규칙 (사용자 지시 모음)" 규칙 1~7, 관리자 결정 3 · (a) (10/22) - 규칙 1: Windows 는 스타일과 상관없이 Total Commander 키 (F3 보기 · F5 복사 · F6 이동 · F7 새 폴더 · F8 삭제 · Space/Insert 고르기 · Ctrl+R 새로 고침) + Ctrl+C · X · V · A (explorer_keys · _tcKeys) - 규칙 2: Android 도 한 번 = 선택 · 두 번 = 실행 (explorerClick 처음 값 'select') - 규칙 3: 펼친 폴더를 다시 누르면 접기 처음 값 켬 (explorerRetapCollapse) - 규칙 4: 줄 끝 표시는 선택 모드에서만 (explorerAlwaysMarks 처음 값 끔), 모양 V (Icons.check) - 규칙 7 · 결정 3: 처음 배치 두 창 (폰 세로도), 'auto' 는 설정으로 남김 - 규칙 이전 판에서 저장된 예전 처음 값은 한 번 새 값으로 옮기고 업데이트 뒤 한 번 알림 (explorerRulesV1, 알림 explorerMarksRule · explorerRetapRule · explorerClickRule · explorerLayoutRule) - 근거: 10/8 사용자 결정 "예전 기본값을 옮긴 설정은 업데이트 뒤 한 번 알림". 그 뒤 사용자가 고른 값은 그대로 - 결정 (a): Windows 처음 스타일 = TC 와 저장된 'xplore' 묻기 (결정 1) 는 E6 (TC 모양) 과 함께 - 이번에는 넣지 않음 - 번역 9개 추가 · 안 쓰게 된 5개 뺌 (빠진 번역 0) 기대값을 바꾼 시험 (규칙 번호): - 규칙 7: decisions_test 처음 배치 'dual' / settings_store_test 예전 'dual' 그대로 · 'explorerLayout' 알림 없음 - 규칙 4: explorer_test "두 창: …" 첫 화면에 표시 없음 / "선택 모드: …" V 모양 · 고름 여부 · [선택 끝] 뒤 표시 없음 · 설정 '늘 보이기' 를 켜면 보임 / explorer_text_scale_test 3개 (✔ 칸 자리) 는 사용자 설정 explorerAlwaysMarks 를 켜서 - 규칙 3: explorer_test 다시 눌러 접기 (처음 값 켬) - 규칙 1: explorer_test 빠른 보기 F3 · 새로 고침 Ctrl+R (2곳, Windows 에서 F5 는 복사) - 새 시험: settings_store_test "E3: 규칙 이전 판 처음 값 옮기기 · 알림 · 다시 고른 값 그대로" 확인: analyze 0, 전체 단위 2회 통과 1,299 · 건너뜀 17 · 실패 0. Selection 지문 그대로 (d438fac0) Co-Authored-By: Claude Opus 5.5 --- assets/l10n/en.json | 14 ++++--- assets/l10n/ja.json | 14 ++++--- assets/l10n/zh-Hans.json | 14 ++++--- lib/app/settings.dart | 55 ++++++++++++++++----------- lib/ui/explorer_keys.dart | 6 ++- lib/ui/explorer_page.dart | 12 +++--- lib/ui/migration_notice.dart | 6 +++ lib/ui/settings_page.dart | 10 ++--- test/decisions_test.dart | 3 +- test/explorer_test.dart | 61 ++++++++++++++++++------------ test/explorer_text_scale_test.dart | 2 + test/settings_store_test.dart | 27 ++++++++++++- 12 files changed, 146 insertions(+), 78 deletions(-) diff --git a/assets/l10n/en.json b/assets/l10n/en.json index 5df7d33..7dc699c 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -424,10 +424,9 @@ "Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "Available on Windows only. On this device, documents open in another app.", "Windows 오류 0x{0}": "Windows error 0x{0}", "Windows 탐색기": "Windows Explorer", + "X-plore 스타일: 선택 표시 늘 보이기": "X-plore style: always show selection marks", "X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore style: swipe sideways to switch panes", - "X-plore 스타일: 표시 동그라미 늘 보이기": "X-plore style: always show the mark circles", "X-plore 스타일에서 X-plore 처럼 한 트리에 보입니다 (위 줄 탭 · ★ 는 그대로)": "In the X-plore style they appear in one tree like X-plore (the tabs and ★ at the top stay)", - "X-plore 처럼. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "Like X-plore. When off, folders collapse and expand only with the › arrow (the list doesn't move on a double tap)", "YouTube · 토렌트 다운로드 목록": "YouTube · torrent download list", "YouTube → yt-dlp, 마그넷 · .torrent → aria2 (완료 후 올려 주기 안 함)": "YouTube → yt-dlp, magnet · .torrent → aria2 (no seeding after completion)", "YouTube 가 로봇 확인을 요구합니다. 환경 설정 > 다운로드 > YouTube 쿠키 에서 브라우저(Firefox 권장) 또는 cookies.txt 를 지정한 뒤 다시 받으세요.": "YouTube is asking for a robot check. In Settings > Downloads > YouTube cookies, choose a browser (Firefox recommended) or a cookies.txt, then download again.", @@ -903,9 +902,9 @@ "꺼냈습니다: {0}": "Exported: {0}", "끄기": "Off", "끄면 (기본) Tor 화면을 떠날 때 (다른 화면 · 앱이 뒤로) 지웁니다. 일반 브라우저와는 늘 따로입니다.": "When off (default), they are cleared when you leave the Tor screen (another screen, or the app goes to the background). They are always kept separate from the normal browser.", + "끄면 (처음 값) [선택] 을 켰을 때만 보입니다. 다른 스타일은 늘 선택 모드에서만": "When off (default), marks appear only while [Select] is on. Other styles always show them only in select mode", "끄면 더 안전하지만 많은 사이트가 보이지 않습니다 (다음에 열 때부터).": "Off is safer, but many sites will not display (from the next time it opens).", "끄면 마지막 설정으로 바로 시작: 원어 {0}, 언어 {1}, {2}": "When off, starts right away with the last settings: source {0}, languages {1}, {2}", - "끄면 선택 모드에서만 보입니다 (다른 스타일은 늘 선택 모드에서만)": "When off, they show only in selection mode (other styles always show them only in selection mode)", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). Chrome 엔진은 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "When off, page scripts do not run (fewer ads · trackers, but script-built sites such as YouTube will not show). With the Chrome engine, page translation · video detection also stop.", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "When off, page scripts do not run (fewer ads · trackers, but script-built sites such as YouTube will not show). Page translation · video detection also stop.", "끄면 환경 설정 · 다운로드 목록 · 다른 화면을 보는 동안에도 웹 페이지의 소리가 계속 들립니다": "When off, the web page keeps playing sound while you look at settings, the download list or other screens", @@ -1196,7 +1195,7 @@ "두 쪽 나눠 보기: 자동 - 가로로 긴 그림만 (누르면 켜기)": "Split two-page spreads: auto - wide images only (tap to turn on)", "두 쪽 나눠 보기: 자동 - 두 쪽 스캔으로 보이는 그림만 (누르면 켜기)": "Two-page split: Auto - only images that look like two-page scans (tap to turn on)", "두 쪽 나눠 보기: 켜기 (누르면 끄기)": "Split two-page spreads: on (tap to turn off)", - "두 창": "Two panes", + "두 창 (기본)": "Two panes (default)", "두 창 배치": "Two-pane arrangement", "두 창 사이": "Between panes", "두 창 파일 탐색기 · 복사 · 이동 · WebDAV": "Dual-pane file explorer · copy · move · WebDAV", @@ -2747,6 +2746,7 @@ "채우기 (가운데 잘라냄)": "Fill (crop the center)", "처리 장치": "Processor", "처음 · 🏠 주소 (기본: Tor 연결 확인 페이지)": "Start / 🏠 address (default: the Tor connection check page)", + "처음 값 켬. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "On by default. When off, collapse and expand only with the › arrow (the list does not move on a double tap)", "처음 값으로": "Reset", "처음 보는 서버 인증서입니다": "This server certificate is new", "처음 보는 서버 인증서입니다 (자체 서명). 지문을 확인한 뒤 믿을지 정하세요": "This server certificate is new (self-signed). Check the fingerprint, then decide whether to trust it", @@ -2887,7 +2887,11 @@ "파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "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)", "파일 탐색기 창: 두 창 → 화면 크기 따라 (폰 세로는 한 창). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "File explorer panes: two panes → by screen size (one pane on a portrait phone). To revert: Settings > File explorer > Pane layout", + "파일 탐색기 창: 화면 크기 따라 → 두 창 (폰 세로도 좌우). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "File explorer panes: by screen size → two panes (side by side even on a phone in portrait). To undo: Settings > File explorer > Pane layout", + "파일 탐색기: 줄 끝 선택 표시는 [선택] 을 켰을 때만 보입니다 (모양 V). 늘 보이게: 환경 설정 > 파일 탐색기 > X-plore 스타일: 선택 표시 늘 보이기": "File explorer: the selection mark at the end of each row (a V) appears only while [Select] is on. To always show it: Settings > File explorer > X-plore style: always show selection marks", "파일 탐색기: 파일을 한 번 누르면 바로 엽니다 (고르기 · 메뉴는 길게 누르기). 바꾸기: 환경 설정 > 파일 탐색기 > 누르기": "File explorer: a single tap now opens a file right away (long-press to select or for the menu). To change: Settings > File explorer > Tap", + "파일 탐색기: 파일을 한 번 누르면 선택, 두 번 누르면 엽니다. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "File explorer: tap a file once to select it, twice to open it. To undo: Settings > File explorer > Tap", + "파일 탐색기: 펼친 폴더를 다시 누르면 접힙니다. 되돌리기: 환경 설정 > 파일 탐색기 > 펼친 폴더를 다시 누르면 접기": "File explorer: tapping an expanded folder again collapses it. To undo: Settings > File explorer > Tap an open folder again to collapse", "파일 탐색기의 길게 누르기 [목록에서 숨기기] 로 숨긴 것. 파일은 그대로이고 목록에서만 빠집니다": "Hidden with long-press [Hide from list] in the file explorer. The files are unchanged; they are only left out of the list", "파일 폴더": "File folder", "파일 확인 중…": "Checking files…", @@ -3073,7 +3077,7 @@ "화면 크게": "Larger", "화면 크기": "Size", "화면 크기 · 종료": "Screen size · Exit", - "화면 크기 따라 (기본)": "By screen size (default)", + "화면 크기 따라": "By screen size", "화면이 있는 컴포넌트는 하나 이상 남아 있어야 합니다.": "At least one component with a screen must stay installed.", "화질": "Quality", "확인": "OK", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 262c96d..e4a0142 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -424,10 +424,9 @@ "Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "Windows でのみ使えます。この端末では文書を他のアプリで開きます。", "Windows 오류 0x{0}": "Windows エラー 0x{0}", "Windows 탐색기": "Windows エクスプローラー", + "X-plore 스타일: 선택 표시 늘 보이기": "X-plore スタイル: 選択マークを常に表示", "X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore スタイル: 横にスワイプでペイン切り替え", - "X-plore 스타일: 표시 동그라미 늘 보이기": "X-plore スタイル: 選択の丸を常に表示", "X-plore 스타일에서 X-plore 처럼 한 트리에 보입니다 (위 줄 탭 · ★ 는 그대로)": "X-plore スタイルで X-plore のように 1 つのツリーに表示します (上部のタブ・★ はそのまま)", - "X-plore 처럼. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "X-plore のように。オフのときは › 矢印でのみ開閉します (2 回押しても一覧は動きません)", "YouTube · 토렌트 다운로드 목록": "YouTube・トレント ダウンロード一覧", "YouTube → yt-dlp, 마그넷 · .torrent → aria2 (완료 후 올려 주기 안 함)": "YouTube → yt-dlp、マグネット・.torrent → aria2(完了後のアップロードなし)", "YouTube 가 로봇 확인을 요구합니다. 환경 설정 > 다운로드 > YouTube 쿠키 에서 브라우저(Firefox 권장) 또는 cookies.txt 를 지정한 뒤 다시 받으세요.": "YouTube がロボット確認を求めています。環境設定 > ダウンロード > YouTube クッキー でブラウザー(Firefox 推奨)または cookies.txt を指定してから、もう一度ダウンロードしてください。", @@ -903,9 +902,9 @@ "꺼냈습니다: {0}": "取り出しました: {0}", "끄기": "オフ", "끄면 (기본) Tor 화면을 떠날 때 (다른 화면 · 앱이 뒤로) 지웁니다. 일반 브라우저와는 늘 따로입니다.": "オフ (既定) の場合は Tor 画面を離れるとき (別の画面 · アプリがバックグラウンドへ) に消去します。通常のブラウザーとは常に別です。", + "끄면 (처음 값) [선택] 을 켰을 때만 보입니다. 다른 스타일은 늘 선택 모드에서만": "オフ (既定) のときは [選択] をオンにしたときだけ表示されます。ほかのスタイルは常に選択モードのときだけ", "끄면 더 안전하지만 많은 사이트가 보이지 않습니다 (다음에 열 때부터).": "オフの方が安全ですが、多くのサイトが表示されません (次に開くときから)。", "끄면 마지막 설정으로 바로 시작: 원어 {0}, 언어 {1}, {2}": "オフにすると前回の設定ですぐ開始: 原語 {0}、言語 {1}、{2}", - "끄면 선택 모드에서만 보입니다 (다른 스타일은 늘 선택 모드에서만)": "オフにすると選択モードのときだけ表示します (他のスタイルは常に選択モードのときだけ)", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). Chrome 엔진은 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "オフにするとページのスクリプトを実行しません (広告 · 追跡は減りますが、YouTube のようにスクリプトで作られたサイトは表示されません)。Chrome エンジンではページ翻訳 · 動画の検出も止まります。", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "オフにするとページのスクリプトを実行しません (広告 · 追跡は減りますが、YouTube のようにスクリプトで作られたサイトは表示されません)。ページ翻訳 · 動画の検出も止まります。", "끄면 환경 설정 · 다운로드 목록 · 다른 화면을 보는 동안에도 웹 페이지의 소리가 계속 들립니다": "オフにすると、環境設定・ダウンロード一覧・他の画面を見ている間も Web ページの音が流れ続けます", @@ -1196,7 +1195,7 @@ "두 쪽 나눠 보기: 자동 - 가로로 긴 그림만 (누르면 켜기)": "見開きを分けて表示: 自動 - 横長の画像のみ (押すとオン)", "두 쪽 나눠 보기: 자동 - 두 쪽 스캔으로 보이는 그림만 (누르면 켜기)": "見開き分割: 自動 - 見開きスキャンらしい画像だけ (押すとオン)", "두 쪽 나눠 보기: 켜기 (누르면 끄기)": "見開きを分けて表示: オン (押すとオフ)", - "두 창": "2 ペイン", + "두 창 (기본)": "2 画面 (既定)", "두 창 배치": "2 ペインの配置", "두 창 사이": "ペインの間", "두 창 파일 탐색기 · 복사 · 이동 · WebDAV": "2 画面ファイルエクスプローラー・コピー・移動・WebDAV", @@ -2747,6 +2746,7 @@ "채우기 (가운데 잘라냄)": "フィル(中央を切り抜き)", "처리 장치": "処理装置", "처음 · 🏠 주소 (기본: Tor 연결 확인 페이지)": "最初・🏠 のアドレス (既定: Tor 接続確認ページ)", + "처음 값 켬. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "既定でオン。オフにすると折りたたみ · 展開は › 矢印だけ (2 回押しても一覧が動かない)", "처음 값으로": "既定に戻す", "처음 보는 서버 인증서입니다": "初めて見るサーバー証明書です", "처음 보는 서버 인증서입니다 (자체 서명). 지문을 확인한 뒤 믿을지 정하세요": "初めて見るサーバー証明書です (自己署名)。指紋を確認してから信頼するか決めてください", @@ -2887,7 +2887,11 @@ "파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "ファイルエクスプローラー一覧の行の高さ。狭くすると指でタップしにくい場合があります", "파일 탐색기 위쪽 ⋮ 메뉴에서도 바꿀 수 있습니다 (좌우 · 위아래 · 버튼 줄 위치 · 버튼 구성)": "ファイルエクスプローラー上部の ⋮ メニューからも変えられます (左右 · 上下 · ボタン列の位置 · ボタン構成)", "파일 탐색기 창: 두 창 → 화면 크기 따라 (폰 세로는 한 창). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "ファイルエクスプローラーの画面: 2 画面 → 画面サイズに合わせる (縦向きのスマホは 1 画面)。元に戻す: 環境設定 > ファイルエクスプローラー > 画面配置", + "파일 탐색기 창: 화면 크기 따라 → 두 창 (폰 세로도 좌우). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "ファイルエクスプローラーの画面: 画面サイズに合わせる → 2 画面 (スマホ縦でも左右)。戻すには: 環境設定 > ファイルエクスプローラー > ペインの配置", + "파일 탐색기: 줄 끝 선택 표시는 [선택] 을 켰을 때만 보입니다 (모양 V). 늘 보이게: 환경 설정 > 파일 탐색기 > X-plore 스타일: 선택 표시 늘 보이기": "ファイルエクスプローラー: 行末の選択マーク (V) は [選択] をオンにしたときだけ表示されます。常に表示するには: 環境設定 > ファイルエクスプローラー > X-plore スタイル: 選択マークを常に表示", "파일 탐색기: 파일을 한 번 누르면 바로 엽니다 (고르기 · 메뉴는 길게 누르기). 바꾸기: 환경 설정 > 파일 탐색기 > 누르기": "ファイルエクスプローラー: ファイルを 1 回タップするとすぐ開きます (選択 · メニューは長押し)。変更: 環境設定 > ファイルエクスプローラー > タップ", + "파일 탐색기: 파일을 한 번 누르면 선택, 두 번 누르면 엽니다. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "ファイルエクスプローラー: ファイルを 1 回押すと選択、2 回押すと開きます。戻すには: 環境設定 > ファイルエクスプローラー > タップ", + "파일 탐색기: 펼친 폴더를 다시 누르면 접힙니다. 되돌리기: 환경 설정 > 파일 탐색기 > 펼친 폴더를 다시 누르면 접기": "ファイルエクスプローラー: 展開したフォルダーをもう一度押すと折りたたみます。戻すには: 環境設定 > ファイルエクスプローラー > 開いたフォルダーをもう一度押すと閉じる", "파일 탐색기의 길게 누르기 [목록에서 숨기기] 로 숨긴 것. 파일은 그대로이고 목록에서만 빠집니다": "ファイルエクスプローラーの長押し [リストから隠す] で隠したもの。ファイルはそのままで、リストから外れるだけです", "파일 폴더": "ファイル フォルダー", "파일 확인 중…": "ファイルを確認中…", @@ -3073,7 +3077,7 @@ "화면 크게": "画面を大きく", "화면 크기": "画面サイズ", "화면 크기 · 종료": "画面サイズ・終了", - "화면 크기 따라 (기본)": "画面サイズに合わせる (既定)", + "화면 크기 따라": "画面サイズに合わせる", "화면이 있는 컴포넌트는 하나 이상 남아 있어야 합니다.": "画面のあるコンポーネントを少なくとも1つ残す必要があります。", "화질": "画質", "확인": "OK", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index a2ddf12..40ab8b0 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -424,10 +424,9 @@ "Windows 에서만 쓸 수 있습니다. 이 기기에서는 문서를 다른 앱으로 엽니다.": "仅限 Windows。在本设备上会用其他应用打开文档。", "Windows 오류 0x{0}": "Windows 错误 0x{0}", "Windows 탐색기": "Windows 资源管理器", + "X-plore 스타일: 선택 표시 늘 보이기": "X-plore 样式: 始终显示选择标记", "X-plore 스타일: 옆으로 밀면 창 바꾸기": "X-plore 样式: 左右滑动切换窗格", - "X-plore 스타일: 표시 동그라미 늘 보이기": "X-plore 样式: 始终显示标记圆圈", "X-plore 스타일에서 X-plore 처럼 한 트리에 보입니다 (위 줄 탭 · ★ 는 그대로)": "在 X-plore 样式中像 X-plore 一样显示在一棵树中 (顶部的标签 · ★ 保持不变)", - "X-plore 처럼. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "像 X-plore 一样。关闭时仅用 › 箭头折叠 · 展开 (双击时列表不会移动)", "YouTube · 토렌트 다운로드 목록": "YouTube · 种子下载列表", "YouTube → yt-dlp, 마그넷 · .torrent → aria2 (완료 후 올려 주기 안 함)": "YouTube → yt-dlp,磁力链接 · .torrent → aria2(完成后不做种)", "YouTube 가 로봇 확인을 요구합니다. 환경 설정 > 다운로드 > YouTube 쿠키 에서 브라우저(Firefox 권장) 또는 cookies.txt 를 지정한 뒤 다시 받으세요.": "YouTube 要求进行机器人验证。请在 设置 > 下载 > YouTube Cookie 中指定浏览器(推荐 Firefox)或 cookies.txt 后重新下载。", @@ -903,9 +902,9 @@ "꺼냈습니다: {0}": "已导出: {0}", "끄기": "关闭", "끄면 (기본) Tor 화면을 떠날 때 (다른 화면 · 앱이 뒤로) 지웁니다. 일반 브라우저와는 늘 따로입니다.": "关闭时(默认)在离开 Tor 页面时(切换到其他页面或应用转到后台)清除。始终与普通浏览器分开。", + "끄면 (처음 값) [선택] 을 켰을 때만 보입니다. 다른 스타일은 늘 선택 모드에서만": "关闭 (默认) 时仅在打开 [选择] 时显示。其他样式始终仅在选择模式中显示", "끄면 더 안전하지만 많은 사이트가 보이지 않습니다 (다음에 열 때부터).": "关闭更安全,但很多网站将无法显示 (从下次打开起)。", "끄면 마지막 설정으로 바로 시작: 원어 {0}, 언어 {1}, {2}": "关闭后将直接使用上次的设置开始: 原语言 {0},语言 {1},{2}", - "끄면 선택 모드에서만 보입니다 (다른 스타일은 늘 선택 모드에서만)": "关闭时仅在选择模式下显示 (其他样式始终仅在选择模式下显示)", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). Chrome 엔진은 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "关闭后不运行页面脚本 (广告 · 跟踪会减少,但 YouTube 等由脚本构建的网站将无法显示)。使用 Chrome 引擎时,页面翻译 · 视频检测也会停止。", "끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). 끄면 페이지 번역 · 동영상 찾기도 멈춥니다.": "关闭后不运行页面脚本 (广告 · 跟踪会减少,但 YouTube 等由脚本构建的网站将无法显示)。页面翻译 · 视频检测也会停止。", "끄면 환경 설정 · 다운로드 목록 · 다른 화면을 보는 동안에도 웹 페이지의 소리가 계속 들립니다": "关闭后,在查看设置、下载列表或其他界面时网页声音会继续播放", @@ -1196,7 +1195,7 @@ "두 쪽 나눠 보기: 자동 - 가로로 긴 그림만 (누르면 켜기)": "拆分双页: 自动 - 仅横向图片 (点按开启)", "두 쪽 나눠 보기: 자동 - 두 쪽 스캔으로 보이는 그림만 (누르면 켜기)": "双页拆分:自动 - 仅拆分看起来像双页扫描的图片(点按开启)", "두 쪽 나눠 보기: 켜기 (누르면 끄기)": "拆分双页: 开启 (点按关闭)", - "두 창": "双窗格", + "두 창 (기본)": "双窗格 (默认)", "두 창 배치": "双窗格排列", "두 창 사이": "窗格之间", "두 창 파일 탐색기 · 복사 · 이동 · WebDAV": "双窗格文件浏览器 · 复制 · 移动 · WebDAV", @@ -2747,6 +2746,7 @@ "채우기 (가운데 잘라냄)": "填充(裁切中间)", "처리 장치": "处理器", "처음 · 🏠 주소 (기본: Tor 연결 확인 페이지)": "起始 · 🏠 地址 (默认: Tor 连接检查页面)", + "처음 값 켬. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)": "默认开启。关闭后只能用 › 箭头折叠 · 展开 (双击时列表不会移动)", "처음 값으로": "恢复默认", "처음 보는 서버 인증서입니다": "这是首次见到的服务器证书", "처음 보는 서버 인증서입니다 (자체 서명). 지문을 확인한 뒤 믿을지 정하세요": "这是首次见到的服务器证书 (自签名)。请确认指纹后决定是否信任", @@ -2887,7 +2887,11 @@ "파일 탐색기 목록의 줄 높이. 좁게는 손가락으로 누르기 어려울 수 있습니다": "文件管理器列表的行高。紧凑时可能难以用手指点按", "파일 탐색기 위쪽 ⋮ 메뉴에서도 바꿀 수 있습니다 (좌우 · 위아래 · 버튼 줄 위치 · 버튼 구성)": "也可以在文件浏览器顶部的 ⋮ 菜单中更改 (左右 · 上下 · 按钮栏位置 · 按钮配置)", "파일 탐색기 창: 두 창 → 화면 크기 따라 (폰 세로는 한 창). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "文件浏览器窗格: 双窗格 → 按屏幕大小 (竖屏手机为单窗格)。恢复: 设置 > 文件浏览器 > 窗格布局", + "파일 탐색기 창: 화면 크기 따라 → 두 창 (폰 세로도 좌우). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치": "文件浏览器窗格: 随屏幕大小 → 双窗格 (手机竖屏也左右)。恢复: 设置 > 文件浏览器 > 窗格布局", + "파일 탐색기: 줄 끝 선택 표시는 [선택] 을 켰을 때만 보입니다 (모양 V). 늘 보이게: 환경 설정 > 파일 탐색기 > X-plore 스타일: 선택 표시 늘 보이기": "文件浏览器: 行尾的选择标记 (V) 仅在打开 [选择] 时显示。要始终显示: 设置 > 文件浏览器 > X-plore 样式: 始终显示选择标记", "파일 탐색기: 파일을 한 번 누르면 바로 엽니다 (고르기 · 메뉴는 길게 누르기). 바꾸기: 환경 설정 > 파일 탐색기 > 누르기": "文件浏览器:单击文件会立即打开(选择 · 菜单请长按)。更改:设置 > 文件浏览器 > 点按", + "파일 탐색기: 파일을 한 번 누르면 선택, 두 번 누르면 엽니다. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기": "文件浏览器: 点按文件一次为选择, 两次为打开。恢复: 设置 > 文件浏览器 > 点按", + "파일 탐색기: 펼친 폴더를 다시 누르면 접힙니다. 되돌리기: 환경 설정 > 파일 탐색기 > 펼친 폴더를 다시 누르면 접기": "文件浏览器: 再次点按已展开的文件夹会将其折叠。恢复: 设置 > 文件浏览器 > 再次点按已展开的文件夹可折叠", "파일 탐색기의 길게 누르기 [목록에서 숨기기] 로 숨긴 것. 파일은 그대로이고 목록에서만 빠집니다": "在文件浏览器中长按 [从列表中隐藏] 隐藏的项。文件本身不变,只是不在列表中显示", "파일 폴더": "文件夹", "파일 확인 중…": "正在检查文件…", @@ -3073,7 +3077,7 @@ "화면 크게": "放大界面", "화면 크기": "画面大小", "화면 크기 · 종료": "界面大小 · 退出", - "화면 크기 따라 (기본)": "按屏幕大小 (默认)", + "화면 크기 따라": "随屏幕大小", "화면이 있는 컴포넌트는 하나 이상 남아 있어야 합니다.": "至少要保留一个带界面的组件。", "화질": "画质", "확인": "确定", diff --git a/lib/app/settings.dart b/lib/app/settings.dart index ebe45be..fb9d579 100644 --- a/lib/app/settings.dart +++ b/lib/app/settings.dart @@ -412,16 +412,17 @@ class AppSettings { bool torNoticeSeen = false; // ── 파일 탐색기 (X-plore 참고) ── - /// 창: 'dual' 두 창 (기본) · 'split' 왼쪽 폴더 트리 + 오른쪽 그 폴더의 파일 목록 · 'single' 한 창 - String explorerLayout = 'auto'; + /// 창: 'dual' 두 창 (기본 - 폰 세로도, 규칙 7 · 결정 3 10/22) · 'split' 왼쪽 폴더 트리 + 오른쪽 그 폴더의 파일 목록 · + /// 'single' 한 창 · 'auto' 화면 크기 따라 (폰 세로는 한 창) + String explorerLayout = 'dual'; /// X-plore 비교 a: 한 창일 때 복사 · 이동 - 'clip' 담아 두었다가 붙여넣기 (지금 그대로, 처음 값) / /// 'other' 숨은 다른 창의 폴더로 바로 (X-plore 처럼, 위에 "갈 곳" 줄). 예전 동작과 달라 처음 값은 그대로 (관리자 결정) String explorerOnePaneCopy = 'clip'; - /// X-plore 비교 d: 펼쳐 둔 지금 폴더를 다시 누르면 접기 (X-plore 처럼). 처음 값 끔 - 189 (관리자 지시: 이름으로는 접지 않아 - /// 다시 · 두 번 눌러도 아래 줄이 움직이지 않게) 와 부딪혀 설정으로만. 켜도 두 번 누르기의 두 번째는 접지 않는다 - bool explorerRetapCollapse = false; + /// 펼친 폴더를 다시 누르면 접기 (규칙 3 "폴더 한 번 누르기 = 펼치기 · 접기", 처음 값 켬 - E3 10/22). + /// 두 번 누르기의 두 번째는 접지 않는다 ([ClickPolicy] repeat) + bool explorerRetapCollapse = true; /// X-plore 비교 40 (관리자 지시 10/20): 파일 탐색기 목록만의 글자 크기 (%, 80~150, 처음 100 = 지금 그대로). /// 앱 화면 크기 · 기기 글자 크기와 곱해진다 @@ -438,8 +439,8 @@ class AppSettings { /// 처음 값 켬 (X-plore 스타일에서만 보임 - 다른 스타일은 그대로) bool explorerRootsInTree = true; - /// X-plore 비교 ⚖ (관리자 결정 10/18): X-plore 스타일에서 줄 끝 표시 동그라미를 늘 보이기 (처음 값 켬). 다른 스타일은 선택 모드에서만 (10/5) - bool explorerAlwaysMarks = true; + /// X-plore 스타일에서 줄 끝 표시를 늘 보이기. 처음 값 끔 - 규칙 4 "선택 표시는 선택 모드에서만" (E3 10/22) + bool explorerAlwaysMarks = false; /// X-plore 비교 ⚖: X-plore 스타일 탐색기 안에서 옆으로 밀기 = 창 바꾸기 (설정으로만, 처음 값 끔 - 10/8 사용자 결정 "화면 가운데를 밀면 화면 이동" 을 지킴, 관리자 판단 10/18) @@ -448,11 +449,12 @@ class AppSettings { /// X-plore 비교 e (관리자 결정): Android 앱 휴지통 보관 일 (처음 14일, 0 = 끔 - 예전처럼 바로 영구 삭제). Windows 는 시스템 휴지통 int androidTrashDays = 14; - /// 누르기: 'select' 한 번 = 선택 · 두 번 = 열기 (기본) / 'open' 한 번 = 바로 열기. + /// 누르기: 'select' 한 번 = 선택 · 두 번 = 열기 (기본, 두 기기 - 규칙 2) / 'open' 한 번 = 바로 열기. /// 길게 누르기 · 오른쪽 클릭은 늘 기능 메뉴. - String explorerClick = Platform.isAndroid ? 'open' : 'select'; + String explorerClick = 'select'; - /// 모양: 'xplore' (기본) · 'windows' Windows 탐색기 · 'totalcmd' Total Commander + /// 모양: 'xplore' (기본) · 'windows' Windows 탐색기 · 'totalcmd' Total Commander. + /// 규칙 1 의 Windows 처음 값 (Total Commander) 과 저장된 값 묻기 (결정 1) 는 E6 (TC 모양) 과 함께 (관리자 결정 10/22) String explorerStyle = 'xplore'; /// 두 창 배치: 'side' 좌우 (기본, 118 - 폰 세로에서도) · 'stacked' 위아래 · 'auto' 화면 모양 따라 (가로로 넓으면 좌우, 세로로 길면 위아래) @@ -885,6 +887,8 @@ class AppSettings { 'explorerClick': explorerClick, // 예전 기본값 (두 창 · 한 번 누르면 선택) 은 사용자가 고른 값이 아니므로 한 번 새 기본값으로 옮긴다 'explorerV2': true, + // E3 (10/22): 선택 규칙 (사용자 지시 모음) 의 처음 값으로 한 번 옮겼음 + 'explorerRulesV1': true, 'explorerStyle': explorerStyle, 'explorerOrientation': explorerOrientation, 'explorerOrientV3': true, @@ -1116,7 +1120,8 @@ class AppSettings { ..torNoticeSeen = j['torNoticeSeen'] == true // 'auto' = 넓은 화면은 두 창, 좁은 화면 (폰 세로) 은 한 창. 예전 기본 'dual' 은 한 번 'auto' 로 ..explorerOnePaneCopy = j['explorerOnePaneCopy'] == 'other' ? 'other' : 'clip' - ..explorerRetapCollapse = j['explorerRetapCollapse'] == true + // E3: 규칙 이전 판이 저장한 false 는 예전 처음 값 - 한 번 규칙 3 (켬) 으로 + ..explorerRetapCollapse = j['explorerRulesV1'] == true ? j['explorerRetapCollapse'] != false : true // 값이 없거나 틀리면 지금 그대로 (100% · 보통), 범위 밖은 끝으로 ..explorerTextScale = j['explorerTextScale'] is num ? (j['explorerTextScale'] as num).round().clamp(explorerTextScaleMin, explorerTextScaleMax) @@ -1124,20 +1129,18 @@ class AppSettings { ..explorerRowSpacing = const {'compact', 'normal', 'wide'}.contains(j['explorerRowSpacing']) ? j['explorerRowSpacing'] as String : 'normal' ..batchRenameLast = j['batchRenameLast'] is Map ? Map.from(j['batchRenameLast'] as Map) : {} ..explorerRootsInTree = j['explorerRootsInTree'] != false - ..explorerAlwaysMarks = j['explorerAlwaysMarks'] != false + // E3: 규칙 이전 판이 저장한 true 는 예전 처음 값 - 한 번 규칙 4 (끔) 로 + ..explorerAlwaysMarks = j['explorerRulesV1'] == true && j['explorerAlwaysMarks'] == true ..explorerSwipePanes = j['explorerSwipePanes'] == true ..androidTrashDays = switch (j['androidTrashDays']) { final int d when d >= 0 && d <= 365 => d, _ => 14 } - ..explorerLayout = const ['single', 'split', 'auto'].contains(j['explorerLayout']) - ? j['explorerLayout'] as String - : j['explorerLayout'] == 'dual' && j['explorerV2'] == true - ? 'dual' - : 'auto' - // Android 는 손가락으로 한 번 누르면 바로 실행이 기본. 예전 기본 'select' 는 Android 에서 한 번 'open' 으로 - ..explorerClick = j['explorerClick'] == 'open' - ? 'open' - : j['explorerClick'] == 'select' && (j['explorerV2'] == true || !Platform.isAndroid) - ? 'select' - : (Platform.isAndroid ? 'open' : 'select') + // E3 (규칙 7 · 결정 3): 처음 값 두 창. 규칙 이전 판의 'auto' 는 예전 처음 값이라 한 번 두 창으로 + ..explorerLayout = switch (j['explorerLayout']) { + 'single' || 'split' => j['explorerLayout'] as String, + 'auto' when j['explorerRulesV1'] == true => 'auto', + _ => 'dual', + } + // 규칙 2: 두 기기 모두 한 번 = 선택. 규칙 이전 Android 의 'open' 은 예전 처음 값이라 한 번 'select' 로 + ..explorerClick = j['explorerClick'] == 'open' && (j['explorerRulesV1'] == true || !Platform.isAndroid) ? 'open' : 'select' ..explorerStyle = const ['windows', 'totalcmd'].contains(j['explorerStyle']) ? j['explorerStyle'] as String : 'xplore' // 118: 예전 기본 '화면 모양 따라' 는 한 번 좌우로 옮긴다 (사용자가 고른 위아래는 그대로) ..explorerOrientation = j['explorerOrientation'] == 'stacked' @@ -1392,6 +1395,12 @@ class AppSettings { // 193: 그 설정이 생기기 전 판 (설정 파일은 있는데 그 항목이 없음 - 예: 10.03) 에서 올라온 사람이 바로 겪는 동작 변화. // 예전 판에는 키가 없어 위의 "예전 값 → 새 값" 조건에 걸리지 않았다 (P11: 탐색기에서 한 번 눌렀는데 바로 열려 놀람) if (j.isNotEmpty && !j.containsKey('explorerClick') && s.explorerClick == 'open') 'explorerClickNew', + // E3 (10/22): 선택 규칙의 처음 값으로 옮긴 것 - 규칙 이전 판에서 올라온 사람에게 한 번 + if (j.isNotEmpty && j['explorerRulesV1'] != true && j['explorerAlwaysMarks'] != false) 'explorerMarksRule', + if (j.isNotEmpty && j['explorerRulesV1'] != true && j['explorerRetapCollapse'] != true) 'explorerRetapRule', + if (j.isNotEmpty && j['explorerRulesV1'] != true && Platform.isAndroid && j['explorerClick'] != 'select') + 'explorerClickRule', + if (j.isNotEmpty && j['explorerRulesV1'] != true && (j['explorerLayout'] ?? 'auto') == 'auto') 'explorerLayoutRule', if (j.isNotEmpty && !j.containsKey('screenOrientation') && s.screenOrientation == 'auto' && Platform.isAndroid) 'orientation', if (j.isNotEmpty && !j.containsKey('swipeNav') && s.swipeNav) 'swipeNav', if (j.isNotEmpty && !j.containsKey('runInBackground') && s.runInBackground && Platform.isAndroid) 'background', diff --git a/lib/ui/explorer_keys.dart b/lib/ui/explorer_keys.dart index b75ffdb..1afcf11 100644 --- a/lib/ui/explorer_keys.dart +++ b/lib/ui/explorer_keys.dart @@ -35,7 +35,7 @@ String explorerKeyLabel(String id) => switch (id) { _ => id, }; -/// 스타일의 처음 키 (83 과 같음). [tc] Total Commander 스타일 +/// 처음 키. [tc] Total Commander 키 (규칙 1, 10/22: Windows 는 스타일과 상관없이 TC 키 + Ctrl+C · X · V · A) Map> defaultExplorerKeys({required bool tc}) => { 'delete': [const SingleActivator(LogicalKeyboardKey.delete), if (tc) const SingleActivator(LogicalKeyboardKey.f8)], 'deletePermanent': [const SingleActivator(LogicalKeyboardKey.delete, shift: true)], @@ -53,6 +53,10 @@ Map> defaultExplorerKeys({required bool tc}) => { 'markNext': [const SingleActivator(LogicalKeyboardKey.insert)], 'mark': [const SingleActivator(LogicalKeyboardKey.space)], 'quickLook': [const SingleActivator(LogicalKeyboardKey.f3)], + 'clipCopy': [const SingleActivator(LogicalKeyboardKey.keyC, control: true)], + 'clipCut': [const SingleActivator(LogicalKeyboardKey.keyX, control: true)], + 'paste': [const SingleActivator(LogicalKeyboardKey.keyV, control: true)], + 'selectAll': [const SingleActivator(LogicalKeyboardKey.keyA, control: true)], } else ...{ 'quickLook': [const SingleActivator(LogicalKeyboardKey.space)], 'refresh': [const SingleActivator(LogicalKeyboardKey.f5)], diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index ff5069a..d7d9d28 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -4453,8 +4453,8 @@ class _ExplorerPageState extends State { width: 420, child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ group(tr('창'), layout, [ - ('auto', tr('화면 크기 따라 (기본)')), - ('dual', tr('두 창')), + ('auto', tr('화면 크기 따라')), + ('dual', tr('두 창 (기본)')), ('split', tr('폴더 + 파일 목록')), ('single', tr('한 창')), ], (v) => layout = v), @@ -5255,7 +5255,7 @@ class _ExplorerPageState extends State { } /// 83: 지금 스타일이 Total Commander 인지 (Rsync 화면은 스타일 단축키를 쓰지 않는다 - 늘 같은 버튼) - bool get _tcKeys => !widget.rsync && _look == ExplorerStyle.totalcmd; + bool get _tcKeys => !widget.rsync && Platform.isWindows; /// 83 · X-plore 비교 5-1 ⑥ (관리자 지시 10/21): 동작마다의 키 → 할 일. 처음 키는 스타일대로 (Total Commander: F5 복사 · F6 이동 · /// F7 새 폴더 · F8 지우기 · Insert/Space 표시 · Ctrl+R 새로 고침 / Windows 탐색기 · X-plore: Ctrl+C · X · V · A · F5 새로 고침), @@ -5925,9 +5925,9 @@ class _ExplorerPageState extends State { : tr('표시 (여러 개 고르기)'), visualDensity: VisualDensity.compact, padding: EdgeInsets.zero, - // ✔ 모양: 고르지 않은 것은 흐리게, 고른 것은 진하게 - icon: Icon(marked ? Icons.check_circle : Icons.check_circle_outline, - size: look.rich ? 22 : 18, + // 규칙 4: V 모양 - 고르지 않은 것은 흐리게, 고른 것은 진하게 + icon: Icon(Icons.check, + size: look.rich ? 24 : 20, color: marked ? JjColors.accent : JjColors.textDim.withValues(alpha: 0.35)), onPressed: () { setState(() => _active = _panes.indexOf(pane)); diff --git a/lib/ui/migration_notice.dart b/lib/ui/migration_notice.dart index 6164164..5c3a978 100644 --- a/lib/ui/migration_notice.dart +++ b/lib/ui/migration_notice.dart @@ -33,6 +33,12 @@ Future showMigrationNotice(BuildContext context, AppController c) async { '끄기: 환경 설정 > 컴포넌트 > 좌우로 밀어 화면 이동'), 'copySkipNewer' => tr('복사 · 이동: 받는 쪽 파일이 더 새것이면 건너뜁니다 (예전에는 예전 원본으로 덮어쓰고, 이동이면 원본도 지웠습니다). ' '건너뛴 파일은 원본에 남고 끝 알림에 그 수가 나옵니다. 되돌리기: 환경 설정 > 파일 탐색기 (또는 Rsync) > 받는 쪽이 더 새것이면 건너뛰기'), + 'explorerMarksRule' => tr('파일 탐색기: 줄 끝 선택 표시는 [선택] 을 켰을 때만 보입니다 (모양 V). ' + '늘 보이게: 환경 설정 > 파일 탐색기 > X-plore 스타일: 선택 표시 늘 보이기'), + 'explorerRetapRule' => tr('파일 탐색기: 펼친 폴더를 다시 누르면 접힙니다. 되돌리기: 환경 설정 > 파일 탐색기 > 펼친 폴더를 다시 누르면 접기'), + 'explorerClickRule' => tr('파일 탐색기: 파일을 한 번 누르면 선택, 두 번 누르면 엽니다. 되돌리기: 환경 설정 > 파일 탐색기 > 누르기'), + 'explorerLayoutRule' => + tr('파일 탐색기 창: 화면 크기 따라 → 두 창 (폰 세로도 좌우). 되돌리기: 환경 설정 > 파일 탐색기 > 창 배치'), 'robocopyLinks' => tr('robocopy 복사 · 이동: 원본 안의 링크 · junction 은 따라가지 않고 건너뜁니다 (/XJ - 예전에는 링크가 가리키는 바깥 폴더의 파일까지 ' '복사 · 이동했습니다). 링크째 복사하려면 robocopy 옵션 칸에 /SJ 를 넣으세요'), _ => id, diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index 4ed9275..c91bd89 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -987,8 +987,8 @@ class _SettingsPageState extends State { trailing: DropdownButton( value: s.explorerLayout, items: [ - DropdownMenuItem(value: 'auto', child: Text(tr('화면 크기 따라 (기본)'))), - DropdownMenuItem(value: 'dual', child: Text(tr('두 창'))), + DropdownMenuItem(value: 'auto', child: Text(tr('화면 크기 따라'))), + DropdownMenuItem(value: 'dual', child: Text(tr('두 창 (기본)'))), DropdownMenuItem(value: 'split', child: Text(tr('폴더 + 파일 목록'))), DropdownMenuItem(value: 'single', child: Text(tr('한 창'))), ], @@ -1011,8 +1011,8 @@ class _SettingsPageState extends State { ), // X-plore 비교 ⚖ (관리자 결정): X-plore 스타일의 표시 동그라미 · 밀기 · 폴더 표시 규칙 SwitchListTile( - title: Text(tr('X-plore 스타일: 표시 동그라미 늘 보이기')), - subtitle: Text(tr('끄면 선택 모드에서만 보입니다 (다른 스타일은 늘 선택 모드에서만)')), + title: Text(tr('X-plore 스타일: 선택 표시 늘 보이기')), + subtitle: Text(tr('끄면 (처음 값) [선택] 을 켰을 때만 보입니다. 다른 스타일은 늘 선택 모드에서만')), value: s.explorerAlwaysMarks, onChanged: (v) => c.updateSettings((x) => x.explorerAlwaysMarks = v), ), @@ -1032,7 +1032,7 @@ class _SettingsPageState extends State { // X-plore 비교 d: 지금 폴더를 다시 누르면 접기 SwitchListTile( title: Text(tr('펼친 폴더를 다시 누르면 접기')), - subtitle: Text(tr('X-plore 처럼. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)')), + subtitle: Text(tr('처음 값 켬. 끄면 접기 · 펼치기는 › 화살표로만 (두 번 눌러도 목록이 움직이지 않음)')), value: s.explorerRetapCollapse, onChanged: (v) => c.updateSettings((x) => x.explorerRetapCollapse = v), ), diff --git a/test/decisions_test.dart b/test/decisions_test.dart index a3a7226..8e12738 100644 --- a/test/decisions_test.dart +++ b/test/decisions_test.dart @@ -36,7 +36,8 @@ void main() { // 이미 있던 설정의 기본값도 결정대로 expect([s.webPauseOnLeave, s.autoCheckUpdates, s.youtubeAdSkip, s.youtubeAdHide, s.runInBackground, s.swipeNav], everyElement(isTrue)); - expect([s.liveSyncOnStart, s.liveSyncIntervalSec, s.explorerLayout, s.zipComic], ['auto', 30, 'auto', false]); + // 탐색기 처음 배치 = 두 창 (규칙 7 · 관리자 결정 3, 10/22) + expect([s.liveSyncOnStart, s.liveSyncIntervalSec, s.explorerLayout, s.zipComic], ['auto', 30, 'dual', false]); expect([AppSettings.uiScaleMin, AppSettings.uiScaleMax], [0.5, 2.5]); // 바꾼 값은 남는다 final b = AppSettings.fromJson((AppSettings() diff --git a/test/explorer_test.dart b/test/explorer_test.dart index 1675fde..68b60cb 100644 --- a/test/explorer_test.dart +++ b/test/explorer_test.dart @@ -91,10 +91,10 @@ void main() { testWidgets('두 창: 폴더 한 번 = 펼치기 · 파일 한 번 = 선택 · 두 번 = 실행 · 길게 = 메뉴 · 다른 창으로 복사', (tester) async { await open(tester); - // 두 창 + 가운데 버튼 줄. 선택 동그라미: X-plore 스타일 (처음 값) 은 늘 보임 (관리자 결정 10/18 - 설정으로 끌 수 있음) + // 두 창 + 가운데 버튼 줄. 선택 표시는 선택 모드에서만 (규칙 4, 10/22 - 설정으로 늘 보이게 할 수 있음) expect(find.text('상위 폴더'), findsOneWidget); expect(find.text('doc.txt'), findsOneWidget); - expect(find.byTooltip('표시 (여러 개 고르기)'), findsWidgets); + expect(find.byTooltip('표시 (여러 개 고르기)'), findsNothing); // 모니터링 · rsync 는 Rsync 화면에만 expect(find.text('모니터링'), findsNothing); expect(find.text('좌 → 우'), findsNothing); @@ -265,9 +265,8 @@ void main() { expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue); }); - testWidgets('X-plore d: 설정 "다시 누르면 접기" - 펼친 지금 폴더를 다시 누르면 접힘 · 두 번 누르기의 두 번째는 접지 않음 (처음 값 끔)', (tester) async { - expect(AppSettings().explorerRetapCollapse, isFalse, reason: '189 그대로'); - c.settings.explorerRetapCollapse = true; + testWidgets('규칙 3: 펼친 폴더를 다시 누르면 접힘 · 두 번 누르기의 두 번째는 접지 않음 (처음 값 켬)', (tester) async { + expect(AppSettings().explorerRetapCollapse, isTrue, reason: '규칙 3 (10/22)'); await open(tester); await act(tester, () => tester.tap(find.text('sub'))); await settle(tester, () => find.text('inner.txt').evaluate().isNotEmpty); @@ -382,7 +381,7 @@ void main() { ..addFile(ArchiveFile.string('new.txt', 'new content')) ..addFile(ArchiveFile.string('more.txt', 'more')))); }); - await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.f5)); + await act(tester, () => refreshKey(tester)); await settle(tester, () => find.text('new.txt').evaluate().isNotEmpty); expect(find.text('new.txt'), findsOneWidget); expect(find.text('more.txt'), findsOneWidget); @@ -567,37 +566,38 @@ void main() { expect(find.textContaining('0개 표시함'), findsNWidgets(2)); // 두 창 모두 (각 창의 선택 수) expect(shell.opened, isEmpty); - IconData markIcon(String name) => (tester - .widget(find.descendant( - of: find.ancestor(of: find.text(name), matching: find.byType(Material)).first, - matching: find.byType(IconButton))) - .icon as Icon) - .icon!; + // 줄 끝 표시 단추: 규칙 4 - 모양은 V (Icons.check), 고른 것은 "표시 지우기" + IconButton markOf(String name) => tester.widget(find.descendant( + of: find.ancestor(of: find.text(name), matching: find.byType(Material)).first, matching: find.byType(IconButton))); + bool markIcon(String name) { + expect((markOf(name).icon as Icon).icon, Icons.check, reason: '규칙 4: V 모양'); + return markOf(name).tooltip == '표시 지우기'; + } // 폴더: 누르면 자기 자신과 안의 것 모두 선택 (펼쳐서 ✔ 로 보여 줌) await act(tester, () => tester.tap(find.text('sub'))); await settle(tester, () => find.text('inner.txt').evaluate().isNotEmpty); expect(find.textContaining('1개 표시함'), findsOneWidget); // 폴더 하나로 안의 것까지 - expect([markIcon('sub'), markIcon('inner.txt'), markIcon('second.txt')], everyElement(Icons.check_circle)); + expect([markIcon('sub'), markIcon('inner.txt'), markIcon('second.txt')], everyElement(isTrue)); // 안의 것 하나를 누르면 그것만 빠지고 나머지는 고른 채로 await act(tester, () => tester.tap(find.text('inner.txt'))); await settle(tester, () => false, rounds: 5); - expect(markIcon('inner.txt'), Icons.check_circle_outline); - expect(markIcon('second.txt'), Icons.check_circle); - expect(markIcon('sub'), Icons.check_circle_outline); + expect(markIcon('inner.txt'), isFalse); + expect(markIcon('second.txt'), isTrue); + expect(markIcon('sub'), isFalse); // 하나를 뺀 폴더를 다시 누르면 그 폴더와 안의 것 모두 취소 (관리자 결정 2, 10/22 - 규칙 6 "다시 누르면 전체 취소") await act(tester, () => tester.tap(find.text('sub'))); await settle(tester, () => false, rounds: 5); expect(find.textContaining('0개 표시함'), findsNWidgets(2)); - expect([markIcon('sub'), markIcon('inner.txt'), markIcon('second.txt')], everyElement(Icons.check_circle_outline)); - // [선택 끝] → 표시가 모두 풀린다 (X-plore 스타일은 동그라미 자체는 늘 보임 - 관리자 결정 10/18) + expect([markIcon('sub'), markIcon('inner.txt'), markIcon('second.txt')], everyElement(isFalse)); + // [선택 끝] → 표시가 모두 풀리고 표시 단추도 사라진다 (규칙 4: 선택 모드에서만) await act(tester, () => tester.tap(find.text('선택 끝').first)); // 두 창 모두에 있음 expect(find.byTooltip('표시 지우기'), findsNothing); - expect(find.byTooltip('표시 (여러 개 고르기)'), findsWidgets); - // 설정에서 끄면 선택 모드에서만 (예전대로) - await tester.runAsync(() => c.updateSettings((x) => x.explorerAlwaysMarks = false)); - await settle(tester, () => false, rounds: 5); expect(find.byTooltip('표시 (여러 개 고르기)'), findsNothing); + // 설정 "늘 보이기" 를 켜면 X-plore 스타일은 선택 모드 밖에서도 보임 + await tester.runAsync(() => c.updateSettings((x) => x.explorerAlwaysMarks = true)); + await settle(tester, () => false, rounds: 5); + expect(find.byTooltip('표시 (여러 개 고르기)'), findsWidgets); }); testWidgets('65: 여러 개를 지울 때 하나가 실패해도 나머지는 지우고, 못 지운 것을 알린다', (tester) async { @@ -1524,12 +1524,12 @@ void main() { await calm(tester); }); - testWidgets('81: Space 로 빠른 보기 (글 파일 앞부분) · 필터에 치는 중에는 그냥 빈칸 · TC 는 F3', (tester) async { + testWidgets('81: 빠른 보기 (글 파일 앞부분) - Windows 는 TC 키 F3 (규칙 1), Android 는 Space · 필터에 치는 중에는 그냥 빈칸', (tester) async { c.settings.explorerStyle = 'windows'; await open(tester); await act(tester, () => tester.tap(find.text('doc.txt'))); await settle(tester, () => false, rounds: 15); - await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.space)); + await act(tester, () => tester.sendKeyEvent(Platform.isWindows ? LogicalKeyboardKey.f3 : LogicalKeyboardKey.space)); await settle(tester, () => find.byType(QuickLook).evaluate().isNotEmpty); expect(find.byType(QuickLook), findsOneWidget); await settle(tester, () => find.text('hello').evaluate().isNotEmpty); @@ -1875,7 +1875,7 @@ void main() { // 바뀌면 (새로 고침) 다시 계산 File(p.join(left, 'sub', 'more.bin')).writeAsBytesSync(List.filled(3000, 1)); - await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.f5)); + await act(tester, () => refreshKey(tester)); await settle(tester, () => find.text('6KB').evaluate().isNotEmpty); expect(find.text('6KB'), findsWidgets); }); @@ -1951,6 +1951,17 @@ Finder markOf(String name) => find.descendant( ); /// 누르기 등을 실제 비동기 구역에서 (그 때 시작한 파일 작업이 테스트의 가짜 시간에 묶이지 않게) +/// 새로 고침 키: Windows 는 TC 키 Ctrl+R (규칙 1 - F5 는 복사), 그 밖은 F5 +Future refreshKey(WidgetTester tester) async { + if (!Platform.isWindows) { + await tester.sendKeyEvent(LogicalKeyboardKey.f5); + return; + } + await tester.sendKeyDownEvent(LogicalKeyboardKey.controlLeft); + await tester.sendKeyEvent(LogicalKeyboardKey.keyR); + await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft); +} + Future act(WidgetTester tester, Future Function() f) async { await tester.runAsync(f); await tester.pump(); diff --git a/test/explorer_text_scale_test.dart b/test/explorer_text_scale_test.dart index 6e7b36d..6041c61 100644 --- a/test/explorer_text_scale_test.dart +++ b/test/explorer_text_scale_test.dart @@ -58,6 +58,8 @@ void main() { // 폴더 + 파일 목록: 오른쪽 목록은 그 폴더의 것만이라 짧다 (트리는 Temp 의 다른 폴더가 많아 아래로 밀린다) ..explorerLayout = 'split' ..explorerStyle = style + // ✔ 칸 자리를 보는 시험: 표시를 늘 보이게 (설정 - 처음 값은 규칙 4 로 선택 모드에서만) + ..explorerAlwaysMarks = true ..explorerTextScale = scale ..explorerRowSpacing = spacing; await tester.runAsync(() => tester.pumpWidget(MaterialApp( diff --git a/test/settings_store_test.dart b/test/settings_store_test.dart index 48dea72..98d29d7 100644 --- a/test/settings_store_test.dart +++ b/test/settings_store_test.dart @@ -141,9 +141,11 @@ void main() { 'runInBackground': false, }); expect(old.screenOrientation, 'auto'); - expect(old.explorerLayout, 'auto'); + // 규칙 7 · 결정 3 (10/22): 처음 배치가 두 창이 되어 예전 'dual' 은 그대로 (옮겼다는 알림 없음) + expect(old.explorerLayout, 'dual'); expect(old.zipComic, isFalse); - expect(old.migrated, containsAll(['orientation', 'explorerLayout', 'zipComic'])); + expect(old.migrated, containsAll(['orientation', 'zipComic'])); + expect(old.migrated, isNot(contains('explorerLayout'))); // 다시 저장 · 읽기: 이제 사용자가 고른 값으로 보고 그대로 · 알림 없음 final again = AppSettings.fromJson((old ..explorerLayout = 'dual' @@ -156,6 +158,27 @@ void main() { expect(AppSettings.fromJson({}).migrated, isEmpty); }); + test('E3 (10/22): 선택 규칙 이전 판의 처음 값은 한 번 규칙대로 옮기고 알림, 저장한 뒤에는 고른 값 그대로', () { + final old = AppSettings.fromJson({ + 'explorerV2': true, + 'explorerLayout': 'auto', + 'explorerAlwaysMarks': true, + 'explorerRetapCollapse': false, + 'explorerClick': 'open', + }); + expect([old.explorerLayout, old.explorerAlwaysMarks, old.explorerRetapCollapse], ['dual', false, true]); + expect(old.explorerClick, Platform.isAndroid ? 'select' : 'open'); // Windows 의 'open' 은 사용자가 고른 값 + expect(old.migrated, containsAll(['explorerLayoutRule', 'explorerMarksRule', 'explorerRetapRule'])); + // 저장한 뒤 사용자가 다시 고른 값은 그대로 · 다시 묻지 않음 + final again = AppSettings.fromJson((old + ..explorerLayout = 'auto' + ..explorerAlwaysMarks = true + ..explorerRetapCollapse = false) + .toJson()); + expect([again.explorerLayout, again.explorerAlwaysMarks, again.explorerRetapCollapse], ['auto', true, false]); + expect(again.migrated, isEmpty); + }); + test('193: 예전 판에서 올라오면 바뀐 동작 (229 받는 쪽이 더 새것이면 건너뜀 · robocopy 링크) 을 한 번 알린다', () { final old = AppSettings.fromJson({'copyMethodFolder': 'robocopy', 'explorerV2': true}); expect(old.migrated, contains('copySkipNewer'));