Files
OldThink/Content.Client/_White/UserInterface/Radial/RadialContainer.xaml.cs

428 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Linq;
using System.Numerics;
using System.Text;
using Content.Client.Message;
using Content.Client.Resources;
using Robust.Client.Animations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Animations;
using Robust.Shared.Console;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Client._White.UserInterface.Radial;
public sealed class RadialContainerCommandTest : LocalizedCommands
{
public override string Command => "radialtest";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
string[] tips =
{
"Testovый туултип. Здесь можете расписать разную инфу о кнопке/действии",
"Из окна дуло. Штирлиц закрыл окно. Дуло исчезло.",
"Негры пидорасы Негры пидорасы Негры пидорасы Негры пидорасы"
};
var radial = new RadialContainer();
for (int i = 0; i < 8; i++)
{
var testButton = radial.AddButton("Action " + i, "/Textures/Interface/emotions.svg.192dpi.png");
testButton.Tooltip = tips[IoCManager.Resolve<IRobustRandom>().Next(0, 2)];
testButton.Controller.OnPressed += (_) => { Logger.Debug("Press gay"); };
}
radial.CloseButton.Controller.OnPressed += (_) =>
{
Logger.Debug("Close event for your own logic");
};
radial.OpenAttachedLocalPlayer();
}
}
[GenerateTypedNameReferences, Virtual]
public partial class RadialContainer : Control
{
private EntityUid? _attachedEntity;
private bool _isAttached = false;
private bool _isOpened = false;
private Vector2 _focusSize = new Vector2(64, 64);
private Vector2 _normalSize = new Vector2(50, 50);
private float _moveAniTime = 0.3f;
private float _focusAniTime = 0.25f;
private string _backgroundTexture = "/Textures/Interface/Default/SlotBackground.png";
private const int MaxButtons = 8;
public const string MoveAnimationKey = "move";
public const string InSizeAnimationKey = "insize";
public const string OutSizeAnimationKey = "outsize";
public Action? Closed;
public float FocusSize
{
get => _focusSize.Y;
set => _focusSize = new Vector2(value, value);
}
public float NormalSize
{
get => _normalSize.Y;
set => _normalSize = new Vector2(value, value);
}
public float MoveAnimationTime
{
get => _moveAniTime;
set => _moveAniTime = value;
}
public float FocusAnimationTime
{
get => _focusAniTime;
set => _focusAniTime = value;
}
public bool IsAction = true;
public float VerticalOffset = 0.0f;
public float DistanceAvaible = 10.0f;
public RadialContainer() : base()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void Open(Vector2 position)
{
AddToRoot();
LayoutContainer.SetPosition(this, position);
UpdateButtons();
}
public void OpenCentered()
{
AddToRoot();
if (Parent != null)
LayoutContainer.SetPosition(this, (Parent.Size/2) - (this.Size/2));
else
LayoutContainer.SetPosition(this, (UserInterfaceManager.MainViewport.Size/2) - (this.Size/2));
UpdateButtons();
}
public void OpenCenteredLeft() => OpenCenteredAt(new Vector2(0.37f, 0.5f));
public void OpenCenteredAt(Vector2 position)
{
AddToRoot();
if (Parent == null)
return;
LayoutContainer.SetPosition(this, (Parent.Size * position) - (this.Size/2));
UpdateButtons();
}
/// <summary>
/// Open on attached entity in the world.
/// </summary>
public void OpenAttached(EntityUid uid)
{
if (uid == EntityUid.Invalid)
return;
AddToRoot();
_attachedEntity = uid;
_isAttached = true;
UpdateButtons();
}
/// <summary>
/// Open on our (player) attached entity.
/// </summary>
public void OpenAttachedLocalPlayer()
{
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (localPlayer == null)
return;
AddToRoot();
_attachedEntity = localPlayer.ControlledEntity;
_isAttached = true;
UpdateButtons();
}
public void Close(bool canDispose = true)
{
Parent?.RemoveChild(this);
Visible = false;
_isOpened = false;
Closed?.Invoke();
if (canDispose)
Dispose();
}
public RadialButton AddButton(string action, string? texture = null)
{
var button = new RadialButton();
button.Content = action;
button.Controller.TextureNormal = IoCManager.Resolve<IResourceCache>().GetTexture(_backgroundTexture);
if (texture != null)
button.BackgroundTexture.Texture = IoCManager.Resolve<IResourceCache>().GetTexture(texture);
Layout.AddChild(button);
return button;
}
public RadialButton AddButton(string action, Texture? texture)
{
var button = new RadialButton();
button.Content = action;
button.Controller.TextureNormal = IoCManager.Resolve<IResourceCache>().GetTexture(_backgroundTexture);
if (texture != null)
button.BackgroundTexture.Texture = texture;
Layout.AddChild(button);
return button;
}
private void AddToRoot()
{
if (_isOpened)
return;
UserInterfaceManager.WindowRoot.AddChild(this);
_isOpened = !_isOpened;
}
private void UpdateButtons()
{
Visible = true;
var angleDegrees = 360/Layout.ChildCount;
var stepAngle = -angleDegrees + -90;
var distance = GetDistance();
foreach (var child in Layout.Children)
{
var button = (RadialButton)child;
button.ButtonSize = _normalSize;
stepAngle += angleDegrees;
var pos = GetPointFromPolar(stepAngle, distance);
PlayRadialAnimation(button, pos, MoveAnimationKey);
button.Controller.OnMouseEntered += (_) =>
{
PlaySizeAnimation(button, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
RichTextLabelExt.SetMarkup(ActionLabel, button.Content ?? string.Empty);
ActionLabel.Visible = IsAction;
};
button.Controller.OnMouseExited += (_) =>
{
PlaySizeAnimation(button, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
ActionLabel.Visible = false;
};
}
CloseButton.ButtonSize = _normalSize;
CloseButton.Controller.OnMouseEntered += (_) =>
{
PlaySizeAnimation(CloseButton, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
RichTextLabelExt.SetMarkup(ActionLabel, CloseButton.Content ?? string.Empty);
ActionLabel.Visible = true;
};
CloseButton.Controller.OnMouseExited += (_) =>
{
PlaySizeAnimation(CloseButton, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
ActionLabel.Visible = false;
};
CloseButton.Controller.OnPressed += (_) =>
{
Close();
};
}
private void PlayRadialAnimation(Control button, Vector2 pos, string playKey)
{
var anim = new Animation
{
Length = TimeSpan.FromMilliseconds(_moveAniTime * 1000),
AnimationTracks =
{
new AnimationTrackControlProperty
{
Property = nameof(RadialButton.Offset),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(new Vector2(0,0), 0f),
new AnimationTrackProperty.KeyFrame(pos, _moveAniTime)
}
}
}
};
if (!button.HasRunningAnimation(playKey))
button.PlayAnimation(anim, playKey);
}
private void PlaySizeAnimation(Control button, Vector2 size, string playKey, string? stopKey)
{
var anim = new Animation
{
Length = TimeSpan.FromMilliseconds(_focusAniTime * 1000),
AnimationTracks =
{
new AnimationTrackControlProperty
{
Property = nameof(RadialButton.ButtonSize),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(button.Size, 0f),
new AnimationTrackProperty.KeyFrame(size, _focusAniTime)
}
}
}
};
if (stopKey != null && button.HasRunningAnimation(stopKey))
button.StopAnimation(stopKey);
if (!button.HasRunningAnimation(playKey))
button.PlayAnimation(anim, playKey);
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
foreach (var child in Layout.Children)
{
var button = (RadialButton)child;
LayoutContainer.SetPosition(child, button.Offset - (button.Size/2));
}
LayoutContainer.SetPosition(CloseButton, CloseButton.Offset - (CloseButton.Size/2));
LayoutContainer.SetPosition(ActionLabel,
new Vector2(0 - (GetTextWidth(ActionLabel) / 4), GetDistance(4.5f) ));
}
protected override void FrameUpdate(FrameEventArgs args)
{
if (!_isAttached)
return;
base.FrameUpdate(args);
var entityManager = IoCManager.Resolve<IEntityManager>();
var eyeManager = IoCManager.Resolve<IEyeManager>();
if (entityManager.Deleted(_attachedEntity))
{
Timer.Spawn(0, Die);
return;
}
if (!entityManager.TryGetComponent<TransformComponent>(_attachedEntity, out var xform) || xform.MapID != eyeManager.CurrentMap)
{
return;
}
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (localPlayer == null)
return;
// Check distance beetween entities
if (entityManager.TryGetComponent<TransformComponent>(localPlayer.ControlledEntity, out var myxform))
{
var onePoint = xform.WorldPosition;
var twoPoint = myxform.WorldPosition;
var distance = (onePoint - twoPoint).Length();
if (DistanceAvaible < distance)
{
Timer.Spawn(0, Die);
return;
}
}
var offset = (-eyeManager.CurrentEye.Rotation).ToWorldVec() * -VerticalOffset;
var worldPos = xform.WorldPosition + offset;
var lowerCenter = eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - new Vector2(DesiredSize.X / 2, DesiredSize.Y / 2);
// Round to nearest 0.5
screenPos = (screenPos * 2).Rounded() / 2;
LayoutContainer.SetPosition(this, screenPos);
}
private int GetTextWidth(RichTextLabel text)
{
var font = GetFont(text);
var msg = text.GetMessage();
if (msg == null)
return 0;
var width = 0;
foreach (var t in msg)
{
var metrics = font.GetCharMetrics(new Rune(t), 1);
if (metrics != null)
width += metrics.Value.Width + metrics.Value.Advance;
}
return width;
}
private void Die()
{
if (Disposed)
return;
Close();
}
private Font GetFont(Control element)
{
if (element.TryGetStyleProperty<Font>("font", out var font))
{
return font;
}
return UserInterfaceManager.ThemeDefaults.DefaultFont;
}
private float GetDistance(float offset = 0.0f)
{
var distance = FocusSize * 1.2f;
if (Enumerable.Count<Control>(Layout.Children) <= MaxButtons)
return distance + offset;
for (var i = 0; i < (Enumerable.Count<Control>(Layout.Children) - MaxButtons); i++)
{
distance += (NormalSize/3);
}
return distance + offset;
}
private static Vector2 GetPointFromPolar(double angleDegrees, double distance)
{
var angleRadians = angleDegrees * (Math.PI / 180.0);
var x = distance * Math.Cos(angleRadians);
var y = distance * Math.Sin(angleRadians);
return new Vector2((int)Math.Round(x), (int)Math.Round(y));
}
}