JJ_MKVMaker/lib/ui/explorer/explorer_layout.dart

109 lines
6.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/material.dart';
import '../../core/file_ops.dart' show samePath;
import '../../l10n/tr.dart';
import '../explorer_look.dart';
import '../app_actions.dart' show isCompact;
import 'explorer_controller.dart';
import 'explorer_pane.dart';
/// 191: Rsync 화면이 좌우 두 창 + 가운데 버튼 줄로 보이는 가장 작은 폭 (이보다 좁으면 창 하나씩)
const double rsyncTwoPaneMinWidth = 560;
/// 약속 (E5d-6): 창 배치 규칙 - 두 창 · 폴더 + 파일 목록 · 한 창 (설정, "자동" 은 화면 크기 따라), 좁아서 잠깐 한 창 ([splitTight]) ·
/// 좁은 "폴더 + 파일 목록" ([narrowSplit]) 은 [ExplorerPanesBody] 가 폭을 보고 정한다. 줄 높이 (목록 글자 크기 · 줄 간격),
/// 복사 · 이동 갈 곳 창 ([other]), 밀어서 창 바꾸기, 항목이 보이게 스크롤 ([reveal]). [context]: 화면 (글자 크기 · 폭)
class ExplorerLayout {
ExplorerLayout(this.ctl, {required this.context});
final ExplorerController ctl;
final BuildContext Function() context;
/// 180: 왼쪽 창 · 오른쪽 창 · 파일 목록 (배치가 바뀌어도 같은 위젯)
final viewKeys = [GlobalKey(), GlobalKey(), GlobalKey()];
/// 모양 · 배치가 바뀌면 (줄 높이 · 창 너비가 달라져) 보던 폴더가 밀려나므로 다시 그 폴더로
String shape = '';
/// 화면에 보일 경로 (5 · 14): Android 는 "SD 카드 › JJ_sdtest › jj_mkv", WebDAV 는 "☁ 서버 › 폴더", Windows 는 그대로
/// 그 항목이 목록 위쪽 1/4 쯤에 보이게 스크롤 (깊은 폴더로 가면 위쪽 폴더들의 다른 항목에 밀려 안 보이므로)
void reveal(ExplorerPane pane, String path) => WidgetsBinding.instance.addPostFrameCallback((_) => _revealNow(pane, path));
void _revealNow(ExplorerPane pane, String path) {
if (!ctl.host.alive || !pane.scroll.hasClients) return;
// 화면의 트리와 같은 줄로 센다 (폴더 + 파일 목록 배치 · Rsync 화면은 폴더만)
final i = ctl.rows(pane, foldersOnly: split || ctl.rsync).indexWhere((r) => r.kind == 0 && samePath(r.entry.path, path));
if (i < 0) return;
final pos = pane.scroll.position;
// 180: 끝 (maxScrollExtent) 으로 자르지 않는다 - 줄이 막 늘어난 때는 끝이 아직 앞의 것이라 보던 폴더로 못 간다.
// 넘치면 다음 배치 때 실제 끝으로 맞춰진다
pane.scroll.jumpTo((i * rowHeight() - pos.viewportDimension / 4).clamp(0.0, double.infinity));
}
/// 28 확인 (10/20): 창의 목록이 화면에 새로 붙으면 (좁은 Rsync 화면의 [오른쪽] 탭을 처음 볼 때 · 폭이 바뀌어 두 창이 될 때)
/// 그 창의 지금 폴더를 보이게 한다. 켤 때 그려지지 않은 창은 [ctl.goTo] 의 스크롤을 건너뛰어, 나중에 보면 맨 위 (저장 장치) 에 있었다
void revealNewViews() => WidgetsBinding.instance.addPostFrameCallback((_) {
if (!ctl.host.alive) return;
for (final pane in ctl.panes) {
final on = pane.scroll.hasClients;
if (on && !pane.onScreen && pane.current.isNotEmpty) _revealNow(pane, pane.current);
pane.onScreen = on;
}
});
/// X-plore 비교 40: 목록 글자 크기 (설정 %) · 줄 간격을 넣은 줄 높이
double get listTextScale => ctl.c.settings.explorer.textScale / 100;
double rowHeight() => ctl.look.rowHeightOf(context(), textScale: listTextScale, spacing: rowSpacingFactor(ctl.c.settings.explorer.rowSpacing));
/// 왼쪽 폴더 트리 + 오른쪽 파일 목록 배치
bool get split => !ctl.rsync && ctl.c.settings.explorer.layout == 'split' && !splitTight;
/// 관리자 지시 10/20 (28 검토): 글자가 커서 (기기 글자 × 목록 글자 크기) 좁은 화면에 트리 · 목록 두 창이 다 들어가지 않으면
/// 그동안만 한 창 (트리 + 위 경로 막대) 으로 보인다 - 고른 배치 (설정) 는 그대로, 화면이 넓어지거나 글자가 작아지면 다시 두 창. [ExplorerPanesBody] 가 정한다
bool splitTight = false;
/// 관리자 지시 10/20: 좁은 화면 (폭 < [narrowSplitWidth]) 의 "폴더 + 파일 목록" 좌우 배치 - 트리 창에 최소 폭을 두고
/// ✔ 칸 · 크기 줄을 숨긴다 (412 폭에서 트리가 77px 이라 폴더 이름이 한 글자도 안 보였다). [ExplorerPanesBody] 가 정한다
bool narrowSplit = false;
static const narrowSplitWidth = 600.0;
/// 좌우 두 창 (Rsync 화면은 늘)
bool get dual =>
ctl.rsync ||
ctl.c.settings.explorer.layout == 'dual' ||
// 자동: 넓으면 두 창, 좁은 화면 (폰 세로) 은 한 창 - 같은 목록이 반씩 잘려 두 번 보이지 않게
(ctl.c.settings.explorer.layout == 'auto' && !isCompact(context()));
/// 트리 줄. [foldersOnly]: 폴더만 ("폴더 + 파일 목록" 배치의 왼쪽)
/// X-plore 비교 c: 트리 맨 위에 저장 장치 · 서버 · 즐겨찾기 (X-plore 스타일 · 설정)
/// X-plore 비교 ⚖: X-plore 스타일에서 표시 동그라미 늘 보이기 (설정)
bool get alwaysMarks => !ctl.rsync && ctl.look == ExplorerStyle.xplore && ctl.c.settings.explorer.alwaysMarks;
/// X-plore 비교 ⚖: X-plore 스타일 탐색기 안에서 밀기 = 창 바꾸기 (설정)
bool get swipePanes => !ctl.rsync && ctl.look == ExplorerStyle.xplore && ctl.c.settings.explorer.swipePanes;
/// 밀어서 창 바꾸기: 두 창이면 다른 창을 활성으로, 한 창이면 숨은 창과 바꿔 보인다
bool switchPane(int dir) {
if (!swipePanes) return false;
if (dual || split) {
ctl.active = 1 - ctl.active;
} else {
final t = ctl.panes[0];
ctl.panes[0] = ctl.panes[1];
ctl.panes[1] = t;
ctl.active = 0;
}
ctl.update();
ctl.remember(ctl.pane, ctl.pane.current);
ctl.host.snack(trf('창 바꿈: {0}', [ctl.displayPath(ctl.pane.current)]));
return true;
}
/// 복사 · 이동 갈 곳 창 (두 창 · 숨은 다른 창, 없으면 null - 담아 두기)
ExplorerPane? get other => dual || hiddenTarget ? ctl.panes[1 - ctl.active] : null;
/// X-plore 비교 a: 한 창인데 "숨은 다른 창으로" 복사 · 이동 (위에 갈 곳 줄)
bool get hiddenTarget => !ctl.rsync && !dual && !split && ctl.c.settings.explorer.onePaneCopy == 'other';
}