This commit is contained in:
黄嘉宇 2024-05-24 08:46:25 +08:00
commit 40fea0b5e3
118 changed files with 801 additions and 33994 deletions

1
.gitignore vendored
View File

@ -21,4 +21,3 @@
/SXElectricalInspection/UserSettings
/SXElectricalInspection/Assets/Vuplex
/SXElectricalInspection/Assets/ZFBrowser
/SXElectricalInspection/SXYD

View File

@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
//============================================================
//支持中文文件使用UTF-8编码
@ -20,6 +21,7 @@ namespace Components
public bool isMove = false;
public bool isOnce = false;
public bool isLooked = true;
public TextMeshPro customTip;
// Use this for initialization
private void Start()
{
@ -27,6 +29,8 @@ namespace Components
{
waypoints[i].GetComponent<MeshRenderer>().enabled = false;
}
customTip = GetComponentInChildren<TextMeshPro>();
customTip.text = $"客户";
}
private void Update()
{
@ -52,7 +56,7 @@ namespace Components
}
else
{
if (isMove && !isOnce)
{
npcAnimator.SetBool("isWalk", false);

File diff suppressed because one or more lines are too long

View File

@ -84241,6 +84241,112 @@ GameObject:
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1001 &948269265946380923
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 8536413114597608435}
m_Modifications:
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.y
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.z
value: 0.005
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.w
value: 0.08386724
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.y
value: 0.99647695
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0.031
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0.004
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 170.378
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3992741034969977438, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_text
value: "\u5BA2\u6237\uFF1A\u5F20\u4E09\u4E09"
objectReference: {fileID: 0}
- target: {fileID: 9071954252016149054, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Name
value: CustomTips
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
--- !u!224 &948269265946380924 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
m_PrefabInstance: {fileID: 948269265946380923}
m_PrefabAsset: {fileID: 0}
--- !u!4 &949679825232361145
Transform:
m_ObjectHideFlags: 0
@ -138134,6 +138240,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8536413112843351733}
- {fileID: 948269265946380924}
m_Father: {fileID: 543450652020508569}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -28194,6 +28194,108 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 556673683}
m_Mesh: {fileID: -2698334616775272820, guid: f0d39a02f6856e347a9d428d34dae52c, type: 3}
--- !u!1001 &557124774
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 1485847831}
m_Modifications:
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.y
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.z
value: 0.005
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.w
value: 0.08386724
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.y
value: 0.99647695
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0.031
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0.004
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 170.378
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9071954252016149054, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Name
value: CustomTips
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
--- !u!224 &557124775 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
m_PrefabInstance: {fileID: 557124774}
m_PrefabAsset: {fileID: 0}
--- !u!1 &560058967
GameObject:
m_ObjectHideFlags: 0
@ -78228,6 +78330,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1581498715}
- {fileID: 557124775}
m_Father: {fileID: 543450652428544425}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -79690,6 +79690,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1581498715}
- {fileID: 1648933296}
m_Father: {fileID: 543450652428544425}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -89093,6 +89094,108 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1646650562}
m_Mesh: {fileID: -6040273486352078962, guid: d6187d5c5284e6241bed7be6c0b0f50d, type: 3}
--- !u!1001 &1648933295
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 1485847831}
m_Modifications:
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.y
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.z
value: 0.005
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.w
value: 0.08386724
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.y
value: 0.99647695
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0.031
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0.004
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 170.378
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9071954252016149054, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Name
value: CustomTips
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
--- !u!224 &1648933296 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
m_PrefabInstance: {fileID: 1648933295}
m_PrefabAsset: {fileID: 0}
--- !u!1 &1650584439
GameObject:
m_ObjectHideFlags: 0

File diff suppressed because it is too large Load Diff

View File

@ -10969,6 +10969,108 @@ MeshFilter:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 220181192}
m_Mesh: {fileID: 798797162813015926, guid: f0d39a02f6856e347a9d428d34dae52c, type: 3}
--- !u!1001 &220592146
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 1485847831}
m_Modifications:
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMax.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchorMin.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_SizeDelta.y
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalPosition.z
value: 0.005
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.w
value: 0.08386724
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.y
value: 0.99647695
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0.031
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0.004
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 170.378
objectReference: {fileID: 0}
- target: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9071954252016149054, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
propertyPath: m_Name
value: CustomTips
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
--- !u!224 &220592147 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 2319937656742358519, guid: d900a32d5d0bf254490837e7d5d88d60, type: 3}
m_PrefabInstance: {fileID: 220592146}
m_PrefabAsset: {fileID: 0}
--- !u!1 &221445124
GameObject:
m_ObjectHideFlags: 0
@ -77791,6 +77893,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1581498715}
- {fileID: 220592147}
m_Father: {fileID: 543450652428544425}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -114216,6 +114319,7 @@ MonoBehaviour:
isMove: 0
isOnce: 0
isLooked: 1
customTip: {fileID: 0}
--- !u!4 &919491327080544340
Transform:
m_ObjectHideFlags: 0

View File

