Changed payload editor to Avalon
- Syntax highlighting - Highlight for JSON validation errors - Much improved tab handling and all the other advantages of using a proper editor
This commit is contained in:
parent
78de8e5196
commit
b549729bf5
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -19,7 +19,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Images\Connecting.svg" />
|
<None Remove="Images\Busy.svg" />
|
||||||
<None Remove="Images\Dock.svg" />
|
<None Remove="Images\Dock.svg" />
|
||||||
<None Remove="Images\Error.svg" />
|
<None Remove="Images\Error.svg" />
|
||||||
<None Remove="Images\Ok.svg" />
|
<None Remove="Images\Ok.svg" />
|
||||||
@ -40,6 +40,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AvalonEdit" Version="6.1.2.30" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="SharpVectors" Version="1.7.7" />
|
<PackageReference Include="SharpVectors" Version="1.7.7" />
|
||||||
<PackageReference Include="SimpleInjector" Version="5.3.2" />
|
<PackageReference Include="SimpleInjector" Version="5.3.2" />
|
||||||
@ -54,7 +55,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Images\Undock.svg" />
|
<Resource Include="Images\Undock.svg" />
|
||||||
<Resource Include="Images\Connecting.svg" />
|
<Resource Include="Images\Busy.svg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ui="clr-namespace:PettingZoo.UI">
|
xmlns:ui="clr-namespace:PettingZoo.UI"
|
||||||
|
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit">
|
||||||
<!-- Global styling -->
|
<!-- Global styling -->
|
||||||
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
||||||
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
||||||
@ -83,10 +84,11 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
|
||||||
<Style x:Key="Payload" TargetType="{x:Type TextBox}">
|
<Style x:Key="Payload" TargetType="{x:Type avalonedit:TextEditor}">
|
||||||
<Setter Property="AcceptsReturn" Value="True" />
|
|
||||||
<Setter Property="AcceptsTab" Value="True" />
|
|
||||||
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
|
|
||||||
<Setter Property="FontFamily" Value="Consolas,Courier New" />
|
<Setter Property="FontFamily" Value="Consolas,Courier New" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="ControlBorder" TargetType="{x:Type Border}">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
37
PettingZoo/UI/ErrorHighlightingTransformer.cs
Normal file
37
PettingZoo/UI/ErrorHighlightingTransformer.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using ICSharpCode.AvalonEdit.Document;
|
||||||
|
using ICSharpCode.AvalonEdit.Rendering;
|
||||||
|
|
||||||
|
namespace PettingZoo.UI
|
||||||
|
{
|
||||||
|
public class ErrorHighlightingTransformer : DocumentColorizingTransformer
|
||||||
|
{
|
||||||
|
public Brush BackgroundBrush { get; set; }
|
||||||
|
public TextPosition? ErrorPosition { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public ErrorHighlightingTransformer()
|
||||||
|
{
|
||||||
|
BackgroundBrush = new SolidColorBrush(Color.FromRgb(255, 230, 230));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void ColorizeLine(DocumentLine line)
|
||||||
|
{
|
||||||
|
if (ErrorPosition == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (line.LineNumber != Math.Max(ErrorPosition.Value.Row, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lineStartOffset = line.Offset;
|
||||||
|
|
||||||
|
ChangeLinePart(lineStartOffset, lineStartOffset + line.Length,
|
||||||
|
element =>
|
||||||
|
{
|
||||||
|
element.BackgroundBrush = BackgroundBrush;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -69,6 +69,8 @@ namespace PettingZoo.UI
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
BindingOperations.ClearBinding(this, ItemsSourceProperty);
|
BindingOperations.ClearBinding(this, ItemsSourceProperty);
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
<StatusBar DockPanel.Dock="Bottom">
|
<StatusBar DockPanel.Dock="Bottom">
|
||||||
<StatusBarItem>
|
<StatusBarItem>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Image Source="{svgc:SvgImage Source=/Images/Connecting.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusConnecting}" />
|
<Image Source="{svgc:SvgImage Source=/Images/Busy.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusConnecting}" />
|
||||||
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusOk}" />
|
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusOk}" />
|
||||||
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusError}" />
|
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusError}" />
|
||||||
<TextBlock Text="{Binding ConnectionStatus}" VerticalAlignment="Center"/>
|
<TextBlock Text="{Binding ConnectionStatus}" VerticalAlignment="Center"/>
|
||||||
|
@ -115,6 +115,8 @@ namespace PettingZoo.UI.Main
|
|||||||
private static T? GetParent<T>(object originalSource) where T : DependencyObject
|
private static T? GetParent<T>(object originalSource) where T : DependencyObject
|
||||||
{
|
{
|
||||||
var current = originalSource as DependencyObject;
|
var current = originalSource as DependencyObject;
|
||||||
|
if (current is not Visual)
|
||||||
|
return null;
|
||||||
|
|
||||||
while (current != null)
|
while (current != null)
|
||||||
{
|
{
|
||||||
|
@ -17,15 +17,23 @@
|
|||||||
<RadioButton Content="JSON" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Json}}" />
|
<RadioButton Content="JSON" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Json}}" />
|
||||||
<RadioButton Content="Plain text" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Plain}}" />
|
<RadioButton Content="Plain text" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Plain}}" />
|
||||||
<RadioButton Content="Other" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Other}}" />
|
<RadioButton Content="Other" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Other}}" />
|
||||||
<TextBox Width="200" Text="{Binding ContentType, UpdateSourceTrigger=PropertyChanged}" />
|
<TextBox Name="TextBoxForBorder" Width="200" Text="{Binding ContentType, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Visibility="{Binding JsonValidationVisibility}" Margin="0,8,0,0">
|
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Visibility="{Binding ValidationVisibility}" Margin="0,8,0,0">
|
||||||
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding JsonValidationOk}" />
|
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ValidationOk}" />
|
||||||
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding JsonValidationError}" />
|
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ValidationError}" />
|
||||||
<TextBlock Text="{Binding JsonValidationMessage}" Margin="4" />
|
<Image Source="{svgc:SvgImage Source=/Images/Busy.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ValidationValidating}" />
|
||||||
|
<TextBlock Text="{Binding ValidationMessage}" Margin="4" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBox Text="{Binding Payload, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Payload}" />
|
<Border Style="{StaticResource ControlBorder}" Name="EditorBorder">
|
||||||
|
<avalonedit:TextEditor
|
||||||
|
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||||
|
Name="Editor"
|
||||||
|
SyntaxHighlighting="{Binding SyntaxHighlighting}"
|
||||||
|
Style="{StaticResource Payload}"
|
||||||
|
/>
|
||||||
|
</Border>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -86,6 +86,9 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private readonly ErrorHighlightingTransformer errorHighlightingTransformer = new();
|
||||||
|
|
||||||
public PayloadEditorControl()
|
public PayloadEditorControl()
|
||||||
{
|
{
|
||||||
// Keep the exposed properties in sync with the ViewModel
|
// Keep the exposed properties in sync with the ViewModel
|
||||||
@ -133,6 +136,42 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
// I'm not sure how to get a standard control border, all I could find were workaround:
|
||||||
|
// https://social.msdn.microsoft.com/Forums/en-US/5e007497-8d5a-401d-ac5b-9e1356fe9b64/default-borderbrush-for-textbox-listbox-etc
|
||||||
|
// So I'll just copy it from another TextBox. I truly hate WPF some times for making standard things so complicated. </rant>
|
||||||
|
EditorBorder.BorderBrush = TextBoxForBorder.BorderBrush;
|
||||||
|
|
||||||
|
Editor.Options.IndentationSize = 2;
|
||||||
|
Editor.TextArea.TextView.LineTransformers.Add(errorHighlightingTransformer);
|
||||||
|
|
||||||
|
// Avalon doesn't play nice with bindings it seems:
|
||||||
|
// https://stackoverflow.com/questions/18964176/two-way-binding-to-avalonedit-document-text-using-mvvm
|
||||||
|
Editor.Document.Text = Payload;
|
||||||
|
|
||||||
|
Editor.TextChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
Payload = Editor.Document.Text;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
viewModel.PropertyChanged += (_, args) =>
|
||||||
|
{
|
||||||
|
if (args.PropertyName != nameof(viewModel.ValidationInfo))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
if (errorHighlightingTransformer.ErrorPosition == viewModel.ValidationInfo.ErrorPosition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
errorHighlightingTransformer.ErrorPosition = viewModel.ValidationInfo.ErrorPosition;
|
||||||
|
|
||||||
|
// TODO this can probably be optimized to only redraw the affected line
|
||||||
|
Editor.TextArea.TextView.Redraw();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Setting the DataContext for the UserControl is a major PITA when binding the control's properties,
|
// Setting the DataContext for the UserControl is a major PITA when binding the control's properties,
|
||||||
// so I've moved the ViewModel one level down to get the best of both worlds...
|
// so I've moved the ViewModel one level down to get the best of both worlds...
|
||||||
DataContextContainer.DataContext = viewModel;
|
DataContextContainer.DataContext = viewModel;
|
||||||
|
@ -88,20 +88,29 @@ namespace PettingZoo.UI.Tab.Publisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Invalid JSON: {0}.
|
/// Looks up a localized string similar to Invalid: {0}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string JsonValidationError {
|
internal static string ValidationError {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("JsonValidationError", resourceCulture);
|
return ResourceManager.GetString("ValidationError", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Valid JSON.
|
/// Looks up a localized string similar to Valid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string JsonValidationOk {
|
internal static string ValidationOk {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("JsonValidationOk", resourceCulture);
|
return ResourceManager.GetString("ValidationOk", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Validating....
|
||||||
|
/// </summary>
|
||||||
|
internal static string ValidationValidating {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ValidationValidating", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,10 +112,10 @@
|
|||||||
<value>2.0</value>
|
<value>2.0</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="reader">
|
<resheader name="reader">
|
||||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ContentTypeJson" xml:space="preserve">
|
<data name="ContentTypeJson" xml:space="preserve">
|
||||||
<value>JSON</value>
|
<value>JSON</value>
|
||||||
@ -126,10 +126,13 @@
|
|||||||
<data name="ContentTypePlain" xml:space="preserve">
|
<data name="ContentTypePlain" xml:space="preserve">
|
||||||
<value>Plain text</value>
|
<value>Plain text</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="JsonValidationError" xml:space="preserve">
|
<data name="ValidationError" xml:space="preserve">
|
||||||
<value>Invalid JSON: {0}</value>
|
<value>Invalid: {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="JsonValidationOk" xml:space="preserve">
|
<data name="ValidationOk" xml:space="preserve">
|
||||||
<value>Valid JSON</value>
|
<value>Valid</value>
|
||||||
|
</data>
|
||||||
|
<data name="ValidationValidating" xml:space="preserve">
|
||||||
|
<value>Validating...</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using ICSharpCode.AvalonEdit.Highlighting;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace PettingZoo.UI.Tab.Publisher
|
namespace PettingZoo.UI.Tab.Publisher
|
||||||
@ -15,6 +16,38 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public enum ValidationStatus
|
||||||
|
{
|
||||||
|
NotSupported,
|
||||||
|
Validating,
|
||||||
|
Ok,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public readonly struct ValidationInfo
|
||||||
|
{
|
||||||
|
public ValidationStatus Status { get; }
|
||||||
|
public string Message { get; }
|
||||||
|
public TextPosition? ErrorPosition { get; }
|
||||||
|
|
||||||
|
|
||||||
|
public ValidationInfo(ValidationStatus status, string? message = null, TextPosition? errorPosition = null)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
Message = message ?? status switch
|
||||||
|
{
|
||||||
|
ValidationStatus.NotSupported => "",
|
||||||
|
ValidationStatus.Validating => PayloadEditorStrings.ValidationValidating,
|
||||||
|
ValidationStatus.Ok => PayloadEditorStrings.ValidationOk,
|
||||||
|
ValidationStatus.Error => throw new InvalidOperationException(@"Message required for Error validation status"),
|
||||||
|
_ => throw new ArgumentException(@"Unsupported validation status", nameof(status))
|
||||||
|
};
|
||||||
|
ErrorPosition = errorPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class PayloadEditorViewModel : BaseViewModel
|
public class PayloadEditorViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string ContentTypeJson = "application/json";
|
private const string ContentTypeJson = "application/json";
|
||||||
@ -24,8 +57,7 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
private PayloadEditorContentType contentTypeSelection = PayloadEditorContentType.Json;
|
private PayloadEditorContentType contentTypeSelection = PayloadEditorContentType.Json;
|
||||||
private bool fixedJson;
|
private bool fixedJson;
|
||||||
|
|
||||||
private bool jsonValid = true;
|
private ValidationInfo validationInfo = new(ValidationStatus.Ok);
|
||||||
private string jsonValidationMessage;
|
|
||||||
|
|
||||||
private string payload = "";
|
private string payload = "";
|
||||||
|
|
||||||
@ -59,7 +91,7 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
get => contentTypeSelection;
|
get => contentTypeSelection;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (!SetField(ref contentTypeSelection, value, otherPropertiesChanged: new [] { nameof(JsonValidationVisibility) }))
|
if (!SetField(ref contentTypeSelection, value, otherPropertiesChanged: new [] { nameof(ValidationVisibility), nameof(SyntaxHighlighting) }))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ContentType = ContentTypeSelection switch
|
ContentType = ContentTypeSelection switch
|
||||||
@ -80,23 +112,22 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
set => SetField(ref fixedJson, value);
|
set => SetField(ref fixedJson, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Visibility JsonValidationVisibility => ContentTypeSelection == PayloadEditorContentType.Json ? Visibility.Visible : Visibility.Collapsed;
|
|
||||||
public Visibility JsonValidationOk => JsonValid ? Visibility.Visible : Visibility.Collapsed;
|
|
||||||
public Visibility JsonValidationError => !JsonValid ? Visibility.Visible : Visibility.Collapsed;
|
|
||||||
|
|
||||||
|
public ValidationInfo ValidationInfo
|
||||||
public string JsonValidationMessage
|
|
||||||
{
|
{
|
||||||
get => jsonValidationMessage;
|
get => validationInfo;
|
||||||
private set => SetField(ref jsonValidationMessage, value);
|
private set => SetField(ref validationInfo, value, otherPropertiesChanged: new[] { nameof(ValidationOk), nameof(ValidationError), nameof(ValidationValidating), nameof(ValidationMessage) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool JsonValid
|
public Visibility ValidationVisibility => ContentTypeSelection == PayloadEditorContentType.Json ? Visibility.Visible : Visibility.Collapsed;
|
||||||
{
|
|
||||||
get => jsonValid;
|
public string ValidationMessage => ValidationInfo.Message;
|
||||||
private set => SetField(ref jsonValid, value, otherPropertiesChanged: new[] { nameof(JsonValidationOk), nameof(JsonValidationError) });
|
|
||||||
}
|
public Visibility ValidationOk => ValidationInfo.Status == ValidationStatus.Ok ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
public Visibility ValidationError => ValidationInfo.Status == ValidationStatus.Error ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
public Visibility ValidationValidating => ValidationInfo.Status == ValidationStatus.Validating ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
|
||||||
|
|
||||||
public Visibility ContentTypeVisibility => FixedJson ? Visibility.Collapsed : Visibility.Visible;
|
public Visibility ContentTypeVisibility => FixedJson ? Visibility.Collapsed : Visibility.Visible;
|
||||||
|
|
||||||
@ -108,26 +139,48 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IHighlightingDefinition? SyntaxHighlighting => ContentTypeSelection == PayloadEditorContentType.Json
|
||||||
|
? HighlightingManager.Instance.GetDefinition(@"Json")
|
||||||
|
: null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public PayloadEditorViewModel()
|
public PayloadEditorViewModel()
|
||||||
{
|
{
|
||||||
jsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
var observable = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
|
||||||
|
|
||||||
Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
|
|
||||||
h => PropertyChanged += h,
|
h => PropertyChanged += h,
|
||||||
h => PropertyChanged -= h)
|
h => PropertyChanged -= h)
|
||||||
.Where(e => e.EventArgs.PropertyName == nameof(Payload))
|
.Where(e => e.EventArgs.PropertyName == nameof(Payload));
|
||||||
|
|
||||||
|
observable
|
||||||
|
.Subscribe(_ => ValidatingPayload());
|
||||||
|
|
||||||
|
observable
|
||||||
.Throttle(TimeSpan.FromMilliseconds(500))
|
.Throttle(TimeSpan.FromMilliseconds(500))
|
||||||
.Subscribe(_ => ValidatePayload());
|
.Subscribe(_ => ValidatePayload());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ValidatingPayload()
|
||||||
|
{
|
||||||
|
if (ValidationInfo.Status == ValidationStatus.Validating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ContentTypeSelection != PayloadEditorContentType.Json)
|
||||||
|
{
|
||||||
|
ValidationInfo = new ValidationInfo(ValidationStatus.NotSupported);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationInfo = new ValidationInfo(ValidationStatus.Validating);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ValidatePayload()
|
private void ValidatePayload()
|
||||||
{
|
{
|
||||||
if (ContentTypeSelection != PayloadEditorContentType.Json)
|
if (ContentTypeSelection != PayloadEditorContentType.Json)
|
||||||
{
|
{
|
||||||
JsonValid = true;
|
ValidationInfo = new ValidationInfo(ValidationStatus.NotSupported);
|
||||||
JsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +189,15 @@ namespace PettingZoo.UI.Tab.Publisher
|
|||||||
if (!string.IsNullOrEmpty(Payload))
|
if (!string.IsNullOrEmpty(Payload))
|
||||||
JToken.Parse(Payload);
|
JToken.Parse(Payload);
|
||||||
|
|
||||||
JsonValid = true;
|
ValidationInfo = new ValidationInfo(ValidationStatus.Ok);
|
||||||
JsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
}
|
||||||
|
catch (JsonReaderException e)
|
||||||
|
{
|
||||||
|
ValidationInfo = new ValidationInfo(ValidationStatus.Error, e.Message, new TextPosition(e.LineNumber, e.LinePosition));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
JsonValid = false;
|
ValidationInfo = new ValidationInfo(ValidationStatus.Error, e.Message);
|
||||||
JsonValidationMessage = string.Format(PayloadEditorStrings.JsonValidationError, e.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
PettingZoo/UI/TextPosition.cs
Normal file
43
PettingZoo/UI/TextPosition.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PettingZoo.UI
|
||||||
|
{
|
||||||
|
public readonly struct TextPosition : IEquatable<TextPosition>
|
||||||
|
{
|
||||||
|
public int Row { get; }
|
||||||
|
public int Column { get; }
|
||||||
|
|
||||||
|
|
||||||
|
public TextPosition(int row, int column)
|
||||||
|
{
|
||||||
|
Row = row;
|
||||||
|
Column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool Equals(TextPosition other)
|
||||||
|
{
|
||||||
|
return Row == other.Row && Column == other.Column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is TextPosition other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Row, Column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(TextPosition left, TextPosition right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(TextPosition left, TextPosition right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user