-
Notifications
You must be signed in to change notification settings - Fork 0
Desktop UI
PCEdit.Desktop is the only UI head: Avalonia 12, running on Linux, Windows and macOS. It is
deliberately thin — a set of views, a handful of converters, and the implementations of the platform
interfaces App Core declares.
| Package | Role |
|---|---|
Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent, Avalonia.Fonts.Inter
|
The UI framework (kept in lockstep) |
CommunityToolkit.Mvvm |
ObservableObject, [ObservableProperty], [RelayCommand]
|
Microsoft.Extensions.DependencyInjection |
The composition root |
Microsoft.ICU.ICU4C.Runtime.linux-x64 |
App-local ICU — referenced for every build, but only a linux-x64 publish carries its libraries and loads them. See Building & Packaging
|
Avalonia.BuildServices |
Referenced only to exclude it — see below |
Exact versions live in PCEdit.Desktop.csproj, and the resolved graph — every transitive package,
with its content hash — in the committed packages.lock.json. Dependabot proposes updates weekly, with the
Avalonia packages grouped so they move together.
Avalonia pulls in Avalonia.BuildServices, whose targets run a task on every build that reports
to avaloniaui.net (hashed project and machine name, a machine GUID, OS, IDE, CI detection). It is
build-time only and never reaches a published PCEdit, but it fires on every contributor machine and
CI run. Since v1.4.3 the csproj references it directly with ExcludeAssets="all", which drops
its targets entirely, and CI fails if obj/*.nuget.g.targets ever imports it again. Its version is
pinned deliberately: an Avalonia upgrade that needs a newer one fails restore with NU1605 instead of
quietly re-enabling the reporting.
Program.cs → App.axaml.cs:
- Build the
IServiceProvider— Core services as singletons, page ViewModels as singletons, and the Avalonia implementations of every platform interface. - Apply the stored UI culture.
- Show
MainWindow.
Program.BuildAvaloniaApp() calls .WithInterFont(), so Inter is the app-wide font.
<AssemblyName>PCEdit</AssemblyName>— the published executable isPCEdit(which is what the AppImage packaging expects) and asset URIs areavares://PCEdit/..., notPCEdit.Desktop.
MainWindow.axaml is a SplitView:
-
Nav pane — a
ListBox Classes="nav"bound two-way toMainWindowViewModel.SelectedNavItem. The selected row shows the terraform-spectrum gradient on its leading edge over a faint tint. Below it, the 15-locale languageComboBox; in the footer, the loaded file's path. -
Content — a
Grid: row 0 is the header bar (page title, the primary Save button withHotKey="Ctrl+S", and the save/dirty state); row 1 is an error banner, shown only when a page'sLoad()threw, so a malformed save cannot take the app down with the file still open; row 2 is aContentControlbound toCurrentPage.
MainWindow.axaml.cs handles two things itself:
- Closing — if the workspace is dirty, the close is cancelled and a discard-confirmation dialog is shown.
-
First open — the disclaimer dialog appears when
IDisclaimerGate.HasAcknowledgedis false.
The other page views are pre-built at Background priority so the first navigation is not a stall.
ViewLocator maps PCEdit.App.Core.ViewModels.XxxViewModel → PCEdit.Desktop.Views.XxxView and
caches the view per ViewModel instance. Since page ViewModels are singletons, re-navigating does
not rebuild the visual tree.
App.axaml defines colour tokens (SurfacePage, SurfaceCard, BrandFill, Status*Text, …) in
ResourceDictionary.ThemeDictionaries for Light and Dark — a Planet Crafter terraforming
palette: rust world → blue sky → green biosphere, with a muted biosphere green as the brand accent.
RequestedThemeVariant="Default", so the app follows the OS theme.
Important
The token keys are referenced by name from the styles and views, always as
DynamicResource. Renaming a token silently breaks the status colours. Keep them stable.
Styles/Controls.axaml holds the type scale — h1 (hero), pageHeading, h2 (section),
caption, micro — and Border.headingRule, the green bar to the left of every section heading.
Things that will cost you an afternoon if you get them wrong:
- Avalonia 12 uses
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"— not2009. -
Compiled bindings are off (
AvaloniaUseCompiledBindingsByDefault=false); bindings are reflection-based. - The Inventories list is a
ListBoxand must not be wrapped in aScrollViewer— a wrapping scroll viewer gives it infinite height and defeats virtualization. On a real save that is the difference between instant and unusable. -
TextBoxplaceholder text isPlaceholderText, not the obsoleteWatermark. - Localized text uses the
{m:Loc Key}/{m:LocFormat Key=…, Path=…}markup extensions, which bindILocalizer.Currentthrough a converter so text re-reads live on a language change.LocFormattakes a second argument throughPath2(v1.5.0). See Localization.
| Interface | Implementation | Backed by |
|---|---|---|
IFilePickerService |
AvaloniaFilePickerService |
IStorageProvider |
INavigationService |
AvaloniaNavigationService |
MainWindowViewModel + a modal Window
|
IDialogService |
AvaloniaDialogService |
MessageDialog window |
IScreenReaderAnnouncer |
AvaloniaScreenReaderAnnouncer |
A hidden live-region TextBlock
|
IAppVersionInfo |
AvaloniaAppVersionInfo |
Assembly version |
ILanguageStore, IDisclaimerGate
|
JsonSettingsStore |
<ApplicationData>/PCEdit/settings.json |
ISaveBackupService |
LocalFileSaveBackupService |
<LocalApplicationData>/PCEdit/backups |
The backups deliberately sit under LocalApplicationData rather than the ApplicationData holding
settings.json: on Windows that is the roaming profile, and megabytes of save copies have no
business syncing between machines.
Converters/AppConverters.cs holds thin IValueConverter wrappers; the classification logic
(Presentation/VitalStatus) stays in Core, where it is unit tested. Colour never comes from a
converter that resolves a brush — that keeps the old theme's colour after a live light/dark
switch (fixed in v1.7.0). Instead VitalLevelIsConverter, StatusKindIsConverter and
EnumIsConverter set style classes, and Styles/Controls.axaml colours them with
DynamicResource (TextBlock.vitalLow / .vitalCritical, TextBlock.status / .status.success /
.status.error). When two matching styles set the same property the later one wins, so order them
general first.
Not an afterthought, and worth preserving in any change:
- Status messages are announced through
IScreenReaderAnnouncer, errors with an error prefix. - Expanders carry a spoken hint describing their current state.
- Controls that need one have an explicit accessible name (the
*A11ystring keys). An item's Move button names the item and its id, so identical items are distinguishable. - The type scale and the status palette are designed to stay legible in both themes.
dotnet run --project PCEdit.Desktop/PCEdit.Desktop.csprojPackaging the three platform artifacts is covered in Building & Packaging.
PCEdit · unofficial fan tool for The Planet Crafter · GPL-3.0-or-later · back up your saves (disclaimer)
Using PCEdit
- Installation
- Save File Locations
- Quick Start
- Worlds & Planets
- Overview & Terraforming
- Inventories
- Logistics Editor
- Terra Tokens
- Teleport
- Languages & Settings
- FAQ & Troubleshooting
Development
- Architecture
- Save File Format
- Save File Library
- App Core
- Desktop UI
- Localization
- Item Catalog
- Testing
Shipping