WIP X-plore 맞추기 (관리자 지시 10/20): 그림 보기 아래 갤러리 줄 - 썸네일 줄, 누르면 그 장으로, 지금 장은 테두리 · 장이 바뀌면 따라감 · 그림 원본 (폴더 · ZIP) 만 · 처음 끔 (위 막대 버튼 · 환경 설정 > 보기에서 켬) · 시험 (검토 대기)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>xplore_view
parent
dc77998e63
commit
99de4f4bc9
|
|
@ -800,11 +800,13 @@
|
||||||
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "Images · comic reading, PDF view · page editing, ZIP list · comic reading",
|
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "Images · comic reading, PDF view · page editing, ZIP list · comic reading",
|
||||||
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "Images and prompts leave this device ({0})",
|
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "Images and prompts leave this device ({0})",
|
||||||
"그림 보기 대상": "Image viewer file types",
|
"그림 보기 대상": "Image viewer file types",
|
||||||
|
"그림 보기 아래 갤러리 줄": "Gallery strip in the image viewer",
|
||||||
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "Can't read the image file - damaged or unsupported format",
|
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "Can't read the image file - damaged or unsupported format",
|
||||||
"그림으로 바꾸는 중": "Turning into an image",
|
"그림으로 바꾸는 중": "Turning into an image",
|
||||||
"그림을 만들지 못했습니다: {0}": "Couldn't make the image: {0}",
|
"그림을 만들지 못했습니다: {0}": "Couldn't make the image: {0}",
|
||||||
"그림을 바꿔 만들기": "Make from this picture",
|
"그림을 바꿔 만들기": "Make from this picture",
|
||||||
"그림을 받을 수 없습니다 ({0})": "Couldn't get the image ({0})",
|
"그림을 받을 수 없습니다 ({0})": "Couldn't get the image ({0})",
|
||||||
|
"그림을 볼 때 아래에 작은 그림 줄을 보입니다 (누르면 그 장으로)": "Shows a row of thumbnails at the bottom while viewing images (tap one to jump to it)",
|
||||||
"그림을 열 수 없습니다": "Can't open the image",
|
"그림을 열 수 없습니다": "Can't open the image",
|
||||||
"그림을 올릴 수 없습니다 ({0})": "Couldn't upload the image ({0})",
|
"그림을 올릴 수 없습니다 ({0})": "Couldn't upload the image ({0})",
|
||||||
"그림을 읽을 수 없습니다": "Cannot read the image",
|
"그림을 읽을 수 없습니다": "Cannot read the image",
|
||||||
|
|
@ -1732,6 +1734,8 @@
|
||||||
"쓸 수 없는 이름": "Invalid name",
|
"쓸 수 없는 이름": "Invalid name",
|
||||||
"쓸 수 없는 이름입니다": "This name cannot be used",
|
"쓸 수 없는 이름입니다": "This name cannot be used",
|
||||||
"아랍어": "Arabic",
|
"아랍어": "Arabic",
|
||||||
|
"아래 갤러리 줄 보이기": "Show the gallery strip",
|
||||||
|
"아래 갤러리 줄 숨기기": "Hide the gallery strip",
|
||||||
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "Some features are unavailable because the programs below are missing. Download them from the official sources and install them in the program folder?",
|
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "Some features are unavailable because the programs below are missing. Download them from the official sources and install them in the program folder?",
|
||||||
"아래로": "Down",
|
"아래로": "Down",
|
||||||
"아래에 줄 추가": "Add line below",
|
"아래에 줄 추가": "Add line below",
|
||||||
|
|
|
||||||
|
|
@ -800,11 +800,13 @@
|
||||||
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "画像・マンガ表示、PDF 表示・ページ編集、ZIP 一覧・マンガ表示",
|
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "画像・マンガ表示、PDF 表示・ページ編集、ZIP 一覧・マンガ表示",
|
||||||
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "画像・プロンプトが端末の外に送られます ({0})",
|
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "画像・プロンプトが端末の外に送られます ({0})",
|
||||||
"그림 보기 대상": "画像表示の対象",
|
"그림 보기 대상": "画像表示の対象",
|
||||||
|
"그림 보기 아래 갤러리 줄": "画像ビューアー下のギャラリー列",
|
||||||
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "画像ファイルを読み込めません - 破損しているか未対応の形式",
|
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "画像ファイルを読み込めません - 破損しているか未対応の形式",
|
||||||
"그림으로 바꾸는 중": "画像に変換中",
|
"그림으로 바꾸는 중": "画像に変換中",
|
||||||
"그림을 만들지 못했습니다: {0}": "画像を作成できませんでした: {0}",
|
"그림을 만들지 못했습니다: {0}": "画像を作成できませんでした: {0}",
|
||||||
"그림을 바꿔 만들기": "画像を変えて作成",
|
"그림을 바꿔 만들기": "画像を変えて作成",
|
||||||
"그림을 받을 수 없습니다 ({0})": "画像を受け取れません ({0})",
|
"그림을 받을 수 없습니다 ({0})": "画像を受け取れません ({0})",
|
||||||
|
"그림을 볼 때 아래에 작은 그림 줄을 보입니다 (누르면 그 장으로)": "画像を見るとき下にサムネイルの列を表示します (タップでその画像へ)",
|
||||||
"그림을 열 수 없습니다": "画像を開けません",
|
"그림을 열 수 없습니다": "画像を開けません",
|
||||||
"그림을 올릴 수 없습니다 ({0})": "画像をアップロードできません ({0})",
|
"그림을 올릴 수 없습니다 ({0})": "画像をアップロードできません ({0})",
|
||||||
"그림을 읽을 수 없습니다": "画像を読み込めません",
|
"그림을 읽을 수 없습니다": "画像を読み込めません",
|
||||||
|
|
@ -1732,6 +1734,8 @@
|
||||||
"쓸 수 없는 이름": "使えない名前",
|
"쓸 수 없는 이름": "使えない名前",
|
||||||
"쓸 수 없는 이름입니다": "使用できない名前です",
|
"쓸 수 없는 이름입니다": "使用できない名前です",
|
||||||
"아랍어": "アラビア語",
|
"아랍어": "アラビア語",
|
||||||
|
"아래 갤러리 줄 보이기": "下のギャラリー列を表示",
|
||||||
|
"아래 갤러리 줄 숨기기": "下のギャラリー列を隠す",
|
||||||
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "以下のプログラムがないため一部の機能が使えません。公式配布元からダウンロードしてプログラムフォルダーにインストールしますか?",
|
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "以下のプログラムがないため一部の機能が使えません。公式配布元からダウンロードしてプログラムフォルダーにインストールしますか?",
|
||||||
"아래로": "下へ",
|
"아래로": "下へ",
|
||||||
"아래에 줄 추가": "下に行を追加",
|
"아래에 줄 추가": "下に行を追加",
|
||||||
|
|
|
||||||
|
|
@ -800,11 +800,13 @@
|
||||||
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "图片 · 漫画阅读,PDF 查看 · 页面编辑,ZIP 列表 · 漫画阅读",
|
"그림 · 만화 보기, PDF 보기 · 페이지 편집, ZIP 목록 · 만화 보기": "图片 · 漫画阅读,PDF 查看 · 页面编辑,ZIP 列表 · 漫画阅读",
|
||||||
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "图片和提示词将离开本设备 ({0})",
|
"그림 · 프롬프트가 기기 밖으로 나갑니다 ({0})": "图片和提示词将离开本设备 ({0})",
|
||||||
"그림 보기 대상": "图片查看的文件类型",
|
"그림 보기 대상": "图片查看的文件类型",
|
||||||
|
"그림 보기 아래 갤러리 줄": "图片查看器底部图库条",
|
||||||
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "无法读取图片文件 - 已损坏或格式不受支持",
|
"그림 파일을 읽을 수 없습니다 - 깨졌거나 지원하지 않는 형식": "无法读取图片文件 - 已损坏或格式不受支持",
|
||||||
"그림으로 바꾸는 중": "正在生成图像",
|
"그림으로 바꾸는 중": "正在生成图像",
|
||||||
"그림을 만들지 못했습니다: {0}": "无法生成图片: {0}",
|
"그림을 만들지 못했습니다: {0}": "无法生成图片: {0}",
|
||||||
"그림을 바꿔 만들기": "基于图片生成",
|
"그림을 바꿔 만들기": "基于图片生成",
|
||||||
"그림을 받을 수 없습니다 ({0})": "无法获取图片 ({0})",
|
"그림을 받을 수 없습니다 ({0})": "无法获取图片 ({0})",
|
||||||
|
"그림을 볼 때 아래에 작은 그림 줄을 보입니다 (누르면 그 장으로)": "查看图片时在底部显示缩略图条 (点按可跳到该图)",
|
||||||
"그림을 열 수 없습니다": "无法打开图片",
|
"그림을 열 수 없습니다": "无法打开图片",
|
||||||
"그림을 올릴 수 없습니다 ({0})": "无法上传图片 ({0})",
|
"그림을 올릴 수 없습니다 ({0})": "无法上传图片 ({0})",
|
||||||
"그림을 읽을 수 없습니다": "无法读取图片",
|
"그림을 읽을 수 없습니다": "无法读取图片",
|
||||||
|
|
@ -1732,6 +1734,8 @@
|
||||||
"쓸 수 없는 이름": "无效名称",
|
"쓸 수 없는 이름": "无效名称",
|
||||||
"쓸 수 없는 이름입니다": "不能使用此名称",
|
"쓸 수 없는 이름입니다": "不能使用此名称",
|
||||||
"아랍어": "阿拉伯语",
|
"아랍어": "阿拉伯语",
|
||||||
|
"아래 갤러리 줄 보이기": "显示底部图库条",
|
||||||
|
"아래 갤러리 줄 숨기기": "隐藏底部图库条",
|
||||||
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "由于缺少以下程序,部分功能无法使用。要从官方来源下载并安装到程序文件夹吗?",
|
"아래 프로그램이 없어 일부 기능을 쓸 수 없습니다. 공식 배포처에서 내려받아 프로그램 폴더에 설치할까요?": "由于缺少以下程序,部分功能无法使用。要从官方来源下载并安装到程序文件夹吗?",
|
||||||
"아래로": "向下",
|
"아래로": "向下",
|
||||||
"아래에 줄 추가": "在下方添加一行",
|
"아래에 줄 추가": "在下方添加一行",
|
||||||
|
|
|
||||||
|
|
@ -555,6 +555,9 @@ class AppSettings {
|
||||||
/// 117: 보기 화면 "계속 보기" 간격 (초, 1 ~ 60)
|
/// 117: 보기 화면 "계속 보기" 간격 (초, 1 ~ 60)
|
||||||
int readerAutoSeconds = 3;
|
int readerAutoSeconds = 3;
|
||||||
|
|
||||||
|
/// X-plore 맞추기 (관리자 지시 10/20): 그림 보기 화면 아래 갤러리 줄 (썸네일, 누르면 그 장으로). 처음 끔
|
||||||
|
bool readerGallery = false;
|
||||||
|
|
||||||
/// 119: 한 장에 두 쪽이 붙은 그림을 반씩 나눠 보기 - 'auto' 가로가 세로보다 긴 그림만 · 'on' 늘 · 'off' 안 함
|
/// 119: 한 장에 두 쪽이 붙은 그림을 반씩 나눠 보기 - 'auto' 가로가 세로보다 긴 그림만 · 'on' 늘 · 'off' 안 함
|
||||||
String readerSplit = 'auto';
|
String readerSplit = 'auto';
|
||||||
|
|
||||||
|
|
@ -867,6 +870,7 @@ class AppSettings {
|
||||||
'readerBrightness': readerBrightness,
|
'readerBrightness': readerBrightness,
|
||||||
'readerSystemBars': readerSystemBars,
|
'readerSystemBars': readerSystemBars,
|
||||||
'readerAutoSeconds': readerAutoSeconds,
|
'readerAutoSeconds': readerAutoSeconds,
|
||||||
|
'readerGallery': readerGallery,
|
||||||
'readerSplit': readerSplit,
|
'readerSplit': readerSplit,
|
||||||
'startMenuShortcut': startMenuShortcut,
|
'startMenuShortcut': startMenuShortcut,
|
||||||
'syncStopToast': syncStopToast,
|
'syncStopToast': syncStopToast,
|
||||||
|
|
@ -1122,6 +1126,7 @@ class AppSettings {
|
||||||
..readerBrightness = ((j['readerBrightness'] as num?)?.toDouble() ?? 0).clamp(-0.7, 0.7)
|
..readerBrightness = ((j['readerBrightness'] as num?)?.toDouble() ?? 0).clamp(-0.7, 0.7)
|
||||||
..readerSystemBars = j['readerSystemBars'] != false
|
..readerSystemBars = j['readerSystemBars'] != false
|
||||||
..readerAutoSeconds = ((j['readerAutoSeconds'] as num?)?.toInt() ?? 3).clamp(1, 60)
|
..readerAutoSeconds = ((j['readerAutoSeconds'] as num?)?.toInt() ?? 3).clamp(1, 60)
|
||||||
|
..readerGallery = j['readerGallery'] as bool? ?? false
|
||||||
..readerSplit = const ['auto', 'on', 'off'].contains(j['readerSplit']) ? j['readerSplit'] as String : 'auto'
|
..readerSplit = const ['auto', 'on', 'off'].contains(j['readerSplit']) ? j['readerSplit'] as String : 'auto'
|
||||||
..startMenuShortcut = j['startMenuShortcut'] != false
|
..startMenuShortcut = j['startMenuShortcut'] != false
|
||||||
..syncStopToast = j['syncStopToast'] != false
|
..syncStopToast = j['syncStopToast'] != false
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,14 @@ class ViewerSettings extends StatelessWidget {
|
||||||
onChanged: (v) => c.updateSettings((x) => x.readerSplit = v!),
|
onChanged: (v) => c.updateSettings((x) => x.readerSplit = v!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// X-plore 맞추기 (관리자 지시 10/20): 아래 갤러리 줄 (보기 화면 위 막대에서도 켜고 끔)
|
||||||
|
SwitchListTile(
|
||||||
|
secondary: const Icon(Icons.view_carousel_outlined),
|
||||||
|
value: s.readerGallery,
|
||||||
|
onChanged: (v) => c.updateSettings((x) => x.readerGallery = v),
|
||||||
|
title: Text(tr('그림 보기 아래 갤러리 줄')),
|
||||||
|
subtitle: Text(tr('그림을 볼 때 아래에 작은 그림 줄을 보입니다 (누르면 그 장으로)')),
|
||||||
|
),
|
||||||
// 117: 계속 보기 간격
|
// 117: 계속 보기 간격
|
||||||
SettingTile(
|
SettingTile(
|
||||||
leading: const Icon(Icons.slideshow_outlined),
|
leading: const Icon(Icons.slideshow_outlined),
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,12 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
/// PDF 는 페이지를 지우거나 넣으면 다시 그린다
|
/// PDF 는 페이지를 지우거나 넣으면 다시 그린다
|
||||||
int _version = 0;
|
int _version = 0;
|
||||||
|
|
||||||
|
/// X-plore 맞추기 (관리자 지시 10/20): 아래 갤러리 줄 - 그림 원본 (폴더 · ZIP) 에서만
|
||||||
|
bool get _pictures => src is ImageFilesSource || src is ZipImagesSource;
|
||||||
|
final _thumbs = <int, Future<ReaderImage>>{};
|
||||||
|
final _gallery = ScrollController();
|
||||||
|
static const _thumbExtent = 64.0;
|
||||||
|
|
||||||
/// [다시 시도] 를 누른 횟수 (장마다) - 그 장의 화면을 새로 만든다 (28 지적: 같은 키면 실패한 그림을 그대로 다시 썼다)
|
/// [다시 시도] 를 누른 횟수 (장마다) - 그 장의 화면을 새로 만든다 (28 지적: 같은 키면 실패한 그림을 그대로 다시 썼다)
|
||||||
final _retry = <int, int>{};
|
final _retry = <int, int>{};
|
||||||
|
|
||||||
|
|
@ -344,6 +350,10 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
for (final f in _thumbs.values) {
|
||||||
|
unawaited(f.then((x) => x.dispose(), onError: (_) {}));
|
||||||
|
}
|
||||||
|
_gallery.dispose();
|
||||||
_life.dispose();
|
_life.dispose();
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (_playing) unawaited(ReaderPage.keepScreenOn(false).catchError((_) {}));
|
if (_playing) unawaited(ReaderPage.keepScreenOn(false).catchError((_) {}));
|
||||||
|
|
@ -653,6 +663,10 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
one(_Act(const Icon(Icons.delete_outline), tr('이 페이지 지우기'), _deletePage)),
|
one(_Act(const Icon(Icons.delete_outline), tr('이 페이지 지우기'), _deletePage)),
|
||||||
one(_Act(const Icon(Icons.add_photo_alternate_outlined), tr('그림을 페이지로 넣기 (이 페이지 뒤)'), _insertImages)),
|
one(_Act(const Icon(Icons.add_photo_alternate_outlined), tr('그림을 페이지로 넣기 (이 페이지 뒤)'), _insertImages)),
|
||||||
],
|
],
|
||||||
|
// X-plore 맞추기 (관리자 지시 10/20): 아래 갤러리 줄 켜고 끄기 (그림 원본만)
|
||||||
|
if (_pictures)
|
||||||
|
one(_Act(const Icon(Icons.view_carousel_outlined), s.readerGallery ? tr('아래 갤러리 줄 숨기기') : tr('아래 갤러리 줄 보이기'),
|
||||||
|
_toggleGallery, on: s.readerGallery)),
|
||||||
// 119 · 120
|
// 119 · 120
|
||||||
one(_Act(const Icon(Icons.vertical_split_outlined), split, _cycleSplit, on: s.readerSplit != 'off')),
|
one(_Act(const Icon(Icons.vertical_split_outlined), split, _cycleSplit, on: s.readerSplit != 'off')),
|
||||||
one(_Act(const Icon(Icons.rotate_right), tr('90° 회전 (시계 방향)'), () => _rotate(1), on: _turns != 0)),
|
one(_Act(const Icon(Icons.rotate_right), tr('90° 회전 (시계 방향)'), () => _rotate(1), on: _turns != 0)),
|
||||||
|
|
@ -810,13 +824,72 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
/// 쪽 번호: 통째면 "12", 반쪽이면 "12-1" · "12-2"
|
/// 쪽 번호: 통째면 "12", 반쪽이면 "12-1" · "12-2"
|
||||||
String _label(ReaderSlot s) => s.part < 0 ? '${s.page + 1}' : '${s.page + 1}-${s.part + 1}';
|
String _label(ReaderSlot s) => s.part < 0 ? '${s.page + 1}' : '${s.page + 1}-${s.part + 1}';
|
||||||
|
|
||||||
|
Future<void> _toggleGallery() async {
|
||||||
|
await c.updateSettings((x) => x.readerGallery = !x.readerGallery);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {});
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollGallery(animate: false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [page] 번째 장 (파일) 으로 (두 쪽 나눠 보고 있으면 그 장의 첫 쪽)
|
||||||
|
void _goPage(int page) {
|
||||||
|
final i = _v.indexWhere((e) => e.page == page);
|
||||||
|
if (i >= 0 && _pages.hasClients) _pages.jumpToPage(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 갤러리 줄을 지금 장이 가운데 오게
|
||||||
|
void _scrollGallery({bool animate = true}) {
|
||||||
|
if (!_gallery.hasClients) return;
|
||||||
|
final pos = _gallery.position;
|
||||||
|
final to = (_cur.page * _thumbExtent - pos.viewportDimension / 2 + _thumbExtent / 2).clamp(0.0, pos.maxScrollExtent);
|
||||||
|
animate ? _gallery.animateTo(to, duration: const Duration(milliseconds: 200), curve: Curves.easeOut) : _gallery.jumpTo(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ReaderImage> _thumb(int i) => _thumbs.putIfAbsent(i, () => src.load(i, maxWidth: 160));
|
||||||
|
|
||||||
|
Widget _galleryStrip() => SizedBox(
|
||||||
|
height: 76,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _gallery,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
reverse: c.settings.readerRtl,
|
||||||
|
itemExtent: _thumbExtent,
|
||||||
|
itemCount: src.length,
|
||||||
|
itemBuilder: (_, i) => GestureDetector(
|
||||||
|
key: ValueKey('thumb$i'),
|
||||||
|
onTap: () => _goPage(i),
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.all(3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: i == _cur.page ? JjColors.accent : Colors.white24, width: i == _cur.page ? 2 : 1)),
|
||||||
|
child: FutureBuilder<ReaderImage>(
|
||||||
|
future: _thumb(i),
|
||||||
|
builder: (_, snap) {
|
||||||
|
final r = snap.data;
|
||||||
|
if (snap.hasError) return const Icon(Icons.broken_image_outlined, color: Colors.white38, size: 20);
|
||||||
|
if (r == null) return const SizedBox();
|
||||||
|
return r.image != null
|
||||||
|
? RawImage(image: r.image, fit: BoxFit.cover)
|
||||||
|
: Image(
|
||||||
|
image: r.provider!,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (_, _, _) => const Icon(Icons.broken_image_outlined, color: Colors.white38, size: 20));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _bottomBar() {
|
Widget _bottomBar() {
|
||||||
final n = src.length;
|
final n = src.length;
|
||||||
final count = _v.length;
|
final count = _v.length;
|
||||||
return Container(
|
return Container(
|
||||||
color: _barColor,
|
color: _barColor,
|
||||||
padding: EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom, left: 12, right: 12),
|
padding: EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom, left: 12, right: 12),
|
||||||
child: Row(children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
if (_pictures && c.settings.readerGallery && n > 0) _galleryStrip(),
|
||||||
|
Row(children: [
|
||||||
// 125: 반으로 나눈 쪽이 있으면 전체 수가 파일 수 (장) 임을 단위로 ("2-1 / 3장")
|
// 125: 반으로 나눈 쪽이 있으면 전체 수가 파일 수 (장) 임을 단위로 ("2-1 / 3장")
|
||||||
Text(
|
Text(
|
||||||
count != n ? trf('{0} / {1}장', [_label(_cur), n]) : '${n == 0 ? 0 : _label(_cur)} / $n',
|
count != n ? trf('{0} / {1}장', [_label(_cur), n]) : '${n == 0 ? 0 : _label(_cur)} / $n',
|
||||||
|
|
@ -843,6 +916,7 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: const TextStyle(color: Colors.white70, fontSize: 12)),
|
style: const TextStyle(color: Colors.white70, fontSize: 12)),
|
||||||
),
|
),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -883,11 +957,14 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
// 확대해 보는 동안은 밀어도 넘기지 않는다 (그림 옮기기)
|
// 확대해 보는 동안은 밀어도 넘기지 않는다 (그림 옮기기)
|
||||||
physics: _holdPage ? const NeverScrollableScrollPhysics() : null,
|
physics: _holdPage ? const NeverScrollableScrollPhysics() : null,
|
||||||
itemCount: _v.length,
|
itemCount: _v.length,
|
||||||
onPageChanged: (i) => setState(() {
|
onPageChanged: (i) {
|
||||||
_index = i;
|
setState(() {
|
||||||
_resetZoom();
|
_index = i;
|
||||||
_trim();
|
_resetZoom();
|
||||||
}),
|
_trim();
|
||||||
|
});
|
||||||
|
_scrollGallery();
|
||||||
|
},
|
||||||
itemBuilder: (_, i) => _page(i, box.biggest),
|
itemBuilder: (_, i) => _page(i, box.biggest),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,32 @@ void main() {
|
||||||
expect(AppSettings.fromJson(c.settings.toJson()).readerSystemBars, isFalse);
|
expect(AppSettings.fromJson(c.settings.toJson()).readerSystemBars, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('X-plore 맞추기: 아래 갤러리 줄 - 처음은 끔, 켜면 썸네일 · 누르면 그 장으로, 위 막대에서 끄기', (tester) async {
|
||||||
|
tester.view.physicalSize = const Size(1400, 1200);
|
||||||
|
tester.view.devicePixelRatio = 1;
|
||||||
|
addTearDown(tester.view.reset);
|
||||||
|
expect(AppSettings().readerGallery, isFalse, reason: '처음 값은 지금 동작 그대로');
|
||||||
|
expect(AppSettings.fromJson({'readerGallery': true}).readerGallery, isTrue);
|
||||||
|
final files = [
|
||||||
|
for (final (i, n) in ['a.png', 'b.png', 'c.png'].indexed) (File(p.join(tmp.path, n))..writeAsBytesSync(_png(i * 80, 0, 0))).path,
|
||||||
|
];
|
||||||
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||||
|
await tester.pumpWidget(MaterialApp(home: ReaderPage(c: c, source: ImageFilesSource(files, tempDir: tmp.path, title: 'imgs'))));
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(const ValueKey('thumb0')), findsNothing, reason: '처음은 끔');
|
||||||
|
await tester.tap(find.byTooltip('아래 갤러리 줄 보이기'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(c.settings.readerGallery, isTrue);
|
||||||
|
expect(find.byKey(const ValueKey('thumb2')), findsOneWidget);
|
||||||
|
await tester.tap(find.byKey(const ValueKey('thumb2')));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('3 / 3'), findsOneWidget, reason: '누른 장으로');
|
||||||
|
await tester.tap(find.byTooltip('아래 갤러리 줄 숨기기'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.byKey(const ValueKey('thumb0')), findsNothing);
|
||||||
|
await tester.pumpWidget(const SizedBox());
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('그림이 깨졌으면 화면 글 + [자세히], 파일을 고친 뒤 [다시 시도] 하면 열린다 (28 지적: 같은 키 · 같은 그림이라 Image 가 다시 읽지 않았다 - 장의 키를 올리고 그림 캐시도 비움)', (tester) async {
|
testWidgets('그림이 깨졌으면 화면 글 + [자세히], 파일을 고친 뒤 [다시 시도] 하면 열린다 (28 지적: 같은 키 · 같은 그림이라 Image 가 다시 읽지 않았다 - 장의 키를 올리고 그림 캐시도 비움)', (tester) async {
|
||||||
tester.view.physicalSize = const Size(900, 1200);
|
tester.view.physicalSize = const Size(900, 1200);
|
||||||
tester.view.devicePixelRatio = 1;
|
tester.view.devicePixelRatio = 1;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue