384 lines
12 KiB
C#
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");
|
|
}
|
|
}
|