@ -1 +1 @@
Build from LGZN_H at 2024/5/23 10:13:34
Build from ADAM at 2024/5/23 15:59:05

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 56b8faaae4b14ef46be6a4552ecae7fb
folderAsset: yes
timeCreated: 1447088713
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,10 +0,0 @@
using System;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
public class FlagsFieldAttribute : PropertyAttribute {}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 578312a754d9da142ad199b54e09488f
timeCreated: 1450462477
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 35bf0bd4ded61a34f9be34f6738e4010
timeCreated: 1447801917
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 10
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,216 +0,0 @@

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using AOT;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Callbacks used by Browser.cs
/// Man, that file's getting big. (Breaking it up would likely involve backwards-incompatible API changes, so
/// let it be for now.)
/// </summary>
public partial class Browser {
/// <summary>
/// Map of browserId => Browser fro looking up C# objects when we get a native callback.
///
/// Note that this reflects the native state, that is, we include Browsers that havne't fully initialized yet,
/// but instead list what we need to lookup callbacks coming from the native side.
///
/// (We used to rely on closures and generated trampolines for this data, but il2cpp doens't support that.)
/// </summary>
internal static Dictionary<int, Browser> allBrowsers = new Dictionary<int, Browser>();
private static Browser GetBrowser(int browserId) {
lock (allBrowsers) {
Browser ret;
if (allBrowsers.TryGetValue(browserId, out ret)) return ret;
}
Debug.LogWarning("Got a callback for brower id " + browserId + " which doesn't exist.");
return null;
}
[MonoPInvokeCallback(typeof(BrowserNative.ForwardJSCallFunc))]
private static void CB_ForwardJSCallFunc(int browserId, int callbackId, string data, int size) {
var browser = GetBrowser(browserId);
if (!browser) return;
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
JSResultFunc cb;
if (!browser.registeredCallbacks.TryGetValue(callbackId, out cb)) {
Debug.LogWarning("Got a JS callback for event " + callbackId + ", but no such event is registered.");
return;
}
var isError = false;
if (data.StartsWith("fail-")) {
isError = true;
data = data.Substring(5);
}
JSONNode node;
try {
node = JSONNode.Parse(data);
} catch (SerializationException) {
Debug.LogWarning("Invalid JSON sent from browser: " + data);
return;
}
try {
cb(node, isError);
} catch (Exception ex) {
//user's function died, log it and carry on
Debug.LogException(ex);
return;
}
});
}
[MonoPInvokeCallback(typeof(BrowserNative.ChangeFunc))]
private static void CB_ChangeFunc(int browserId, BrowserNative.ChangeType changeType, string arg1) {
//Note: we may have been Object.Destroy'd at this point, so guard against that.
//That means we can't trust that `browser == null` means we don't have a browser, we may have an object that
//is destroyed, but not actually null.
Browser browser;
lock (allBrowsers) {
if (!allBrowsers.TryGetValue(browserId, out browser)) return;
}
if (changeType == BrowserNative.ChangeType.CHT_BROWSER_CLOSE) {
//We can't continue if the browser is closed, so goodbye.
//At this point, we may or may not be destroyed, but if not, become destroyed.
//Debug.Log("Got close notification for " + unsafeBrowserId);
if (browser) {
//Need to be destroyed.
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
Destroy(browser.gameObject);
});
} else {
//If we are (Unity) destroyed, we won't get another update, so we can't rely on thingsToDo
//That said, there's not anything else for us to do but step out of allThingsToRemember.
}
//The native side has acknowledged it's done, now we can finally let the native trampolines be GC'd
lock (allThingsToRemember) allThingsToRemember.Remove(browser.unsafeBrowserId);
//Now that we know we can allow ourselves to be GC'd and destroyed (without leaking to allThingsToRemember)
//go ahead and allow it. (And we won't get any more native callbacks at this point.)
lock (allBrowsers) allBrowsers.Remove(browserId);
//Just in case someone tries to call something, make sure CheckSanity and such fail.
browser.browserId = 0;
} else if (browser) {
lock (browser.thingsToDo) browser.thingsToDo.Add(() => browser.OnItemChange(changeType, arg1));
}
}
[MonoPInvokeCallback(typeof(BrowserNative.DisplayDialogFunc))]
private static void CB_DisplayDialogFunc(
int browserId, BrowserNative.DialogType dialogType, IntPtr textPtr,
IntPtr promptTextPtr, IntPtr sourceURL
) {
var browser = GetBrowser(browserId);
if (!browser) return;
var text = Util.PtrToStringUTF8(textPtr);
var promptText = Util.PtrToStringUTF8(promptTextPtr);
//var url = Util.PtrToStringUTF8(urlPtr);
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
browser.CreateDialogHandler();
browser.dialogHandler.HandleDialog(dialogType, text, promptText);
});
}
[MonoPInvokeCallback(typeof(BrowserNative.ShowContextMenuFunc))]
private static void CB_ShowContextMenuFunc(
int browserId, string json, int x, int y, BrowserNative.ContextMenuOrigin origin
) {
var browser = GetBrowser(browserId);
if (!browser) return;
if (json != null && (browser.allowContextMenuOn & origin) == 0) {
//ignore this
BrowserNative.zfb_sendContextMenuResults(browserId, -1);
return;
}
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
if (json != null) browser.CreateDialogHandler();
if (browser.dialogHandler != null) browser.dialogHandler.HandleContextMenu(json, x, y);
});
}
[MonoPInvokeCallback(typeof(BrowserNative.ConsoleFunc))]
private static void CB_ConsoleFunc(int browserId, string message, string source, int line) {
var browser = GetBrowser(browserId);
if (!browser) return;
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
browser.onConsoleMessage(message, source + ":" + line);
});
}
[MonoPInvokeCallback(typeof(BrowserNative.ReadyFunc))]
private static void CB_ReadyFunc(int browserId) {
var browser = GetBrowser(browserId);
if (!browser) return;
//We could be on any thread at this time, so schedule the callbacks to fire during the next InputUpdate
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
browser.browserId = browserId;
// ReSharper disable once PossibleNullReferenceException
browser.onNativeReady(browserId);
});
}
[MonoPInvokeCallback(typeof(BrowserNative.NavStateFunc))]
private static void CB_NavStateFunc(int browserId, bool canGoBack, bool canGoForward, bool lodaing, IntPtr urlRaw) {
var browser = GetBrowser(browserId);
if (!browser) return;
var url = Util.PtrToStringUTF8(urlRaw);
lock (browser.thingsToDo) browser.thingsToDo.Add(() => {
browser.navState.canGoBack = canGoBack;
browser.navState.canGoForward = canGoForward;
browser.navState.loading = lodaing;
browser.navState.url = url;
browser._url = url;//update the inspector
browser.onNavStateChange();
});
}
[MonoPInvokeCallback(typeof(BrowserNative.NewWindowFunc))]
private static void CB_NewWindowFunc(int creatorBrowserId, int newBrowserId, IntPtr urlPtr) {
var browser = GetBrowser(creatorBrowserId);
if (!browser) return;
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
var url = Util.PtrToStringUTF8(urlPtr);
if (url == "about:inspector" || browser.newWindowAction == NewWindowAction.NewWindow) lock (browser.thingsToDo) {
browser.thingsToDo.Add(() => {
PopUpBrowser.Create(newBrowserId);
});
return;
}
#endif
lock (browser.thingsToDo) {
browser.thingsToDo.Add(() => {
var newBrowser = browser.newWindowHandler.CreateBrowser(browser);
newBrowser.RequestNativeBrowser(newBrowserId);
});
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a30036008e7d3434291fcfba205bf239
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,170 +0,0 @@
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CursorType = ZenFulcrum.EmbeddedBrowser.BrowserNative.CursorType;
using Object = UnityEngine.Object;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Manages finding and copying cursors for you.
* Each instance has one active cursor and a Texture2D you can read from to use it.
*/
public class BrowserCursor {
public class CursorInfo {
public int atlasOffset;
public Vector2 hotspot;
}
private static Dictionary<CursorType, CursorInfo> mapping = new Dictionary<CursorType, CursorInfo>();
private static bool loaded = false;
private static int size;
private static Texture2D allCursors;
/// <summary>
/// Fired when the mouse cursor's appearance or hotspot changes.
/// Also fired when the mouse enters/leaves the browser.
/// </summary>
public event Action cursorChange = () => {};
private static void Load() {
if (loaded) return;
allCursors = Resources.Load<Texture2D>("Browser/Cursors");
if (!allCursors) throw new Exception("Failed to find browser allCursors");
size = allCursors.height;
var listObj = Resources.Load<TextAsset>("Browser/Cursors");
foreach (var row in listObj.text.Split('\n')) {
var info = row.Split(',');
var k = (CursorType)Enum.Parse(typeof(CursorType), info[0]);
var v = new CursorInfo() {
atlasOffset = int.Parse(info[1]),
hotspot = new Vector2(int.Parse(info[2]), int.Parse(info[3])),
};
mapping[k] = v;
}
loaded = true;
}
/**
* Texture for the current cursor.
* If the cursor should be hidden, this will be null.
*/
public virtual Texture2D Texture { get; protected set; }
/**
* Hotspot for the current cursor. (0, 0) indicates the top-left of the texture is the hotspot.
* (1, 1) indicates the bottom-right.
*/
public virtual Vector2 Hotspot { get; protected set; }
private bool _hasMouse;
/// <summary>
/// True when the mouse is over the browser, false otherwise.
/// </summary>
public bool HasMouse {
get {
return _hasMouse;
}
set {
_hasMouse = value;
cursorChange();
}
}
protected Texture2D normalTexture;
protected Texture2D customTexture;
public BrowserCursor() {
Load();
normalTexture = new Texture2D(size, size, TextureFormat.ARGB32, false);
#if UNITY_EDITOR
normalTexture.alphaIsTransparency = true;
#endif
SetActiveCursor(BrowserNative.CursorType.Pointer);
}
/// <summary>
/// Switches the active cursor type. After calling this you can access the cursor image through this.Texture.
/// </summary>
/// <param name="type"></param>
public virtual void SetActiveCursor(CursorType type) {
if (type == CursorType.Custom) throw new ArgumentException("Use SetCustomCursor to set custom cursors.", "type");
if (type == CursorType.None) {
Texture = null;
//Side note: if you copy down a bunch of transparent pixels and try to set the mouse cursor to that
//both OS X and Windows fail to do what you'd expect.
//Edit: OS X is now crashing for me if you try to do that.
cursorChange();
return;
}
var info = mapping[type];
var pixelData = allCursors.GetPixels(info.atlasOffset * size, 0, size, size);
Hotspot = info.hotspot;
normalTexture.SetPixels(pixelData);
normalTexture.Apply(true);
Texture = normalTexture;
cursorChange();
}
/// <summary>
/// Sets a custom cursor.
/// </summary>
/// <param name="cursor">ARGB texture to set</param>
/// <param name="hotspot"></param>
public virtual void SetCustomCursor(Texture2D cursor, Vector2 hotspot) {
var pixels = cursor.GetPixels32();
//First off, is it completely blank? 'Cuz if so that can cause OS X to crash.
var hasData = false;
for (int i = 0; i < pixels.Length; i++) {
if (pixels[i].a != 0) {
hasData = true;
break;
}
}
if (!hasData) {
//it's blank, so handle it like a regular blank cursor
SetActiveCursor(CursorType.None);
return;
}
if (!customTexture || customTexture.width != cursor.width || customTexture.height != cursor.height) {
Object.Destroy(customTexture);
customTexture = new Texture2D(cursor.width, cursor.height, TextureFormat.ARGB32, false);
#if UNITY_EDITOR
customTexture.alphaIsTransparency = true;
#endif
}
customTexture.SetPixels32(pixels);
customTexture.Apply(true);
this.Hotspot = hotspot;
Texture = customTexture;
cursorChange();
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: d74b3f6909bb41f4c86d03a80156416f
timeCreated: 1448061720
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,335 +0,0 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
#define ZF_OSX
#endif
#if UNITY_EDITOR_LINX || UNITY_STANDALONE_LINUX
#define ZF_LINUX
#endif
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/** Helper class for reading data from an IUIHandler, converting it, and feeding it to the native backend. */
internal class BrowserInput {
private readonly Browser browser;
public BrowserInput(Browser browser) {
this.browser = browser;
}
private bool kbWasFocused = false;
private bool mouseWasFocused = false;
public void HandleInput() {
browser.UIHandler.InputUpdate();
bool focusChanged = false;
if (browser.UIHandler.MouseHasFocus || mouseWasFocused) {
HandleMouseInput();
}
if (browser.UIHandler.MouseHasFocus != mouseWasFocused) {
browser.UIHandler.BrowserCursor.HasMouse = browser.UIHandler.MouseHasFocus;
focusChanged = true;
}
mouseWasFocused = browser.UIHandler.MouseHasFocus;
if (kbWasFocused != browser.UIHandler.KeyboardHasFocus) focusChanged = true;
if (browser.UIHandler.KeyboardHasFocus) {
if (!kbWasFocused) {
BrowserNative.zfb_setFocused(browser.browserId, kbWasFocused = true);
}
HandleKeyInput();
} else {
if (kbWasFocused) {
BrowserNative.zfb_setFocused(browser.browserId, kbWasFocused = false);
}
}
if (focusChanged) {
browser._RaiseFocusEvent(browser.UIHandler.MouseHasFocus, browser.UIHandler.KeyboardHasFocus);
}
}
private static HashSet<KeyCode> keysToReleaseOnFocusLoss = new HashSet<KeyCode>();
public List<Event> extraEventsToInject = new List<Event>();
private MouseButton prevButtons = 0;
private Vector2 prevPos;
private class ButtonHistory {
public float lastPressTime;
public int repeatCount;
public Vector3 lastPosition;
public void ButtonPress(Vector3 mousePos, IBrowserUI uiHandler, Vector2 browserSize) {
var now = Time.realtimeSinceStartup;
if (now - lastPressTime > uiHandler.InputSettings.multiclickSpeed) {
//too long ago? forget the past
repeatCount = 0;
}
if (repeatCount > 0) {
//close enough to be a multiclick?
var p1 = Vector2.Scale(mousePos, browserSize);
var p2 = Vector2.Scale(lastPosition, browserSize);
if (Vector2.Distance(p1, p2) > uiHandler.InputSettings.multiclickTolerance) {
repeatCount = 0;
}
}
repeatCount++;
lastPressTime = now;
lastPosition = mousePos;
}
}
private readonly ButtonHistory leftClickHistory = new ButtonHistory();
private void HandleMouseInput() {
var handler = browser.UIHandler;
var mousePos = handler.MousePosition;
var currentButtons = handler.MouseButtons;
var mouseScroll = handler.MouseScroll;
if (mousePos != prevPos) {
BrowserNative.zfb_mouseMove(browser.browserId, mousePos.x, 1 - mousePos.y);
}
FeedScrolling(mouseScroll, handler.InputSettings.scrollSpeed);
var leftChange = (prevButtons & MouseButton.Left) != (currentButtons & MouseButton.Left);
var leftDown = (currentButtons & MouseButton.Left) == MouseButton.Left;
var middleChange = (prevButtons & MouseButton.Middle) != (currentButtons & MouseButton.Middle);
var middleDown = (currentButtons & MouseButton.Middle) == MouseButton.Middle;
var rightChange = (prevButtons & MouseButton.Right) != (currentButtons & MouseButton.Right);
var rightDown = (currentButtons & MouseButton.Right) == MouseButton.Right;
if (leftChange) {
if (leftDown) leftClickHistory.ButtonPress(mousePos, handler, browser.Size);
BrowserNative.zfb_mouseButton(
browser.browserId, BrowserNative.MouseButton.MBT_LEFT, leftDown,
leftDown ? leftClickHistory.repeatCount : 0
);
}
if (middleChange) {
//no double-clicks, to be consistent with other browsers
BrowserNative.zfb_mouseButton(
browser.browserId, BrowserNative.MouseButton.MBT_MIDDLE, middleDown, 1
);
}
if (rightChange) {
//no double-clicks, to be consistent with other browsers
BrowserNative.zfb_mouseButton(
browser.browserId, BrowserNative.MouseButton.MBT_RIGHT, rightDown, 1
);
}
prevPos = mousePos;
prevButtons = currentButtons;
}
private Vector2 accumulatedScroll;
private float lastScrollEvent;
/// <summary>
/// How often (in sec) we can send scroll events to the browser without it choking up.
/// The right number seems to depend on how hard the page is to render, so there's not a perfect number.
/// Hopefully this one works well, though.
/// </summary>
private const float maxScrollEventRate = 1 / 15f;
/// <summary>
/// Feeds scroll events to the browser.
/// In particular, it will clump together scrolling "floods" into fewer larger scrolls
/// to prevent the backend from getting choked up and taking forever to execute the requests.
/// </summary>
/// <param name="mouseScroll"></param>
private void FeedScrolling(Vector2 mouseScroll, float scrollSpeed) {
accumulatedScroll += mouseScroll * scrollSpeed;
if (accumulatedScroll.sqrMagnitude != 0 && Time.realtimeSinceStartup > lastScrollEvent + maxScrollEventRate) {
//Debug.Log("Do scroll: " + accumulatedScroll);
//The backend seems to have trouble coping with horizontal AND vertical scroll. So only do one at a time.
//(And if we do both at once, vertical appears to get priority and horizontal gets ignored.)
if (Mathf.Abs(accumulatedScroll.x) > Mathf.Abs(accumulatedScroll.y)) {
BrowserNative.zfb_mouseScroll(browser.browserId, (int)accumulatedScroll.x, 0);
accumulatedScroll.x = 0;
accumulatedScroll.y = Mathf.Round(accumulatedScroll.y * .5f);//reduce the thing we weren't doing so it's less likely to accumulate strange
} else {
BrowserNative.zfb_mouseScroll(browser.browserId, 0, (int)accumulatedScroll.y);
accumulatedScroll.x = Mathf.Round(accumulatedScroll.x * .5f);
accumulatedScroll.y = 0;
}
lastScrollEvent = Time.realtimeSinceStartup;
}
}
private void HandleKeyInput() {
var keyEvents = browser.UIHandler.KeyEvents;
if (keyEvents.Count > 0) HandleKeyInput(keyEvents);
if (extraEventsToInject.Count > 0) {
HandleKeyInput(extraEventsToInject);
extraEventsToInject.Clear();
}
}
private void HandleKeyInput(List<Event> keyEvents) {
#if ZF_OSX
ReconstructInputs(keyEvents);
#endif
foreach (var ev in keyEvents) {
var keyCode = KeyMappings.GetWindowsKeyCode(ev);
if (ev.character == '\n') ev.character = '\r';//'cuz that's what Chromium expects
if (ev.character == 0) {
if (ev.type == EventType.KeyDown) keysToReleaseOnFocusLoss.Add(ev.keyCode);
else keysToReleaseOnFocusLoss.Remove(ev.keyCode);
}
// if (false) {
// if (ev.character != 0) Debug.Log("k >>> " + ev.character);
// else if (ev.type == EventType.KeyUp) Debug.Log("k ^^^ " + ev.keyCode);
// else if (ev.type == EventType.KeyDown) Debug.Log("k vvv " + ev.keyCode);
// }
FireCommands(ev);
if (ev.character != 0 && ev.type == EventType.KeyDown) {
#if ZF_LINUX
//It seems, on Linux, we don't get keydown, keypress, keyup, we just get a keypress, keyup.
//So, fire the keydown just before the keypress.
BrowserNative.zfb_keyEvent(browser.browserId, true, keyCode);
//Thanks for being consistent, Unity.
#endif
BrowserNative.zfb_characterEvent(browser.browserId, ev.character, keyCode);
} else {
BrowserNative.zfb_keyEvent(browser.browserId, ev.type == EventType.KeyDown, keyCode);
}
}
}
public void HandleFocusLoss() {
foreach (var keyCode in keysToReleaseOnFocusLoss) {
//Debug.Log("Key " + keyCode + " is held, release");
var wCode = KeyMappings.GetWindowsKeyCode(new Event() { keyCode = keyCode });
BrowserNative.zfb_keyEvent(browser.browserId, false, wCode);
}
keysToReleaseOnFocusLoss.Clear();
}
#if ZF_OSX
/** Used by ReconstructInputs */
protected HashSet<KeyCode> pressedKeys = new HashSet<KeyCode>();
/**
* OS X + Unity has issues.
*
* Mac editor: If I press cmd+A: The "keydown A" event doesn't get sent,
* though we do get a keypress A and a keyup A.
* Mac player: We get duplicate keyUPs normally. If cmd is down we get duplicate keyDOWNs instead.
*/
protected void ReconstructInputs(List<Event> keyEvents) {
for (int i = 0; i < keyEvents.Count; ++i) {//int loop, not iterator, we mutate during iteration
var ev = keyEvents[i];
if (ev.type == EventType.KeyDown && ev.character == 0) {
pressedKeys.Add(ev.keyCode);
//Repeated keydown events sent in the same frame are always bogus (but valid if in different
//frames for held key repeats)
//Remove duplicated key down events in this tick.
for (int j = i + 1; j < keyEvents.Count; ++j) {
if (keyEvents[j].Equals(ev)) keyEvents.RemoveAt(j--);
}
} else if (ev.type == EventType.KeyDown) {
//key down with character.
//...did the key actually get pressed, though?
if (ev.keyCode != KeyCode.None && !pressedKeys.Contains(ev.keyCode)) {
//no. insert a keydown before the press
var downEv = new Event(ev) {
type = EventType.KeyDown,
character = (char)0
};
keyEvents.Insert(i++, downEv);
pressedKeys.Add(ev.keyCode);
}
} else if (ev.type == EventType.KeyUp) {
if (!pressedKeys.Contains(ev.keyCode)) {
//Ignore duplicate key up events
keyEvents.RemoveAt(i--);
}
pressedKeys.Remove(ev.keyCode);
}
}
}
#endif
/**
* OS X + Unity has issues.
* Commands won't be run if the command is not in the application menu.
* Here we trap keystrokes and manually fire the relevant events in the browser.
*
* Also, ctrl+A stopped working with CEF at some point on Windows.
*/
protected void FireCommands(Event ev) {
#if ZF_OSX
if (ev.type != EventType.KeyDown || ev.character != 0 || !ev.command) return;
switch (ev.keyCode) {
case KeyCode.C:
browser.SendFrameCommand(BrowserNative.FrameCommand.Copy);
break;
case KeyCode.X:
browser.SendFrameCommand(BrowserNative.FrameCommand.Cut);
break;
case KeyCode.V:
browser.SendFrameCommand(BrowserNative.FrameCommand.Paste);
break;
case KeyCode.A:
browser.SendFrameCommand(BrowserNative.FrameCommand.SelectAll);
break;
case KeyCode.Z:
if (ev.shift) browser.SendFrameCommand(BrowserNative.FrameCommand.Redo);
else browser.SendFrameCommand(BrowserNative.FrameCommand.Undo);
break;
case KeyCode.Y:
//I, for one, prefer Y for redo, but shift+Z is more idiomatic on OS X
//Support both.
browser.SendFrameCommand(BrowserNative.FrameCommand.Redo);
break;
}
#else
//mmm, yeah. I guess Unity doesn't send us the keydown on a ctrl+a keystroke anymore.
if (ev.type != EventType.KeyUp || !ev.control) return;
switch (ev.keyCode) {
case KeyCode.A:
browser.SendFrameCommand(BrowserNative.FrameCommand.SelectAll);
break;
}
#endif
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: d00cb9636aac20d4989efeaf7de81909
timeCreated: 1450218467
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 78ee81db88d224e40843c8e826697dee
timeCreated: 1447282504
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,28 +0,0 @@
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Helper class for setting options on the browser system as a whole berfore the
/// backend initalizes.
/// </summary>
public class BrowserSystemSettings : MonoBehaviour {
[Tooltip("Where should we save the user's web profile (cookies, localStorage, etc.)? Leave blank to forget every times we restart.")]
public string profilePath;
[Tooltip("What user agent should we send to web pages when we request sites? Leave blank to use the default.")]
public string userAgent;
public void Awake() {
if (!string.IsNullOrEmpty(profilePath)) {
BrowserNative.ProfilePath = profilePath;
}
if (!string.IsNullOrEmpty(userAgent)) {
UserAgent.SetUserAgent(userAgent);
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 731482151f2ac2041ab65282156b1664
timeCreated: 1510797762
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: e460e45c0d5976544a2af3c86b00bdb9
folderAsset: yes
timeCreated: 1453258062
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,31 +0,0 @@
using System.Collections;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// These classes handle rendering the cursor of a browser.
///
/// Using one is optional. You can opt not to show a cursor.
/// </summary>
[RequireComponent(typeof(PointerUIBase))]
abstract public class CursorRendererBase : MonoBehaviour {
protected BrowserCursor cursor;
public virtual void OnEnable() {
StartCoroutine(Setup());
}
private IEnumerator Setup() {
if (cursor != null) yield break;
yield return null;//wait a frame to let the browser UI get set up
cursor = GetComponent<Browser>().UIHandler.BrowserCursor;
cursor.cursorChange += CursorChange;
}
protected abstract void CursorChange();
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 30820dfc981b58e48a678a9b0e422fef
timeCreated: 1511217990
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,42 +0,0 @@
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Handles rendering a browser's cursor by letting using t he OS-level mouse cursor
/// (and changing it as needed).
/// </summary>
public class CursorRendererOS : CursorRendererBase {
[Tooltip("If true, the mouse cursor should be visible when it's not on a browser.")]
public bool cursorNormallyVisible = true;
protected override void CursorChange() {
if (!cursor.HasMouse) {
//no browser, do default
Cursor.visible = cursorNormallyVisible;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
} else {
if (cursor.Texture != null) {
//browser, show this cursor
Cursor.visible = true;
Cursor.SetCursor(
cursor.Texture, cursor.Hotspot,
#if UNITY_STANDALONE_OSX
//Not sure why, but we get really ugly looking "garbled" shadows around the mouse cursor.
//I hate latency, but a software cursor is probably less irritating than looking at
//that ugly stuff.
CursorMode.ForceSoftware
#else
CursorMode.Auto
#endif
);
} else {
//browser, so no cursor
Cursor.visible = false;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
}
}
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: b27bad8d50722bb4083b964da1d1cae4
timeCreated: 1511217990
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,30 +0,0 @@
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Renders a browser's cursor by rendering something in the center of the screen.
/// </summary>
public class CursorRendererOverlay : CursorRendererBase {
[Tooltip("How large should we render the cursor?")]
public float scale = .5f;
protected override void CursorChange() {}
public void OnGUI() {
if (cursor == null) return;
if (!cursor.HasMouse || !cursor.Texture) return;
var tex = cursor.Texture;
var pos = new Rect(Screen.width / 2f, Screen.height / 2f, tex.width * scale, tex.height * scale);
pos.x -= cursor.Hotspot.x * scale;
pos.y -= cursor.Hotspot.y * scale;
GUI.DrawTexture(pos, tex);
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 62aba41510529884eaf9bdaa450168d7
timeCreated: 1511217990
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,81 +0,0 @@
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Renders a browser's cursor by add polygons to the scene offset from browser's face.
/// </summary>
public class CursorRendererWorldSpace : CursorRendererBase {
[Tooltip("How far to keep the cursor from the surface of the browser. Set it as low as you can without causing z-fighting." +
" (Note: The default cursor material will always render on top of everything, this is more useful if you use a different material.")]
public float zOffset = .005f;
[Tooltip("How large should the cursor be when rendered? (meters)")]
public float size = .1f;
private GameObject cursorHolder, cursorImage;
private PointerUIBase pointerUI;
private bool cursorVisible;
public void Awake() {
pointerUI = GetComponent<PointerUIBase>();
cursorHolder = new GameObject("Cursor for " + name);
cursorHolder.transform.localScale = new Vector3(size, size, size);
//If we place it in the parent, the scale can get wonky in some cases.
//so don't cursorHolder.transform.parent = transform;
cursorImage = GameObject.CreatePrimitive(PrimitiveType.Quad);
cursorImage.name = "Cursor Image";
cursorImage.transform.parent = cursorHolder.transform;
var mr = cursorImage.GetComponent<MeshRenderer>();
mr.sharedMaterial = Resources.Load<Material>("Browser/CursorDecal");
Destroy(cursorImage.GetComponent<Collider>());
cursorImage.transform.SetParent(cursorHolder.transform, false);
cursorImage.transform.localScale = new Vector3(1, 1, 1);
cursorHolder.SetActive(false);
}
protected override void CursorChange() {
if (cursor.HasMouse && cursor.Texture) {
cursorVisible = true;
var cursorRenderer = cursorImage.GetComponent<Renderer>();
cursorRenderer.material.mainTexture = cursor.Texture;
var hs = cursor.Hotspot;
cursorRenderer.transform.localPosition = new Vector3(
.5f - hs.x / cursor.Texture.width,
-.5f + hs.y / cursor.Texture.height,
0
);
} else {
cursorVisible = false;
}
}
public void LateUpdate() {
Vector3 pos;
Quaternion rot;
pointerUI.GetCurrentHitLocation(out pos, out rot);
if (float.IsNaN(pos.x)) {
cursorHolder.SetActive(false);
} else {
cursorHolder.SetActive(cursorVisible);
cursorHolder.transform.position = pos + rot * new Vector3(0, 0, -zOffset);
cursorHolder.transform.rotation = rot;
}
}
public void OnDestroy() {
Destroy(cursorHolder.gameObject);
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: b0f8a9c4898c5a644a048df52d9472fd
timeCreated: 1511217990
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,101 +0,0 @@
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace ZenFulcrum.EmbeddedBrowser {
[Flags]
public enum MouseButton {
Left = 0x1,
Middle = 0x2,
Right = 0x4,
}
public class BrowserInputSettings {
/**
* How fast do we scroll?
*/
public int scrollSpeed = 120;
/**
* How far can the cursor wander from its position before won't consider another click as a double/triple click?
* Value is number of pixels in browser space.
*/
public float multiclickTolerance = 6;
/**
* How long must we wait between clicks before we don't consider it a double/triple/etc. click?
* Measured in seconds.
*/
public float multiclickSpeed = .7f;
}
/**
* Proxy for browser input (and current mouse cursor).
* You can create your own implementation to take input however you'd like. To use your implementation,
* create a new instance and assign it to browser.UIHandler just after creating the browser.
*/
public interface IBrowserUI {
/** Called once per frame by the browser before fetching properties. */
void InputUpdate();
/**
* Returns true if the browser will be getting mouse events. Typically this is true when the mouse if over the browser.
*
* If this is false, the Mouse* properties will be ignored.
*/
bool MouseHasFocus { get; }
/**
* Current mouse position.
*
* Returns the current position of the mouse with (0, 0) in the bottom-left corner and (1, 1) in the
* top-right corner.
*/
Vector2 MousePosition { get; }
/** Bitmask of currently depressed mouse buttons */
MouseButton MouseButtons { get; }
/**
* Delta X and Y scroll values since the last time InputUpdate() was called.
*
* Return 1 for every "click" of the scroll wheel, or smaller numbers for more incremental scrolling.
*/
Vector2 MouseScroll { get; }
/**
* Returns true when the browser will receive keyboard events.
*
* In the simplest case, return the same value as MouseHasFocus, but you can track focus yourself if desired.
*
* If this is false, the Key* properties will be ignored.
*/
bool KeyboardHasFocus { get; }
/**
* List of key up/down events that have happened since the last InputUpdate() call.
*
* The returned list is not to be altered or retained.
*/
List<Event> KeyEvents { get; }
/**
* Returns a BrowserCursor instance. The Browser will update the current cursor to reflect the
* mouse's position on the page.
*
* The IBrowserUI is responsible for changing the actual cursor, be it the mouse cursor or some in-game display.
*/
BrowserCursor BrowserCursor { get; }
/**
* These settings are used to interpret the input data.
*/
BrowserInputSettings InputSettings { get; }
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 387f33fa3a8867e4fad235ad24e7fc95
timeCreated: 1448050780
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,112 +0,0 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
#define ZF_OSX
#endif
using System.Collections.Generic;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Helper class for IBrowserUI implementations for getting/generating keyboard events for sending to an IBrowserUI.
/// </summary>
public class KeyEvents {
/** Fills up with key events as they happen. */
protected List<Event> keyEvents = new List<Event>();
/** Swaps with keyEvents on InputUpdate and is returned in the main getter. */
protected List<Event> keyEventsLast = new List<Event>();
/// <summary>
/// After calling InputUpdate, contains the key events to send to the browser.
/// </summary>
public List<Event> Events {
get { return keyEventsLast; }
}
/** List of keys Unity won't give us events for. So we have to poll. */
static readonly KeyCode[] keysToCheck = {
#if ZF_OSX
//On windows you get GUI events for ctrl, super, alt. On mac...you don't!
KeyCode.LeftCommand,
KeyCode.RightCommand,
KeyCode.LeftControl,
KeyCode.RightControl,
KeyCode.LeftAlt,
KeyCode.RightAlt,
//KeyCode.CapsLock, unity refuses to inform us of this, so there's not much we can do
#endif
//Unity consistently doesn't send events for shift across all platforms.
KeyCode.LeftShift,
KeyCode.RightShift,
};
/// <summary>
/// Call once per frame before grabbing
/// </summary>
public void InputUpdate() {
//Note: keyEvents gets filled in OnGUI as things happen. InputUpdate get called just before it's read.
//To get the right events to the right place at the right time, swap the "double buffer" of key events.
var tmp = keyEvents;
keyEvents = keyEventsLast;
keyEventsLast = tmp;
keyEvents.Clear();
//Unity doesn't include events for some keys, so fake it by checking each frame.
for (int i = 0; i < keysToCheck.Length; i++) {
if (Input.GetKeyDown(keysToCheck[i])) {
//Prepend down, postpend up. We don't know which happened first, but pressing
//modifiers usually precedes other key presses and releasing tends to follow.
keyEventsLast.Insert(0, new Event() { type = EventType.KeyDown, keyCode = keysToCheck[i] });
} else if (Input.GetKeyUp(keysToCheck[i])) {
keyEventsLast.Add(new Event() { type = EventType.KeyUp, keyCode = keysToCheck[i] });
}
}
}
/// <summary>
/// Call this with any GUI events you get from Unity that you want to have passed to the browser.
/// </summary>
/// <param name="ev"></param>
public void Feed(Event ev) {
if (ev.type != EventType.KeyDown && ev.type != EventType.KeyUp) return;
// if (ev.character != 0) Debug.Log("ev >>> " + ev.character);
// else if (ev.type == EventType.KeyUp) Debug.Log("ev ^^^ " + ev.keyCode);
// else if (ev.type == EventType.KeyDown) Debug.Log("ev vvv " + ev.keyCode);
keyEvents.Add(new Event(ev));
}
/// <summary>
/// Injects a key press. Call Release laster to let go.
/// If the key you are pressing represents a character this may not type that character. Use Type() instead.
/// </summary>
/// <param name="key"></param>
public void Press(KeyCode key) {
keyEvents.Add(new Event {
type = EventType.KeyDown, keyCode = key
});
}
public void Release(KeyCode key) {
keyEvents.Add(new Event {
type = EventType.KeyUp, keyCode = key
});
}
/// <summary>
/// Types the given text in. THis does not simulate pressing each key and releasing it.
/// </summary>
/// <param name="text"></param>
public void Type(string text) {
for (int i = 0; i < text.Length; i++) {
//fixme: multibyte chars >16 bits
keyEvents.Add(new Event {
type = EventType.KeyDown, character = text[i],
});
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 34df8173f4fc89a4a94045d4973f1dac
timeCreated: 1495912815
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: cab02f7db18ee9f41abc1d23c3818b09
folderAsset: yes
timeCreated: 1511210876
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,200 +0,0 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
#define ZF_OSX
#endif
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* This class will handle input to a browser based on the mouse position and a mesh collider on the browser.
* Mouse positions are looked up according to the UVs on the *collider* mesh. Generally, you will want to use
* the same or a visually similar (including UVs) mesh for the renderer.
*/
[Obsolete("Use PointerUIMesh instead.")]
public class ClickMeshBrowserUI : MonoBehaviour, IBrowserUI {
/**
* Creates a new UI handler.
* We will attach to {parent}, which must have the mesh we are interacting with.
* In most cases, this will also be the same object that has the Browser we will be fed to. (a la browser.UIHandler)
*/
public static ClickMeshBrowserUI Create(MeshCollider meshCollider) {
var ui = meshCollider.gameObject.AddComponent<ClickMeshBrowserUI>();
ui.meshCollider = meshCollider;
return ui;
}
public void Awake() {
BrowserCursor = new BrowserCursor();
BrowserCursor.cursorChange += CursorUpdated;
InputSettings = new BrowserInputSettings();
}
protected MeshCollider meshCollider;
/**
* How far can we reach to touch a browser?
*
* HideInInspector:
* Showing it in the inspector would imply that changing the value would be used, but in most practical cases
* with FPSBrowserUI, the value will be overridden by the FPSCursorRenderer.
*/
[HideInInspector]
public float maxDistance = float.PositiveInfinity;
/** Fills up with key events as they happen. */
protected List<Event> keyEvents = new List<Event>();
/** Swaps with keyEvents on InputUpdate and is returned in the main getter. */
protected List<Event> keyEventsLast = new List<Event>();
/** Returns the user's interacting ray, usually the mouse pointer in some form. */
protected virtual Ray LookRay {
get { return Camera.main.ScreenPointToRay(Input.mousePosition); }
}
/** List of keys Unity won't give us events for. So we have to poll. */
static readonly KeyCode[] keysToCheck = {
#if ZF_OSX
//On windows you get GUI events for ctrl, super, alt. On mac...you don't!
KeyCode.LeftCommand,
KeyCode.RightCommand,
KeyCode.LeftControl,
KeyCode.RightControl,
KeyCode.LeftAlt,
KeyCode.RightAlt,
//KeyCode.CapsLock, unity refuses to inform us of this, so there's not much we can do
#endif
//Unity consistently doesn't send events for shift across all platforms.
KeyCode.LeftShift,
KeyCode.RightShift,
};
public virtual void InputUpdate() {
//Note: keyEvents gets filled in OnGUI as things happen. InputUpdate get called just before it's read.
//To get the right events to the right place at the right time, swap the "double buffer" of key events.
var tmp = keyEvents;
keyEvents = keyEventsLast;
keyEventsLast = tmp;
keyEvents.Clear();
//Trace mouse from the main camera
var mouseRay = LookRay;
RaycastHit hit;
Physics.Raycast(mouseRay, out hit, maxDistance);
if (hit.transform != meshCollider.transform) {
//not looking at it.
MousePosition = new Vector3(0, 0);
MouseButtons = 0;
MouseScroll = new Vector2(0, 0);
MouseHasFocus = false;
KeyboardHasFocus = false;
LookOff();
return;
}
LookOn();
MouseHasFocus = true;
KeyboardHasFocus = true;
//convert ray hit to useful mouse position on page
var localPoint = hit.textureCoord;
MousePosition = localPoint;
var buttons = (MouseButton)0;
if (Input.GetMouseButton(0)) buttons |= MouseButton.Left;
if (Input.GetMouseButton(1)) buttons |= MouseButton.Right;
if (Input.GetMouseButton(2)) buttons |= MouseButton.Middle;
MouseButtons = buttons;
MouseScroll = Input.mouseScrollDelta;
//Unity doesn't include events for some keys, so fake it by checking each frame.
for (int i = 0; i < keysToCheck.Length; i++) {
if (Input.GetKeyDown(keysToCheck[i])) {
//Prepend down, postpend up. We don't know which happened first, but pressing
//modifiers usually precedes other key presses and releasing tends to follow.
keyEventsLast.Insert(0, new Event() {type = EventType.KeyDown, keyCode = keysToCheck[i]});
} else if (Input.GetKeyUp(keysToCheck[i])) {
keyEventsLast.Add(new Event() {type = EventType.KeyUp, keyCode = keysToCheck[i]});
}
}
}
public void OnGUI() {
var ev = Event.current;
if (ev.type != EventType.KeyDown && ev.type != EventType.KeyUp) return;
// if (ev.character != 0) Debug.Log("ev >>> " + ev.character);
// else if (ev.type == EventType.KeyUp) Debug.Log("ev ^^^ " + ev.keyCode);
// else if (ev.type == EventType.KeyDown) Debug.Log("ev vvv " + ev.keyCode);
keyEvents.Add(new Event(ev));
}
protected bool mouseWasOver = false;
protected void LookOn() {
if (BrowserCursor != null) {
CursorUpdated();
}
mouseWasOver = true;
}
protected void LookOff() {
if (BrowserCursor != null && mouseWasOver) {
SetCursor(null);
}
mouseWasOver = false;
}
protected void CursorUpdated() {
SetCursor(BrowserCursor);
}
/**
* Sets the current mouse cursor.
* If the cursor is null we are not looking at this browser.
*
* This base implementation changes the mouse cursor, but you could change an in-game reticle, etc.
*/
protected virtual void SetCursor(BrowserCursor newCursor) {
//note that HandleKeyInputBrowserCursor can change while we don't have focus.
//In such a case, don't do anything
if (!MouseHasFocus && newCursor != null) return;
if (newCursor == null) {
Cursor.visible = true;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
} else {
if (newCursor.Texture != null) {
Cursor.visible = true;
Cursor.SetCursor(newCursor.Texture, newCursor.Hotspot, CursorMode.Auto);
} else {
Cursor.visible = false;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
}
}
}
public bool MouseHasFocus { get; protected set; }
public Vector2 MousePosition { get; protected set; }
public MouseButton MouseButtons { get; protected set; }
public Vector2 MouseScroll { get; protected set; }
public bool KeyboardHasFocus { get; protected set; }
public List<Event> KeyEvents { get { return keyEventsLast; } }
public BrowserCursor BrowserCursor { get; protected set; }
public BrowserInputSettings InputSettings { get; protected set; }
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: beafc338c0850714f8831f03b4ba9a67
timeCreated: 1450133185
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,62 +0,0 @@
using System;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Works like ClickMeshBrowserUI, but assumes you are pointing at buttons with your nose
* (a camera or object's transform.forward) instead of with a visible mouse pointer.
*
* This relies on the given FPSCursorRenderer to render the cursor.
*
* Unlike MeshColliderBrowserUI, this won't be used by default. If you'd like to use it,
* call CursorCrosshair.SetUpBrowserInput.
*
* As with MeshColliderBrowserUI, pass in the mesh we interact on to {meshCollider}.
*
* {worldPointer} is the object we are pointing with. Usually you can use Camera.main.transsform.
* Its world-space forward direction will be used to get the user's interaction ray.
*/
[RequireComponent(typeof(Browser))]
[RequireComponent(typeof(MeshCollider))]
[Obsolete("Use PointerUIMesh instead.")]
public class FPSBrowserUI : ClickMeshBrowserUI {
protected Transform worldPointer;
protected FPSCursorRenderer cursorRenderer;
public void Start() {
FPSCursorRenderer.SetUpBrowserInput(GetComponent<Browser>(), GetComponent<MeshCollider>());
}
public static FPSBrowserUI Create(MeshCollider meshCollider, Transform worldPointer, FPSCursorRenderer cursorRenderer) {
var ui = meshCollider.gameObject.GetComponent<FPSBrowserUI>();
if (!ui) ui = meshCollider.gameObject.AddComponent<FPSBrowserUI>();
ui.meshCollider = meshCollider;
ui.worldPointer = worldPointer;
ui.cursorRenderer = cursorRenderer;
return ui;
}
protected override Ray LookRay {
get { return new Ray(worldPointer.position, worldPointer.forward); }
}
protected override void SetCursor(BrowserCursor newCursor) {
if (newCursor != null && !MouseHasFocus) return;
cursorRenderer.SetCursor(newCursor, this);
}
public override void InputUpdate() {
if (!cursorRenderer.EnableInput) {
MouseHasFocus = false;
KeyboardHasFocus = false;
return;
}
base.InputUpdate();
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b336f8b13576fc5459369f2a394339d5
timeCreated: 1452987345
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,82 +0,0 @@
using System;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Draws a crosshair in the middle of the screen which changes to cursors as you mouseover
* things in world-space browsers.
*
* Often, this will be created automatically. If you want to alter parameters, add this script
* to an object (such as the camera) and edit them there.
*/
[Obsolete("Use PointerUIMesh and CursorRendererOverlay instead.")]
public class FPSCursorRenderer : MonoBehaviour {
private static FPSCursorRenderer _instance;
public static FPSCursorRenderer Instance {
get {
if (!_instance) {
_instance = FindObjectOfType<FPSCursorRenderer>();
if (!_instance) {
var go = new GameObject("Cursor Crosshair");
_instance = go.AddComponent<FPSCursorRenderer>();
}
}
return _instance;
}
}
[Tooltip("How large should we render the cursor?")]
public float scale = .5f;
[Tooltip("How far can we reach to push buttons and such?")]
public float maxDistance = 7f;
[Tooltip("What are we using to point at things? Leave as null to use Camera.main")]
public Transform pointer;
/**
* Toggle this to enable/disable input for all FPSBrowserUI objects.
* This is useful, for example, during plot sequences and pause menus.
*/
public bool EnableInput { get; set; }
public static void SetUpBrowserInput(Browser browser, MeshCollider mesh) {
var crossHair = Instance;
var pointer = crossHair.pointer;
if (!pointer) pointer = Camera.main.transform;//nb: don't use crossHair.pointer ?? camera, will incorrectly return null
var fpsUI = FPSBrowserUI.Create(mesh, pointer, crossHair);
fpsUI.maxDistance = crossHair.maxDistance;
browser.UIHandler = fpsUI;
}
protected BrowserCursor baseCursor, currentCursor;
public void Start() {
EnableInput = true;
baseCursor = new BrowserCursor();
baseCursor.SetActiveCursor(BrowserNative.CursorType.Cross);
}
public void OnGUI() {
if (!EnableInput) return;
var cursor = currentCursor ?? baseCursor;
var tex = cursor.Texture;
if (tex == null) return;//hidden cursor
var pos = new Rect(Screen.width / 2f, Screen.height / 2f, tex.width * scale, tex.height * scale);
pos.x -= cursor.Hotspot.x * scale;
pos.y -= cursor.Hotspot.y * scale;
GUI.DrawTexture(pos, tex);
}
public void SetCursor(BrowserCursor newCursor, FPSBrowserUI ui) {
currentCursor = newCursor;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 27abec923057368408f62c2ebf6d54b3
timeCreated: 1453246003
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,185 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ZenFulcrum.EmbeddedBrowser {
/** Attach this script to a GUI Image to use a browser on it. */
[RequireComponent(typeof(RawImage))]
[RequireComponent(typeof(Browser))]
[Obsolete("Use PointerUIGUI and CursorRendererOS instead.")]
public class GUIBrowserUI :
MonoBehaviour,
IBrowserUI,
ISelectHandler, IDeselectHandler,
IPointerEnterHandler, IPointerExitHandler,
IPointerDownHandler
{
protected RawImage myImage;
protected Browser browser;
public bool enableInput = true, autoResize = true;
protected void Awake() {
BrowserCursor = new BrowserCursor();
InputSettings = new BrowserInputSettings();
browser = GetComponent<Browser>();
myImage = GetComponent<RawImage>();
browser.afterResize += UpdateTexture;
browser.UIHandler = this;
BrowserCursor.cursorChange += () => {
SetCursor(BrowserCursor);
};
rTransform = GetComponent<RectTransform>();
}
protected void OnEnable() {
if (autoResize) StartCoroutine(WatchResize());
}
/** Automatically resizes the browser to match the size of this object. */
private IEnumerator WatchResize() {
Rect currentSize = new Rect();
while (enabled) {
var rect = rTransform.rect;
if (rect.size.x <= 0 || rect.size.y <= 0) rect.size = new Vector2(512, 512);
if (rect.size != currentSize.size) {
browser.Resize((int)rect.size.x, (int)rect.size.y);
currentSize = rect;
}
//yield return new WaitForSeconds(.5f); won't work if you pause the game, which, BTW, is a great time to resize the screen ;-)
yield return null;
}
}
protected void UpdateTexture(Texture2D texture) {
myImage.texture = texture;
myImage.uvRect = new Rect(0, 0, 1, 1);
}
protected List<Event> keyEvents = new List<Event>();
protected List<Event> keyEventsLast = new List<Event>();
protected BaseRaycaster raycaster;
protected RectTransform rTransform;
// protected List<RaycastResult> raycastResults = new List<RaycastResult>();
public virtual void InputUpdate() {
var tmp = keyEvents;
keyEvents = keyEventsLast;
keyEventsLast = tmp;
keyEvents.Clear();
if (MouseHasFocus) {
if (!raycaster) raycaster = GetComponentInParent<BaseRaycaster>();
// raycastResults.Clear();
// raycaster.Raycast(data, raycastResults);
// if (raycastResults.Count != 0) {
// Vector2 pos = raycastResults[0].stuff
Vector2 pos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)transform, Input.mousePosition, raycaster.eventCamera, out pos
);
pos.x = pos.x / rTransform.rect.width + rTransform.pivot.x;
pos.y = pos.y / rTransform.rect.height + rTransform.pivot.y;
MousePosition = pos;
var buttons = (MouseButton)0;
if (Input.GetMouseButton(0)) buttons |= MouseButton.Left;
if (Input.GetMouseButton(1)) buttons |= MouseButton.Right;
if (Input.GetMouseButton(2)) buttons |= MouseButton.Middle;
MouseButtons = buttons;
MouseScroll = Input.mouseScrollDelta;
} else {
MouseButtons = 0;
}
//Unity doesn't include events for some keys, so fake it.
if (Input.GetKeyDown(KeyCode.LeftShift) || Input.GetKeyDown(KeyCode.RightShift)) {
//Note: doesn't matter if left or right shift, the browser can't tell.
//(Prepend event. We don't know what happened first, but pressing shift usually precedes other key presses)
keyEventsLast.Insert(0, new Event() { type = EventType.KeyDown, keyCode = KeyCode.LeftShift });
}
if (Input.GetKeyUp(KeyCode.LeftShift) || Input.GetKeyUp(KeyCode.RightShift)) {
//Note: doesn't matter if left or right shift, the browser can't tell.
keyEventsLast.Add(new Event() { type = EventType.KeyUp, keyCode = KeyCode.LeftShift });
}
}
public void OnGUI() {
var ev = Event.current;
if (ev.type != EventType.KeyDown && ev.type != EventType.KeyUp) return;
keyEvents.Add(new Event(ev));
}
protected virtual void SetCursor(BrowserCursor newCursor) {
if (!_mouseHasFocus && newCursor != null) return;
if (newCursor == null) {
Cursor.visible = true;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
} else {
if (newCursor.Texture != null) {
Cursor.visible = true;
Cursor.SetCursor(newCursor.Texture, newCursor.Hotspot, CursorMode.Auto);
} else {
Cursor.visible = false;
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
}
}
}
protected bool _mouseHasFocus;
public bool MouseHasFocus { get { return _mouseHasFocus && enableInput; } }
public Vector2 MousePosition { get; private set; }
public MouseButton MouseButtons { get; private set; }
public Vector2 MouseScroll { get; private set; }
protected bool _keyboardHasFocus;
public bool KeyboardHasFocus { get { return _keyboardHasFocus && enableInput; } }
public List<Event> KeyEvents { get { return keyEventsLast; } }
public BrowserCursor BrowserCursor { get; private set; }
public BrowserInputSettings InputSettings { get; private set; }
public void OnSelect(BaseEventData eventData) {
_keyboardHasFocus = true;
}
public void OnDeselect(BaseEventData eventData) {
_keyboardHasFocus = false;
}
public void OnPointerEnter(PointerEventData eventData) {
_mouseHasFocus = true;
SetCursor(BrowserCursor);
}
public void OnPointerExit(PointerEventData eventData) {
_mouseHasFocus = false;
SetCursor(null);
}
public void OnPointerDown(PointerEventData eventData) {
EventSystem.current.SetSelectedGameObject(gameObject);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 312e08c685ba1ee4e96f0ff4128d6e49
timeCreated: 1453317757
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,434 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#endif
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Base class that handles input for mouse/touch/pointer/VR/nose inputs.
/// The concept is thus:
/// You have an arbitrary number of 3D (FPS player, VR pointer, and world ray) and
/// 2D (mouse, touch, and screen position) pointers and you want any of them
/// to be able to interact with the Browser.
///
/// Concrete implementations of this class handle interacting with different rendered mediums
/// (such as a mesh or a GUI renderer).
/// </summary>
[RequireComponent(typeof(Browser))]
public abstract class PointerUIBase : MonoBehaviour, IBrowserUI {
public readonly KeyEvents keyEvents = new KeyEvents();
protected Browser browser;
protected bool appFocused = true;
/// <summary>
/// Called once per tick. Handlers registered here should look at the pointers they have
/// and tell us about them.
/// </summary>
public event Action onHandlePointers = () => {};
protected int currentPointerId;
protected readonly List<PointerState> currentPointers = new List<PointerState>();
[Tooltip(
"When clicking, how far (in browser-space pixels) must the cursor be moved before we send this movement to the browser backend? " +
"This helps keep us from unintentionally dragging when we meant to just click, esp. under VR where it's hard to hold the cursor still."
)]
public float dragMovementThreshold = 0;
/// <summary>
/// Cursor location when we most recently went from 0 buttons to any buttons down.
/// </summary>
protected Vector2 mouseDownPosition;
/// <summary>
/// True when we have the any mouse button down AND we've moved at least dragMovementThreshold after that.
/// </summary>
protected bool dragging = false;
#region Pointer Core
public struct PointerState {
/// <summary>
/// Unique value for this pointer, to distinguish it by. Must be > 0.
/// </summary>
public int id;
/// <summary>
/// Is the pointer a 2d or 3d pointer?
/// </summary>
public bool is2D;
public Vector2 position2D;
public Ray position3D;
/// <summary>
/// Currently depressed "buttons" on this pointer.
/// </summary>
public MouseButton activeButtons;
/// <summary>
/// If the pointer can scroll, delta scrolling values since last frame.
/// </summary>
public Vector2 scrollDelta;
}
/// <summary>
/// Called when the browser gets clicked with any mouse button.
/// (More precisely, when we go from having no buttons down to 1+ buttons down.)
/// </summary>
public event Action onClick = () => {};
public virtual void Awake() {
BrowserCursor = new BrowserCursor();
BrowserCursor.cursorChange += CursorUpdated;
InputSettings = new BrowserInputSettings();
browser = GetComponent<Browser>();
browser.UIHandler = this;
onHandlePointers += OnHandlePointers;
if (disableMouseEmulation) Input.simulateMouseWithTouches = false;
}
public virtual void InputUpdate() {
keyEvents.InputUpdate();
p_currentDown = p_anyDown = p_currentOver = p_anyOver = -1;
currentPointers.Clear();
onHandlePointers();
CalculatePointer();
}
public void OnApplicationFocus(bool focused) {
appFocused = focused;
}
/// <summary>
/// Converts a 2D screen-space coordinate to browser-space coordinates.
/// If the given point doesn't map to the browser, return float.NaN for the position.
/// </summary>
/// <param name="screenPosition"></param>
/// <param name="pointerId"></param>
/// <returns></returns>
protected abstract Vector2 MapPointerToBrowser(Vector2 screenPosition, int pointerId);
/// <summary>
/// Converts a 3D world-space ray to a browser-space coordinate.
/// If the given ray doesn't map to the browser, return float.NaN for the position.
/// </summary>
/// <param name="worldRay"></param>
/// <param name="pointerId"></param>
/// <returns></returns>
protected abstract Vector2 MapRayToBrowser(Ray worldRay, int pointerId);
/// <summary>
/// Returns the current position+rotation of the active pointer in world space.
/// If there is none, or it doesn't make sense to map to world space, position will
/// be NaNs.
///
/// Coordinates are in world space. The rotation should point up in the direction the browser sees as up.
/// Z+ should go "into" the browser surface.
/// </summary>
/// <param name="pos"></param>
/// <param name="rot"></param>
public abstract void GetCurrentHitLocation(out Vector3 pos, out Quaternion rot);
/** Indexes into currentPointers for useful items this frame. -1 if N/A. */
protected int p_currentDown, p_anyDown, p_currentOver, p_anyOver;
/// <summary>
/// Feeds the state of the given pointer into the handler.
/// </summary>
/// <param name="state"></param>
public virtual void FeedPointerState(PointerState state) {
if (state.is2D) state.position2D = MapPointerToBrowser(state.position2D, state.id);
else {
Debug.DrawRay(state.position3D.origin, state.position3D.direction * (Mathf.Min(500, maxDistance)), Color.cyan);
state.position2D = MapRayToBrowser(state.position3D, state.id);
//Debug.Log("Pointer " + state.id + " at " + state.position3D.origin + " pointing " + state.position3D.direction + " maps to " + state.position2D);
}
if (float.IsNaN(state.position2D.x)) return;
if (state.id == currentPointerId) {
p_currentOver = currentPointers.Count;
if (state.activeButtons != 0) p_currentDown = currentPointers.Count;
} else {
p_anyOver = currentPointers.Count;
if (state.activeButtons != 0) p_anyDown = currentPointers.Count;
}
currentPointers.Add(state);
}
protected virtual void CalculatePointer() {
// if (!appFocused) {
// MouseIsOff();
// return;
// }
/*
* The position/priority we feed to the browser is determined in this order:
* - Pointer we used earlier with a button down
* - Pointer with a button down
* - Pointer we used earlier
* - Any pointer that is over the browser
* Pointers that aren't over the browser are ignored.
* If multiple pointers meet the criteria we may pick any that qualify.
*/
PointerState stateToUse;
if (p_currentDown >= 0) {
//last frame's pointer with a button down
stateToUse = currentPointers[p_currentDown];
} else if (p_anyDown >= 0) {
//mouse button count became > 0 this frame
stateToUse = currentPointers[p_anyDown];
} else if (p_currentOver >= 0) {
//just hovering (use the pointer from last frame)
stateToUse = currentPointers[p_currentOver];
} else if (p_anyOver >= 0) {
//just hovering (use any pointer over us)
stateToUse = currentPointers[p_anyOver];
} else {
//no pointers over us
MouseIsOff();
return;
}
MouseIsOver();
if (MouseButtons == 0 && stateToUse.activeButtons != 0) {
//no buttons -> 1+ buttons
onClick();
//start drag prevention
dragging = false;
mouseDownPosition = stateToUse.position2D;
}
if (float.IsNaN(stateToUse.position2D.x)) Debug.LogError("Using an invalid pointer");// "shouldn't happen"
if (stateToUse.activeButtons != 0 || MouseButtons != 0) {
//Button(s) held or being released, do some extra logic to prevent unintentional dragging during clicks.
if (!dragging && stateToUse.activeButtons != 0) {//only check distance if buttons(s) held and not already dragging
//Check to see if we passed the drag threshold.
var size = browser.Size;
var distance = Vector2.Distance(
Vector2.Scale(stateToUse.position2D, size),//convert from [0, 1] to pixels
Vector2.Scale(mouseDownPosition, size)//convert from [0, 1] to pixels
);
if (distance > dragMovementThreshold) {
dragging = true;
}
}
if (dragging) MousePosition = stateToUse.position2D;
else MousePosition = mouseDownPosition;
} else {
//no buttons held (or being release), no need to fiddle with the position
MousePosition = stateToUse.position2D;
}
MouseButtons = stateToUse.activeButtons;
MouseScroll = stateToUse.scrollDelta;
currentPointerId = stateToUse.id;
}
public void OnGUI() {
keyEvents.Feed(Event.current);
}
protected bool mouseWasOver = false;
protected void MouseIsOver() {
MouseHasFocus = true;
KeyboardHasFocus = true;
if (BrowserCursor != null) {
CursorUpdated();
}
mouseWasOver = true;
}
protected void MouseIsOff() {
// if (BrowserCursor != null && mouseWasOver) {
// SetCursor(null);
// }
mouseWasOver = false;
MouseHasFocus = false;
if (focusForceCount <= 0) KeyboardHasFocus = false;
MouseButtons = 0;
MouseScroll = Vector2.zero;
currentPointerId = 0;
}
protected void CursorUpdated() {
// SetCursor(BrowserCursor);
}
private int focusForceCount = 0;
/// <summary>
/// Sets a flag to keep the keyboard focus on this browser, even if it has no pointers.
/// Useful for focusing it to type things in via external keyboard.
///
/// Call again with force = false to return to the default behavior. (You must call force
/// on and force off an equal number of times to revert to the default behavior.)
///
/// </summary>
/// <param name="force"></param>
public void ForceKeyboardHasFocus(bool force) {
if (force) ++focusForceCount;
else --focusForceCount;
if (focusForceCount == 1) KeyboardHasFocus = true;
else if (focusForceCount == 0) KeyboardHasFocus = false;
}
#endregion
#region Input Handlers
[Tooltip("Camera to use to interpret 2D inputs and to originate FPS rays from. Defaults to Camera.main.")]
public Camera viewCamera;
public bool enableMouseInput = true;
public bool enableTouchInput = true;
public bool enableFPSInput = false;
public bool enableVRInput = false;
[Tooltip("(For ray-based interaction) How close must you be to the browser to be able to interact with it?")]
public float maxDistance = float.PositiveInfinity;
[Space(5)]
[Tooltip("Disable Input.simulateMouseWithTouches globally. This will prevent touches from appearing as mouse events.")]
public bool disableMouseEmulation = false;
protected virtual void OnHandlePointers() {
if (enableFPSInput) FeedFPS();
//special case to avoid duplicate pointer from the first touch (ignore mouse)
if (enableMouseInput && enableTouchInput && Input.simulateMouseWithTouches && Input.touchCount > 0) {
FeedTouchPointers();
return;
}
if (enableMouseInput) FeedMousePointer();
if (enableTouchInput) FeedTouchPointers();
#if UNITY_2017_2_OR_NEWER
if (enableVRInput) FeedVRPointers();
#endif
}
/// <summary>
/// Calls FeedPointerState with all the items in Input.touches.
/// (Does not happen automatically, call when desired.)
/// </summary>
protected virtual void FeedTouchPointers() {
for (int i = 0; i < Input.touchCount; i++) {
var touch = Input.GetTouch(i);
FeedPointerState(new PointerState {
id = 10 + touch.fingerId,
is2D = true,
position2D = touch.position,
activeButtons = (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary) ? MouseButton.Left : 0,
});
}
}
/// <summary>
/// Calls FeedPointerState with the current mouse state.
/// (Does not happen automatically, call when desired.)
/// </summary>
protected virtual void FeedMousePointer() {
var buttons = (MouseButton)0;
if (Input.GetMouseButton(0)) buttons |= MouseButton.Left;
if (Input.GetMouseButton(1)) buttons |= MouseButton.Right;
if (Input.GetMouseButton(2)) buttons |= MouseButton.Middle;
FeedPointerState(new PointerState {
id = 1,
is2D = true,
position2D = Input.mousePosition,
activeButtons = buttons,
scrollDelta = Input.mouseScrollDelta,
});
}
protected virtual void FeedFPS() {
var buttons =
(Input.GetButton("Fire1") ? MouseButton.Left : 0) |
(Input.GetButton("Fire2") ? MouseButton.Right : 0) |
(Input.GetButton("Fire3") ? MouseButton.Middle : 0)
;
var camera = viewCamera ? viewCamera : Camera.main;
var scrollDelta = Input.mouseScrollDelta;
//Don't double-count scrolling if we are processing the mouse too.
if (enableMouseInput) scrollDelta = Vector2.zero;
FeedPointerState(new PointerState {
id = 2,
is2D = false,
position3D = new Ray(camera.transform.position, camera.transform.forward),
activeButtons = buttons,
scrollDelta = scrollDelta,
});
}
#if UNITY_2017_2_OR_NEWER
protected VRBrowserHand[] vrHands = null;
protected virtual void FeedVRPointers() {
if (vrHands == null) {
vrHands = FindObjectsOfType<VRBrowserHand>();
if (vrHands.Length == 0 && XRSettings.enabled) {
Debug.LogWarning("VR input is enabled, but no VRBrowserHands were found in the scene", this);
}
}
for (int i = 0; i < vrHands.Length; i++) {
if (!vrHands[i].Tracked) continue;
FeedPointerState(new PointerState {
id = 100 + i,
is2D = false,
position3D = new Ray(vrHands[i].transform.position, vrHands[i].transform.forward),
activeButtons = vrHands[i].DepressedButtons,
scrollDelta = vrHands[i].ScrollDelta,
});
}
}
#endif
#endregion
public virtual bool MouseHasFocus { get; protected set; }
public virtual Vector2 MousePosition { get; protected set; }
public virtual MouseButton MouseButtons { get; protected set; }
public virtual Vector2 MouseScroll { get; protected set; }
public virtual bool KeyboardHasFocus { get; protected set; }
public virtual List<Event> KeyEvents { get { return keyEvents.Events; } }
public virtual BrowserCursor BrowserCursor { get; protected set; }
public virtual BrowserInputSettings InputSettings { get; protected set; }
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 7bd56d627d06bc64382a847aa240ae1d
timeCreated: 1495912815
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,126 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ZenFulcrum.EmbeddedBrowser {
/** Attach this script to a GUI Image to use a browser on it. */
[RequireComponent(typeof(RawImage))]
public class PointerUIGUI :
PointerUIBase,
IBrowserUI,
ISelectHandler, IDeselectHandler,
IPointerEnterHandler, IPointerExitHandler,
IPointerDownHandler
{
protected RawImage myImage;
public bool enableInput = true;
public bool automaticResize = true;
public override void Awake() {
base.Awake();
myImage = GetComponent<RawImage>();
browser.afterResize += UpdateTexture;
// BrowserCursor.cursorChange += () => {
// SetCursor(BrowserCursor);
// };
rTransform = GetComponent<RectTransform>();
}
protected void OnEnable() {
if (automaticResize) StartCoroutine(WatchResize());
}
/** Automatically resizes the browser to match the size of this object. */
private IEnumerator WatchResize() {
Rect currentSize = new Rect();
while (enabled) {
var rect = rTransform.rect;
if (rect.size.x <= 0 || rect.size.y <= 0) rect.size = new Vector2(512, 512);
if (rect.size != currentSize.size) {
browser.Resize((int)rect.size.x, (int)rect.size.y);
currentSize = rect;
}
yield return null;
}
}
protected void UpdateTexture(Texture2D texture) {
myImage.texture = texture;
myImage.uvRect = new Rect(0, 0, 1, 1);
}
protected BaseRaycaster raycaster;
protected RectTransform rTransform;
// protected List<RaycastResult> raycastResults = new List<RaycastResult>();
protected override Vector2 MapPointerToBrowser(Vector2 screenPosition, int pointerId) {
if (!raycaster) raycaster = GetComponentInParent<BaseRaycaster>();
Vector2 pos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)transform, screenPosition, raycaster.eventCamera, out pos
);
pos.x = pos.x / rTransform.rect.width + rTransform.pivot.x;
pos.y = pos.y / rTransform.rect.height + rTransform.pivot.y;
return pos;
}
protected override Vector2 MapRayToBrowser(Ray worldRay, int pointerId) {
var evs = EventSystem.current;
if (!evs) return new Vector2(float.NaN, float.NaN);
//todo: world-space GUI
return new Vector2(float.NaN, float.NaN);
}
public override void GetCurrentHitLocation(out Vector3 pos, out Quaternion rot) {
//todo: world space GUI
pos = new Vector3(float.NaN, float.NaN, float.NaN);
rot = Quaternion.identity;
}
protected bool _mouseHasFocus;
public override bool MouseHasFocus {
get { return _mouseHasFocus && enableInput; }
protected set { _mouseHasFocus = value; }
}
protected bool _keyboardHasFocus;
public override bool KeyboardHasFocus { get { return _keyboardHasFocus && enableInput; } }
public void OnSelect(BaseEventData eventData) {
_keyboardHasFocus = true;
Input.imeCompositionMode = IMECompositionMode.Off;//CEF will handle the IME
}
public void OnDeselect(BaseEventData eventData) {
_keyboardHasFocus = false;
Input.imeCompositionMode = IMECompositionMode.Auto;
}
public void OnPointerEnter(PointerEventData eventData) {
_mouseHasFocus = true;
// SetCursor(BrowserCursor);
}
public void OnPointerExit(PointerEventData eventData) {
_mouseHasFocus = false;
// SetCursor(null);
}
public void OnPointerDown(PointerEventData eventData) {
EventSystem.current.SetSelectedGameObject(gameObject);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 9f0449828438f1c4eb0712205cc11bb7
timeCreated: 1495924470
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// A BrowserUI that tracks pointer interaction through a camera to a mesh of some sort.
/// </summary>
[RequireComponent(typeof(MeshCollider))]
public class PointerUIMesh : PointerUIBase {
protected MeshCollider meshCollider;
protected Dictionary<int, RaycastHit> rayHits = new Dictionary<int, RaycastHit>();
[Tooltip("Which layers should UI rays collide with (and be able to hit)?")]
public LayerMask layerMask = -1;
public override void Awake() {
base.Awake();
meshCollider = GetComponent<MeshCollider>();
}
protected override Vector2 MapPointerToBrowser(Vector2 screenPosition, int pointerId) {
var camera = viewCamera ? viewCamera : Camera.main;
return MapRayToBrowser(camera.ScreenPointToRay(screenPosition), pointerId);
}
protected override Vector2 MapRayToBrowser(Ray worldRay, int pointerId) {
RaycastHit hit;
var rayHit = Physics.Raycast(worldRay, out hit, maxDistance, layerMask);
//store hit data for GetCurrentHitLocation
rayHits[pointerId] = hit;
if (!rayHit || hit.collider.transform != meshCollider.transform) {
//not aimed at it
return new Vector3(float.NaN, float.NaN);
} else {
return hit.textureCoord;
}
}
public override void GetCurrentHitLocation(out Vector3 pos, out Quaternion rot) {
if (currentPointerId == 0) {
//no pointer
pos = new Vector3(float.NaN, float.NaN, float.NaN);
rot = Quaternion.identity;
return;
}
var hitInfo = rayHits[currentPointerId];
//We need to know which way is up, so the cursor has the correct "up".
//There's a couple ways to do this:
//1. Use the barycentric coordinates and some math to figure out what direction the collider's
// v (from the uv) is getting bigger/smaller, then do some math to find out what direction
// that is in world space.
//2. Just use the collider's local orientation's up. This isn't accurate on highly
// distorted meshes, but is much simpler to calculate.
//For now, we use method 2.
var up = hitInfo.collider.transform.up;
pos = hitInfo.point;
rot = Quaternion.LookRotation(-hitInfo.normal, up);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 70425c8c18e6a674da5c39ca0c09003c
timeCreated: 1495915500
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,100 +0,0 @@
using System;
using System.Text.RegularExpressions;
using UnityEngine;
using NativeCookie = ZenFulcrum.EmbeddedBrowser.BrowserNative.NativeCookie;
namespace ZenFulcrum.EmbeddedBrowser {
public class Cookie {
public static void Init() {
//Empty function on this class to call so we can get the cctor to call on the correct thread.
//(Regex construction tends to crash if it tries to run from certain threads.)
}
private CookieManager cookies;
private NativeCookie original;
public string name = "", value = "", domain = "", path = "";
/** Creation/access time of the cookie. Mostly untested/unsupported at present. */
public DateTime creation, lastAccess;
/** Null for normal cookies, a time for cookies that expire. Mostly untested/unsupported at present. */
public DateTime? expires;
public bool secure, httpOnly;
public Cookie(CookieManager cookies) {
this.cookies = cookies;
}
internal Cookie(CookieManager cookies, NativeCookie cookie) {
this.cookies = cookies;
original = cookie;
Copy(original, this);
}
/** Deletes this cookie from the browser. */
public void Delete() {
if (original == null) return;
BrowserNative.zfb_editCookie(cookies.browser.browserId, original, BrowserNative.CookieAction.Delete);
original = null;
}
/** Updates any changes to this cookie in the browser, creating the cookie if it's new. */
public void Update() {
if (original != null) Delete();
original = new NativeCookie();
Copy(this, original);
BrowserNative.zfb_editCookie(cookies.browser.browserId, original, BrowserNative.CookieAction.Create);
}
static readonly Regex dateRegex = new Regex(@"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}).(\d{3})");
public static void Copy(NativeCookie src, Cookie dest) {
dest.name = src.name;
dest.value = src.value;
dest.domain = src.domain;
dest.path = src.path;
Func<string, DateTime> convert = s => {
var m = dateRegex.Match(s);
return new DateTime(
int.Parse(m.Groups[1].ToString()),
int.Parse(m.Groups[2].ToString()),
int.Parse(m.Groups[3].ToString()),
int.Parse(m.Groups[4].ToString()),
int.Parse(m.Groups[5].ToString()),
int.Parse(m.Groups[6].ToString()),
int.Parse(m.Groups[7].ToString())
);
};
dest.creation = convert(src.creation);
dest.expires = src.expires == null ? (DateTime?)null : convert(src.expires);
dest.lastAccess = convert(src.lastAccess);
dest.secure = src.secure != 0;
dest.httpOnly = src.httpOnly != 0;
}
public static void Copy(Cookie src, NativeCookie dest) {
dest.name = src.name;
dest.value = src.value;
dest.domain = src.domain;
dest.path = src.path;
Func<DateTime, string> convert = s => s.ToString("yyyy-MM-dd hh:mm:ss.fff");
dest.creation = convert(src.creation);
dest.expires = src.expires == null ? null : convert(src.expires.Value);
dest.lastAccess = convert(src.lastAccess);
dest.secure = src.secure ? (byte)1 : (byte)0;
dest.httpOnly = src.httpOnly ? (byte)1 : (byte)0;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: f7a2a645481f96e4682c86cc9b22dff9
timeCreated: 1478892646
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using AOT;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
public class CookieManager {
internal readonly Browser browser;
public CookieManager(Browser browser) {
this.browser = browser;
}
private class CookieFetch {
public BrowserNative.GetCookieFunc nativeCB;
public Promise<List<Cookie>> promise;
public CookieManager manager;
public List<Cookie> result;
}
private static CookieFetch currentFetch;
/**
* Returns a list of all cookies in the browser across all domains.
*
* Note that cookies are shared between browser instances.
*
* If the browser is not ready yet (browser.IsReady or WhenReady()) this will return an empty list.
*
* This method is not reentrant! You must wait for the returned promise to resolve before calling it again,
* even on a differnet object.
*/
public IPromise<List<Cookie>> GetCookies() {
if (currentFetch != null) {
//This method Wait for the previous promise to resolve, then make your call.
//If this limitation actually affects you, let me know.
throw new InvalidOperationException("GetCookies is not reentrant");
}
Cookie.Init();
var result = new List<Cookie>();
if (!browser.IsReady || !browser.enabled) return Promise<List<Cookie>>.Resolved(result);
var promise = new Promise<List<Cookie>>();
BrowserNative.GetCookieFunc cookieFunc = CB_GetCookieFunc;
BrowserNative.zfb_getCookies(browser.browserId, cookieFunc);
currentFetch = new CookieFetch {
promise = promise,
nativeCB = cookieFunc,
manager = this,
result = result,
};
return promise;
}
[MonoPInvokeCallback(typeof(BrowserNative.GetCookieFunc))]
private static void CB_GetCookieFunc(BrowserNative.NativeCookie cookie) {
try {
if (cookie == null) {
var result = currentFetch.result;
var promise = currentFetch.promise;
currentFetch.manager.browser.RunOnMainThread(() => promise.Resolve(result));
currentFetch = null;
return;
}
currentFetch.result.Add(new Cookie(currentFetch.manager, cookie));
} catch (Exception ex) {
Debug.LogException(ex);
}
}
/**
* Deletes all cookies in the browser.
*/
public void ClearAll() {
if (browser.DeferUnready(ClearAll)) return;
BrowserNative.zfb_clearCookies(browser.browserId);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 536726be6d65c4f4fa53116569aa4be5
timeCreated: 1478910267
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,121 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Helper for browser dialog boxes, like alert(). You don't need to use this directly, it will
* automatically be added where it's needed.
*/
[RequireComponent(typeof(Browser))]
public class DialogHandler : MonoBehaviour {
protected static string dialogPage;
public delegate void DialogCallback(bool affirm, string text1, string text2);
public delegate void MenuCallback(int commandId);
public static DialogHandler Create(Browser parent, DialogCallback dialogCallback, MenuCallback contextCallback) {
if (dialogPage == null) {
dialogPage = Resources.Load<TextAsset>("Browser/Dialogs").text;
}
var go = new GameObject("Browser Dialog for " + parent.name);
var handler = go.AddComponent<DialogHandler>();
handler.parentBrowser = parent;
handler.dialogCallback = dialogCallback;
var db = handler.dialogBrowser = handler.GetComponent<Browser>();
db.UIHandler = parent.UIHandler;
db.EnableRendering = false;
db.EnableInput = false;
db.allowContextMenuOn = BrowserNative.ContextMenuOrigin.Editable;
//Use the parent texture. Except, we don't actually use it. So
//mostly we just mimic the size and don't consume more texture memory.
db.Resize(parent.Texture);
db.LoadHTML(dialogPage, "zfb://dialog");
db.UIHandler = parent.UIHandler;
db.RegisterFunction("reportDialogResult", args => {
dialogCallback(args[0], args[1], args[2]);
handler.Hide();
});
db.RegisterFunction("reportContextMenuResult", args => {
contextCallback(args[0]);
handler.Hide();
});
return handler;
}
protected Browser parentBrowser;
protected Browser dialogBrowser;
protected DialogCallback dialogCallback;
protected MenuCallback contextCallback;
public void HandleDialog(BrowserNative.DialogType type, string text, string promptDefault = null) {
if (type == BrowserNative.DialogType.DLT_HIDE) {
Hide();
return;
}
Show();
//Debug.Log("HandleDialog " + type + " text " + text + " prompt " + promptDefault);
switch (type) {
case BrowserNative.DialogType.DLT_ALERT:
dialogBrowser.CallFunction("showAlert", text);
break;
case BrowserNative.DialogType.DLT_CONFIRM:
dialogBrowser.CallFunction("showConfirm", text);
break;
case BrowserNative.DialogType.DLT_PROMPT:
dialogBrowser.CallFunction("showPrompt", text, promptDefault);
break;
case BrowserNative.DialogType.DLT_PAGE_UNLOAD:
dialogBrowser.CallFunction("showConfirmNav", text);
break;
case BrowserNative.DialogType.DLT_PAGE_RELOAD:
dialogBrowser.CallFunction("showConfirmReload", text);
break;
case BrowserNative.DialogType.DLT_GET_AUTH:
dialogBrowser.CallFunction("showAuthPrompt", text);
break;
default:
throw new ArgumentOutOfRangeException("type", type, null);
}
}
public void Show() {
parentBrowser.SetOverlay(dialogBrowser);
parentBrowser.EnableInput = false;
dialogBrowser.EnableInput = true;
dialogBrowser.UpdateCursor();
}
public void Hide() {
parentBrowser.SetOverlay(null);
parentBrowser.EnableInput = true;
dialogBrowser.EnableInput = false;
parentBrowser.UpdateCursor();
if (dialogBrowser.IsLoaded) dialogBrowser.CallFunction("reset");
}
public void HandleContextMenu(string menuJSON, int x, int y) {
if (menuJSON == null) {
Hide();
return;
}
Show();
dialogBrowser.CallFunction("showContextMenu", menuJSON, x, y);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 8c5bf0b11246b1f42a262f8a64026d33
timeCreated: 1449001312
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,226 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Helper class for tracking and managing downloads.
* You can manage and handle downloads without this, but you may find it useful for dealing with the more
* common file downloading use cases.
*
* Usage: create one and call manager.ManageDownloads(browser) on a browser you want it to handle.
* or throw one in the scene and set "manageAllBrowsers" to true (before loading the scene) for it
* to automatically hook into all browsers that start in the scene.
*/
public class DownloadManager : MonoBehaviour {
[Tooltip("If true, this will find all the browser in the scene at startup and take control of their downloads.")]
public bool manageAllBrowsers = false;
[Tooltip("If true, a \"Save as\" style dialog will be given for all downloads.")]
public bool promptForFileNames;
[Tooltip("Where to save files. If null or blank, defaults to the user's downloads directory.")]
public string saveFolder;
[Tooltip("If given this text element will be updated with download status info.")]
public Text statusBar;
public class Download {
public Browser browser;
public int downloadId;
public string name;
public string path;
public int speed;
public int percent;
public string status;
}
public List<Download> downloads = new List<Download>();
public void Awake() {
if (manageAllBrowsers) {
foreach (var browser in FindObjectsOfType<Browser>()) {
ManageDownloads(browser);
}
}
}
public void ManageDownloads(Browser browser) {
browser.onDownloadStarted = (id, info) => {
HandleDownloadStarted(browser, id, info);
};
browser.onDownloadStatus += (id, info) => {
HandleDownloadStatus(browser, id, info);
};
}
private void HandleDownloadStarted(Browser browser, int downloadId, JSONNode info) {
//Debug.Log("Download requested: " + info.AsJSON);
var download = new Download {
browser = browser,
downloadId = downloadId,
name = info["suggestedName"],
};
if (promptForFileNames) {
browser.DownloadCommand(downloadId, BrowserNative.DownloadAction.Begin, null);
} else {
DirectoryInfo downloadFolder;
if (string.IsNullOrEmpty(saveFolder)) {
downloadFolder = new DirectoryInfo(GetUserDownloadFolder());
} else {
downloadFolder = new DirectoryInfo(saveFolder);
if (!downloadFolder.Exists) downloadFolder.Create();
}
var filePath = downloadFolder.FullName + "/" + new FileInfo(info["suggestedName"]).Name;
while (File.Exists(filePath)) {
var ext = Path.GetExtension(filePath);
var left = Path.GetFileNameWithoutExtension(filePath);
var time = DateTime.Now.ToString("yyyy-MM-dd hh_mm_ss");
filePath = downloadFolder.FullName + "/" + left + " " + time + ext;
}
browser.DownloadCommand(downloadId, BrowserNative.DownloadAction.Begin, filePath);
}
downloads.Add(download);
}
private void HandleDownloadStatus(Browser browser, int downloadId, JSONNode info) {
//Debug.Log("Download status: " + info.AsJSON);
for (int i = 0; i < downloads.Count; i++) {
if (downloads[i].browser != browser || downloads[i].downloadId != downloadId) continue;
var download = downloads[i];
download.status = info["status"];
download.speed = info["speed"];
download.percent = info["percentComplete"];
if (!string.IsNullOrEmpty(info["fullPath"])) download.name = Path.GetFileName(info["fullPath"]);
break;
}
}
public void Update() {
if (statusBar) {
statusBar.text = Status;
}
}
public void PauseAll() {
for (int i = 0; i < downloads.Count; i++) {
if (downloads[i].status == "working") {
downloads[i].browser.DownloadCommand(downloads[i].downloadId, BrowserNative.DownloadAction.Pause);
}
}
}
public void ResumeAll() {
for (int i = 0; i < downloads.Count; i++) {
if (downloads[i].status == "working") {
downloads[i].browser.DownloadCommand(downloads[i].downloadId, BrowserNative.DownloadAction.Resume);
}
}
}
public void CancelAll() {
for (int i = 0; i < downloads.Count; i++) {
if (downloads[i].status == "working") {
downloads[i].browser.DownloadCommand(downloads[i].downloadId, BrowserNative.DownloadAction.Cancel);
}
}
}
public void ClearAll() {
CancelAll();
downloads.Clear();
}
private StringBuilder sb = new StringBuilder();
/** Returns a string summarizing things that are downloading. */
public string Status {
get {
if (downloads.Count == 0) return "";
sb.Length = 0;
var rate = 0;
for (int i = downloads.Count - 1; i >= 0; i--) {
if (sb.Length > 0) sb.Append(", ");
sb.Append(downloads[i].name);
if (downloads[i].status == "working") {
if (downloads[i].percent >= 0) sb.Append(" (").Append(downloads[i].percent).Append("%)");
else sb.Append(" (??%)");
rate += downloads[i].speed;
} else {
sb.Append(" (").Append(downloads[i].status).Append(")");
}
}
var ret = "Downloads";
if (rate > 0) {
ret += " (" + Mathf.Round(rate / (1024f * 1024) * 100) / 100f + "MiB/s)";
}
return ret + ": " + sb.ToString();
}
}
/** Gets the user's download folder, creating it if needed. */
public static string GetUserDownloadFolder() {
switch (System.Environment.OSVersion.Platform) {
case PlatformID.Win32NT: {
IntPtr path;
var r = SHGetKnownFolderPath(
new Guid("{374DE290-123F-4565-9164-39C4925E467B}"), //downloads
0x8000, //KF_FLAG_CREATE
IntPtr.Zero, //current user
out path
);
if (r == 0) {
var ret = Marshal.PtrToStringUni(path);
Marshal.FreeCoTaskMem(path);
return ret;
} else {
throw new Exception(
"Failed to get user download directory",
new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())
);
}
}
case PlatformID.Unix: {
var path = System.Environment.GetEnvironmentVariable("HOME") + "/Downloads";
var di = new DirectoryInfo(path);
if (!di.Exists) di.Create();
return path;
}
case PlatformID.MacOSX:
throw new NotImplementedException();
default:
throw new NotImplementedException();
}
}
[DllImport("Shell32.dll")]
private static extern int SHGetKnownFolderPath(
[MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags, IntPtr hToken,
out IntPtr ppszPath
);
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 7d247427dfff6304db2b292811004b23
timeCreated: 1510867277
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 4c5a65551ba9c9949a3b3fefeb7fc1bd
folderAsset: yes
timeCreated: 1447281187
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,133 +0,0 @@
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
namespace ZenFulcrum.EmbeddedBrowser {
[CustomEditor(typeof(Browser))]
[CanEditMultipleObjects]
public class BrowserEditor : Editor {
private static string script = "document.body.style.background = 'red';\n";
private static string html = "Hello, <i>world</i>!\n";
private static string[] commandNames;
private static BrowserNative.FrameCommand[] commandValues;
static BrowserEditor() {
var els = Enum.GetValues(typeof(BrowserNative.FrameCommand));
commandNames = new string[els.Length];
commandValues = new BrowserNative.FrameCommand[els.Length];
int i = 0;
foreach (BrowserNative.FrameCommand cmd in els) {
commandNames[i] = cmd.ToString();
commandValues[i] = cmd;
++i;
}
}
public override bool RequiresConstantRepaint() {
//The buttons get stale if we don't keep repainting them.
return Application.isPlaying;
}
public override void OnInspectorGUI() {
base.OnInspectorGUI();
if (Application.isPlaying && !serializedObject.isEditingMultipleObjects) {
RenderActions();
} else if (!Application.isPlaying) {
GUILayout.Label("Additional options available in play mode");
}
}
private void RenderActions() {
var browser = (Browser)target;
if (!browser.IsReady) {
GUILayout.Label("Starting...");
return;
}
GUILayout.BeginVertical("box");
GUILayout.Label("Apply items above:");
GUILayout.BeginHorizontal("box");
{
if (GUILayout.Button("Go to URL")) browser.LoadURL(serializedObject.FindProperty("_url").stringValue, false);
if (GUILayout.Button("Force to URL")) browser.Url = serializedObject.FindProperty("_url").stringValue;
if (GUILayout.Button("Resize")) {
browser.Resize(
serializedObject.FindProperty("_width").intValue,
serializedObject.FindProperty("_height").intValue
);
}
if (GUILayout.Button("Set Zoom")) browser.Zoom = serializedObject.FindProperty("_zoom").floatValue;
}
GUILayout.EndHorizontal();
GUILayout.Label("Actions:");
GUILayout.BeginHorizontal();
{
GUI.enabled = browser.CanGoBack;
if (GUILayout.Button("Go back")) browser.GoBack();
GUI.enabled = browser.CanGoForward;
if (GUILayout.Button("Go forward")) browser.GoForward();
GUI.enabled = true;
if (browser.IsLoadingRaw) {
if (GUILayout.Button("Stop")) browser.Stop();
} else {
if (GUILayout.Button("Reload")) browser.Reload();
}
if (GUILayout.Button("Force Reload")) browser.Reload(true);
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
{
if (GUILayout.Button("Show Dev Tools")) browser.ShowDevTools();
if (GUILayout.Button("Hide Dev Tools")) browser.ShowDevTools(false);
}
GUILayout.EndHorizontal();
GUILayout.Label("Script:");
script = EditorGUILayout.TextArea(script);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Eval JavaScript")) {
browser.EvalJS(script, "editor command");
}
if (GUILayout.Button("Eval JavaScript CSP")) {
browser.EvalJSCSP(script, "editor command");
}
GUILayout.EndHorizontal();
int pVal = EditorGUILayout.Popup("Send Command:", -1, commandNames);
if (pVal != -1) {
browser.SendFrameCommand(commandValues[pVal]);
}
GUILayout.Label("HTML:");
html = EditorGUILayout.TextArea(html);
if (GUILayout.Button("Load HTML")) {
browser.LoadHTML(html);
}
GUILayout.EndVertical();
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 28d4a442795171f4b92b03a99fcbdc6f
timeCreated: 1447280847
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,13 +0,0 @@
using UnityEditor;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
[CustomPropertyDrawer(typeof(FlagsFieldAttribute))]
public class FlagsEditor : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 4846ded01fb9d6b489529816869b1d20
timeCreated: 1450462477
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,332 +0,0 @@
//This file is partially subject to Chromium's BSD license, read the class notes for more details.
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using CursorType = ZenFulcrum.EmbeddedBrowser.BrowserNative.CursorType;
/**
* Utility for generating the cursor icons.
*
* This isn't really here for general usage, but if you're willing to read the source and
* fiddle with things this may give you a head start from starting with nothing.
*
* The default icons are pulled from
* https://chromium.googlesource.com/chromium/src.git/+/master/ui/resources/default_100_percent/common/pointers/
* https://chromium.googlesource.com/chromium/src.git/+/master/ui/resources/ui_resources.grd
* and
* https://chromium.googlesource.com/chromium/src.git/+/master/ui/base/cursor/cursors_aura.cc
* This tool is used with a local directory, {IconGenerator.path}, filled with those icons.
*
* You also need to add a "loading.png" to the folder.
*
* To use this script, update the local path to your icons, define ZF_ICON_GENERATOR, and run it in
* from Assets->ZF Browser->Generate Icons.
*/
[ExecuteInEditMode]
public class IconGenerator {
private const string path = @"/my/path/to/chromium/ui-resources/default_100_percent/common/pointers";
private const string destAsset = "ZFBrowser/Resources/Browser/Cursors";
public static bool useBig = false;
#if ZF_ICON_GENERATOR
[MenuItem("Assets/ZF Browser/Generate Icons")]
#endif
public static void GenerateIcons() {
var icons = new SortedDictionary<string, Texture2D>();
var w = -1;
var h = -1;
foreach (var file in Directory.GetFiles(path)) {
if (useBig && !file.Contains("_big.png")) continue;
if (!useBig && file.Contains("_big.png")) continue;
var tex = new Texture2D(0, 0);
tex.LoadImage(File.ReadAllBytes(file));
if (w < 0) {
w = tex.width;
h = tex.height;
} else if (w != tex.width || h != tex.height) {
throw new Exception("Icons are not all the same size. This differs: " + file);
}
var name = Path.GetFileNameWithoutExtension(file);
if (useBig) name = name.Substring(0, name.Length - 4);
icons[name] = tex;
}
//Also add one for "cursor: none"
icons["_none_"] = null;
var res = new Texture2D(w * icons.Count, h, TextureFormat.ARGB32, false);
var descData = new StringBuilder();
var namesToPositions = new Dictionary<string, int>();
var i = 0;
foreach (var kvp in icons) {
if (kvp.Value == null) {
Fill(new Color(0, 0, 0, 0), res, i * w, 0, w, h);
} else {
Copy(kvp.Value, res, i * w, 0);
}
namesToPositions[kvp.Key] = i++;
}
foreach (var kvp in mapping) {
var pos = -1;
try {
if (kvp.Value.name != "_custom_") pos = namesToPositions[kvp.Value.name];
} catch (KeyNotFoundException) {
throw new KeyNotFoundException("No file found for " + kvp.Value.name);
}
if (descData.Length != 0) descData.Append("\n");
var hotspot = kvp.Value.hotspot;
if (!useBig) {
hotspot.x = Mathf.Round(hotspot.x * .5f) - 3;
hotspot.y = Mathf.Round(kvp.Value.hotspot.y * .5f) - 4;
}
descData
.Append(kvp.Key).Append(",")
.Append(pos).Append(",")
.Append(hotspot.x).Append(",")
.Append(hotspot.y)
;
}
var resName = Application.dataPath + "/" + destAsset;
File.WriteAllBytes(
resName + ".png",
res.EncodeToPNG()
);
File.WriteAllText(
resName + ".csv",
descData.ToString()
);
AssetDatabase.Refresh();
Debug.Log("Wrote icons files to " + resName + ".(png|csv) size: " + w + "x" + h);
}
private static void Fill(Color color, Texture2D dest, int sx, int sy, int w, int h) {
for (int x = sx; x < w; ++x) {
for (int y = sy; y < h; ++y) {
dest.SetPixel(x, y, color);
}
}
}
private static void Copy(Texture2D src, Texture2D dest, int destX, int destY) {
//slow, but fine for a utility
for (int x = 0; x < src.width; ++x) {
for (int y = 0; y < src.height; ++y) {
dest.SetPixel(x + destX, y + destY, src.GetPixel(x, y));
}
}
}
private struct CursorInfo {
public CursorInfo(string name, Vector2 hotspot) {
this.name = name;
this.hotspot = hotspot;
}
public string name;
public Vector2 hotspot;
}
private static Dictionary<CursorType, CursorInfo> mapping = new Dictionary<CursorType, CursorInfo>() {
//Hotspots in for the default Chromium cursors can be found in ui/base/cursor/cursors_aura.cc, this is adapted
//from there.
//Note that we are always using the 2x (_big) icons.
{
//{19, 11}, {38, 22}} alias kCursorAlias IDR_AURA_CURSOR_ALIAS CT_ALIAS
CursorType.Alias,
new CursorInfo("alias", new Vector2(19, 11))
}, {
//{30, 30}, {60, 60}} cell kCursorCell IDR_AURA_CURSOR_CELL CT_CELL
CursorType.Cell,
new CursorInfo("cell", new Vector2(30, 30))
}, {
//{35, 29}, {70, 58}} sb_h_double_arrow kCursorColumnResize IDR_AURA_CURSOR_COL_RESIZE CT_COLUMNRESIZE
CursorType.ColumnResize,
new CursorInfo("sb_h_double_arrow", new Vector2(35, 29))
}, {
//{11, 11}, {22, 22}} context_menu kCursorContextMenu IDR_AURA_CURSOR_CONTEXT_MENU CT_CONTEXTMENU
CursorType.ContextMenu,
new CursorInfo("context_menu", new Vector2(11, 11))
}, {
//{10, 10}, {20, 20}} copy kCursorCopy IDR_AURA_CURSOR_COPY CT_COPY
CursorType.Copy,
new CursorInfo("copy", new Vector2(10, 10))
}, {
//{31, 30}, {62, 60}} crosshair kCursorCross IDR_AURA_CURSOR_CROSSHAIR CT_CROSS
CursorType.Cross,
new CursorInfo("crosshair", new Vector2(31, 30))
}, {
//{??, ??}, {??, ??}} custom kCursorCustom IDR_NONE CT_CUSTOM
CursorType.Custom,
new CursorInfo("_custom_", new Vector2(-1, -1))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorEastPanning IDR_NONE CT_EASTPANNING
CursorType.EastPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{35, 29}, {70, 58}} sb_h_double_arrow kCursorEastResize IDR_AURA_CURSOR_EAST_RESIZE CT_EASTRESIZE
CursorType.EastResize,
new CursorInfo("sb_h_double_arrow", new Vector2(35, 29))
}, {
//{35, 29}, {70, 58}} sb_h_double_arrow kCursorEastWestResize IDR_AURA_CURSOR_EAST_WEST_RESIZE CT_EASTWESTRESIZE
CursorType.EastWestResize,
new CursorInfo("sb_h_double_arrow", new Vector2(35, 29))
}, {
//{21, 11}, {42, 22}} fleur kCursorGrab IDR_AURA_CURSOR_GRAB CT_GRAB
CursorType.Grab,
new CursorInfo("fleur", new Vector2(21, 11))
}, {
//{20, 12}, {40, 24}} hand3 kCursorGrabbing IDR_AURA_CURSOR_GRABBING CT_GRABBING
CursorType.Grabbing,
new CursorInfo("hand3", new Vector2(20, 12))
}, {
//{25, 7}, {50, 14}} hand2 kCursorHand IDR_AURA_CURSOR_HAND CT_HAND
CursorType.Hand,
new CursorInfo("hand2", new Vector2(25, 7))
}, {
//{10, 11}, {20, 22}} help kCursorHelp IDR_AURA_CURSOR_HELP CT_HELP
CursorType.Help,
new CursorInfo("help", new Vector2(10, 11))
}, {
//{30, 32}, {60, 64}} xterm kCursorIBeam IDR_AURA_CURSOR_IBEAM CT_IBEAM
CursorType.IBeam,
new CursorInfo("xterm", new Vector2(30, 32))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorMiddlePanning IDR_NONE CT_MIDDLEPANNING
CursorType.MiddlePanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{32, 31}, {64, 62}} move kCursorMove IDR_AURA_CURSOR_MOVE CT_MOVE
CursorType.Move,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{10, 10}, {20, 20}} nodrop kCursorNoDrop IDR_AURA_CURSOR_NO_DROP CT_NODROP
CursorType.NoDrop,
new CursorInfo("nodrop", new Vector2(10, 10))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorNone IDR_NONE CT_NONE
CursorType.None,
new CursorInfo("_none_", new Vector2(0, 0))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorNorthEastPanning IDR_NONE CT_NORTHEASTPANNING
CursorType.NorthEastPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{31, 28}, {62, 56}} top_right_corner kCursorNorthEastResize IDR_AURA_CURSOR_NORTH_EAST_RESIZE CT_NORTHEASTRESIZE
CursorType.NorthEastResize,
new CursorInfo("top_right_corner", new Vector2(31, 28))
}, {
//{32, 30}, {64, 60}} top_right_corner kCursorNorthEastSouthWestResize IDR_AURA_CURSOR_NORTH_EAST_SOUTH_WEST_RESIZE CT_NORTHEASTSOUTHWESTRESIZE
CursorType.NorthEastSouthWestResize,
new CursorInfo("top_right_corner", new Vector2(32, 30))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorNorthPanning IDR_NONE CT_NORTHPANNING
CursorType.NorthPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{29, 32}, {58, 64}} sb_v_double_arrow kCursorNorthResize IDR_AURA_CURSOR_NORTH_RESIZE CT_NORTHRESIZE
CursorType.NorthResize,
new CursorInfo("sb_v_double_arrow", new Vector2(29, 32))
}, {
//{29, 32}, {58, 64}} sb_v_double_arrow kCursorNorthSouthResize IDR_AURA_CURSOR_NORTH_SOUTH_RESIZE CT_NORTHSOUTHRESIZE
CursorType.NorthSouthResize,
new CursorInfo("sb_v_double_arrow", new Vector2(29, 32))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorNorthWestPanning IDR_NONE CT_NORTHWESTPANNING
CursorType.NorthWestPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{28, 28}, {56, 56}} top_left_corner kCursorNorthWestResize IDR_AURA_CURSOR_NORTH_WEST_RESIZE CT_NORTHWESTRESIZE
CursorType.NorthWestResize,
new CursorInfo("top_left_corner", new Vector2(28, 28))
}, {
//{32, 31}, {64, 62}} top_left_corner kCursorNorthWestSouthEastResize IDR_AURA_CURSOR_NORTH_WEST_SOUTH_EAST_RESIZE CT_NORTHWESTSOUTHEASTRESIZE
CursorType.NorthWestSouthEastResize,
new CursorInfo("top_left_corner", new Vector2(32, 31))
}, {
//{10, 10}, {20, 20}} nodrop kCursorNotAllowed IDR_AURA_CURSOR_NO_DROP CT_NOTALLOWED
CursorType.NotAllowed,
new CursorInfo("nodrop", new Vector2(10, 10))
}, {
//{10, 10}, {20, 20}} left_ptr kCursorPointer IDR_AURA_CURSOR_PTR CT_POINTER
CursorType.Pointer,
new CursorInfo("left_ptr", new Vector2(10, 10))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorProgress IDR_NONE CT_PROGRESS
CursorType.Progress,
new CursorInfo("loading", new Vector2(32, 32))
}, {
//{29, 32}, {58, 64}} sb_v_double_arrow kCursorRowResize IDR_AURA_CURSOR_ROW_RESIZE CT_ROWRESIZE
CursorType.RowResize,
new CursorInfo("sb_v_double_arrow", new Vector2(29, 32))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorSouthEastPanning IDR_NONE CT_SOUTHEASTPANNING
CursorType.SouthEastPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{28, 28}, {56, 56}} top_left_corner kCursorSouthEastResize IDR_AURA_CURSOR_SOUTH_EAST_RESIZE CT_SOUTHEASTRESIZE
CursorType.SouthEastResize,
new CursorInfo("top_left_corner", new Vector2(28, 28))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorSouthPanning IDR_NONE CT_SOUTHPANNING
CursorType.SouthPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{29, 32}, {58, 64}} sb_v_double_arrow kCursorSouthResize IDR_AURA_CURSOR_SOUTH_RESIZE CT_SOUTHRESIZE
CursorType.SouthResize,
new CursorInfo("sb_v_double_arrow", new Vector2(29, 32))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorSouthWestPanning IDR_NONE CT_SOUTHWESTPANNING
CursorType.SouthWestPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{31, 28}, {62, 56}} top_right_corner kCursorSouthWestResize IDR_AURA_CURSOR_SOUTH_WEST_RESIZE CT_SOUTHWESTRESIZE
CursorType.SouthWestResize,
new CursorInfo("top_right_corner", new Vector2(31, 28))
}, {
//{32, 30}, {64, 60}} xterm_horiz kCursorVerticalText IDR_AURA_CURSOR_XTERM_HORIZ CT_VERTICALTEXT
CursorType.VerticalText,
new CursorInfo("xterm_horiz", new Vector2(32, 30))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorWait IDR_NONE CT_WAIT
CursorType.Wait,
new CursorInfo("loading", new Vector2(32, 32))
}, {
//{??, ??}, {??, ??}} _unknown_ kCursorWestPanning IDR_NONE CT_WESTPANNING
CursorType.WestPanning,
new CursorInfo("move", new Vector2(32, 31))
}, {
//{35, 29}, {70, 58}} sb_h_double_arrow kCursorWestResize IDR_AURA_CURSOR_WEST_RESIZE CT_WESTRESIZE
CursorType.WestResize,
new CursorInfo("sb_h_double_arrow", new Vector2(35, 29))
}, {
//{25, 26}, {50, 52}} zoom_in kCursorZoomIn IDR_AURA_CURSOR_ZOOM_IN CT_ZOOMIN
CursorType.ZoomIn,
new CursorInfo("zoom_in", new Vector2(25, 26))
}, {
//{26, 26}, {52, 52}} zoom_out kCursorZoomOut IDR_AURA_CURSOR_ZOOM_OUT CT_ZOOMOUT
CursorType.ZoomOut,
new CursorInfo("zoom_out", new Vector2(26, 26))
},
};
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 7924e40fd70097143810a7ad82727b47
timeCreated: 1447967713
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,200 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
using System.Runtime.InteropServices;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Getting CEF running on a build result requires some fiddling to get all the files in the right place.
*/
class PostBuildStandalone {
static readonly List<string> byBinFiles = new List<string>() {
"natives_blob.bin",
"snapshot_blob.bin",
"v8_context_snapshot.bin",
"icudtl.dat",
};
[PostProcessBuild(10)]
public static void PostprocessLinuxOrWindowsBuild(BuildTarget target, string buildFile) {
//prereq
var windows = target == BuildTarget.StandaloneWindows || target == BuildTarget.StandaloneWindows64;
var linux = target == BuildTarget.StandaloneLinux || target == BuildTarget.StandaloneLinuxUniversal || target == BuildTarget.StandaloneLinux64;
if (!windows && !linux) return;
if (target == BuildTarget.StandaloneLinux || target == BuildTarget.StandaloneLinuxUniversal) {
throw new Exception("ZFBrowser: Only x86_64 Linux is supported");
}
//base info
string buildType;
if (windows) buildType = "w" + (target == BuildTarget.StandaloneWindows64 ? "64" : "32");
else buildType = "l64";
Debug.Log("ZFBrowser: Post processing " + buildFile + " as " + buildType);
string buildName;
if (windows) buildName = Regex.Match(buildFile, @"/([^/]+)\.exe$").Groups[1].Value;
else buildName = Regex.Match(buildFile, @"\/([^\/]+?)(\.x86(_64)?)?$").Groups[1].Value;
var buildPath = Directory.GetParent(buildFile);
var dataPath = buildPath + "/" + buildName + "_Data";
var pluginsPath = dataPath + "/Plugins/";
//start copying
//can't use FileLocations because we may not be building the same type as the editor
var platformPluginsSrc = ZFFolder + "/Plugins/" + buildType;
//(Unity will copy the .dll and .so files for us)
//Copy "root" .bin files
foreach (var file in byBinFiles) {
File.Copy(platformPluginsSrc + "/" + file, pluginsPath + file, true);
}
File.Copy(ZFFolder + "/ThirdPartyNotices.txt", pluginsPath + "/ThirdPartyNotices.txt", true);
//Copy the needed resources
var resSrcDir = platformPluginsSrc + "/CEFResources";
foreach (var filePath in Directory.GetFiles(resSrcDir)) {
var fileName = new FileInfo(filePath).Name;
if (fileName.EndsWith(".meta")) continue;
File.Copy(filePath, pluginsPath + fileName, true);
}
//Slave process (doesn't get automatically copied by Unity like the shared libs)
var exeExt = windows ? ".exe" : "";
File.Copy(
platformPluginsSrc + "/" + FileLocations.SlaveExecutable + exeExt,
pluginsPath + FileLocations.SlaveExecutable + exeExt,
true
);
if (linux) MakeExecutable(pluginsPath + FileLocations.SlaveExecutable + exeExt);
//Locales
var localesSrcDir = platformPluginsSrc + "/CEFResources/locales";
var localesDestDir = dataPath + "/Plugins/locales";
Directory.CreateDirectory(localesDestDir);
foreach (var filePath in Directory.GetFiles(localesSrcDir)) {
var fileName = new FileInfo(filePath).Name;
if (fileName.EndsWith(".meta")) continue;
File.Copy(filePath, localesDestDir + "/" + fileName, true);
}
//Newer versions of Unity put the shared libs in the wrong place. Move them to where we expect them.
if (linux && File.Exists(pluginsPath + "x86_64/zf_cef.so")) {
foreach (var libFile in new[] {"zf_cef.so", "libEGL.so", "libGLESv2.so", "libwidevinecdmadapter.so", "libZFProxyWeb.so"}) {
ForceMove(pluginsPath + "x86_64/" + libFile, pluginsPath + libFile);
}
}
WriteBrowserAssets(dataPath + "/" + StandaloneWebResources.DefaultPath);
}
[PostProcessBuild(10)]
public static void PostprocessMacBuild(BuildTarget target, string buildFile) {
#if UNITY_2017_3_OR_NEWER
if (target != BuildTarget.StandaloneOSX) return;
#else
if (target == BuildTarget.StandaloneOSXUniversal || target == BuildTarget.StandaloneOSXIntel) {
throw new Exception("Only x86_64 is supported");
}
if (target != BuildTarget.StandaloneOSXIntel64) return;
#endif
Debug.Log("Post processing " + buildFile);
//var buildName = Regex.Match(buildFile, @"\/([^\/]+?)\.app$").Groups[1].Value;
var buildPath = buildFile;
var platformPluginsSrc = ZFFolder + "/Plugins/m64";
//Copy app bits
CopyDirectory(
platformPluginsSrc + "/BrowserLib.app/Contents/Frameworks/Chromium Embedded Framework.framework",
buildPath + "/Contents/Frameworks/Chromium Embedded Framework.framework"
);
CopyDirectory(
platformPluginsSrc + "/BrowserLib.app/Contents/Frameworks/ZFGameBrowser.app",
buildPath + "/Contents/Frameworks/ZFGameBrowser.app"
);
MakeExecutable(buildPath + "/BrowserLib.app/Contents/Frameworks/ZFGameBrowser.app/Contents/MacOS/ZFGameBrowser");
if (!Directory.Exists(buildPath + "/Contents/Plugins")) Directory.CreateDirectory(buildPath + "/Contents/Plugins");
File.Copy(platformPluginsSrc + "/libZFProxyWeb.dylib", buildPath + "/Contents/Plugins/libZFProxyWeb.dylib", true);
File.Copy(ZFFolder + "/ThirdPartyNotices.txt", buildPath + "/ThirdPartyNotices.txt", true);
//BrowserAssets
WriteBrowserAssets(buildPath + "/Contents/" + StandaloneWebResources.DefaultPath);
}
private static void WriteBrowserAssets(string path) {
//Debug.Log("Writing browser assets to " + path);
var htmlDir = Application.dataPath + "/../BrowserAssets";
var allData = new Dictionary<string, byte[]>();
if (Directory.Exists(htmlDir)) {
foreach (var file in Directory.GetFiles(htmlDir, "*", SearchOption.AllDirectories)) {
var localPath = file.Substring(htmlDir.Length).Replace("\\", "/");
allData[localPath] = File.ReadAllBytes(file);
}
}
var wr = new StandaloneWebResources(path);
wr.WriteData(allData);
}
private static void ForceMove(string src, string dest) {
if (File.Exists(dest)) File.Delete(dest);
File.Move(src, dest);
}
private static string ZFFolder {
get {
var path = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileName();
path = Directory.GetParent(path).Parent.Parent.FullName;
return path;
}
}
private static void CopyDirectory(string src, string dest) {
foreach (var dir in Directory.GetDirectories(src, "*", SearchOption.AllDirectories)) {
Directory.CreateDirectory(dir.Replace(src, dest));
}
foreach (var file in Directory.GetFiles(src, "*", SearchOption.AllDirectories)) {
if (file.EndsWith(".meta")) continue;
File.Copy(file, file.Replace(src, dest), true);
}
}
private static void MakeExecutable(string fileName) {
#if UNITY_EDITOR_WIN
Debug.LogWarning("ZFBrowser: Be sure to mark the file \"" + fileName + "\" as executable (chmod +x) when you distribute it. If it's not executable the browser won't work.");
#else
//dec 493 = oct 755 = -rwxr-xr-x
chmod(fileName, 493);
#endif
}
[DllImport("__Internal")] static extern int symlink(string destStr, string symFile);
[DllImport("__Internal")] static extern int chmod(string file, int mode);
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 821c048fda6531349be543e9b923a328
timeCreated: 1449181504
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,10 +0,0 @@
{
"name": "ZFBrowser-Editor",
"references": [
"ZFBrowser"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: fe934ebeb18e2174a8b814db25fbce70
timeCreated: 1518479097
licenseType: Store
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
#if UNITY_2017_3_OR_NEWER
using UnityEngine.Networking;
#endif
namespace ZenFulcrum.EmbeddedBrowser {
/**
* WebResources implementation that grabs resources directly from Assets/../BrowserAssets.
*/
class EditorWebResources : WebResources {
protected string basePath;
public EditorWebResources() {
//NB: If you try to read Application.dataPath later you may not be on the main thread and it won't work.
basePath = Path.GetDirectoryName(Application.dataPath) + "/BrowserAssets";
}
/// <summary>
/// Looks for "../asdf", "asdf/..\asdf", etc.
/// </summary>
private readonly Regex matchDots = new Regex(@"(^|[/\\])\.[2,]($|[/\\])");
public override void HandleRequest(int id, string url) {
var parsedURL = new Uri(url);
#if UNITY_2017_3_OR_NEWER
var path = UnityWebRequest.UnEscapeURL(parsedURL.AbsolutePath);
#else
var path = WWW.UnEscapeURL(parsedURL.AbsolutePath);
#endif
if (matchDots.IsMatch(path)) {
SendError(id, "Invalid path", 400);
return;
}
var file = new FileInfo(Application.dataPath + "/../BrowserAssets/" + path);
if (!file.Exists) {
SendError(id, "Not found", 404);
return;
}
//fixme: send 404 if file capitalization doesn't match. (Unfortunately, not a quick one-liner)
SendFile(id, file);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 59d86905d3bcefc4586e70747332bb6f
timeCreated: 1449617902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,238 +0,0 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
#define ZF_OSX
#endif
using System;
using System.Collections;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Helper/worker class for displaying an external keyboard and
/// sending the input to a browser.
///
/// Don't put this script on your target browser directly, add a separate browser
/// to the scene and attach it to that instead.
///
/// </summary>
[RequireComponent(typeof(Browser))]
[RequireComponent(typeof(PointerUIBase))]
public class ExternalKeyboard : MonoBehaviour {
[Tooltip("Set to true before startup to have the keyboard automatically hook to the browser with the most recently focused text field.")]
public bool automaticFocus;
[Tooltip("Browser to start as the focused browser for this keyboard. Not really needed if automaticFocus is on.")]
public Browser initialBrowser;
[Tooltip("Set to true to have the keyboard automatically hide when we don't have a text entry box to type into.")]
public bool hideWhenUnneeded = true;
protected PointerUIBase activeBrowserUI;
protected Browser keyboardBrowser;
protected bool forcingFocus;
protected Browser _activeBrowser;
/// <summary>
/// Browser that gets input if we press keys on the keyboard.
/// </summary>
protected virtual Browser ActiveBrowser {
get { return _activeBrowser; }
set {
_SetActiveBrowser(value);
DoFocus(_activeBrowser);
}
}
protected void _SetActiveBrowser(Browser browser) {
if (ActiveBrowser) {
if (activeBrowserUI && forcingFocus) {
activeBrowserUI.ForceKeyboardHasFocus(false);
forcingFocus = false;
}
}
_activeBrowser = browser;
activeBrowserUI = ActiveBrowser.GetComponent<PointerUIBase>();
if (!activeBrowserUI) {
//We can't focus the browser when we type, so the typed letters won't appear as we type.
Debug.LogWarning("Browser does not haver a PointerUI, external keyboard may not work properly.");
}
}
/// <summary>
/// Called when the focus of the keyboard changes.
/// The browser is the browser we are focused on (may or may not be different), editable will be true if we
/// are expected to type in the new focus, false if not.
/// </summary>
public event Action<Browser, bool> onFocusChange = (browser, editable) => {};
public void Awake() {
var keyboardPage = Resources.Load<TextAsset>("Browser/Keyboard").text;
keyboardBrowser = GetComponent<Browser>();
keyboardBrowser.onBrowserFocus += OnBrowserFocus;
keyboardBrowser.LoadHTML(keyboardPage);
keyboardBrowser.RegisterFunction("textTyped", TextTyped);
keyboardBrowser.RegisterFunction("commandEntered", CommandEntered);
if (initialBrowser) ActiveBrowser = initialBrowser;
if (automaticFocus) {
StartCoroutine(FindAndListenForBrowsers());
}
}
protected IEnumerator FindAndListenForBrowsers() {
yield return null;
foreach (var browser in FindObjectsOfType<Browser>()) {
if (browser == keyboardBrowser) continue;
ObserveBrowser(browser);
}
Browser.onAnyBrowserCreated += ObserveBrowser;
//in theory we shouldn't need to deal with browsers being destroyed since the whole callback chain should get cleaned up
//(might need some more work if you repeatedly destroy and recreate keyboards, though)
}
protected void ObserveBrowser(Browser browser) {
browser.onNodeFocus += (tagName, editable, value) => {
if (!this) return;
if (!browser.focusState.hasMouseFocus) return;
DoFocus(browser);
};
var pointerUI = browser.GetComponent<PointerUIBase>();
if (pointerUI) {
pointerUI.onClick += () => {
DoFocus(browser);
};
}
}
protected void DoFocus(Browser browser) {
if (browser != ActiveBrowser) {
_SetActiveBrowser(browser);
}
bool visible;
if (browser) visible = browser.focusState.focusedNodeEditable;
else visible = false;
SetVisible(visible);
onFocusChange(_activeBrowser, visible);
}
protected void SetVisible(bool visible) {
var renderer = GetComponent<Renderer>();
if (renderer) renderer.enabled = visible;
var collider = GetComponent<Collider>();
if (collider) collider.enabled = visible;
}
protected void OnBrowserFocus(bool mouseFocused, bool kbFocused) {
//when our keyboard is focused, focus the browser we will be typing into.
if (!activeBrowserUI) return;
if ((mouseFocused || kbFocused) && !forcingFocus) {
activeBrowserUI.ForceKeyboardHasFocus(true);
forcingFocus = true;
}
if (!(mouseFocused || kbFocused) && forcingFocus) {
activeBrowserUI.ForceKeyboardHasFocus(false);
forcingFocus = false;
}
}
protected void CommandEntered(JSONNode args) {
if (!ActiveBrowser) return;
string command = args[0];
bool shiftPressed = args[1];
if (shiftPressed) ActiveBrowser.PressKey(KeyCode.LeftShift, KeyAction.Press);
#if ZF_OSX
const KeyCode wordShifter = KeyCode.LeftAlt;
#else
const KeyCode wordShifter = KeyCode.LeftControl;
#endif
switch (command) {
case "backspace":
ActiveBrowser.PressKey(KeyCode.Backspace);
break;
case "copy":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.Copy);
break;
case "cut":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.Cut);
break;
case "delete":
ActiveBrowser.PressKey(KeyCode.Delete);
break;
case "down":
ActiveBrowser.PressKey(KeyCode.DownArrow);
break;
case "end":
ActiveBrowser.PressKey(KeyCode.End);
break;
case "home":
ActiveBrowser.PressKey(KeyCode.Home);
break;
case "insert":
ActiveBrowser.PressKey(KeyCode.Insert);
break;
case "left":
ActiveBrowser.PressKey(KeyCode.LeftArrow);
break;
case "pageDown":
ActiveBrowser.PressKey(KeyCode.PageDown);
break;
case "pageUp":
ActiveBrowser.PressKey(KeyCode.PageUp);
break;
case "paste":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.Paste);
break;
case "redo":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.Redo);
break;
case "right":
ActiveBrowser.PressKey(KeyCode.RightArrow);
break;
case "selectAll":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.SelectAll);
break;
case "undo":
ActiveBrowser.SendFrameCommand(BrowserNative.FrameCommand.Undo);
break;
case "up":
ActiveBrowser.PressKey(KeyCode.UpArrow);
break;
case "wordLeft":
ActiveBrowser.PressKey(wordShifter, KeyAction.Press);
ActiveBrowser.PressKey(KeyCode.LeftArrow);
ActiveBrowser.PressKey(wordShifter, KeyAction.Release);
break;
case "wordRight":
ActiveBrowser.PressKey(wordShifter, KeyAction.Press);
ActiveBrowser.PressKey(KeyCode.RightArrow);
ActiveBrowser.PressKey(wordShifter, KeyAction.Release);
break;
}
if (shiftPressed) ActiveBrowser.PressKey(KeyCode.LeftShift, KeyAction.Release);
}
protected void TextTyped(JSONNode args) {
if (!ActiveBrowser) return;
ActiveBrowser.TypeText(args[0]);
}
}
}

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: faa68da6a73a843439bee4b722ce18b4
timeCreated: 1511320118
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,144 +0,0 @@
using System;
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace ZenFulcrum.EmbeddedBrowser {
public static class FileLocations {
public const string SlaveExecutable = "ZFGameBrowser";
private static CEFDirs _dirs;
public static CEFDirs Dirs {
get { return _dirs ?? (_dirs = GetCEFDirs()); }
}
public class CEFDirs {
/** Where to find cef.pak, et al */
public string resourcesPath;
/** Where to find .dll, .so, natives_blob.bin, etc */
public string binariesPath;
/** Where to find en-US.pak et al */
public string localesPath;
/** The executable to run for browser processes. */
public string subprocessFile;
/** Editor/application log file */
public string logFile;
}
private static CEFDirs GetCEFDirs() {
#if UNITY_EDITOR
//In the editor we don't know exactly where we are at, but we can look up one of our scripts and move from there
var guids = AssetDatabase.FindAssets("EditorWebResources");
if (guids.Length != 1) throw new FileNotFoundException("Failed to locate a single EditorWebResources file");
string scriptPath = AssetDatabase.GUIDToAssetPath(guids[0]);
// ReSharper disable once PossibleNullReferenceException
var baseDir = Directory.GetParent(scriptPath).Parent.FullName + "/Plugins";
string resourcesPath, localesDir;
var platformDir = baseDir;
#if UNITY_EDITOR_WIN
#if UNITY_EDITOR_64
platformDir += "/w64";
#else
platformDir += "/w32";
#endif
resourcesPath = platformDir + "/CEFResources";
localesDir = resourcesPath + "/locales";
//Silly MS.
resourcesPath = resourcesPath.Replace("/", "\\");
localesDir = localesDir.Replace("/", "\\");
platformDir = platformDir.Replace("/", "\\");
var subprocessFile = platformDir + "/" + SlaveExecutable + ".exe";
var logFile = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Unity/Editor/Editor.log";
#elif UNITY_EDITOR_LINUX
#if UNITY_EDITOR_64
platformDir += "/l64";
#else
platformDir += "/w32";
#endif
resourcesPath = platformDir + "/CEFResources";
localesDir = resourcesPath + "/locales";
var subprocessFile = platformDir + "/" + SlaveExecutable;
var logFile = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/unity3d/Editor.log";
#elif UNITY_EDITOR_OSX
platformDir += "/m64";
resourcesPath = platformDir + "/BrowserLib.app/Contents/Frameworks/Chromium Embedded Framework.framework/Resources";
localesDir = resourcesPath;
//Chromium's base::mac::GetAppBundlePath will walk up the tree until it finds an ".app" folder and start
//looking for pieces from there. That's why everything is hidden in a fake "BrowserLib.app"
//folder that's not actually an app.
var subprocessFile = platformDir + "/BrowserLib.app/Contents/Frameworks/" + SlaveExecutable + ".app/Contents/MacOS/" + SlaveExecutable;
var logFile = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/Library/Logs/Unity/Editor.log";
#else
//If you want to build your app without ZFBrowser on some platforms change this to an exception or
//tweak the .asmdef
#error ZFBrowser is not supported on this platform
#endif
return new CEFDirs() {
resourcesPath = resourcesPath,
binariesPath = platformDir,
localesPath = localesDir,
subprocessFile = subprocessFile,
logFile = logFile,
};
#elif UNITY_STANDALONE_WIN
var resourcesPath = Application.dataPath + "/Plugins";
var logFile = Application.dataPath + "/output_log.txt";
#if UNITY_2017_2_OR_NEWER
var appLowDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "Low/" + Application.companyName + "/" + Application.productName;
if (Directory.Exists(appLowDir)) {
logFile = appLowDir + "/output_log.txt";
}
#endif
return new CEFDirs() {
resourcesPath = resourcesPath,
binariesPath = resourcesPath,
localesPath = resourcesPath + "/locales",
subprocessFile = resourcesPath + "/" + SlaveExecutable + ".exe",
logFile = logFile,
};
#elif UNITY_STANDALONE_LINUX
var resourcesPath = Application.dataPath + "/Plugins";
return new CEFDirs() {
resourcesPath = resourcesPath,
binariesPath = resourcesPath,
localesPath = resourcesPath + "/locales",
subprocessFile = resourcesPath + "/" + SlaveExecutable,
logFile = "/dev/null",
// Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/unity3d/" +
// Application.companyName + "/" + Application.productName + "/Player.log",
};
#elif UNITY_STANDALONE_OSX
return new CEFDirs() {
resourcesPath = Application.dataPath + "/Frameworks/Chromium Embedded Framework.framework/Resources",
binariesPath = Application.dataPath + "/Plugins",
localesPath = Application.dataPath + "/Frameworks/Chromium Embedded Framework.framework/Resources",
subprocessFile = Application.dataPath + "/Frameworks/ZFGameBrowser.app/Contents/MacOS/" + SlaveExecutable,
logFile = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/Library/Logs/Unity/Player.log",
};
#else
#error Web textures are not supported on this platform
#endif
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 6fee78664a3055740bb2c20fa9c2d38a
timeCreated: 1454001980
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,21 +0,0 @@
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Handler for creating new windows. (See Browser.NewWindowAction.NewBrowser)
///
/// When the browser needs to open a new window, myHandler.CreateBrowser will be called. Create a
/// new browser how and where you will, then return it. The new Browser will be filled with
/// the new page.
///
/// Call browser.SetNewWindowHandler(NewWindowAction.NewBrowser, myClass) to have a browser use it.
/// </summary>
public interface INewWindowHandler {
/**
* Creates a new Browser object to hold a new page.
* The returned Browser object will then be linked and load the new page.
*/
Browser CreateBrowser(Browser parent);
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 3ee8fedccb68f1a46965dec4d88bd321
timeCreated: 1449697159
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,269 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Stand-in class for a JavaScript value that can be one of many different types.
*
* Bad lookups are safe. That is, if you try to look up something that doesn't exist you will not get an exception,
* but an "invalid" node. Use Check() if you want an exception on invalid lookups:
*
* var node = JSONNode.Parse(@"{""a"":1}");
* node["a"].IsValid == true;
* node["bob"].IsValid == false
* node["bob"]["foo"].IsValid == false //but doesn't throw an exception
* node["a"].Check() //okay
* node["bob"].Check() //throw exception
*
* Values can be implicitly converted to JSONNodes and back. That means you don't have to use properties like
* "IntValue" and "StringValue". Simply try to use the node as that type and it will convert to the value
* if it's that type or return a default value if it isn't:
*
* var node = JSONNode.Parse(@"{""a"":1, ""b"": ""apples""}");
* int a = node["a"];
* a == 1;
* string b = node["b"];
* b == "apples";
* string str = node["a"];
* str == null; //null is the default value for a string.
*
* You can also use new JSONNode(value) for many different types, though often it's easier to just assign a
* value and let it auto-convert.
*
* Because they act a little special, use node.IsNull and node.IsValid to check for null and invalid values.
* Real null still acts like null, though, so use JSONNode.NullNode to create a "null" JSONNode.
* You can also use JSONNode.InvalidNode to get an invalid JSONNode outright.
*
* Note that, while reading blind is safe, assignment is not. Attempting to assign object keys to an integer, for example,
* will throw an exception. To append to an array, call .Add() or assign to -1. To remove an object key or array element,
* assign JSONNode.InvalidNode to it.
*
*/
public class JSONNode {
/** Parses the given JSON document to a JSONNode. Throws a SerializationException on parse error. */
public static JSONNode Parse(string json) {
return JSONParser.Parse(json);
}
public static readonly JSONNode InvalidNode = new JSONNode(NodeType.Invalid);
public static readonly JSONNode NullNode = new JSONNode(NodeType.Null);
public enum NodeType {
/** Getting this value would result in undefined or ordinarily throw some type of exception trying to fetch it. */
Invalid,
String,
Number,
Object,
Array,
Bool,
Null,
}
public NodeType _type = NodeType.Invalid;
private string _stringValue;
private double _numberValue;
private Dictionary<string, JSONNode> _objectValue;
private List<JSONNode> _arrayValue;
private bool _boolValue;
public JSONNode(NodeType type = NodeType.Null) {
this._type = type;
if (type == NodeType.Object) _objectValue = new Dictionary<string, JSONNode>();
else if (type == NodeType.Array) _arrayValue = new List<JSONNode>();
}
public JSONNode(string value) {
this._type = NodeType.String;
_stringValue = value;
}
public JSONNode(double value) {
this._type = NodeType.Number;
_numberValue = value;
}
public JSONNode(Dictionary<string, JSONNode> value) {
this._type = NodeType.Object;
_objectValue = value;
}
public JSONNode(List<JSONNode> value) {
this._type = NodeType.Array;
_arrayValue = value;
}
public JSONNode(bool value) {
this._type = NodeType.Bool;
_boolValue = value;
}
public NodeType Type { get { return _type; } }
public bool IsValid {
get { return _type != NodeType.Invalid; }
}
/**
* Checks if the node is valid. If not, throws an exception.
* Returns {this}, which allows you to add this statement inline in you expressions.
*
* Example:
* var node = data["key1"][1].Check();
* int val = data["maxSize"].Check()["elements"][3];
*/
public JSONNode Check() {
if (_type == NodeType.Invalid) throw new InvalidJSONNodeException();
return this;
}
public static implicit operator string(JSONNode n) {
return n._type == NodeType.String ? n._stringValue : null;
}
public static implicit operator JSONNode(string v) {
return new JSONNode(v);
}
public static implicit operator int(JSONNode n) {
return n._type == NodeType.Number ? (int)n._numberValue : 0;
}
public static implicit operator JSONNode(int v) {
return new JSONNode(v);
}
public static implicit operator float(JSONNode n) {
return n._type == NodeType.Number ? (float)n._numberValue : 0;
}
public static implicit operator JSONNode(float v) {
return new JSONNode(v);
}
public static implicit operator double(JSONNode n) {
return n._type == NodeType.Number ? n._numberValue : 0;
}
public static implicit operator JSONNode(double v) {
return new JSONNode(v);
}
/**
* Setter/getter for keys on an object. All keys are strings.
* Assign JSONNode.InvalidValue to delete a key.
*/
public JSONNode this[string k] {
get {
if (_type == NodeType.Object) {
JSONNode ret;
if (_objectValue.TryGetValue(k, out ret)) return ret;
}
return InvalidNode;
}
set {
if (_type != NodeType.Object) throw new InvalidJSONNodeException();
if (value._type == NodeType.Invalid) _objectValue.Remove(k);
else _objectValue[k] = value;
}
}
public static implicit operator Dictionary<string, JSONNode>(JSONNode n) {
return n._type == NodeType.Object ? n._objectValue : null;
}
/**
* Setter/getter for indicies on an array.
* Assign JSONNode.InvalidValue to delete a key.
* Assign to "-1" to append to the end.
*/
public JSONNode this[int idx] {
get {
if (_type == NodeType.Array && idx >= 0 && idx < _arrayValue.Count) {
return _arrayValue[idx];
}
return InvalidNode;
}
set {
if (_type != NodeType.Array) throw new InvalidJSONNodeException();
if (idx == -1) {
if (value._type == NodeType.Invalid) {
_arrayValue.RemoveAt(_arrayValue.Count - 1);
} else {
_arrayValue.Add(value);
}
} else {
if (value._type == NodeType.Invalid) {
_arrayValue.RemoveAt(idx);
} else {
_arrayValue[idx] = value;
}
}
}
}
public static implicit operator List<JSONNode>(JSONNode n) {
return n._type == NodeType.Array ? n._arrayValue : null;
}
/** Adds an items if the node is an array. */
public void Add(JSONNode item) {
if (_type != NodeType.Array) throw new InvalidJSONNodeException();
_arrayValue.Add(item);
}
/** If we are an array or object, returns the size, otherwise returns 0. */
public int Count {
get {
switch (_type) {
case NodeType.Array: return _arrayValue.Count;
case NodeType.Object: return _objectValue.Count;
default: return 0;
}
}
}
/** True if the value of this node is exactly null. */
public bool IsNull {
get { return _type == NodeType.Null; }
}
public static implicit operator bool(JSONNode n) {
return n._type == NodeType.Bool ? n._boolValue : false;
}
public static implicit operator JSONNode(bool v) {
return new JSONNode(v);
}
/** Returns a native value representation of our value. */
public object Value {
get {
switch (_type) {
case NodeType.Invalid:
Check();
return null;//we don't get here.
case NodeType.String:
return _stringValue;
case NodeType.Number:
return _numberValue;
case NodeType.Object:
return _objectValue;
case NodeType.Array:
return _arrayValue;
case NodeType.Bool:
return _boolValue;
case NodeType.Null:
return null;
default:
throw new ArgumentOutOfRangeException();
}
}
}
/** Serializes the JSON node and returns a JSON string. */
public string AsJSON {
get {
return JSONParser.Serialize(this);
}
}
}
public class InvalidJSONNodeException : Exception {}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 0ae4f653ec4655d47b160575a231ddc9
timeCreated: 1447780609
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,541 +0,0 @@
/*
* This is a customized parser, based on "SimpleJson.cs", for JSONNode
*
* SimpleJson.cs is open source software and this entire file may be dealt with as described below:
*/
//-----------------------------------------------------------------------
// <copyright file="SimpleJson.cs" company="The Outercurve Foundation">
// Copyright (c) 2011, The Outercurve Foundation, 2015 Zen Fulcrum LLC
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.opensource.org/licenses/mit-license.php
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
// <author>Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me)</author>
// <website>https://github.com/facebook-csharp-sdk/simple-json</website>
//-----------------------------------------------------------------------
// ReSharper disable InconsistentNaming
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization;
using System.Text;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList&lt;object>) and JsonObject(IDictionary&lt;string,object>).
/// All numbers are parsed to doubles.
/// </summary>
internal static class JSONParser {
private const int TOKEN_NONE = 0;
private const int TOKEN_CURLY_OPEN = 1;
private const int TOKEN_CURLY_CLOSE = 2;
private const int TOKEN_SQUARED_OPEN = 3;
private const int TOKEN_SQUARED_CLOSE = 4;
private const int TOKEN_COLON = 5;
private const int TOKEN_COMMA = 6;
private const int TOKEN_STRING = 7;
private const int TOKEN_NUMBER = 8;
private const int TOKEN_TRUE = 9;
private const int TOKEN_FALSE = 10;
private const int TOKEN_NULL = 11;
private const int BUILDER_CAPACITY = 2000;
private static readonly char[] EscapeTable;
private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' };
// private static readonly string EscapeCharactersString = new string(EscapeCharacters);
static JSONParser() {
EscapeTable = new char[93];
EscapeTable['"'] = '"';
EscapeTable['\\'] = '\\';
EscapeTable['\b'] = 'b';
EscapeTable['\f'] = 'f';
EscapeTable['\n'] = 'n';
EscapeTable['\r'] = 'r';
EscapeTable['\t'] = 't';
}
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An IList&lt;object>, a IDictionary&lt;string,object>, a double, a string, null, true, or false</returns>
public static JSONNode Parse(string json) {
JSONNode obj;
if (TryDeserializeObject(json, out obj))
return obj;
throw new SerializationException("Invalid JSON string");
}
/// <summary>
/// Try parsing the json string into a value.
/// </summary>
/// <param name="json">
/// A JSON string.
/// </param>
/// <param name="obj">
/// The object.
/// </param>
/// <returns>
/// Returns true if successfull otherwise false.
/// </returns>
public static bool TryDeserializeObject(string json, out JSONNode obj) {
bool success = true;
if (json != null) {
char[] charArray = json.ToCharArray();
int index = 0;
obj = ParseValue(charArray, ref index, ref success);
} else
obj = null;
return success;
}
public static string EscapeToJavascriptString(string jsonString) {
if (string.IsNullOrEmpty(jsonString))
return jsonString;
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < jsonString.Length; ) {
c = jsonString[i++];
if (c == '\\') {
int remainingLength = jsonString.Length - i;
if (remainingLength >= 2) {
char lookahead = jsonString[i];
if (lookahead == '\\') {
sb.Append('\\');
++i;
} else if (lookahead == '"') {
sb.Append("\"");
++i;
} else if (lookahead == 't') {
sb.Append('\t');
++i;
} else if (lookahead == 'b') {
sb.Append('\b');
++i;
} else if (lookahead == 'n') {
sb.Append('\n');
++i;
} else if (lookahead == 'r') {
sb.Append('\r');
++i;
}
}
} else {
sb.Append(c);
}
}
return sb.ToString();
}
static JSONNode ParseObject(char[] json, ref int index, ref bool success) {
JSONNode table = new JSONNode(JSONNode.NodeType.Object);
int token;
// {
NextToken(json, ref index);
bool done = false;
while (!done) {
token = LookAhead(json, index);
if (token == TOKEN_NONE) {
success = false;
return null;
} else if (token == TOKEN_COMMA)
NextToken(json, ref index);
else if (token == TOKEN_CURLY_CLOSE) {
NextToken(json, ref index);
return table;
} else {
// name
string name = ParseString(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
// :
token = NextToken(json, ref index);
if (token != TOKEN_COLON) {
success = false;
return null;
}
// value
JSONNode value = ParseValue(json, ref index, ref success);
if (!success) {
success = false;
return null;
}
table[name] = value;
}
}
return table;
}
static JSONNode ParseArray(char[] json, ref int index, ref bool success) {
JSONNode array = new JSONNode(JSONNode.NodeType.Array);
// [
NextToken(json, ref index);
bool done = false;
while (!done) {
int token = LookAhead(json, index);
if (token == TOKEN_NONE) {
success = false;
return null;
} else if (token == TOKEN_COMMA)
NextToken(json, ref index);
else if (token == TOKEN_SQUARED_CLOSE) {
NextToken(json, ref index);
break;
} else {
JSONNode value = ParseValue(json, ref index, ref success);
if (!success)
return null;
array.Add(value);
}
}
return array;
}
static JSONNode ParseValue(char[] json, ref int index, ref bool success) {
switch (LookAhead(json, index)) {
case TOKEN_STRING:
return ParseString(json, ref index, ref success);
case TOKEN_NUMBER:
return ParseNumber(json, ref index, ref success);
case TOKEN_CURLY_OPEN:
return ParseObject(json, ref index, ref success);
case TOKEN_SQUARED_OPEN:
return ParseArray(json, ref index, ref success);
case TOKEN_TRUE:
NextToken(json, ref index);
return true;
case TOKEN_FALSE:
NextToken(json, ref index);
return false;
case TOKEN_NULL:
NextToken(json, ref index);
return JSONNode.NullNode;
case TOKEN_NONE:
break;
}
success = false;
return JSONNode.InvalidNode;
}
static JSONNode ParseString(char[] json, ref int index, ref bool success) {
StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
char c;
EatWhitespace(json, ref index);
// "
c = json[index++];
bool complete = false;
while (!complete) {
if (index == json.Length)
break;
c = json[index++];
if (c == '"') {
complete = true;
break;
} else if (c == '\\') {
if (index == json.Length)
break;
c = json[index++];
if (c == '"')
s.Append('"');
else if (c == '\\')
s.Append('\\');
else if (c == '/')
s.Append('/');
else if (c == 'b')
s.Append('\b');
else if (c == 'f')
s.Append('\f');
else if (c == 'n')
s.Append('\n');
else if (c == 'r')
s.Append('\r');
else if (c == 't')
s.Append('\t');
else if (c == 'u') {
int remainingLength = json.Length - index;
if (remainingLength >= 4) {
// parse the 32 bit hex into an integer codepoint
uint codePoint;
if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint)))
return "";
// convert the integer codepoint to a unicode char and add to string
if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate
{
index += 4; // skip 4 chars
remainingLength = json.Length - index;
if (remainingLength >= 6) {
uint lowCodePoint;
if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) {
if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate
{
s.Append((char)codePoint);
s.Append((char)lowCodePoint);
index += 6; // skip 6 chars
continue;
}
}
}
success = false; // invalid surrogate pair
return "";
}
s.Append(ConvertFromUtf32((int)codePoint));
// skip 4 chars
index += 4;
} else
break;
}
} else
s.Append(c);
}
if (!complete) {
success = false;
return null;
}
return s.ToString();
}
private static string ConvertFromUtf32(int utf32) {
// http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm
if (utf32 < 0 || utf32 > 0x10FFFF)
throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF.");
if (0xD800 <= utf32 && utf32 <= 0xDFFF)
throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range.");
if (utf32 < 0x10000)
return new string((char)utf32, 1);
utf32 -= 0x10000;
return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) });
}
static JSONNode ParseNumber(char[] json, ref int index, ref bool success) {
EatWhitespace(json, ref index);
int lastIndex = GetLastIndexOfNumber(json, index);
int charLength = (lastIndex - index) + 1;
JSONNode returnNumber;
string str = new string(json, index, charLength);
if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) {
double number;
success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
returnNumber = number;
} else {
long number;
success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);
returnNumber = number;
}
index = lastIndex + 1;
return returnNumber;
}
static int GetLastIndexOfNumber(char[] json, int index) {
int lastIndex;
for (lastIndex = index; lastIndex < json.Length; lastIndex++)
if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break;
return lastIndex - 1;
}
static void EatWhitespace(char[] json, ref int index) {
for (; index < json.Length; index++)
if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break;
}
static int LookAhead(char[] json, int index) {
int saveIndex = index;
return NextToken(json, ref saveIndex);
}
static int NextToken(char[] json, ref int index) {
EatWhitespace(json, ref index);
if (index == json.Length)
return TOKEN_NONE;
char c = json[index];
index++;
switch (c) {
case '{':
return TOKEN_CURLY_OPEN;
case '}':
return TOKEN_CURLY_CLOSE;
case '[':
return TOKEN_SQUARED_OPEN;
case ']':
return TOKEN_SQUARED_CLOSE;
case ',':
return TOKEN_COMMA;
case '"':
return TOKEN_STRING;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return TOKEN_NUMBER;
case ':':
return TOKEN_COLON;
}
index--;
int remainingLength = json.Length - index;
// false
if (remainingLength >= 5) {
if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') {
index += 5;
return TOKEN_FALSE;
}
}
// true
if (remainingLength >= 4) {
if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') {
index += 4;
return TOKEN_TRUE;
}
}
// null
if (remainingLength >= 4) {
if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') {
index += 4;
return TOKEN_NULL;
}
}
return TOKEN_NONE;
}
public static string Serialize(JSONNode node) {
StringBuilder sb = new StringBuilder();
var success = SerializeValue(node, sb);
if (!success) throw new SerializationException("Failed to serialize JSON");
return sb.ToString();
}
static bool SerializeValue(JSONNode value, StringBuilder builder) {
bool success = true;
// if (value == null) {
// builder.Append("null");
// return success;
// }
switch (value.Type) {
case JSONNode.NodeType.String:
success = SerializeString(value, builder);
break;
case JSONNode.NodeType.Object: {
Dictionary<String, JSONNode> dict = value;
success = SerializeObject(dict.Keys, dict.Values, builder);
break;
}
case JSONNode.NodeType.Array:
success = SerializeArray((List<JSONNode>)value, builder);
break;
case JSONNode.NodeType.Number:
success = SerializeNumber(value, builder);
break;
case JSONNode.NodeType.Bool:
builder.Append(value ? "true" : "false");
break;
case JSONNode.NodeType.Null:
builder.Append("null");
break;
case JSONNode.NodeType.Invalid:
throw new SerializationException("Cannot serialize invalid JSONNode");
default:
throw new SerializationException("Unknown JSONNode type");
}
return success;
}
static bool SerializeObject(IEnumerable<string> keys, IEnumerable<JSONNode> values, StringBuilder builder) {
builder.Append("{");
var ke = keys.GetEnumerator();
var ve = values.GetEnumerator();
bool first = true;
while (ke.MoveNext() && ve.MoveNext()) {
var key = ke.Current;
var value = ve.Current;
if (!first)
builder.Append(",");
string stringKey = key;
if (stringKey != null)
SerializeString(stringKey, builder);
else
if (!SerializeValue(value, builder)) return false;
builder.Append(":");
if (!SerializeValue(value, builder))
return false;
first = false;
}
builder.Append("}");
return true;
}
static bool SerializeArray(IEnumerable<JSONNode> anArray, StringBuilder builder) {
builder.Append("[");
bool first = true;
foreach (var value in anArray) {
if (!first)
builder.Append(",");
if (!SerializeValue(value, builder))
return false;
first = false;
}
builder.Append("]");
return true;
}
static bool SerializeString(string aString, StringBuilder builder) {
// Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged)
if (aString.IndexOfAny(EscapeCharacters) == -1) {
builder.Append('"');
builder.Append(aString);
builder.Append('"');
return true;
}
builder.Append('"');
int safeCharacterCount = 0;
char[] charArray = aString.ToCharArray();
for (int i = 0; i < charArray.Length; i++) {
char c = charArray[i];
// Non ascii characters are fine, buffer them up and send them to the builder
// in larger chunks if possible. The escape table is a 1:1 translation table
// with \0 [default(char)] denoting a safe character.
if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) {
safeCharacterCount++;
} else {
if (safeCharacterCount > 0) {
builder.Append(charArray, i - safeCharacterCount, safeCharacterCount);
safeCharacterCount = 0;
}
builder.Append('\\');
builder.Append(EscapeTable[c]);
}
}
if (safeCharacterCount > 0) {
builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount);
}
builder.Append('"');
return true;
}
static bool SerializeNumber(double number, StringBuilder builder) {
builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture));
return true;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 8cb978759cbdf48468bc42a00fe0e849
timeCreated: 1447780610
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,156 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
static class KeyMappings {
private static Dictionary<KeyCode, int> mappings = new Dictionary<KeyCode, int>() {
//I'm not gonna lie. I just opened http://www.w3.org/2002/09/tests/keys.html
//and copied down the values my keyboard produced. >_<
{KeyCode.Escape, 27},
{KeyCode.F1, 112},
{KeyCode.F2, 113},
{KeyCode.F3, 114},
{KeyCode.F4, 115},
{KeyCode.F5, 116},
{KeyCode.F6, 117},
{KeyCode.F7, 118},
{KeyCode.F8, 119},
{KeyCode.F9, 120},
{KeyCode.F10, 121},
{KeyCode.F11, 122},
{KeyCode.F12, 123},
{KeyCode.SysReq, 44}, {KeyCode.Print, 44},
{KeyCode.ScrollLock, 145},
{KeyCode.Pause, 19},
{KeyCode.BackQuote, 192},
{KeyCode.Alpha0, 48},
{KeyCode.Alpha1, 49},
{KeyCode.Alpha2, 50},
{KeyCode.Alpha3, 51},
{KeyCode.Alpha4, 52},
{KeyCode.Alpha5, 53},
{KeyCode.Alpha6, 54},
{KeyCode.Alpha7, 55},
{KeyCode.Alpha8, 56},
{KeyCode.Alpha9, 57},
{KeyCode.Minus, 189},
{KeyCode.Equals, 187},
{KeyCode.Backspace, 8},
{KeyCode.Tab, 9},
//char keys
{KeyCode.LeftBracket, 219},
{KeyCode.RightBracket, 221},
{KeyCode.Backslash, 220},
{KeyCode.CapsLock, 20},
//char keys
{KeyCode.Semicolon, 186},
{KeyCode.Quote, 222},
{KeyCode.Return, 13},
{KeyCode.LeftShift, 16},
//char keys
{KeyCode.Comma, 188},
{KeyCode.Period, 190},
{KeyCode.Slash, 191},
{KeyCode.RightShift, 16},
{KeyCode.LeftControl, 17},
{KeyCode.LeftCommand, 91}, {KeyCode.LeftWindows, 91},
{KeyCode.LeftAlt, 18},
{KeyCode.Space, 32},
{KeyCode.RightAlt, 18},
{KeyCode.RightCommand, 92}, {KeyCode.RightWindows, 92},
{KeyCode.Menu, 93},
{KeyCode.RightControl, 17},
{KeyCode.Insert, 45},
{KeyCode.Home, 36},
{KeyCode.PageUp, 33},
{KeyCode.Delete, 46},
{KeyCode.End, 35},
{KeyCode.PageDown, 34},
{KeyCode.UpArrow, 38},
{KeyCode.LeftArrow, 37},
{KeyCode.DownArrow, 40},
{KeyCode.RightArrow, 39},
{KeyCode.Numlock, 144},
{KeyCode.KeypadDivide, 111},
{KeyCode.KeypadMultiply, 106},
{KeyCode.KeypadMinus, 109},
{KeyCode.Keypad7, 103},
{KeyCode.Keypad8, 104},
{KeyCode.Keypad9, 105},
{KeyCode.KeypadPlus, 107},
{KeyCode.Keypad4, 100},
{KeyCode.Keypad5, 101},
{KeyCode.Keypad6, 102},
{KeyCode.Keypad1, 97},
{KeyCode.Keypad2, 98},
{KeyCode.Keypad3, 99},
{KeyCode.KeypadEnter, 13},
{KeyCode.Keypad0, 96},
{KeyCode.KeypadPeriod, 110},
};
private static Dictionary<int, KeyCode> reverseMappings = new Dictionary<int, KeyCode>();
static KeyMappings() {
foreach (var kvp in mappings) {
reverseMappings[kvp.Value] = kvp.Key;
}
for (int i = (int)KeyCode.A; i <= (int)KeyCode.Z; i++) {
var key = (KeyCode)i;
var keyCode = i - (int)KeyCode.A + 65;
mappings[key] = keyCode;
reverseMappings[keyCode] = key;
}
}
public static int GetWindowsKeyCode(Event ev) {
int ukc = (int)ev.keyCode;//unity key code
//When dealing with characters return the Unicode char as the keycode.
if (ukc == 0) {
//enter is special.
if (ev.character == 10) return 13;
return ev.character;
}
// if (ukc >= (int)KeyCode.A && ukc <= (int)KeyCode.Z) {
// return ukc - (int)KeyCode.A + 65;
// }
int ret;
if (mappings.TryGetValue(ev.keyCode, out ret)) {
return ret;
}
//Don't recognize it, we'll just have to return something, but it's almost sure to be wrong.
Debug.LogWarning("Unknown key mapping: " + ev);
return ukc;
}
public static KeyCode GetUnityKeyCode(int windowsKeyCode) {
KeyCode ret;
if (reverseMappings.TryGetValue(windowsKeyCode, out ret)) return ret;
return 0;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: de63d991f0c0f0b41a32bebdc64b329a
timeCreated: 1447440809
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,203 +0,0 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/// <summary>
/// Manages a browser that's been opened in an OS-native window outside the game's main window.
/// (Presently only used for OS X.)
/// </summary>
public class PopUpBrowser : MonoBehaviour, IBrowserUI {
private int windowId;
private int browserId;
private List<string> messages = new List<string>();
private Browser browser;
private BrowserNative.WindowCallbackFunc callbackRef;//don't let this get GC'd
private Vector2 delayedResize;
public static void Create(int possibleBrowserId) {
var go = new GameObject("Pop up browser");
var browser = go.AddComponent<Browser>();
browser.RequestNativeBrowser(possibleBrowserId);
var pop = go.AddComponent<PopUpBrowser>();
pop.callbackRef = pop.HandleWindowMessage;
pop.windowId = BrowserNative.zfb_windowCreate("", pop.callbackRef);
pop.browserId = possibleBrowserId;
pop.BrowserCursor = new BrowserCursor();
pop.KeyEvents = new List<Event>();
pop.browser = browser;
pop.StartCoroutine(pop.FixFocus());
pop.InputSettings = new BrowserInputSettings();
browser.UIHandler = pop;
browser.EnableRendering = false;//rendering is done differently, so don't run the usual code
}
private void HandleWindowMessage(int windowId, IntPtr data) {
var msgJSON = Util.PtrToStringUTF8(data);
//if (!msgJSON.Contains("mouseMove")) Debug.Log("Window message: " + msgJSON);
lock (messages) messages.Add(msgJSON);
}
public void OnDestroy() {
if (!BrowserNative.SymbolsLoaded) return;
BrowserNative.zfb_windowClose(windowId);
}
private IEnumerator FixFocus() {
//OS X: Magically, new browser windows that are focused don't think they are focused, even though we told them so.
//Hack workaround.
yield return null;
BrowserNative.zfb_setFocused(browserId, false);
yield return null;
BrowserNative.zfb_setFocused(browserId, true);
yield return null;
BrowserNative.zfb_setFocused(browserId, KeyboardHasFocus);
}
public void InputUpdate() {
MouseScroll = Vector2.zero;
KeyEvents.Clear();
delayedResize = new Vector2(float.NaN, float.NaN);
lock (messages) {
for (int i = 0; i < messages.Count; i++) {
HandleMessage(messages[i]);
}
messages.Clear();
}
if (!float.IsNaN(delayedResize.x)) {
browser.Resize((int)delayedResize.x, (int)delayedResize.y);
}
}
private void HandleMessage(string message) {
var msg = JSONNode.Parse(message);
switch ((string)msg["type"]) {
case "mouseDown":
case "mouseUp": {
int button = msg["button"];
MouseButton flag = 0;
if (button == 0) flag = MouseButton.Left;
else if (button == 1) flag = MouseButton.Right;
else if (button == 2) flag = MouseButton.Middle;
if (msg["type"] == "mouseDown") MouseButtons |= flag;
else MouseButtons &= ~flag;
break;
}
case "mouseMove": {
var screenPos = new Vector2(msg["x"], msg["y"]);
screenPos.x = screenPos.x / browser.Size.x;
screenPos.y = screenPos.y / browser.Size.y;
MousePosition = screenPos;
//Debug.Log("mouse now at " + screenPos);
break;
}
case "mouseFocus":
MouseHasFocus = msg["focus"];
break;
case "keyboardFocus":
KeyboardHasFocus = msg["focus"];
break;
case "mouseScroll": {
const float mult = 1 / 3f;
MouseScroll += new Vector2(msg["x"], msg["y"]) * mult;
break;
}
case "keyDown":
case "keyUp": {
var ev = new Event();
ev.type = msg["type"] == "keyDown" ? EventType.KeyDown : EventType.KeyUp;
ev.character = (char)0;
ev.keyCode = KeyMappings.GetUnityKeyCode(msg["code"]);
SetMods(ev);
//Debug.Log("Convert wkc " + (int)msg["code"] + " to ukc " + ev.keyCode);
KeyEvents.Add(ev);
break;
}
case "keyPress": {
string characters = msg["characters"];
foreach (char c in characters) {
var ev = new Event();
ev.type = EventType.KeyDown;
SetMods(ev);
ev.character = c;
ev.keyCode = 0;
KeyEvents.Add(ev);
}
break;
}
case "resize":
//on OS X (editor at least), resizing hangs the update loop, so we suddenly end up with a bajillion resize
//messages we were unable to process. Just record it here and when we've processed everything we'll resize
delayedResize.x = msg["w"];
delayedResize.y = msg["h"];
break;
case "close":
Destroy(gameObject);
break;
default:
Debug.LogWarning("Unknown window event: " + msg.AsJSON);
break;
}
}
private bool shiftDown, controlDown, altDown, commandDown;
private void SetMods(Event ev) {
switch (ev.keyCode) {
case KeyCode.LeftShift: case KeyCode.RightShift:
shiftDown = ev.type == EventType.KeyDown;
break;
case KeyCode.LeftControl: case KeyCode.RightControl:
controlDown = ev.type == EventType.KeyDown;
break;
case KeyCode.LeftAlt: case KeyCode.RightAlt:
altDown = ev.type == EventType.KeyDown;
break;
case KeyCode.LeftCommand: case KeyCode.RightCommand:
case KeyCode.LeftWindows: case KeyCode.RightWindows:
commandDown = ev.type == EventType.KeyDown;
break;
}
ev.shift = shiftDown;
ev.control = controlDown;
ev.alt = altDown;
ev.command = commandDown;
}
public void Update() {
if (!BrowserNative.SymbolsLoaded) return;
BrowserNative.zfb_windowRender(windowId, browserId);
}
public bool MouseHasFocus { get; private set; }
public Vector2 MousePosition { get; private set; }
public MouseButton MouseButtons { get; private set; }
public Vector2 MouseScroll { get; private set; }
public bool KeyboardHasFocus { get; private set; }
public List<Event> KeyEvents { get; private set; }
public BrowserCursor BrowserCursor { get; private set; }
public BrowserInputSettings InputSettings { get; private set; }
}
}
#endif

View File

@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: 6172fca72ca9adf4da2ac4b4e3136708
timeCreated: 1517329102
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 48b908734323b95409dfd20950349e02
folderAsset: yes
timeCreated: 1479581013
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
namespace ZenFulcrum.EmbeddedBrowser.Promises
{
/// <summary>
/// General extensions to LINQ.
/// </summary>
public static class EnumerableExt
{
public static IEnumerable<T> Empty<T>()
{
return new T[0];
}
public static IEnumerable<T> LazyEach<T>(this IEnumerable<T> source, Action<T> fn)
{
foreach (var item in source)
{
fn.Invoke(item);
yield return item;
}
}
public static void Each<T>(this IEnumerable<T> source, Action<T> fn)
{
foreach (var item in source)
{
fn.Invoke(item);
}
}
public static void Each<T>(this IEnumerable<T> source, Action<T, int> fn)
{
int index = 0;
foreach (T item in source)
{
fn.Invoke(item, index);
index++;
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b77f44a328c5e2c40ba15d0fc85741fc
timeCreated: 1479581013
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,842 +0,0 @@
using ZenFulcrum.EmbeddedBrowser.Promises;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ZenFulcrum.EmbeddedBrowser
{
/// <summary>
/// Implements a C# promise.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
///
/// This can also be waited on in a Unity coroutine and queried for its value.
/// </summary>
public interface IPromise<PromisedT>
{
/// <summary>
/// Set the name of the promise, useful for debugging.
/// </summary>
IPromise<PromisedT> WithName(string name);
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// onRejected is called on error.
/// </summary>
void Done(Action<PromisedT> onResolved, Action<Exception> onRejected);
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// Adds a default error handler.
/// </summary>
void Done(Action<PromisedT> onResolved);
/// <summary>
/// Complete the promise. Adds a default error handler.
/// </summary>
void Done();
/// <summary>
/// Handle errors for the promise.
/// </summary>
IPromise<PromisedT> Catch(Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
/// </summary>
IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, IPromise<ConvertedT>> onResolved);
/// <summary>
/// Add a resolved callback that chains a non-value promise.
/// </summary>
IPromise Then(Func<PromisedT, IPromise> onResolved);
/// <summary>
/// Add a resolved callback.
/// </summary>
IPromise<PromisedT> Then(Action<PromisedT> onResolved);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
/// </summary>
IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, IPromise<ConvertedT>> onResolved, Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a non-value promise.
/// </summary>
IPromise Then(Func<PromisedT, IPromise> onResolved, Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// </summary>
IPromise<PromisedT> Then(Action<PromisedT> onResolved, Action<Exception> onRejected);
/// <summary>
/// Return a new promise with a different value.
/// May also change the type of the value.
/// </summary>
IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, ConvertedT> transform);
/// <summary>
/// Return a new promise with a different value.
/// May also change the type of the value.
/// </summary>
[Obsolete("Use Then instead")]
IPromise<ConvertedT> Transform<ConvertedT>(Func<PromisedT, ConvertedT> transform);
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Returns a promise for a collection of the resolved results.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
IPromise<IEnumerable<ConvertedT>> ThenAll<ConvertedT>(Func<PromisedT, IEnumerable<IPromise<ConvertedT>>> chain);
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Converts to a non-value promise.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
IPromise ThenAll(Func<PromisedT, IEnumerable<IPromise>> chain);
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Returns a promise that resolves when the first of the promises has resolved.
/// Yields the value from the first promise that has resolved.
/// </summary>
IPromise<ConvertedT> ThenRace<ConvertedT>(Func<PromisedT, IEnumerable<IPromise<ConvertedT>>> chain);
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Converts to a non-value promise.
/// Returns a promise that resolves when the first of the promises has resolved.
/// Yields the value from the first promise that has resolved.
/// </summary>
IPromise ThenRace(Func<PromisedT, IEnumerable<IPromise>> chain);
/// <summary>
/// Returns the resulting value if resolved.
/// Throws the rejection if rejected.
/// Throws an exception if not settled.
/// </summary>
PromisedT Value { get; }
/// <summary>
/// Returns an enumerable that yields null until the promise is settled.
/// ("To WaitFor" like the WaitForXXYY functions Unity provides.)
/// Suitable for use with a Unity coroutine's "yield return promise.ToWaitFor()"
/// Once it finishes, use promise.Value to retrieve the value/error.
///
/// If throwOnFail is true, the coroutine will abort on promise rejection.
/// </summary>
/// <returns></returns>
IEnumerator ToWaitFor(bool abortOnFail = false);
}
/// <summary>
/// Interface for a promise that can be rejected.
/// </summary>
public interface IRejectable
{
/// <summary>
/// Reject the promise with an exception.
/// </summary>
void Reject(Exception ex);
}
/// <summary>
/// Interface for a promise that can be rejected or resolved.
/// </summary>
public interface IPendingPromise<PromisedT> : IRejectable
{
/// <summary>
/// Resolve the promise with a particular value.
/// </summary>
void Resolve(PromisedT value);
}
/// <summary>
/// Specifies the state of a promise.
/// </summary>
public enum PromiseState
{
Pending, // The promise is in-flight.
Rejected, // The promise has been rejected.
Resolved // The promise has been resolved.
};
/// <summary>
/// Implements a C# promise.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
/// </summary>
public class Promise<PromisedT> : IPromise<PromisedT>, IPendingPromise<PromisedT>, IPromiseInfo
{
/// <summary>
/// The exception when the promise is rejected.
/// </summary>
private Exception rejectionException;
/// <summary>
/// The value when the promises is resolved.
/// </summary>
private PromisedT resolveValue;
/// <summary>
/// Error handler.
/// </summary>
private List<RejectHandler> rejectHandlers;
/// <summary>
/// Completed handlers that accept a value.
/// </summary>
private List<Action<PromisedT>> resolveCallbacks;
private List<IRejectable> resolveRejectables;
/// <summary>
/// ID of the promise, useful for debugging.
/// </summary>
public int Id { get; private set; }
/// <summary>
/// Name of the promise, when set, useful for debugging.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Tracks the current state of the promise.
/// </summary>
public PromiseState CurState { get; private set; }
public Promise()
{
this.CurState = PromiseState.Pending;
this.Id = ++Promise.nextPromiseId;
if (Promise.EnablePromiseTracking)
{
Promise.pendingPromises.Add(this);
}
}
public Promise(Action<Action<PromisedT>, Action<Exception>> resolver)
{
this.CurState = PromiseState.Pending;
this.Id = ++Promise.nextPromiseId;
if (Promise.EnablePromiseTracking)
{
Promise.pendingPromises.Add(this);
}
try
{
resolver(
// Resolve
value => Resolve(value),
// Reject
ex => Reject(ex)
);
}
catch (Exception ex)
{
Reject(ex);
}
}
/// <summary>
/// Add a rejection handler for this promise.
/// </summary>
private void AddRejectHandler(Action<Exception> onRejected, IRejectable rejectable)
{
if (rejectHandlers == null)
{
rejectHandlers = new List<RejectHandler>();
}
rejectHandlers.Add(new RejectHandler() { callback = onRejected, rejectable = rejectable }); ;
}
/// <summary>
/// Add a resolve handler for this promise.
/// </summary>
private void AddResolveHandler(Action<PromisedT> onResolved, IRejectable rejectable)
{
if (resolveCallbacks == null)
{
resolveCallbacks = new List<Action<PromisedT>>();
}
if (resolveRejectables == null)
{
resolveRejectables = new List<IRejectable>();
}
resolveCallbacks.Add(onResolved);
resolveRejectables.Add(rejectable);
}
/// <summary>
/// Invoke a single handler.
/// </summary>
private void InvokeHandler<T>(Action<T> callback, IRejectable rejectable, T value)
{
// Argument.NotNull(() => callback);
// Argument.NotNull(() => rejectable);
try
{
callback(value);
}
catch (Exception ex)
{
rejectable.Reject(ex);
}
}
/// <summary>
/// Helper function clear out all handlers after resolution or rejection.
/// </summary>
private void ClearHandlers()
{
rejectHandlers = null;
resolveCallbacks = null;
resolveRejectables = null;
}
/// <summary>
/// Invoke all reject handlers.
/// </summary>
private void InvokeRejectHandlers(Exception ex)
{
// Argument.NotNull(() => ex);
if (rejectHandlers != null)
{
rejectHandlers.Each(handler => InvokeHandler(handler.callback, handler.rejectable, ex));
}
ClearHandlers();
}
/// <summary>
/// Invoke all resolve handlers.
/// </summary>
private void InvokeResolveHandlers(PromisedT value)
{
if (resolveCallbacks != null)
{
for (int i = 0, maxI = resolveCallbacks.Count; i < maxI; i++) {
InvokeHandler(resolveCallbacks[i], resolveRejectables[i], value);
}
}
ClearHandlers();
}
/// <summary>
/// Reject the promise with an exception.
/// </summary>
public void Reject(Exception ex)
{
// Argument.NotNull(() => ex);
if (CurState != PromiseState.Pending)
{
throw new ApplicationException("Attempt to reject a promise that is already in state: " + CurState + ", a promise can only be rejected when it is still in state: " + PromiseState.Pending);
}
rejectionException = ex;
CurState = PromiseState.Rejected;
if (Promise.EnablePromiseTracking)
{
Promise.pendingPromises.Remove(this);
}
InvokeRejectHandlers(ex);
}
/// <summary>
/// Resolve the promise with a particular value.
/// </summary>
public void Resolve(PromisedT value)
{
if (CurState != PromiseState.Pending)
{
throw new ApplicationException("Attempt to resolve a promise that is already in state: " + CurState + ", a promise can only be resolved when it is still in state: " + PromiseState.Pending);
}
resolveValue = value;
CurState = PromiseState.Resolved;
if (Promise.EnablePromiseTracking)
{
Promise.pendingPromises.Remove(this);
}
InvokeResolveHandlers(value);
}
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// onRejected is called on error.
/// </summary>
public void Done(Action<PromisedT> onResolved, Action<Exception> onRejected)
{
Then(onResolved, onRejected)
.Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// Adds a default error handler.
/// </summary>
public void Done(Action<PromisedT> onResolved)
{
Then(onResolved)
.Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Complete the promise. Adds a default error handler.
/// </summary>
public void Done()
{
Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Set the name of the promise, useful for debugging.
/// </summary>
public IPromise<PromisedT> WithName(string name)
{
this.Name = name;
return this;
}
/// <summary>
/// Handle errors for the promise.
/// </summary>
public IPromise<PromisedT> Catch(Action<Exception> onRejected)
{
// Argument.NotNull(() => onRejected);
var resultPromise = new Promise<PromisedT>();
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>
{
resultPromise.Resolve(v);
};
Action<Exception> rejectHandler = ex =>
{
onRejected(ex);
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
/// </summary>
public IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, IPromise<ConvertedT>> onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback that chains a non-value promise.
/// </summary>
public IPromise Then(Func<PromisedT, IPromise> onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback.
/// </summary>
public IPromise<PromisedT> Then(Action<PromisedT> onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
/// </summary>
public IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, IPromise<ConvertedT>> onResolved, Action<Exception> onRejected)
{
// This version of the function must supply an onResolved.
// Otherwise there is now way to get the converted value to pass to the resulting promise.
// Argument.NotNull(() => onResolved);
var resultPromise = new Promise<ConvertedT>();
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>
{
onResolved(v)
.Then(
// Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise.
(ConvertedT chainedValue) => resultPromise.Resolve(chainedValue),
ex => resultPromise.Reject(ex)
);
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a non-value promise.
/// </summary>
public IPromise Then(Func<PromisedT, IPromise> onResolved, Action<Exception> onRejected)
{
var resultPromise = new Promise();
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>
{
if (onResolved != null)
{
onResolved(v)
.Then(
() => resultPromise.Resolve(),
ex => resultPromise.Reject(ex)
);
}
else
{
resultPromise.Resolve();
}
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// </summary>
public IPromise<PromisedT> Then(Action<PromisedT> onResolved, Action<Exception> onRejected)
{
var resultPromise = new Promise<PromisedT>();
resultPromise.WithName(Name);
Action<PromisedT> resolveHandler = v =>
{
if (onResolved != null)
{
onResolved(v);
}
resultPromise.Resolve(v);
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Return a new promise with a different value.
/// May also change the type of the value.
/// </summary>
public IPromise<ConvertedT> Then<ConvertedT>(Func<PromisedT, ConvertedT> transform)
{
// Argument.NotNull(() => transform);
return Then(value => Promise<ConvertedT>.Resolved(transform(value)));
}
/// <summary>
/// Return a new promise with a different value.
/// May also change the type of the value.
/// </summary>
[Obsolete("Use Then instead")]
public IPromise<ConvertedT> Transform<ConvertedT>(Func<PromisedT, ConvertedT> transform)
{
// Argument.NotNull(() => transform);
return Then(value => Promise<ConvertedT>.Resolved(transform(value)));
}
/// <summary>
/// Helper function to invoke or register resolve/reject handlers.
/// </summary>
private void ActionHandlers(IRejectable resultPromise, Action<PromisedT> resolveHandler, Action<Exception> rejectHandler)
{
if (CurState == PromiseState.Resolved)
{
InvokeHandler(resolveHandler, resultPromise, resolveValue);
}
else if (CurState == PromiseState.Rejected)
{
InvokeHandler(rejectHandler, resultPromise, rejectionException);
}
else
{
AddResolveHandler(resolveHandler, resultPromise);
AddRejectHandler(rejectHandler, resultPromise);
}
}
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Returns a promise for a collection of the resolved results.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
public IPromise<IEnumerable<ConvertedT>> ThenAll<ConvertedT>(Func<PromisedT, IEnumerable<IPromise<ConvertedT>>> chain)
{
return Then(value => Promise<ConvertedT>.All(chain(value)));
}
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Converts to a non-value promise.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
public IPromise ThenAll(Func<PromisedT, IEnumerable<IPromise>> chain)
{
return Then(value => Promise.All(chain(value)));
}
/// <summary>
/// Returns a promise that resolves when all of the promises in the enumerable argument have resolved.
/// Returns a promise of a collection of the resolved results.
/// </summary>
public static IPromise<IEnumerable<PromisedT>> All(params IPromise<PromisedT>[] promises)
{
return All((IEnumerable<IPromise<PromisedT>>)promises); // Cast is required to force use of the other All function.
}
/// <summary>
/// Returns a promise that resolves when all of the promises in the enumerable argument have resolved.
/// Returns a promise of a collection of the resolved results.
/// </summary>
public static IPromise<IEnumerable<PromisedT>> All(IEnumerable<IPromise<PromisedT>> promises)
{
var promisesArray = promises.ToArray();
if (promisesArray.Length == 0)
{
return Promise<IEnumerable<PromisedT>>.Resolved(EnumerableExt.Empty<PromisedT>());
}
var remainingCount = promisesArray.Length;
var results = new PromisedT[remainingCount];
var resultPromise = new Promise<IEnumerable<PromisedT>>();
resultPromise.WithName("All");
promisesArray.Each((promise, index) =>
{
promise
.Catch(ex =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
// If a promise errorred and the result promise is still pending, reject it.
resultPromise.Reject(ex);
}
})
.Then(result =>
{
results[index] = result;
--remainingCount;
if (remainingCount <= 0)
{
// This will never happen if any of the promises errorred.
resultPromise.Resolve(results);
}
})
.Done();
});
return resultPromise;
}
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Returns a promise that resolves when the first of the promises has resolved.
/// Yields the value from the first promise that has resolved.
/// </summary>
public IPromise<ConvertedT> ThenRace<ConvertedT>(Func<PromisedT, IEnumerable<IPromise<ConvertedT>>> chain)
{
return Then(value => Promise<ConvertedT>.Race(chain(value)));
}
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Converts to a non-value promise.
/// Returns a promise that resolves when the first of the promises has resolved.
/// Yields the value from the first promise that has resolved.
/// </summary>
public IPromise ThenRace(Func<PromisedT, IEnumerable<IPromise>> chain)
{
return Then(value => Promise.Race(chain(value)));
}
public PromisedT Value
{
get
{
if (CurState == PromiseState.Pending) throw new InvalidOperationException("Promise not settled");
else if (CurState == PromiseState.Rejected) throw rejectionException;
return resolveValue;
}
}
class Enumerated<T> : IEnumerator {
private Promise<T> promise;
private bool abortOnFail;
public Enumerated(Promise<T> promise, bool abortOnFail) {
this.promise = promise;
this.abortOnFail = abortOnFail;
}
public bool MoveNext() {
if (abortOnFail && promise.CurState == PromiseState.Rejected) {
throw promise.rejectionException;
}
return promise.CurState == PromiseState.Pending;
}
public void Reset() { }
public object Current { get { return null; } }
}
public IEnumerator ToWaitFor(bool abortOnFail) {
var ret = new Enumerated<PromisedT>(this, abortOnFail);
//someone will poll for completion, so act like we've been terminated
Done(x => {}, ex => {});
return ret;
}
/// <summary>
/// Returns a promise that resolves when the first of the promises in the enumerable argument have resolved.
/// Returns the value from the first promise that has resolved.
/// </summary>
public static IPromise<PromisedT> Race(params IPromise<PromisedT>[] promises)
{
return Race((IEnumerable<IPromise<PromisedT>>)promises); // Cast is required to force use of the other function.
}
/// <summary>
/// Returns a promise that resolves when the first of the promises in the enumerable argument have resolved.
/// Returns the value from the first promise that has resolved.
/// </summary>
public static IPromise<PromisedT> Race(IEnumerable<IPromise<PromisedT>> promises)
{
var promisesArray = promises.ToArray();
if (promisesArray.Length == 0)
{
throw new ApplicationException("At least 1 input promise must be provided for Race");
}
var resultPromise = new Promise<PromisedT>();
resultPromise.WithName("Race");
promisesArray.Each((promise, index) =>
{
promise
.Catch(ex =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
// If a promise errorred and the result promise is still pending, reject it.
resultPromise.Reject(ex);
}
})
.Then(result =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
resultPromise.Resolve(result);
}
})
.Done();
});
return resultPromise;
}
/// <summary>
/// Convert a simple value directly into a resolved promise.
/// </summary>
public static IPromise<PromisedT> Resolved(PromisedT promisedValue)
{
var promise = new Promise<PromisedT>();
promise.Resolve(promisedValue);
return promise;
}
/// <summary>
/// Convert an exception directly into a rejected promise.
/// </summary>
public static IPromise<PromisedT> Rejected(Exception ex)
{
// Argument.NotNull(() => ex);
var promise = new Promise<PromisedT>();
promise.Reject(ex);
return promise;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 56222919992acdd489a2deff63c33981
timeCreated: 1479581013
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,162 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ZenFulcrum.EmbeddedBrowser
{
/// <summary>
/// A class that wraps a pending promise with it's predicate and time data
/// </summary>
internal class PredicateWait
{
/// <summary>
/// Predicate for resolving the promise
/// </summary>
public Func<TimeData, bool> predicate;
/// <summary>
/// The time the promise was started
/// </summary>
public float timeStarted;
/// <summary>
/// The pending promise which is an interface for a promise that can be rejected or resolved.
/// </summary>
public IPendingPromise pendingPromise;
/// <summary>
/// The time data specific to this pending promise. Includes elapsed time and delta time.
/// </summary>
public TimeData timeData;
}
/// <summary>
/// Time data specific to a particular pending promise.
/// </summary>
public struct TimeData
{
/// <summary>
/// The amount of time that has elapsed since the pending promise started running
/// </summary>
public float elapsedTime;
/// <summary>
/// The amount of time since the last time the pending promise was updated.
/// </summary>
public float deltaTime;
}
public interface IPromiseTimer
{
/// <summary>
/// Resolve the returned promise once the time has elapsed
/// </summary>
IPromise WaitFor(float seconds);
/// <summary>
/// Resolve the returned promise once the predicate evaluates to true
/// </summary>
IPromise WaitUntil(Func<TimeData, bool> predicate);
/// <summary>
/// Resolve the returned promise once the predicate evaluates to false
/// </summary>
IPromise WaitWhile(Func<TimeData, bool> predicate);
/// <summary>
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
/// </summary>
void Update(float deltaTime);
}
public class PromiseTimer : IPromiseTimer
{
/// <summary>
/// The current running total for time that this PromiseTimer has run for
/// </summary>
private float curTime;
/// <summary>
/// Currently pending promises
/// </summary>
private List<PredicateWait> waiting = new List<PredicateWait>();
/// <summary>
/// Resolve the returned promise once the time has elapsed
/// </summary>
public IPromise WaitFor(float seconds)
{
return WaitUntil(t => t.elapsedTime >= seconds);
}
/// <summary>
/// Resolve the returned promise once the predicate evaluates to false
/// </summary>
public IPromise WaitWhile(Func<TimeData, bool> predicate)
{
return WaitUntil(t => !predicate(t));
}
/// <summary>
/// Resolve the returned promise once the predicate evalutes to true
/// </summary>
public IPromise WaitUntil(Func<TimeData, bool> predicate)
{
var promise = new Promise();
var wait = new PredicateWait()
{
timeStarted = curTime,
pendingPromise = promise,
timeData = new TimeData(),
predicate = predicate
};
waiting.Add(wait);
return promise;
}
/// <summary>
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
/// </summary>
public void Update(float deltaTime)
{
curTime += deltaTime;
int i = 0;
while (i < waiting.Count)
{
var wait = waiting[i];
var newElapsedTime = curTime - wait.timeStarted;
wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime;
wait.timeData.elapsedTime = newElapsedTime;
bool result;
try
{
result = wait.predicate(wait.timeData);
}
catch (Exception ex)
{
wait.pendingPromise.Reject(ex);
waiting.RemoveAt(i);
continue;
}
if (result)
{
wait.pendingPromise.Resolve();
waiting.RemoveAt(i);
}
else
{
i++;
}
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: bab6d12600d36db4b81c0aa04f0fd685
timeCreated: 1479581013
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,930 +0,0 @@
using ZenFulcrum.EmbeddedBrowser.Promises;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ZenFulcrum.EmbeddedBrowser
{
/// <summary>
/// Implements a non-generic C# promise, this is a promise that simply resolves without delivering a value.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
///
/// This can also be waited on in a Unity coroutine.
/// </summary>
public interface IPromise
{
/// <summary>
/// Set the name of the promise, useful for debugging.
/// </summary>
IPromise WithName(string name);
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// onRejected is called on error.
/// </summary>
void Done(Action onResolved, Action<Exception> onRejected);
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// Adds a default error handler.
/// </summary>
void Done(Action onResolved);
/// <summary>
/// Complete the promise. Adds a default error handler.
/// </summary>
void Done();
/// <summary>
/// Handle errors for the promise.
/// </summary>
IPromise Catch(Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
/// </summary>
IPromise<ConvertedT> Then<ConvertedT>(Func<IPromise<ConvertedT>> onResolved);
/// <summary>
/// Add a resolved callback that chains a non-value promise.
/// </summary>
IPromise Then(Func<IPromise> onResolved);
/// <summary>
/// Add a resolved callback.
/// </summary>
IPromise Then(Action onResolved);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
/// </summary>
IPromise<ConvertedT> Then<ConvertedT>(Func<IPromise<ConvertedT>> onResolved, Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a non-value promise.
/// </summary>
IPromise Then(Func<IPromise> onResolved, Action<Exception> onRejected);
/// <summary>
/// Add a resolved callback and a rejected callback.
/// </summary>
IPromise Then(Action onResolved, Action<Exception> onRejected);
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
IPromise ThenAll(Func<IEnumerable<IPromise>> chain);
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Converts to a non-value promise.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
IPromise<IEnumerable<ConvertedT>> ThenAll<ConvertedT>(Func<IEnumerable<IPromise<ConvertedT>>> chain);
/// <summary>
/// Chain a sequence of operations using promises.
/// Reutrn a collection of functions each of which starts an async operation and yields a promise.
/// Each function will be called and each promise resolved in turn.
/// The resulting promise is resolved after each promise is resolved in sequence.
/// </summary>
IPromise ThenSequence(Func<IEnumerable<Func<IPromise>>> chain);
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Returns a promise that resolves when the first of the promises has resolved.
/// </summary>
IPromise ThenRace(Func<IEnumerable<IPromise>> chain);
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Converts to a value promise.
/// Returns a promise that resolves when the first of the promises has resolved.
/// </summary>
IPromise<ConvertedT> ThenRace<ConvertedT>(Func<IEnumerable<IPromise<ConvertedT>>> chain);
/// <summary>
/// Returns an enumerable that yields null until the promise is settled.
/// ("To WaitFor" like the WaitForXXYY functions Unity provides.)
/// Suitable for use with a Unity coroutine's "yield return promise.ToWaitFor()"
///
/// If throwOnFail is true, the coroutine will abort on promise rejection.
/// </summary>
/// <returns></returns>
IEnumerator ToWaitFor(bool abortOnFail = false);
}
/// <summary>
/// Interface for a promise that can be rejected or resolved.
/// </summary>
public interface IPendingPromise : IRejectable
{
/// <summary>
/// Resolve the promise with a particular value.
/// </summary>
void Resolve();
}
/// <summary>
/// Used to list information of pending promises.
/// </summary>
public interface IPromiseInfo
{
/// <summary>
/// Id of the promise.
/// </summary>
int Id { get; }
/// <summary>
/// Human-readable name for the promise.
/// </summary>
string Name { get; }
}
/// <summary>
/// Arguments to the UnhandledError event.
/// </summary>
public class ExceptionEventArgs : EventArgs
{
internal ExceptionEventArgs(Exception exception)
{
// Argument.NotNull(() => exception);
this.Exception = exception;
}
public Exception Exception
{
get;
private set;
}
}
/// <summary>
/// Represents a handler invoked when the promise is rejected.
/// </summary>
public struct RejectHandler
{
/// <summary>
/// Callback fn.
/// </summary>
public Action<Exception> callback;
/// <summary>
/// The promise that is rejected when there is an error while invoking the handler.
/// </summary>
public IRejectable rejectable;
}
/// <summary>
/// Implements a non-generic C# promise, this is a promise that simply resolves without delivering a value.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
/// </summary>
public class Promise : IPromise, IPendingPromise, IPromiseInfo
{
static Promise()
{
UnhandledException += (sender, args) => {
UnityEngine.Debug.LogWarning("Rejection: " + args.Exception.Message + "\n" + args.Exception.StackTrace);
};
}
/// <summary>
/// Set to true to enable tracking of promises.
/// </summary>
public static bool EnablePromiseTracking = false;
/// <summary>
/// Event raised for unhandled errors.
/// For this to work you have to complete your promises with a call to Done().
/// </summary>
public static event EventHandler<ExceptionEventArgs> UnhandledException
{
add { unhandlerException += value; }
remove { unhandlerException -= value; }
}
private static EventHandler<ExceptionEventArgs> unhandlerException;
/// <summary>
/// Id for the next promise that is created.
/// </summary>
internal static int nextPromiseId = 0;
/// <summary>
/// Information about pending promises.
/// </summary>
internal static HashSet<IPromiseInfo> pendingPromises = new HashSet<IPromiseInfo>();
/// <summary>
/// Information about pending promises, useful for debugging.
/// This is only populated when 'EnablePromiseTracking' is set to true.
/// </summary>
public static IEnumerable<IPromiseInfo> GetPendingPromises()
{
return pendingPromises;
}
/// <summary>
/// The exception when the promise is rejected.
/// </summary>
private Exception rejectionException;
/// <summary>
/// Error handlers.
/// </summary>
private List<RejectHandler> rejectHandlers;
/// <summary>
/// Represents a handler invoked when the promise is resolved.
/// </summary>
public struct ResolveHandler
{
/// <summary>
/// Callback fn.
/// </summary>
public Action callback;
/// <summary>
/// The promise that is rejected when there is an error while invoking the handler.
/// </summary>
public IRejectable rejectable;
}
/// <summary>
/// Completed handlers that accept no value.
/// </summary>
private List<ResolveHandler> resolveHandlers;
/// <summary>
/// ID of the promise, useful for debugging.
/// </summary>
public int Id { get; private set; }
/// <summary>
/// Name of the promise, when set, useful for debugging.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Tracks the current state of the promise.
/// </summary>
public PromiseState CurState { get; private set; }
public Promise()
{
this.CurState = PromiseState.Pending;
if (EnablePromiseTracking)
{
pendingPromises.Add(this);
}
}
public Promise(Action<Action, Action<Exception>> resolver)
{
this.CurState = PromiseState.Pending;
if (EnablePromiseTracking)
{
pendingPromises.Add(this);
}
try
{
resolver(
// Resolve
() => Resolve(),
// Reject
ex => Reject(ex)
);
}
catch (Exception ex)
{
Reject(ex);
}
}
/// <summary>
/// Add a rejection handler for this promise.
/// </summary>
private void AddRejectHandler(Action<Exception> onRejected, IRejectable rejectable)
{
if (rejectHandlers == null)
{
rejectHandlers = new List<RejectHandler>();
}
rejectHandlers.Add(new RejectHandler()
{
callback = onRejected,
rejectable = rejectable
});
}
/// <summary>
/// Add a resolve handler for this promise.
/// </summary>
private void AddResolveHandler(Action onResolved, IRejectable rejectable)
{
if (resolveHandlers == null)
{
resolveHandlers = new List<ResolveHandler>();
}
resolveHandlers.Add(new ResolveHandler()
{
callback = onResolved,
rejectable = rejectable
});
}
/// <summary>
/// Invoke a single error handler.
/// </summary>
private void InvokeRejectHandler(Action<Exception> callback, IRejectable rejectable, Exception value)
{
// Argument.NotNull(() => callback);
// Argument.NotNull(() => rejectable);
try
{
callback(value);
}
catch (Exception ex)
{
rejectable.Reject(ex);
}
}
/// <summary>
/// Invoke a single resolve handler.
/// </summary>
private void InvokeResolveHandler(Action callback, IRejectable rejectable)
{
// Argument.NotNull(() => callback);
// Argument.NotNull(() => rejectable);
try
{
callback();
}
catch (Exception ex)
{
rejectable.Reject(ex);
}
}
/// <summary>
/// Helper function clear out all handlers after resolution or rejection.
/// </summary>
private void ClearHandlers()
{
rejectHandlers = null;
resolveHandlers = null;
}
/// <summary>
/// Invoke all reject handlers.
/// </summary>
private void InvokeRejectHandlers(Exception ex)
{
// Argument.NotNull(() => ex);
if (rejectHandlers != null)
{
rejectHandlers.Each(handler => InvokeRejectHandler(handler.callback, handler.rejectable, ex));
}
ClearHandlers();
}
/// <summary>
/// Invoke all resolve handlers.
/// </summary>
private void InvokeResolveHandlers()
{
if (resolveHandlers != null)
{
resolveHandlers.Each(handler => InvokeResolveHandler(handler.callback, handler.rejectable));
}
ClearHandlers();
}
/// <summary>
/// Reject the promise with an exception.
/// </summary>
public void Reject(Exception ex)
{
// Argument.NotNull(() => ex);
if (CurState != PromiseState.Pending)
{
throw new ApplicationException("Attempt to reject a promise that is already in state: " + CurState + ", a promise can only be rejected when it is still in state: " + PromiseState.Pending);
}
rejectionException = ex;
CurState = PromiseState.Rejected;
if (EnablePromiseTracking)
{
pendingPromises.Remove(this);
}
InvokeRejectHandlers(ex);
}
/// <summary>
/// Resolve the promise with a particular value.
/// </summary>
public void Resolve()
{
if (CurState != PromiseState.Pending)
{
throw new ApplicationException("Attempt to resolve a promise that is already in state: " + CurState + ", a promise can only be resolved when it is still in state: " + PromiseState.Pending);
}
CurState = PromiseState.Resolved;
if (EnablePromiseTracking)
{
pendingPromises.Remove(this);
}
InvokeResolveHandlers();
}
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// onRejected is called on error.
/// </summary>
public void Done(Action onResolved, Action<Exception> onRejected)
{
Then(onResolved, onRejected)
.Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Completes the promise.
/// onResolved is called on successful completion.
/// Adds a default error handler.
/// </summary>
public void Done(Action onResolved)
{
Then(onResolved)
.Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Complete the promise. Adds a defualt error handler.
/// </summary>
public void Done()
{
Catch(ex =>
Promise.PropagateUnhandledException(this, ex)
);
}
/// <summary>
/// Set the name of the promise, useful for debugging.
/// </summary>
public IPromise WithName(string name)
{
this.Name = name;
return this;
}
/// <summary>
/// Handle errors for the promise.
/// </summary>
public IPromise Catch(Action<Exception> onRejected)
{
// Argument.NotNull(() => onRejected);
var resultPromise = new Promise();
resultPromise.WithName(Name);
Action resolveHandler = () =>
{
resultPromise.Resolve();
};
Action<Exception> rejectHandler = ex =>
{
onRejected(ex);
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
/// </summary>
public IPromise<ConvertedT> Then<ConvertedT>(Func<IPromise<ConvertedT>> onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback that chains a non-value promise.
/// </summary>
public IPromise Then(Func<IPromise> onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback.
/// </summary>
public IPromise Then(Action onResolved)
{
return Then(onResolved, null);
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
/// </summary>
public IPromise<ConvertedT> Then<ConvertedT>(Func<IPromise<ConvertedT>> onResolved, Action<Exception> onRejected)
{
// This version of the function must supply an onResolved.
// Otherwise there is now way to get the converted value to pass to the resulting promise.
// Argument.NotNull(() => onResolved);
var resultPromise = new Promise<ConvertedT>();
resultPromise.WithName(Name);
Action resolveHandler = () =>
{
onResolved()
.Then(
// Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise.
(ConvertedT chainedValue) => resultPromise.Resolve(chainedValue),
ex => resultPromise.Reject(ex)
);
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a non-value promise.
/// </summary>
public IPromise Then(Func<IPromise> onResolved, Action<Exception> onRejected)
{
var resultPromise = new Promise();
resultPromise.WithName(Name);
Action resolveHandler = () =>
{
if (onResolved != null)
{
onResolved()
.Then(
() => resultPromise.Resolve(),
ex => resultPromise.Reject(ex)
);
}
else
{
resultPromise.Resolve();
}
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Add a resolved callback and a rejected callback.
/// </summary>
public IPromise Then(Action onResolved, Action<Exception> onRejected)
{
var resultPromise = new Promise();
resultPromise.WithName(Name);
Action resolveHandler = () =>
{
if (onResolved != null)
{
onResolved();
}
resultPromise.Resolve();
};
Action<Exception> rejectHandler = ex =>
{
if (onRejected != null)
{
onRejected(ex);
}
resultPromise.Reject(ex);
};
ActionHandlers(resultPromise, resolveHandler, rejectHandler);
return resultPromise;
}
/// <summary>
/// Helper function to invoke or register resolve/reject handlers.
/// </summary>
private void ActionHandlers(IRejectable resultPromise, Action resolveHandler, Action<Exception> rejectHandler)
{
if (CurState == PromiseState.Resolved)
{
InvokeResolveHandler(resolveHandler, resultPromise);
}
else if (CurState == PromiseState.Rejected)
{
InvokeRejectHandler(rejectHandler, resultPromise, rejectionException);
}
else
{
AddResolveHandler(resolveHandler, resultPromise);
AddRejectHandler(rejectHandler, resultPromise);
}
}
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
public IPromise ThenAll(Func<IEnumerable<IPromise>> chain)
{
return Then(() => Promise.All(chain()));
}
/// <summary>
/// Chain an enumerable of promises, all of which must resolve.
/// Converts to a non-value promise.
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
/// </summary>
public IPromise<IEnumerable<ConvertedT>> ThenAll<ConvertedT>(Func<IEnumerable<IPromise<ConvertedT>>> chain)
{
return Then(() => Promise<ConvertedT>.All(chain()));
}
/// <summary>
/// Returns a promise that resolves when all of the promises in the enumerable argument have resolved.
/// Returns a promise of a collection of the resolved results.
/// </summary>
public static IPromise All(params IPromise[] promises)
{
return All((IEnumerable<IPromise>)promises); // Cast is required to force use of the other All function.
}
/// <summary>
/// Returns a promise that resolves when all of the promises in the enumerable argument have resolved.
/// Returns a promise of a collection of the resolved results.
/// </summary>
public static IPromise All(IEnumerable<IPromise> promises)
{
var promisesArray = promises.ToArray();
if (promisesArray.Length == 0)
{
return Promise.Resolved();
}
var remainingCount = promisesArray.Length;
var resultPromise = new Promise();
resultPromise.WithName("All");
promisesArray.Each((promise, index) =>
{
promise
.Catch(ex =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
// If a promise errorred and the result promise is still pending, reject it.
resultPromise.Reject(ex);
}
})
.Then(() =>
{
--remainingCount;
if (remainingCount <= 0)
{
// This will never happen if any of the promises errorred.
resultPromise.Resolve();
}
})
.Done();
});
return resultPromise;
}
/// <summary>
/// Chain a sequence of operations using promises.
/// Reutrn a collection of functions each of which starts an async operation and yields a promise.
/// Each function will be called and each promise resolved in turn.
/// The resulting promise is resolved after each promise is resolved in sequence.
/// </summary>
public IPromise ThenSequence(Func<IEnumerable<Func<IPromise>>> chain)
{
return Then(() => Sequence(chain()));
}
/// <summary>
/// Chain a number of operations using promises.
/// Takes a number of functions each of which starts an async operation and yields a promise.
/// </summary>
public static IPromise Sequence(params Func<IPromise>[] fns)
{
return Sequence((IEnumerable<Func<IPromise>>)fns);
}
/// <summary>
/// Chain a sequence of operations using promises.
/// Takes a collection of functions each of which starts an async operation and yields a promise.
/// </summary>
public static IPromise Sequence(IEnumerable<Func<IPromise>> fns)
{
return fns.Aggregate(
Promise.Resolved(),
(prevPromise, fn) =>
{
return prevPromise.Then(() => fn());
}
);
}
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Returns a promise that resolves when the first of the promises has resolved.
/// </summary>
public IPromise ThenRace(Func<IEnumerable<IPromise>> chain)
{
return Then(() => Promise.Race(chain()));
}
/// <summary>
/// Takes a function that yields an enumerable of promises.
/// Converts to a value promise.
/// Returns a promise that resolves when the first of the promises has resolved.
/// </summary>
public IPromise<ConvertedT> ThenRace<ConvertedT>(Func<IEnumerable<IPromise<ConvertedT>>> chain)
{
return Then(() => Promise<ConvertedT>.Race(chain()));
}
/// <summary>
/// Returns a promise that resolves when the first of the promises in the enumerable argument have resolved.
/// Returns the value from the first promise that has resolved.
/// </summary>
public static IPromise Race(params IPromise[] promises)
{
return Race((IEnumerable<IPromise>)promises); // Cast is required to force use of the other function.
}
/// <summary>
/// Returns a promise that resolves when the first of the promises in the enumerable argument have resolved.
/// Returns the value from the first promise that has resolved.
/// </summary>
public static IPromise Race(IEnumerable<IPromise> promises)
{
var promisesArray = promises.ToArray();
if (promisesArray.Length == 0)
{
throw new ApplicationException("At least 1 input promise must be provided for Race");
}
var resultPromise = new Promise();
resultPromise.WithName("Race");
promisesArray.Each((promise, index) =>
{
promise
.Catch(ex =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
// If a promise errorred and the result promise is still pending, reject it.
resultPromise.Reject(ex);
}
})
.Then(() =>
{
if (resultPromise.CurState == PromiseState.Pending)
{
resultPromise.Resolve();
}
})
.Done();
});
return resultPromise;
}
/// <summary>
/// Convert a simple value directly into a resolved promise.
/// </summary>
public static IPromise Resolved()
{
var promise = new Promise();
promise.Resolve();
return promise;
}
/// <summary>
/// Convert an exception directly into a rejected promise.
/// </summary>
public static IPromise Rejected(Exception ex)
{
// Argument.NotNull(() => ex);
var promise = new Promise();
promise.Reject(ex);
return promise;
}
/// <summary>
/// Raises the UnhandledException event.
/// </summary>
internal static void PropagateUnhandledException(object sender, Exception ex)
{
if (unhandlerException != null)
{
unhandlerException(sender, new ExceptionEventArgs(ex));
}
}
class Enumerated : IEnumerator {
private Promise promise;
private bool abortOnFail;
public Enumerated(Promise promise, bool abortOnFail) {
this.promise = promise;
this.abortOnFail = abortOnFail;
}
public bool MoveNext() {
if (abortOnFail && promise.CurState == PromiseState.Rejected) {
throw promise.rejectionException;
}
return promise.CurState == PromiseState.Pending;
}
public void Reset() { }
public object Current { get { return null; } }
}
public IEnumerator ToWaitFor(bool abortOnFail = false) {
var ret = new Enumerated(this, abortOnFail);
//someone will poll for completion, so act like we've been terminated
Done(() => {}, ex => {});
return ret;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 8c0ade0b4d4a8704baa1b214a700ed50
timeCreated: 1479581013
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,22 +0,0 @@
using UnityEngine;
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Hooks to ensure that zfb gets shut down on app (or playmode) exit.
* (This used to be used only for builds, but now that we can cleanly shut down the browser
* system after every playmode run it's used in the Editor too.)
*/
class StandaloneShutdown : MonoBehaviour {
public static void Create() {
var go = new GameObject("ZFB Shutdown");
go.AddComponent<StandaloneShutdown>();
DontDestroyOnLoad(go);
}
public void OnApplicationQuit() {
BrowserNative.UnloadNative();
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 88efef500fce2a24b96f9f6a661a15fb
timeCreated: 1449682818
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,151 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading;
using UnityEngine;
#if UNITY_2017_3_OR_NEWER
using UnityEngine.Networking;
#endif
namespace ZenFulcrum.EmbeddedBrowser {
/**
* Implements fetching BrowserAssets for standalone builds.
*
* During build, everything in BrowserAssets is packaged into a single file with the following format:
* brString {FileHeader}
* i32 numEntries
* {numEntries} IndexEntry objects
* data //The data section is a series of filedata chunks, as laid out in the index.
*/
public class StandaloneWebResources : WebResources {
public struct IndexEntry {
public string name;
public long offset;
public int length;
}
private const string FileHeader = "zfbRes_v1";
protected Dictionary<string, IndexEntry> toc = new Dictionary<string, IndexEntry>();
protected string dataFile;
public StandaloneWebResources(string dataFile) {
this.dataFile = dataFile;
}
public const string DefaultPath = "Resources/browser_assets";
public void LoadIndex() {
using (var data = new BinaryReader(File.OpenRead(dataFile))) {
var header = data.ReadString();
if (header != FileHeader) throw new Exception("Invalid web resource file");
var num = data.ReadInt32();
for (int i = 0; i < num; ++i) {
var entry = new IndexEntry() {
name = data.ReadString(),
offset = data.ReadInt64(),
length = data.ReadInt32(),
};
toc[entry.name] = entry;
}
}
}
public override void HandleRequest(int id, string url) {
var parsedURL = new Uri(url);
#if UNITY_2017_3_OR_NEWER
var path = UnityWebRequest.UnEscapeURL(parsedURL.AbsolutePath);
#else
var path = WWW.UnEscapeURL(parsedURL.AbsolutePath);
#endif
IndexEntry entry;
if (!toc.TryGetValue(path, out entry)) {
SendError(id, "Not found", 404);
return;
}
new Thread(() => {
try {
var ext = Path.GetExtension(entry.name);
if (ext.Length > 0) ext = ext.Substring(1);
string mimeType;
if (!extensionMimeTypes.TryGetValue(ext, out mimeType)) {
mimeType = extensionMimeTypes["*"];
}
using (var file = File.OpenRead(dataFile)) {
var pre = new ResponsePreamble {
headers = null,
length = entry.length,
mimeType = mimeType,
statusCode = 200,
};
SendPreamble(id, pre);
file.Seek(entry.offset, SeekOrigin.Begin);
var data = new byte[entry.length];
var readLen = file.Read(data, 0, entry.length);
if (readLen != data.Length) throw new Exception("Insufficient data for file");
SendData(id, data);
}
} catch (Exception ex) {
Debug.LogException(ex);
}
}).Start();
}
public void WriteData(Dictionary<string, byte[]> files) {
var entries = new Dictionary<string, IndexEntry>();
using (var file = File.OpenWrite(dataFile)) {
var writer = new BinaryWriter(file, Encoding.UTF8 /*, true (Mono too old)*/);
writer.Write(FileHeader);
writer.Write(files.Count);
var tocStart = file.Position;
foreach (var kvp in files) {
writer.Write(kvp.Key);
writer.Write(0L);
writer.Write(0);
}
//we'll come back and fill it in right later
foreach (var kvp in files) {
var data = kvp.Value;
var entry = new IndexEntry {
name = kvp.Key,
length = kvp.Value.Length,
offset = file.Position,
};
writer.Write(data);
entries[kvp.Key] = entry;
}
//now go back and write the correct data.
writer.Seek((int)tocStart, SeekOrigin.Begin);
foreach (var kvp in files) {
var entry = entries[kvp.Key];
writer.Write(kvp.Key);
writer.Write(entry.offset);
writer.Write(entry.length);
}
}
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 116a67bb616e372428beb4db63fa0591
timeCreated: 1449617902
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More