Просмотр исходного кода

Ava UI: Avalonia 11 & FluentAvalonia 2 Support (#4362)

* It builds

(Doesn’t run waiting on FluentAvalonia Preview 5 Release)

* Enable CompiledBindings by default

* Ignore `PointerPressedEventArgs` Init warning

* Define MIME and UTI Types

* Update `UserProfileImageSelectorView` to StorageProvider API

* PFS0 Magic

* Update `MainWindowViewModel` to StorageProvider API

* Update `SettingsUIView` to StorageProvider API

* Update `ApplicationHelper` to StorageProvider API

* Use `IsCheckChanged`

* Rename events

* Update Fluent Avalonia to Preivew 5

* More package updates

* Fix long selection bar

* return glyph value directly, instead of using a binding

* fix menu item checkboxes

* Fix build

* Update to Preview 6

Unicorn conflict

Fix remaining package oopsie

* Fix issues from merge

* Fix some warnings

* Warnings

* Squashed commit of the following:

commit 79d1c190dba48e405a833f654691e47509a29792
Author: Mary <mary@mary.zone>
Date:   Sun Apr 16 11:38:07 2023 +0200

    chore: Update Silk.NET to 2.17.1 (#4686)

commit 2bc88467eb377a0ca1a8b51700300422422c8c37
Author: Ac_K <Acoustik666@gmail.com>
Date:   Sun Apr 16 09:37:31 2023 +0000

    Update README.md

commit baf8752e74488a419074ae1d484e54a00bc01973
Author: Vincenzo Nizza <vincenzonizzaufficio@gmail.com>
Date:   Sun Apr 16 11:19:33 2023 +0200

    Ensure the updater doesn't delete hidden or system files (#4626)

    * Copy desktop.ini to update directory if it exists in HomeDir

    * EnumerateFilesToDelete() exclude files with "Hidden" and "System" attributes

commit d5e4378aea086d9219f890e33cf81d566d96b9ae
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Date:   Sun Apr 16 09:02:06 2023 +0000

    nuget: bump DynamicData from 7.13.1 to 7.13.5 (#4654)

    Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.1 to 7.13.5.
    - [Release notes](https://github.com/reactiveui/DynamicData/releases)
    - [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
    - [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.1...7.13.5)

    ---
    updated-dependencies:
    - dependency-name: DynamicData
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...

    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

commit 6dbcdfea47e60aadefd59a75e43549793481f853
Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date:   Sun Apr 16 09:09:02 2023 +0200

    Ava: Fix nca extraction window never closing & minor cleanup (#4569)

    * ava: Remove unused doWhileDeferred parameters

    * ava: Minimally improve swkbd dialog

    It's currently impossible to get the dialog to redirect focus to the InputBox.

    * ava: Fix nca extraction dialog never closing

    Also contains some minor cleanup

commit c5258cf082b10f335f81487f22b7eeb86075e09e
Author: NitroTears <73270647+NitroTears@users.noreply.github.com>
Date:   Sun Apr 16 11:03:35 2023 +1000

    Ability to hide file types in Game List (#4555)

    * Added HiddenFileTypes to config state, and check to file enumeration

    * Added hiddenfiletypes checkboxes to the UI

    * Added Ava version of HiddenFileTypes

    * Inverted Hide to Show with file types, minor formatting

    * all variables with a reference to 'hidden' is now 'shown'

    * one more variable name changed

    * review feedback

    * added FileTypes extension methof to get the correlating config value

    * moved extension method to new folder and file in Ryujinx.Ui.Common

    * added default case for ToggleFileType

    * changed exception type to OutOfRangeException

commit 5c89e22bb98072adc240c2eb2d26d25fa119fe7d
Author: Daniel Shala <daniel.shala08@gmail.com>
Date:   Sat Apr 15 18:11:24 2023 +0200

    Added check for eventual symlink when displaying game files. (#4526)

    * Added check for eventual symlink when displaying game files.

    * Moved symlink check logic

    * Moved symlink check logic

    * Fixed prev commit

    ---------

    Co-authored-by: Daniel Shala <danielshala00@gmail.com>

commit 11ecff2ff04633d261b9a43db792f6438df63f40
Author: Alex Barney <thealexbarney@gmail.com>
Date:   Fri Apr 14 16:00:34 2023 -0700

    Rename Hipc to Cmif where appropriate (#3880)

commit 4c3f09644a033dbf70258c4c0e5a848263b16bbd
Author: MutantAura <44103205+MutantAura@users.noreply.github.com>
Date:   Wed Apr 12 20:18:40 2023 +0100

    Move swkbd message null check into constructor (#4671)

commit e187a8870a6f19ac0a85b08aece3c1a1e196e379
Author: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date:   Wed Apr 12 03:09:47 2023 +0200

    HLE: Deal with empty title names properly (#4643)

    * hle: Deal with empty titleNames in some languages

    * gui: Fix displaying the wrong title name

    * Remove unnecessary bounds check

    * Fix a NRE when getting the version string

    * Restore empty string logic

commit a64fee29dc6b8e523d61abb7e79ceaa95a558c6c
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 08:23:41 2023 +0100

    Vulkan: add situational "Fast Flush" mode (#4667)

    * Flush in the middle of long command buffers.

    * Vulkan: add situational "Fast Flush" mode

    The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed.

    However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync.

    The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual.

    The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough.

    This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue.

    Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general).

    * Add conversions in milliseconds next to flush timers.

commit 9ef94c8292beda825fa76e05ad2e561c6d571c95
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 07:55:04 2023 +0100

    ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661)

    * ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext

    Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition.

    Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile.

    * Remove unreachable code.

    * Add ulong conversion for offsets

    * Nit

commit 915d6d044cbf8c89935f14b8c7e085ad729f0e28
Author: riperiperi <rhy3756547@hotmail.com>
Date:   Tue Apr 11 07:32:31 2023 +0100

    OpenGL: Fix OBS/Overlays again by binding FB before present (#4668)

    This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled.

    I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome.

commit a4780ab33b9ca6b698917ded3ef6db6e6716cad1
Author: MutantAura <44103205+MutantAura@users.noreply.github.com>
Date:   Mon Apr 10 23:04:31 2023 +0100

    Force activate parent window before dialog is shown (#4663)

* Fix build

Extraction dialogue not working

* Avalonia Preview 7

Needs Fluent Avalonia update still…

* Fix Render Scaling

* Update Fluent Avalonia

* Remove `pfs0` as runnable file type

* Restore Info.plist formatting

* Plist Format

* Update Avalonia.Svg.Skia

* Update theme code (TODO)

* swtich to using theme variants  for light dark

* Fix crashes

* Text centering issues

* Update `TitleUpdateViewModel` to StorageProvider API

* Fixed for new PR

(Will crash on launch)

* Fixes…

* UI: Fix sections extraction (#4820)

* UI: Fix sections extraction

There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check.
I have fixed the inverted handler of ExeFs/Logo introduced in #4755.

Fixes #4521

* Addresses feedback

* Fix issues…

* Preview 8

* Fix fuck ups

* Fixes

* More cleanup

* Ava 11 RC

Maybe there is a god

* Update FluentAvalonia

* update svg

* Second RC (kill me)

* It builds

* Ava 11

* Remove unnecessary usings

* Fix build

* Formatting

* GAS GAS GAS!!!!

* Fix DLC Window Crash

* Linux runner try not to crash challenge (impossible)

* Add app.manifest

* Fix accidental Silk.NET.Vulkan bump

* Try fix truncation

* Linux fix popup Windows

* Fix cutoff text on windows

* Status bar styling fixes

* Volume Toggle Split Button Fixes

* Fix load bar color

* Fix shortcuts

* Best we're gonna get

* Fix spacing

Co-authored-by: Exhigh <exhigh01@gmail.com>

* Formatting

* Fix Profile Dropdown

* Fix Window Startup Position

* Format Fixes

* Fix stupid mistake

* Fix accidental change

* Scaling Handler (peri pls make sure is working)

* Remove Locale Reflection Binding Use + Unsued Usings

* Fix formatting

Code styling

Ughhhh

Fix interface

Make TimeZoneConverter internal

* Remove bell workaround (no longer needed)

* Disable accent menu

* Update to Ava 11.0.2

* Peri suggestions

* Formatting

* Cleanup a bunch of jank

* Dependency update

* Berry fixes and suggestions

* Final suggestions

* Rename assemblyIdentity to Ryujinx.Emulator.Avalonia

---------

Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: Exhigh <exhigh01@gmail.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Isaac Marovitz 2 лет назад
Родитель
Сommit
7b2225c6b0
75 измененных файлов с 1219 добавлено и 1048 удалено
  1. 9 10
      Directory.Packages.props
  2. 107 2
      distribution/macos/Info.plist
  3. 8 0
      src/Ryujinx.Ava/App.axaml
  4. 11 28
      src/Ryujinx.Ava/App.axaml.cs
  5. 20 21
      src/Ryujinx.Ava/AppHost.cs
  6. 0 65
      src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml
  7. 0 57
      src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml
  8. 228 133
      src/Ryujinx.Ava/Assets/Styles/Styles.xaml
  9. 85 0
      src/Ryujinx.Ava/Assets/Styles/Themes.xaml
  10. 114 111
      src/Ryujinx.Ava/Common/ApplicationHelper.cs
  11. 0 7
      src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs
  12. 3 6
      src/Ryujinx.Ava/Program.cs
  13. 5 8
      src/Ryujinx.Ava/Ryujinx.Ava.csproj
  14. 1 1
      src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml
  15. 1 9
      src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs
  16. 3 3
      src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml
  17. 3 1
      src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml
  18. 15 3
      src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs
  19. 12 10
      src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml
  20. 1 7
      src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs
  21. 10 8
      src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
  22. 1 7
      src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs
  23. 1 9
      src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs
  24. 0 52
      src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs
  25. 0 15
      src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs
  26. 0 1
      src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs
  27. 28 0
      src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs
  28. 2 2
      src/Ryujinx.Ava/UI/Models/UserProfile.cs
  29. 8 6
      src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
  30. 5 5
      src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
  31. 8 11
      src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
  32. 19 17
      src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
  33. 115 100
      src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
  34. 19 18
      src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
  35. 9 10
      src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml
  36. 35 34
      src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs
  37. 0 1
      src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml
  38. 0 1
      src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml
  39. 64 25
      src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml
  40. 3 3
      src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
  41. 28 11
      src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml
  42. 6 0
      src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs
  43. 6 7
      src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
  44. 1 2
      src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml
  45. 0 1
      src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml
  46. 2 3
      src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml
  47. 2 3
      src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml
  48. 1 1
      src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs
  49. 0 1
      src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml
  50. 0 1
      src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml
  51. 1 2
      src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml
  52. 71 39
      src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml
  53. 2 17
      src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs
  54. 1 2
      src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml
  55. 23 17
      src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs
  56. 0 1
      src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml
  57. 2 3
      src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml
  58. 0 1
      src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml
  59. 16 22
      src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs
  60. 1 2
      src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml
  61. 2 4
      src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml
  62. 4 5
      src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml
  63. 3 4
      src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml
  64. 8 7
      src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml
  65. 6 6
      src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml
  66. 2 7
      src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs
  67. 3 5
      src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml
  68. 10 10
      src/Ryujinx.Ava/UI/Windows/MainWindow.axaml
  69. 33 32
      src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs
  70. 14 15
      src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml
  71. 3 3
      src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs
  72. 2 2
      src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs
  73. 2 4
      src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml
  74. 1 1
      src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs
  75. 10 0
      src/Ryujinx.Ava/app.manifest

+ 9 - 10
Directory.Packages.props

@@ -3,21 +3,21 @@
     <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
   </PropertyGroup>
   <ItemGroup>
-    <PackageVersion Include="Avalonia" Version="0.10.21" />
-    <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
-    <PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
-    <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
-    <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
-    <PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
-    <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
+    <PackageVersion Include="Avalonia" Version="11.0.3" />
+    <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.3" />
+    <PackageVersion Include="Avalonia.Desktop" Version="11.0.3" />
+    <PackageVersion Include="Avalonia.Diagnostics" Version="11.0.3" />
+    <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.3" />
+    <PackageVersion Include="Avalonia.Svg" Version="11.0.0" />
+    <PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0" />
     <PackageVersion Include="CommandLineParser" Version="2.9.1" />
     <PackageVersion Include="Concentus" Version="1.1.7" />
     <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
     <PackageVersion Include="DynamicData" Version="7.14.2" />
-    <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
+    <PackageVersion Include="FluentAvaloniaUI" Version="2.0.1" />
     <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
     <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
-    <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
+    <PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
     <PackageVersion Include="LibHac" Version="0.18.0" />
     <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
     <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
@@ -48,6 +48,5 @@
     <PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
     <PackageVersion Include="System.Management" Version="7.0.2" />
     <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
-    <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
   </ItemGroup>
 </Project>

+ 107 - 2
distribution/macos/Info.plist

@@ -44,10 +44,115 @@
     <string>public.app-category.games</string>
     <key>LSMinimumSystemVersion</key>
     <string>11.0</string>
+    <key>UTExportedTypeDeclarations</key>
+    <array>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Extensible Application Markup Language</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.xml</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.xaml</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>xaml</string>
+          </array>
+        </dict>
+      </dict>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Nintendo Submission Package</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.data</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.nsp</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>nsp</string>
+          </array>
+        </dict>
+      </dict>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Nintendo Switch Cartridge</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.data</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.xci</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>xci</string>
+          </array>
+        </dict>
+      </dict>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Nintendo Content Archive</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.data</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.nca</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>nca</string>
+          </array>
+        </dict>
+      </dict>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Nintendo Relocatable Object</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.data</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.nro</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>nro</string>
+          </array>
+        </dict>
+      </dict>
+      <dict>
+        <key>UTTypeDescription</key>
+        <string>Nintendo Shared Object</string>
+        <key>UTTypeConformsTo</key>
+        <array>
+          <string>public.data</string>
+        </array>
+        <key>UTTypeIdentifier</key>
+        <string>com.ryujinx.nso</string>
+        <key>UTTypeTagSpecification</key>
+        <dict>
+          <key>public.filename-extension</key>
+          <array>
+            <string>nso</string>
+          </array>
+        </dict>
+      </dict>
+    </array>
     <key>LSEnvironment</key>
     <dict>
-        <key>COMPlus_DefaultStackSize</key>
+        <key>DOTNET_DefaultStackSize</key>
         <string>200000</string>
     </dict>
 </dict>
-</plist>
+</plist>

+ 8 - 0
src/Ryujinx.Ava/App.axaml

@@ -3,7 +3,15 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:sty="using:FluentAvalonia.Styling">
+    <Application.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <MergeResourceInclude Source="/Assets/Styles/Themes.xaml"/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Application.Resources>
     <Application.Styles>
         <sty:FluentAvaloniaTheme PreferSystemTheme="False" />
+        <StyleInclude Source="/Assets/Styles/Styles.xaml"/>
     </Application.Styles>
 </Application>

+ 11 - 28
src/Ryujinx.Ava/App.axaml.cs

@@ -3,7 +3,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 using Avalonia.Styling;
 using Avalonia.Threading;
-using FluentAvalonia.Styling;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Windows;
@@ -24,6 +23,11 @@ namespace Ryujinx.Ava
             Name = $"Ryujinx {Program.Version}";
 
             AvaloniaXamlLoader.Load(this);
+
+            if (OperatingSystem.IsMacOS())
+            {
+                Process.Start("/usr/bin/defaults", "write org.ryujinx.Ryujinx ApplePressAndHoldEnabled -bool false");
+            }
         }
 
         public override void OnFrameworkInitializationCompleted()
@@ -89,8 +93,6 @@ namespace Ryujinx.Ava
                 string themePath = ConfigurationState.Instance.Ui.CustomThemePath;
                 bool enableCustomTheme = ConfigurationState.Instance.Ui.EnableCustomTheme;
 
-                const string BaseStyleUrl = "avares://Ryujinx.Ava/Assets/Styles/Base{0}.xaml";
-
                 if (string.IsNullOrWhiteSpace(baseStyle))
                 {
                     ConfigurationState.Instance.Ui.BaseStyle.Value = "Dark";
@@ -98,31 +100,12 @@ namespace Ryujinx.Ava
                     baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
                 }
 
-                var theme = AvaloniaLocator.Current.GetService<FluentAvaloniaTheme>();
-
-                theme.RequestedTheme = baseStyle;
-
-                var currentStyles = this.Styles;
-
-                // Remove all styles except the base style.
-                if (currentStyles.Count > 1)
+                RequestedThemeVariant = baseStyle switch
                 {
-                    currentStyles.RemoveRange(1, currentStyles.Count - 1);
-                }
-
-                IStyle newStyles = null;
-
-                // Load requested style, and fallback to Dark theme if loading failed.
-                try
-                {
-                    newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, baseStyle), UriKind.Absolute));
-                }
-                catch (XamlLoadException)
-                {
-                    newStyles = (Styles)AvaloniaXamlLoader.Load(new Uri(string.Format(BaseStyleUrl, "Dark"), UriKind.Absolute));
-                }
-
-                currentStyles.Add(newStyles);
+                    "Light" => ThemeVariant.Light,
+                    "Dark" => ThemeVariant.Dark,
+                    _ => ThemeVariant.Default
+                };
 
                 if (enableCustomTheme)
                 {
@@ -133,7 +116,7 @@ namespace Ryujinx.Ava
                             var themeContent = File.ReadAllText(themePath);
                             var customStyle = AvaloniaRuntimeXamlLoader.Parse<IStyle>(themeContent);
 
-                            currentStyles.Add(customStyle);
+                            Styles.Add(customStyle);
                         }
                         catch (Exception ex)
                         {

+ 20 - 21
src/Ryujinx.Ava/AppHost.cs

@@ -3,6 +3,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Input;
+using Avalonia.Rendering;
 using Avalonia.Threading;
 using LibHac.Tools.FsSystem;
 using Ryujinx.Audio.Backends.Dummy;
@@ -54,6 +55,7 @@ using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
 using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
 using Image = SixLabors.ImageSharp.Image;
 using InputManager = Ryujinx.Input.HLE.InputManager;
+using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
 using Key = Ryujinx.Input.Key;
 using MouseButton = Ryujinx.Input.MouseButton;
 using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
@@ -167,9 +169,9 @@ namespace Ryujinx.Ava
 
             ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed;
 
-            _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved;
-            _topLevel.PointerEnter += TopLevel_PointerEnterOrMoved;
-            _topLevel.PointerLeave += TopLevel_PointerLeave;
+            _topLevel.PointerMoved += TopLevel_PointerEnteredOrMoved;
+            _topLevel.PointerEntered += TopLevel_PointerEnteredOrMoved;
+            _topLevel.PointerExited += TopLevel_PointerExited;
 
             if (OperatingSystem.IsWindows())
             {
@@ -194,26 +196,23 @@ namespace Ryujinx.Ava
             _gpuDoneEvent = new ManualResetEvent(false);
         }
 
-        private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
+        private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e)
         {
             if (sender is MainWindow window)
             {
                 _lastCursorMoveTime = Stopwatch.GetTimestamp();
 
-                if (RendererHost.EmbeddedWindow.TransformedBounds != null)
-                {
-                    var point = e.GetCurrentPoint(window).Position;
-                    var bounds = RendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
+                var point = e.GetCurrentPoint(window).Position;
+                var bounds = RendererHost.EmbeddedWindow.Bounds;
 
-                    _isCursorInRenderer = point.X >= bounds.X &&
-                                          point.X <= bounds.Width + bounds.X &&
-                                          point.Y >= bounds.Y &&
-                                          point.Y <= bounds.Height + bounds.Y;
-                }
+                _isCursorInRenderer = point.X >= bounds.X &&
+                                      point.X <= bounds.Width + bounds.X &&
+                                      point.Y >= bounds.Y &&
+                                      point.Y <= bounds.Height + bounds.Y;
             }
         }
 
-        private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
+        private void TopLevel_PointerExited(object sender, PointerEventArgs e)
         {
             _isCursorInRenderer = false;
         }
@@ -265,7 +264,7 @@ namespace Ryujinx.Ava
         {
             if (_renderer != null)
             {
-                double scale = _topLevel.PlatformImpl.RenderScaling;
+                double scale = _topLevel.RenderScaling;
 
                 _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
             }
@@ -359,7 +358,7 @@ namespace Ryujinx.Ava
 
             _viewModel.SetUiProgressHandlers(Device);
 
-            RendererHost.SizeChanged += Window_SizeChanged;
+            RendererHost.BoundsChanged += Window_BoundsChanged;
 
             _isActive = true;
 
@@ -469,9 +468,9 @@ namespace Ryujinx.Ava
             ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
             ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough;
 
-            _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
-            _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
-            _topLevel.PointerLeave -= TopLevel_PointerLeave;
+            _topLevel.PointerMoved -= TopLevel_PointerEnteredOrMoved;
+            _topLevel.PointerEntered -= TopLevel_PointerEnteredOrMoved;
+            _topLevel.PointerExited -= TopLevel_PointerExited;
 
             _gpuCancellationTokenSource.Cancel();
             _gpuCancellationTokenSource.Dispose();
@@ -849,7 +848,7 @@ namespace Ryujinx.Ava
             return deviceDriver;
         }
 
-        private void Window_SizeChanged(object sender, Size e)
+        private void Window_BoundsChanged(object sender, Size e)
         {
             Width = (int)e.Width;
             Height = (int)e.Height;
@@ -899,7 +898,7 @@ namespace Ryujinx.Ava
             Width = (int)RendererHost.Bounds.Width;
             Height = (int)RendererHost.Bounds.Height;
 
-            _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
+            _renderer.Window.SetSize((int)(Width * _topLevel.RenderScaling), (int)(Height * _topLevel.RenderScaling));
 
             _chrono.Start();
 

+ 0 - 65
src/Ryujinx.Ava/Assets/Styles/BaseDark.xaml

@@ -1,65 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-    <StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
-    <Design.PreviewWith>
-        <Border Height="2000" Padding="20">
-            <StackPanel Spacing="5">
-                <TextBlock Text="Code Font Family" />
-                <Grid RowDefinitions="*,Auto">
-                    <Menu Grid.Row="1" Width="100">
-                        <MenuItem Header="File">
-                            <MenuItem Header="Test 1" />
-                            <MenuItem Header="Test 2" />
-                            <MenuItem Header="Test 3">
-                                <MenuItem.Icon>
-                                    <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
-                                </MenuItem.Icon>
-                            </MenuItem>
-                        </MenuItem>
-                    </Menu>
-                    <StackPanel Orientation="Horizontal">
-                        <Button
-                            Name="btnAdd"
-                            HorizontalAlignment="Right"
-                            Content="Add" />
-                        <Button
-                            Name="btnRem"
-                            HorizontalAlignment="Right"
-                            Content="Add" />
-                        <TextBox
-                            Width="100"
-                            VerticalAlignment="Center"
-                            Text="Rrrrr"
-                            UseFloatingWatermark="True"
-                            Watermark="Hello" />
-                        <CheckBox>Test Check</CheckBox>
-                    </StackPanel>
-                </Grid>
-            </StackPanel>
-        </Border>
-    </Design.PreviewWith>
-    <Styles.Resources>
-        <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
-        <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
-        <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
-        <Color x:Key="ControlFillColorSecondary">#008AA8</Color>
-        <SolidColorBrush x:Key="ControlFillColorSecondaryBrush" Color="{StaticResource ControlFillColorSecondary}" />
-        <StaticResource x:Key="ButtonBackgroundPointerOver" ResourceKey="ControlFillColorSecondaryBrush" />
-        <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
-        <Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
-        <Color x:Key="SystemAccentColorDark3">#FF00525E</Color>
-        <Color x:Key="SystemAccentColorLight1">#FF00dbff</Color>
-        <Color x:Key="SystemAccentColorLight2">#FF19dfff</Color>
-        <Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color>
-        <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
-        <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
-        <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
-        <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
-        <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
-        <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
-        <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
-        <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
-        <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
-        <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
-    </Styles.Resources>
-</Styles>

+ 0 - 57
src/Ryujinx.Ava/Assets/Styles/BaseLight.xaml

@@ -1,57 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-    <StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
-    <Design.PreviewWith>
-        <Border Height="2000" Padding="20">
-            <StackPanel Spacing="5">
-                <TextBlock Text="Code Font Family" />
-                <Grid RowDefinitions="*,Auto">
-                    <Menu Grid.Row="1" Width="100">
-                        <MenuItem Header="File">
-                            <MenuItem Header="Test 1" />
-                            <MenuItem Header="Test 2" />
-                            <MenuItem Header="Test 3">
-                                <MenuItem.Icon>
-                                    <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
-                                </MenuItem.Icon>
-                            </MenuItem>
-                        </MenuItem>
-                    </Menu>
-                    <StackPanel Orientation="Horizontal">
-                        <Button
-                            Name="btnAdd"
-                            HorizontalAlignment="Right"
-                            Content="Add" />
-                        <Button
-                            Name="btnRem"
-                            HorizontalAlignment="Right"
-                            Content="Add" />
-                        <TextBox
-                            Width="100"
-                            VerticalAlignment="Center"
-                            Text="Rrrrr"
-                            UseFloatingWatermark="True"
-                            Watermark="Hello" />
-                        <CheckBox>Test Check</CheckBox>
-                    </StackPanel>
-                </Grid>
-            </StackPanel>
-        </Border>
-    </Design.PreviewWith>
-    <Styles.Resources>
-        <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
-        <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
-        <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
-        <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
-        <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
-        <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
-        <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
-        <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
-        <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
-        <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
-        <Color x:Key="ThemeForegroundColor">#FF000000</Color>
-        <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
-        <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
-        <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
-        <Color x:Key="SecondaryTextColor">#A0000000</Color>
-    </Styles.Resources>
-</Styles>

+ 228 - 133
src/Ryujinx.Ava/Assets/Styles/Styles.xaml

@@ -3,17 +3,20 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
     <Design.PreviewWith>
-        <Border Height="2000" Padding="20">
+        <Border Height="2000"
+                Padding="20">
             <StackPanel Spacing="5">
                 <TextBlock Text="Code Font Family" />
                 <Grid RowDefinitions="*,Auto">
-                    <Menu Grid.Row="1" Width="100">
+                    <Menu Grid.Row="1"
+                          Width="100">
                         <MenuItem Header="File">
                             <MenuItem Header="Test 1" />
                             <MenuItem Header="Test 2" />
                             <MenuItem Header="Test 3">
                                 <MenuItem.Icon>
-                                    <CheckBox Margin="0" IsChecked="{Binding Checkbox, Mode=TwoWay}" />
+                                    <CheckBox Margin="0"
+                                              IsChecked="{ReflectionBinding Checkbox, Mode=TwoWay}" />
                                 </MenuItem.Icon>
                             </MenuItem>
                         </MenuItem>
@@ -42,57 +45,80 @@
         </Border>
     </Design.PreviewWith>
     <Style Selector="Border.small">
-        <Setter Property="Width" Value="100" />
+        <Setter Property="Width"
+                Value="100" />
     </Style>
     <Style Selector="Border.normal">
-        <Setter Property="Width" Value="130" />
+        <Setter Property="Width"
+                Value="130" />
     </Style>
     <Style Selector="Border.large">
-        <Setter Property="Width" Value="160" />
+        <Setter Property="Width"
+                Value="160" />
     </Style>
     <Style Selector="Border.huge">
-        <Setter Property="Width" Value="200" />
+        <Setter Property="Width"
+                Value="200" />
     </Style>
     <Style Selector="Border.settings">
-        <Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" />
-        <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
-        <Setter Property="BorderThickness" Value="1" />
-        <Setter Property="CornerRadius" Value="5" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeDarkColor}" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
+        <Setter Property="BorderThickness"
+                Value="1" />
+        <Setter Property="CornerRadius"
+                Value="5" />
     </Style>
     <Style Selector="Image.small">
-        <Setter Property="Width" Value="50" />
+        <Setter Property="Width"
+                Value="50" />
     </Style>
     <Style Selector="Image.normal">
-        <Setter Property="Width" Value="80" />
+        <Setter Property="Width"
+                Value="80" />
     </Style>
     <Style Selector="Image.large">
-        <Setter Property="Width" Value="100" />
+        <Setter Property="Width"
+                Value="100" />
     </Style>
     <Style Selector="Image.huge">
-        <Setter Property="Width" Value="120" />
+        <Setter Property="Width"
+                Value="120" />
     </Style>
     <Style Selector="#TitleBarHost &gt; Image">
-        <Setter Property="Margin" Value="10" />
+        <Setter Property="Margin"
+                Value="10" />
     </Style>
     <Style Selector="#TitleBarHost &gt; Label">
-        <Setter Property="Margin" Value="5" />
-        <Setter Property="FontSize" Value="14" />
+        <Setter Property="Margin"
+                Value="5" />
+        <Setter Property="FontSize"
+                Value="14" />
     </Style>
     <Style Selector="Button.SystemCaption">
-        <Setter Property="MinWidth" Value="10" />
+        <Setter Property="MinWidth"
+                Value="10" />
     </Style>
     <Style Selector="DataGridColumnHeader">
-        <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
-        <Setter Property="HorizontalContentAlignment" Value="Center" />
-        <Setter Property="BorderThickness" Value="1" />
-        <Setter Property="VerticalContentAlignment" Value="Center" />
-        <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
-        <Setter Property="SeparatorBrush" Value="{DynamicResource ThemeControlBorderColor}" />
-        <Setter Property="Padding" Value="5" />
-        <Setter Property="Background" Value="{DynamicResource ThemeContentBackgroundColor}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeForegroundBrush}" />
+        <Setter Property="HorizontalContentAlignment"
+                Value="Center" />
+        <Setter Property="BorderThickness"
+                Value="1" />
+        <Setter Property="VerticalContentAlignment"
+                Value="Center" />
+        <Setter Property="SeparatorBrush"
+                Value="{DynamicResource ThemeControlBorderColor}" />
+        <Setter Property="Padding"
+                Value="5" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeContentBackgroundColor}" />
         <Setter Property="Template">
             <ControlTemplate>
-                <Grid Background="{TemplateBinding Background}" ColumnDefinitions="*,Auto">
+                <Grid Background="{TemplateBinding Background}"
+                      ColumnDefinitions="*,Auto">
                     <Grid
                         Margin="{TemplateBinding Padding}"
                         HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
@@ -122,193 +148,262 @@
         </Setter>
     </Style>
     <Style Selector="DataGrid">
-        <Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" />
-        <Setter Property="AlternatingRowBackground" Value="#00FFFFFF" />
-        <Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
-        <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
-        <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
+        <Setter Property="RowBackground"
+                Value="{DynamicResource ThemeAccentBrush4}" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeBackgroundBrush}" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource ThemeBorderLowColor}" />
+        <Setter Property="BorderThickness"
+                Value="{DynamicResource ThemeBorderThickness}" />
     </Style>
     <Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
-        <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
+        <Setter Property="Fill"
+                Value="{DynamicResource SystemAccentColor}" />
+        <Setter Property="Opacity"
+                Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
     </Style>
     <Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource SystemListLowColor}" />
+        <Setter Property="Fill"
+                Value="{DynamicResource SystemListLowColor}" />
     </Style>
     <Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
-        <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
+        <Setter Property="Fill"
+                Value="{DynamicResource SystemAccentColor}" />
+        <Setter Property="Opacity"
+                Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
     </Style>
     <Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
-        <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
+        <Setter Property="Fill"
+                Value="{DynamicResource SystemAccentColor}" />
+        <Setter Property="Opacity"
+                Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
     </Style>
     <Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource SystemAccentColor}" />
-        <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
+        <Setter Property="Fill"
+                Value="{DynamicResource SystemAccentColor}" />
+        <Setter Property="Opacity"
+                Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
     </Style>
     <Style Selector="DataGridCell">
-        <Setter Property="HorizontalAlignment" Value="Center" />
-        <Setter Property="HorizontalContentAlignment" Value="Center" />
+        <Setter Property="HorizontalAlignment"
+                Value="Center" />
+        <Setter Property="HorizontalContentAlignment"
+                Value="Center" />
     </Style>
     <Style Selector="DataGridCell.Left">
-        <Setter Property="HorizontalAlignment" Value="Left" />
+        <Setter Property="HorizontalAlignment"
+                Value="Left" />
     </Style>
     <Style Selector="CheckBox">
-        <Setter Property="BorderThickness" Value="1" />
+        <Setter Property="BorderThickness"
+                Value="1" />
     </Style>
 
     <Style Selector="MenuItem">
-        <Setter Property="Height" Value="{DynamicResource MenuItemHeight}" />
-        <Setter Property="Padding" Value="{DynamicResource MenuItemPadding}" />
-        <Setter Property="FontSize" Value="12" />
+        <Setter Property="Height"
+                Value="{DynamicResource MenuItemHeight}" />
+        <Setter Property="Padding"
+                Value="{DynamicResource MenuItemPadding}" />
+        <Setter Property="FontSize"
+                Value="12" />
     </Style>
     <Style Selector="MenuItem:selected /template/ Border#root">
-        <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
-        <Setter Property="BorderBrush" Value="{DynamicResource ThemeControlBorderColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeControlBorderColor}" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource ThemeControlBorderColor}" />
     </Style>
     <Style Selector="TabItem > ScrollViewer">
-        <Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
-        <Setter Property="Margin" Value="0,-5,0,0" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeBackgroundColor}" />
+        <Setter Property="Margin"
+                Value="0,-5,0,0" />
     </Style>
     <Style Selector="TabItem > ScrollViewer > Border">
-        <Setter Property="BorderThickness" Value="0,1,0,0" />
-        <Setter Property="Background" Value="{DynamicResource ThemeBackgroundColor}" />
-        <Setter Property="BorderBrush" Value="{DynamicResource HighlightBrush}" />
+        <Setter Property="BorderThickness"
+                Value="0,1,0,0" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeBackgroundColor}" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource HighlightBrush}" />
     </Style>
     <Style Selector="Button">
-        <Setter Property="MinWidth" Value="80" />
+        <Setter Property="MinWidth"
+                Value="80" />
     </Style>
     <Style Selector="ProgressBar /template/ Border#ProgressBarTrack">
-        <Setter Property="IsVisible" Value="False" />
+        <Setter Property="IsVisible"
+                Value="False" />
     </Style>
     <Style Selector="ToggleButton">
-        <Setter Property="Padding" Value="0,-5,0,0" />
+        <Setter Property="Padding"
+                Value="0,-5,0,0" />
     </Style>
     <Style Selector="TabItem">
-        <Setter Property="FontSize" Value="14" />
-        <Setter Property="BorderThickness" Value="0,0,1,0" />
-        <Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" />
-        <Setter Property="Background" Value="{DynamicResource HighlightColor}" />
+        <Setter Property="FontSize"
+                Value="14" />
+        <Setter Property="BorderThickness"
+                Value="0,0,1,0" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource ThemeButtonForegroundColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource SystemAccentColorLight2}" />
     </Style>
     <Style Selector="TabItem:pointerover">
