Observable collection base code for those without a true backing collection.

Observable sorted dictionary
Grid view now displays blocks grouped by subtype.
Null propagation in entity view models because WPF.
This commit is contained in:
Westin Miller
2017-12-04 23:52:03 -08:00
parent 5b098c68aa
commit c188367749
17 changed files with 909 additions and 540 deletions

View File

@@ -15,10 +15,10 @@ namespace Torch.Server.ViewModels.Blocks
{
public class BlockViewModel : EntityViewModel
{
public IMyTerminalBlock Block { get; }
public IMyTerminalBlock Block => (IMyTerminalBlock) Entity;
public MtObservableList<PropertyViewModel> Properties { get; } = new MtObservableList<PropertyViewModel>();
public string FullName => $"{Block.CubeGrid.CustomName} - {Block.CustomName}";
public string FullName => $"{Block?.CubeGrid.CustomName} - {Block?.CustomName}";
public override string Name
{
@@ -38,7 +38,7 @@ namespace Torch.Server.ViewModels.Blocks
public long BuiltBy
{
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
get => ((MySlimBlock)Block?.SlimBlock)?.BuiltBy ?? 0;
set
{
TorchBase.Instance.Invoke(() =>
@@ -59,7 +59,6 @@ namespace Torch.Server.ViewModels.Blocks
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
{
Block = block;
if (Block == null)
return;

View File

@@ -32,7 +32,7 @@ namespace Torch.Server.ViewModels.Entities
public virtual string Name
{
get => Entity.DisplayName;
get => Entity?.DisplayName;
set
{
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
@@ -42,7 +42,7 @@ namespace Torch.Server.ViewModels.Entities
public virtual string Position
{
get => Entity.GetPosition().ToString();
get => Entity?.GetPosition().ToString();
set
{
if (!Vector3D.TryParse(value, out Vector3D v))

View File

@@ -1,69 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Sandbox.Definitions;
using Sandbox.Game.Entities;
using Sandbox.Game.Entities.Cube;
using Sandbox.ModAPI;
using Torch.API.Managers;
using Torch.Collections;
using Torch.Server.ViewModels.Blocks;
using VRage.Game;
namespace Torch.Server.ViewModels.Entities
{
public class GridViewModel : EntityViewModel, ILazyLoad
{
private MyCubeGrid Grid => (MyCubeGrid)Entity;
public MtObservableList<BlockViewModel> Blocks { get; } = new MtObservableList<BlockViewModel>();
private static readonly MyCubeBlockDefinition _fillerDefinition = new MyCubeBlockDefinition()
{
Id = new MyDefinitionId(typeof(MyObjectBuilder_DefinitionBase), "")
};
private class CubeBlockDefinitionComparer : IComparer<MyCubeBlockDefinition>
{
public static readonly CubeBlockDefinitionComparer Default = new CubeBlockDefinitionComparer();
public int Compare(MyCubeBlockDefinition x, MyCubeBlockDefinition y)
{
if (x == null && y == null)
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
if (ReferenceEquals(x, y))
return 0;
MyDefinitionId xi = x.Id;
MyDefinitionId yi = y.Id;
if (xi == yi)
return 0;
if (xi.TypeId != yi.TypeId)
return string.CompareOrdinal(((Type) xi.TypeId).Name, ((Type) yi.TypeId).Name);
return xi.SubtypeId == yi.SubtypeId ? 0 : string.CompareOrdinal(xi.SubtypeName, yi.SubtypeName);
}
}
private MyCubeGrid Grid => (MyCubeGrid) Entity;
public MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>
Blocks { get; } =
new MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>(
CubeBlockDefinitionComparer.Default);
/// <inheritdoc />
public string DescriptiveName { get; }
public GridViewModel() { }
public GridViewModel()
{
}
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
{
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
Blocks.Add(new BlockViewModel(null, Tree));
Blocks.Add(_fillerDefinition, new MtObservableSortedDictionary<long, BlockViewModel>());
}
private void Grid_OnBlockRemoved(Sandbox.Game.Entities.Cube.MySlimBlock obj)
{
if (obj.FatBlock != null)
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
RemoveBlock(obj.FatBlock);
OnPropertyChanged(nameof(Name));
}
private void RemoveBlock(MyCubeBlock block)
{
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
return;
if (group.Remove(block.EntityId) && group.Count == 0 && Blocks.Count > 1)
Blocks.Remove(block.BlockDefinition);
}
private void AddBlock(MyTerminalBlock block)
{
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
group = Blocks[block.BlockDefinition] = new MtObservableSortedDictionary<long, BlockViewModel>();
group.Add(block.EntityId, new BlockViewModel(block, Tree));
}
private void Grid_OnBlockAdded(Sandbox.Game.Entities.Cube.MySlimBlock obj)
{
var block = obj.FatBlock as IMyTerminalBlock;
var block = obj.FatBlock as MyTerminalBlock;
if (block != null)
Blocks.Add(new BlockViewModel(block, Tree));
AddBlock(block);
OnPropertyChanged(nameof(Name));
}
private bool _load;
public void Load()
{
if (_load)
return;
_load = true;
Blocks.Clear();
TorchBase.Instance.Invoke(() =>
{
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
{
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
}
Blocks.Clear();
foreach (var block in Grid.GetFatBlocks().OfType<MyTerminalBlock>())
AddBlock(block);
Grid.OnBlockAdded += Grid_OnBlockAdded;
Grid.OnBlockRemoved += Grid_OnBlockRemoved;
Tree.ControlDispatcher.BeginInvoke(() =>
{
Blocks.Sort(b => b.Block.CustomName);
});
});
}
}
}
}

View File

@@ -29,11 +29,10 @@ namespace Torch.Server.ViewModels.Entities
await TorchBase.Instance.InvokeAsync(() => MyEntities.GetTopMostEntitiesInBox(ref box, entities)).ConfigureAwait(false);
foreach (var entity in entities.Where(e => e is IMyCubeGrid))
{
var gridModel = Tree.Grids.FirstOrDefault(g => g.Entity.EntityId == entity.EntityId);
if (gridModel == null)
if (Tree.Grids.TryGetValue(entity.EntityId, out var gridModel))
{
gridModel = new GridViewModel((MyCubeGrid)entity, Tree);
Tree.Grids.Add(gridModel);
Tree.Grids.Add(entity.EntityId, gridModel);
}
AttachedGrids.Add(gridModel);

View File

@@ -20,10 +20,10 @@ namespace Torch.Server.ViewModels
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
//TODO: these should be sorted sets for speed
public MtObservableList<GridViewModel> Grids { get; set; } = new MtObservableList<GridViewModel>();
public MtObservableList<CharacterViewModel> Characters { get; set; } = new MtObservableList<CharacterViewModel>();
public MtObservableList<EntityViewModel> FloatingObjects { get; set; } = new MtObservableList<EntityViewModel>();
public MtObservableList<VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableList<VoxelMapViewModel>();
public MtObservableSortedDictionary<long, GridViewModel> Grids { get; set; } = new MtObservableSortedDictionary<long, GridViewModel>();
public MtObservableSortedDictionary<long, CharacterViewModel> Characters { get; set; } = new MtObservableSortedDictionary<long, CharacterViewModel>();
public MtObservableSortedDictionary<long, EntityViewModel> FloatingObjects { get; set; } = new MtObservableSortedDictionary<long, EntityViewModel>();
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
public Dispatcher ControlDispatcher => _control.Dispatcher;
private EntityViewModel _currentEntity;
@@ -35,6 +35,11 @@ namespace Torch.Server.ViewModels
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
}
// I hate you today WPF
public EntityTreeViewModel() : this(null)
{
}
public EntityTreeViewModel(UserControl control)
{
_control = control;
@@ -53,16 +58,16 @@ namespace Torch.Server.ViewModels
switch (obj)
{
case MyCubeGrid grid:
Grids.RemoveWhere(m => m.Id == grid.EntityId);
Grids.Remove(grid.EntityId);
break;
case MyCharacter character:
Characters.RemoveWhere(m => m.Id == character.EntityId);
Characters.Remove(character.EntityId);
break;
case MyFloatingObject floating:
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
FloatingObjects.Remove(floating.EntityId);
break;
case MyVoxelBase voxel:
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
VoxelMaps.Remove(voxel.EntityId);
break;
}
}
@@ -80,16 +85,16 @@ namespace Torch.Server.ViewModels
switch (obj)
{
case MyCubeGrid grid:
Grids.Add(new GridViewModel(grid, this));
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
break;
case MyCharacter character:
Characters.Add(new CharacterViewModel(character, this));
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
break;
case MyFloatingObject floating:
FloatingObjects.Add(new FloatingObjectViewModel(floating, this));
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
break;
case MyVoxelBase voxel:
VoxelMaps.Add(new VoxelMapViewModel(voxel, this));
VoxelMaps.Add(obj.EntityId, new VoxelMapViewModel(voxel, this));
break;
}
}