├── .github ├── ISSUE_TEMPLATE │ └── cn-bug-report.yaml └── workflows │ ├── Release.yml │ ├── mirrorchyan.yml │ └── mirrorchyan_release_note.yml ├── .gitignore ├── App.config ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── Assets ├── Localization │ ├── Strings.Designer.cs │ ├── Strings.en-us.resx │ ├── Strings.resx │ └── Strings.zh-hant.resx ├── Primary │ ├── Magenta.xaml │ └── Primary.xaml ├── Properties │ ├── AutoScroll.cs │ └── ScrollViewerBinding.cs ├── Style.xaml ├── Style │ ├── MainStyle.xaml │ ├── MdXaml.xaml │ └── Styles │ │ ├── Brush.xaml │ │ ├── Color.xaml │ │ └── Geometry.xaml ├── Theme.xaml └── Theme │ ├── DarkTheme.xaml │ └── LightTheme.xaml ├── Configuration ├── ConfigurationHelper.cs ├── ConfigurationKeys.cs ├── GlobalConfiguration.cs ├── MFAConfiguration.cs └── MaaFWConfiguration.cs ├── Custom ├── AnomalyDetectionAction.cs ├── ChooseOperatorAciton.cs ├── ChooseTeamAction.cs ├── FinshAction.cs ├── InputParameterAction.cs ├── LevelRecognitionAction.cs └── SaveMoneyAction.cs ├── DLL ├── msvcp140.dll └── vcruntime140.dll ├── Extensions ├── ComboBoxExtensions.cs ├── MFAExtensions.cs └── Maa │ ├── MaaControllerTypes.cs │ ├── MaaCustomAction.cs │ ├── MaaInterface.cs │ ├── MaaInterfaceAdvancedOption.cs │ └── MaaProcessor.cs ├── Helper ├── AutoInitDictionary.cs ├── BrushConverterHelper.cs ├── Converters │ ├── AutoConverter.cs │ ├── CustomIsEnabledConverter.cs │ ├── ListDoubleStringConverter.cs │ ├── ListIntStringConverter.cs │ ├── ListStringArrayConverter.cs │ ├── ListStringConverter.cs │ ├── MaaInterfaceSelectAdvancedConverter.cs │ ├── MaaInterfaceSelectOptionConverter.cs │ ├── MaaResourceVersionConverter.cs │ ├── NullStringConverter.cs │ ├── NullableDoubleConverter.cs │ ├── NullableIntConverter.cs │ ├── NullableStringConverter.cs │ ├── NullableUIntConverter.cs │ ├── NullableUIntOrObjectConverter.cs │ ├── NullableUIntStringConverter.cs │ ├── ReplaceConverter.cs │ ├── SingleIntListConverter.cs │ ├── SingleIntListOrAutoConverter.cs │ ├── SingleOrDoubleListConverter.cs │ ├── SingleOrIntListConverter.cs │ ├── SingleOrListConverter.cs │ ├── SingleOrNestedListConverter.cs │ ├── StrictPlaceholderConverter.cs │ ├── StringOrBoolOrObjectConverter.cs │ ├── StringOrObjectConverter.cs │ ├── SubtractConverter.cs │ ├── UIntOrObjectConverter.cs │ └── UniversalEnumConverter.cs ├── DeviceWindowTemplateSelector.cs ├── DispatcherHelper.cs ├── DragDropHandler.cs ├── Editor │ ├── ListAutoStringEditor.cs │ ├── ListDoubleStringEditor.cs │ ├── ListIntStringEditor.cs │ ├── ListStringArrayEditor.cs │ ├── ListStringEditor.cs │ ├── NullableDoubleEditor.cs │ ├── NullableIntEditor.cs │ ├── NullableStringEditor.cs │ ├── NullableUIntEditor.cs │ ├── NullableUIntOrObjectEditor.cs │ ├── NullableUIntStringEditor.cs │ ├── SingleIntListEditor.cs │ ├── SingleIntListOrAutoEditor.cs │ └── StringComboBoxEditor.cs ├── EmulatorHelper.cs ├── Exceptions │ ├── MaaErrorHandleException.cs │ └── MaaStopException.cs ├── ExternalNotificationHelper.cs ├── GrowlHelper.cs ├── HotKeyHelper.cs ├── IconHelper.cs ├── Instances.cs ├── JsonHelper.cs ├── LanguageHelper.cs ├── LoggerService.cs ├── MFAUrls.cs ├── MessageBoxHelper.cs ├── Notification │ ├── INotificationPoster.cs │ ├── NotificationAction.cs │ ├── NotificationContent.cs │ ├── NotificationHint.cs │ ├── NotificationImplWinRT.cs │ └── NotificationImplWpf.cs ├── OCRHelper.cs ├── ServiceProviderExtension.cs ├── SimpleEncryptionHelper.cs ├── TaskManager.cs ├── ThemeHelper.cs ├── ToastNotification.cs ├── ValueType │ ├── Attribute.cs │ ├── CustomValue.cs │ ├── MFAHotKey.cs │ ├── MFATask.cs │ ├── ObservableQueue.cs │ ├── TaskInterfaceItem.cs │ ├── TaskModel.cs │ └── ValueBoxes.cs └── VersionChecker.cs ├── LICENSE ├── MFAWPF.csproj ├── MFAWPF.sln ├── MFAWPF.sln.DotSettings.user ├── NuGet.Config ├── Properties └── launchSettings.json ├── README.md ├── README_en.md ├── Resource ├── base │ └── model │ │ └── ocr │ │ ├── README.md │ │ ├── det.onnx │ │ ├── keys.txt │ │ └── rec.onnx └── pipeline.schema.json ├── Services └── ApplicationHostService.cs ├── ViewModels ├── Tool │ ├── DragItemViewModel.cs │ ├── LocalizationViewModel.cs │ ├── LogItemViewModel.cs │ └── TaskItemViewModel.cs ├── UI │ ├── AnnouncementViewModel.cs │ ├── ConnectingViewModel.cs │ ├── Dialog │ │ ├── AddTaskDialogViewModel.cs │ │ └── EditTaskDialogViewModel.cs │ ├── RootViewModel.cs │ ├── SettingsViewModel.cs │ └── TaskQueueViewModel.cs ├── UserControl │ └── Settings │ │ ├── ConnectSettingsUserControlModel.cs │ │ ├── ExternalNotificationSettingsUserControlModel.cs │ │ ├── GameSettingsUserControlModel.cs │ │ ├── GuiSettingsUserControlModel.cs │ │ ├── PerformanceUserControlModel.cs │ │ ├── StartSettingsUserControlModel.cs │ │ ├── TimerSettingsUserControlModel.cs │ │ └── VersionUpdateSettingsUserControlModel.cs └── ViewModel.cs ├── Views ├── UI │ ├── AnnouncementView.xaml │ ├── AnnouncementView.xaml.cs │ ├── ConnectingView.xaml │ ├── ConnectingView.xaml.cs │ ├── Dialog │ │ ├── AdbEditorDialog.xaml │ │ ├── AdbEditorDialog.xaml.cs │ │ ├── AddTaskDialog.xaml │ │ ├── AddTaskDialog.xaml.cs │ │ ├── ColorExtractionDialog.xaml │ │ ├── ColorExtractionDialog.xaml.cs │ │ ├── CropImageDialog.xaml │ │ ├── CropImageDialog.xaml.cs │ │ ├── DownloadDialog.xaml │ │ ├── DownloadDialog.xaml.cs │ │ ├── EditTaskDialog.xaml │ │ ├── EditTaskDialog.xaml.cs │ │ ├── RecognitionTextDialog.xaml │ │ ├── RecognitionTextDialog.xaml.cs │ │ ├── SelectionRegionDialog.xaml │ │ ├── SelectionRegionDialog.xaml.cs │ │ ├── SwipeDialog.xaml │ │ ├── SwipeDialog.xaml.cs │ │ ├── TaskFlowChartDialog.xaml │ │ └── TaskFlowChartDialog.xaml.cs │ ├── ErrorView.xaml │ ├── ErrorView.xaml.cs │ ├── NotifyIcon.xaml │ ├── NotifyIcon.xaml.cs │ ├── RootView.xaml │ ├── RootView.xaml.cs │ ├── SettingsView.xaml │ ├── SettingsView.xaml.cs │ ├── TaskQueueView.xaml │ └── TaskQueueView.xaml.cs └── UserControl │ ├── AttributeButton.cs │ ├── AttributeTag.cs │ ├── CardControl.xaml │ ├── CardControl.xaml.cs │ ├── CustomAutoCompleteTextBox.cs │ ├── CustomAutoListControl.xaml │ ├── CustomAutoListControl.xaml.cs │ ├── CustomListControl.xaml │ ├── CustomListControl.xaml.cs │ ├── CustomTextBlock.cs │ ├── CustomWindow.cs │ ├── HotKeyEditorUserControl.xaml │ ├── HotKeyEditorUserControl.xaml.cs │ ├── PinButton.cs │ └── Settings │ ├── AboutUserControl.xaml │ ├── AboutUserControl.xaml.cs │ ├── ConfigurationMgrUserControl.xaml │ ├── ConfigurationMgrUserControl.xaml.cs │ ├── ConnectSettingsUserControl.xaml │ ├── ConnectSettingsUserControl.xaml.cs │ ├── ExternalNotificationSettingsUserControl.xaml │ ├── ExternalNotificationSettingsUserControl.xaml.cs │ ├── GameSettingsUserControl.xaml │ ├── GameSettingsUserControl.xaml.cs │ ├── GuiSettingsUserControl.xaml │ ├── GuiSettingsUserControl.xaml.cs │ ├── HotKeySettingsUserControl.xaml │ ├── HotKeySettingsUserControl.xaml.cs │ ├── PerformanceUserControl.xaml │ ├── PerformanceUserControl.xaml.cs │ ├── StartSettingsUserControl.xaml │ ├── StartSettingsUserControl.xaml.cs │ ├── TaskOptionSettingsUserControl.xaml │ ├── TaskOptionSettingsUserControl.xaml.cs │ ├── TaskQueueSettingsUserControl.xaml │ ├── TaskQueueSettingsUserControl.xaml.cs │ ├── TimerSettingsUserControl.xaml │ ├── TimerSettingsUserControl.xaml.cs │ ├── VersionUpdateSettingsUserControl.xaml │ └── VersionUpdateSettingsUserControl.xaml.cs ├── cliff.toml ├── docs ├── en_us │ └── CustomRecognition_Action.md └── zh_cn │ └── 自定义识别_操作.md ├── logo.ico ├── logo.png └── workflows └── install.yml /.github/ISSUE_TEMPLATE/cn-bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug 反馈(使用中文) 2 | description: 运行错误、操作异常、连接错误等 3 | labels: ["bug"] 4 | body: 5 | - type: checkboxes 6 | id: checks 7 | attributes: 8 | label: 在提问之前... 9 | description: | 10 | 请确认自己完成了要求之后再进行勾选 11 | options: 12 | - label: 我知道这个项目是独立的通用GUI项目,在这里反馈资源问题(如M9A)并不正确 13 | required: true 14 | - label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决 15 | required: true 16 | - label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等 17 | required: true 18 | - label: 我使用的是当前更新版本的最新版,且已查看版本发布至今和 Pull Requests 中尚未发布的更新内容,并未提及该 Bug 已被修复的情况 19 | required: true 20 | - label: 我已检查了置顶议题(Pinned Issue)(公告)、活跃议题(Open Issue)、已关闭议题(Closed Issue),确认我的问题未被提及 21 | required: true 22 | - type: textarea 23 | id: describe 24 | attributes: 25 | label: 问题描述 26 | description: 描述问题时请尽可能详细 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: version 31 | attributes: 32 | label: Version 33 | description: >- 34 | 请提供 MFA 版本。可在 MFA -> 设置 -> 软件更新中找到。 35 | placeholder: | 36 | MFA Version: 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: logs 41 | attributes: 42 | label: 日志和配置文件 43 | description: | 44 | **请在关闭 MFA 后,上传以下 5 个文件:** 45 | 1. `debug` 文件夹中的 `maa.log` 日志文件,并说明问题出现的大致时间点 46 | 2. `log` 文件夹中的最新log 47 | 48 | 如果你在使用 MacBook,请点击屏幕左上角的“文件”,点击“打开日志文件夹” 49 | **请直接将完整的文件拖拽进来,而非自己裁切或复制的片段;若文件体积过大可压缩后再上传** 50 | placeholder: | 51 | 请确认上传文件前已关闭 MFA 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: configuration 56 | attributes: 57 | label: 配置信息 58 | description: | 59 | 请说明操作系统及版本、模拟器品牌、模拟器分辨率、DPI、帧率; 60 | 最后请说明 GPU 加速推理是否开启,若开启请提供 GPU 型号。 61 | validations: 62 | required: true 63 | - type: textarea 64 | id: screenshots 65 | attributes: 66 | label: 截图或录屏 67 | description: | 68 | 可上传屏幕截图或录制以帮助解释你的问题,包括但不限于 MFA 软件截图、游戏画面截图 69 | 若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图) 70 | 用其他的工具(如QQ/微信)截取的图片包含窗口边框且长宽比、分辨率不固定,不利于我们排除bug 71 | 若文件体积过大可压缩后再上传 72 | validations: 73 | required: false 74 | - type: textarea 75 | id: others 76 | attributes: 77 | label: 还有别的吗? 78 | description: | 79 | 任何能让我们对你所遇到的问题有更多了解的东西 80 | validations: 81 | required: false 82 | -------------------------------------------------------------------------------- /.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # 匹配以'v'开头的tag 7 | 8 | jobs: 9 | changelog: 10 | name: Generate changelog 11 | runs-on: ubuntu-latest 12 | outputs: 13 | release_body: ${{ steps.git-cliff.outputs.content }} 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Generate a changelog 21 | uses: orhun/git-cliff-action@v4 22 | id: git-cliff 23 | with: 24 | config: cliff.toml 25 | args: -vv --latest --strip header 26 | env: 27 | OUTPUT: CHANGES.md 28 | GITHUB_REPO: ${{ github.repository }} 29 | 30 | build: 31 | runs-on: ubuntu-latest 32 | name: Upload the release 33 | needs: changelog 34 | steps: 35 | - name: Checkout code 36 | uses: actions/checkout@v4 37 | with: 38 | fetch-depth: 0 # 获取完整历史记录以访问所有tags 39 | 40 | - name: .NET Core 41 | uses: actions/setup-dotnet@v1 42 | with: 43 | dotnet-version: 8.0.x 44 | 45 | - name: Install dependencies 46 | run: dotnet restore 47 | 48 | - name: Build 49 | run: dotnet publish -c Release 50 | 51 | - name: Zip 52 | run: | 53 | cd /home/runner/work/MFAWPF/bin/AnyCPU/Release/win-x64/publish/ 54 | zip -r ../MFAWPF-${{ github.ref_name }}.zip * 55 | 56 | - name: Release 57 | uses: softprops/action-gh-release@v1 58 | with: 59 | tag_name: ${{ github.ref_name }} 60 | files: | 61 | /home/runner/work/MFAWPF/bin/AnyCPU/Release/win-x64/*.zip 62 | body: ${{ needs.changelog.outputs.release_body }} 63 | draft: false 64 | prerelease: false 65 | 66 | - name: Trigger MirrorChyan 67 | run: | 68 | gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan 69 | gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan_release_note 70 | env: 71 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | -------------------------------------------------------------------------------- /.github/workflows/mirrorchyan.yml: -------------------------------------------------------------------------------- 1 | name: mirrorchyan 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | mirrorchyan: 8 | runs-on: macos-latest 9 | strategy: 10 | matrix: 11 | os: [win] 12 | arch: [x86_64] 13 | 14 | steps: 15 | - id: uploading 16 | uses: MirrorChyan/uploading-action@v1 17 | with: 18 | filetype: latest-release 19 | filename: "MFA*.zip" 20 | mirrorchyan_rid: MFAWPF 21 | 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | owner: ${{ github.repository_owner }} 24 | repo: ${{ github.event.repository.name }} 25 | upload_token: ${{ secrets.MirrorChyanUploadToken }} 26 | os: ${{ matrix.os }} 27 | arch: ${{ matrix.arch }} 28 | -------------------------------------------------------------------------------- /.github/workflows/mirrorchyan_release_note.yml: -------------------------------------------------------------------------------- 1 | name: mirrorchyan_release_note 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [edited] 7 | 8 | jobs: 9 | mirrorchyan: 10 | runs-on: macos-latest 11 | 12 | steps: 13 | - id: uploading 14 | uses: MirrorChyan/release-note-action@v1 15 | with: 16 | mirrorchyan_rid: MFAWPF 17 | 18 | upload_token: ${{ secrets.MirrorChyanUploadToken }} 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | [Bb]in/ 15 | [Oo]bj/ 16 | !Resource/pipeline.schema.json 17 | !Resource/controller_config.json 18 | !Resource/MaaAgentBinary/ 19 | 20 | # MSTest test Results 21 | [Tt]est[Rr]esult*/ 22 | [Bb]uild[Ll]og.* 23 | 24 | *_i.c 25 | *_p.c 26 | *_i.h 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.svclog 48 | *.scc 49 | 50 | # Visual C++ cache files 51 | ipch/ 52 | *.aps 53 | *.ncb 54 | *.opensdf 55 | *.sdf 56 | *.cachefile 57 | 58 | # Visual Studio profiler 59 | *.psess 60 | *.vsp 61 | *.vspx 62 | 63 | # Guidance Automation Toolkit 64 | *.gpState 65 | 66 | # ReSharper is a .NET coding add-in 67 | _ReSharper*/ 68 | *.[Rr]e[Ss]harper 69 | *.DotSettings.user 70 | 71 | # Click-Once directory 72 | publish/ 73 | 74 | # Publish Web Output 75 | *.Publish.xml 76 | *.pubxml 77 | *.azurePubxml 78 | 79 | # NuGet Packages Directory 80 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 81 | packages/ 82 | ## TODO: If the tool you use requires repositories.config, also uncomment the next line 83 | !packages/repositories.config 84 | 85 | # Windows Azure Build Output 86 | csx/ 87 | *.build.csdef 88 | 89 | # Windows Store app package directory 90 | AppPackages/ 91 | 92 | # Others 93 | sql/ 94 | *.Cache 95 | ClientBin/ 96 | [Ss]tyle[Cc]op.* 97 | ![Ss]tyle[Cc]op.targets 98 | ~$* 99 | *~ 100 | *.dbmdl 101 | *.[Pp]ublish.xml 102 | 103 | *.publishsettings 104 | 105 | # RIA/Silverlight projects 106 | Generated_Code/ 107 | 108 | # Backup & report files from converting an old project file to a newer 109 | # Visual Studio version. Backup files are not needed, because we have git ;-) 110 | _UpgradeReport_Files/ 111 | Backup*/ 112 | UpgradeLog*.XML 113 | UpgradeLog*.htm 114 | 115 | # SQL Server files 116 | App_Data/*.mdf 117 | App_Data/*.ldf 118 | 119 | # ========================= 120 | # Windows detritus 121 | # ========================= 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | ehthumbs.db 126 | .vs 127 | 128 | # Folder config file 129 | Desktop.ini 130 | 131 | # Recycle Bin used on file shares 132 | $RECYCLE.BIN/ 133 | 134 | # Mac desktop service store files 135 | .DS_Store 136 | 137 | _NCrunch* 138 | 139 | 140 | # 钉钉的token和secret 141 | auth.txt -------------------------------------------------------------------------------- /App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /App.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] -------------------------------------------------------------------------------- /Assets/Primary/Magenta.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | #FFD80073 5 | #FFD80073 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | #FFD80073 18 | #FFD80073 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assets/Primary/Primary.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | #FF326CF3 5 | #FF326CF3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | #FF326CF3 18 | #FF326CF3 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Assets/Properties/AutoScroll.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace MFAWPF.Styles.Properties 6 | { 7 | 8 | public static class AutoScroll 9 | { 10 | private static bool _autoScroll; 11 | 12 | public static bool GetAutoScroll(DependencyObject obj) 13 | { 14 | return (bool)obj.GetValue(AutoScrollProperty); 15 | } 16 | 17 | public static void SetAutoScroll(DependencyObject obj, bool value) 18 | { 19 | obj.SetValue(AutoScrollProperty, value); 20 | } 21 | 22 | public static readonly DependencyProperty AutoScrollProperty = 23 | DependencyProperty.RegisterAttached("AutoScroll", typeof(bool), typeof(AutoScroll), new PropertyMetadata(false, AutoScrollPropertyChanged)); 24 | 25 | private static void AutoScrollPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 26 | { 27 | if (d is ScrollViewer scrollViewer) 28 | { 29 | bool alwaysScrollToEnd = (e.NewValue != null) && (bool)e.NewValue; 30 | if (alwaysScrollToEnd) 31 | { 32 | scrollViewer.ScrollToEnd(); 33 | scrollViewer.ScrollChanged += ScrollChanged; 34 | } 35 | else 36 | { 37 | scrollViewer.ScrollChanged -= ScrollChanged; 38 | } 39 | } 40 | else if (d is ListBox listBox) 41 | { 42 | INotifyCollectionChanged view = listBox.Items; 43 | view.CollectionChanged += (_, arg) => 44 | { 45 | switch (arg.Action) 46 | { 47 | case NotifyCollectionChangedAction.Add: 48 | listBox.ScrollIntoView(listBox.Items[arg.NewStartingIndex]); 49 | break; 50 | } 51 | }; 52 | } 53 | else 54 | { 55 | throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); 56 | } 57 | } 58 | 59 | private static void ScrollChanged(object sender, ScrollChangedEventArgs e) 60 | { 61 | if (sender is not ScrollViewer scroll) 62 | { 63 | throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); 64 | } 65 | 66 | if (e.ExtentHeightChange == 0) 67 | { 68 | _autoScroll = Math.Abs(scroll.VerticalOffset - scroll.ScrollableHeight) < 1e-6; 69 | } 70 | 71 | if (_autoScroll && e.ExtentHeightChange != 0) 72 | { 73 | scroll.ScrollToVerticalOffset(scroll.ExtentHeight); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Assets/Style.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Assets/Style/Styles/Brush.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Assets/Style/Styles/Color.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | #FFDB3340 5 | #FFDB3340 6 | 7 | #FFE9AF20 8 | #FFE9AF20 9 | 10 | #FF00BCD4 11 | #FF00BCD4 12 | 13 | #FF2DB84d 14 | #FF2DB84d 15 | 16 | #FFD80073 17 | #FF9F0055 18 | #FFBB33FF 19 | #FF7D00BC 20 | 21 | -------------------------------------------------------------------------------- /Assets/Theme.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Configuration/GlobalConfiguration.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper; 2 | using System.Configuration; 3 | 4 | 5 | namespace MFAWPF.Configuration; 6 | 7 | public static class GlobalConfiguration 8 | { 9 | private static System.Configuration.Configuration GetConfigurationInstance() 10 | { 11 | return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); 12 | } 13 | 14 | public static void SetValue(string key, string value) 15 | { 16 | var config = GetConfigurationInstance(); 17 | if (config.AppSettings.Settings[key] == null) 18 | { 19 | config.AppSettings.Settings.Add(key, value); 20 | } 21 | else 22 | { 23 | config.AppSettings.Settings[key].Value = value; 24 | } 25 | 26 | try 27 | { 28 | config.Save(ConfigurationSaveMode.Modified); 29 | ConfigurationManager.RefreshSection("appSettings"); 30 | } 31 | catch (ConfigurationErrorsException ex) 32 | { 33 | LoggerService.LogError(new InvalidOperationException("Failed to save configuration.", ex)); 34 | } 35 | } 36 | 37 | public static string GetValue(string key, string defaultValue = "") 38 | { 39 | var config = GetConfigurationInstance(); 40 | return config.AppSettings.Settings[key]?.Value ?? defaultValue; 41 | } 42 | 43 | public static string GetTimer(int i, string defaultValue) 44 | { 45 | return GetValue($"Timer.Timer{i + 1}", defaultValue); 46 | } 47 | 48 | public static void SetTimer(int i, string value) 49 | { 50 | SetValue($"Timer.Timer{i + 1}", value); 51 | } 52 | 53 | public static string GetTimerHour(int i, string defaultValue) 54 | { 55 | 56 | return GetValue($"Timer.Timer{i + 1}Hour", defaultValue); 57 | } 58 | 59 | public static void SetTimerHour(int i, string value) 60 | { 61 | SetValue($"Timer.Timer{i + 1}Hour", value); 62 | } 63 | 64 | public static string GetTimerMin(int i, string defaultValue) 65 | { 66 | return GetValue($"Timer.Timer{i + 1}Min", defaultValue); 67 | } 68 | 69 | public static void SetTimerMin(int i, string value) 70 | { 71 | SetValue($"Timer.Timer{i + 1}Min", value); 72 | } 73 | 74 | public static string GetTimerConfig(int i, string defaultValue) 75 | { 76 | return GetValue($"Timer.Timer{i + 1}.Config", defaultValue); 77 | } 78 | 79 | public static void SetTimerConfig(int i, string value) 80 | { 81 | SetValue($"Timer.Timer{i + 1}.Config", value); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Configuration/MFAConfiguration.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using MFAWPF.Helper; 4 | 5 | 6 | namespace MFAWPF.Configuration; 7 | 8 | public partial class MFAConfiguration : ObservableObject 9 | { 10 | [ObservableProperty] public string _name; 11 | [ObservableProperty] public string _fileName; 12 | [ObservableProperty] public Dictionary _config; 13 | 14 | 15 | [RelayCommand] 16 | public void DeleteConfiguration(MFAConfiguration configuration) 17 | { 18 | var configName = configuration.Name; 19 | if (!ConfigurationHelper.Configs.Any(c => c.Name.Equals(configName, StringComparison.OrdinalIgnoreCase))) 20 | { 21 | LoggerService.LogError($"Configuration {configName} does not exist"); 22 | return; 23 | } 24 | 25 | if (ConfigurationHelper.ConfigName == configName) 26 | { 27 | LoggerService.LogError($"Configuration {configName} is current configuration, cannot delete"); 28 | return; 29 | } 30 | Instances.SettingsViewModel.ConfigurationList.Remove(configuration); 31 | ConfigurationHelper.DeleteConfig(configName); 32 | } 33 | 34 | public override string ToString() 35 | => Name; 36 | } 37 | -------------------------------------------------------------------------------- /Configuration/MaaFWConfiguration.cs: -------------------------------------------------------------------------------- 1 | using MaaFramework.Binding; 2 | using MFAWPF.Extensions.Maa; 3 | using MFAWPF.Helper; 4 | 5 | namespace MFAWPF.Configuration; 6 | 7 | public class MaaFWConfiguration 8 | { 9 | public AdbDeviceCoreConfig AdbDevice { get; set; } = new(); 10 | public DesktopWindowCoreConfig DesktopWindow { get; set; } = new(); 11 | 12 | } 13 | 14 | public class DesktopWindowCoreConfig 15 | { 16 | public string Name { get; set; } = string.Empty; 17 | public nint HWnd { get; set; } 18 | 19 | public Win32InputMethod Input { get; set; } = Win32InputMethod.SendMessage; 20 | 21 | public Win32ScreencapMethod ScreenCap { get; set; } = Win32ScreencapMethod.FramePool; 22 | public LinkOption Link { get; set; } = LinkOption.Start; 23 | public CheckStatusOption Check { get; set; } = CheckStatusOption.ThrowIfNotSucceeded; 24 | } 25 | 26 | public class AdbDeviceCoreConfig 27 | { 28 | public string Name { get; set; } = string.Empty; 29 | public string AdbPath { get; set; } = "adb"; 30 | public string AdbSerial { get; set; } = ""; 31 | public string Config { get; set; } = "{}"; 32 | public AdbInputMethods Input { get; set; } = AdbInputMethods.Maatouch; 33 | public AdbScreencapMethods ScreenCap { get; set; } = AdbScreencapMethods.Default; 34 | } -------------------------------------------------------------------------------- /Custom/ChooseOperatorAciton.cs: -------------------------------------------------------------------------------- 1 | using MaaFramework.Binding; 2 | using MaaFramework.Binding.Custom; 3 | using MFAWPF.Extensions; 4 | using MFAWPF.Extensions.Maa; 5 | using MFAWPF.Helper; 6 | using MFAWPF.ViewModels; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System; 12 | using System.Runtime; 13 | 14 | namespace MFAWPF.Custom; 15 | 16 | public class ChooseOperatorAciton : MaaCustomAction 17 | { 18 | public override string Name { get; set; } = nameof(ChooseOperatorAciton); 19 | 20 | 21 | protected override void ErrorHandle(IMaaContext context, RunArgs args) 22 | { 23 | context.OverrideNext(args.NodeName, ["启动检测"]); 24 | } 25 | 26 | protected override bool Execute(IMaaContext context, RunArgs args) 27 | { 28 | int i = 0; 29 | context.OverrideNext(args.NodeName, []); 30 | var choosing = () => 31 | { 32 | var image = context.GetImage(); 33 | RecognitionDetail detail; 34 | if (context.TemplateMatch("Sarkaz@Roguelike@EnterAfterRecruit.png", image, out detail, 0.7, 1050, 226, 220, 250)) 35 | { 36 | context.Click(detail.HitBox.X, detail.HitBox.Y); 37 | var enterMap = () => 38 | { 39 | context.GetImage(ref image); 40 | if (context.TemplateMatch("rougelikeInfo.png", image, out detail, 0.8, 53, 1, 178, 64)) 41 | { 42 | return true; 43 | } 44 | context.Click(1142, 369); 45 | return false; 46 | }; 47 | enterMap.Until(); 48 | return true; 49 | } 50 | bool catchO = false; 51 | if (i++ == 0) 52 | { 53 | if (args.RecognitionDetail.HitBox != null) 54 | { 55 | context.Click(args.RecognitionDetail.HitBox.X, args.RecognitionDetail.HitBox.Y); 56 | catchO = true; 57 | } 58 | } 59 | else 60 | { 61 | if (context.TemplateMatch("Sarkaz@Roguelike@Recruit.png", image, out detail, 0.7, 198, 468, 951, 84)) 62 | { 63 | context.Click(detail.HitBox.X, detail.HitBox.Y); 64 | catchO = true; 65 | } 66 | } 67 | 68 | var abandonOp = () => 69 | { 70 | context.GetImage(ref image); 71 | if (context.TemplateMatch("abandon.png", image, out detail, 0.8, 912, 654, 120, 50)) 72 | { 73 | context.Click(detail.HitBox.X, detail.HitBox.Y); 74 | return true; 75 | } 76 | return false; 77 | }; 78 | var confirmAbandon = () => 79 | { 80 | context.GetImage(ref image); 81 | if (context.TemplateMatch("Sarkaz@Roguelike@StageTraderRefreshConfirm.png", image, out detail, 0.8, 640, 414, 504, 154)) 82 | { 83 | context.Click(detail.HitBox.X, detail.HitBox.Y); 84 | return true; 85 | } 86 | return false; 87 | }; 88 | 89 | if (catchO) 90 | { 91 | abandonOp.Until(); 92 | confirmAbandon.Until(); 93 | } 94 | return false; 95 | }; 96 | choosing.Until(); 97 | context.OverrideNext(args.NodeName, ["关卡检测"]); 98 | return true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Custom/FinshAction.cs: -------------------------------------------------------------------------------- 1 | using MaaFramework.Binding; 2 | using MaaFramework.Binding.Custom; 3 | using MFAWPF.Extensions; 4 | using MFAWPF.Extensions.Maa; 5 | using MFAWPF.Helper; 6 | using MFAWPF.ViewModels; 7 | using MFAWPF.Views; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | 13 | namespace MFAWPF.Custom; 14 | 15 | public class FinshAction : MaaCustomAction 16 | { 17 | public override string Name { get; set; } = nameof(FinshAction); 18 | 19 | 20 | protected override void ErrorHandle(IMaaContext context, RunArgs args) 21 | { 22 | context.OverrideNext(args.NodeName, ["启动检测"]); 23 | } 24 | 25 | protected override bool Execute(IMaaContext context, RunArgs args) 26 | { 27 | Thread.Sleep(1000); 28 | var confirm = () => 29 | { 30 | var image = context.GetImage(); 31 | if (context.TemplateMatch("money.png", image, out _, 0.92, 0, 429, 78, 72)) 32 | { 33 | return true; 34 | } 35 | context.Click(631, 610); 36 | return false; 37 | }; 38 | confirm.Until(150); 39 | context.OverrideNext(args.NodeName, ["启动检测"]); 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Custom/InputParameterAction.cs: -------------------------------------------------------------------------------- 1 | using MaaFramework.Binding; 2 | using MaaFramework.Binding.Custom; 3 | using MFAWPF.Extensions; 4 | using MFAWPF.Extensions.Maa; 5 | using MFAWPF.Helper; 6 | using MFAWPF.ViewModels; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | 12 | namespace MFAWPF.Custom; 13 | 14 | public class InputParameterAction : MaaCustomAction 15 | { 16 | public override string Name { get; set; } = nameof(InputParameterAction); 17 | 18 | 19 | protected override void ErrorHandle(IMaaContext context, RunArgs args) 20 | { 21 | context.OverrideNext(args.NodeName, ["启动检测"]); 22 | } 23 | 24 | protected override bool Execute(IMaaContext context, RunArgs args) 25 | { 26 | var shouldRun = true; 27 | var enter1 = () => 28 | { 29 | if (shouldRun) 30 | return true; 31 | var image = context.GetImage(); 32 | RecognitionDetail detail; 33 | if (context.OCR("启用参数", image, out detail, 847, 570, 98, 28)) 34 | { 35 | context.Click(detail.HitBox.X, detail.HitBox.Y); 36 | return true; 37 | } 38 | if (context.OCR("参数已启用", image, out detail, 852, 569, 104, 29)) 39 | { 40 | shouldRun = true; 41 | return true; 42 | } 43 | return false; 44 | }; 45 | enter1.Until(); 46 | 47 | if (shouldRun) 48 | { 49 | context.OverrideNext(args.NodeName, ["开始探险"]); 50 | return true; 51 | } 52 | var enter2 = () => 53 | { 54 | var image = context.GetImage(); 55 | if (context.TemplateMatch("fh2.png", image, out var detail, 0.8, 185, 124, 72, 71)) 56 | { 57 | context.Click(701, 226); 58 | return true; 59 | } 60 | return false; 61 | }; 62 | enter2.Until(); 63 | var enter3 = () => 64 | { 65 | var image = context.GetImage(); 66 | if (context.TemplateMatch("editconfirm.png", image, out var detail, 0.8, 686, 399, 562, 171)) 67 | { 68 | context.Click(258, 332); 69 | return true; 70 | } 71 | return false; 72 | }; 73 | enter3.Until(); 74 | 75 | 76 | Thread.Sleep(150); 77 | var confirm = () => 78 | { 79 | var image = context.GetImage(); 80 | if (context.TemplateMatch("editconfirm.png", image, out var detail, 0.8, 693, 379, 551, 218)) 81 | { 82 | context.InputText("[U1eIA1I29844Ae925i,rogue_4,6]"); 83 | Thread.Sleep(300); 84 | context.Click(detail.HitBox.X, detail.HitBox.Y); 85 | Thread.Sleep(600); 86 | context.Click(detail.HitBox.X, detail.HitBox.Y); 87 | return true; 88 | } 89 | return false; 90 | }; 91 | confirm.Until(); 92 | var leaving = () => 93 | { 94 | var image = context.GetImage(); 95 | if (context.TemplateMatch("fh2.png", image, out var detail, 0.8, 185, 124, 72, 71)) 96 | { 97 | context.Click(966, 322); 98 | Thread.Sleep(700); 99 | context.Click(1167, 338); 100 | return true; 101 | } 102 | return false; 103 | }; 104 | leaving.Until(); 105 | context.OverrideNext(args.NodeName, ["开始探险"]); 106 | return true; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /DLL/msvcp140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAWPF/12e65484cf483e7bb68aca3e8e749406c52398c8/DLL/msvcp140.dll -------------------------------------------------------------------------------- /DLL/vcruntime140.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAWPF/12e65484cf483e7bb68aca3e8e749406c52398c8/DLL/vcruntime140.dll -------------------------------------------------------------------------------- /Extensions/ComboBoxExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Input; 4 | 5 | namespace MFAWPF.Extensions; 6 | 7 | public static class ComboBoxExtensions 8 | { 9 | public static readonly DependencyProperty DisableNavigationOnLostFocusProperty = 10 | DependencyProperty.RegisterAttached( 11 | "DisableNavigationOnLostFocus", 12 | typeof(bool), 13 | typeof(ComboBoxExtensions), 14 | new PropertyMetadata(false, OnDisableNavigationOnLostFocusChanged)); 15 | 16 | public static bool GetDisableNavigationOnLostFocus(DependencyObject obj) => 17 | (bool)obj.GetValue(DisableNavigationOnLostFocusProperty); 18 | 19 | public static void SetDisableNavigationOnLostFocus(DependencyObject obj, bool value) => 20 | obj.SetValue(DisableNavigationOnLostFocusProperty, value); 21 | 22 | private static void OnDisableNavigationOnLostFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 23 | { 24 | if (d is ComboBox comboBox) 25 | { 26 | comboBox.PreviewKeyDown -= HandlePreviewKeyDown; 27 | comboBox.PreviewMouseWheel -= HandlePreviewMouseWheel; 28 | 29 | if ((bool)e.NewValue) 30 | { 31 | comboBox.PreviewKeyDown += HandlePreviewKeyDown; 32 | comboBox.PreviewMouseWheel += HandlePreviewMouseWheel; 33 | } 34 | } 35 | } 36 | 37 | private static bool IsInputAllowed(ComboBox comboBox) => 38 | comboBox.IsDropDownOpen; 39 | 40 | private static void HandlePreviewKeyDown(object sender, KeyEventArgs e) 41 | { 42 | var comboBox = (ComboBox)sender; 43 | if (!IsInputAllowed(comboBox) && (e.Key == Key.Up || e.Key == Key.Down)) 44 | { 45 | e.Handled = true; 46 | } 47 | } 48 | 49 | private static void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e) 50 | { 51 | var comboBox = (ComboBox)sender; 52 | if (!IsInputAllowed(comboBox)) 53 | { 54 | e.Handled = true; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Extensions/Maa/MaaControllerTypes.cs: -------------------------------------------------------------------------------- 1 | namespace MFAWPF.Extensions.Maa; 2 | 3 | public enum MaaControllerTypes 4 | { 5 | None = 0, 6 | Win32, 7 | Adb 8 | } 9 | 10 | public static class MaaControllerHelper 11 | { 12 | public static string ToResourceKey(this MaaControllerTypes type) 13 | { 14 | return type switch 15 | { 16 | MaaControllerTypes.Win32 => "TabWin32", 17 | MaaControllerTypes.Adb => "TabADB", 18 | _ => "TabADB" 19 | }; 20 | } 21 | 22 | public static MaaControllerTypes ToMaaControllerTypes(this string? type, MaaControllerTypes defaultValue = MaaControllerTypes.Adb) 23 | { 24 | if (string.IsNullOrWhiteSpace(type)) 25 | return defaultValue; 26 | if (type.Contains("win32", StringComparison.OrdinalIgnoreCase)) 27 | return MaaControllerTypes.Win32; 28 | if (type.Contains("adb", StringComparison.OrdinalIgnoreCase)) 29 | return MaaControllerTypes.Adb; 30 | return defaultValue; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Extensions/Maa/MaaCustomAction.cs: -------------------------------------------------------------------------------- 1 | using MaaFramework.Binding; 2 | using MaaFramework.Binding.Custom; 3 | using MFAWPF.Helper; 4 | using MFAWPF.Helper.Exceptions; 5 | 6 | namespace MFAWPF.Extensions.Maa; 7 | 8 | public abstract class MaaCustomAction : IMaaCustomAction 9 | { 10 | public abstract string Name { get; set; } 11 | 12 | public bool Run(in IMaaContext inContext, in RunArgs inArgs,in RunResults runResults) 13 | { 14 | var context = inContext; 15 | var args = inArgs; 16 | try 17 | { 18 | return Execute(context, args); 19 | } 20 | catch (OperationCanceledException) 21 | { 22 | LoggerService.LogInfo("Stopping MaaCustomAction"); 23 | return false; 24 | } 25 | catch (MaaStopException) 26 | { 27 | LoggerService.LogInfo("Stopping MaaCustomAction"); 28 | return false; 29 | } 30 | catch (MaaErrorHandleException) 31 | { 32 | LoggerService.LogInfo("ErrorHandle MaaCustomAction"); 33 | ErrorHandle(context, args); 34 | return true; 35 | } 36 | } 37 | 38 | protected abstract bool Execute(IMaaContext context, RunArgs args); 39 | 40 | protected virtual void ErrorHandle(IMaaContext context, RunArgs args) {} 41 | } 42 | -------------------------------------------------------------------------------- /Helper/AutoInitDictionary.cs: -------------------------------------------------------------------------------- 1 | namespace MFAWPF.Helper; 2 | 3 | public class AutoInitDictionary : Dictionary 4 | { 5 | public AutoInitDictionary() 6 | { 7 | this["exploreCount"] = 0; 8 | } 9 | 10 | // 重写索引器,确保不存在的键被访问时初始化为0 11 | public new int this[string key] 12 | { 13 | get 14 | { 15 | if (!ContainsKey(key)) 16 | { 17 | this[key] = 0; 18 | } 19 | 20 | return base[key]; 21 | } 22 | set => base[key] = value; 23 | } 24 | } -------------------------------------------------------------------------------- /Helper/Converters/AutoConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class AutoConverter : JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) 9 | { 10 | return objectType == typeof(object); // We are converting to object as it can be any type. 11 | } 12 | 13 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 14 | JsonSerializer serializer) 15 | { 16 | JToken token = JToken.Load(reader); 17 | 18 | switch (token.Type) 19 | { 20 | case JTokenType.Boolean: 21 | return token.ToObject(); 22 | 23 | case JTokenType.Integer: 24 | long longValue = token.ToObject(); 25 | if (longValue is >= uint.MinValue and <= uint.MaxValue) 26 | return (uint)longValue; 27 | 28 | return longValue; 29 | 30 | case JTokenType.Float: 31 | return token.ToObject(); 32 | 33 | case JTokenType.String: 34 | return token.ToString(); 35 | 36 | case JTokenType.Array: 37 | var firstElement = token.First; 38 | if (firstElement?.Type == JTokenType.Integer) 39 | return token.ToObject>(); 40 | if (firstElement?.Type == JTokenType.String) 41 | return token.ToObject>(); 42 | if (firstElement?.Type == JTokenType.Array) 43 | return token.ToObject>>(); 44 | 45 | break; 46 | } 47 | 48 | throw new JsonSerializationException($"Invalid JSON format for AutoConverter. Unexpected Type \"{objectType}\""); 49 | } 50 | 51 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 52 | { 53 | switch (value) 54 | { 55 | case bool booleanValue: 56 | writer.WriteValue(booleanValue); 57 | break; 58 | 59 | case int intValue: 60 | writer.WriteValue(intValue); 61 | break; 62 | 63 | case uint uintValue: 64 | writer.WriteValue(uintValue); 65 | break; 66 | 67 | case double doubleValue: 68 | writer.WriteValue(doubleValue); 69 | break; 70 | 71 | case string stringValue: 72 | writer.WriteValue(stringValue); 73 | break; 74 | 75 | case List intList: 76 | serializer.Serialize(writer, intList); 77 | break; 78 | 79 | case List stringList: 80 | serializer.Serialize(writer, stringList); 81 | break; 82 | 83 | case List> nestedIntList: 84 | serializer.Serialize(writer, nestedIntList); 85 | break; 86 | 87 | default: 88 | throw new JsonSerializationException($"Unexpected value type \"{value?.GetType()}\" for AutoConverter."); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Helper/Converters/CustomIsEnabledConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class CustomIsEnabledConverter : IMultiValueConverter 7 | { 8 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 9 | { 10 | if (values is [bool isChecked, bool idle]) 11 | { 12 | bool isCheckedValue = isChecked; 13 | return (isCheckedValue && idle) || !isCheckedValue; 14 | } 15 | 16 | return false; 17 | } 18 | 19 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 20 | { 21 | return []; 22 | } 23 | } -------------------------------------------------------------------------------- /Helper/Converters/ListDoubleStringConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.ValueType; 2 | using System.Collections.ObjectModel; 3 | using System.Globalization; 4 | using System.Windows; 5 | using System.Windows.Data; 6 | 7 | namespace MFAWPF.Helper.Converters; 8 | 9 | public class ListDoubleStringConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value is double d) 14 | { 15 | return new ObservableCollection> 16 | { 17 | new(d.ToString(CultureInfo.InvariantCulture)) 18 | }; 19 | } 20 | 21 | if (value is IEnumerable doubles) 22 | { 23 | return new ObservableCollection>( 24 | doubles.Select(dx => new CustomValue(dx.ToString(CultureInfo.InvariantCulture)))); 25 | } 26 | 27 | return new ObservableCollection>(); 28 | } 29 | 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | if (value is IEnumerable> customValueList) 34 | { 35 | var list = customValueList.ToList(); 36 | 37 | try 38 | { 39 | var result = list 40 | .Select(cv => double.Parse(cv.Value ?? string.Empty)) 41 | .ToList(); 42 | if (result.Count == 1) 43 | return result[0]; 44 | return result.Count > 0 ? result : null; 45 | } 46 | catch 47 | { 48 | return DependencyProperty.UnsetValue; 49 | } 50 | } 51 | 52 | return null; 53 | } 54 | } -------------------------------------------------------------------------------- /Helper/Converters/ListIntStringConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.ValueType; 2 | using System.Collections.ObjectModel; 3 | using System.Globalization; 4 | using System.Windows; 5 | using System.Windows.Data; 6 | 7 | namespace MFAWPF.Helper.Converters; 8 | 9 | public class ListIntStringConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value is IEnumerable intCollection) 14 | { 15 | return new ObservableCollection>() 16 | { 17 | new(string.Join(",", intCollection.ToList())) 18 | }; 19 | } 20 | 21 | if (value is IEnumerable> intCCollection) 22 | { 23 | return new ObservableCollection>( 24 | intCCollection.Select(ic => new CustomValue(string.Join(",", ic.ToList())))); 25 | } 26 | 27 | return new ObservableCollection>(); 28 | } 29 | 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | if (value is IEnumerable> customValueList) 34 | { 35 | var list = customValueList.ToList(); 36 | if (list.Count == 1) 37 | { 38 | if (bool.TryParse(list[0].Value, out var result) && result) 39 | return result; 40 | } 41 | 42 | try 43 | { 44 | var result = list 45 | .Where(cv => cv.Value != null) 46 | .Select(cv => cv.Value? 47 | .Split(',') 48 | .Select(int.Parse) 49 | .ToList()) 50 | .ToList(); 51 | return result.Count > 0 ? result : null; 52 | } 53 | catch 54 | { 55 | return DependencyProperty.UnsetValue; 56 | } 57 | } 58 | 59 | 60 | return null; 61 | } 62 | } -------------------------------------------------------------------------------- /Helper/Converters/ListStringArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.ValueType; 2 | using System.Collections.ObjectModel; 3 | using System.Globalization; 4 | using System.Windows; 5 | using System.Windows.Data; 6 | 7 | namespace MFAWPF.Helper.Converters; 8 | 9 | public class ListStringArrayConverter : IValueConverter 10 | { 11 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 12 | { 13 | if (value is IEnumerable ls) 14 | { 15 | return new ObservableCollection>( 16 | ls.Select(array => new CustomValue($"[{string.Join(",", array)}]")).ToList() 17 | ); 18 | } 19 | 20 | return new ObservableCollection>(); 21 | } 22 | 23 | 24 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 25 | { 26 | if (value is IEnumerable> collection) 27 | { 28 | var result = collection.Select(customValue => 29 | { 30 | var trimmed = customValue.Value?.Trim('[', ']'); 31 | var splitArray = trimmed?.Split(","); 32 | return splitArray?.Length == 2 ? splitArray : null; 33 | }).ToList(); 34 | 35 | if (result.Any(array => array == null)) 36 | { 37 | return DependencyProperty.UnsetValue; 38 | } 39 | 40 | return result; 41 | } 42 | 43 | 44 | return null; 45 | } 46 | } -------------------------------------------------------------------------------- /Helper/Converters/ListStringConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.ValueType; 2 | using System.Collections.ObjectModel; 3 | using System.Globalization; 4 | using System.Windows.Data; 5 | 6 | namespace MFAWPF.Helper.Converters; 7 | 8 | public class ListStringConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value is IEnumerable stringCollection) 13 | { 14 | return new ObservableCollection>(stringCollection 15 | .Select(s => new CustomValue(s)).ToList()); 16 | } 17 | 18 | if (value is string sx) 19 | { 20 | return new ObservableCollection> 21 | { 22 | new(sx) 23 | }; 24 | } 25 | 26 | return new ObservableCollection>(); 27 | } 28 | 29 | 30 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 31 | { 32 | if (value is IEnumerable> customValueList) 33 | { 34 | var list = customValueList.Select(cv => cv.Value).ToList(); 35 | return list.Count > 0 ? list : null; 36 | } 37 | 38 | return null; 39 | } 40 | } -------------------------------------------------------------------------------- /Helper/Converters/MaaInterfaceSelectAdvancedConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Extensions.Maa; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace MFAWPF.Helper.Converters; 6 | 7 | 8 | public class MaaInterfaceSelectAdvancedConverter(bool serializeAsStringArray) : JsonConverter 9 | { 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(List); 13 | } 14 | 15 | 16 | public override object ReadJson(JsonReader reader, 17 | Type objectType, 18 | object existingValue, 19 | JsonSerializer serializer) 20 | { 21 | var token = JToken.Load(reader); 22 | 23 | switch (token.Type) 24 | { 25 | case JTokenType.Array: 26 | var firstElement = token.First; 27 | 28 | if (firstElement?.Type == JTokenType.String) 29 | { 30 | var list = new List(); 31 | foreach (var item in token) 32 | { 33 | list.Add(new MaaInterface.MaaInterfaceSelectAdvanced 34 | { 35 | Name = item.ToString(), 36 | }); 37 | } 38 | 39 | return list; 40 | } 41 | 42 | if (firstElement?.Type == JTokenType.Object) 43 | { 44 | return token.ToObject>(serializer); 45 | } 46 | 47 | break; 48 | case JTokenType.String: 49 | var oName = token.ToObject(serializer); 50 | return new List 51 | { 52 | new() 53 | { 54 | Name = oName ?? "" 55 | } 56 | }; 57 | case JTokenType.None: 58 | return null; 59 | } 60 | 61 | Console.WriteLine($"Invalid JSON format for MaaInterfaceSelectAdvancedConverter. Unexpected type {objectType}."); 62 | return null; 63 | } 64 | 65 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 66 | { 67 | var array = new JArray(); 68 | 69 | if (value is List selectOptions) 70 | { 71 | if (serializeAsStringArray) 72 | { 73 | foreach (var option in selectOptions) 74 | { 75 | array.Add(option.Name); 76 | } 77 | } 78 | else 79 | { 80 | foreach (var option in selectOptions) 81 | { 82 | var obj = JObject.FromObject(option); 83 | array.Add(obj); 84 | } 85 | } 86 | 87 | array.WriteTo(writer); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Helper/Converters/MaaInterfaceSelectOptionConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Extensions.Maa; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace MFAWPF.Helper.Converters; 6 | 7 | public class MaaInterfaceSelectOptionConverter : JsonConverter 8 | { 9 | private readonly bool _serializeAsStringArray; 10 | 11 | public MaaInterfaceSelectOptionConverter(bool serializeAsStringArray) 12 | { 13 | _serializeAsStringArray = serializeAsStringArray; 14 | } 15 | 16 | public override bool CanConvert(Type objectType) 17 | { 18 | return objectType == typeof(List); 19 | } 20 | 21 | 22 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 23 | JsonSerializer serializer) 24 | { 25 | JToken token = JToken.Load(reader); 26 | switch (token.Type) 27 | { 28 | case JTokenType.Array: 29 | var firstElement = token.First; 30 | 31 | if (firstElement?.Type == JTokenType.String) 32 | { 33 | var list = new List(); 34 | foreach (var item in token) 35 | { 36 | list.Add(new MaaInterface.MaaInterfaceSelectOption 37 | { 38 | Name = item.ToString(), 39 | Index = 0 40 | }); 41 | } 42 | 43 | return list; 44 | } 45 | 46 | if (firstElement?.Type == JTokenType.Object) 47 | { 48 | return token.ToObject>(serializer); 49 | } 50 | 51 | break; 52 | case JTokenType.String: 53 | var oName = token.ToObject(serializer); 54 | return new List 55 | { 56 | new() 57 | { 58 | Name = oName ?? "", Index = 0 59 | } 60 | }; 61 | case JTokenType.None: 62 | return null; 63 | } 64 | 65 | Console.WriteLine($"Invalid JSON format for MaaInterfaceSelectOptionConverter. Unexpected type {objectType}."); 66 | return null; 67 | } 68 | 69 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 70 | { 71 | var array = new JArray(); 72 | 73 | if (value is List selectOptions) 74 | { 75 | if (_serializeAsStringArray) 76 | { 77 | foreach (var option in selectOptions) 78 | { 79 | array.Add(option.Name); 80 | } 81 | } 82 | else 83 | { 84 | foreach (var option in selectOptions) 85 | { 86 | JObject obj = new JObject 87 | { 88 | ["name"] = option.Name, 89 | ["index"] = option.Index 90 | }; 91 | array.Add(obj); 92 | } 93 | } 94 | 95 | array.WriteTo(writer); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /Helper/Converters/MaaResourceVersionConverter.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Extensions.Maa; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace MFAWPF.Helper.Converters; 6 | 7 | public class MaaResourceVersionConverter : JsonConverter 8 | { 9 | public override bool CanConvert(Type objectType) 10 | { 11 | return objectType == typeof(string) || objectType == typeof(MaaInterface.MaaResourceVersion); 12 | } 13 | 14 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 15 | JsonSerializer serializer) 16 | { 17 | var token = JToken.Load(reader); 18 | var res = token.ToString(); 19 | if (res.Contains('{') || res.Contains('}')) 20 | { 21 | var version = token.ToObject(serializer); 22 | return version?.Version; 23 | } 24 | 25 | return res; 26 | } 27 | 28 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 29 | { 30 | if (value is string strValue) 31 | { 32 | writer.WriteValue(strValue); 33 | } 34 | 35 | if (value is MaaInterface.MaaResourceVersion mrv) 36 | writer.WriteValue(mrv.Version); 37 | } 38 | } -------------------------------------------------------------------------------- /Helper/Converters/NullStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullStringConverter : IValueConverter 7 | { 8 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 9 | { 10 | return value?.ToString() ?? string.Empty; 11 | } 12 | 13 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 14 | { 15 | return string.IsNullOrEmpty(value?.ToString()) ? null : value; 16 | } 17 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableDoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullableDoubleConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | return value.ToString(); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, 16 | System.Globalization.CultureInfo culture) 17 | { 18 | var strValue = value as string; 19 | if (string.IsNullOrWhiteSpace(strValue)) 20 | return null; 21 | if (double.TryParse(strValue, out var result)) 22 | return result; 23 | return DependencyProperty.UnsetValue; 24 | } 25 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableIntConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullableIntConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | return value.ToString(); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, 16 | System.Globalization.CultureInfo culture) 17 | { 18 | var strValue = value as string; 19 | if (string.IsNullOrWhiteSpace(strValue)) 20 | return null; 21 | if (int.TryParse(strValue, out var result)) 22 | return result; 23 | return DependencyProperty.UnsetValue; 24 | } 25 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableStringConverter.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullableStringConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | return value.ToString(); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, 16 | System.Globalization.CultureInfo culture) 17 | { 18 | var strValue = value as string; 19 | if (string.IsNullOrWhiteSpace(strValue)) 20 | return null; 21 | return strValue; 22 | } 23 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableUIntConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullableUIntConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | return value.ToString(); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, 16 | System.Globalization.CultureInfo culture) 17 | { 18 | 19 | var strValue = value as string; 20 | if (string.IsNullOrWhiteSpace(strValue)) 21 | return null; 22 | if (uint.TryParse(strValue, out var result)) 23 | return result; 24 | return DependencyProperty.UnsetValue; 25 | } 26 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableUIntOrObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | using Newtonsoft.Json; 4 | 5 | namespace MFAWPF.Helper.Converters; 6 | 7 | public class NullableUIntOrObjectConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 10 | { 11 | if (value == null) 12 | return string.Empty; 13 | return value.ToString(); 14 | } 15 | 16 | public object ConvertBack(object value, Type targetType, object parameter, 17 | System.Globalization.CultureInfo culture) 18 | { 19 | var strValue = value as string; 20 | if (string.IsNullOrWhiteSpace(strValue)) 21 | return null; 22 | if (uint.TryParse(strValue, out var result)) 23 | return result; 24 | if (strValue.Contains('{') && strValue.Contains('}')) 25 | { 26 | return JsonConvert.DeserializeObject(strValue); 27 | } 28 | 29 | return DependencyProperty.UnsetValue; 30 | } 31 | } -------------------------------------------------------------------------------- /Helper/Converters/NullableUIntStringConverter.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class NullableUIntStringConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | return value.ToString(); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, 16 | System.Globalization.CultureInfo culture) 17 | { 18 | var strValue = value as string; 19 | if (string.IsNullOrWhiteSpace(strValue)) 20 | return null; 21 | if (uint.TryParse(strValue, out var result)) 22 | return result; 23 | return strValue; 24 | } 25 | } -------------------------------------------------------------------------------- /Helper/Converters/ReplaceConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class ReplaceConverter : JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) 9 | { 10 | return objectType == typeof(List); 11 | } 12 | 13 | public override object ReadJson(JsonReader reader, 14 | Type objectType, 15 | object existingValue, 16 | JsonSerializer serializer) 17 | { 18 | var token = JToken.Load(reader); 19 | if (token.Type == JTokenType.Array) 20 | { 21 | if (token.First?.Type == JTokenType.Array) 22 | { 23 | return token.ToObject>(); 24 | } 25 | 26 | var list = new List 27 | { 28 | token.ToObject() ?? [] 29 | }; 30 | return list; 31 | } 32 | 33 | throw new JsonSerializationException("Unexpected token type: " + token.Type); 34 | } 35 | 36 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 37 | { 38 | if (value is List list) 39 | { 40 | if (list.Count == 1) 41 | { 42 | serializer.Serialize(writer, list[0]); 43 | } 44 | else 45 | { 46 | serializer.Serialize(writer, list); 47 | } 48 | } 49 | else 50 | { 51 | writer.WriteValue(value?.ToString() ?? string.Empty); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Helper/Converters/SingleIntListConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SingleIntListConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | if (value is List li) 13 | { 14 | return string.Join(",", li); 15 | } 16 | 17 | return value.ToString(); 18 | } 19 | 20 | public object ConvertBack(object value, Type targetType, object parameter, 21 | System.Globalization.CultureInfo culture) 22 | { 23 | var strValue = value as string; 24 | if (string.IsNullOrWhiteSpace(strValue)) 25 | return null; 26 | 27 | try 28 | { 29 | var result = strValue 30 | .Split(',') 31 | .Select(int.Parse) 32 | .ToList(); 33 | return result; 34 | } 35 | catch 36 | { 37 | return DependencyProperty.UnsetValue; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Helper/Converters/SingleIntListOrAutoConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SingleIntListOrAutoConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | if (value == null) 11 | return string.Empty; 12 | if (value is List li) 13 | { 14 | return string.Join(",", li); 15 | } 16 | 17 | if (value is bool b) 18 | return b.ToString(); 19 | if (value is string s) 20 | return s; 21 | return value.ToString(); 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, 25 | System.Globalization.CultureInfo culture) 26 | { 27 | var strValue = value as string; 28 | if (string.IsNullOrWhiteSpace(strValue)) 29 | return null; 30 | if (bool.TryParse(strValue, out var b) && b) 31 | return true; 32 | if (!strValue.Contains(",")) 33 | { 34 | return strValue; 35 | } 36 | 37 | try 38 | { 39 | var result = strValue 40 | .Split(',') 41 | .Select(int.Parse) 42 | .ToList(); 43 | return result; 44 | } 45 | catch 46 | { 47 | return DependencyProperty.UnsetValue; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Helper/Converters/SingleOrDoubleListConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SingleOrDoubleListConverter : JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) 9 | { 10 | return objectType == typeof(object) || objectType == typeof(double); 11 | } 12 | 13 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 14 | JsonSerializer serializer) 15 | { 16 | JToken token = JToken.Load(reader); 17 | if (token.Type == JTokenType.Float) 18 | { 19 | return token.ToObject(); 20 | } 21 | 22 | if (token.Type == JTokenType.Array) 23 | { 24 | return token.ToObject>(); 25 | } 26 | 27 | return null; 28 | } 29 | 30 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 31 | { 32 | if (value is null) 33 | { 34 | return; 35 | } 36 | 37 | if (value is double d) 38 | { 39 | writer.WriteValue(d); 40 | return; 41 | } 42 | 43 | if (value is List list) 44 | { 45 | if (list.Count == 1) 46 | { 47 | writer.WriteValue(list[0]); 48 | } 49 | else 50 | { 51 | serializer.Serialize(writer, list); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Helper/Converters/SingleOrIntListConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SingleOrIntListConverter : JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) 9 | { 10 | return objectType == typeof(object); 11 | } 12 | 13 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 14 | { 15 | JToken token = JToken.Load(reader); 16 | if (token.Type == JTokenType.Integer) 17 | { 18 | return new List { token.ToObject() }; 19 | } 20 | 21 | if (token.Type == JTokenType.Array) 22 | { 23 | return token.ToObject>(); 24 | } 25 | 26 | return null; 27 | } 28 | 29 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 30 | { 31 | if (value is List list) 32 | { 33 | if (list.Count == 1) 34 | { 35 | writer.WriteValue(list[0]); 36 | } 37 | else 38 | { 39 | serializer.Serialize(writer, list); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Helper/Converters/SingleOrListConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SingleOrListConverter : JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) 9 | { 10 | return objectType == typeof(object); 11 | } 12 | 13 | public override object ReadJson(JsonReader reader, 14 | Type objectType, 15 | object existingValue, 16 | JsonSerializer serializer) 17 | { 18 | JToken token = JToken.Load(reader); 19 | if (token is { Type: JTokenType.String }) 20 | { 21 | return new List 22 | { 23 | token.ToString() 24 | }; 25 | } 26 | 27 | if (token is { Type: JTokenType.Array }) 28 | { 29 | return token.ToObject>(); 30 | } 31 | 32 | return null; 33 | } 34 | 35 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 36 | { 37 | if (value is List list) 38 | { 39 | serializer.Serialize(writer, list); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Helper/Converters/SingleOrNestedListConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System.Collections; 4 | using System.Text; 5 | using System.Windows.Documents; 6 | 7 | namespace MFAWPF.Helper.Converters; 8 | 9 | public class SingleOrNestedListConverter : JsonConverter 10 | { 11 | public override bool CanConvert(Type objectType) 12 | { 13 | return objectType == typeof(List) || objectType == typeof(List>) || objectType == typeof(string) || objectType == typeof(bool); 14 | } 15 | 16 | public override object ReadJson(JsonReader reader, 17 | Type objectType, 18 | object existingValue, 19 | JsonSerializer serializer) 20 | { 21 | JToken token = JToken.Load(reader); 22 | 23 | if (token.Type == JTokenType.Array) 24 | { 25 | if (token.First is { Type: JTokenType.Array }) 26 | return token.ToObject>>(); 27 | 28 | return token.ToObject>(); 29 | } 30 | 31 | if (token.Type == JTokenType.String) 32 | { 33 | return token.ToString(); 34 | } 35 | 36 | if (token.Type == JTokenType.Boolean) 37 | { 38 | return token.ToString(); 39 | } 40 | 41 | throw new JsonSerializationException($"Invalid JSON format for SingleOrNestedListConverter. Unexpected Type \"{objectType}\""); 42 | } 43 | 44 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 45 | { 46 | if (value is IEnumerable> nestedList) 47 | { 48 | serializer.Serialize(writer, nestedList); 49 | } 50 | else if (value is IEnumerable singleList) 51 | { 52 | serializer.Serialize(writer, singleList); 53 | } 54 | else if (value is string s) 55 | { 56 | writer.WriteValue(s); 57 | } 58 | else if (value is bool b) 59 | { 60 | writer.WriteValue(b); 61 | } 62 | else 63 | { 64 | throw new JsonSerializationException($"Unexpected value type \"{value?.GetType()}\" for CustomListConverter."); 65 | } 66 | } 67 | 68 | private List FlattenMultiDimArray(Array multiDimArray) 69 | { 70 | List result = new List(); 71 | if (multiDimArray.Rank == 1) 72 | { 73 | foreach (int element in multiDimArray) 74 | { 75 | result.Add(element); 76 | } 77 | } 78 | else 79 | { 80 | foreach (var element in multiDimArray) 81 | { 82 | if (element is Array subArray) 83 | { 84 | result.AddRange(FlattenMultiDimArray(subArray)); 85 | } 86 | } 87 | } 88 | return result; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Helper/Converters/StrictPlaceholderConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class StrictPlaceholderConverter: JsonConverter 7 | { 8 | public override bool CanConvert(Type objectType) => true; 9 | 10 | public override object ReadJson(JsonReader reader, Type objectType, 11 | object existingValue, JsonSerializer serializer) 12 | { 13 | var token = JToken.Load(reader); 14 | 15 | if (token.Type == JTokenType.String && 16 | token.Value() == "[placeholder]") 17 | { 18 | return GetDefaultValue(objectType); // 根据目标类型返回安全值 19 | } 20 | 21 | return token.ToObject(objectType); 22 | } 23 | 24 | private object GetDefaultValue(Type targetType) => 25 | Type.GetTypeCode(targetType) switch { 26 | TypeCode.Int32 => 0, 27 | TypeCode.Boolean => false, 28 | TypeCode.String => "[placeholder]", // 保持字符串形式 29 | _ => Activator.CreateInstance(targetType) 30 | }; 31 | 32 | public override void WriteJson(JsonWriter writer, object value, 33 | JsonSerializer serializer) => serializer.Serialize(writer, value); 34 | } -------------------------------------------------------------------------------- /Helper/Converters/StringOrBoolOrObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | 5 | namespace MFAWPF.Helper.Converters; 6 | 7 | public class StringOrBoolOrObjectConverter : JsonConverter 8 | { 9 | // 允许转换的类型:字符串或动态对象 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(string) || objectType == typeof(bool) || objectType == typeof(object); 13 | } 14 | 15 | // 反序列化逻辑 16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 17 | { 18 | JToken token = JToken.Load(reader); 19 | 20 | // 情况 1:JSON 值为字符串 21 | if (token.Type == JTokenType.String) 22 | { 23 | return token.ToObject(); 24 | } 25 | if (token.Type == JTokenType.Boolean) 26 | { 27 | return token.ToObject(); 28 | } 29 | 30 | // 情况 2:JSON 值为对象(动态解析为 JObject) 31 | if (token.Type == JTokenType.Object) 32 | { 33 | // 使用 JObject 动态处理任意结构 34 | return token.ToObject(); 35 | } 36 | 37 | throw new JsonSerializationException($"不支持的 JSON 类型:{token.Type}"); 38 | } 39 | 40 | // 序列化逻辑 41 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 42 | { 43 | // 情况 1:输入为字符串类型 44 | if (value is string str) 45 | { 46 | writer.WriteValue(str); 47 | return; 48 | } 49 | if (value is bool b) 50 | { 51 | writer.WriteValue(b); 52 | return; 53 | } 54 | // 情况 2:输入为动态对象(如 JObject 或自定义类型) 55 | if (value is JObject jObj) 56 | { 57 | jObj.WriteTo(writer); 58 | } 59 | else 60 | { 61 | // 处理其他动态对象(如字典) 62 | JToken.FromObject(value, serializer).WriteTo(writer); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Helper/Converters/StringOrObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class StringOrObjectConverter : JsonConverter 7 | { 8 | // 允许转换的类型:字符串或动态对象 9 | public override bool CanConvert(Type objectType) 10 | { 11 | return objectType == typeof(string) || objectType == typeof(object); 12 | } 13 | 14 | // 反序列化逻辑 15 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 16 | { 17 | JToken token = JToken.Load(reader); 18 | 19 | // 情况 1:JSON 值为字符串 20 | if (token.Type == JTokenType.String) 21 | { 22 | return token.ToObject(); 23 | } 24 | 25 | // 情况 2:JSON 值为对象(动态解析为 JObject) 26 | if (token.Type == JTokenType.Object) 27 | { 28 | // 使用 JObject 动态处理任意结构 29 | return token.ToObject(); 30 | } 31 | 32 | throw new JsonSerializationException($"不支持的 JSON 类型:{token.Type}"); 33 | } 34 | 35 | // 序列化逻辑 36 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 37 | { 38 | // 情况 1:输入为字符串类型 39 | if (value is string str) 40 | { 41 | writer.WriteValue(str); 42 | return; 43 | } 44 | 45 | // 情况 2:输入为动态对象(如 JObject 或自定义类型) 46 | if (value is JObject jObj) 47 | { 48 | jObj.WriteTo(writer); 49 | } 50 | else 51 | { 52 | // 处理其他动态对象(如字典) 53 | JToken.FromObject(value, serializer).WriteTo(writer); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Helper/Converters/SubtractConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class SubtractConverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 9 | { 10 | if (value is double originalWidth && parameter is string parameterString && double.TryParse(parameterString, out double subtractValue)) 11 | { 12 | return originalWidth - subtractValue; 13 | } 14 | return value; 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | throw new Exception("Type not exists!"); 20 | } 21 | } -------------------------------------------------------------------------------- /Helper/Converters/UniversalEnumConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Numerics; 3 | 4 | namespace MFAWPF.Helper.Converters; 5 | 6 | public class UniversalEnumConverter : JsonConverter where T : struct, Enum 7 | { 8 | public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) 9 | { 10 | if (reader.Value is BigInteger bigInt) 11 | return ConvertFromBigInteger(bigInt); 12 | if (reader.Value != null && reader.TokenType == JsonToken.Integer) 13 | return (T)Enum.ToObject(typeof(T), reader.Value); 14 | if (reader.TokenType == JsonToken.String) 15 | return Enum.Parse(reader.Value?.ToString() ?? string.Empty, true); 16 | throw new JsonException($"无法转换 {reader.Value} 到 {typeof(T)}"); 17 | } 18 | private T ConvertFromBigInteger(BigInteger bigInt) 19 | { 20 | Type underlyingType = Enum.GetUnderlyingType(typeof(T)); 21 | 22 | try 23 | { 24 | checked 25 | { 26 | return underlyingType switch 27 | { 28 | _ when underlyingType == typeof(ulong) => (T)Enum.ToObject(typeof(T), (ulong)bigInt), 29 | _ when underlyingType == typeof(long) => (T)Enum.ToObject(typeof(T), (long)bigInt), 30 | _ when underlyingType == typeof(uint) => (T)Enum.ToObject(typeof(T), (uint)bigInt), 31 | _ when underlyingType == typeof(int) => (T)Enum.ToObject(typeof(T), (int)bigInt), 32 | _ => throw new NotSupportedException($"不支持的底层类型: {underlyingType.Name}") 33 | }; 34 | } 35 | } 36 | catch (OverflowException) 37 | { 38 | throw new JsonException($"数值 {bigInt} 超过 {underlyingType.Name} 的范围"); 39 | } 40 | } 41 | public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) 42 | { 43 | writer.WriteValue(value.ToString()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Helper/DeviceWindowTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using MaaFramework.Binding; 4 | 5 | namespace MFAWPF.Helper; 6 | 7 | public class DeviceWindowTemplateSelector : DataTemplateSelector 8 | { 9 | public DataTemplate DeviceInfoTemplate { get; set; } 10 | public DataTemplate WindowInfoTemplate { get; set; } 11 | 12 | public override DataTemplate? SelectTemplate(object item, DependencyObject container) 13 | { 14 | if (item is AdbDeviceInfo) 15 | return DeviceInfoTemplate ; 16 | 17 | if (item is DesktopWindowInfo) 18 | return WindowInfoTemplate; 19 | 20 | 21 | return base.SelectTemplate(item, container); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Helper/DispatcherHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Threading; 3 | 4 | namespace MFAWPF.Helper; 5 | 6 | public static class DispatcherHelper 7 | { 8 | public static void RunOnMainThread(Action action) 9 | { 10 | RunOnUIThread(Application.Current, action); 11 | } 12 | 13 | public static void RunOnUIThread(this DispatcherObject d, Action action) 14 | { 15 | var dispatcher = d?.Dispatcher; 16 | if (dispatcher!=null) 17 | { 18 | if (dispatcher.CheckAccess()) 19 | { 20 | action(); 21 | } 22 | else 23 | { 24 | dispatcher.BeginInvoke(action); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Helper/DragDropHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.ObjectModel; 3 | using GongSolutions.Wpf.DragDrop; 4 | using MFAWPF.Configuration; 5 | using MFAWPF.Extensions.Maa; 6 | using MFAWPF.Helper.ValueType; 7 | using MFAWPF.ViewModels; 8 | using MFAWPF.Views; 9 | using MFAWPF.Views.UI; 10 | using DragItemViewModel = MFAWPF.ViewModels.Tool.DragItemViewModel; 11 | 12 | 13 | namespace MFAWPF.Helper; 14 | 15 | public class DragDropHandler : IDropTarget 16 | { 17 | public void DragOver(IDropInfo dropInfo) 18 | { 19 | // 确保拖动有效性并显示可视化反馈 20 | if (dropInfo is { Data: not null, TargetCollection: not null }) 21 | { 22 | dropInfo.Effects = System.Windows.DragDropEffects.Move; 23 | dropInfo.DropTargetAdorner = DropTargetAdorners.Insert; 24 | } 25 | } 26 | 27 | public void Drop(IDropInfo dropInfo) 28 | { 29 | // 当拖放完成后 30 | if (dropInfo is { Data: not null, TargetCollection: IList targetCollection }) 31 | { 32 | var insertIndex = dropInfo.InsertIndex; 33 | 34 | // 确保移除和插入顺序正确,处理新索引 35 | if (dropInfo.Data is IEnumerable items) 36 | { 37 | foreach (var item in items) 38 | { 39 | var oldIndex = targetCollection.IndexOf(item); 40 | 41 | if (oldIndex != -1) 42 | { 43 | // 如果拖拽在向下移动,则需要修正插入索引 44 | if (insertIndex > oldIndex) 45 | { 46 | insertIndex--; 47 | } 48 | 49 | targetCollection.RemoveAt(oldIndex); 50 | targetCollection.Insert(insertIndex, item); 51 | } 52 | } 53 | } 54 | else 55 | { 56 | // 处理单个项目的拖拽 57 | var item = dropInfo.Data; 58 | var oldIndex = targetCollection.IndexOf(item); 59 | 60 | if (oldIndex != -1) 61 | { 62 | if (insertIndex > oldIndex) 63 | { 64 | insertIndex--; 65 | } 66 | 67 | targetCollection.RemoveAt(oldIndex); 68 | targetCollection.Insert(insertIndex, item); 69 | } 70 | } 71 | 72 | 73 | if (targetCollection is ObservableCollection dragItemViewModels) 74 | { 75 | List tasks = new(); 76 | foreach (var dragItem in dragItemViewModels) 77 | { 78 | if (dragItem.InterfaceItem != null) 79 | tasks.Add(dragItem.InterfaceItem); 80 | } 81 | 82 | if (MaaInterface.Instance != null) 83 | MaaInterface.Instance.Task = tasks; 84 | // 保存当前的 ItemsSource 到 JSON 85 | ConfigurationHelper.SetValue(ConfigurationKeys.TaskItems, 86 | Instances.TaskQueueViewModel.TaskItemViewModels.ToList().Select(model => model.InterfaceItem)); 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Helper/Editor/ListAutoStringEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.ObjectModel; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using HandyControl.Controls; 6 | using MFAWPF.Views.UserControl; 7 | using MFAWPF.Helper.Converters; 8 | using MFAWPF.ViewModels; 9 | using MFAWPF.ViewModels.Tool; 10 | using MFAWPF.Views.UI; 11 | 12 | namespace MFAWPF.Helper.Editor; 13 | 14 | public class ListAutoStringEditor : PropertyEditorBase 15 | { 16 | public override FrameworkElement CreateElement(PropertyItem propertyItem) 17 | { 18 | var autoListControl = new CustomAutoListControl 19 | { 20 | MinHeight = 50, TaskDialogDataList = GetItemsSource(propertyItem), 21 | DisplayMemberPath = GetDisplayMemberPath(propertyItem) 22 | }; 23 | 24 | return autoListControl; 25 | } 26 | 27 | // 实现抽象方法,返回 ItemsProperty 作为绑定的 DependencyProperty 28 | public override DependencyProperty GetDependencyProperty() => CustomAutoListControl.ItemsProperty; 29 | 30 | // 动态设置 ItemsSource,根据字段名称定制选项 31 | public static List AutoProperty() 32 | { 33 | return ["Roi", "Next", "OnError", "Interrupt", "Begin", "End", "Target"]; 34 | } 35 | 36 | private IEnumerable GetItemsSource(PropertyItem propertyItem) 37 | { 38 | if (propertyItem.PropertyName == "Roi" || propertyItem.PropertyName == "Next" || 39 | propertyItem.PropertyName == "OnError" || propertyItem.PropertyName == "Interrupt") 40 | { 41 | return RootView.TaskDialog?.Data?.DataList; 42 | } 43 | 44 | if (AutoProperty().Contains(propertyItem.PropertyName)) 45 | { 46 | var originalDataList = RootView.TaskDialog?.Data?.DataList; 47 | if (originalDataList != null) 48 | { 49 | var newDataList = new ObservableCollection(originalDataList); 50 | newDataList.Add(new TaskItemViewModel { Name = "True" }); 51 | return newDataList; 52 | } 53 | 54 | return null; 55 | } 56 | 57 | if (propertyItem.PropertyName.Contains("Color")) 58 | { 59 | return RootView.TaskDialog?.Data?.Colors; 60 | } 61 | 62 | return new ObservableCollection(); 63 | } 64 | 65 | // 根据属性的字段名称,动态返回不同的 DisplayMemberPath 66 | private string GetDisplayMemberPath(PropertyItem propertyItem) 67 | { 68 | if (AutoProperty().Contains(propertyItem.PropertyName)) 69 | { 70 | return "Name"; 71 | } 72 | 73 | if (propertyItem.PropertyName.Contains("Color")) 74 | { 75 | return "Name"; 76 | } 77 | 78 | return string.Empty; // 默认的 DisplayMemberPath 79 | } 80 | 81 | 82 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new ListStringConverter(); 83 | } -------------------------------------------------------------------------------- /Helper/Editor/ListDoubleStringEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using HandyControl.Controls; 3 | using MFAWPF.Helper.Converters; 4 | 5 | namespace MFAWPF.Helper.Editor; 6 | 7 | public class ListDoubleStringEditor: ListStringEditor 8 | { 9 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new ListDoubleStringConverter(); 10 | } -------------------------------------------------------------------------------- /Helper/Editor/ListIntStringEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using HandyControl.Controls; 3 | using MFAWPF.Helper.Converters; 4 | 5 | namespace MFAWPF.Helper.Editor; 6 | 7 | public class ListIntStringEditor : ListStringEditor 8 | { 9 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new ListIntStringConverter(); 10 | } -------------------------------------------------------------------------------- /Helper/Editor/ListStringArrayEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using HandyControl.Controls; 3 | using MFAWPF.Helper.Converters; 4 | 5 | namespace MFAWPF.Helper.Editor; 6 | 7 | public class ListStringArrayEditor : ListStringEditor 8 | { 9 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new ListStringArrayConverter(); 10 | } -------------------------------------------------------------------------------- /Helper/Editor/ListStringEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Views.UserControl; 5 | using MFAWPF.Helper.Converters; 6 | 7 | namespace MFAWPF.Helper.Editor; 8 | 9 | public class ListStringEditor : PropertyEditorBase 10 | { 11 | public override FrameworkElement CreateElement(PropertyItem propertyItem) => new CustomListControl 12 | { 13 | MinHeight = 50 14 | }; 15 | 16 | public override BindingMode GetBindingMode(PropertyItem propertyItem) => BindingMode.TwoWay; 17 | 18 | public override DependencyProperty GetDependencyProperty() => CustomListControl.ItemsProperty; 19 | 20 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new ListStringConverter(); 21 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableDoubleEditor.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Helper.Converters; 5 | 6 | namespace MFAWPF.Helper.Editor; 7 | 8 | public class NullableDoubleEditor : NullableStringEditor 9 | { 10 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableDoubleConverter(); 11 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableIntEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Helper.Converters; 5 | 6 | namespace MFAWPF.Helper.Editor; 7 | 8 | public class NullableIntEditor : NullableStringEditor 9 | { 10 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableIntConverter(); 11 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableStringEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Helper.Converters; 5 | 6 | namespace MFAWPF.Helper.Editor; 7 | 8 | public class NullableStringEditor : PropertyEditorBase 9 | { 10 | public override FrameworkElement CreateElement(PropertyItem propertyItem) 11 | { 12 | var ctrl = new System.Windows.Controls.TextBox 13 | { 14 | IsReadOnly = propertyItem.IsReadOnly 15 | }; 16 | InfoElement.SetShowClearButton(ctrl, true); 17 | return ctrl; 18 | } 19 | 20 | public override DependencyProperty GetDependencyProperty() => System.Windows.Controls.TextBox.TextProperty; 21 | 22 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableStringConverter(); 23 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableUIntEditor.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Helper.Converters; 5 | 6 | namespace MFAWPF.Helper.Editor; 7 | 8 | public class NullableUIntEditor : NullableStringEditor 9 | { 10 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableUIntConverter(); 11 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableUIntOrObjectEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using HandyControl.Controls; 3 | using MFAWPF.Helper.Converters; 4 | 5 | namespace MFAWPF.Helper.Editor; 6 | 7 | public class NullableUIntOrObjectEditor: NullableStringEditor 8 | { 9 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableUIntOrObjectConverter(); 10 | } -------------------------------------------------------------------------------- /Helper/Editor/NullableUIntStringEditor.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows.Data; 3 | using HandyControl.Controls; 4 | using MFAWPF.Helper.Converters; 5 | 6 | namespace MFAWPF.Helper.Editor; 7 | 8 | public class NullableUIntStringEditor : NullableStringEditor 9 | { 10 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullableUIntStringConverter(); 11 | } -------------------------------------------------------------------------------- /Helper/Editor/SingleIntListEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using HandyControl.Controls; 3 | using MFAWPF.Helper.Converters; 4 | 5 | namespace MFAWPF.Helper.Editor; 6 | 7 | public class SingleIntListEditor : NullableStringEditor 8 | { 9 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new SingleIntListConverter(); 10 | } -------------------------------------------------------------------------------- /Helper/Editor/SingleIntListOrAutoEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.ObjectModel; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using HandyControl.Controls; 6 | using MFAWPF.Views.UserControl; 7 | using MFAWPF.Helper.Converters; 8 | using MFAWPF.ViewModels; 9 | using MFAWPF.ViewModels.Tool; 10 | using MFAWPF.Views.UI; 11 | using ComboBox = System.Windows.Controls.ComboBox; 12 | 13 | namespace MFAWPF.Helper.Editor; 14 | 15 | public class SingleIntListOrAutoEditor : PropertyEditorBase 16 | { 17 | public override FrameworkElement CreateElement(PropertyItem propertyItem) 18 | { 19 | var ctrl = new CustomAutoCompleteTextBox 20 | { 21 | IsReadOnly = propertyItem.IsReadOnly, DataList = GetItemsSource(propertyItem), 22 | DisplayMemberPath = GetDisplayMemberPath(propertyItem) 23 | }; 24 | InfoElement.SetShowClearButton(ctrl, true); 25 | return ctrl; 26 | } 27 | 28 | public static List AutoProperty() 29 | { 30 | return ["Roi", "Begin", "End", "Target"]; 31 | } 32 | 33 | private IEnumerable GetItemsSource(PropertyItem propertyItem) 34 | { 35 | if (AutoProperty().Contains(propertyItem.PropertyName)) 36 | { 37 | var originalDataList = RootView.TaskDialog?.Data?.DataList; 38 | if (originalDataList != null) 39 | { 40 | var newDataList = new ObservableCollection(originalDataList); 41 | if (propertyItem.PropertyName != "Roi") 42 | newDataList.Add(new TaskItemViewModel { Name = "True" }); 43 | return newDataList; 44 | } 45 | 46 | return null; 47 | } 48 | 49 | return new ObservableCollection(); 50 | } 51 | 52 | private string GetDisplayMemberPath(PropertyItem propertyItem) 53 | { 54 | if (AutoProperty().Contains(propertyItem.PropertyName)) 55 | { 56 | return "Name"; 57 | } 58 | 59 | return string.Empty; 60 | } 61 | 62 | public override DependencyProperty GetDependencyProperty() => ComboBox.TextProperty; 63 | 64 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new SingleIntListOrAutoConverter(); 65 | } -------------------------------------------------------------------------------- /Helper/Editor/StringComboBoxEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls.Primitives; 3 | using System.Windows.Data; 4 | using HandyControl.Controls; 5 | using MFAWPF.Helper.Converters; 6 | 7 | namespace MFAWPF.Helper.Editor; 8 | 9 | public class StringComboBoxEditor : PropertyEditorBase 10 | { 11 | // 重写编辑器的控件类型 12 | public override FrameworkElement CreateElement(PropertyItem propertyItem) 13 | { 14 | var comboBox = new System.Windows.Controls.ComboBox 15 | { 16 | IsEditable = false, 17 | IsSynchronizedWithCurrentItem = true, 18 | SelectedItem = propertyItem.Value 19 | }; 20 | 21 | // 动态设置 ItemsSource,根据字段名称定制选项 22 | comboBox.ItemsSource = GetItemsSource(propertyItem); 23 | 24 | comboBox.SelectionChanged += (_, _) => { propertyItem.Value = comboBox.SelectedItem; }; 25 | 26 | return comboBox; 27 | } 28 | 29 | // 实现抽象方法,返回 ComboBox.SelectedItemProperty 作为绑定的 DependencyProperty 30 | public override DependencyProperty GetDependencyProperty() => Selector.SelectedItemProperty; 31 | 32 | 33 | // 根据属性的字段名称,设置不同的ItemsSource 34 | private IEnumerable GetItemsSource(PropertyItem propertyItem) 35 | { 36 | // 根据字段名称,动态返回选项 37 | if (propertyItem.PropertyName == "Recognition") 38 | { 39 | return new List 40 | { 41 | "", 42 | "DirectHit", 43 | "TemplateMatch", 44 | "FeatureMatch", 45 | "ColorMatch", 46 | "OCR", 47 | "NeuralNetworkClassify", 48 | "NeuralNetworkDetect", 49 | "Custom" 50 | }; 51 | } 52 | 53 | if (propertyItem.PropertyName == "Action") 54 | { 55 | return new List 56 | { 57 | "", 58 | "DoNothing", 59 | "Click", 60 | "Swipe", 61 | "MultiSwipe", 62 | "Key", 63 | "Text", 64 | "StartApp", 65 | "StopApp", 66 | "StopTask", 67 | "Command", 68 | "Custom" 69 | }; 70 | } 71 | 72 | if (propertyItem.PropertyName == "OrderBy") 73 | { 74 | return new List 75 | { 76 | "", 77 | "Horizontal", 78 | "Vertical", 79 | "Score", 80 | "Random", 81 | "Area", 82 | "Length" 83 | }; 84 | } 85 | 86 | if (propertyItem.PropertyName == "Detector") 87 | { 88 | return new List 89 | { 90 | "", 91 | "SIFT", 92 | "KAZE", 93 | "AKAZE", 94 | "BRISK", 95 | "ORB" 96 | }; 97 | } 98 | 99 | // 如果没有匹配的字段名称,返回空选项 100 | return new List { "" }; 101 | } 102 | 103 | // 重写GetConverter来实现IValueConverter 104 | protected override IValueConverter GetConverter(PropertyItem propertyItem) => new NullStringConverter(); 105 | } -------------------------------------------------------------------------------- /Helper/Exceptions/MaaErrorHandleException.cs: -------------------------------------------------------------------------------- 1 | namespace MFAWPF.Helper.Exceptions; 2 | 3 | public class MaaErrorHandleException : Exception 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Helper/Exceptions/MaaStopException.cs: -------------------------------------------------------------------------------- 1 | namespace MFAWPF.Helper.Exceptions; 2 | 3 | public class MaaStopException : Exception 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Helper/GrowlHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using HandyControl.Controls; 3 | using HandyControl.Data; 4 | 5 | 6 | namespace MFAWPF.Helper; 7 | 8 | public static class GrowlHelper 9 | { 10 | public static void Warning(string message, string token = "") 11 | { 12 | DispatcherHelper.RunOnMainThread(() => 13 | { 14 | Growl.Warning(new GrowlInfo 15 | { 16 | IsCustom = true, 17 | Message = message, 18 | WaitTime = 3, 19 | Token = token, 20 | IconKey = ResourceToken.WarningGeometry, 21 | IconBrushKey = ResourceToken.WarningBrush, 22 | }); 23 | }); 24 | } 25 | 26 | public static void WarningGlobal(string message, string token = "") 27 | { 28 | HandyControl.Tools.DispatcherHelper.RunOnMainThread(() => 29 | { 30 | Growl.InfoGlobal(new GrowlInfo 31 | { 32 | IsCustom = true, 33 | Message = message, 34 | WaitTime = 3, 35 | IconKey = ResourceToken.WarningGeometry, 36 | IconBrushKey = ResourceToken.WarningBrush, 37 | Token = token 38 | }); 39 | }); 40 | } 41 | 42 | public static void Error(string message, string token = "") 43 | { 44 | DispatcherHelper.RunOnMainThread(() => 45 | { 46 | Growl.Info(new GrowlInfo 47 | { 48 | IsCustom = true, 49 | Message = message, 50 | WaitTime = 6, 51 | IconKey = ResourceToken.ErrorGeometry, 52 | IconBrushKey = ResourceToken.DangerBrush, 53 | Icon = null, 54 | Token = token 55 | }); 56 | 57 | }); 58 | } 59 | 60 | public static void ErrorGlobal(string message, string token = "") 61 | { 62 | DispatcherHelper.RunOnMainThread(() => 63 | { 64 | Growl.InfoGlobal(new GrowlInfo 65 | { 66 | IsCustom = true, 67 | Message = message, 68 | WaitTime = 6, 69 | IconKey = ResourceToken.ErrorGeometry, 70 | IconBrushKey = ResourceToken.DangerBrush, 71 | Icon = null, 72 | Token = token 73 | }); 74 | }); 75 | } 76 | public static void InfoGlobal(string message, string token = "") 77 | { 78 | DispatcherHelper.RunOnMainThread(() => 79 | { 80 | Growl.InfoGlobal(message); 81 | }); 82 | } 83 | 84 | public static void Info(string message, string token = "") 85 | { 86 | DispatcherHelper.RunOnMainThread(() => 87 | { 88 | Growl.Info(message); 89 | }); 90 | } 91 | 92 | public static void SuccessGlobal(string message, string token = "") 93 | { 94 | DispatcherHelper.RunOnMainThread(() => 95 | { 96 | Growl.SuccessGlobal(message); 97 | }); 98 | } 99 | 100 | public static void Success(string message, string token = "") 101 | { 102 | DispatcherHelper.RunOnMainThread(() => 103 | { 104 | Growl.Success(message); 105 | }); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Helper/HotKeyHelper.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.ValueType; 2 | using System.Runtime.InteropServices; 3 | using System.Security; 4 | using System.Windows; 5 | using System.Windows.Input; 6 | using System.Windows.Interop; 7 | 8 | namespace MFAWPF.Helper; 9 | 10 | public static class HotKeyHelper 11 | { 12 | #region Windows API 封装 13 | private const int WM_HOTKEY = 0x0312; 14 | private const int ERROR_HOTKEY_ALREADY_REGISTERED = 1409; 15 | private const int ERROR_ACCESS_DENIED = 5; 16 | 17 | [DllImport("user32.dll", SetLastError = true)] 18 | private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); 19 | 20 | [DllImport("user32.dll", SetLastError = true)] 21 | private static extern bool UnregisterHotKey(IntPtr hWnd, int id); 22 | 23 | [DllImport("kernel32", SetLastError = true)] 24 | private static extern ushort GlobalAddAtom(string lpString); 25 | 26 | [DllImport("kernel32", SetLastError = true)] 27 | private static extern ushort GlobalDeleteAtom(ushort nAtom); 28 | 29 | private static uint ConvertModifiers(ModifierKeys modifiers) 30 | { 31 | uint fsModifiers = 0; 32 | if ((modifiers & ModifierKeys.Alt) != 0) fsModifiers |= 0x0001; 33 | if ((modifiers & ModifierKeys.Control) != 0) fsModifiers |= 0x0002; 34 | if ((modifiers & ModifierKeys.Shift) != 0) fsModifiers |= 0x0004; 35 | if ((modifiers & ModifierKeys.Windows) != 0) fsModifiers |= 0x0008; 36 | return fsModifiers; 37 | } 38 | #endregion 39 | 40 | 41 | #region 热键管理 42 | private static readonly Dictionary _hotkeyCallbacks = new(); 43 | private static int _hotkeyIdCounter = 0x0000; 44 | private static readonly Dictionary, int> _registeredKeyCombos = new(); 45 | 46 | public static bool RegisterHotKey(Window? window, MFAHotKey hotKey, Action callback) 47 | { 48 | if (window == null || hotKey.IsTip) 49 | return false; 50 | 51 | var hWnd = new WindowInteropHelper(window).EnsureHandle(); 52 | var fsModifiers = ConvertModifiers(hotKey.Modifiers); 53 | var vk = (uint)KeyInterop.VirtualKeyFromKey(hotKey.Key); 54 | var keyCombo = Tuple.Create(fsModifiers, vk); 55 | 56 | if (_registeredKeyCombos.ContainsKey(keyCombo)) 57 | { 58 | LoggerService.LogWarning($"重复注册热键: {hotKey}"); 59 | return false; 60 | } 61 | 62 | var id = _hotkeyIdCounter++; 63 | if (!RegisterHotKey(hWnd, id, fsModifiers, vk)) 64 | { 65 | int errorCode = Marshal.GetLastWin32Error(); 66 | if (errorCode == ERROR_HOTKEY_ALREADY_REGISTERED) 67 | LoggerService.LogError($"热键已被其他程序占用: {hotKey}"); 68 | return false; 69 | } 70 | 71 | _registeredKeyCombos[keyCombo] = id; 72 | _hotkeyCallbacks[id] = callback; 73 | return true; 74 | } 75 | 76 | public static void UnregisterHotKey(Window? window, MFAHotKey hotKey) 77 | { 78 | if (window == null || hotKey.IsTip) 79 | return; 80 | 81 | var hWnd = new WindowInteropHelper(window).Handle; 82 | var fsModifiers = ConvertModifiers(hotKey.Modifiers); 83 | var vk = (uint)KeyInterop.VirtualKeyFromKey(hotKey.Key); 84 | var keyCombo = Tuple.Create(fsModifiers, vk); 85 | 86 | if (_registeredKeyCombos.TryGetValue(keyCombo, out int id)) 87 | { 88 | UnregisterHotKey(hWnd, id); 89 | 90 | _registeredKeyCombos.Remove(keyCombo); 91 | _hotkeyCallbacks.Remove(id); 92 | } 93 | } 94 | #endregion 95 | } -------------------------------------------------------------------------------- /Helper/IconHelper.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.IO; 3 | using System.Windows; 4 | using System.Windows.Media.Imaging; 5 | 6 | namespace MFAWPF.Helper; 7 | 8 | public class IconHelper 9 | { 10 | private static readonly Lazy LazyIcon = new(LoadIconWithFallback); 11 | public static BitmapSource ICON => LazyIcon.Value; 12 | 13 | private static BitmapSource LoadIconWithFallback() 14 | { 15 | try 16 | { 17 | string exeDirectory = AppContext.BaseDirectory; 18 | string iconPath = Path.Combine(exeDirectory, "logo.ico"); 19 | 20 | if (File.Exists(iconPath)) 21 | { 22 | using var fileStream = File.OpenRead(iconPath); 23 | return CreateBitmapSource(fileStream); 24 | } 25 | 26 | var sri = Application.GetResourceStream(new Uri("pack://application:,,,/logo.ico")); 27 | if (sri != null) 28 | { 29 | using var resourceStream = sri.Stream; 30 | return CreateBitmapSource(resourceStream); 31 | } 32 | 33 | LoggerService.LogWarning("未找到内嵌图标资源"); 34 | return CreateEmptyImage(); 35 | } 36 | catch (Exception ex) 37 | { 38 | LoggerService.LogError($"图标加载失败: {ex}"); 39 | return CreateEmptyImage(); 40 | } 41 | } 42 | 43 | private static BitmapSource CreateBitmapSource(Stream stream) 44 | { 45 | var decoder = BitmapDecoder.Create( 46 | stream, 47 | BitmapCreateOptions.PreservePixelFormat, 48 | BitmapCacheOption.OnLoad 49 | ); 50 | return decoder.Frames[0]; 51 | } 52 | 53 | private static BitmapSource CreateEmptyImage() 54 | { 55 | return new BitmapImage(); // 返回空图像避免NullReference 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Helper/LoggerService.cs: -------------------------------------------------------------------------------- 1 | using Serilog; 2 | 3 | namespace MFAWPF.Helper; 4 | 5 | public static class LoggerService 6 | { 7 | private static readonly ILogger Logger = new LoggerConfiguration() 8 | .WriteTo.File( 9 | $"logs/log-{DateTime.Now.ToString("yyyy-MM-dd")}.txt", 10 | outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}][{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger(); 11 | 12 | public static void LogInfo(string message) 13 | { 14 | Logger.Information(message); 15 | Console.WriteLine("[INFO]" + message); 16 | } 17 | 18 | public static void LogInfo(object message) 19 | { 20 | Logger.Information(message.ToString()); 21 | Console.WriteLine("[INFO]" + message); 22 | } 23 | public static void LogError(object e) 24 | { 25 | Logger.Error(e?.ToString() ?? string.Empty); 26 | 27 | Console.WriteLine("[ERROR]" + e); 28 | } 29 | 30 | public static void LogError(string message) 31 | { 32 | Logger.Error(message); 33 | Console.WriteLine("[ERROR]" + message); 34 | } 35 | 36 | public static void LogWarning(string message) 37 | { 38 | Logger.Warning(message); 39 | Console.WriteLine("[WARN]" + message); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Helper/MFAUrls.cs: -------------------------------------------------------------------------------- 1 | namespace MFAWPF.Helper; 2 | 3 | public static class MFAUrls 4 | { 5 | public const string GitHub = "https://github.com/SweetSmellFox/MFAWPF"; 6 | 7 | public const string GitHubIssues = $"{GitHub}/issues"; 8 | 9 | public const string NewIssueUri = $"{GitHubIssues}/new?assignees=&labels=bug&template=cn-bug-report.yaml"; 10 | 11 | public const string PurchaseLink = "https://mirrorchyan.com"; 12 | } 13 | -------------------------------------------------------------------------------- /Helper/Notification/INotificationPoster.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MaaWpfGui - A part of the MaaCoreArknights project 3 | // Copyright (C) 2021 MistEO and Contributors 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Affero General Public License v3.0 only as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY 12 | // 13 | 14 | 15 | using System; 16 | 17 | namespace MFAWPF.Helper.Notification; 18 | 19 | internal interface INotificationPoster 20 | { 21 | void ShowNotification(NotificationContent content); 22 | 23 | event EventHandler ActionActivated; 24 | } 25 | -------------------------------------------------------------------------------- /Helper/Notification/NotificationAction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MaaWpfGui - A part of the MaaCoreArknights project 3 | // Copyright (C) 2021 MistEO and Contributors 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Affero General Public License v3.0 only as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY 12 | // 13 | 14 | namespace MFAWPF.Helper.Notification; 15 | 16 | internal record class NotificationAction(string Label, string Tag); 17 | -------------------------------------------------------------------------------- /Helper/Notification/NotificationContent.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MaaWpfGui - A part of the MaaCoreArknights project 3 | // Copyright (C) 2021 MistEO and Contributors 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Affero General Public License v3.0 only as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY 12 | // 13 | 14 | using System.Collections.Generic; 15 | 16 | namespace MFAWPF.Helper.Notification; 17 | 18 | internal class NotificationContent 19 | { 20 | public string Summary { get; set; } 21 | 22 | public string Body { get; set; } 23 | 24 | private readonly List _actions = []; 25 | 26 | public IList Actions => _actions; 27 | 28 | private readonly List _hints = []; 29 | 30 | public IList Hints => _hints; 31 | } 32 | -------------------------------------------------------------------------------- /Helper/Notification/NotificationHint.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MaaWpfGui - A part of the MaaCoreArknights project 3 | // Copyright (C) 2021 MistEO and Contributors 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Affero General Public License v3.0 only as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY 12 | // 13 | 14 | using System; 15 | 16 | namespace MFAWPF.Helper.Notification; 17 | 18 | public class NotificationHint 19 | { 20 | public static NotificationHint Expandable { get; } = new NotificationHint(); 21 | 22 | public static NotificationHint NewVersion { get; } = new NotificationHint(); 23 | 24 | internal class NotificationHintRowCount : NotificationHint 25 | { 26 | public int Value { get; } 27 | 28 | public NotificationHintRowCount(int rowCount) 29 | { 30 | Value = rowCount; 31 | } 32 | } 33 | 34 | internal class NotificationHintExpirationTime : NotificationHint 35 | { 36 | public TimeSpan Value { get; } 37 | 38 | public NotificationHintExpirationTime(TimeSpan value) 39 | { 40 | Value = value; 41 | } 42 | } 43 | 44 | public static NotificationHint RowCount(int rowCount) => new NotificationHintRowCount(rowCount); 45 | 46 | public static NotificationHint ExpirationTime(TimeSpan value) => new NotificationHintExpirationTime(value); 47 | } 48 | -------------------------------------------------------------------------------- /Helper/Notification/NotificationImplWinRT.cs: -------------------------------------------------------------------------------- 1 | // 2 | // MaaWpfGui - A part of the MaaCoreArknights project 3 | // Copyright (C) 2021 MistEO and Contributors 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Affero General Public License v3.0 only as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY 12 | // 13 | 14 | using MFAWPF.Helper; 15 | using System; 16 | using System.Windows; 17 | using Microsoft.Toolkit.Uwp.Notifications; 18 | using Notification.Wpf; 19 | using Serilog; 20 | using Windows.UI.Notifications; 21 | using ToastNotification = MFAWPF.Helper.ToastNotification; 22 | 23 | 24 | namespace MFAWPF.Helper.Notification; 25 | 26 | internal class NotificationImplWinRT : INotificationPoster, IDisposable 27 | { 28 | public event EventHandler? ActionActivated; 29 | 30 | public NotificationImplWinRT() 31 | { 32 | ToastNotificationManagerCompat.OnActivated += OnWinRTActivated; 33 | } 34 | 35 | public void Dispose() 36 | { 37 | ToastNotificationManagerCompat.OnActivated -= OnWinRTActivated; 38 | 39 | // ToastNotificationManagerCompat.History.Clear(); 40 | } 41 | 42 | private void OnWinRTActivated(ToastNotificationActivatedEventArgsCompat args) 43 | { 44 | ActionActivated?.Invoke(this, args.Argument); 45 | } 46 | 47 | public void ShowNotification(NotificationContent content) 48 | { 49 | try 50 | { 51 | DispatcherHelper.RunOnMainThread(() => 52 | { 53 | 54 | var builder = new ToastContentBuilder().AddText(content.Body).AddText(content.Summary); 55 | 56 | foreach (var action in content.Actions) 57 | { 58 | builder.AddButton(new ToastButton() 59 | .SetContent(action.Label) 60 | .AddArgument(action.Tag)); 61 | } 62 | builder.Show(); 63 | }); 64 | } 65 | catch (Exception e) 66 | { 67 | LoggerService.LogError(e); 68 | // ignored 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Helper/ServiceProviderExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System.Windows.Markup; 3 | 4 | namespace MFAWPF.Helper; 5 | 6 | public class ServiceProviderExtension : MarkupExtension 7 | { 8 | public Type ServiceType { get; set; } 9 | 10 | public override object ProvideValue(IServiceProvider serviceProvider) 11 | { 12 | return App.Services.GetRequiredService(ServiceType); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Helper/SimpleEncryptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace MFAWPF.Helper; 5 | 6 | public static class SimpleEncryptionHelper 7 | { 8 | 9 | public static string Encrypt(string plainText, DataProtectionScope dataProtectionScope = DataProtectionScope.CurrentUser) 10 | { 11 | if (string.IsNullOrEmpty(plainText)) 12 | { 13 | return string.Empty; 14 | } 15 | 16 | try 17 | { 18 | var data = Encoding.UTF8.GetBytes(plainText); 19 | var encryptedData = ProtectedData.Protect(data, null, dataProtectionScope); 20 | return Convert.ToBase64String(encryptedData); 21 | } 22 | catch (Exception e) 23 | { 24 | LoggerService.LogError("Failed to encrypt text: " + e.Message); 25 | return plainText; 26 | } 27 | } 28 | 29 | public static string Decrypt(string encryptedText, DataProtectionScope dataProtectionScope = DataProtectionScope.CurrentUser) 30 | { 31 | if (string.IsNullOrEmpty(encryptedText)) 32 | { 33 | return string.Empty; 34 | } 35 | 36 | try 37 | { 38 | var data = Convert.FromBase64String(encryptedText); 39 | var decryptedData = ProtectedData.Unprotect(data, null, dataProtectionScope); 40 | return Encoding.UTF8.GetString(decryptedData); 41 | } 42 | catch (Exception e) 43 | { 44 | LoggerService.LogError("Failed to decrypt text: " + e.Message); 45 | return encryptedText; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Helper/ThemeHelper.cs: -------------------------------------------------------------------------------- 1 | using HandyControl.Themes; 2 | using System.Runtime.InteropServices; 3 | using Microsoft.Win32; 4 | 5 | namespace MFAWPF.Helper; 6 | 7 | public class ThemeHelper 8 | { 9 | public static void UpdateThemeIndexChanged(int value) 10 | { 11 | switch (value) 12 | { 13 | case 0: 14 | ThemeManager.Current.UsingWindowsAppTheme = false; 15 | ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light; 16 | break; 17 | case 1: 18 | ThemeManager.Current.UsingWindowsAppTheme = false; 19 | ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark; 20 | break; 21 | default: 22 | ThemeManager.Current.UsingWindowsAppTheme = true; 23 | break; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Helper/ValueType/Attribute.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper.Converters; 2 | using Newtonsoft.Json; 3 | 4 | namespace MFAWPF.Helper.ValueType; 5 | 6 | public class Attribute 7 | { 8 | public string Key { get; set; } 9 | [JsonConverter(typeof(AutoConverter))] public object Value { get; set; } 10 | 11 | public Attribute(string key, object value) 12 | { 13 | Key = key; 14 | Value = value; 15 | } 16 | 17 | public Attribute() 18 | { 19 | } 20 | 21 | static string ConvertListToString(List> listOfLists) 22 | { 23 | var formattedLists = listOfLists 24 | .Select(innerList => $"[{string.Join(",", innerList)}]"); 25 | return string.Join(",", formattedLists); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | if (Value is List> lli) 31 | return $"\"{Key}\" : [{ConvertListToString(lli)}]"; 32 | 33 | if (Value is List li) 34 | return $"\"{Key}\" : [{string.Join(",", li)}]"; 35 | 36 | if (Value is List ls) 37 | return $"\"{Key}\" : [{string.Join(",", ls)}]"; 38 | 39 | if (Value is string s) 40 | return $"\"{Key}\" : \"{s}\""; 41 | 42 | return $"\"{Key}\" : {Value}"; 43 | } 44 | 45 | public string GetKey() 46 | { 47 | return $"{Key}"; 48 | } 49 | 50 | public string GetValue() 51 | { 52 | if (Value is List> lli) 53 | return $"{ConvertListToString(lli)}"; 54 | 55 | if (Value is List li) 56 | return $"{string.Join(",", li)}"; 57 | 58 | if (Value is List ls) 59 | return $"{string.Join(",", ls)}"; 60 | 61 | if (Value is string s) 62 | return s; 63 | 64 | return $"{Value}"; 65 | } 66 | 67 | public static bool operator ==(Attribute a1, object a2) 68 | { 69 | return a2 is Attribute attribute && a1?.Key?.Equals(attribute.Key) == true && a1.Value == attribute.Value; 70 | } 71 | 72 | public static bool operator !=(Attribute a1, object a2) 73 | { 74 | return a2 is not Attribute attribute || a1?.Key?.Equals(attribute.Key) != true || a1.Value != attribute.Value; 75 | } 76 | 77 | public override bool Equals(object obj) 78 | { 79 | return this == obj; 80 | } 81 | 82 | private int _cachedHashCode; 83 | 84 | public override int GetHashCode() 85 | { 86 | return HashCode.Combine(Key, Value); 87 | } 88 | } -------------------------------------------------------------------------------- /Helper/ValueType/CustomValue.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MFAWPF.Helper.ValueType; 4 | 5 | public class CustomValue : ObservableObject 6 | { 7 | public CustomValue(T value) 8 | { 9 | Value = value; 10 | } 11 | public CustomValue(string name,T value) 12 | { 13 | Name = name; 14 | Value = value; 15 | } 16 | 17 | private T _value; 18 | 19 | public T Value 20 | { 21 | get => _value; 22 | set => SetProperty(ref _value, value); 23 | } 24 | 25 | private string _name; 26 | 27 | public string Name 28 | { 29 | get => _name; 30 | set => SetProperty(ref _name, value); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return Value?.ToString() ?? string.Empty; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Helper/ValueType/MFATask.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MaaFramework.Binding; 3 | using MFAWPF.Extensions.Maa; 4 | using MFAWPF.Views.UI; 5 | 6 | namespace MFAWPF.Helper.ValueType; 7 | 8 | public partial class MFATask : ObservableObject 9 | { 10 | public enum MFATaskType 11 | { 12 | MFA, 13 | MAAFW 14 | } 15 | 16 | [ObservableProperty] private string? _name = string.Empty; 17 | [ObservableProperty] private MFATaskType _type = MFATaskType.MFA; 18 | [ObservableProperty] private int _count = 1; 19 | [ObservableProperty] private Func _action; 20 | [ObservableProperty] private Dictionary _tasks = new(); 21 | 22 | 23 | public async Task Run(CancellationToken token) 24 | { 25 | try 26 | { 27 | for (int i = 0; i < Count; i++) 28 | { 29 | token.ThrowIfCancellationRequested(); 30 | if (Type == MFATaskType.MAAFW) 31 | RootView.AddLogByKey("TaskStart", null,true, Name ?? string.Empty); 32 | await Action(); 33 | } 34 | return true; 35 | } 36 | catch (OperationCanceledException) 37 | { 38 | return false; 39 | } 40 | catch (Exception ex) 41 | { 42 | Console.WriteLine($"捕获异常: {ex.Message}"); 43 | return false; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Helper/ValueType/ObservableQueue.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MFAWPF.Helper.ValueType; 4 | 5 | public partial class ObservableQueue : ObservableObject 6 | { 7 | private readonly Queue _queue = new(); 8 | 9 | [ObservableProperty] private int _count; 10 | 11 | public EventHandler? CountChanged; 12 | 13 | public ObservableQueue() 14 | { 15 | Count = _queue.Count; 16 | } 17 | partial void OnCountChanged(int oldValue, int newValue) 18 | { 19 | CountChanged?.Invoke(this, new CountChangedEventArgs(oldValue, newValue)); 20 | } 21 | 22 | public void Enqueue(T task) 23 | { 24 | _queue.Enqueue(task); 25 | Count = _queue.Count; 26 | } 27 | 28 | public T Dequeue() 29 | { 30 | var task = _queue.Dequeue(); 31 | Count = _queue.Count; 32 | return task; 33 | } 34 | 35 | public void Clear() 36 | { 37 | _queue.Clear(); 38 | Count = _queue.Count; 39 | } 40 | 41 | public class CountChangedEventArgs(int oldValue, int newValue) : EventArgs 42 | { 43 | public int OldValue => oldValue; 44 | public int NewValue => newValue; 45 | } 46 | } -------------------------------------------------------------------------------- /Helper/ValueType/TaskInterfaceItem.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Extensions.Maa; 2 | using MFAWPF.Helper.Converters; 3 | using Newtonsoft.Json; 4 | 5 | namespace MFAWPF.Helper.ValueType; 6 | 7 | public class TaskInterfaceItem 8 | { 9 | [JsonProperty("name")] public string? Name; 10 | [JsonProperty("entry")] public string? Entry; 11 | [JsonConverter(typeof(SingleOrListConverter))] [JsonProperty("doc")] 12 | public List? Document; 13 | [JsonProperty("check", 14 | NullValueHandling = NullValueHandling.Include, 15 | DefaultValueHandling = DefaultValueHandling.Include)] 16 | public bool? Check = false; 17 | [JsonProperty("repeatable")] public bool? Repeatable; 18 | [JsonProperty("repeat_count")] public int? RepeatCount; 19 | 20 | [JsonProperty("advanced")] public List? Advanced; 21 | 22 | [JsonProperty("option")] public List? Option; 23 | 24 | [JsonProperty("pipeline_override")] public Dictionary? PipelineOverride; 25 | 26 | public override string ToString() 27 | { 28 | var settings = new JsonSerializerSettings 29 | { 30 | Formatting = Formatting.Indented, 31 | NullValueHandling = NullValueHandling.Ignore, 32 | DefaultValueHandling = DefaultValueHandling.Ignore 33 | }; 34 | 35 | return JsonConvert.SerializeObject(this, settings); 36 | } 37 | 38 | /// 39 | /// Creates a deep copy of the current instance. 40 | /// 41 | /// A new instance that is a deep copy of the current instance. 42 | public TaskInterfaceItem Clone() 43 | { 44 | return JsonConvert.DeserializeObject(ToString()) ?? new TaskInterfaceItem(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Helper/ValueType/ValueBoxes.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace MFAWPF.Helper.ValueType; 5 | 6 | internal static class ValueBoxes 7 | { 8 | internal static object TrueBox = true; 9 | 10 | internal static object FalseBox = false; 11 | 12 | internal static object VerticalBox = Orientation.Vertical; 13 | 14 | internal static object HorizontalBox = Orientation.Horizontal; 15 | 16 | internal static object VisibleBox = Visibility.Visible; 17 | 18 | internal static object CollapsedBox = Visibility.Collapsed; 19 | 20 | internal static object HiddenBox = Visibility.Hidden; 21 | 22 | internal static object Double01Box = .1; 23 | 24 | internal static object Double0Box = .0; 25 | 26 | internal static object Double1Box = 1.0; 27 | 28 | internal static object Double10Box = 10.0; 29 | 30 | internal static object Double20Box = 20.0; 31 | 32 | internal static object Double100Box = 100.0; 33 | 34 | internal static object Double200Box = 200.0; 35 | 36 | internal static object Double300Box = 300.0; 37 | 38 | internal static object DoubleNeg1Box = -1.0; 39 | 40 | internal static object Int0Box = 0; 41 | 42 | internal static object Int1Box = 1; 43 | 44 | internal static object Int2Box = 2; 45 | 46 | internal static object Int5Box = 5; 47 | 48 | internal static object Int99Box = 99; 49 | 50 | internal static object BooleanBox(bool value) => value ? TrueBox : FalseBox; 51 | 52 | internal static object OrientationBox(Orientation value) => 53 | value == Orientation.Horizontal ? HorizontalBox : VerticalBox; 54 | 55 | internal static object VisibilityBox(Visibility value) 56 | { 57 | return value switch 58 | { 59 | Visibility.Visible => VisibleBox, 60 | Visibility.Hidden => HiddenBox, 61 | Visibility.Collapsed => CollapsedBox, 62 | _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) 63 | }; 64 | } 65 | } -------------------------------------------------------------------------------- /MFAWPF.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MFAWPF", "MFAWPF.csproj", "{B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Debug|ARM64 = Debug|ARM64 9 | Debug|x64 = Debug|x64 10 | Release|Any CPU = Release|Any CPU 11 | Release|ARM64 = Release|ARM64 12 | Release|x64 = Release|x64 13 | RelWithDebInfo|Any CPU = RelWithDebInfo|Any CPU 14 | RelWithDebInfo|ARM64 = RelWithDebInfo|ARM64 15 | RelWithDebInfo|x64 = RelWithDebInfo|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|ARM64.ActiveCfg = Debug|ARM64 21 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|ARM64.Build.0 = Debug|ARM64 22 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|x64.ActiveCfg = Debug|x64 23 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Debug|x64.Build.0 = Debug|x64 24 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|ARM64.ActiveCfg = Release|ARM64 27 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|ARM64.Build.0 = Release|ARM64 28 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|x64.ActiveCfg = Release|x64 29 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.Release|x64.Build.0 = Release|x64 30 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|Any CPU 31 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|Any CPU.Build.0 = RelWithDebInfo|Any CPU 32 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64 33 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64 34 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 35 | {B9FDFBC8-AA6F-49C9-A7BB-A6AE1FCB4476}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "MFAWPF": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /Resource/base/model/ocr/README.md: -------------------------------------------------------------------------------- 1 | # PaddleOCR model 2 | 3 | 2023/09/29 4 | 5 | from 6 | 7 | ## det model 8 | 9 | ch_PP-OCRv4_det 10 | 【最新】原始超轻量模型,支持中英文、多语种文本检测 11 | 12 | 13 | 14 | ## rec model 15 | 16 | ch_PP-OCRv4_rec 17 | 【最新】超轻量模型,支持中英文、数字识别 18 | 19 | 20 | 21 | ## rec label 22 | 23 | 24 | -------------------------------------------------------------------------------- /Resource/base/model/ocr/det.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAWPF/12e65484cf483e7bb68aca3e8e749406c52398c8/Resource/base/model/ocr/det.onnx -------------------------------------------------------------------------------- /Resource/base/model/ocr/rec.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAWPF/12e65484cf483e7bb68aca3e8e749406c52398c8/Resource/base/model/ocr/rec.onnx -------------------------------------------------------------------------------- /Services/ApplicationHostService.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Views; 2 | using MFAWPF.Views.UI; 3 | using Microsoft.Extensions.Hosting; 4 | using System.Windows; 5 | 6 | namespace MFAWPF.Services; 7 | 8 | public class ApplicationHostService(IServiceProvider serviceProvider) : IHostedService 9 | { 10 | private Window _window; 11 | 12 | /// 13 | /// Triggered when the application host is ready to start the service. 14 | /// 15 | /// Indicates that the start process has been aborted. 16 | public async Task StartAsync(CancellationToken cancellationToken) 17 | { 18 | await HandleActivationAsync(); 19 | } 20 | 21 | /// 22 | /// Triggered when the application host is performing a graceful shutdown. 23 | /// 24 | /// Indicates that the shutdown process should no longer be graceful. 25 | public async Task StopAsync(CancellationToken cancellationToken) 26 | { 27 | await Task.CompletedTask; 28 | } 29 | 30 | /// 31 | /// Creates main window during activation. 32 | /// 33 | async private Task HandleActivationAsync() 34 | { 35 | await Task.CompletedTask; 36 | 37 | if (!Application.Current.Windows.OfType().Any()) 38 | { 39 | _window = (serviceProvider.GetService(typeof(RootView)) as Window)!; 40 | _window!.Show(); 41 | 42 | } 43 | 44 | await Task.CompletedTask; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ViewModels/Tool/LocalizationViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MFAWPF.Extensions; 3 | using MFAWPF.Helper; 4 | 5 | namespace MFAWPF.ViewModels.Tool; 6 | 7 | public partial class LocalizationViewModel : ViewModel 8 | { 9 | [ObservableProperty] private string _resourceKey = string.Empty; 10 | 11 | partial void OnResourceKeyChanged(string value) 12 | { 13 | UpdateName(); 14 | } 15 | 16 | public LocalizationViewModel() { } 17 | 18 | private readonly string[]? _formatArgsKeys; 19 | 20 | public LocalizationViewModel(string resourceKey) 21 | { 22 | ResourceKey = resourceKey; 23 | LanguageHelper.LanguageChanged += OnLanguageChanged; 24 | } 25 | 26 | public LocalizationViewModel(string resourceKey, params string[] keys) 27 | { 28 | ResourceKey = resourceKey; 29 | _formatArgsKeys = keys; 30 | LanguageHelper.LanguageChanged += OnLanguageChanged; 31 | } 32 | private void OnLanguageChanged(object sender, EventArgs e) 33 | { 34 | UpdateName(); 35 | } 36 | [ObservableProperty] private string _name = string.Empty; 37 | [ObservableProperty] private object? _other; 38 | 39 | private void UpdateName() 40 | { 41 | if (string.IsNullOrWhiteSpace(ResourceKey)) 42 | return; 43 | if (_formatArgsKeys != null && _formatArgsKeys.Length != 0) 44 | Name = ResourceKey.ToLocalizationFormatted(true, _formatArgsKeys); 45 | else 46 | Name = ResourceKey.ToLocalization(); 47 | } 48 | 49 | 50 | public override string ToString() 51 | => ResourceKey; 52 | } 53 | -------------------------------------------------------------------------------- /ViewModels/Tool/TaskItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using MFAWPF.Helper; 2 | using MFAWPF.Helper.ValueType; 3 | using Newtonsoft.Json; 4 | 5 | namespace MFAWPF.ViewModels.Tool; 6 | 7 | public class TaskItemViewModel : ViewModel 8 | { 9 | private string _name = "未命名"; 10 | 11 | public string Name 12 | { 13 | get => _name; 14 | set 15 | { 16 | if (Task != null) 17 | Task.Name = value; 18 | SetProperty(ref _name, value); 19 | } 20 | } 21 | 22 | private TaskModel _task; 23 | 24 | /// 25 | /// Gets or sets the time. 26 | /// 27 | public TaskModel Task 28 | { 29 | get => _task; 30 | set 31 | { 32 | if (value != null) 33 | Name = value.Name; 34 | SetProperty(ref _task, value); 35 | } 36 | } 37 | 38 | public override string ToString() 39 | { 40 | var settings = new JsonSerializerSettings 41 | { 42 | Formatting = Formatting.Indented, 43 | NullValueHandling = NullValueHandling.Ignore, 44 | DefaultValueHandling = DefaultValueHandling.Ignore 45 | }; 46 | Dictionary taskModels = new Dictionary(); 47 | if (Task != null) 48 | { 49 | taskModels.Add(Name, Task); 50 | } 51 | 52 | return JsonConvert.SerializeObject(taskModels, settings); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /ViewModels/UI/AnnouncementViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using HandyControl.Tools.Extension; 3 | using MFAWPF.Configuration; 4 | using MFAWPF.Helper; 5 | using MFAWPF.Views.UI; 6 | using System.IO; 7 | using System.Windows; 8 | using Exception = System.Exception; 9 | 10 | namespace MFAWPF.ViewModels.UI; 11 | 12 | public partial class AnnouncementViewModel : ViewModel 13 | { 14 | public static readonly string AnnouncementFileName = "Announcement.md"; 15 | [ObservableProperty] private string _announcementInfo = string.Empty; 16 | 17 | [ObservableProperty] private bool _doNotRemindThisAnnouncementAgain = Convert.ToBoolean(GlobalConfiguration.GetValue(ConfigurationKeys.DoNotShowAgain, bool.FalseString)); 18 | partial void OnDoNotRemindThisAnnouncementAgainChanged(bool value) 19 | { 20 | GlobalConfiguration.SetValue(ConfigurationKeys.DoNotShowAgain, value.ToString()); 21 | } 22 | 23 | 24 | public void CheckAnnouncement() 25 | { 26 | if (DoNotRemindThisAnnouncementAgain) return; 27 | try 28 | { 29 | var resourcePath = Path.Combine(AppContext.BaseDirectory, "resource"); 30 | var mdPath = Path.Combine(resourcePath, AnnouncementFileName); 31 | 32 | if (File.Exists(mdPath)) 33 | { 34 | var content = File.ReadAllText(mdPath); 35 | AnnouncementInfo = content; 36 | } 37 | } 38 | catch (Exception ex) 39 | { 40 | LoggerService.LogError($"读取公告文件失败: {ex.Message}"); 41 | AnnouncementInfo = ""; 42 | } 43 | finally 44 | { 45 | 46 | if (!string.IsNullOrWhiteSpace(AnnouncementInfo) && !AnnouncementInfo.Trim().Equals("placeholder", StringComparison.OrdinalIgnoreCase)) 47 | { 48 | var announcementView = new AnnouncementView(); 49 | announcementView.Show(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ViewModels/UI/Dialog/AddTaskDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace MFAWPF.ViewModels.UI.Dialog; 4 | 5 | public class AddTaskDialogViewModel: ViewModel 6 | { 7 | private ObservableCollection _dataList = new (); 8 | 9 | public ObservableCollection DataList 10 | { 11 | get => _dataList; 12 | set => SetProperty(ref _dataList, value); 13 | } 14 | 15 | private int _selectedIndex ; 16 | 17 | public int SelectedIndex 18 | { 19 | get => _selectedIndex; 20 | set => 21 | SetProperty(ref _selectedIndex, value); 22 | } 23 | 24 | 25 | public AddTaskDialogViewModel() 26 | { 27 | SelectedIndex = -1; 28 | } 29 | } -------------------------------------------------------------------------------- /ViewModels/UI/RootViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using HandyControl.Controls; 3 | using HandyControl.Data; 4 | using HandyControl.Tools.Command; 5 | using MFAWPF.Configuration; 6 | using MFAWPF.Helper; 7 | using MFAWPF.ViewModels.Tool; 8 | using System.Collections.ObjectModel; 9 | using System.Text.RegularExpressions; 10 | using System.Windows; 11 | using System.Windows.Media; 12 | 13 | namespace MFAWPF.ViewModels.UI; 14 | 15 | public partial class RootViewModel : ViewModel 16 | { 17 | [ObservableProperty] private bool _idle = true; 18 | 19 | [ObservableProperty] private bool _lockController = true; 20 | 21 | [ObservableProperty] private bool _isRunning; 22 | partial void OnIsRunningChanged(bool value) 23 | { 24 | Idle = !value; 25 | } 26 | 27 | public void SetIdle(bool value) 28 | { 29 | Idle = value; 30 | } 31 | 32 | [ObservableProperty] private bool _enableEdit = ConfigurationHelper.GetValue(ConfigurationKeys.EnableEdit, false); 33 | 34 | partial void OnEnableEditChanged(bool value) 35 | { 36 | ConfigurationHelper.SetValue(ConfigurationKeys.EnableEdit, value); 37 | } 38 | 39 | [ObservableProperty] private bool _isUpdating; 40 | 41 | [ObservableProperty] private bool _isVisible = true; 42 | 43 | public void SetUpdating(bool isUpdating) 44 | { 45 | IsUpdating = isUpdating; 46 | } 47 | public void ToggleVisible() 48 | { 49 | IsVisible = !IsVisible; 50 | } 51 | 52 | partial void OnIsVisibleChanged(bool value) 53 | { 54 | if (value) 55 | { 56 | Application.Current.MainWindow?.Show(); 57 | } 58 | else 59 | { 60 | Application.Current.MainWindow?.Hide(); 61 | } 62 | } 63 | 64 | 65 | 66 | [ObservableProperty] private string? _resourceName; 67 | 68 | [ObservableProperty] private bool _isResourceNameVisible; 69 | 70 | [ObservableProperty] private string? _resourceVersion; 71 | 72 | [ObservableProperty] private bool _isResourceVersionVisible; 73 | 74 | [ObservableProperty] private string? _customTitle; 75 | 76 | [ObservableProperty] private bool _isCustomTitleVisible; 77 | 78 | [ObservableProperty] private bool _isDefaultTitleVisible = true; 79 | 80 | [ObservableProperty] private bool _isVersionVisible = true; 81 | 82 | public void ShowResourceName(string name) 83 | { 84 | ResourceName = name; 85 | IsResourceNameVisible = true; 86 | } 87 | 88 | public void ShowResourceVersion(string version) 89 | { 90 | ResourceVersion = version; 91 | IsResourceVersionVisible = true; 92 | } 93 | 94 | public void ShowCustomTitle(string title) 95 | { 96 | CustomTitle = title; 97 | IsCustomTitleVisible = true; 98 | IsDefaultTitleVisible = false; 99 | IsVersionVisible = false; 100 | IsResourceNameVisible = false; 101 | IsResourceVersionVisible = false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ViewModels/UserControl/Settings/GameSettingsUserControlModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MaaFramework.Binding; 3 | using MFAWPF.Configuration; 4 | using MFAWPF.Extensions; 5 | using MFAWPF.Extensions.Maa; 6 | using MFAWPF.Helper; 7 | using System.Collections.ObjectModel; 8 | using System.Windows; 9 | 10 | namespace MFAWPF.ViewModels.UserControl.Settings; 11 | 12 | public partial class GameSettingsUserControlModel : ViewModel 13 | { 14 | [ObservableProperty] private bool _enableRecording = ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.Recording, false); 15 | 16 | [ObservableProperty] private bool _enableSaveDraw = ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.SaveDraw, false); 17 | 18 | [ObservableProperty] private bool _showHitDraw = ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.ShowHitDraw, false); 19 | 20 | [ObservableProperty] private string _prescript = ConfigurationHelper.GetValue(ConfigurationKeys.Prescript, string.Empty); 21 | 22 | [ObservableProperty] private string _postScript = ConfigurationHelper.GetValue(ConfigurationKeys.Postscript, string.Empty); 23 | 24 | [ObservableProperty] private bool _isDebugMode = ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.Recording, false) 25 | || ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.SaveDraw, false) 26 | || ConfigurationHelper.MaaConfig.GetConfig(ConfigurationKeys.ShowHitDraw, false); 27 | private bool _shouldTip = true; 28 | 29 | partial void OnIsDebugModeChanged(bool value) 30 | { 31 | if (value && _shouldTip) 32 | { 33 | MessageBoxHelper.Show("DebugModeWarning".ToLocalization(), "Tip".ToLocalization(), MessageBoxButton.OK, MessageBoxImage.Warning); 34 | _shouldTip = false; 35 | } 36 | } 37 | 38 | partial void OnEnableRecordingChanged(bool value) 39 | { 40 | ConfigurationHelper.MaaConfig.SetConfig(ConfigurationKeys.Recording, value); 41 | MaaProcessor.MaaUtility.SetOption_Recording(value); 42 | IsDebugMode = EnableSaveDraw || EnableRecording || ShowHitDraw; 43 | } 44 | 45 | partial void OnEnableSaveDrawChanged(bool value) 46 | { 47 | ConfigurationHelper.MaaConfig.SetConfig(ConfigurationKeys.SaveDraw, value); 48 | MaaProcessor.MaaUtility.SetOption_SaveDraw(value); 49 | IsDebugMode = EnableSaveDraw || EnableRecording || ShowHitDraw; 50 | } 51 | 52 | partial void OnShowHitDrawChanged(bool value) 53 | { 54 | ConfigurationHelper.MaaConfig.SetConfig(ConfigurationKeys.ShowHitDraw, value); 55 | MaaProcessor.MaaUtility.SetOption_ShowHitDraw(value); 56 | IsDebugMode = EnableSaveDraw || EnableRecording || ShowHitDraw; 57 | } 58 | 59 | partial void OnPrescriptChanged(string value) 60 | { 61 | ConfigurationHelper.SetValue(ConfigurationKeys.Prescript, value); 62 | } 63 | 64 | partial void OnPostScriptChanged(string value) 65 | { 66 | ConfigurationHelper.SetValue(ConfigurationKeys.Postscript, value); 67 | } 68 | 69 | [ObservableProperty] private ObservableCollection _currentResources = []; 70 | 71 | [ObservableProperty] private string _currentResource = ConfigurationHelper.GetValue(ConfigurationKeys.Resource, string.Empty); 72 | 73 | partial void OnCurrentResourceChanged(string value) 74 | { 75 | ConfigurationHelper.SetValue(ConfigurationKeys.Resource, value); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ViewModels/UserControl/Settings/GuiSettingsUserControlModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MFAWPF.Configuration; 3 | using MFAWPF.Helper; 4 | using System.Collections.ObjectModel; 5 | 6 | namespace MFAWPF.ViewModels.UserControl.Settings; 7 | 8 | public partial class GuiSettingsUserControlModel: ViewModel 9 | { 10 | public ObservableCollection SupportedLanguages => LanguageHelper.SupportedLanguages; 11 | 12 | [ObservableProperty] private int _languageIndex = ConfigurationHelper.GetValue(ConfigurationKeys.LangIndex, 0); 13 | 14 | partial void OnLanguageIndexChanged(int value) 15 | { 16 | LanguageHelper.ChangeLanguage(SupportedLanguages[value]); 17 | ConfigurationHelper.SetValue(ConfigurationKeys.LangIndex, value); 18 | } 19 | 20 | [ObservableProperty] private ObservableCollection _themes = 21 | [ 22 | new("LightColor"), 23 | new("DarkColor"), 24 | new("FollowingSystem"), 25 | ]; 26 | 27 | [ObservableProperty] private int _themeIndex = ConfigurationHelper.GetValue(ConfigurationKeys.ThemeIndex, 0); 28 | 29 | partial void OnThemeIndexChanged(int value) 30 | { 31 | ThemeHelper.UpdateThemeIndexChanged(value); 32 | ConfigurationHelper.SetValue(ConfigurationKeys.ThemeIndex, value); 33 | } 34 | 35 | private bool _shouldMinimizeToTray = ConfigurationHelper.GetValue(ConfigurationKeys.ShouldMinimizeToTray, false); 36 | 37 | public bool ShouldMinimizeToTray 38 | { 39 | set 40 | { 41 | SetProperty(ref _shouldMinimizeToTray, value); 42 | ConfigurationHelper.SetValue(ConfigurationKeys.ShouldMinimizeToTray, value); 43 | } 44 | get => _shouldMinimizeToTray; 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /ViewModels/UserControl/Settings/PerformanceUserControlModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MaaFramework.Binding; 3 | using MFAWPF.Configuration; 4 | using MFAWPF.Helper.Converters; 5 | using System.Collections.ObjectModel; 6 | 7 | namespace MFAWPF.ViewModels.UserControl.Settings; 8 | 9 | public partial class PerformanceUserControlModel : ViewModel 10 | { 11 | public static ObservableCollection GpuOptions => 12 | [ 13 | new("GpuOptionAuto") 14 | { 15 | Other = InferenceDevice.Auto 16 | }, 17 | new("GpuOptionDisable") 18 | { 19 | Other = InferenceDevice.CPU 20 | } 21 | ]; 22 | 23 | [ObservableProperty] private InferenceDevice _gpuOption = ConfigurationHelper.GetValue(ConfigurationKeys.GPUOption, InferenceDevice.Auto, new UniversalEnumConverter()); 24 | 25 | partial void OnGpuOptionChanged(InferenceDevice value) 26 | { 27 | ConfigurationHelper.SetValue(ConfigurationKeys.GPUOption, value.ToString()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ViewModels/UserControl/Settings/StartSettingsUserControlModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using MFAWPF.Configuration; 4 | using MFAWPF.Extensions; 5 | using Microsoft.Win32; 6 | 7 | namespace MFAWPF.ViewModels.UserControl.Settings; 8 | 9 | public partial class StartSettingsUserControlModel: ViewModel 10 | { 11 | [ObservableProperty] private bool _autoMinimize = ConfigurationHelper.GetValue(ConfigurationKeys.AutoMinimize, false); 12 | 13 | [ObservableProperty] private bool _autoHide = ConfigurationHelper.GetValue(ConfigurationKeys.AutoHide, false); 14 | 15 | [ObservableProperty] private string _softwarePath = ConfigurationHelper.GetValue(ConfigurationKeys.SoftwarePath, string.Empty); 16 | 17 | [ObservableProperty] private string _emulatorConfig = ConfigurationHelper.GetValue(ConfigurationKeys.EmulatorConfig, string.Empty); 18 | 19 | [ObservableProperty] private double _waitSoftwareTime = ConfigurationHelper.GetValue(ConfigurationKeys.WaitSoftwareTime, 60.0); 20 | 21 | 22 | partial void OnAutoMinimizeChanged(bool value) 23 | { 24 | ConfigurationHelper.SetValue(ConfigurationKeys.AutoMinimize, value); 25 | } 26 | 27 | partial void OnAutoHideChanged(bool value) 28 | { 29 | ConfigurationHelper.SetValue(ConfigurationKeys.AutoHide, value); 30 | } 31 | 32 | partial void OnSoftwarePathChanged(string value) 33 | { 34 | ConfigurationHelper.SetValue(ConfigurationKeys.SoftwarePath, value); 35 | } 36 | 37 | partial void OnEmulatorConfigChanged(string value) 38 | { 39 | ConfigurationHelper.SetValue(ConfigurationKeys.EmulatorConfig, value); 40 | } 41 | 42 | partial void OnWaitSoftwareTimeChanged(double value) 43 | { 44 | ConfigurationHelper.SetValue(ConfigurationKeys.WaitSoftwareTime, value); 45 | } 46 | 47 | 48 | [RelayCommand] 49 | private void SelectSoft() 50 | { 51 | var openFileDialog = new OpenFileDialog 52 | { 53 | Title = "SelectExecutableFile".ToLocalization(), 54 | Filter = "ExeFilter".ToLocalization() 55 | }; 56 | 57 | if (openFileDialog.ShowDialog().IsTrue()) 58 | { 59 | SoftwarePath = openFileDialog.FileName; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ViewModels/ViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MFAWPF.ViewModels; 6 | 7 | public class ViewModel : ObservableObject 8 | { 9 | protected bool SetCurrentProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string propertyName = null) 10 | { 11 | OnPropertyChanging(propertyName); 12 | field = newValue; 13 | OnPropertyChanged(propertyName); 14 | return true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Views/UI/AnnouncementView.xaml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 |