-        <Setter Property="Foreground" Value="{DynamicResource ThemeButtonForegroundColor}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeButtonForegroundColor}" />
     </Style>
     <Style Selector="TabItem:selected">
-        <Setter Property="Background" Value="{DynamicResource HighlightColor}" />
-        <Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource SystemAccentColorLight2}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeBackgroundColor}" />
     </Style>
     <Style Selector="TextBlock">
-        <Setter Property="Margin" Value="{DynamicResource TextMargin}" />
-        <Setter Property="FontSize" Value="{DynamicResource FontSize}" />
-        <Setter Property="VerticalAlignment" Value="Center" />
-        <Setter Property="TextWrapping" Value="WrapWithOverflow" />
+        <Setter Property="Margin"
+                Value="{DynamicResource TextMargin}" />
+        <Setter Property="FontSize"
+                Value="{DynamicResource FontSize}" />
+        <Setter Property="VerticalAlignment"
+                Value="Center" />
+        <Setter Property="TextWrapping"
+                Value="WrapWithOverflow" />
     </Style>
     <Style Selector="TextBlock.h1">
-        <Setter Property="Margin" Value="{DynamicResource TextMargin}" />
-        <Setter Property="VerticalAlignment" Value="Center" />
-        <Setter Property="FontWeight" Value="Bold" />
-        <Setter Property="FontSize" Value="16" />
-        <Setter Property="TextWrapping" Value="WrapWithOverflow" />
+        <Setter Property="Margin"
+                Value="{DynamicResource TextMargin}" />
+        <Setter Property="VerticalAlignment"
+                Value="Center" />
+        <Setter Property="FontWeight"
+                Value="Bold" />
+        <Setter Property="FontSize"
+                Value="16" />
+        <Setter Property="TextWrapping"
+                Value="WrapWithOverflow" />
     </Style>
     <Style Selector="Separator">
