mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-14 04:00:27 +00:00
feat: unp4k UI update
This commit is contained in:
parent
aaa97429b9
commit
1c4eafccca
@ -261,18 +261,32 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m103(v0) => "Launcher internal version information: ${v0}";
|
static String m103(v0) => "Launcher internal version information: ${v0}";
|
||||||
|
|
||||||
static String m104(v0) => "Opening file: ${v0}";
|
static String m104(v0) => "Export Selected (${v0})";
|
||||||
|
|
||||||
static String m105(v0, v1) =>
|
static String m105(v0) => "Extraction failed: ${v0}";
|
||||||
|
|
||||||
|
static String m106(v0) => "Extraction complete: ${v0}";
|
||||||
|
|
||||||
|
static String m107(v0) => "Extracting: ${v0}";
|
||||||
|
|
||||||
|
static String m108(v0) => "Extraction completed, ${v0} files total";
|
||||||
|
|
||||||
|
static String m109(v0) => "Current file: ${v0}";
|
||||||
|
|
||||||
|
static String m110(v0, v1) => "Extracting (${v0}/${v1})";
|
||||||
|
|
||||||
|
static String m111(v0) => "Opening file: ${v0}";
|
||||||
|
|
||||||
|
static String m112(v0, v1) =>
|
||||||
"Loading complete: ${v0} files, time taken: ${v1} ms";
|
"Loading complete: ${v0} files, time taken: ${v1} ms";
|
||||||
|
|
||||||
static String m106(v0) => "Reading file: ${v0}...";
|
static String m113(v0) => "Reading file: ${v0}...";
|
||||||
|
|
||||||
static String m107(v0, v1) => "Processing files (${v0}/${v1})...";
|
static String m114(v0, v1) => "Processing files (${v0}/${v1})...";
|
||||||
|
|
||||||
static String m108(v0) => "Unknown file type\n${v0}";
|
static String m115(v0) => "Unknown file type\n${v0}";
|
||||||
|
|
||||||
static String m109(v0) => "P4K Viewer -> ${v0}";
|
static String m116(v0) => "P4K Viewer -> ${v0}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@ -2184,6 +2198,33 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"tools_rsi_launcher_enhance_working_msg2": MessageLookupByLibrary.simpleMessage(
|
"tools_rsi_launcher_enhance_working_msg2": MessageLookupByLibrary.simpleMessage(
|
||||||
"Installing patch, this will take some time depending on your computer\'s performance...",
|
"Installing patch, this will take some time depending on your computer\'s performance...",
|
||||||
),
|
),
|
||||||
|
"tools_unp4k_action_cancel_multi_select":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Cancel Multi-Select"),
|
||||||
|
"tools_unp4k_action_deselect_all": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Deselect All",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_export_selected": m104,
|
||||||
|
"tools_unp4k_action_extract_failed": m105,
|
||||||
|
"tools_unp4k_action_extract_success": m106,
|
||||||
|
"tools_unp4k_action_extracting": m107,
|
||||||
|
"tools_unp4k_action_multi_select": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Multi-Select",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_save_as": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Save As...",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_select_all": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Select All",
|
||||||
|
),
|
||||||
|
"tools_unp4k_extract_cancelled": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Extraction cancelled",
|
||||||
|
),
|
||||||
|
"tools_unp4k_extract_completed": m108,
|
||||||
|
"tools_unp4k_extract_current_file": m109,
|
||||||
|
"tools_unp4k_extract_dialog_title": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Extract Files",
|
||||||
|
),
|
||||||
|
"tools_unp4k_extract_progress": m110,
|
||||||
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
||||||
"Missing Runtime",
|
"Missing Runtime",
|
||||||
),
|
),
|
||||||
@ -2195,18 +2236,42 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
||||||
"Initializing...",
|
"Initializing...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_open_file": m104,
|
"tools_unp4k_msg_open_file": m111,
|
||||||
"tools_unp4k_msg_read_completed": m105,
|
"tools_unp4k_msg_read_completed": m112,
|
||||||
"tools_unp4k_msg_read_file": m106,
|
"tools_unp4k_msg_read_file": m113,
|
||||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||||
"Reading P4K file...",
|
"Reading P4K file...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||||
"Processing files...",
|
"Processing files...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading3": m107,
|
"tools_unp4k_msg_reading3": m114,
|
||||||
"tools_unp4k_msg_unknown_file_type": m108,
|
"tools_unp4k_msg_unknown_file_type": m115,
|
||||||
"tools_unp4k_title": m109,
|
"tools_unp4k_search_no_result": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"No matching files found",
|
||||||
|
),
|
||||||
|
"tools_unp4k_search_placeholder": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Search files (supports regex)...",
|
||||||
|
),
|
||||||
|
"tools_unp4k_searching": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Searching...",
|
||||||
|
),
|
||||||
|
"tools_unp4k_sort_date_asc": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Older First",
|
||||||
|
),
|
||||||
|
"tools_unp4k_sort_date_desc": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Newer First",
|
||||||
|
),
|
||||||
|
"tools_unp4k_sort_default": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Default Sort",
|
||||||
|
),
|
||||||
|
"tools_unp4k_sort_size_asc": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Smaller First",
|
||||||
|
),
|
||||||
|
"tools_unp4k_sort_size_desc": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Larger First",
|
||||||
|
),
|
||||||
|
"tools_unp4k_title": m116,
|
||||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||||
"Click file to preview",
|
"Click file to preview",
|
||||||
),
|
),
|
||||||
|
|||||||
@ -242,17 +242,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m103(v0) => "ランチャー内部バージョン情報:${v0}";
|
static String m103(v0) => "ランチャー内部バージョン情報:${v0}";
|
||||||
|
|
||||||
static String m104(v0) => "ファイルを開く:${v0}";
|
static String m111(v0) => "ファイルを開く:${v0}";
|
||||||
|
|
||||||
static String m105(v0, v1) => "読み込み完了:${v0}ファイル、所要時間:${v1} ms";
|
static String m112(v0, v1) => "読み込み完了:${v0}ファイル、所要時間:${v1} ms";
|
||||||
|
|
||||||
static String m106(v0) => "ファイルを読み込み中:${v0}...";
|
static String m113(v0) => "ファイルを読み込み中:${v0}...";
|
||||||
|
|
||||||
static String m107(v0, v1) => "ファイルを処理中(${v0}/${v1})...";
|
static String m114(v0, v1) => "ファイルを処理中(${v0}/${v1})...";
|
||||||
|
|
||||||
static String m108(v0) => "不明なファイルタイプ\n${v0}";
|
static String m115(v0) => "不明なファイルタイプ\n${v0}";
|
||||||
|
|
||||||
static String m109(v0) => "P4Kビューア -> ${v0}";
|
static String m116(v0) => "P4Kビューア -> ${v0}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@ -1971,18 +1971,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"この機能を使用するには.NET8ランタイムをインストールする必要があります。下のボタンをクリックしてダウンロードしてインストールし、インストールが成功したらこのページを再度開いて使用を続行してください。",
|
"この機能を使用するには.NET8ランタイムをインストールする必要があります。下のボタンをクリックしてダウンロードしてインストールし、インストールが成功したらこのページを再度開いて使用を続行してください。",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初期化中..."),
|
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初期化中..."),
|
||||||
"tools_unp4k_msg_open_file": m104,
|
"tools_unp4k_msg_open_file": m111,
|
||||||
"tools_unp4k_msg_read_completed": m105,
|
"tools_unp4k_msg_read_completed": m112,
|
||||||
"tools_unp4k_msg_read_file": m106,
|
"tools_unp4k_msg_read_file": m113,
|
||||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||||
"P4Kファイルを読み込み中...",
|
"P4Kファイルを読み込み中...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||||
"ファイルを処理中...",
|
"ファイルを処理中...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading3": m107,
|
"tools_unp4k_msg_reading3": m114,
|
||||||
"tools_unp4k_msg_unknown_file_type": m108,
|
"tools_unp4k_msg_unknown_file_type": m115,
|
||||||
"tools_unp4k_title": m109,
|
"tools_unp4k_title": m116,
|
||||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||||
"プレビューするファイルをクリック",
|
"プレビューするファイルをクリック",
|
||||||
),
|
),
|
||||||
|
|||||||
@ -256,18 +256,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m103(v0) => "Внутренняя версия лаунчера: ${v0}";
|
static String m103(v0) => "Внутренняя версия лаунчера: ${v0}";
|
||||||
|
|
||||||
static String m104(v0) => "Открытие файла: ${v0}";
|
static String m111(v0) => "Открытие файла: ${v0}";
|
||||||
|
|
||||||
static String m105(v0, v1) =>
|
static String m112(v0, v1) =>
|
||||||
"Загрузка завершена: ${v0} файлов, время: ${v1} мс";
|
"Загрузка завершена: ${v0} файлов, время: ${v1} мс";
|
||||||
|
|
||||||
static String m106(v0) => "Чтение файла: ${v0}...";
|
static String m113(v0) => "Чтение файла: ${v0}...";
|
||||||
|
|
||||||
static String m107(v0, v1) => "Обработка файлов (${v0}/${v1})...";
|
static String m114(v0, v1) => "Обработка файлов (${v0}/${v1})...";
|
||||||
|
|
||||||
static String m108(v0) => "Неизвестный тип файла\n${v0}";
|
static String m115(v0) => "Неизвестный тип файла\n${v0}";
|
||||||
|
|
||||||
static String m109(v0) => "Просмотрщик P4K -> ${v0}";
|
static String m116(v0) => "Просмотрщик P4K -> ${v0}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@ -2224,18 +2224,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage(
|
||||||
"Инициализация...",
|
"Инициализация...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_open_file": m104,
|
"tools_unp4k_msg_open_file": m111,
|
||||||
"tools_unp4k_msg_read_completed": m105,
|
"tools_unp4k_msg_read_completed": m112,
|
||||||
"tools_unp4k_msg_read_file": m106,
|
"tools_unp4k_msg_read_file": m113,
|
||||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||||
"Чтение файла P4K...",
|
"Чтение файла P4K...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||||
"Обработка файлов...",
|
"Обработка файлов...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading3": m107,
|
"tools_unp4k_msg_reading3": m114,
|
||||||
"tools_unp4k_msg_unknown_file_type": m108,
|
"tools_unp4k_msg_unknown_file_type": m115,
|
||||||
"tools_unp4k_title": m109,
|
"tools_unp4k_title": m116,
|
||||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage(
|
||||||
"Нажмите на файл для предварительного просмотра",
|
"Нажмите на файл для предварительного просмотра",
|
||||||
),
|
),
|
||||||
|
|||||||
@ -240,17 +240,31 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m103(v0) => "启动器内部版本信息:${v0}";
|
static String m103(v0) => "启动器内部版本信息:${v0}";
|
||||||
|
|
||||||
static String m104(v0) => "打开文件:${v0}";
|
static String m104(v0) => "导出选中项 (${v0})";
|
||||||
|
|
||||||
static String m105(v0, v1) => "加载完毕:${v0} 个文件,用时:${v1} ms";
|
static String m105(v0) => "提取失败:${v0}";
|
||||||
|
|
||||||
static String m106(v0) => "读取文件:${v0} ...";
|
static String m106(v0) => "提取完成:${v0}";
|
||||||
|
|
||||||
static String m107(v0, v1) => "正在处理文件 (${v0}/${v1}) ...";
|
static String m107(v0) => "正在提取:${v0}";
|
||||||
|
|
||||||
static String m108(v0) => "未知文件类型\n${v0}";
|
static String m108(v0) => "提取完成,共 ${v0} 个文件";
|
||||||
|
|
||||||
static String m109(v0) => "P4K 查看器 -> ${v0}";
|
static String m109(v0) => "当前文件:${v0}";
|
||||||
|
|
||||||
|
static String m110(v0, v1) => "正在提取 (${v0}/${v1})";
|
||||||
|
|
||||||
|
static String m111(v0) => "打开文件:${v0}";
|
||||||
|
|
||||||
|
static String m112(v0, v1) => "加载完毕:${v0} 个文件,用时:${v1} ms";
|
||||||
|
|
||||||
|
static String m113(v0) => "读取文件:${v0} ...";
|
||||||
|
|
||||||
|
static String m114(v0, v1) => "正在处理文件 (${v0}/${v1}) ...";
|
||||||
|
|
||||||
|
static String m115(v0) => "未知文件类型\n${v0}";
|
||||||
|
|
||||||
|
static String m116(v0) => "P4K 查看器 -> ${v0}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@ -1849,6 +1863,31 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
MessageLookupByLibrary.simpleMessage("生成补丁 ..."),
|
MessageLookupByLibrary.simpleMessage("生成补丁 ..."),
|
||||||
"tools_rsi_launcher_enhance_working_msg2":
|
"tools_rsi_launcher_enhance_working_msg2":
|
||||||
MessageLookupByLibrary.simpleMessage("安装补丁,这需要一点时间,取决于您的计算机性能 ..."),
|
MessageLookupByLibrary.simpleMessage("安装补丁,这需要一点时间,取决于您的计算机性能 ..."),
|
||||||
|
"tools_unp4k_action_cancel_multi_select":
|
||||||
|
MessageLookupByLibrary.simpleMessage("取消多选"),
|
||||||
|
"tools_unp4k_action_deselect_all": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"取消全选",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_export_selected": m104,
|
||||||
|
"tools_unp4k_action_extract_failed": m105,
|
||||||
|
"tools_unp4k_action_extract_success": m106,
|
||||||
|
"tools_unp4k_action_extracting": m107,
|
||||||
|
"tools_unp4k_action_multi_select": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"多选",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_save_as": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"另存为...",
|
||||||
|
),
|
||||||
|
"tools_unp4k_action_select_all": MessageLookupByLibrary.simpleMessage("全选"),
|
||||||
|
"tools_unp4k_extract_cancelled": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"提取已取消",
|
||||||
|
),
|
||||||
|
"tools_unp4k_extract_completed": m108,
|
||||||
|
"tools_unp4k_extract_current_file": m109,
|
||||||
|
"tools_unp4k_extract_dialog_title": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"提取文件",
|
||||||
|
),
|
||||||
|
"tools_unp4k_extract_progress": m110,
|
||||||
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_missing_runtime": MessageLookupByLibrary.simpleMessage(
|
||||||
"缺少运行库",
|
"缺少运行库",
|
||||||
),
|
),
|
||||||
@ -1858,18 +1897,30 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"使用此功能需安装 .NET8 运行库,请点击下方按钮下载安装,安装成功后重新打开此页面即可继续使用。",
|
"使用此功能需安装 .NET8 运行库,请点击下方按钮下载安装,安装成功后重新打开此页面即可继续使用。",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
||||||
"tools_unp4k_msg_open_file": m104,
|
"tools_unp4k_msg_open_file": m111,
|
||||||
"tools_unp4k_msg_read_completed": m105,
|
"tools_unp4k_msg_read_completed": m112,
|
||||||
"tools_unp4k_msg_read_file": m106,
|
"tools_unp4k_msg_read_file": m113,
|
||||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||||
"正在读取P4K 文件 ...",
|
"正在读取P4K 文件 ...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||||
"正在处理文件 ...",
|
"正在处理文件 ...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading3": m107,
|
"tools_unp4k_msg_reading3": m114,
|
||||||
"tools_unp4k_msg_unknown_file_type": m108,
|
"tools_unp4k_msg_unknown_file_type": m115,
|
||||||
"tools_unp4k_title": m109,
|
"tools_unp4k_search_no_result": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"未找到匹配文件",
|
||||||
|
),
|
||||||
|
"tools_unp4k_search_placeholder": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"搜索文件(支持正则)...",
|
||||||
|
),
|
||||||
|
"tools_unp4k_searching": MessageLookupByLibrary.simpleMessage("正在搜索..."),
|
||||||
|
"tools_unp4k_sort_date_asc": MessageLookupByLibrary.simpleMessage("旧文件优先"),
|
||||||
|
"tools_unp4k_sort_date_desc": MessageLookupByLibrary.simpleMessage("新文件优先"),
|
||||||
|
"tools_unp4k_sort_default": MessageLookupByLibrary.simpleMessage("默认排序"),
|
||||||
|
"tools_unp4k_sort_size_asc": MessageLookupByLibrary.simpleMessage("小文件优先"),
|
||||||
|
"tools_unp4k_sort_size_desc": MessageLookupByLibrary.simpleMessage("大文件优先"),
|
||||||
|
"tools_unp4k_title": m116,
|
||||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("单击文件以预览"),
|
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("单击文件以预览"),
|
||||||
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
||||||
"将左侧载具拖动到右侧列表中,这将会为载具名称增加 001、002 .. 等前缀,方便您在游戏内 UI 快速定位载具。在右侧列表上下拖动可以调整载具的顺序。",
|
"将左侧载具拖动到右侧列表中,这将会为载具名称增加 001、002 .. 等前缀,方便您在游戏内 UI 快速定位载具。在右侧列表上下拖动可以调整载具的顺序。",
|
||||||
|
|||||||
@ -236,17 +236,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m103(v0) => "啟動器內部版本資訊:${v0}";
|
static String m103(v0) => "啟動器內部版本資訊:${v0}";
|
||||||
|
|
||||||
static String m104(v0) => "打開文件:${v0}";
|
static String m111(v0) => "打開文件:${v0}";
|
||||||
|
|
||||||
static String m105(v0, v1) => "載入完畢:${v0} 個文件,用時:${v1} ms";
|
static String m112(v0, v1) => "載入完畢:${v0} 個文件,用時:${v1} ms";
|
||||||
|
|
||||||
static String m106(v0) => "讀取文件:${v0} ...";
|
static String m113(v0) => "讀取文件:${v0} ...";
|
||||||
|
|
||||||
static String m107(v0, v1) => "正在處理文件 (${v0}/${v1}) ...";
|
static String m114(v0, v1) => "正在處理文件 (${v0}/${v1}) ...";
|
||||||
|
|
||||||
static String m108(v0) => "未知文件類型\n${v0}";
|
static String m115(v0) => "未知文件類型\n${v0}";
|
||||||
|
|
||||||
static String m109(v0) => "P4K 查看器 -> ${v0}";
|
static String m116(v0) => "P4K 查看器 -> ${v0}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@ -1849,18 +1849,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"使用此功能需安裝 .NET8 運行庫,請點擊下方按鈕下載安裝,安裝成功後重新打開此頁面即可繼續使用。",
|
"使用此功能需安裝 .NET8 運行庫,請點擊下方按鈕下載安裝,安裝成功後重新打開此頁面即可繼續使用。",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
"tools_unp4k_msg_init": MessageLookupByLibrary.simpleMessage("初始化中..."),
|
||||||
"tools_unp4k_msg_open_file": m104,
|
"tools_unp4k_msg_open_file": m111,
|
||||||
"tools_unp4k_msg_read_completed": m105,
|
"tools_unp4k_msg_read_completed": m112,
|
||||||
"tools_unp4k_msg_read_file": m106,
|
"tools_unp4k_msg_read_file": m113,
|
||||||
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading": MessageLookupByLibrary.simpleMessage(
|
||||||
"正在讀取P4K 文件 ...",
|
"正在讀取P4K 文件 ...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
"tools_unp4k_msg_reading2": MessageLookupByLibrary.simpleMessage(
|
||||||
"正在處理文件 ...",
|
"正在處理文件 ...",
|
||||||
),
|
),
|
||||||
"tools_unp4k_msg_reading3": m107,
|
"tools_unp4k_msg_reading3": m114,
|
||||||
"tools_unp4k_msg_unknown_file_type": m108,
|
"tools_unp4k_msg_unknown_file_type": m115,
|
||||||
"tools_unp4k_title": m109,
|
"tools_unp4k_title": m116,
|
||||||
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("單擊文件以預覽"),
|
"tools_unp4k_view_file": MessageLookupByLibrary.simpleMessage("單擊文件以預覽"),
|
||||||
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
"tools_vehicle_sorting_info": MessageLookupByLibrary.simpleMessage(
|
||||||
"將左側載具拖動到右側列表中,這將會為載具名稱增加 001、002 .. 等前綴,方便您在遊戲內 UI 快速定位載具。在右側列表上下拖動可以調整載具的順序。",
|
"將左側載具拖動到右側列表中,這將會為載具名稱增加 001、002 .. 等前綴,方便您在遊戲內 UI 快速定位載具。在右側列表上下拖動可以調整載具的順序。",
|
||||||
|
|||||||
@ -28,9 +28,10 @@ class S {
|
|||||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||||
|
|
||||||
static Future<S> load(Locale locale) {
|
static Future<S> load(Locale locale) {
|
||||||
final name = (locale.countryCode?.isEmpty ?? false)
|
final name =
|
||||||
? locale.languageCode
|
(locale.countryCode?.isEmpty ?? false)
|
||||||
: locale.toString();
|
? locale.languageCode
|
||||||
|
: locale.toString();
|
||||||
final localeName = Intl.canonicalizedLocale(name);
|
final localeName = Intl.canonicalizedLocale(name);
|
||||||
return initializeMessages(localeName).then((_) {
|
return initializeMessages(localeName).then((_) {
|
||||||
Intl.defaultLocale = localeName;
|
Intl.defaultLocale = localeName;
|
||||||
@ -7901,6 +7902,226 @@ class S {
|
|||||||
args: [v0],
|
args: [v0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Search files (supports regex)...`
|
||||||
|
String get tools_unp4k_search_placeholder {
|
||||||
|
return Intl.message(
|
||||||
|
'Search files (supports regex)...',
|
||||||
|
name: 'tools_unp4k_search_placeholder',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Default Sort`
|
||||||
|
String get tools_unp4k_sort_default {
|
||||||
|
return Intl.message(
|
||||||
|
'Default Sort',
|
||||||
|
name: 'tools_unp4k_sort_default',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Smaller First`
|
||||||
|
String get tools_unp4k_sort_size_asc {
|
||||||
|
return Intl.message(
|
||||||
|
'Smaller First',
|
||||||
|
name: 'tools_unp4k_sort_size_asc',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Larger First`
|
||||||
|
String get tools_unp4k_sort_size_desc {
|
||||||
|
return Intl.message(
|
||||||
|
'Larger First',
|
||||||
|
name: 'tools_unp4k_sort_size_desc',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Older First`
|
||||||
|
String get tools_unp4k_sort_date_asc {
|
||||||
|
return Intl.message(
|
||||||
|
'Older First',
|
||||||
|
name: 'tools_unp4k_sort_date_asc',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Newer First`
|
||||||
|
String get tools_unp4k_sort_date_desc {
|
||||||
|
return Intl.message(
|
||||||
|
'Newer First',
|
||||||
|
name: 'tools_unp4k_sort_date_desc',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Save As...`
|
||||||
|
String get tools_unp4k_action_save_as {
|
||||||
|
return Intl.message(
|
||||||
|
'Save As...',
|
||||||
|
name: 'tools_unp4k_action_save_as',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extracting: {v0}`
|
||||||
|
String tools_unp4k_action_extracting(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Extracting: $v0',
|
||||||
|
name: 'tools_unp4k_action_extracting',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extraction complete: {v0}`
|
||||||
|
String tools_unp4k_action_extract_success(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Extraction complete: $v0',
|
||||||
|
name: 'tools_unp4k_action_extract_success',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extraction failed: {v0}`
|
||||||
|
String tools_unp4k_action_extract_failed(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Extraction failed: $v0',
|
||||||
|
name: 'tools_unp4k_action_extract_failed',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `No matching files found`
|
||||||
|
String get tools_unp4k_search_no_result {
|
||||||
|
return Intl.message(
|
||||||
|
'No matching files found',
|
||||||
|
name: 'tools_unp4k_search_no_result',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Searching...`
|
||||||
|
String get tools_unp4k_searching {
|
||||||
|
return Intl.message(
|
||||||
|
'Searching...',
|
||||||
|
name: 'tools_unp4k_searching',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extract Files`
|
||||||
|
String get tools_unp4k_extract_dialog_title {
|
||||||
|
return Intl.message(
|
||||||
|
'Extract Files',
|
||||||
|
name: 'tools_unp4k_extract_dialog_title',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extracting ({v0}/{v1})`
|
||||||
|
String tools_unp4k_extract_progress(Object v0, Object v1) {
|
||||||
|
return Intl.message(
|
||||||
|
'Extracting ($v0/$v1)',
|
||||||
|
name: 'tools_unp4k_extract_progress',
|
||||||
|
desc: '',
|
||||||
|
args: [v0, v1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Current file: {v0}`
|
||||||
|
String tools_unp4k_extract_current_file(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Current file: $v0',
|
||||||
|
name: 'tools_unp4k_extract_current_file',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extraction cancelled`
|
||||||
|
String get tools_unp4k_extract_cancelled {
|
||||||
|
return Intl.message(
|
||||||
|
'Extraction cancelled',
|
||||||
|
name: 'tools_unp4k_extract_cancelled',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Extraction completed, {v0} files total`
|
||||||
|
String tools_unp4k_extract_completed(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Extraction completed, $v0 files total',
|
||||||
|
name: 'tools_unp4k_extract_completed',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Multi-Select`
|
||||||
|
String get tools_unp4k_action_multi_select {
|
||||||
|
return Intl.message(
|
||||||
|
'Multi-Select',
|
||||||
|
name: 'tools_unp4k_action_multi_select',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Export Selected ({v0})`
|
||||||
|
String tools_unp4k_action_export_selected(Object v0) {
|
||||||
|
return Intl.message(
|
||||||
|
'Export Selected ($v0)',
|
||||||
|
name: 'tools_unp4k_action_export_selected',
|
||||||
|
desc: '',
|
||||||
|
args: [v0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Cancel Multi-Select`
|
||||||
|
String get tools_unp4k_action_cancel_multi_select {
|
||||||
|
return Intl.message(
|
||||||
|
'Cancel Multi-Select',
|
||||||
|
name: 'tools_unp4k_action_cancel_multi_select',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Select All`
|
||||||
|
String get tools_unp4k_action_select_all {
|
||||||
|
return Intl.message(
|
||||||
|
'Select All',
|
||||||
|
name: 'tools_unp4k_action_select_all',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Deselect All`
|
||||||
|
String get tools_unp4k_action_deselect_all {
|
||||||
|
return Intl.message(
|
||||||
|
'Deselect All',
|
||||||
|
name: 'tools_unp4k_action_deselect_all',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
|||||||
@ -1424,5 +1424,27 @@
|
|||||||
"splash_db_reset_done": "[Diagnostic] Database reset complete, preparing to exit application",
|
"splash_db_reset_done": "[Diagnostic] Database reset complete, preparing to exit application",
|
||||||
"splash_db_reset_msg": "Database has been reset, application will exit. Please restart the application.",
|
"splash_db_reset_msg": "Database has been reset, application will exit. Please restart the application.",
|
||||||
"splash_reset_db_failed": "[Diagnostic] Failed to reset database: {v0}",
|
"splash_reset_db_failed": "[Diagnostic] Failed to reset database: {v0}",
|
||||||
"@splash_reset_db_failed": {}
|
"@splash_reset_db_failed": {},
|
||||||
}
|
"tools_unp4k_search_placeholder": "Search files (supports regex)...",
|
||||||
|
"tools_unp4k_sort_default": "Default Sort",
|
||||||
|
"tools_unp4k_sort_size_asc": "Smaller First",
|
||||||
|
"tools_unp4k_sort_size_desc": "Larger First",
|
||||||
|
"tools_unp4k_sort_date_asc": "Older First",
|
||||||
|
"tools_unp4k_sort_date_desc": "Newer First",
|
||||||
|
"tools_unp4k_action_save_as": "Save As...",
|
||||||
|
"tools_unp4k_action_extracting": "Extracting: {v0}",
|
||||||
|
"tools_unp4k_action_extract_success": "Extraction complete: {v0}",
|
||||||
|
"tools_unp4k_action_extract_failed": "Extraction failed: {v0}",
|
||||||
|
"tools_unp4k_search_no_result": "No matching files found",
|
||||||
|
"tools_unp4k_searching": "Searching...",
|
||||||
|
"tools_unp4k_extract_dialog_title": "Extract Files",
|
||||||
|
"tools_unp4k_extract_progress": "Extracting ({v0}/{v1})",
|
||||||
|
"tools_unp4k_extract_current_file": "Current file: {v0}",
|
||||||
|
"tools_unp4k_extract_cancelled": "Extraction cancelled",
|
||||||
|
"tools_unp4k_extract_completed": "Extraction completed, {v0} files total",
|
||||||
|
"tools_unp4k_action_multi_select": "Multi-Select",
|
||||||
|
"tools_unp4k_action_export_selected": "Export Selected ({v0})",
|
||||||
|
"tools_unp4k_action_cancel_multi_select": "Cancel Multi-Select",
|
||||||
|
"tools_unp4k_action_select_all": "Select All",
|
||||||
|
"tools_unp4k_action_deselect_all": "Deselect All"
|
||||||
|
}
|
||||||
@ -1139,5 +1139,27 @@
|
|||||||
"splash_db_reset_done": "[诊断] 数据库重置完成,准备退出应用",
|
"splash_db_reset_done": "[诊断] 数据库重置完成,准备退出应用",
|
||||||
"splash_db_reset_msg": "数据库已重置,应用将退出。请重新启动应用。",
|
"splash_db_reset_msg": "数据库已重置,应用将退出。请重新启动应用。",
|
||||||
"splash_reset_db_failed": "[诊断] 重置数据库失败: {v0}",
|
"splash_reset_db_failed": "[诊断] 重置数据库失败: {v0}",
|
||||||
"@splash_reset_db_failed": {}
|
"@splash_reset_db_failed": {},
|
||||||
|
"tools_unp4k_search_placeholder": "搜索文件(支持正则)...",
|
||||||
|
"tools_unp4k_sort_default": "默认排序",
|
||||||
|
"tools_unp4k_sort_size_asc": "小文件优先",
|
||||||
|
"tools_unp4k_sort_size_desc": "大文件优先",
|
||||||
|
"tools_unp4k_sort_date_asc": "旧文件优先",
|
||||||
|
"tools_unp4k_sort_date_desc": "新文件优先",
|
||||||
|
"tools_unp4k_action_save_as": "另存为...",
|
||||||
|
"tools_unp4k_action_extracting": "正在提取:{v0}",
|
||||||
|
"tools_unp4k_action_extract_success": "提取完成:{v0}",
|
||||||
|
"tools_unp4k_action_extract_failed": "提取失败:{v0}",
|
||||||
|
"tools_unp4k_search_no_result": "未找到匹配文件",
|
||||||
|
"tools_unp4k_searching": "正在搜索...",
|
||||||
|
"tools_unp4k_extract_dialog_title": "提取文件",
|
||||||
|
"tools_unp4k_extract_progress": "正在提取 ({v0}/{v1})",
|
||||||
|
"tools_unp4k_extract_current_file": "当前文件:{v0}",
|
||||||
|
"tools_unp4k_extract_cancelled": "提取已取消",
|
||||||
|
"tools_unp4k_extract_completed": "提取完成,共 {v0} 个文件",
|
||||||
|
"tools_unp4k_action_multi_select": "多选",
|
||||||
|
"tools_unp4k_action_export_selected": "导出选中项 ({v0})",
|
||||||
|
"tools_unp4k_action_cancel_multi_select": "取消多选",
|
||||||
|
"tools_unp4k_action_select_all": "全选",
|
||||||
|
"tools_unp4k_action_deselect_all": "取消全选"
|
||||||
}
|
}
|
||||||
@ -16,6 +16,24 @@ part 'unp4kc.freezed.dart';
|
|||||||
|
|
||||||
part 'unp4kc.g.dart';
|
part 'unp4kc.g.dart';
|
||||||
|
|
||||||
|
/// 排序类型枚举
|
||||||
|
enum Unp4kSortType {
|
||||||
|
/// 默认排序(文件夹优先,按名称)
|
||||||
|
defaultSort,
|
||||||
|
|
||||||
|
/// 文件大小升序
|
||||||
|
sizeAsc,
|
||||||
|
|
||||||
|
/// 文件大小降序
|
||||||
|
sizeDesc,
|
||||||
|
|
||||||
|
/// 修改时间升序
|
||||||
|
dateAsc,
|
||||||
|
|
||||||
|
/// 修改时间降序
|
||||||
|
dateDesc,
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class Unp4kcState with _$Unp4kcState {
|
abstract class Unp4kcState with _$Unp4kcState {
|
||||||
const factory Unp4kcState({
|
const factory Unp4kcState({
|
||||||
@ -26,6 +44,21 @@ abstract class Unp4kcState with _$Unp4kcState {
|
|||||||
String? endMessage,
|
String? endMessage,
|
||||||
MapEntry<String, String>? tempOpenFile,
|
MapEntry<String, String>? tempOpenFile,
|
||||||
@Default("") String errorMessage,
|
@Default("") String errorMessage,
|
||||||
|
@Default("") String searchQuery,
|
||||||
|
@Default(false) bool isSearching,
|
||||||
|
|
||||||
|
/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||||
|
MemoryFileSystem? searchFs,
|
||||||
|
|
||||||
|
/// 搜索匹配的文件路径集合
|
||||||
|
Set<String>? searchMatchedFiles,
|
||||||
|
@Default(Unp4kSortType.defaultSort) Unp4kSortType sortType,
|
||||||
|
|
||||||
|
/// 是否处于多选模式
|
||||||
|
@Default(false) bool isMultiSelectMode,
|
||||||
|
|
||||||
|
/// 多选模式下选中的文件路径集合
|
||||||
|
@Default({}) Set<String> selectedItems,
|
||||||
}) = _Unp4kcState;
|
}) = _Unp4kcState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,20 +143,15 @@ class Unp4kCModel extends _$Unp4kCModel {
|
|||||||
|
|
||||||
List<AppUnp4kP4kItemData>? getFiles() {
|
List<AppUnp4kP4kItemData>? getFiles() {
|
||||||
final path = state.curPath.replaceAll("\\", "/");
|
final path = state.curPath.replaceAll("\\", "/");
|
||||||
final fs = state.fs;
|
|
||||||
|
// 如果有搜索结果,使用搜索的虚拟文件系统
|
||||||
|
final fs = state.searchFs ?? state.fs;
|
||||||
if (fs == null) return null;
|
if (fs == null) return null;
|
||||||
|
|
||||||
final dir = fs.directory(path);
|
final dir = fs.directory(path);
|
||||||
if (!dir.existsSync()) return [];
|
if (!dir.existsSync()) return [];
|
||||||
final files = dir.listSync(recursive: false, followLinks: false);
|
final files = dir.listSync(recursive: false, followLinks: false);
|
||||||
files.sort((a, b) {
|
|
||||||
if (a is Directory && b is File) {
|
|
||||||
return -1;
|
|
||||||
} else if (a is File && b is Directory) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return a.path.compareTo(b.path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
final result = <AppUnp4kP4kItemData>[];
|
final result = <AppUnp4kP4kItemData>[];
|
||||||
for (var file in files) {
|
for (var file in files) {
|
||||||
if (file is File) {
|
if (file is File) {
|
||||||
@ -138,10 +166,211 @@ class Unp4kCModel extends _$Unp4kCModel {
|
|||||||
result.add(AppUnp4kP4kItemData(name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
result.add(AppUnp4kP4kItemData(name: file.path.replaceAll("/", "\\"), isDirectory: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 应用排序
|
||||||
|
_sortFiles(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 对文件列表进行排序
|
||||||
|
void _sortFiles(List<AppUnp4kP4kItemData> files) {
|
||||||
|
switch (state.sortType) {
|
||||||
|
case Unp4kSortType.defaultSort:
|
||||||
|
// 默认排序:文件夹优先,按名称排序
|
||||||
|
files.sort((a, b) {
|
||||||
|
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||||
|
return -1;
|
||||||
|
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return (a.name ?? "").compareTo(b.name ?? "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Unp4kSortType.sizeAsc:
|
||||||
|
// 文件大小升序(文件夹大小视为0)
|
||||||
|
files.sort((a, b) {
|
||||||
|
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||||
|
return -1;
|
||||||
|
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
final sizeA = (a.isDirectory ?? false) ? 0 : (a.size ?? 0);
|
||||||
|
final sizeB = (b.isDirectory ?? false) ? 0 : (b.size ?? 0);
|
||||||
|
return sizeA.compareTo(sizeB);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Unp4kSortType.sizeDesc:
|
||||||
|
// 文件大小降序
|
||||||
|
files.sort((a, b) {
|
||||||
|
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||||
|
return -1;
|
||||||
|
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
final sizeA = (a.isDirectory ?? false) ? 0 : (a.size ?? 0);
|
||||||
|
final sizeB = (b.isDirectory ?? false) ? 0 : (b.size ?? 0);
|
||||||
|
return sizeB.compareTo(sizeA);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Unp4kSortType.dateAsc:
|
||||||
|
// 修改时间升序
|
||||||
|
files.sort((a, b) {
|
||||||
|
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||||
|
return -1;
|
||||||
|
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
final dateA = a.dateModified ?? 0;
|
||||||
|
final dateB = b.dateModified ?? 0;
|
||||||
|
return dateA.compareTo(dateB);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Unp4kSortType.dateDesc:
|
||||||
|
// 修改时间降序
|
||||||
|
files.sort((a, b) {
|
||||||
|
if ((a.isDirectory ?? false) && !(b.isDirectory ?? false)) {
|
||||||
|
return -1;
|
||||||
|
} else if (!(a.isDirectory ?? false) && (b.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
final dateA = a.dateModified ?? 0;
|
||||||
|
final dateB = b.dateModified ?? 0;
|
||||||
|
return dateB.compareTo(dateA);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置排序类型
|
||||||
|
void setSortType(Unp4kSortType sortType) {
|
||||||
|
state = state.copyWith(sortType: sortType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行搜索(异步)
|
||||||
|
Future<void> search(String query) async {
|
||||||
|
if (query.isEmpty) {
|
||||||
|
// 清除搜索,返回根目录
|
||||||
|
state = state.copyWith(
|
||||||
|
searchQuery: "",
|
||||||
|
searchFs: null,
|
||||||
|
searchMatchedFiles: null,
|
||||||
|
isSearching: false,
|
||||||
|
curPath: "\\",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存当前路径,用于搜索后尝试保持
|
||||||
|
final currentPath = state.curPath;
|
||||||
|
|
||||||
|
state = state.copyWith(searchQuery: query, isSearching: true, endMessage: S.current.tools_unp4k_searching);
|
||||||
|
|
||||||
|
// 使用 compute 在后台线程执行搜索
|
||||||
|
final allFiles = state.files;
|
||||||
|
if (allFiles == null) {
|
||||||
|
state = state.copyWith(isSearching: false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final searchResult = await compute(_searchFiles, _SearchParams(allFiles, query));
|
||||||
|
final matchedFiles = searchResult.matchedFiles;
|
||||||
|
|
||||||
|
// 构建搜索结果的虚拟文件系统
|
||||||
|
final searchFs = MemoryFileSystem(style: FileSystemStyle.posix);
|
||||||
|
for (var filePath in matchedFiles) {
|
||||||
|
await searchFs.file(filePath.replaceAll("\\", "/")).create(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前路径是否有搜索结果
|
||||||
|
String targetPath = "\\";
|
||||||
|
if (currentPath != "\\") {
|
||||||
|
final checkPath = currentPath.replaceAll("\\", "/");
|
||||||
|
final dir = searchFs.directory(checkPath);
|
||||||
|
if (dir.existsSync() && dir.listSync().isNotEmpty) {
|
||||||
|
// 当前目录有结果,保持当前路径
|
||||||
|
targetPath = currentPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
searchFs: searchFs,
|
||||||
|
searchMatchedFiles: matchedFiles,
|
||||||
|
isSearching: false,
|
||||||
|
curPath: targetPath,
|
||||||
|
endMessage: matchedFiles.isEmpty
|
||||||
|
? S.current.tools_unp4k_search_no_result
|
||||||
|
: S.current.tools_unp4k_msg_read_completed(matchedFiles.length, 0),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
dPrint("[unp4k] search error: $e");
|
||||||
|
state = state.copyWith(isSearching: false, endMessage: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 清除搜索
|
||||||
|
void clearSearch() {
|
||||||
|
state = state.copyWith(
|
||||||
|
searchQuery: "",
|
||||||
|
searchFs: null,
|
||||||
|
searchMatchedFiles: null,
|
||||||
|
isSearching: false,
|
||||||
|
curPath: "\\",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 进入多选模式
|
||||||
|
void enterMultiSelectMode() {
|
||||||
|
state = state.copyWith(isMultiSelectMode: true, selectedItems: {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 退出多选模式
|
||||||
|
void exitMultiSelectMode() {
|
||||||
|
state = state.copyWith(isMultiSelectMode: false, selectedItems: {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 切换选中状态
|
||||||
|
void toggleSelectItem(String itemPath) {
|
||||||
|
final currentSelected = Set<String>.from(state.selectedItems);
|
||||||
|
if (currentSelected.contains(itemPath)) {
|
||||||
|
currentSelected.remove(itemPath);
|
||||||
|
} else {
|
||||||
|
currentSelected.add(itemPath);
|
||||||
|
}
|
||||||
|
state = state.copyWith(selectedItems: currentSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全选当前目录的文件
|
||||||
|
void selectAll(List<AppUnp4kP4kItemData>? files) {
|
||||||
|
if (files == null) return;
|
||||||
|
final currentSelected = Set<String>.from(state.selectedItems);
|
||||||
|
for (var file in files) {
|
||||||
|
final path = file.name ?? "";
|
||||||
|
if (path.isNotEmpty) {
|
||||||
|
currentSelected.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = state.copyWith(selectedItems: currentSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 取消全选当前目录的文件
|
||||||
|
void deselectAll(List<AppUnp4kP4kItemData>? files) {
|
||||||
|
if (files == null) return;
|
||||||
|
final currentSelected = Set<String>.from(state.selectedItems);
|
||||||
|
for (var file in files) {
|
||||||
|
final path = file.name ?? "";
|
||||||
|
currentSelected.remove(path);
|
||||||
|
}
|
||||||
|
state = state.copyWith(selectedItems: currentSelected);
|
||||||
|
}
|
||||||
|
|
||||||
void changeDir(String name, {bool fullPath = false}) {
|
void changeDir(String name, {bool fullPath = false}) {
|
||||||
|
// 切换目录时退出多选模式
|
||||||
|
if (state.isMultiSelectMode) {
|
||||||
|
state = state.copyWith(isMultiSelectMode: false, selectedItems: {});
|
||||||
|
}
|
||||||
|
// 切换目录时不清除搜索,只改变当前路径
|
||||||
if (fullPath) {
|
if (fullPath) {
|
||||||
state = state.copyWith(curPath: name);
|
state = state.copyWith(curPath: name);
|
||||||
} else {
|
} else {
|
||||||
@ -197,6 +426,202 @@ class Unp4kCModel extends _$Unp4kCModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 提取文件或文件夹到指定目录(带进度回调和取消支持)
|
||||||
|
/// [item] 要提取的文件或文件夹
|
||||||
|
/// [outputDir] 输出目录
|
||||||
|
/// [onProgress] 进度回调 (当前文件索引, 总文件数, 当前文件名)
|
||||||
|
/// [isCancelled] 取消检查函数,返回 true 表示取消
|
||||||
|
/// 返回值:(是否成功, 已提取文件数, 错误信息)
|
||||||
|
Future<(bool, int, String?)> extractToDirectoryWithProgress(
|
||||||
|
AppUnp4kP4kItemData item,
|
||||||
|
String outputDir, {
|
||||||
|
void Function(int current, int total, String currentFile)? onProgress,
|
||||||
|
bool Function()? isCancelled,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final itemPath = item.name ?? "";
|
||||||
|
var filePath = itemPath;
|
||||||
|
if (filePath.startsWith("\\")) {
|
||||||
|
filePath = filePath.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.isDirectory ?? false) {
|
||||||
|
// 提取文件夹:遍历所有以该路径为前缀的文件
|
||||||
|
final allFiles = state.files;
|
||||||
|
if (allFiles != null) {
|
||||||
|
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||||
|
|
||||||
|
// 收集所有需要提取的文件
|
||||||
|
final filesToExtract = <MapEntry<String, AppUnp4kP4kItemData>>[];
|
||||||
|
for (var entry in allFiles.entries) {
|
||||||
|
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||||
|
filesToExtract.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final total = filesToExtract.length;
|
||||||
|
var current = 0;
|
||||||
|
|
||||||
|
for (var entry in filesToExtract) {
|
||||||
|
// 检查是否取消
|
||||||
|
if (isCancelled?.call() == true) {
|
||||||
|
return (false, current, S.current.tools_unp4k_extract_cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryPath = entry.key;
|
||||||
|
if (entryPath.startsWith("\\")) {
|
||||||
|
entryPath = entryPath.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
current++;
|
||||||
|
onProgress?.call(current, total, entryPath);
|
||||||
|
|
||||||
|
final fullOutputPath = "$outputDir\\$entryPath";
|
||||||
|
await unp4k_api.p4KExtractToDisk(filePath: entryPath, outputPath: fullOutputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(current));
|
||||||
|
return (true, current, null);
|
||||||
|
}
|
||||||
|
return (true, 0, null);
|
||||||
|
} else {
|
||||||
|
// 提取单个文件
|
||||||
|
onProgress?.call(1, 1, filePath);
|
||||||
|
|
||||||
|
// 检查是否取消
|
||||||
|
if (isCancelled?.call() == true) {
|
||||||
|
return (false, 0, S.current.tools_unp4k_extract_cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
final fullOutputPath = "$outputDir\\$filePath";
|
||||||
|
await unp4k_api.p4KExtractToDisk(filePath: filePath, outputPath: fullOutputPath);
|
||||||
|
|
||||||
|
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(1));
|
||||||
|
return (true, 1, null);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
dPrint("[unp4k] extractToDirectoryWithProgress error: $e");
|
||||||
|
return (false, 0, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取文件夹中需要提取的文件数量
|
||||||
|
int getFileCountInDirectory(AppUnp4kP4kItemData item) {
|
||||||
|
if (!(item.isDirectory ?? false)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final itemPath = item.name ?? "";
|
||||||
|
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||||
|
final allFiles = state.files;
|
||||||
|
|
||||||
|
if (allFiles == null) return 0;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (var entry in allFiles.entries) {
|
||||||
|
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取多选项的总文件数量
|
||||||
|
int getFileCountForSelectedItems(Set<String> selectedItems) {
|
||||||
|
int count = 0;
|
||||||
|
final allFiles = state.files;
|
||||||
|
if (allFiles == null) return 0;
|
||||||
|
|
||||||
|
for (var itemPath in selectedItems) {
|
||||||
|
final item = allFiles[itemPath];
|
||||||
|
if (item != null) {
|
||||||
|
if (item.isDirectory ?? false) {
|
||||||
|
count += getFileCountInDirectory(item);
|
||||||
|
} else {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 可能是文件夹(虚拟路径)
|
||||||
|
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||||
|
for (var entry in allFiles.entries) {
|
||||||
|
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 批量提取多个选中项到指定目录(带进度回调和取消支持)
|
||||||
|
Future<(bool, int, String?)> extractSelectedItemsWithProgress(
|
||||||
|
Set<String> selectedItems,
|
||||||
|
String outputDir, {
|
||||||
|
void Function(int current, int total, String currentFile)? onProgress,
|
||||||
|
bool Function()? isCancelled,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final allFiles = state.files;
|
||||||
|
if (allFiles == null) return (true, 0, null);
|
||||||
|
|
||||||
|
// 收集所有需要提取的文件
|
||||||
|
final filesToExtract = <String>[];
|
||||||
|
|
||||||
|
for (var itemPath in selectedItems) {
|
||||||
|
final item = allFiles[itemPath];
|
||||||
|
if (item != null) {
|
||||||
|
if (item.isDirectory ?? false) {
|
||||||
|
// 文件夹:收集所有子文件
|
||||||
|
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||||
|
for (var entry in allFiles.entries) {
|
||||||
|
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||||
|
filesToExtract.add(entry.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单个文件
|
||||||
|
filesToExtract.add(itemPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 可能是虚拟文件夹路径
|
||||||
|
final prefix = itemPath.endsWith("\\") ? itemPath : "$itemPath\\";
|
||||||
|
for (var entry in allFiles.entries) {
|
||||||
|
if (entry.key.startsWith(prefix) && !(entry.value.isDirectory ?? false)) {
|
||||||
|
filesToExtract.add(entry.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final total = filesToExtract.length;
|
||||||
|
var current = 0;
|
||||||
|
|
||||||
|
for (var filePath in filesToExtract) {
|
||||||
|
// 检查是否取消
|
||||||
|
if (isCancelled?.call() == true) {
|
||||||
|
return (false, current, S.current.tools_unp4k_extract_cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
var extractPath = filePath;
|
||||||
|
if (extractPath.startsWith("\\")) {
|
||||||
|
extractPath = extractPath.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
current++;
|
||||||
|
onProgress?.call(current, total, extractPath);
|
||||||
|
|
||||||
|
final fullOutputPath = "$outputDir\\$extractPath";
|
||||||
|
await unp4k_api.p4KExtractToDisk(filePath: extractPath, outputPath: fullOutputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(endMessage: S.current.tools_unp4k_extract_completed(current));
|
||||||
|
return (true, current, null);
|
||||||
|
} catch (e) {
|
||||||
|
dPrint("[unp4k] extractSelectedItemsWithProgress error: $e");
|
||||||
|
return (false, 0, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 从 P4K 文件中提取指定文件到内存
|
/// 从 P4K 文件中提取指定文件到内存
|
||||||
/// [p4kPath] P4K 文件路径
|
/// [p4kPath] P4K 文件路径
|
||||||
/// [filePath] 要提取的文件路径(P4K 内部路径)
|
/// [filePath] 要提取的文件路径(P4K 内部路径)
|
||||||
@ -211,3 +636,54 @@ class Unp4kCModel extends _$Unp4kCModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 搜索参数类
|
||||||
|
class _SearchParams {
|
||||||
|
final Map<String, AppUnp4kP4kItemData> files;
|
||||||
|
final String query;
|
||||||
|
|
||||||
|
_SearchParams(this.files, this.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 搜索结果类
|
||||||
|
class _SearchResult {
|
||||||
|
final Set<String> matchedFiles;
|
||||||
|
|
||||||
|
_SearchResult(this.matchedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 在后台线程执行搜索
|
||||||
|
_SearchResult _searchFiles(_SearchParams params) {
|
||||||
|
final matchedFiles = <String>{};
|
||||||
|
|
||||||
|
// 尝试编译正则表达式,如果失败则使用普通字符串匹配
|
||||||
|
RegExp? regex;
|
||||||
|
try {
|
||||||
|
regex = RegExp(params.query, caseSensitive: false);
|
||||||
|
} catch (e) {
|
||||||
|
// 正则无效,回退到普通字符串匹配
|
||||||
|
regex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var entry in params.files.entries) {
|
||||||
|
final item = entry.value;
|
||||||
|
final name = item.name ?? "";
|
||||||
|
|
||||||
|
// 跳过文件夹本身
|
||||||
|
if (item.isDirectory ?? false) continue;
|
||||||
|
|
||||||
|
bool matches = false;
|
||||||
|
if (regex != null) {
|
||||||
|
matches = regex.hasMatch(name);
|
||||||
|
} else {
|
||||||
|
matches = name.toLowerCase().contains(params.query.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
// 添加匹配的文件路径
|
||||||
|
matchedFiles.add(name.startsWith("\\") ? name : "\\$name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _SearchResult(matchedFiles);
|
||||||
|
}
|
||||||
|
|||||||
@ -14,7 +14,11 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$Unp4kcState implements DiagnosticableTreeMixin {
|
mixin _$Unp4kcState implements DiagnosticableTreeMixin {
|
||||||
|
|
||||||
bool get startUp; Map<String, AppUnp4kP4kItemData>? get files; MemoryFileSystem? get fs; String get curPath; String? get endMessage; MapEntry<String, String>? get tempOpenFile; String get errorMessage;
|
bool get startUp; Map<String, AppUnp4kP4kItemData>? get files; MemoryFileSystem? get fs; String get curPath; String? get endMessage; MapEntry<String, String>? get tempOpenFile; String get errorMessage; String get searchQuery; bool get isSearching;/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||||
|
MemoryFileSystem? get searchFs;/// 搜索匹配的文件路径集合
|
||||||
|
Set<String>? get searchMatchedFiles; Unp4kSortType get sortType;/// 是否处于多选模式
|
||||||
|
bool get isMultiSelectMode;/// 多选模式下选中的文件路径集合
|
||||||
|
Set<String> get selectedItems;
|
||||||
/// Create a copy of Unp4kcState
|
/// Create a copy of Unp4kcState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@ -26,21 +30,21 @@ $Unp4kcStateCopyWith<Unp4kcState> get copyWith => _$Unp4kcStateCopyWithImpl<Unp4
|
|||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
properties
|
properties
|
||||||
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage));
|
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage))..add(DiagnosticsProperty('searchQuery', searchQuery))..add(DiagnosticsProperty('isSearching', isSearching))..add(DiagnosticsProperty('searchFs', searchFs))..add(DiagnosticsProperty('searchMatchedFiles', searchMatchedFiles))..add(DiagnosticsProperty('sortType', sortType))..add(DiagnosticsProperty('isMultiSelectMode', isMultiSelectMode))..add(DiagnosticsProperty('selectedItems', selectedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other.files, files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other.files, files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.searchQuery, searchQuery) || other.searchQuery == searchQuery)&&(identical(other.isSearching, isSearching) || other.isSearching == isSearching)&&(identical(other.searchFs, searchFs) || other.searchFs == searchFs)&&const DeepCollectionEquality().equals(other.searchMatchedFiles, searchMatchedFiles)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.isMultiSelectMode, isMultiSelectMode) || other.isMultiSelectMode == isMultiSelectMode)&&const DeepCollectionEquality().equals(other.selectedItems, selectedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(files),fs,curPath,endMessage,tempOpenFile,errorMessage);
|
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(files),fs,curPath,endMessage,tempOpenFile,errorMessage,searchQuery,isSearching,searchFs,const DeepCollectionEquality().hash(searchMatchedFiles),sortType,isMultiSelectMode,const DeepCollectionEquality().hash(selectedItems));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage)';
|
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage, searchQuery: $searchQuery, isSearching: $isSearching, searchFs: $searchFs, searchMatchedFiles: $searchMatchedFiles, sortType: $sortType, isMultiSelectMode: $isMultiSelectMode, selectedItems: $selectedItems)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +55,7 @@ abstract mixin class $Unp4kcStateCopyWith<$Res> {
|
|||||||
factory $Unp4kcStateCopyWith(Unp4kcState value, $Res Function(Unp4kcState) _then) = _$Unp4kcStateCopyWithImpl;
|
factory $Unp4kcStateCopyWith(Unp4kcState value, $Res Function(Unp4kcState) _then) = _$Unp4kcStateCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage
|
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ class _$Unp4kcStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of Unp4kcState
|
/// Create a copy of Unp4kcState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,Object? searchQuery = null,Object? isSearching = null,Object? searchFs = freezed,Object? searchMatchedFiles = freezed,Object? sortType = null,Object? isMultiSelectMode = null,Object? selectedItems = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,files: freezed == files ? _self.files : files // ignore: cast_nullable_to_non_nullable
|
as bool,files: freezed == files ? _self.files : files // ignore: cast_nullable_to_non_nullable
|
||||||
@ -77,7 +81,14 @@ as MemoryFileSystem?,curPath: null == curPath ? _self.curPath : curPath // ignor
|
|||||||
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||||
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,searchQuery: null == searchQuery ? _self.searchQuery : searchQuery // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isSearching: null == isSearching ? _self.isSearching : isSearching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,searchFs: freezed == searchFs ? _self.searchFs : searchFs // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MemoryFileSystem?,searchMatchedFiles: freezed == searchMatchedFiles ? _self.searchMatchedFiles : searchMatchedFiles // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Set<String>?,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Unp4kSortType,isMultiSelectMode: null == isMultiSelectMode ? _self.isMultiSelectMode : isMultiSelectMode // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,selectedItems: null == selectedItems ? _self.selectedItems : selectedItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Set<String>,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,10 +173,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _Unp4kcState() when $default != null:
|
case _Unp4kcState() when $default != null:
|
||||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -183,10 +194,10 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _Unp4kcState():
|
case _Unp4kcState():
|
||||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||||
throw StateError('Unexpected subclass');
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -203,10 +214,10 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _Unp4kcState() when $default != null:
|
case _Unp4kcState() when $default != null:
|
||||||
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage);case _:
|
return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessage,_that.tempOpenFile,_that.errorMessage,_that.searchQuery,_that.isSearching,_that.searchFs,_that.searchMatchedFiles,_that.sortType,_that.isMultiSelectMode,_that.selectedItems);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -218,7 +229,7 @@ return $default(_that.startUp,_that.files,_that.fs,_that.curPath,_that.endMessag
|
|||||||
|
|
||||||
|
|
||||||
class _Unp4kcState with DiagnosticableTreeMixin implements Unp4kcState {
|
class _Unp4kcState with DiagnosticableTreeMixin implements Unp4kcState {
|
||||||
const _Unp4kcState({required this.startUp, final Map<String, AppUnp4kP4kItemData>? files, this.fs, required this.curPath, this.endMessage, this.tempOpenFile, this.errorMessage = ""}): _files = files;
|
const _Unp4kcState({required this.startUp, final Map<String, AppUnp4kP4kItemData>? files, this.fs, required this.curPath, this.endMessage, this.tempOpenFile, this.errorMessage = "", this.searchQuery = "", this.isSearching = false, this.searchFs, final Set<String>? searchMatchedFiles, this.sortType = Unp4kSortType.defaultSort, this.isMultiSelectMode = false, final Set<String> selectedItems = const {}}): _files = files,_searchMatchedFiles = searchMatchedFiles,_selectedItems = selectedItems;
|
||||||
|
|
||||||
|
|
||||||
@override final bool startUp;
|
@override final bool startUp;
|
||||||
@ -236,6 +247,33 @@ class _Unp4kcState with DiagnosticableTreeMixin implements Unp4kcState {
|
|||||||
@override final String? endMessage;
|
@override final String? endMessage;
|
||||||
@override final MapEntry<String, String>? tempOpenFile;
|
@override final MapEntry<String, String>? tempOpenFile;
|
||||||
@override@JsonKey() final String errorMessage;
|
@override@JsonKey() final String errorMessage;
|
||||||
|
@override@JsonKey() final String searchQuery;
|
||||||
|
@override@JsonKey() final bool isSearching;
|
||||||
|
/// 搜索结果的虚拟文件系统(支持分级展示)
|
||||||
|
@override final MemoryFileSystem? searchFs;
|
||||||
|
/// 搜索匹配的文件路径集合
|
||||||
|
final Set<String>? _searchMatchedFiles;
|
||||||
|
/// 搜索匹配的文件路径集合
|
||||||
|
@override Set<String>? get searchMatchedFiles {
|
||||||
|
final value = _searchMatchedFiles;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_searchMatchedFiles is EqualUnmodifiableSetView) return _searchMatchedFiles;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableSetView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override@JsonKey() final Unp4kSortType sortType;
|
||||||
|
/// 是否处于多选模式
|
||||||
|
@override@JsonKey() final bool isMultiSelectMode;
|
||||||
|
/// 多选模式下选中的文件路径集合
|
||||||
|
final Set<String> _selectedItems;
|
||||||
|
/// 多选模式下选中的文件路径集合
|
||||||
|
@override@JsonKey() Set<String> get selectedItems {
|
||||||
|
if (_selectedItems is EqualUnmodifiableSetView) return _selectedItems;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableSetView(_selectedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Create a copy of Unp4kcState
|
/// Create a copy of Unp4kcState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -248,21 +286,21 @@ _$Unp4kcStateCopyWith<_Unp4kcState> get copyWith => __$Unp4kcStateCopyWithImpl<_
|
|||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
properties
|
properties
|
||||||
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
..add(DiagnosticsProperty('type', 'Unp4kcState'))
|
||||||
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage));
|
..add(DiagnosticsProperty('startUp', startUp))..add(DiagnosticsProperty('files', files))..add(DiagnosticsProperty('fs', fs))..add(DiagnosticsProperty('curPath', curPath))..add(DiagnosticsProperty('endMessage', endMessage))..add(DiagnosticsProperty('tempOpenFile', tempOpenFile))..add(DiagnosticsProperty('errorMessage', errorMessage))..add(DiagnosticsProperty('searchQuery', searchQuery))..add(DiagnosticsProperty('isSearching', isSearching))..add(DiagnosticsProperty('searchFs', searchFs))..add(DiagnosticsProperty('searchMatchedFiles', searchMatchedFiles))..add(DiagnosticsProperty('sortType', sortType))..add(DiagnosticsProperty('isMultiSelectMode', isMultiSelectMode))..add(DiagnosticsProperty('selectedItems', selectedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other._files, _files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Unp4kcState&&(identical(other.startUp, startUp) || other.startUp == startUp)&&const DeepCollectionEquality().equals(other._files, _files)&&(identical(other.fs, fs) || other.fs == fs)&&(identical(other.curPath, curPath) || other.curPath == curPath)&&(identical(other.endMessage, endMessage) || other.endMessage == endMessage)&&(identical(other.tempOpenFile, tempOpenFile) || other.tempOpenFile == tempOpenFile)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.searchQuery, searchQuery) || other.searchQuery == searchQuery)&&(identical(other.isSearching, isSearching) || other.isSearching == isSearching)&&(identical(other.searchFs, searchFs) || other.searchFs == searchFs)&&const DeepCollectionEquality().equals(other._searchMatchedFiles, _searchMatchedFiles)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.isMultiSelectMode, isMultiSelectMode) || other.isMultiSelectMode == isMultiSelectMode)&&const DeepCollectionEquality().equals(other._selectedItems, _selectedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(_files),fs,curPath,endMessage,tempOpenFile,errorMessage);
|
int get hashCode => Object.hash(runtimeType,startUp,const DeepCollectionEquality().hash(_files),fs,curPath,endMessage,tempOpenFile,errorMessage,searchQuery,isSearching,searchFs,const DeepCollectionEquality().hash(_searchMatchedFiles),sortType,isMultiSelectMode,const DeepCollectionEquality().hash(_selectedItems));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||||
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage)';
|
return 'Unp4kcState(startUp: $startUp, files: $files, fs: $fs, curPath: $curPath, endMessage: $endMessage, tempOpenFile: $tempOpenFile, errorMessage: $errorMessage, searchQuery: $searchQuery, isSearching: $isSearching, searchFs: $searchFs, searchMatchedFiles: $searchMatchedFiles, sortType: $sortType, isMultiSelectMode: $isMultiSelectMode, selectedItems: $selectedItems)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -273,7 +311,7 @@ abstract mixin class _$Unp4kcStateCopyWith<$Res> implements $Unp4kcStateCopyWith
|
|||||||
factory _$Unp4kcStateCopyWith(_Unp4kcState value, $Res Function(_Unp4kcState) _then) = __$Unp4kcStateCopyWithImpl;
|
factory _$Unp4kcStateCopyWith(_Unp4kcState value, $Res Function(_Unp4kcState) _then) = __$Unp4kcStateCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage
|
bool startUp, Map<String, AppUnp4kP4kItemData>? files, MemoryFileSystem? fs, String curPath, String? endMessage, MapEntry<String, String>? tempOpenFile, String errorMessage, String searchQuery, bool isSearching, MemoryFileSystem? searchFs, Set<String>? searchMatchedFiles, Unp4kSortType sortType, bool isMultiSelectMode, Set<String> selectedItems
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -290,7 +328,7 @@ class __$Unp4kcStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of Unp4kcState
|
/// Create a copy of Unp4kcState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? startUp = null,Object? files = freezed,Object? fs = freezed,Object? curPath = null,Object? endMessage = freezed,Object? tempOpenFile = freezed,Object? errorMessage = null,Object? searchQuery = null,Object? isSearching = null,Object? searchFs = freezed,Object? searchMatchedFiles = freezed,Object? sortType = null,Object? isMultiSelectMode = null,Object? selectedItems = null,}) {
|
||||||
return _then(_Unp4kcState(
|
return _then(_Unp4kcState(
|
||||||
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
startUp: null == startUp ? _self.startUp : startUp // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,files: freezed == files ? _self._files : files // ignore: cast_nullable_to_non_nullable
|
as bool,files: freezed == files ? _self._files : files // ignore: cast_nullable_to_non_nullable
|
||||||
@ -299,7 +337,14 @@ as MemoryFileSystem?,curPath: null == curPath ? _self.curPath : curPath // ignor
|
|||||||
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
as String,endMessage: freezed == endMessage ? _self.endMessage : endMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
as String?,tempOpenFile: freezed == tempOpenFile ? _self.tempOpenFile : tempOpenFile // ignore: cast_nullable_to_non_nullable
|
||||||
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
as MapEntry<String, String>?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,searchQuery: null == searchQuery ? _self.searchQuery : searchQuery // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isSearching: null == isSearching ? _self.isSearching : isSearching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,searchFs: freezed == searchFs ? _self.searchFs : searchFs // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MemoryFileSystem?,searchMatchedFiles: freezed == searchMatchedFiles ? _self._searchMatchedFiles : searchMatchedFiles // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Set<String>?,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Unp4kSortType,isMultiSelectMode: null == isMultiSelectMode ? _self.isMultiSelectMode : isMultiSelectMode // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,selectedItems: null == selectedItems ? _self._selectedItems : selectedItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Set<String>,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ final class Unp4kCModelProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$unp4kCModelHash() => r'a296a499158e78848a698c3fda92c4c88ff039be';
|
String _$unp4kCModelHash() => r'b46274b1409dc904db2d96acf692869edf034b9f';
|
||||||
|
|
||||||
abstract class _$Unp4kCModel extends $Notifier<Unp4kcState> {
|
abstract class _$Unp4kCModel extends $Notifier<Unp4kcState> {
|
||||||
Unp4kcState build();
|
Unp4kcState build();
|
||||||
|
|||||||
@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$advancedLocalizationUIModelHash() =>
|
String _$advancedLocalizationUIModelHash() =>
|
||||||
r'5ff4d8156fbae4dcf69cb3fbcabfb9abda69ffbb';
|
r'4527ea29b07d4e525367d380d2aeb3ece4f99f4f';
|
||||||
|
|
||||||
abstract class _$AdvancedLocalizationUIModel
|
abstract class _$AdvancedLocalizationUIModel
|
||||||
extends $Notifier<AdvancedLocalizationUIState> {
|
extends $Notifier<AdvancedLocalizationUIState> {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:file_sizes/file_sizes.dart';
|
import 'package:file_sizes/file_sizes.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
@ -58,183 +59,658 @@ class UnP4kcUI extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: Column(
|
: Stack(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Column(
|
||||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .06)),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
height: 36,
|
children: [
|
||||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
Container(
|
||||||
child: SuperListView.builder(
|
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .06)),
|
||||||
itemCount: paths.length - 1,
|
height: 36,
|
||||||
scrollDirection: Axis.horizontal,
|
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
child: Row(
|
||||||
var path = paths[index];
|
|
||||||
if (path.isEmpty) {
|
|
||||||
path = "\\";
|
|
||||||
}
|
|
||||||
final fullPath = "${paths.sublist(0, index + 1).join("\\")}\\";
|
|
||||||
return Row(
|
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
// 搜索模式下显示返回按钮
|
||||||
icon: Text(path),
|
if (state.searchFs != null) ...[
|
||||||
onPressed: () {
|
IconButton(
|
||||||
model.changeDir(fullPath, fullPath: true);
|
icon: const Icon(FluentIcons.back, size: 14),
|
||||||
},
|
onPressed: () {
|
||||||
|
model.clearSearch();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
"[${S.current.tools_unp4k_searching.replaceAll('...', '')}]",
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
Expanded(
|
||||||
|
child: SuperListView.builder(
|
||||||
|
itemCount: paths.length - 1,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
var path = paths[index];
|
||||||
|
if (path.isEmpty) {
|
||||||
|
path = "\\";
|
||||||
|
}
|
||||||
|
final fullPath = "${paths.sublist(0, index + 1).join("\\")}\\";
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Text(path),
|
||||||
|
onPressed: () {
|
||||||
|
model.changeDir(fullPath, fullPath: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Icon(FluentIcons.chevron_right, size: 12),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Icon(FluentIcons.chevron_right, size: 12),
|
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
Expanded(
|
||||||
),
|
child: Row(
|
||||||
Expanded(
|
children: [
|
||||||
child: Row(
|
SizedBox(
|
||||||
children: [
|
width: MediaQuery.of(context).size.width * .3,
|
||||||
Container(
|
child: _FileListPanel(state: state, model: model, files: files),
|
||||||
width: MediaQuery.of(context).size.width * .3,
|
),
|
||||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .01)),
|
Expanded(
|
||||||
child: SuperListView.builder(
|
child: state.tempOpenFile == null
|
||||||
padding: const EdgeInsets.only(top: 6, bottom: 6, left: 3, right: 12),
|
? Center(child: Text(S.current.tools_unp4k_view_file))
|
||||||
itemBuilder: (BuildContext context, int index) {
|
: state.tempOpenFile?.key == "loading"
|
||||||
final item = files![index];
|
? makeLoading(context)
|
||||||
final fileName = item.name?.replaceAll(state.curPath.trim(), "") ?? "?";
|
: Padding(
|
||||||
return Container(
|
padding: const EdgeInsets.all(12),
|
||||||
margin: const EdgeInsets.only(top: 4, bottom: 4),
|
child: Column(
|
||||||
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .05)),
|
children: [
|
||||||
child: IconButton(
|
if (state.tempOpenFile?.key == "text")
|
||||||
onPressed: () {
|
Expanded(child: _TextTempWidget(state.tempOpenFile?.value ?? ""))
|
||||||
if (item.isDirectory ?? false) {
|
else
|
||||||
model.changeDir(fileName);
|
Expanded(
|
||||||
} else {
|
child: Center(
|
||||||
model.openFile(item.name ?? "");
|
child: Column(
|
||||||
}
|
mainAxisSize: MainAxisSize.min,
|
||||||
},
|
|
||||||
icon: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
if (item.isDirectory ?? false)
|
|
||||||
const Icon(FluentIcons.folder_fill, color: Color.fromRGBO(255, 224, 138, 1))
|
|
||||||
else if (fileName.endsWith(".xml"))
|
|
||||||
const Icon(FluentIcons.file_code)
|
|
||||||
else
|
|
||||||
const Icon(FluentIcons.open_file),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
fileName,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (!(item.isDirectory ?? true)) ...[
|
|
||||||
const SizedBox(height: 1),
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
FileSize.getSize(item.size),
|
S.current.tools_unp4k_msg_unknown_file_type(
|
||||||
style: TextStyle(
|
state.tempOpenFile?.value ?? "",
|
||||||
fontSize: 10,
|
|
||||||
color: Colors.white.withValues(alpha: .6),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(height: 32),
|
||||||
Text(
|
FilledButton(
|
||||||
item.dateModified != null
|
child: Padding(
|
||||||
? DateTime.fromMillisecondsSinceEpoch(
|
padding: const EdgeInsets.all(4),
|
||||||
item.dateModified!,
|
child: Text(S.current.action_open_folder),
|
||||||
).toString().substring(0, 19)
|
|
||||||
: "",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Colors.white.withValues(alpha: .6),
|
|
||||||
),
|
),
|
||||||
|
onPressed: () {
|
||||||
|
SystemHelper.openDir(state.tempOpenFile?.value ?? "");
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 3),
|
|
||||||
Icon(
|
|
||||||
FluentIcons.chevron_right,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.white.withValues(alpha: .6),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
itemCount: files?.length ?? 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
child: state.tempOpenFile == null
|
|
||||||
? Center(child: Text(S.current.tools_unp4k_view_file))
|
|
||||||
: state.tempOpenFile?.key == "loading"
|
|
||||||
? makeLoading(context)
|
|
||||||
: Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
if (state.tempOpenFile?.key == "text")
|
|
||||||
Expanded(child: _TextTempWidget(state.tempOpenFile?.value ?? ""))
|
|
||||||
else
|
|
||||||
Expanded(
|
|
||||||
child: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
S.current.tools_unp4k_msg_unknown_file_type(
|
|
||||||
state.tempOpenFile?.value ?? "",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
FilledButton(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
child: Text(S.current.action_open_folder),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
SystemHelper.openDir(state.tempOpenFile?.value ?? "");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
if (state.endMessage != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text("${state.endMessage}", style: const TextStyle(fontSize: 12)),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
if (state.endMessage != null)
|
// 搜索加载遮罩
|
||||||
Padding(
|
if (state.isSearching)
|
||||||
padding: const EdgeInsets.all(8.0),
|
Container(
|
||||||
child: Text("${state.endMessage}", style: const TextStyle(fontSize: 12)),
|
color: Colors.black.withValues(alpha: .7),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const ProgressRing(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(S.current.tools_unp4k_searching, style: const TextStyle(fontSize: 16)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 文件列表面板组件
|
||||||
|
class _FileListPanel extends HookConsumerWidget {
|
||||||
|
final Unp4kcState state;
|
||||||
|
final Unp4kCModel model;
|
||||||
|
final List<AppUnp4kP4kItemData>? files;
|
||||||
|
|
||||||
|
const _FileListPanel({required this.state, required this.model, required this.files});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final searchController = useTextEditingController(text: state.searchQuery);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor.withValues(alpha: .01)),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// 搜索栏和排序选择器
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextBox(
|
||||||
|
controller: searchController,
|
||||||
|
placeholder: S.current.tools_unp4k_search_placeholder,
|
||||||
|
prefix: const Padding(padding: EdgeInsets.only(left: 8), child: Icon(FluentIcons.search, size: 14)),
|
||||||
|
suffix: searchController.text.isNotEmpty || state.searchFs != null
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(FluentIcons.clear, size: 12),
|
||||||
|
onPressed: () {
|
||||||
|
searchController.clear();
|
||||||
|
model.clearSearch();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
onSubmitted: (value) {
|
||||||
|
model.search(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ComboBox<Unp4kSortType>(
|
||||||
|
value: state.sortType,
|
||||||
|
items: [
|
||||||
|
ComboBoxItem(value: Unp4kSortType.defaultSort, child: Text(S.current.tools_unp4k_sort_default)),
|
||||||
|
ComboBoxItem(value: Unp4kSortType.sizeAsc, child: Text(S.current.tools_unp4k_sort_size_asc)),
|
||||||
|
ComboBoxItem(value: Unp4kSortType.sizeDesc, child: Text(S.current.tools_unp4k_sort_size_desc)),
|
||||||
|
ComboBoxItem(value: Unp4kSortType.dateAsc, child: Text(S.current.tools_unp4k_sort_date_asc)),
|
||||||
|
ComboBoxItem(value: Unp4kSortType.dateDesc, child: Text(S.current.tools_unp4k_sort_date_desc)),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
model.setSortType(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 多选模式工具栏
|
||||||
|
if (state.isMultiSelectMode)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).accentColor.withValues(alpha: .1),
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(color: FluentTheme.of(context).accentColor.withValues(alpha: .3), width: 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
S.current.tools_unp4k_action_export_selected(state.selectedItems.length),
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Button(child: Text(S.current.tools_unp4k_action_select_all), onPressed: () => model.selectAll(files)),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Button(
|
||||||
|
child: Text(S.current.tools_unp4k_action_deselect_all),
|
||||||
|
onPressed: () => model.deselectAll(files),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: state.selectedItems.isNotEmpty ? () => _exportSelected(context) : null,
|
||||||
|
child: Text(S.current.tools_unp4k_action_save_as),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(FluentIcons.cancel, size: 14),
|
||||||
|
onPressed: () => model.exitMultiSelectMode(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 文件列表
|
||||||
|
Expanded(
|
||||||
|
child: files == null || files!.isEmpty
|
||||||
|
? Center(
|
||||||
|
child: Text(
|
||||||
|
state.searchFs != null ? S.current.tools_unp4k_search_no_result : '',
|
||||||
|
style: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SuperListView.builder(
|
||||||
|
padding: const EdgeInsets.only(top: 6, bottom: 6, left: 3, right: 12),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final item = files![index];
|
||||||
|
return _FileListItem(item: item, state: state, model: model);
|
||||||
|
},
|
||||||
|
itemCount: files?.length ?? 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _exportSelected(BuildContext context) async {
|
||||||
|
final outputDir = await FilePicker.platform.getDirectoryPath(dialogTitle: S.current.tools_unp4k_action_save_as);
|
||||||
|
if (outputDir != null && context.mounted) {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (dialogContext) {
|
||||||
|
return _MultiExtractProgressDialog(selectedItems: state.selectedItems, outputDir: outputDir, model: model);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// 提取完成后退出多选模式
|
||||||
|
model.exitMultiSelectMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 文件列表项组件
|
||||||
|
class _FileListItem extends HookWidget {
|
||||||
|
final AppUnp4kP4kItemData item;
|
||||||
|
final Unp4kcState state;
|
||||||
|
final Unp4kCModel model;
|
||||||
|
|
||||||
|
const _FileListItem({required this.item, required this.state, required this.model});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final flyoutController = useMemoized(() => FlyoutController());
|
||||||
|
// 显示相对于当前路径的文件名
|
||||||
|
final fileName = item.name?.replaceAll(state.curPath.trim(), "") ?? "?";
|
||||||
|
final itemPath = item.name ?? "";
|
||||||
|
final isSelected = state.selectedItems.contains(itemPath);
|
||||||
|
|
||||||
|
return FlyoutTarget(
|
||||||
|
controller: flyoutController,
|
||||||
|
child: GestureDetector(
|
||||||
|
onSecondaryTapUp: (details) => _showContextMenu(context, flyoutController),
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(top: 4, bottom: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected
|
||||||
|
? FluentTheme.of(context).accentColor.withValues(alpha: .2)
|
||||||
|
: FluentTheme.of(context).cardColor.withValues(alpha: .05),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (state.isMultiSelectMode) {
|
||||||
|
// 多选模式下点击切换选中状态
|
||||||
|
model.toggleSelectItem(itemPath);
|
||||||
|
} else if (item.isDirectory ?? false) {
|
||||||
|
final dirName = item.name?.replaceAll(state.curPath.trim(), "") ?? "";
|
||||||
|
model.changeDir(dirName);
|
||||||
|
} else {
|
||||||
|
model.openFile(item.name ?? "");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 4, right: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 多选模式下显示复选框
|
||||||
|
if (state.isMultiSelectMode) ...[
|
||||||
|
Checkbox(
|
||||||
|
checked: isSelected,
|
||||||
|
onChanged: (value) {
|
||||||
|
model.toggleSelectItem(itemPath);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
if (item.isDirectory ?? false)
|
||||||
|
const Icon(FluentIcons.folder_fill, color: Color.fromRGBO(255, 224, 138, 1))
|
||||||
|
else if (fileName.endsWith(".xml"))
|
||||||
|
const Icon(FluentIcons.file_code)
|
||||||
|
else
|
||||||
|
const Icon(FluentIcons.open_file),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(fileName, style: const TextStyle(fontSize: 13), textAlign: TextAlign.start),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (!(item.isDirectory ?? true)) ...[
|
||||||
|
const SizedBox(height: 1),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
FileSize.getSize(item.size),
|
||||||
|
style: TextStyle(fontSize: 10, color: Colors.white.withValues(alpha: .6)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
item.dateModified != null
|
||||||
|
? DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
item.dateModified!,
|
||||||
|
).toString().substring(0, 19)
|
||||||
|
: "",
|
||||||
|
style: TextStyle(fontSize: 10, color: Colors.white.withValues(alpha: .6)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showContextMenu(BuildContext context, FlyoutController controller) {
|
||||||
|
// 保存外部 context,因为 flyout 的 context 在关闭后会失效
|
||||||
|
final outerContext = context;
|
||||||
|
controller.showFlyout(
|
||||||
|
autoModeConfiguration: FlyoutAutoConfiguration(preferredMode: FlyoutPlacementMode.bottomCenter),
|
||||||
|
barrierColor: Colors.transparent,
|
||||||
|
builder: (flyoutContext) {
|
||||||
|
return MenuFlyout(
|
||||||
|
items: [
|
||||||
|
MenuFlyoutItem(
|
||||||
|
leading: const Icon(FluentIcons.save_as, size: 16),
|
||||||
|
text: Text(S.current.tools_unp4k_action_save_as),
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.of(flyoutContext).pop();
|
||||||
|
await _saveAs(outerContext);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// 多选模式切换
|
||||||
|
if (!state.isMultiSelectMode)
|
||||||
|
MenuFlyoutItem(
|
||||||
|
leading: const Icon(FluentIcons.checkbox_composite, size: 16),
|
||||||
|
text: Text(S.current.tools_unp4k_action_multi_select),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(flyoutContext).pop();
|
||||||
|
model.enterMultiSelectMode();
|
||||||
|
// 自动选中当前项
|
||||||
|
model.toggleSelectItem(item.name ?? "");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveAs(BuildContext context) async {
|
||||||
|
final outputDir = await FilePicker.platform.getDirectoryPath(dialogTitle: S.current.tools_unp4k_action_save_as);
|
||||||
|
if (outputDir != null && context.mounted) {
|
||||||
|
await _showExtractProgressDialog(context, outputDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showExtractProgressDialog(BuildContext context, String outputDir) async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (dialogContext) {
|
||||||
|
return _ExtractProgressDialog(item: item, outputDir: outputDir, model: model);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 提取进度对话框
|
||||||
|
class _ExtractProgressDialog extends HookWidget {
|
||||||
|
final AppUnp4kP4kItemData item;
|
||||||
|
final String outputDir;
|
||||||
|
final Unp4kCModel model;
|
||||||
|
|
||||||
|
const _ExtractProgressDialog({required this.item, required this.outputDir, required this.model});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isCancelled = useState(false);
|
||||||
|
final currentFile = useState("");
|
||||||
|
final currentIndex = useState(0);
|
||||||
|
final totalFiles = useState(0);
|
||||||
|
final isCompleted = useState(false);
|
||||||
|
final errorMessage = useState<String?>(null);
|
||||||
|
final extractedCount = useState(0);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
// 获取文件数量
|
||||||
|
totalFiles.value = model.getFileCountInDirectory(item);
|
||||||
|
|
||||||
|
// 开始提取
|
||||||
|
_startExtraction(isCancelled, currentFile, currentIndex, totalFiles, isCompleted, errorMessage, extractedCount);
|
||||||
|
return null;
|
||||||
|
}, const []);
|
||||||
|
|
||||||
|
final progress = totalFiles.value > 0 ? currentIndex.value / totalFiles.value : 0.0;
|
||||||
|
|
||||||
|
return ContentDialog(
|
||||||
|
title: Text(S.current.tools_unp4k_extract_dialog_title),
|
||||||
|
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 300),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!isCompleted.value && errorMessage.value == null) ...[
|
||||||
|
// 进度条
|
||||||
|
ProgressBar(value: progress * 100),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// 进度文本
|
||||||
|
Text(
|
||||||
|
S.current.tools_unp4k_extract_progress(currentIndex.value, totalFiles.value),
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
// 当前文件
|
||||||
|
Text(
|
||||||
|
S.current.tools_unp4k_extract_current_file(
|
||||||
|
currentFile.value.length > 60
|
||||||
|
? "...${currentFile.value.substring(currentFile.value.length - 60)}"
|
||||||
|
: currentFile.value,
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
] else if (errorMessage.value != null) ...[
|
||||||
|
// 错误信息
|
||||||
|
const Icon(FluentIcons.error_badge, size: 48, color: Color(0xFFE81123)),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(errorMessage.value!, style: const TextStyle(fontSize: 14)),
|
||||||
|
] else ...[
|
||||||
|
// 完成
|
||||||
|
const Icon(FluentIcons.completed_solid, size: 48, color: Color(0xFF107C10)),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(S.current.tools_unp4k_extract_completed(extractedCount.value), style: const TextStyle(fontSize: 14)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
if (!isCompleted.value && errorMessage.value == null)
|
||||||
|
Button(
|
||||||
|
onPressed: () {
|
||||||
|
isCancelled.value = true;
|
||||||
|
},
|
||||||
|
child: Text(S.current.home_action_cancel),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(S.current.action_close)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _startExtraction(
|
||||||
|
ValueNotifier<bool> isCancelled,
|
||||||
|
ValueNotifier<String> currentFile,
|
||||||
|
ValueNotifier<int> currentIndex,
|
||||||
|
ValueNotifier<int> totalFiles,
|
||||||
|
ValueNotifier<bool> isCompleted,
|
||||||
|
ValueNotifier<String?> errorMessage,
|
||||||
|
ValueNotifier<int> extractedCount,
|
||||||
|
) async {
|
||||||
|
final result = await model.extractToDirectoryWithProgress(
|
||||||
|
item,
|
||||||
|
outputDir,
|
||||||
|
onProgress: (current, total, file) {
|
||||||
|
currentIndex.value = current;
|
||||||
|
totalFiles.value = total;
|
||||||
|
currentFile.value = file;
|
||||||
|
},
|
||||||
|
isCancelled: () => isCancelled.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
final (success, count, error) = result;
|
||||||
|
extractedCount.value = count;
|
||||||
|
|
||||||
|
if (!success && error != null) {
|
||||||
|
errorMessage.value = error;
|
||||||
|
} else {
|
||||||
|
isCompleted.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 批量提取进度对话框
|
||||||
|
class _MultiExtractProgressDialog extends HookWidget {
|
||||||
|
final Set<String> selectedItems;
|
||||||
|
final String outputDir;
|
||||||
|
final Unp4kCModel model;
|
||||||
|
|
||||||
|
const _MultiExtractProgressDialog({required this.selectedItems, required this.outputDir, required this.model});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isCancelled = useState(false);
|
||||||
|
final currentFile = useState("");
|
||||||
|
final currentIndex = useState(0);
|
||||||
|
final totalFiles = useState(0);
|
||||||
|
final isCompleted = useState(false);
|
||||||
|
final errorMessage = useState<String?>(null);
|
||||||
|
final extractedCount = useState(0);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
// 获取文件数量
|
||||||
|
totalFiles.value = model.getFileCountForSelectedItems(selectedItems);
|
||||||
|
|
||||||
|
// 开始提取
|
||||||
|
_startExtraction(isCancelled, currentFile, currentIndex, totalFiles, isCompleted, errorMessage, extractedCount);
|
||||||
|
return null;
|
||||||
|
}, const []);
|
||||||
|
|
||||||
|
final progress = totalFiles.value > 0 ? currentIndex.value / totalFiles.value : 0.0;
|
||||||
|
|
||||||
|
return ContentDialog(
|
||||||
|
title: Text(S.current.tools_unp4k_extract_dialog_title),
|
||||||
|
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 300),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!isCompleted.value && errorMessage.value == null) ...[
|
||||||
|
// 进度条
|
||||||
|
ProgressBar(value: progress * 100),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// 进度文本
|
||||||
|
Text(
|
||||||
|
S.current.tools_unp4k_extract_progress(currentIndex.value, totalFiles.value),
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
// 当前文件
|
||||||
|
Text(
|
||||||
|
S.current.tools_unp4k_extract_current_file(
|
||||||
|
currentFile.value.length > 60
|
||||||
|
? "...${currentFile.value.substring(currentFile.value.length - 60)}"
|
||||||
|
: currentFile.value,
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .7)),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
] else if (errorMessage.value != null) ...[
|
||||||
|
// 错误信息
|
||||||
|
const Icon(FluentIcons.error_badge, size: 48, color: Color(0xFFE81123)),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(errorMessage.value!, style: const TextStyle(fontSize: 14)),
|
||||||
|
] else ...[
|
||||||
|
// 完成
|
||||||
|
const Icon(FluentIcons.completed_solid, size: 48, color: Color(0xFF107C10)),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(S.current.tools_unp4k_extract_completed(extractedCount.value), style: const TextStyle(fontSize: 14)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
if (!isCompleted.value && errorMessage.value == null)
|
||||||
|
Button(
|
||||||
|
onPressed: () {
|
||||||
|
isCancelled.value = true;
|
||||||
|
},
|
||||||
|
child: Text(S.current.home_action_cancel),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(S.current.action_close)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _startExtraction(
|
||||||
|
ValueNotifier<bool> isCancelled,
|
||||||
|
ValueNotifier<String> currentFile,
|
||||||
|
ValueNotifier<int> currentIndex,
|
||||||
|
ValueNotifier<int> totalFiles,
|
||||||
|
ValueNotifier<bool> isCompleted,
|
||||||
|
ValueNotifier<String?> errorMessage,
|
||||||
|
ValueNotifier<int> extractedCount,
|
||||||
|
) async {
|
||||||
|
final result = await model.extractSelectedItemsWithProgress(
|
||||||
|
selectedItems,
|
||||||
|
outputDir,
|
||||||
|
onProgress: (current, total, file) {
|
||||||
|
currentIndex.value = current;
|
||||||
|
totalFiles.value = total;
|
||||||
|
currentFile.value = file;
|
||||||
|
},
|
||||||
|
isCancelled: () => isCancelled.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
final (success, count, error) = result;
|
||||||
|
extractedCount.value = count;
|
||||||
|
|
||||||
|
if (!success && error != null) {
|
||||||
|
errorMessage.value = error;
|
||||||
|
} else {
|
||||||
|
isCompleted.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _TextTempWidget extends HookConsumerWidget {
|
class _TextTempWidget extends HookConsumerWidget {
|
||||||
final String filePath;
|
final String filePath;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user