diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index 9584726..c49adfd 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -264,12 +264,16 @@ class _SettingsPageState extends State { AppActions(c: c, onSettingsPage: true), ]), ), + // 묶음 바로가기 (누르면 그 묶음으로). 스크롤해도 위에 그대로 + Padding( + padding: const EdgeInsets.fromLTRB(24, 12, 24, 0), + child: _groupBar(desk), + ), Expanded( - child: ListView( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), - children: [ - // 묶음 바로가기 (누르면 그 묶음으로) - _groupBar(desk), + // 모든 묶음을 한 번에 그린다 (ListView 는 화면 밖 묶음을 그리지 않아 바로가기로 갈 수 없었음) + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 4, 24, 16), + child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _group('general', Icons.tune, tr('일반'), [ // 화면 언어 (읽을 수 없는 언어를 골라도 바로 찾아 되돌릴 수 있게 맨 위) const LanguageSettings(), @@ -696,7 +700,7 @@ class _SettingsPageState extends State { ]), ), ]), - ], + ]), ), ), ]), @@ -736,7 +740,9 @@ class _SettingsPageState extends State { onPressed: () { final ctx = _groupKeys[id]?.currentContext; if (ctx != null) { - Scrollable.ensureVisible(ctx, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); + // 그 묶음의 제목이 맨 위에 오게 + Scrollable.ensureVisible(ctx, + alignment: 0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } }, ), diff --git a/test/queue_test.dart b/test/queue_test.dart index 69c3414..69c1d33 100644 --- a/test/queue_test.dart +++ b/test/queue_test.dart @@ -204,6 +204,29 @@ void main() { expect(find.byTooltip('작업 기록 숨기기'), findsOneWidget); }); + testWidgets('환경 설정: 위쪽 묶음 버튼을 누르면 (화면 밖 묶음도) 그 묶음으로 간다', (tester) async { + tester.view.physicalSize = const Size(1400, 800); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + final c = AppController(PlatformServices(mediaTool: _Tool(), storage: DesktopStorageService())); + await tester.pumpWidget(MaterialApp(home: SettingsPage(c: c))); + await tester.pumpAndSettle(); + final groups = ['다운로드', '웹 브라우저', '자막 (AI · 인터넷)', 'MKV 만들기', '화면', '실행 · 종료', '재생', '일반']; + for (final g in groups) { + await tester.tap(find.widgetWithText(ActionChip, g)); + await tester.pumpAndSettle(); + // 묶음 제목 (버튼이 아닌 카드 안의 글) 이 화면 위쪽에 보인다 + final title = find.descendant(of: find.byType(Card), matching: find.text(g)).first; + final y = tester.getTopLeft(title).dy; + expect(y, inInclusiveRange(0, 400), reason: '$g 묶음 제목 위치 $y'); + } + // 끝의 묶음 (프로그램 정보) 도 화면에 보인다 + await tester.tap(find.widgetWithText(ActionChip, '프로그램 정보')); + await tester.pumpAndSettle(); + final about = find.descendant(of: find.byType(Card), matching: find.text('프로그램 정보')).first; + expect(tester.getTopLeft(about).dy, inInclusiveRange(0, 800)); + }); + testWidgets('환경 설정: 새 항목이 깨지지 않고 보임', (tester) async { // 스크롤 없이 모두 보이도록 세로로 긴 화면 tester.view.physicalSize = const Size(1400, 3200);