-        <Setter Property="Background" Value="{DynamicResource ThemeControlBorderColor}" />
-        <Setter Property="Foreground" Value="{DynamicResource ThemeControlBorderColor}" />
-        <Setter Property="MinHeight" Value="1" />
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeControlBorderColor}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeControlBorderColor}" />
+        <Setter Property="MinHeight"
+                Value="1" />
     </Style>
     <Style Selector=":is(Button).DateTimeFlyoutButtonStyle">
-        <Setter Property="Background" Value="{DynamicResource HighlightColor}" />
-        <Setter Property="Foreground" Value="{DynamicResource ThemeBackgroundColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource SystemAccentColorLight2}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeBackgroundColor}" />
     </Style>
     <Style Selector="DatePickerPresenter">
-        <Setter Property="BorderThickness" Value="1" />
-        <Setter Property="BorderBrush" Value="{DynamicResource ThemeButtonForegroundColor}" />
+        <Setter Property="BorderThickness"
+                Value="1" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource ThemeButtonForegroundColor}" />
     </Style>
     <Style Selector="DataGridCell">
-        <Setter Property="FontSize" Value="14" />
+        <Setter Property="FontSize"
+                Value="14" />
     </Style>
     <Style Selector="CheckBox TextBlock">
-        <Setter Property="Margin" Value="0,5,0,0" />
+        <Setter Property="Margin"
+                Value="0,5,0,0" />
     </Style>
     <Style Selector="ContextMenu">
-        <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
-        <Setter Property="BorderThickness" Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" />
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
+        <Setter Property="BorderThickness"
+                Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" />
     </Style>
     <Style Selector="TextBox">
-        <Setter Property="VerticalContentAlignment" Value="Center" />
+        <Setter Property="VerticalContentAlignment"
+                Value="Center" />
     </Style>
     <Style Selector="TextBox.NumberBoxTextBoxStyle">
-        <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
+        <Setter Property="Foreground"
+                Value="{DynamicResource ThemeForegroundColor}" />
     </Style>
     <Style Selector="ListBox ListBoxItem">
-        <Setter Property="Padding" Value="0" />
-        <Setter Property="Margin" Value="0" />
-        <Setter Property="CornerRadius" Value="5" />
-        <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
-        <Setter Property="BorderThickness" Value="2"/>
+        <Setter Property="Padding"
+                Value="0" />
+        <Setter Property="Margin"
+                Value="0" />
+        <Setter Property="CornerRadius"
+                Value="5" />
+        <Setter Property="Background"
+                Value="{DynamicResource AppListBackgroundColor}" />
+        <Setter Property="BorderThickness"
+                Value="2"/>
         <Style.Animations>
             <Animation Duration="0:0:0.7">
                 <KeyFrame Cue="0%">
-                    <Setter Property="MaxHeight" Value="0" />
-                    <Setter Property="Opacity" Value="0.0" />
+                    <Setter Property="MaxHeight"
+                            Value="0" />
+                    <Setter Property="Opacity"
+                            Value="0.0" />
                 </KeyFrame>
                 <KeyFrame Cue="50%">
-                    <Setter Property="MaxHeight" Value="1000" />
-                    <Setter Property="Opacity" Value="0.3" />
+                    <Setter Property="MaxHeight"
+                            Value="1000" />
+                    <Setter Property="Opacity"
+                            Value="0.3" />
                 </KeyFrame>
                 <KeyFrame Cue="100%">
-                    <Setter Property="MaxHeight" Value="1000" />
-                    <Setter Property="Opacity" Value="1.0" />
+                    <Setter Property="MaxHeight"
+                            Value="1000" />
+                    <Setter Property="Opacity"
+                            Value="1.0" />
                 </KeyFrame>
             </Animation>
         </Style.Animations>
     </Style>
     <Style Selector="ListBox ListBoxItem:selected /template/ ContentPresenter">
-        <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource AppListBackgroundColor}" />
+    </Style>
+    <Style Selector="ListBox">
+        <Setter Property="Background"
+                Value="{DynamicResource ThemeContentBackgroundColor}" />
+    </Style>
+    <Style Selector="FlyoutPresenter, ContextMenu, MenuFlyoutPresenter">
+        <Setter Property="BorderBrush"
+                Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
     </Style>
     <Style Selector="ListBox ListBoxItem:pointerover /template/ ContentPresenter">
-        <Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
+        <Setter Property="Background"
+                Value="{DynamicResource AppListHoverBackgroundColor}" />
     </Style>
     <Styles.Resources>
-        <SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
-        <StaticResource x:Key="ListViewItemBackgroundSelected" ResourceKey="ThemeAccentColorBrush" />
-        <StaticResource x:Key="ListViewItemBackgroundPressed" ResourceKey="SystemAccentColorDark1" />
-        <StaticResource x:Key="ListViewItemBackgroundPointerOver" ResourceKey="SystemAccentColorDark2" />
-        <StaticResource x:Key="ListViewItemBackgroundSelectedPressed" ResourceKey="ThemeAccentColorBrush" />
-        <StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver" ResourceKey="SystemAccentColorDark2" />
+        <SolidColorBrush x:Key="ThemeAccentColorBrush"
+                         Color="{DynamicResource SystemAccentColor}" />
+        <StaticResource x:Key="ListViewItemBackgroundSelected"
+                        ResourceKey="ThemeAccentColorBrush" />
+        <StaticResource x:Key="ListViewItemBackgroundPressed"
+                        ResourceKey="SystemAccentColorDark1" />
+        <StaticResource x:Key="ListViewItemBackgroundPointerOver"
+                        ResourceKey="SystemAccentColorDark2" />
+        <StaticResource x:Key="ListViewItemBackgroundSelectedPressed"
+                        ResourceKey="ThemeAccentColorBrush" />
+        <StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver"
+                        ResourceKey="SystemAccentColorDark2" />
         <SolidColorBrush
             x:Key="DataGridGridLinesBrush"
             Opacity="0.4"
             Color="{DynamicResource SystemBaseMediumLowColor}" />
-        <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
-        <SolidColorBrush x:Key="MenuFlyoutPresenterBorderBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
-        <SolidColorBrush x:Key="FlyoutBorderThemeBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
-        <SolidColorBrush x:Key="ListBoxBackground" Color="{DynamicResource ThemeContentBackgroundColor}" />
-        <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource ThemeForegroundColor}" />
-        <SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}" />
-        <SolidColorBrush x:Key="SplitButtonBackgroundChecked" Color="#00E81123" />
-        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="#00E81123" />
-        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="#00E81123" />
-        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="#00E81123" />
+        <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
+                         Color="{DynamicResource DataGridSelectionColor}" />
+        <SolidColorBrush x:Key="SplitButtonBackgroundChecked"
+                         Color="#00E81123" />
+        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver"
+                         Color="#00E81123" />
+        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed"
+                         Color="#00E81123" />
+        <SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled"
+                         Color="#00E81123" />
         <Thickness x:Key="PageMargin">40 0 40 0</Thickness>
         <Thickness x:Key="Margin">0 5 0 5</Thickness>
         <Thickness x:Key="MenuItemPadding">5 0 5 0</Thickness>
-        <Color x:Key="MenuFlyoutPresenterBorderColor">#00000000</Color>
-        <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
-        <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
-        <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
-        <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
-        <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
-        <Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
-        <Color x:Key="VsyncDisabled">#FFFF4554</Color>
-        <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
-        <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
-        <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
         <x:Double x:Key="ScrollBarThickness">15</x:Double>
         <x:Double x:Key="FontSizeSmall">8</x:Double>
         <x:Double x:Key="FontSizeNormal">10</x:Double>

+ 85 - 0
src/Ryujinx.Ava/Assets/Styles/Themes.xaml

@@ -0,0 +1,85 @@
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    <ResourceDictionary.ThemeDictionaries>
+        <ResourceDictionary x:Key="Default">
+            <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
+                             Color="{DynamicResource DataGridSelectionColor}" />
+            <SolidColorBrush x:Key="ThemeAccentColorBrush"
+                             Color="{DynamicResource SystemAccentColor}" />
+            <SolidColorBrush x:Key="ThemeAccentBrush4"
+                             Color="{DynamicResource ThemeAccentColor4}" />
+            <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
+            <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
+            <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
+            <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
+            <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
+            <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
+            <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
+            <Color x:Key="ThemeForegroundColor">#FF000000</Color>
+            <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
+            <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
+            <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
+            <Color x:Key="SecondaryTextColor">#A0000000</Color>
+            <Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
+            <Color x:Key="VsyncDisabled">#FFFF4554</Color>
+        </ResourceDictionary>
+        <ResourceDictionary x:Key="Light">
+            <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
+                             Color="{DynamicResource DataGridSelectionColor}" />
+            <SolidColorBrush x:Key="ThemeAccentColorBrush"
+                             Color="{DynamicResource SystemAccentColor}" />
+            <SolidColorBrush x:Key="ThemeAccentBrush4"
+                             Color="{DynamicResource ThemeAccentColor4}" />
+            <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
+            <Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
+            <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
+            <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
+            <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
+            <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
+            <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
+            <Color x:Key="ThemeForegroundColor">#FF000000</Color>
+            <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
+            <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
+            <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
+            <Color x:Key="SecondaryTextColor">#A0000000</Color>
+        </ResourceDictionary>
+        <ResourceDictionary x:Key="Dark">
+            <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
+                             Color="{DynamicResource DataGridSelectionColor}" />
+            <SolidColorBrush x:Key="ThemeAccentColorBrush"
+                             Color="{DynamicResource SystemAccentColor}" />
+            <SolidColorBrush x:Key="ThemeAccentBrush4"
+                             Color="{DynamicResource ThemeAccentColor4}" />
+            <Color x:Key="ControlFillColorSecondary">#008AA8</Color>
+            <Color x:Key="SystemAccentColor">#FF00C3E3</Color>
+            <Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
+            <Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
+            <Color x:Key="SystemAccentColorDark3">#FF00525E</Color>
+            <Color x:Key="SystemAccentColorLight1">#FF00dbff</Color>
+            <Color x:Key="SystemAccentColorLight2">#FF19dfff</Color>
+            <Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color>
+            <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
+            <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
+            <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
+            <Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
+            <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
+            <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
+            <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
+            <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
+            <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
+            <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
+        </ResourceDictionary>
+    </ResourceDictionary.ThemeDictionaries>
+</ResourceDictionary>

+ 114 - 111
src/Ryujinx.Ava/Common/ApplicationHelper.cs

@@ -1,5 +1,5 @@
-using Avalonia.Controls;
 using Avalonia.Controls.Notifications;
+using Avalonia.Platform.Storage;
 using Avalonia.Threading;
 using LibHac;
 using LibHac.Account;
@@ -143,14 +143,20 @@ namespace Ryujinx.Ava.Common
             }
         }
 
-        public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
+        public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
         {
-            OpenFolderDialog folderDialog = new()
+            var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
             {
                 Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
-            };
+                AllowMultiple = false
+            });
 
-            string destination = await folderDialog.ShowAsync(_owner);
+            if (result.Count == 0)
+            {
+                return;
+            }
+
+            var destination = result[0].Path.LocalPath;
             var cancellationToken = new CancellationTokenSource();
 
             UpdateWaitWindow waitingDialog = new(
@@ -158,148 +164,145 @@ namespace Ryujinx.Ava.Common
                 LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
                 cancellationToken);
 
-            if (!string.IsNullOrWhiteSpace(destination))
+            Thread extractorThread = new(() =>
             {
-                Thread extractorThread = new(() =>
-                {
-                    Dispatcher.UIThread.Post(waitingDialog.Show);
+                Dispatcher.UIThread.Post(waitingDialog.Show);
 
-                    using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
+                using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
 
-                    Nca mainNca = null;
-                    Nca patchNca = null;
+                Nca mainNca = null;
+                Nca patchNca = null;
 
-                    string extension = Path.GetExtension(titleFilePath).ToLower();
-                    if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
-                    {
-                        PartitionFileSystem pfs;
+                string extension = Path.GetExtension(titleFilePath).ToLower();
+                if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
+                {
+                    PartitionFileSystem pfs;
 
-                        if (extension == ".xci")
-                        {
-                            pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
-                        }
-                        else
-                        {
-                            pfs = new PartitionFileSystem(file.AsStorage());
-                        }
+                    if (extension == ".xci")
+                    {
+                        pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
+                    }
+                    else
+                    {
+                        pfs = new PartitionFileSystem(file.AsStorage());
+                    }
 
-                        foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
-                        {
-                            using var ncaFile = new UniqueRef<IFile>();
+                    foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
+                    {
+                        using var ncaFile = new UniqueRef<IFile>();
 
-                            pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                        pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-                            Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
-                            if (nca.Header.ContentType == NcaContentType.Program)
+                        Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
+                        if (nca.Header.ContentType == NcaContentType.Program)
+                        {
+                            int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+                            if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
+                            {
+                                patchNca = nca;
+                            }
+                            else
                             {
-                                int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
-                                if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
-                                {
-                                    patchNca = nca;
-                                }
-                                else
-                                {
-                                    mainNca = nca;
-                                }
+                                mainNca = nca;
                             }
                         }
                     }
-                    else if (extension == ".nca")
-                    {
-                        mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
-                    }
+                }
+                else if (extension == ".nca")
+                {
+                    mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
+                }
 
-                    if (mainNca == null)
-                    {
-                        Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
+                if (mainNca == null)
+                {
+                    Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
 
-                        Dispatcher.UIThread.InvokeAsync(async () =>
-                        {
-                            waitingDialog.Close();
+                    Dispatcher.UIThread.InvokeAsync(async () =>
+                    {
+                        waitingDialog.Close();
 
-                            await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
-                        });
+                        await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
+                    });
 
-                        return;
-                    }
+                    return;
+                }
 
-                    (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
-                    if (updatePatchNca != null)
-                    {
-                        patchNca = updatePatchNca;
-                    }
+                (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
+                if (updatePatchNca != null)
+                {
+                    patchNca = updatePatchNca;
+                }
 
-                    int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
+                int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
 
-                    try
+                try
+                {
+                    bool sectionExistsInPatch = false;
+                    if (patchNca != null)
                     {
-                        bool sectionExistsInPatch = false;
-                        if (patchNca != null)
-                        {
-                            sectionExistsInPatch = patchNca.CanOpenSection(index);
-                        }
+                        sectionExistsInPatch = patchNca.CanOpenSection(index);
+                    }
 
-                        IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
-                                                                         : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
+                    IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
+                                                                     : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
 
-                        FileSystemClient fsClient = _horizonClient.Fs;
+                    FileSystemClient fsClient = _horizonClient.Fs;
 
-                        string source = DateTime.Now.ToFileTime().ToString()[10..];
-                        string output = DateTime.Now.ToFileTime().ToString()[10..];
+                    string source = DateTime.Now.ToFileTime().ToString()[10..];
+                    string output = DateTime.Now.ToFileTime().ToString()[10..];
 
-                        using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
-                        using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
+                    using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
+                    using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
 
-                        fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
-                        fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
+                    fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
+                    fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
 
-                        (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
+                    (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
 
-                        if (!canceled)
+                    if (!canceled)
+                    {
+                        if (resultCode.Value.IsFailure())
                         {
-                            if (resultCode.Value.IsFailure())
-                            {
-                                Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
-
-                                Dispatcher.UIThread.InvokeAsync(async () =>
-                                {
-                                    waitingDialog.Close();
+                            Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
 
-                                    await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
-                                });
-                            }
-                            else if (resultCode.Value.IsSuccess())
+                            Dispatcher.UIThread.InvokeAsync(async () =>
                             {
-                                Dispatcher.UIThread.Post(waitingDialog.Close);
+                                waitingDialog.Close();
 
-                                NotificationHelper.Show(
-                                    LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
-                                    $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
-                                    NotificationType.Information);
-                            }
+                                await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
+                            });
                         }
-
-                        fsClient.Unmount(source.ToU8Span());
-                        fsClient.Unmount(output.ToU8Span());
-                    }
-                    catch (ArgumentException ex)
-                    {
-                        Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
-
-                        Dispatcher.UIThread.InvokeAsync(async () =>
+                        else if (resultCode.Value.IsSuccess())
                         {
-                            waitingDialog.Close();
+                            Dispatcher.UIThread.Post(waitingDialog.Close);
 
-                            await ContentDialogHelper.CreateErrorDialog(ex.Message);
-                        });
+                            NotificationHelper.Show(
+                                LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
+                                $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
+                                NotificationType.Information);
+                        }
                     }
-                })
+
+                    fsClient.Unmount(source.ToU8Span());
+                    fsClient.Unmount(output.ToU8Span());
+                }
+                catch (ArgumentException ex)
                 {
-                    Name = "GUI.NcaSectionExtractorThread",
-                    IsBackground = true,
-                };
-                extractorThread.Start();
-            }
+                    Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
+
+                    Dispatcher.UIThread.InvokeAsync(async () =>
+                    {
+                        waitingDialog.Close();
+
+                        await ContentDialogHelper.CreateErrorDialog(ex.Message);
+                    });
+                }
+            })
+            {
+                Name = "GUI.NcaSectionExtractorThread",
+                IsBackground = true,
+            };
+            extractorThread.Start();
         }
 
         public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token)

