Files
XUniWindowController/Runtime/Scripts/UniWindowController.cs

1249 lines
38 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.
/*
* UniWindowController.cs
*
* Author: Kirurobo http://twitter.com/kirurobo
* License: MIT
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
#if UNITY_EDITOR
using UnityEditor;
using System.Reflection;
using UnityEngine.Events;
using System.Linq;
#endif
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace Kirurobo
{
/// @cond DOXYGEN_SHOW_INTERNAL_CLASSES
/// <summary>
/// 使布尔属性可编辑
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class EditablePropertyAttribute : UnityEngine.PropertyAttribute
{
}
/// <summary>
/// 将属性设置为只读
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class ReadOnlyAttribute : UnityEngine.PropertyAttribute
{
}
/// @endcond
/// <summary>
/// Windows/Mac 统一窗口控制器
/// </summary>
public class UniWindowController : MonoBehaviour
{
/// <summary>
/// 与 UniWinCore.TransparentType 相同
/// </summary>
public enum TransparentType : int
{
None = 0,
Alpha = 1,
ColorKey = 2,
}
/// <summary>
/// 指定点击测试方法(即切换点击穿透)
/// </summary>
public enum HitTestType : int
{
None = 0,
Opacity = 1,
Raycast = 2,
}
/// <summary>
/// 标识 <see cref="OnStateChanged">OnStateChanged</see> 事件发生时的类型
/// </summary>
[Flags]
public enum WindowStateEventType : int
{
None = 0,
StyleChanged = 1,
Resized = 2,
// 以下规格可能会有变化
TopMostEnabled = 16 + 1 + 8,
TopMostDisabled = 16 + 1,
BottomMostEnabled = 32 + 1 + 8,
BottomMostDisabled = 32 + 1,
WallpaperModeEnabled = 64 + 1 + 8,
WallpaperModeDisabled = 64 + 1,
};
/// <summary>
/// 鼠标按键
/// </summary>
[Flags]
public enum MouseButton : int
{
None = 0,
Left = 1,
Right = 2,
Middle = 4,
}
/// <summary>
/// 修饰键
/// </summary>
[Flags]
public enum ModifierKey : int
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Command = 8,
}
/// <summary>
/// 获取当前 UniWindowController 实例
/// </summary>
public static UniWindowController current => _current ? _current : FindOrCreateInstance();
private static UniWindowController _current;
/// <summary>
/// 底层类
/// </summary>
private UniWinCore _uniWinCore = null;
/// <summary>
/// 此窗口是否接收鼠标事件
/// </summary>
public bool isClickThrough
{
get { return _isClickThrough; }
set { SetClickThrough(value); }
}
private bool _isClickThrough = false;
/// <summary>
/// 此窗口是否透明
/// </summary>
public bool isTransparent
{
get { return _isTransparent; }
set { SetTransparent(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时设置为透明")]
private bool _isTransparent = false;
/// <summary>
/// 窗口透明度0.0 到 1.0
/// </summary>
public float alphaValue
{
get { return _alphaValue; }
set { SetAlphaValue(value); }
}
[SerializeField, EditableProperty, Tooltip("窗口透明度"), Range(0f, 1f)]
private float _alphaValue = 1.0f;
/// <summary>
/// 此窗口是否置顶
/// </summary>
public bool isTopmost
{
get { return ((_uniWinCore == null) ? _isTopmost : _isTopmost = _uniWinCore.IsTopmost); }
set { SetTopmost(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时置顶")]
private bool _isTopmost = false;
/// <summary>
/// 此窗口是否置底
/// </summary>
public bool isBottommost
{
get { return ((_uniWinCore == null) ? _isBottommost : _isBottommost = _uniWinCore.IsBottommost); }
set { SetBottommost(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时置底")]
private bool _isBottommost = false;
/// <summary>
/// 此窗口是否最大化
/// </summary>
public bool isZoomed
{
get { return ((_uniWinCore == null) ? _isZoomed : _isZoomed = _uniWinCore.GetZoomed()); }
set { SetZoomed(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时最大化")]
private bool _isZoomed = false;
/// <summary>
/// 此窗口是否适配到显示器
/// </summary>
public bool shouldFitMonitor
{
get { return _shouldFitMonitor; }
set { FitToMonitor(value, _monitorToFit); }
}
[SerializeField, EditableProperty, Tooltip("选中后将窗口适配到显示器")]
private bool _shouldFitMonitor = false;
/// <summary>
/// 适配窗口的目标显示器索引0, 1, ...
/// </summary>
public int monitorToFit
{
get { return _monitorToFit; }
set { FitToMonitor(_shouldFitMonitor, value); }
}
private int _monitorToFit = 0;
/// <summary>
/// 启用/禁用接受文件拖放
/// </summary>
public bool allowDropFiles
{
get { return _allowDropFiles; }
set { SetAllowDrop(value); }
}
[SerializeField, EditableProperty, Tooltip("启用文件或文件夹拖放")]
private bool _allowDropFiles = false;
/// <summary>
/// 是否启用点击穿透自动判定
/// 如果禁用,可以手动修改 isClickThrough
/// </summary>
public bool isHitTestEnabled = true;
/// <summary>
/// 点击穿透自动判定的方法
/// </summary>
[Tooltip("选择方法")] public HitTestType hitTestType = HitTestType.Opacity;
/// <summary>
/// 点击穿透判定方法为不透明度时使用的阈值
/// 鼠标下方像素的 alpha 达到此值时判定为命中
/// </summary>
[Tooltip("点击测试类型为 Opacity 时可用"), RangeAttribute(0f, 1f)]
public float opacityThreshold = 0.1f;
/// <summary>
/// 点击穿透判定方法为 raycast 时的最远距离
/// </summary>
private float raycastMaxDepth = 100.0f;
/// <summary>
/// 启用后,窗口透明时会自动将相机背景改为单色黑色透明
/// </summary>
[Header("高级设置")] [Tooltip("窗口透明时更改相机背景")]
public bool autoSwitchCameraBackground = true;
/// <summary>
/// 启用后,启动时如果是全屏则会强制退出全屏
///
/// 用于即使启动对话框设置了全屏,也能切换为窗口模式
/// 仅在启动时生效
/// 在 Mac 上,即使强制退出全屏,似乎仍会变成另一个画面,效果不大
/// </summary>
[Tooltip("启动时强制窗口模式")] public bool forceWindowed = false;
/// <summary>
/// 相机实例
/// </summary>
[Tooltip("未设置时使用主相机")] public Camera currentCamera;
/// <summary>
/// 透明方式的指定
/// </summary>
[Header("仅限 Windows")] [Tooltip("选择透明方式。*仅 Windows 可用")]
public TransparentType transparentType = TransparentType.Alpha;
/// <summary>
/// 当透明类型为 ColorKey 时使用的键色
/// </summary>
[Tooltip("将在窗口下次变为透明时使用")] public Color32 keyColor = new Color32(0x01, 0x00, 0x01, 0x00);
/// <summary>
/// 在 macOS 上,是否允许将窗口放置在菜单栏上方
/// </summary>
public bool isFreePositioningEnabled
{
get
{
return ((_uniWinCore == null)
? _isFreePositioningEnabled
: _isFreePositioningEnabled = _uniWinCore.IsFreePositioningEnabled);
}
set { SetFreePositioning(value); }
}
[Header("仅限 macOS")] [Tooltip("禁用 constrainFrameRect() *仅 macOS 可用")] [SerializeField, EditableProperty]
private bool _isFreePositioningEnabled = false;
/// <summary>
/// 鼠标指针是否在不透明像素或物体上
/// </summary>
[Header("状态")] [SerializeField, ReadOnly, Tooltip("鼠标指针是否在不透明像素上?(只读)")]
private bool onObject = true;
/// <summary>
/// 鼠标指针下方的像素颜色。(只读)
/// </summary>
[SerializeField, ReadOnly, Tooltip("鼠标指针下方的像素颜色。(只读)")]
public Color pickedColor;
/// <summary>
/// 获取/设置窗口坐标
/// </summary>
public Vector2 windowPosition
{
get { return (_uniWinCore != null ? _uniWinCore.GetWindowPosition() : Vector2.zero); }
set { _uniWinCore?.SetWindowPosition(value); }
}
/// <summary>
/// 获取/设置窗口大小
/// </summary>
public Vector2 windowSize
{
get { return (_uniWinCore != null ? _uniWinCore.GetWindowSize() : Vector2.zero); }
set { _uniWinCore?.SetWindowSize(value); }
}
/// <summary>
/// 获取客户区域大小
/// </summary>
public Vector2 clientSize
{
get { return (_uniWinCore != null ? _uniWinCore.GetClientSize() : Vector2.zero); }
}
/// <summary>
/// 获取/设置鼠标光标坐标
/// </summary>
public Vector2 cursorPosition
{
get { return UniWinCore.GetCursorPosition(); }
set { UniWinCore.SetCursorPosition(value); }
}
/// <summary>
/// 初始状态的窗口位置和大小
/// </summary>
private Rect originalWindowRectangle;
// 存储相机原有背景,以便在窗口透明时替换为 alpha 为零的黑色
private CameraClearFlags originalCameraClearFlags;
private Color originalCameraBackground;
/// <summary>
/// 存储光标下方 1 像素颜色的纹理
/// </summary>
private Texture2D colorPickerTexture = null;
/// <summary>
/// Raycast 使用的鼠标事件信息
/// </summary>
private PointerEventData pointerEventData;
/// <summary>
/// Raycast 时的图层遮罩
/// </summary>
private int hitTestLayerMask;
/// <summary>
/// 窗口样式改变时发生
/// </summary>
public event OnStateChangedDelegate OnStateChanged;
public delegate void OnStateChangedDelegate(WindowStateEventType type);
public delegate void FilesDelegate(string[] files);
/// <summary>
/// 文件或文件夹被拖放后发生
/// </summary>
public event FilesDelegate OnDropFiles;
/// <summary>
/// 显示器设置或分辨率改变时发生
/// </summary>
public event OnMonitorChangedDelegate OnMonitorChanged;
public delegate void OnMonitorChangedDelegate();
// 用于初始化
void Awake()
{
// 用作单例。如果已有实例,则销毁自身
if (this != current)
{
Destroy(this.gameObject);
return;
}
else
{
_current = this;
}
// 强制退出全屏。在编辑器中不做任何操作
#if !UNITY_EDITOR
if (forceWindowed && Screen.fullScreen)
{
Screen.fullScreen = false;
}
#endif
if (!currentCamera)
{
// 寻找主相机
currentCamera = Camera.main;
// 如果主相机未找到,则使用 Find 查找
//if (!currentCamera)
//{
// currentCamera = GameObject.FindAnyObjectByType<Camera>();
//}
}
// 记录相机原始背景
if (currentCamera)
{
originalCameraClearFlags = currentCamera.clearFlags;
originalCameraBackground = currentCamera.backgroundColor;
}
// 鼠标事件信息
pointerEventData = new PointerEventData(EventSystem.current);
// 使用 Ignore Raycast 之外的图层作为有效遮罩
hitTestLayerMask = ~LayerMask.GetMask("Ignore Raycast");
// 准备用于提取鼠标下方像素颜色的纹理
colorPickerTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
// 创建窗口控制实例
_uniWinCore = new UniWinCore();
}
/// <summary>
/// 适配到指定显示器
/// </summary>
private void UpdateMonitorFitting()
{
if (!_shouldFitMonitor) return;
int monitors = UniWinCore.GetMonitorCount();
int targetMonitorIndex = _monitorToFit;
if (targetMonitorIndex < 0)
{
targetMonitorIndex = 0;
}
if (monitors <= targetMonitorIndex)
{
targetMonitorIndex = monitors - 1;
}
if (targetMonitorIndex >= 0)
{
_uniWinCore.FitToMonitor(targetMonitorIndex);
}
}
/// <summary>
/// 查找现有实例或创建新实例
/// </summary>
/// <returns></returns>
private static UniWindowController FindOrCreateInstance()
{
#if UNITY_2022_1_OR_NEWER
var instance = GameObject.FindAnyObjectByType<UniWindowController>();
#else
var instance = GameObject.FindObjectOfType<UniWindowController>();
#endif
// 目前禁止自动创建
// // 场景中未找到时创建新实例
// if (!instance)
// {
// var obj = new GameObject(nameof(UniWindowController));
// obj.AddComponent<UniWindowController>();
// }
return instance;
}
void Start()
{
//// New Input System 存在兼容性问题,用于验证输出
// #if ENABLE_LEGACY_INPUT_MANAGER
// Debug.Log("使用旧版输入管理器。");
// #elif ENABLE_INPUT_SYSTEM
// Debug.Log("使用新版输入系统。");
// Debug.Log("后台运行 " + Mouse.current.canRunInBackground);
// #else
// Debug.Log("鼠标位置不可用。");
// #endif
// 启动获取鼠标光标下颜色的协程
StartCoroutine(HitTestCoroutine());
// 获取初始窗口大小和位置
StoreOriginalWindowRectangle();
// 适配到所选显示器
OnMonitorChanged += UpdateMonitorFitting;
UpdateMonitorFitting();
}
void OnDestroy()
{
if (_uniWinCore != null)
{
_uniWinCore.Dispose();
}
// 同时销毁实例
if (this == current)
{
_current = null;
}
}
void StoreOriginalWindowRectangle()
{
if (_uniWinCore != null)
{
var size = _uniWinCore.GetWindowSize();
var pos = _uniWinCore.GetWindowPosition();
originalWindowRectangle = new Rect(pos, size);
}
}
// 每一帧调用 Update
void Update()
{
// 如果尚未获取自身窗口,则获取
if (_uniWinCore == null || !_uniWinCore.IsActive)
{
UpdateTargetWindow();
}
else
{
_uniWinCore.Update();
}
// 处理事件
UpdateEvents();
// 更新键盘、鼠标操作对下方窗口的穿透状态
UpdateClickThrough();
}
/// <summary>
/// 检查并处理 UniWinCore 事件
/// </summary>
private void UpdateEvents()
{
if (_uniWinCore == null) return;
if (_uniWinCore.ObserveDroppedFiles(out var droppedFiles))
{
OnDropFiles?.Invoke(droppedFiles);
}
if (_uniWinCore.ObserveMonitorChanged())
{
OnMonitorChanged?.Invoke();
}
if (_uniWinCore.ObserveWindowStyleChanged(out var type))
{
// // 指定了适配显示器时,最大化被取消的情况下
// if (shouldFitMonitor && !uniWinCore.GetZoomed())
// {
// //StartCoroutine("ForceZoomed"); // 延迟强制最大化
// //SetZoomed(true); // 强制最大化 ←不一定生效
// //shouldFitMonitor = false; // 禁用适配
// }
if (_shouldFitMonitor) StartCoroutine("ForceZoomed"); // 延迟强制最大化
OnStateChanged?.Invoke((WindowStateEventType)type);
}
}
IEnumerator ForceZoomed()
{
yield return new WaitForSeconds(0.5f);
if (_shouldFitMonitor && !_uniWinCore.GetZoomed()) SetZoomed(true);
yield return null;
}
/// <summary>
/// 指定相机。如果之前有相机,则恢复其背景
/// </summary>
/// <param name="newCamera"></param>
public void SetCamera(Camera newCamera)
{
// 如果相机已更改,恢复其设置
if (newCamera != currentCamera)
{
SetCameraBackground(false);
}
currentCamera = newCamera;
// 记录相机的原始背景
if (currentCamera)
{
originalCameraClearFlags = currentCamera.clearFlags;
originalCameraBackground = currentCamera.backgroundColor;
SetCameraBackground(_isTransparent);
}
}
/// <summary>
/// 将鼠标/触摸操作穿透到下方窗口
/// </summary>
/// <param name="isThrough"></param>
void SetClickThrough(bool isThrough)
{
_uniWinCore?.EnableClickThrough(isThrough);
_isClickThrough = isThrough;
}
/// <summary>
/// 根据像素颜色切换操作接收状态
/// </summary>
void UpdateClickThrough()
{
// 没有自动点击测试则结束
if (!isHitTestEnabled || hitTestType == HitTestType.None) return;
// 鼠标光标隐藏状态视为在透明像素上
bool hit = (onObject);
if (_isClickThrough)
{
// 如果当前是点击穿透状态,仅在命中时取消穿透
if (hit)
{
SetClickThrough(false);
}
}
else
{
// 如果当前不是穿透状态,仅在透明且未命中时启用穿透
if (isTransparent && !hit)
{
SetClickThrough(true);
}
}
}
/// <summary>
/// 在协程中重复进行光标下颜色或 Raycast 的点击测试
/// 使用 WaitForEndOfFrame() 所以采用协程
/// </summary>
/// <returns></returns>
private IEnumerator HitTestCoroutine()
{
while (Application.isPlaying)
{
yield return new WaitForEndOfFrame();
// Windows 下,如果是单色透明则点击测试由 OS 负责,所以始终为命中
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
if (transparentType == TransparentType.ColorKey)
{
onObject = true;
}
else
#endif
if (hitTestType == HitTestType.Opacity)
{
HitTestByOpaquePixel();
}
else if (hitTestType == HitTestType.Raycast)
{
HitTestByRaycast();
}
else
{
// 无点击测试时始终为 true
onObject = true;
}
}
yield return null;
}
/// <summary>
/// 将屏幕上的鼠标坐标换算为 Unity 的屏幕坐标系
/// </summary>
private Vector2 GetClientCursorPosition()
{
// New Input System 在没有焦点时无法获取鼠标坐标,因此自行计算
Vector2 mousePos = UniWinCore.GetCursorPosition();
Vector2 winPos = windowPosition;
Rect clientRect = _uniWinCore.GetClientRectangle();
Vector2 unityPos = new Vector2(
(mousePos.x - winPos.x - clientRect.x) * Screen.width / clientRect.width,
(mousePos.y - winPos.y - clientRect.y) * Screen.height / clientRect.height
);
// // 调试用
// // 与 Unity 获取的值进行比较
// #if ENABLE_LEGACY_INPUT_MANAGER
// Vector2 position = Input.mousePosition;
// #elif ENABLE_INPUT_SYSTEM
// Vector2 position = Mouse.current.position.ReadValue();
// #endif
// if (!position.Equals(unityPos))
// {
// Debug.LogWarning("鼠标位置差异 : " + position + " / " + unityPos);
// }
// 在编辑器中始终使用 Unity 功能获取鼠标坐标
// Game 窗口可能不是唯一窗口,或 Scale 不同,无法简单计算
#if UNITY_EDITOR
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.mousePosition;
#elif UNITY_EDITOR && ENABLE_INPUT_SYSTEM
return Mouse.current.position.ReadValue();
#else
return unityPos;
#endif
#else
return unityPos;
#endif
}
/// <summary>
/// 检查鼠标下方是否有不透明像素
/// </summary>
private void HitTestByOpaquePixel()
{
Vector2 mousePos = GetClientCursorPosition();
// 检查鼠标坐标
if (GetOnOpaquePixel(mousePos))
{
//Debug.Log("鼠标 " + mousePos);
onObject = true;
//activeFingerId = -1; // 取消触摸追踪
return;
}
else
{
onObject = false;
}
}
/// <summary>
/// 返回指定坐标的像素是否透明
/// </summary>
/// <param name="mousePos">坐标[px]。必须在绘制范围内。</param>
/// <returns></returns>
private bool GetOnOpaquePixel(Vector2 mousePos)
{
float w = Screen.width;
float h = Screen.height;
//Debug.Log(w + ", " + h);
// 在屏幕外则视为透明
if (
mousePos.x < 0 || mousePos.x >= w
|| mousePos.y < 0 || mousePos.y >= h
)
{
return false;
}
// 如果不是透明状态,在范围内则视为不透明
if (!_isTransparent) return true;
// 如果是 LayeredWindow点击测试由 OS 负责窗口内返回true
if (transparentType == TransparentType.ColorKey) return true;
// 根据指定坐标的绘制结果进行判断
try // 在 WaitForEndOfFrame 时机执行的话,不需要 try 应该也没问题
{
// 参考 http://tsubakit1.hateblo.jp/entry/20131203/1386000440
colorPickerTexture.ReadPixels(new Rect(mousePos, Vector2.one), 0, 0);
Color color = colorPickerTexture.GetPixels32()[0];
pickedColor = color;
return (color.a >= opacityThreshold); // alpha 达到阈值则视为不透明
}
catch (System.Exception ex)
{
Debug.LogError(ex.Message);
return false;
}
}
/// <summary>
/// 检查鼠标下方是否有对象
/// </summary>
private void HitTestByRaycast()
{
Vector2 position = GetClientCursorPosition();
// // 判断是否在 uGUI 上
var raycastResults = new List<RaycastResult>();
pointerEventData.position = position;
EventSystem.current.RaycastAll(pointerEventData, raycastResults);
foreach (var result in raycastResults)
{
// 考虑图层遮罩Ignore Raycast 以外的命中)
if (((1 << result.gameObject.layer) & hitTestLayerMask) > 0)
{
onObject = true;
return;
}
}
// 如果忽略图层限制直接命中,使用下面代码
// // 如果判定为在 uGUI 上,则结束
// if (EventSystem.current.IsPointerOverGameObject())
// {
// onObject = true;
// return;
// }
if (currentCamera && currentCamera.isActiveAndEnabled)
{
Ray ray = currentCamera.ScreenPointToRay(position);
// 判断是否在 3D 对象上
if (Physics.Raycast(ray, out _, raycastMaxDepth))
{
onObject = true;
return;
}
// 判断是否在 2D 对象上
var rayHit2D = Physics2D.GetRayIntersection(ray);
Debug.DrawRay(ray.origin, ray.direction, Color.blue, 2f, false);
if (rayHit2D.collider != null)
{
onObject = true;
return;
}
}
else
{
// 如果相机无效,则获取主相机
currentCamera = Camera.main;
}
// 若均未命中,则判定为不在对象上
onObject = false;
}
/// <summary>
/// 如果自己的窗口句柄不确定,则重新查找
/// </summary>
private void UpdateTargetWindow()
{
if (_uniWinCore == null)
{
_uniWinCore = new UniWinCore();
}
// 如果尚未获取窗口,则执行获取处理
if (!_uniWinCore.IsActive)
{
_uniWinCore.AttachMyWindow();
// 获取到窗口后设置初始值
if (_uniWinCore.IsActive)
{
_uniWinCore.SetTransparentType((UniWinCore.TransparentType)transparentType);
_uniWinCore.SetKeyColor(keyColor);
_uniWinCore.SetAlphaValue(_alphaValue);
SetTransparent(_isTransparent);
if (_isBottommost)
{
SetBottommost(_isBottommost);
}
else
{
SetTopmost(_isTopmost);
}
SetZoomed(_isZoomed);
SetClickThrough(_isClickThrough);
SetAllowDrop(_allowDropFiles);
SetFreePositioning(_isFreePositioningEnabled);
// 获取窗口时执行与显示器更改相同的处理
OnMonitorChanged?.Invoke();
}
}
else
{
#if UNITY_EDITOR
// 在编辑器中,由于 Game 视图可能被关闭或停靠,如果发生变化则更改目标窗口
// 如果活动窗口与当前目标相同,则不执行任何操作
_uniWinCore.AttachMyActiveWindow();
#endif
}
}
/// <summary>
/// 窗口焦点变化时调用
/// </summary>
/// <param name="focus"></param>
private void OnApplicationFocus(bool focus)
{
if (focus)
{
UpdateTargetWindow();
// 获取焦点的瞬间,强制关闭点击穿透
if (_isTransparent && isHitTestEnabled && transparentType != TransparentType.ColorKey)
{
SetClickThrough(false);
}
}
}
/// <summary>
/// 窗口变为透明状态时,自动将背景改为透明单色
/// </summary>
/// <param name="transparent"></param>
void SetCameraBackground(bool transparent)
{
// 如果未指定相机或未启用自动切换,则不执行任何操作
if (!currentCamera || !autoSwitchCameraBackground) return;
// 如果需要透明,则将相机背景改为透明色
if (transparent)
{
// 如果尚未透明化,则记忆当前相机信息
if (!isTransparent)
{
originalCameraClearFlags = currentCamera.clearFlags;
originalCameraBackground = currentCamera.backgroundColor;
}
currentCamera.clearFlags = CameraClearFlags.SolidColor;
if (transparentType == TransparentType.ColorKey)
{
currentCamera.backgroundColor = keyColor;
}
else
{
currentCamera.backgroundColor = Color.clear;
}
}
else
{
currentCamera.clearFlags = originalCameraClearFlags;
currentCamera.backgroundColor = originalCameraBackground;
}
}
/// <summary>
/// 切换透明化状态
/// </summary>
/// <param name="transparent"></param>
private void SetTransparent(bool transparent)
{
SetCameraBackground(transparent);
_isTransparent = transparent;
#if !UNITY_EDITOR
if (_uniWinCore != null)
{
_uniWinCore.EnableTransparent(transparent);
}
#endif
UpdateClickThrough();
}
/// <summary>
/// 更改透明方式
/// </summary>
/// <param name="type"></param>
public void SetTransparentType(TransparentType type)
{
if (_uniWinCore != null)
{
// 如果正在透明中,则先解除再重新透明
if (_isTransparent)
{
SetTransparent(false);
_uniWinCore.SetTransparentType((UniWinCore.TransparentType)type);
transparentType = type;
SetTransparent(true);
}
else
{
_uniWinCore.SetTransparentType((UniWinCore.TransparentType)type);
transparentType = type;
}
}
}
/// <summary>
/// 设置窗口透明度
/// </summary>
/// <param name="alpha">0.0 到 1.0</param>
private void SetAlphaValue(float alpha)
{
_alphaValue = alpha;
_uniWinCore?.SetAlphaValue(_alphaValue);
}
/// <summary>
/// 切换置顶
/// </summary>
/// <param name="topmost"></param>
private void SetTopmost(bool topmost)
{
//if (_isTopmost == topmost) return;
if (_uniWinCore == null) return;
_uniWinCore.EnableTopmost(topmost);
_isTopmost = _uniWinCore.IsTopmost;
_isBottommost = _uniWinCore.IsBottommost;
}
/// <summary>
/// 切换始终置底
/// </summary>
/// <param name="bottommost"></param>
private void SetBottommost(bool bottommost)
{
if (_uniWinCore == null) return;
_uniWinCore.EnableBottommost(bottommost);
_isBottommost = _uniWinCore.IsBottommost;
_isTopmost = _uniWinCore.IsTopmost;
}
/// <summary>
/// 最大化/还原
/// </summary>
/// <param name="zoomed"></param>
private void SetZoomed(bool zoomed)
{
if (_uniWinCore == null) return;
_uniWinCore.SetZoomed(zoomed);
_isZoomed = _uniWinCore.GetZoomed();
}
private void SetAllowDrop(bool enabled)
{
if (_uniWinCore == null) return;
_uniWinCore.SetAllowDrop(enabled);
_allowDropFiles = enabled;
}
/// <summary>
/// 在 macOS 上,允许将窗口放置在包含菜单栏上方在内的任意位置
/// </summary>
/// <param name="enabled"></param>
private void SetFreePositioning(bool enabled)
{
if (_uniWinCore == null) return;
_uniWinCore.EnableFreePositioning(enabled);
_isFreePositioningEnabled = _uniWinCore.IsFreePositioningEnabled;
}
/// <summary>
/// 获取连接的显示器数量
/// </summary>
/// <returns></returns>
public static int GetMonitorCount()
{
//if (uniWinCore == null) return 0;
return UniWinCore.GetMonitorCount();
}
/// <summary>
/// 获取显示器的位置和大小
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public static Rect GetMonitorRect(int index)
{
if (UniWinCore.GetMonitorRectangle(index, out Vector2 position, out Vector2 size))
{
return new Rect(position, size);
}
return Rect.zero;
}
/// <summary>
/// 适配到指定显示器
/// </summary>
/// <returns></returns>
private bool FitToMonitor(bool shouldFit, int monitorIndex)
{
if (_uniWinCore == null)
{
_shouldFitMonitor = shouldFit;
_monitorToFit = monitorIndex;
return false;
}
if (shouldFit)
{
if (!_shouldFitMonitor)
{
// 之前未适配的情况
_monitorToFit = monitorIndex;
_shouldFitMonitor = shouldFit;
UpdateMonitorFitting();
}
else
{
if (_monitorToFit != monitorIndex)
{
// 适配的显示器发生变化的情况
_monitorToFit = monitorIndex;
UpdateMonitorFitting();
}
}
}
else
{
if (_shouldFitMonitor)
{
// 之前是适配状态,现在被取消的情况
_monitorToFit = monitorIndex;
_shouldFitMonitor = shouldFit;
UpdateMonitorFitting();
_uniWinCore.SetZoomed(false);
//uniWinCore.SetWindowSize(originalWindowRectangle.size);
//uniWinCore.SetWindowPosition(originalWindowRectangle.position);
}
else
{
// 未在适配中时,仅更改选择
_monitorToFit = monitorIndex;
}
}
return true;
}
/// <summary>
/// 获取鼠标光标位置
/// </summary>
/// <returns>光标位置</returns>
public static Vector2 GetCursorPosition()
{
return UniWinCore.GetCursorPosition();
}
/// <summary>
/// 设置鼠标光标位置
/// </summary>
/// <param name="position"></param>
public static void SetCursorPosition(Vector2 position)
{
UniWinCore.SetCursorPosition(position);
}
/// <summary>
/// 获取鼠标按键状态
/// </summary>
/// <returns></returns>
public static MouseButton GetMouseButtons()
{
int buttons = UniWinCore.GetMouseButtons();
return (MouseButton)buttons;
}
/// <summary>
/// 获取按下的修饰键
/// </summary>
/// <returns></returns>
public static ModifierKey GetModifierKeys()
{
int mod = UniWinCore.GetModifierKeys();
return (ModifierKey)mod;
}
/// <summary>
/// 退出时需要恢复窗口状态
/// </summary>
void OnApplicationQuit()
{
if (Application.isPlaying)
{
if (_uniWinCore != null)
{
// 在编辑器中恢复窗口状态
// 在独立构建中会看到恢复过程,因此跳过
#if UNITY_EDITOR
_uniWinCore.SetWindowSize(originalWindowRectangle.size);
_uniWinCore.SetWindowPosition(originalWindowRectangle.position);
_uniWinCore.DetachWindow();
#endif
_uniWinCore.Dispose();
}
}
}
/// <summary>
/// 将焦点给予自身窗口
/// </summary>
public void Focus()
{
if (_uniWinCore != null)
{
//uniWin.SetFocus();
}
}
/// <summary>
/// 仅供调试用。用于获取各阶段参考信息的函数
/// </summary>
/// <returns></returns>
[Obsolete]
public int GetDebugInfo()
{
if (_uniWinCore != null)
{
return UniWinCore.GetDebugInfo();
}
return 0;
}
}
}