diff --git a/README-ZH.md b/README-ZH.md index 3d717275..fa932e46 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -262,7 +262,6 @@ EmojiUtils.configuration = new EmojiResourceConfiguration( 我们提供了一个包装好的`UnityObjectDetector`组件以及`onRelease`回调函数,借此您可以实现简单地将物体(例如Hierarchy内的场景物体、Project窗口下的文件等)拖拽至区域内,来获得`UnityEngine.Object[] `类型的引用并进行操作。 - ## 调试UIWidgets应用程序 #### 定义UIWidgets_DEBUG diff --git a/README.md b/README.md index 485a94c8..4221fe58 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,6 @@ EmojiUtils.configuration = new EmojiResourceConfiguration( With the provided packaged stateful widget `UnityObjectDetector` and its `onRelease` callback function, you can easily drag some objects (for example GameObject from Hierarchy, files from Project Window, etc) into the area, get the UnityEngine.Object[] references and make further modification. - ## Debug UIWidgets Application #### Define UIWidgets_DEBUG diff --git a/Runtime/debugger/inspector_window.cs b/Runtime/debugger/inspector_window.cs index 7d9c7d83..e9546ceb 100644 --- a/Runtime/debugger/inspector_window.cs +++ b/Runtime/debugger/inspector_window.cs @@ -45,7 +45,7 @@ public class WidgetsInpsectorWindow : EditorWindow { List m_UpdateActions = new List(); - [MenuItem("Window/Analysis/UIWidgets Inspector")] + [MenuItem("Window/UIWidgets/Inspector")] public static void Init() { WidgetsInpsectorWindow window = (WidgetsInpsectorWindow) GetWindow(typeof(WidgetsInpsectorWindow)); diff --git a/Runtime/editor/UIWidgetsResourcesImporter.cs b/Runtime/editor/UIWidgetsResourcesImporter.cs new file mode 100644 index 00000000..efe5c92b --- /dev/null +++ b/Runtime/editor/UIWidgetsResourcesImporter.cs @@ -0,0 +1,139 @@ +#if UNITY_EDITOR +using UnityEngine.Serialization; +using System.IO; +using UnityEngine; +using UnityEditor; + +namespace Unity.UIWidgets.editor +{ + [System.Serializable] + public class UIWidgetsResourcesImporter + { + bool m_UIWidgetsResourcesImported; + + public void OnDestroy() + { + } + + public void OnGUI() + { + string packageFullPath = GetPackageFullPath(); + m_UIWidgetsResourcesImported = Directory.Exists("Assets/UIWidgetsResources") || + Directory.Exists(packageFullPath + "/Runtime/Resources/fonts"); + // Display options to import Essential resources + GUILayout.BeginVertical(EditorStyles.helpBox); + { + GUILayout.Label("UIWidgets Resources", EditorStyles.boldLabel); + GUILayout.Label("We need to add resources to your project that are essential for using UIWidgets. " + + "These new resources will be placed at the root of your project in the \"UIWidgetsResources\" folder.", + new GUIStyle(EditorStyles.label) { wordWrap = true } ); + GUILayout.Space(5f); + + GUI.enabled = !m_UIWidgetsResourcesImported; + if (GUILayout.Button("Import UIWidgets Resources")) + { + AssetDatabase.importPackageCompleted += ImportCallback; + + AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/UIWidgetsResources.unitypackage", false); + } + GUILayout.Space(5f); + GUI.enabled = true; + } + GUILayout.EndVertical(); + GUILayout.Space(5f); + } + + internal void RegisterResourceImportCallback() + { + AssetDatabase.importPackageCompleted += ImportCallback; + } + + void ImportCallback(string packageName) + { + if (packageName == "UIWidgetsResources") + { + m_UIWidgetsResourcesImported = true; + + #if UNITY_2018_3_OR_NEWER + SettingsService.NotifySettingsProviderChanged(); + #endif + } + + Debug.Log("[" + packageName + "] have been imported."); + + AssetDatabase.importPackageCompleted -= ImportCallback; + } + + static string GetPackageFullPath() + { + string packagePath = Path.GetFullPath("Packages/com.unity.uiwidgets"); + if (Directory.Exists(packagePath)) + { + return packagePath; + } + + packagePath = Path.GetFullPath("Packages/UIWidgets"); + if (Directory.Exists(packagePath)) + { + return packagePath; + } + + packagePath = Path.GetFullPath("Assets/UIWidgets"); + if (Directory.Exists(packagePath)) + { + return packagePath; + } + + return null; + } + } + + public class UIWidgetsResourcesImporterWindow : EditorWindow + { + [FormerlySerializedAs("m_ResourceImporter")] [SerializeField] + UIWidgetsResourcesImporter resourcesImporter; + + public static void ShowResourcesImporterWindow() + { + var window = GetWindow(); + window.titleContent = new GUIContent("UIWidgets Resources Importer"); + window.Focus(); + } + + void OnEnable() + { + SetEditorWindowSize(); + + if (resourcesImporter == null) + resourcesImporter = new UIWidgetsResourcesImporter(); + resourcesImporter.RegisterResourceImportCallback(); + } + + void OnDestroy() + { + resourcesImporter.OnDestroy(); + } + + void OnGUI() + { + resourcesImporter.OnGUI(); + } + + void OnInspectorUpdate() + { + Repaint(); + } + + void SetEditorWindowSize() + { + EditorWindow editorWindow = this; + + Vector2 windowSize = new Vector2(640, 110); + editorWindow.minSize = windowSize; + editorWindow.maxSize = windowSize; + } + } + +} + +#endif diff --git a/Runtime/editor/UIWidgetsResourcesImporter.cs.meta b/Runtime/editor/UIWidgetsResourcesImporter.cs.meta new file mode 100644 index 00000000..d0f705e2 --- /dev/null +++ b/Runtime/editor/UIWidgetsResourcesImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51056842ae7b141b48f34f9552d3abcb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/UIWidgetsPanel.cs b/Runtime/engine/UIWidgetsPanel.cs index a72ffabf..c886fea3 100644 --- a/Runtime/engine/UIWidgetsPanel.cs +++ b/Runtime/engine/UIWidgetsPanel.cs @@ -138,6 +138,13 @@ void _handleViewMetricsChanged(string method, List args) { this._displayMetrics.onViewMetricsChanged(); } + protected virtual void InitWindowAdapter() { + D.assert(this._windowAdapter == null); + this._windowAdapter = new UIWidgetWindowAdapter(this); + + this._windowAdapter.OnEnable(); + } + protected override void OnEnable() { base.OnEnable(); @@ -153,10 +160,7 @@ protected override void OnEnable() { _repaintEvent = new Event {type = EventType.Repaint}; } - D.assert(this._windowAdapter == null); - this._windowAdapter = new UIWidgetWindowAdapter(this); - - this._windowAdapter.OnEnable(); + this.InitWindowAdapter(); Widget root; using (this._windowAdapter.getScope()) { diff --git a/Runtime/engine/raycastable.meta b/Runtime/engine/raycastable.meta new file mode 100644 index 00000000..4b305bd7 --- /dev/null +++ b/Runtime/engine/raycastable.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d8e15e1221dae40e68226ec6d8ed8ddb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/RaycastManager.cs b/Runtime/engine/raycastable/RaycastManager.cs new file mode 100644 index 00000000..f9c7aca8 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastManager.cs @@ -0,0 +1,125 @@ +using System.Collections.Generic; +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.ui; +using UnityEngine; + +namespace Unity.UIWidgets.engine.raycast { + public class RaycastableRect { + bool _isDirty = true; + + public bool isDirty { + get { return this._isDirty; } + } + + public float left; + public float right; + public float top; + public float bottom; + + public void MarkDirty() { + this._isDirty = true; + } + + public void UnmarkDirty() { + this._isDirty = false; + } + + public void UpdateRect(float left, float top, float width, float height) { + this.left = left; + this.right = left + width; + this.top = top; + this.bottom = top + height; + } + + public bool CheckInRect(Vector2 pos) { + return pos.x >= this.left && + pos.x < this.right && + pos.y >= this.top && + pos.y < this.bottom; + } + } + + public class RaycastManager { + static RaycastManager _instance; + + public static RaycastManager instance { + get { + if (_instance == null) { + _instance = new RaycastManager(); + } + + return _instance; + } + } + + public readonly Dictionary> raycastHandlerMap = + new Dictionary>(); + + public static void NewWindow(int windowHashCode) { + if (!instance.raycastHandlerMap.ContainsKey(windowHashCode)) { + instance.raycastHandlerMap.Add(windowHashCode, new Dictionary()); + } + } + + public static void DisposeWindow(int windowHashCode) { + if (instance.raycastHandlerMap.ContainsKey(windowHashCode)) { + instance.raycastHandlerMap.Remove(windowHashCode); + } + } + + public static void AddToList(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}, " + + $"Make sure using UIWidgetsRaycastablePanel instead of UIWidgetsPanel " + + $"while using RaycastableContainer."); + D.assert(!instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map already contains Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode][widgetHashCode] = new RaycastableRect(); + } + + public static void MarkDirty(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode][widgetHashCode].MarkDirty(); + } + + public static void UpdateSizeOffset(int widgetHashCode, int windowHashCode, Size size, Offset offset) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + if (instance.raycastHandlerMap[windowHashCode][widgetHashCode].isDirty) { + instance.raycastHandlerMap[windowHashCode][widgetHashCode] + .UpdateRect(offset.dx, offset.dy, size.width, size.height); + instance.raycastHandlerMap[windowHashCode][widgetHashCode].UnmarkDirty(); + } + } + + public static void RemoveFromList(int widgetHashCode, int windowHashCode) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + D.assert(instance.raycastHandlerMap[windowHashCode].ContainsKey(widgetHashCode), () => + $"Raycast Handler Map doesn't contain Widget {widgetHashCode} at Window {windowHashCode}"); + + instance.raycastHandlerMap[windowHashCode].Remove(widgetHashCode); + } + + public static bool CheckCastThrough(int windowHashCode, Vector2 pos) { + D.assert(instance.raycastHandlerMap.ContainsKey(windowHashCode), () => + $"Raycast Handler Map doesn't contain Window {windowHashCode}"); + + foreach (var item in instance.raycastHandlerMap[windowHashCode]) { + if (item.Value.CheckInRect(pos)) { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/RaycastManager.cs.meta b/Runtime/engine/raycastable/RaycastManager.cs.meta new file mode 100644 index 00000000..74519d43 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9574f12b230354e6f87fc5fc0c98c96e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs b/Runtime/engine/raycastable/RaycastableContainer.cs new file mode 100644 index 00000000..429d6524 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastableContainer.cs @@ -0,0 +1,106 @@ +using Unity.UIWidgets.foundation; +using Unity.UIWidgets.rendering; +using Unity.UIWidgets.ui; +using Unity.UIWidgets.widgets; + +namespace Unity.UIWidgets.engine.raycast { + class RaycastableBox : SingleChildRenderObjectWidget { + public RaycastableBox( + Key key = null, + Widget child = null + ) : base(key, child) { + this.windowHashCode = Window.instance.GetHashCode(); + } + + readonly int windowHashCode; + + public override RenderObject createRenderObject(BuildContext context) { + return new RenderRaycastableBox( + windowHashCode: this.windowHashCode, + widget: this + ); + } + + public override Element createElement() { + return new _RaycastableBoxRenderElement(windowHashCode: this.windowHashCode, widget: this); + } + } + + class RenderRaycastableBox : RenderProxyBox { + public RenderRaycastableBox( + int windowHashCode, + RaycastableBox widget + ) { + this.widgetHashCode = widget.GetHashCode(); + this.windowHashCode = windowHashCode; + } + + readonly int widgetHashCode; + readonly int windowHashCode; + + public override void paint(PaintingContext context, Offset offset) { + RaycastManager.UpdateSizeOffset(this.widgetHashCode, this.windowHashCode, this.size, offset); + + base.paint(context, offset); + } + } + + class _RaycastableBoxRenderElement : SingleChildRenderObjectElement { + public _RaycastableBoxRenderElement( + int windowHashCode, + RaycastableBox widget + ) : base(widget) { + this.windowHashCode = windowHashCode; + } + + public new RaycastableBox widget { + get { return base.widget as RaycastableBox; } + } + + int widgetHashCode; + int windowHashCode; + + public override void mount(Element parent, object newSlot) { + this.widgetHashCode = this.widget.GetHashCode(); + RaycastManager.AddToList(this.widgetHashCode, this.windowHashCode); + base.mount(parent, newSlot); + } + + public override void update(Widget newWidget) { + RaycastManager.MarkDirty(this.widgetHashCode, this.windowHashCode); + base.update(newWidget); + } + + public override void unmount() { + RaycastManager.RemoveFromList(this.widgetHashCode, this.windowHashCode); + base.unmount(); + } + } + + public class RaycastableContainer : StatelessWidget { + public RaycastableContainer( + Widget child = null, + Key key = null + ) : base(key) { + this.child = child; + } + + public readonly Widget child; + + public override Widget build(BuildContext context) { + Widget current = this.child; + + if (this.child == null) { + current = new LimitedBox( + maxWidth: 0.0f, + maxHeight: 0.0f, + child: new ConstrainedBox(constraints: BoxConstraints.expand()) + ); + } + + current = new RaycastableBox(child: current); + + return current; + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/RaycastableContainer.cs.meta b/Runtime/engine/raycastable/RaycastableContainer.cs.meta new file mode 100644 index 00000000..3bb75ef0 --- /dev/null +++ b/Runtime/engine/raycastable/RaycastableContainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9af0c7d6aab134f5ba187ff34acf2377 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs new file mode 100644 index 00000000..2fc70de1 --- /dev/null +++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs @@ -0,0 +1,40 @@ +using Unity.UIWidgets.engine; +using UnityEngine; + +namespace Unity.UIWidgets.engine.raycast { + [RequireComponent(typeof(RectTransform))] + public class UIWidgetsRaycastablePanel : UIWidgetsPanel, ICanvasRaycastFilter { + int windowHashCode; + + protected override void InitWindowAdapter() { + base.InitWindowAdapter(); + this.windowHashCode = this.window.GetHashCode(); + RaycastManager.NewWindow(this.windowHashCode); + } + + protected override void OnDisable() { + base.OnDisable(); + RaycastManager.DisposeWindow(this.windowHashCode); + } + + public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) { + if (!this.enabled) { + return true; + } + + Vector2 local; + RectTransformUtility.ScreenPointToLocalPointInRectangle(this.rectTransform, screenPoint, eventCamera, + out local); + + Rect rect = this.rectTransform.rect; + + // Convert top left corner as reference origin point. + local.x += this.rectTransform.pivot.x * rect.width; + local.y -= this.rectTransform.pivot.y * rect.height; + local.x = local.x / this.devicePixelRatio; + local.y = -local.y / this.devicePixelRatio; + + return !RaycastManager.CheckCastThrough(this.windowHashCode, local); + } + } +} \ No newline at end of file diff --git a/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta new file mode 100644 index 00000000..f5ff1f78 --- /dev/null +++ b/Runtime/engine/raycastable/UIWidgetsRaycastablePanel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5265d12f7193408b90993bdf987f058 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/rendering/table.cs b/Runtime/rendering/table.cs index f2022b3b..c0774716 100644 --- a/Runtime/rendering/table.cs +++ b/Runtime/rendering/table.cs @@ -841,12 +841,17 @@ List _computeColumnWidths(BoxConstraints constraints) { float deficit = tableWidth - maxWidthConstraint; int availableColumns = this.columns; - float minimumDeficit = 0.00000001f; - while (deficit > 0.0f && totalFlex > minimumDeficit) { + + //(Xingwei Zhu) this deficit is double and set to be 0.00000001f in flutter. + //since we use float by default, making it larger should make sense in most cases + float minimumDeficit = 0.0001f; + while (deficit > minimumDeficit && totalFlex > minimumDeficit) { float newTotalFlex = 0.0f; for (int x = 0; x < this.columns; x++) { if (flexes[x] != null) { - float newWidth = widths[x] - deficit * flexes[x].Value / totalFlex; + //(Xingwei Zhu) in case deficit * flexes[x].Value / totalFlex => 0 if deficit is really small, leading to dead loop, + //we amend it with a default larger value to ensure that this loop will eventually end + float newWidth = widths[x] - Mathf.Max(minimumDeficit, deficit * flexes[x].Value / totalFlex); D.assert(newWidth.isFinite()); if (newWidth <= minWidths[x]) { deficit -= widths[x] - minWidths[x]; diff --git a/Runtime/service/keyboard.cs b/Runtime/service/keyboard.cs index 09414190..3a4aab50 100644 --- a/Runtime/service/keyboard.cs +++ b/Runtime/service/keyboard.cs @@ -364,7 +364,7 @@ void _handleMethodCall(string method, List args) { } } -#if UNITY_WEBGL +#if UNITY_WEBGL && !UNITY_EDITOR class UIWidgetsWebGLKeyboardDelegate : AbstractUIWidgetsKeyboardDelegate { public override void show() { diff --git a/Runtime/service/text_input.cs b/Runtime/service/text_input.cs index 3f4ab70b..52a1bbe7 100644 --- a/Runtime/service/text_input.cs +++ b/Runtime/service/text_input.cs @@ -590,7 +590,7 @@ public static TextInputConnection attach(TextInputClient client, TextInputConfig else { keyboardDelegate = new UIWidgetsTouchScreenKeyboardDelegate(); } -#elif UNITY_WEBGL +#elif UNITY_WEBGL && !UNITY_EDITOR keyboardDelegate = new UIWidgetsWebGLKeyboardDelegate(); #else keyboardDelegate = new DefaultKeyboardDelegate(); diff --git a/Runtime/ui/painting/txt/font_manager.cs b/Runtime/ui/painting/txt/font_manager.cs index c6d6052a..1ce5f0cb 100644 --- a/Runtime/ui/painting/txt/font_manager.cs +++ b/Runtime/ui/painting/txt/font_manager.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Unity.UIWidgets.editor; using Unity.UIWidgets.foundation; using UnityEngine; @@ -35,6 +36,23 @@ public class FontManager { public void addFont(Font font, string familyName, FontWeight fontWeight = null, FontStyle fontStyle = FontStyle.normal) { + if (font == null) { + D.assert(() => { + Debug.LogWarning($"Font missing (when adding font for {familyName})!"); + return true; + }); +#if UNITY_EDITOR + if (Resources.Load("fonts/MaterialIcons-Regular") == null) { + D.assert(() => { + Debug.Log("It appears that you have not imported UIWidgetsResources."); + return true; + }); + UIWidgetsResourcesImporterWindow.ShowResourcesImporterWindow(); + } +#endif + return; + } + fontWeight = fontWeight ?? FontWeight.normal; D.assert(font != null); diff --git a/Runtime/ui/txt/emoji.cs b/Runtime/ui/txt/emoji.cs index b1a91dec..79bd00fa 100644 --- a/Runtime/ui/txt/emoji.cs +++ b/Runtime/ui/txt/emoji.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Unity.UIWidgets.editor; using Unity.UIWidgets.foundation; using UnityEngine; @@ -434,6 +435,15 @@ public static EmojiResourceConfiguration configuration { _image = new Image(Resources.Load(value.spriteSheetAssetName)); } catch (Exception e) { +#if UNITY_EDITOR + if (Resources.Load("images/EmojiIOS13.2") == null) { + D.assert(() => { + Debug.Log("It appears that you have not imported UIWidgetsResources."); + return true; + }); + UIWidgetsResourcesImporterWindow.ShowResourcesImporterWindow(); + } +#endif _image = null; Debug.LogError(e.StackTrace); } diff --git a/Scripts/asset_store b/Scripts/asset_store new file mode 100755 index 00000000..b5ec5561 --- /dev/null +++ b/Scripts/asset_store @@ -0,0 +1,349 @@ +#!/bin/sh + +function usage() { + echo "$0 " + echo + echo "path to target project : the project to upload to asset store" + exit -1 +} + +if [ "$#" != "1" ]; then + usage +fi + +target_project=$1 +echo "Copying to $target_project" + +if ! [ -d $target_project ]; then + echo "Target project does not exist!" + exit -1 +fi + +target_dir=$target_project/Assets/UIWidgets +rm -rf $target_dir +echo "rm -rf $target_dir" +mkdir -p $target_dir +echo "mkdir -p $target_dir" + +cp -R ../Runtime $target_dir/Runtime +echo "Copied Runtime" + +cp -R ../Editor $target_dir/Editor +echo "Copied Editor" + +cp -R ../Samples $target_dir/Samples +echo "Copied Samples" + +cp -R ../Tests/Resources $target_dir/Resources +echo "Copied Resources" + +rm -f $target_dir/Samples/UIWidgetSample/Resources/file_example_MOV_480_700kB.mov* + +mkdir -p $target_dir/Documentation +cat > $target_dir/Documentation/README.txt << END +# UIWidgets + +## Introduction + +UIWidgets is a plugin package for Unity Editor which helps developers to create, debug and deploy efficient, +cross-platform Apps using the Unity Engine. + +UIWidgets is mainly derived from Flutter (https://github.com/flutter/flutter). However, taking advantage of +the powerful Unity Engine, it offers developers many new features to improve their Apps +as well as the develop workflow significantly. + + +#### Efficiency + +Using the latest Unity rendering SDKs, a UIWidgets App can run very fast and keep >60fps in most times. + + +#### Cross-Platform + +A UIWidgets App can be deployed on all kinds of platforms including PCs, mobile devices and web page directly, like +any other Unity projects. + +#### Multimedia Support + +Except for basic 2D UIs, developers are also able to include 3D Models, audios, particle-systems to their UIWidgets Apps. + + +#### Developer-Friendly + +A UIWidgets App can be debug in the Unity Editor directly with many advanced tools like +CPU/GPU Profiling, FPS Profiling. + +## Requirement + +#### Unity + +Install Unity 2018.3 or above. You can download the latest Unity on https://unity3d.com/get-unity/download. + +#### UIWidgets Package + +Visit our Github repository https://github.com/UnityTech/UIWidgets + to download the latest UIWidgets package. + +Move the downloaded package folder into the Package folder of your Unity project. + +Generally, you can make it using a console (or terminal) application by just a few commands as below: + + cd /Packages + git clone https://github.com/UnityTech/UIWidgets.git com.unity.uiwidgets + +## Getting Start + +#### i. Overview + +In this tutorial, we will create a very simple UIWidgets App as the kick-starter. The app contains +only a text label and a button. The text label will count the times of clicks upon the button. + +First of all, please open or create a Unity Project and open it with Unity Editor. + +And then open Project Settings, go to Player section and add "UIWidgets_DEBUG" to the Scripting Define Symbols field. +This enables the debug mode of UIWidgets for your development. Remove this for your release build afterwards. + +#### ii. Scene Build + +A UIWidgets App is usually built upon a Unity UI Canvas. Please follow the steps to create a +UI Canvas in Unity. +1. Create a new Scene by "File -> New Scene"; +1. Create a UI Canvas in the scene by "GameObject -> UI -> Canvas"; +1. Add a Panel (i.e., Panel 1) to the UI Canvas by right click on the Canvas and select "UI -> Panel". Then remove the +Image Component from the Panel. + +#### iii. Create Widget + +A UIWidgets App is written in C# Scripts. Please follow the steps to create an App and play it +in Unity Editor. + +1. Create a new C# Script named "UIWidgetsExample.cs" and paste the following codes into it. + + using System.Collections.Generic; + using Unity.UIWidgets.animation; + using Unity.UIWidgets.engine; + using Unity.UIWidgets.foundation; + using Unity.UIWidgets.material; + using Unity.UIWidgets.painting; + using Unity.UIWidgets.ui; + using Unity.UIWidgets.widgets; + using UnityEngine; + using FontStyle = Unity.UIWidgets.ui.FontStyle; + + namespace UIWidgetsSample { + public class UIWidgetsExample : UIWidgetsPanel { + protected override void OnEnable() { + // if you want to use your own font or font icons. + // FontManager.instance.addFont(Resources.Load(path: "path to your font"), "font family name"); + + // load custom font with weight & style. The font weight & style corresponds to fontWeight, fontStyle of + // a TextStyle object + // FontManager.instance.addFont(Resources.Load(path: "path to your font"), "Roboto", FontWeight.w500, + // FontStyle.italic); + + // add material icons, familyName must be "Material Icons" + // FontManager.instance.addFont(Resources.Load(path: "path to material icons"), "Material Icons"); + + base.OnEnable(); + } + + protected override Widget createWidget() { + return new WidgetsApp( + home: new ExampleApp(), + pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) => + new PageRouteBuilder( + settings: settings, + pageBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation) => builder(context) + ) + ); + } + + class ExampleApp : StatefulWidget { + public ExampleApp(Key key = null) : base(key) { + } + + public override State createState() { + return new ExampleState(); + } + } + + class ExampleState : State { + int counter = 0; + + public override Widget build(BuildContext context) { + return new Column( + children: new List { + new Text("Counter: " + this.counter), + new GestureDetector( + onTap: () => { + this.setState(() + => { + this.counter++; + }); + }, + child: new Container( + padding: EdgeInsets.symmetric(20, 20), + color: Colors.blue, + child: new Text("Click Me") + ) + ) + } + ); + } + } + } + } + +1. Save this script and attach it to Panel 1 as its component. +1. Press the "Play" Button to start the App in Unity Editor. + +#### iv. Build App + +Finally, the UIWidgets App can be built to packages for any specific platform by the following steps. +1. Open the Build Settings Panel by "File -> Build Settings..." +1. Choose a target platform and click "Build". Then the Unity Editor will automatically assemble +all relevant resources and generate the final App package. + +#### How to load images? + +1. Put your images files in Resources folder. e.g. image1.png. +2. You can add image1@2.png and image1@3.png in the same folder to support HD screens. +3. Use Image.asset("image1") to load the image. Note: as in Unity, ".png" is not needed. + +UIWidgets supports Gif as well! +1. Suppose you have loading1.gif. Rename it to loading1.gif.bytes and copy it to Resources folder. +2. You can add loading1@2.gif.bytes and loading1@3.gif.bytes in the same folder to support HD screens. +3. Use Image.asset("loading1.gif") to load the gif images. + +#### Using Window Scope + +If you see the error AssertionError: Window.instance is null or null pointer error of Window.instance, +it means the code is not running in the window scope. In this case, you can enclose your code +with window scope as below: + +using(WindowProvider.of(your gameObject with UIWidgetsPanel).getScope()) { + // code dealing with UIWidgets, + // e.g. setState(() => {....}) +} + + +This is needed if the code is in methods +not invoked by UIWidgets. For example, if the code is in completed callback of UnityWebRequest, +you need to enclose them with window scope. +Please see "./Samples/UIWidgetSample/HttpRequestSample.cs" for detail. +For callback/event handler methods from UIWidgets (e.g Widget.build, State.initState...), you don't need do +it yourself, since the framework ensure it's in window scope. + +#### Show Status Bar on Android + +Status bar is always hidden by default when an Unity project is running on an Android device. If you + want to show the status bar in your App, this + solution (https://github.com/Over17/UnityShowAndroidStatusBar) seems to be + compatible to UIWidgets, therefore can be used as a good option before we release our + full support solution on this issue. + + Besides, + please set "Render Outside Safe Area" to true in the "Player Settings" to make this plugin working properly on Android P or later. + +#### Automatically Adjust Frame Rate + +To build an App that is able to adjust the frame rate automatically, please open Project Settings, and +in the Quality tab, set the "V Sync Count" option of the target platform to "Don't Sync". The default +logic is to set the frame rate to 25 when the screen is static, and change the frame rate to 60 whenever +the screen changes. If you would like to modify the behavior of speeding up or cooling down the frame +rate, please set Window.onFrameRateSpeedUp and/or Window.onFrameRateCoolDown to your own functions. + +#### WebGL Canvas Device Pixel Ratio Plugin + +The width and height of the Canvas in browser may differ from the number of pixels the Canvas occupies on the screen. +Therefore, the image may blur in the built WebGL program. +The Plugin Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio_20xx.x.jslib (2018.3 and 2019.1 for now) solves this issue. +Please select the plugin of the Unity version corresponding to your project, and disable other versions of this plugin, as +follows: select this plugin in the Project panel, and uncheck WebGL under Select platforms for plugin in the Inspector panel. +If you need to disable this plugin for any reason, please disable all the versions of this plugin as described above. + +This plugin overrides the following parameters in the Unity WebGL building module: + +JS_SystemInfo_GetWidth +JS_SystemInfo_GetHeight +JS_SystemInfo_GetCurrentCanvasWidth +JS_SystemInfo_GetCurrentCanvasHeight +\$Browser +\$JSEvents + +If you would like to implement your own WebGL plugin, and your plugin overrides at least one of the above parameters, +you need to disable the UIWidgetsCanvasDevicePixelRatio plugin in the above mentioned way to avoid possible conflicts. +If you still need the function provided by this plugin, you can manually apply the modification to Unity WebGL +building module introduced in this plugin. All the modifications introduced in UIWidgetsCanvasDevicePixelRatio are +marked by ////////// Modification Start //////////// and ////////// Modification End ////////////. +In the marked codes, all the multiplications and divisions with devicePixelRatio are introduced by our modification. +To learn about the original script in detail, please refer to SystemInfo.js and UnityNativeJS/UnityNative.js in +PlaybackEngines/WebGLSupport/BuildTools/lib in your Unity Editor installation. + +#### Image Import Setting + +Unity, by default, resizes the width and height of an imported image to the nearest integer that is a power of 2. +In UIWidgets, you should almost always disable this by selecting the image in the "Project" panel, then in the +"Inspector" panel set the "Non Power of 2" option (in "Advanced") to "None", to prevent your image from being +resized unexpectedly. + +## Debug UIWidgets Application + +#### Define UIWidgets_DEBUG + +It's recommended to define the UIWidgets_DEBUG script symbol in editor, this will turn on +debug assertion in UIWidgets, which will help to find potential bugs earlier. To do this: +please go to Player Settings -> Other Settings -> Configuration -> Scripting Define Symbols, +and add UIWidgets_DEBUG. +The symbol is for debug purpose, please remove it from your release build. + +#### UIWidgets Inspector + +The UIWidgets Inspector tool is for visualizing and exploring the widget trees. You can find it +via Window/Analysis/UIWidgets inspector in Editor menu. + +Note + +* UIWidgets_DEBUG needs to be define for inspector to work properly. +* Inspector currently only works in Editor Play Mode, inspect standalone built application is not supported for now. + +## Learn + +#### Samples + +You can find many UIWidgets App samples in the UIWidgets package in the Samples folder. +Feel free to try them out and make modifications to see the results. +To get started, the UIWidgetsTheatre scene provides you +a list of carefully selected samples to start with. + +You can also try UIWidgets-based Editor windows by clicking UIWidgetsTest on the main menu +and open one of the dropdown samples. + +#### Wiki + +The develop team is still working on the UIWidgets Wiki. However, since UIWidgets is mainly derived from Flutter, + you can refer to Flutter Wiki to access detailed descriptions of UIWidgets APIs + from those of their Flutter counterparts. +Meanwhile, you can join the discussion channel at (https://connect.unity.com/g/uiwidgets) + +#### FAQ + +Can I create standalone App using UIWidgets? Yes +Can I use UIWidgets to build game UIs? Yes +Can I develop Unity Editor plugins using UIWidgets? Yes +Is UIWidgets a extension of UGUI/NGUI? No +Is UIWidgets just a copy of Flutter? No +Can I create UI with UIWidgets by simply drag&drop? No +Do I have to pay for using UIWidgets? No +Any IDE recommendation for UIWidgets? Rider, VSCode(Open .sln) + +## Contact Us + +Keven Gu (kg@unity3d.com) +Xingwei Zhu (xingwei.zhu@unity3d.com) +Fan Zhang (fzhang@unity3d.com) +Yuncong Zhang (yuncong.zhang@unity3d.com) + +END + diff --git a/Scripts/packman b/Scripts/packman new file mode 100755 index 00000000..2d875134 --- /dev/null +++ b/Scripts/packman @@ -0,0 +1,124 @@ +#!/bin/sh + +repo_url="git@github.cds.internal.unity3d.com:unity/com.unity.uiwidgets.git" +package_name="com.unity.uiwidgets" +target_version="844bac835" + +function usage() { + echo "$0 [path to target repository]" + echo + echo "path to target repository : The repository to upload to package manager ends with " + echo " com.unity.uiwidgets. If not provided, will clone from" + echo " $repo_url" + exit -1 +} + +if [ "$0" != "./`basename $0`" ]; then + echo "This script must be invoked in the folder containing this script." + exit -1 +fi + +if [ "$1" == "-h" ]; then + usage +fi + +if [ "$#" != "1" ]; then + tmpdir=`mktemp -d` + target_repository="$tmpdir/$package_name" + echo "Repository path not provided. Will clone from" + echo "$repo_url" + echo "to $tmpdir/$package_name" + git clone $repo_url $target_repository +else + target_repository=`cd $1; pwd; cd - > /dev/null` +fi + +# Check the validity of the target directory +if ! [ -d "$target_repository" ]; then + echo "Target respository ($target_repository) does not exist!" + exit -1 +fi + +current_directory=`pwd` +cd $target_repository +if [ "`git remote get-url origin`" != "$repo_url" ]; then + cd $current_directory + echo "Target repository is not pointing to $repo_url" + exit -1 +fi +cd $current_directory + +echo "Copying to $target_repository" + +# Copy the UIWidgets Repository to a temporary dir and +# checkout to target_version +tmpdir=`mktemp -d` +curr_repo_name=$(basename `cd ..; pwd; cd - > /dev/null`) # Should be UIWidgets +cp -R ../.. $tmpdir +echo "Switching to $tmpdir/$curr_repo_name" +cd $tmpdir/$curr_repo_name +git reset --hard $target_version # Cleanup any possible local change +if [ "$?" != "0" ]; then + echo "Failed to setup the repo of target version" + exit -1 +fi + +rm -rf $target_repository/Documentation~ +mkdir $target_repository/Documentation~ +sed '/README-ZH.md/d ; s/CONTRIBUTING.md/CONTRIBUTING/g' README.md > $target_repository/Documentation~/com.unity.uiwidgets.md +cp $target_repository/Documentation~/{com.unity.uiwidgets.md,index.md} +cat > $target_repository/Documentation~/CONTRIBUTING.md << END +This repository exists for the sole purpose of publishing to package manager. Please contribute to https://github.com/UnityTech/UIWidgets instead. + +END +cat CONTRIBUTING.md >> $target_repository/Documentation~/CONTRIBUTING.md +sed '/README-ZH.md/d ; s/CONTRIBUTING.md/CONTRIBUTING/g' README-ZH.md > $target_repository/Documentation~/index-zh.md +cat > $target_repository/Documentation~/TableOfContents.md << END +* [UIWidgets Documentation](index) +* [UIWidgets中文文档](index-zh) +END +echo "Copied Documentation" + +rm -rf $target_repository/Runtime +cp -R Runtime $target_repository/ +echo "Copied Runtime" + +rm -rf $target_repository/Runtime/Plugins +rm -rf $target_repository/Runtime/Plugins.meta +rm -rf $target_repository/Runtime/Resources/images +rm -rf $target_repository/Runtime/Resources/images.meta +rm -rf $target_repository/Runtime/Resources/fonts +rm -rf $target_repository/Runtime/Resources/fonts.meta +echo "Removed Resources and Plugins" + +rm -rf $target_repository/Editor +cp -R Editor $target_repository/ +echo "Copied Editor" + +echo "Cloning Samples" +rm -rf $target_repository/Samples +git clone https://github.com/UIWidgets/UIWidgetsSamples.git $target_repository/Samples +rm -rf $target_repository/Samples/.git +echo "Cloned Samples" + +cp README.md* $target_repository +echo "Copied README" + +cd $target_repository +git add . +git commit -m "Update to $curr_repo_name:$target_version" + +echo "Please complete the following steps mannually" +echo +echo "cd $target_repository" +echo "Update CHANGELOG.md, adding entry for the new version." +echo "Update package.json, changing the version number." +echo "Update the resource package in 'Package Resources' folder, if needed." +echo "git commit" +echo "git push" +echo "Visit https://yamato.cds.internal.unity3d.com/jobs/242-com.unity.uiwidgets and wait for the jobs to succeed." +echo "Update the tags:" +echo "git tag rc-x.x.x-preview.x && git push -u origin rc-x.x.x-preview.x" +echo "Waiting for the jobs to succeed" +echo "git tag vx.x.x-preview.x && git push -u origin vx.x.x-preview.x" +echo "Waiting for the jobs to succeed" diff --git a/Scripts/packman.meta b/Scripts/packman.meta new file mode 100644 index 00000000..9a51c69d --- /dev/null +++ b/Scripts/packman.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d8e7ac7f92b26442f8cdfed4f1f766d8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index 5fd9bb09..64b21591 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.uiwidgets", "displayName":"UIWidgets", - "version": "1.5.4-preview.1", + "version": "1.5.4-preview.5", "unity": "2018.4", "description": "UIWidgets allows you to build beautiful cross-platform apps through Unity", "dependencies": {