+ 0 - 7
src/Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs

@@ -31,7 +31,6 @@ namespace Ryujinx.Ava.Input
             _control.KeyDown += OnKeyPress;
             _control.KeyUp += OnKeyRelease;
             _control.TextInput += Control_TextInput;
-            _control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
         }
 
         private void Control_TextInput(object sender, TextInputEventArgs e)
@@ -39,12 +38,6 @@ namespace Ryujinx.Ava.Input
             TextInput?.Invoke(this, e.Text);
         }
 
-        private void Control_LastChanceTextInput(object sender, TextInputEventArgs e)
-        {
-            // Swallow event
-            e.Handled = true;
-        }
-
         public event Action<string> OnGamepadConnected
         {
             add { }

+ 3 - 6
src/Ryujinx.Ava/Program.cs

@@ -59,15 +59,12 @@ namespace Ryujinx.Ava
                 {
                     EnableMultiTouch = true,
                     EnableIme = true,
-                    UseEGL = false,
-                    UseGpu = true,
+                    RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
                 })
                 .With(new Win32PlatformOptions
                 {
-                    EnableMultitouch = true,
-                    UseWgl = false,
-                    AllowEglInitialization = false,
-                    CompositionBackdropCornerRadius = 8.0f,
+                    WinUICompositionBackdropCornerRadius = 8.0f,
+                    RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software },
                 })
                 .UseSkia();
         }

+ 5 - 8
src/Ryujinx.Ava/Ryujinx.Ava.csproj

@@ -10,6 +10,8 @@
     <RootNamespace>Ryujinx.Ava</RootNamespace>
     <ApplicationIcon>Ryujinx.ico</ApplicationIcon>
     <TieredPGO>true</TieredPGO>
+    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
   </PropertyGroup>
 
   <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
@@ -26,7 +28,7 @@
   <ItemGroup>
     <PackageReference Include="Avalonia" />
     <PackageReference Include="Avalonia.Desktop" />
-    <PackageReference Include="Avalonia.Diagnostics" />
+    <PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" />
     <PackageReference Include="Avalonia.Controls.DataGrid" />
     <PackageReference Include="Avalonia.Markup.Xaml.Loader" />
     <PackageReference Include="Avalonia.Svg" />
@@ -34,7 +36,6 @@
     <PackageReference Include="jp2masa.Avalonia.Flexbox" />
     <PackageReference Include="DynamicData" />
     <PackageReference Include="FluentAvaloniaUI" />
-    <PackageReference Include="XamlNameReferenceGenerator" />
 
     <PackageReference Include="OpenTK.Core" />
     <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
@@ -97,10 +98,7 @@
       <SubType>Designer</SubType>
     </AvaloniaResource>
     <AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
-    <AvaloniaResource Include="Assets\Styles\BaseLight.xaml">
-      <Generator>MSBuild:Compile</Generator>
-    </AvaloniaResource>
-    <AvaloniaResource Include="Assets\Styles\BaseDark.xaml">
+    <AvaloniaResource Include="Assets\Styles\Themes.xaml">
       <Generator>MSBuild:Compile</Generator>
     </AvaloniaResource>
     <AvaloniaResource Include="Assets\Styles\Styles.xaml" />
@@ -123,8 +121,7 @@
     <None Remove="Assets\Locales\zh_CN.json" />
     <None Remove="Assets\Locales\zh_TW.json" />
     <None Remove="Assets\Styles\Styles.xaml" />
-    <None Remove="Assets\Styles\BaseDark.xaml" />
-    <None Remove="Assets\Styles\BaseLight.xaml" />
+    <None Remove="Assets\Styles\Themes.xaml" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml

@@ -38,7 +38,7 @@
             Grid.Column="1"
             Margin="10"
             VerticalAlignment="Stretch"
-            Text="{Binding Message}"
+            Text="{ReflectionBinding Message}"
             TextWrapping="Wrap" />
         <StackPanel
             Name="ButtonStack"

+ 1 - 9
src/Ryujinx.Ava/UI/Applet/ErrorAppletWindow.axaml.cs

@@ -4,9 +4,6 @@ using Avalonia.Threading;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.UI.Windows;
 using System.Threading.Tasks;
-#if DEBUG
-using Avalonia;
-#endif
 
 namespace Ryujinx.Ava.UI.Applet
 {
@@ -21,9 +18,7 @@ namespace Ryujinx.Ava.UI.Applet
             Message = message;
             DataContext = this;
             InitializeComponent();
-#if DEBUG
-            this.AttachDevTools();
-#endif
+
             int responseId = 0;
 
             if (buttons != null)
@@ -44,9 +39,6 @@ namespace Ryujinx.Ava.UI.Applet
         {
             DataContext = this;
             InitializeComponent();
-#if DEBUG
-            this.AttachDevTools();
-#endif
         }
 
         public string Message { get; set; }

+ 3 - 3
src/Ryujinx.Ava/UI/Applet/SwkbdAppletDialog.axaml

@@ -34,13 +34,13 @@
             Grid.Row="1"
             Grid.Column="1"
             Margin="5"
-            Text="{Binding MainText}"
+            Text="{ReflectionBinding MainText}"
             TextWrapping="Wrap" />
         <TextBlock
             Grid.Row="2"
             Grid.Column="1"
             Margin="5"
-            Text="{Binding SecondaryText}"
+            Text="{ReflectionBinding SecondaryText}"
             TextWrapping="Wrap" />
         <TextBox
             Name="Input"
@@ -50,7 +50,7 @@
             VerticalAlignment="Center"
             Focusable="True"
             KeyUp="Message_KeyUp"
-            Text="{Binding Message}"
+            Text="{ReflectionBinding Message}"
             TextInput="Message_TextInput"
             TextWrapping="Wrap"
             UseFloatingWatermark="True" />

+ 3 - 1
src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml

@@ -2,7 +2,9 @@
     x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
+    xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+    xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+    x:DataType="viewModels:MainWindowViewModel">
 	<MenuItem
         Click="RunApplication_Click"
         Header="{locale:Locale GameListContextMenuRunApplication}" />

+ 15 - 3
src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs

@@ -299,7 +299,11 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
+                await ApplicationHelper.ExtractSection(
+                    viewModel.StorageProvider,
+                    NcaSectionType.Code,
+                    viewModel.SelectedApplication.Path,
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -309,7 +313,11 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
+                await ApplicationHelper.ExtractSection(
+                    viewModel.StorageProvider,
+                    NcaSectionType.Data,
+                    viewModel.SelectedApplication.Path,
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -319,7 +327,11 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
+                await ApplicationHelper.ExtractSection(
+                    viewModel.StorageProvider,
+                    NcaSectionType.Logo,
+                    viewModel.SelectedApplication.Path,
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 

+ 12 - 10
src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml

@@ -11,7 +11,9 @@
     d:DesignHeight="450"
     d:DesignWidth="800"
     Focusable="True"
-    mc:Ignorable="d">
+    mc:Ignorable="d"
+    xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+    x:DataType="viewModels:MainWindowViewModel">
     <UserControl.Resources>
         <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
         <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
@@ -27,7 +29,7 @@
             VerticalAlignment="Stretch"
             ContextFlyout="{StaticResource ApplicationContextMenu}"
             DoubleTapped="GameList_DoubleTapped"
-            Items="{Binding AppsObservableList}"
+            ItemsSource="{Binding AppsObservableList}"
             SelectionChanged="GameList_SelectionChanged">
             <ListBox.ItemsPanel>
                 <ItemsPanelTemplate>
@@ -43,8 +45,8 @@
                     <Setter Property="Margin" Value="5" />
                     <Setter Property="CornerRadius" Value="4" />
                 </Style>
-                <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
-                    <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
+                <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
+                    <Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.GridItemSelectorSize}" />
                 </Style>
             </ListBox.Styles>
             <ListBox.ItemTemplate>
@@ -54,10 +56,10 @@
                             Margin="10"
                             HorizontalAlignment="Stretch"
                             VerticalAlignment="Stretch"
-                            Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
-                            Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
-                            Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
-                            Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
+                            Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
+                            Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
+                            Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
+                            Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
                             ClipToBounds="True"
                             CornerRadius="4">
                             <Grid>
@@ -76,9 +78,9 @@
                                     Margin="0,10,0,0"
                                     HorizontalAlignment="Stretch"
                                     VerticalAlignment="Stretch"
-                                    IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
+                                    IsVisible="{ReflectionBinding $parent[UserControl].DataContext.ShowNames}">
                                     <TextBlock
-                                        HorizontalAlignment="Stretch"
+                                        HorizontalAlignment="Center"
                                         VerticalAlignment="Center"
                                         Text="{Binding TitleName}"
                                         TextAlignment="Center"

+ 1 - 7
src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs

@@ -1,7 +1,6 @@
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Interactivity;
-using Avalonia.Markup.Xaml;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ui.App.Common;
@@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls
             InitializeComponent();
         }
 
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
-        public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
+        public void GameList_DoubleTapped(object sender, TappedEventArgs args)
         {
             if (sender is ListBox listBox)
             {

+ 10 - 8
src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml

@@ -10,7 +10,9 @@
     d:DesignHeight="450"
     d:DesignWidth="800"
     Focusable="True"
-    mc:Ignorable="d">
+    mc:Ignorable="d"
+    xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+    x:DataType="viewModels:MainWindowViewModel">
     <UserControl.Resources>
         <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
         <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
@@ -27,7 +29,7 @@
             VerticalAlignment="Stretch"
             ContextFlyout="{StaticResource ApplicationContextMenu}"
             DoubleTapped="GameList_DoubleTapped"
-            Items="{Binding AppsObservableList}"
+            ItemsSource="{Binding AppsObservableList}"
             SelectionChanged="GameList_SelectionChanged">
             <ListBox.ItemsPanel>
                 <ItemsPanelTemplate>
@@ -39,8 +41,8 @@
                 </ItemsPanelTemplate>
             </ListBox.ItemsPanel>
             <ListBox.Styles>
-                <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
-                    <Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" />
+                <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
+                    <Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.ListItemSelectorSize}" />
                 </Style>
             </ListBox.Styles>
             <ListBox.ItemTemplate>
@@ -65,10 +67,10 @@
                                     Grid.RowSpan="3"
                                     Grid.Column="0"
                                     Margin="0"
-                                    Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
-                                    Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
-                                    Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
-                                    Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
+                                    Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
+                                    Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
+                                    Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
+                                    Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
                                     Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
                                 <Border
                                     Grid.Column="2"

+ 1 - 7
src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs

@@ -1,7 +1,6 @@
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Interactivity;
-using Avalonia.Markup.Xaml;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ui.App.Common;
@@ -25,12 +24,7 @@ namespace Ryujinx.Ava.UI.Controls
             InitializeComponent();
         }
 
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
-        public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
+        public void GameList_DoubleTapped(object sender, TappedEventArgs args)
         {
             if (sender is ListBox listBox)
             {

+ 1 - 9
src/Ryujinx.Ava/UI/Helpers/GlyphValueConverter.cs

@@ -1,6 +1,4 @@
-using Avalonia.Data;
 using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.MarkupExtensions;
 using FluentAvalonia.UI.Controls;
 using System;
 using System.Collections.Generic;
@@ -38,13 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers
 
         public override object ProvideValue(IServiceProvider serviceProvider)
         {
-            ReflectionBindingExtension binding = new($"[{_key}]")
-            {
-                Mode = BindingMode.OneWay,
-                Source = this,
-            };
-
-            return binding.ProvideValue(serviceProvider);
+            return this[_key];
         }
     }
 }

+ 0 - 52
src/Ryujinx.Ava/UI/Helpers/HotKeyControl.cs

@@ -1,52 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Input;
-using System;
-using System.Windows.Input;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    public class HotKeyControl : ContentControl, ICommandSource
-    {
-        public static readonly StyledProperty<object> CommandParameterProperty =
-            AvaloniaProperty.Register<HotKeyControl, object>(nameof(CommandParameter));
-
-        public static readonly DirectProperty<HotKeyControl, ICommand> CommandProperty =
-            AvaloniaProperty.RegisterDirect<HotKeyControl, ICommand>(nameof(Command),
-                control => control.Command, (control, command) => control.Command = command, enableDataValidation: true);
-
-        public static readonly StyledProperty<KeyGesture> HotKeyProperty = HotKeyManager.HotKeyProperty.AddOwner<Button>();
-
-        private ICommand _command;
-        private bool _commandCanExecute;
-
-        public ICommand Command
-        {
-            get { return _command; }
-            set { SetAndRaise(CommandProperty, ref _command, value); }
-        }
-
-        public KeyGesture HotKey
-        {
-            get { return GetValue(HotKeyProperty); }
-            set { SetValue(HotKeyProperty, value); }
-        }
-
-        public object CommandParameter
-        {
-            get { return GetValue(CommandParameterProperty); }
-            set { SetValue(CommandParameterProperty, value); }
-        }
-
-        public void CanExecuteChanged(object sender, EventArgs e)
-        {
-            var canExecute = Command == null || Command.CanExecute(CommandParameter);
-
-            if (canExecute != _commandCanExecute)
-            {
-                _commandCanExecute = canExecute;
-                UpdateIsEffectivelyEnabled();
-            }
-        }
-    }
-}

+ 0 - 15
src/Ryujinx.Ava/UI/Helpers/LoggerAdapter.cs

@@ -42,21 +42,6 @@ namespace Ryujinx.Ava.UI.Helpers
             GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, null));
         }
 
-        public void Log<T0>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
-        {
-            GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0 }));
-        }
-
-        public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
-        {
-            GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 }));
-        }
-
-        public void Log<T0, T1, T2>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
-        {
-            GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1, propertyValue2 }));
-        }
-
         public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
         {
             GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, propertyValues));

+ 0 - 1
src/Ryujinx.Ava/UI/Helpers/OffscreenTextBox.cs

@@ -31,7 +31,6 @@ namespace Ryujinx.Ava.UI.Helpers
             OnTextInput(new TextInputEventArgs
             {
                 Text = text,
-                Device = KeyboardDevice.Instance,
                 Source = this,
                 RoutedEvent = TextInputEvent,
             });

+ 28 - 0
src/Ryujinx.Ava/UI/Helpers/TimeZoneConverter.cs

@@ -0,0 +1,28 @@
+using Avalonia.Data.Converters;
+using System;
+using System.Globalization;
+using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
+
+namespace Ryujinx.Ava.UI.Helpers
+{
+    internal class TimeZoneConverter : IValueConverter
+    {
+        public static TimeZoneConverter Instance = new();
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value == null)
+            {
+                return null;
+            }
+
+            var timeZone = (TimeZone)value;
+            return string.Format("{0}  {1}   {2}", timeZone.UtcDifference, timeZone.Location, timeZone.Abbreviation);
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 2 - 2
src/Ryujinx.Ava/UI/Models/UserProfile.cs

@@ -1,4 +1,3 @@
-using Avalonia;
 using Avalonia.Media;
 using Ryujinx.Ava.UI.Controls;
 using Ryujinx.Ava.UI.ViewModels;
@@ -88,7 +87,8 @@ namespace Ryujinx.Ava.UI.Models
 
         private void UpdateBackground()
         {
-            Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color);
+            var currentApplication = Avalonia.Application.Current;
+            currentApplication.Styles.TryGetResource("ControlFillColorSecondary", currentApplication.ActualThemeVariant, out object color);
 
             if (color is not null)
             {

+ 8 - 6
src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs

@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Renderer
         private UpdateBoundsCallbackDelegate _updateBoundsCallback;
 
         public event EventHandler<IntPtr> WindowCreated;
-        public event EventHandler<Size> SizeChanged;
+        public event EventHandler<Size> BoundsChanged;
 
         public EmbeddedWindow()
         {
@@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Renderer
 
         private void StateChanged(Rect rect)
         {
-            SizeChanged?.Invoke(this, rect.Size);
+            BoundsChanged?.Invoke(this, rect.Size);
             _updateBoundsCallback?.Invoke(rect);
         }
 
@@ -149,9 +149,10 @@ namespace Ryujinx.Ava.UI.Renderer
                         msg == WindowsMessages.Rbuttonup ||
                         msg == WindowsMessages.Mousemove)
                     {
-                        Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
+                        Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), this).Value;
                         Pointer pointer = new(0, PointerType.Mouse, true);
 
+#pragma warning disable CS0618 // Type or member is obsolete (As of Avalonia 11, the constructors for PointerPressedEventArgs & PointerEventArgs are marked as obsolete)
                         switch (msg)
                         {
                             case WindowsMessages.Lbuttondown:
@@ -164,7 +165,7 @@ namespace Ryujinx.Ava.UI.Renderer
                                     var evnt = new PointerPressedEventArgs(
                                         this,
                                         pointer,
-                                        VisualRoot,
+                                        this,
                                         rootVisualPosition,
                                         (ulong)Environment.TickCount64,
                                         properties,
@@ -184,7 +185,7 @@ namespace Ryujinx.Ava.UI.Renderer
                                     var evnt = new PointerReleasedEventArgs(
                                         this,
                                         pointer,
-                                        VisualRoot,
+                                        this,
                                         rootVisualPosition,
                                         (ulong)Environment.TickCount64,
                                         properties,
@@ -201,7 +202,7 @@ namespace Ryujinx.Ava.UI.Renderer
                                         PointerMovedEvent,
                                         this,
                                         pointer,
-                                        VisualRoot,
+                                        this,
                                         rootVisualPosition,
                                         (ulong)Environment.TickCount64,
                                         new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
@@ -212,6 +213,7 @@ namespace Ryujinx.Ava.UI.Renderer
                                     break;
                                 }
                         }
+#pragma warning restore CS0618
                     }
                 }
 

