/*
* 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
///
/// 使布尔属性可编辑
///
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class EditablePropertyAttribute : UnityEngine.PropertyAttribute
{
}
///
/// 将属性设置为只读
///
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class ReadOnlyAttribute : UnityEngine.PropertyAttribute
{
}
/// @endcond
///
/// Windows/Mac 统一窗口控制器
///
public class UniWindowController : MonoBehaviour
{
///
/// 与 UniWinCore.TransparentType 相同
///
public enum TransparentType : int
{
None = 0,
Alpha = 1,
ColorKey = 2,
}
///
/// 指定点击测试方法(即切换点击穿透)
///
public enum HitTestType : int
{
None = 0,
Opacity = 1,
Raycast = 2,
}
///
/// 标识 OnStateChanged 事件发生时的类型
///
[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,
};
///
/// 鼠标按键
///
[Flags]
public enum MouseButton : int
{
None = 0,
Left = 1,
Right = 2,
Middle = 4,
}
///
/// 修饰键
///
[Flags]
public enum ModifierKey : int
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Command = 8,
}
///
/// 获取当前 UniWindowController 实例
///
public static UniWindowController current => _current ? _current : FindOrCreateInstance();
private static UniWindowController _current;
///
/// 底层类
///
private UniWinCore _uniWinCore = null;
///
/// 此窗口是否接收鼠标事件
///
public bool isClickThrough
{
get { return _isClickThrough; }
set { SetClickThrough(value); }
}
private bool _isClickThrough = false;
///
/// 此窗口是否透明
///
public bool isTransparent
{
get { return _isTransparent; }
set { SetTransparent(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时设置为透明")]
private bool _isTransparent = false;
///
/// 窗口透明度(0.0 到 1.0)
///
public float alphaValue
{
get { return _alphaValue; }
set { SetAlphaValue(value); }
}
[SerializeField, EditableProperty, Tooltip("窗口透明度"), Range(0f, 1f)]
private float _alphaValue = 1.0f;
///
/// 此窗口是否置顶
///
public bool isTopmost
{
get { return ((_uniWinCore == null) ? _isTopmost : _isTopmost = _uniWinCore.IsTopmost); }
set { SetTopmost(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时置顶")]
private bool _isTopmost = false;
///
/// 此窗口是否置底
///
public bool isBottommost
{
get { return ((_uniWinCore == null) ? _isBottommost : _isBottommost = _uniWinCore.IsBottommost); }
set { SetBottommost(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时置底")]
private bool _isBottommost = false;
///
/// 此窗口是否最大化
///
public bool isZoomed
{
get { return ((_uniWinCore == null) ? _isZoomed : _isZoomed = _uniWinCore.GetZoomed()); }
set { SetZoomed(value); }
}
[SerializeField, EditableProperty, Tooltip("选中后启动时最大化")]
private bool _isZoomed = false;
///
/// 此窗口是否适配到显示器
///
public bool shouldFitMonitor
{
get { return _shouldFitMonitor; }
set { FitToMonitor(value, _monitorToFit); }
}
[SerializeField, EditableProperty, Tooltip("选中后将窗口适配到显示器")]
private bool _shouldFitMonitor = false;
///
/// 适配窗口的目标显示器索引(0, 1, ...)
///
public int monitorToFit
{
get { return _monitorToFit; }
set { FitToMonitor(_shouldFitMonitor, value); }
}
private int _monitorToFit = 0;
///
/// 启用/禁用接受文件拖放
///
public bool allowDropFiles
{
get { return _allowDropFiles; }
set { SetAllowDrop(value); }
}
[SerializeField, EditableProperty, Tooltip("启用文件或文件夹拖放")]
private bool _allowDropFiles = false;
///
/// 是否启用点击穿透自动判定
/// 如果禁用,可以手动修改 isClickThrough
///
public bool isHitTestEnabled = true;
///
/// 点击穿透自动判定的方法
///
[Tooltip("选择方法")] public HitTestType hitTestType = HitTestType.Opacity;
///
/// 点击穿透判定方法为不透明度时使用的阈值
/// 鼠标下方像素的 alpha 达到此值时判定为命中
///
[Tooltip("点击测试类型为 Opacity 时可用"), RangeAttribute(0f, 1f)]
public float opacityThreshold = 0.1f;
///
/// 点击穿透判定方法为 raycast 时的最远距离
///
private float raycastMaxDepth = 100.0f;
///
/// 启用后,窗口透明时会自动将相机背景改为单色黑色透明
///
[Header("高级设置")] [Tooltip("窗口透明时更改相机背景")]
public bool autoSwitchCameraBackground = true;
///
/// 启用后,启动时如果是全屏则会强制退出全屏
///
/// 用于即使启动对话框设置了全屏,也能切换为窗口模式
/// 仅在启动时生效
/// 在 Mac 上,即使强制退出全屏,似乎仍会变成另一个画面,效果不大
///
[Tooltip("启动时强制窗口模式")] public bool forceWindowed = false;
///
/// 相机实例
///
[Tooltip("未设置时使用主相机")] public Camera currentCamera;
///
/// 透明方式的指定
///
[Header("仅限 Windows")] [Tooltip("选择透明方式。*仅 Windows 可用")]
public TransparentType transparentType = TransparentType.Alpha;
///
/// 当透明类型为 ColorKey 时使用的键色
///
[Tooltip("将在窗口下次变为透明时使用")] public Color32 keyColor = new Color32(0x01, 0x00, 0x01, 0x00);
///
/// 在 macOS 上,是否允许将窗口放置在菜单栏上方
///
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;
///
/// 鼠标指针是否在不透明像素或物体上
///
[Header("状态")] [SerializeField, ReadOnly, Tooltip("鼠标指针是否在不透明像素上?(只读)")]
private bool onObject = true;
///
/// 鼠标指针下方的像素颜色。(只读)
///
[SerializeField, ReadOnly, Tooltip("鼠标指针下方的像素颜色。(只读)")]
public Color pickedColor;
///
/// 获取/设置窗口坐标
///
public Vector2 windowPosition
{
get { return (_uniWinCore != null ? _uniWinCore.GetWindowPosition() : Vector2.zero); }
set { _uniWinCore?.SetWindowPosition(value); }
}
///
/// 获取/设置窗口大小
///
public Vector2 windowSize
{
get { return (_uniWinCore != null ? _uniWinCore.GetWindowSize() : Vector2.zero); }
set { _uniWinCore?.SetWindowSize(value); }
}
///
/// 获取客户区域大小
///
public Vector2 clientSize
{
get { return (_uniWinCore != null ? _uniWinCore.GetClientSize() : Vector2.zero); }
}
///
/// 获取/设置鼠标光标坐标
///
public Vector2 cursorPosition
{
get { return UniWinCore.GetCursorPosition(); }
set { UniWinCore.SetCursorPosition(value); }
}
///
/// 初始状态的窗口位置和大小
///
private Rect originalWindowRectangle;
// 存储相机原有背景,以便在窗口透明时替换为 alpha 为零的黑色
private CameraClearFlags originalCameraClearFlags;
private Color originalCameraBackground;
///
/// 存储光标下方 1 像素颜色的纹理
///
private Texture2D colorPickerTexture = null;
///
/// Raycast 使用的鼠标事件信息
///
private PointerEventData pointerEventData;
///
/// Raycast 时的图层遮罩
///
private int hitTestLayerMask;
///
/// 窗口样式改变时发生
///
public event OnStateChangedDelegate OnStateChanged;
public delegate void OnStateChangedDelegate(WindowStateEventType type);
public delegate void FilesDelegate(string[] files);
///
/// 文件或文件夹被拖放后发生
///
public event FilesDelegate OnDropFiles;
///
/// 显示器设置或分辨率改变时发生
///
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();
//}
}
// 记录相机原始背景
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();
}
///
/// 适配到指定显示器
///
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);
}
}
///
/// 查找现有实例或创建新实例
///
///
private static UniWindowController FindOrCreateInstance()
{
#if UNITY_2022_1_OR_NEWER
var instance = GameObject.FindAnyObjectByType();
#else
var instance = GameObject.FindObjectOfType();
#endif
// 目前禁止自动创建
// // 场景中未找到时创建新实例
// if (!instance)
// {
// var obj = new GameObject(nameof(UniWindowController));
// obj.AddComponent();
// }
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();
}
///
/// 检查并处理 UniWinCore 事件
///
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;
}
///
/// 指定相机。如果之前有相机,则恢复其背景
///
///
public void SetCamera(Camera newCamera)
{
// 如果相机已更改,恢复其设置
if (newCamera != currentCamera)
{
SetCameraBackground(false);
}
currentCamera = newCamera;
// 记录相机的原始背景
if (currentCamera)
{
originalCameraClearFlags = currentCamera.clearFlags;
originalCameraBackground = currentCamera.backgroundColor;
SetCameraBackground(_isTransparent);
}
}
///
/// 将鼠标/触摸操作穿透到下方窗口
///
///
void SetClickThrough(bool isThrough)
{
_uniWinCore?.EnableClickThrough(isThrough);
_isClickThrough = isThrough;
}
///
/// 根据像素颜色切换操作接收状态
///
void UpdateClickThrough()
{
// 没有自动点击测试则结束
if (!isHitTestEnabled || hitTestType == HitTestType.None) return;
// 鼠标光标隐藏状态视为在透明像素上
bool hit = (onObject);
if (_isClickThrough)
{
// 如果当前是点击穿透状态,仅在命中时取消穿透
if (hit)
{
SetClickThrough(false);
}
}
else
{
// 如果当前不是穿透状态,仅在透明且未命中时启用穿透
if (isTransparent && !hit)
{
SetClickThrough(true);
}
}
}
///
/// 在协程中重复进行光标下颜色或 Raycast 的点击测试
/// 使用 WaitForEndOfFrame() 所以采用协程
///
///
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;
}
///
/// 将屏幕上的鼠标坐标换算为 Unity 的屏幕坐标系
///
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
}
///
/// 检查鼠标下方是否有不透明像素
///
private void HitTestByOpaquePixel()
{
Vector2 mousePos = GetClientCursorPosition();
// 检查鼠标坐标
if (GetOnOpaquePixel(mousePos))
{
//Debug.Log("鼠标 " + mousePos);
onObject = true;
//activeFingerId = -1; // 取消触摸追踪
return;
}
else
{
onObject = false;
}
}
///
/// 返回指定坐标的像素是否透明
///
/// 坐标[px]。必须在绘制范围内。
///
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;
}
}
///
/// 检查鼠标下方是否有对象
///
private void HitTestByRaycast()
{
Vector2 position = GetClientCursorPosition();
// // 判断是否在 uGUI 上
var raycastResults = new List();
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;
}
///
/// 如果自己的窗口句柄不确定,则重新查找
///
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
}
}
///
/// 窗口焦点变化时调用
///
///
private void OnApplicationFocus(bool focus)
{
if (focus)
{
UpdateTargetWindow();
// 获取焦点的瞬间,强制关闭点击穿透
if (_isTransparent && isHitTestEnabled && transparentType != TransparentType.ColorKey)
{
SetClickThrough(false);
}
}
}
///
/// 窗口变为透明状态时,自动将背景改为透明单色
///
///
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;
}
}
///
/// 切换透明化状态
///
///
private void SetTransparent(bool transparent)
{
SetCameraBackground(transparent);
_isTransparent = transparent;
#if !UNITY_EDITOR
if (_uniWinCore != null)
{
_uniWinCore.EnableTransparent(transparent);
}
#endif
UpdateClickThrough();
}
///
/// 更改透明方式
///
///
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;
}
}
}
///
/// 设置窗口透明度
///
/// 0.0 到 1.0
private void SetAlphaValue(float alpha)
{
_alphaValue = alpha;
_uniWinCore?.SetAlphaValue(_alphaValue);
}
///
/// 切换置顶
///
///
private void SetTopmost(bool topmost)
{
//if (_isTopmost == topmost) return;
if (_uniWinCore == null) return;
_uniWinCore.EnableTopmost(topmost);
_isTopmost = _uniWinCore.IsTopmost;
_isBottommost = _uniWinCore.IsBottommost;
}
///
/// 切换始终置底
///
///
private void SetBottommost(bool bottommost)
{
if (_uniWinCore == null) return;
_uniWinCore.EnableBottommost(bottommost);
_isBottommost = _uniWinCore.IsBottommost;
_isTopmost = _uniWinCore.IsTopmost;
}
///
/// 最大化/还原
///
///
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;
}
///
/// 在 macOS 上,允许将窗口放置在包含菜单栏上方在内的任意位置
///
///
private void SetFreePositioning(bool enabled)
{
if (_uniWinCore == null) return;
_uniWinCore.EnableFreePositioning(enabled);
_isFreePositioningEnabled = _uniWinCore.IsFreePositioningEnabled;
}
///
/// 获取连接的显示器数量
///
///
public static int GetMonitorCount()
{
//if (uniWinCore == null) return 0;
return UniWinCore.GetMonitorCount();
}
///
/// 获取显示器的位置和大小
///
///
///
public static Rect GetMonitorRect(int index)
{
if (UniWinCore.GetMonitorRectangle(index, out Vector2 position, out Vector2 size))
{
return new Rect(position, size);
}
return Rect.zero;
}
///
/// 适配到指定显示器
///
///
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;
}
///
/// 获取鼠标光标位置
///
/// 光标位置
public static Vector2 GetCursorPosition()
{
return UniWinCore.GetCursorPosition();
}
///
/// 设置鼠标光标位置
///
///
public static void SetCursorPosition(Vector2 position)
{
UniWinCore.SetCursorPosition(position);
}
///
/// 获取鼠标按键状态
///
///
public static MouseButton GetMouseButtons()
{
int buttons = UniWinCore.GetMouseButtons();
return (MouseButton)buttons;
}
///
/// 获取按下的修饰键
///
///
public static ModifierKey GetModifierKeys()
{
int mod = UniWinCore.GetModifierKeys();
return (ModifierKey)mod;
}
///
/// 退出时需要恢复窗口状态
///
void OnApplicationQuit()
{
if (Application.isPlaying)
{
if (_uniWinCore != null)
{
// 在编辑器中恢复窗口状态
// 在独立构建中会看到恢复过程,因此跳过
#if UNITY_EDITOR
_uniWinCore.SetWindowSize(originalWindowRectangle.size);
_uniWinCore.SetWindowPosition(originalWindowRectangle.position);
_uniWinCore.DetachWindow();
#endif
_uniWinCore.Dispose();
}
}
}
///
/// 将焦点给予自身窗口
///
public void Focus()
{
if (_uniWinCore != null)
{
//uniWin.SetFocus();
}
}
///
/// 仅供调试用。用于获取各阶段参考信息的函数
///
///
[Obsolete]
public int GetDebugInfo()
{
if (_uniWinCore != null)
{
return UniWinCore.GetDebugInfo();
}
return 0;
}
}
}