fish-station/Content.Client/_Sunrise/CartridgeLoader/Cartridges/PhotoUiFragment.xaml.cs

384 lines
12 KiB
C#

using System.Linq;
using System.Numerics;
using Content.Client.Viewport;
using Content.Shared._Sunrise.CartridgeLoader.Cartridges;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Shared.Timing;
namespace Content.Client._Sunrise.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class PhotoUiFragment : BoxContainer
{
public event Action? OnCapturePhoto;
public event Action<string>? OnDeletePhoto;
public event Action<string, string?, string?>? OnSendPhotoToMessenger;
public event Action? OnRequestGallery;
public event Action<bool>? OnToggleFlash;
[Dependency] private readonly NetTexturesManager _netTexturesManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private Dictionary<string, PhotoItemControl> _photoControls = new();
private string? _currentViewPhotoId;
private EntityUid? _ownerEntity;
private bool _serverCameraReady = true;
private readonly Robust.Shared.Graphics.Eye _previewEye = new();
public PhotoUiFragment()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
Orientation = LayoutOrientation.Vertical;
HorizontalExpand = true;
VerticalExpand = true;
CaptureButton.OnPressed += _ => OnCapturePhoto?.Invoke();
BackFromImageParams.OnPressed += _ => CloseFullPhoto();
DeleteImageBtn.OnPressed += _ =>
{
if (_currentViewPhotoId != null)
{
OnDeletePhoto?.Invoke(_currentViewPhotoId);
CloseFullPhoto();
}
};
FlashCheckBox.OnToggled += args => OnToggleFlash?.Invoke(args.Pressed);
ToggleOverlayButton.OnPressed += _ => ToggleCaptureAreaOverlay();
ToggleOverlayButton.Pressed = _entityManager.System<PhotoOverlaySystem>().OverlayEnabled;
CameraTabBtn.OnPressed += _ => SwitchTab(true);
GalleryTabBtn.OnPressed += _ => SwitchTab(false);
SwitchTab(true);
var system = _entityManager.System<PhotoCartridgeClientSystem>();
ZoomSlider.Value = (system.CaptureDistance - system.MinCaptureDistance) / 8.0f;
ZoomSlider.OnValueChanged += args =>
{
system.CaptureDistance = system.MinCaptureDistance + args.Value * 8.0f;
};
SetupCameraPreview();
}
public void SetOwner(EntityUid owner)
{
_ownerEntity = owner;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_entityManager.System<PhotoOverlaySystem>().SetOverlayEnabled(false);
}
}
private void SwitchTab(bool showCamera)
{
if (ImageViewOverlay.Visible)
ImageViewOverlay.Visible = false;
CameraTabBtn.Pressed = showCamera;
GalleryTabBtn.Pressed = !showCamera;
CameraView.Visible = showCamera;
GalleryView.Visible = !showCamera;
if (!showCamera)
{
OnRequestGallery?.Invoke();
}
}
private void CloseFullPhoto()
{
ImageViewOverlay.Visible = false;
GalleryView.Visible = true;
CameraTabBtn.Pressed = false;
GalleryTabBtn.Pressed = true;
}
private void OpenFullPhoto(string photoId, PhotoMetadata metadata, Texture? texture)
{
_currentViewPhotoId = photoId;
CameraView.Visible = false;
GalleryView.Visible = false;
ImageViewOverlay.Visible = true;
FullImageRect.Texture = texture;
FullImageTimestamp.Text = FormatTimestamp(metadata.Timestamp);
}
private string FormatTimestamp(TimeSpan timestamp)
{
var age = _gameTiming.CurTime - timestamp;
var realTimeOfCapture = DateTime.UtcNow - age;
var pdaTime = realTimeOfCapture + TimeSpan.FromHours(3);
return pdaTime.ToString("HH:mm:ss");
}
private void SetupCameraPreview()
{
if (CameraPreview != null)
{
CameraPreview.Eye = _previewEye;
CameraPreview.ViewportSize = new Vector2i(256, 256);
CameraPreview.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
CameraPreview.FixedRenderScale = 1;
_previewEye.Zoom = _eyeManager.CurrentEye.Zoom * 0.4f;
}
}
/// <summary>
/// Переключает видимость оверлея области съемки
/// </summary>
private void ToggleCaptureAreaOverlay()
{
var system = _entityManager.System<PhotoOverlaySystem>();
system.SetOverlayEnabled(!system.OverlayEnabled, _ownerEntity);
ToggleOverlayButton.Pressed = system.OverlayEnabled;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
var photoSystem = _entityManager.System<PhotoCartridgeClientSystem>();
CaptureButton.Disabled = !_serverCameraReady || !photoSystem.CameraReady;
if (!CameraView.Visible || CameraPreview == null)
return;
if (_playerManager.LocalPlayer?.ControlledEntity is { } player)
{
var source = _ownerEntity ?? player;
var xform = _entityManager.GetComponent<TransformComponent>(source);
var sourcePos = xform.MapPosition;
var targetPos = photoSystem.GetClampedCapturePosition(source, photoSystem.CaptureDistance);
_previewEye.Position = sourcePos.Offset(targetPos - sourcePos.Position);
_previewEye.Rotation = _eyeManager.CurrentEye.Rotation;
_previewEye.Zoom = new Vector2(3f / 8f, 3f / 8f);
}
}
public void UpdateState(PhotoUiState state)
{
if (!string.IsNullOrEmpty(state.ErrorMessage))
{
ErrorMessageLabel.Text = state.ErrorMessage;
ErrorMessageLabel.Visible = true;
}
else
{
ErrorMessageLabel.Visible = false;
}
_serverCameraReady = state.CameraReady;
FlashCheckBox.Pressed = state.FlashEnabled;
PhotoCountLabel.Text = Loc.GetString("photo-cartridge-photos-count", ("count", state.Photos.Count), ("max", 50));
UpdateGallery(state.Photos);
}
private void UpdateGallery(Dictionary<string, PhotoMetadata> photos)
{
var toRemove = _photoControls.Keys.Except(photos.Keys).ToList();
foreach (var photoId in toRemove)
{
if (_photoControls.TryGetValue(photoId, out var control))
{
control.Dispose();
_photoControls.Remove(photoId);
}
}
var sortedPhotos = photos.OrderByDescending(p => p.Value.Timestamp);
foreach (var (photoId, metadata) in sortedPhotos)
{
if (!_photoControls.ContainsKey(photoId))
{
var control = new PhotoItemControl(photoId, metadata, _netTexturesManager, _resourceCache, _gameTiming);
control.OnPressed += _ => OpenFullPhoto(photoId, metadata, control.GetTexture());
GalleryContainer.AddChild(control);
_photoControls[photoId] = control;
}
else
{
_photoControls[photoId].UpdateMetadata(metadata);
_photoControls[photoId].SetPositionInParent(0);
}
}
int index = 0;
foreach (var (photoId, _) in sortedPhotos)
{
if (_photoControls.TryGetValue(photoId, out var control))
{
control.SetPositionInParent(index++);
}
}
}
}
/// <summary>
/// Элемент управления для отображения одной фотографии в галерее
/// </summary>
public sealed class PhotoItemControl : ContainerButton
{
private readonly string _photoId;
private PhotoMetadata _metadata;
private readonly NetTexturesManager _netTexturesManager;
private readonly IResourceCache _resourceCache;
private readonly IGameTiming _gameTiming;
private TextureRect _previewRect;
private Label _timestampLabel;
public PhotoItemControl(string photoId, PhotoMetadata metadata, NetTexturesManager netTexturesManager, IResourceCache resourceCache, IGameTiming gameTiming)
{
_photoId = photoId;
_metadata = metadata;
_netTexturesManager = netTexturesManager;
_resourceCache = resourceCache;
_gameTiming = gameTiming;
var vBox = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
HorizontalExpand = true,
VerticalExpand = true,
Margin = new Thickness(2)
};
AddChild(vBox);
_previewRect = new TextureRect
{
MinHeight = 84,
HorizontalExpand = true,
VerticalExpand = true,
Stretch = TextureRect.StretchMode.KeepAspectCentered,
Margin = new Thickness(0, 0, 0, 2)
};
var borderPanel = new PanelContainer
{
HorizontalExpand = true,
VerticalExpand = true,
PanelOverride = new StyleBoxFlat
{
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderThickness = new Thickness(2)
}
};
vBox.AddChild(borderPanel);
borderPanel.AddChild(_previewRect);
OnMouseEntered += _ =>
{
if (borderPanel.PanelOverride is StyleBoxFlat style)
{
style.BorderColor = Color.White.WithAlpha(0.6f);
}
};
OnMouseExited += _ =>
{
if (borderPanel.PanelOverride is StyleBoxFlat style)
{
style.BorderColor = Color.Transparent;
}
};
_timestampLabel = new Label
{
Text = FormatTimestamp(metadata.Timestamp),
Align = Label.AlignMode.Center,
StyleClasses = { "LabelSubText" },
ClipText = true
};
vBox.AddChild(_timestampLabel);
LoadImage();
}
public Texture? GetTexture()
{
return _previewRect.Texture;
}
public void UpdateMetadata(PhotoMetadata metadata)
{
_metadata = metadata;
_timestampLabel.Text = FormatTimestamp(metadata.Timestamp);
}
private void LoadImage()
{
var isAvailable = _netTexturesManager.EnsureResource(_metadata.ImagePath);
if (isAvailable)
{
LoadImageTexture(_metadata.ImagePath);
}
else
{
_netTexturesManager.ResourceLoaded += OnResourceLoaded;
}
}
private void OnResourceLoaded(string resourcePath)
{
if (resourcePath == _metadata.ImagePath)
{
LoadImageTexture(resourcePath);
_netTexturesManager.ResourceLoaded -= OnResourceLoaded;
}
}
private void LoadImageTexture(string imagePath)
{
try
{
var uploadedPath = _netTexturesManager.GetUploadedPath(imagePath);
if (_resourceCache.TryGetResource<TextureResource>(uploadedPath, out var textureResource))
{
_previewRect.Texture = textureResource.Texture;
}
}
catch (Exception)
{
}
}
private string FormatTimestamp(TimeSpan timestamp)
{
var age = _gameTiming.CurTime - timestamp;
var realTimeOfCapture = DateTime.UtcNow - age;
var pdaTime = realTimeOfCapture + TimeSpan.FromHours(3);
return pdaTime.ToString("HH:mm:ss");
}
}