+ 5 - 5
src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Renderer
         public readonly EmbeddedWindow EmbeddedWindow;
 
         public event EventHandler<EventArgs> WindowCreated;
-        public event Action<object, Size> SizeChanged;
+        public event Action<object, Size> BoundsChanged;
 
         public RendererHost()
         {
@@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer
         private void Initialize()
         {
             EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated;
-            EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged;
+            EmbeddedWindow.BoundsChanged += CurrentWindow_BoundsChanged;
 
             Content = EmbeddedWindow;
         }
@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Renderer
             if (EmbeddedWindow != null)
             {
                 EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
-                EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
+                EmbeddedWindow.BoundsChanged -= CurrentWindow_BoundsChanged;
             }
 
             GC.SuppressFinalize(this);
@@ -55,9 +55,9 @@ namespace Ryujinx.Ava.UI.Renderer
             Dispose();
         }
 
-        private void CurrentWindow_SizeChanged(object sender, Size e)
+        private void CurrentWindow_BoundsChanged(object sender, Size e)
         {
-            SizeChanged?.Invoke(sender, e);
+            BoundsChanged?.Invoke(sender, e);
         }
 
         private void CurrentWindow_WindowCreated(object sender, IntPtr e)

+ 8 - 11
src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

@@ -1,4 +1,3 @@
-using Avalonia;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Threading;
@@ -88,21 +87,19 @@ namespace Ryujinx.Ava.UI.ViewModels
         {
             Version = Program.Version;
 
-            var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
-
             if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
             {
-                GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
-                DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
-                PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
-                TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
+                GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
+                DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
+                PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
+                TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
             }
             else
             {
-                GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
-                DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
-                PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
-                TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common")));
+                GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
+                DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
+                PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
+                TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common")));
             }
 
             Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);

+ 19 - 17
src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs

@@ -1,6 +1,6 @@
 using Avalonia.Collections;
-using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Platform.Storage;
 using Avalonia.Threading;
 using DynamicData;
 using LibHac.Common;
@@ -90,12 +90,19 @@ namespace Ryujinx.Ava.UI.ViewModels
             get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
         }
 
+        public IStorageProvider StorageProvider;
+
         public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
             _virtualFileSystem = virtualFileSystem;
 
             _titleId = titleId;
 
+            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                StorageProvider = desktop.MainWindow.StorageProvider;
+            }
+
             _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
 
             try
@@ -195,29 +202,24 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public async void Add()
         {
-            OpenFileDialog dialog = new()
+            var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
                 Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
                 AllowMultiple = true,
-            };
-
-            dialog.Filters.Add(new FileDialogFilter
-            {
-                Name = "NSP",
-                Extensions = { "nsp" },
-            });
-
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
-                if (files != null)
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    foreach (string file in files)
+                    new("NSP")
                     {
-                        await AddDownloadableContent(file);
+                        Patterns = new[] { "*.nsp" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
+                        MimeTypes = new[] { "application/x-nx-nsp" }
                     }
                 }
+            });
+
+            foreach (var file in result)
+            {
+                await AddDownloadableContent(file.Path.LocalPath);
             }
         }
 

+ 115 - 100
src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

@@ -3,6 +3,7 @@ using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Input;
 using Avalonia.Media;
+using Avalonia.Platform.Storage;
 using Avalonia.Threading;
 using DynamicData;
 using DynamicData.Binding;
@@ -105,8 +106,6 @@ namespace Ryujinx.Ava.UI.ViewModels
         public ApplicationData ListSelectedApplication;
         public ApplicationData GridSelectedApplication;
 
-        public event Action ReloadGameList;
-
         private string TitleName { get; set; }
         internal AppHost AppHost { get; set; }
 
@@ -131,6 +130,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public void Initialize(
             ContentManager contentManager,
+            IStorageProvider storageProvider,
             ApplicationLibrary applicationLibrary,
             VirtualFileSystem virtualFileSystem,
             AccountManager accountManager,
@@ -144,6 +144,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             TopLevel topLevel)
         {
             ContentManager = contentManager;
+            StorageProvider = storageProvider;
             ApplicationLibrary = applicationLibrary;
             VirtualFileSystem = virtualFileSystem;
             AccountManager = accountManager;
@@ -891,6 +892,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         }
 
         public ContentManager ContentManager { get; private set; }
+        public IStorageProvider StorageProvider { get; private set; }
         public ApplicationLibrary ApplicationLibrary { get; private set; }
         public VirtualFileSystem VirtualFileSystem { get; private set; }
         public AccountManager AccountManager { get; private set; }
@@ -1188,7 +1190,9 @@ namespace Ryujinx.Ava.UI.ViewModels
                 {
                     Application.Current.Styles.TryGetResource(args.VSyncEnabled
                         ? "VsyncEnabled"
-                        : "VsyncDisabled", out object color);
+                        : "VsyncDisabled",
+                        Avalonia.Application.Current.ActualThemeVariant,
+                        out object color);
 
                     if (color is not null)
                     {
@@ -1259,6 +1263,16 @@ namespace Ryujinx.Ava.UI.ViewModels
             ShowMenuAndStatusBar = false;
         }
 
+        public void ToggleStartGamesInFullscreen()
+        {
+            StartGamesInFullscreen = !StartGamesInFullscreen;
+        }
+
+        public void ToggleShowConsole()
+        {
+            ShowConsole = !ShowConsole;
+        }
+
         public void SetListMode()
         {
             Glyph = Glyph.List;
@@ -1271,43 +1285,57 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public async void InstallFirmwareFromFile()
         {
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
-                OpenFileDialog dialog = new() { AllowMultiple = false };
-                dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
-
-                string[] file = await dialog.ShowAsync(desktop.MainWindow);
-
-                if (file != null && file.Length > 0)
+                AllowMultiple = false,
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    await HandleFirmwareInstallation(file[0]);
+                    new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
+                    {
+                        Patterns = new[] { "*.xci", "*.zip" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
+                        MimeTypes = new[] { "application/x-nx-xci", "application/zip" }
+                    },
+                    new("XCI")
+                    {
+                        Patterns = new[] { "*.xci" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
+                        MimeTypes = new[] { "application/x-nx-xci" }
+                    },
+                    new("ZIP")
+                    {
+                        Patterns = new[] { "*.zip" },
+                        AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
+                        MimeTypes = new[] { "application/zip" }
+                    },
                 }
+            });
+
+            if (result.Count > 0)
+            {
+                await HandleFirmwareInstallation(result[0].Path.LocalPath);
             }
         }
 
         public async void InstallFirmwareFromFolder()
         {
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
             {
-                OpenFolderDialog dialog = new();
-
-                string folder = await dialog.ShowAsync(desktop.MainWindow);
+                AllowMultiple = false
+            });
 
-                if (!string.IsNullOrEmpty(folder))
-                {
-                    await HandleFirmwareInstallation(folder);
-                }
+            if (result.Count > 0)
+            {
+                await HandleFirmwareInstallation(result[0].Path.LocalPath);
             }
         }
 
-        public static void OpenRyujinxFolder()
+        public void OpenRyujinxFolder()
         {
             OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
         }
 
-        public static void OpenLogsFolder()
+        public void OpenLogsFolder()
         {
             string logPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs");
 
@@ -1349,25 +1377,6 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
         }
 
-        public void ToggleFileType(string fileType)
-        {
-            _ = fileType switch
-            {
-#pragma warning disable IDE0055 // Disable formatting
-                "NSP"  => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
-                "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
-                "XCI"  => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
-                "NCA"  => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
-                "NRO"  => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
-                "NSO"  => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
-                    _  => throw new ArgumentOutOfRangeException(fileType),
-#pragma warning restore IDE0055
-            };
-
-            ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
-            LoadApplications();
-        }
-
         public async void ManageProfiles()
         {
             await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
@@ -1378,78 +1387,84 @@ namespace Ryujinx.Ava.UI.ViewModels
             AppHost.Device.System.SimulateWakeUpMessage();
         }
 
-        public async void LoadApplications()
-        {
-            await Dispatcher.UIThread.InvokeAsync(() =>
-            {
-                Applications.Clear();
-
-                StatusBarVisible = true;
-                StatusBarProgressMaximum = 0;
-                StatusBarProgressValue = 0;
-
-                LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
-            });
-
-            ReloadGameList?.Invoke();
-        }
-
         public async void OpenFile()
         {
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
-                OpenFileDialog dialog = new()
+                Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
+                AllowMultiple = false,
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
-                };
-
-                dialog.Filters.Add(new FileDialogFilter
-                {
-                    Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
-                    Extensions =
+                    new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
+                    {
+                        Patterns = new[] { "*.nsp", "*.xci", "*.nca", "*.nro", "*.nso" },
+                        AppleUniformTypeIdentifiers = new[]
+                        {
+                            "com.ryujinx.nsp",
+                            "com.ryujinx.xci",
+                            "com.ryujinx.nca",
+                            "com.ryujinx.nro",
+                            "com.ryujinx.nso"
+                        },
+                        MimeTypes = new[]
+                        {
+                            "application/x-nx-nsp",
+                            "application/x-nx-xci",
+                            "application/x-nx-nca",
+                            "application/x-nx-nro",
+                            "application/x-nx-nso"
+                        }
+                    },
+                    new("NSP")
                     {
-                        "nsp",
-                        "pfs0",
-                        "xci",
-                        "nca",
-                        "nro",
-                        "nso",
+                        Patterns = new[] { "*.nsp" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
+                        MimeTypes = new[] { "application/x-nx-nsp" }
+                    },
+                    new("XCI")
+                    {
+                        Patterns = new[] { "*.xci" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
+                        MimeTypes = new[] { "application/x-nx-xci" }
+                    },
+                    new("NCA")
+                    {
+                        Patterns = new[] { "*.nca" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
+                        MimeTypes = new[] { "application/x-nx-nca" }
+                    },
+                    new("NRO")
+                    {
+                        Patterns = new[] { "*.nro" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
+                        MimeTypes = new[] { "application/x-nx-nro" }
+                    },
+                    new("NSO")
+                    {
+                        Patterns = new[] { "*.nso" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
+                        MimeTypes = new[] { "application/x-nx-nso" }
                     },
-                });
-
-#pragma warning disable IDE0055 // Disable formatting
-                dialog.Filters.Add(new FileDialogFilter { Name = "NSP",  Extensions = { "nsp" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "XCI",  Extensions = { "xci" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "NCA",  Extensions = { "nca" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "NRO",  Extensions = { "nro" } });
-                dialog.Filters.Add(new FileDialogFilter { Name = "NSO",  Extensions = { "nso" } });
-#pragma warning restore IDE0055
-
-                string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
-                if (files != null && files.Length > 0)
-                {
-                    LoadApplication(files[0]);
                 }
+            });
+
+            if (result.Count > 0)
+            {
+                LoadApplication(result[0].Path.LocalPath);
             }
         }
 
         public async void OpenFolder()
         {
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
             {
-                OpenFolderDialog dialog = new()
-                {
-                    Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
-                };
-
-                string folder = await dialog.ShowAsync(desktop.MainWindow);
+                Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
+                AllowMultiple = false
+            });
 
-                if (!string.IsNullOrWhiteSpace(folder) && Directory.Exists(folder))
-                {
-                    LoadApplication(folder);
-                }
+            if (result.Count > 0)
+            {
+                LoadApplication(result[0].Path.LocalPath);
             }
         }
 

+ 19 - 18
src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs

@@ -1,7 +1,7 @@
 using Avalonia;
 using Avalonia.Collections;
-using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Platform.Storage;
 using Avalonia.Threading;
 using LibHac.Common;
 using LibHac.Fs;
@@ -70,12 +70,19 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
         }
 
+        public IStorageProvider StorageProvider;
+
         public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
             VirtualFileSystem = virtualFileSystem;
 
             TitleId = titleId;
 
+            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                StorageProvider = desktop.MainWindow.StorageProvider;
+            }
+
             TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
 
             try
@@ -202,29 +209,23 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public async void Add()
         {
-            OpenFileDialog dialog = new()
+            var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
-                Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
                 AllowMultiple = true,
-            };
-
-            dialog.Filters.Add(new FileDialogFilter
-            {
-                Name = "NSP",
-                Extensions = { "nsp" },
-            });
-
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
-                if (files != null)
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    foreach (string file in files)
+                    new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
                     {
-                        AddUpdate(file);
+                        Patterns = new[] { "*.nsp" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
+                        MimeTypes = new[] { "application/x-nx-nsp" }
                     }
                 }
+            });
+
+            foreach (var file in result)
+            {
+                AddUpdate(file.Path.LocalPath);
             }
 
             SortUpdates();

+ 9 - 10
src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml

@@ -14,7 +14,6 @@
     d:DesignWidth="800"
     x:Class="Ryujinx.Ava.UI.Views.Input.ControllerInputView"
     x:DataType="viewModels:ControllerInputViewModel"
-    x:CompileBindings="True"
     mc:Ignorable="d"
     Focusable="True">
     <Design.DataContext>
@@ -66,7 +65,7 @@
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Center"
                         SelectionChanged="PlayerIndexBox_OnSelectionChanged"
-                        Items="{Binding PlayerIndexes}"
+                        ItemsSource="{Binding PlayerIndexes}"
                         SelectedIndex="{Binding PlayerId}">
                         <ComboBox.ItemTemplate>
                             <DataTemplate>
@@ -94,15 +93,15 @@
                         HorizontalAlignment="Left"
                         VerticalAlignment="Center"
                         Text="{locale:Locale ControllerSettingsProfile}" />
-                    <ui:ComboBox
+                    <ui:FAComboBox
                         Grid.Column="1"
                         IsEditable="True"
                         Name="ProfileBox"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Center"
                         SelectedIndex="0"
-                        Items="{Binding ProfilesList}"
-                        Text="{Binding ProfileName}" />
+                        ItemsSource="{Binding ProfilesList}" 
+                        Text="{Binding ProfileName, Mode=TwoWay}" />
                     <Button
                         Grid.Column="2"
                         MinWidth="0"
@@ -170,7 +169,7 @@
                         Name="DeviceBox"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Center"
-                        Items="{Binding DeviceList}"
+                        ItemsSource="{Binding DeviceList}"
                         SelectedIndex="{Binding Device}" />
                     <Button
                         Grid.Column="2"
@@ -203,8 +202,8 @@
                     <ComboBox
                         Grid.Column="1"
                         HorizontalAlignment="Stretch"
-                        Items="{ReflectionBinding Controllers}"
-                        SelectedIndex="{ReflectionBinding Controller}">
+                        ItemsSource="{Binding Controllers}"
+                        SelectedIndex="{Binding Controller}">
                         <ComboBox.ItemTemplate>
                             <DataTemplate DataType="models:ControllerModel">
                                 <TextBlock Text="{Binding Name}" />
@@ -723,7 +722,7 @@
                             <Button
                                 Margin="10"
                                 Grid.Column="1"
-                                Command="{ReflectionBinding ShowMotionConfig}">
+                                Command="{Binding ShowMotionConfig}">
                                 <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
                             </Button>
                         </Grid>
@@ -750,7 +749,7 @@
                             <Button
                                 Margin="10"
                                 Grid.Column="1"
-                                Command="{ReflectionBinding ShowRumbleConfig}">
+                                Command="{Binding ShowRumbleConfig}">
                                 <TextBlock Text="{locale:Locale ControllerSettingsConfigureGeneral}" />
                             </Button>
                         </Grid>

+ 35 - 34
src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs

@@ -31,8 +31,7 @@ namespace Ryujinx.Ava.UI.Views.Input
             {
                 if (visual is ToggleButton button && visual is not CheckBox)
                 {
-                    button.Checked += Button_Checked;
-                    button.Unchecked += Button_Unchecked;
+                    button.IsCheckedChanged += Button_IsCheckedChanged;
                 }
             }
         }
@@ -47,48 +46,56 @@ namespace Ryujinx.Ava.UI.Views.Input
             }
         }
 
