Untitled
unknown
plain_text
10 months ago
20 kB
13
Indexable
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Visualization.Tools.UIAssets.TextureData;
using Visualization.Tools.UIAssets.Panels;
namespace Visualization.Tools.UIAssets.Viewport
{
public class ViewportManager : MonoBehaviour
{
private enum EditMode { HORIZONTAL, VERTICAL, CORNER, MAXIMIZED, NONE };
[SerializeField] private Camera _mainCamera;
[SerializeField] private ViewportLayout _currentLayout;
public ViewportLayout CurrentLayout
{
set { _currentLayout = value; PopulateViewports(_currentLayout); }
}
private EditMode _editMode = EditMode.NONE;
[SerializeField] private bool _canEditBorders = true;
[SerializeField] private float _screenBorderThreshold = 10f;
private Vector2 _viewportBorderThreshold;
[SerializeField] private List<Viewport> _viewports = new List<Viewport>();
public List<Viewport> Viewports => _viewports;
private const int VIEWPORT_SHOW_DISPLAY = 0;
private const int VIEWPORT_HIDE_DISPLAY = 7;
private const int SHOW_DISPLAY = 0;
[SerializeField] private CursorTextureData _sideBorderCursorTextureData;
[SerializeField] private CursorTextureData _topBottomBorderCursorTextureData;
[SerializeField] private CursorTextureData _cornerCursorTextureData;
[SerializeField] private TextureData.TextureData _clockWiseTextureData;
[SerializeField] private TextureData.TextureData _counterClockwiseTextureData;
[SerializeField] private TextureData.TextureData _maximizeWindowTextureData;
[SerializeField] private TextureData.TextureData _minimizeWindowTextureData;
private Vector3 _currentMousePosition;
private Vector3 _lastMousePosition;
private Vector3 _mousePositionDelta;
private Vector2 _viewportPosition;
private Viewport _activeViewport;
private Viewport _lastFocused;
private bool _isMaximized;
private Rect? _preMaximizedRect;
private float? _preCameraPriority;
private bool _nearTopBorder, _nearBottomBorder, _nearRightBorder, _nearLeftBorder = false;
private bool _shiftPressed = false;
private bool _spacePressed = false;
private bool _leftControlPressed = false;
private bool _tabPressed = false;
private readonly Dictionary<Camera, float> _defaultDepths = new();
private void Setup()
{
_sideBorderCursorTextureData ??= Resources.Load<CursorTextureData>("TextureData/LeftRightBorderCursor");
_topBottomBorderCursorTextureData ??= Resources.Load<CursorTextureData>("TextureData/TopBottomBorderCursor");
_cornerCursorTextureData ??= Resources.Load<CursorTextureData>("TextureData/CornerCursor");
_clockWiseTextureData ??= Resources.Load<TextureData.TextureData>("TextureData/ClockwiseScreen");
_counterClockwiseTextureData ??= Resources.Load<TextureData.TextureData>("TextureData/CounterClockwiseScreen");
_maximizeWindowTextureData ??= Resources.Load<TextureData.TextureData>("TextureData/MaximizeWindowTextureData");
_minimizeWindowTextureData ??= Resources.Load<TextureData.TextureData>("TextureData/MinimizeWindowTextureData");
}
private void Reset() { Setup(); }
private void Awake() { Setup(); }
private void Start()
{
var sceneViewportIDs = FindObjectsOfType<ViewportID>();
foreach (var sceneViewportID in sceneViewportIDs) HideViewport(sceneViewportID);
PopulateViewports();
foreach (var v in _viewports)
{
var cam = v.ViewportID?.ViewportCamera;
if (!cam) continue;
if (!_defaultDepths.ContainsKey(cam)) _defaultDepths[cam] = cam.depth;
if (!cam.TryGetComponent(out PhysicsRaycaster _))
cam.gameObject.AddComponent<PhysicsRaycaster>();
}
}
private void Update()
{
_currentMousePosition = Input.mousePosition;
_mousePositionDelta = _currentMousePosition - _lastMousePosition;
if (!_isMaximized)
{
var top = GetTopmostViewportUnderPointer();
if (top != _lastFocused && top != null)
{
EnableOnly(top);
_lastFocused = top;
}
}
PerformEditModeAction();
_lastMousePosition = _currentMousePosition;
}
public void Subscribe(Viewport viewport) { _viewports.Add(viewport); }
public void Unsubscribe(Viewport viewport) { _viewports.Remove(viewport); }
private void PerformEditModeAction()
{
switch (_editMode)
{
case EditMode.HORIZONTAL:
if (_currentMousePosition.x < 0 || _currentMousePosition.x > Screen.width) break;
if (_nearLeftBorder) ShiftViewportBorderByPixel(_activeViewport, (_mousePositionDelta.x, 0, 0, 0));
else if (_nearRightBorder) ShiftViewportBorderByPixel(_activeViewport, (0, _mousePositionDelta.x, 0, 0));
break;
case EditMode.VERTICAL:
if (_currentMousePosition.y < 0 || _currentMousePosition.y > Screen.height) break;
if (_nearTopBorder) ShiftViewportBorderByPixel(_activeViewport, (0, 0, _mousePositionDelta.y, 0));
if (_nearBottomBorder) ShiftViewportBorderByPixel(_activeViewport, (0, 0, 0, _mousePositionDelta.y));
break;
case EditMode.MAXIMIZED:
CheckKeyEvents();
break;
case EditMode.NONE:
if (HasMouseMoved() && !_isMaximized) UpdateManagerDataOnViewport();
CheckKeyEvents();
CaptureEditModeChange();
break;
}
if (Input.GetKeyUp(KeyCode.Mouse0)) _editMode = EditMode.NONE;
}
private void UpdateManagerDataOnViewport()
{
_nearLeftBorder = _nearRightBorder = _nearBottomBorder = _nearTopBorder = false;
foreach (Viewport viewport in _viewports)
{
if (!IsPointerInViewport(_currentMousePosition, viewport)) continue;
_activeViewport = viewport;
_viewportPosition = viewport.ScreenToCustomViewport(_currentMousePosition);
_viewportBorderThreshold = new Vector2(
_screenBorderThreshold / viewport.PixelWidth,
_screenBorderThreshold / viewport.PixelHeight);
_nearLeftBorder = _viewportPosition.x < _viewportBorderThreshold.x;
_nearRightBorder = _viewportPosition.x > 1 - _viewportBorderThreshold.x;
_nearBottomBorder = _viewportPosition.y < _viewportBorderThreshold.y;
_nearTopBorder = _viewportPosition.y > 1 - _viewportBorderThreshold.y;
break;
}
}
private void CaptureEditModeChange()
{
if (!_canEditBorders) return;
if (IsNearCorner(_viewportPosition))
{
Cursor.SetCursor(_cornerCursorTextureData.Texture, _cornerCursorTextureData.Hotspot, CursorMode.Auto);
if (Input.GetKeyDown(KeyCode.Mouse0)) _editMode = EditMode.CORNER;
}
else if (_nearRightBorder || _nearLeftBorder)
{
Cursor.SetCursor(_sideBorderCursorTextureData.Texture, _sideBorderCursorTextureData.Hotspot, CursorMode.Auto);
if (Input.GetKeyDown(KeyCode.Mouse0)) _editMode = EditMode.HORIZONTAL;
}
else if (_nearTopBorder || _nearBottomBorder)
{
Cursor.SetCursor(_topBottomBorderCursorTextureData.Texture, _topBottomBorderCursorTextureData.Hotspot, CursorMode.Auto);
if (Input.GetKeyDown(KeyCode.Mouse0)) _editMode = EditMode.VERTICAL;
}
else
{
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
}
if (Input.GetKeyUp(KeyCode.Mouse0)) _editMode = EditMode.NONE;
}
private void CheckKeyEvents()
{
if (Input.GetKeyDown(KeyCode.LeftShift)) _shiftPressed = true;
if (Input.GetKeyUp(KeyCode.LeftShift)) _shiftPressed = false;
if (Input.GetKeyDown(KeyCode.Space)) _spacePressed = true;
if (Input.GetKeyUp(KeyCode.Space)) _spacePressed = false;
if (Input.GetKeyDown(KeyCode.LeftControl)) _leftControlPressed = true;
if (Input.GetKeyUp(KeyCode.LeftControl)) _leftControlPressed = false;
if (Input.GetKeyDown(KeyCode.Tab)) _tabPressed = true;
if (Input.GetKeyUp(KeyCode.Tab)) _tabPressed = false;
if (_tabPressed)
{
if (!_isMaximized)
{
if (_shiftPressed) ShiftViewportsBackward();
else ShiftViewportsForward();
_tabPressed = false;
}
return;
}
if (_spacePressed && _leftControlPressed)
{
ToggleMaximizedView();
_spacePressed = false;
return;
}
}
private void ShiftViewports(bool direction)
{
var tempList = _viewports.Where(v => v.ViewportID.ViewportType.Type != "UI").ToList();
tempList = direction ? tempList.AsEnumerable().Reverse().ToList() : tempList;
var tempID = tempList[0].ViewportID;
for (int i = 0; i < tempList.Count - 1; i++)
tempList[i].ViewportID = tempList[i + 1].ViewportID;
tempList[^1].ViewportID = tempID;
}
public void ShiftViewportsForward()
{
var imagePopup = FindObjectOfType<ImagePopup>();
if (imagePopup != null) imagePopup.ShowImage(_clockWiseTextureData.Texture, 0.5f, 0.5f);
ShiftViewports(true);
}
public void ShiftViewportsBackward()
{
var imagePopup = FindObjectOfType<ImagePopup>();
if (imagePopup != null) imagePopup.ShowImage(_counterClockwiseTextureData.Texture, 0.5f, 0.5f);
ShiftViewports(false);
}
public void ToggleMaximizedView()
{
_isMaximized = !_isMaximized;
SetMaximizedView(_isMaximized);
}
private void SetMaximizedView(bool isMaximized)
{
if (isMaximized)
{
if (_activeViewport == null)
{
_activeViewport = GetTopmostViewportUnderPointer() ?? _viewports.FirstOrDefault();
if (_activeViewport == null) return;
}
_preMaximizedRect = new Rect(_activeViewport.ViewportRect);
_preCameraPriority = _activeViewport.ViewportCamera.depth;
_activeViewport.ViewportRect = new Rect(0, 0, 1, 1);
_activeViewport.ViewportCamera.depth = 999;
EnableOnly(_activeViewport);
var imagePopup = FindObjectOfType<ImagePopup>();
if (imagePopup != null) imagePopup.ShowImage(_maximizeWindowTextureData.Texture, 0.5f, 0.5f);
_editMode = EditMode.MAXIMIZED;
_isMaximized = true;
}
else
{
if (_preMaximizedRect == null || _preCameraPriority == null) return;
_activeViewport.ViewportRect = (Rect)_preMaximizedRect;
_activeViewport.ViewportCamera.depth = (float)_preCameraPriority;
_preMaximizedRect = null;
_preCameraPriority = null;
RestoreAll();
var img = FindObjectOfType<ImagePopup>();
if (img != null) img.ShowImage(_minimizeWindowTextureData.Texture, 0.5f, 0.5f);
_editMode = EditMode.NONE;
_isMaximized = false;
_lastFocused = null;
}
}
private void ShiftViewportBorderByPixel(
Viewport viewport,
(float leftPixelDelta, float rightPixelDelta, float topPixelDelta, float bottomPixelDelta) pixelDelta)
{
(float leftNormalDelta, float rightNormalDelta, float topNormalDelta, float bottomNormalDelta) normalDelta =
(pixelDelta.leftPixelDelta / Screen.width,
pixelDelta.rightPixelDelta / Screen.width,
pixelDelta.topPixelDelta / Screen.height,
pixelDelta.bottomPixelDelta / Screen.height);
ShiftViewportBorderByNormal(viewport, normalDelta);
}
private void ShiftViewportBorderByNormal(
Viewport viewport,
(float leftNormalDelta, float rightNormalDelta, float topNormalDelta, float bottomNormalDelta) normalDelta)
{
viewport.ViewportRect = new Rect(
viewport.ViewportRect.x + normalDelta.leftNormalDelta,
viewport.ViewportRect.y + normalDelta.bottomNormalDelta,
viewport.ViewportRect.width + normalDelta.rightNormalDelta - normalDelta.leftNormalDelta,
viewport.ViewportRect.height + normalDelta.topNormalDelta - normalDelta.bottomNormalDelta);
}
public void PopulateViewports(ViewportLayout viewportLayout)
{
HideViewports();
_viewports.Clear();
foreach (var layout in viewportLayout.Layouts)
{
ViewportID viewportID = FindObjectsOfType<ViewportID>()
.FirstOrDefault(id => layout.ID.Equals(id.ID));
if (viewportID == null)
{
Debug.LogError("Unable to find ID listed in layout.");
return;
}
new Viewport(this, viewportID, layout.name, layout.ViewportRect, viewportLayout.Padding);
ShowViewport(viewportID);
}
}
private void ShowViewport(ViewportID viewportID)
{
if (viewportID == null) return;
viewportID.ViewportCamera.targetDisplay = VIEWPORT_SHOW_DISPLAY;
}
private void ShowViewport(Viewport viewport) => ShowViewport(viewport.ViewportID);
private void ShowViewports(List<Viewport> viewports)
{
if (viewports == null) return;
foreach (Viewport viewport in viewports) ShowViewport(viewport);
}
private void ShowViewports() => ShowViewports(_viewports);
private void HideViewport(ViewportID viewportID)
{
if (viewportID == null) return;
viewportID.ViewportCamera.targetDisplay = VIEWPORT_HIDE_DISPLAY;
}
private void HideViewport(Viewport viewport) => HideViewport(viewport.ViewportID);
private void HideViewports(List<Viewport> viewports)
{
if (viewports == null) return;
foreach (Viewport viewport in viewports) HideViewport(viewport);
}
private void HideViewports() => HideViewports(_viewports);
public void PopulateViewports() => PopulateViewports(_currentLayout);
public void UpdateViewportLayout()
{
if (_currentLayout == null)
{
Debug.LogError("No Viewport Layout assigned.");
return;
}
_currentLayout.UpdateLayoutFromViewports(_viewports);
}
private void PrintViewportData(Viewport viewport)
{
Debug.Log($"{viewport.name} viewport point: {_viewportPosition}");
Debug.Log("viewport pixelRect: " + viewport.ViewportRect);
Debug.Log($"viewport pixelposition: {new Vector2(viewport.PixelWidth * _viewportPosition.x, viewport.PixelHeight * _viewportPosition.y)}");
}
private bool IsNearCorner(Vector2 viewportPosition)
{
return Vector2.Distance(new Vector2(0, 0), viewportPosition) < _viewportBorderThreshold.magnitude ||
Vector2.Distance(new Vector2(1, 0), viewportPosition) < _viewportBorderThreshold.magnitude ||
Vector2.Distance(new Vector2(0, 1), viewportPosition) < _viewportBorderThreshold.magnitude ||
Vector2.Distance(new Vector2(1, 1), viewportPosition) < _viewportBorderThreshold.magnitude;
}
private bool HasMouseMoved() => _lastMousePosition != Input.mousePosition;
public bool IsPointerInViewport(Vector2 pointerPosition, Viewport viewport)
{
var viewportRegionNormals = viewport.ScreenToCustomViewport(pointerPosition);
return viewportRegionNormals.x >= 0 && viewportRegionNormals.x <= 1 &&
viewportRegionNormals.y >= 0 && viewportRegionNormals.y <= 1;
}
private bool IsPointerOverUI()
{
var es = EventSystem.current;
if (es == null) return false;
var data = new PointerEventData(es) { position = Input.mousePosition };
var results = new List<RaycastResult>();
es.RaycastAll(data, results);
return results.Any(r =>
{
var c = r.gameObject.GetComponentInParent<Canvas>();
return c != null && c.renderMode != RenderMode.WorldSpace;
});
}
public List<Viewport> GetViewportsUnderPointer()
{
if (IsPointerOverUI())
return new List<Viewport>();
return _viewports
.Where(v => v.ViewportID.ViewportCamera.targetDisplay == SHOW_DISPLAY
&& IsPointerInViewport(Input.mousePosition, v))
.ToList();
}
public Viewport GetTopmostViewportUnderPointer()
{
var hits = GetViewportsUnderPointer();
if (hits.Count == 0) return null;
return hits.OrderBy(v => v.ViewportID.ViewportCamera.depth).Last();
}
public bool CanInteract(Viewport vp) => vp != null && vp == GetTopmostViewportUnderPointer();
public bool CanInteract(Camera cam)
{
var top = GetTopmostViewportUnderPointer();
return top != null && top.ViewportID.ViewportCamera == cam;
}
private void EnableOnly(Viewport top)
{
foreach (var v in _viewports)
{
bool on = (v == top);
var cam = v.ViewportID?.ViewportCamera;
if (cam)
{
var pr = cam.GetComponent<PhysicsRaycaster>();
if (pr) pr.enabled = on;
}
foreach (var gr in v.ViewportID.GetComponentsInChildren<GraphicRaycaster>(true))
gr.enabled = on;
}
}
private void RestoreAll()
{
foreach (var v in _viewports)
{
var cam = v.ViewportID?.ViewportCamera;
if (cam)
{
var pr = cam.GetComponent<PhysicsRaycaster>();
if (pr) pr.enabled = true;
}
foreach (var gr in v.ViewportID.GetComponentsInChildren<GraphicRaycaster>(true))
gr.enabled = true;
}
}
}
}
Editor is loading...
Leave a Comment