WIP P0 파일 탐색기: 글자 칸에 치는 중에는 파일 단축키가 키를 가로채지 않게 (15 지적 · 관리자 P0) (검토 대기)
- 예전: 화면의 CallbackShortcuts 가 글자 칸 (빠른 필터 등) 보다 먼저 키를 받아, 칸에서 Delete 를 누르면 파일 지우기 창이 뜨고 Ctrl+A · C · X · V · Space · F8 · Backspace 도 파일 동작으로 쓰였다 (Ctrl+V 는 파일을 지금 폴더에 붙여넣을 수 있었다). Delete · F2 · Backspace 는 83 전부터 있던 것. - 이제 단축키는 Focus.onKeyEvent 로 받아, 글자 칸에 포커스가 있으면 넘긴다 (칸의 기본 동작). 모든 모양 · Rsync 화면 공통. Esc 는 지금처럼 필터 칸이 받아 필터를 닫는다. - 찾기 · 이름 바꾸기 · 경로 입력은 창 (다른 경로) 이라 이 단축키 아래에 있지 않다. - 시험: explorer_test P0 (windows · xplore · totalcmd 각각 - 필터 칸에서 Ctrl+A 는 글 전체 선택, Ctrl+C · V · Delete · Space · F8 · Backspace 가 파일을 표시 · 담기 · 지우기 하지 않고 칸이 그대로, Esc 로 닫힘). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음6
parent
5411558779
commit
a780139eeb
|
|
@ -3167,8 +3167,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
}
|
||||
|
||||
Widget _buildPage(BuildContext context) {
|
||||
return CallbackShortcuts(
|
||||
bindings: {
|
||||
// P0 (15 지적 10/17): 글자 칸 (빠른 필터 · 경로 등) 에 치는 중이면 파일 단축키를 쓰지 않는다 - 예전에는 CallbackShortcuts 가
|
||||
// 글자 칸보다 먼저 키를 받아, 칸에서 Delete 를 누르면 파일 지우기 창이 뜨고 Ctrl+A · C · V · Space 도 파일에 쓰였다
|
||||
final bindings = <ShortcutActivator, VoidCallback>{
|
||||
// 83: 스타일에 맞는 단축키 (Total Commander: F5 복사 · F6 이동 · F7 새 폴더 · F8 지우기 · Insert/Space 표시 ·
|
||||
// Ctrl+R 새로 고침 / Windows 탐색기 · X-plore: Ctrl+C · X · V · A · F5 새로 고침)
|
||||
..._styleKeys(),
|
||||
|
|
@ -3195,7 +3196,11 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
const SingleActivator(LogicalKeyboardKey.arrowRight, alt: true): () => _travel(_panes[_active], forward: true),
|
||||
const SingleActivator(LogicalKeyboardKey.tab): () =>
|
||||
setState(() => _active = _dual ? 1 - _active : 0),
|
||||
},
|
||||
};
|
||||
return Focus(
|
||||
canRequestFocus: false,
|
||||
skipTraversal: true,
|
||||
onKeyEvent: (_, ev) => _shortcut(bindings, ev),
|
||||
child: Focus(
|
||||
focusNode: _keys,
|
||||
autofocus: true,
|
||||
|
|
@ -3652,6 +3657,21 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 글자 칸에 치는 중인지 (그 칸이 키를 받아야 한다)
|
||||
static bool get _typing => FocusManager.instance.primaryFocus?.context?.findAncestorStateOfType<EditableTextState>() != null;
|
||||
|
||||
/// 탐색기 단축키: 글자 칸에 치는 중이면 넘기고 (칸의 기본 동작 - 선택 · 복사 · 붙여넣기 · 지우기 · 빈칸), 아니면 맞는 것을 한다
|
||||
KeyEventResult _shortcut(Map<ShortcutActivator, VoidCallback> bindings, KeyEvent ev) {
|
||||
if (ev is KeyUpEvent || _typing) return KeyEventResult.ignored;
|
||||
for (final b in bindings.entries) {
|
||||
if (b.key.accepts(ev, HardwareKeyboard.instance)) {
|
||||
b.value();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
KeyEventResult _typeToFilter(KeyEvent ev) {
|
||||
if (ev is! KeyDownEvent) return KeyEventResult.ignored;
|
||||
final ch = ev.character;
|
||||
|
|
@ -3661,9 +3681,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
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) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
if (_typing) return KeyEventResult.ignored;
|
||||
if (!_ready || _panes.isEmpty) return KeyEventResult.ignored;
|
||||
final pane = _panes[_active];
|
||||
_openFilter(pane, pane.filterOpen ? pane.filterCtl.text + ch : ch);
|
||||
|
|
|
|||
|
|
@ -1013,6 +1013,52 @@ void main() {
|
|||
expect(find.textContaining('담았습니다'), findsOneWidget);
|
||||
});
|
||||
|
||||
// P0 (15 지적 10/17): 글자 칸에 치는 중에는 파일 단축키가 키를 가로채지 않는다 (예전: 필터 칸의 Delete → 파일 지우기 창)
|
||||
for (final style in const ['windows', 'xplore', 'totalcmd']) {
|
||||
testWidgets('P0 $style: 빠른 필터 칸에서 Ctrl+A · C · V · Delete · Space · F8 · Backspace 는 파일에 쓰이지 않는다', (tester) async {
|
||||
c.settings.explorerStyle = style;
|
||||
await open(tester);
|
||||
await act(tester, () => tester.tap(find.text(style == 'totalcmd' ? 'doc' : 'doc.txt')));
|
||||
await settle(tester, () => false, rounds: 15);
|
||||
// Ctrl+F 로 필터 칸 열고 글자
|
||||
Future<void> ctrl(LogicalKeyboardKey k) => act(tester, () async {
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlLeft);
|
||||
await tester.sendKeyEvent(k);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
||||
});
|
||||
await ctrl(LogicalKeyboardKey.keyF);
|
||||
await calm(tester);
|
||||
final field = find.byType(TextField);
|
||||
expect(field, findsOneWidget);
|
||||
await tester.enterText(field, 'do');
|
||||
await calm(tester);
|
||||
await ctrl(LogicalKeyboardKey.keyA);
|
||||
await calm(tester);
|
||||
final ctl = tester.widget<TextField>(field).controller!;
|
||||
expect(ctl.selection, const TextSelection(baseOffset: 0, extentOffset: 2), reason: 'Ctrl+A 는 칸의 글 전체 선택');
|
||||
await ctrl(LogicalKeyboardKey.keyC);
|
||||
await calm(tester);
|
||||
await ctrl(LogicalKeyboardKey.keyV);
|
||||
await calm(tester);
|
||||
for (final k in [LogicalKeyboardKey.delete, LogicalKeyboardKey.space, LogicalKeyboardKey.f8, LogicalKeyboardKey.backspace]) {
|
||||
await act(tester, () => tester.sendKeyEvent(k));
|
||||
await calm(tester);
|
||||
}
|
||||
expect(find.textContaining('표시함'), findsNothing, reason: '파일이 표시되지 않음');
|
||||
expect(find.textContaining('담았습니다'), findsNothing, reason: '파일을 담지 않음');
|
||||
expect(find.textContaining('휴지통'), findsNothing, reason: '지우기 창이 뜨지 않음');
|
||||
expect(find.textContaining('지울까요'), findsNothing);
|
||||
expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue);
|
||||
expect(field, findsOneWidget, reason: '칸은 그대로 열려 있음 (Backspace 로 위 폴더로 가지 않음)');
|
||||
// Esc 는 지금처럼 필터를 닫는다
|
||||
await act(tester, () => tester.sendKeyEvent(LogicalKeyboardKey.escape));
|
||||
await calm(tester);
|
||||
expect(find.byType(TextField), findsNothing);
|
||||
ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars();
|
||||
await calm(tester);
|
||||
});
|
||||
}
|
||||
|
||||
testWidgets('84: 열 머리를 누르면 그 기준으로 · 다시 누르면 거꾸로 (처음: 모든 폴더 같은 정렬)', (tester) async {
|
||||
File(p.join(left, 'a_big.txt')).writeAsStringSync('x' * 50);
|
||||
c.settings.explorerStyle = 'windows';
|
||||
|
|
|
|||
Loading…
Reference in New Issue