-        private void Button_Checked(object sender, RoutedEventArgs e)
+        private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
         {
             if (sender is ToggleButton button)
             {
-                if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
+                if ((bool)button.IsChecked)
                 {
-                    return;
-                }
+                    if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
+                    {
+                        return;
+                    }
 
-                bool isStick = button.Tag != null && button.Tag.ToString() == "stick";
+                    bool isStick = button.Tag != null && button.Tag.ToString() == "stick";
 
-                if (_currentAssigner == null && (bool)button.IsChecked)
-                {
-                    _currentAssigner = new ButtonKeyAssigner(button);
+                    if (_currentAssigner == null)
+                    {
+                        _currentAssigner = new ButtonKeyAssigner(button);
 
-                    FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
+                        this.Focus(NavigationMethod.Pointer);
 
-                    PointerPressed += MouseClick;
+                        PointerPressed += MouseClick;
 
-                    IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
-                    IButtonAssigner assigner = CreateButtonAssigner(isStick);
+                        IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
+                        IButtonAssigner assigner = CreateButtonAssigner(isStick);
 
-                    _currentAssigner.ButtonAssigned += (sender, e) =>
+                        _currentAssigner.ButtonAssigned += (sender, e) =>
+                        {
+                            if (e.IsAssigned)
+                            {
+                                ViewModel.IsModified = true;
+                            }
+                        };
+
+                        _currentAssigner.GetInputAndAssign(assigner, keyboard);
+                    }
+                    else
                     {
-                        if (e.IsAssigned)
+                        if (_currentAssigner != null)
                         {
-                            ViewModel.IsModified = true;
-                        }
-                    };
+                            ToggleButton oldButton = _currentAssigner.ToggledButton;
 
-                    _currentAssigner.GetInputAndAssign(assigner, keyboard);
+                            _currentAssigner.Cancel();
+                            _currentAssigner = null;
+                            button.IsChecked = false;
+                        }
+                    }
                 }
                 else
                 {
-                    if (_currentAssigner != null)
-                    {
-                        ToggleButton oldButton = _currentAssigner.ToggledButton;
-
-                        _currentAssigner.Cancel();
-                        _currentAssigner = null;
-                        button.IsChecked = false;
-                    }
+                    _currentAssigner?.Cancel();
+                    _currentAssigner = null;
                 }
             }
         }
@@ -120,12 +127,6 @@ namespace Ryujinx.Ava.UI.Views.Input
             return assigner;
         }
 
-        private void Button_Unchecked(object sender, RoutedEventArgs e)
-        {
-            _currentAssigner?.Cancel();
-            _currentAssigner = null;
-        }
-
         private void MouseClick(object sender, PointerPressedEventArgs e)
         {
             bool shouldUnbind = false;

+ 0 - 1
src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml

@@ -8,7 +8,6 @@
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
     x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
-    x:CompileBindings="True"
     x:DataType="viewModels:MotionInputViewModel"
     Focusable="True">
     <Grid Margin="10">

+ 0 - 1
src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml

@@ -8,7 +8,6 @@
     mc:Ignorable="d"
     x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
     x:DataType="viewModels:RumbleInputViewModel"
-    x:CompileBindings="True"
     Focusable="True">
     <Grid Margin="10">
         <Grid.RowDefinitions>

+ 64 - 25
src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml

@@ -7,8 +7,7 @@
     mc:Ignorable="d"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     x:DataType="viewModels:MainWindowViewModel"
-    x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"
-    x:CompileBindings="True">
+    x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView">
     <Design.DataContext>
         <viewModels:MainWindowViewModel />
     </Design.DataContext>
@@ -25,12 +24,12 @@
             </Menu.ItemsPanel>
             <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
                 <MenuItem
-                    Command="{ReflectionBinding OpenFile}"
+                    Command="{Binding OpenFile}"
                     Header="{locale:Locale MenuBarFileOpenFromFile}"
                     IsEnabled="{Binding EnableNonGameRunningControls}"
                     ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
                 <MenuItem
-                    Command="{ReflectionBinding OpenFolder}"
+                    Command="{Binding OpenFolder}"
                     Header="{locale:Locale MenuBarFileOpenUnpacked}"
                     IsEnabled="{Binding EnableNonGameRunningControls}"
                     ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
@@ -42,11 +41,11 @@
                 </MenuItem>
                 <Separator />
                 <MenuItem
-                    Command="{ReflectionBinding OpenRyujinxFolder}"
+                    Command="{Binding OpenRyujinxFolder}"
                     Header="{locale:Locale MenuBarFileOpenEmuFolder}"
                     ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
                 <MenuItem
-                    Command="{ReflectionBinding OpenLogsFolder}"
+                    Command="{Binding OpenLogsFolder}"
                     Header="{locale:Locale MenuBarFileOpenLogsFolder}"
                     ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
                 <Separator />
@@ -57,35 +56,75 @@
             </MenuItem>
             <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
                 <MenuItem
-                    Command="{ReflectionBinding ToggleFullscreen}"
+                    Padding="-10,0,0,0"
+                    Command="{Binding ToggleFullscreen}"
                     Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
                     InputGesture="F11" />
-                <MenuItem>
+                <MenuItem
+                    Padding="0"
+                    Command="{Binding ToggleStartGamesInFullscreen}"
+                    Header="{locale:Locale MenuBarOptionsStartGamesInFullscreen}">
                     <MenuItem.Icon>
-                        <CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
-                                  MinWidth="250">
-                            <TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
-                        </CheckBox>
+                        <CheckBox
+                            MinWidth="{DynamicResource CheckBoxSize}"
+                            MinHeight="{DynamicResource CheckBoxSize}"
+                            IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
+                            Padding="0" />
                     </MenuItem.Icon>
+                    <MenuItem.Styles>
+                        <Style Selector="Viewbox#PART_IconPresenter">
+                            <Setter Property="MaxHeight" Value="36" />
+                            <Setter Property="MinHeight" Value="36" />
+                            <Setter Property="MaxWidth" Value="36" />
+                            <Setter Property="MinWidth" Value="36" />
+                        </Style>
+                        <Style Selector="ContentPresenter#PART_HeaderPresenter">
+                            <Setter Property="Padding" Value="-10,0,0,0" />
+                        </Style>
+                    </MenuItem.Styles>
                 </MenuItem>
-                <MenuItem IsVisible="{Binding ShowConsoleVisible}">
+                <MenuItem
+                    Padding="0"
+                    IsVisible="{Binding ShowConsoleVisible}"
+                    Command="{Binding ToggleShowConsole}"
+                    Header="{locale:Locale MenuBarOptionsShowConsole}">
                     <MenuItem.Icon>
-                        <CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
-                                  MinWidth="250">
-                            <TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
-                        </CheckBox>
+                        <CheckBox 
+                            MinWidth="{DynamicResource CheckBoxSize}"
+                            MinHeight="{DynamicResource CheckBoxSize}"
+                            IsChecked="{Binding ShowConsole, Mode=TwoWay}"
+                            Padding="0" />
                     </MenuItem.Icon>
+                    <MenuItem.Styles>
+                        <Style Selector="Viewbox#PART_IconPresenter">
+                            <Setter Property="MaxHeight" Value="36" />
+                            <Setter Property="MinHeight" Value="36" />
+                            <Setter Property="MaxWidth" Value="36" />
+                            <Setter Property="MinWidth" Value="36" />
+                        </Style>
+                        <Style Selector="ContentPresenter#PART_HeaderPresenter">
+                            <Setter Property="Padding" Value="-10,0,0,0" />
+                        </Style>
+                    </MenuItem.Styles>
                 </MenuItem>
                 <Separator />
-                <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
-				<MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" />
+                <MenuItem
+                    Name="ChangeLanguageMenuItem" 
+                    Padding="-10,0,0,0"
+                    Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
+                <MenuItem 
+                    Name="ToggleFileTypesMenuItem" 
+                    Padding="-10,0,0,0"
+                    Header="{locale:Locale MenuBarShowFileTypes}" />
                 <Separator />
                 <MenuItem
                     Click="OpenSettings"
+                    Padding="-10,0,0,0"
                     Header="{locale:Locale MenuBarOptionsSettings}"
                     ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
                 <MenuItem
-                    Command="{ReflectionBinding ManageProfiles}"
+                    Command="{Binding ManageProfiles}"
+                    Padding="-10,0,0,0"
                     Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
                     IsEnabled="{Binding EnableNonGameRunningControls}"
                     ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
@@ -113,7 +152,7 @@
                     InputGesture="Escape"
                     IsEnabled="{Binding IsGameRunning}"
                     ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
-                <MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
+                <MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
                 <Separator />
                 <MenuItem
                     Name="ScanAmiiboMenuItem"
@@ -122,12 +161,12 @@
                     Header="{locale:Locale MenuBarActionsScanAmiibo}"
                     IsEnabled="{Binding IsAmiiboRequested}" />
                 <MenuItem
-                    Command="{ReflectionBinding TakeScreenshot}"
+                    Command="{Binding TakeScreenshot}"
                     Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
                     InputGesture="{Binding ScreenshotKey}"
                     IsEnabled="{Binding IsGameRunning}" />
                 <MenuItem
-                    Command="{ReflectionBinding HideUi}"
+                    Command="{Binding HideUi}"
                     Header="{locale:Locale MenuBarFileToolsHideUi}"
                     InputGesture="{Binding ShowUiKey}"
                     IsEnabled="{Binding IsGameRunning}" />
@@ -138,8 +177,8 @@
             </MenuItem>
             <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
                 <MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
-                    <MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
-                    <MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
+                    <MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
+                    <MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
                 </MenuItem>
                 <MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
                     <MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>

+ 3 - 3
src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs

@@ -30,8 +30,8 @@ namespace Ryujinx.Ava.UI.Views.Main
         {
             InitializeComponent();
 
-            ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems();
-            ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems();
+            ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
+            ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();
         }
 
         private CheckBox[] GenerateToggleFileTypeItems()
@@ -45,7 +45,7 @@ namespace Ryujinx.Ava.UI.Views.Main
                 {
                     Content = $".{fileName}",
                     IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
-                    Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)),
+                    Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)),
                 });
             }
 

+ 28 - 11
src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml

@@ -8,7 +8,6 @@
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
     x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
-    x:CompileBindings="True"
     x:DataType="viewModels:MainWindowViewModel">
     <Design.DataContext>
         <viewModels:MainWindowViewModel />
@@ -46,7 +45,7 @@
                     Margin="0,0,5,0"
                     VerticalAlignment="Center"
                     Background="Transparent"
-                    Command="{ReflectionBinding LoadApplications}">
+                    Click="Refresh_OnClick">
                     <ui:SymbolIcon
                         Width="50"
                         Height="100"
@@ -64,7 +63,7 @@
                     Grid.Column="2"
                     Height="6"
                     VerticalAlignment="Center"
-                    Foreground="{DynamicResource HighlightColor}"
+                    Foreground="{DynamicResource SystemAccentColorLight2}"
                     IsVisible="{Binding StatusBarVisible}"
                     Maximum="{Binding StatusBarProgressMaximum}"
                     Value="{Binding StatusBarProgressValue}" />
@@ -93,6 +92,7 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -109,6 +109,7 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -125,20 +126,32 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
-            <ui:ToggleSplitButton
+            <ToggleSplitButton
                 Name="VolumeStatus"
                 Padding="5,0,5,0"
                 HorizontalAlignment="Left"
                 VerticalAlignment="Center"
                 VerticalContentAlignment="Center"
-                Background="{DynamicResource ThemeContentBackgroundColor}"
-                BorderThickness="0"
                 Content="{Binding VolumeStatusText}"
                 IsChecked="{Binding VolumeMuted}"
-                IsVisible="{Binding !ShowLoadProgress}">
-                <ui:ToggleSplitButton.Flyout>
+                IsVisible="{Binding !ShowLoadProgress}"
+                Background="Transparent"
+                BorderThickness="0"
+                CornerRadius="0">
+                <ToggleSplitButton.Styles>
+                    <Style Selector=":checked">
+                        <Style Selector="^:checked ContentPresenter">
+                            <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
+                        </Style>
+                    </Style>
+                    <Style Selector="Border#SeparatorBorder">
+                        <Setter Property="Opacity" Value="0" />
+                    </Style>
+                </ToggleSplitButton.Styles>
+                <ToggleSplitButton.Flyout>
                     <Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
                         <Grid Margin="0">
                             <Slider
@@ -156,13 +169,14 @@
                                 Value="{Binding Volume}" />
                         </Grid>
                     </Flyout>
-                </ui:ToggleSplitButton.Flyout>
-            </ui:ToggleSplitButton>
+                </ToggleSplitButton.Flyout>
+            </ToggleSplitButton>
             <Border
                 Width="2"
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -177,6 +191,7 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -191,6 +206,7 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -205,6 +221,7 @@
                 Height="12"
                 Margin="0"
                 BorderBrush="Gray"
+                Background="Gray"
                 BorderThickness="1"
                 IsVisible="{Binding !ShowLoadProgress}" />
             <TextBlock
@@ -229,4 +246,4 @@
                 Text="{locale:Locale StatusBarSystemVersion}" />
         </StackPanel>
     </Grid>
-</UserControl>
+</UserControl>

+ 6 - 0
src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
@@ -48,5 +49,10 @@ namespace Ryujinx.Ava.UI.Views.Main
 
             ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
         }
+
+        private void Refresh_OnClick(object sender, RoutedEventArgs e)
+        {
+            Window.LoadApplications();
+        }
     }
 }

+ 6 - 7
src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml

@@ -9,7 +9,6 @@
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
     x:Class="Ryujinx.Ava.UI.Views.Main.MainViewControls"
-    x:CompileBindings="True"
     x:DataType="viewModels:MainWindowViewModel">
     <Design.DataContext>
         <viewModels:MainWindowViewModel />
@@ -23,7 +22,7 @@
             MinWidth="40"
             Margin="5,2,0,2"
             VerticalAlignment="Stretch"
-            Command="{ReflectionBinding SetListMode}"
+            Command="{Binding SetListMode}"
             IsEnabled="{Binding IsGrid}">
             <ui:FontIcon
                 Margin="0"
@@ -37,7 +36,7 @@
             MinWidth="40"
             Margin="5,2,5,2"
             VerticalAlignment="Stretch"
-            Command="{ReflectionBinding SetGridMode}"
+            Command="{Binding SetGridMode}"
             IsEnabled="{Binding IsList}">
             <ui:FontIcon
                 Margin="0"
@@ -79,13 +78,13 @@
             KeyUp="SearchBox_OnKeyUp"
             Text="{Binding SearchText}"
             Watermark="{locale:Locale MenuSearch}" />
-        <ui:DropDownButton
+        <DropDownButton
             Width="150"
             HorizontalAlignment="Right"
             VerticalAlignment="Center"
             Content="{Binding SortName}"
             DockPanel.Dock="Right">
-            <ui:DropDownButton.Flyout>
+            <DropDownButton.Flyout>
                 <Flyout Placement="Bottom">
                     <StackPanel
                         Margin="0"
@@ -164,8 +163,8 @@
                             Tag="Descending" />
                     </StackPanel>
                 </Flyout>
-            </ui:DropDownButton.Flyout>
-        </ui:DropDownButton>
+            </DropDownButton.Flyout>
+        </DropDownButton>
         <TextBlock
             Margin="10,0"
             HorizontalAlignment="Right"

+ 1 - 2
src/Ryujinx.Ava/UI/Views/Settings/SettingsAudioView.axaml

@@ -1,4 +1,4 @@
-<UserControl 
+<UserControl
     x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsAudioView"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -8,7 +8,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />

+ 0 - 1
src/Ryujinx.Ava/UI/Views/Settings/SettingsCPUView.axaml

@@ -7,7 +7,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />

+ 2 - 3
src/Ryujinx.Ava/UI/Views/Settings/SettingsGraphicsView.axaml

@@ -1,4 +1,4 @@
-<UserControl 
+<UserControl
     x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -9,7 +9,6 @@
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     Design.Width="1000"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />
@@ -54,7 +53,7 @@
                                 HorizontalContentAlignment="Left"
                                 ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
                                 SelectedIndex="{Binding PreferredGpuIndex}"
-                                Items="{Binding AvailableGpus}"/>
+                                ItemsSource="{Binding AvailableGpus}"/>
                     </StackPanel>
                 </StackPanel>
                 <Separator Height="1" />

+ 2 - 3
src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml

@@ -1,4 +1,4 @@
-<UserControl 
+<UserControl
     x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -8,7 +8,6 @@
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel"
     Focusable="True">
     <Design.DataContext>
@@ -17,7 +16,7 @@
     <UserControl.Resources>
         <helpers:KeyValueConverter x:Key="Key" />
     </UserControl.Resources>
-    <ScrollViewer 
+    <ScrollViewer
         Name="HotkeysPage"
         HorizontalAlignment="Stretch"
         VerticalAlignment="Stretch"

+ 1 - 1
src/Ryujinx.Ava/UI/Views/Settings/SettingsHotkeysView.axaml.cs

