Merge remote-tracking branch 'origin/master' into 1.10
This commit is contained in:
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// Represents single blend point.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
protected class BlendPoint : Control
|
||||
internal class BlendPoint : Control
|
||||
{
|
||||
private readonly BlendPointsEditor _editor;
|
||||
private readonly int _index;
|
||||
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int Index => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates that user is moving this point with a mouse.
|
||||
/// </summary>
|
||||
public bool IsMouseDown => _isMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||
/// </summary>
|
||||
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||
|
||||
/// <summary>
|
||||
/// BLend points array.
|
||||
/// </summary>
|
||||
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <returns>The blend point control position.</returns>
|
||||
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
||||
{
|
||||
if (_rangeX.IsZero)
|
||||
{
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
_rangeX = new Float2(data0.X, data0.Y);
|
||||
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||
}
|
||||
GetPointsArea(out var pointsArea);
|
||||
if (_is2D)
|
||||
{
|
||||
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
pointsArea.Center.Y
|
||||
);
|
||||
}
|
||||
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -424,7 +440,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
// Update blend point
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location) - BlendPoint.DefaultSize * 0.5f;
|
||||
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
||||
var tooltip = asset?.ShortName ?? string.Empty;
|
||||
tooltip += "\nX: " + location.X;
|
||||
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
SetAsset((int)b.Tag, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
var containsFocus = ContainsFocus;
|
||||
GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
|
||||
// Background
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
//Render2D.DrawRectangle(pointsArea, Color.Red);
|
||||
_node.DrawEditorBackground(ref rect);
|
||||
|
||||
// Grid
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
_node.DrawEditorGrid(ref rect);
|
||||
|
||||
base.Draw();
|
||||
|
||||
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space background.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space grid.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
_editor.GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_editor.Is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the editor UI.
|
||||
/// </summary>
|
||||
@@ -983,6 +1017,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
private readonly FloatValueBox _animationX;
|
||||
private readonly Label _animationYLabel;
|
||||
private readonly FloatValueBox _animationY;
|
||||
private Float2[] _triangles;
|
||||
private Color[] _triangleColors;
|
||||
private Float2[] _selectedTriangles;
|
||||
private Color[] _selectedColors;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
@@ -1051,6 +1089,143 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTriangles()
|
||||
{
|
||||
// Remove cache
|
||||
_triangles = null;
|
||||
_triangleColors = null;
|
||||
_selectedTriangles = null;
|
||||
_selectedColors = null;
|
||||
}
|
||||
|
||||
private void CacheTriangles()
|
||||
{
|
||||
// Get locations of blend point vertices
|
||||
int pointsCount = _editor.PointsCount;
|
||||
int count = 0, j = 0;
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
count++;
|
||||
}
|
||||
var vertices = new Float2[count];
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
{
|
||||
var dataA = (Float4)Values[4 + i * 2];
|
||||
vertices[j++] = new Float2(dataA.X, dataA.Y);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
|
||||
_triangleColors = null;
|
||||
|
||||
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
|
||||
if (_triangles == null || _triangles.Length == 0)
|
||||
{
|
||||
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
|
||||
var triangles = new List<Float2>();
|
||||
int verticesLeft = vertices.Length;
|
||||
while (verticesLeft >= 3)
|
||||
{
|
||||
verticesLeft -= 3;
|
||||
triangles.Add(vertices[verticesLeft + 0]);
|
||||
triangles.Add(vertices[verticesLeft + 1]);
|
||||
triangles.Add(vertices[verticesLeft + 2]);
|
||||
}
|
||||
if (verticesLeft == 1)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
else if (verticesLeft == 2)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[1]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
_triangles = triangles.ToArray();
|
||||
}
|
||||
|
||||
// Project to the blend space for drawing
|
||||
for (int i = 0; i < _triangles.Length; i++)
|
||||
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
|
||||
|
||||
// Check if anything is selected
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1)
|
||||
{
|
||||
// Find triangles that contain selected point
|
||||
var dataA = (Float4)Values[4 + selectedIndex * 2];
|
||||
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
|
||||
var selectedTriangles = new List<Float2>();
|
||||
var selectedColors = new List<Color>();
|
||||
var style = Style.Current;
|
||||
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
|
||||
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
|
||||
_triangleColors = new Color[_triangles.Length];
|
||||
for (int i = 0; i < _triangles.Length; i += 3)
|
||||
{
|
||||
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
|
||||
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
|
||||
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
|
||||
if (is0 || is1 || is2)
|
||||
{
|
||||
selectedTriangles.Add(_triangles[i + 0]);
|
||||
selectedTriangles.Add(_triangles[i + 1]);
|
||||
selectedTriangles.Add(_triangles[i + 2]);
|
||||
selectedColors.Add(is0 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is1 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is2 ? Color.White : Color.Transparent);
|
||||
}
|
||||
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
|
||||
}
|
||||
_selectedTriangles = selectedTriangles.ToArray();
|
||||
_selectedColors = selectedColors.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorBackground(ref rect);
|
||||
|
||||
// Draw triangulated multi blend space
|
||||
var style = Style.Current;
|
||||
if (_triangles == null)
|
||||
CacheTriangles();
|
||||
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
|
||||
Render2D.FillTriangles(_triangles, _triangleColors);
|
||||
else
|
||||
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
|
||||
Render2D.DrawTriangles(_triangles, style.Foreground);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorGrid(ref rect);
|
||||
|
||||
// Highlight selected blend point
|
||||
var style = Style.Current;
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
|
||||
{
|
||||
var point = _editor.BlendPoints[selectedIndex];
|
||||
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
|
||||
Render2D.PushTint(ref highlightColor);
|
||||
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
|
||||
Render2D.PopTint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||
{
|
||||
@@ -1075,6 +1250,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_animationX.Enabled = isValid;
|
||||
_animationYLabel.Enabled = isValid;
|
||||
_animationY.Enabled = isValid;
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
base.OnValuesChanged();
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
base.OnLoaded(action);
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
@@ -590,7 +591,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, FlaxEditor.Tools.Terrain.PaintTerrainGizmoMode.TerrainLayerNames),
|
||||
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()),
|
||||
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -425,6 +425,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
UpdateCombo();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
return (Guid)Values[0] == param.ID;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
@@ -937,13 +943,17 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
// Deselect if that parameter is selected
|
||||
if ((Guid)Values[0] == param.ID)
|
||||
{
|
||||
_combobox.SelectedIndex = -1;
|
||||
}
|
||||
|
||||
UpdateCombo();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
return (Guid)Values[0] == param.ID;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
|
||||
@@ -33,5 +33,12 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
/// <param name="param">The parameter.</param>
|
||||
void OnParamDeleted(SurfaceParameter param);
|
||||
|
||||
/// <summary>
|
||||
/// Get if the parameter is referenced in a graph. Referenced in this case means in a graph and at least one node in-/output connected to another node.
|
||||
/// </summary>
|
||||
/// <param name="param">The parameter.</param>
|
||||
/// <returns>If the parameter is referenced.</returns>
|
||||
bool IsParamUsed(SurfaceParameter param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
||||
typeof(TooltipAttribute),
|
||||
typeof(HideInEditorAttribute),
|
||||
typeof(NoAnimateAttribute),
|
||||
typeof(ButtonAttribute),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -84,5 +84,16 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
MarkAsEdited();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
if (Nodes[i] is IParametersDependantNode node && node.IsParamUsed(param))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -776,6 +776,15 @@ namespace FlaxEditor.Surface
|
||||
private void DeleteParameter(int index)
|
||||
{
|
||||
var window = (IVisjectSurfaceWindow)Values[0];
|
||||
SurfaceParameter param = window.VisjectSurface.Parameters[index];
|
||||
|
||||
if (Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter && window.VisjectSurface.IsParamUsed(param))
|
||||
{
|
||||
string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph.\n\nYou can disable this warning in the editor settings.";
|
||||
if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK)
|
||||
return;
|
||||
}
|
||||
|
||||
var action = new AddRemoveParamAction
|
||||
{
|
||||
Window = window,
|
||||
|
||||
Reference in New Issue
Block a user