WIP 83 파일 탐색기 스타일별 단축키: Total Commander F5~F8 · Insert/Space 표시, Windows 탐색기 Ctrl+C · X · V · A (검토 대기)
- 관리자 지시 (F12): 스타일에 맞는 단축키. - Total Commander 모양: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert (표시하고 다음 줄로) · Space (표시) · Ctrl+R 새로 고침. Space 는 이 모양에서 빠른 필터를 열지 않는다 (필터가 열려 있으면 그대로 글자). - Windows 탐색기 · X-plore 모양: Ctrl+C (담기) · Ctrl+X (잘라내기) · Ctrl+V (지금 폴더에 붙여넣기) · Ctrl+A (지금 폴더 모두 표시) · F5 새로 고침 (예전과 같음). - 모든 모양 공통 (예전과 같음): Delete · Shift+Delete · F2 · Esc · Ctrl+F · Backspace · Alt+← → · Tab. - Rsync 화면은 스타일 단축키를 쓰지 않는다 (F5 새로 고침 그대로). - 내 판단: TC 모양에서는 F5 가 복사라 새로 고침을 Ctrl+R 로 (Total Commander 와 같게). - 환경 설정 > 파일 탐색기 > 스타일 설명에 단축키를 적었다. - 시험: explorer_test 83 (TC: Insert · Space 표시 · Space 가 필터를 열지 않음 · F5 복사 창 · F7 새 폴더 / Windows: Ctrl+A · Ctrl+C). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음6
parent
5007d30afa
commit
5411558779
|
|
@ -859,6 +859,7 @@
|
|||
"다음부터 묻지 않기 (환경 설정 > 실행 에서 바꿀 수 있음)": "Don't ask again (can be changed in Settings > Run)",
|
||||
"단계": "Steps",
|
||||
"단어 첫 글자만 크게 (Abc)": "Capitalize words (Abc)",
|
||||
"단축키 - Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 · Ctrl+R 새로 고침. 그 밖: Ctrl+C · X · V (복사 · 잘라내기 · 붙여넣기) · Ctrl+A 모두 표시 · F5 새로 고침": "Shortcuts - Total Commander: F5 copy · F6 move · F7 new folder · F8 delete · Insert/Space mark · Ctrl+R refresh. Others: Ctrl+C · X · V (copy · cut · paste) · Ctrl+A mark all · F5 refresh",
|
||||
"단축키 해제": "Unregister hotkey",
|
||||
"닫기": "Close",
|
||||
"닫힘 (실행 안 함)": "Closed (not run)",
|
||||
|
|
|
|||
|
|
@ -859,6 +859,7 @@
|
|||
"다음부터 묻지 않기 (환경 설정 > 실행 에서 바꿀 수 있음)": "次回から確認しない(環境設定 > 実行 で変更できます)",
|
||||
"단계": "ステップ",
|
||||
"단어 첫 글자만 크게 (Abc)": "単語の先頭だけ大文字 (Abc)",
|
||||
"단축키 - Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 · Ctrl+R 새로 고침. 그 밖: Ctrl+C · X · V (복사 · 잘라내기 · 붙여넣기) · Ctrl+A 모두 표시 · F5 새로 고침": "ショートカット - Total Commander: F5 コピー · F6 移動 · F7 新しいフォルダー · F8 削除 · Insert/Space マーク · Ctrl+R 更新。その他: Ctrl+C · X · V (コピー · 切り取り · 貼り付け) · Ctrl+A すべてマーク · F5 更新",
|
||||
"단축키 해제": "ショートカット解除",
|
||||
"닫기": "閉じる",
|
||||
"닫힘 (실행 안 함)": "閉じた (実行しない)",
|
||||
|
|
|
|||
|
|
@ -859,6 +859,7 @@
|
|||
"다음부터 묻지 않기 (환경 설정 > 실행 에서 바꿀 수 있음)": "下次不再询问(可在 设置 > 运行 中更改)",
|
||||
"단계": "步数",
|
||||
"단어 첫 글자만 크게 (Abc)": "单词首字母大写 (Abc)",
|
||||
"단축키 - Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 · Ctrl+R 새로 고침. 그 밖: Ctrl+C · X · V (복사 · 잘라내기 · 붙여넣기) · Ctrl+A 모두 표시 · F5 새로 고침": "快捷键 - Total Commander: F5 复制 · F6 移动 · F7 新建文件夹 · F8 删除 · Insert/Space 标记 · Ctrl+R 刷新。其他: Ctrl+C · X · V (复制 · 剪切 · 粘贴) · Ctrl+A 全部标记 · F5 刷新",
|
||||
"단축키 해제": "注销快捷键",
|
||||
"닫기": "关闭",
|
||||
"닫힘 (실행 안 함)": "已关闭 (未执行)",
|
||||
|
|
|
|||
|
|
@ -3169,7 +3169,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
Widget _buildPage(BuildContext context) {
|
||||
return CallbackShortcuts(
|
||||
bindings: {
|
||||
const SingleActivator(LogicalKeyboardKey.f5): () => _button(ExplorerButton.refresh),
|
||||
// 83: 스타일에 맞는 단축키 (Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 ·
|
||||
// Ctrl+R 새로 고침 / Windows 탐색기 · X-plore: Ctrl+C · X · V · A · F5 새로 고침)
|
||||
..._styleKeys(),
|
||||
const SingleActivator(LogicalKeyboardKey.delete): () => _button(ExplorerButton.delete),
|
||||
// 65: Shift+Delete = 휴지통을 거치지 않고 (창에서 한 번 더 확인)
|
||||
const SingleActivator(LogicalKeyboardKey.delete, shift: true): () {
|
||||
|
|
@ -3603,11 +3605,60 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
_keys.requestFocus();
|
||||
}
|
||||
|
||||
/// 83: 지금 스타일이 Total Commander 인지 (Rsync 화면은 스타일 단축키를 쓰지 않는다 - 늘 같은 버튼)
|
||||
bool get _tcKeys => !widget.rsync && _look == ExplorerStyle.totalcmd;
|
||||
|
||||
/// 83: 스타일에 맞는 단축키
|
||||
Map<ShortcutActivator, VoidCallback> _styleKeys() {
|
||||
if (widget.rsync) return {const SingleActivator(LogicalKeyboardKey.f5): () => _button(ExplorerButton.refresh)};
|
||||
if (_tcKeys) {
|
||||
return {
|
||||
const SingleActivator(LogicalKeyboardKey.f5): () => _button(ExplorerButton.copy),
|
||||
const SingleActivator(LogicalKeyboardKey.f6): () => _button(ExplorerButton.move),
|
||||
const SingleActivator(LogicalKeyboardKey.f7): () => _button(ExplorerButton.newFolder),
|
||||
const SingleActivator(LogicalKeyboardKey.f8): () => _button(ExplorerButton.delete),
|
||||
const SingleActivator(LogicalKeyboardKey.keyR, control: true): () => _button(ExplorerButton.refresh),
|
||||
// Insert: 표시하고 다음 줄로 · Space: 표시 (TC 처럼)
|
||||
const SingleActivator(LogicalKeyboardKey.insert): () => _markFocused(next: true),
|
||||
const SingleActivator(LogicalKeyboardKey.space): () => _markFocused(next: false),
|
||||
};
|
||||
}
|
||||
return {
|
||||
const SingleActivator(LogicalKeyboardKey.f5): () => _button(ExplorerButton.refresh),
|
||||
const SingleActivator(LogicalKeyboardKey.keyC, control: true): () => _toClipboard(_targets(_pane), move: false),
|
||||
const SingleActivator(LogicalKeyboardKey.keyX, control: true): () => _toClipboard(_targets(_pane), move: true),
|
||||
const SingleActivator(LogicalKeyboardKey.keyV, control: true): () => _paste(_pane, _pane.current),
|
||||
const SingleActivator(LogicalKeyboardKey.keyA, control: true): () {
|
||||
if (!_selecting) setState(() => _selecting = true);
|
||||
_markWhere(_pane, _pane.current, (_) => true);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// 83: (TC) 지금 줄을 표시 · 풀기, [next] 면 다음 줄로
|
||||
Future<void> _markFocused({required bool next}) async {
|
||||
final pane = _pane;
|
||||
final f = pane.focused;
|
||||
if (f == null || samePath(f, pane.root)) return;
|
||||
final list = _shown(pane, vDirname(f));
|
||||
final i = list.indexWhere((x) => samePath(x.path, f));
|
||||
if (i < 0) return;
|
||||
if (!_selecting) setState(() => _selecting = true);
|
||||
await _toggleMark(pane, list[i]);
|
||||
if (next && i + 1 < list.length) {
|
||||
pane.focused = list[i + 1].path;
|
||||
pane.changed();
|
||||
_reveal(pane, list[i + 1].path);
|
||||
}
|
||||
}
|
||||
|
||||
KeyEventResult _typeToFilter(KeyEvent ev) {
|
||||
if (ev is! KeyDownEvent) return KeyEventResult.ignored;
|
||||
final ch = ev.character;
|
||||
final kb = HardwareKeyboard.instance;
|
||||
if (ch == null || ch.isEmpty || ch.runes.first < 0x20 || ch.runes.first == 0x7f) return KeyEventResult.ignored;
|
||||
// 83: TC 모양에서 Space 는 표시 (빠른 필터가 아니라)
|
||||
if (ch == ' ' && _tcKeys && !_panes[_active].filterOpen) return KeyEventResult.ignored;
|
||||
if (kb.isControlPressed || kb.isAltPressed || kb.isMetaPressed) return KeyEventResult.ignored;
|
||||
// 글자 칸 (필터 · 경로 입력 등) 에 치는 중이면 그 칸이 받는다
|
||||
if (FocusManager.instance.primaryFocus?.context?.findAncestorStateOfType<EditableTextState>() != null) {
|
||||
|
|
|
|||
|
|
@ -793,8 +793,13 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
),
|
||||
SettingTile(
|
||||
title: Text(tr('스타일')),
|
||||
subtitle: Text(tr('아이콘 · 목록 모양: X-plore (썸네일 · 두 줄) · Windows 탐색기 (컬러 아이콘 · 열) · '
|
||||
'Total Commander (촘촘한 목록 · [폴더])')),
|
||||
// 83: 스타일마다 단축키
|
||||
subtitle: Text([
|
||||
tr('아이콘 · 목록 모양: X-plore (썸네일 · 두 줄) · Windows 탐색기 (컬러 아이콘 · 열) · '
|
||||
'Total Commander (촘촘한 목록 · [폴더])'),
|
||||
tr('단축키 - Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 · Ctrl+R 새로 고침. '
|
||||
'그 밖: Ctrl+C · X · V (복사 · 잘라내기 · 붙여넣기) · Ctrl+A 모두 표시 · F5 새로 고침'),
|
||||
].join('\n')),
|
||||
trailing: DropdownButton<String>(
|
||||
value: s.explorerStyle,
|
||||
items: [
|
||||
|
|
|
|||
|
|
@ -964,6 +964,55 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
testWidgets('83: Total Commander 모양 단축키 - F5 복사 · F7 새 폴더 · Space/Insert 표시 (Space 는 빠른 필터가 아님)', (tester) async {
|
||||
File(p.join(left, 'e.txt')).writeAsStringSync('e');
|
||||
c.settings.explorerStyle = 'totalcmd';
|
||||
await open(tester);
|
||||
await act(tester, () => tester.tap(find.text('doc')));
|
||||
await settle(tester, () => false, rounds: 15);
|
||||
// Insert: 표시하고 다음 줄 (e.txt) 로, Space: 지금 줄 (e.txt) 표시
|
||||
await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.insert));
|
||||
await calm(tester);
|
||||
expect(find.textContaining('1개 표시함'), findsOneWidget);
|
||||
await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.space));
|
||||
await calm(tester);
|
||||
expect(find.textContaining('2개 표시함'), findsOneWidget, reason: 'Insert 뒤 다음 줄이 표시됨');
|
||||
expect(find.byIcon(Icons.filter_alt), findsNothing, reason: 'Space 가 빠른 필터를 열지 않는다');
|
||||
// F5 = 복사 (확인 창) - 취소
|
||||
await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.f5));
|
||||
await calm(tester);
|
||||
expect(find.widgetWithText(FilledButton, '복사'), findsOneWidget);
|
||||
await tester.tap(find.text('취소').last);
|
||||
await calm(tester);
|
||||
// F7 = 새 폴더
|
||||
await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.f7));
|
||||
await calm(tester);
|
||||
expect(find.text('새 폴더'), findsWidgets);
|
||||
await tester.tap(find.text('취소').last);
|
||||
await calm(tester);
|
||||
});
|
||||
|
||||
testWidgets('83: Windows 탐색기 모양 단축키 - Ctrl+A 모두 표시 · Ctrl+C 담기 · Ctrl+V 붙여넣기', (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, () async {
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlLeft);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyA);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
||||
});
|
||||
await calm(tester);
|
||||
expect(find.textContaining('2개 표시함'), findsOneWidget, reason: 'doc.txt · sub');
|
||||
await act(tester, () async {
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlLeft);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.keyC);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
||||
});
|
||||
await calm(tester);
|
||||
expect(find.textContaining('담았습니다'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('84: 열 머리를 누르면 그 기준으로 · 다시 누르면 거꾸로 (처음: 모든 폴더 같은 정렬)', (tester) async {
|
||||
File(p.join(left, 'a_big.txt')).writeAsStringSync('x' * 50);
|
||||
c.settings.explorerStyle = 'windows';
|
||||
|
|
|
|||
Loading…
Reference in New Issue