@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
                 {
                     _currentAssigner = new ButtonKeyAssigner(button);
 
-                    FocusManager.Instance?.Focus(this, NavigationMethod.Pointer);
+                    this.Focus(NavigationMethod.Pointer);
 
                     PointerPressed += MouseClick;
 

+ 0 - 1
src/Ryujinx.Ava/UI/Views/Settings/SettingsInputView.axaml

@@ -8,7 +8,6 @@
     xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />

+ 0 - 1
src/Ryujinx.Ava/UI/Views/Settings/SettingsLoggingView.axaml

@@ -8,7 +8,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />

+ 1 - 2
src/Ryujinx.Ava/UI/Views/Settings/SettingsNetworkView.axaml

@@ -7,7 +7,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />
@@ -37,7 +36,7 @@
                     <ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
                               ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
                               HorizontalContentAlignment="Left"
-                              Items="{Binding NetworkInterfaceList}"
+                              ItemsSource="{Binding NetworkInterfaceList}"
                               Width="250" />
                 </StackPanel>
             </StackPanel>

+ 71 - 39
src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml

@@ -3,12 +3,15 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
-    x:CompileBindings="True"
-    x:DataType="viewModels:SettingsViewModel"
-    mc:Ignorable="d">
+    xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
+    mc:Ignorable="d"
+    x:DataType="viewModels:SettingsViewModel">
+    <UserControl.Resources>
+        <helpers:TimeZoneConverter x:Key="TimeZone" />
+    </UserControl.Resources>
     <Design.DataContext>
         <viewModels:SettingsViewModel />
     </Design.DataContext>
@@ -24,18 +27,24 @@
                 HorizontalAlignment="Stretch"
                 Orientation="Vertical"
                 Spacing="10">
-                <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
-                <StackPanel Margin="10,0,0,0" Orientation="Vertical">
-                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+                <TextBlock
+                    Classes="h1"
+                    Text="{locale:Locale SettingsTabSystemCore}" />
+                <StackPanel
+                    Margin="10,0,0,0"
+                    Orientation="Vertical">
+                    <StackPanel
+                        Margin="0,0,0,10"
+                        Orientation="Horizontal">
                         <TextBlock
-                            Width="250"
                             VerticalAlignment="Center"
-                            Text="{locale:Locale SettingsTabSystemSystemRegion}" />
+                            Text="{locale:Locale SettingsTabSystemSystemRegion}"
+                            Width="250" />
                         <ComboBox
-                            Width="350"
-                            HorizontalContentAlignment="Left"
                             SelectedIndex="{Binding Region}"
-                            ToolTip.Tip="{locale:Locale RegionTooltip}">
+                            ToolTip.Tip="{locale:Locale RegionTooltip}"
+                            HorizontalContentAlignment="Left"
+                            Width="350">
                             <ComboBoxItem>
                                 <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
                             </ComboBoxItem>
@@ -59,17 +68,19 @@
                             </ComboBoxItem>
                         </ComboBox>
                     </StackPanel>
-                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+                    <StackPanel
+                        Margin="0,0,0,10"
+                        Orientation="Horizontal">
                         <TextBlock
-                            Width="250"
                             VerticalAlignment="Center"
                             Text="{locale:Locale SettingsTabSystemSystemLanguage}"
-                            ToolTip.Tip="{locale:Locale LanguageTooltip}" />
+                            ToolTip.Tip="{locale:Locale LanguageTooltip}"
+                            Width="250" />
                         <ComboBox
-                            Width="350"
-                            HorizontalContentAlignment="Left"
                             SelectedIndex="{Binding Language}"
-                            ToolTip.Tip="{locale:Locale LanguageTooltip}">
+                            ToolTip.Tip="{locale:Locale LanguageTooltip}"
+                            HorizontalContentAlignment="Left"
+                            Width="350">
                             <ComboBoxItem>
                                 <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
                             </ComboBoxItem>
@@ -126,63 +137,84 @@
                             </ComboBoxItem>
                         </ComboBox>
                     </StackPanel>
-                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+                    <StackPanel
+                        Margin="0,0,0,10"
+                        Orientation="Horizontal">
                         <TextBlock
-                            Width="250"
                             VerticalAlignment="Center"
                             Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
-                            ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
+                            ToolTip.Tip="{locale:Locale TimezoneTooltip}"
+                            Width="250" />
                         <AutoCompleteBox
                             Name="TimeZoneBox"
                             Width="350"
-                            FilterMode="Contains"
-                            Items="{Binding TimeZones}"
                             MaxDropDownHeight="500"
+                            FilterMode="Contains"
+                            ItemsSource="{Binding TimeZones}"
                             SelectionChanged="TimeZoneBox_OnSelectionChanged"
                             Text="{Binding Path=TimeZone, Mode=OneWay}"
                             TextChanged="TimeZoneBox_OnTextChanged"
-                            ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
+                            ToolTip.Tip="{locale:Locale TimezoneTooltip}" 
+                            ValueMemberBinding="{Binding Mode=OneWay, Converter={StaticResource TimeZone}}" />
                     </StackPanel>
-                    <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
+                    <StackPanel
+                        Margin="0,0,0,10"
+                        Orientation="Horizontal">
                         <TextBlock
-                            Width="250"
                             VerticalAlignment="Center"
                             Text="{locale:Locale SettingsTabSystemSystemTime}"
-                            ToolTip.Tip="{locale:Locale TimeTooltip}" />
+                            ToolTip.Tip="{locale:Locale TimeTooltip}"
+                            Width="250"/>
                         <DatePicker
-                            Width="350"
-                            VerticalAlignment="Center"
+                            VerticalAlignment="Center" 
                             SelectedDate="{Binding CurrentDate}"
-                            ToolTip.Tip="{locale:Locale TimeTooltip}" />
+                            ToolTip.Tip="{locale:Locale TimeTooltip}"
+                            Width="350" />
                     </StackPanel>
-                    <StackPanel Margin="250,0,0,10" Orientation="Horizontal">
+                    <StackPanel
+                        Margin="250,0,0,10"
+                        Orientation="Horizontal">
                         <TimePicker
-                            Width="350"
                             VerticalAlignment="Center"
                             ClockIdentifier="24HourClock"
                             SelectedTime="{Binding CurrentTime}"
+                            Width="350"
                             ToolTip.Tip="{locale:Locale TimeTooltip}" />
                     </StackPanel>
                     <CheckBox IsChecked="{Binding EnableVsync}">
-                        <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
+                        <TextBlock
+                            Text="{locale:Locale SettingsTabSystemEnableVsync}"
+                            ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
                     </CheckBox>
                     <CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
-                        <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
+                        <TextBlock
+                            Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
+                            ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
                     </CheckBox>
                 </StackPanel>
                 <Separator Height="1" />
-                <StackPanel Orientation="Vertical" Spacing="2">
-                    <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
-                    <TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabSystemHacksNote}" />
+                <StackPanel
+                    Orientation="Vertical"
+                    Spacing="2">
+                    <TextBlock
+                        Classes="h1"
+                        Text="{locale:Locale SettingsTabSystemHacks}" />
+                    <TextBlock
+                        Foreground="{DynamicResource SecondaryTextColor}"
+                        Text="{locale:Locale SettingsTabSystemHacksNote}" />
                 </StackPanel>
                 <StackPanel
                     Margin="10,0,0,0"
                     HorizontalAlignment="Stretch"
                     Orientation="Vertical">
-                    <CheckBox IsChecked="{Binding ExpandDramSize}" ToolTip.Tip="{locale:Locale DRamTooltip}">
+                    <CheckBox
+                        IsChecked="{Binding ExpandDramSize}"
+                        ToolTip.Tip="{locale:Locale DRamTooltip}">
                         <TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
                     </CheckBox>
-                    <CheckBox IsChecked="{Binding IgnoreMissingServices}" ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
+                    <CheckBox
+                        IsChecked="{Binding IgnoreMissingServices}"
+                        ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
                         <TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
                     </CheckBox>
                 </StackPanel>

+ 2 - 17
src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs

@@ -1,9 +1,5 @@
 using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Data.Converters;
 using Ryujinx.Ava.UI.ViewModels;
-using System;
-using System.Linq;
 using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
 
 namespace Ryujinx.Ava.UI.Views.Settings
@@ -15,15 +11,6 @@ namespace Ryujinx.Ava.UI.Views.Settings
         public SettingsSystemView()
         {
             InitializeComponent();
-
-            FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0}  {1}   {2}", parts.ToArray()).Trim());
-            MultiBinding tzMultiBinding = new() { Converter = converter };
-
-            tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
-            tzMultiBinding.Bindings.Add(new Binding("Location"));
-            tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
-
-            TimeZoneBox.ValueMemberBinding = tzMultiBinding;
         }
 
         private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -39,13 +26,11 @@ namespace Ryujinx.Ava.UI.Views.Settings
             }
         }
 
-        private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
+        private void TimeZoneBox_OnTextChanged(object sender, TextChangedEventArgs e)
         {
             if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone)
             {
-                {
-                    ViewModel.ValidateAndSetTimeZone(timeZone.Location);
-                }
+                ViewModel.ValidateAndSetTimeZone(timeZone.Location);
             }
         }
     }

+ 1 - 2
src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml

@@ -7,7 +7,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
         <viewModels:SettingsViewModel />
@@ -66,7 +65,7 @@
                     <ListBox
                         Name="GameList"
                         MinHeight="230"
-                        Items="{Binding GameDirectories}">
+                        ItemsSource="{Binding GameDirectories}">
                         <ListBox.Styles>
                             <Style Selector="ListBoxItem">
                                 <Setter Property="Padding" Value="10" />

+ 23 - 17
src/Ryujinx.Ava/UI/Views/Settings/SettingsUIView.axaml.cs

@@ -1,7 +1,7 @@
-using Avalonia;
 using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Interactivity;
+using Avalonia.Platform.Storage;
+using Avalonia.VisualTree;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.UI.ViewModels;
 using System.Collections.Generic;
@@ -30,13 +30,16 @@ namespace Ryujinx.Ava.UI.Views.Settings
             }
             else
             {
-                if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+                if (this.GetVisualRoot() is Window window)
                 {
-                    path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
+                    var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+                    {
+                        AllowMultiple = false
+                    });
 
-                    if (!string.IsNullOrWhiteSpace(path))
+                    if (result.Count > 0)
                     {
-                        ViewModel.GameDirectories.Add(path);
+                        ViewModel.GameDirectories.Add(result[0].Path.LocalPath);
                         ViewModel.DirectoryChanged = true;
                     }
                 }
@@ -61,22 +64,25 @@ namespace Ryujinx.Ava.UI.Views.Settings
 
         public async void BrowseTheme(object sender, RoutedEventArgs e)
         {
-            var dialog = new OpenFileDialog
+            var window = this.GetVisualRoot() as Window;
+            var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
                 Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
                 AllowMultiple = false,
-            };
-
-            dialog.Filters.Add(new FileDialogFilter { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
-
-            if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                var file = await dialog.ShowAsync(desktop.MainWindow);
-
-                if (file != null && file.Length > 0)
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    ViewModel.CustomThemePath = file[0];
+                    new("xml")
+                    {
+                        Patterns = new[] { "*.xaml" },
+                        AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
+                        MimeTypes = new[] { "application/xaml+xml" }
+                    }
                 }
+            });
+
+            if (result.Count > 0)
+            {
+                ViewModel.CustomThemePath = result[0].Path.LocalPath;
             }
         }
     }

+ 0 - 1
src/Ryujinx.Ava/UI/Views/User/UserEditorView.axaml

@@ -13,7 +13,6 @@
     Padding="0"
     mc:Ignorable="d"
     Focusable="True"
-    x:CompileBindings="True"
     x:DataType="models:TempProfile">
     <UserControl.Resources>
         <helpers:BitmapArrayValueConverter x:Key="ByteImage" />

+ 2 - 3
src/Ryujinx.Ava/UI/Views/User/UserFirmwareAvatarSelectorView.axaml

@@ -12,7 +12,6 @@
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
-    x:CompileBindings="True"
     x:DataType="viewModels:UserFirmwareAvatarSelectorViewModel"
     Focusable="True">
     <Design.DataContext>
@@ -36,7 +35,7 @@
             BorderThickness="0"
             SelectedIndex="{Binding SelectedIndex}"
             Height="400"
-            Items="{Binding Images}"
+            ItemsSource="{Binding Images}"
             HorizontalAlignment="Stretch"
             VerticalAlignment="Center">
             <ListBox.ItemsPanel>
@@ -54,7 +53,7 @@
                     <Setter Property="MaxWidth" Value="85" />
                     <Setter Property="MinWidth" Value="85" />
                 </Style>
-                <Style Selector="ListBoxItem /template/ Border#SelectionIndicator">
+                <Style Selector="ListBoxItem /template/ Rectangle#SelectionIndicator">
                     <Setter Property="MinHeight" Value="70" />
                 </Style>
             </ListBox.Styles>

+ 0 - 1
src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml

@@ -9,7 +9,6 @@
     Focusable="True"
     mc:Ignorable="d"
     x:Class="Ryujinx.Ava.UI.Views.User.UserProfileImageSelectorView"
-    x:CompileBindings="True"
     x:DataType="viewModles:UserProfileImageSelectorViewModel"
     Width="500"
     d:DesignWidth="500">

+ 16 - 22
src/Ryujinx.Ava/UI/Views/User/UserProfileImageSelectorView.axaml.cs

@@ -1,5 +1,6 @@
 using Avalonia.Controls;
 using Avalonia.Interactivity;
+using Avalonia.Platform.Storage;
 using Avalonia.VisualTree;
 using FluentAvalonia.UI.Controls;
 using FluentAvalonia.UI.Navigation;
@@ -10,6 +11,7 @@ using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.HLE.FileSystem;
 using SixLabors.ImageSharp;
 using SixLabors.ImageSharp.Processing;
+using System.Collections.Generic;
 using System.IO;
 using Image = SixLabors.ImageSharp.Image;
 
@@ -63,33 +65,25 @@ namespace Ryujinx.Ava.UI.Views.User
 
         private async void Import_OnClick(object sender, RoutedEventArgs e)
         {
-            OpenFileDialog dialog = new();
-            dialog.Filters.Add(new FileDialogFilter
+            var window = this.GetVisualRoot() as Window;
+            var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
             {
-                Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
-                Extensions = { "jpg", "jpeg", "png", "bmp" },
-            });
-            dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } });
-            dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } });
-            dialog.Filters.Add(new FileDialogFilter { Name = "BMP", Extensions = { "bmp" } });
-
-            dialog.AllowMultiple = false;
-
-            string[] image = await dialog.ShowAsync(((TopLevel)_parent.GetVisualRoot()) as Window);
-
-            if (image != null)
-            {
-                if (image.Length > 0)
+                AllowMultiple = false,
+                FileTypeFilter = new List<FilePickerFileType>
                 {
-                    string imageFile = image[0];
-
-                    _profile.Image = ProcessProfileImage(File.ReadAllBytes(imageFile));
-
-                    if (_profile.Image != null)
+                    new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
                     {
-                        _parent.GoBack();
+                        Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
+                        AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
+                        MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" }
                     }
                 }
+            });
+
+            if (result.Count > 0)
+            {
+                _profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath));
+                _parent.GoBack();
             }
         }
 

+ 1 - 2
src/Ryujinx.Ava/UI/Views/User/UserRecovererView.axaml

@@ -12,7 +12,6 @@
     xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     x:Class="Ryujinx.Ava.UI.Views.User.UserRecovererView"
-    x:CompileBindings="True"
     x:DataType="viewModels:UserProfileViewModel"
     Focusable="True">
     <Design.DataContext>
@@ -33,7 +32,7 @@
                 <ListBox
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
-                    Items="{Binding LostProfiles}">
+                    ItemsSource="{Binding LostProfiles}">
                     <ListBox.ItemTemplate>
                         <DataTemplate>
                             <Border

+ 2 - 4
src/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml

@@ -14,7 +14,6 @@
     Height="450"
     Width="550"
     x:Class="Ryujinx.Ava.UI.Views.User.UserSaveManagerView"
-    x:CompileBindings="True"
     x:DataType="viewModels:UserSaveManagerViewModel"
     Focusable="True">
     <Design.DataContext>
@@ -107,8 +106,7 @@
             VerticalAlignment="Stretch">
             <ListBox
                 Name="SaveList"
-                VirtualizationMode="None"
-                Items="{Binding Views}"
+                ItemsSource="{Binding Views}"
                 HorizontalAlignment="Stretch"
                 VerticalAlignment="Stretch">
                 <ListBox.Styles>
@@ -117,7 +115,7 @@
                         <Setter Property="Margin" Value="5" />
                         <Setter Property="CornerRadius" Value="4" />
                     </Style>
-                    <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
+                    <Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
                         <Setter Property="IsVisible" Value="False" />
                     </Style>
                 </ListBox.Styles>

+ 4 - 5
src/Ryujinx.Ava/UI/Views/User/UserSelectorView.axaml

@@ -15,7 +15,6 @@
     d:DesignWidth="800"
     mc:Ignorable="d"
     Focusable="True"
-    x:CompileBindings="True"
     x:DataType="viewModels:UserProfileViewModel">
     <UserControl.Resources>
         <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
@@ -38,7 +37,7 @@
                 VerticalAlignment="Center"
                 SelectionChanged="ProfilesList_SelectionChanged"
                 Background="Transparent"
-                Items="{Binding Profiles}">
+                ItemsSource="{Binding Profiles}">
                 <ListBox.ItemsPanel>
                     <ItemsPanelTemplate>
                         <flex:FlexPanel
@@ -53,7 +52,7 @@
                         <Setter Property="Margin" Value="5 5 0 5" />
                         <Setter Property="CornerRadius" Value="5" />
                     </Style>
-                    <Style Selector="Border#SelectionIndicator">
+                    <Style Selector="Rectangle#SelectionIndicator">
                         <Setter Property="Opacity" Value="0" />
                     </Style>
                 </ListBox.Styles>
@@ -61,8 +60,8 @@
                     <DataTemplate
                         DataType="models:UserProfile">
                         <Grid
-                            PointerEnter="Grid_PointerEntered"
-                            PointerLeave="Grid_OnPointerExited">
+                            PointerEntered="Grid_PointerEntered"
+                            PointerExited="Grid_OnPointerExited">
                             <Border
                                 HorizontalAlignment="Stretch"
                                 VerticalAlignment="Stretch"

+ 3 - 4
src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml

@@ -13,7 +13,6 @@
     Margin="0,-12,0,0"
     d:DesignHeight="260"
     d:DesignWidth="550"
-    x:CompileBindings="True"
     x:DataType="viewModel:AboutWindowViewModel"
     Focusable="True"
     mc:Ignorable="d">
@@ -64,14 +63,14 @@
                             FontWeight="Bold"
                             Text="Ryujinx"
                             TextAlignment="Center"
-                            Width="100" />
+                            Width="110" />
                         <TextBlock
                             HorizontalAlignment="Center"
                             VerticalAlignment="Center"
                             FontSize="11"
                             Text="(REE-YOU-JINX)"
                             TextAlignment="Center"
