Merge branch 'master' into 1.5
# Conflicts: # Source/Engine/Serialization/Stream.cpp
This commit is contained in:
@@ -9,6 +9,8 @@ using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
@@ -266,7 +268,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
/// <summary>
|
||||
/// Starts the actor renaming action.
|
||||
/// </summary>
|
||||
public void StartRenaming()
|
||||
public void StartRenaming(EditorWindow window)
|
||||
{
|
||||
// Block renaming during scripts reload
|
||||
if (Editor.Instance.ProgressReporting.CompileScripts.IsActive)
|
||||
@@ -274,16 +276,22 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
|
||||
Select();
|
||||
|
||||
// Disable scrolling of scene view
|
||||
Editor.Instance.Windows.SceneWin.ScrollingOnSceneTreeView(false);
|
||||
// Disable scrolling of view
|
||||
if (window is SceneTreeWindow)
|
||||
(window as SceneTreeWindow).ScrollingOnSceneTreeView(false);
|
||||
else if (window is PrefabWindow)
|
||||
(window as PrefabWindow).ScrollingOnTreeView(false);
|
||||
|
||||
// Start renaming the actor
|
||||
var dialog = RenamePopup.Show(this, HeaderRect, _actorNode.Name, false);
|
||||
dialog.Renamed += OnRenamed;
|
||||
dialog.Closed += popup =>
|
||||
{
|
||||
// Enable scrolling of scene view
|
||||
Editor.Instance.Windows.SceneWin.ScrollingOnSceneTreeView(true);
|
||||
// Enable scrolling of view
|
||||
if (window is SceneTreeWindow)
|
||||
(window as SceneTreeWindow).ScrollingOnSceneTreeView(true);
|
||||
else if (window is PrefabWindow)
|
||||
(window as PrefabWindow).ScrollingOnTreeView(true);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,9 +299,6 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename"))
|
||||
Actor.Name = renamePopup.Text;
|
||||
|
||||
// Enable scrolling of scene view
|
||||
Editor.Instance.Windows.SceneWin.ScrollingOnSceneTreeView(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -153,6 +153,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
OnPasteAction(pasteAction);
|
||||
}
|
||||
|
||||
// Scroll to new selected node
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -180,6 +183,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
OnPasteAction(pasteAction);
|
||||
}
|
||||
|
||||
// Scroll to new selected node
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
|
||||
private void OnPasteAction(PasteActorsAction pasteAction)
|
||||
@@ -328,6 +334,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
}, action2.ActionString);
|
||||
action.Do();
|
||||
Undo.AddAction(action);
|
||||
|
||||
_treePanel.PerformLayout();
|
||||
_treePanel.PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private DragHandlers _dragHandlers;
|
||||
|
||||
public SceneTreePanel(PrefabWindow window)
|
||||
: base(ScrollBars.Vertical)
|
||||
: base(ScrollBars.None)
|
||||
{
|
||||
_window = window;
|
||||
Offsets = Margin.Zero;
|
||||
@@ -246,24 +246,53 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Spawning actors options
|
||||
|
||||
contextMenu.AddSeparator();
|
||||
var spawnMenu = contextMenu.AddChildMenu("New");
|
||||
var newActorCm = spawnMenu.ContextMenu;
|
||||
for (int i = 0; i < SceneTreeWindow.SpawnActorsGroups.Length; i++)
|
||||
{
|
||||
var group = SceneTreeWindow.SpawnActorsGroups[i];
|
||||
|
||||
if (group.Types.Length == 1)
|
||||
// Go through each actor and add it to the context menu if it has the ActorContextMenu attribute
|
||||
foreach (var actorType in Editor.CodeEditing.Actors.Get())
|
||||
{
|
||||
if (actorType.IsAbstract)
|
||||
continue;
|
||||
ActorContextMenuAttribute attribute = null;
|
||||
foreach (var e in actorType.GetAttributes(true))
|
||||
{
|
||||
var type = group.Types[0].Value;
|
||||
newActorCm.AddButton(group.Types[0].Key, () => Spawn(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupCm = newActorCm.AddChildMenu(group.Name).ContextMenu;
|
||||
for (int j = 0; j < group.Types.Length; j++)
|
||||
if (e is ActorContextMenuAttribute actorContextMenuAttribute)
|
||||
{
|
||||
var type = group.Types[j].Value;
|
||||
groupCm.AddButton(group.Types[j].Key, () => Spawn(type));
|
||||
attribute = actorContextMenuAttribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attribute == null)
|
||||
continue;
|
||||
var splitPath = attribute.Path.Split('/');
|
||||
ContextMenuChildMenu childCM = null;
|
||||
bool mainCM = true;
|
||||
for (int i = 0; i < splitPath?.Length; i++)
|
||||
{
|
||||
if (i == splitPath.Length - 1)
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
contextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type));
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type));
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
childCM = contextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
}
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +339,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
if (selection.Count != 0)
|
||||
Select(actor);
|
||||
actor.TreeNode.StartRenaming();
|
||||
actor.TreeNode.StartRenaming(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private readonly SplitPanel _split1;
|
||||
private readonly SplitPanel _split2;
|
||||
private readonly TextBox _searchBox;
|
||||
private readonly Panel _treePanel;
|
||||
private readonly PrefabTree _tree;
|
||||
private readonly PrefabWindowViewport _viewport;
|
||||
private readonly CustomEditorPresenter _propertiesEditor;
|
||||
@@ -132,17 +133,26 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
_searchBox.TextChanged += OnSearchBoxTextChanged;
|
||||
|
||||
_treePanel = new Panel()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = new Margin(0.0f, 0.0f, headerPanel.Bottom, 0.0f),
|
||||
ScrollBars = ScrollBars.Both,
|
||||
IsScrollable = true,
|
||||
Parent = sceneTreePanel,
|
||||
};
|
||||
|
||||
// Prefab structure tree
|
||||
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
||||
_tree = new PrefabTree
|
||||
{
|
||||
Y = headerPanel.Bottom,
|
||||
Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node
|
||||
IsScrollable = true,
|
||||
};
|
||||
_tree.AddChild(Graph.Root.TreeNode);
|
||||
_tree.SelectedChanged += OnTreeSelectedChanged;
|
||||
_tree.RightClick += OnTreeRightClick;
|
||||
_tree.Parent = sceneTreePanel;
|
||||
_tree.Parent = _treePanel;
|
||||
headerPanel.Parent = sceneTreePanel;
|
||||
|
||||
// Prefab viewport
|
||||
@@ -192,6 +202,32 @@ namespace FlaxEditor.Windows.Assets
|
||||
InputActions.Add(options => options.Rename, Rename);
|
||||
InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables vertical and horizontal scrolling on the tree panel.
|
||||
/// </summary>
|
||||
/// <param name="enabled">The state to set scrolling to</param>
|
||||
public void ScrollingOnTreeView(bool enabled)
|
||||
{
|
||||
if (_treePanel.VScrollBar != null)
|
||||
_treePanel.VScrollBar.ThumbEnabled = enabled;
|
||||
if (_treePanel.HScrollBar != null)
|
||||
_treePanel.HScrollBar.ThumbEnabled = enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scrolls to the selected node in the tree.
|
||||
/// </summary>
|
||||
public void ScrollToSelectedNode()
|
||||
{
|
||||
// Scroll to node
|
||||
var nodeSelection = _tree.Selection;
|
||||
if (nodeSelection.Count != 0)
|
||||
{
|
||||
var scrollControl = nodeSelection[nodeSelection.Count - 1];
|
||||
_treePanel.ScrollViewTo(scrollControl);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearchBoxTextChanged()
|
||||
{
|
||||
@@ -211,6 +247,28 @@ namespace FlaxEditor.Windows.Assets
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseUp(location, button))
|
||||
return true;
|
||||
|
||||
if (button == MouseButton.Right && _treePanel.ContainsPoint(ref location))
|
||||
{
|
||||
_tree.Deselect();
|
||||
var locationCM = location + _searchBox.BottomLeft;
|
||||
ShowContextMenu(Parent, ref locationCM);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == MouseButton.Left && _treePanel.ContainsPoint(ref location))
|
||||
{
|
||||
_tree.Deselect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
{
|
||||
_isScriptsReloading = true;
|
||||
|
||||
@@ -60,23 +60,57 @@ namespace FlaxEditor.Windows
|
||||
if (isSingleActorSelected)
|
||||
{
|
||||
var convertMenu = contextMenu.AddChildMenu("Convert");
|
||||
var convertActorCm = convertMenu.ContextMenu;
|
||||
for (int i = 0; i < SpawnActorsGroups.Length; i++)
|
||||
convertMenu.ContextMenu.AutoSort = true;
|
||||
foreach (var actorType in Editor.CodeEditing.Actors.Get())
|
||||
{
|
||||
var group = SpawnActorsGroups[i];
|
||||
if (actorType.IsAbstract)
|
||||
continue;
|
||||
|
||||
if (group.Types.Length == 1)
|
||||
ActorContextMenuAttribute attribute = null;
|
||||
foreach (var e in actorType.GetAttributes(true))
|
||||
{
|
||||
var type = group.Types[0].Value;
|
||||
convertActorCm.AddButton(group.Types[0].Key, () => Editor.SceneEditing.Convert(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu;
|
||||
for (int j = 0; j < group.Types.Length; j++)
|
||||
if (e is ActorContextMenuAttribute actorContextMenuAttribute)
|
||||
{
|
||||
var type = group.Types[j].Value;
|
||||
groupCm.AddButton(group.Types[j].Key, () => Editor.SceneEditing.Convert(type));
|
||||
attribute = actorContextMenuAttribute;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attribute == null)
|
||||
continue;
|
||||
var splitPath = attribute?.Path.Split('/');
|
||||
ContextMenuChildMenu childCM = convertMenu;
|
||||
bool mainCM = true;
|
||||
for (int i = 0; i < splitPath?.Length; i++)
|
||||
{
|
||||
if (i == splitPath.Length - 1)
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
convertMenu.ContextMenu.AddButton(splitPath[i].Trim(), () => Editor.SceneEditing.Convert(actorType.Type));
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Editor.SceneEditing.Convert(actorType.Type));
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove new path for converting menu
|
||||
if (splitPath[i] == "New")
|
||||
continue;
|
||||
|
||||
if (mainCM)
|
||||
{
|
||||
childCM = convertMenu.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
}
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,24 +148,50 @@ namespace FlaxEditor.Windows
|
||||
|
||||
contextMenu.AddSeparator();
|
||||
|
||||
var spawnMenu = contextMenu.AddChildMenu("New");
|
||||
var newActorCm = spawnMenu.ContextMenu;
|
||||
for (int i = 0; i < SpawnActorsGroups.Length; i++)
|
||||
// go through each actor and add it to the context menu if it has the ActorContextMenu attribute
|
||||
foreach (var actorType in Editor.CodeEditing.Actors.Get())
|
||||
{
|
||||
var group = SpawnActorsGroups[i];
|
||||
if (actorType.IsAbstract || !actorType.HasAttribute(typeof(ActorContextMenuAttribute), true))
|
||||
continue;
|
||||
|
||||
if (group.Types.Length == 1)
|
||||
ActorContextMenuAttribute attribute = null;
|
||||
foreach (var actorAttribute in actorType.GetAttributes(true))
|
||||
{
|
||||
var type = group.Types[0].Value;
|
||||
newActorCm.AddButton(group.Types[0].Key, () => Spawn(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupCm = newActorCm.AddChildMenu(group.Name).ContextMenu;
|
||||
for (int j = 0; j < group.Types.Length; j++)
|
||||
if (actorAttribute is ActorContextMenuAttribute actorContextMenuAttribute)
|
||||
{
|
||||
var type = group.Types[j].Value;
|
||||
groupCm.AddButton(group.Types[j].Key, () => Spawn(type));
|
||||
attribute = actorContextMenuAttribute;
|
||||
}
|
||||
}
|
||||
var splitPath = attribute?.Path.Split('/');
|
||||
ContextMenuChildMenu childCM = null;
|
||||
bool mainCM = true;
|
||||
for (int i = 0; i < splitPath?.Length; i++)
|
||||
{
|
||||
if (i == splitPath.Length - 1)
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
contextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type));
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM?.ContextMenu.AddButton(splitPath[i].Trim(), () => Spawn(actorType.Type));
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mainCM)
|
||||
{
|
||||
childCM = contextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
mainCM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childCM = childCM?.ContextMenu.GetOrAddChildMenu(splitPath[i].Trim());
|
||||
}
|
||||
childCM.ContextMenu.AutoSort = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,113 +22,6 @@ namespace FlaxEditor.Windows
|
||||
/// <seealso cref="FlaxEditor.Windows.SceneEditorWindow" />
|
||||
public partial class SceneTreeWindow : SceneEditorWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// The spawnable actors group.
|
||||
/// </summary>
|
||||
public struct ActorsGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// The group name.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The types to spawn (name and type).
|
||||
/// </summary>
|
||||
public KeyValuePair<string, Type>[] Types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Spawnable actors (groups with single entry are inlined without a child menu)
|
||||
/// </summary>
|
||||
public static readonly ActorsGroup[] SpawnActorsGroups =
|
||||
{
|
||||
new ActorsGroup
|
||||
{
|
||||
Types = new[] { new KeyValuePair<string, Type>("Actor", typeof(EmptyActor)) }
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Types = new[] { new KeyValuePair<string, Type>("Model", typeof(StaticModel)) }
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Types = new[] { new KeyValuePair<string, Type>("Camera", typeof(Camera)) }
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Name = "Lights",
|
||||
Types = new[]
|
||||
{
|
||||
new KeyValuePair<string, Type>("Directional Light", typeof(DirectionalLight)),
|
||||
new KeyValuePair<string, Type>("Point Light", typeof(PointLight)),
|
||||
new KeyValuePair<string, Type>("Spot Light", typeof(SpotLight)),
|
||||
new KeyValuePair<string, Type>("Sky Light", typeof(SkyLight)),
|
||||
}
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Name = "Visuals",
|
||||
Types = new[]
|
||||
{
|
||||
new KeyValuePair<string, Type>("Environment Probe", typeof(EnvironmentProbe)),
|
||||
new KeyValuePair<string, Type>("Sky", typeof(Sky)),
|
||||
new KeyValuePair<string, Type>("Skybox", typeof(Skybox)),
|
||||
new KeyValuePair<string, Type>("Exponential Height Fog", typeof(ExponentialHeightFog)),
|
||||
new KeyValuePair<string, Type>("PostFx Volume", typeof(PostFxVolume)),
|
||||
new KeyValuePair<string, Type>("Decal", typeof(Decal)),
|
||||
new KeyValuePair<string, Type>("Particle Effect", typeof(ParticleEffect)),
|
||||
}
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Name = "Physics",
|
||||
Types = new[]
|
||||
{
|
||||
new KeyValuePair<string, Type>("Rigid Body", typeof(RigidBody)),
|
||||
new KeyValuePair<string, Type>("Character Controller", typeof(CharacterController)),
|
||||
new KeyValuePair<string, Type>("Box Collider", typeof(BoxCollider)),
|
||||
new KeyValuePair<string, Type>("Sphere Collider", typeof(SphereCollider)),
|
||||
new KeyValuePair<string, Type>("Capsule Collider", typeof(CapsuleCollider)),
|
||||
new KeyValuePair<string, Type>("Mesh Collider", typeof(MeshCollider)),
|
||||
new KeyValuePair<string, Type>("Fixed Joint", typeof(FixedJoint)),
|
||||
new KeyValuePair<string, Type>("Distance Joint", typeof(DistanceJoint)),
|
||||
new KeyValuePair<string, Type>("Slider Joint", typeof(SliderJoint)),
|
||||
new KeyValuePair<string, Type>("Spherical Joint", typeof(SphericalJoint)),
|
||||
new KeyValuePair<string, Type>("Hinge Joint", typeof(HingeJoint)),
|
||||
new KeyValuePair<string, Type>("D6 Joint", typeof(D6Joint)),
|
||||
}
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Name = "Other",
|
||||
Types = new[]
|
||||
{
|
||||
new KeyValuePair<string, Type>("Animated Model", typeof(AnimatedModel)),
|
||||
new KeyValuePair<string, Type>("Bone Socket", typeof(BoneSocket)),
|
||||
new KeyValuePair<string, Type>("CSG Box Brush", typeof(BoxBrush)),
|
||||
new KeyValuePair<string, Type>("Audio Source", typeof(AudioSource)),
|
||||
new KeyValuePair<string, Type>("Audio Listener", typeof(AudioListener)),
|
||||
new KeyValuePair<string, Type>("Scene Animation", typeof(SceneAnimationPlayer)),
|
||||
new KeyValuePair<string, Type>("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)),
|
||||
new KeyValuePair<string, Type>("Nav Link", typeof(NavLink)),
|
||||
new KeyValuePair<string, Type>("Nav Modifier Volume", typeof(NavModifierVolume)),
|
||||
new KeyValuePair<string, Type>("Spline", typeof(Spline)),
|
||||
}
|
||||
},
|
||||
new ActorsGroup
|
||||
{
|
||||
Name = "GUI",
|
||||
Types = new[]
|
||||
{
|
||||
new KeyValuePair<string, Type>("UI Control", typeof(UIControl)),
|
||||
new KeyValuePair<string, Type>("UI Canvas", typeof(UICanvas)),
|
||||
new KeyValuePair<string, Type>("Text Render", typeof(TextRender)),
|
||||
new KeyValuePair<string, Type>("Sprite Render", typeof(SpriteRender)),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
private TextBox _searchBox;
|
||||
private Tree _tree;
|
||||
private Panel _sceneTreePanel;
|
||||
@@ -249,7 +142,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (selection.Count != 0)
|
||||
Editor.SceneEditing.Select(actor);
|
||||
actor.TreeNode.StartRenaming();
|
||||
actor.TreeNode.StartRenaming(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +335,15 @@ namespace FlaxEditor.Windows
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buttons == MouseButton.Left)
|
||||
{
|
||||
if (Editor.StateMachine.CurrentState.CanEditScene)
|
||||
{
|
||||
Editor.SceneEditing.Deselect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user