diff --git a/assets/l10n/en.json b/assets/l10n/en.json index 16f23c7..a41b299 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -730,6 +730,7 @@ "기억한 복사가 없습니다. 파일 탐색기에서 복사 · 이동하면 여기에 자동으로 등록됩니다.": "No remembered copies. Copies · moves in the file explorer are added here automatically.", "기타 즐겨찾기": "Other bookmarks", "길게 누르기 · 오른쪽 클릭은 늘 기능 메뉴 (복사 · 이동 · 삭제 · 이름 변경 …). Ctrl + 클릭: 여러 개 고르기": "Long press · right click always opens the action menu (copy · move · delete · rename …). Ctrl + click: select several", + "길게 누르면 {0}": "long-press: {0}", "길이": "Length", "길이 {0}": "Length {0}", "끄기": "Off", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 50edc67..be25b79 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -730,6 +730,7 @@ "기억한 복사가 없습니다. 파일 탐색기에서 복사 · 이동하면 여기에 자동으로 등록됩니다.": "記憶したコピーはありません。ファイルエクスプローラーでコピー · 移動すると自動で登録されます。", "기타 즐겨찾기": "その他のブックマーク", "길게 누르기 · 오른쪽 클릭은 늘 기능 메뉴 (복사 · 이동 · 삭제 · 이름 변경 …). Ctrl + 클릭: 여러 개 고르기": "長押し · 右クリックは常に機能メニュー (コピー · 移動 · 削除 · 名前の変更 …)。Ctrl + クリック: 複数選択", + "길게 누르면 {0}": "長押しで{0}", "길이": "長さ", "길이 {0}": "長さ {0}", "끄기": "オフ", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 3dabdc4..8d66aa2 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -730,6 +730,7 @@ "기억한 복사가 없습니다. 파일 탐색기에서 복사 · 이동하면 여기에 자동으로 등록됩니다.": "没有记住的复制。在文件浏览器中复制 · 移动后会自动登记到这里。", "기타 즐겨찾기": "其他书签", "길게 누르기 · 오른쪽 클릭은 늘 기능 메뉴 (복사 · 이동 · 삭제 · 이름 변경 …). Ctrl + 클릭: 여러 개 고르기": "长按 · 右键总是打开功能菜单 (复制 · 移动 · 删除 · 重命名 …)。Ctrl + 点击: 多选", + "길게 누르면 {0}": "长按: {0}", "길이": "时长", "길이 {0}": "时长 {0}", "끄기": "关闭", diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index 8d34998..aabdff1 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -1718,6 +1718,21 @@ class _ExplorerPageState extends State { } } + /// X-plore b: 가운데 버튼 길게 누르기 - 복사 ↔ 이동, 지우기 → 영구 삭제로 묻기 (확인 창은 그대로) + Future _buttonLong(ExplorerButton b) async { + switch (b) { + case ExplorerButton.copy: + return _button(ExplorerButton.move); + case ExplorerButton.move: + return _button(ExplorerButton.copy); + case ExplorerButton.delete: + _shiftDelete = true; + return _button(ExplorerButton.delete); + default: + return; + } + } + Future _button(ExplorerButton b) async { final pane = _pane; switch (b) { @@ -3461,6 +3476,8 @@ class _ExplorerPageState extends State { final bar = s.explorerToolbar == 'hidden' && !widget.rsync ? null : _Toolbar( + // X-plore b: 길게 누르기 = 짝 동작 (Rsync 화면은 없음) + onLongPress: widget.rsync ? null : _buttonLong, // Rsync 화면: 늘 같은 버튼 (→ ← ⇄ 모니터링) 을 두 창 사이에 buttons: widget.rsync ? ExplorerButton.rsyncButtons : ExplorerButton.fromSettings(s.explorerButtons), vertical: side && !rsyncOne, @@ -4511,12 +4528,16 @@ class _Toolbar extends StatelessWidget { /// 좁아도 [더 보기] 로 보내지 않는 버튼 (창 배치 · 담아 둔 것이 있을 때 붙여넣기) final Set pinned; + + /// X-plore b: 길게 누르기 = 짝 동작 (복사 ↔ 이동, 지우기 → 영구 삭제). 짝이 없는 버튼은 null + final void Function(ExplorerButton)? onLongPress; const _Toolbar( {required this.buttons, required this.vertical, required this.onPressed, required this.enabled, required this.selected, + this.onLongPress, this.twoPanes = true, this.pinned = const {ExplorerButton.orient}, bool? panesSide}) @@ -4526,6 +4547,14 @@ class _Toolbar extends StatelessWidget { /// 191 좁은 Rsync 화면 (창 하나씩, 버튼 줄은 가로) 은 좌우 final bool panesSide; + /// X-plore b: 길게 누르면 하는 짝 동작의 이름 (없으면 null) + static String? longPressLabel(ExplorerButton b) => switch (b) { + ExplorerButton.copy => '이동', + ExplorerButton.move => '복사', + ExplorerButton.delete => '영구 삭제', + _ => null, + }; + /// 118: 지금 배치를 보여 준다 (누르면 반대로) String _orientLabel() => !twoPanes ? '두 창으로' : panesSide ? '좌우 ⇆' : '위아래 ⇅'; String _orientTip() => !twoPanes @@ -4562,10 +4591,14 @@ class _Toolbar extends StatelessWidget { Widget btn(ExplorerButton b) { final on = enabled(b); final sel = selected(b); + final pair = longPressLabel(b); + final long = onLongPress; return Tooltip( - message: tr(b == ExplorerButton.orient ? _orientTip() : _label(b)), + message: tr(b == ExplorerButton.orient ? _orientTip() : _label(b)) + + (pair != null && long != null ? ' · ${trf('길게 누르면 {0}', [tr(pair)])}' : ''), child: InkWell( onTap: on ? () => onPressed(b) : null, + onLongPress: on && pair != null && long != null ? () => long(b) : null, borderRadius: BorderRadius.circular(6), child: SizedBox( width: 72, diff --git a/test/explorer_test.dart b/test/explorer_test.dart index 2ab96f1..2413602 100644 --- a/test/explorer_test.dart +++ b/test/explorer_test.dart @@ -159,6 +159,23 @@ void main() { await tester.pump(const Duration(seconds: 2)); }); + testWidgets('X-plore b: 가운데 버튼 길게 누르기 = 짝 동작 - [복사] 길게 = 이동 창 · [삭제] 길게 = 영구 삭제 창', (tester) async { + await open(tester); + await act(tester, () => tester.tap(find.text('doc.txt'))); + await settle(tester, () => false, rounds: 15); + await tester.longPress(find.text('복사').first); + await calm(tester); + expect(find.widgetWithText(FilledButton, '이동'), findsOneWidget, reason: '복사 길게 = 이동'); + await tester.tap(find.text('취소').last); + await calm(tester); + await tester.longPress(find.text('삭제').first); + await calm(tester); + expect(find.widgetWithText(FilledButton, '영구 삭제'), findsOneWidget, reason: '삭제 길게 = 영구 삭제로 묻기'); + await tester.tap(find.text('취소').last); + await calm(tester); + expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue); + }); + testWidgets('178 보충: 설정의 원본 지우기 옵션은 복사 창에서 빠진 까닭을 알린다 (이동 창에는 없음)', (tester) async { c.settings ..copyMethodFile = 'robocopy'