diff --git a/.editorconfig b/.editorconfig index ea0c3b57..b947be64 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,22 +1,167 @@ -[*.cs] +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org -# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -dotnet_diagnostic.CS8618.severity = silent +# This file is the top-most EditorConfig file +root = true -dotnet_diagnostic.CS8604.severity = silent +########################################## +# Common Settings +########################################## -dotnet_diagnostic.CS8622.severity = silent +[*] +indent_style = space +end_of_line = crlf +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 -dotnet_diagnostic.CS8625.severity = silent +########################################## +# File Extension Settings +########################################## -# CS8602: Dereference of a possibly null reference. -dotnet_diagnostic.CS8602.severity = silent +[*.{yml,yaml}] +indent_size = 2 -# CS8603: Possible null reference return. -dotnet_diagnostic.CS8603.severity = silent +[.vsconfig] +indent_size = 2 +end_of_line = lf -# CS0252: Possible unintended reference comparison; left hand side needs cast -dotnet_diagnostic.CS0252.severity = silent +[*.sln] +indent_style = tab +indent_size = 2 -# CS8600: Converting null literal or possible null value to non-nullable type. -dotnet_diagnostic.CS8600.severity = silent +[*.{csproj,proj,projitems,shproj}] +indent_size = 2 + +[*.{json,slnf}] +indent_size = 2 +end_of_line = lf + +[*.{props,targets}] +indent_size = 2 + +[*.xaml] +indent_size = 2 +charset = utf-8-bom + +[*.xml] +indent_size = 2 +end_of_line = lf + +[*.plist] +indent_size = 2 +indent_style = tab +end_of_line = lf + +[*.manifest] +indent_size = 2 + +[*.appxmanifest] +indent_size = 2 + +[*.{json,css,webmanifest}] +indent_size = 2 +end_of_line = lf + +[web.config] +indent_size = 2 +end_of_line = lf + +[*.sh] +indent_size = 2 +end_of_line = lf + +[*.cs] +# EOL should be normalized by Git. See https://github.com/dotnet/format/issues/1099 +end_of_line = unset + +# See https://github.com/dotnet/roslyn/issues/20356#issuecomment-310143926 +trim_trailing_whitespace = false + +tab_width = 4 +indent_size = 4 + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion + +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 76877e92..f0774448 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,52 +1,83 @@ -name: 🐛 Bug Report -description: Create a bug report to help improve Emerald -labels: [bug] -title: "🐛 " +name: 🐞 Bug +title: '[BUG] ' +description: File a bug report +labels: ["bug", "to verify"] +assignees: [] body: - - type: checkboxes + - type: markdown attributes: - label: ⏱️ Before you start... - options: - - label: Are you on the latest version of Emerald? You might be using an old version. - required: true - - label: Have you checked whether or not a similar issue has already been reported? - required: true - - type: textarea - attributes: - label: 🪟 What build of Windows is this issue present? (optional) - description: Windows key + R > winver.exe - placeholder: build 25174.1010 + value: | + Thanks for taking the time to fill out this bug report! Please make sure to add as much detail as you can. - type: textarea + id: description attributes: - label: 🔢 What version of Emerald are you on? - description: Go to Emerald > Settings > About + label: Description + description: Please give a detailed description of the issue that you're seeing. You can add screenshots and videos as well. + placeholder: Tell us what you see! validations: required: true - type: textarea + id: repro-steps attributes: - label: 📄 Description - description: A clear and concise description of what the bug is. + label: Steps to Reproduce + description: Describe all the steps we need to take to show the behavior that you have observed. Also, include what you expected to happen and what did actually happen. + placeholder: | + 1. Create a File > New App + 2. Add a `Button` like so: `<Button Text="this is a bug" />` + 3. Click the added button and observe the bug 🐞 + + Expected outcome: a bug was added + Actual outcome: a ladybug appeared validations: required: true - - type: textarea + - type: dropdown + id: platform-with-bug attributes: - label: 🪜 Steps To Reproduce - description: Steps to reproduce the behavior. - placeholder: | - 1. Go to '....' - 2. Click on '....' - 3. Scroll down to '....' - 4. See the error + label: Platform with bug + description: What Platform is this bug affecting? + options: + - Core + - Android + - iOS + - MacCatalyst + - Windows + - Skia + - WebAssembly + - Other validations: - required: false - - type: textarea + required: true + + - type: dropdown + id: platforms-affected attributes: - label: 🤔 Expected behavior - description: A clear and concise description of what you expected to happen. + label: Affected platforms + description: Select all or any platform that you see this issue on. This helps us determine if it's something platform-specific or in the core. If you were only able to test on 1 platform, please check the last option to inform us about that. + multiple: true + options: + - Android + - iOS + - MacOS + - MacCatalyst + - Windows + - GTK + - Linux Framebuffer + - WPF + - Skia Desktop + - WebAssembly + - Other + - I was *not* able test on other platforms validations: required: true + + - type: textarea + id: workaround + attributes: + label: Did you find any workaround? + description: Did you find any workaround for this issue? This can unblock other people while waiting for this issue to be resolved or even give us a hint on how to fix this. + - type: textarea + id: logs attributes: - label: 📸 Assets - description: | - A list of assets (errors, screenshots) relevant to the bug. + label: Relevant log output + description: Please copy and paste any relevant log output. This will be auomatically formatted into code, so no need for back ticks. + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 96fd262d..47b3fe78 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,44 +1,39 @@ -name: ✨ Feature Request -description: Request a new feature for Emerald -labels: [feature request] -title: "✨ " +name: Feature Request +description: Suggest an idea for this project +labels: ["enhancement"] +assignees: [] body: - type: markdown attributes: value: | - ##### ⏱️ Before you start... - - [ ] Have you checked whether or not a similar feature request has already been reported? + Fill in this template with some detail. A detailed description, perhaps supported with some screenshots, mockups, and a (public) API design in pseudo-code. - type: textarea + id: description attributes: - label: 📄 Description - description: A clear and concise description of what your idea is. Include things like possible use cases, drawbacks, etc. + label: Description + description: Please give us a detailed description of the feature that you envision. Focus on _what_ this feature does, over the _why_ you want this feature. What would be the end-result when implemented? Feel free to add any diagrams, (mocked up) screenshots, or other materials that support your story. + placeholder: I would love to have a button that I can make shiny. This is something that is supported on all the underlaying platforms. validations: required: true - type: textarea + id: api-changes attributes: - label: 🗃️ Alternative solutions - description: Describe more ways this idea could be implemented. - validations: - required: false - - type: textarea - attributes: - label: ✅ Tasks - description: Give an overview of all the specific things you would like to be changed or implemented. - value: | - ```[tasklist] - ### High Priority - - [ ] Something - - [ ] Another thing - - [ ] https://github.com/link/to/an/issue - ``` - ```[tasklist] - ### Nice to have - - [ ] Something - - [ ] Another thing - - [ ] https://github.com/link/to/an/issue + label: API Changes + description: Include a list of all API changes, additions, subtractions as would be required by your proposal. These APIs should be considered placeholders, so the naming is not as important as getting the concepts correct. If possible you should include some example (pseudo-)code of usage of your new API. + placeholder: | + ```csharp + var button = new Button (); + button.MakeShiny = true; // new API ``` + + The MakeShiny API works even if the button is already visible. + validations: + required: true - type: textarea + id: use-case attributes: - label: 📸 Assets - description: | - A list of assets (screenshots, mockups) relevant to this feature request. + label: Intended Use-Case + description: Provide a detailed example of where your proposal would be used and for what purpose. Focus on _why_ you want this feature instead of _what_ the feature does. + placeholder: I have a situation where I would really want a shiny button to make it stand out from the rest of the plain and boring buttons. + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/sample/bug_report.yml b/.github/ISSUE_TEMPLATE/sample/bug_report.yml new file mode 100644 index 00000000..f0774448 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sample/bug_report.yml @@ -0,0 +1,83 @@ +name: 🐞 Bug +title: '[BUG] <title>' +description: File a bug report +labels: ["bug", "to verify"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Please make sure to add as much detail as you can. + - type: textarea + id: description + attributes: + label: Description + description: Please give a detailed description of the issue that you're seeing. You can add screenshots and videos as well. + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: repro-steps + attributes: + label: Steps to Reproduce + description: Describe all the steps we need to take to show the behavior that you have observed. Also, include what you expected to happen and what did actually happen. + placeholder: | + 1. Create a File > New App + 2. Add a `Button` like so: `<Button Text="this is a bug" />` + 3. Click the added button and observe the bug 🐞 + + Expected outcome: a bug was added + Actual outcome: a ladybug appeared + validations: + required: true + - type: dropdown + id: platform-with-bug + attributes: + label: Platform with bug + description: What Platform is this bug affecting? + options: + - Core + - Android + - iOS + - MacCatalyst + - Windows + - Skia + - WebAssembly + - Other + validations: + required: true + + - type: dropdown + id: platforms-affected + attributes: + label: Affected platforms + description: Select all or any platform that you see this issue on. This helps us determine if it's something platform-specific or in the core. If you were only able to test on 1 platform, please check the last option to inform us about that. + multiple: true + options: + - Android + - iOS + - MacOS + - MacCatalyst + - Windows + - GTK + - Linux Framebuffer + - WPF + - Skia Desktop + - WebAssembly + - Other + - I was *not* able test on other platforms + validations: + required: true + + - type: textarea + id: workaround + attributes: + label: Did you find any workaround? + description: Did you find any workaround for this issue? This can unblock other people while waiting for this issue to be resolved or even give us a hint on how to fix this. + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be auomatically formatted into code, so no need for back ticks. + render: shell diff --git a/.github/ISSUE_TEMPLATE/sample/config.yml b/.github/ISSUE_TEMPLATE/sample/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sample/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/sample/feature_request.yml b/.github/ISSUE_TEMPLATE/sample/feature_request.yml new file mode 100644 index 00000000..47b3fe78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sample/feature_request.yml @@ -0,0 +1,39 @@ +name: Feature Request +description: Suggest an idea for this project +labels: ["enhancement"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Fill in this template with some detail. A detailed description, perhaps supported with some screenshots, mockups, and a (public) API design in pseudo-code. + - type: textarea + id: description + attributes: + label: Description + description: Please give us a detailed description of the feature that you envision. Focus on _what_ this feature does, over the _why_ you want this feature. What would be the end-result when implemented? Feel free to add any diagrams, (mocked up) screenshots, or other materials that support your story. + placeholder: I would love to have a button that I can make shiny. This is something that is supported on all the underlaying platforms. + validations: + required: true + - type: textarea + id: api-changes + attributes: + label: API Changes + description: Include a list of all API changes, additions, subtractions as would be required by your proposal. These APIs should be considered placeholders, so the naming is not as important as getting the concepts correct. If possible you should include some example (pseudo-)code of usage of your new API. + placeholder: | + ```csharp + var button = new Button (); + button.MakeShiny = true; // new API + ``` + + The MakeShiny API works even if the button is already visible. + validations: + required: true + - type: textarea + id: use-case + attributes: + label: Intended Use-Case + description: Provide a detailed example of where your proposal would be used and for what purpose. Focus on _why_ you want this feature instead of _what_ the feature does. + placeholder: I have a situation where I would really want a shiny button to make it stand out from the rest of the plain and boring buttons. + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/sample/spec.yml b/.github/ISSUE_TEMPLATE/sample/spec.yml new file mode 100644 index 00000000..6abc3944 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/sample/spec.yml @@ -0,0 +1,80 @@ +name: Spec +description: An official specification for enhancements. +labels: ["enhancement"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to provide us with a detailed specification of your idea. + + In a spec you will give a detailed and complete representation of the (public) APIs that are implemented as part of this change. Additionally please think about backwards compatibility, breaking changes, supported platforms and the difficulty. + - type: textarea + id: description + attributes: + label: Description + description: Provide a concise description of the feature and the motivation for adding it. This can be a modified version from the feature request prior to this. + validations: + required: true + - type: textarea + id: api-changes + attributes: + label: (Public) API Changes + description: Include a complete list of all API changes, additions, subtractions as would be required by your proposal. + value: | + # [ class ] + + ## Properties + + | API | Description | + | ------------- | ------------- | + | [name] | Gets or sets [description]. | + + ## Events + + | API | Description | + | ------------- | ------------- | + | [name] | [API documentation/description] | + validations: + required: true + - type: textarea + id: usage-scenarios + attributes: + label: Usage Scenarios + description: Give us a couple of scenarios that demonstrate how developers would consume the above APIs. + placeholder: | + # C# Example + ```csharp + var thing = new MyNewControl(); + thing.BeAwesome = true; + thing.Color = Colors.Cornsilk; + ``` + + # XAML Example + ```xaml + <MyNewControl x:Name="thing" BeAwesome="true" Color="Cornsilk" /> + ``` + validations: + required: true + - type: textarea + id: backwards-compatibility + attributes: + label: Backward Compatibility + description: Please describe here anything in terms of backwards compatibility. Will there be breaking changes? Do we need to update dependencies to support this? What are the minimum supported API/OS levels? And lastly, are there any platforms that can't support this and why? + placeholder: | + Minimum API levels? + Breaking changes? + Unsupported platforms? + validations: + required: true + - type: dropdown + id: difficulty + attributes: + label: Difficulty + description: What do you feel will be the difficulty of this change all things considering? No exact science, just your gut feeling. + options: + - Low + - Medium + - High + validations: + required: true \ No newline at end of file diff --git a/.github/Install-WindowsSdkISO.ps1 b/.github/Install-WindowsSdkISO.ps1 new file mode 100644 index 00000000..f4964d23 --- /dev/null +++ b/.github/Install-WindowsSdkISO.ps1 @@ -0,0 +1,314 @@ +[CmdletBinding()] +param([Parameter(Mandatory=$true)] + [string]$buildNumber) + +# Ensure the error action preference is set to the default for PowerShell3, 'Stop' +$ErrorActionPreference = 'Stop' + +# Constants +$WindowsSDKOptions = @("OptionId.UWPCpp") +$WindowsSDKRegPath = "HKLM:\Software\Microsoft\Windows Kits\Installed Roots" +$WindowsSDKRegRootKey = "KitsRoot10" +$WindowsSDKVersion = "10.0.$buildNumber.0" +$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options" +$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification" +$PublicKeyTokens = @("31bf3856ad364e35") + +function Download-File +{ + param ([string] $outDir, + [string] $downloadUrl, + [string] $downloadName) + + $downloadPath = Join-Path $outDir "$downloadName.download" + $downloadDest = Join-Path $outDir $downloadName + $downloadDestTemp = Join-Path $outDir "$downloadName.tmp" + + Write-Host -NoNewline "Downloading $downloadName..." + + try + { + $webclient = new-object System.Net.WebClient + $webclient.DownloadFile($downloadUrl, $downloadPath) + } + catch [System.Net.WebException] + { + Write-Host + Write-Warning "Failed to fetch updated file from $downloadUrl" + if (!(Test-Path $downloadDest)) + { + throw "$downloadName was not found at $downloadDest" + } + else + { + Write-Warning "$downloadName may be out of date" + } + } + + Unblock-File $downloadPath + + $downloadDestTemp = $downloadPath; + + # Delete and rename to final dest + if (Test-Path -PathType Container $downloadDest) + { + [System.IO.Directory]::Delete($downloadDest, $true) + } + + Move-Item -Force $downloadDestTemp $downloadDest + Write-Host "Done" + + return $downloadDest +} + +function Get-ISODriveLetter +{ + param ([string] $isoPath) + + $diskImage = Get-DiskImage -ImagePath $isoPath + if ($diskImage) + { + $volume = Get-Volume -DiskImage $diskImage + + if ($volume) + { + $driveLetter = $volume.DriveLetter + if ($driveLetter) + { + $driveLetter += ":" + return $driveLetter + } + } + } + + return $null +} + +function Mount-ISO +{ + param ([string] $isoPath) + + # Check if image is already mounted + $isoDrive = Get-ISODriveLetter $isoPath + + if (!$isoDrive) + { + Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null + } + + $isoDrive = Get-ISODriveLetter $isoPath + Write-Verbose "$isoPath mounted to ${isoDrive}:" +} + +function Dismount-ISO +{ + param ([string] $isoPath) + + $isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter + + if ($isoDrive) + { + Write-Verbose "$isoPath dismounted" + Dismount-DiskImage -ImagePath $isoPath | Out-Null + } +} + +function Disable-StrongName +{ + param ([string] $publicKeyToken = "*") + + reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null + if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") + { + reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null + } +} + +function Test-Admin +{ + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal $identity + $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Test-RegistryPathAndValue +{ + param ( + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] $path, + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] $value) + + try + { + if (Test-Path $path) + { + Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null + return $true + } + } + catch + { + } + + return $false +} + +function Test-InstallWindowsSDK +{ + $retval = $true + + if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) + { + # A Windows SDK is installed + # Is an SDK of our version installed with the options we need? + if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") + { + # It appears we have what we need. Double check the disk + $sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey + if ($sdkRoot) + { + if (Test-Path $sdkRoot) + { + $refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion" + if (Test-Path $refPath) + { + $umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion" + if (Test-Path $umdPath) + { + # Pretty sure we have what we need + $retval = $false + } + } + } + } + } + } + + return $retval +} + +function Test-InstallStrongNameHijack +{ + foreach($publicKeyToken in $PublicKeyTokens) + { + $key = "$StrongNameRegPath\*,$publicKeyToken" + if (!(Test-Path $key)) + { + return $true + } + } + + return $false +} + +Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..." +$InstallWindowsSDK = Test-InstallWindowsSDK +if ($InstallWindowsSDK) +{ + Write-Host "Installation required" +} +else +{ + Write-Host "INSTALLED" +} + +$StrongNameHijack = Test-InstallStrongNameHijack +Write-Host -NoNewline "Checking if StrongName bypass required..." + +if ($StrongNameHijack) +{ + Write-Host "REQUIRED" +} +else +{ + Write-Host "Done" +} + +if ($StrongNameHijack -or $InstallWindowsSDK) +{ + if (!(Test-Admin)) + { + Write-Host + throw "ERROR: Elevation required" + } +} + +if ($InstallWindowsSDK) +{ + # Static(ish) link for Windows SDK + # Note: there is a delay from Windows SDK announcements to availability via the static link + $uri = "https://go.microsoft.com/fwlink/?prd=11966&pver=1.0&plcid=0x409&clcid=0x409&ar=Flight&sar=Sdsurl&o1=$buildNumber" + + if($buildNumber -eq 18362) + { + # Workaround for removed SDK + $uri = "https://go.microsoft.com/fwlink/?linkid=2083448"; + } + + if($buildNumber -eq 19041) + { + # Workaround for missing SDK + $uri = "https://software-download.microsoft.com/download/pr/19041.1.191206-1406.vb_release_WindowsSDK.iso"; + } + + if ($env:TEMP -eq $null) + { + $env:TEMP = Join-Path $env:SystemDrive 'temp' + } + + $winsdkTempDir = Join-Path $env:TEMP "WindowsSDK" + + if (![System.IO.Directory]::Exists($winsdkTempDir)) + { + [void][System.IO.Directory]::CreateDirectory($winsdkTempDir) + } + + $file = "winsdk_$buildNumber.iso" + + Write-Verbose "Getting WinSDK from $uri" + $downloadFile = Download-File $winsdkTempDir $uri $file + + # TODO Check if zip, exe, iso, etc. + try + { + Write-Host -NoNewline "Mounting ISO $file..." + Mount-ISO $downloadFile + Write-Host "Done" + + $isoDrive = Get-ISODriveLetter $downloadFile + + if (Test-Path $isoDrive) + { + Write-Host -NoNewLine "Installing WinSDK..." + + $setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe" + Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q" + Write-Host "Done" + } + else + { + throw "Could not find mounted ISO at ${isoDrive}" + } + } + finally + { + Write-Host -NoNewline "Dismounting ISO $file..." + #Dismount-ISO $downloadFile + Write-Host "Done" + } +} + +if ($StrongNameHijack) +{ + Write-Host -NoNewline "Disabling StrongName for Windows SDK..." + + foreach($key in $PublicKeyTokens) + { + Disable-StrongName $key + } + + Write-Host "Done" +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..1147b3ac --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: +- package-ecosystem: nuget + directory: "/" + schedule: + interval: weekly + time: "10:00" + open-pull-requests-limit: 10 + labels: + - kind/dependency + - do-not-merge/work-in-progress diff --git a/.github/steps/install_dependencies/action.yml b/.github/steps/install_dependencies/action.yml new file mode 100644 index 00000000..f8cd87b1 --- /dev/null +++ b/.github/steps/install_dependencies/action.yml @@ -0,0 +1,45 @@ +name: Install Dependencies +description: "" + +inputs: + target-platform: + description: 'The platform to install dependencies for. #See available values at https://platform.uno/docs/articles/external/uno.check/doc/using-uno-check.html' + required: false + default: 'all' + dotnet-version: + description: 'Installs and sets the .NET SDK Version' + required: false + default: '8.0.x' + sdkVersion: + description: 'The version of the Windows Sdk' + required: false + default: '19041' + +runs: + using: "composite" + steps: + # Install .NET + - name: Setup .NET ${{ inputs.dotnet-version }} + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '${{ inputs.dotnet-version }}' + + # Install Windows SDK + - name: Install Windows SDK ${{ inputs.sdkVersion }} + shell: pwsh + if: ${{ runner.os == 'Windows' }} + run: .\.github\Install-WindowsSdkISO.ps1 ${{ inputs.sdkVersion }} + + # Run Uno.Check + - name: Install ${{ inputs.target-platform }} Workloads + shell: pwsh + run: | + dotnet tool install -g uno.check + ("${{ inputs.target-platform }} ".Split(' ') | ForEach-Object { + $target = $_.Replace("_win", "").Replace("_macos", "") + if (![string]::IsNullOrEmpty($target)) { + echo "target: $target" + uno-check -v --ci --non-interactive --fix --target $target --skip vswin --skip vsmac --skip xcode --skip vswinworkloads --skip androidemulator --skip dotnetnewunotemplates + echo "uno-check finished for target: $target " + } + }) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7f5b9cbf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: + - main + - release/** + + pull_request: + types: [opened, synchronize, reopened] + branches: + - main + - release/** +env: + STEP_TIMEOUT_MINUTES: 60 + +jobs: + smoke_test: + name: Uno + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Dependencies + timeout-minutes: ${{ fromJSON(env.STEP_TIMEOUT_MINUTES) }} + uses: "./.github/steps/install_dependencies" + + # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1.3.1 + + - name: Build Emerald (Debug) + shell: pwsh + run: msbuild ./Emerald/Emerald.csproj /r diff --git a/.gitignore b/.gitignore index 163adce0..d65e71a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -29,7 +29,6 @@ x86/ bld/ [Bb]in/ [Oo]bj/ -[Oo]ut/ [Ll]og/ [Ll]ogs/ @@ -91,6 +90,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -294,6 +294,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -350,6 +361,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -361,6 +375,34 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +# Single Target Config +solution-config.props +# Publish Profiles +!**/Properties/PublishProfiles/*.pubxml + +# Azure Active Directory tokens /Emerald.App/Emerald.App/MsalClientID.txt /Emerald.Core/MsalClientID.txt -/Emerald.App/Emerald.App/GithubClientID.txt +Emerald.App/MsalClientID.txt diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..a63ad400 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "unoplatform.vscode" + ], +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..1d6e6504 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": "Uno Platform Mobile", + "type": "Uno", + "request": "launch", + // any Uno* task will do, this is simply to satisfy vscode requirement when a launch.json is present + "preLaunchTask": "Uno: android | Debug | android-x64" + }, + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": "Uno Platform Desktop (Debug)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build-desktop", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/Emerald/bin/Debug/net8.0-desktop/Emerald.dll", + "args": [], + "launchSettingsProfile": "Emerald (Desktop)", + "env": { + "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" + }, + "cwd": "${workspaceFolder}/Emerald", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3405922d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + "*.xaml": "$(capture).xaml.cs" + }, + "files.associations": { + "global.json": "jsonc" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..ac5be062 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,31 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build-desktop", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Emerald/Emerald.csproj", + "/property:GenerateFullPaths=true", + "/property:TargetFramework=net8.0-desktop", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish-desktop", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Emerald/Emerald.csproj", + "/property:GenerateFullPaths=true", + "/property:TargetFramework=net8.0-desktop", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} diff --git a/.vsconfig b/.vsconfig new file mode 100644 index 00000000..8e1dd5e5 --- /dev/null +++ b/.vsconfig @@ -0,0 +1,19 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.VisualStudio.Component.CoreEditor", + "Microsoft.VisualStudio.Workload.CoreEditor", + "Microsoft.NetCore.Component.SDK", + "Microsoft.NetCore.Component.DevelopmentTools", + "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", + "Microsoft.VisualStudio.Component.TextTemplating", + "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging", + "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", + "Microsoft.VisualStudio.Component.Debugger.JustInTime", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Component.MonoDebugger", + "Microsoft.VisualStudio.ComponentGroup.Maui.All", + "Microsoft.VisualStudio.Workload.NetCrossPlat", + "Microsoft.VisualStudio.Workload.NetCoreTools" + ] +} diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..47ddeb81 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,23 @@ +<Project> + <PropertyGroup> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> + + <!-- + Adding NoWarn to remove build warnings + NU1507: Warning when there are multiple package sources when using CPM with no source mapping + NETSDK1201: Warning that specifying RID won't create self containing app + PRI257: Ignore default language (en) not being one of the included resources (eg en-us, en-uk) + --> + <NoWarn>$(NoWarn);NU1507;NETSDK1201;PRI257</NoWarn> + </PropertyGroup> + + <!-- See https://aka.platform.uno/using-uno-sdk#implicit-packages for more information regarding the Implicit Packages version properties. --> + <PropertyGroup> + <UnoExtensionsVersion>4.1.24</UnoExtensionsVersion> + <UnoToolkitVersion>6.3.7</UnoToolkitVersion> + <UnoThemesVersion>5.0.13</UnoThemesVersion> + <UnoCSharpMarkupVersion>5.2.14</UnoCSharpMarkupVersion> + </PropertyGroup> +</Project> diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..f75adf7e --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,2 @@ +<Project> +</Project> diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..086d7816 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,30 @@ +<Project ToolsVersion="15.0"> + <!-- + To update the version of Uno, you should instead update the Sdk version in the global.json file. + + See https://aka.platform.uno/using-uno-sdk for more information. + --> + <ItemGroup> + <!--Emerald.App--> + <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.3.2" /> + <PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.1.240916" /> + <PackageVersion Include="CommunityToolkit.WinUI.Helpers" Version="8.1.240916" /> + <PackageVersion Include="CommunityToolkit.WinUI.UI.Animations" Version="7.1.2" /> + <PackageVersion Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" /> + <PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.106" /> + <PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" /> + <PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.57" /> + <PackageVersion Include="Octokit" Version="13.0.1" /> + <PackageVersion Include="PInvoke.User32" Version="0.7.124" /> + <PackageVersion Include="ProjBobcat" Version="1.16.0" /> + <PackageVersion Include="System.Text.Json" Version="8.0.5" /> + <PackageVersion Include="WinUIEx" Version="2.3.4" /> + <PackageVersion Include="XboxAuthNet.Game.Msal" Version="0.0.5" /> + <!--Emerald.CoreX--> + <PackageVersion Include="CmlLib.Core" Version="4.0.2" /> + <PackageVersion Include="CmlLib.Core.Auth.Microsoft" Version="3.2.0" /> + <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0-preview.7.24405.7" /> + <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> + <PackageVersion Include="RestSharp" Version="112.0.0" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj index c76b14af..9920f1a8 100644 --- a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj +++ b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj @@ -158,12 +158,13 @@ </ProjectReference> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000"> + <PackageReference Include="Microsoft.WindowsAppSDK"> <IncludeAssets>build</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26031-preview"> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools"> <IncludeAssets>build</IncludeAssets> </PackageReference> + <PackageReference Include="System.Text.Json" /> </ItemGroup> <ItemGroup> <Folder Include="Properties\" /> diff --git a/Emerald.App/Emerald.App/Emerald.App.csproj b/Emerald.App/Emerald.App/Emerald.App.csproj index b80e8e74..bfa64968 100644 --- a/Emerald.App/Emerald.App/Emerald.App.csproj +++ b/Emerald.App/Emerald.App/Emerald.App.csproj @@ -1,33 +1,27 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> - <TargetFramework>net7.0-windows10.0.22000.0</TargetFramework> + <TargetFramework>net8.0-windows10.0.22000.0</TargetFramework> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <RootNamespace>Emerald.WinUI</RootNamespace> <ApplicationManifest>app.manifest</ApplicationManifest> <Platforms>x86;x64;arm64</Platforms> - <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers> + <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers> <UseWinUI>true</UseWinUI> <Configurations>Debug;Release</Configurations> <Nullable>enable</Nullable> <SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion> + <EnableWindowsTargeting>true</EnableWindowsTargeting> </PropertyGroup> <ItemGroup> - <None Remove="GithubClientID.txt" /> <None Remove="Helpers\rlbl1cxv.0sq~" /> <None Remove="MsalClientID.txt" /> <None Remove="Restart.bat" /> <None Remove="Views\Store\InstallerPage.xaml" /> </ItemGroup> - <ItemGroup> - <Content Include="GithubClientID.txt"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </Content> - </ItemGroup> - <ItemGroup> <Content Include="MsalClientID.txt"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> @@ -38,16 +32,17 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> - <PackageReference Include="CommunityToolkit.WinUI.UI.Animations" Version="7.1.2" /> - <PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" /> - <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" /> - <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26031-preview" /> - <PackageReference Include="Octokit" Version="10.0.0" /> - <PackageReference Include="PInvoke.User32" Version="0.7.124" /> - <PackageReference Include="ProjBobcat" Version="1.16.0" /> - <PackageReference Include="WinUIEx" Version="2.3.4" /> - <PackageReference Include="XboxAuthNet.Game.Msal" Version="0.0.5" /> + <PackageReference Include="CommunityToolkit.Mvvm" /> + <PackageReference Include="CommunityToolkit.WinUI.UI.Animations" /> + <PackageReference Include="CommunityToolkit.WinUI.UI.Controls" /> + <PackageReference Include="Microsoft.WindowsAppSDK" /> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" /> + <PackageReference Include="Octokit" /> + <PackageReference Include="PInvoke.User32" /> + <PackageReference Include="ProjBobcat" /> + <PackageReference Include="System.Text.Json" /> + <PackageReference Include="WinUIEx" /> + <PackageReference Include="XboxAuthNet.Game.Msal" /> <Manifest Include="$(ApplicationManifest)" /> </ItemGroup> diff --git a/Emerald.App/Emerald.App/Helpers/Extensions.cs b/Emerald.App/Emerald.App/Helpers/Extensions.cs index 2d61c177..5f0a94d0 100644 --- a/Emerald.App/Emerald.App/Helpers/Extensions.cs +++ b/Emerald.App/Emerald.App/Helpers/Extensions.cs @@ -1,4 +1,4 @@ -using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.Windows.ApplicationModel.Resources; using System; @@ -112,15 +112,11 @@ public static string ToBinaryString(this string str) public static string ToMD5(this string s) { StringBuilder sb = new(); + byte[] hashValue = MD5.HashData(Encoding.UTF8.GetBytes(s)); - using (MD5 md5 = MD5.Create()) + foreach (byte b in hashValue) { - byte[] hashValue = md5.ComputeHash(Encoding.UTF8.GetBytes(s)); - - foreach (byte b in hashValue) - { - sb.Append($"{b:X2}"); - } + sb.Append($"{b:X2}"); } return sb.ToString(); diff --git a/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs b/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs index e32a4597..5b70d088 100644 --- a/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs +++ b/Emerald.App/Emerald.App/Helpers/MarkupExtensions/FontIcon.cs @@ -1,11 +1,11 @@ -using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Markup; namespace Emerald.WinUI.Helpers { [MarkupExtensionReturnType(ReturnType = typeof(Microsoft.UI.Xaml.Controls.FontIcon))] public sealed class FontIcon : MarkupExtension { - public string Glyph { get; set; } + public string Glyph { get; set; } = "\xe8a5"; public int FontSize { get; set; } = 16; diff --git a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs index 19f0dc87..168a0c28 100644 --- a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs +++ b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs @@ -1,429 +1,424 @@ -using CmlLib.Core; +using CmlLib.Core; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.Helpers; using Emerald.Core.Store.Enums; using Microsoft.UI; using Microsoft.UI.Xaml; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using System; using System.Collections.Generic; using Windows.UI; -namespace Emerald.WinUI.Helpers.Settings.JSON +namespace Emerald.WinUI.Helpers.Settings.JSON; + +public class JSON : Models.Model { - public class JSON : Models.Model - { - public string Serialize() - => JsonConvert.SerializeObject(this, Formatting.Indented); - } + public string Serialize() + => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); +} - public class SettingsBackup : JSON - { - public string Backup { get; set; } - public string Name { get; set; } - public DateTime Time { get; set; } - //ik there is Time.ToString() lol - public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}"; - } +public class SettingsBackup : JSON +{ + public string Backup { get; set; } + public string Name { get; set; } + public DateTime Time { get; set; } + // ik there is Time.ToString() lol + public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}"; +} - public class Backups : JSON - { - public SettingsBackup[] AllBackups { get; set; } = Array.Empty<SettingsBackup>(); - public string APIVersion { get; private set; } = "1.0"; - } +public class Backups : JSON +{ + public SettingsBackup[] AllBackups { get; set; } = Array.Empty<SettingsBackup>(); + public string APIVersion { get; private set; } = "1.0"; +} - public class Settings : JSON +public class Settings : JSON +{ + public static Settings CreateNew() => new() { - public static Settings CreateNew() => new() + App = new() { - App = new() + Discord = new(), + Appearance = new() { - Discord = new(), - Appearance = new() - { - MicaTintColor = (int)Enums.MicaTintColor.NoColor, - Theme = (int)ElementTheme.Default - } - }, - Minecraft = new() + MicaTintColor = (int)Enums.MicaTintColor.NoColor, + Theme = (int)ElementTheme.Default + } + }, + Minecraft = new() + { + Path = MinecraftPath.GetOSDefaultPath(), + RAM = DirectResoucres.MaxRAM / 2, + MCVerionsConfiguration = new(), + JVM = new(), + Downloader = new() { - Path = MinecraftPath.GetOSDefaultPath(), - RAM = DirectResoucres.MaxRAM / 2, - MCVerionsConfiguration = new(), - JVM = new(), - Downloader = new() - { - AssetsCheck = true, - HashCheck = true - } + AssetsCheck = true, + HashCheck = true } - }; + } + }; - public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion; - public DateTime LastSaved { get; set; } = DateTime.Now; - public Minecraft Minecraft { get; set; } = new(); + public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion; + public DateTime LastSaved { get; set; } = DateTime.Now; + public Minecraft Minecraft { get; set; } = new(); - public App App { get; set; } = new(); - } + public App App { get; set; } = new(); +} - public partial class Minecraft : JSON +public partial class Minecraft : JSON +{ + public Minecraft() { - public Minecraft() + JVM.PropertyChanged += (_, _) + => InvokePropertyChanged(); + PropertyChanged += (_, e) => { - JVM.PropertyChanged += (_, _) - => InvokePropertyChanged(); - PropertyChanged += (_, e) => - { - if (e.PropertyName != null) - InvokePropertyChanged(); - }; - } - - [JsonIgnore] - public double RAMinGB => Math.Round((RAM / 1024.00), 2); + if (e.PropertyName != null) + InvokePropertyChanged(); + }; + } + [JsonIgnore] + public double RAMinGB => Math.Round((RAM / 1024.00), 2); - [ObservableProperty] - private string _Path; - [ObservableProperty] - private int _RAM; + [ObservableProperty] + private string _Path; - [ObservableProperty] - private bool _IsAdmin; + [ObservableProperty] + private int _RAM; - public Downloader Downloader { get; set; } = new(); + [ObservableProperty] + private bool _IsAdmin; - public MCVerionsConfiguration MCVerionsConfiguration { get; set; } + public Downloader Downloader { get; set; } = new(); - public JVM JVM { get; set; } = new(); + public MCVerionsConfiguration MCVerionsConfiguration { get; set; } - public bool ReadLogs() - => JVM.GameLogs && !IsAdmin; - } + public JVM JVM { get; set; } = new(); - public class Account : JSON - { - public string Type { get; set; } - public string Username { get; set; } - public string AccessToken { get; set; } - public string ClientToken { get; set; } - public string UUID { get; set; } - public bool LastAccessed { get; set; } - } + public bool ReadLogs() + => JVM.GameLogs && !IsAdmin; +} - public partial class Downloader : JSON - { +public class Account : JSON +{ + public string Type { get; set; } + public string Username { get; set; } + public string AccessToken { get; set; } + public string ClientToken { get; set; } + public string UUID { get; set; } + public bool LastAccessed { get; set; } +} - [ObservableProperty] - private bool _HashCheck; +public partial class Downloader : JSON +{ + [ObservableProperty] + private bool _HashCheck; - [ObservableProperty] - private bool _AssetsCheck; - } + [ObservableProperty] + private bool _AssetsCheck; +} - public partial class JVM : JSON +public partial class JVM : JSON +{ + public JVM() { - public JVM() + this.PropertyChanged += (_, e) => { - this.PropertyChanged += (_, e) => - { - if (e.PropertyName != null) - this.InvokePropertyChanged(); - }; - } - - [ObservableProperty] - private string[] _Arguments; - - [ObservableProperty] - private double _ScreenWidth; + if (e.PropertyName != null) + this.InvokePropertyChanged(); + }; + } - [ObservableProperty] - private double _ScreenHeight; + [ObservableProperty] + private string[] _Arguments; - [ObservableProperty] - private bool _FullScreen; + [ObservableProperty] + private double _ScreenWidth; - [ObservableProperty] - private bool _GameLogs; + [ObservableProperty] + private double _ScreenHeight; - [JsonIgnore] - public string ScreenSizeStatus => - FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize()); + [ObservableProperty] + private bool _FullScreen; - [JsonIgnore] - public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize()); + [ObservableProperty] + private bool _GameLogs; - } + [JsonIgnore] + public string ScreenSizeStatus => + FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize()); - public class App : JSON - { - public Appearance Appearance { get; set; } = new(); - public bool AutoLogin { get; set; } - public Discord Discord { get; set; } = new(); - public NewsFilter NewsFilter { get; set; } = new(); - public Store Store { get; set; } = new(); - public Updates Updates { get; set; } = new(); - public bool AutoClose { get; set; } - public bool HideOnLaunch { get; set; } - public bool WindowsHello { get; set; } + [JsonIgnore] + public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize()); +} - } - public class Updates : JSON - { - - public bool CheckAtStartup { get; set; } = true; - public bool AutoDownload { get; set; } - public bool IncludePreReleases { get; set; } - } - public partial class StoreFilter : JSON - { - [ObservableProperty] - private bool _Fabric; +public class App : JSON +{ + public Appearance Appearance { get; set; } = new(); + public bool AutoLogin { get; set; } + public Discord Discord { get; set; } = new(); + public NewsFilter NewsFilter { get; set; } = new(); + public Store Store { get; set; } = new(); + public Updates Updates { get; set; } = new(); + public bool AutoClose { get; set; } + public bool HideOnLaunch { get; set; } + public bool WindowsHello { get; set; } +} +public class Updates : JSON +{ + public bool CheckAtStartup { get; set; } = true; + public bool AutoDownload { get; set; } + public bool IncludePreReleases { get; set; } +} +public partial class StoreFilter : JSON +{ + [ObservableProperty] + private bool _Fabric; - [ObservableProperty] - private bool _Forge; + [ObservableProperty] + private bool _Forge; - [ObservableProperty] - private bool _Adventure; + [ObservableProperty] + private bool _Adventure; - [ObservableProperty] - private bool _Cursed; + [ObservableProperty] + private bool _Cursed; - [ObservableProperty] - private bool _Decoration; + [ObservableProperty] + private bool _Decoration; - [ObservableProperty] - private bool _Equipment; + [ObservableProperty] + private bool _Equipment; - [ObservableProperty] - private bool _Food; + [ObservableProperty] + private bool _Food; - [ObservableProperty] - private bool _Library; + [ObservableProperty] + private bool _Library; - [ObservableProperty] - private bool _Magic; + [ObservableProperty] + private bool _Magic; - [ObservableProperty] - private bool _Misc; + [ObservableProperty] + private bool _Misc; - [ObservableProperty] - private bool _Optimization; + [ObservableProperty] + private bool _Optimization; - [ObservableProperty] - private bool _Storage; + [ObservableProperty] + private bool _Storage; - [ObservableProperty] - private bool _Technology; + [ObservableProperty] + private bool _Technology; - [ObservableProperty] - private bool _Utility; + [ObservableProperty] + private bool _Utility; - [ObservableProperty] - private bool _Worldgen; + [ObservableProperty] + private bool _Worldgen; - [JsonIgnore] - public bool All + [JsonIgnore] + public bool All + { + get => GetResult().Length == 0; + set { - get => GetResult().Length == 0; - set - { - _Fabric = _Forge = _Adventure = _Cursed = _Decoration = _Equipment = _Food = _Library = _Magic = _Misc = _Optimization = _Storage = _Technology = _Utility = _Worldgen = false; - InvokePropertyChanged(null); - } + Fabric = Forge = Adventure = Cursed = Decoration = Equipment = Food = Library = Magic = Misc = Optimization = Storage = Technology = Utility = Worldgen = false; + InvokePropertyChanged(null); } - public SearchCategories[] GetResult() - { - if (Fabric & Forge & Adventure & Cursed & Decoration & Equipment & Food & Library & Magic & Misc & Optimization & Storage & Technology & Utility & Worldgen) - return Array.Empty<SearchCategories>(); + } + public SearchCategories[] GetResult() + { + if (Fabric & Forge & Adventure & Cursed & Decoration & Equipment & Food & Library & Magic & Misc & Optimization & Storage & Technology & Utility & Worldgen) + return Array.Empty<SearchCategories>(); - var r = new List<SearchCategories>(); + var r = new List<SearchCategories>(); - if (Fabric) - r.Add(SearchCategories.Fabric); + if (Fabric) + r.Add(SearchCategories.Fabric); - if (Forge) - r.Add(SearchCategories.Forge); + if (Forge) + r.Add(SearchCategories.Forge); - if (Adventure) - r.Add(SearchCategories.Adventure); + if (Adventure) + r.Add(SearchCategories.Adventure); - if (Cursed) - r.Add(SearchCategories.Cursed); + if (Cursed) + r.Add(SearchCategories.Cursed); - if (Decoration) - r.Add(SearchCategories.Decoration); + if (Decoration) + r.Add(SearchCategories.Decoration); - if (Equipment) - r.Add(SearchCategories.Equipment); + if (Equipment) + r.Add(SearchCategories.Equipment); - if (Food) - r.Add(SearchCategories.Food); + if (Food) + r.Add(SearchCategories.Food); - if (Library) - r.Add(SearchCategories.Library); + if (Library) + r.Add(SearchCategories.Library); - if (Magic) - r.Add(SearchCategories.Magic); + if (Magic) + r.Add(SearchCategories.Magic); - if (Misc) - r.Add(SearchCategories.Misc); + if (Misc) + r.Add(SearchCategories.Misc); - if (Optimization) - r.Add(SearchCategories.Optimization); + if (Optimization) + r.Add(SearchCategories.Optimization); - if (Storage) - r.Add(SearchCategories.Storage); + if (Storage) + r.Add(SearchCategories.Storage); - if (Technology) - r.Add(SearchCategories.Technology); + if (Technology) + r.Add(SearchCategories.Technology); - if (Utility) - r.Add(SearchCategories.Utility); + if (Utility) + r.Add(SearchCategories.Utility); - if (Worldgen) - r.Add(SearchCategories.Worldgen); + if (Worldgen) + r.Add(SearchCategories.Worldgen); - return r.ToArray(); - } + return r.ToArray(); } - public class Store : JSON - { - public StoreFilter Filter { get; set; } = new(); - public StoreSortOptions SortOptions { get; set; } = new(); - } - - public partial class StoreSortOptions : JSON - { +} +public class Store : JSON +{ + public StoreFilter Filter { get; set; } = new(); + public StoreSortOptions SortOptions { get; set; } = new(); +} - [ObservableProperty] - private bool _Relevance = true; +public partial class StoreSortOptions : JSON +{ + [ObservableProperty] + private bool _Relevance = true; - [ObservableProperty] - private bool _Downloads; + [ObservableProperty] + private bool _Downloads; - [ObservableProperty] - private bool _Follows; + [ObservableProperty] + private bool _Follows; - [ObservableProperty] - private bool _Updated; + [ObservableProperty] + private bool _Updated; - [ObservableProperty] - private bool _Newest; + [ObservableProperty] + private bool _Newest; - public SearchSortOptions GetResult() - { - if (!(Relevance || Downloads || Follows || Updated || Newest)) - return SearchSortOptions.Relevance; - else - return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest))); - } - } - public partial class NewsFilter : JSON + public SearchSortOptions GetResult() { - [ObservableProperty] - private bool _Java = false; + if (!(Relevance || Downloads || Follows || Updated || Newest)) + return SearchSortOptions.Relevance; + else + return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest))); + } +} +public partial class NewsFilter : JSON +{ + [ObservableProperty] + private bool _Java = false; - [ObservableProperty] - private bool _Bedrock = false; + [ObservableProperty] + private bool _Bedrock = false; - [ObservableProperty] - private bool _Dungeons = false; + [ObservableProperty] + private bool _Dungeons = false; - [ObservableProperty] - private bool _Legends = false; + [ObservableProperty] + private bool _Legends = false; - [JsonIgnore] - public bool All + [JsonIgnore] + public bool All + { + get => GetResult().Length == 4; + set { - get => GetResult().Length == 4; - set - { - Java = Bedrock = Dungeons = Legends = false; - InvokePropertyChanged(null); - } + Java = Bedrock = Dungeons = Legends = false; + InvokePropertyChanged(null); } - public string[] GetResult() - { - var r = new List<string>(); + } + public string[] GetResult() + { + var r = new List<string>(); - if (!Java && !Bedrock && !Dungeons && !Legends) - { - r.Add("Minecraft: Java Edition"); - r.Add("Minecraft for Windows"); - r.Add("Minecraft Dungeons"); - r.Add("Minecraft Legends"); - return r.ToArray(); - } + if (!Java && !Bedrock && !Dungeons && !Legends) + { + r.Add("Minecraft: Java Edition"); + r.Add("Minecraft for Windows"); + r.Add("Minecraft Dungeons"); + r.Add("Minecraft Legends"); + return r.ToArray(); + } - if (Java) - r.Add("Minecraft: Java Edition"); + if (Java) + r.Add("Minecraft: Java Edition"); - if (Bedrock) - r.Add("Minecraft for Windows"); + if (Bedrock) + r.Add("Minecraft for Windows"); - if (Dungeons) - r.Add("Minecraft Dungeons"); + if (Dungeons) + r.Add("Minecraft Dungeons"); - if (Legends) - r.Add("Minecraft Legends"); + if (Legends) + r.Add("Minecraft Legends"); - return r.ToArray(); - } - } - public class Discord : JSON - { + return r.ToArray(); } - public partial class MCVerionsConfiguration : JSON - { - [ObservableProperty] - private bool _Release = true; +} +public class Discord : JSON +{ +} +public partial class MCVerionsConfiguration : JSON +{ + [ObservableProperty] + private bool _Release = true; - [ObservableProperty] - private bool _Custom = false; + [ObservableProperty] + private bool _Custom = false; - [ObservableProperty] - private bool _OldBeta = false; + [ObservableProperty] + private bool _OldBeta = false; - [ObservableProperty] - private bool _OldAlpha = false; + [ObservableProperty] + private bool _OldAlpha = false; - [ObservableProperty] - private bool _Snapshot = false; - } + [ObservableProperty] + private bool _Snapshot = false; +} - public partial class Appearance : JSON - { - [ObservableProperty] - private int _NavIconType = 1; +public partial class Appearance : JSON +{ + [ObservableProperty] + private int _NavIconType = 1; - public bool ShowFontIcons => NavIconType == 0; + public bool ShowFontIcons => NavIconType == 0; - [ObservableProperty] - private int _Theme; + [ObservableProperty] + private int _Theme; - [ObservableProperty] - private int _MicaTintColor; + [ObservableProperty] + private int _MicaTintColor; - [ObservableProperty] - private int _MicaType = 0; + [ObservableProperty] + private int _MicaType = 0; - [ObservableProperty] - private (int A, int R, int G, int B)? _CustomMicaTintColor; + [ObservableProperty] + private (int A, int R, int G, int B)? _CustomMicaTintColor; - //I had to do this because whenever the app has no Mica it won't change the background color when requested theme changes unless the Windows main theme gets changed. - [JsonIgnore] - public Color Win10BackgroundColor => (SystemInformation.Instance.OperatingSystemVersion.Build < 22000) ? (Emerald.WinUI.App.Current.ActualTheme == ElementTheme.Light ? Colors.White : Colors.Black) : Colors.Transparent; + //I had to do this because whenever the app has no Mica it won't change the background color when requested theme changes unless the Windows main theme gets changed. + [JsonIgnore] + public Color Win10BackgroundColor => (SystemInformation.Instance.OperatingSystemVersion.Build < 22000) ? (Emerald.WinUI.App.Current.ActualTheme == ElementTheme.Light ? Colors.White : Colors.Black) : Colors.Transparent; - public Appearance() + public Appearance() + { + this.PropertyChanged += (_, e) => { - this.PropertyChanged += (_, e) => - { - if (e.PropertyName != null) - this.InvokePropertyChanged(); - }; - } + if (e.PropertyName != null) + this.InvokePropertyChanged(); + }; } } diff --git a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs index e083f7d2..d47d9054 100644 --- a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs +++ b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs @@ -1,5 +1,5 @@ -using Emerald.WinUI.Helpers.Settings.JSON; -using Newtonsoft.Json; +using Emerald.WinUI.Helpers.Settings.JSON; +using System.Text.Json; using System; using System.Collections.Generic; using System.Linq; @@ -19,13 +19,12 @@ public static T GetSerializedFromSettings<T>(string key, T def) string json; try { - json = ApplicationData.Current.RoamingSettings.Values[key] as string; - return JsonConvert.DeserializeObject<T>(json); + return JsonSerializer.Deserialize<T>(json); } catch { - json = JsonConvert.SerializeObject(def); + json = JsonSerializer.Serialize(def); ApplicationData.Current.RoamingSettings.Values[key] = json; return def; } @@ -39,14 +38,14 @@ public static void LoadData() { APINoMatch?.Invoke(null, ApplicationData.Current.RoamingSettings.Values["Settings"] as string); ApplicationData.Current.RoamingSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize(); - Settings = JsonConvert.DeserializeObject<JSON.Settings>(ApplicationData.Current.RoamingSettings.Values["Settings"] as string); + Settings = JsonSerializer.Deserialize<JSON.Settings>(ApplicationData.Current.RoamingSettings.Values["Settings"] as string); } } public static async Task CreateBackup(string system) { string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); - var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject<Backups>(json); + var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); bl.Add(new SettingsBackup() { Time = DateTime.Now, Backup = system }); @@ -59,7 +58,7 @@ public static async Task CreateBackup(string system) public static async Task DeleteBackup(int Index) { string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); - var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject<Backups>(json); + var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); bl.RemoveAt(Index); @@ -72,7 +71,7 @@ public static async Task DeleteBackup(int Index) public static async Task DeleteBackup(DateTime time) { string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); - var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject<Backups>(json); + var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); bl.Remove(x => x.Time == time); @@ -84,7 +83,7 @@ public static async Task DeleteBackup(DateTime time) public static async Task RenameBackup(DateTime time, string name) { string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); - var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject<Backups>(json); + var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); bl.FirstOrDefault(x => x.Time == time).Name = name; @@ -97,7 +96,7 @@ public static async Task RenameBackup(DateTime time, string name) public static async Task<List<SettingsBackup>> GetBackups() { string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); - var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonConvert.DeserializeObject<Backups>(json); + var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); return l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); } @@ -106,7 +105,7 @@ public static void SaveData() { Settings.LastSaved = DateTime.Now; ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize(); - ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonConvert.SerializeObject(Accounts); + ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts); } } } \ No newline at end of file diff --git a/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs b/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs index 246642bb..fc74e220 100644 --- a/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs +++ b/Emerald.App/Emerald.App/Helpers/Updater/Updater.cs @@ -1,11 +1,11 @@ -using CmlLib.Core; +using CmlLib.Core; using CommunityToolkit.WinUI.Helpers; using Emerald.Core; using Emerald.Core.Tasks; using Emerald.WinUI.Models; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Shapes; -using Newtonsoft.Json; +using System.Text.Json; using Octokit; using ProjBobcat.Class.Model; using System; @@ -39,9 +39,7 @@ public Updater() bool IsInitialized = false; public async System.Threading.Tasks.Task Initialize() { - var cId = await FileIO.ReadTextAsync(await StorageFile.GetFileFromPathAsync($"{Windows.ApplicationModel.Package.Current.InstalledPath}\\GithubClientID.txt")); - - Client = new GitHubClient(new Octokit.ProductHeaderValue(cId)); + Client = new GitHubClient(new Octokit.ProductHeaderValue("Riverside.Emerald")); IsInitialized = true; } private bool isRunning = false; @@ -95,7 +93,6 @@ public async void CheckForUpdates(bool OnlyInformifHigherAvailable = false) var msg = await MessageBox.Show("UpdateAvailable".Localize(), "## Version: " + ver.ToString() + "\n\n###ReleaseNotes".Localize() + "\n\n " + rel.Body,Enums.MessageBoxButtons.CustomWithCancel, "UpdateNow".Localize()); if(msg == Enums.MessageBoxResults.Cancel) goto Return; - } else if(ver < currentver) { @@ -106,8 +103,6 @@ public async void CheckForUpdates(bool OnlyInformifHigherAvailable = false) var msg = await MessageBox.Show("DowngradeAvailable".Localize(), "DowngradeDescription".Localize(),Enums.MessageBoxButtons.Ok); goto Return; - - } else if(ver == currentver) { @@ -164,4 +159,4 @@ private async void Install(string path) Process.Start(startInfo); } } -} \ No newline at end of file +} diff --git a/Emerald.App/Emerald.App/Helpers/WindowManager.cs b/Emerald.App/Emerald.App/Helpers/WindowManager.cs index 52cbc0ef..0519e1e3 100644 --- a/Emerald.App/Emerald.App/Helpers/WindowManager.cs +++ b/Emerald.App/Emerald.App/Helpers/WindowManager.cs @@ -1,4 +1,4 @@ -using CommunityToolkit.WinUI.Helpers; +using CommunityToolkit.WinUI.Helpers; using Microsoft.UI; using Microsoft.UI.Composition; using Microsoft.UI.Composition.SystemBackdrops; @@ -48,7 +48,6 @@ public static MicaBackground IntializeWindow(Window window) /// <exception cref="NullReferenceException"/> public static void SetTitleBar(Window window, UIElement AppTitleBar) { - FrameworkElement RootUI = (FrameworkElement)window.Content; if (AppWindowTitleBar.IsCustomizationSupported()) { @@ -168,7 +167,6 @@ private void MicaBackground_ActualThemeChanged(FrameworkElement sender, object a { SetConfigurationSourceTheme(); } - } private void SetConfigurationSourceTheme() diff --git a/Emerald.App/Emerald.App/MainWindow.xaml.cs b/Emerald.App/Emerald.App/MainWindow.xaml.cs index 7147aef9..080ee662 100644 --- a/Emerald.App/Emerald.App/MainWindow.xaml.cs +++ b/Emerald.App/Emerald.App/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -using Emerald.Core; +using Emerald.Core; using Emerald.Core.Tasks; using Emerald.WinUI.Helpers; using Emerald.WinUI.Helpers.Settings.JSON; @@ -87,7 +87,6 @@ public async void Initialize(object s, RoutedEventArgs e) if (BackupState.WantBackup) { - var r = await MessageBox.Show( Localized.Error.Localize(), Localized.LoadSettingsFailed.Localize(), @@ -156,7 +155,6 @@ void Tasks() TaskView.ChangeDescription(e.ID, string.Join(" ", (e.Message ?? "").Split(" ").Select(s => s.Localize()))); TaskView.ChangeSeverty(e.ID, e.Success ? InfoBarSeverity.Success : InfoBarSeverity.Error); ShowMiniTask(c, string.Join(" ", (e.Message ?? "").Split(" ").Select(s => s.Localize())), e.Success ? InfoBarSeverity.Success : InfoBarSeverity.Error); - }; } diff --git a/Emerald.App/Emerald.App/Models/Account.cs b/Emerald.App/Emerald.App/Models/Account.cs index 9c09afdb..5efae4e5 100644 --- a/Emerald.App/Emerald.App/Models/Account.cs +++ b/Emerald.App/Emerald.App/Models/Account.cs @@ -1,4 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; using Emerald.Core; using Emerald.WinUI.Enums; using Emerald.WinUI.Helpers; @@ -7,7 +7,6 @@ namespace Emerald.WinUI.Models { public partial class Account : Model { - [ObservableProperty] private string _UserName; diff --git a/Emerald.App/Emerald.App/Models/NavViewHeader.cs b/Emerald.App/Emerald.App/Models/NavViewHeader.cs index e725700e..a2fdde70 100644 --- a/Emerald.App/Emerald.App/Models/NavViewHeader.cs +++ b/Emerald.App/Emerald.App/Models/NavViewHeader.cs @@ -1,11 +1,10 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.UI.Xaml; namespace Emerald.WinUI.Models { public partial class NavViewHeader : Model { - [ObservableProperty] private string _HeaderText; diff --git a/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs b/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs index 84ef0769..8aa58e65 100644 --- a/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs +++ b/Emerald.App/Emerald.App/UserControls/AdaptiveItemPane.xaml.cs @@ -4,13 +4,11 @@ namespace Emerald.WinUI.UserControls { - /// <summary> /// A nice control from the RiseMP /// </summary> public sealed partial class AdaptiveItemPane : UserControl { - public static DependencyProperty LeftPaneProperty = DependencyProperty.Register("LeftPane", typeof(object), typeof(AdaptiveItemPane), new PropertyMetadata(null)); diff --git a/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs b/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs index 8933a8c1..2d219a94 100644 --- a/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs +++ b/Emerald.App/Emerald.App/UserControls/ExpanderItem.xaml.cs @@ -64,7 +64,5 @@ public Visibility BorderVisibility // Using a DependencyProperty as the backing store for BorderVisibility. This enables animation, styling, binding, etc... public static readonly DependencyProperty BorderVisibilityProperty = DependencyProperty.Register("BorderVisibility", typeof(Visibility), typeof(ExpanderItem), new PropertyMetadata(null)); - - } } diff --git a/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs index e21a2308..84f7669c 100644 --- a/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Home/AccountsPage.xaml.cs @@ -1,4 +1,4 @@ -using CmlLib.Core.Auth; +using CmlLib.Core.Auth; using Emerald.Core; using Emerald.WinUI.Helpers; using Emerald.WinUI.Models; @@ -122,7 +122,6 @@ private void btnSelectAll_Click(object sender, RoutedEventArgs e) item.IsChecked = val; } UpdateAll(); - } private void RemoveSelected() => Accounts.Remove(x => x.IsChecked); @@ -161,7 +160,6 @@ private void CancelLogin_Click(object sender, RoutedEventArgs e) var a = (sender as Button).DataContext as Account; SetEditor(null); Accounts.Add(a); - } public void UpdateMainSource() => SS.Accounts = Accounts.Select(x => @@ -244,7 +242,6 @@ private async void mitMicrosoftAdd_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - Core.Tasks.TasksHelper.CompleteTask(taskID, false, ex.Message); } } diff --git a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs index ab24c351..f2c077a4 100644 --- a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs @@ -1,4 +1,4 @@ -using CmlLib.Core; +using CmlLib.Core; using CmlLib.Core.Auth; using CmlLib.Core.Downloader; using CommunityToolkit.WinUI.Helpers; @@ -92,7 +92,6 @@ public async void Initialize() var r = await MessageBox.Show("Error".Localize(), "MCPathFailed".Localize().Replace("{Path}", SS.Settings.Minecraft.Path), MessageBoxButtons.CustomWithCancel, "Yes".Localize(), "SetDifMCPath".Localize()); if (r == MessageBoxResults.Cancel) Process.GetCurrentProcess().Kill(); // Application.Current.Exit() didn't kill the process - else if (r == MessageBoxResults.CustomResult2) { var fop = new FolderPicker @@ -106,7 +105,6 @@ public async void Initialize() SS.Settings.Minecraft.Path = f.Path; } } - } App.Current.Launcher.InitializeLauncher(new MinecraftPath(SS.Settings.Minecraft.Path)); @@ -152,7 +150,6 @@ public async void Initialize() if (SystemInformation.Instance.IsFirstRun) ShowTips(); - } public void ShowTips() @@ -262,7 +259,6 @@ void FindSubVers(MinecraftVersion ver) var found = splitText.All((key) => v.Version.ToLower().Contains(key)); if (found) suitableItems.Add(v); - } } @@ -463,7 +459,6 @@ private void AdaptiveItemPane_OnStretched(object sender, EventArgs e) s.Children.OfType<TextBlock>().ToList().ForEach(x => x.HorizontalAlignment = HorizontalAlignment.Left); NewsButton.HorizontalContentAlignment = ChangelogsButton.HorizontalContentAlignment = HorizontalAlignment.Left; - } } } diff --git a/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs index 0bafae92..0298ea6f 100644 --- a/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Home/NewsPage.xaml.cs @@ -27,7 +27,6 @@ public NewsPage() await Task.Delay(300); pnlEmpty.Visibility = App.Current.Launcher.News.Entries.Any() ? Visibility.Collapsed : Visibility.Visible; }; - } private void BackButton_Click(object sender, RoutedEventArgs e) => BackRequested?.Invoke(this, new EventArgs()); diff --git a/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs index 3632cc60..32cb10d4 100644 --- a/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Settings/AboutPage.xaml.cs @@ -1,4 +1,4 @@ -using CommunityToolkit.WinUI.UI.Controls; +using CommunityToolkit.WinUI.UI.Controls; using Emerald.Core; using Emerald.WinUI.Enums; using Emerald.WinUI.Helpers; @@ -102,7 +102,6 @@ private async void ToggleSwitch_Toggled(object sender, RoutedEventArgs e) { SS.Settings.App.WindowsHello = true; } - } private async void DeleteBackup_Click(object sender, RoutedEventArgs e) diff --git a/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs index b2d95df1..189fda80 100644 --- a/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Settings/AppearancePage.xaml.cs @@ -1,4 +1,4 @@ -using Emerald.Core; +using Emerald.Core; using Emerald.WinUI.Helpers; using Microsoft.UI.Xaml.Controls; using System.Collections.ObjectModel; @@ -93,7 +93,6 @@ private void GVColorList_SelectionChanged(object sender, SelectionChangedEventAr private void CustomTintColor_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - var c = SS.Settings.App.Appearance.CustomMicaTintColor; var cp = new ColorPicker() { diff --git a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml index f55b5ec7..29886196 100644 --- a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml +++ b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml @@ -13,59 +13,59 @@ xmlns:uc="using:Emerald.WinUI.UserControls" mc:Ignorable="d"> - <Grid Margin="16"> - <Grid.Resources> - <ResourceDictionary Source="/ResourceDictionaries/Expanders.xaml" /> - </Grid.Resources> + <Grid Margin="16"> + <Grid.Resources> + <ResourceDictionary Source="/ResourceDictionaries/Expanders.xaml" /> + </Grid.Resources> - <StackPanel Spacing="16"> - <StackPanel Spacing="4"> - <TextBlock FontWeight="SemiBold" Text="{helpers:Localize Name=GeneralSettings}" /> - <uc:Expander + <StackPanel Spacing="16"> + <StackPanel Spacing="4"> + <TextBlock FontWeight="SemiBold" Text="{helpers:Localize Name=GeneralSettings}" /> + <uc:Expander Title="{helpers:Localize Name=MinecraftPath}" Description="{helpers:Localize Name=MinecraftPathDescription}" ExpanderStyle="Static" Icon=""> - <HyperlinkButton + <HyperlinkButton ToolTipService.ToolTip="{x:Bind SS:SettingsSystem.Settings.Minecraft.Path, Mode=OneWay}" Click="btnChangeMPath_Click"> - <TextBlock + <TextBlock MaxWidth="250" VerticalAlignment="Center" Text="{x:Bind SS:SettingsSystem.Settings.Minecraft.Path, Mode=OneWay}" TextTrimming="CharacterEllipsis" /> - </HyperlinkButton> - </uc:Expander> + </HyperlinkButton> + </uc:Expander> - <uc:Expander + <uc:Expander Title="{helpers:Localize Name=RAM}" Description="{helpers:Localize Name=RAMDescription}" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <uc:Expander.HeaderControls> - <StackPanel Orientation="Horizontal" Spacing="5"> - <TextBlock x:Name="txtRam" VerticalAlignment="Center"> + <uc:Expander.HeaderControls> + <StackPanel Orientation="Horizontal" Spacing="5"> + <TextBlock x:Name="txtRam" VerticalAlignment="Center"> <Run Text="{x:Bind SS:SettingsSystem.Settings.Minecraft.RAMinGB, Mode=OneWay}" /> GB</TextBlock> - <RepeatButton + <RepeatButton Padding="5" Click="btnRamPlus_Click" Content="{helpers:FontIcon Glyph=}" FontWeight="SemiBold" /> - <RepeatButton + <RepeatButton Padding="5" Click="btnRamMinus_Click" Content="{helpers:FontIcon Glyph=}" FontWeight="SemiBold" /> - </StackPanel> - </uc:Expander.HeaderControls> - <uc:Expander.Controls> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="auto" /> - </Grid.ColumnDefinitions> - <Slider + </StackPanel> + </uc:Expander.HeaderControls> + <uc:Expander.Controls> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="auto" /> + </Grid.ColumnDefinitions> + <Slider Grid.Column="0" Maximum="{x:Bind Main:DirectResoucres.MaxRAM}" Minimum="{x:Bind Main:DirectResoucres.MinRAM}" @@ -73,78 +73,78 @@ SmallChange="1" LargeChange="64" Value="{x:Bind SS:SettingsSystem.Settings.Minecraft.RAM, Mode=TwoWay}" /> - </Grid> - </uc:Expander.Controls> - </uc:Expander> + </Grid> + </uc:Expander.Controls> + </uc:Expander> - <uc:Expander + <uc:Expander Title="{helpers:Localize Name=DownloadSettings}" Description="{helpers:Localize Name=DownloadSettingsDescription}" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <StackPanel Spacing="6"> - <uc:ExpanderItem + <StackPanel Spacing="6"> + <uc:ExpanderItem Title="{helpers:Localize Name=AssetsCheck}" Margin="0,-10,0,0" BorderVisibility="Collapsed"> - <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.Downloader.AssetsCheck, Mode=TwoWay}" /> - </uc:ExpanderItem> - <uc:ExpanderItem Title="{helpers:Localize Name=HashCheck}" Margin="0,0,0,-10"> - <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.Downloader.HashCheck, Mode=TwoWay}" /> - </uc:ExpanderItem> - </StackPanel> - </uc:Expander> - <uc:Expander + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.Downloader.AssetsCheck, Mode=TwoWay}" /> + </uc:ExpanderItem> + <uc:ExpanderItem Title="{helpers:Localize Name=HashCheck}" Margin="0,0,0,-10"> + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.Downloader.HashCheck, Mode=TwoWay}" /> + </uc:ExpanderItem> + </StackPanel> + </uc:Expander> + <uc:Expander Title="{helpers:Localize Name=AutoLogin}" Description="{helpers:Localize Name=AutoLoginDescription}" ExpanderStyle="Static" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <ToggleSwitch + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.App.AutoLogin, Mode=TwoWay}" OffContent="{helpers:Localize Name=No}" OnContent="{helpers:Localize Name=Yes}" /> - </uc:Expander> - </StackPanel> + </uc:Expander> + </StackPanel> - <StackPanel Spacing="4"> - <TextBlock FontWeight="SemiBold" Text="{helpers:Localize Name=AdvancedMinecraftSettings}" /> + <StackPanel Spacing="4"> + <TextBlock FontWeight="SemiBold" Text="{helpers:Localize Name=AdvancedMinecraftSettings}" /> - <uc:Expander + <uc:Expander Title="{helpers:Localize Name=MCasAdmin}" Description="{helpers:Localize Name=MCasAdminDescription}" ExpanderStyle="Static" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <ToggleSwitch + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.IsAdmin, Mode=TwoWay}" OffContent="{helpers:Localize Name=No}" OnContent="{helpers:Localize Name=Yes}" /> - </uc:Expander> - - <uc:Expander + </uc:Expander> + + <uc:Expander Title="{helpers:Localize Name=MCWindowsSize}" Description="{helpers:Localize Name=MCWindowsSizeDescription}" Icon=""> - <uc:Expander.HeaderControls> - <TextBlock Text="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.ScreenSizeStatus, Mode=OneWay}"/> - </uc:Expander.HeaderControls> - <StackPanel Spacing="5"> - <uc:ExpanderItem + <uc:Expander.HeaderControls> + <TextBlock Text="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.ScreenSizeStatus, Mode=OneWay}"/> + </uc:Expander.HeaderControls> + <StackPanel Spacing="5"> + <uc:ExpanderItem Title="{helpers:Localize Name=Width}" Margin="0,-10,0,0" BorderVisibility="Collapsed"> - <NumberBox + <NumberBox Minimum="0" Value="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.ScreenWidth, Mode=TwoWay}" SpinButtonPlacementMode="Inline" LargeChange="80" SmallChange="10" PlaceholderText="{helpers:Localize Name=Default}"/> - </uc:ExpanderItem> - <uc:ExpanderItem + </uc:ExpanderItem> + <uc:ExpanderItem Title="{helpers:Localize Name=Height}"> - <NumberBox + <NumberBox Margin="0,4,0,0" Minimum="0" Value="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.ScreenHeight, Mode=TwoWay}" @@ -152,33 +152,33 @@ LargeChange="80" SmallChange="10" PlaceholderText="{helpers:Localize Name=Default}"/> - </uc:ExpanderItem> - <uc:ExpanderItem Title="{helpers:Localize Name=FullScreen}" Margin="0,0,0,-10"> - <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.FullScreen, Mode=TwoWay}" /> - </uc:ExpanderItem> - </StackPanel> - </uc:Expander> - - <uc:Expander + </uc:ExpanderItem> + <uc:ExpanderItem Title="{helpers:Localize Name=FullScreen}" Margin="0,0,0,-10"> + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.FullScreen, Mode=TwoWay}" /> + </uc:ExpanderItem> + </StackPanel> + </uc:Expander> + + <uc:Expander Title="{helpers:Localize Name=JVMArgs}" Description="{helpers:Localize Name=JVMArgsDescription}" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <uc:ArgumentsListView /> - </uc:Expander> + <uc:ArgumentsListView /> + </uc:Expander> - <uc:Expander + <uc:Expander Title="{helpers:Localize Name=GameLogs}" Description="{helpers:Localize Name=GameLogsDescription}" ExpanderStyle="Static" Icon="" IsEnabled="{x:Bind Main:App.Current.Launcher.UIState, Mode=OneWay}"> - <ToggleSwitch + <ToggleSwitch IsOn="{x:Bind SS:SettingsSystem.Settings.Minecraft.JVM.GameLogs, Mode=TwoWay}" OffContent="{helpers:Localize Name=No}" OnContent="{helpers:Localize Name=Yes}" /> - </uc:Expander> - </StackPanel> - </StackPanel> - </Grid> -</Page> \ No newline at end of file + </uc:Expander> + </StackPanel> + </StackPanel> + </Grid> +</Page> diff --git a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs index 1fe2010a..be703784 100644 --- a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs @@ -1,4 +1,4 @@ -using CmlLib.Core; +using CmlLib.Core; using Emerald.WinUI.Enums; using Emerald.WinUI.Helpers; using Microsoft.UI.Xaml; @@ -54,10 +54,8 @@ async void Try() Try(); else Start(); - } } - } Start(); } @@ -72,7 +70,5 @@ private void btnRamMinus_Click(object sender, RoutedEventArgs e) => private void btnAutoRAM_Click(object sender, RoutedEventArgs e) => SS.Settings.Minecraft.RAM = DirectResoucres.MaxRAM / 2; - - } } diff --git a/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs b/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs index a72cf7a9..1f580601 100644 --- a/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs +++ b/Emerald.App/Emerald.App/Views/Store/InstallerPage.xaml.cs @@ -9,7 +9,7 @@ namespace Emerald.WinUI.Views.Store public sealed partial class InstallerPage : Page { public StoreItem Item { get; set; } - private ObservableCollection<Version> Versions = new(); + private ObservableCollection<Core.Store.Results.Version> Versions = new(); public InstallerPage() { InitializeComponent(); diff --git a/Emerald.Core/Emerald.Core.csproj b/Emerald.Core/Emerald.Core.csproj index 8bb6d6e5..80509451 100644 --- a/Emerald.Core/Emerald.Core.csproj +++ b/Emerald.Core/Emerald.Core.csproj @@ -1,20 +1,18 @@ <Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> - <TargetFramework>net7.0-windows10.0.22000.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - <RootNamespace>Emerald.Core</RootNamespace> - <Configurations>Debug;Release</Configurations> - <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion> - <RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers> - </PropertyGroup> + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> + <EnableWindowsTargeting>true</EnableWindowsTargeting> + </PropertyGroup> - <ItemGroup> - <PackageReference Include="CmlLib.Core" Version="3.3.10" /> - <PackageReference Include="CmlLib.Core.Auth.Microsoft" Version="3.0.1" /> - <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231008000" /> - <PackageReference Include="ProjBobcat" Version="1.16.0" /> - </ItemGroup> + <ItemGroup> + <PackageReference Include="CmlLib.Core" Version="3.3.10" /> + <PackageReference Include="CmlLib.Core.Auth.Microsoft" Version="3.0.1" /> + <PackageReference Include="ProjBobcat" Version="1.16.0" /> + <PackageReference Include="System.Text.Json" Version="8.0.5" /> + </ItemGroup> -</Project> +</Project> \ No newline at end of file diff --git a/Emerald.Core/Launcher.cs b/Emerald.Core/Launcher.cs index 33b44c6b..901af079 100644 --- a/Emerald.Core/Launcher.cs +++ b/Emerald.Core/Launcher.cs @@ -1,4 +1,4 @@ -using CmlLib.Core; +using CmlLib.Core; using CmlLib.Core.Downloader; using CmlLib.Core.Files; using CmlLib.Core.Installer.FabricMC; @@ -12,7 +12,6 @@ using Emerald.Core.Tasks; using ProjBobcat.Class.Model.Optifine; using System.ComponentModel; -using Windows.System; using ProgressChangedEventArgs = Emerald.Core.Args.ProgressChangedEventArgs; namespace Emerald.Core @@ -141,7 +140,7 @@ void ProgChange(object sender, System.ComponentModel.ProgressChangedEventArgs e) prog = e.ProgressPercentage; if (createTask) TasksHelper.EditProgressTask(id, prog, message: message); - }; + } void FileChange(DownloadFileChangedEventArgs e) { @@ -149,7 +148,7 @@ void FileChange(DownloadFileChangedEventArgs e) if (createTask) TasksHelper.EditProgressTask(id, prog, message: message); - }; + } try { diff --git a/Emerald.Core/News/JSON/Dimensions.cs b/Emerald.Core/News/JSON/Dimensions.cs index 7016bea8..54f415af 100644 --- a/Emerald.Core/News/JSON/Dimensions.cs +++ b/Emerald.Core/News/JSON/Dimensions.cs @@ -1,6 +1,5 @@ namespace Emerald.Core.News.JSON { - public class Dimensions { public int width { get; set; } diff --git a/Emerald.Core/News/JSON/Entry.cs b/Emerald.Core/News/JSON/Entry.cs index 908cf4ea..4abbe8df 100644 --- a/Emerald.Core/News/JSON/Entry.cs +++ b/Emerald.Core/News/JSON/Entry.cs @@ -2,7 +2,6 @@ namespace Emerald.Core.News.JSON { - public class Entry { [JsonPropertyName("title")] diff --git a/Emerald.Core/News/NewsHelper.cs b/Emerald.Core/News/NewsHelper.cs index 09718ca9..4965e924 100644 --- a/Emerald.Core/News/NewsHelper.cs +++ b/Emerald.Core/News/NewsHelper.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json; using System.Collections.ObjectModel; using System.ComponentModel; @@ -77,14 +77,13 @@ public ObservableCollection<JSON.Entry> Entries response = await wc.GetStringAsync(url); } - var json = JsonConvert.DeserializeObject<JSON.Root>(response); + var json = JsonSerializer.Deserialize<JSON.Root>(response); AllEntries = json?.entries != null ? new(json.entries.ToList()) : new(); Entries.Clear(); foreach (var item in AllEntries.Where(x => filter == null || filter.Contains(x.Category))) Entries.Add(item); - } catch { diff --git a/Emerald.Core/Optifine.cs b/Emerald.Core/Optifine.cs index 77eceba3..bc6b1147 100644 --- a/Emerald.Core/Optifine.cs +++ b/Emerald.Core/Optifine.cs @@ -1,5 +1,5 @@ -using CmlLib.Core; -using Newtonsoft.Json; +using CmlLib.Core; +using System.Text.Json; using ProjBobcat.Class.Helper; using ProjBobcat.Class.Model.Optifine; using ProjBobcat.DefaultComponent.Installer; @@ -27,7 +27,7 @@ public async Task<List<OptifineDownloadVersionModel>> GetOptifineVersions() c.Dispose(); - return JsonConvert.DeserializeObject<List<OptifineDownloadVersionModel>>(json); + return JsonSerializer.Deserialize<List<OptifineDownloadVersionModel>>(json); } catch { @@ -49,7 +49,7 @@ public async Task<List<OptifineDownloadVersionModel>> GetOptifineVersions() javaResult.Add(java); } - if (!javaResult.Any()) + if (javaResult.Count == 0) return (false, "NoJRE"); ProgressChanged(this, 0); diff --git a/Emerald.Core/Store/Labrinth.cs b/Emerald.Core/Store/Labrinth.cs index 8a1cc2e5..2b5dadd9 100644 --- a/Emerald.Core/Store/Labrinth.cs +++ b/Emerald.Core/Store/Labrinth.cs @@ -1,6 +1,6 @@ -using CmlLib.Core; +using CmlLib.Core; using Emerald.Core.Tasks; -using Newtonsoft.Json; +using System.Text.Json; using System.Collections.Specialized; using System.ComponentModel; using System.Net.Http.Headers; @@ -34,7 +34,6 @@ private async Task<string> Get(string code) return await response.Content.ReadAsStringAsync(); throw new Exception("Failed to GET: \"" + code + "\", StatusCode: " + response.StatusCode.ToString()); - } private int DownloadTaskID; @@ -75,7 +74,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa { int taskID = name == "" ? Tasks.TasksHelper.AddTask(Localized.GettingMods) : TasksHelper.AddTask(Localized.SearchStore, name); - string categouriesString = (categories != null && categories.Any() && categories.Length != 15) ? $"[\"categories:{string.Join("\"],[\"categories:", categories)}\"],".ToLower() : ""; + string categouriesString = (categories != null && categories.Length != 0 && categories.Length != 15) ? $"[\"categories:{string.Join("\"],[\"categories:", categories)}\"],".ToLower() : ""; Results.SearchResult s; @@ -84,7 +83,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa var fn = string.IsNullOrEmpty(name) ? "" : $"query={name}"; var url = $"search?{fn}&index={sortOptions.ToString().ToLower()}&facets=[{categouriesString}[\"project_type:mod\"]]&limit={limit}"; var json = await Get(url); - s = JsonConvert.DeserializeObject<Results.SearchResult>(json); + s = JsonSerializer.Deserialize<Results.SearchResult>(json); Tasks.TasksHelper.CompleteTask(taskID, true, name); return s; @@ -104,7 +103,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa try { var json = await Get("project/" + id); - s = JsonConvert.DeserializeObject<Results.ModrinthProject>(json); + s = JsonSerializer.Deserialize<Results.ModrinthProject>(json); Tasks.TasksHelper.CompleteTask(taskID, true, name); return s; } @@ -123,7 +122,7 @@ private async void ModrinthDownload(string link, string folderdir, string fileNa try { var json = await Get("project/" + id + "/version"); - s = JsonConvert.DeserializeObject<List<Results.Version>>(json); + s = JsonSerializer.Deserialize<List<Results.Version>>(json); Tasks.TasksHelper.CompleteTask(taskID, true); return s; } @@ -163,15 +162,12 @@ public enum SearchCategories Technology, Utility, Worldgen - } } namespace Emerald.Core.Store.Results { - public class File { - [JsonPropertyName("hashes")] public Hashes Hashes { get; set; } @@ -244,7 +240,6 @@ public class Version : INotifyPropertyChanged public class Hashes { - [JsonPropertyName("sha512")] public string Sha512 { get; set; } @@ -254,7 +249,6 @@ public class Hashes public class SearchResult { - [JsonPropertyName("hits")] public Hit[] Hits { get; set; } @@ -270,7 +264,6 @@ public class SearchResult public class Hit { - [JsonPropertyName("slug")] public string Slug { get; set; } @@ -421,7 +414,6 @@ public class License public class Donation_Urls { - [JsonPropertyName("id")] public string ID { get; set; } diff --git a/Emerald.Core/Util.cs b/Emerald.Core/Util.cs index 6c459be6..56b7c2bc 100644 --- a/Emerald.Core/Util.cs +++ b/Emerald.Core/Util.cs @@ -1,4 +1,4 @@ -namespace Emerald.Core +namespace Emerald.Core { public static class Util { @@ -91,7 +91,7 @@ private async Task ProcessContentStream(long? totalDownloadSize, Stream contentS using var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true); do { - var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length); + var bytesRead = await contentStream.ReadAsync(buffer); if (bytesRead == 0) { isMoreToRead = false; diff --git a/Emerald.CoreX/Core.cs b/Emerald.CoreX/Core.cs new file mode 100644 index 00000000..4f8ef55d --- /dev/null +++ b/Emerald.CoreX/Core.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Logging; +using CmlLib.Core; +using CmlLib.Core.VersionMetadata; +namespace Emerald.CoreX; + +public class Core(ILogger logger) +{ + public MinecraftLauncher Launcher { get; set; } + public void InitializeLauncher() + { + Launcher = new MinecraftLauncher(); + } +} \ No newline at end of file diff --git a/Emerald.CoreX/Emerald.CoreX.csproj b/Emerald.CoreX/Emerald.CoreX.csproj new file mode 100644 index 00000000..60f221b0 --- /dev/null +++ b/Emerald.CoreX/Emerald.CoreX.csproj @@ -0,0 +1,19 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="CmlLib.Core" /> + <PackageReference Include="CmlLib.Core.Auth.Microsoft" /> + <PackageReference Include="CommunityToolkit.Mvvm" /> + <PackageReference Include="ProjBobcat" /> + <PackageReference Include="Microsoft.Extensions.Logging" /> + <PackageReference Include="Newtonsoft.Json" /> + <PackageReference Include="RestSharp" /> + <PackageReference Include="System.Text.Json" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Emerald.CoreX/Enums/MCVersionType.cs b/Emerald.CoreX/Enums/MCVersionType.cs new file mode 100644 index 00000000..6d90c3d0 --- /dev/null +++ b/Emerald.CoreX/Enums/MCVersionType.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Enums +{ + public enum MCVersionType + { + Vanilla, + Fabric, + Lite, + Forge, + Optifine, + Undefined + } +} diff --git a/Emerald.CoreX/Helpers/FileDownloader.cs b/Emerald.CoreX/Helpers/FileDownloader.cs new file mode 100644 index 00000000..8023e4bc --- /dev/null +++ b/Emerald.CoreX/Helpers/FileDownloader.cs @@ -0,0 +1,124 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Emerald.CoreX.Helpers; + +public class FileDownloader +{ + private readonly ILogger _logger; + private readonly HttpClient _httpClient; + + public FileDownloader(ILogger logger, HttpClient httpClient) + { + _logger = logger; + _httpClient = httpClient; + } + + /// <summary> + /// Downloads a file from the specified URL to the given file path. + /// </summary> + /// <param name="url">The URL of the file to download.</param> + /// <param name="filePath">The path where the file should be saved.</param> + /// <param name="expectedHashes">Optional. The expected hashes for file integrity verification.</param> + /// <param name="progress">Optional. An IProgress{double} to report download progress.</param> + /// <param name="cancellationToken">Optional. A CancellationToken to support cancellation of the download.</param> + /// <returns>A task representing the asynchronous download operation.</returns> + public async Task DownloadFileAsync(string url, string filePath, Hashes? expectedHashes = null, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + _logger.LogInformation($"Downloading file from URL: {url}"); + + try + { + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + + // Ensure any existing file is closed before overwriting + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + + using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response.EnsureSuccessStatusCode(); + + var totalBytes = response.Content.Headers.ContentLength ?? -1L; + using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); + using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192, true); + + var buffer = new byte[8192]; + long totalBytesRead = 0; + int bytesRead; + + while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken)) > 0) + { + await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken); + totalBytesRead += bytesRead; + progress?.Report(totalBytes > 0 ? (double)totalBytesRead / totalBytes * 100 : 0); + } + + fileStream.Dispose(); + contentStream.Dispose(); + _logger.LogInformation($"Successfully downloaded file to: {filePath}"); + + if (expectedHashes != null && !await VerifyFileIntegrityAsync(filePath, expectedHashes)) + { + throw new Exception("Downloaded file failed integrity check"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred while downloading file from URL: {url}"); + throw; + } + } + + + /// <summary> + /// Verifies the integrity of a file against expected hashes. + /// </summary> + /// <param name="filePath">The path of the file to verify.</param> + /// <param name="expectedHashes">The expected hashes for the file.</param> + /// <returns>True if the file integrity is verified, false otherwise.</returns> + private async Task<bool> VerifyFileIntegrityAsync(string filePath, Hashes expectedHashes) + { + try + { + using var sha1 = SHA1.Create(); + using var sha512 = SHA512.Create(); + + // Open the file with read-only access and allow other processes to read the file + using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + + // Compute SHA-1 hash + var sha1Hash = BitConverter.ToString(await sha1.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant(); + + // Reset stream position for the next hash computation + stream.Position = 0; + + // Compute SHA-512 hash + var sha512Hash = BitConverter.ToString(await sha512.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant(); + + return sha1Hash == expectedHashes.Sha1 && sha512Hash == expectedHashes.Sha512; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred during file integrity verification for file: {filePath}"); + throw; + } + } + +} + + +public class Hashes +{ + [JsonPropertyName("sha512")] public string Sha512 { get; set; } + + [JsonPropertyName("sha1")] public string Sha1 { get; set; } +} diff --git a/Emerald.CoreX/Installers/Fabric.cs b/Emerald.CoreX/Installers/Fabric.cs new file mode 100644 index 00000000..53c693ed --- /dev/null +++ b/Emerald.CoreX/Installers/Fabric.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Installers +{ + internal class Fabric : IModLoaderInstaller + { + } +} diff --git a/Emerald.CoreX/Installers/Forge.cs b/Emerald.CoreX/Installers/Forge.cs new file mode 100644 index 00000000..a51d3718 --- /dev/null +++ b/Emerald.CoreX/Installers/Forge.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Installers +{ + internal class Forge : IModLoaderInstaller + { + } +} diff --git a/Emerald.CoreX/Installers/IModLoaderInstaller.cs b/Emerald.CoreX/Installers/IModLoaderInstaller.cs new file mode 100644 index 00000000..a91b12c6 --- /dev/null +++ b/Emerald.CoreX/Installers/IModLoaderInstaller.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Installers +{ + internal interface IModLoaderInstaller + { + } +} diff --git a/Emerald.CoreX/Installers/LiteLoader.cs b/Emerald.CoreX/Installers/LiteLoader.cs new file mode 100644 index 00000000..8798c807 --- /dev/null +++ b/Emerald.CoreX/Installers/LiteLoader.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Installers +{ + internal class LiteLoader: IModLoaderInstaller + { + } +} diff --git a/Emerald.CoreX/Installers/Optifine.cs b/Emerald.CoreX/Installers/Optifine.cs new file mode 100644 index 00000000..dec37deb --- /dev/null +++ b/Emerald.CoreX/Installers/Optifine.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Installers +{ + internal class Optifine : IModLoaderInstaller + { + } +} diff --git a/Emerald.CoreX/Models/MCVersion.cs b/Emerald.CoreX/Models/MCVersion.cs new file mode 100644 index 00000000..84248297 --- /dev/null +++ b/Emerald.CoreX/Models/MCVersion.cs @@ -0,0 +1,29 @@ +using CmlLib.Core.Version; +using CmlLib.Core.VersionMetadata; +using CommunityToolkit.Mvvm.ComponentModel; +using Emerald.CoreX.Enums; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Models +{ + public partial class MCVersion + { + public MCVersionType Type { get; private set; } + public MVersionType ReleaseType { get; private set; } + + public string Name { get; private set; } + + public string DisplayName { get; set; } + + public IVersionMetadata Metadata { get; private set; } + + public bool Local { get; private set; } + + public MCVersion[] Subversions { get; private set; } + } +} diff --git a/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs b/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs new file mode 100644 index 00000000..b764f6dc --- /dev/null +++ b/Emerald.CoreX/Store/Modrinth/IModrinthStore.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Emerald.CoreX.Store.Modrinth.JSON; + +namespace Emerald.CoreX.Store.Modrinth; + +public interface IModrinthStore +{ + public Task<SearchResult?> SearchAsync(string query, int limit = 15, + SearchSortOptions sortOptions = SearchSortOptions.Relevance, string[]? categories = null); + public Task LoadCategoriesAsync(); + public Task<StoreItem?> GetItemAsync(string id); + public Task<List<ItemVersion>?> GetVersionsAsync(string id); + public Task DownloadItemAsync(ItemFile file, string projectType, IProgress<double>? progress = null, CancellationToken cancellationToken = default); + public Category[] Categories { get; } +} diff --git a/Emerald.CoreX/Store/Modrinth/JSON.cs b/Emerald.CoreX/Store/Modrinth/JSON.cs new file mode 100644 index 00000000..42bde2e5 --- /dev/null +++ b/Emerald.CoreX/Store/Modrinth/JSON.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Text.Json; +using System.Text.Json.Serialization; +using Emerald.CoreX.Helpers; + +namespace Emerald.CoreX.Store.Modrinth.JSON; + +public class SearchResult +{ + [JsonPropertyName("hits")] public List<SearchHit> Hits { get; set; } + + [JsonPropertyName("offset")] public int Offset { get; set; } + + [JsonPropertyName("limit")] public int Limit { get; set; } + + [JsonPropertyName("total_hits")] public int TotalHits { get; set; } +} + +public class Category +{ + public string icon { get; set; } + public string name { get; set; } + public string project_type { get; set; } + public string header { get; set; } +} + +public class SearchHit +{ + [JsonPropertyName("slug")] public string Slug { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("categories")] public string[] Categories { get; set; } + + [JsonPropertyName("client_side")] public string ClientSide { get; set; } + + [JsonPropertyName("server_side")] public string ServerSide { get; set; } + + [JsonPropertyName("project_type")] public string ProjectType { get; set; } + + [JsonPropertyName("downloads")] public int Downloads { get; set; } + + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } + + [JsonPropertyName("project_id")] public string ProjectId { get; set; } + + [JsonPropertyName("author")] public string Author { get; set; } + + [JsonPropertyName("versions")] public string[] Versions { get; set; } + + [JsonPropertyName("follows")] public int Follows { get; set; } + + [JsonPropertyName("date_created")] public DateTime DateCreated { get; set; } + + [JsonPropertyName("date_modified")] public DateTime DateModified { get; set; } + + [JsonPropertyName("latest_version")] public string LatestVersion { get; set; } + + [JsonPropertyName("license")] public string License { get; set; } + + [JsonPropertyName("gallery")] public string[] Gallery { get; set; } +} + +public class StoreItem +{ + [JsonPropertyName("id")] public string ID { get; set; } + + [JsonPropertyName("slug")] public string Slug { get; set; } + + [JsonPropertyName("project_type")] public string ProjectType { get; set; } + + [JsonPropertyName("team")] public string Team { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("body")] public string Body { get; set; } + + [JsonPropertyName("body_url")] public string BodyUrl { get; set; } + + [JsonPropertyName("published")] public DateTime PublishedDate { get; set; } + + [JsonPropertyName("updated")] public DateTime UpdatedDate { get; set; } + + [JsonPropertyName("status")] public string Status { get; set; } + + [JsonPropertyName("moderator_message")] public object? ModeratorMessage { get; set; } + + [JsonPropertyName("license")] public License License { get; set; } + + [JsonPropertyName("client_side")] public string ClientSide { get; set; } + + [JsonPropertyName("server_side")] public string ServerSide { get; set; } + + [JsonPropertyName("downloads")] public int Downloads { get; set; } + + [JsonPropertyName("followers")] public int Followers { get; set; } + + [JsonPropertyName("categories")] public string[] Categories { get; set; } + + [JsonPropertyName("versions")] public string[] Versions { get; set; } + + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } + + [JsonPropertyName("issues_url")] public string IssuesUrl { get; set; } + + [JsonPropertyName("source_url")] public string SourceUrl { get; set; } + + [JsonPropertyName("wiki_url")] public object? WikiUrl { get; set; } + + [JsonPropertyName("discord_url")] public string DiscordUrl { get; set; } + + [JsonPropertyName("donation_urls")] public DonationUrls[] DonationUrls { get; set; } + + [JsonPropertyName("gallery")] public object[] Gallery { get; set; } +} + +public class ItemVersion : INotifyPropertyChanged +{ + public bool IsDetailsVisible { get; set; } = false; + public string? FileName => Files.FirstOrDefault(x => x.Primary)?.Filename; + + [JsonPropertyName("id")] public string ID { get; set; } + + [JsonPropertyName("project_id")] public string ProjectId { get; set; } + + [JsonPropertyName("author_id")] public string AuthorId { get; set; } + + [JsonPropertyName("featured")] public bool Featured { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("version_number")] public string VersionNumber { get; set; } + + [JsonPropertyName("changelog")] public string Changelog { get; set; } + + [JsonPropertyName("changelog_url")] public string? ChangelogUrl { get; set; } + + [JsonPropertyName("date_published")] public DateTime DatePublished { get; set; } + + [JsonPropertyName("downloads")] public int Downloads { get; set; } + + [JsonPropertyName("version_type")] public string VersionType { get; set; } + + [JsonPropertyName("files")] public ItemFile[] Files { get; set; } + + [JsonPropertyName("dependencies")] public Dependency[] Dependencies { get; set; } + + [JsonPropertyName("game_versions")] public string[] GameVersions { get; set; } + + [JsonPropertyName("loaders")] public string[] Loaders { get; set; } + + public event PropertyChangedEventHandler? PropertyChanged; + + public void InvokePropertyChanged(string? propertyName = null) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} + +public class Dependency +{ + [JsonPropertyName("version_id")] public string VersionId { get; set; } + + [JsonPropertyName("project_id")] public string ProjectId { get; set; } + + [JsonPropertyName("file_name")] public string FileName { get; set; } + + [JsonPropertyName("dependency_type")] public string DependencyType { get; set; } +} + +public class ItemFile +{ + [JsonPropertyName("hashes")] public Hashes Hashes { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } + + [JsonPropertyName("filename")] public string Filename { get; set; } + + [JsonPropertyName("primary")] public bool Primary { get; set; } + + [JsonPropertyName("size")] public int Size { get; set; } +} + +public class License +{ + [JsonPropertyName("id")] public string ID { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} + +public class DonationUrls +{ + [JsonPropertyName("id")] public string ID { get; set; } + + [JsonPropertyName("platform")] public string Platform { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs b/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs new file mode 100644 index 00000000..d905ee27 --- /dev/null +++ b/Emerald.CoreX/Store/Modrinth/ModrinthStore.cs @@ -0,0 +1,211 @@ +using CmlLib.Core; +using System.Text.Json; +using RestSharp; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Emerald.CoreX.Store.Modrinth.JSON; +using Emerald.CoreX.Helpers; + +namespace Emerald.CoreX.Store.Modrinth; + +public abstract class ModrinthStore : IModrinthStore +{ + protected readonly RestClient _client; + protected readonly ILogger _logger; + protected readonly string _projectType; + protected readonly FileDownloader _fileDownloader; + public MinecraftPath MCPath { get; } + public Category[] Categories { get; private set; } = []; + + /// <summary> + /// Initializes a new instance of the ModrinthStore class. + /// </summary> + /// <param name="path">The Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + /// <param name="projectType">The type of project (e.g., mod, plugin, resourcepack).</param> + protected ModrinthStore(MinecraftPath path, ILogger logger, string projectType) + { + _client = new RestClient("https://api.modrinth.com/v2/"); + _client.AddDefaultHeader("Accept", "application/json"); + MCPath = path; + _logger = logger; + _projectType = projectType; + _fileDownloader = new FileDownloader(logger, new()); + } + + + /// <summary> + /// Loads categories for the specified project type from the Modrinth API. + /// </summary> + /// <returns>A task representing the asynchronous operation.</returns> + public async Task LoadCategoriesAsync() + { + var request = new RestRequest("tag/category"); + + try + { + var response = await _client.ExecuteAsync(request); + if (response.IsSuccessful) + { + var all = JsonSerializer.Deserialize<List<Category>>(response.Content); + + var _categories = all + .Where(i => i.header == "categories" + && i.project_type == _projectType + && !string.IsNullOrWhiteSpace(i.icon) + && !string.IsNullOrWhiteSpace(i.name)) + .ToList(); + Categories = _categories.ToArray(); + _logger.LogInformation($"Loaded {_categories.Count} {_projectType} categories."); + } + else + { + _logger.LogError($"Failed to load {_projectType} categories. Status code: {response.StatusCode}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred while loading {_projectType} categories."); + } + } + + /// <summary> + /// Searches for items in the Modrinth store based on the provided query and options. + /// </summary> + /// <param name="query">The search query string.</param> + /// <param name="limit">The maximum number of results to return (default is 15).</param> + /// <param name="sortOptions">The sorting options for the search results.</param> + /// <param name="categories">An optional array of category names to filter the results.</param> + /// <returns>A task that represents the asynchronous operation. The task result contains the search results or null if an error occurred.</returns> + public virtual async Task<SearchResult?> SearchAsync(string query, int limit = 15, + SearchSortOptions sortOptions = SearchSortOptions.Relevance, string[]? categories = null) + { + _logger.LogInformation($"Searching store for {_projectType}s with query: {query}"); + + try + { + // Prepare the facets parameter correctly + string facets = "[[\"project_type:" + _projectType + "\"]"; + if (categories != null && categories.Length != 0) + { + var categoryFacets = categories.Select(cat => $"\"categories:{cat}\""); + facets += ",[" + string.Join(",", categoryFacets) + "]"; + } + facets += "]"; + + var request = new RestRequest("search") + .AddParameter("index", sortOptions.ToString().ToLowerInvariant()) + .AddParameter("facets", facets) + .AddParameter("limit", limit); + + if (!string.IsNullOrEmpty(query)) + { + request.AddParameter("query", query); + } + + var response = await _client.ExecuteAsync(request); + if (response.IsSuccessful) + { + var result = JsonSerializer.Deserialize<SearchResult>(response.Content); + _logger.LogInformation($"Search completed successfully. Found {result.TotalHits} {_projectType}s."); + return result; + } + else + { + _logger.LogError($"API request failed: {response.ErrorMessage}"); + throw new Exception($"API request failed: {response.ErrorMessage}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred while searching for {_projectType}s"); + return null; + } + } + + /// <summary> + /// Retrieves detailed information about a specific item from the Modrinth store. + /// </summary> + /// <param name="id">The unique identifier of the item.</param> + /// <returns>A task that represents the asynchronous operation. The task result contains the item details or null if an error occurred.</returns> + public virtual async Task<StoreItem?> GetItemAsync(string id) + { + _logger.LogInformation($"Fetching {_projectType} with ID: {id}"); + + try + { + var request = new RestRequest($"project/{id}"); + var response = await _client.ExecuteAsync(request); + + if (response.IsSuccessful) + { + var item = JsonSerializer.Deserialize<StoreItem>(response.Content); + _logger.LogInformation($"Successfully fetched {_projectType} with ID: {id}"); + return item; + } + else + { + _logger.LogError($"API request failed: {response.ErrorMessage}"); + throw new Exception($"API request failed: {response.ErrorMessage}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred while fetching {_projectType} with ID: {id}"); + return null; + } + } + + /// <summary> + /// Retrieves all versions of a specific item from the Modrinth store. + /// </summary> + /// <param name="id">The unique identifier of the item.</param> + /// <returns>A task that represents the asynchronous operation. The task result contains a list of item versions or null if an error occurred.</returns> + public virtual async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + _logger.LogInformation($"Fetching versions for {_projectType} with ID: {id}"); + + try + { + var request = new RestRequest($"project/{id}/version"); + var response = await _client.ExecuteAsync(request); + + if (response.IsSuccessful) + { + var versions = JsonSerializer.Deserialize<List<ItemVersion>>(response.Content); + _logger.LogInformation($"Successfully fetched versions for {_projectType} with ID: {id}"); + return versions; + } + else + { + _logger.LogError($"API request failed: {response.ErrorMessage}"); + throw new Exception($"API request failed: {response.ErrorMessage}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error occurred while fetching versions for {_projectType} with ID: {id}"); + return null; + } + } + + /// <summary> + /// Downloads a specific file for an item from the Modrinth store. + /// </summary> + /// <param name="file">The file information object containing download details.</param> + /// <param name="projectType">The type of project being downloaded (e.g., "mods", "resourcepacks").</param> + /// <param name="progress">Optional. An IProgress{double} to report download progress.</param> + /// <param name="cancellationToken">Optional. A CancellationToken to support cancellation of the download.</param> + /// <returns>A task that represents the asynchronous download operation.</returns> + public virtual async Task DownloadItemAsync(ItemFile file, string projectType, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + var filePath = Path.Combine(MCPath.BasePath, projectType, file.Filename); + await _fileDownloader.DownloadFileAsync(file.Url, filePath, file.Hashes, progress, cancellationToken); + } +} diff --git a/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs b/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs new file mode 100644 index 00000000..416e8af9 --- /dev/null +++ b/Emerald.CoreX/Store/Modrinth/SearchSortOptions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Emerald.CoreX.Store.Modrinth; +public enum SearchSortOptions +{ + Relevance, + Downloads, + Follows, + Updated, + Newest +} diff --git a/Emerald.CoreX/Store/Modrinth/Stores.cs b/Emerald.CoreX/Store/Modrinth/Stores.cs new file mode 100644 index 00000000..9f2b48b0 --- /dev/null +++ b/Emerald.CoreX/Store/Modrinth/Stores.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Emerald.CoreX.Store.Modrinth.JSON; +using Microsoft.Extensions.Logging; +using CmlLib.Core; +namespace Emerald.CoreX.Store.Modrinth; + +public class ModStore : ModrinthStore +{ + /// <summary> + /// Initializes a new instance of the ModStore class with a custom Minecraft path. + /// </summary> + /// <param name="path">The custom Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + public ModStore(MinecraftPath path, ILogger logger) : base(path, logger, "mod") + { + } + + /// <summary> + /// Initializes a new instance of the ModStore class with the default Minecraft path. + /// </summary> + /// <param name="logger">The logger instance.</param> + public ModStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger) + { + } + + public override async Task<StoreItem?> GetItemAsync(string id) + { + // Implement mod-specific logic if needed + return await base.GetItemAsync(id); + } + + public override async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + // Implement mod-specific logic if needed + return await base.GetVersionsAsync(id); + } + + public override async Task DownloadItemAsync(ItemFile file, string projectType, IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + // Implement mod-specific download logic + await base.DownloadItemAsync(file, "mods",progress, cancellationToken); + } +} + +public class PluginStore : ModrinthStore +{ + /// <summary> + /// Initializes a new instance of the PluginStore class with a custom Minecraft path. + /// </summary> + /// <param name="path">The custom Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + public PluginStore(MinecraftPath path, ILogger logger) : base(path, logger, "plugin") + { + } + + /// <summary> + /// Initializes a new instance of the PluginStore class with the default Minecraft path. + /// </summary> + /// <param name="logger">The logger instance.</param> + public PluginStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger) + { + } + + public override async Task<StoreItem?> GetItemAsync(string id) + { + return await base.GetItemAsync(id); + } + + public override async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + return await base.GetVersionsAsync(id); + } + + public override async Task DownloadItemAsync(ItemFile file, string projectType, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + await base.DownloadItemAsync(file, "mods", progress, cancellationToken); + } +} + +public class ResourcePackStore : ModrinthStore +{ + /// <summary> + /// Initializes a new instance of the ResourcePackStore class with a custom Minecraft path. + /// </summary> + /// <param name="path">The custom Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + public ResourcePackStore(MinecraftPath path, ILogger logger) : base(path, logger, "resourcepack") + { + } + + /// <summary> + /// Initializes a new instance of the ResourcePackStore class with the default Minecraft path. + /// </summary> + /// <param name="logger">The logger instance.</param> + public ResourcePackStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger) + { + } + + public override async Task<StoreItem?> GetItemAsync(string id) + { + return await base.GetItemAsync(id); + } + + public override async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + return await base.GetVersionsAsync(id); + } + + public override async Task DownloadItemAsync(ItemFile file, string projectType, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + await base.DownloadItemAsync(file, "resourcepacks", progress, cancellationToken); + } +} + +public class ShaderStore : ModrinthStore +{ + /// <summary> + /// Initializes a new instance of the ShaderStore class with a custom Minecraft path. + /// </summary> + /// <param name="path">The custom Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + public ShaderStore(MinecraftPath path, ILogger logger) : base(path, logger, "shader") + { + } + + /// <summary> + /// Initializes a new instance of the ShaderStore class with the default Minecraft path. + /// </summary> + /// <param name="logger">The logger instance.</param> + public ShaderStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger) + { + } + + public override async Task<StoreItem?> GetItemAsync(string id) + { + // Implement shader-specific logic if needed + return await base.GetItemAsync(id); + } + + public override async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + // Implement shader-specific logic if needed + return await base.GetVersionsAsync(id); + } + + public override async Task DownloadItemAsync(ItemFile file, string projectType, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + // Implement shader-specific download logic + await base.DownloadItemAsync(file, "shaderpacks", progress, cancellationToken); + } +} + +public class ModpackStore : ModrinthStore +{ + /// <summary> + /// Initializes a new instance of the ModpackStore class with a custom Minecraft path. + /// </summary> + /// <param name="path">The custom Minecraft path.</param> + /// <param name="logger">The logger instance.</param> + public ModpackStore(MinecraftPath path, ILogger logger) : base(path, logger, "modpack") + { + } + + /// <summary> + /// Initializes a new instance of the ModpackStore class with the default Minecraft path. + /// </summary> + /// <param name="logger">The logger instance.</param> + public ModpackStore(ILogger logger) : this(new MinecraftPath(MinecraftPath.GetOSDefaultPath()), logger) + { + } + + public override async Task<StoreItem?> GetItemAsync(string id) + { + // Implement modpack-specific logic if needed + return await base.GetItemAsync(id); + } + + public override async Task<List<ItemVersion>?> GetVersionsAsync(string id) + { + // Implement modpack-specific logic if needed + return await base.GetVersionsAsync(id); + } + + public override async Task DownloadItemAsync(ItemFile file, string projectType, + IProgress<double>? progress = null, CancellationToken cancellationToken = default) + { + // Implement modpack-specific download logic + await base.DownloadItemAsync(file, "modpacks", progress, cancellationToken); + } +} diff --git a/Emerald.sln b/Emerald.sln index 053ff471..bb5184e0 100644 --- a/Emerald.sln +++ b/Emerald.sln @@ -1,87 +1,109 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.2.32602.215 +VisualStudioVersion = 17.11.35103.136 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.Core", "Emerald.Core\Emerald.Core.csproj", "{90389A34-9F42-4489-871A-DC726D74854D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald", "Emerald\Emerald.csproj", "{9D3213F4-E514-4E7D-872A-725DB4872436}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.App", "Emerald.App\Emerald.App\Emerald.App.csproj", "{F2065381-1265-4FEB-841A-E5798E62761D}" -EndProject -Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package.WinUI", "Emerald.App\Emerald.App.Package\Package.WinUI.wapproj", "{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{2BB1A64C-5C5D-415D-B816-66E2152E2703}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3142BDF3-E95C-4F0F-8701-CF981F6EA3C0}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitattributes = .gitattributes .gitignore = .gitignore - .github\azp\azp.yml = .github\azp\azp.yml - README.md = README.md + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + Directory.Packages.props = Directory.Packages.props + global.json = global.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmlLib", "CMLLib\CmlLib\CmlLib.csproj", "{7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.App", "Emerald.App\Emerald.App\Emerald.App.csproj", "{196FD412-FCBA-4266-A75E-5648F6AADDFB}" +EndProject +Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package.WinUI", "Emerald.App\Emerald.App.Package\Package.WinUI.wapproj", "{59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.Core", "Emerald.Core\Emerald.Core.csproj", "{BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emerald.CoreX", "Emerald.CoreX\Emerald.CoreX.csproj", "{D103EFF9-90EA-49C7-9DD9-636E6056E9C3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|ARM64 = Debug|ARM64 + Debug|arm64 = Debug|arm64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|ARM64 = Release|ARM64 + Release|arm64 = Release|arm64 Release|x64 = Release|x64 Release|x86 = Release|x86 + SkipOld|Any CPU = SkipOld|Any CPU + SkipOld|arm64 = SkipOld|arm64 + SkipOld|x64 = SkipOld|x64 + SkipOld|x86 = SkipOld|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM.Build.0 = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|ARM64.Build.0 = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x64.ActiveCfg = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x64.Build.0 = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x86.ActiveCfg = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Debug|x86.Build.0 = Debug|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|Any CPU.Build.0 = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM.ActiveCfg = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM.Build.0 = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM64.ActiveCfg = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|ARM64.Build.0 = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|x64.ActiveCfg = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|x64.Build.0 = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|x86.ActiveCfg = Release|Any CPU - {90389A34-9F42-4489-871A-DC726D74854D}.Release|x86.Build.0 = Release|Any CPU - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|Any CPU.ActiveCfg = Debug|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|Any CPU.Build.0 = Debug|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM.ActiveCfg = Debug|x86 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM.Build.0 = Debug|x86 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM64.ActiveCfg = Debug|arm64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|ARM64.Build.0 = Debug|arm64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x64.ActiveCfg = Debug|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x64.Build.0 = Debug|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x86.ActiveCfg = Debug|x86 - {F2065381-1265-4FEB-841A-E5798E62761D}.Debug|x86.Build.0 = Debug|x86 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|Any CPU.ActiveCfg = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|Any CPU.Build.0 = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM.ActiveCfg = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM.Build.0 = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM64.ActiveCfg = Release|arm64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|ARM64.Build.0 = Release|arm64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x64.ActiveCfg = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x64.Build.0 = Release|x64 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x86.ActiveCfg = Release|x86 - {F2065381-1265-4FEB-841A-E5798E62761D}.Release|x86.Build.0 = Release|x86 + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.ActiveCfg = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.Build.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|arm64.Deploy.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.Build.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x64.Deploy.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.Build.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Debug|x86.Deploy.0 = Debug|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|Any CPU.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|arm64.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x64.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.Release|x86.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|Any CPU.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|arm64.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x64.Deploy.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.ActiveCfg = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.Build.0 = Release|Any CPU + {9D3213F4-E514-4E7D-872A-725DB4872436}.SkipOld|x86.Deploy.0 = Release|Any CPU + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|Any CPU.ActiveCfg = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|Any CPU.Build.0 = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|arm64.ActiveCfg = Debug|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|arm64.Build.0 = Debug|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x64.ActiveCfg = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x64.Build.0 = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x86.ActiveCfg = Debug|x86 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Debug|x86.Build.0 = Debug|x86 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|Any CPU.ActiveCfg = Release|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|Any CPU.Build.0 = Release|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|arm64.ActiveCfg = Release|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|arm64.Build.0 = Release|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x64.ActiveCfg = Release|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x64.Build.0 = Release|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x86.ActiveCfg = Release|x86 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.Release|x86.Build.0 = Release|x86 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|Any CPU.ActiveCfg = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|Any CPU.Build.0 = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|arm64.ActiveCfg = Debug|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|arm64.Build.0 = Debug|arm64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x64.ActiveCfg = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x64.Build.0 = Debug|x64 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x86.ActiveCfg = Debug|x86 + {196FD412-FCBA-4266-A75E-5648F6AADDFB}.SkipOld|x86.Build.0 = Debug|x86 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.ActiveCfg = Debug|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.Build.0 = Debug|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|Any CPU.Deploy.0 = Debug|x64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.ActiveCfg = Debug|x86 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.Build.0 = Debug|x86 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM.Deploy.0 = Debug|x86 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.ActiveCfg = Debug|arm64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.Build.0 = Debug|arm64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|ARM64.Deploy.0 = Debug|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.ActiveCfg = Debug|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.Build.0 = Debug|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|arm64.Deploy.0 = Debug|arm64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.ActiveCfg = Debug|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.Build.0 = Debug|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Debug|x64.Deploy.0 = Debug|x64 @@ -91,43 +113,80 @@ Global {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.ActiveCfg = Release|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.Build.0 = Release|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|Any CPU.Deploy.0 = Release|x64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.ActiveCfg = Release|x64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.Build.0 = Release|x64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM.Deploy.0 = Release|x64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.ActiveCfg = Release|arm64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.Build.0 = Release|arm64 - {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|ARM64.Deploy.0 = Release|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.ActiveCfg = Release|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.Build.0 = Release|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|arm64.Deploy.0 = Release|arm64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.ActiveCfg = Release|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.Build.0 = Release|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x64.Deploy.0 = Release|x64 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.ActiveCfg = Release|x86 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.Build.0 = Release|x86 {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.Release|x86.Deploy.0 = Release|x86 - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM.Build.0 = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|ARM64.Build.0 = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x64.ActiveCfg = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x64.Build.0 = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x86.ActiveCfg = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Debug|x86.Build.0 = Debug|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|Any CPU.Build.0 = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM.ActiveCfg = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM.Build.0 = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM64.ActiveCfg = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|ARM64.Build.0 = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x64.ActiveCfg = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x64.Build.0 = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x86.ActiveCfg = Release|Any CPU - {7FAC2A8B-C2EF-47FA-9598-FB2B414A7156}.Release|x86.Build.0 = Release|Any CPU + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.ActiveCfg = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.Build.0 = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|Any CPU.Deploy.0 = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.ActiveCfg = SkipOld|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.Build.0 = SkipOld|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|arm64.Deploy.0 = SkipOld|arm64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.ActiveCfg = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.Build.0 = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x64.Deploy.0 = SkipOld|x64 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.ActiveCfg = SkipOld|x86 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.Build.0 = SkipOld|x86 + {59A6F3D1-B6E1-48AD-80D3-215DF2791AC1}.SkipOld|x86.Deploy.0 = SkipOld|x86 + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|arm64.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|arm64.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x64.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x86.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Debug|x86.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|Any CPU.Build.0 = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|arm64.ActiveCfg = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|arm64.Build.0 = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x64.ActiveCfg = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x64.Build.0 = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x86.ActiveCfg = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.Release|x86.Build.0 = Release|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|Any CPU.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|Any CPU.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|arm64.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|arm64.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x64.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x64.Build.0 = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x86.ActiveCfg = Debug|Any CPU + {BE4AEE6B-3F2E-4FFB-940D-3E4916EAE913}.SkipOld|x86.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|arm64.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|arm64.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x64.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Debug|x86.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|Any CPU.Build.0 = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|arm64.ActiveCfg = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|arm64.Build.0 = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x64.ActiveCfg = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x64.Build.0 = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x86.ActiveCfg = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.Release|x86.Build.0 = Release|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|Any CPU.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|Any CPU.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|arm64.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|arm64.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x64.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x64.Build.0 = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x86.ActiveCfg = Debug|Any CPU + {D103EFF9-90EA-49C7-9DD9-636E6056E9C3}.SkipOld|x86.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4830E5AF-9C61-4A32-B34F-82CF4072248B} + SolutionGuid = {4D769F43-A252-4DC4-B54B-055DBC12610B} EndGlobalSection EndGlobal diff --git a/Emerald/App.xaml b/Emerald/App.xaml new file mode 100644 index 00000000..30649aad --- /dev/null +++ b/Emerald/App.xaml @@ -0,0 +1,18 @@ +<Application x:Class="Emerald.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + <Application.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <!-- Load WinUI resources --> + <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> + <!-- Load Uno.UI.Toolkit resources --> + <ToolkitResources xmlns="using:Uno.Toolkit.UI" /> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> + </Application.Resources> + + <!-- Add resources here --> + +</Application> diff --git a/Emerald/App.xaml.cs b/Emerald/App.xaml.cs new file mode 100644 index 00000000..acd2d2c1 --- /dev/null +++ b/Emerald/App.xaml.cs @@ -0,0 +1,71 @@ +using Uno.Resizetizer; + +namespace Emerald; +public partial class App : Application +{ + /// <summary> + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// </summary> + public App() + { + this.InitializeComponent(); + } + + public Window? MainWindow { get; private set; } + protected IHost? Host { get; private set; } + + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var builder = this.CreateBuilder(args) + .Configure(host => host +#if DEBUG + // Switch to Development environment when running in DEBUG + .UseEnvironment(Environments.Development) +#endif + .ConfigureServices((context, services) => + { + // TODO: Register your services + //services.AddSingleton<IMyService, MyService>(); + }) + ); + MainWindow = builder.Window; + + +#if DEBUG + MainWindow.UseStudio(); +#endif + MainWindow.SetWindowIcon(); + + Host = builder.Build(); + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (MainWindow.Content is not Frame rootFrame) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + // Place the frame in the current Window + MainWindow.Content = rootFrame; + } +#if WINDOWS + var mica = Emerald.Helpers.WindowManager.IntializeWindow(MainWindow); +#endif + + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(MainPage), args.Arguments); + } + // Ensure the current window is active + MainWindow.Activate(); + } + + /// <summary> + /// Gets the current <see cref="App"/> instance in use + /// </summary> + public new static App Current => (App)Application.Current; +} diff --git a/Emerald/Assets/Icons/icon.svg b/Emerald/Assets/Icons/icon.svg new file mode 100644 index 00000000..a15af53a --- /dev/null +++ b/Emerald/Assets/Icons/icon.svg @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="456" + height="456" + viewBox="0 0 456 456" + version="1.1" + id="svg453" + sodipodi:docname="icon.svg" + inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs457" /> + <sodipodi:namedview + id="namedview455" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="1.8574561" + inkscape:cx="228.26919" + inkscape:cy="228.26919" + inkscape:window-width="1920" + inkscape:window-height="1027" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="svg453" /> + <rect + x="0" + y="0" + width="456" + height="456" + fill="#FFFFFF" + id="rect451" /> +</svg> diff --git a/Emerald/Assets/Icons/icon_foreground.svg b/Emerald/Assets/Icons/icon_foreground.svg new file mode 100644 index 00000000..8ffc41ae --- /dev/null +++ b/Emerald/Assets/Icons/icon_foreground.svg @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="450" + height="450" + viewBox="0 0 50.369617 49.826836" + version="1.1" + id="svg151" + sodipodi:docname="icon_foreground.svg" + inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview153" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="1.250876" + inkscape:cx="218.64677" + inkscape:cy="175.87674" + inkscape:window-width="1920" + inkscape:window-height="1027" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="g149" /> + <defs + id="defs105"> + <path + id="aj28a0fd1a" + d="M 1.738,0.156 3.927,2.323 2.347,3.919 0.101,1.81 Z" /> + <path + id="fdje57jgic" + d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" /> + <path + id="6bg72xwlze" + d="M 2.398,0.044 3.994,1.624 1.886,3.869 0.232,2.232 Z" /> + <path + id="eaqjnja8wg" + d="M 1.736,0.023 3.981,2.132 2.344,3.786 0.156,1.619 Z" /> + </defs> + <g + fill="none" + fill-rule="evenodd" + id="g149" + transform="translate(-2.9304427e-4,-1.6465461e-4)"> + <g + id="g147"> + <g + id="g145"> + <path + fill="#7a67f8" + d="M 34.758,38.865 H 34.746 C 31.892,38.86 29.342,36.882 26.152,33.692 l -6.93,-6.873 2.166,-2.188 6.937,6.88 c 3.075,3.074 4.876,4.272 6.427,4.275 h 0.005 c 1.567,0 3.467,-1.262 6.558,-4.353 l 3.541,-3.587 c 1.784,-1.784 2.57,-3.34 2.408,-4.762 -0.13,-1.156 -0.894,-2.397 -2.401,-3.904 L 44.83,19.146 C 43.202,17.414 41.211,15.483 39.131,14.414 38.745,12.437 37.48,10.881 37.3,10.596 c 3.803,0.559 7.197,3.703 9.758,6.424 2.788,2.794 5.803,7.176 -0.018,12.996 l -3.54,3.588 c -3.251,3.25 -5.844,5.261 -8.742,5.261" + id="path107" /> + <path + fill="#f85977" + d="m 25.399,28.608 6.492,-6.562 c 3.076,-3.076 4.274,-4.877 4.276,-6.428 0.004,-1.567 -1.257,-3.469 -4.352,-6.563 L 28.228,5.515 C 24.58,1.867 22.369,2.699 19.561,5.507 L 19.528,5.54 c -1.54,1.448 -3.237,3.182 -4.346,5.01 -1.031,0.073 -2.361,0.424 -3.997,1.518 0.906,-3.397 3.737,-6.422 6.216,-8.755 2.794,-2.789 7.177,-5.804 12.997,0.017 l 3.588,3.54 c 3.255,3.256 5.266,5.851 5.26,8.754 -0.005,2.854 -1.982,5.404 -5.172,8.594 l -6.489,6.559 z" + id="path109" /> + <path + fill="#159bff" + d="M 12.522,38.707 C 8.939,37.946 5.746,34.972 3.308,32.382 2.035,31.106 0.321,29.13 0.042,26.663 c -0.274,-2.414 0.8,-4.795 3.283,-7.278 l 3.542,-3.588 c 3.25,-3.25 5.843,-5.261 8.74,-5.261 h 0.013 c 2.854,0.005 5.404,1.983 8.593,5.172 l 7.046,6.976 -2.165,2.19 -7.053,-6.983 c -3.076,-3.076 -4.876,-4.273 -6.427,-4.276 h -0.006 c -1.566,0 -3.466,1.261 -6.557,4.352 L 5.51,21.555 c -1.784,1.784 -2.57,3.34 -2.409,4.762 0.131,1.156 0.894,2.396 2.402,3.904 l 0.033,0.034 c 1.55,1.649 3.43,3.479 5.401,4.573 0.168,1.739 1.2,3.297 1.585,3.88" + id="path111" /> + <path + fill="#67e5ad" + d="m 26.32,49.827 c -1.925,0 -4.114,-0.886 -6.557,-3.33 l -3.588,-3.54 C 9.167,35.949 9.151,32.546 16.086,25.61 l 6.802,-6.872 2.193,2.162 -6.812,6.882 c -3.076,3.076 -4.273,4.877 -4.276,6.427 -0.003,1.568 1.258,3.47 4.352,6.563 l 3.588,3.541 c 3.646,3.647 5.858,2.816 8.666,0.008 l 0.034,-0.033 c 1.654,-1.555 3.5,-3.46 4.593,-5.437 1.661,-0.14 2.9,-0.841 3.835,-1.438 -0.8,3.537 -3.738,6.69 -6.302,9.102 -1.62,1.618 -3.777,3.312 -6.439,3.312" + id="path113" /> + <g + transform="translate(21.154,18.577)" + id="g120"> + <mask + id="8jptpqrneb" + fill="#ffffff"> + <use + xlink:href="#aj28a0fd1a" + id="use115" /> + </mask> + <path + d="M 0.101,1.81 1.738,0.156 3.927,2.323 2.347,3.919 Z" + mask="url(#8jptpqrneb)" + id="path118" /> + </g> + <g + transform="translate(27.404,20.981)" + id="g127"> + <mask + id="b2iljpfwbd" + fill="#ffffff"> + <use + xlink:href="#fdje57jgic" + id="use122" /> + </mask> + <path + d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" + mask="url(#b2iljpfwbd)" + id="path125" /> + </g> + <g + transform="translate(18.99,24.587)" + id="g134"> + <mask + id="gj70tyfpnf" + fill="#ffffff"> + <use + xlink:href="#6bg72xwlze" + id="use129" /> + </mask> + <path + d="M 1.886,3.869 0.232,2.232 2.398,0.044 3.994,1.624 Z" + mask="url(#gj70tyfpnf)" + id="path132" /> + </g> + <g + transform="translate(25.24,26.99)" + id="g141"> + <mask + id="z7vhvduckh" + fill="#ffffff"> + <use + xlink:href="#eaqjnja8wg" + id="use136" /> + </mask> + <path + d="M 3.981,2.132 2.344,3.786 0.156,1.619 1.736,0.023 Z" + mask="url(#z7vhvduckh)" + id="path139" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/Emerald/Assets/SharedAssets.md b/Emerald/Assets/SharedAssets.md new file mode 100644 index 00000000..1b84a74a --- /dev/null +++ b/Emerald/Assets/SharedAssets.md @@ -0,0 +1,32 @@ +# Shared Assets + +See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md + +## Here is a cheat sheet + +1. Add the image file to the `Assets` directory of a shared project. +2. Set the build action to `Content`. +3. (Recommended) Provide an asset for various scales/dpi + +### Examples + +```text +\Assets\Images\logo.scale-100.png +\Assets\Images\logo.scale-200.png +\Assets\Images\logo.scale-400.png + +\Assets\Images\scale-100\logo.png +\Assets\Images\scale-200\logo.png +\Assets\Images\scale-400\logo.png +``` + +### Table of scales + +| Scale | WinUI | iOS/MacCatalyst | Android | +|-------|:-----------:|:---------------:|:-------:| +| `100` | scale-100 | @1x | mdpi | +| `125` | scale-125 | N/A | N/A | +| `150` | scale-150 | N/A | hdpi | +| `200` | scale-200 | @2x | xhdpi | +| `300` | scale-300 | @3x | xxhdpi | +| `400` | scale-400 | N/A | xxxhdpi | diff --git a/Emerald/Assets/Splash/splash_screen.svg b/Emerald/Assets/Splash/splash_screen.svg new file mode 100644 index 00000000..8ffc41ae --- /dev/null +++ b/Emerald/Assets/Splash/splash_screen.svg @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="450" + height="450" + viewBox="0 0 50.369617 49.826836" + version="1.1" + id="svg151" + sodipodi:docname="icon_foreground.svg" + inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview153" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + showgrid="false" + inkscape:zoom="1.250876" + inkscape:cx="218.64677" + inkscape:cy="175.87674" + inkscape:window-width="1920" + inkscape:window-height="1027" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="g149" /> + <defs + id="defs105"> + <path + id="aj28a0fd1a" + d="M 1.738,0.156 3.927,2.323 2.347,3.919 0.101,1.81 Z" /> + <path + id="fdje57jgic" + d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" /> + <path + id="6bg72xwlze" + d="M 2.398,0.044 3.994,1.624 1.886,3.869 0.232,2.232 Z" /> + <path + id="eaqjnja8wg" + d="M 1.736,0.023 3.981,2.132 2.344,3.786 0.156,1.619 Z" /> + </defs> + <g + fill="none" + fill-rule="evenodd" + id="g149" + transform="translate(-2.9304427e-4,-1.6465461e-4)"> + <g + id="g147"> + <g + id="g145"> + <path + fill="#7a67f8" + d="M 34.758,38.865 H 34.746 C 31.892,38.86 29.342,36.882 26.152,33.692 l -6.93,-6.873 2.166,-2.188 6.937,6.88 c 3.075,3.074 4.876,4.272 6.427,4.275 h 0.005 c 1.567,0 3.467,-1.262 6.558,-4.353 l 3.541,-3.587 c 1.784,-1.784 2.57,-3.34 2.408,-4.762 -0.13,-1.156 -0.894,-2.397 -2.401,-3.904 L 44.83,19.146 C 43.202,17.414 41.211,15.483 39.131,14.414 38.745,12.437 37.48,10.881 37.3,10.596 c 3.803,0.559 7.197,3.703 9.758,6.424 2.788,2.794 5.803,7.176 -0.018,12.996 l -3.54,3.588 c -3.251,3.25 -5.844,5.261 -8.742,5.261" + id="path107" /> + <path + fill="#f85977" + d="m 25.399,28.608 6.492,-6.562 c 3.076,-3.076 4.274,-4.877 4.276,-6.428 0.004,-1.567 -1.257,-3.469 -4.352,-6.563 L 28.228,5.515 C 24.58,1.867 22.369,2.699 19.561,5.507 L 19.528,5.54 c -1.54,1.448 -3.237,3.182 -4.346,5.01 -1.031,0.073 -2.361,0.424 -3.997,1.518 0.906,-3.397 3.737,-6.422 6.216,-8.755 2.794,-2.789 7.177,-5.804 12.997,0.017 l 3.588,3.54 c 3.255,3.256 5.266,5.851 5.26,8.754 -0.005,2.854 -1.982,5.404 -5.172,8.594 l -6.489,6.559 z" + id="path109" /> + <path + fill="#159bff" + d="M 12.522,38.707 C 8.939,37.946 5.746,34.972 3.308,32.382 2.035,31.106 0.321,29.13 0.042,26.663 c -0.274,-2.414 0.8,-4.795 3.283,-7.278 l 3.542,-3.588 c 3.25,-3.25 5.843,-5.261 8.74,-5.261 h 0.013 c 2.854,0.005 5.404,1.983 8.593,5.172 l 7.046,6.976 -2.165,2.19 -7.053,-6.983 c -3.076,-3.076 -4.876,-4.273 -6.427,-4.276 h -0.006 c -1.566,0 -3.466,1.261 -6.557,4.352 L 5.51,21.555 c -1.784,1.784 -2.57,3.34 -2.409,4.762 0.131,1.156 0.894,2.396 2.402,3.904 l 0.033,0.034 c 1.55,1.649 3.43,3.479 5.401,4.573 0.168,1.739 1.2,3.297 1.585,3.88" + id="path111" /> + <path + fill="#67e5ad" + d="m 26.32,49.827 c -1.925,0 -4.114,-0.886 -6.557,-3.33 l -3.588,-3.54 C 9.167,35.949 9.151,32.546 16.086,25.61 l 6.802,-6.872 2.193,2.162 -6.812,6.882 c -3.076,3.076 -4.273,4.877 -4.276,6.427 -0.003,1.568 1.258,3.47 4.352,6.563 l 3.588,3.541 c 3.646,3.647 5.858,2.816 8.666,0.008 l 0.034,-0.033 c 1.654,-1.555 3.5,-3.46 4.593,-5.437 1.661,-0.14 2.9,-0.841 3.835,-1.438 -0.8,3.537 -3.738,6.69 -6.302,9.102 -1.62,1.618 -3.777,3.312 -6.439,3.312" + id="path113" /> + <g + transform="translate(21.154,18.577)" + id="g120"> + <mask + id="8jptpqrneb" + fill="#ffffff"> + <use + xlink:href="#aj28a0fd1a" + id="use115" /> + </mask> + <path + d="M 0.101,1.81 1.738,0.156 3.927,2.323 2.347,3.919 Z" + mask="url(#8jptpqrneb)" + id="path118" /> + </g> + <g + transform="translate(27.404,20.981)" + id="g127"> + <mask + id="b2iljpfwbd" + fill="#ffffff"> + <use + xlink:href="#fdje57jgic" + id="use122" /> + </mask> + <path + d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" + mask="url(#b2iljpfwbd)" + id="path125" /> + </g> + <g + transform="translate(18.99,24.587)" + id="g134"> + <mask + id="gj70tyfpnf" + fill="#ffffff"> + <use + xlink:href="#6bg72xwlze" + id="use129" /> + </mask> + <path + d="M 1.886,3.869 0.232,2.232 2.398,0.044 3.994,1.624 Z" + mask="url(#gj70tyfpnf)" + id="path132" /> + </g> + <g + transform="translate(25.24,26.99)" + id="g141"> + <mask + id="z7vhvduckh" + fill="#ffffff"> + <use + xlink:href="#eaqjnja8wg" + id="use136" /> + </mask> + <path + d="M 3.981,2.132 2.344,3.786 0.156,1.619 1.736,0.023 Z" + mask="url(#z7vhvduckh)" + id="path139" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/Emerald/Assets/icon.png b/Emerald/Assets/icon.png new file mode 100644 index 00000000..94000d94 Binary files /dev/null and b/Emerald/Assets/icon.png differ diff --git a/Emerald/DirectResources.cs b/Emerald/DirectResources.cs new file mode 100644 index 00000000..f2d25777 --- /dev/null +++ b/Emerald/DirectResources.cs @@ -0,0 +1,39 @@ +using CommunityToolkit.WinUI.Helpers; +using Emerald.Helpers; +using System.Reflection; +using System.Runtime.InteropServices; +using Windows.UI; + +namespace Emerald; + +public static class DirectResoucres +{ + public static int MaxRAM + => Extensions.GetMemoryGB() * 1024; + + public static int MinRAM + => 512; + + //Used this thing for major setting changes because older settings could crash the program + public static string SettingsAPIVersion + => "1.3"; + + public static string BuildType + { + get + { +#if DEBUG + return "DEBUG"; +#else + return "RELEASE"; +#endif + } + } + public static Architecture Architecture => RuntimeInformation.ProcessArchitecture; + + public static string AppVersion + => Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + public static Color LayerFillColorDefaultColor + => (Color)App.Current.Resources["LayerFillColorDefault"]; +} diff --git a/Emerald/Emerald.csproj b/Emerald/Emerald.csproj new file mode 100644 index 00000000..523591d0 --- /dev/null +++ b/Emerald/Emerald.csproj @@ -0,0 +1,93 @@ +<Project Sdk="Uno.Sdk"> + <PropertyGroup> + <TargetFrameworks> + net8.0-maccatalyst; + net8.0-windows10.0.22621; + net8.0-desktop; + </TargetFrameworks> + + <OutputType>Exe</OutputType> + <UnoSingleProject>true</UnoSingleProject> + + <!-- Display name --> + <ApplicationTitle>Emerald</ApplicationTitle> + <!-- App Identifier --> + <ApplicationId>Riverside.Emerald</ApplicationId> + <!-- Versions --> + <ApplicationDisplayVersion>1.0.0.0</ApplicationDisplayVersion> + <ApplicationVersion>1</ApplicationVersion> + <!-- + If you encounter this error message: + + error NETSDK1148: A referenced assembly was compiled using a newer version of Microsoft.Windows.SDK.NET.dll. + Please update to a newer .NET SDK in order to reference this assembly. + + This means that the two packages below must be aligned with the "build" version number of + the "Microsoft.Windows.SDK.BuildTools" package above, and the "revision" version number + must be the highest found in https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref. + --> + <!-- + I had to add this unless it throws an error, with duplicate Webview files. + <ErrorOnDuplicatePublishOutputFiles>true</ErrorOnDuplicatePublishOutputFiles> + + --> + <!-- + UnoFeatures let's you quickly add and manage implicit package references based on the features you want to use. + https://aka.platform.uno/singleproject-features + --> + <UnoFeatures> + Lottie; + Hosting; + Toolkit; + Mvvm; + Localization; + ThemeService; + </UnoFeatures> + </PropertyGroup> + + <ItemGroup> + <None Remove="Assets\icon.png" /> + <None Remove="UserControls\ArgumentsListView.xaml" /> + <None Remove="UserControls\Titlebar.xaml" /> + <None Remove="Views\Settings\GeneralPage.xaml" /> + <None Remove="Views\Settings\SettingsPage.xaml" /> + </ItemGroup> + <ItemGroup> + <PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" /> + <PackageReference Include="CommunityToolkit.WinUI.Helpers" /> + <PackageReference Include="Microsoft.Windows.CsWin32"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="System.Text.Json" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Emerald.CoreX\Emerald.CoreX.csproj" /> + </ItemGroup> + <ItemGroup> + <Page Update="UserControls\Titlebar.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <Page Update="Views\Settings\SettingsPage.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <UpToDateCheckInput Remove="Views\Settings\GeneralPage.xaml" /> + </ItemGroup> + + <ItemGroup> + <Page Update="Views\Settings\GeneralPage.xaml"> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + + <ItemGroup> + <UpToDateCheckInput Remove="UserControls\ArgumentsListView.xaml" /> + </ItemGroup> + +</Project> diff --git a/Emerald/GlobalUsings.cs b/Emerald/GlobalUsings.cs new file mode 100644 index 00000000..6b345c18 --- /dev/null +++ b/Emerald/GlobalUsings.cs @@ -0,0 +1,7 @@ +global using System.Collections.Immutable; +global using CommunityToolkit.Mvvm.ComponentModel; +global using CommunityToolkit.Mvvm.Input; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using ApplicationExecutionState = Windows.ApplicationModel.Activation.ApplicationExecutionState; diff --git a/Emerald/Helpers/Converters/BoolToVisibility.cs b/Emerald/Helpers/Converters/BoolToVisibility.cs new file mode 100644 index 00000000..3cd23e81 --- /dev/null +++ b/Emerald/Helpers/Converters/BoolToVisibility.cs @@ -0,0 +1,18 @@ + +using Microsoft.UI.Xaml.Data; + +namespace Emerald.Helpers.Converters; +public class BoolToVisibility : IValueConverter + { + public bool Reversed { get; set; } + + public object Convert(object value, Type targetType, object parameter, string language) + { + return (value is bool b) ? (((Reversed || parameter == "reversed") && parameter != "normal") ? (!b ? Visibility.Visible : Visibility.Collapsed) : (b ? Visibility.Visible : Visibility.Collapsed)) : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new InvalidOperationException(); + } + } diff --git a/Emerald/Helpers/Extensions.cs b/Emerald/Helpers/Extensions.cs new file mode 100644 index 00000000..62482347 --- /dev/null +++ b/Emerald/Helpers/Extensions.cs @@ -0,0 +1,155 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Windows.ApplicationModel.Resources; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Windows.System.Diagnostics; + +namespace Emerald.Helpers; + +public static class Extensions +{ + private static readonly ConcurrentDictionary<string, string> cachedResources = new(); + + //public static void ShowAt(this TeachingTip tip, FrameworkElement element, TeachingTipPlacementMode placement = TeachingTipPlacementMode.Auto, bool closeWhenClick = true, bool addToMainGrid = true) + //{ + // if (addToMainGrid) + // (App.Current.MainWindow.Content as Grid).Children.Add(tip); + + // tip.Target = element; + // tip.PreferredPlacement = placement; + // tip.IsOpen = true; + // if (closeWhenClick) + // { + // tip.ActionButtonClick += (_, _) => tip.IsOpen = false; + // tip.CloseButtonClick += (_, _) => tip.IsOpen = false; + // } + //} + + public static int GetMemoryGB() + { + SystemMemoryUsageReport systemMemoryUsageReport = SystemDiagnosticInfo.GetForCurrentSystem().MemoryUsage.GetReport(); + + long memkb = Convert.ToInt64(systemMemoryUsageReport.TotalPhysicalSizeInBytes); + return Convert.ToInt32(memkb / Math.Pow(1024, 3)); + } + + public static string KiloFormat(this int num) + { + if (num >= 100000000) + return (num / 1000000).ToString("#,0M"); + + if (num >= 10000000) + return (num / 1000000).ToString("0.#") + "M"; + + if (num >= 100000) + return (num / 1000).ToString("#,0K"); + + if (num >= 1000) + return (num / 100).ToString("0.#") + "K"; + + return num.ToString("#,0"); + } + + //public static ContentDialog ToContentDialog(this UIElement content, string title, string closebtnText = null, ContentDialogButton defaultButton = ContentDialogButton.Close) + //{ + // ContentDialog dialog = new() + // { + // XamlRoot = App.Current.MainWindow.Content.XamlRoot, + // Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + // Title = title, + // CloseButtonText = closebtnText, + // DefaultButton = defaultButton, + // Content = content, + // RequestedTheme = (ElementTheme)Settings.SettingsSystem.Settings.App.Appearance.Theme + // }; + // return dialog; + //} + + public static int Remove<T>(this ObservableCollection<T> coll, Func<T, bool> condition) + { + var itemsToRemove = coll.Where(condition).ToList(); + + foreach (var itemToRemove in itemsToRemove) + { + coll.Remove(itemToRemove); + } + return itemsToRemove.Count; + } + + public static int Remove<T>(this List<T> coll, Func<T, bool> condition) + { + var itemsToRemove = coll.Where(condition).ToList(); + + foreach (var itemToRemove in itemsToRemove) + { + coll.Remove(itemToRemove); + } + + return itemsToRemove.Count; + } + public static void AddRange<T>(this ObservableCollection<T> cll, IEnumerable<T> items) + { + foreach (var item in items) + cll.Add(item); + } + + public static string ToBinaryString(this string str) + { + var binary = ""; + foreach (char ch in str) + { + binary += Convert.ToString((int)ch, 2); + } + return binary; + } + + public static string ToMD5(this string s) + { + StringBuilder sb = new(); + byte[] hashValue = MD5.HashData(Encoding.UTF8.GetBytes(s)); + + foreach (byte b in hashValue) + { + sb.Append($"{b:X2}"); + } + + return sb.ToString(); + } + + public static string Localize(this string resourceKey) + { + try + { + string s = Windows.ApplicationModel.Resources.ResourceLoader + .GetForViewIndependentUse() + .GetString(resourceKey); + + return string.IsNullOrEmpty(s) ? resourceKey : s; + } + catch + { + return resourceKey; + } + } + + //public static string Localize(this Core.Localized resourceKey) => + // resourceKey.ToString().Localize(); + + //public static Models.Account ToAccount(this CmlLib.Core.Auth.MSession session, bool plusCount = true) + //{ + // bool isOffline = session.AccessToken == "access_token"; + // return new Models.Account(session.Username, isOffline ? null : session.AccessToken, isOffline ? null : session.UUID, plusCount ? MainWindow.HomePage.AccountsPage.AllCount++ : 0, false, session.ClientToken); + //} + + //public static CmlLib.Core.Auth.MSession ToMSession(this Models.Account account) + //{ + // bool isOffline = account.UUID == null; + // return new CmlLib.Core.Auth.MSession(account.UserName, isOffline ? "access_token" : account.AccessToken, isOffline ? Guid.NewGuid().ToString().Replace("-", "") : account.UUID) { ClientToken = account.ClientToken }; + //} +} diff --git a/Emerald/Helpers/MarkupExtensions/ConditionString.cs b/Emerald/Helpers/MarkupExtensions/ConditionString.cs new file mode 100644 index 00000000..da03abbe --- /dev/null +++ b/Emerald/Helpers/MarkupExtensions/ConditionString.cs @@ -0,0 +1,21 @@ +using Microsoft.UI.Xaml.Markup; + +namespace Emerald.Helpers; + +[MarkupExtensionReturnType(ReturnType = typeof(string))] +public class ConditionString : MarkupExtension +{ + public string TrueString { get; set; } + + public string FalseString { get; set; } + + public bool Condition { get; set; } + + public string Result + => Condition ? TrueString : FalseString; + + public override string ToString() + => Result; + + protected override object ProvideValue() => Result; +} diff --git a/Emerald/Helpers/MarkupExtensions/FontIcon.cs b/Emerald/Helpers/MarkupExtensions/FontIcon.cs new file mode 100644 index 00000000..5731c5f7 --- /dev/null +++ b/Emerald/Helpers/MarkupExtensions/FontIcon.cs @@ -0,0 +1,14 @@ +using Microsoft.UI.Xaml.Markup; + +namespace Emerald.Helpers; + +[MarkupExtensionReturnType(ReturnType = typeof(Microsoft.UI.Xaml.Controls.FontIcon))] +public sealed class FontIcon : MarkupExtension +{ + public string Glyph { get; set; } = "\xe8a5"; + + public int FontSize { get; set; } = 16; + + protected override object ProvideValue() + => new Microsoft.UI.Xaml.Controls.FontIcon() { Glyph = Glyph, FontSize = FontSize }; +} diff --git a/Emerald/Helpers/MarkupExtensions/LocalizeString.cs b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs new file mode 100644 index 00000000..495182dc --- /dev/null +++ b/Emerald/Helpers/MarkupExtensions/LocalizeString.cs @@ -0,0 +1,12 @@ +using Microsoft.UI.Xaml.Markup; +using CommunityToolkit.Mvvm; +namespace Emerald.Helpers; + +[MarkupExtensionReturnType(ReturnType = typeof(string))] +public sealed class Localize : MarkupExtension +{ + public string Name { get; set; } + + protected override object ProvideValue() + => Name.Localize(); +} diff --git a/Emerald/Helpers/Settings/Enums/MicaTintColor.cs b/Emerald/Helpers/Settings/Enums/MicaTintColor.cs new file mode 100644 index 00000000..a92be213 --- /dev/null +++ b/Emerald/Helpers/Settings/Enums/MicaTintColor.cs @@ -0,0 +1,8 @@ +namespace Emerald.Helpers.Settings.Enums; + +public enum MicaTintColor : int +{ + NoColor = 0, + AccentColor = 1, + CustomColor = 2 +} diff --git a/Emerald/Helpers/Settings/JSON.cs b/Emerald/Helpers/Settings/JSON.cs new file mode 100644 index 00000000..11b1815a --- /dev/null +++ b/Emerald/Helpers/Settings/JSON.cs @@ -0,0 +1,365 @@ +using CmlLib.Core; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using System.Text.Json; +using System.Text.Json.Serialization; +using System; +using System.Collections.Generic; +using Windows.UI; +using Emerald.CoreX.Store.Modrinth; +namespace Emerald.Helpers.Settings.JSON; + +public class JSON : Models.Model +{ + public string Serialize() + => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); +} + +public class SettingsBackup : JSON +{ + public string Backup { get; set; } + public string Name { get; set; } + public DateTime Time { get; set; } + // ik there is Time.ToString() lol + public string DateString => $"{Time.ToLongDateString()} {Time.ToShortTimeString()}"; +} + +public class Backups : JSON +{ + public SettingsBackup[] AllBackups { get; set; } = Array.Empty<SettingsBackup>(); + public string APIVersion { get; private set; } = "1.0"; +} + +public class Settings : JSON +{ + public static Settings CreateNew() => new() + { + App = new() + { + Discord = new(), + Appearance = new() + { + MicaTintColor = (int)Enums.MicaTintColor.NoColor, + Theme = (int)ElementTheme.Default + } + }, + Minecraft = new() + { + Path = MinecraftPath.GetOSDefaultPath(), + // RAM = DirectResoucres.MaxRAM / 2, + MCVerionsConfiguration = new(), + JVM = new(), + Downloader = new() + { + AssetsCheck = true, + HashCheck = true + } + } + }; + + // public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion; + public DateTime LastSaved { get; set; } = DateTime.Now; + public Minecraft Minecraft { get; set; } = new(); + + public App App { get; set; } = new(); +} + +public partial class Minecraft : JSON +{ + public Minecraft() + { + JVM.PropertyChanged += (_, _) + => InvokePropertyChanged(); + PropertyChanged += (_, e) => + { + if (e.PropertyName != null) + InvokePropertyChanged(); + }; + } + + [JsonIgnore] + public double RAMinGB => Math.Round((RAM / 1024.00), 2); + + + [ObservableProperty] + private string _Path; + + [ObservableProperty] + private int _RAM; + + [ObservableProperty] + private bool _IsAdmin; + + public Downloader Downloader { get; set; } = new(); + + public MCVerionsConfiguration MCVerionsConfiguration { get; set; } + + public JVM JVM { get; set; } = new(); + + public bool ReadLogs() + => JVM.GameLogs && !IsAdmin; +} + +public class Account : JSON +{ + public string Type { get; set; } + public string Username { get; set; } + public string AccessToken { get; set; } + public string ClientToken { get; set; } + public string UUID { get; set; } + public bool LastAccessed { get; set; } +} + +public partial class Downloader : JSON +{ + [ObservableProperty] + private bool _HashCheck; + + [ObservableProperty] + private bool _AssetsCheck; +} + +public partial class JVM : JSON +{ + public JVM() + { + this.PropertyChanged += (_, e) => + { + if (e.PropertyName != null) + this.InvokePropertyChanged(); + }; + } + + [ObservableProperty] + private string[] _Arguments; + + [ObservableProperty] + private double _ScreenWidth; + + [ObservableProperty] + private double _ScreenHeight; + + [ObservableProperty] + private bool _FullScreen; + + [ObservableProperty] + private bool _GameLogs; + + [JsonIgnore] + public string ScreenSizeStatus => + FullScreen ? "FullScreen".Localize() : ((ScreenWidth > 0 && ScreenHeight > 0) ? $"{ScreenWidth} × {ScreenHeight}" : "Default".Localize()); + + [JsonIgnore] + public bool SetSize => !(ScreenSizeStatus == "FullScreen".Localize() || ScreenSizeStatus == "Default".Localize()); +} + +public class App : JSON +{ + public Appearance Appearance { get; set; } = new(); + public bool AutoLogin { get; set; } + public Discord Discord { get; set; } = new(); + public NewsFilter NewsFilter { get; set; } = new(); + public Store Store { get; set; } = new(); + public Updates Updates { get; set; } = new(); + public bool AutoClose { get; set; } + public bool HideOnLaunch { get; set; } + public bool WindowsHello { get; set; } +} +public class Updates : JSON +{ + public bool CheckAtStartup { get; set; } = true; + public bool AutoDownload { get; set; } + public bool IncludePreReleases { get; set; } +} +public partial class StoreFilter : JSON +{ + [ObservableProperty] + private bool _Fabric; + + [ObservableProperty] + private bool _Forge; + + [ObservableProperty] + private bool _Adventure; + + [ObservableProperty] + private bool _Cursed; + + [ObservableProperty] + private bool _Decoration; + + [ObservableProperty] + private bool _Equipment; + + [ObservableProperty] + private bool _Food; + + [ObservableProperty] + private bool _Library; + + [ObservableProperty] + private bool _Magic; + + [ObservableProperty] + private bool _Misc; + + [ObservableProperty] + private bool _Optimization; + + [ObservableProperty] + private bool _Storage; + + [ObservableProperty] + private bool _Technology; + + [ObservableProperty] + private bool _Utility; + + [ObservableProperty] + private bool _Worldgen; + + [JsonIgnore] + public bool All + { + get =>true; + set + { + _Fabric = _Forge = _Adventure = _Cursed = _Decoration = _Equipment = _Food = _Library = _Magic = _Misc = _Optimization = _Storage = _Technology = _Utility = _Worldgen = false; + InvokePropertyChanged(null); + } + } +} +public class Store : JSON +{ + public StoreFilter Filter { get; set; } = new(); + public StoreSortOptions SortOptions { get; set; } = new(); +} + +public partial class StoreSortOptions : JSON +{ + [ObservableProperty] + private bool _Relevance = true; + + [ObservableProperty] + private bool _Downloads; + + [ObservableProperty] + private bool _Follows; + + [ObservableProperty] + private bool _Updated; + + [ObservableProperty] + private bool _Newest; + + public SearchSortOptions GetResult() + { + if (!(Relevance || Downloads || Follows || Updated || Newest)) + return SearchSortOptions.Relevance; + else + return Relevance ? SearchSortOptions.Relevance : (Downloads ? SearchSortOptions.Downloads : (Follows ? SearchSortOptions.Follows : (Updated ? SearchSortOptions.Updated : SearchSortOptions.Newest))); + } +} +public partial class NewsFilter : JSON +{ + [ObservableProperty] + private bool _Java = false; + + [ObservableProperty] + private bool _Bedrock = false; + + [ObservableProperty] + private bool _Dungeons = false; + + [ObservableProperty] + private bool _Legends = false; + + [JsonIgnore] + public bool All + { + get => GetResult().Length == 4; + set + { + Java = Bedrock = Dungeons = Legends = false; + InvokePropertyChanged(null); + } + } + public string[] GetResult() + { + var r = new List<string>(); + + if (!Java && !Bedrock && !Dungeons && !Legends) + { + r.Add("Minecraft: Java Edition"); + r.Add("Minecraft for Windows"); + r.Add("Minecraft Dungeons"); + r.Add("Minecraft Legends"); + return r.ToArray(); + } + + if (Java) + r.Add("Minecraft: Java Edition"); + + if (Bedrock) + r.Add("Minecraft for Windows"); + + if (Dungeons) + r.Add("Minecraft Dungeons"); + + if (Legends) + r.Add("Minecraft Legends"); + + return r.ToArray(); + } +} +public class Discord : JSON +{ +} +public partial class MCVerionsConfiguration : JSON +{ + [ObservableProperty] + private bool _Release = true; + + [ObservableProperty] + private bool _Custom = false; + + [ObservableProperty] + private bool _OldBeta = false; + + [ObservableProperty] + private bool _OldAlpha = false; + + [ObservableProperty] + private bool _Snapshot = false; +} + +public partial class Appearance : JSON +{ + [ObservableProperty] + private int _NavIconType = 1; + + public bool ShowFontIcons => NavIconType == 0; + + [ObservableProperty] + private int _Theme; + + [ObservableProperty] + private int _MicaTintColor; + + [ObservableProperty] + private int _MicaType = 0; + + [ObservableProperty] + private (int A, int R, int G, int B)? _CustomMicaTintColor; + + + public Appearance() + { + this.PropertyChanged += (_, e) => + { + if (e.PropertyName != null) + this.InvokePropertyChanged(); + }; + } +} diff --git a/Emerald/Helpers/Settings/SettingsSystem.cs b/Emerald/Helpers/Settings/SettingsSystem.cs new file mode 100644 index 00000000..5a25b55a --- /dev/null +++ b/Emerald/Helpers/Settings/SettingsSystem.cs @@ -0,0 +1,111 @@ +using Emerald.Helpers.Settings.JSON; +using System.Text.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Windows.Storage; + +namespace Emerald.Helpers.Settings; + +public static class SettingsSystem +{ + public static JSON.Settings Settings { get; private set; } = JSON.Settings.CreateNew(); + public static Account[] Accounts { get; set; } + + public static event EventHandler<string>? APINoMatch; +// public static T GetSerializedFromSettings<T>(string key, T def) +// { +// string json; +// try +// { +// json = ApplicationData.Current.RoamingSettings.Values[key] as string; +// return JsonSerializer.Deserialize<T>(json); +// } +// catch +// { +// json = JsonSerializer.Serialize(def); +// ApplicationData.Current.RoamingSettings.Values[key] = json; +// return def; +// } +// } +// public static void LoadData() +// { +// Settings = GetSerializedFromSettings("Settings", JSON.Settings.CreateNew()); +// Accounts = GetSerializedFromSettings("Accounts", Array.Empty<Account>()); + +// if (Settings.APIVersion != DirectResoucres.SettingsAPIVersion) +// { +// APINoMatch?.Invoke(null, ApplicationData.Current.RoamingSettings.Values["Settings"] as string); +// ApplicationData.Current.RoamingSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize(); +// Settings = JsonSerializer.Deserialize<JSON.Settings>(ApplicationData.Current.RoamingSettings.Values["Settings"] as string); +// } +// } + +// public static async Task CreateBackup(string system) +// { +// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); +// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); + +// var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); +// bl.Add(new SettingsBackup() { Time = DateTime.Now, Backup = system }); +// l.AllBackups = bl.ToArray(); +// json = l.Serialize(); + +// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); +// } + +// public static async Task DeleteBackup(int Index) +// { +// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); +// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); + +// var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); +// bl.RemoveAt(Index); +// l.AllBackups = bl.ToArray(); +// json = l.Serialize(); + +// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); +// } + +// public static async Task DeleteBackup(DateTime time) +// { +// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); +// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); + +// var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); +// bl.Remove(x => x.Time == time); +// l.AllBackups = bl.ToArray(); +// json = l.Serialize(); + +// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); +// } +// public static async Task RenameBackup(DateTime time, string name) +// { +// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); +// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); + +// var bl = l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); +// bl.FirstOrDefault(x => x.Time == time).Name = name; +// l.AllBackups = bl.ToArray(); +// json = l.Serialize(); + +// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); +// } + +// public static async Task<List<SettingsBackup>> GetBackups() +// { +// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); +// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize<Backups>(json); + +// return l.AllBackups == null ? new List<SettingsBackup>() : l.AllBackups.ToList(); +// } + +// public static void SaveData() +// { +// Settings.LastSaved = DateTime.Now; +// ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize(); +// ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts); +// } +//} +} diff --git a/Emerald/Helpers/WindowManager.cs b/Emerald/Helpers/WindowManager.cs new file mode 100644 index 00000000..28629aee --- /dev/null +++ b/Emerald/Helpers/WindowManager.cs @@ -0,0 +1,175 @@ +using Microsoft.UI; +using Microsoft.UI.Composition; +using Microsoft.UI.Composition.SystemBackdrops; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using System; +using System.Runtime.InteropServices; +using Windows.ApplicationModel; +using WinRT; +using WinRT.Interop; + +namespace Emerald.Helpers; + +#if WINDOWS +public static class WindowManager +{ + /// <summary> + /// Add mica and the icon to the <paramref name="window"/> + /// </summary> + public static MicaBackground IntializeWindow(Window window) + { + var s = new MicaBackground(window); + s.TrySetMicaBackdrop(); + return s; + } + + /// <summary> + /// Sets the customized titlebar if supported + /// </summary> + /// <exception cref="NullReferenceException"/> + public static void SetTitleBar(Window window, UIElement AppTitleBar) + { + FrameworkElement RootUI = (FrameworkElement)window.Content; + if (AppWindowTitleBar.IsCustomizationSupported()) + { + var titlebar = window.AppWindow.TitleBar; + titlebar.ExtendsContentIntoTitleBar = true; + + void SetColor(ElementTheme acualTheme) + { + titlebar.ButtonBackgroundColor = titlebar.ButtonInactiveBackgroundColor = titlebar.ButtonPressedBackgroundColor = Colors.Transparent; + switch (acualTheme) + { + case ElementTheme.Dark: + titlebar.ButtonHoverBackgroundColor = Colors.Black; + titlebar.ButtonForegroundColor = Colors.White; + titlebar.ButtonHoverForegroundColor = Colors.White; + titlebar.ButtonPressedForegroundColor = Colors.Silver; + break; + case ElementTheme.Light: + titlebar.ButtonHoverBackgroundColor = Colors.White; + titlebar.ButtonForegroundColor = Colors.Black; + titlebar.ButtonHoverForegroundColor = Colors.Black; + titlebar.ButtonPressedForegroundColor = Colors.DarkGray; + break; + } + } + + RootUI.ActualThemeChanged += (s, _) => SetColor(s.ActualTheme); + window.SetTitleBar(AppTitleBar); + SetColor(RootUI.ActualTheme); + } + else + { + window.ExtendsContentIntoTitleBar = true; + window.SetTitleBar(AppTitleBar); + } + } +} + +public class WindowsSystemDispatcherQueueHelper +{ + private object? _dispatcherQueueController; + + [StructLayout(LayoutKind.Sequential)] + internal struct DispatcherQueueOptions + { + internal int dwSize; + internal int threadType; + internal int apartmentType; + } + + [DllImport("CoreMessaging.dll")] + private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object? dispatcherQueueController); + + public void EnsureWindowsSystemDispatcherQueueController() + { + if (Windows.System.DispatcherQueue.GetForCurrentThread() != null) + { + // one already exists, so we'll just use it. + return; + } + + if (_dispatcherQueueController == null) + { + DispatcherQueueOptions options; + options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions)); + options.threadType = 2; + options.apartmentType = 2; + + CreateDispatcherQueueController(options, ref _dispatcherQueueController); + } + } +} + +public class MicaBackground +{ + private readonly Window _window; + public readonly MicaController MicaController = new(); + private SystemBackdropConfiguration _backdropConfiguration = new(); + private readonly WindowsSystemDispatcherQueueHelper _dispatcherQueueHelper = new(); + + public MicaBackground(Window window) + { + _window = window; + } + + public bool TrySetMicaBackdrop() + { + if (MicaController.IsSupported()) + { + _dispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController(); + _window.Activated += WindowOnActivated; + _window.Closed += WindowOnClosed; + ((FrameworkElement)_window.Content).ActualThemeChanged += MicaBackground_ActualThemeChanged; + _backdropConfiguration.IsInputActive = true; + + _backdropConfiguration.Theme = _window.Content switch + { + FrameworkElement { ActualTheme: ElementTheme.Dark } => SystemBackdropTheme.Dark, + FrameworkElement { ActualTheme: ElementTheme.Light } => SystemBackdropTheme.Light, + FrameworkElement { ActualTheme: ElementTheme.Default } => SystemBackdropTheme.Default, + _ => throw new InvalidOperationException("Unknown theme") + }; + + MicaController.AddSystemBackdropTarget(_window.As<ICompositionSupportsSystemBackdrop>()); + MicaController.SetSystemBackdropConfiguration(_backdropConfiguration); + + return true; + } + + return false; + } + + private void MicaBackground_ActualThemeChanged(FrameworkElement sender, object args) + { + if (_backdropConfiguration != null) + { + SetConfigurationSourceTheme(); + } + } + + private void SetConfigurationSourceTheme() + { + switch (((FrameworkElement)_window.Content).ActualTheme) + { + case ElementTheme.Dark: _backdropConfiguration.Theme = SystemBackdropTheme.Dark; break; + case ElementTheme.Light: _backdropConfiguration.Theme = SystemBackdropTheme.Light; break; + case ElementTheme.Default: _backdropConfiguration.Theme = SystemBackdropTheme.Default; break; + } + } + + private void WindowOnClosed(object sender, WindowEventArgs args) + { + MicaController.Dispose(); + _window.Activated -= WindowOnActivated; + _backdropConfiguration = null!; + } + + private void WindowOnActivated(object sender, WindowActivatedEventArgs args) + { + _backdropConfiguration.IsInputActive = args.WindowActivationState is not WindowActivationState.Deactivated; + } +} +#endif diff --git a/Emerald/MainPage.xaml b/Emerald/MainPage.xaml new file mode 100644 index 00000000..63a41b90 --- /dev/null +++ b/Emerald/MainPage.xaml @@ -0,0 +1,131 @@ +<Page x:Class="Emerald.MainPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:Emerald" + xmlns:utu="using:Uno.Toolkit.UI" + xmlns:models="using:Emerald.Models" + xmlns:uc="using:Emerald.UserControls" + xmlns:conv="using:Emerald.Helpers.Converters" + xmlns:helpers="using:Emerald.Helpers"> + <Page.Resources> + <conv:BoolToVisibility x:Key="BoolToVisibilty"/> + </Page.Resources> + <Grid x:Name="MainGrid"> + <uc:TitleBar + x:Name="AppTitleBar" + Height="48" + Margin="12,0,0,0" + VerticalAlignment="Top" /> + <NavigationView + Background="Transparent" + ItemInvoked="navView_ItemInvoked" + x:Name="NavView" + Margin="0,48,0,0" + AlwaysShowHeader="True" + IsBackButtonVisible="Collapsed" + IsPaneToggleButtonVisible="False" + IsSettingsVisible="False" + OpenPaneLength="80" + PaneDisplayMode="Left"> + + <NavigationView.HeaderTemplate> + <DataTemplate x:DataType="models:NavViewHeader"> + <Grid Margin="{x:Bind HeaderMargin}" x:DefaultBindMode="OneWay"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="auto" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="auto" /> + <ColumnDefinition Width="auto" /> + </Grid.ColumnDefinitions> + <TextBlock + Style="{ThemeResource TitleTextBlockStyle}" + Text="{x:Bind HeaderText, Mode=OneWay}" /> + <ContentPresenter + Grid.Column="2" + Margin="0,0,5,0" + Content="{x:Bind CustomContent}" + Visibility="{x:Bind CustomContentVisibility}" /> + <Button + Grid.Column="3" + Margin="0,0,5,0" + Content="{x:Bind CustomButtonText}" + Visibility="{x:Bind CustomButtonVisibility}" /> + </Grid> + </DataTemplate> + </NavigationView.HeaderTemplate> + + <NavigationView.Resources> + <x:Double x:Key="NavigationViewSelectionIndicatorWidth">4</x:Double> + <x:Double x:Key="NavigationViewSelectionIndicatorHeight">24</x:Double> + <x:Double x:Key="NavigationViewSelectionIndicatorRadius">4</x:Double> + <Thickness x:Key="NavigationViewItemContentPresenterMargin">0</Thickness> + </NavigationView.Resources> + + <NavigationView.MenuItemTemplate> + <DataTemplate x:DataType="models:SquareNavigationViewItem"> + <NavigationViewItem + Height="64" + IsEnabled="{x:Bind IsEnabled, Mode=OneWay}" + IsSelected="{x:Bind IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" + Tag="{x:Bind Name}"> + <Grid + Margin="-5,4,-9,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + x:DefaultBindMode="OneWay" + RowSpacing="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ChildrenTransitions> + <TransitionCollection> + <RepositionThemeTransition /> + <AddDeleteThemeTransition /> + </TransitionCollection> + </Grid.ChildrenTransitions> + <ContentPresenter + HorizontalAlignment="Right" + VerticalAlignment="Top" + Content="{x:Bind InfoBadge}" /> + <Image + x:Name="Image" + Width="28" + Height="28" + x:Load="{x:Bind ShowThumbnail}" + Source="{x:Bind Thumbnail}" /> + <FontIcon + x:Name="fontIcon" + x:Load="{x:Bind ShowFontIcons}" + Visibility="{x:Bind FontIconVisibility}" + FontSize="22" + Glyph="{x:Bind FontIconGlyph}" /> + <FontIcon + x:Name="fontIconSolid" + x:Load="{x:Bind ShowFontIcons}" + Visibility="{x:Bind SolidFontIconVisibility}" + FontSize="22" + Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" + Glyph="{x:Bind SolidFontIconGlyph}" /> + <TextBlock + x:Name="txtNavItemName" + Grid.Row="1" + Visibility="{x:Bind SelectionVisibility}" + HorizontalAlignment="Center" + x:Load="{x:Bind IsSelected.Equals(x:False), Mode=OneWay}" + FontSize="10" + Foreground="{ThemeResource ApplicationSecondaryForegroundThemeBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Bind Name}" /> + </Grid> + </NavigationViewItem> + </DataTemplate> + </NavigationView.MenuItemTemplate> + + + + <Frame x:Name="frame"/> + + </NavigationView> + </Grid> +</Page> diff --git a/Emerald/MainPage.xaml.cs b/Emerald/MainPage.xaml.cs new file mode 100644 index 00000000..a2496167 --- /dev/null +++ b/Emerald/MainPage.xaml.cs @@ -0,0 +1,125 @@ +using Emerald.Helpers; +using Emerald.Models; +using Emerald.Views.Settings; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Emerald; + +public sealed partial class MainPage : Page +{ + public MainPage() + { + this.InitializeComponent(); + this.Loaded += MainPage_Loaded; + NavView.ItemInvoked += MainNavigationView_ItemInvoked; + } + private void MainNavigationView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + if (!args.IsSettingsInvoked && NavView.SelectedItem is SquareNavigationViewItem itm) + { + itm.InvokePropertyChanged(); + } + } + private void MainPage_Loaded(object sender, RoutedEventArgs e) + { +#if WINDOWS + Emerald.Helpers.WindowManager.SetTitleBar(App.Current.MainWindow, AppTitleBar); +#endif + NavView.MenuItems.Add(new SquareNavigationViewItem("Home".Localize()) + { + FontIconGlyph = "\xE80F", + Tag = "Home", + SolidFontIconGlyph = "\xEA8A", + IsSelected = true, + ShowFontIcons = true + }); + NavView.MenuItems.Add(new SquareNavigationViewItem("Store".Localize()) + { + Tag = "Store", + FontIconGlyph = "\xE7BF", + SolidFontIconGlyph = "\xE7BF", + IsSelected = false, + ShowFontIcons = true + }); + NavView.MenuItems.Add(new SquareNavigationViewItem("News".Localize()) + { + Tag = "News", + FontIconGlyph = "\xF57E", + SolidFontIconGlyph = "\xF57E", + IsSelected = false, + ShowFontIcons = true + }); + + NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Tasks".Localize()) + { + Tag = "Tasks", + FontIconGlyph = "\xE9D5", + SolidFontIconGlyph = "\xE9D5", + IsSelected = false, + ShowFontIcons = true + }); + NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Logs".Localize()) + { + Tag = "Logs", + FontIconGlyph = "\xE756", + SolidFontIconGlyph = "\xE756", + IsSelected = false, + ShowFontIcons = true + }); + NavView.FooterMenuItems.Add(new SquareNavigationViewItem("Settings".Localize()) + { + Tag = "Settings", + FontIconGlyph = "\xE713", + SolidFontIconGlyph = "\xE713", + IsSelected = false, + ShowFontIcons = true + }); + + NavView.SelectedItem = NavView.MenuItems[0]; + + NavView.Header = new NavViewHeader() { HeaderText = "Home".Localize(), HeaderMargin = GetNavViewHeaderMargin() }; + NavView.DisplayModeChanged += (_, _) => (NavView.Header as NavViewHeader).HeaderMargin = GetNavViewHeaderMargin(); + Navigate(NavView.SelectedItem as SquareNavigationViewItem); + } + + private Thickness GetNavViewHeaderMargin() + { + if (NavView.DisplayMode == NavigationViewDisplayMode.Minimal) + { + NavView.IsPaneToggleButtonVisible = true; + return new Thickness(35, -40, 0, 0); + } + else + { + NavView.IsPaneToggleButtonVisible = false; + return new Thickness(-30, -20, 0, 10); + } + } + private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + Navigate(NavView.SelectedItem as SquareNavigationViewItem); + } + + private void Navigate(SquareNavigationViewItem itm) + { + switch (itm.Tag) + { + default: + NavigateOnce(typeof(SettingsPage)); + break; + } + (NavView.Header as NavViewHeader).HeaderText = itm.Tag == "Tasks" ? (NavView.Header as NavViewHeader).HeaderText : itm.Name; + (NavView.Header as NavViewHeader).HeaderMargin = GetNavViewHeaderMargin(); + + } + + private void NavigateOnce(Type type) + { + + if (frame.Content == null || frame.Content.GetType() != type) + { + frame.Navigate(type, null, new EntranceNavigationTransitionInfo()); + } + } +} diff --git a/Emerald/Models/ArgTemplate.cs b/Emerald/Models/ArgTemplate.cs new file mode 100644 index 00000000..ee708800 --- /dev/null +++ b/Emerald/Models/ArgTemplate.cs @@ -0,0 +1,13 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Emerald.Models; + +//Copied from Emerald.UWP +[ObservableObject] +public partial class ArgTemplate +{ + [ObservableProperty] + private string arg; + + public int Count { get; set; } +} diff --git a/Emerald/Models/Model.cs b/Emerald/Models/Model.cs new file mode 100644 index 00000000..552f279d --- /dev/null +++ b/Emerald/Models/Model.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System.ComponentModel; + +namespace Emerald.Models; + +public partial class Model : ObservableObject +{ + internal void Set<T>(ref T obj, T value, string name = null) + { + SetProperty(ref obj, value, name); + } + + public void InvokePropertyChanged(string name = null) + { + OnPropertyChanged(new PropertyChangedEventArgs(name)); + } +} diff --git a/Emerald/Models/NavViewHeader.cs b/Emerald/Models/NavViewHeader.cs new file mode 100644 index 00000000..093bdbaa --- /dev/null +++ b/Emerald/Models/NavViewHeader.cs @@ -0,0 +1,23 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Emerald.Models; + +public partial class NavViewHeader : Model +{ + [ObservableProperty] + private string _HeaderText; + + [ObservableProperty] + private string _CustomButtonText; + + public Visibility CustomButtonVisibility { get => CustomButtonText == null ? Visibility.Collapsed : Visibility.Visible; } + + + [ObservableProperty] + private string _CustomContent; + + public Visibility CustomContentVisibility { get => CustomContent == null ? Visibility.Collapsed : Visibility.Visible; } + + [ObservableProperty] + private Thickness _HeaderMargin; +} diff --git a/Emerald/Models/SquareNavigationViewItem.cs b/Emerald/Models/SquareNavigationViewItem.cs new file mode 100644 index 00000000..3f9f2ac5 --- /dev/null +++ b/Emerald/Models/SquareNavigationViewItem.cs @@ -0,0 +1,63 @@ +using System.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Emerald.Models; + +public partial class SquareNavigationViewItem : Model +{ + public SquareNavigationViewItem() + { + PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons)) + { + InvokePropertyChanged(null); + } + }; + } + public SquareNavigationViewItem(string name, bool isSelected = false, ImageSource image = null, InfoBadge infoBadge = null) + { + Name = name; + IsSelected = isSelected; + Thumbnail = image; + InfoBadge = infoBadge; + PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons)) + { + InvokePropertyChanged(null); + } + }; + } + public string Tag { get; set; } + + [ObservableProperty] + private string _Name; + + [ObservableProperty] + private string _FontIconGlyph; + + [ObservableProperty] + private string _SolidFontIconGlyph; + + [ObservableProperty] + private bool _IsSelected; + + [ObservableProperty] + private bool _IsEnabled = true; + + [ObservableProperty] + private ImageSource _Thumbnail; + + [ObservableProperty] + private InfoBadge _InfoBadge; + + [ObservableProperty] + private bool _ShowFontIcons; + + public Visibility FontIconVisibility => ShowFontIcons && !IsSelected ? Visibility.Visible : Visibility.Collapsed; + public Visibility SolidFontIconVisibility => ShowFontIcons && IsSelected ? Visibility.Visible : Visibility.Collapsed; + public Visibility SelectionVisibility => IsSelected ? Visibility.Collapsed : Visibility.Visible; + + public bool ShowThumbnail => !ShowFontIcons; +} diff --git a/Emerald/Package.appxmanifest b/Emerald/Package.appxmanifest new file mode 100644 index 00000000..29558f1d --- /dev/null +++ b/Emerald/Package.appxmanifest @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Package + xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" + xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" + IgnorableNamespaces="uap rescap"> + + <Identity + Name="Emerald" + Publisher="O=Riverside Valley" + Version="1.0.0.0" /> + + <Properties> + <DisplayName>Emerald</DisplayName> + <PublisherDisplayName>Emerald</PublisherDisplayName> + </Properties> + + <Dependencies> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> + </Dependencies> + + <Resources> + <Resource Language="x-generate"/> + </Resources> + + <Applications> + <Application Id="App" + Executable="$targetnametoken$.exe" + EntryPoint="$targetentrypoint$"> + <uap:VisualElements + DisplayName="Emerald" + Description="Emerald"> + <uap:SplashScreen /> + </uap:VisualElements> + </Application> + </Applications> + + <Capabilities> + <rescap:Capability Name="runFullTrust" /> + </Capabilities> +</Package> diff --git a/Emerald/Platforms/Desktop/Program.cs b/Emerald/Platforms/Desktop/Program.cs new file mode 100644 index 00000000..505e2c1b --- /dev/null +++ b/Emerald/Platforms/Desktop/Program.cs @@ -0,0 +1,19 @@ +using Uno.UI.Runtime.Skia; + +namespace Emerald; +public class Program +{ + [STAThread] + public static void Main(string[] args) + { + var host = SkiaHostBuilder.Create() + .App(() => new App()) + .UseX11() + .UseLinuxFrameBuffer() + .UseMacOS() + .UseWindows() + .Build(); + + host.Run(); + } +} diff --git a/Emerald/Platforms/MacCatalyst/Entitlements.plist b/Emerald/Platforms/MacCatalyst/Entitlements.plist new file mode 100644 index 00000000..24c31036 --- /dev/null +++ b/Emerald/Platforms/MacCatalyst/Entitlements.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + </dict> +</plist> diff --git a/Emerald/Platforms/MacCatalyst/Info.plist b/Emerald/Platforms/MacCatalyst/Info.plist new file mode 100644 index 00000000..1bb02ddc --- /dev/null +++ b/Emerald/Platforms/MacCatalyst/Info.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>UIDeviceFamily</key> + <array> + <integer>2</integer> + </array> + <key>LSApplicationCategoryType</key> + <string>public.app-category.utilities</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>XSAppIconAssets</key> + <string>Assets.xcassets/icon.appiconset</string> + + <!-- + Adjust this to your application's encryption usage. + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + --> + </dict> +</plist> diff --git a/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs b/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs new file mode 100644 index 00000000..d26d16e3 --- /dev/null +++ b/Emerald/Platforms/MacCatalyst/Main.maccatalyst.cs @@ -0,0 +1,13 @@ +using UIKit; + +namespace Emerald.MacCatalyst; +public class EntryPoint +{ + // This is the main entry point of the application. + public static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(App)); + } +} diff --git a/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json b/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json new file mode 100644 index 00000000..69555e44 --- /dev/null +++ b/Emerald/Platforms/MacCatalyst/Media.xcassets/LaunchImages.launchimage/Contents.json @@ -0,0 +1,58 @@ +{ + "images": [ + { + "orientation": "portrait", + "extent": "full-screen", + "minimum-system-version": "7.0", + "scale": "2x", + "size": "640x960", + "idiom": "iphone" + }, + { + "orientation": "portrait", + "extent": "full-screen", + "minimum-system-version": "7.0", + "subtype": "retina4", + "scale": "2x", + "size": "640x1136", + "idiom": "iphone" + }, + { + "orientation": "portrait", + "extent": "full-screen", + "minimum-system-version": "7.0", + "scale": "1x", + "size": "768x1024", + "idiom": "ipad" + }, + { + "orientation": "landscape", + "extent": "full-screen", + "minimum-system-version": "7.0", + "scale": "1x", + "size": "1024x768", + "idiom": "ipad" + }, + { + "orientation": "portrait", + "extent": "full-screen", + "minimum-system-version": "7.0", + "scale": "2x", + "size": "1536x2048", + "idiom": "ipad" + }, + { + "orientation": "landscape", + "extent": "full-screen", + "minimum-system-version": "7.0", + "scale": "2x", + "size": "2048x1536", + "idiom": "ipad" + } + ], + "properties": {}, + "info": { + "version": 1, + "author": "" + } +} \ No newline at end of file diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml new file mode 100644 index 00000000..d5147f10 --- /dev/null +++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-arm64.pubxml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <PublishProtocol>FileSystem</PublishProtocol> + <Platform>arm64</Platform> + <RuntimeIdentifier>win-arm64</RuntimeIdentifier> + <PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir> + <SelfContained>true</SelfContained> + <PublishSingleFile>False</PublishSingleFile> + <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun> + <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> + <!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 --> + <!-- + <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> + <TrimMode>partial</TrimMode> + <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> + --> + </PropertyGroup> +</Project> diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml new file mode 100644 index 00000000..4fea954e --- /dev/null +++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x64.pubxml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <PublishProtocol>FileSystem</PublishProtocol> + <Platform>x64</Platform> + <RuntimeIdentifier>win-x64</RuntimeIdentifier> + <PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir> + <SelfContained>true</SelfContained> + <PublishSingleFile>False</PublishSingleFile> + <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun> + <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> + <!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 --> + <!-- + <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> + <TrimMode>partial</TrimMode> + <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> + --> + </PropertyGroup> +</Project> diff --git a/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml new file mode 100644 index 00000000..928cb25e --- /dev/null +++ b/Emerald/Platforms/Windows/Properties/PublishProfiles/win-x86.pubxml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +https://go.microsoft.com/fwlink/?LinkID=208121. +--> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <PublishProtocol>FileSystem</PublishProtocol> + <Platform>x86</Platform> + <RuntimeIdentifier>win-x86</RuntimeIdentifier> + <PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir> + <SelfContained>true</SelfContained> + <PublishSingleFile>False</PublishSingleFile> + <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun> + <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> + <!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 --> + <!-- + <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> + <TrimMode>partial</TrimMode> + <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> + --> + </PropertyGroup> +</Project> diff --git a/Emerald/Properties/launchSettings.json b/Emerald/Properties/launchSettings.json new file mode 100644 index 00000000..a8c8aed9 --- /dev/null +++ b/Emerald/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:8080", + "sslPort": 0 + } + }, + "profiles": { + // Note: In order to select this profile, you'll need to comment the `Packaged` profile below until this is fixed: https://aka.platform.uno/wasdk-maui-debug-profile-issue + "Emerald (WinAppSDK Unpackaged)": { + "commandName": "Project", + "compatibleTargetFramework": "windows" + }, + "Emerald (WinAppSDK Packaged)": { + "commandName": "MsixPackage", + "compatibleTargetFramework": "windows" + }, + "Emerald (Desktop)": { + "commandName": "Project", + "compatibleTargetFramework": "desktop" + }, + "Emerald (Desktop WSL2)": { + "commandName": "WSL2", + "commandLineArgs": "{ProjectDir}/bin/Debug/net8.0-desktop/Emerald.dll", + "distributionName": "", + "compatibleTargetFramework": "desktop" + } + } +} diff --git a/Emerald/ReadMe.md b/Emerald/ReadMe.md new file mode 100644 index 00000000..93482da2 --- /dev/null +++ b/Emerald/ReadMe.md @@ -0,0 +1,7 @@ +# Getting Started + +Welcome to the Uno Platform! + +To discover how to get started with your new app: https://aka.platform.uno/get-started + +For more information on how to use the Uno.Sdk or upgrade Uno Platform packages in your solution: https://aka.platform.uno/using-uno-sdk \ No newline at end of file diff --git a/Emerald/Strings/en/Resources.resw b/Emerald/Strings/en/Resources.resw new file mode 100644 index 00000000..7297f452 --- /dev/null +++ b/Emerald/Strings/en/Resources.resw @@ -0,0 +1,760 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ApplicationName" xml:space="preserve"> + <value>Emerald-en</value> + </data> + <data name="About" xml:space="preserve"> + <value>About</value> + </data> + <data name="AccentColor" xml:space="preserve"> + <value>Accent color</value> + </data> + <data name="AccessToken" xml:space="preserve"> + <value>Access token</value> + </data> + <data name="AccountDetails" xml:space="preserve"> + <value>Account Details</value> + </data> + <data name="Accounts" xml:space="preserve"> + <value>Accounts</value> + </data> + <data name="AccountType" xml:space="preserve"> + <value>Account Type</value> + </data> + <data name="Add" xml:space="preserve"> + <value>Add</value> + </data> + <data name="AdvancedMinecraftSettings" xml:space="preserve"> + <value>Advanced Settings</value> + </data> + <data name="All" xml:space="preserve"> + <value>All</value> + </data> + <data name="alpha" xml:space="preserve"> + <value>Alpha</value> + </data> + <data name="Appearance" xml:space="preserve"> + <value>Appearance</value> + </data> + <data name="AssetsCheck" xml:space="preserve"> + <value>Assets check</value> + </data> + <data name="AuthWindowshello" xml:space="preserve"> + <value>Authenticating using Windows Hello...</value> + </data> + <data name="AutoDownloadUpdates" xml:space="preserve"> + <value>Automatically download updates if available</value> + </data> + <data name="AutoLogin" xml:space="preserve"> + <value>Auto login</value> + </data> + <data name="AutoLoginDescription" xml:space="preserve"> + <value>Automatically tries to login with the most recent account at startup</value> + </data> + <data name="BegLogIn" xml:space="preserve"> + <value>Please Login</value> + </data> + <data name="BegVer" xml:space="preserve"> + <value>Please enter a version</value> + </data> + <data name="beta" xml:space="preserve"> + <value>Beta</value> + </data> + <data name="BuildType" xml:space="preserve"> + <value>Build type</value> + </data> + <data name="Changelogs" xml:space="preserve"> + <value>Changelogs</value> + </data> + <data name="CheckForUpdates" xml:space="preserve"> + <value>Check for updates</value> + </data> + <data name="CheckForUpdatesAtStartup" xml:space="preserve"> + <value>Check for updates when app starts</value> + </data> + <data name="CheckingUpdates" xml:space="preserve"> + <value>Checking for updates</value> + </data> + <data name="CheckNow" xml:space="preserve"> + <value>Check Now</value> + </data> + <data name="ChooseAccount" xml:space="preserve"> + <value>Choose an account</value> + </data> + <data name="ChooseAVersion" xml:space="preserve"> + <value>Choose a version</value> + </data> + <data name="Clear" xml:space="preserve"> + <value>Clear</value> + </data> + <data name="ClearAll" xml:space="preserve"> + <value>Clear All</value> + </data> + <data name="Colorful" xml:space="preserve"> + <value>Colorful</value> + </data> + <data name="ColorMode" xml:space="preserve"> + <value>Color Mode</value> + </data> + <data name="ColorModeDescription" xml:space="preserve"> + <value>Choose your color</value> + </data> + <data name="ConfigureOptifine" xml:space="preserve"> + <value>Configure Optifine</value> + </data> + <data name="ConnectingGithub" xml:space="preserve"> + <value>Connecting to github</value> + </data> + <data name="Contribute" xml:space="preserve"> + <value>Contribute</value> + </data> + <data name="CopyVersion" xml:space="preserve"> + <value>Copy version</value> + </data> + <data name="CreateAColor" xml:space="preserve"> + <value>Create a color</value> + </data> + <data name="CreateBackup" xml:space="preserve"> + <value>Create Backup</value> + </data> + <data name="CreateOldSettingsBackup" xml:space="preserve"> + <value>Create a backup of the old settings</value> + </data> + <data name="Credits" xml:space="preserve"> + <value>Credits</value> + </data> + <data name="CreditsDescription" xml:space="preserve"> + <value>Respect...</value> + </data> + <data name="CustomColor" xml:space="preserve"> + <value>Custom color</value> + </data> + <data name="CustomColors" xml:space="preserve"> + <value>Custom colors</value> + </data> + <data name="Dark" xml:space="preserve"> + <value>Dark</value> + </data> + <data name="Default" xml:space="preserve"> + <value>Default</value> + </data> + <data name="Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="DeviceCode" xml:space="preserve"> + <value>Using DeviceCode API</value> + </data> + <data name="Discord" xml:space="preserve"> + <value>Discord</value> + </data> + <data name="Downgrade" xml:space="preserve"> + <value>Downgrade</value> + </data> + <data name="DowngradeAvailable" xml:space="preserve"> + <value>Downgrade Available</value> + </data> + <data name="DowngradeDescription" xml:space="preserve"> + <value>The current version is higher than the latest version in the servers. You must be using a private beta. Please wait until a higher stable version be available</value> + </data> + <data name="Download" xml:space="preserve"> + <value>Download</value> + </data> + <data name="DownloaderBusy" xml:space="preserve"> + <value>The mod downloader is busy</value> + </data> + <data name="Downloading" xml:space="preserve"> + <value>Downloading</value> + </data> + <data name="DownloadingUpdate" xml:space="preserve"> + <value>Download Update</value> + </data> + <data name="DownloadMod" xml:space="preserve"> + <value>Download Mod</value> + </data> + <data name="Downloads" xml:space="preserve"> + <value>Downloads</value> + </data> + <data name="DownloadSettings" xml:space="preserve"> + <value>Download Settings</value> + </data> + <data name="DownloadSettingsDescription" xml:space="preserve"> + <value>Optional. Recommended to turn these on</value> + </data> + <data name="Duplicate" xml:space="preserve"> + <value>Duplicate</value> + </data> + <data name="EmbededView" xml:space="preserve"> + <value>Using an embeded webview</value> + </data> + <data name="Empty" xml:space="preserve"> + <value>Empty</value> + </data> + <data name="Error" xml:space="preserve"> + <value>Error</value> + </data> + <data name="Finish" xml:space="preserve"> + <value>Finish!</value> + </data> + <data name="Followers" xml:space="preserve"> + <value>Followers</value> + </data> + <data name="FullScreen" xml:space="preserve"> + <value>Full Screen</value> + </data> + <data name="GameIsRunning" xml:space="preserve"> + <value>Game is running...</value> + </data> + <data name="GameLogs" xml:space="preserve"> + <value>Read game logs</value> + </data> + <data name="GameLogsDescription" xml:space="preserve"> + <value>For debug perposes</value> + </data> + <data name="General" xml:space="preserve"> + <value>General</value> + </data> + <data name="GeneralSettings" xml:space="preserve"> + <value>General Settings</value> + </data> + <data name="GetFabric" xml:space="preserve"> + <value>Get Fabric</value> + </data> + <data name="GetMods" xml:space="preserve"> + <value>Get Mods</value> + </data> + <data name="GettingFabric" xml:space="preserve"> + <value>Getting Fabric</value> + </data> + <data name="GettingInheritedVersion" xml:space="preserve"> + <value>Getting inherited version...</value> + </data> + <data name="GettingMods" xml:space="preserve"> + <value>Load Available Mods</value> + </data> + <data name="GettingOptifine" xml:space="preserve"> + <value>Getting Optifine...</value> + </data> + <data name="GettingVers" xml:space="preserve"> + <value>Getting available versions...</value> + </data> + <data name="GetVerFailed" xml:space="preserve"> + <value>Failed to get the version</value> + </data> + <data name="Github" xml:space="preserve"> + <value>Github</value> + </data> + <data name="GoodEvening" xml:space="preserve"> + <value>Good Evening</value> + </data> + <data name="GoodLuck" xml:space="preserve"> + <value>Good Luck</value> + </data> + <data name="GoodMorning" xml:space="preserve"> + <value>Good Morning</value> + </data> + <data name="GotIt" xml:space="preserve"> + <value>Got it!</value> + </data> + <data name="GotoLinkAndEnterDeviceCode" xml:space="preserve"> + <value>Go to {Link} and enter the code below to sign-in</value> + </data> + <data name="HashCheck" xml:space="preserve"> + <value>Hash check</value> + </data> + <data name="Height" xml:space="preserve"> + <value>Screen Height</value> + </data> + <data name="Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="IncludePreReleases" xml:space="preserve"> + <value>Include pre-releases</value> + </data> + <data name="InitializeCore" xml:space="preserve"> + <value>Initialize Launcher Core</value> + </data> + <data name="Install" xml:space="preserve"> + <value>Install</value> + </data> + <data name="InstallUpdate" xml:space="preserve"> + <value>Install Update</value> + </data> + <data name="JVMArgs" xml:space="preserve"> + <value>JVM Arguments</value> + </data> + <data name="JVMArgsDescription" xml:space="preserve"> + <value>For Endermans!</value> + </data> + <data name="Latest" xml:space="preserve"> + <value>Latest</value> + </data> + <data name="Launch" xml:space="preserve"> + <value>Launch!</value> + </data> + <data name="LaunchingMinecraft" xml:space="preserve"> + <value>Launching Minecraft...</value> + </data> + <data name="LaunchMC" xml:space="preserve"> + <value>Launch Minecraft</value> + </data> + <data name="Light" xml:space="preserve"> + <value>Light</value> + </data> + <data name="Links" xml:space="preserve"> + <value>Links</value> + </data> + <data name="LinksDescription" xml:space="preserve"> + <value>Keep up with the latest updates</value> + </data> + <data name="LoadChangeLogs" xml:space="preserve"> + <value>Load ChangeLogs</value> + </data> + <data name="LoadDownloadVers" xml:space="preserve"> + <value>Load Download Versions</value> + </data> + <data name="LoadingChangeLog" xml:space="preserve"> + <value>Loading ChangeLog</value> + </data> + <data name="LoadingDownloadVers" xml:space="preserve"> + <value>Loading Download Versions</value> + </data> + <data name="LoadingMod" xml:space="preserve"> + <value>Loading Mod</value> + </data> + <data name="LoadMod" xml:space="preserve"> + <value>Load Mod</value> + </data> + <data name="LoadSettingsFailed" xml:space="preserve"> + <value>Failed to load the app settings and accounts. The version of the setting system is different. You may have recently updated the app. Contact a developer of this app to recover the settings</value> + </data> + <data name="Login" xml:space="preserve"> + <value>Login</value> + </data> + <data name="LoginWithMicrosoft" xml:space="preserve"> + <value>Login with Microsoft</value> + </data> + <data name="Logs" xml:space="preserve"> + <value>Logs</value> + </data> + <data name="MainPage_Login" xml:space="preserve"> + <value>Login</value> + </data> + <data name="MainPage_txtbxStatus.PlaceholderText" xml:space="preserve"> + <value>For a better RPC please add a small status!</value> + </data> + <data name="MainPage_txtStatus.Text" xml:space="preserve"> + <value>Status:</value> + </data> + <data name="MCasAdmin" xml:space="preserve"> + <value>Launch minecraft as admin</value> + </data> + <data name="MCasAdminDescription" xml:space="preserve"> + <value>Do this at your own risk!</value> + </data> + <data name="MCBedrock" xml:space="preserve"> + <value>Minecraft for Windows</value> + </data> + <data name="MCDungeons" xml:space="preserve"> + <value>Minecraft Dungeons</value> + </data> + <data name="MCJava" xml:space="preserve"> + <value>Minecraft: Java Edition</value> + </data> + <data name="MCLegends" xml:space="preserve"> + <value>Minecraft Legends</value> + </data> + <data name="MCPathFailed" xml:space="preserve"> + <value>Failed to initialize Minecraft path at `{Path}`. Do you want to try again?</value> + </data> + <data name="MCWindowsSize" xml:space="preserve"> + <value>Minecraft Windows Size</value> + </data> + <data name="MCWindowsSizeDescription" xml:space="preserve"> + <value>Change width, height or full screen</value> + </data> + <data name="MergeAccount" xml:space="preserve"> + <value>Merge Account</value> + </data> + <data name="MergeMsAcExistingWithNew" xml:space="preserve"> + <value>{Count} Microsoft account with same UUID already exist(s) in the accounts. Do you want to merge them together?</value> + </data> + <data name="MicaType" xml:space="preserve"> + <value>Mica Type</value> + </data> + <data name="MicaTypeDescription" xml:space="preserve"> + <value>Normal Mica or Mica Alt (Tabbed)</value> + </data> + <data name="MicrosoftAccount" xml:space="preserve"> + <value>Microsoft Account</value> + </data> + <data name="MicrosoftOrOffline" xml:space="preserve"> + <value>Microsoft or offline</value> + </data> + <data name="MinecraftPath" xml:space="preserve"> + <value>Minecraft Path</value> + </data> + <data name="MinecraftPathDescription" xml:space="preserve"> + <value>The path of the minecraft client</value> + </data> + <data name="MinecraftSettings" xml:space="preserve"> + <value>Minecraft Settings</value> + </data> + <data name="ModsNoInternet" xml:space="preserve"> + <value>Cannot find mods with follwing name or you may got disconnected from the internet.</value> + </data> + <data name="More" xml:space="preserve"> + <value>More</value> + </data> + <data name="MoreVerInfo" xml:space="preserve"> + <value>More version info</value> + </data> + <data name="NavIconStyle" xml:space="preserve"> + <value>Navigation Buttons Icon Style</value> + </data> + <data name="NavIconStyleDescription" xml:space="preserve"> + <value>Select a beautiful icons set for your sidebar</value> + </data> + <data name="NewAccount" xml:space="preserve"> + <value>New Account</value> + </data> + <data name="NewHere" xml:space="preserve"> + <value>Looks like you are new here. Shall we go on a quick check out?</value> + </data> + <data name="News" xml:space="preserve"> + <value>News</value> + </data> + <data name="NewsNoInternet" xml:space="preserve"> + <value>Cannot find news with follwing title or you may got disconnected from the internet.</value> + </data> + <data name="NoColor" xml:space="preserve"> + <value>No color</value> + </data> + <data name="NoMsixUpdate" xml:space="preserve"> + <value>Failed to find the installer package in the release for this architecture</value> + </data> + <data name="NoNetwork" xml:space="preserve"> + <value>Seems like you don't have good internet...</value> + </data> + <data name="NoThanks" xml:space="preserve"> + <value>No Thanks</value> + </data> + <data name="NoUpdates" xml:space="preserve"> + <value>You are on the latest version</value> + </data> + <data name="NoUpdatesAvailable" xml:space="preserve"> + <value>No Updates Available</value> + </data> + <data name="OfflineAccount" xml:space="preserve"> + <value>Offline account</value> + </data> + <data name="OfflineMode" xml:space="preserve"> + <value>Offline Mode</value> + </data> + <data name="Options" xml:space="preserve"> + <value>Options</value> + </data> + <data name="PickVer" xml:space="preserve"> + <value>Pick a Version</value> + </data> + <data name="RAM" xml:space="preserve"> + <value>Memory</value> + </data> + <data name="RAMDescription" xml:space="preserve"> + <value>The allocated memory of the minecraft client</value> + </data> + <data name="RamFailed" xml:space="preserve"> + <value>Failed to get the RAM of the device.</value> + <comment>Rarely Used</comment> + </data> + <data name="Ready" xml:space="preserve"> + <value>Ready</value> + </data> + <data name="RefreshVerFailed" xml:space="preserve"> + <value>Failed to get the Minecraft versions</value> + </data> + <data name="RefreshVers" xml:space="preserve"> + <value>Refresh Versions</value> + </data> + <data name="release" xml:space="preserve"> + <value>Release</value> + </data> + <data name="ReleaseNotes" xml:space="preserve"> + <value>Release notes</value> + </data> + <data name="Remove" xml:space="preserve"> + <value>Remove</value> + </data> + <data name="Retry" xml:space="preserve"> + <value>Retry</value> + </data> + <data name="Save" xml:space="preserve"> + <value>Save</value> + </data> + <data name="Search" xml:space="preserve"> + <value>Search</value> + </data> + <data name="SearchingStore" xml:space="preserve"> + <value>Searching Store</value> + </data> + <data name="SearchStore" xml:space="preserve"> + <value>Search Store</value> + </data> + <data name="Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="SelectAll" xml:space="preserve"> + <value>Select All</value> + </data> + <data name="SelectNone" xml:space="preserve"> + <value>Select None</value> + </data> + <data name="SetDifMCPath" xml:space="preserve"> + <value>Change Path</value> + </data> + <data name="Settings" xml:space="preserve"> + <value>Settings</value> + </data> + <data name="SettingsBackups" xml:space="preserve"> + <value>Settings Backups</value> + </data> + <data name="SettingsBackupsDescription" xml:space="preserve"> + <value>Create, delete, view and try to load backups of your settings</value> + </data> + <data name="SettingsLoadWarn" xml:space="preserve"> + <value>This will overwrite the current settings and restart the application. If the application failed to load the settings, the previous settings will be lost. Do you want to create a backup of this settings before load?</value> + </data> + <data name="SettingsPage_txtSettings.Text" xml:space="preserve"> + <value>Settings</value> + </data> + <data name="SignInWithMS" xml:space="preserve"> + <value>Sign-in with Microsoft</value> + </data> + <data name="Store" xml:space="preserve"> + <value>Store</value> + </data> + <data name="Sure" xml:space="preserve"> + <value>Sure!</value> + </data> + <data name="SwitchOffline" xml:space="preserve"> + <value>Switch to offline mode</value> + </data> + <data name="SystemDefault" xml:space="preserve"> + <value>System Default</value> + </data> + <data name="TaskIntro" xml:space="preserve"> + <value>If you clear a running task, it won't get stopped. It'll run in the background without showing a progress.</value> + </data> + <data name="Tasks" xml:space="preserve"> + <value>Tasks</value> + </data> + <data name="ThatsIt" xml:space="preserve"> + <value>That's it! Go to settings for more options</value> + </data> + <data name="TimeLeft" xml:space="preserve"> + <value>{Time} left</value> + </data> + <data name="TryLoad" xml:space="preserve"> + <value>Try to load</value> + </data> + <data name="TypeCustom" xml:space="preserve"> + <value>Local</value> + </data> + <data name="TypeOldAlpha" xml:space="preserve"> + <value>OldAlpha</value> + </data> + <data name="TypeOldBeta" xml:space="preserve"> + <value>OldBeta</value> + </data> + <data name="TypeRelease" xml:space="preserve"> + <value>Release</value> + </data> + <data name="TypeSnapshot" xml:space="preserve"> + <value>Snapshot</value> + </data> + <data name="UnexpectedRestart" xml:space="preserve"> + <value>Some unexpected thing happend. Please restart the app!</value> + </data> + <data name="UpdateAvailable" xml:space="preserve"> + <value>Update Available</value> + </data> + <data name="UpdateNow" xml:space="preserve"> + <value>Update now</value> + </data> + <data name="Updates" xml:space="preserve"> + <value>Updates</value> + </data> + <data name="UpdatesDescription" xml:space="preserve"> + <value>Auto update, include prerelease etc..</value> + </data> + <data name="Username" xml:space="preserve"> + <value>Username</value> + </data> + <data name="UsingBrowser" xml:space="preserve"> + <value>Using the default browser</value> + </data> + <data name="UUID" xml:space="preserve"> + <value>UUID</value> + </data> + <data name="VaniilaOrFabric" xml:space="preserve"> + <value>Fabric and Vanilla is officially supported</value> + </data> + <data name="Version" xml:space="preserve"> + <value>Version</value> + </data> + <data name="View" xml:space="preserve"> + <value>View</value> + </data> + <data name="Warning" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="Welcome" xml:space="preserve"> + <value>Welcome!</value> + <comment>Rarely Used</comment> + </data> + <data name="Whoops" xml:space="preserve"> + <value>Whoops!</value> + </data> + <data name="Width" xml:space="preserve"> + <value>Screen Width</value> + </data> + <data name="Win32Error" xml:space="preserve"> + <value>It seems your java setting has problem</value> + <comment>Rarely Used</comment> + </data> + <data name="WindowsHello" xml:space="preserve"> + <value>Windows Hello</value> + </data> + <data name="WindowsHelloDescription" xml:space="preserve"> + <value>Do not create, view, load, delete backups without Windows Hello authentication</value> + </data> + <data name="WindowTintColor" xml:space="preserve"> + <value>Window tint color</value> + </data> + <data name="WindowTintColorDescription" xml:space="preserve"> + <value>Show a color overlay behind the app content</value> + </data> + <data name="WinHelloChangeFailed" xml:space="preserve"> + <value>Unable to change the setting because you failed to authenticate with Windows Hello</value> + </data> + <data name="WrongRAM" xml:space="preserve"> + <value>Invalid RAM</value> + <comment>Rarely Used</comment> + </data> +</root> \ No newline at end of file diff --git a/Emerald/UserControls/ArgumentsListView.xaml b/Emerald/UserControls/ArgumentsListView.xaml new file mode 100644 index 00000000..ca819bcd --- /dev/null +++ b/Emerald/UserControls/ArgumentsListView.xaml @@ -0,0 +1,62 @@ +<UserControl + x:Class="Emerald.UserControls.ArgumentsListView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:Emerald.UserControls" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + d:DesignHeight="300" + xmlns:models="using:Emerald.Models" + d:DesignWidth="400"> + <Grid> + <Grid.Resources> + <ResourceDictionary> + <SolidColorBrush x:Key="TextBoxBackground" Color="Transparent" /> + <SolidColorBrush x:Key="TextBoxBackgroundPointerOver" Color="Transparent" /> + <SolidColorBrush x:Key="TextBoxBackgroundPressed" Color="Transparent" /> + </ResourceDictionary> + </Grid.Resources> + <StackPanel> + <StackPanel Margin="0,5" Orientation="Horizontal"> + <Button + x:Name="btnAdd" + Padding="7" + Click="btnAdd_Click"> + <FontIcon Glyph="" /> + </Button> + <Button + x:Name="btnRemove" + Margin="5,0,0,0" + Padding="7" + Click="btnRemove_Click"> + <FontIcon Glyph="" /> + </Button> + </StackPanel> + <ListView x:Name="view" SelectionChanged="view_SelectionChanged"> + <ListView.Resources> + <SolidColorBrush x:Key="ListViewItemBackgroundPointerOver" Color="Transparent" /> + </ListView.Resources> + <ListView.ItemContainerTransitions> + <TransitionCollection> + <AddDeleteThemeTransition /> + </TransitionCollection> + </ListView.ItemContainerTransitions> + <ListView.ItemTemplate> + <DataTemplate x:DataType="models:ArgTemplate"> + <TextBox + Margin="-9,-4" + VerticalAlignment="Stretch" + Background="Transparent" + BorderThickness="0" + GotFocus="TextBox_GotFocus" + PlaceholderText="Empty" + PointerPressed="TextBox_PointerPressed" + Text="{x:Bind Arg,Mode=TwoWay}" + TextChanged="TextBox_TextChanged" /> + </DataTemplate> + </ListView.ItemTemplate> + </ListView> + </StackPanel> + </Grid> +</UserControl> diff --git a/Emerald/UserControls/ArgumentsListView.xaml.cs b/Emerald/UserControls/ArgumentsListView.xaml.cs new file mode 100644 index 00000000..6ac337f1 --- /dev/null +++ b/Emerald/UserControls/ArgumentsListView.xaml.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System.Collections.ObjectModel; +using SS = Emerald.Helpers.Settings.SettingsSystem; +using Emerald.Models; + + +namespace Emerald.UserControls; + + //Copied from Emerald.UWP + public sealed partial class ArgumentsListView : UserControl + { + private int count = 0; + public ArgumentsListView() + { + InitializeComponent(); + view.ItemsSource = Source; + UpdateSource(); + } + + private ObservableCollection<ArgTemplate> Source = new(); + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + count++; + var r = new ArgTemplate { Arg = "", Count = count }; + Source.Add(r); + UpdateMainSource(); + view.SelectedItem = r; + } + public void UpdateSource() + { + Source.Clear(); + if (SS.Settings.Minecraft.JVM.Arguments != null) + { + foreach (var item in SS.Settings.Minecraft.JVM.Arguments) + { + count++; + var r = new ArgTemplate { Arg = item, Count = count }; + r.PropertyChanged += (_, _) => + { + UpdateMainSource(); + }; + Source.Add(r); + } + } + btnRemove.IsEnabled = Source.Any(); + } + private void UpdateMainSource() => + SS.Settings.Minecraft.JVM.Arguments = Source.Select(x => x.Arg).ToArray(); + + private void btnRemove_Click(object sender, RoutedEventArgs e) + { + foreach (var item in view.SelectedItems) + { + Source.Remove((ArgTemplate)item); + } + UpdateMainSource(); + } + + private void TextBox_PointerPressed(object sender, PointerRoutedEventArgs e) + { + view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count)); + } + + private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count)); + UpdateMainSource(); + } + + private void TextBox_GotFocus(object sender, RoutedEventArgs e) + { + view.SelectedIndex = Source.IndexOf(Source.FirstOrDefault(x => x.Count == ((sender as FrameworkElement).DataContext as ArgTemplate).Count)); + } + + private void view_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + btnRemove.IsEnabled = view.SelectedItems.Any(); + } + } diff --git a/Emerald/UserControls/Titlebar.xaml b/Emerald/UserControls/Titlebar.xaml new file mode 100644 index 00000000..76be090a --- /dev/null +++ b/Emerald/UserControls/Titlebar.xaml @@ -0,0 +1,41 @@ +<UserControl + x:Class="Emerald.UserControls.TitleBar" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:Emerald.UserControls" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + d:DesignHeight="300" + d:DesignWidth="400"> + + + <Grid + Height="48" + VerticalAlignment="Top" + Background="Transparent"> + <StackPanel Orientation="Horizontal"> + <Image + x:Name="AppFontIcon" + Width="24" + Height="24" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Source="/Assets/icon.png" /> + <TextBlock + x:Name="AppTitle" + Margin="8,0,0,0" + VerticalAlignment="Center" + Style="{ThemeResource CaptionTextBlockStyle}" + Text="Emerald" /> + <TextBlock + x:Name="PreviewTitle" + Margin="4,0,0,0" + VerticalAlignment="Center" + FontSize="11" + FontWeight="SemiBold" + Foreground="{ThemeResource TextFillColorSecondaryBrush}" + Text="DEV" /> + </StackPanel> + </Grid> +</UserControl> diff --git a/Emerald/UserControls/Titlebar.xaml.cs b/Emerald/UserControls/Titlebar.xaml.cs new file mode 100644 index 00000000..b134aa33 --- /dev/null +++ b/Emerald/UserControls/Titlebar.xaml.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Emerald.UserControls; +public sealed partial class TitleBar : UserControl +{ + public TitleBar() + { + this.InitializeComponent(); + } +} diff --git a/Emerald/Views/Settings/GeneralPage.xaml b/Emerald/Views/Settings/GeneralPage.xaml new file mode 100644 index 00000000..c998ca54 --- /dev/null +++ b/Emerald/Views/Settings/GeneralPage.xaml @@ -0,0 +1,149 @@ +<Page + x:Class="Emerald.Views.Settings.GeneralPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Emerald.Views.Settings" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" xmlns:Main="using:Emerald" xmlns:SS="using:Emerald.Helpers.Settings" xmlns:helpers="using:Emerald.Helpers" + DataContext="{x:Bind SS:SettingsSystem.Settings}" xmlns:cn="using:CommunityToolkit.WinUI.Controls" xmlns:uc="using:Emerald.UserControls"> + <Grid> + <StackPanel Spacing="16"> + <StackPanel Spacing="4"> + <TextBlock FontWeight="SemiBold" Text="{helpers:Localize Name=GeneralSettings}" /> + <cn:SettingsCard + HeaderIcon="{helpers:FontIcon Glyph=}" + Header="{helpers:Localize Name=MinecraftPath}" + Description="{helpers:Localize Name=MinecraftPathDescription}"> + <HyperlinkButton + ToolTipService.ToolTip="{Binding Minecraft.Path, Mode=OneWay}" + Click="btnChangeMPath_Click"> + <TextBlock + MaxWidth="250" + VerticalAlignment="Center" + Text="{Binding Minecraft.Path, Mode=OneWay}" + TextTrimming="CharacterEllipsis" /> + </HyperlinkButton> + </cn:SettingsCard> + <cn:SettingsCard + HeaderIcon="{helpers:FontIcon Glyph=}" + Header="{helpers:Localize Name=RAM}" + Description="{helpers:Localize Name=RAMDescription}"> + <StackPanel + Orientation="Horizontal" + Spacing="5"> + <TextBlock + x:Name="txtRam" + VerticalAlignment="Center"> + <Run + Text="{Binding Minecraft.RAMinGB, Mode=OneWay}" /> + GB</TextBlock> + <RepeatButton + Padding="5" + Click="btnRamPlus_Click" + Content="{helpers:FontIcon Glyph=}" + FontWeight="SemiBold" /> + <RepeatButton + Padding="5" + Click="btnRamMinus_Click" + Content="{helpers:FontIcon Glyph=}" + FontWeight="SemiBold" /> + </StackPanel> + </cn:SettingsCard> + <cn:SettingsExpander + Header="{helpers:Localize Name=DownloadSettings}" + HeaderIcon="{helpers:FontIcon Glyph=}" + Description="{helpers:Localize Name=DownloadSettingsDescription}"> + <cn:SettingsExpander.Items> + <cn:SettingsCard + Header="{helpers:Localize Name=AssetsCheck}"> + <ToggleSwitch + IsOn="{Binding Minecraft.Downloader.AssetsCheck, Mode=TwoWay}" /> + </cn:SettingsCard> + <cn:SettingsCard + Header="{helpers:Localize Name=HashCheck}"> + <ToggleSwitch + IsOn="{Binding Minecraft.Downloader.HashCheck, Mode=TwoWay}" /> + </cn:SettingsCard> + </cn:SettingsExpander.Items> + </cn:SettingsExpander> + <cn:SettingsCard + Header="{helpers:Localize Name=AutoLogin}" + Description="{helpers:Localize Name=AutoLoginDescription}" + HeaderIcon="{helpers:FontIcon Glyph=}"> + <ToggleSwitch + IsOn="{Binding App.AutoLogin, Mode=TwoWay}" + OffContent="{helpers:Localize Name=No}" + OnContent="{helpers:Localize Name=Yes}" /> + </cn:SettingsCard> + </StackPanel> + <StackPanel + Spacing="4"> + <TextBlock + FontWeight="SemiBold" + Text="{helpers:Localize Name=AdvancedMinecraftSettings}" /> + <cn:SettingsCard + Header="{helpers:Localize Name=MCasAdmin}" + Description="{helpers:Localize Name=MCasAdminDescription}" + HeaderIcon="{helpers:FontIcon Glyph=}"> + <ToggleSwitch + IsOn="{Binding Minecraft.IsAdmin, Mode=TwoWay}" + OffContent="{helpers:Localize Name=No}" + OnContent="{helpers:Localize Name=Yes}" /> + </cn:SettingsCard> + <cn:SettingsCard + Header="{helpers:Localize Name=GameLogs}" + Description="{helpers:Localize Name=GameLogsDescription}" + HeaderIcon="{helpers:FontIcon Glyph=}"> + <ToggleSwitch + IsOn="{Binding Minecraft.JVM.GameLogs, Mode=TwoWay}" + OffContent="{helpers:Localize Name=No}" + OnContent="{helpers:Localize Name=Yes}" /> + </cn:SettingsCard> + <cn:SettingsExpander + Header="{helpers:Localize Name=MCWindowsSize}" + Description="{helpers:Localize Name=MCWindowsSizeDescription}" + HeaderIcon="{helpers:FontIcon Glyph=}"> + <cn:SettingsExpander.Content> + <TextBlock + Text="{Binding Minecraft.JVM.ScreenSizeStatus, Mode=OneWay}" /> + </cn:SettingsExpander.Content> + <cn:SettingsExpander.Items> + <cn:SettingsCard + Header="{helpers:Localize Name=Width}"> + <NumberBox + Minimum="0" + Value="{Binding Minecraft.JVM.ScreenWidth, Mode=TwoWay}" + SpinButtonPlacementMode="Inline" + LargeChange="80" + SmallChange="10" + PlaceholderText="{helpers:Localize Name=Default}" /> + </cn:SettingsCard> + <cn:SettingsCard + Header="{helpers:Localize Name=Height}"> + <NumberBox + Minimum="0" + Value="{Binding Minecraft.JVM.ScreenHeight, Mode=TwoWay}" + SpinButtonPlacementMode="Inline" + LargeChange="80" + SmallChange="10" + PlaceholderText="{helpers:Localize Name=Default}" /> + </cn:SettingsCard> + <cn:SettingsCard + Header="{helpers:Localize Name=FullScreen}"> + <ToggleSwitch + IsOn="{Binding Minecraft.JVM.FullScreen, Mode=TwoWay}" /> + </cn:SettingsCard> + </cn:SettingsExpander.Items> + </cn:SettingsExpander> + <cn:SettingsExpander + Header="{helpers:Localize Name=JVMArgs}" + Description="{helpers:Localize Name=JVMArgsDescription}" + HeaderIcon="{helpers:FontIcon Glyph=}"> + <cn:SettingsExpander.ItemsHeader> + <Grid + Padding="12" + Background="{StaticResource ExpanderHeaderBackground}"> + <uc:ArgumentsListView /> + </Grid> + </cn:SettingsExpander.ItemsHeader> + </cn:SettingsExpander> + </StackPanel> + </StackPanel> + </Grid> +</Page> diff --git a/Emerald/Views/Settings/GeneralPage.xaml.cs b/Emerald/Views/Settings/GeneralPage.xaml.cs new file mode 100644 index 00000000..5f0e5161 --- /dev/null +++ b/Emerald/Views/Settings/GeneralPage.xaml.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using CmlLib.Core; +using CommunityToolkit.WinUI.Controls; +using Emerald.Helpers; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Storage.Pickers; + +using SS = Emerald.Helpers.Settings.SettingsSystem; +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace Emerald.Views.Settings; +/// <summary> +/// An empty page that can be used on its own or navigated to within a Frame. +/// </summary> +public sealed partial class GeneralPage : Page +{ + public GeneralPage() + { + this.InitializeComponent(); + } + private void btnChangeMPath_Click(object sender, RoutedEventArgs e) + { + //MinecraftPath mcP; + //string path = ""; + //async void Start() + //{ + // var fop = new FolderPicker + // { + // CommitButtonText = "Select".Localize() + // }; + // WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current.MainWindow)); + // var f = await fop.PickSingleFolderAsync(); + + // if (f != null) + // path = f.Path; + // else + // return; + + // Try(); + + // async void Try() + // { + // try + // { + // mcP = new(path); + // SS.Settings.Minecraft.Path = path; + // App.Current.Launcher.InitializeLauncher(mcP); + // } + // catch + // { + // var r = await MessageBox.Show("Error".Localize(), "MCPathFailed".Localize().Replace("{Path}", path), MessageBoxButtons.Custom, "Yes".Localize(), "SetDifMCPath".Localize()); + // if (r == MessageBoxResults.Yes) + // Try(); + // else + // Start(); + // } + // } + //} + //Start(); + } + + private void btnRamPlus_Click(object sender, RoutedEventArgs e) => + SS.Settings.Minecraft.RAM = SS.Settings.Minecraft.RAM + (SS.Settings.Minecraft.RAM >= DirectResoucres.MaxRAM ? 0 : (DirectResoucres.MaxRAM - SS.Settings.Minecraft.RAM >= 50 ? 50 : DirectResoucres.MaxRAM - SS.Settings.Minecraft.RAM)); + + + private void btnRamMinus_Click(object sender, RoutedEventArgs e) => + SS.Settings.Minecraft.RAM = SS.Settings.Minecraft.RAM - (SS.Settings.Minecraft.RAM <= DirectResoucres.MinRAM ? 0 : 50); + + + private void btnAutoRAM_Click(object sender, RoutedEventArgs e) => + SS.Settings.Minecraft.RAM = DirectResoucres.MaxRAM / 2; +} diff --git a/Emerald/Views/Settings/SettingsPage.xaml b/Emerald/Views/Settings/SettingsPage.xaml new file mode 100644 index 00000000..4d7b56dc --- /dev/null +++ b/Emerald/Views/Settings/SettingsPage.xaml @@ -0,0 +1,68 @@ +<Page + x:Class="Emerald.Views.Settings.SettingsPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:helpers="using:Emerald.Helpers" + xmlns:local="using:Emerald.Views.Settings" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:uc="using:Emerald.UserControls" + mc:Ignorable="d"> + + <Page.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <ResourceDictionary> + <SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" /> + <Thickness x:Key="NavigationViewContentGridBorderThickness">0</Thickness> + <CornerRadius x:Key="NavigationViewContentGridCornerRadius">0</CornerRadius> + <Thickness x:Key="NavigationViewTopPaneHeight">40</Thickness> + </ResourceDictionary> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> + </Page.Resources> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + </Grid.RowDefinitions> + + <NavigationView + x:Name="navView" + Margin="8,-8,8,0" + Background="Transparent" + IsBackButtonVisible="Collapsed" + IsSettingsVisible="False" + ItemInvoked="navView_ItemInvoked" + PaneDisplayMode="Top"> + + <NavigationView.MenuItems> + <NavigationViewItem + Icon="{helpers:FontIcon Glyph=}" + Content="{helpers:Localize Name=General}" + IsSelected="True" + Tag="General" /> + <NavigationViewItem + Icon="{helpers:FontIcon Glyph=}" + Content="{helpers:Localize Name=Appearance}" + Tag="Appearance" /> + <NavigationViewItem + Icon="{helpers:FontIcon Glyph=}" + Content="{helpers:Localize Name=About}" + Tag="About" /> + </NavigationView.MenuItems> + + </NavigationView> + + <Grid + Grid.Row="1" + Background="{ThemeResource LayerFillColorDefaultBrush}" + BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" + BorderThickness="0,1,0,0"> + <ScrollViewer> + <Frame Padding="16" x:Name="contentframe" /> + </ScrollViewer> + </Grid> + </Grid> +</Page> diff --git a/Emerald/Views/Settings/SettingsPage.xaml.cs b/Emerald/Views/Settings/SettingsPage.xaml.cs new file mode 100644 index 00000000..4720b56c --- /dev/null +++ b/Emerald/Views/Settings/SettingsPage.xaml.cs @@ -0,0 +1,44 @@ +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; +using System; + +namespace Emerald.Views.Settings; + +public sealed partial class SettingsPage : Page +{ + public SettingsPage() + { + InitializeComponent(); + + //Loaded += (_, _) => Navigate(navView.SelectedItem as NavigationViewItem); + } + + private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + Navigate(navView.SelectedItem as NavigationViewItem); + } + + private void Navigate(NavigationViewItem itm) + { + switch (itm.Tag) + { + case "Appearance": + NavigateOnce(typeof(SettingsPage)); + break; + case "About": + NavigateOnce(typeof(SettingsPage)); + break; + default: + NavigateOnce(typeof(GeneralPage)); + break; + } + } + + private void NavigateOnce(Type type) + { + if (contentframe.Content == null || contentframe.Content.GetType() != type) + { + contentframe.Navigate(type, null, new DrillInNavigationTransitionInfo()); + } + } +} diff --git a/Emerald/app.manifest b/Emerald/app.manifest new file mode 100644 index 00000000..7c69768c --- /dev/null +++ b/Emerald/app.manifest @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="Emerald.Windows.app"/> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8. + For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 + + It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.--> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <!-- The combination of below two tags have the following effect: + 1) Per-Monitor for >= Windows 10 Anniversary Update + 2) System < Windows 10 Anniversary Update + --> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness> + </windowsSettings> + </application> +</assembly> diff --git a/README.md b/README.md index f7139fd5..861c3709 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ There are multiple ways to participate in the community: ├──Emerald.App // Emerald app code and packager | ├──Emerald.App // Emerald app code (such as code related to UI but not Minecraft) | └──Emerald.App.Package // Package code for generating an uploadable MSIX bundle. -└──Emerald.Core // Emerald core code (such as code related to launching and modifying Minecraft +├──Emerald.Core // Emerald core code (such as code related to launching and modifying Minecraft) +└──Emerald.CoreX // Emerald core code for the ability to run different Minecraft installation profiles and mods. ``` ## 🔨 Building the Code diff --git a/global.json b/global.json new file mode 100644 index 00000000..d0de00bf --- /dev/null +++ b/global.json @@ -0,0 +1,9 @@ +{ + // To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information. + "msbuild-sdks": { + "Uno.Sdk": "5.5.49" + }, + "sdk": { + "allowPrerelease": false + } +}