-                            Width="100" />
+                            Width="110" />
                     </flex:FlexPanel>
                 </Grid>
                 <TextBlock
@@ -265,4 +264,4 @@
             </StackPanel>
         </Grid>
     </Grid>
-</UserControl>
+</UserControl>

+ 8 - 7
src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml

@@ -1,4 +1,4 @@
-<window:StyleableWindow 
+<window:StyleableWindow
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -6,14 +6,15 @@
     xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
-    mc:Ignorable="d" 
-    d:DesignWidth="400" 
+    mc:Ignorable="d"
+    d:DesignWidth="400"
     d:DesignHeight="350"
     x:Class="Ryujinx.Ava.UI.Windows.AmiiboWindow"
+    x:DataType="viewModels:AmiiboWindowViewModel"
     CanResize="False"
     WindowStartupLocation="CenterOwner"
-    Width="800" 
-    MinHeight="650" 
+    Width="800"
+    MinHeight="650"
     Height="650"
     SizeToContent="Manual"
     MinWidth="600"
@@ -35,11 +36,11 @@
             </Grid.ColumnDefinitions>
             <StackPanel Spacing="10" Orientation="Horizontal" HorizontalAlignment="Left">
                 <TextBlock VerticalAlignment="Center" Text="{locale:Locale AmiiboSeriesLabel}" />
-                <ComboBox SelectedIndex="{Binding SeriesSelectedIndex}" Items="{Binding AmiiboSeries}" MinWidth="100" />
+                <ComboBox SelectedIndex="{Binding SeriesSelectedIndex}" ItemsSource="{Binding AmiiboSeries}" MinWidth="100" />
             </StackPanel>
             <StackPanel Spacing="10" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
                 <TextBlock VerticalAlignment="Center" Text="{locale:Locale AmiiboCharacterLabel}" />
-                <ComboBox SelectedIndex="{Binding AmiiboSelectedIndex}" MinWidth="100" Items="{Binding AmiiboList}" />
+                <ComboBox SelectedIndex="{Binding AmiiboSelectedIndex}" MinWidth="100" ItemsSource="{Binding AmiiboList}" />
             </StackPanel>
         </Grid>
         <StackPanel Margin="20" Grid.Row="2">

+ 6 - 6
src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml

@@ -40,7 +40,7 @@
             HorizontalAlignment="Center"
             VerticalAlignment="Center"
             LineHeight="18"
-            Text="{Binding Heading}"
+            Text="{ReflectionBinding Heading}"
             TextAlignment="Center"
             TextWrapping="Wrap" />
         <TextBlock
@@ -61,7 +61,7 @@
             MinWidth="160"
             HorizontalAlignment="Center"
             VerticalAlignment="Center"
-            Text="{Binding BuildId}"
+            Text="{ReflectionBinding BuildId}"
             IsReadOnly="True" />
         <Border
             Grid.Row="3"
@@ -77,7 +77,7 @@
                 MinHeight="300"
                 HorizontalAlignment="Stretch"
                 VerticalAlignment="Stretch"
-                Items="{Binding LoadedCheats}">
+                ItemsSource="{ReflectionBinding LoadedCheats}">
                 <TreeView.Styles>
                     <Styles>
                         <Style Selector="TreeViewItem:empty /template/ ItemsPresenter">
@@ -120,15 +120,15 @@
                     Name="SaveButton"
                     MinWidth="90"
                     Margin="5"
-                    Command="{Binding Save}"
-                    IsVisible="{Binding !NoCheatsFound}">
+                    Command="{ReflectionBinding Save}"
+                    IsVisible="{ReflectionBinding !NoCheatsFound}">
                     <TextBlock Text="{locale:Locale SettingsButtonSave}" />
                 </Button>
                 <Button
                     Name="CancelButton"
                     MinWidth="90"
                     Margin="5"
-                    Command="{Binding Close}">
+                    Command="{ReflectionBinding Close}">
                     <TextBlock Text="{locale:Locale InputDialogCancel}" />
                 </Button>
             </DockPanel>

+ 2 - 7
src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs

@@ -1,8 +1,5 @@
 using Avalonia.Controls;
 using Avalonia.Media;
-#if DEBUG
-using Avalonia;
-#endif
 
 namespace Ryujinx.Ava.UI.Windows
 {
@@ -11,11 +8,9 @@ namespace Ryujinx.Ava.UI.Windows
         public ContentDialogOverlayWindow()
         {
             InitializeComponent();
-#if DEBUG
-            this.AttachDevTools();
-#endif
+
             ExtendClientAreaToDecorationsHint = true;
-            TransparencyLevelHint = WindowTransparencyLevel.Transparent;
+            TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent };
             WindowStartupLocation = WindowStartupLocation.Manual;
             SystemDecorations = SystemDecorations.None;
             ExtendClientAreaTitleBarHeightHint = 0;

+ 3 - 5
src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml

@@ -11,7 +11,6 @@
     Width="500"
     Height="380"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:DownloadableContentManagerViewModel"
     Focusable="True">
     <Grid>
@@ -53,8 +52,8 @@
                 </StackPanel>
                 <TextBox
                     Grid.Column="2"
-                    MinHeight="27"
-                    MaxHeight="27"
+                    MinHeight="29"
+                    MaxHeight="29"
                     HorizontalAlignment="Stretch"
                     Watermark="{locale:Locale Search}"
                     Text="{Binding Search}" />
@@ -71,12 +70,11 @@
             Padding="2.5">
             <ListBox
                 AutoScrollToSelectedItem="False"
-                VirtualizationMode="None"
                 SelectionMode="Multiple, Toggle"
                 Background="Transparent"
                 SelectionChanged="OnSelectionChanged"
                 SelectedItems="{Binding SelectedDownloadableContents, Mode=TwoWay}"
-                Items="{Binding Views}">
+                ItemsSource="{Binding Views}">
                 <ListBox.DataTemplates>
                     <DataTemplate
                         DataType="models:DownloadableContentModel">

+ 10 - 10
src/Ryujinx.Ava/UI/Windows/MainWindow.axaml

@@ -18,9 +18,9 @@
     MinHeight="672"
     d:DesignHeight="720"
     d:DesignWidth="1280"
-    x:CompileBindings="True"
     x:DataType="viewModels:MainWindowViewModel"
     mc:Ignorable="d"
+    WindowStartupLocation="Manual"
     Focusable="True">
     <Window.Styles>
         <Style Selector="TitleBar:fullscreen">
@@ -33,19 +33,19 @@
     <Window.Resources>
         <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
     </Window.Resources>
+    <Window.KeyBindings>
+        <KeyBinding Gesture="Alt+Return" Command="{Binding ToggleFullscreen}" />
+        <KeyBinding Gesture="F11" Command="{Binding ToggleFullscreen}" />
+        <KeyBinding Gesture="Ctrl+Cmd+F" Command="{Binding ToggleFullscreen}" />
+        <KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" />
+        <KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
+    </Window.KeyBindings>
     <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto" />
             <RowDefinition Height="*" />
         </Grid.RowDefinitions>
         <helpers:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" />
-        <StackPanel Grid.Row="0" IsVisible="False">
-            <helpers:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" />
-            <helpers:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" />
-            <helpers:HotKeyControl Name="FullscreenHotKeyMacOS" Command="{ReflectionBinding ToggleFullscreen}" />
-            <helpers:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" />
-            <helpers:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" />
-        </StackPanel>
         <Grid
             Grid.Row="1"
             HorizontalAlignment="Stretch"
@@ -67,7 +67,7 @@
                 VerticalAlignment="Stretch"
                 IsVisible="{Binding ShowMenuAndStatusBar}"
                 Orientation="Vertical">
-                <main:MainMenuBarView 
+                <main:MainMenuBarView
                     Name="MenuBarView" />
             </StackPanel>
             <ContentControl
@@ -197,7 +197,7 @@
                     </Grid>
                 </Grid>
             </Grid>
-            <main:MainStatusBarView 
+            <main:MainStatusBarView
                 Name="StatusBarView"
                 Grid.Row="2" />
         </Grid>

+ 33 - 32
src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Controls;
-using Avalonia.Input;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
 using Avalonia.Threading;
 using FluentAvalonia.UI.Controls;
 using Ryujinx.Ava.Common;
@@ -21,7 +22,6 @@ using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Helper;
 using System;
-using System.ComponentModel;
 using System.IO;
 using System.Runtime.Versioning;
 using System.Threading.Tasks;
@@ -85,6 +85,7 @@ namespace Ryujinx.Ava.UI.Windows
 
                 ViewModel.Initialize(
                     ContentManager,
+                    StorageProvider,
                     ApplicationLibrary,
                     VirtualFileSystem,
                     AccountManager,
@@ -102,11 +103,16 @@ namespace Ryujinx.Ava.UI.Windows
                 LoadGameList();
 
                 this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
+                this.ScalingChanged += OnScalingChanged;
             }
 
             ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
             ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
-            ViewModel.ReloadGameList += ReloadGameList;
+        }
+
+        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+        {
+            base.OnApplyTemplate(e);
 
             NotificationHelper.SetNotificationManager(this);
         }
@@ -130,10 +136,9 @@ namespace Ryujinx.Ava.UI.Windows
             _isLoading = false;
         }
 
-        protected override void HandleScalingChanged(double scale)
+        private void OnScalingChanged(object sender, EventArgs e)
         {
-            Program.DesktopScaleFactor = scale;
-            base.HandleScalingChanged(scale);
+            Program.DesktopScaleFactor = this.RenderScaling;
         }
 
         public void AddApplication(ApplicationData applicationData)
@@ -221,16 +226,6 @@ namespace Ryujinx.Ava.UI.Windows
             });
         }
 
-        protected override void HandleWindowStateChanged(WindowState state)
-        {
-            ViewModel.WindowState = state;
-
-            if (state != WindowState.Minimized)
-            {
-                Renderer.Start();
-            }
-        }
-
         private void Initialize()
         {
             _userChannelPersistence = new UserChannelPersistence();
@@ -367,14 +362,12 @@ namespace Ryujinx.Ava.UI.Windows
             ApplicationList.ApplicationOpened += Application_Opened;
 
             ApplicationList.DataContext = ViewModel;
-
-            LoadHotKeys();
         }
 
         private void SetWindowSizePosition()
         {
             PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
-                                                   ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
+                                        ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
 
             ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
             ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
@@ -447,18 +440,7 @@ namespace Ryujinx.Ava.UI.Windows
 #pragma warning restore IDE0055
         }
 
-        public void LoadHotKeys()
-        {
-#pragma warning disable IDE0055 // Disable formatting
-            HotKeyManager.SetHotKey(FullscreenHotKey,      new KeyGesture(Key.Enter, KeyModifiers.Alt));
-            HotKeyManager.SetHotKey(FullscreenHotKey2,     new KeyGesture(Key.F11));
-            HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta));
-            HotKeyManager.SetHotKey(DockToggleHotKey,      new KeyGesture(Key.F9));
-            HotKeyManager.SetHotKey(ExitHotKey,            new KeyGesture(Key.Escape));
-#pragma warning restore IDE0055
-        }
-
-        private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)
+        private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e)
         {
             var volumeSplitButton = sender as ToggleSplitButton;
             if (ViewModel.IsGameRunning)
@@ -476,7 +458,7 @@ namespace Ryujinx.Ava.UI.Windows
             }
         }
 
-        protected override void OnClosing(CancelEventArgs e)
+        protected override void OnClosing(WindowClosingEventArgs e)
         {
             if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
             {
@@ -548,6 +530,25 @@ namespace Ryujinx.Ava.UI.Windows
             ReloadGameList();
         }
 
+        public void ToggleFileType(string fileType)
+        {
+            _ = fileType switch
+            {
+#pragma warning disable IDE0055 // Disable formatting
+                "NSP"  => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
+                "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
+                "XCI"  => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
+                "NCA"  => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
+                "NRO"  => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
+                "NSO"  => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value  = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
+                _  => throw new ArgumentOutOfRangeException(fileType),
+#pragma warning restore IDE0055
+            };
+
+            ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
+            LoadApplications();
+        }
+
         private void ReloadGameList()
         {
             if (_isLoading)

+ 14 - 15
src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml

@@ -15,7 +15,6 @@
     MinWidth="800"
     MinHeight="480"
     WindowStartupLocation="CenterOwner"
-    x:CompileBindings="True"
     x:DataType="viewModels:SettingsViewModel"
     mc:Ignorable="d"
     Focusable="True">
@@ -59,44 +58,44 @@
                     IsSelected="True"
                     Content="{locale:Locale SettingsTabGeneral}"
                     Tag="UiPage"
-                    Icon="New" />
+                    IconSource="New" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabInput}"
                     Tag="InputPage"
-                    Icon="Games" />
+                    IconSource="Games" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabHotkeys}"
                     Tag="HotkeysPage"
-                    Icon="Keyboard" />
+                    IconSource="Keyboard" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabSystem}"
                     Tag="SystemPage"
-                    Icon="Settings" />
+                    IconSource="Settings" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabCpu}"
                     Tag="CpuPage">
-                    <ui:NavigationViewItem.Icon>
-                        <ui:FontIcon
+                    <ui:NavigationViewItem.IconSource>
+                        <ui:FontIconSource
                             FontFamily="avares://Ryujinx.Ava/Assets/Fonts#Segoe Fluent Icons"
                             Glyph="{helpers:GlyphValueConverter Chip}" />
-                    </ui:NavigationViewItem.Icon>
+                    </ui:NavigationViewItem.IconSource>
                 </ui:NavigationViewItem>
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabGraphics}"
                     Tag="GraphicsPage"
-                    Icon="Image" />
+                    IconSource="Image" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabAudio}"
-                    Icon="Audio"
+                    IconSource="Audio"
                     Tag="AudioPage" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabNetwork}"
                     Tag="NetworkPage"
-                    Icon="Globe" />
+                    IconSource="Globe" />
                 <ui:NavigationViewItem
                     Content="{locale:Locale SettingsTabLogging}"
                     Tag="LoggingPage"
-                    Icon="Document" />
+                    IconSource="Document" />
             </ui:NavigationView.MenuItems>
             <ui:NavigationView.Styles>
                 <Style Selector="Grid#PlaceholderGrid">
@@ -115,14 +114,14 @@
                 HotKey="Enter"
                 Classes="accent"
                 Content="{locale:Locale SettingsButtonOk}"
-                Command="{ReflectionBinding OkButton}" />
+                Command="{Binding OkButton}" />
             <Button
                 HotKey="Escape"
                 Content="{locale:Locale SettingsButtonCancel}"
-                Command="{ReflectionBinding CancelButton}" />
+                Command="{Binding CancelButton}" />
             <Button
                 Content="{locale:Locale SettingsButtonApply}"
-                Command="{ReflectionBinding ApplyButton}" />
+                Command="{Binding ApplyButton}" />
         </ReversibleStackPanel>
     </Grid>
 </window:StyleableWindow>

+ 3 - 3
src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

@@ -1,10 +1,10 @@
+using Avalonia.Controls;
 using FluentAvalonia.Core;
 using FluentAvalonia.UI.Controls;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.HLE.FileSystem;
 using System;
-using System.ComponentModel;
 
 namespace Ryujinx.Ava.UI.Windows
 {
@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Windows
 
             if (Owner is MainWindow window && ViewModel.DirectoryChanged)
             {
-                window.ViewModel.LoadApplications();
+                window.LoadApplications();
             }
         }
 
@@ -93,7 +93,7 @@ namespace Ryujinx.Ava.UI.Windows
             }
         }
 
-        protected override void OnClosing(CancelEventArgs e)
+        protected override void OnClosing(WindowClosingEventArgs e)
         {
             HotkeysPage.Dispose();
             InputPage.Dispose();

+ 2 - 2
src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs

@@ -11,12 +11,12 @@ namespace Ryujinx.Ava.UI.Windows
 {
     public class StyleableWindow : Window
     {
-        public IBitmap IconImage { get; set; }
+        public Bitmap IconImage { get; set; }
 
         public StyleableWindow()
         {
             WindowStartupLocation = WindowStartupLocation.CenterOwner;
-            TransparencyLevelHint = WindowTransparencyLevel.None;
+            TransparencyLevelHint = new[] { WindowTransparencyLevel.None };
 
             using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
 

+ 2 - 4
src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml

@@ -11,7 +11,6 @@
     Width="500"
     Height="300"
     mc:Ignorable="d"
-    x:CompileBindings="True"
     x:DataType="viewModels:TitleUpdateViewModel"
     Focusable="True">
     <Grid>
@@ -29,10 +28,9 @@
             CornerRadius="5"
             Padding="2.5">
             <ListBox
-                VirtualizationMode="None"
                 Background="Transparent"
                 SelectedItem="{Binding SelectedUpdate, Mode=TwoWay}"
-                Items="{Binding Views}">
+                ItemsSource="{Binding Views}">
                 <ListBox.DataTemplates>
                     <DataTemplate
                         DataType="models:TitleUpdateModel">
@@ -103,7 +101,7 @@
                 <Button
                     Name="AddButton"
                     MinWidth="90"
-                    Command="{ReflectionBinding Add}">
+                    Command="{Binding Add}">
                     <TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" />
                 </Button>
                 <Button

+ 1 - 1
src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs

@@ -61,7 +61,7 @@ namespace Ryujinx.Ava.UI.Windows
 
             if (VisualRoot is MainWindow window)
             {
-                window.ViewModel.LoadApplications();
+                window.LoadApplications();
             }
 
             ((ContentDialog)Parent).Hide();

+ 10 - 0
src/Ryujinx.Ava/app.manifest

@@ -0,0 +1,10 @@
+<?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="Ryujinx.Emulator.Avalonia"/>
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- Windows 10 & 11 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+</assembly>