Unreal Engine Archives Explorer
Go to file
Rob Trame 24c6a68cce
[P2-003] Migrate all 10 dialog Windows from WPF/AdonisUI to Avalonia (#70)
* feat(P2-003): migrate all 10 dialog Windows from WPF/AdonisUI to Avalonia

Resolves #16 [P2-003] Migrate all 11 XAML view windows to Avalonia Window.

Views migrated (XAML + code-behind):
- About: root Window, AdonisUI namespace/styles removed
- AesManager: root Window, BoolToVisibility→IsVisible, ListView→DataGrid
- AudioPlayer: root Window, CSCore TODO stubs, OpenFileDialog TODO stub
- BackupManager: root Window, IsNotZeroConverter for IsEnabled binding
- CustomDir: root Window, AdonisUI removals
- DirectorySelector: root Window, AdonisUI removals, Visibility→IsVisible
- ImageMerger: root Window, BitmapImage→Avalonia Bitmap, WPF dialogs→TODO stubs
- SearchView: root Window, ListView+GridView→DataGrid x2, ContextMenus
- SettingsView: root Window, DataTriggers→IsVisible/HyperlinkButton/converters,
  SelectedItemChanged→SelectionChanged, VistaFolderBrowserDialog→TODO stub
- UpdateView: root Window, adonisControls:SplitButton→SplitButton+MenuFlyout

Support:
- FModel.csproj: add Avalonia.Controls.DataGrid v11.3.12
- Add IsNotZeroConverter (Avalonia.Data.Converters IValueConverter)

* chore(P2-003): apply post-push refinements to migrated view files

* fix: address review findings from Avalonia migration code review

Critical:
- MainWindow.xaml.cs: unsubscribe all PropertyChanged handlers in OnClosing to
  prevent memory leak and use-after-free on restart (C2)
- MainWindow.xaml.cs: remove ConfigureAwait(false) from async void OnLoaded —
  continuation must stay on Avalonia UI thread (C1)

Major:
- MainWindow.xaml: replace WPF KeyboardNavigation.TabNavigation with
  Avalonia IsTabStop="False" on the feature-preview CheckBox (M3)
- MainWindow.xaml.cs: guard PixelDensity against zero in OnLoaded (m2)
- MainWindow.xaml.cs: add _syncingSelection re-entrancy guard to SyncSelection
  to prevent infinite SelectionChanged event loop (m4)
- App.xaml.cs: add try/catch inside ShowErrorDialog async lambda to prevent
  silent exception swallowing; add TaskScheduler.UnobservedTaskException
  handler in OnFrameworkInitializationCompleted (M1, S1)
- App.xaml.cs: remove redundant Environment.Exit(0) from AppExit handler (m1)
- Helper.cs: null-guard GetWindow<T> return value; add comment re: Show() race (S2)

Views (review fixes):
- SettingsView.xaml.cs: replace TryBrowse/OnBrowseMappings stubs with real
  StorageProvider implementations (OpenFolderPickerAsync, OpenFilePickerAsync)
- ImageMerger.xaml.cs: fix async void + MemoryStream disposal in OnExport
- UpdateView.xaml: fix Grid nesting of ItemsControl + empty-state TextBlock;
  add IsNotZeroConverter-based empty-state visibility
- CustomDir.xaml/cs, DirectorySelector.xaml/cs: cancel button handlers
- Converters: migrate System.Windows.Data IValueConverter usings to
  Avalonia.Data.Converters for BoolToToggleConverter, EndpointToTypeConverter,
  EnumToStringConverter, IsNotZeroConverter, SizeToStringConverter

* fix: address second-round review findings

C1 - SettingsView.xaml.cs: replace WPF editor ShowDialog() calls with
  async void stubs + TODO(P2-016) comments; DictionaryEditor and
  EndpointEditor are not yet migrated to Avalonia so these used the
  WPF synchronous ShowDialog() which is unavailable and returns the
  wrong type

C2 - UpdateView.xaml: add TODO noting CommitsView is still a WPF
  System.Windows.Data.ListCollectionView; once UpdateViewModel is
  migrated the Count binding should be replaced with a dedicated
  HasNoCommits bool property for reliable INPC notification

M1+S1 - Helper.cs: null-guard GetOpenedWindow result in OpenWindow<T>
  to prevent NRE if the window deregisters between IsWindowOpen and
  the dereference

M2 - MainWindow.xaml.cs: add TODO tech-debt comment on the double
  UpdateStatusLabel dispatch when Kind changes; note consolidation
  into a single UpdateStatusUI() for a later pass

M3 - ImageMerger.xaml.cs: replace ContinueWith(..., FromCurrentSync-
  Context()) with try/finally after await Task.Run; eliminates the
  synchronization-context capture hazard when called via ConfigureAwait

m1 - App.xaml.cs: UnobservedTaskException now calls ShowErrorDialog
  for unexpected faults; silently absorbs OperationCanceledException;
  SetObserved() moved before the early-return guard

m2 - IsNotZeroConverter.cs: fix 2-space indentation to 4-space to
  match rest of codebase

m3 - EndpointToTypeConverter.cs: replace misleading NotImplemented-
  Exception with ArgumentException carrying a descriptive message

* fix: address third-round review findings

- App.xaml.cs: check all InnerExceptions for OperationCanceledException,
  not just InnerException[0], so a mixed AggregateException is never
  silently absorbed (m1)
- Helper.cs: add Dispatcher.UIThread.RunJobs() between Show() and the
  window-registry lookup in GetWindow<T> to close the registration race
  on first open (S1); also add missing System.Collections.Generic using
  that caused a pre-existing IEnumerable<> CS0246 error
- SettingsView.xaml.cs: clarify EndpointEditor TODO comments — original
  WPF code used ShowDialog() (modal), so migration target is
  ShowDialog(this), not Show() (m2)

* fix: add missing System.Linq using in App.xaml.cs

InnerExceptions.All() is a LINQ extension method. The project has no
implicit usings, so the explicit using was required. The build check
after the previous commit only grepped for Helper.cs errors, so the
CS1061 on App.xaml.cs was not caught until the follow-up review.

* fix: address PR reviewer findings across six view files

C1 - AudioPlayer.xaml: replace undeclared adonisUi:Brushes.ForegroundBrush
     DynamicResource (compile error) with Fill="White", consistent with all
     other icon paths in the file.

C2 - SettingsView.xaml.cs: fix OnBrowseMappings storing into non-existent
     UserSettings.Default.MappingsPath; now sets
     _applicationView.SettingsView.MappingEndpoint.FilePath which is the
     property the XAML TextBox is bound to (compile error + silent data loss).

C3 - UpdateView.xaml: remove WPF-only ItemsControl.GroupStyle / GroupStyle /
     GroupItem / HeaderStringFormat block (XAML parse failure on Avalonia).
     Date grouping will be re-added alongside the CommitsView ViewModel
     migration (see existing TODO). Also fix FallbackValue=False → 0 so
     IsNotZeroConverter receives the expected int type when the binding
     target can't be resolved.

M1 - ImageMerger.xaml.cs (UpButton case): remove spurious
     SelectedItems.Add(indices) call which was adding the raw int[] to the
     selected-items collection. Items are already marked IsSelected=true
     inside the move loop; the Add call was a no-op at best and a runtime
     inconsistency at worst.

M2 - DirectorySelector.xaml: remove self-referential
     Width={Binding ActualWidth, ElementName=OkGuysButWhoFuckingAsked}
     (control binding its own width to itself — produces binding warnings
     and has no useful effect; layout is determined by content/padding).

m1 - SearchView.xaml.cs: restore WPF DataTrigger behavior — subscribe to
     PropertyChanged on each SearchViewModel and update the corresponding
     TextBox.Watermark when HasRegexEnabled toggles, giving 'Write your
     regex pattern...' vs 'Write your pattern...' hint text.

* fix: unsubscribe PropertyChanged handlers in SearchView on close

SearchVm and RefVm are application-lifetime singletons. Subscribing
anonymous lambdas capturing 'this' (the window's TextBox fields)
meant each closed-and-reopened SearchView was kept permanently
alive by the singletons' PropertyChanged invocation lists.

Fix: promote the handlers to named local functions, cache the VM
references at construction time, and unsubscribe both handlers in
the Closed event so the window becomes eligible for GC immediately
after it is closed.

Also adds the required 'using System.ComponentModel' for the
PropertyChangedEventArgs parameter type.

* fix: migrate WPF converters to Avalonia.Data.Converters interfaces

CaseInsensitiveStringEqualsConverter: replace System.Windows.Data.IValueConverter
with Avalonia.Data.Converters.IValueConverter. Avalonia cannot call WPF converter
implementations, so any SettingsView.xaml binding using this converter would fail
silently at runtime. Also null-guards value/parameter before calling .ToString()
to avoid NREs on binding fallback values.

MultiParameterConverter: replace System.Windows.Data.IMultiValueConverter with
Avalonia.Data.Converters.IMultiValueConverter. The Avalonia interface takes
IList<object?> rather than object[], so update the signature accordingly. Return
values.ToArray() to preserve the same object[] snapshot that the WPF
values.Clone() call produced, keeping CommandParameter multi-binding callers in
SearchView.xaml working. Avalonia's IMultiValueConverter has no ConvertBack, so
that method is removed.
2026-03-14 15:02:31 -06:00
.github chore: update identity, branding, and CI for FModel Linux fork (#69) 2026-03-12 11:13:00 -06:00
.vscode Set up dev env 2026-03-11 15:51:34 -06:00
CUE4Parse@06fbf1aced chore: point CUE4Parse submodule at r6e/CUE4Parse-Linux (#66) 2026-03-11 19:49:17 -06:00
FModel [P2-003] Migrate all 10 dialog Windows from WPF/AdonisUI to Avalonia (#70) 2026-03-14 15:02:31 -06:00
.editorconfig chore: update identity, branding, and CI for FModel Linux fork (#69) 2026-03-12 11:13:00 -06:00
.gitignore Finish setting up env 2026-03-11 17:28:01 -06:00
.gitmodules chore: point CUE4Parse submodule at r6e/CUE4Parse-Linux (#66) 2026-03-11 19:49:17 -06:00
LICENSE Re-add GPL-3 license 2021-05-22 16:33:08 -04:00
NOTICE chore: update identity, branding, and CI for FModel Linux fork (#69) 2026-03-12 11:13:00 -06:00
README.md chore: update identity, branding, and CI for FModel Linux fork (#69) 2026-03-12 11:13:00 -06:00

FModel Linux — Unreal Engine Archives Explorer for Linux

CI Status Latest


This is an unofficial Linux port of FModel, originally created by Asval (4sval) and contributors. It is not affiliated with or endorsed by the upstream project or its maintainers. Please do not report Linux-port-specific issues upstream.

Description

FModel Linux is a Linux port of FModel — an archive explorer for Unreal Engine games. It uses CUE4Parse as its core parsing library, providing robust support for the latest UE4 and UE5 archive formats, along with a comprehensive set of tools for previewing and converting game packages.

This fork replaces the Windows-only WPF UI stack with Avalonia UI and removes other Windows-specific dependencies to enable native Linux support. It is maintained by r6e.

Installation

Installation instructions for the Linux port are available in the project wiki (work in progress).

For the upstream Windows release, refer to the official FModel installation guide.

Supporting the Upstream Project

This fork does not accept donations. If you find FModel valuable, please consider supporting the original FModel project and its contributors.

License

FModel Linux is a derivative work licensed under GPL-3. The original FModel project is copyright © Asval and FModel contributors. Licenses of third-party libraries used are listed in NOTICE.