WIP 좁은 화면 "폴더 + 파일 목록" 트리 창 (관리자 지시 10/20): 폭 < 600 이면 트리에 최소 폭 (130px 또는 버튼 줄을 뺀 폭의 42%) · 트리에는 ✔ 칸 · 크기 줄 없이 이름만 (선택은 오른쪽 목록), 줄에 들어가면 이름을 두 줄까지 · 작은 아이콘 칸 (28) · 화살표 36 · 들여쓰기 반 (폭의 1/10 까지) - 412 폭에서 트리 77px 로 이름이 한 글자도 안 보이던 것 · 넓은 화면은 그대로 (2 : 3, ✔) · 끌어서 폭 바꾸기는 지금 없음 · 시험 (412 · 360 × 3 스타일, Android 화살표 폭) · 캡처는 Android 플랫폼으로 (Windows 로 그리면 화살표가 20 이라 폰보다 넉넉해 보였다) (검토 대기)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>xplore_view
parent
ba968cbed9
commit
9ff8343835
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
@ -28,7 +29,9 @@ void main() {
|
|||
final c = AppController(PlatformServices.create());
|
||||
c.settings.explorerPaths = [tmp.path, tmp.path];
|
||||
final key = GlobalKey();
|
||||
for (final layout in ['single', 'split']) {
|
||||
// 폰처럼 (Android 는 트리 펼치기 화살표가 44 로 넓다 - Windows 로 그리면 폰보다 넉넉해 보인다)
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.android;
|
||||
for (final (layout, width) in [('single', 412.0), ('split', 412.0), ('split', 360.0)]) {
|
||||
for (final style in ['xplore', 'windows', 'totalcmd']) {
|
||||
for (final (scale, spacing) in [(100, 'normal'), (150, 'wide'), (80, 'compact')]) {
|
||||
c.settings
|
||||
|
|
@ -36,7 +39,7 @@ void main() {
|
|||
..explorerStyle = style
|
||||
..explorerTextScale = scale
|
||||
..explorerRowSpacing = spacing;
|
||||
tester.view.physicalSize = const Size(412, 900);
|
||||
tester.view.physicalSize = Size(width, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
await tester.runAsync(() => tester.pumpWidget(MaterialApp(
|
||||
theme: buildTheme(),
|
||||
|
|
@ -46,16 +49,17 @@ void main() {
|
|||
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 250)));
|
||||
await tester.pump();
|
||||
}
|
||||
expect(tester.takeException(), isNull, reason: '$layout $style $scale% $spacing 넘치지 않음');
|
||||
expect(tester.takeException(), isNull, reason: '$layout $width $style $scale% $spacing 넘치지 않음');
|
||||
final boundary = key.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||
final image = await tester.runAsync(() => boundary.toImage());
|
||||
final bytes = await tester.runAsync(() => image!.toByteData(format: ui.ImageByteFormat.png));
|
||||
File('$out/explorer_text_${layout}_${style}_${scale}_$spacing.png').writeAsBytesSync(bytes!.buffer.asUint8List());
|
||||
File('$out/explorer_text_$layout${width.round()}_${style}_${scale}_$spacing.png').writeAsBytesSync(bytes!.buffer.asUint8List());
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 300)));
|
||||
}
|
||||
}
|
||||
}
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
tester.view.reset();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1153,6 +1153,11 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
/// 왼쪽 폴더 트리 + 오른쪽 파일 목록 배치
|
||||
bool get _split => !widget.rsync && c.settings.explorerLayout == 'split';
|
||||
|
||||
/// 관리자 지시 10/20: 좁은 화면 (폭 < [narrowSplitWidth]) 의 "폴더 + 파일 목록" 좌우 배치 - 트리 창에 최소 폭을 두고
|
||||
/// ✔ 칸 · 크기 줄을 숨긴다 (412 폭에서 트리가 77px 이라 폴더 이름이 한 글자도 안 보였다). [_body] 가 정한다
|
||||
bool _narrowSplit = false;
|
||||
static const narrowSplitWidth = 600.0;
|
||||
|
||||
/// 좌우 두 창 (Rsync 화면은 늘)
|
||||
bool get _dual =>
|
||||
widget.rsync ||
|
||||
|
|
@ -4589,6 +4594,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
// 191: Rsync 화면이 좁으면 (폴드 접은 화면 · 폰 세로의 좌우 두 창) 창 하나씩 - [왼쪽 | 오른쪽] 으로 바꿔 본다
|
||||
// (두 창 + 가운데 버튼 줄이면 창이 140px 남짓이라 폴더 이름이 잘렸다)
|
||||
final rsyncOne = widget.rsync && side && box.maxWidth < rsyncTwoPaneMinWidth;
|
||||
_narrowSplit = split && side && box.maxWidth < narrowSplitWidth;
|
||||
final bar = s.explorerToolbar == 'hidden' && !widget.rsync
|
||||
? null
|
||||
: _Toolbar(
|
||||
|
|
@ -4627,8 +4633,14 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
Expanded(child: keep(1, _paneView(1, foldersOnly: widget.rsync))),
|
||||
if (edge) bar,
|
||||
] else if (split) ...[
|
||||
// 왼쪽 폴더 트리 (좁게) · 오른쪽 그 폴더의 파일 목록
|
||||
Expanded(flex: 2, child: keep(0, _paneView(0, foldersOnly: true))),
|
||||
// 왼쪽 폴더 트리 (좁게) · 오른쪽 그 폴더의 파일 목록. 좁은 화면은 트리에 최소 폭 (130px 또는 버튼 줄 (72) 을 뺀 폭의 42%) -
|
||||
// 버튼 줄까지 넣어 38% 로 하면 360 폭에서 오른쪽 목록의 파일 이름이 두세 글자만 남았다
|
||||
if (_narrowSplit)
|
||||
SizedBox(
|
||||
width: math.max(130.0, (box.maxWidth - (middle || edge ? 72 : 0)) * 0.42),
|
||||
child: keep(0, _paneView(0, foldersOnly: true)))
|
||||
else
|
||||
Expanded(flex: 2, child: keep(0, _paneView(0, foldersOnly: true))),
|
||||
if (middle) bar,
|
||||
Expanded(flex: 3, child: keep(2, _listView(_panes[0]))),
|
||||
if (edge) bar,
|
||||
|
|
@ -5476,6 +5488,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
Widget _rowView(_Pane pane, _Row row, {bool list = false, bool compact = false}) {
|
||||
final e = row.entry;
|
||||
final look = _look;
|
||||
// 좁은 "폴더 + 파일 목록" 의 트리 (관리자 지시 10/20): ✔ 칸 · 크기 줄 없이 이름만, 줄에 두 줄이 들어가면 이름을 두 줄까지
|
||||
final slim = compact && _narrowSplit && !list;
|
||||
final nameLines = slim && _rowH() >= MediaQuery.textScalerOf(context).scale(look.fontSize) * _listTextScale * 2.5 + 6 ? 2 : 1;
|
||||
final isCurrent = e.isDir && !list && !row.isUp && samePath(pane.current, e.path);
|
||||
final isFocused = !row.isUp && pane.focused != null && samePath(pane.focused!, e.path);
|
||||
final marked = _isMarked(pane, e.path);
|
||||
|
|
@ -5495,7 +5510,8 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
color: isCurrent ? JjColors.accent : null,
|
||||
);
|
||||
|
||||
final chevronWidth = treeChevronWidth(defaultTargetPlatform);
|
||||
// 좁은 트리 (slim) 는 조금 좁게 (36 - 줄 높이 전체를 누르므로 손가락으로도 눌림)
|
||||
final chevronWidth = slim ? math.min(36.0, treeChevronWidth(defaultTargetPlatform)) : treeChevronWidth(defaultTargetPlatform);
|
||||
// 트리의 펼치기 화살표 (따로 누름). 189: 접기 · 펼치기는 이것으로만 하므로 줄 높이 전체를 누를 수 있게,
|
||||
// 손가락으로 누르는 기기 (Android) 는 폭도 넓게
|
||||
Widget chevron() => SizedBox(
|
||||
|
|
@ -5525,11 +5541,17 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
// 좁은 창 (폰 세로의 좌우 두 창) 에서 깊은 폴더: 들여쓰기는 이름 자리를 남기는 만큼만 (넘치지 않게)
|
||||
Widget nameCell() => LayoutBuilder(builder: (context, box) {
|
||||
final fixed = (list ? 0 : chevronWidth) + (look.rich ? 64 : look.iconSize + 12) + 48.0;
|
||||
final indent = list ? 0.0 : (row.depth * look.indent).clamp(0.0, (box.maxWidth - fixed).clamp(0.0, double.infinity));
|
||||
// 좁은 트리 (slim): 들여쓰기는 반만 (폭의 1/10 까지) - 깊은 폴더에서 이름이 한 글자도 안 보이지 않게
|
||||
final indent = list
|
||||
? 0.0
|
||||
: slim
|
||||
? (row.depth * look.indent / 2).clamp(0.0, box.maxWidth * 0.1).toDouble()
|
||||
: (row.depth * look.indent).clamp(0.0, (box.maxWidth - fixed).clamp(0.0, double.infinity)).toDouble();
|
||||
// 아주 좁은 창 (412 폭 "폴더 + 파일 목록" 의 트리, 77px 쯤): 아이콘 칸 · 틈을 남은 폭만큼만 (아이콘은 줄여 그림) - 넘치지 않게
|
||||
final room = (box.maxWidth - indent - (list ? 0 : chevronWidth)).clamp(0.0, double.infinity);
|
||||
final iconW = math.min(look.rich ? 56.0 : look.iconSize + 6, room);
|
||||
final gapW = math.min(look.rich ? 8.0 : 6.0, room - iconW);
|
||||
// 좁은 트리 (slim) 는 큰 썸네일 칸 (56) 대신 작은 아이콘 칸 (28 까지, 아이콘은 줄여 그림)
|
||||
final iconW = math.min(slim ? math.min(look.iconSize, 24.0) + 4 : look.rich ? 56.0 : look.iconSize + 6, room);
|
||||
final gapW = math.min(slim ? 4.0 : look.rich ? 8.0 : 6.0, room - iconW);
|
||||
return Row(children: [
|
||||
SizedBox(width: indent),
|
||||
if (!list) chevron(),
|
||||
|
|
@ -5557,8 +5579,10 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(name, maxLines: 1, overflow: TextOverflow.ellipsis, style: nameStyle),
|
||||
if (look.rich && e.isDir && pane.errors[e.path] != null)
|
||||
Text(name, maxLines: nameLines, overflow: TextOverflow.ellipsis, style: nameStyle),
|
||||
if (slim)
|
||||
const SizedBox.shrink()
|
||||
else if (look.rich && e.isDir && pane.errors[e.path] != null)
|
||||
Text(tr('읽을 수 없음'), style: dim.copyWith(color: Colors.redAccent))
|
||||
// 80: 열 없는 모양 (X-plore · 좁은 화면) 은 폴더 이름 아래에 크기
|
||||
else if (e.isDir && !row.isRoot && !row.isUp && (!look.columns || compact) && _dirSize(pane, e) != null)
|
||||
|
|
@ -5630,7 +5654,9 @@ class _ExplorerPageState extends State<ExplorerPage> {
|
|||
),
|
||||
),
|
||||
// 표시 동그라미는 두 번 누르기 영역 밖 (기다리지 않고 바로)
|
||||
if ((real || (widget.rsync && row.isRoot)) && (_selecting || widget.rsync || _alwaysMarks))
|
||||
if (slim)
|
||||
const SizedBox.shrink()
|
||||
else if ((real || (widget.rsync && row.isRoot)) && (_selecting || widget.rsync || _alwaysMarks))
|
||||
SizedBox(
|
||||
width: 36,
|
||||
child: IconButton(
|
||||
|
|
|
|||
|
|
@ -75,6 +75,46 @@ void main() {
|
|||
await tester.pump(const Duration(seconds: 1));
|
||||
}
|
||||
|
||||
// 관리자 지시 10/20: 좁은 화면의 "폴더 + 파일 목록" - 트리 창 최소 폭 (120px 또는 38%), 트리에는 ✔ 칸 · 크기 줄 없이 이름이 보임
|
||||
for (final width in [412.0, 360.0]) {
|
||||
for (final style in ['xplore', 'windows', 'totalcmd']) {
|
||||
testWidgets('좁은 폴더 + 파일 목록 ($width 폭, $style): 트리 창이 넓어 폴더 이름이 보이고 ✔ 칸은 오른쪽 목록에만', (tester) async {
|
||||
tester.view.physicalSize = Size(width, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await open(tester, style, 'sub');
|
||||
expect(tester.takeException(), isNull);
|
||||
// 트리 (먼저 그려짐) 의 'sub' 이름이 다 보인다
|
||||
final tree = find.text(style == 'totalcmd' ? '[sub]' : 'sub').first;
|
||||
final para = tester.renderObject<RenderParagraph>(tree);
|
||||
// 시험 글꼴 (Ahem) 은 글자마다 글자 크기만큼 넓어 실제보다 훨씬 길다 - 이름 칸 폭으로 본다 (실제 글꼴은 캡처 시험)
|
||||
expect(para.constraints.maxWidth, greaterThanOrEqualTo(20), reason: '트리의 폴더 이름 칸이 남는다 (예전 412 폭은 0, 실제 글꼴로 360 폭에서 한 줄 5글자쯤)');
|
||||
final treeRight = tester.getTopRight(tree).dx;
|
||||
final listLeft = tester.getTopLeft(find.text(style == 'totalcmd' ? 'a' : 'a.txt')).dx;
|
||||
expect(listLeft, greaterThan(treeRight), reason: '트리는 왼쪽, 목록은 오른쪽');
|
||||
// 트리 줄에는 ✔ 단추가 없다 (오른쪽 목록 줄에만) - X-plore 는 늘 ✔
|
||||
if (style == 'xplore') {
|
||||
final marks = find.byTooltip('표시 (여러 개 고르기)');
|
||||
expect(marks, findsWidgets);
|
||||
for (final m in marks.evaluate()) {
|
||||
expect(tester.getTopLeft(find.byWidget(m.widget)).dx, greaterThan(treeRight), reason: '✔ 는 오른쪽 목록에만');
|
||||
}
|
||||
}
|
||||
await close(tester);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
testWidgets('넓은 화면 (1280) 의 폴더 + 파일 목록은 그대로 (2 : 3, 트리에도 ✔)', (tester) async {
|
||||
tester.view.physicalSize = const Size(1280, 900);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await open(tester, 'xplore', 'sub');
|
||||
final treeMarks = find.byTooltip('표시 (여러 개 고르기)').evaluate().where((m) => tester.getTopLeft(find.byWidget(m.widget)).dx < 500);
|
||||
expect(treeMarks, isNotEmpty, reason: '넓은 화면은 지금 그대로');
|
||||
await close(tester);
|
||||
});
|
||||
|
||||
for (final style in ['xplore', 'windows', 'totalcmd']) {
|
||||
final a = style == 'totalcmd' ? 'a' : 'a.txt';
|
||||
testWidgets('40: $style - 처음 값 (100% · 보통) 은 지금 그대로 · 150% · 넓게는 글자 1.5배 · 줄 높이 늘어남 · 412 폭에서 넘치지 않음', (tester) async {
|
||||
|
|
|
|||
Loading…
Reference in New Issue