mirror of
https://github.com/2dust/v2rayN.git
synced 2026-03-22 00:08:16 +05:00
Json editor with syntax highlighting (#8932)
* Add json editor * Replace JsonEditor with TextBox * Replace JsonEditor with TextBox * Remove TextMateSharp.Grammars * Fix two way bind * Update ResUI.ru.resx --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
36
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
36
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -924,6 +924,42 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Copy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuEditCopy {
|
||||
get {
|
||||
return ResourceManager.GetString("menuEditCopy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Format 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuEditFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("menuEditFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Paste 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuEditPaste {
|
||||
get {
|
||||
return ResourceManager.GetString("menuEditPaste", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Select all 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuEditSelectAll {
|
||||
get {
|
||||
return ResourceManager.GetString("menuEditSelectAll", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Edit 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>کپی</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>انتخاب همه</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1665,4 +1665,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>Copier</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>Tout sélect</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>Másolás</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>Összes kijelölése</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1668,4 +1668,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>Select all</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1668,4 +1668,16 @@
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Группировка по регионам</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>Скопировать</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>Выбрать все</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1665,4 +1665,16 @@
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>按地区分组</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>复制</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>全选</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>粘贴</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>格式化</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1665,4 +1665,16 @@
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>按區域分組</value>
|
||||
</data>
|
||||
<data name="menuEditCopy" xml:space="preserve">
|
||||
<value>複製</value>
|
||||
</data>
|
||||
<data name="menuEditSelectAll" xml:space="preserve">
|
||||
<value>全選</value>
|
||||
</data>
|
||||
<data name="menuEditPaste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="menuEditFormat" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuServers}"
|
||||
Width="900"
|
||||
@@ -658,16 +659,13 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TransportExtraTip}" />
|
||||
<TextBox
|
||||
<views:JsonEditor
|
||||
x:Name="txtExtra"
|
||||
Width="400"
|
||||
MinHeight="100"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Classes="TextArea"
|
||||
MinLines="6"
|
||||
TextWrapping="Wrap" />
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
@@ -749,13 +747,13 @@
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<TextBox
|
||||
<views:JsonEditor
|
||||
x:Name="txtFinalmask"
|
||||
Width="400"
|
||||
MinHeight="100"
|
||||
Margin="{StaticResource Margin4}"
|
||||
AcceptsReturn="True"
|
||||
Classes="TextArea"
|
||||
TextWrapping="NoWrap" />
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:v2rayN.Desktop.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
@@ -399,12 +400,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="HTTP/SOCKS">
|
||||
<TextBox
|
||||
Name="txtnormalDNSCompatible"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<local:JsonEditor Name="txtnormalDNSCompatible" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
@@ -473,12 +469,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="HTTP/SOCKS">
|
||||
<TextBox
|
||||
Name="txtnormalDNS2Compatible"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<local:JsonEditor Name="txtnormalDNS2Compatible" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
@@ -488,12 +479,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="{x:Static resx:ResUI.TbSettingsTunMode}">
|
||||
<TextBox
|
||||
Name="txttunDNS2Compatible"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<local:JsonEditor Name="txttunDNS2Compatible" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuFullConfigTemplate}"
|
||||
Width="900"
|
||||
@@ -94,12 +95,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="xray config template json">
|
||||
<TextBox
|
||||
x:Name="rayFullConfigTemplate"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<views:JsonEditor x:Name="rayFullConfigTemplate" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
@@ -166,12 +162,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="sing-box config template json">
|
||||
<TextBox
|
||||
x:Name="sbFullConfigTemplate"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<views:JsonEditor x:Name="sbFullConfigTemplate" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
@@ -181,12 +172,7 @@
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="sing-box tun config template json">
|
||||
<TextBox
|
||||
x:Name="sbFullTunConfigTemplate"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
<views:JsonEditor x:Name="sbFullTunConfigTemplate" VerticalAlignment="Stretch" />
|
||||
</HeaderedContentControl>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
23
v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml
Normal file
23
v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml
Normal file
@@ -0,0 +1,23 @@
|
||||
<UserControl
|
||||
x:Class="v2rayN.Desktop.Views.JsonEditor"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ae="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib">
|
||||
|
||||
<ae:TextEditor
|
||||
Name="Editor"
|
||||
FontFamily="Cascadia Code,Consolas,Monospace"
|
||||
FontSize="14"
|
||||
ShowLineNumbers="True">
|
||||
<ae:TextEditor.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Click="FormatJson_Click" Header="{x:Static resx:ResUI.menuEditFormat}" />
|
||||
<Separator />
|
||||
<MenuItem Click="Copy_Click" Header="{x:Static resx:ResUI.menuEditCopy}" />
|
||||
<MenuItem Click="Paste_Click" Header="{x:Static resx:ResUI.menuEditPaste}" />
|
||||
<MenuItem Click="SelectAll_Click" Header="{x:Static resx:ResUI.menuEditSelectAll}" />
|
||||
</ContextMenu>
|
||||
</ae:TextEditor.ContextMenu>
|
||||
</ae:TextEditor>
|
||||
</UserControl>
|
||||
96
v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs
Normal file
96
v2rayN/v2rayN.Desktop/Views/JsonEditor.axaml.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System.Text.Json;
|
||||
using System.Xml;
|
||||
using AvaloniaEdit.Highlighting;
|
||||
using AvaloniaEdit.Highlighting.Xshd;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class JsonEditor : UserControl
|
||||
{
|
||||
private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true };
|
||||
|
||||
private static readonly Lazy<IHighlightingDefinition> SHighlightingDark =
|
||||
new(() => BuildHighlighting(dark: true), isThreadSafe: true);
|
||||
|
||||
private static readonly Lazy<IHighlightingDefinition> SHighlightingLight =
|
||||
new(() => BuildHighlighting(dark: false), isThreadSafe: true);
|
||||
|
||||
public static readonly StyledProperty<string> TextProperty =
|
||||
AvaloniaProperty.Register<JsonEditor, string>(nameof(Text), defaultValue: string.Empty);
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
}
|
||||
|
||||
public JsonEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light;
|
||||
Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value;
|
||||
Editor.TextArea.TextView.Options.EnableHyperlinks = false;
|
||||
|
||||
Editor.TextChanged += (_, _) =>
|
||||
{
|
||||
if (Text != Editor.Text)
|
||||
{
|
||||
SetCurrentValue(TextProperty, Editor.Text);
|
||||
}
|
||||
};
|
||||
|
||||
this.GetObservable(TextProperty).Subscribe(text =>
|
||||
{
|
||||
if (Editor.Text != text)
|
||||
{
|
||||
Editor.Text = text ?? string.Empty;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static IHighlightingDefinition BuildHighlighting(bool dark)
|
||||
{
|
||||
var keyColor = dark ? "#9CDCFE" : "#0451A5";
|
||||
var strColor = dark ? "#CE9178" : "#A31515";
|
||||
var numColor = dark ? "#B5CEA8" : "#098658";
|
||||
var kwColor = dark ? "#569CD6" : "#0000FF";
|
||||
var xshd = $"""
|
||||
<?xml version="1.0"?>
|
||||
<SyntaxDefinition name="JSON" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
|
||||
<Color name="Key" foreground="{keyColor}" />
|
||||
<Color name="String" foreground="{strColor}" />
|
||||
<Color name="Number" foreground="{numColor}" />
|
||||
<Color name="Keyword" foreground="{kwColor}" fontWeight="bold" />
|
||||
<RuleSet>
|
||||
<Rule color="Key">"([^"\\]|\\.)*"(?=\s*:)</Rule>
|
||||
<Rule color="String">"([^"\\]|\\.)*"</Rule>
|
||||
<Rule color="Number">-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?</Rule>
|
||||
<Keywords color="Keyword">
|
||||
<Word>true</Word>
|
||||
<Word>false</Word>
|
||||
<Word>null</Word>
|
||||
</Keywords>
|
||||
</RuleSet>
|
||||
</SyntaxDefinition>
|
||||
""";
|
||||
using var reader = XmlReader.Create(new StringReader(xshd));
|
||||
return HighlightingLoader.Load(reader, HighlightingManager.Instance);
|
||||
}
|
||||
|
||||
private void FormatJson_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(Editor.Text);
|
||||
Editor.Text = JsonUtils.Serialize(obj, SIndentedOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void Copy_Click(object? sender, RoutedEventArgs e) => Editor.Copy();
|
||||
private void Paste_Click(object? sender, RoutedEventArgs e) => Editor.Paste();
|
||||
private void SelectAll_Click(object? sender, RoutedEventArgs e) => Editor.SelectAll();
|
||||
}
|
||||
@@ -79,8 +79,8 @@
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewSelectAll"
|
||||
Click="menuMsgViewSelectAll_Click"
|
||||
InputGesture="Ctrl+A"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}"
|
||||
InputGesture="Ctrl+A" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopy"
|
||||
Click="menuMsgViewCopy_Click"
|
||||
|
||||
Reference in New Issue
Block a user