604 lines
22 KiB
C#
604 lines
22 KiB
C#
/**
|
||
* UniWindowController 的示例脚本
|
||
*
|
||
* Author: Kirurobo http://twitter.com/kirurobo
|
||
* License: MIT
|
||
*/
|
||
|
||
using System;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using UnityEngine.UI;
|
||
|
||
namespace Kirurobo
|
||
{
|
||
/// <summary>
|
||
/// 使用 Toggle 开关 WindowController 设置的示例
|
||
/// </summary>
|
||
public class UiSampleController : MonoBehaviour
|
||
{
|
||
private UniWindowController uniwinc;
|
||
private UniWindowMoveHandle uniWinMoveHandle;
|
||
private RectTransform canvasRect;
|
||
|
||
private float mouseMoveSS = 0f; // 鼠标轨迹平方和。[px^2]
|
||
private float mouseMoveSSThreshold = 36f; // 点击(非拖拽)阈值。[px^2]
|
||
private Vector3 lastMousePosition; // 右键点击位置。
|
||
private float lastEventOccurredTime = -5f; // 上次事件发生的时间戳 [s]
|
||
private float eventMessageTimeout = 1f; // 在此时间段内显示事件消息 [s]
|
||
#if ENABLE_LEGACY_INPUT_MANAGER
|
||
private float touchDuration = 0f;
|
||
private float touchDurationThreshold = 0.5f; // 长按时间阈值。[s]
|
||
#endif
|
||
public Toggle transparentToggle;
|
||
public Slider alphaSlider;
|
||
public Toggle topmostToggle;
|
||
public Toggle bottommostToggle;
|
||
[FormerlySerializedAs("maximizedToggle")] public Toggle zoomedToggle;
|
||
public Toggle dragMoveToggle;
|
||
public Toggle allowDropToggle;
|
||
public Dropdown fitWindowDropdown;
|
||
public Toggle showBorderlineToggle;
|
||
public Button widthDownButton;
|
||
public Button widthUpButton;
|
||
public Button heightDownButton;
|
||
public Button heightUpButton;
|
||
public Dropdown transparentTypeDropdown;
|
||
public Dropdown hitTestTypeDropdown;
|
||
public Toggle clickThroughToggle;
|
||
public Image pickedColorImage;
|
||
public Text pickedColorText;
|
||
public Text messageText;
|
||
public Text clientSizeText;
|
||
public Button menuCloseButton;
|
||
public RectTransform menuPanel;
|
||
public RectTransform borderlinePanel;
|
||
|
||
/// <summary>
|
||
/// 初始化
|
||
/// </summary>
|
||
void Start()
|
||
{
|
||
// 查找 UniWindowController
|
||
uniwinc = UniWindowController.current;
|
||
|
||
// 查找 UniWindowDragMove
|
||
uniWinMoveHandle = GameObject.FindAnyObjectByType<UniWindowMoveHandle>();
|
||
|
||
// 获取 Canvas 的 RectTransform
|
||
if (menuPanel) canvasRect = menuPanel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();
|
||
|
||
// 根据有效显示器数量创建选项
|
||
UpdateMonitorDropdown();
|
||
|
||
// 将 Toggle 的选中状态与当前状态同步
|
||
UpdateUI();
|
||
|
||
if (uniwinc)
|
||
{
|
||
// 操作 UI 时将其反映到窗口
|
||
transparentToggle?.onValueChanged.AddListener(val => uniwinc.isTransparent = val);
|
||
alphaSlider?.onValueChanged.AddListener(val => uniwinc.alphaValue = val);
|
||
topmostToggle?.onValueChanged.AddListener(val => uniwinc.isTopmost = val);
|
||
bottommostToggle?.onValueChanged.AddListener(val => uniwinc.isBottommost = val);
|
||
zoomedToggle?.onValueChanged.AddListener(val => uniwinc.isZoomed = val);
|
||
allowDropToggle?.onValueChanged.AddListener(val => uniwinc.allowDropFiles = val);
|
||
|
||
fitWindowDropdown?.onValueChanged.AddListener(val => SetFitToMonitor(val));
|
||
|
||
widthDownButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(-100, 0));
|
||
widthUpButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(+100, 0));
|
||
heightDownButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(0, -100));
|
||
heightUpButton?.onClick.AddListener(() => uniwinc.windowSize += new Vector2(0, +100));
|
||
|
||
clickThroughToggle?.onValueChanged.AddListener(val => uniwinc.isClickThrough = val);
|
||
|
||
transparentTypeDropdown?.onValueChanged.AddListener(val => uniwinc.SetTransparentType((UniWindowController.TransparentType)val));
|
||
hitTestTypeDropdown?.onValueChanged.AddListener(val => uniwinc.hitTestType = (UniWindowController.HitTestType)val);
|
||
menuCloseButton?.onClick.AddListener(CloseMenu);
|
||
|
||
if (uniWinMoveHandle) dragMoveToggle?.onValueChanged.AddListener(val => uniWinMoveHandle.enabled = val);
|
||
|
||
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
|
||
// 如果不是 Windows,则禁用透明方式选择
|
||
//if (transparentTypeDropdown) transparentTypeDropdown.interactable = false;
|
||
//if (transparentTypeDropdown) transparentTypeDropdown.enabled = false;
|
||
if (transparentTypeDropdown) transparentTypeDropdown.gameObject.SetActive(false);
|
||
#endif
|
||
|
||
// 添加事件
|
||
uniwinc.OnStateChanged += (type) =>
|
||
{
|
||
UpdateUI();
|
||
//Debug.Log("Window state changed: " + type);
|
||
ShowEventMessage("状态改变: " + type);
|
||
//ShowEventMessage("State changed: " + type + "4:isKey 2:canBecomeKey, 1:canBecomeMain : " + uniwinc.GetDebugInfo().ToString());
|
||
ShowClientSize();
|
||
};
|
||
uniwinc.OnMonitorChanged += () => {
|
||
UpdateMonitorDropdown();
|
||
UpdateUI();
|
||
ShowEventMessage("分辨率已改变!");
|
||
ShowClientSize();
|
||
};
|
||
uniwinc.OnDropFiles += files =>
|
||
{
|
||
ShowEventMessage(string.Join(Environment.NewLine, files));
|
||
};
|
||
}
|
||
|
||
// 即使 UinWinC 未就绪也能运行的 Listener
|
||
showBorderlineToggle?.onValueChanged.AddListener(val => borderlinePanel.gameObject.SetActive(val));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示带超时功能的消息
|
||
/// </summary>
|
||
/// <param name="message"></param>
|
||
private void ShowEventMessage(string message)
|
||
{
|
||
lastEventOccurredTime = Time.time;
|
||
if (messageText) messageText.text = message;
|
||
|
||
Debug.Log(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 每帧执行的处理
|
||
/// </summary>
|
||
private void Update()
|
||
{
|
||
// 更新点击测试相关的显示
|
||
UpdateHitTestUI();
|
||
|
||
// 显示窗口位置和大小以确认运行状态
|
||
if ((lastEventOccurredTime + eventMessageTimeout) < Time.time)
|
||
{
|
||
ShowWindowMetrics();
|
||
}
|
||
|
||
// 鼠标右键点击显示菜单。移动距离在阈值以下视为点击。
|
||
if (InputProxy.GetMouseButtonDown(1))
|
||
{
|
||
lastMousePosition = InputProxy.mousePosition;
|
||
ResetTouchDuration();
|
||
}
|
||
if (InputProxy.GetMouseButton(1))
|
||
{
|
||
mouseMoveSS += (InputProxy.mousePosition - lastMousePosition).sqrMagnitude;
|
||
}
|
||
if (InputProxy.GetMouseButtonUp(1))
|
||
{
|
||
if (mouseMoveSS < mouseMoveSSThreshold)
|
||
{
|
||
ShowMenu(lastMousePosition);
|
||
}
|
||
mouseMoveSS = 0f;
|
||
ResetTouchDuration();
|
||
}
|
||
|
||
#if ENABLE_LEGACY_INPUT_MANAGER
|
||
// 长按也可显示菜单
|
||
if (Input.touchSupported && (Input.touchCount > 0))
|
||
{
|
||
Touch touch = Input.GetTouch(0);
|
||
if (touch.phase == TouchPhase.Began)
|
||
{
|
||
lastMousePosition = Input.mousePosition;
|
||
ResetTouchDuration();
|
||
}
|
||
if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
|
||
{
|
||
mouseMoveSS += touch.deltaPosition.sqrMagnitude;
|
||
touchDuration += touch.deltaTime;
|
||
}
|
||
if (touch.phase == TouchPhase.Ended)
|
||
{
|
||
if ((mouseMoveSS < mouseMoveSSThreshold) && (touchDuration >= touchDurationThreshold))
|
||
{
|
||
ShowMenu(lastMousePosition);
|
||
}
|
||
mouseMoveSS = 0f;
|
||
ResetTouchDuration();
|
||
}
|
||
}
|
||
#elif ENABLE_INPUT_SYSTEM
|
||
// 目前 New Input System 不支持触摸
|
||
// EnhancedTouch 不能与 InputAction 同时使用?
|
||
#endif
|
||
|
||
// 按键也可更改设置
|
||
if (uniwinc)
|
||
{
|
||
// 切换透明模式
|
||
if (InputProxy.GetKeyUp("t"))
|
||
{
|
||
uniwinc.isTransparent = !uniwinc.isTransparent;
|
||
}
|
||
|
||
// 切换始终置顶
|
||
if (InputProxy.GetKeyUp("f"))
|
||
{
|
||
uniwinc.isTopmost = !uniwinc.isTopmost;
|
||
}
|
||
|
||
// 切换始终置底
|
||
if (InputProxy.GetKeyUp("b"))
|
||
{
|
||
uniwinc.isBottommost = !uniwinc.isBottommost;
|
||
}
|
||
|
||
// 切换最大化
|
||
if (InputProxy.GetKeyUp("z"))
|
||
{
|
||
uniwinc.isZoomed = !uniwinc.isZoomed;
|
||
}
|
||
|
||
// 切换自由定位
|
||
if (InputProxy.GetKeyUp("p"))
|
||
{
|
||
uniwinc.isFreePositioningEnabled = !uniwinc.isFreePositioningEnabled;
|
||
}
|
||
}
|
||
|
||
|
||
// 测试打开文件面板
|
||
if (InputProxy.GetKeyUp("o"))
|
||
{
|
||
FilePanel.Settings ds = new FilePanel.Settings
|
||
{
|
||
flags = FilePanel.Flag.AllowMultipleSelection,
|
||
title = "打开!",
|
||
filters = new FilePanel.Filter[]{
|
||
new FilePanel.Filter("Image files", "png", "jpg", "jpeg"),
|
||
new FilePanel.Filter("All files", "*"),
|
||
},
|
||
initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
|
||
initialFile = "test.png",
|
||
};
|
||
FilePanel.OpenFilePanel(ds, (files) => ShowEventMessage(string.Join(Environment.NewLine, files)));
|
||
}
|
||
|
||
// 测试保存文件面板
|
||
if (InputProxy.GetKeyUp("s"))
|
||
{
|
||
FilePanel.Settings ds = new FilePanel.Settings
|
||
{
|
||
flags = FilePanel.Flag.AllowMultipleSelection,
|
||
title = "保存!",
|
||
filters = new FilePanel.Filter[]{
|
||
//// TODO: 指定文件类型时,macOS 保存对话框打开会失败
|
||
//// 给 NSSavePanel.accessoryView 指定内容时会发生此问题。
|
||
//// NSOpenPanel 继承自它,但不会发生此问题。
|
||
// new FilePanel.Filter("Shell script", "sh"),
|
||
// new FilePanel.Filter("Log", "log"),
|
||
// new FilePanel.Filter("Plain text", "txt"),
|
||
// new FilePanel.Filter("All files", "*"),
|
||
},
|
||
initialFile = "Test.txt",
|
||
initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
|
||
};
|
||
FilePanel.SaveFilePanel(ds, (files) => ShowEventMessage(string.Join(Environment.NewLine, files)));
|
||
}
|
||
|
||
// 按下 [ESC] 时退出或停止播放
|
||
if (InputProxy.GetKeyUp("escape"))
|
||
{
|
||
#if UNITY_EDITOR
|
||
UnityEditor.EditorApplication.isPlaying = false;
|
||
#else
|
||
Application.Quit();
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置持续触摸时间的记录
|
||
/// 仅在 Legacy Input Manager 中处理以避免警告
|
||
/// </summary>
|
||
void ResetTouchDuration() {
|
||
#if ENABLE_LEGACY_INPUT_MANAGER
|
||
touchDuration = 0f;
|
||
#endif
|
||
}
|
||
|
||
/// <summary>
|
||
/// 适配显示器下拉列表更改时的处理
|
||
/// </summary>
|
||
/// <param name="val"></param>
|
||
void SetFitToMonitor(int val)
|
||
{
|
||
if (!uniwinc) return;
|
||
|
||
if (val < 1)
|
||
{
|
||
// 下拉列表第一项为不适配
|
||
uniwinc.shouldFitMonitor = false;
|
||
|
||
// 允许更改最大化状态
|
||
if (zoomedToggle) zoomedToggle.interactable = true;
|
||
}
|
||
else
|
||
{
|
||
// 由于从第二项开始,显示器编号减1
|
||
uniwinc.monitorToFit = val - 1;
|
||
uniwinc.shouldFitMonitor = true; // 从false变为true时才会移动窗口,因此先指定显示器编号再更改
|
||
|
||
// 禁止更改最大化状态
|
||
if (zoomedToggle) zoomedToggle.interactable = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示窗口位置和坐标
|
||
/// </summary>
|
||
void ShowWindowMetrics()
|
||
{
|
||
if (uniwinc)
|
||
{
|
||
var winPos = uniwinc.windowPosition;
|
||
//var curPos = uniwinc.GetClientCursorPosition();
|
||
OutputMessage(
|
||
"位置: " + winPos
|
||
+ "\n大小: " + uniwinc.windowSize
|
||
+ "\n相对鼠标:" + (uniwinc.cursorPosition - winPos)
|
||
//+ "\nScr. Cur.:" + curPos
|
||
+ "\nUnity鼠标:" + (Vector2)InputProxy.mousePosition
|
||
);
|
||
ShowClientSize();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 聚焦时刷新 UI
|
||
/// </summary>
|
||
/// <param name="hasFocus"></param>
|
||
private void OnApplicationFocus(bool hasFocus)
|
||
{
|
||
if (hasFocus)
|
||
{
|
||
UpdateUI();
|
||
|
||
if (uniwinc)
|
||
{
|
||
OutputMessage("已聚焦");
|
||
}
|
||
else
|
||
{
|
||
OutputMessage("未找到 UniWindowController");
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在指定坐标显示上下文菜单
|
||
/// </summary>
|
||
/// <param name="position">指定中心坐标</param>
|
||
private void ShowMenu(Vector2 position)
|
||
{
|
||
if (menuPanel)
|
||
{
|
||
Vector2 pos = position * (canvasRect.sizeDelta.x / Screen.width);
|
||
float w = menuPanel.rect.width;
|
||
float h = menuPanel.rect.height;
|
||
|
||
// 以指定坐标为中心进行位置调整
|
||
pos.y = Mathf.Max(Mathf.Min(pos.y, Screen.height - h / 2f), h / 2f); // 如果超出则向上移动
|
||
pos.x = Mathf.Max(Mathf.Min(pos.x, Screen.width - w / 2f), w / 2f); // 如果超出右侧则向左移动
|
||
|
||
menuPanel.pivot = Vector2.one * 0.5f; // 设置为中心
|
||
menuPanel.anchorMin = Vector2.zero;
|
||
menuPanel.anchorMax = Vector2.zero;
|
||
menuPanel.anchoredPosition = pos;
|
||
menuPanel.gameObject.SetActive(true);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭上下文菜单
|
||
/// </summary>
|
||
private void CloseMenu()
|
||
{
|
||
if (menuPanel)
|
||
{
|
||
menuPanel.gameObject.SetActive(false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将实际状态反映到 UI 显示
|
||
/// </summary>
|
||
private void UpdateUI()
|
||
{
|
||
if (uniwinc)
|
||
{
|
||
if (transparentToggle)
|
||
{
|
||
transparentToggle.SetIsOnWithoutNotify(uniwinc.isTransparent);
|
||
}
|
||
|
||
if (alphaSlider)
|
||
{
|
||
alphaSlider.SetValueWithoutNotify(uniwinc.alphaValue);
|
||
}
|
||
|
||
if (topmostToggle)
|
||
{
|
||
topmostToggle.SetIsOnWithoutNotify(uniwinc.isTopmost);
|
||
}
|
||
|
||
if (bottommostToggle)
|
||
{
|
||
bottommostToggle.SetIsOnWithoutNotify(uniwinc.isBottommost);
|
||
}
|
||
|
||
if (zoomedToggle)
|
||
{
|
||
zoomedToggle.SetIsOnWithoutNotify(uniwinc.isZoomed);
|
||
}
|
||
|
||
if (allowDropToggle)
|
||
{
|
||
allowDropToggle.SetIsOnWithoutNotify(uniwinc.allowDropFiles);
|
||
}
|
||
|
||
if (dragMoveToggle)
|
||
{
|
||
dragMoveToggle.isOn = (uniWinMoveHandle && uniWinMoveHandle.isActiveAndEnabled);
|
||
}
|
||
|
||
if (fitWindowDropdown)
|
||
{
|
||
if (uniwinc.shouldFitMonitor)
|
||
{
|
||
fitWindowDropdown.value = uniwinc.monitorToFit + 1;
|
||
if (zoomedToggle) zoomedToggle.interactable = false;
|
||
}
|
||
else
|
||
{
|
||
fitWindowDropdown.value = 0;
|
||
if (zoomedToggle) zoomedToggle.interactable = true;
|
||
}
|
||
fitWindowDropdown.RefreshShownValue();
|
||
}
|
||
|
||
if (transparentTypeDropdown)
|
||
{
|
||
transparentTypeDropdown.value = (int)uniwinc.transparentType;
|
||
transparentTypeDropdown.RefreshShownValue();
|
||
}
|
||
|
||
|
||
if (hitTestTypeDropdown)
|
||
{
|
||
hitTestTypeDropdown.value = (int)uniwinc.hitTestType;
|
||
hitTestTypeDropdown.RefreshShownValue();
|
||
}
|
||
|
||
// 同时更新点击测试部分的显示
|
||
UpdateHitTestUI();
|
||
}
|
||
|
||
// 即使没有 UniWinC 也能运行的部分
|
||
if (showBorderlineToggle && borderlinePanel)
|
||
{
|
||
borderlinePanel.gameObject.SetActive(showBorderlineToggle.isOn);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新点击测试相关的 UI
|
||
/// 由于会自动变化,需要比 UpdateUI() 更频繁地更新
|
||
/// </summary>
|
||
public void UpdateHitTestUI()
|
||
{
|
||
if (uniwinc)
|
||
{
|
||
if (clickThroughToggle)
|
||
{
|
||
clickThroughToggle.SetIsOnWithoutNotify(uniwinc.isClickThrough);
|
||
if (uniwinc.hitTestType == UniWindowController.HitTestType.None)
|
||
{
|
||
clickThroughToggle.interactable = true;
|
||
}
|
||
else
|
||
{
|
||
clickThroughToggle.interactable = false;
|
||
}
|
||
}
|
||
|
||
if (uniwinc.hitTestType == UniWindowController.HitTestType.Opacity && uniwinc.isTransparent)
|
||
{
|
||
if (pickedColorImage)
|
||
{
|
||
pickedColorImage.color = uniwinc.pickedColor;
|
||
}
|
||
|
||
if (pickedColorText)
|
||
{
|
||
pickedColorText.text = $"Alpha:{uniwinc.pickedColor.a:P0}";
|
||
pickedColorText.color = Color.black;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (pickedColorImage)
|
||
{
|
||
pickedColorImage.color = Color.gray;
|
||
}
|
||
|
||
if (pickedColorText)
|
||
{
|
||
pickedColorText.text = $"颜色拾取器已禁用";
|
||
pickedColorText.color = Color.gray;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新显示器选择下拉列表的选项
|
||
/// 之后需要调用 UpdateUI()
|
||
/// </summary>
|
||
void UpdateMonitorDropdown()
|
||
{
|
||
if (!fitWindowDropdown) return;
|
||
|
||
// 删除除第一项以外的选项
|
||
fitWindowDropdown.options.RemoveRange(1, fitWindowDropdown.options.Count - 1);
|
||
|
||
if (!uniwinc)
|
||
{
|
||
fitWindowDropdown.value = 0;
|
||
}
|
||
else
|
||
{
|
||
int count = UniWindowController.GetMonitorCount();
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
fitWindowDropdown.options.Add(new Dropdown.OptionData("适配显示器 " + i));
|
||
}
|
||
if (uniwinc.monitorToFit >= count)
|
||
{
|
||
uniwinc.monitorToFit = count - 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 如果 UI 中有文本框,则显示消息。否则输出到控制台
|
||
/// </summary>
|
||
/// <param name="text"></param>
|
||
public void OutputMessage(string text)
|
||
{
|
||
if (messageText)
|
||
{
|
||
messageText.text = text;
|
||
}
|
||
else
|
||
{
|
||
Debug.Log(text);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 如果 UI 中有客户端大小文本框,则显示消息。否则输出到控制台
|
||
/// </summary>
|
||
public void ShowClientSize()
|
||
{
|
||
if (!uniwinc) return;
|
||
|
||
string text = "Client " + uniwinc.clientSize;
|
||
if (clientSizeText)
|
||
{
|
||
clientSizeText.text = text;
|
||
}
|
||
else
|
||
{
|
||
Debug.Log(text);
|
||
}
|
||
}
|
||
}
|
||
}
|