From eee52f26cdfc48096cb7d9655b4a433a2e96ff48 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 1 Oct 2025 17:07:28 +1000 Subject: [PATCH 1/4] sound properties predicated on driving sound type --- Dat/FileParsing/LocoBinaryReader.cs | 3 - Dat/FileParsing/LocoBinaryWriter.cs | 3 - Dat/Loaders/Vehicle/VehicleObjectLoader.cs | 6 +- .../Objects/Vehicle/FrictionSound.cs | 3 +- .../Objects/Vehicle/GearboxMotorSound.cs | 3 +- .../Objects/Vehicle/SimpleMotorSound.cs | 3 +- .../Objects/Vehicle/VehicleObject.cs | 2 +- .../ObjectModels/{ => Types}/ColourSwatch.cs | 2 +- .../ObjectModels/Types/ObjectModelHeader.cs | 4 + Definitions/ObjectModels/Types/Pos.cs | 4 +- Gui/App.axaml | 13 +- Gui/Gui.csproj | 4 + .../Graphics/ColourRemapSwatchViewModel.cs | 2 +- .../LocoTypes/Objects/VehicleViewModel.cs | 128 ++++++++++++------ Gui/ViewModels/Pos3ViewModel.cs | 27 ++++ Gui/Views/ExtendedPropertyGrid.cs | 63 +++++++++ Gui/Views/Pos3View.axaml | 34 +++++ Gui/Views/Pos3View.cs | 5 + Tests/IdempotenceTests.cs | 1 + Tests/ImagePaletteConversionTests.cs | 1 + 20 files changed, 249 insertions(+), 62 deletions(-) rename Definitions/ObjectModels/{ => Types}/ColourSwatch.cs (85%) create mode 100644 Gui/ViewModels/Pos3ViewModel.cs create mode 100644 Gui/Views/ExtendedPropertyGrid.cs create mode 100644 Gui/Views/Pos3View.axaml create mode 100644 Gui/Views/Pos3View.cs diff --git a/Dat/FileParsing/LocoBinaryReader.cs b/Dat/FileParsing/LocoBinaryReader.cs index e3012996..ef3ed0dd 100644 --- a/Dat/FileParsing/LocoBinaryReader.cs +++ b/Dat/FileParsing/LocoBinaryReader.cs @@ -283,7 +283,6 @@ public EmitterAnimation[] ReadEmitterAnimations(int count) public FrictionSound ReadFrictionSound() => new() { - SoundObjectId = ReadByte(), MinSpeed = ReadInt32(), SpeedFreqFactor = ReadByte(), BaseFrequency = ReadUInt16(), @@ -295,7 +294,6 @@ public FrictionSound ReadFrictionSound() public SimpleMotorSound ReadSimpleMotorSound() => new() { - SoundObjectId = ReadByte(), IdleFrequency = ReadUInt16(), IdleVolume = ReadByte(), CoastingFrequency = ReadUInt16(), @@ -312,7 +310,6 @@ public SimpleMotorSound ReadSimpleMotorSound() public GearboxMotorSound ReadGearboxMotorSound() => new() { - SoundObjectId = ReadByte(), IdleFrequency = ReadUInt16(), IdleVolume = ReadByte(), FirstGearFrequency = ReadUInt16(), diff --git a/Dat/FileParsing/LocoBinaryWriter.cs b/Dat/FileParsing/LocoBinaryWriter.cs index ebdbb309..1c70e857 100644 --- a/Dat/FileParsing/LocoBinaryWriter.cs +++ b/Dat/FileParsing/LocoBinaryWriter.cs @@ -167,7 +167,6 @@ public void Write(EmitterAnimation[] animations) public void Write(FrictionSound sound) { - Write(sound.SoundObjectId); Write(sound.MinSpeed); Write(sound.SpeedFreqFactor); Write(sound.BaseFrequency); @@ -178,7 +177,6 @@ public void Write(FrictionSound sound) public void Write(SimpleMotorSound sound) { - Write(sound.SoundObjectId); Write(sound.IdleFrequency); Write(sound.IdleVolume); Write(sound.CoastingFrequency); @@ -194,7 +192,6 @@ public void Write(SimpleMotorSound sound) public void Write(GearboxMotorSound sound) { - Write(sound.SoundObjectId); Write(sound.IdleFrequency); Write(sound.IdleVolume); Write(sound.FirstGearFrequency); diff --git a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs index 5f4bf213..9b33aab9 100644 --- a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs +++ b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs @@ -73,7 +73,7 @@ private static void LoadVariable(LocoBinaryReader br, VehicleObject model, byte // track type if (!model.Flags.HasFlag(VehicleObjectFlags.AnyRoadType) && (model.Mode == TransportMode.Rail || model.Mode == TransportMode.Road)) { - model.TrackType = br.ReadS5Header(); + model.RoadOrTrackType = br.ReadS5Header(); } // required track extra @@ -184,6 +184,7 @@ private static void LoadFixed(LocoBinaryReader br, VehicleObject model, out byte br.SkipObjectId(); // RackRailType, not part of object definition model.DrivingSoundType = ((DatDrivingSoundType)br.ReadByte()).Convert(); + br.SkipByte(); // this is the object id which is the first byte of all of the sound union structs switch (model.DrivingSoundType) { case DrivingSoundType.Friction: @@ -252,6 +253,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)model.DrivingSoundType.Convert()); // sound union + bw.WriteEmptyBytes(1); // this is the object id which is the first byte of all of the sound union structs switch (model.DrivingSoundType) { case DrivingSoundType.Friction: @@ -299,7 +301,7 @@ private static void SaveVariable(VehicleObject model, LocoBinaryWriter bw) // track type if (!model.Flags.HasFlag(VehicleObjectFlags.AnyRoadType) && (model.Mode == TransportMode.Rail || model.Mode == TransportMode.Road)) { - bw.WriteS5Header(model.TrackType); + bw.WriteS5Header(model.RoadOrTrackType); } // track extras diff --git a/Definitions/ObjectModels/Objects/Vehicle/FrictionSound.cs b/Definitions/ObjectModels/Objects/Vehicle/FrictionSound.cs index 6586e3ac..1cb378f7 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/FrictionSound.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/FrictionSound.cs @@ -1,3 +1,4 @@ +using Definitions.ObjectModels.Types; using System.ComponentModel; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -5,7 +6,7 @@ namespace Definitions.ObjectModels.Objects.Vehicle; [TypeConverter(typeof(ExpandableObjectConverter))] public class FrictionSound { - public uint8_t SoundObjectId { get; set; } + public ObjectModelHeader SoundObject { get; set; } public int32_t MinSpeed { get; set; } public uint8_t SpeedFreqFactor { get; set; } public uint16_t BaseFrequency { get; set; } diff --git a/Definitions/ObjectModels/Objects/Vehicle/GearboxMotorSound.cs b/Definitions/ObjectModels/Objects/Vehicle/GearboxMotorSound.cs index 6876c950..55c72409 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/GearboxMotorSound.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/GearboxMotorSound.cs @@ -1,3 +1,4 @@ +using Definitions.ObjectModels.Types; using System.ComponentModel; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -5,7 +6,7 @@ namespace Definitions.ObjectModels.Objects.Vehicle; [TypeConverter(typeof(ExpandableObjectConverter))] public class GearboxMotorSound { - public uint8_t SoundObjectId { get; set; } + public ObjectModelHeader SoundObject { get; set; } public uint16_t IdleFrequency { get; set; } public uint8_t IdleVolume { get; set; } public uint16_t FirstGearFrequency { get; set; } // All subsequent gears are based on this frequency diff --git a/Definitions/ObjectModels/Objects/Vehicle/SimpleMotorSound.cs b/Definitions/ObjectModels/Objects/Vehicle/SimpleMotorSound.cs index 867afa39..7e438189 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/SimpleMotorSound.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/SimpleMotorSound.cs @@ -1,3 +1,4 @@ +using Definitions.ObjectModels.Types; using System.ComponentModel; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -5,7 +6,7 @@ namespace Definitions.ObjectModels.Objects.Vehicle; [TypeConverter(typeof(ExpandableObjectConverter))] public class SimpleMotorSound { - public uint8_t SoundObjectId { get; set; } + public ObjectModelHeader SoundObject { get; set; } public uint16_t IdleFrequency { get; set; } public uint8_t IdleVolume { get; set; } public uint16_t CoastingFrequency { get; set; } diff --git a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs index 28146626..ea29946e 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs @@ -9,7 +9,7 @@ public class VehicleObject : ILocoStruct public TransportMode Mode { get; set; } public VehicleType Type { get; set; } public uint8_t NumCarComponents { get; set; } - public ObjectModelHeader? TrackType { get; set; } + public ObjectModelHeader? RoadOrTrackType { get; set; } public object_id TrackTypeId { get; set; } public uint8_t CostIndex { get; set; } public int16_t CostFactor { get; set; } diff --git a/Definitions/ObjectModels/ColourSwatch.cs b/Definitions/ObjectModels/Types/ColourSwatch.cs similarity index 85% rename from Definitions/ObjectModels/ColourSwatch.cs rename to Definitions/ObjectModels/Types/ColourSwatch.cs index 0bc31010..5c59176e 100644 --- a/Definitions/ObjectModels/ColourSwatch.cs +++ b/Definitions/ObjectModels/Types/ColourSwatch.cs @@ -1,4 +1,4 @@ -namespace Definitions.ObjectModels; +namespace Definitions.ObjectModels.Types; public enum ColourSwatch { diff --git a/Definitions/ObjectModels/Types/ObjectModelHeader.cs b/Definitions/ObjectModels/Types/ObjectModelHeader.cs index d6a9148d..d9fa0292 100644 --- a/Definitions/ObjectModels/Types/ObjectModelHeader.cs +++ b/Definitions/ObjectModels/Types/ObjectModelHeader.cs @@ -6,6 +6,10 @@ namespace Definitions.ObjectModels.Types; [TypeConverter(typeof(ExpandableObjectConverter))] public class ObjectModelHeader(string name, ObjectType objectType, ObjectSource objectSource, uint datchecksum) : ILocoStruct { + public ObjectModelHeader() + : this(string.Empty, ObjectType.Airport, ObjectSource.Custom, 0) + { } + public string Name { get; set; } = name; public ObjectType ObjectType { get; set; } = objectType; public ObjectSource ObjectSource { get; set; } = objectSource; diff --git a/Definitions/ObjectModels/Types/Pos.cs b/Definitions/ObjectModels/Types/Pos.cs index bdc04946..1f098171 100644 --- a/Definitions/ObjectModels/Types/Pos.cs +++ b/Definitions/ObjectModels/Types/Pos.cs @@ -8,7 +8,7 @@ public class Pos2 public coord_t X { get; set; } public coord_t Y { get; set; } - public static Pos2 Zero => new Pos2 { X = 0, Y = 0 }; + public static Pos2 Zero => new() { X = 0, Y = 0 }; } [TypeConverter(typeof(ExpandableObjectConverter))] @@ -18,5 +18,5 @@ public class Pos3 public coord_t Y { get; set; } public coord_t Z { get; set; } - public static Pos3 Zero => new Pos3 { X = 0, Y = 0, Z = 0 }; + public static Pos3 Zero => new() { X = 0, Y = 0, Z = 0 }; } diff --git a/Gui/App.axaml b/Gui/App.axaml index 45a17c64..614b925e 100644 --- a/Gui/App.axaml +++ b/Gui/App.axaml @@ -18,6 +18,8 @@ + + - + + @@ -109,7 +118,7 @@ - + diff --git a/Gui/Gui.csproj b/Gui/Gui.csproj index 69b63b17..b06e9855 100644 --- a/Gui/Gui.csproj +++ b/Gui/Gui.csproj @@ -81,5 +81,9 @@ ObjectEditorView.axaml + + Pos3View.axaml + Code + diff --git a/Gui/ViewModels/Graphics/ColourRemapSwatchViewModel.cs b/Gui/ViewModels/Graphics/ColourRemapSwatchViewModel.cs index 0e5477e8..36648ade 100644 --- a/Gui/ViewModels/Graphics/ColourRemapSwatchViewModel.cs +++ b/Gui/ViewModels/Graphics/ColourRemapSwatchViewModel.cs @@ -1,4 +1,4 @@ -using Definitions.ObjectModels; +using Definitions.ObjectModels.Types; using ReactiveUI.Fody.Helpers; using AvaColour = Avalonia.Media.Color; diff --git a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs index 678930ea..ab1c67f0 100644 --- a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs @@ -2,6 +2,8 @@ using Definitions.ObjectModels.Objects.Cargo; using Definitions.ObjectModels.Objects.Vehicle; using Definitions.ObjectModels.Types; +using DynamicData.Binding; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -30,16 +32,59 @@ public VehicleViewModel(VehicleObject model) : base(model) CargoTypeSpriteOffsets = new([.. model.CargoTypeSpriteOffsets.Select(x => new CargoTypeSpriteOffset(x.Key, x.Value))]); StartSounds = new(model.StartSounds); var_135 = new(model.var_135); - TrackType = model.TrackType; + RoadOrTrackType = model.RoadOrTrackType; RackRail = model.RackRail; HasRackRail = model.Flags.HasFlag(VehicleObjectFlags.RackRail); + SimpleMotorSound = model.SimpleMotorSound ?? new SimpleMotorSound(); + FrictionSound = model.FrictionSound ?? new FrictionSound(); + GearboxMotorSound = model.GearboxMotorSound ?? new GearboxMotorSound(); + Sound = model.Sound; + + _ = this.WhenAnyValue(x => x.RackRail) + .Subscribe((_) => model.RackRail = RackRail); + + #region Road/Track Type Binding + _ = this.WhenAnyValue(x => x.Mode, x => x.Flags) .Subscribe((_) => this.RaisePropertyChanged(nameof(IsTrackTypeSettable))); _ = this.WhenAnyValue(x => x.IsTrackTypeSettable) - .Subscribe((_) => this.RaisePropertyChanged(nameof(TrackType))); + .Subscribe((_) => this.RaisePropertyChanged(nameof(RoadOrTrackType))); + + _ = this.WhenAnyValue(x => x.RoadOrTrackType) + .Subscribe((_) => model.RoadOrTrackType = RoadOrTrackType); + + #endregion + + #region Sound Properties Binding + _ = this.WhenAnyValue(x => x.DrivingSoundType) + .Subscribe((_) => + { + this.RaisePropertyChanged(nameof(SimpleMotorSound)); + this.RaisePropertyChanged(nameof(FrictionSound)); + this.RaisePropertyChanged(nameof(GearboxMotorSound)); + }); + + _ = this.WhenAnyValue(x => x.Sound) + .Subscribe((_) => model.Sound = Sound); + + _ = this.WhenAnyValue(x => x.SimpleMotorSound) + .Subscribe((_) => model.SimpleMotorSound = SimpleMotorSound); + _ = this.WhenAnyValue(x => x.FrictionSound) + .Subscribe((_) => model.FrictionSound = FrictionSound); + _ = this.WhenAnyValue(x => x.GearboxMotorSound) + .Subscribe((_) => model.GearboxMotorSound = GearboxMotorSound); + + _ = this.WhenPropertyChanged(x => x.SimpleMotorSound) + .Subscribe((_) => model.SimpleMotorSound = SimpleMotorSound); + _ = this.WhenPropertyChanged(x => x.FrictionSound) + .Subscribe((_) => model.FrictionSound = FrictionSound); + _ = this.WhenPropertyChanged(x => x.GearboxMotorSound) + .Subscribe((_) => model.GearboxMotorSound = GearboxMotorSound); + + #endregion } [Category("Stats")] @@ -116,11 +161,12 @@ public VehicleObjectFlags Flags } } + [Browsable(false)] bool IsTrackTypeSettable - => (!model.Flags.HasFlag(VehicleObjectFlags.AnyRoadType) && (model.Mode == TransportMode.Rail || model.Mode == TransportMode.Road)); + => !model.Flags.HasFlag(VehicleObjectFlags.AnyRoadType) && (model.Mode == TransportMode.Rail || model.Mode == TransportMode.Road); - [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(IsTrackTypeSettable), true)] - public ObjectModelHeader? TrackType { get; set; } + [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(IsTrackTypeSettable), true), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public ObjectModelHeader? RoadOrTrackType { get; set; } public bool HasRackRail { @@ -134,7 +180,7 @@ public bool HasRackRail } } - [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(HasRackRail), true)] + [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(HasRackRail), true), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? RackRail { get; set; } [Range(0, 4)] @@ -144,10 +190,10 @@ public uint8_t NumCarComponents set => model.NumCarComponents = value; } - [Length(0, 8)] + [Length(0, 8), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleVehicles { get; init; } - [Length(0, 4)] + [Length(0, 4), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList RequiredTrackExtras { get; init; } [Description("If 0, boat has a single wake animation. if > 0, boat has 2 wakes, offset horizontally by this value")] @@ -192,59 +238,53 @@ public CompanyColourType SpecialColourSchemeIndex set => model.CompanyColourSchemeIndex = value; } // called "ColourType" in the loco codebase - [Category("Sprites"), Editable(false)] public BindingList CarComponents { get; init; } - [Category("Sprites"), Editable(false)] public BindingList BodySprites { get; init; } - [Category("Sprites"), Editable(false)] public BindingList BogieSprites { get; init; } - [Category("Sprites"), Editable(false)] public BindingList Animation { get; init; } + [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CarComponents { get; init; } + [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BodySprites { get; init; } + [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BogieSprites { get; init; } + [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Animation { get; init; } - [Category("Cargo")] + [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo1 { get; init; } - [Category("Cargo")] + [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo2 { get; init; } - [Category("Cargo"), Length(0, 32), Description("This is a dictionary. For every cargo defined in both CompatibleCargoCategories, an entry must exist in this dictionary.")] + [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), Length(0, 32), Description("This is a dictionary. For every cargo defined in both CompatibleCargoCategories, an entry must exist in this dictionary.")] public BindingList CargoTypeSpriteOffsets { get; init; } - [Category("Sound")] - public ObjectModelHeader? Sound - { - get => model.Sound; - set => model.Sound = value; - } - - [Category("Sound")] + [Category("Sound"), ConditionTarget] public DrivingSoundType DrivingSoundType { get => model.DrivingSoundType; - set => model.DrivingSoundType = value; + set + { + model.DrivingSoundType = value; + this.RaisePropertyChanged(nameof(DrivingSoundType)); + } } - [Category("Sound")] - public FrictionSound? FrictionSound - { - get => model.FrictionSound; - set => model.FrictionSound = value; - } + [Browsable(false)] + [DependsOnProperty(nameof(DrivingSoundType))] + [ConditionTarget] + bool IsDrivingSoundTypeSet + => model.DrivingSoundType != DrivingSoundType.None; - [Category("Sound")] - public SimpleMotorSound? SimpleMotorSound - { - get => model.SimpleMotorSound; - set => model.SimpleMotorSound = value; - } + [Category("Sound"), Reactive, ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(IsDrivingSoundTypeSet), true)] + public ObjectModelHeader? Sound { get; set; } - [Category("Sound")] - public GearboxMotorSound? GearboxMotorSound - { - get => model.GearboxMotorSound; - set => model.GearboxMotorSound = value; - } + [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.Friction)] + public FrictionSound FrictionSound { get; set; } + + [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.SimpleMotor)] + public SimpleMotorSound SimpleMotorSound { get; set; } + + [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), ConditionTarget, PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.GearboxMotor)] + public GearboxMotorSound GearboxMotorSound { get; set; } - [Category("Sound")] + [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList StartSounds { get; init; } - [Category("")] + [Category(""), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList var_135 { get; init; } public override void CopyBackToModel() diff --git a/Gui/ViewModels/Pos3ViewModel.cs b/Gui/ViewModels/Pos3ViewModel.cs new file mode 100644 index 00000000..a994b5d9 --- /dev/null +++ b/Gui/ViewModels/Pos3ViewModel.cs @@ -0,0 +1,27 @@ +using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; + +namespace Gui.ViewModels; + +public class Pos3ViewModel : MiniReactiveObject +{ + public Pos3 Pos; + + public coord_t X + { + get => Pos.X; + set => Pos.X = value; + } + + public coord_t Y + { + get => Pos.Y; + set => Pos.Y = value; + } + + public coord_t Z + { + get => Pos.Z; + set => Pos.Z = value; + } +} diff --git a/Gui/Views/ExtendedPropertyGrid.cs b/Gui/Views/ExtendedPropertyGrid.cs new file mode 100644 index 00000000..10a12f8f --- /dev/null +++ b/Gui/Views/ExtendedPropertyGrid.cs @@ -0,0 +1,63 @@ +using Avalonia.Controls; +using Avalonia.PropertyGrid.Controls; +using Avalonia.PropertyGrid.Controls.Factories; +using Definitions.ObjectModels.Types; +using Gui.ViewModels; + +namespace Gui.Views; + +public class ExtendedPropertyGrid : PropertyGrid +{ + public ExtendedPropertyGrid() + { + Factories.AddFactory(new Pos3CellEditFactory()); + } +} + +internal class Pos3CellEditFactory : AbstractCellEditFactory +{ + public override bool Accept(object accessToken) => accessToken is ExtendedPropertyGrid; + + public override Control? HandleNewProperty(PropertyCellContext context) + { + var propertyDescriptor = context.Property; + _ = context.Target; + + if (propertyDescriptor.PropertyType != typeof(Pos3)) + { + return null; + } + + var control = new Pos3View(); + + return control; + } + + public override bool HandlePropertyChanged(PropertyCellContext context) + { + var propertyDescriptor = context.Property; + var target = context.Target; + var control = context.CellEdit!; + + if (propertyDescriptor.PropertyType != typeof(Pos3)) + { + return false; + } + + ValidateProperty(control, propertyDescriptor, target); + + if (control is Pos3View vv) + { + var pos = (Pos3)propertyDescriptor.GetValue(target)!; + + var model = new Pos3ViewModel { Pos = pos }; + vv.DataContext = model; + + model.PropertyChanged += (s, e) => SetAndRaise(context, control, model.Pos); + + return true; + } + + return false; + } +} diff --git a/Gui/Views/Pos3View.axaml b/Gui/Views/Pos3View.axaml new file mode 100644 index 00000000..f4772b4a --- /dev/null +++ b/Gui/Views/Pos3View.axaml @@ -0,0 +1,34 @@ + + + + + + + diff --git a/Gui/Views/Pos3View.cs b/Gui/Views/Pos3View.cs new file mode 100644 index 00000000..da46ea30 --- /dev/null +++ b/Gui/Views/Pos3View.cs @@ -0,0 +1,5 @@ +using Avalonia.Controls.Primitives; + +namespace Gui.Views; + +public class Pos3View : TemplatedControl; diff --git a/Tests/IdempotenceTests.cs b/Tests/IdempotenceTests.cs index 3001878a..ffbeeade 100644 --- a/Tests/IdempotenceTests.cs +++ b/Tests/IdempotenceTests.cs @@ -1,6 +1,7 @@ using Dat.Converters; using Dat.FileParsing; using Definitions.ObjectModels; +using Definitions.ObjectModels.Types; using Gui.ViewModels; using NUnit.Framework; using NUnit.Framework.Internal; diff --git a/Tests/ImagePaletteConversionTests.cs b/Tests/ImagePaletteConversionTests.cs index b0c38a6d..5fdd8e08 100644 --- a/Tests/ImagePaletteConversionTests.cs +++ b/Tests/ImagePaletteConversionTests.cs @@ -4,6 +4,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using Definitions.ObjectModels; +using Definitions.ObjectModels.Types; namespace Dat.Tests; From dec1f174b053fc441425bc266cc1f582ff4e549d Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 1 Oct 2025 17:11:22 +1000 Subject: [PATCH 2/4] fix unit tests --- Dat/Loaders/Vehicle/VehicleObjectLoader.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs index 9b33aab9..bb7f797b 100644 --- a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs +++ b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs @@ -184,18 +184,20 @@ private static void LoadFixed(LocoBinaryReader br, VehicleObject model, out byte br.SkipObjectId(); // RackRailType, not part of object definition model.DrivingSoundType = ((DatDrivingSoundType)br.ReadByte()).Convert(); - br.SkipByte(); // this is the object id which is the first byte of all of the sound union structs switch (model.DrivingSoundType) { case DrivingSoundType.Friction: + br.SkipByte(); // this is the object id which is the first byte of all of the sound union structs. it will be read from Sound property model.FrictionSound = br.ReadFrictionSound(); br.SkipByte(StructSizes.SoundData - StructSizes.FrictionSound); break; case DrivingSoundType.SimpleMotor: + br.SkipByte(); // this is the object id which is the first byte of all of the sound union structs. it will be read from Sound property model.SimpleMotorSound = br.ReadSimpleMotorSound(); br.SkipByte(StructSizes.SoundData - StructSizes.SimpleMotorSound); break; case DrivingSoundType.GearboxMotor: + br.SkipByte(); // this is the object id which is the first byte of all of the sound union structs. it will be read from Sound property model.GearboxMotorSound = br.ReadGearboxMotorSound(); br.SkipByte(StructSizes.SoundData - StructSizes.GearboxMotorSound); break; @@ -253,21 +255,23 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)model.DrivingSoundType.Convert()); // sound union - bw.WriteEmptyBytes(1); // this is the object id which is the first byte of all of the sound union structs switch (model.DrivingSoundType) { case DrivingSoundType.Friction: ArgumentNullException.ThrowIfNull(model.FrictionSound); + bw.WriteEmptyBytes(1); // this is the object id which is the first byte of all of the sound union structs bw.Write(model.FrictionSound); bw.WriteEmptyBytes(StructSizes.SoundData - StructSizes.FrictionSound); break; case DrivingSoundType.SimpleMotor: ArgumentNullException.ThrowIfNull(model.SimpleMotorSound); + bw.WriteEmptyBytes(1); // this is the object id which is the first byte of all of the sound union structs bw.Write(model.SimpleMotorSound); bw.WriteEmptyBytes(StructSizes.SoundData - StructSizes.SimpleMotorSound); break; case DrivingSoundType.GearboxMotor: ArgumentNullException.ThrowIfNull(model.GearboxMotorSound); + bw.WriteEmptyBytes(1); // this is the object id which is the first byte of all of the sound union structs bw.Write(model.GearboxMotorSound); bw.WriteEmptyBytes(StructSizes.SoundData - StructSizes.GearboxMotorSound); break; From 8de341ffb489ce17991158abde507e5234bb8eb4 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 1 Oct 2025 17:25:34 +1000 Subject: [PATCH 3/4] hide more category headers --- .../Objects/Vehicle/EmitterAnimation.cs | 2 +- .../LocoTypes/Objects/AirportViewModel.cs | 27 +++--- .../LocoTypes/Objects/BridgeViewModel.cs | 9 +- .../LocoTypes/Objects/DockViewModel.cs | 13 ++- .../LocoTypes/Objects/IndustryViewModel.cs | 31 +++++-- .../LocoTypes/Objects/LandViewModel.cs | 3 + .../LocoTypes/Objects/RegionViewModel.cs | 7 +- .../LocoTypes/Objects/RoadStationViewModel.cs | 3 + .../LocoTypes/Objects/RoadViewModel.cs | 21 ++++- .../LocoTypes/Objects/SoundViewModel.cs | 2 + .../LocoTypes/Objects/SteamViewModel.cs | 4 + .../LocoTypes/Objects/TownNamesViewModel.cs | 5 +- .../LocoTypes/Objects/TrackSignalViewModel.cs | 2 + .../Objects/TrackStationViewModel.cs | 7 +- .../LocoTypes/Objects/TrackViewModel.cs | 26 ++++-- .../LocoTypes/Objects/VehicleViewModel.cs | 87 ++++++++++++++----- 16 files changed, 186 insertions(+), 63 deletions(-) diff --git a/Definitions/ObjectModels/Objects/Vehicle/EmitterAnimation.cs b/Definitions/ObjectModels/Objects/Vehicle/EmitterAnimation.cs index fb604e13..39c051ee 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/EmitterAnimation.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/EmitterAnimation.cs @@ -7,7 +7,7 @@ namespace Definitions.ObjectModels.Objects.Vehicle; [TypeConverter(typeof(ExpandableObjectConverter))] public class EmitterAnimation : ILocoStruct { - public ObjectModelHeader AnimationObject { get; set; } // will be SteamObject + public ObjectModelHeader AnimationObject { get; set; } // will be SteamObject public uint8_t EmitterVerticalPos { get; set; } public SimpleAnimationType Type { get; set; } diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs index 307426df..1e5717cd 100644 --- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs @@ -1,6 +1,7 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Common; +using PropertyModels.ComponentModel; using PropertyModels.Extensions; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -80,22 +81,30 @@ public int16_t SellCostFactor set => Model.SellCostFactor = value; } - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] + [Category("Building")] + [Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] + [Category("Building")] + [Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; init; } = model.BuildingComponents.BuildingHeights.ToBindingList(); - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] + [Category("Building")] + [Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("Building")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingPositions { get; init; } = model.BuildingPositions.ToBindingList(); [Category("Movement")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList MovementNodes { get; init; } = model.MovementNodes.ToBindingList(); [Category("Movement")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList MovementEdges { get; init; } = model.MovementEdges.ToBindingList(); [Category("")] @@ -111,16 +120,4 @@ public uint32_t var_B6 get => Model.var_B6; set => Model.var_B6 = value; } - - //public AirportViewModel(AirportObject model) - // : base(model) - //{ - // BuildingHeights = new(model.BuildingComponents.BuildingHeights); - // BuildingAnimations = new(model.BuildingComponents.BuildingAnimations); - // BuildingVariations = new(model.BuildingComponents.BuildingVariations.Select(x => new BindingList(x))); - // BuildingPositions = new(model.BuildingPositions); - // MovementNodes = new(model.MovementNodes); - // MovementEdges = new(model.MovementEdges); - // var_B6 = new BindingList(model.var_B6); - //} } diff --git a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs index 897dca15..e0531bcb 100644 --- a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Bridge; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -92,9 +93,13 @@ public int16_t SellCostFactor set => Model.SellCostFactor = value; } - [Category("Compatible")] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); + [Category("Compatible")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); - [Category("Compatible")] public BindingList CompatibleRoadObjects { get; init; } = new(model.CompatibleRoadObjects); + [Category("Compatible")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList CompatibleRoadObjects { get; init; } = new(model.CompatibleRoadObjects); [Category("")] public uint8_t var_03 diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs index f8b187f0..4b55a0f5 100644 --- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs @@ -2,6 +2,7 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using System.ComponentModel; @@ -32,6 +33,7 @@ public uint16_t ObsoleteYear set => Model.ObsoleteYear = value; } + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public Pos2 BoatPosition { get => Model.BoatPosition; @@ -59,13 +61,18 @@ public int16_t SellCostFactor set => Model.SellCostFactor = value; } - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingVariationCount)] + [Category("Building")] + [Length(1, DockObjectLoader.Constants.BuildingVariationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingHeightCount)] + [Category("Building")] + [Length(1, DockObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; init; } = model.BuildingComponents.BuildingHeights.ToBindingList(); - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] + [Category("Building")] + [Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs index 50344a15..cae17d3b 100644 --- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs @@ -2,6 +2,7 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using System.ComponentModel; @@ -54,12 +55,16 @@ public uint32_t Colours } [Category("Production")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList InitialProductionRate { get; init; } = new(model.InitialProductionRate); - [Category("Production"), Length(0, IndustryObjectLoader.Constants.MaxProducedCargoType)] + [Category("Production")] + [Length(0, IndustryObjectLoader.Constants.MaxProducedCargoType)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList ProducedCargo { get; init; } = new(model.ProducedCargo); - [Category("Production"), Length(0, IndustryObjectLoader.Constants.MaxProducedCargoType)] + [Category("Production")] + [Length(0, IndustryObjectLoader.Constants.MaxProducedCargoType)] public BindingList RequiredCargo { get; init; } = new(model.RequiredCargo); [Category("Production")] @@ -118,16 +123,23 @@ public uint8_t FarmNumStagesOfGrowth set => Model.FarmNumStagesOfGrowth = value; } - [Category("Building"), Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] + [Category("Building")] + [Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> AnimationSequences { get; init; } = model.AnimationSequences.Select(x => x.ToBindingList()).ToBindingList(); - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] + [Category("Building")] + [Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] + [Category("Building")] + [Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; init; } = model.BuildingComponents.BuildingHeights.ToBindingList(); - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] + [Category("Building")] + [Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("Building")] @@ -168,10 +180,13 @@ public Colour ScaffoldingColour set => Model.ScaffoldingColour = value; } - [Category("Building"), Length(0, IndustryObjectLoader.Constants.MaxWallTypeCount)] + [Category("Building")] + [Length(0, IndustryObjectLoader.Constants.MaxWallTypeCount)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList WallTypes { get; init; } = model.WallTypes.ToBindingList(); [Category("Building")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? BuildingWall { get => Model.BuildingWall; @@ -179,6 +194,7 @@ public ObjectModelHeader? BuildingWall } [Category("Building")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? BuildingWallEntrance { get => Model.BuildingWallEntrance; @@ -186,6 +202,7 @@ public ObjectModelHeader? BuildingWallEntrance } [Category("")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList var_38 { get; init; } = model.var_38.ToBindingList(); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs index 4490be4d..052a0ad3 100644 --- a/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Land; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; namespace Gui.ViewModels; @@ -60,12 +61,14 @@ public uint8_t VariationLikelihood set => Model.VariationLikelihood = value; } + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader CliffEdgeHeader { get => Model.CliffEdgeHeader; set => Model.CliffEdgeHeader = value; } + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? ReplacementLandHeader { get => Model.ReplacementLandHeader; diff --git a/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs index 69ceeb64..d85607a3 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Region; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using System.ComponentModel; namespace Gui.ViewModels; @@ -19,9 +20,13 @@ public uint8_t pad_07 set => Model.pad_07 = value; } - [Category("Cargo")] public BindingList CargoInfluenceObjects { get; init; } = new(model.CargoInfluenceObjects); + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList DependentObjects { get; init; } = new(model.DependentObjects); + [Category("Cargo")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList CargoInfluenceObjects { get; init; } = new(model.CargoInfluenceObjects); + [Category("Cargo")] public BindingList CargoInfluenceTownFilter { get; init; } = new(model.CargoInfluenceTownFilter); } diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs index b1d62cb7..9f3a0cea 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs @@ -2,6 +2,7 @@ using Definitions.ObjectModels.Objects.RoadStation; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -78,8 +79,10 @@ public ObjectModelHeader? CargoType } [Category("Cargo")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CargoOffset[][][] CargoOffsets { get; init; } = model.CargoOffsets; [Category("Compatible")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleRoadObjects { get; init; } = new(model.CompatibleRoadObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs index 320518ff..3737d0a6 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Road; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -75,14 +76,26 @@ public uint8_t CostIndex } [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader Tunnel { get => Model.Tunnel; set => Model.Tunnel = value; } - [Category("Compatible Objects")] public BindingList Bridges { get; set; } = new(model.Bridges); - [Category("Compatible Objects")] public BindingList Stations { get; set; } = new(model.Stations); - [Category("Compatible Objects")] public BindingList Mods { get; set; } = new(model.RoadMods); - [Category("Compatible Objects")] public BindingList TracksAndRoads { get; set; } = new(model.TracksAndRoads); + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Bridges { get; set; } = new(model.Bridges); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Stations { get; set; } = new(model.Stations); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Mods { get; set; } = new(model.RoadMods); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList TracksAndRoads { get; set; } = new(model.TracksAndRoads); } diff --git a/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs index e9f85a12..443e4c34 100644 --- a/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.Sound; +using PropertyModels.ComponentModel; using System.ComponentModel; namespace Gui.ViewModels; @@ -18,6 +19,7 @@ public uint32_t Volume set => Model.Volume = value; } + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public SoundObjectData SoundObjectData { get => Model.SoundObjectData; diff --git a/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs index 7c369164..c9c66270 100644 --- a/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Steam; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -27,9 +28,12 @@ public uint32_t var_0A set => Model.var_0A = value; } + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList SoundEffects { get; init; } = new(model.SoundEffects); + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList FrameInfoType0 { get; init; } = new(model.FrameInfoType0); + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList FrameInfoType1 { get; init; } = new(model.FrameInfoType1); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs index 76894ee4..3ee82454 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.TownNames; +using PropertyModels.ComponentModel; using PropertyModels.Extensions; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -8,7 +9,9 @@ namespace Gui.ViewModels; public class TownNamesViewModel(TownNamesObject model) : LocoObjectViewModel(model) { - [Length(6, 6), Editable(false)] + [Length(6, 6)] + [Editable(false)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Categories { get; set; } = model.Categories.ToBindingList(); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs index 2e5d1f50..386c40f3 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs @@ -1,6 +1,7 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.TrackSignal; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -65,5 +66,6 @@ public uint16_t ObsoleteYear } [Length(0, TrackSignalObjectLoader.Constants.ModsLength)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs index adf4aa23..28104f49 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs @@ -2,14 +2,15 @@ using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Objects.TrackStation; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; namespace Gui.ViewModels; -public class TrackStationViewModel(TrackStationObject model) : LocoObjectViewModel(model) +public class TrackStationViewModel(TrackStationObject model) + : LocoObjectViewModel(model) { - public uint8_t PaintStyle { get => Model.PaintStyle; @@ -87,8 +88,10 @@ public uint8_t var_0D public uint8_t[][] var_6E { get; set; } = model.var_6E; [Category("Cargo")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CargoOffset[][][] CargoOffsets { get; init; } = model.CargoOffsets; [Category("Compatible")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs index f6d8fdf1..ffd55048 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Types; +using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; using TrackObject = Definitions.ObjectModels.Objects.Track.TrackObject; @@ -78,15 +79,30 @@ public uint8_t var_06 } [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader Tunnel { get => Model.Tunnel; set => Model.Tunnel = value; } - [Category("Compatible Objects")] public BindingList TracksAndRoads { get; init; } = new(model.TracksAndRoads); - [Category("Compatible Objects")] public BindingList TrackExtras { get; init; } = new(model.TrackMods); - [Category("Compatible Objects")] public BindingList Signals { get; init; } = new(model.Signals); - [Category("Compatible Objects")] public BindingList Bridges { get; init; } = new(model.Bridges); - [Category("Compatible Objects")] public BindingList Stations { get; init; } = new(model.Stations); + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList TracksAndRoads { get; init; } = new(model.TracksAndRoads); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList TrackExtras { get; init; } = new(model.TrackMods); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Signals { get; init; } = new(model.Signals); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Bridges { get; init; } = new(model.Bridges); + + [Category("Compatible Objects")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Stations { get; init; } = new(model.Stations); } diff --git a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs index ab1c67f0..06b8cffd 100644 --- a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs @@ -165,9 +165,13 @@ public VehicleObjectFlags Flags bool IsTrackTypeSettable => !model.Flags.HasFlag(VehicleObjectFlags.AnyRoadType) && (model.Mode == TransportMode.Rail || model.Mode == TransportMode.Road); - [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(IsTrackTypeSettable), true), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Reactive] + [ConditionTarget] + [PropertyVisibilityCondition(nameof(IsTrackTypeSettable), true)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? RoadOrTrackType { get; set; } + [ConditionTarget] public bool HasRackRail { get => model.Flags.HasFlag(VehicleObjectFlags.RackRail); @@ -180,7 +184,10 @@ public bool HasRackRail } } - [Reactive, ConditionTarget, PropertyVisibilityCondition(nameof(HasRackRail), true), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Reactive] + [ConditionTarget] + [PropertyVisibilityCondition(nameof(HasRackRail), true)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? RackRail { get; set; } [Range(0, 4)] @@ -190,10 +197,12 @@ public uint8_t NumCarComponents set => model.NumCarComponents = value; } - [Length(0, 8), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Length(0, 8)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleVehicles { get; init; } - [Length(0, 4), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Length(0, 4)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList RequiredTrackExtras { get; init; } [Description("If 0, boat has a single wake animation. if > 0, boat has 2 wakes, offset horizontally by this value")] @@ -203,28 +212,32 @@ public uint8_t ShipWakeSpacing set => model.ShipWakeSpacing = value; } - [Category("Cost"), Range(0, 32)] + [Category("Cost")] + [Range(0, 32)] public uint8_t CostIndex { get => model.CostIndex; set => model.CostIndex = value; } - [Category("Cost"), Range(1, int16_t.MaxValue)] + [Category("Cost")] + [Range(1, int16_t.MaxValue)] public int16_t CostFactor { get => model.CostFactor; set => model.CostFactor = value; } - [Category("Cost"), Range(0, 32)] + [Category("Cost")] + [Range(0, 32)] public uint8_t RunCostIndex { get => model.RunCostIndex; set => model.RunCostIndex = value; } - [Category("Cost"), Range(0, int16_t.MaxValue)] + [Category("Cost")] + [Range(0, int16_t.MaxValue)] public int16_t RunCostFactor { get => model.RunCostFactor; @@ -238,21 +251,38 @@ public CompanyColourType SpecialColourSchemeIndex set => model.CompanyColourSchemeIndex = value; } // called "ColourType" in the loco codebase - [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CarComponents { get; init; } - [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BodySprites { get; init; } - [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BogieSprites { get; init; } - [Category("Sprites"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Animation { get; init; } + [Category("Sprites")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList CarComponents { get; init; } + + [Category("Sprites")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList BodySprites { get; init; } + + [Category("Sprites")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList BogieSprites { get; init; } + + [Category("Sprites")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + public BindingList Animation { get; init; } - [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Category("Cargo")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo1 { get; init; } - [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Category("Cargo")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo2 { get; init; } - [Category("Cargo"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), Length(0, 32), Description("This is a dictionary. For every cargo defined in both CompatibleCargoCategories, an entry must exist in this dictionary.")] + [Category("Cargo")] + [Length(0, 32)] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Description("This is a dictionary. For every cargo defined in both CompatibleCargoCategories, an entry must exist in this dictionary.")] public BindingList CargoTypeSpriteOffsets { get; init; } - [Category("Sound"), ConditionTarget] + [Category("Sound")] + [ConditionTarget] public DrivingSoundType DrivingSoundType { get => model.DrivingSoundType; @@ -269,26 +299,39 @@ public DrivingSoundType DrivingSoundType bool IsDrivingSoundTypeSet => model.DrivingSoundType != DrivingSoundType.None; - [Category("Sound"), Reactive, ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(IsDrivingSoundTypeSet), true)] + [Category("Sound")] + [Reactive] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [PropertyVisibilityCondition(nameof(IsDrivingSoundTypeSet), true)] public ObjectModelHeader? Sound { get; set; } - [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.Friction)] + [Category("Sound")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.Friction)] public FrictionSound FrictionSound { get; set; } - [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.SimpleMotor)] + [Category("Sound")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.SimpleMotor)] public SimpleMotorSound SimpleMotorSound { get; set; } - [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No), ConditionTarget, PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.GearboxMotor)] + [Category("Sound")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [ConditionTarget] + [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.GearboxMotor)] public GearboxMotorSound GearboxMotorSound { get; set; } - [Category("Sound"), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Category("Sound")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList StartSounds { get; init; } - [Category(""), ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] + [Category("")] + [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList var_135 { get; init; } public override void CopyBackToModel() { + // this should be done with the reactive properties, but for now we'll leave it like this Model.MaxCargo = [CompatibleCargo1.MaxCargo, CompatibleCargo2.MaxCargo]; Model.CompatibleCargoCategories = [ From 7f2cd0e94775aa10dca0999bb77297a4ae1d0ba4 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 1 Oct 2025 17:38:01 +1000 Subject: [PATCH 4/4] actually remove category headers, use different layout style in property grid --- Gui/App.axaml | 4 ++-- .../LocoTypes/Objects/AirportViewModel.cs | 6 ------ .../LocoTypes/Objects/BridgeViewModel.cs | 3 --- .../LocoTypes/Objects/DockViewModel.cs | 4 ---- .../LocoTypes/Objects/IndustryViewModel.cs | 10 ---------- .../LocoTypes/Objects/LandViewModel.cs | 3 --- .../LocoTypes/Objects/RegionViewModel.cs | 3 --- .../LocoTypes/Objects/RoadStationViewModel.cs | 3 --- .../LocoTypes/Objects/RoadViewModel.cs | 6 ------ .../LocoTypes/Objects/SoundViewModel.cs | 2 -- .../LocoTypes/Objects/SteamViewModel.cs | 4 ---- .../LocoTypes/Objects/TownNamesViewModel.cs | 2 -- .../LocoTypes/Objects/TrackSignalViewModel.cs | 2 -- .../LocoTypes/Objects/TrackStationViewModel.cs | 3 --- .../LocoTypes/Objects/TrackViewModel.cs | 7 ------- .../LocoTypes/Objects/VehicleViewModel.cs | 17 ----------------- Gui/Views/ImageView.axaml | 4 ++-- 17 files changed, 4 insertions(+), 79 deletions(-) diff --git a/Gui/App.axaml b/Gui/App.axaml index 614b925e..29d2584c 100644 --- a/Gui/App.axaml +++ b/Gui/App.axaml @@ -69,7 +69,7 @@ - + @@ -118,7 +118,7 @@ - + diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs index 1e5717cd..2e0d49db 100644 --- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs @@ -1,7 +1,6 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Common; -using PropertyModels.ComponentModel; using PropertyModels.Extensions; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -83,7 +82,6 @@ public int16_t SellCostFactor [Category("Building")] [Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); [Category("Building")] @@ -92,19 +90,15 @@ public int16_t SellCostFactor [Category("Building")] [Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("Building")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingPositions { get; init; } = model.BuildingPositions.ToBindingList(); [Category("Movement")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList MovementNodes { get; init; } = model.MovementNodes.ToBindingList(); [Category("Movement")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList MovementEdges { get; init; } = model.MovementEdges.ToBindingList(); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs index e0531bcb..5a860eab 100644 --- a/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Bridge; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -94,11 +93,9 @@ public int16_t SellCostFactor } [Category("Compatible")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); [Category("Compatible")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleRoadObjects { get; init; } = new(model.CompatibleRoadObjects); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs index 4b55a0f5..9671277a 100644 --- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using System.ComponentModel; @@ -33,7 +32,6 @@ public uint16_t ObsoleteYear set => Model.ObsoleteYear = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public Pos2 BoatPosition { get => Model.BoatPosition; @@ -63,7 +61,6 @@ public int16_t SellCostFactor [Category("Building")] [Length(1, DockObjectLoader.Constants.BuildingVariationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); [Category("Building")] @@ -72,7 +69,6 @@ public int16_t SellCostFactor [Category("Building")] [Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs index cae17d3b..141b3ab5 100644 --- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using System.ComponentModel; @@ -55,12 +54,10 @@ public uint32_t Colours } [Category("Production")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList InitialProductionRate { get; init; } = new(model.InitialProductionRate); [Category("Production")] [Length(0, IndustryObjectLoader.Constants.MaxProducedCargoType)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList ProducedCargo { get; init; } = new(model.ProducedCargo); [Category("Production")] @@ -125,12 +122,10 @@ public uint8_t FarmNumStagesOfGrowth [Category("Building")] [Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> AnimationSequences { get; init; } = model.AnimationSequences.Select(x => x.ToBindingList()).ToBindingList(); [Category("Building")] [Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList> BuildingVariations { get; init; } = new(model.BuildingComponents.BuildingVariations.Select(x => x.ToBindingList()).ToBindingList()); [Category("Building")] @@ -139,7 +134,6 @@ public uint8_t FarmNumStagesOfGrowth [Category("Building")] [Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BuildingAnimations { get; init; } = model.BuildingComponents.BuildingAnimations.ToBindingList(); [Category("Building")] @@ -182,11 +176,9 @@ public Colour ScaffoldingColour [Category("Building")] [Length(0, IndustryObjectLoader.Constants.MaxWallTypeCount)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList WallTypes { get; init; } = model.WallTypes.ToBindingList(); [Category("Building")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? BuildingWall { get => Model.BuildingWall; @@ -194,7 +186,6 @@ public ObjectModelHeader? BuildingWall } [Category("Building")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? BuildingWallEntrance { get => Model.BuildingWallEntrance; @@ -202,7 +193,6 @@ public ObjectModelHeader? BuildingWallEntrance } [Category("")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList var_38 { get; init; } = model.var_38.ToBindingList(); [Category("")] diff --git a/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs index 052a0ad3..4490be4d 100644 --- a/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Land; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; namespace Gui.ViewModels; @@ -61,14 +60,12 @@ public uint8_t VariationLikelihood set => Model.VariationLikelihood = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader CliffEdgeHeader { get => Model.CliffEdgeHeader; set => Model.CliffEdgeHeader = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? ReplacementLandHeader { get => Model.ReplacementLandHeader; diff --git a/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs index d85607a3..dbc29001 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Region; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using System.ComponentModel; namespace Gui.ViewModels; @@ -20,11 +19,9 @@ public uint8_t pad_07 set => Model.pad_07 = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList DependentObjects { get; init; } = new(model.DependentObjects); [Category("Cargo")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CargoInfluenceObjects { get; init; } = new(model.CargoInfluenceObjects); [Category("Cargo")] diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs index 9f3a0cea..b1d62cb7 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels.Objects.RoadStation; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -79,10 +78,8 @@ public ObjectModelHeader? CargoType } [Category("Cargo")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CargoOffset[][][] CargoOffsets { get; init; } = model.CargoOffsets; [Category("Compatible")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleRoadObjects { get; init; } = new(model.CompatibleRoadObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs index 3737d0a6..517a5b9c 100644 --- a/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Road; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -76,7 +75,6 @@ public uint8_t CostIndex } [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader Tunnel { get => Model.Tunnel; @@ -84,18 +82,14 @@ public ObjectModelHeader Tunnel } [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Bridges { get; set; } = new(model.Bridges); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Stations { get; set; } = new(model.Stations); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Mods { get; set; } = new(model.RoadMods); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList TracksAndRoads { get; set; } = new(model.TracksAndRoads); } diff --git a/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs index 443e4c34..e9f85a12 100644 --- a/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs @@ -1,5 +1,4 @@ using Definitions.ObjectModels.Objects.Sound; -using PropertyModels.ComponentModel; using System.ComponentModel; namespace Gui.ViewModels; @@ -19,7 +18,6 @@ public uint32_t Volume set => Model.Volume = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public SoundObjectData SoundObjectData { get => Model.SoundObjectData; diff --git a/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs index c9c66270..7c369164 100644 --- a/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Steam; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -28,12 +27,9 @@ public uint32_t var_0A set => Model.var_0A = value; } - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList SoundEffects { get; init; } = new(model.SoundEffects); - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList FrameInfoType0 { get; init; } = new(model.FrameInfoType0); - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList FrameInfoType1 { get; init; } = new(model.FrameInfoType1); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs index 3ee82454..0f72c819 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs @@ -1,5 +1,4 @@ using Definitions.ObjectModels.Objects.TownNames; -using PropertyModels.ComponentModel; using PropertyModels.Extensions; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -11,7 +10,6 @@ public class TownNamesViewModel(TownNamesObject model) { [Length(6, 6)] [Editable(false)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Categories { get; set; } = model.Categories.ToBindingList(); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs index 386c40f3..2e5d1f50 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs @@ -1,7 +1,6 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.TrackSignal; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; using System.ComponentModel.DataAnnotations; @@ -66,6 +65,5 @@ public uint16_t ObsoleteYear } [Length(0, TrackSignalObjectLoader.Constants.ModsLength)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs index 28104f49..94104e04 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Objects.TrackStation; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; @@ -88,10 +87,8 @@ public uint8_t var_0D public uint8_t[][] var_6E { get; set; } = model.var_6E; [Category("Cargo")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CargoOffset[][][] CargoOffsets { get; init; } = model.CargoOffsets; [Category("Compatible")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleTrackObjects { get; init; } = new(model.CompatibleTrackObjects); } diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs index ffd55048..f44f32e1 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs @@ -1,6 +1,5 @@ using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Types; -using PropertyModels.ComponentModel; using PropertyModels.ComponentModel.DataAnnotations; using System.ComponentModel; using TrackObject = Definitions.ObjectModels.Objects.Track.TrackObject; @@ -79,7 +78,6 @@ public uint8_t var_06 } [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader Tunnel { get => Model.Tunnel; @@ -87,22 +85,17 @@ public ObjectModelHeader Tunnel } [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList TracksAndRoads { get; init; } = new(model.TracksAndRoads); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList TrackExtras { get; init; } = new(model.TrackMods); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Signals { get; init; } = new(model.Signals); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Bridges { get; init; } = new(model.Bridges); [Category("Compatible Objects")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Stations { get; init; } = new(model.Stations); } diff --git a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs index 06b8cffd..08185998 100644 --- a/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs @@ -168,7 +168,6 @@ bool IsTrackTypeSettable [Reactive] [ConditionTarget] [PropertyVisibilityCondition(nameof(IsTrackTypeSettable), true)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? RoadOrTrackType { get; set; } [ConditionTarget] @@ -187,7 +186,6 @@ public bool HasRackRail [Reactive] [ConditionTarget] [PropertyVisibilityCondition(nameof(HasRackRail), true)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public ObjectModelHeader? RackRail { get; set; } [Range(0, 4)] @@ -198,11 +196,9 @@ public uint8_t NumCarComponents } [Length(0, 8)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CompatibleVehicles { get; init; } [Length(0, 4)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList RequiredTrackExtras { get; init; } [Description("If 0, boat has a single wake animation. if > 0, boat has 2 wakes, offset horizontally by this value")] @@ -252,32 +248,25 @@ public CompanyColourType SpecialColourSchemeIndex } // called "ColourType" in the loco codebase [Category("Sprites")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList CarComponents { get; init; } [Category("Sprites")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BodySprites { get; init; } [Category("Sprites")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList BogieSprites { get; init; } [Category("Sprites")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList Animation { get; init; } [Category("Cargo")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo1 { get; init; } [Category("Cargo")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public CompatibleCargo CompatibleCargo2 { get; init; } [Category("Cargo")] [Length(0, 32)] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] [Description("This is a dictionary. For every cargo defined in both CompatibleCargoCategories, an entry must exist in this dictionary.")] public BindingList CargoTypeSpriteOffsets { get; init; } @@ -301,32 +290,26 @@ bool IsDrivingSoundTypeSet [Category("Sound")] [Reactive] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] [PropertyVisibilityCondition(nameof(IsDrivingSoundTypeSet), true)] public ObjectModelHeader? Sound { get; set; } [Category("Sound")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.Friction)] public FrictionSound FrictionSound { get; set; } [Category("Sound")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.SimpleMotor)] public SimpleMotorSound SimpleMotorSound { get; set; } [Category("Sound")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] [ConditionTarget] [PropertyVisibilityCondition(nameof(DrivingSoundType), DrivingSoundType.GearboxMotor)] public GearboxMotorSound GearboxMotorSound { get; set; } [Category("Sound")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList StartSounds { get; init; } [Category("")] - [ExpandableObjectDisplayMode(IsCategoryVisible = NullableBooleanType.No)] public BindingList var_135 { get; init; } public override void CopyBackToModel() diff --git a/Gui/Views/ImageView.axaml b/Gui/Views/ImageView.axaml index f67379ba..74fce643 100644 --- a/Gui/Views/ImageView.axaml +++ b/Gui/Views/ImageView.axaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vmg="using:Gui.ViewModels.Graphics" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" - xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" + xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" xmlns:gmc="using:Gui.Models.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -15,7 +15,7 @@ - +