좁은 Rsync 화면은 창 하나씩 (191) · 방향 실행 버튼을 앞에 (208)
- 191 (관리자 지시): 좌우 두 창인데 폭이 560 보다 좁으면 (폴드 접은 화면 · 폰 세로) 창이 140~330px 라 폴더 이름이 잘렸다. 창 하나씩 보이고 위에 [왼쪽 · 폴더 | 오른쪽 · 폴더] 로 바꿔 본다. 버튼 줄은 가로, 이름은 창 배치대로 (좌 → 우). - 208 (관리자 지시): Rsync 버튼 순서를 [좌 → 우 · 좌 ← 우 · 좌 ⇄ 우 · 모니터링 · …] 로 - 좁아서 [더 보기] 로 가는 것은 뒤쪽 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>fix_mkv
parent
a94009524c
commit
996491547b
|
|
@ -1294,6 +1294,7 @@
|
|||
"예전 버전으로 되돌립니다. 지금 버전의 설정 (동영상 목록 · 즐겨찾기 포함) 은 보관해 두었다가, 나중에 이 버전으로 다시 오면 되살릴 수 있습니다.": "This rolls back to an earlier version. The current version's settings (including the video list and bookmarks) are kept and can be restored when you come back to this version later.",
|
||||
"오늘 받을 수 있는 자막 수를 모두 사용했습니다. ({0})": "You have used all of today's subtitle downloads. ({0})",
|
||||
"오래된 작업 기록 파일 (app.log.1 등). 지금 쓰는 기록은 남깁니다": "Old job log files (app.log.1 etc.). The current log is kept",
|
||||
"오른쪽": "Right",
|
||||
"오른쪽 90°": "Right 90°",
|
||||
"오픈 소스 라이선스": "Open source licenses",
|
||||
"올려 둔 버전이 없습니다.": "No versions have been published.",
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,7 @@
|
|||
"예전 버전으로 되돌립니다. 지금 버전의 설정 (동영상 목록 · 즐겨찾기 포함) 은 보관해 두었다가, 나중에 이 버전으로 다시 오면 되살릴 수 있습니다.": "以前のバージョンに戻します。現在のバージョンの設定 (動画リスト · ブックマークを含む) は保管しておき、あとでこのバージョンに戻ると復元できます。",
|
||||
"오늘 받을 수 있는 자막 수를 모두 사용했습니다. ({0})": "今日ダウンロードできる字幕数をすべて使いました。({0})",
|
||||
"오래된 작업 기록 파일 (app.log.1 등). 지금 쓰는 기록은 남깁니다": "古い作業記録ファイル(app.log.1 など)。現在の記録は残します",
|
||||
"오른쪽": "右",
|
||||
"오른쪽 90°": "右 90°",
|
||||
"오픈 소스 라이선스": "オープンソースライセンス",
|
||||
"올려 둔 버전이 없습니다.": "公開したバージョンがありません。",
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,7 @@
|
|||
"예전 버전으로 되돌립니다. 지금 버전의 설정 (동영상 목록 · 즐겨찾기 포함) 은 보관해 두었다가, 나중에 이 버전으로 다시 오면 되살릴 수 있습니다.": "将回退到以前的版本。当前版本的设置 (包括视频列表 · 书签) 会被保存,以后回到此版本时可以恢复。",
|
||||
"오늘 받을 수 있는 자막 수를 모두 사용했습니다. ({0})": "今天可下载的字幕数已用完。({0})",
|
||||
"오래된 작업 기록 파일 (app.log.1 등). 지금 쓰는 기록은 남깁니다": "旧的任务记录文件(app.log.1 等)。当前记录会保留",
|
||||
"오른쪽": "右",
|
||||
"오른쪽 90°": "向右 90°",
|
||||
"오픈 소스 라이선스": "开源许可",
|
||||
"올려 둔 버전이 없습니다.": "没有已发布的版本。",
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ import 'explorer_bookmarks.dart';
|
|||
///
|
||||
/// [rsync] 면 Rsync 화면: 같은 모양이지만 늘 좌우 두 창 · 폴더만 · 창마다 폴더 하나만 고른다.
|
||||
/// 가운데 → (왼쪽 → 오른쪽) · ← (오른쪽 → 왼쪽) · ⇄ (둘 다 함께) 로 rsync, [모니터링] 에 실행한 것이 남는다.
|
||||
/// 191: Rsync 화면이 좌우 두 창 + 가운데 버튼 줄로 보이는 가장 작은 폭 (이보다 좁으면 창 하나씩)
|
||||
const double rsyncTwoPaneMinWidth = 560;
|
||||
|
||||
/// 189: 트리 화살표 칸의 폭 - 손가락으로 누르는 기기 (Android · iOS) 는 넓게 (줄 높이와 함께 누르는 영역)
|
||||
double treeChevronWidth(TargetPlatform platform) =>
|
||||
platform == TargetPlatform.android || platform == TargetPlatform.iOS ? 44 : 20;
|
||||
|
|
@ -158,7 +161,8 @@ enum ExplorerButton {
|
|||
static List<ExplorerButton> get explorerValues => [for (final b in values) if (!b.rsyncOnly) b];
|
||||
|
||||
/// Rsync 화면의 버튼 (늘 이대로, 두 창 사이)
|
||||
static const rsyncButtons = [up, refresh, newFolder, bookmarks, toRight, toLeft, both, monitor];
|
||||
// 208: 방향 실행 (Rsync 화면의 핵심) 을 앞에 - 좁아서 [더 보기] 로 가는 것은 뒤쪽
|
||||
static const rsyncButtons = [toRight, toLeft, both, monitor, up, refresh, newFolder, bookmarks];
|
||||
|
||||
/// 147 (관리자): 처음 값 (버튼 구성을 바꾸지 않았을 때) 은 자주 쓰는 것부터 - 좁은 화면에서 [더 보기] 로 가는 것은 뒤쪽
|
||||
static List<ExplorerButton> get defaultOrder {
|
||||
|
|
@ -2815,12 +2819,16 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
'stacked' => false,
|
||||
_ => box.maxWidth >= box.maxHeight,
|
||||
};
|
||||
// 191: Rsync 화면이 좁으면 (폴드 접은 화면 · 폰 세로의 좌우 두 창) 창 하나씩 - [왼쪽 | 오른쪽] 으로 바꿔 본다
|
||||
// (두 창 + 가운데 버튼 줄이면 창이 140px 남짓이라 폴더 이름이 잘렸다)
|
||||
final rsyncOne = widget.rsync && side && box.maxWidth < rsyncTwoPaneMinWidth;
|
||||
final bar = s.explorerToolbar == 'hidden' && !widget.rsync
|
||||
? null
|
||||
: _Toolbar(
|
||||
// Rsync 화면: 늘 같은 버튼 (→ ← ⇄ 모니터링) 을 두 창 사이에
|
||||
buttons: widget.rsync ? ExplorerButton.rsyncButtons : ExplorerButton.fromSettings(s.explorerButtons),
|
||||
vertical: side,
|
||||
vertical: side && !rsyncOne,
|
||||
panesSide: side,
|
||||
onPressed: _button,
|
||||
enabled: (b) => switch (b) {
|
||||
ExplorerButton.paste => _clip.isNotEmpty,
|
||||
|
|
@ -2835,6 +2843,14 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
final edge = bar != null && !widget.rsync && s.explorerToolbar == 'edge';
|
||||
// 180: 창마다 같은 열쇠 - 가로 ↔ 세로 배치가 바뀌어도 (창 크기 · 작업 현황이 열림) 창을 새로 만들지 않는다 (스크롤이 맨 위로 가지 않게)
|
||||
Widget keep(int k, Widget w) => KeyedSubtree(key: _viewKeys[k], child: w);
|
||||
if (rsyncOne) {
|
||||
final i = _active.clamp(0, 1);
|
||||
return Column(children: [
|
||||
_rsyncPaneSwitch(),
|
||||
?bar,
|
||||
Expanded(child: keep(i, _paneView(i, foldersOnly: true))),
|
||||
]);
|
||||
}
|
||||
final children = <Widget>[
|
||||
if (dual) ...[
|
||||
Expanded(child: keep(0, _paneView(0, foldersOnly: widget.rsync))),
|
||||
|
|
@ -2857,6 +2873,32 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
});
|
||||
}
|
||||
|
||||
/// 191: 좁은 Rsync 화면의 [왼쪽 | 오른쪽] - 그 창에서 고른 폴더 (없으면 지금 폴더) 이름과 함께
|
||||
Widget _rsyncPaneSwitch() => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 4, 4, 0),
|
||||
child: Row(children: [
|
||||
for (final (k, side) in [(0, tr('왼쪽')), (1, tr('오른쪽'))])
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: ChoiceChip(
|
||||
showCheckmark: false,
|
||||
selected: _active == k,
|
||||
onSelected: (_) => setState(() => _active = k),
|
||||
label: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
'$side · ${vBasename(_panes[k].marked.isNotEmpty ? _panes[k].marked.first : _panes[k].current)}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
/// 180: 왼쪽 창 · 오른쪽 창 · 파일 목록 (배치가 바뀌어도 같은 위젯 - [_body])
|
||||
final _viewKeys = [GlobalKey(), GlobalKey(), GlobalKey()];
|
||||
|
||||
|
|
@ -3485,20 +3527,26 @@ class _Toolbar extends StatelessWidget {
|
|||
required this.enabled,
|
||||
required this.selected,
|
||||
this.twoPanes = true,
|
||||
this.pinned = const {ExplorerButton.orient}});
|
||||
this.pinned = const {ExplorerButton.orient},
|
||||
bool? panesSide})
|
||||
: panesSide = panesSide ?? vertical;
|
||||
|
||||
/// 두 창이 좌우로 놓였는지 (버튼 이름 "좌 → 우" / "위 → 아래"). 보통 [vertical] 과 같고,
|
||||
/// 191 좁은 Rsync 화면 (창 하나씩, 버튼 줄은 가로) 은 좌우
|
||||
final bool panesSide;
|
||||
|
||||
/// 118: 지금 배치를 보여 준다 (누르면 반대로)
|
||||
String _orientLabel() => !twoPanes ? '두 창으로' : vertical ? '좌우 ⇆' : '위아래 ⇅';
|
||||
String _orientLabel() => !twoPanes ? '두 창으로' : panesSide ? '좌우 ⇆' : '위아래 ⇅';
|
||||
String _orientTip() => !twoPanes
|
||||
? '두 창으로 보기 (좌우)'
|
||||
: vertical
|
||||
: panesSide
|
||||
? '창 배치: 좌우 (누르면 위아래)'
|
||||
: '창 배치: 위아래 (누르면 좌우)';
|
||||
|
||||
/// 창이 위아래로 놓이면 (버튼 줄이 가로) "좌 → 우" 대신 "위 → 아래"
|
||||
String _label(ExplorerButton b) => b == ExplorerButton.orient
|
||||
? _orientLabel()
|
||||
: vertical
|
||||
: panesSide
|
||||
? b.label
|
||||
: switch (b) {
|
||||
ExplorerButton.toRight => '위 → 아래',
|
||||
|
|
@ -3508,8 +3556,8 @@ class _Toolbar extends StatelessWidget {
|
|||
};
|
||||
|
||||
IconData _icon(ExplorerButton b) => b == ExplorerButton.orient
|
||||
? (!twoPanes ? Icons.vertical_split_outlined : vertical ? Icons.swap_horiz : Icons.swap_vert)
|
||||
: vertical
|
||||
? (!twoPanes ? Icons.vertical_split_outlined : panesSide ? Icons.swap_horiz : Icons.swap_vert)
|
||||
: panesSide
|
||||
? b.icon
|
||||
: switch (b) {
|
||||
ExplorerButton.toRight => Icons.arrow_downward,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,66 @@ void main() {
|
|||
expect(File(p.join(right, 'doc.txt')).readAsStringSync(), 'hello');
|
||||
});
|
||||
|
||||
testWidgets('191: 좁은 Rsync 화면 (폴드 접은 화면) 은 창 하나씩 - [왼쪽 | 오른쪽] 으로 바꿔 보기, 넓으면 두 창', (tester) async {
|
||||
final tmp = Directory.systemTemp.createTempSync('jj_rsync_narrow_');
|
||||
addTearDown(() => tmp.deleteSync(recursive: true));
|
||||
final l = p.join(tmp.path, 'L'), r = p.join(tmp.path, 'R');
|
||||
Directory(p.join(l, 'sub')).createSync(recursive: true);
|
||||
Directory(p.join(r, 'rsub')).createSync(recursive: true);
|
||||
ExplorerPage.debugVolumes = () => [(tmp.path, 'T:')];
|
||||
addTearDown(() => ExplorerPage.debugVolumes = null);
|
||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||
c.settings
|
||||
..explorerOrientation = 'side'
|
||||
..rsyncPaths = [l, r];
|
||||
tester.view.physicalSize = const Size(400, 860);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true))));
|
||||
await settle(tester, () => find.text('sub').evaluate().isNotEmpty);
|
||||
// 왼쪽 창만 (오른쪽 폴더는 안 보임) · 방향 버튼은 보임
|
||||
expect(find.text('왼쪽 · L'), findsOneWidget);
|
||||
expect(find.text('오른쪽 · R'), findsOneWidget);
|
||||
expect(find.text('rsub'), findsNothing);
|
||||
expect(find.text('좌 → 우'), findsOneWidget);
|
||||
// 창 하나가 화면 폭을 다 쓴다 (예전에는 약 140px)
|
||||
expect(tester.getSize(find.ancestor(of: find.text('sub'), matching: find.byType(ListView)).first).width, greaterThan(300));
|
||||
await tester.tap(find.text('오른쪽 · R'));
|
||||
await settle(tester, () => find.text('rsub').evaluate().isNotEmpty);
|
||||
expect(find.text('rsub'), findsOneWidget);
|
||||
expect(find.text('sub'), findsNothing);
|
||||
// 넓으면 두 창 그대로
|
||||
tester.view.physicalSize = const Size(1280, 800);
|
||||
await settle(tester, () => find.text('sub').evaluate().isNotEmpty);
|
||||
expect(find.text('sub'), findsOneWidget);
|
||||
expect(find.text('rsub'), findsOneWidget);
|
||||
expect(find.text('왼쪽 · L'), findsNothing);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('208: 폰 가로 (낮은 화면) Rsync 화면에서도 방향 실행 버튼 · 모니터링은 늘 보인다 ([더 보기] 로 가는 것은 뒤쪽)', (tester) async {
|
||||
final tmp = Directory.systemTemp.createTempSync('jj_rsync_low_');
|
||||
addTearDown(() => tmp.deleteSync(recursive: true));
|
||||
final l = p.join(tmp.path, 'L'), r = p.join(tmp.path, 'R');
|
||||
Directory(l).createSync(recursive: true);
|
||||
Directory(r).createSync(recursive: true);
|
||||
ExplorerPage.debugVolumes = () => [(tmp.path, 'T:')];
|
||||
addTearDown(() => ExplorerPage.debugVolumes = null);
|
||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||
c.settings
|
||||
..explorerOrientation = 'side'
|
||||
..rsyncPaths = [l, r];
|
||||
tester.view.physicalSize = const Size(860, 400);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true))));
|
||||
await settle(tester, () => find.text('좌 → 우').evaluate().isNotEmpty);
|
||||
for (final t in ['좌 → 우', '좌 ← 우', '좌 ⇄ 우', '모니터링']) {
|
||||
expect(find.text(t), findsOneWidget, reason: t);
|
||||
}
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('25: Rsync 화면 폴더도 길게 누르면 메뉴 (고르기 · 열기 · 이름 변경 · 삭제 · 정보)', (tester) async {
|
||||
final tmp = Directory.systemTemp.createTempSync('jj_rsync_menu_');
|
||||
addTearDown(() => tmp.deleteSync(recursive: true));
|
||||
|
|
|
|||
Loading…
Reference in New Issue