feat: unp4k UI update

This commit is contained in:
xkeyC 2025-12-04 17:43:12 +08:00
parent aaa97429b9
commit 1c4eafccca
13 changed files with 1632 additions and 254 deletions

View File

@ -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",
), ),

View File

@ -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(
"プレビューするファイルをクリック", "プレビューするファイルをクリック",
), ),

View File

@ -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(
"Нажмите на файл для предварительного просмотра", "Нажмите на файл для предварительного просмотра",
), ),

View File

@ -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 快速定位载具。在右侧列表上下拖动可以调整载具的顺序。",

View File

@ -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 快速定位載具。在右側列表上下拖動可以調整載具的順序。",

View File

@ -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> {

View File

@ -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"
}

View File

@ -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": "取消全选"
} }

View File

@ -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);
}

View File

@ -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>,
)); ));
} }

View File

@ -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();

View File

@ -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> {

View File

@ -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;