mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Debugger - Added datamodel debugger
Shared UI - Added data model VMs
This commit is contained in:
parent
e05de0d780
commit
e4c1c99e27
2
src/.idea/.idea.Artemis/.idea/avalonia.xml
generated
2
src/.idea/.idea.Artemis/.idea/avalonia.xml
generated
@ -14,6 +14,8 @@
|
||||
<entry key="Artemis.UI.Avalonia/Screens/Sidebar/Views/SidebarProfileConfigurationView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
|
||||
<entry key="Artemis.UI.Avalonia/Screens/SidebarView.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
|
||||
<entry key="Artemis.UI.Avalonia/Views/MainWindow.axaml" value="Artemis.UI.Avalonia/Artemis.UI.Avalonia.csproj" />
|
||||
<entry key="Avalonia/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugView.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
|
||||
<entry key="Avalonia/Artemis.UI/Styles/Artemis.axaml" value="Avalonia/Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@ -3,8 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.Ninject;
|
||||
using Artemis.Core.ScriptingProviders;
|
||||
using Artemis.Storage;
|
||||
@ -23,17 +21,19 @@ namespace Artemis.Core.Services
|
||||
internal class CoreService : ICoreService
|
||||
{
|
||||
internal static IKernel? Kernel;
|
||||
|
||||
private readonly Stopwatch _frameStopWatch;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<LogEventLevel> _loggingLevel;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IScriptingService _scriptingService;
|
||||
private readonly IRgbService _rgbService;
|
||||
private readonly IScriptingService _scriptingService;
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
|
||||
private int _frames;
|
||||
private DateTime _lastExceptionLog;
|
||||
private DateTime _lastFrameRateSample;
|
||||
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
public CoreService(IKernel kernel,
|
||||
@ -151,6 +151,15 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
_rgbService.CloseRender();
|
||||
_frameStopWatch.Stop();
|
||||
_frames++;
|
||||
|
||||
if ((DateTime.Now - _lastFrameRateSample).TotalSeconds >= 1)
|
||||
{
|
||||
FrameRate = _frames;
|
||||
_frames = 0;
|
||||
_lastFrameRateSample = DateTime.Now;
|
||||
}
|
||||
|
||||
FrameTime = _frameStopWatch.Elapsed;
|
||||
|
||||
LogUpdateExceptions();
|
||||
@ -181,6 +190,7 @@ namespace Artemis.Core.Services
|
||||
Initialized?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public int FrameRate { get; private set; }
|
||||
public TimeSpan FrameTime { get; private set; }
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
public List<string> StartupArguments { get; set; }
|
||||
|
||||
@ -18,6 +18,11 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
TimeSpan FrameTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of frames rendered each second
|
||||
/// </summary>
|
||||
public int FrameRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether profiles are rendered each frame by calling their Render method
|
||||
/// </summary>
|
||||
|
||||
@ -579,9 +579,9 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
if (plugin.IsEnabled)
|
||||
throw new ArtemisCoreException("Cannot remove the settings of an enabled plugin");
|
||||
|
||||
|
||||
_pluginRepository.RemoveSettings(plugin.Guid);
|
||||
foreach (DeviceEntity deviceEntity in _deviceRepository.GetAll().Where(e => e.DeviceProvider == plugin.Guid.ToString()))
|
||||
foreach (DeviceEntity deviceEntity in _deviceRepository.GetAll().Where(e => e.DeviceProvider == plugin.Guid.ToString()))
|
||||
_deviceRepository.Remove(deviceEntity);
|
||||
|
||||
plugin.Settings?.ClearSettings();
|
||||
@ -602,7 +602,10 @@ namespace Artemis.Core.Services
|
||||
if (!saveState)
|
||||
{
|
||||
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
|
||||
throw new ArtemisCoreException("Cannot enable a feature that requires elevation without saving it's state.");
|
||||
if (isAutoEnable)
|
||||
_logger.Warning("Skipped auto-enabling plugin feature {feature} - {plugin} because it requires elevation but state isn't being saved", pluginFeature, pluginFeature.Plugin);
|
||||
else
|
||||
throw new ArtemisCoreException("Cannot enable a feature that requires elevation without saving it's state.");
|
||||
}
|
||||
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
|
||||
@ -32,5 +32,15 @@ namespace Artemis.Core
|
||||
|
||||
return Enum.GetValues(t).Cast<Enum>().Select(e => (e, e.Humanize())).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Humanizes the given enum value using the Humanizer library
|
||||
/// </summary>
|
||||
/// <param name="value">The enum value to humanize</param>
|
||||
/// <returns>A humanized string describing the given value</returns>
|
||||
public static string HumanizeValue(Enum value)
|
||||
{
|
||||
return value.Humanize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,6 +174,468 @@
|
||||
<member name="M:Artemis.UI.Shared.Converters.SKColorToColorConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Converters.TypeToStringConverter">
|
||||
<summary>
|
||||
Converts <see cref="T:System.Type" /> into <see cref="T:System.String" />.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Converters.TypeToStringConverter.Convert(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Converters.TypeToStringConverter.ConvertBack(System.Object,System.Type,System.Object,System.Globalization.CultureInfo)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1">
|
||||
<summary>
|
||||
Represents a <see cref="T:Artemis.Core.Modules.DataModel" /> display view model
|
||||
</summary>
|
||||
<typeparam name="T">The type of the data model</typeparam>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1.DisplayValue">
|
||||
<summary>
|
||||
Gets or sets value that the view model must display
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1.UpdateValue(System.Object)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1.OnDisplayValueUpdated">
|
||||
<summary>
|
||||
Occurs when the display value is updated
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel">
|
||||
<summary>
|
||||
For internal use only, implement <see cref="T:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1" /> instead.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel.PropertyDescription">
|
||||
<summary>
|
||||
Gets the property description of this value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel.InternalGuard">
|
||||
<summary>
|
||||
Prevents this type being implemented directly, implement <see cref="T:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel`1" /> instead.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelDisplayViewModel.UpdateValue(System.Object)">
|
||||
<summary>
|
||||
Updates the display value
|
||||
</summary>
|
||||
<param name="model">The value to set</param>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1">
|
||||
<summary>
|
||||
Represents a <see cref="T:Artemis.Core.Modules.DataModel" /> input view model
|
||||
</summary>
|
||||
<typeparam name="T">The type of the data model</typeparam>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1.#ctor(Artemis.Core.Modules.DataModelPropertyAttribute,`0)">
|
||||
<summary>
|
||||
Creates a new instance of the <see cref="T:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1" /> class
|
||||
</summary>
|
||||
<param name="targetDescription">The description of the property this input VM is representing</param>
|
||||
<param name="initialValue">The initial value to set the input value to</param>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1.InputValue">
|
||||
<summary>
|
||||
Gets or sets the value shown in the input
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1.TargetDescription">
|
||||
<summary>
|
||||
Gets the description of the property this input VM is representing
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1.Submit">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1.Cancel">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel">
|
||||
<summary>
|
||||
For internal use only, implement <see cref="T:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1" /> instead.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.InternalGuard">
|
||||
<summary>
|
||||
Prevents this type being implemented directly, implement <see cref="T:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel`1" /> instead.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.CompatibleConversionTypes">
|
||||
<summary>
|
||||
Gets the types this input view model can support through type conversion. This list is defined when registering the
|
||||
view model.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.Submit">
|
||||
<summary>
|
||||
Submits the input value and removes this view model.
|
||||
<para>This is called automatically when the user presses enter or clicks outside the view</para>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.Cancel">
|
||||
<summary>
|
||||
Discards changes to the input value and removes this view model.
|
||||
<para>This is called automatically when the user presses escape</para>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.OnSubmit">
|
||||
<summary>
|
||||
Called before the current value is submitted
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.DataModelInputViewModel.OnCancel">
|
||||
<summary>
|
||||
Called before the current value is discarded
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration">
|
||||
<summary>
|
||||
Represents a layer brush registered through
|
||||
<see cref="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelInput``1(Artemis.Core.Plugin,System.Collections.Generic.IReadOnlyCollection{System.Type})" /> or
|
||||
<see cref="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelDisplay``1(Artemis.Core.Plugin)" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration.RegistrationType">
|
||||
<summary>
|
||||
Gets the type of registration, either a display or an input
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration.Plugin">
|
||||
<summary>
|
||||
Gets the plugin that registered the visualization
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration.SupportedType">
|
||||
<summary>
|
||||
Gets the type supported by the visualization
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration.ViewModelType">
|
||||
<summary>
|
||||
Gets the view model type of the visualization
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration.CompatibleConversionTypes">
|
||||
<summary>
|
||||
Gets a read only collection of types this visualization can convert to and from
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.RegistrationType">
|
||||
<summary>
|
||||
Represents a type of data model visualization registration
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Shared.DataModelVisualization.RegistrationType.Display">
|
||||
<summary>
|
||||
A visualization used for displaying values
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Artemis.UI.Shared.DataModelVisualization.RegistrationType.Input">
|
||||
<summary>
|
||||
A visualization used for inputting values
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelEventViewModel">
|
||||
<summary>
|
||||
Represents a view model that visualizes an event data model property
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelEventViewModel.DisplayValueType">
|
||||
<summary>
|
||||
Gets the type of event arguments this event triggers and that must be displayed as children
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelEventViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelEventViewModel.GetCurrentValue">
|
||||
<summary>
|
||||
Always returns <see langword="null"/> for data model events
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelEventViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel">
|
||||
<summary>
|
||||
Represents a view model that wraps a regular <see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel" /> but contained in
|
||||
a <see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.Index">
|
||||
<summary>
|
||||
Gets the index of the element within the list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.ListType">
|
||||
<summary>
|
||||
Gets the type of elements contained in the list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.DisplayValue">
|
||||
<summary>
|
||||
Gets the value of the property that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.DisplayViewModel">
|
||||
<summary>
|
||||
Gets the view model that handles displaying the property
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.DisplayPath">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.GetCurrentValue">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertiesViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel">
|
||||
<summary>
|
||||
Represents a view model that visualizes a single data model property contained in a
|
||||
<see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel.Index">
|
||||
<summary>
|
||||
Gets the index of the element within the list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel.ListType">
|
||||
<summary>
|
||||
Gets the type of elements contained in the list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel.GetCurrentValue">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListPropertyViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel">
|
||||
<summary>
|
||||
Represents a view model that visualizes a list data model property
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.List">
|
||||
<summary>
|
||||
Gets the instance of the list that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.ListCount">
|
||||
<summary>
|
||||
Gets amount of elements in the list that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.DisplayValueType">
|
||||
<summary>
|
||||
Gets the type of elements this list contains and that must be displayed as children
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.CountDisplay">
|
||||
<summary>
|
||||
Gets a human readable display count
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.ListChildren">
|
||||
<summary>
|
||||
Gets a list of child view models that visualize the elements in the list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelListViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel">
|
||||
<summary>
|
||||
Represents a view model that visualizes a class (POCO) data model property containing child properties
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel.DisplayValueType">
|
||||
<summary>
|
||||
Gets the type of the property that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel.DisplayValue">
|
||||
<summary>
|
||||
Gets the value of the property that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel.GetCurrentValue">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel">
|
||||
<summary>
|
||||
Represents a view model that visualizes a single data model property contained in a
|
||||
<see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel.DisplayValue">
|
||||
<summary>
|
||||
Gets the value of the property that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel.DisplayValueType">
|
||||
<summary>
|
||||
Gets the type of the property that is being visualized
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel.DisplayViewModel">
|
||||
<summary>
|
||||
Gets the view model used to display the display value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertyViewModel.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration">
|
||||
<summary>
|
||||
Represents a configuration to use while updating a <see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Creates a new instance of the <see cref="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration" /> class
|
||||
</summary>
|
||||
<param name="createEventChildren">A boolean indicating whether or not event children should be created</param>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration.CreateEventChildren">
|
||||
<summary>
|
||||
Gets a boolean indicating whether or not event children should be created
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel">
|
||||
<summary>
|
||||
Represents a base class for a view model that visualizes a part of the data model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.IsRootViewModel">
|
||||
<summary>
|
||||
Gets a boolean indicating whether this view model is at the root of the data model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.DataModelPath">
|
||||
<summary>
|
||||
Gets the data model path to the property this view model is visualizing
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Path">
|
||||
<summary>
|
||||
Gets a string representation of the path backing this model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Depth">
|
||||
<summary>
|
||||
Gets the property depth of the view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.DataModel">
|
||||
<summary>
|
||||
Gets the data model backing this view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.PropertyDescription">
|
||||
<summary>
|
||||
Gets the property description of the property this view model is visualizing
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Parent">
|
||||
<summary>
|
||||
Gets the parent of this view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Children">
|
||||
<summary>
|
||||
Gets or sets an observable collection containing the children of this view model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.IsMatchingFilteredTypes">
|
||||
<summary>
|
||||
Gets a boolean indicating whether the property being visualized matches the types last provided to
|
||||
<see cref="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.ApplyTypeFilter(System.Boolean,System.Type[])" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.IsVisualizationExpanded">
|
||||
<summary>
|
||||
Gets or sets a boolean indicating whether the visualization is expanded, exposing the <see cref="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Children" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.DisplayPath">
|
||||
<summary>
|
||||
Gets a user-friendly representation of the <see cref="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.DataModelPath" />
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Update(Artemis.UI.Shared.Services.IDataModelUIService,Artemis.UI.Shared.DataModelVisualization.Shared.DataModelUpdateConfiguration)">
|
||||
<summary>
|
||||
Updates the datamodel and if in an parent, any children
|
||||
</summary>
|
||||
<param name="dataModelUIService">The data model UI service used during update</param>
|
||||
<param name="configuration">The configuration to apply while updating</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.GetCurrentValue">
|
||||
<summary>
|
||||
Gets the current value of the property being visualized
|
||||
</summary>
|
||||
<returns>The current value of the property being visualized</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.ApplyTypeFilter(System.Boolean,System.Type[])">
|
||||
<summary>
|
||||
Determines whether the provided types match the type of the property being visualized and sets the result in
|
||||
<see cref="P:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.IsMatchingFilteredTypes" />
|
||||
</summary>
|
||||
<param name="looseMatch">Whether the type may be a loose match, meaning it can be cast or converted</param>
|
||||
<param name="filteredTypes">The types to filter</param>
|
||||
</member>
|
||||
<member name="E:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.UpdateRequested">
|
||||
<summary>
|
||||
Occurs when an update to the property this view model visualizes is requested
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.OnUpdateRequested">
|
||||
<summary>
|
||||
Invokes the <see cref="E:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.UpdateRequested" /> event
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Dispose(System.Boolean)">
|
||||
<summary>
|
||||
Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
</summary>
|
||||
<param name="disposing">
|
||||
<see langword="true" /> to release both managed and unmanaged resources;
|
||||
<see langword="false" /> to release only unmanaged resources.
|
||||
</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.DataModelVisualization.Shared.DataModelVisualizationViewModel.Dispose">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayViewModel">
|
||||
<summary>
|
||||
Represents the default data model display view model that is used when no display viewmodel specific for the type
|
||||
is registered
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Events.DataModelInputDynamicEventArgs">
|
||||
<summary>
|
||||
Provides data about selection events raised by <see cref="!:DataModelDynamicViewModel" />
|
||||
@ -239,7 +701,7 @@
|
||||
If applicable, the previous active profile element before the event was raised
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Exceptions.ArtemisSharedUIException">
|
||||
<member name="T:Artemis.UI.Shared.ArtemisSharedUIException">
|
||||
<summary>
|
||||
Represents errors that occur within the Artemis Shared UI library
|
||||
</summary>
|
||||
@ -453,6 +915,104 @@
|
||||
</summary>
|
||||
<returns>The builder that can be used to configure the dialog</returns>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.Services.IDataModelUIService">
|
||||
<summary>
|
||||
A service for UI related data model tasks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.Services.IDataModelUIService.RegisteredDataModelEditors">
|
||||
<summary>
|
||||
Gets a read-only list of all registered data model editors
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Artemis.UI.Shared.Services.IDataModelUIService.RegisteredDataModelDisplays">
|
||||
<summary>
|
||||
Gets a read-only list of all registered data model displays
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.GetMainDataModelVisualization">
|
||||
<summary>
|
||||
Creates a data model visualization view model for the main data model
|
||||
</summary>
|
||||
<returns>
|
||||
A data model visualization view model containing all data model expansions and modules that expand the main
|
||||
data model
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.GetPluginDataModelVisualization(System.Collections.Generic.List{Artemis.Core.Modules.Module},System.Boolean)">
|
||||
<summary>
|
||||
Creates a data model visualization view model for the data model of the provided plugin feature
|
||||
</summary>
|
||||
<param name="modules">The modules to create the data model visualization view model for</param>
|
||||
<param name="includeMainDataModel">
|
||||
Whether or not also to include the main data model (and therefore any modules marked
|
||||
as <see cref="P:Artemis.Core.Modules.Module.IsAlwaysAvailable" />)
|
||||
</param>
|
||||
<returns>A data model visualization view model containing the data model of the provided feature</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.UpdateModules(Artemis.UI.Shared.DataModelVisualization.Shared.DataModelPropertiesViewModel)">
|
||||
<summary>
|
||||
Updates the children of the provided main data model visualization, removing disabled children and adding newly
|
||||
enabled children
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelInput``1(Artemis.Core.Plugin,System.Collections.Generic.IReadOnlyCollection{System.Type})">
|
||||
<summary>
|
||||
Registers a new data model editor
|
||||
</summary>
|
||||
<typeparam name="T">The type of the editor</typeparam>
|
||||
<param name="plugin">The plugin this editor belongs to</param>
|
||||
<param name="compatibleConversionTypes">A collection of extra types this editor supports</param>
|
||||
<returns>A registration that can be used to remove the editor</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelDisplay``1(Artemis.Core.Plugin)">
|
||||
<summary>
|
||||
Registers a new data model display
|
||||
</summary>
|
||||
<typeparam name="T">The type of the display</typeparam>
|
||||
<param name="plugin">The plugin this display belongs to</param>
|
||||
<returns>A registration that can be used to remove the display</returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.RemoveDataModelInput(Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration)">
|
||||
<summary>
|
||||
Removes a data model editor
|
||||
</summary>
|
||||
<param name="registration">
|
||||
The registration of the editor as returned by <see cref="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelInput``1(Artemis.Core.Plugin,System.Collections.Generic.IReadOnlyCollection{System.Type})" />
|
||||
</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.RemoveDataModelDisplay(Artemis.UI.Shared.DataModelVisualization.DataModelVisualizationRegistration)">
|
||||
<summary>
|
||||
Removes a data model display
|
||||
</summary>
|
||||
<param name="registration">
|
||||
The registration of the display as returned by <see cref="M:Artemis.UI.Shared.Services.IDataModelUIService.RegisterDataModelDisplay``1(Artemis.Core.Plugin)" />
|
||||
</param>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.GetDataModelDisplayViewModel(System.Type,Artemis.Core.Modules.DataModelPropertyAttribute,System.Boolean)">
|
||||
<summary>
|
||||
Creates the most appropriate display view model for the provided <paramref name="propertyType" /> that can display
|
||||
a value
|
||||
</summary>
|
||||
<param name="propertyType">The type of data model property to find a display view model for</param>
|
||||
<param name="description">The description of the data model property</param>
|
||||
<param name="fallBackToDefault">
|
||||
If <see langword="true"></see>, a simple .ToString() display view model will be
|
||||
returned if nothing else is found
|
||||
</param>
|
||||
<returns>The most appropriate display view model for the provided <paramref name="propertyType"></paramref></returns>
|
||||
</member>
|
||||
<member name="M:Artemis.UI.Shared.Services.IDataModelUIService.GetDataModelInputViewModel(System.Type,Artemis.Core.Modules.DataModelPropertyAttribute,System.Object,System.Action{System.Object,System.Boolean})">
|
||||
<summary>
|
||||
Creates the most appropriate input view model for the provided <paramref name="propertyType" /> that allows
|
||||
inputting a value
|
||||
</summary>
|
||||
<param name="propertyType">The type of data model property to find a display view model for</param>
|
||||
<param name="description">The description of the data model property</param>
|
||||
<param name="initialValue">The initial value to show in the input</param>
|
||||
<param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
|
||||
<returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
|
||||
</member>
|
||||
<member name="T:Artemis.UI.Shared.ViewModelBase">
|
||||
<summary>
|
||||
Represents the base class for Artemis view models
|
||||
|
||||
@ -10,6 +10,11 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>C:\Repos\Artemis\src\Artemis.UI.Avalonia.Shared\Artemis.UI.Avalonia.Shared.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Artemis.UI.Avalonia.Shared.csproj.DotSettings" />
|
||||
<None Remove="Artemis.UI.Shared.csproj.DotSettings" />
|
||||
<None Remove="DefaultTypes\DataModel\Display\DefaultDataModelDisplayView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
@ -20,6 +25,12 @@
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="1.1.5" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="DefaultTypes\DataModel\Display\DefaultDataModelDisplayView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindowservice/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@ -4,4 +4,4 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.Controls.ProfileConfigurationIcon">
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Artemis.Core;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="T:System.Type" /> into <see cref="T:System.String" />.
|
||||
/// </summary>
|
||||
public class TypeToStringConverter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool humanizeProvided = bool.TryParse(parameter?.ToString(), out bool humanize);
|
||||
if (value is Type type)
|
||||
return type.GetDisplayName(humanizeProvided && humanize);
|
||||
|
||||
return value?.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Artemis.Core.Modules;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataModel" /> display view model
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model</typeparam>
|
||||
public abstract class DataModelDisplayViewModel<T> : DataModelDisplayViewModel
|
||||
{
|
||||
[AllowNull] private T _displayValue = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value that the view model must display
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
public T DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
set
|
||||
{
|
||||
if (Equals(value, _displayValue)) return;
|
||||
this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
OnDisplayValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
internal override object InternalGuard => new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateValue(object? model)
|
||||
{
|
||||
DisplayValue = model is T value ? value : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the display value is updated
|
||||
/// </summary>
|
||||
protected virtual void OnDisplayValueUpdated()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only, implement <see cref="DataModelDisplayViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
public abstract class DataModelDisplayViewModel : ViewModelBase
|
||||
{
|
||||
private DataModelPropertyAttribute? _propertyDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property description of this value
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute? PropertyDescription
|
||||
{
|
||||
get => _propertyDescription;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _propertyDescription, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents this type being implemented directly, implement <see cref="DataModelDisplayViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
internal abstract object InternalGuard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the display value
|
||||
/// </summary>
|
||||
/// <param name="model">The value to set</param>
|
||||
public abstract void UpdateValue(object? model);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Artemis.Core.Modules;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataModel" /> input view model
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the data model</typeparam>
|
||||
public abstract class DataModelInputViewModel<T> : DataModelInputViewModel
|
||||
{
|
||||
private bool _closed;
|
||||
[AllowNull] private T _inputValue = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelInputViewModel{T}" /> class
|
||||
/// </summary>
|
||||
/// <param name="targetDescription">The description of the property this input VM is representing</param>
|
||||
/// <param name="initialValue">The initial value to set the input value to</param>
|
||||
protected DataModelInputViewModel(DataModelPropertyAttribute targetDescription, T initialValue)
|
||||
{
|
||||
TargetDescription = targetDescription;
|
||||
InputValue = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown in the input
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
public T InputValue
|
||||
{
|
||||
get => _inputValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _inputValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the property this input VM is representing
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute TargetDescription { get; }
|
||||
|
||||
internal override object InternalGuard { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void Submit()
|
||||
{
|
||||
if (_closed)
|
||||
return;
|
||||
_closed = true;
|
||||
|
||||
OnSubmit();
|
||||
UpdateCallback(InputValue, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void Cancel()
|
||||
{
|
||||
if (_closed)
|
||||
return;
|
||||
_closed = true;
|
||||
|
||||
OnCancel();
|
||||
UpdateCallback(InputValue, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only, implement <see cref="DataModelInputViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
public abstract class DataModelInputViewModel : ReactiveObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents this type being implemented directly, implement <see cref="DataModelInputViewModel{T}" /> instead.
|
||||
/// </summary>
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
internal abstract object InternalGuard { get; }
|
||||
|
||||
internal Action<object?, bool> UpdateCallback { get; set; } = null!; // Set right after construction
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types this input view model can support through type conversion. This list is defined when registering the
|
||||
/// view model.
|
||||
/// </summary>
|
||||
internal IReadOnlyCollection<Type>? CompatibleConversionTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Submits the input value and removes this view model.
|
||||
/// <para>This is called automatically when the user presses enter or clicks outside the view</para>
|
||||
/// </summary>
|
||||
public abstract void Submit();
|
||||
|
||||
/// <summary>
|
||||
/// Discards changes to the input value and removes this view model.
|
||||
/// <para>This is called automatically when the user presses escape</para>
|
||||
/// </summary>
|
||||
public abstract void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Called before the current value is submitted
|
||||
/// </summary>
|
||||
protected virtual void OnSubmit()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before the current value is discarded
|
||||
/// </summary>
|
||||
protected virtual void OnCancel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a layer brush registered through
|
||||
/// <see cref="IDataModelUIService.RegisterDataModelInput{T}" /> or
|
||||
/// <see cref="IDataModelUIService.RegisterDataModelDisplay{T}" />
|
||||
/// </summary>
|
||||
public class DataModelVisualizationRegistration
|
||||
{
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
|
||||
internal DataModelVisualizationRegistration(IDataModelUIService dataModelUIService,
|
||||
RegistrationType registrationType,
|
||||
Plugin plugin,
|
||||
Type supportedType,
|
||||
Type viewModelType)
|
||||
{
|
||||
_dataModelUIService = dataModelUIService;
|
||||
RegistrationType = registrationType;
|
||||
Plugin = plugin;
|
||||
SupportedType = supportedType;
|
||||
ViewModelType = viewModelType;
|
||||
|
||||
if (Plugin != Constants.CorePlugin)
|
||||
Plugin.Disabled += InstanceOnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of registration, either a display or an input
|
||||
/// </summary>
|
||||
public RegistrationType RegistrationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that registered the visualization
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type supported by the visualization
|
||||
/// </summary>
|
||||
public Type SupportedType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model type of the visualization
|
||||
/// </summary>
|
||||
public Type ViewModelType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only collection of types this visualization can convert to and from
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<Type>? CompatibleConversionTypes { get; internal set; }
|
||||
|
||||
internal void Unsubscribe()
|
||||
{
|
||||
if (Plugin != Constants.CorePlugin)
|
||||
Plugin.Disabled -= InstanceOnDisabled;
|
||||
}
|
||||
|
||||
private void InstanceOnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
if (RegistrationType == RegistrationType.Input)
|
||||
_dataModelUIService.RemoveDataModelInput(this);
|
||||
else if (RegistrationType == RegistrationType.Display)
|
||||
_dataModelUIService.RemoveDataModelDisplay(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type of data model visualization registration
|
||||
/// </summary>
|
||||
public enum RegistrationType
|
||||
{
|
||||
/// <summary>
|
||||
/// A visualization used for displaying values
|
||||
/// </summary>
|
||||
Display,
|
||||
|
||||
/// <summary>
|
||||
/// A visualization used for inputting values
|
||||
/// </summary>
|
||||
Input
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes an event data model property
|
||||
/// </summary>
|
||||
public class DataModelEventViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private Type? _displayValueType;
|
||||
|
||||
internal DataModelEventViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of event arguments this event triggers and that must be displayed as children
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
DisplayValueType = DataModelPath?.GetPropertyType();
|
||||
|
||||
if (configuration != null)
|
||||
{
|
||||
if (configuration.CreateEventChildren)
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
else if (Children.Any())
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
// Only update children if the parent is expanded
|
||||
if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Update(dataModelUIService, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns <see langword="null"/> for data model events
|
||||
/// </summary>
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? ToString()
|
||||
{
|
||||
return DisplayPath ?? Path;
|
||||
}
|
||||
|
||||
internal override int GetChildDepth()
|
||||
{
|
||||
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that wraps a regular <see cref="DataModelPropertiesViewModel" /> but contained in
|
||||
/// a <see cref="DataModelListViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelListPropertiesViewModel : DataModelPropertiesViewModel
|
||||
{
|
||||
private object? _displayValue;
|
||||
private int _index;
|
||||
private Type? _listType;
|
||||
|
||||
internal DataModelListPropertiesViewModel(Type listType, string? name) : base(null, null, null)
|
||||
{
|
||||
ListType = listType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the element within the list
|
||||
/// </summary>
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
set => this.RaiseAndSetIfChanged(ref _index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of elements contained in the list
|
||||
/// </summary>
|
||||
public Type? ListType
|
||||
{
|
||||
get => _listType;
|
||||
set => this.RaiseAndSetIfChanged(ref _listType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public new object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model that handles displaying the property
|
||||
/// </summary>
|
||||
public DataModelVisualizationViewModel? DisplayViewModel => Children.FirstOrDefault();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? DisplayPath => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
if (DisplayViewModel == null)
|
||||
return;
|
||||
|
||||
if (IsVisualizationExpanded && !DisplayViewModel.IsVisualizationExpanded)
|
||||
DisplayViewModel.IsVisualizationExpanded = IsVisualizationExpanded;
|
||||
DisplayViewModel.Update(dataModelUIService, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
return DisplayValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[List item {Index}] {DisplayPath ?? Path}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a single data model property contained in a
|
||||
/// <see cref="DataModelListViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelListPropertyViewModel : DataModelPropertyViewModel
|
||||
{
|
||||
private int _index;
|
||||
private Type? _listType;
|
||||
|
||||
internal DataModelListPropertyViewModel(Type listType, DataModelDisplayViewModel displayViewModel, string? name) : base(null, null, null)
|
||||
{
|
||||
ListType = listType;
|
||||
DisplayViewModel = displayViewModel;
|
||||
}
|
||||
|
||||
internal DataModelListPropertyViewModel(Type listType, string? name) : base(null, null, null)
|
||||
{
|
||||
ListType = listType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the element within the list
|
||||
/// </summary>
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of elements contained in the list
|
||||
/// </summary>
|
||||
public Type? ListType
|
||||
{
|
||||
get => _listType;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listType, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
return DisplayValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
// Display value gets updated by parent, don't do anything if it is null
|
||||
if (DisplayValue == null)
|
||||
return;
|
||||
|
||||
if (DisplayViewModel == null)
|
||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true);
|
||||
|
||||
ListType = DisplayValue.GetType();
|
||||
DisplayViewModel?.UpdateValue(DisplayValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[List item {Index}] {DisplayPath ?? Path} - {DisplayValue}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a list data model property
|
||||
/// </summary>
|
||||
public class DataModelListViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private string _countDisplay;
|
||||
private Type? _displayValueType;
|
||||
private IEnumerable? _list;
|
||||
private ObservableCollection<DataModelVisualizationViewModel> _listChildren;
|
||||
private int _listCount;
|
||||
|
||||
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
_countDisplay = "0 items";
|
||||
_listChildren = new ObservableCollection<DataModelVisualizationViewModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the list that is being visualized
|
||||
/// </summary>
|
||||
public IEnumerable? List
|
||||
{
|
||||
get => _list;
|
||||
private set => this.RaiseAndSetIfChanged(ref _list, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of elements in the list that is being visualized
|
||||
/// </summary>
|
||||
public int ListCount
|
||||
{
|
||||
get => _listCount;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listCount, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of elements this list contains and that must be displayed as children
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human readable display count
|
||||
/// </summary>
|
||||
public string CountDisplay
|
||||
{
|
||||
get => _countDisplay;
|
||||
set => this.RaiseAndSetIfChanged(ref _countDisplay, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of child view models that visualize the elements in the list
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelVisualizationViewModel> ListChildren
|
||||
{
|
||||
get => _listChildren;
|
||||
private set => this.RaiseAndSetIfChanged(ref _listChildren, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
List = GetCurrentValue() as IEnumerable;
|
||||
DisplayValueType = List?.GetType();
|
||||
if (List == null)
|
||||
return;
|
||||
|
||||
int index = 0;
|
||||
foreach (object item in List)
|
||||
{
|
||||
if (item == null)
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child;
|
||||
if (ListChildren.Count <= index)
|
||||
{
|
||||
child = CreateListChild(dataModelUIService, item.GetType(), DataModelPath?.GetPropertyDescription()?.ListItemName);
|
||||
if (child == null)
|
||||
continue;
|
||||
ListChildren.Add(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
child = ListChildren[index];
|
||||
}
|
||||
|
||||
if (child is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
||||
{
|
||||
dataModelListClassViewModel.DisplayValue = item;
|
||||
dataModelListClassViewModel.Index = index;
|
||||
}
|
||||
else if (child is DataModelListPropertyViewModel dataModelListPropertyViewModel)
|
||||
{
|
||||
dataModelListPropertyViewModel.DisplayValue = item;
|
||||
dataModelListPropertyViewModel.Index = index;
|
||||
}
|
||||
|
||||
child.Update(dataModelUIService, configuration);
|
||||
index++;
|
||||
}
|
||||
|
||||
ListCount = index;
|
||||
|
||||
while (ListChildren.Count > ListCount)
|
||||
ListChildren.RemoveAt(ListChildren.Count - 1);
|
||||
|
||||
CountDisplay = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)";
|
||||
}
|
||||
|
||||
private DataModelVisualizationViewModel? CreateListChild(IDataModelUIService dataModelUIService, Type listType, string? name)
|
||||
{
|
||||
// If a display VM was found, prefer to use that in any case
|
||||
DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription);
|
||||
if (typeViewModel != null)
|
||||
return new DataModelListPropertyViewModel(listType, typeViewModel, name);
|
||||
// For primitives, create a property view model, it may be null that is fine
|
||||
if (listType.IsPrimitive || listType.IsEnum || listType == typeof(string))
|
||||
return new DataModelListPropertyViewModel(listType, name);
|
||||
// For other value types create a child view model
|
||||
if (listType.IsClass || listType.IsStruct())
|
||||
return new DataModelListPropertiesViewModel(listType, name);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a class (POCO) data model property containing child properties
|
||||
/// </summary>
|
||||
public class DataModelPropertiesViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private object? _displayValue;
|
||||
private Type? _displayValueType;
|
||||
|
||||
internal DataModelPropertiesViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property that is being visualized
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
private set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
private set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
DisplayValueType = DataModelPath?.GetPropertyType();
|
||||
|
||||
// Only set a display value if ToString returns useful information and not just the type name
|
||||
object? currentValue = GetCurrentValue();
|
||||
if (currentValue != null && currentValue.ToString() != currentValue.GetType().ToString())
|
||||
DisplayValue = currentValue.ToString();
|
||||
else
|
||||
DisplayValue = null;
|
||||
|
||||
// Always populate properties
|
||||
PopulateProperties(dataModelUIService, configuration);
|
||||
|
||||
// Only update children if the parent is expanded
|
||||
if (Parent != null && !Parent.IsRootViewModel && !Parent.IsVisualizationExpanded)
|
||||
return;
|
||||
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Update(dataModelUIService, configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? GetCurrentValue()
|
||||
{
|
||||
if (Parent == null || Parent.IsRootViewModel || IsRootViewModel)
|
||||
return DataModel;
|
||||
return base.GetCurrentValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? ToString()
|
||||
{
|
||||
return DisplayPath ?? Path;
|
||||
}
|
||||
|
||||
internal override int GetChildDepth()
|
||||
{
|
||||
return PropertyDescription != null && !PropertyDescription.ResetsDepth ? Depth + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a view model that visualizes a single data model property contained in a
|
||||
/// <see cref="DataModelPropertiesViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelPropertyViewModel : DataModelVisualizationViewModel
|
||||
{
|
||||
private object? _displayValue;
|
||||
private Type? _displayValueType;
|
||||
private DataModelDisplayViewModel? _displayViewModel;
|
||||
|
||||
internal DataModelPropertyViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
: base(dataModel, parent, dataModelPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the property that is being visualized
|
||||
/// </summary>
|
||||
public object? DisplayValue
|
||||
{
|
||||
get => _displayValue;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property that is being visualized
|
||||
/// </summary>
|
||||
public Type? DisplayValueType
|
||||
{
|
||||
get => _displayValueType;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _displayValueType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model used to display the display value
|
||||
/// </summary>
|
||||
public DataModelDisplayViewModel? DisplayViewModel
|
||||
{
|
||||
get => _displayViewModel;
|
||||
internal set => this.RaiseAndSetIfChanged(ref _displayViewModel, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration)
|
||||
{
|
||||
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
||||
return;
|
||||
|
||||
if (DisplayViewModel == null)
|
||||
{
|
||||
Type? propertyType = DataModelPath?.GetPropertyType();
|
||||
if (propertyType != null)
|
||||
{
|
||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription, true);
|
||||
if (DisplayViewModel != null)
|
||||
DisplayViewModel.PropertyDescription = DataModelPath?.GetPropertyDescription();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayValue = GetCurrentValue();
|
||||
DisplayValueType = DisplayValue != null ? DisplayValue.GetType() : DataModelPath?.GetPropertyType();
|
||||
|
||||
DisplayViewModel?.UpdateValue(DisplayValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (DisplayValueType != null)
|
||||
return $"[{DisplayValueType.Name}] {DisplayPath ?? Path} - {DisplayValue}";
|
||||
return $"{DisplayPath ?? Path} - {DisplayValue}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configuration to use while updating a <see cref="DataModelVisualizationViewModel" />
|
||||
/// </summary>
|
||||
public class DataModelUpdateConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelUpdateConfiguration" /> class
|
||||
/// </summary>
|
||||
/// <param name="createEventChildren">A boolean indicating whether or not event children should be created</param>
|
||||
public DataModelUpdateConfiguration(bool createEventChildren)
|
||||
{
|
||||
CreateEventChildren = createEventChildren;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether or not event children should be created
|
||||
/// </summary>
|
||||
public bool CreateEventChildren { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,373 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DataModelVisualization.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a base class for a view model that visualizes a part of the data model
|
||||
/// </summary>
|
||||
public abstract class DataModelVisualizationViewModel : ReactiveObject, IDisposable
|
||||
{
|
||||
private const int MaxDepth = 4;
|
||||
private ObservableCollection<DataModelVisualizationViewModel> _children;
|
||||
private DataModel? _dataModel;
|
||||
private bool _isMatchingFilteredTypes;
|
||||
private bool _isVisualizationExpanded;
|
||||
private DataModelVisualizationViewModel? _parent;
|
||||
private DataModelPropertyAttribute? _propertyDescription;
|
||||
private bool _populatedStaticChildren;
|
||||
|
||||
internal DataModelVisualizationViewModel(DataModel? dataModel, DataModelVisualizationViewModel? parent, DataModelPath? dataModelPath)
|
||||
{
|
||||
_dataModel = dataModel;
|
||||
_children = new ObservableCollection<DataModelVisualizationViewModel>();
|
||||
_parent = parent;
|
||||
DataModelPath = dataModelPath;
|
||||
IsMatchingFilteredTypes = true;
|
||||
|
||||
if (parent == null)
|
||||
IsRootViewModel = true;
|
||||
else
|
||||
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel?.DataModelDescription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this view model is at the root of the data model
|
||||
/// </summary>
|
||||
public bool IsRootViewModel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model path to the property this view model is visualizing
|
||||
/// </summary>
|
||||
public DataModelPath? DataModelPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the path backing this model
|
||||
/// </summary>
|
||||
public string? Path => DataModelPath?.Path;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property depth of the view model
|
||||
/// </summary>
|
||||
public int Depth { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model backing this view model
|
||||
/// </summary>
|
||||
public DataModel? DataModel
|
||||
{
|
||||
get => _dataModel;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _dataModel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property description of the property this view model is visualizing
|
||||
/// </summary>
|
||||
public DataModelPropertyAttribute? PropertyDescription
|
||||
{
|
||||
get => _propertyDescription;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _propertyDescription, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this view model
|
||||
/// </summary>
|
||||
public DataModelVisualizationViewModel? Parent
|
||||
{
|
||||
get => _parent;
|
||||
protected set => this.RaiseAndSetIfChanged(ref _parent, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an observable collection containing the children of this view model
|
||||
/// </summary>
|
||||
public ObservableCollection<DataModelVisualizationViewModel> Children
|
||||
{
|
||||
get => _children;
|
||||
set => this.RaiseAndSetIfChanged(ref _children, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the property being visualized matches the types last provided to
|
||||
/// <see cref="ApplyTypeFilter" />
|
||||
/// </summary>
|
||||
public bool IsMatchingFilteredTypes
|
||||
{
|
||||
get => _isMatchingFilteredTypes;
|
||||
private set => this.RaiseAndSetIfChanged(ref _isMatchingFilteredTypes, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the visualization is expanded, exposing the <see cref="Children" />
|
||||
/// </summary>
|
||||
public bool IsVisualizationExpanded
|
||||
{
|
||||
get => _isVisualizationExpanded;
|
||||
set
|
||||
{
|
||||
if (!this.RaiseAndSetIfChanged(ref _isVisualizationExpanded, value)) return;
|
||||
RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-friendly representation of the <see cref="DataModelPath" />
|
||||
/// </summary>
|
||||
public virtual string? DisplayPath => DataModelPath != null
|
||||
? string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier))
|
||||
: null;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the datamodel and if in an parent, any children
|
||||
/// </summary>
|
||||
/// <param name="dataModelUIService">The data model UI service used during update</param>
|
||||
/// <param name="configuration">The configuration to apply while updating</param>
|
||||
public abstract void Update(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the property being visualized
|
||||
/// </summary>
|
||||
/// <returns>The current value of the property being visualized</returns>
|
||||
public virtual object? GetCurrentValue()
|
||||
{
|
||||
if (IsRootViewModel)
|
||||
return null;
|
||||
|
||||
return DataModelPath?.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided types match the type of the property being visualized and sets the result in
|
||||
/// <see cref="IsMatchingFilteredTypes" />
|
||||
/// </summary>
|
||||
/// <param name="looseMatch">Whether the type may be a loose match, meaning it can be cast or converted</param>
|
||||
/// <param name="filteredTypes">The types to filter</param>
|
||||
public void ApplyTypeFilter(bool looseMatch, params Type[]? filteredTypes)
|
||||
{
|
||||
if (filteredTypes != null)
|
||||
{
|
||||
if (filteredTypes.All(t => t == null))
|
||||
filteredTypes = null;
|
||||
else
|
||||
filteredTypes = filteredTypes.Where(t => t != null).ToArray();
|
||||
}
|
||||
|
||||
// If the VM has children, its own type is not relevant
|
||||
if (Children.Any())
|
||||
{
|
||||
foreach (DataModelVisualizationViewModel child in Children)
|
||||
child?.ApplyTypeFilter(looseMatch, filteredTypes);
|
||||
|
||||
IsMatchingFilteredTypes = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If null is passed, clear the type filter
|
||||
if (filteredTypes == null || filteredTypes.Length == 0)
|
||||
{
|
||||
IsMatchingFilteredTypes = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the type couldn't be retrieved either way, assume false
|
||||
Type? type = DataModelPath?.GetPropertyType();
|
||||
if (type == null)
|
||||
{
|
||||
IsMatchingFilteredTypes = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (looseMatch)
|
||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
|
||||
t == typeof(Enum) && type.IsEnum ||
|
||||
t == typeof(IEnumerable<>) && type.IsGenericEnumerable() ||
|
||||
type.IsGenericType && t == type.GetGenericTypeDefinition());
|
||||
else
|
||||
IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum);
|
||||
}
|
||||
|
||||
internal virtual int GetChildDepth()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal void PopulateProperties(IDataModelUIService dataModelUIService, DataModelUpdateConfiguration? dataModelUpdateConfiguration)
|
||||
{
|
||||
if (IsRootViewModel && DataModel == null)
|
||||
return;
|
||||
|
||||
Type? modelType = IsRootViewModel ? DataModel?.GetType() : DataModelPath?.GetPropertyType();
|
||||
if (modelType == null)
|
||||
throw new ArtemisSharedUIException("Failed to populate data model visualization properties, couldn't get a property type");
|
||||
|
||||
// Add missing static children only once, they're static after all
|
||||
if (!_populatedStaticChildren)
|
||||
{
|
||||
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(t => t.MetadataToken))
|
||||
{
|
||||
string childPath = AppendToPath(propertyInfo.Name);
|
||||
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
||||
continue;
|
||||
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
|
||||
continue;
|
||||
MethodInfo? getMethod = propertyInfo.GetGetMethod();
|
||||
if (getMethod == null || getMethod.GetParameters().Any())
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
_populatedStaticChildren = true;
|
||||
}
|
||||
|
||||
// Remove static children that should be hidden
|
||||
if (DataModel != null)
|
||||
{
|
||||
ReadOnlyCollection<PropertyInfo> hiddenProperties = DataModel.GetHiddenProperties();
|
||||
foreach (PropertyInfo hiddenProperty in hiddenProperties)
|
||||
{
|
||||
string childPath = AppendToPath(hiddenProperty.Name);
|
||||
DataModelVisualizationViewModel? toRemove = Children.FirstOrDefault(c => c.Path != null && c.Path == childPath);
|
||||
if (toRemove != null)
|
||||
Children.Remove(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing dynamic children
|
||||
object? value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath?.GetValue();
|
||||
if (value is DataModel dataModel)
|
||||
{
|
||||
foreach (string key in dataModel.DynamicChildren.Keys.ToList())
|
||||
{
|
||||
string childPath = AppendToPath(key);
|
||||
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
||||
continue;
|
||||
|
||||
DataModelVisualizationViewModel? child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||
if (child != null)
|
||||
Children.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dynamic children that have been removed from the data model
|
||||
List<DataModelVisualizationViewModel> toRemoveDynamic = Children.Where(c => c.DataModelPath != null && !c.DataModelPath.IsValid).ToList();
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in toRemoveDynamic)
|
||||
Children.Remove(dataModelVisualizationViewModel);
|
||||
}
|
||||
|
||||
private DataModelVisualizationViewModel? CreateChild(IDataModelUIService dataModelUIService, string path, int depth)
|
||||
{
|
||||
if (DataModel == null)
|
||||
throw new ArtemisSharedUIException("Cannot create a data model visualization child VM for a parent without a data model");
|
||||
if (depth > MaxDepth)
|
||||
return null;
|
||||
|
||||
DataModelPath dataModelPath = new(DataModel, path);
|
||||
if (!dataModelPath.IsValid)
|
||||
return null;
|
||||
|
||||
PropertyInfo? propertyInfo = dataModelPath.GetPropertyInfo();
|
||||
Type? propertyType = dataModelPath.GetPropertyType();
|
||||
|
||||
// Skip properties decorated with DataModelIgnore
|
||||
if (propertyInfo != null && Attribute.IsDefined(propertyInfo, typeof(DataModelIgnoreAttribute)))
|
||||
return null;
|
||||
// Skip properties that are in the ignored properties list of the respective profile module/data model expansion
|
||||
if (DataModel.GetHiddenProperties().Any(p => p.Equals(propertyInfo)))
|
||||
return null;
|
||||
|
||||
if (propertyType == null)
|
||||
return null;
|
||||
|
||||
// If a display VM was found, prefer to use that in any case
|
||||
DataModelDisplayViewModel? typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription);
|
||||
if (typeViewModel != null)
|
||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth};
|
||||
// For primitives, create a property view model, it may be null that is fine
|
||||
if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string) || propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
if (propertyType.IsGenericEnumerable())
|
||||
return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
if (propertyType == typeof(DataModelEvent) || propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(DataModelEvent<>))
|
||||
return new DataModelEventViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
// For other value types create a child view model
|
||||
if (propertyType.IsClass || propertyType.IsStruct())
|
||||
return new DataModelPropertiesViewModel(DataModel, this, dataModelPath) {Depth = depth};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string AppendToPath(string toAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
return toAppend;
|
||||
|
||||
StringBuilder builder = new();
|
||||
builder.Append(Path);
|
||||
builder.Append(".");
|
||||
builder.Append(toAppend);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void RequestUpdate()
|
||||
{
|
||||
Parent?.RequestUpdate();
|
||||
OnUpdateRequested();
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an update to the property this view model visualizes is requested
|
||||
/// </summary>
|
||||
public event EventHandler? UpdateRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="UpdateRequested" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnUpdateRequested()
|
||||
{
|
||||
UpdateRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
DataModelPath?.Dispose();
|
||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
||||
dataModelVisualizationViewModel.Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView">
|
||||
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||
<!-- Prefix -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding PropertyDescription.Prefix}"
|
||||
IsVisible="{Binding PropertyDescription.Prefix, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
TextAlignment="Right"
|
||||
Margin="0 0 5 0" />
|
||||
|
||||
<!-- Value -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding DisplayValue, Mode=OneWay}"
|
||||
HorizontalAlignment="Right"
|
||||
IsVisible="{Binding ShowToString}"/>
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="null"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource MaterialDesignCheckBoxDisabled}"
|
||||
IsVisible="{Binding ShowNull}"/>
|
||||
|
||||
<!-- Affix -->
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding PropertyDescription.Affix}"
|
||||
IsVisible="{Binding PropertyDescription.Affix, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Margin="5 0 0 0" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
|
||||
{
|
||||
public partial class DefaultDataModelDisplayView : UserControl
|
||||
{
|
||||
public DefaultDataModelDisplayView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<UserControl x:Class="Artemis.UI.Shared.DefaultTypes.DataModel.Display.DefaultDataModelDisplayView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Artemis.UI.Shared.DefaultTypes.DataModel.Display"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance local:DefaultDataModelDisplayViewModel}">
|
||||
<UserControl.Resources>
|
||||
<shared:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Prefix -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding PropertyDescription.Prefix}"
|
||||
Visibility="{Binding PropertyDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
TextAlignment="Right"
|
||||
Margin="0 0 5 0" />
|
||||
|
||||
<!-- Value -->
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding DisplayValue, Mode=OneWay}"
|
||||
HorizontalAlignment="Right"
|
||||
Visibility="{Binding ShowToString, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="null"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource MaterialDesignCheckBoxDisabled}"
|
||||
Visibility="{Binding ShowNull, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||
|
||||
<!-- Affix -->
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding PropertyDescription.Affix}"
|
||||
Visibility="{Binding PropertyDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}"
|
||||
Margin="5 0 0 0" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Shared.DefaultTypes.DataModel.Display
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the default data model display view model that is used when no display viewmodel specific for the type
|
||||
/// is registered
|
||||
/// </summary>
|
||||
internal class DefaultDataModelDisplayViewModel : DataModelDisplayViewModel<object>
|
||||
{
|
||||
private bool _showNull;
|
||||
private bool _showToString;
|
||||
|
||||
internal DefaultDataModelDisplayViewModel()
|
||||
{
|
||||
ShowNull = true;
|
||||
}
|
||||
|
||||
public bool ShowToString
|
||||
{
|
||||
get => _showToString;
|
||||
private set => this.RaiseAndSetIfChanged(ref _showToString, value);
|
||||
}
|
||||
|
||||
public bool ShowNull
|
||||
{
|
||||
get => _showNull;
|
||||
set => this.RaiseAndSetIfChanged(ref _showNull, value);
|
||||
}
|
||||
|
||||
protected override void OnDisplayValueUpdated()
|
||||
{
|
||||
if (DisplayValue is Enum enumDisplayValue)
|
||||
DisplayValue = EnumUtilities.HumanizeValue(enumDisplayValue);
|
||||
|
||||
ShowToString = DisplayValue != null;
|
||||
ShowNull = DisplayValue == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.UI.Shared.Exceptions
|
||||
namespace Artemis.UI.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents errors that occur within the Artemis Shared UI library
|
||||
|
||||
251
src/Avalonia/Artemis.UI.Shared/Services/DataModelUIService.cs
Normal file
251
src/Avalonia/Artemis.UI.Shared/Services/DataModelUIService.cs
Normal file
@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Artemis.UI.Shared.DefaultTypes.DataModel.Display;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
internal class DataModelUIService : IDataModelUIService
|
||||
{
|
||||
private readonly IDataModelService _dataModelService;
|
||||
private readonly IKernel _kernel;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelDisplays;
|
||||
private readonly List<DataModelVisualizationRegistration> _registeredDataModelEditors;
|
||||
|
||||
public DataModelUIService(IDataModelService dataModelService, IKernel kernel)
|
||||
{
|
||||
_dataModelService = dataModelService;
|
||||
_kernel = kernel;
|
||||
_registeredDataModelEditors = new List<DataModelVisualizationRegistration>();
|
||||
_registeredDataModelDisplays = new List<DataModelVisualizationRegistration>();
|
||||
|
||||
RegisteredDataModelEditors = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelEditors);
|
||||
RegisteredDataModelDisplays = new ReadOnlyCollection<DataModelVisualizationRegistration>(_registeredDataModelDisplays);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
|
||||
public IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
|
||||
|
||||
public DataModelPropertiesViewModel GetMainDataModelVisualization()
|
||||
{
|
||||
DataModelPropertiesViewModel viewModel = new(null, null, null);
|
||||
foreach (DataModel dataModelExpansion in _dataModelService.GetDataModels().Where(d => d.IsExpansion).OrderBy(d => d.DataModelDescription.Name))
|
||||
viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, new DataModelPath(dataModelExpansion)));
|
||||
|
||||
// Update to populate children
|
||||
viewModel.Update(this, null);
|
||||
viewModel.UpdateRequested += (sender, args) => viewModel.Update(this, null);
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public void UpdateModules(DataModelPropertiesViewModel mainDataModelVisualization)
|
||||
{
|
||||
List<DataModelVisualizationViewModel> disabledChildren = mainDataModelVisualization.Children
|
||||
.Where(d => d.DataModel != null && !d.DataModel.Module.IsEnabled)
|
||||
.ToList();
|
||||
foreach (DataModelVisualizationViewModel child in disabledChildren)
|
||||
mainDataModelVisualization.Children.Remove(child);
|
||||
|
||||
foreach (DataModel dataModelExpansion in _dataModelService.GetDataModels().OrderBy(d => d.DataModelDescription.Name))
|
||||
{
|
||||
if (mainDataModelVisualization.Children.All(c => c.DataModel != dataModelExpansion))
|
||||
{
|
||||
mainDataModelVisualization.Children.Add(
|
||||
new DataModelPropertiesViewModel(dataModelExpansion, mainDataModelVisualization, new DataModelPath(dataModelExpansion))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mainDataModelVisualization.Update(this, null);
|
||||
}
|
||||
|
||||
public DataModelPropertiesViewModel? GetPluginDataModelVisualization(List<Module> modules, bool includeMainDataModel)
|
||||
{
|
||||
DataModelPropertiesViewModel root;
|
||||
// This will contain any modules that are always available
|
||||
if (includeMainDataModel)
|
||||
root = GetMainDataModelVisualization();
|
||||
else
|
||||
{
|
||||
root = new DataModelPropertiesViewModel(null, null, null);
|
||||
root.UpdateRequested += (sender, args) => root.Update(this, null);
|
||||
}
|
||||
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
DataModel? dataModel = _dataModelService.GetPluginDataModel(module);
|
||||
if (dataModel == null)
|
||||
continue;
|
||||
|
||||
root.Children.Add(new DataModelPropertiesViewModel(dataModel, root, new DataModelPath(dataModel)));
|
||||
}
|
||||
|
||||
if (!root.Children.Any())
|
||||
return null;
|
||||
|
||||
// Update to populate children
|
||||
root.Update(this, null);
|
||||
return root;
|
||||
}
|
||||
|
||||
public DataModelVisualizationRegistration RegisterDataModelInput<T>(Plugin plugin, IReadOnlyCollection<Type>? compatibleConversionTypes = null) where T : DataModelInputViewModel
|
||||
{
|
||||
compatibleConversionTypes ??= new List<Type>();
|
||||
Type viewModelType = typeof(T);
|
||||
lock (_registeredDataModelEditors)
|
||||
{
|
||||
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
|
||||
DataModelVisualizationRegistration? existing = _registeredDataModelEditors.FirstOrDefault(r => r.SupportedType == supportedType);
|
||||
if (existing != null)
|
||||
{
|
||||
if (existing.Plugin != plugin)
|
||||
throw new ArtemisSharedUIException($"Cannot register data model input for type {supportedType.Name} because an editor was already" +
|
||||
$" registered by {existing.Plugin}");
|
||||
return existing;
|
||||
}
|
||||
|
||||
_kernel.Bind(viewModelType).ToSelf();
|
||||
|
||||
// Create the registration
|
||||
DataModelVisualizationRegistration registration = new(this, RegistrationType.Input, plugin, supportedType, viewModelType)
|
||||
{
|
||||
// Apply the compatible conversion types to the registration
|
||||
CompatibleConversionTypes = compatibleConversionTypes
|
||||
};
|
||||
|
||||
_registeredDataModelEditors.Add(registration);
|
||||
return registration;
|
||||
}
|
||||
}
|
||||
|
||||
public DataModelVisualizationRegistration RegisterDataModelDisplay<T>(Plugin plugin) where T : DataModelDisplayViewModel
|
||||
{
|
||||
Type viewModelType = typeof(T);
|
||||
lock (_registeredDataModelDisplays)
|
||||
{
|
||||
Type supportedType = viewModelType.BaseType!.GetGenericArguments()[0];
|
||||
DataModelVisualizationRegistration? existing = _registeredDataModelDisplays.FirstOrDefault(r => r.SupportedType == supportedType);
|
||||
if (existing != null)
|
||||
{
|
||||
if (existing.Plugin != plugin)
|
||||
throw new ArtemisSharedUIException($"Cannot register data model display for type {supportedType.Name} because an editor was already" +
|
||||
$" registered by {existing.Plugin}");
|
||||
return existing;
|
||||
}
|
||||
|
||||
_kernel.Bind(viewModelType).ToSelf();
|
||||
DataModelVisualizationRegistration registration = new(this, RegistrationType.Display, plugin, supportedType, viewModelType);
|
||||
_registeredDataModelDisplays.Add(registration);
|
||||
return registration;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveDataModelInput(DataModelVisualizationRegistration registration)
|
||||
{
|
||||
lock (_registeredDataModelEditors)
|
||||
{
|
||||
if (_registeredDataModelEditors.Contains(registration))
|
||||
{
|
||||
registration.Unsubscribe();
|
||||
_registeredDataModelEditors.Remove(registration);
|
||||
|
||||
_kernel.Unbind(registration.ViewModelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveDataModelDisplay(DataModelVisualizationRegistration registration)
|
||||
{
|
||||
lock (_registeredDataModelDisplays)
|
||||
{
|
||||
if (_registeredDataModelDisplays.Contains(registration))
|
||||
{
|
||||
registration.Unsubscribe();
|
||||
_registeredDataModelDisplays.Remove(registration);
|
||||
|
||||
_kernel.Unbind(registration.ViewModelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DataModelDisplayViewModel? GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute? description, bool fallBackToDefault)
|
||||
{
|
||||
lock (_registeredDataModelDisplays)
|
||||
{
|
||||
DataModelDisplayViewModel? result;
|
||||
|
||||
DataModelVisualizationRegistration? match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||
if (match != null)
|
||||
{
|
||||
// If this ever happens something is likely wrong with the plugin unload detection
|
||||
if (match.Plugin.Kernel == null)
|
||||
throw new ArtemisSharedUIException("Cannot GetDataModelDisplayViewModel for a registration by an uninitialized plugin");
|
||||
|
||||
result = (DataModelDisplayViewModel) match.Plugin.Kernel.Get(match.ViewModelType);
|
||||
}
|
||||
else if (!fallBackToDefault)
|
||||
result = null;
|
||||
else
|
||||
result = _kernel.Get<DefaultDataModelDisplayViewModel>();
|
||||
|
||||
if (result != null)
|
||||
result.PropertyDescription = description;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback)
|
||||
{
|
||||
lock (_registeredDataModelEditors)
|
||||
{
|
||||
// Prefer a VM that natively supports the type
|
||||
DataModelVisualizationRegistration? match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||
// Fall back on a VM that supports the type through conversion
|
||||
if (match == null)
|
||||
match = _registeredDataModelEditors.FirstOrDefault(d => d.CompatibleConversionTypes != null && d.CompatibleConversionTypes.Contains(propertyType));
|
||||
// Lastly try getting an enum VM if the provided type is an enum
|
||||
if (match == null && propertyType.IsEnum)
|
||||
match = _registeredDataModelEditors.FirstOrDefault(d => d.SupportedType == typeof(Enum));
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
DataModelInputViewModel viewModel = InstantiateDataModelInputViewModel(match, description, initialValue);
|
||||
viewModel.UpdateCallback = updateCallback;
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
|
||||
{
|
||||
// This assumes the type can be converted, that has been checked when the VM was created
|
||||
if (initialValue != null && initialValue.GetType() != registration.SupportedType)
|
||||
initialValue = Convert.ChangeType(initialValue, registration.SupportedType);
|
||||
|
||||
IParameter[] parameters =
|
||||
{
|
||||
new ConstructorArgument("targetDescription", description),
|
||||
new ConstructorArgument("initialValue", initialValue)
|
||||
};
|
||||
|
||||
// If this ever happens something is likely wrong with the plugin unload detection
|
||||
if (registration.Plugin.Kernel == null)
|
||||
throw new ArtemisSharedUIException("Cannot InstantiateDataModelInputViewModel for a registration by an uninitialized plugin");
|
||||
|
||||
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.Plugin.Kernel.Get(registration.ViewModelType, parameters);
|
||||
viewModel.CompatibleConversionTypes = registration.CompatibleConversionTypes;
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.UI.Shared.DataModelVisualization;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
|
||||
namespace Artemis.UI.Shared.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A service for UI related data model tasks
|
||||
/// </summary>
|
||||
public interface IDataModelUIService : IArtemisSharedUIService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only list of all registered data model editors
|
||||
/// </summary>
|
||||
IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelEditors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of all registered data model displays
|
||||
/// </summary>
|
||||
IReadOnlyCollection<DataModelVisualizationRegistration> RegisteredDataModelDisplays { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a data model visualization view model for the main data model
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A data model visualization view model containing all data model expansions and modules that expand the main
|
||||
/// data model
|
||||
/// </returns>
|
||||
DataModelPropertiesViewModel GetMainDataModelVisualization();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a data model visualization view model for the data model of the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="modules">The modules to create the data model visualization view model for</param>
|
||||
/// <param name="includeMainDataModel">
|
||||
/// Whether or not also to include the main data model (and therefore any modules marked
|
||||
/// as <see cref="Module.IsAlwaysAvailable" />)
|
||||
/// </param>
|
||||
/// <returns>A data model visualization view model containing the data model of the provided feature</returns>
|
||||
DataModelPropertiesViewModel? GetPluginDataModelVisualization(List<Module> modules, bool includeMainDataModel);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the children of the provided main data model visualization, removing disabled children and adding newly
|
||||
/// enabled children
|
||||
/// </summary>
|
||||
void UpdateModules(DataModelPropertiesViewModel mainDataModelVisualization);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new data model editor
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the editor</typeparam>
|
||||
/// <param name="plugin">The plugin this editor belongs to</param>
|
||||
/// <param name="compatibleConversionTypes">A collection of extra types this editor supports</param>
|
||||
/// <returns>A registration that can be used to remove the editor</returns>
|
||||
DataModelVisualizationRegistration RegisterDataModelInput<T>(Plugin plugin, IReadOnlyCollection<Type> compatibleConversionTypes) where T : DataModelInputViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new data model display
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the display</typeparam>
|
||||
/// <param name="plugin">The plugin this display belongs to</param>
|
||||
/// <returns>A registration that can be used to remove the display</returns>
|
||||
DataModelVisualizationRegistration RegisterDataModelDisplay<T>(Plugin plugin) where T : DataModelDisplayViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a data model editor
|
||||
/// </summary>
|
||||
/// <param name="registration">
|
||||
/// The registration of the editor as returned by <see cref="RegisterDataModelInput{T}" />
|
||||
/// </param>
|
||||
void RemoveDataModelInput(DataModelVisualizationRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a data model display
|
||||
/// </summary>
|
||||
/// <param name="registration">
|
||||
/// The registration of the display as returned by <see cref="RegisterDataModelDisplay{T}" />
|
||||
/// </param>
|
||||
void RemoveDataModelDisplay(DataModelVisualizationRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the most appropriate display view model for the provided <paramref name="propertyType" /> that can display
|
||||
/// a value
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The type of data model property to find a display view model for</param>
|
||||
/// <param name="description">The description of the data model property</param>
|
||||
/// <param name="fallBackToDefault">
|
||||
/// If <see langword="true"></see>, a simple .ToString() display view model will be
|
||||
/// returned if nothing else is found
|
||||
/// </param>
|
||||
/// <returns>The most appropriate display view model for the provided <paramref name="propertyType"></paramref></returns>
|
||||
DataModelDisplayViewModel? GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute? description, bool fallBackToDefault = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the most appropriate input view model for the provided <paramref name="propertyType" /> that allows
|
||||
/// inputting a value
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The type of data model property to find a display view model for</param>
|
||||
/// <param name="description">The description of the data model property</param>
|
||||
/// <param name="initialValue">The initial value to show in the input</param>
|
||||
/// <param name="updateCallback">A function to call whenever the input was updated (submitted or not)</param>
|
||||
/// <returns>The most appropriate input view model for the provided <paramref name="propertyType" /></returns>
|
||||
DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action<object?, bool> updateCallback);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared.Exceptions;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Services.Interfaces;
|
||||
using Avalonia;
|
||||
|
||||
@ -46,4 +46,9 @@
|
||||
<Resource Include="Assets\Images\Logo\bow.ico" />
|
||||
<Resource Include="Assets\Images\Logo\bow.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Screens\Debugger\Tabs\Settings\DebugSettingsView.axaml.cs">
|
||||
<DependentUpon>DebugSettingsView.axaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -3,6 +3,8 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:reactiveUi="http://reactiveui.net"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||
x:Class="Artemis.UI.Screens.Debugger.DebugView"
|
||||
Title="Artemis | Debugger"
|
||||
@ -10,9 +12,59 @@
|
||||
Height="800"
|
||||
ExtendClientAreaToDecorationsHint="True">
|
||||
|
||||
<controls:NavigationView x:Name="Navigation">
|
||||
|
||||
</controls:NavigationView>
|
||||
|
||||
<Window.Styles>
|
||||
<Style Selector="StackPanel.sidebar-stackpanel avalonia|MaterialIcon">
|
||||
<Setter Property="Margin" Value="-7 0 0 0" />
|
||||
</Style>
|
||||
<Style Selector="StackPanel.sidebar-stackpanel TextBlock">
|
||||
<Setter Property="Margin" Value="15 0 0 0" />
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
<controls:NavigationView x:Name="Navigation"
|
||||
SelectedItem="{Binding SelectedItem}"
|
||||
IsPaneToggleButtonVisible="False"
|
||||
PaneDisplayMode="Left"
|
||||
Margin="0 30 0 0">
|
||||
<controls:NavigationView.MenuItems>
|
||||
<controls:NavigationViewItem Tag="Rendering">
|
||||
<controls:NavigationViewItem.Content>
|
||||
<StackPanel Orientation="Horizontal" Classes="sidebar-stackpanel">
|
||||
<avalonia:MaterialIcon Kind="Paint" />
|
||||
<TextBlock>Rendering</TextBlock>
|
||||
</StackPanel>
|
||||
</controls:NavigationViewItem.Content>
|
||||
</controls:NavigationViewItem>
|
||||
<controls:NavigationViewItem Tag="DataModel">
|
||||
<controls:NavigationViewItem.Content>
|
||||
<StackPanel Orientation="Horizontal" Classes="sidebar-stackpanel">
|
||||
<avalonia:MaterialIcon Kind="FormatListBulletedType" />
|
||||
<TextBlock>Data Model</TextBlock>
|
||||
</StackPanel>
|
||||
</controls:NavigationViewItem.Content>
|
||||
</controls:NavigationViewItem>
|
||||
<controls:NavigationViewItem Tag="Performance">
|
||||
<controls:NavigationViewItem.Content>
|
||||
<StackPanel Orientation="Horizontal" Classes="sidebar-stackpanel">
|
||||
<avalonia:MaterialIcon Kind="Gauge" />
|
||||
<TextBlock>Performance</TextBlock>
|
||||
</StackPanel>
|
||||
</controls:NavigationViewItem.Content>
|
||||
</controls:NavigationViewItem>
|
||||
<controls:NavigationViewItem Tag="Logging">
|
||||
<controls:NavigationViewItem.Content>
|
||||
<StackPanel Orientation="Horizontal" Classes="sidebar-stackpanel">
|
||||
<avalonia:MaterialIcon Kind="Text" />
|
||||
<TextBlock>Logging</TextBlock>
|
||||
</StackPanel>
|
||||
</controls:NavigationViewItem.Content>
|
||||
</controls:NavigationViewItem>
|
||||
</controls:NavigationView.MenuItems>
|
||||
|
||||
<reactiveUi:RoutedViewHost Router="{Binding Router}" Padding="12">
|
||||
<reactiveUi:RoutedViewHost.PageTransition>
|
||||
<CrossFade Duration="0.1" />
|
||||
</reactiveUi:RoutedViewHost.PageTransition>
|
||||
</reactiveUi:RoutedViewHost>
|
||||
|
||||
</controls:NavigationView>
|
||||
</Window>
|
||||
@ -4,6 +4,7 @@ using Artemis.UI.Screens.Debugger.Tabs.DataModel;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Logs;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Performance;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Render;
|
||||
using Artemis.UI.Screens.Debugger.Tabs.Settings;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
@ -50,20 +51,23 @@ namespace Artemis.UI.Screens.Debugger
|
||||
{
|
||||
// Kind of a lame way to do this but it's so static idc
|
||||
ConstructorArgument hostScreen = new("hostScreen", this);
|
||||
switch ((string) item.Content)
|
||||
switch (item.Tag as string)
|
||||
{
|
||||
case "Rendering":
|
||||
Router.Navigate.Execute(_kernel.Get<RenderDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Logs":
|
||||
Router.Navigate.Execute(_kernel.Get<LogsDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Data Model":
|
||||
case "DataModel":
|
||||
Router.Navigate.Execute(_kernel.Get<DataModelDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Performance":
|
||||
Router.Navigate.Execute(_kernel.Get<PerformanceDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Logging":
|
||||
Router.Navigate.Execute(_kernel.Get<LogsDebugViewModel>(hostScreen));
|
||||
break;
|
||||
case "Settings":
|
||||
Router.Navigate.Execute(_kernel.Get<DebugSettingsViewModel>(hostScreen));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,109 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:dataModel="clr-namespace:Artemis.UI.Shared.DataModelVisualization.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Debugger.Tabs.DataModel.DataModelDebugView">
|
||||
<TextBlock>Data Model</TextBlock>
|
||||
<UserControl.Resources>
|
||||
<converters:TypeToStringConverter x:Key="TypeToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<StackPanel>
|
||||
<TreeView Items="{Binding MainDataModel.Children}">
|
||||
<TreeView.Styles>
|
||||
<Style Selector="TreeViewItem">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
|
||||
</Style>
|
||||
</TreeView.Styles>
|
||||
<TreeView.DataTemplates>
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertiesViewModel}" ItemsSource="{Binding Children}">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock FontWeight="Bold">[</TextBlock>
|
||||
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Bold">]</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding DisplayValue}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListViewModel}" ItemsSource="{Binding ListChildren}">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock FontWeight="Bold">[</TextBlock>
|
||||
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Bold">]</TextBlock>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelPropertyViewModel}">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||||
|
||||
<!-- Value description -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock FontWeight="Bold">[</TextBlock>
|
||||
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Bold">]</TextBlock>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||
|
||||
<!-- Value display -->
|
||||
<ContentControl Grid.Column="2" Content="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
||||
</Grid>
|
||||
</TreeDataTemplate>
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListPropertyViewModel}">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||||
|
||||
<!-- Value description -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock FontWeight="Bold">[</TextBlock>
|
||||
<TextBlock FontWeight="Bold" Text="{Binding ListType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Bold">]</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock>List item [</TextBlock>
|
||||
<TextBlock Text="{Binding Index, Mode=OneWay}" />
|
||||
<TextBlock>]</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Value display -->
|
||||
<ContentControl Grid.Column="2" Content="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
||||
</Grid>
|
||||
</TreeDataTemplate>
|
||||
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelEventViewModel}" ItemsSource="{Binding Children}">
|
||||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0 0 5 0">
|
||||
<TextBlock FontWeight="Bold">[</TextBlock>
|
||||
<TextBlock FontWeight="Bold" Text="{Binding DisplayValueType, Converter={StaticResource TypeToStringConverter}, Mode=OneWay}" />
|
||||
<TextBlock FontWeight="Bold">]</TextBlock>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip.Tip="{Binding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding CountDisplay, Mode=OneWay}"
|
||||
FontFamily="Consolas"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</TreeDataTemplate>
|
||||
|
||||
<TreeDataTemplate DataType="{x:Type dataModel:DataModelListPropertiesViewModel}" ItemsSource="{Binding DisplayViewModel.Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock>List item [</TextBlock>
|
||||
<TextBlock Text="{Binding Index, Mode=OneWay}" />
|
||||
<TextBlock>]</TextBlock>
|
||||
</StackPanel>
|
||||
</TreeDataTemplate>
|
||||
</TreeView.DataTemplates>
|
||||
</TreeView>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -1,16 +1,147 @@
|
||||
using Artemis.UI.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.DataModel
|
||||
{
|
||||
public class DataModelDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
|
||||
{
|
||||
public DataModelDebugViewModel(IScreen hostScreen)
|
||||
private readonly IDataModelUIService _dataModelUIService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly Timer _updateTimer;
|
||||
|
||||
private bool _isModuleFilterEnabled;
|
||||
private DataModelPropertiesViewModel? _mainDataModel;
|
||||
private string? _propertySearch;
|
||||
private Module? _selectedModule;
|
||||
private bool _slowUpdates;
|
||||
|
||||
public DataModelDebugViewModel(IScreen hostScreen, IDataModelUIService dataModelUIService, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_dataModelUIService = dataModelUIService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_updateTimer = new Timer(25);
|
||||
_updateTimer.Elapsed += UpdateTimerOnElapsed;
|
||||
|
||||
HostScreen = hostScreen;
|
||||
Modules = new ObservableCollection<Module>();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
Observable.FromEventPattern<PluginFeatureEventArgs>(x => pluginManagementService.PluginFeatureEnabled += x, x => pluginManagementService.PluginFeatureEnabled -= x)
|
||||
.Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
|
||||
.DisposeWith(disposables);
|
||||
Observable.FromEventPattern<PluginFeatureEventArgs>(x => pluginManagementService.PluginFeatureDisabled += x, x => pluginManagementService.PluginFeatureDisabled -= x)
|
||||
.Subscribe(d => PluginFeatureToggled(d.EventArgs.PluginFeature))
|
||||
.DisposeWith(disposables);
|
||||
|
||||
GetDataModel();
|
||||
_updateTimer.Start();
|
||||
Disposable.Create(() => _updateTimer.Stop()).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
public string UrlPathSegment => "data-model";
|
||||
public IScreen HostScreen { get; }
|
||||
|
||||
public DataModelPropertiesViewModel? MainDataModel
|
||||
{
|
||||
get => _mainDataModel;
|
||||
set => this.RaiseAndSetIfChanged(ref _mainDataModel, value);
|
||||
}
|
||||
|
||||
public string? PropertySearch
|
||||
{
|
||||
get => _propertySearch;
|
||||
set => this.RaiseAndSetIfChanged(ref _propertySearch, value);
|
||||
}
|
||||
|
||||
public bool SlowUpdates
|
||||
{
|
||||
get => _slowUpdates;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _slowUpdates, value);
|
||||
_updateTimer.Interval = _slowUpdates ? 500 : 25;
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<Module> Modules { get; }
|
||||
|
||||
public Module? SelectedModule
|
||||
{
|
||||
get => _selectedModule;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _selectedModule, value);
|
||||
GetDataModel();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsModuleFilterEnabled
|
||||
{
|
||||
get => _isModuleFilterEnabled;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _isModuleFilterEnabled, value);
|
||||
|
||||
if (!IsModuleFilterEnabled)
|
||||
SelectedModule = null;
|
||||
else
|
||||
GetDataModel();
|
||||
}
|
||||
}
|
||||
private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (MainDataModel == null)
|
||||
return;
|
||||
|
||||
lock (MainDataModel)
|
||||
{
|
||||
MainDataModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(true));
|
||||
}
|
||||
}
|
||||
|
||||
private void PluginFeatureToggled(PluginFeature pluginFeature)
|
||||
{
|
||||
if (pluginFeature is Module)
|
||||
PopulateModules();
|
||||
}
|
||||
|
||||
private void GetDataModel()
|
||||
{
|
||||
MainDataModel = SelectedModule != null
|
||||
? _dataModelUIService.GetPluginDataModelVisualization(new List<Module>() { SelectedModule }, false)
|
||||
: _dataModelUIService.GetMainDataModelVisualization();
|
||||
}
|
||||
|
||||
private void PopulateModules()
|
||||
{
|
||||
Modules.Clear();
|
||||
Modules.AddRange(_pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).OrderBy(m => m.Info.Name));
|
||||
|
||||
if (MainDataModel == null)
|
||||
return;
|
||||
|
||||
if (SelectedModule == null)
|
||||
{
|
||||
if (MainDataModel != null)
|
||||
_dataModelUIService.UpdateModules(MainDataModel);
|
||||
}
|
||||
else if (!SelectedModule.IsEnabled)
|
||||
SelectedModule = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,13 +4,26 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Debugger.Tabs.Render.RenderDebugView">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock>Render</TextBlock>
|
||||
|
||||
<Image Source="{Binding CurrentFrame}" Grid.Row="1"></Image>
|
||||
</Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Classes="h3">Render</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
On this page you can view what Artemis renders to devices in real time. Artemis will overlay this image on your devices, taking the average of each pixel covering a LED, resulting the image appearing on your devices.
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Classes="subtitle" Margin="0 10">
|
||||
Please note that having this window open can have a performance impact on your system.
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<TextBlock Text="FPS: " />
|
||||
<TextBlock FontWeight="Bold" Text="{Binding CurrentFps}" />
|
||||
<TextBlock Text=" at " />
|
||||
<TextBlock Text="{Binding RenderWidth}" />
|
||||
<TextBlock Text="x" />
|
||||
<TextBlock Text="{Binding RenderHeight}" />
|
||||
<TextBlock Text=" - Renderer: "></TextBlock>
|
||||
<TextBlock Text="{Binding Renderer}"></TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Image Source="{Binding CurrentFrame}" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -1,9 +1,15 @@
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
|
||||
@ -12,11 +18,9 @@ namespace Artemis.UI.Screens.Debugger.Tabs.Render
|
||||
public class RenderDebugViewModel : ActivatableViewModelBase, IRoutableViewModel
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly Timer _fpsTimer;
|
||||
private double _currentFps;
|
||||
|
||||
private SKImage? _currentFrame;
|
||||
private int _frames;
|
||||
private Bitmap? _currentFrame;
|
||||
private string? _frameTargetPath;
|
||||
private string _renderer;
|
||||
private int _renderHeight;
|
||||
@ -25,10 +29,7 @@ namespace Artemis.UI.Screens.Debugger.Tabs.Render
|
||||
public RenderDebugViewModel(DebugViewModel hostScreen, ICoreService coreService)
|
||||
{
|
||||
HostScreen = hostScreen;
|
||||
|
||||
_coreService = coreService;
|
||||
_fpsTimer = new Timer(1000);
|
||||
_fpsTimer.Start();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@ -37,7 +38,7 @@ namespace Artemis.UI.Screens.Debugger.Tabs.Render
|
||||
});
|
||||
}
|
||||
|
||||
public SKImage? CurrentFrame
|
||||
public Bitmap? CurrentFrame
|
||||
{
|
||||
get => _currentFrame;
|
||||
set => this.RaiseAndSetIfChanged(ref _currentFrame, value);
|
||||
@ -69,21 +70,18 @@ namespace Artemis.UI.Screens.Debugger.Tabs.Render
|
||||
|
||||
private void HandleActivation()
|
||||
{
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
_coreService.FrameRendered += CoreServiceOnFrameRendered;
|
||||
_fpsTimer.Elapsed += FpsTimerOnElapsed;
|
||||
}
|
||||
|
||||
private void HandleDeactivation()
|
||||
{
|
||||
_coreService.FrameRendered -= CoreServiceOnFrameRendered;
|
||||
_fpsTimer.Elapsed -= FpsTimerOnElapsed;
|
||||
_fpsTimer.Dispose();
|
||||
}
|
||||
|
||||
private void CoreServiceOnFrameRendered(object? sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
_frames++;
|
||||
|
||||
CurrentFps = _coreService.FrameRate;
|
||||
using SKImage skImage = e.Texture.Surface.Snapshot();
|
||||
SKImageInfo bitmapInfo = e.Texture.ImageInfo;
|
||||
|
||||
@ -102,16 +100,14 @@ namespace Artemis.UI.Screens.Debugger.Tabs.Render
|
||||
|
||||
RenderHeight = bitmapInfo.Height;
|
||||
RenderWidth = bitmapInfo.Width;
|
||||
|
||||
CurrentFrame = e.Texture.Surface.Snapshot();
|
||||
|
||||
// TODO: This performs well enough but look into something else
|
||||
using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
|
||||
{
|
||||
CurrentFrame = new Bitmap(data.AsStream());
|
||||
}
|
||||
}
|
||||
|
||||
private void FpsTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
CurrentFps = _frames;
|
||||
Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software";
|
||||
_frames = 0;
|
||||
}
|
||||
|
||||
public string UrlPathSegment => "render";
|
||||
public IScreen HostScreen { get; }
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Debugger.Tabs.Settings.DebugSettingsView">
|
||||
<TextBlock>Settings</TextBlock>
|
||||
</UserControl>
|
||||
@ -0,0 +1,18 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.Settings
|
||||
{
|
||||
public class DebugSettingsView : ReactiveUserControl<DebugSettingsViewModel>
|
||||
{
|
||||
public DebugSettingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
using Artemis.UI.Shared;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Debugger.Tabs.Settings
|
||||
{
|
||||
public class DebugSettingsViewModel : ActivatableViewModelBase, IRoutableViewModel
|
||||
{
|
||||
public DebugSettingsViewModel(IScreen hostScreen)
|
||||
{
|
||||
HostScreen = hostScreen;
|
||||
}
|
||||
|
||||
public string UrlPathSegment => "logs";
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user