gewentao
6 年前
当前提交
873f85c2
共有 85 个文件被更改,包括 5320 次插入 和 268 次删除
-
5Assets/UIWidgets/Tests/Menu.cs
-
61Assets/UIWidgets/Tests/Paragraph.cs
-
90Assets/UIWidgets/editor/editor_window.cs
-
57Assets/UIWidgets/foundation/node.cs
-
249Assets/UIWidgets/gestures/arena.cs
-
56Assets/UIWidgets/rendering/binding.cs
-
73Assets/UIWidgets/rendering/box.cs
-
40Assets/UIWidgets/rendering/object.cs
-
124Assets/UIWidgets/rendering/proxy_box.cs
-
2Assets/UIWidgets/rendering/sliver_multi_box_adaptor.cs
-
12Assets/UIWidgets/rendering/view.cs
-
221Assets/UIWidgets/scheduler/binding.cs
-
6Assets/UIWidgets/ui/geometry.cs
-
4Assets/UIWidgets/ui/painting/canvas.cs
-
5Assets/UIWidgets/ui/painting/canvas_impl.cs
-
11Assets/UIWidgets/ui/painting/draw_cmd.cs
-
4Assets/UIWidgets/ui/painting/picture.cs
-
5Assets/UIWidgets/ui/text.cs
-
29Assets/UIWidgets/ui/txt/linebreaker.cs
-
226Assets/UIWidgets/ui/txt/paragraph.cs
-
34Assets/UIWidgets/ui/window.cs
-
5Packages/manifest.json
-
2ProjectSettings/ProjectVersion.txt
-
102Assets/UIWidgets/Tests/Gestures.cs
-
3Assets/UIWidgets/Tests/Gestures.cs.meta
-
8Assets/UIWidgets/async.meta
-
144Assets/UIWidgets/foundation/assertions.cs
-
3Assets/UIWidgets/foundation/assertions.cs.meta
-
52Assets/UIWidgets/foundation/basic_types.cs
-
3Assets/UIWidgets/foundation/basic_types.cs.meta
-
38Assets/UIWidgets/foundation/debug.cs
-
3Assets/UIWidgets/foundation/debug.cs.meta
-
1001Assets/UIWidgets/foundation/diagnostics.cs
-
3Assets/UIWidgets/foundation/diagnostics.cs.meta
-
163Assets/UIWidgets/foundation/node.mixin.gen.cs
-
11Assets/UIWidgets/foundation/node.mixin.gen.cs.meta
-
88Assets/UIWidgets/foundation/node.mixin.njk
-
3Assets/UIWidgets/foundation/node.mixin.njk.meta
-
95Assets/UIWidgets/foundation/print.cs
-
3Assets/UIWidgets/foundation/print.cs.meta
-
8Assets/UIWidgets/gestures.meta
-
11Assets/UIWidgets/gestures/arena.cs.meta
-
100Assets/UIWidgets/gestures/binding.cs
-
3Assets/UIWidgets/gestures/binding.cs.meta
-
21Assets/UIWidgets/gestures/constants.cs
-
3Assets/UIWidgets/gestures/constants.cs.meta
-
142Assets/UIWidgets/gestures/converter.cs
-
3Assets/UIWidgets/gestures/converter.cs.meta
-
90Assets/UIWidgets/gestures/drag_details.cs
-
3Assets/UIWidgets/gestures/drag_details.cs.meta
-
142Assets/UIWidgets/gestures/events.cs
-
3Assets/UIWidgets/gestures/events.cs.meta
-
57Assets/UIWidgets/gestures/hit_test.cs
-
3Assets/UIWidgets/gestures/hit_test.cs.meta
-
186Assets/UIWidgets/gestures/lsq_resolver.cs
-
3Assets/UIWidgets/gestures/lsq_resolver.cs.meta
-
281Assets/UIWidgets/gestures/monodrag.cs
-
3Assets/UIWidgets/gestures/monodrag.cs.meta
-
190Assets/UIWidgets/gestures/multitap.cs
-
3Assets/UIWidgets/gestures/multitap.cs.meta
-
66Assets/UIWidgets/gestures/pointer_router.cs
-
3Assets/UIWidgets/gestures/pointer_router.cs.meta
-
239Assets/UIWidgets/gestures/recognizer.cs
-
3Assets/UIWidgets/gestures/recognizer.cs.meta
-
154Assets/UIWidgets/gestures/tap.cs
-
3Assets/UIWidgets/gestures/tap.cs.meta
-
118Assets/UIWidgets/gestures/team.cs
-
3Assets/UIWidgets/gestures/team.cs.meta
-
214Assets/UIWidgets/gestures/velocity_tracker.cs
-
3Assets/UIWidgets/gestures/velocity_tracker.cs.meta
-
51Assets/UIWidgets/ui/pointer.cs
-
3Assets/UIWidgets/ui/pointer.cs.meta
-
73Assets/UIWidgets/ui/txt/font_manager.cs
-
3Assets/UIWidgets/ui/txt/font_manager.cs.meta
-
119Assets/UIWidgets/ui/txt/mesh.cs
-
3Assets/UIWidgets/ui/txt/mesh.cs.meta
-
13Assets/UIWidgets/ui/txt/utils.cs
-
3Assets/UIWidgets/ui/txt/utils.cs.meta
-
26Assets/UIWidgets/async/microtask_queue.cs
-
3Assets/UIWidgets/async/microtask_queue.cs.meta
-
105Assets/UIWidgets/async/priority_queue.cs
-
3Assets/UIWidgets/async/priority_queue.cs.meta
-
67Assets/UIWidgets/async/timer.cs
-
11Assets/UIWidgets/async/timer.cs.meta
|
|||
namespace UIWidgets.foundation { |
|||
public class AbstractNode { |
|||
public int depth { |
|||
get { return this._depth; } |
|||
} |
|||
|
|||
public int _depth = 0; |
|||
|
|||
public void redepthChild(AbstractNode child) { |
|||
if (child._depth <= this._depth) { |
|||
child._depth = this._depth + 1; |
|||
child.redepthChildren(); |
|||
} |
|||
} |
|||
|
|||
public virtual void redepthChildren() { |
|||
} |
|||
|
|||
public object owner { |
|||
get { return this._owner; } |
|||
} |
|||
|
|||
public object _owner; |
|||
|
|||
public bool attached { |
|||
get { return this._owner != null; } |
|||
} |
|||
|
|||
public virtual void attach(object owner) { |
|||
this._owner = owner; |
|||
} |
|||
|
|||
public virtual void detach() { |
|||
this._owner = null; |
|||
} |
|||
|
|||
public AbstractNode parent { |
|||
get { return this._parent; } |
|||
} |
|||
|
|||
public AbstractNode _parent; |
|||
|
|||
public virtual void adoptChild(AbstractNode child) { |
|||
child._parent = this; |
|||
if (this.attached) { |
|||
child.attach(this._owner); |
|||
} |
|||
|
|||
this.redepthChild(child); |
|||
} |
|||
|
|||
public virtual void dropChild(AbstractNode child) { |
|||
child._parent = null; |
|||
if (this.attached) { |
|||
child.detach(); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
public enum GestureDisposition { |
|||
accepted, |
|||
rejected, |
|||
} |
|||
|
|||
public interface GestureArenaMember { |
|||
void acceptGesture(int pointer); |
|||
void rejectGesture(int pointer); |
|||
} |
|||
|
|||
public class GestureArenaEntry { |
|||
public GestureArenaEntry( |
|||
GestureArenaManager arena = null, |
|||
int pointer = 0, |
|||
GestureArenaMember member = null) { |
|||
this._arena = arena; |
|||
this._pointer = pointer; |
|||
this._member = member; |
|||
} |
|||
|
|||
readonly GestureArenaManager _arena; |
|||
readonly int _pointer; |
|||
readonly GestureArenaMember _member; |
|||
|
|||
public virtual void resolve(GestureDisposition disposition) { |
|||
this._arena._resolve(this._pointer, this._member, disposition); |
|||
} |
|||
} |
|||
|
|||
class _GestureArena { |
|||
public List<GestureArenaMember> members = new List<GestureArenaMember>(); |
|||
public bool isOpen = true; |
|||
public bool isHeld = false; |
|||
public bool hasPendingSweep = false; |
|||
|
|||
public GestureArenaMember eagerWinner; |
|||
|
|||
public void add(GestureArenaMember member) { |
|||
D.assert(this.isOpen); |
|||
this.members.Add(member); |
|||
} |
|||
|
|||
public override string ToString() { |
|||
StringBuilder buffer = new StringBuilder(); |
|||
if (this.members.isEmpty()) { |
|||
buffer.Append("<empty>"); |
|||
} else { |
|||
buffer.Append(string.Join(", ", this.members.Select( |
|||
member => member == this.eagerWinner |
|||
? string.Format("{0} (eager winner)", member) |
|||
: member.ToString()).ToArray())); |
|||
} |
|||
|
|||
if (this.isOpen) { |
|||
buffer.Append(" [open]"); |
|||
} |
|||
|
|||
if (this.isHeld) { |
|||
buffer.Append(" [held]"); |
|||
} |
|||
|
|||
if (this.hasPendingSweep) { |
|||
buffer.Append(" [hasPendingSweep]"); |
|||
} |
|||
|
|||
return buffer.ToString(); |
|||
} |
|||
} |
|||
|
|||
public class GestureArenaManager { |
|||
readonly Dictionary<int, _GestureArena> _arenas = new Dictionary<int, _GestureArena>(); |
|||
readonly Window _window; |
|||
|
|||
public GestureArenaManager(Window window) { |
|||
this._window = window; |
|||
} |
|||
|
|||
public GestureArenaEntry add(int pointer, GestureArenaMember member) { |
|||
_GestureArena state = this._arenas.putIfAbsent(pointer, () => { |
|||
D.assert(this._debugLogDiagnostic(pointer, "★ Opening new gesture arena.")); |
|||
return state = new _GestureArena(); |
|||
}); |
|||
|
|||
state.add(member); |
|||
|
|||
D.assert(this._debugLogDiagnostic(pointer, string.Format("Adding: {0}", member))); |
|||
return new GestureArenaEntry(this, pointer, member); |
|||
} |
|||
|
|||
public void close(int pointer) { |
|||
_GestureArena state; |
|||
if (!this._arenas.TryGetValue(pointer, out state)) { |
|||
return; |
|||
} |
|||
|
|||
state.isOpen = false; |
|||
D.assert(this._debugLogDiagnostic(pointer, "Closing", state)); |
|||
this._tryToResolveArena(pointer, state); |
|||
} |
|||
|
|||
public void sweep(int pointer) { |
|||
_GestureArena state; |
|||
if (!this._arenas.TryGetValue(pointer, out state)) { |
|||
return; |
|||
} |
|||
|
|||
D.assert(!state.isOpen); |
|||
if (state.isHeld) { |
|||
state.hasPendingSweep = true; |
|||
D.assert(this._debugLogDiagnostic(pointer, "Delaying sweep", state)); |
|||
return; |
|||
} |
|||
|
|||
D.assert(this._debugLogDiagnostic(pointer, "Sweeping", state)); |
|||
this._arenas.Remove(pointer); |
|||
if (state.members.isNotEmpty()) { |
|||
D.assert(this._debugLogDiagnostic( |
|||
pointer, string.Format("Winner: {0}", state.members.First()))); |
|||
|
|||
state.members.First().acceptGesture(pointer); |
|||
for (int i = 1; i < state.members.Count; i++) { |
|||
state.members[i].rejectGesture(pointer); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void hold(int pointer) { |
|||
_GestureArena state; |
|||
if (!this._arenas.TryGetValue(pointer, out state)) { |
|||
return; |
|||
} |
|||
|
|||
state.isHeld = true; |
|||
D.assert(this._debugLogDiagnostic(pointer, "Holding", state)); |
|||
} |
|||
|
|||
public void release(int pointer) { |
|||
_GestureArena state; |
|||
if (!this._arenas.TryGetValue(pointer, out state)) { |
|||
return; |
|||
} |
|||
|
|||
state.isHeld = false; |
|||
D.assert(this._debugLogDiagnostic(pointer, "Releasing", state)); |
|||
if (state.hasPendingSweep) { |
|||
this.sweep(pointer); |
|||
} |
|||
} |
|||
|
|||
internal void _resolve(int pointer, GestureArenaMember member, GestureDisposition disposition) { |
|||
_GestureArena state; |
|||
if (!this._arenas.TryGetValue(pointer, out state)) { |
|||
return; |
|||
} |
|||
|
|||
D.assert(this._debugLogDiagnostic(pointer, |
|||
string.Format("{0}: {1}", |
|||
disposition == GestureDisposition.accepted ? "Accepting" : "Rejecting", |
|||
member))); |
|||
|
|||
D.assert(state.members.Contains(member)); |
|||
if (disposition == GestureDisposition.rejected) { |
|||
state.members.Remove(member); |
|||
member.rejectGesture(pointer); |
|||
if (!state.isOpen) { |
|||
this._tryToResolveArena(pointer, state); |
|||
} |
|||
} else { |
|||
if (state.isOpen) { |
|||
state.eagerWinner = state.eagerWinner ?? member; |
|||
} else { |
|||
D.assert(this._debugLogDiagnostic(pointer, |
|||
string.Format("Self-declared winner: {0}", member))); |
|||
this._resolveInFavorOf(pointer, state, member); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void _tryToResolveArena(int pointer, _GestureArena state) { |
|||
D.assert(this._arenas[pointer] == state); |
|||
D.assert(!state.isOpen); |
|||
|
|||
if (state.members.Count == 1) { |
|||
this._window.scheduleMicrotask(() => this._resolveByDefault(pointer, state)); |
|||
} else if (state.members.isEmpty()) { |
|||
this._arenas.Remove(pointer); |
|||
D.assert(this._debugLogDiagnostic(pointer, "Arena empty.")); |
|||
} else if (state.eagerWinner != null) { |
|||
D.assert(this._debugLogDiagnostic(pointer, |
|||
string.Format("Eager winner: {0}", state.eagerWinner))); |
|||
this._resolveInFavorOf(pointer, state, state.eagerWinner); |
|||
} |
|||
} |
|||
|
|||
void _resolveByDefault(int pointer, _GestureArena state) { |
|||
if (!this._arenas.ContainsKey(pointer)) { |
|||
return; |
|||
} |
|||
|
|||
D.assert(this._arenas[pointer] == state); |
|||
D.assert(!state.isOpen); |
|||
List<GestureArenaMember> members = state.members; |
|||
D.assert(members.Count == 1); |
|||
this._arenas.Remove(pointer); |
|||
D.assert(this._debugLogDiagnostic(pointer, |
|||
string.Format("Default winner: {0}", state.members.First()))); |
|||
state.members.First().acceptGesture(pointer); |
|||
} |
|||
|
|||
void _resolveInFavorOf(int pointer, _GestureArena state, GestureArenaMember member) { |
|||
D.assert(state == this._arenas[pointer]); |
|||
D.assert(state != null); |
|||
D.assert(state.eagerWinner == null || state.eagerWinner == member); |
|||
D.assert(!state.isOpen); |
|||
|
|||
this._arenas.Remove(pointer); |
|||
foreach (GestureArenaMember rejectedMember in state.members) { |
|||
if (rejectedMember != member) { |
|||
rejectedMember.rejectGesture(pointer); |
|||
} |
|||
} |
|||
|
|||
member.acceptGesture(pointer); |
|||
} |
|||
|
|||
bool _debugLogDiagnostic(int pointer, string message, _GestureArena state = null) { |
|||
D.assert(() => { |
|||
if (D.debugPrintGestureArenaDiagnostics) { |
|||
int? count = state != null ? state.members.Count : (int?) null; |
|||
string s = count != 1 ? "s" : ""; |
|||
Debug.LogFormat("Gesture arena {0} ❙ {1}{2}", |
|||
pointer.ToString().PadRight(4), |
|||
message, |
|||
count != null ? string.Format(" with {0} member{1}.", count, s) : ""); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|
|||
m_EditorVersion: 2018.2.4f1 |
|||
m_EditorVersion: 2018.3.0a9 |
|
|||
using System; |
|||
using System.Linq; |
|||
using UIWidgets.editor; |
|||
using UIWidgets.gestures; |
|||
using UIWidgets.painting; |
|||
using UIWidgets.rendering; |
|||
using UIWidgets.ui; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using Color = UIWidgets.ui.Color; |
|||
|
|||
namespace UIWidgets.Tests { |
|||
public class Gestures : EditorWindow { |
|||
private readonly Func<RenderBox>[] _options; |
|||
|
|||
private readonly string[] _optionStrings; |
|||
|
|||
private int _selected; |
|||
|
|||
Gestures() { |
|||
this._options = new Func<RenderBox>[] { |
|||
this.tap, |
|||
}; |
|||
this._optionStrings = this._options.Select(x => x.Method.Name).ToArray(); |
|||
this._selected = 0; |
|||
|
|||
this.titleContent = new GUIContent("Gestures"); |
|||
} |
|||
|
|||
private WindowAdapter windowAdapter; |
|||
|
|||
private RendererBindings rendererBindings; |
|||
|
|||
[NonSerialized] private bool hasInvoked = false; |
|||
|
|||
void OnGUI() { |
|||
var selected = EditorGUILayout.Popup("test case", this._selected, this._optionStrings); |
|||
if (selected != this._selected || !this.hasInvoked) { |
|||
this._selected = selected; |
|||
this.hasInvoked = true; |
|||
|
|||
var renderBox = this._options[this._selected](); |
|||
this.rendererBindings.setRoot(renderBox); |
|||
} |
|||
|
|||
if (this.windowAdapter != null) { |
|||
this.windowAdapter.OnGUI(); |
|||
} |
|||
} |
|||
|
|||
void Update() { |
|||
if (this.windowAdapter != null) { |
|||
this.windowAdapter.Update(); |
|||
} |
|||
} |
|||
|
|||
private void OnEnable() { |
|||
this.windowAdapter = new WindowAdapter(this); |
|||
this.rendererBindings = new RendererBindings(this.windowAdapter); |
|||
|
|||
this._tapRecognizer = new TapGestureRecognizer(this.rendererBindings.rendererBinding); |
|||
this._tapRecognizer.onTap = () => { Debug.Log("tap"); }; |
|||
|
|||
this._panRecognizer = new PanGestureRecognizer(this.rendererBindings.rendererBinding); |
|||
this._panRecognizer.onUpdate = (details) => { Debug.Log("onUpdate " + details); }; |
|||
|
|||
this._doubleTapGesture = new DoubleTapGestureRecognizer(this.rendererBindings.rendererBinding); |
|||
this._doubleTapGesture.onDoubleTap = () => { Debug.Log("onDoubleTap"); }; |
|||
} |
|||
|
|||
void OnDestroy() { |
|||
this.windowAdapter = null; |
|||
this.rendererBindings = null; |
|||
} |
|||
|
|||
TapGestureRecognizer _tapRecognizer; |
|||
|
|||
PanGestureRecognizer _panRecognizer; |
|||
|
|||
DoubleTapGestureRecognizer _doubleTapGesture; |
|||
|
|||
void _handlePointerDown(PointerDownEvent evt) { |
|||
this._tapRecognizer.addPointer(evt); |
|||
this._panRecognizer.addPointer(evt); |
|||
this._doubleTapGesture.addPointer(evt); |
|||
} |
|||
|
|||
RenderBox tap() { |
|||
return new RenderPointerListener( |
|||
onPointerDown: this._handlePointerDown, |
|||
behavior: HitTestBehavior.opaque, |
|||
child: new RenderConstrainedBox( |
|||
additionalConstraints: BoxConstraints.tight(Size.square(100)), |
|||
child: new RenderDecoratedBox( |
|||
decoration: new BoxDecoration( |
|||
color: new Color(0xFF00FF00) |
|||
) |
|||
)) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b6e93a3e08aa4ed49ebe367645f04fd9 |
|||
timeCreated: 1536304600 |
|
|||
fileFormatVersion: 2 |
|||
guid: d164f4ad25c6f4b51b5f3e6a945a7296 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Debug = UnityEngine.Debug; |
|||
|
|||
namespace UIWidgets.foundation { |
|||
public delegate void UIWidgetsExceptionHandler(UIWidgetsErrorDetails details); |
|||
|
|||
public delegate void InformationCollector(StringBuilder information); |
|||
|
|||
public class UIWidgetsErrorDetails { |
|||
public UIWidgetsErrorDetails( |
|||
Exception exception = null, |
|||
string library = "UIWidgets framework", |
|||
string context = null, |
|||
EnumerableFilter<String> stackFilter = null, |
|||
InformationCollector informationCollector = null, |
|||
bool silent = false |
|||
) { |
|||
this.exception = exception; |
|||
this.library = library; |
|||
this.context = context; |
|||
this.stackFilter = stackFilter; |
|||
this.informationCollector = informationCollector; |
|||
this.silent = silent; |
|||
} |
|||
|
|||
public readonly Exception exception; |
|||
public readonly string library; |
|||
public readonly string context; |
|||
public readonly EnumerableFilter<String> stackFilter; |
|||
public readonly InformationCollector informationCollector; |
|||
public readonly bool silent; |
|||
|
|||
public string exceptionAsString() { |
|||
string longMessage = null; |
|||
|
|||
if (this.exception != null) { |
|||
longMessage = this.exception.Message; |
|||
} |
|||
|
|||
if (longMessage != null) { |
|||
longMessage = longMessage.TrimEnd(); |
|||
} |
|||
|
|||
if (longMessage.isEmpty()) { |
|||
longMessage = "<no message available>"; |
|||
} |
|||
|
|||
return longMessage; |
|||
} |
|||
|
|||
public override string ToString() { |
|||
var buffer = new StringBuilder(); |
|||
if (this.library.isNotEmpty() || this.context.isNotEmpty()) { |
|||
if (this.library.isNotEmpty()) { |
|||
buffer.AppendFormat("Error caught by {0}", this.library); |
|||
if (this.context.isNotEmpty()) { |
|||
buffer.Append(", "); |
|||
} |
|||
} else { |
|||
buffer.Append("Exception "); |
|||
} |
|||
|
|||
if (this.context.isNotEmpty()) { |
|||
buffer.AppendFormat("thrown {0}", this.context); |
|||
} |
|||
|
|||
buffer.Append(". "); |
|||
} else { |
|||
buffer.Append("An error was caught. "); |
|||
} |
|||
|
|||
buffer.AppendLine(this.exceptionAsString()); |
|||
if (this.informationCollector != null) { |
|||
this.informationCollector(buffer); |
|||
} |
|||
|
|||
if (this.exception.StackTrace != null) { |
|||
IEnumerable<string> stackLines = this.exception.StackTrace.TrimEnd().Split('\n'); |
|||
if (this.stackFilter != null) { |
|||
stackLines = this.stackFilter(stackLines); |
|||
} else { |
|||
stackLines = UIWidgetsError.defaultStackFilter(stackLines); |
|||
} |
|||
|
|||
buffer.Append(string.Join("\n", stackLines.ToArray())); |
|||
} |
|||
|
|||
return buffer.ToString().TrimEnd(); |
|||
} |
|||
} |
|||
|
|||
public class UIWidgetsError : AssertionError { |
|||
public UIWidgetsError(string message) : base(message) { |
|||
} |
|||
|
|||
public static UIWidgetsExceptionHandler onError = dumpErrorToConsole; |
|||
|
|||
static int _errorCount = 0; |
|||
|
|||
public static void resetErrorCount() { |
|||
_errorCount = 0; |
|||
} |
|||
|
|||
public static void dumpErrorToConsole(UIWidgetsErrorDetails details) { |
|||
dumpErrorToConsole(details, forceReport: false); |
|||
} |
|||
|
|||
public static void dumpErrorToConsole(UIWidgetsErrorDetails details, bool forceReport = false) { |
|||
D.assert(details != null); |
|||
D.assert(details.exception != null); |
|||
bool reportError = !details.silent; |
|||
D.assert(() => { |
|||
reportError = true; |
|||
return true; |
|||
}); |
|||
if (!reportError && !forceReport) { |
|||
return; |
|||
} |
|||
|
|||
if (_errorCount == 0 || forceReport) { |
|||
Debug.Log(details.ToString()); |
|||
} else { |
|||
Debug.Log("Another exception was thrown: " + details.exceptionAsString()); |
|||
} |
|||
|
|||
_errorCount += 1; |
|||
} |
|||
|
|||
public static IEnumerable<string> defaultStackFilter(IEnumerable<string> frames) { |
|||
return frames; |
|||
} |
|||
|
|||
public static void reportError(UIWidgetsErrorDetails details) { |
|||
D.assert(details != null); |
|||
D.assert(details.exception != null); |
|||
if (onError != null) { |
|||
onError(details); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b1c014fd2b864fe696ffb29b063921b4 |
|||
timeCreated: 1536501637 |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UIWidgets.foundation { |
|||
public delegate IEnumerable<T> EnumerableFilter<T>(IEnumerable<T> input); |
|||
|
|||
public static class CollectionUtils { |
|||
public static V putIfAbsent<K, V>(this IDictionary<K, V> it, K key, Func<V> ifAbsent) { |
|||
V value; |
|||
if (it.TryGetValue(key, out value)) { |
|||
return value; |
|||
} |
|||
|
|||
value = ifAbsent(); |
|||
it[key] = value; |
|||
return value; |
|||
} |
|||
|
|||
public static bool isEmpty<T>(this ICollection<T> it) { |
|||
return it.Count == 0; |
|||
} |
|||
|
|||
public static bool isNotEmpty<T>(this ICollection<T> it) { |
|||
return it.Count != 0; |
|||
} |
|||
|
|||
public static bool isEmpty<T>(this Queue<T> it) { |
|||
return it.Count == 0; |
|||
} |
|||
|
|||
public static bool isNotEmpty<T>(this Queue<T> it) { |
|||
return it.Count != 0; |
|||
} |
|||
|
|||
public static bool isEmpty<TKey, TValue>(this IDictionary<TKey, TValue> it) { |
|||
return it.Count == 0; |
|||
} |
|||
|
|||
public static bool isNotEmpty<TKey, TValue>(this IDictionary<TKey, TValue> it) { |
|||
return it.Count != 0; |
|||
} |
|||
|
|||
public static bool isEmpty(this string it) { |
|||
return string.IsNullOrEmpty(it); |
|||
} |
|||
|
|||
public static bool isNotEmpty(this string it) { |
|||
return !string.IsNullOrEmpty(it); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5677c530963d4d59baef909ab962ab44 |
|||
timeCreated: 1535605561 |
|
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
namespace UIWidgets.foundation { |
|||
public static class D { |
|||
[Conditional("UIWidgets_DEBUG")] |
|||
public static void assert(Func<bool> result, string message = null) { |
|||
D.assert(result(), message); |
|||
} |
|||
|
|||
[Conditional("UIWidgets_DEBUG")] |
|||
public static void assert(bool result, string message = null) { |
|||
if (!result) { |
|||
throw new AssertionError(message); |
|||
} |
|||
} |
|||
|
|||
public static bool debugPrintGestureArenaDiagnostics = true; |
|||
|
|||
public static bool debugPrintHitTestResults = true; |
|||
|
|||
public static bool debugPaintPointersEnabled = false; |
|||
|
|||
public static bool debugPrintRecognizerCallbacksTrace = true; |
|||
|
|||
public static bool debugPrintBeginFrameBanner = true; |
|||
|
|||
public static bool debugPrintEndFrameBanner = true; |
|||
|
|||
public static bool debugPrintScheduleFrameStacks = true; |
|||
} |
|||
|
|||
[Serializable] |
|||
public class AssertionError : Exception { |
|||
public AssertionError(string message) : base(message) { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 89e39060aaba41798359de8d7e6a013e |
|||
timeCreated: 1536123374 |
1001
Assets/UIWidgets/foundation/diagnostics.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 16aaa3f0f74a4188b49c27f602b4341d |
|||
timeCreated: 1536203150 |
|
|||
namespace UIWidgets.foundation { |
|||
|
|||
|
|||
|
|||
|
|||
public class AbstractNode { |
|||
public int depth { |
|||
get { return this._depth; } |
|||
} |
|||
|
|||
int _depth = 0; |
|||
|
|||
protected void redepthChild(AbstractNode child) { |
|||
D.assert(child.owner == this.owner); |
|||
if (child._depth <= this._depth) { |
|||
child._depth = this._depth + 1; |
|||
child.redepthChildren(); |
|||
} |
|||
} |
|||
|
|||
public virtual void redepthChildren() { |
|||
} |
|||
|
|||
public object owner { |
|||
get { return this._owner; } |
|||
} |
|||
|
|||
object _owner; |
|||
|
|||
public bool attached { |
|||
get { return this._owner != null; } |
|||
} |
|||
|
|||
public virtual void attach(object owner) { |
|||
D.assert(owner != null); |
|||
D.assert(this._owner == null); |
|||
this._owner = owner; |
|||
} |
|||
|
|||
public virtual void detach() { |
|||
D.assert(this._owner != null); |
|||
this._owner = null; |
|||
} |
|||
|
|||
public AbstractNode parent { |
|||
get { return this._parent; } |
|||
} |
|||
|
|||
AbstractNode _parent; |
|||
|
|||
protected virtual void adoptChild(AbstractNode child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == null); |
|||
D.assert(() => { |
|||
var node = this; |
|||
while (node.parent != null) |
|||
node = node.parent; |
|||
D.assert(node != child); // indicates we are about to create a cycle
|
|||
return true; |
|||
}); |
|||
|
|||
child._parent = this; |
|||
if (this.attached) { |
|||
child.attach(this._owner); |
|||
} |
|||
|
|||
this.redepthChild(child); |
|||
} |
|||
|
|||
protected virtual void dropChild(AbstractNode child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == this); |
|||
D.assert(child.attached == this.attached); |
|||
|
|||
child._parent = null; |
|||
if (this.attached) { |
|||
child.detach(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
public class AbstractNodeMixinDiagnosticableTree : DiagnosticableTree { |
|||
public int depth { |
|||
get { return this._depth; } |
|||
} |
|||
|
|||
int _depth = 0; |
|||
|
|||
protected void redepthChild(AbstractNodeMixinDiagnosticableTree child) { |
|||
D.assert(child.owner == this.owner); |
|||
if (child._depth <= this._depth) { |
|||
child._depth = this._depth + 1; |
|||
child.redepthChildren(); |
|||
} |
|||
} |
|||
|
|||
public virtual void redepthChildren() { |
|||
} |
|||
|
|||
public object owner { |
|||
get { return this._owner; } |
|||
} |
|||
|
|||
object _owner; |
|||
|
|||
public bool attached { |
|||
get { return this._owner != null; } |
|||
} |
|||
|
|||
public virtual void attach(object owner) { |
|||
D.assert(owner != null); |
|||
D.assert(this._owner == null); |
|||
this._owner = owner; |
|||
} |
|||
|
|||
public virtual void detach() { |
|||
D.assert(this._owner != null); |
|||
this._owner = null; |
|||
} |
|||
|
|||
public AbstractNodeMixinDiagnosticableTree parent { |
|||
get { return this._parent; } |
|||
} |
|||
|
|||
AbstractNodeMixinDiagnosticableTree _parent; |
|||
|
|||
protected virtual void adoptChild(AbstractNodeMixinDiagnosticableTree child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == null); |
|||
D.assert(() => { |
|||
var node = this; |
|||
while (node.parent != null) |
|||
node = node.parent; |
|||
D.assert(node != child); // indicates we are about to create a cycle
|
|||
return true; |
|||
}); |
|||
|
|||
child._parent = this; |
|||
if (this.attached) { |
|||
child.attach(this._owner); |
|||
} |
|||
|
|||
this.redepthChild(child); |
|||
} |
|||
|
|||
protected virtual void dropChild(AbstractNodeMixinDiagnosticableTree child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == this); |
|||
D.assert(child.attached == this.attached); |
|||
|
|||
child._parent = null; |
|||
if (this.attached) { |
|||
child.detach(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b7a18c2b3beb04ccba0b8585c1cdb618 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
namespace UIWidgets.foundation { |
|||
{% macro AbstractNodeMixin(with) %} |
|||
{% set className = 'AbstractNode' if with == '' else 'AbstractNodeMixin' + with %} |
|||
|
|||
{% set extends = '' if with == '' else ' : ' + with %} |
|||
|
|||
public class {{className}} {{extends}} { |
|||
public int depth { |
|||
get { return this._depth; } |
|||
} |
|||
|
|||
int _depth = 0; |
|||
|
|||
protected void redepthChild({{className}} child) { |
|||
D.assert(child.owner == this.owner); |
|||
if (child._depth <= this._depth) { |
|||
child._depth = this._depth + 1; |
|||
child.redepthChildren(); |
|||
} |
|||
} |
|||
|
|||
public virtual void redepthChildren() { |
|||
} |
|||
|
|||
public object owner { |
|||
get { return this._owner; } |
|||
} |
|||
|
|||
object _owner; |
|||
|
|||
public bool attached { |
|||
get { return this._owner != null; } |
|||
} |
|||
|
|||
public virtual void attach(object owner) { |
|||
D.assert(owner != null); |
|||
D.assert(this._owner == null); |
|||
this._owner = owner; |
|||
} |
|||
|
|||
public virtual void detach() { |
|||
D.assert(this._owner != null); |
|||
this._owner = null; |
|||
} |
|||
|
|||
public {{className}} parent { |
|||
get { return this._parent; } |
|||
} |
|||
|
|||
{{className}} _parent; |
|||
|
|||
protected virtual void adoptChild({{className}} child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == null); |
|||
D.assert(() => { |
|||
var node = this; |
|||
while (node.parent != null) |
|||
node = node.parent; |
|||
D.assert(node != child); // indicates we are about to create a cycle |
|||
return true; |
|||
}); |
|||
|
|||
child._parent = this; |
|||
if (this.attached) { |
|||
child.attach(this._owner); |
|||
} |
|||
|
|||
this.redepthChild(child); |
|||
} |
|||
|
|||
protected virtual void dropChild({{className}} child) { |
|||
D.assert(child != null); |
|||
D.assert(child._parent == this); |
|||
D.assert(child.attached == this.attached); |
|||
|
|||
child._parent = null; |
|||
if (this.attached) { |
|||
child.detach(); |
|||
} |
|||
} |
|||
} |
|||
{% endmacro %} |
|||
|
|||
{{ AbstractNodeMixin('') }} |
|||
|
|||
{{ AbstractNodeMixin('DiagnosticableTree') }} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3169ab76e1bc45cf92b23f07f2688b62 |
|||
timeCreated: 1536301833 |
|
|||
using System.Collections.Generic; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace UIWidgets.foundation { |
|||
enum _WordWrapParseMode { |
|||
inSpace, |
|||
inWord, |
|||
atBreak |
|||
} |
|||
|
|||
public static class DebugPrint { |
|||
static readonly Regex _indentPattern = new Regex("^ *(?:[-+*] |[0-9]+[.):] )?"); |
|||
|
|||
public static IEnumerable<string> debugWordWrap(string message, int width, string wrapIndent = "") { |
|||
if (message.Length < width || message.TrimStart()[0] == '#') { |
|||
yield return message; |
|||
yield break; |
|||
} |
|||
|
|||
Match prefixMatch = _indentPattern.Match(message); |
|||
string prefix = wrapIndent + new string(' ', prefixMatch.Groups[0].Value.Length); |
|||
int start = 0; |
|||
int startForLengthCalculations = 0; |
|||
bool addPrefix = false; |
|||
int index = prefix.Length; |
|||
_WordWrapParseMode mode = _WordWrapParseMode.inSpace; |
|||
int lastWordStart = 0; |
|||
int? lastWordEnd = null; |
|||
while (true) { |
|||
switch (mode) { |
|||
case _WordWrapParseMode.inSpace: |
|||
// at start of break point (or start of line); can"t break until next break
|
|||
while ((index < message.Length) && (message[index] == ' ')) { |
|||
index += 1; |
|||
} |
|||
|
|||
lastWordStart = index; |
|||
mode = _WordWrapParseMode.inWord; |
|||
break; |
|||
case _WordWrapParseMode.inWord: // looking for a good break point
|
|||
while ((index < message.Length) && (message[index] != ' ')) { |
|||
index += 1; |
|||
} |
|||
|
|||
mode = _WordWrapParseMode.atBreak; |
|||
break; |
|||
case _WordWrapParseMode.atBreak: // at start of break point
|
|||
if ((index - startForLengthCalculations > width) || (index == message.Length)) { |
|||
// we are over the width line, so break
|
|||
if ((index - startForLengthCalculations <= width) || (lastWordEnd == null)) { |
|||
// we should use this point, because either it doesn"t actually go over the
|
|||
// end (last line), or it does, but there was no earlier break point
|
|||
lastWordEnd = index; |
|||
} |
|||
|
|||
if (addPrefix) { |
|||
yield return prefix + message.Substring(start, lastWordEnd.Value - start); |
|||
} else { |
|||
yield return message.Substring(start, lastWordEnd.Value - start); |
|||
addPrefix = true; |
|||
} |
|||
|
|||
if (lastWordEnd >= message.Length) |
|||
yield break; |
|||
// just yield returned a line
|
|||
if (lastWordEnd == index) { |
|||
// we broke at current position
|
|||
// eat all the spaces, then set our start point
|
|||
while ((index < message.Length) && (message[index] == ' ')) |
|||
index += 1; |
|||
start = index; |
|||
mode = _WordWrapParseMode.inWord; |
|||
} else { |
|||
// we broke at the previous break point, and we"re at the start of a new one
|
|||
D.assert(lastWordStart > lastWordEnd); |
|||
start = lastWordStart; |
|||
mode = _WordWrapParseMode.atBreak; |
|||
} |
|||
|
|||
startForLengthCalculations = start - prefix.Length; |
|||
D.assert(addPrefix); |
|||
lastWordEnd = null; |
|||
} else { |
|||
// save this break point, we"re not yet over the line width
|
|||
lastWordEnd = index; |
|||
// skip to the end of this break point
|
|||
mode = _WordWrapParseMode.inSpace; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3c7a659f877d47afae10826010956757 |
|||
timeCreated: 1536214859 |
|
|||
fileFormatVersion: 2 |
|||
guid: 8617cc0ea80d74e80a0579e70d4e2d2c |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 8f1294e5215474e2d9ff98330207760e |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.scheduler; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public class GestureBinding : SchedulerBinding, HitTestable, HitTestDispatcher, HitTestTarget { |
|||
public GestureBinding(Window window) : base(window) { |
|||
this.window.onPointerEvent += this._handlePointerDataPacket; |
|||
|
|||
this.gestureArena = new GestureArenaManager(window); |
|||
} |
|||
|
|||
readonly Queue<PointerEvent> _pendingPointerEvents = new Queue<PointerEvent>(); |
|||
|
|||
void _handlePointerDataPacket(PointerDataPacket packet) { |
|||
foreach (var pointerEvent in PointerEventConverter.expand(packet.data, this.window.devicePixelRatio)) { |
|||
this._pendingPointerEvents.Enqueue(pointerEvent); |
|||
} |
|||
|
|||
this._flushPointerEventQueue(); |
|||
} |
|||
|
|||
public void cancelPointer(int pointer) { |
|||
if (this._pendingPointerEvents.isEmpty()) { |
|||
this.window.scheduleMicrotask(this._flushPointerEventQueue); |
|||
} |
|||
|
|||
this._pendingPointerEvents.Enqueue( |
|||
new PointerCancelEvent(timeStamp: DateTime.Now, pointer: pointer)); |
|||
} |
|||
|
|||
void _flushPointerEventQueue() { |
|||
while (this._pendingPointerEvents.Count != 0) { |
|||
this._handlePointerEvent(this._pendingPointerEvents.Dequeue()); |
|||
} |
|||
} |
|||
|
|||
public readonly PointerRouter pointerRouter = new PointerRouter(); |
|||
|
|||
public readonly GestureArenaManager gestureArena; |
|||
|
|||
public readonly Dictionary<int, HitTestResult> _hitTests = new Dictionary<int, HitTestResult>(); |
|||
|
|||
void _handlePointerEvent(PointerEvent evt) { |
|||
HitTestResult result; |
|||
if (evt is PointerDownEvent) { |
|||
D.assert(!this._hitTests.ContainsKey(evt.pointer)); |
|||
result = new HitTestResult(); |
|||
this.hitTest(result, evt.position); |
|||
|
|||
this._hitTests[evt.pointer] = result; |
|||
D.assert(() => { |
|||
if (D.debugPrintHitTestResults) { |
|||
Debug.LogFormat("{0}: {1}", evt, result); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
} else if (evt is PointerUpEvent || evt is PointerCancelEvent) { |
|||
result = this._hitTests[evt.pointer]; |
|||
this._hitTests.Remove(evt.pointer); |
|||
} else if (evt.down) { |
|||
result = this._hitTests[evt.pointer]; |
|||
} else { |
|||
return; |
|||
} |
|||
|
|||
if (result != null) { |
|||
this.dispatchEvent(evt, result); |
|||
} |
|||
} |
|||
|
|||
public virtual void hitTest(HitTestResult result, Offset position) { |
|||
result.add(new HitTestEntry(this)); |
|||
} |
|||
|
|||
public void dispatchEvent(PointerEvent evt, HitTestResult result) { |
|||
foreach (HitTestEntry entry in result.path) { |
|||
try { |
|||
entry.target.handleEvent(evt, entry); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogError("Error while dispatching a pointer event: " + ex); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void handleEvent(PointerEvent evt, HitTestEntry entry) { |
|||
this.pointerRouter.route(evt); |
|||
if (evt is PointerDownEvent) { |
|||
this.gestureArena.close(evt.pointer); |
|||
} else if (evt is PointerUpEvent) { |
|||
this.gestureArena.sweep(evt.pointer); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4cdb394be98c414fb19a438686b949c1 |
|||
timeCreated: 1535609218 |
|
|||
using System; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public static class Constants { |
|||
public const double kTouchSlop = 18.0; |
|||
|
|||
public const double kDoubleTapTouchSlop = kTouchSlop; |
|||
|
|||
public const double kDoubleTapSlop = 100.0; |
|||
|
|||
public const double kPanSlop = kTouchSlop * 2.0; |
|||
|
|||
public static readonly TimeSpan kPressTimeout = new TimeSpan(0, 0, 0, 0, 100); |
|||
|
|||
public static readonly TimeSpan kDoubleTapTimeout = new TimeSpan(0, 0, 0, 0, 300); |
|||
|
|||
public const double kMinFlingVelocity = 50.0; |
|||
|
|||
public const double kMaxFlingVelocity = 8000.0; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: fe1823eee58c44999e824c526724caa3 |
|||
timeCreated: 1535608065 |
|
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
class _PointerState { |
|||
public _PointerState(Offset lastPosition) { |
|||
this.lastPosition = lastPosition ?? Offset.zero; |
|||
} |
|||
|
|||
public int pointer { |
|||
get { return this._pointer; } |
|||
} |
|||
|
|||
int _pointer; |
|||
|
|||
static int _pointerCount = 0; |
|||
|
|||
public void startNewPointer() { |
|||
_PointerState._pointerCount += 1; |
|||
this._pointer = _PointerState._pointerCount; |
|||
} |
|||
|
|||
public bool down { |
|||
get { return this._down; } |
|||
} |
|||
|
|||
bool _down = false; |
|||
|
|||
public void setDown() { |
|||
D.assert(!this._down); |
|||
this._down = true; |
|||
} |
|||
|
|||
public void setUp() { |
|||
D.assert(this._down); |
|||
this._down = false; |
|||
} |
|||
|
|||
public Offset lastPosition; |
|||
|
|||
public override string ToString() { |
|||
return string.Format("_PointerState(pointer: {0}, down: {1}, lastPosition: {2})", |
|||
this.pointer, this.down, this.lastPosition); |
|||
} |
|||
} |
|||
|
|||
public static class PointerEventConverter { |
|||
static readonly Dictionary<int, _PointerState> _pointers = new Dictionary<int, _PointerState>(); |
|||
|
|||
static _PointerState _ensureStateForPointer(PointerData datum, Offset position) { |
|||
return _pointers.putIfAbsent( |
|||
datum.device, |
|||
() => new _PointerState(position)); |
|||
} |
|||
|
|||
public static IEnumerable<PointerEvent> expand(IEnumerable<PointerData> data, double devicePixelRatio) { |
|||
foreach (PointerData datum in data) { |
|||
var position = new Offset(datum.physicalX, datum.physicalY) / devicePixelRatio; |
|||
var timeStamp = datum.timeStamp; |
|||
var kind = datum.kind; |
|||
|
|||
switch (datum.change) { |
|||
case PointerChange.down: { |
|||
_PointerState state = _ensureStateForPointer(datum, position); |
|||
D.assert(!state.down); |
|||
if (state.lastPosition != position) { |
|||
// a hover event to be here.
|
|||
state.lastPosition = position; |
|||
} |
|||
state.startNewPointer(); |
|||
state.setDown(); |
|||
yield return new PointerDownEvent( |
|||
timeStamp: timeStamp, |
|||
pointer: state.pointer, |
|||
kind: kind, |
|||
device: datum.device, |
|||
position: position |
|||
); |
|||
} |
|||
break; |
|||
case PointerChange.move: { |
|||
D.assert(_pointers.ContainsKey(datum.device)); |
|||
_PointerState state = _pointers[datum.device]; |
|||
D.assert(state.down); |
|||
Offset offset = position - state.lastPosition; |
|||
state.lastPosition = position; |
|||
yield return new PointerMoveEvent( |
|||
timeStamp: timeStamp, |
|||
pointer: state.pointer, |
|||
kind: kind, |
|||
device: datum.device, |
|||
position: position, |
|||
delta: offset |
|||
); |
|||
} |
|||
break; |
|||
case PointerChange.up: |
|||
case PointerChange.cancel: { |
|||
D.assert(_pointers.ContainsKey(datum.device)); |
|||
_PointerState state = _pointers[datum.device]; |
|||
D.assert(state.down); |
|||
if (position != state.lastPosition) { |
|||
Offset offset = position - state.lastPosition; |
|||
state.lastPosition = position; |
|||
yield return new PointerMoveEvent( |
|||
timeStamp: timeStamp, |
|||
pointer: state.pointer, |
|||
kind: kind, |
|||
device: datum.device, |
|||
position: position, |
|||
delta: offset, |
|||
synthesized: true |
|||
); |
|||
} |
|||
|
|||
D.assert(position == state.lastPosition); |
|||
state.setUp(); |
|||
if (datum.change == PointerChange.up) { |
|||
yield return new PointerUpEvent( |
|||
timeStamp: timeStamp, |
|||
pointer: state.pointer, |
|||
kind: kind, |
|||
device: datum.device, |
|||
position: position |
|||
); |
|||
} else { |
|||
yield return new PointerCancelEvent( |
|||
timeStamp: timeStamp, |
|||
pointer: state.pointer, |
|||
kind: kind, |
|||
device: datum.device, |
|||
position: position |
|||
); |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 929719430a6b48a4b9639a1bb8b37e88 |
|||
timeCreated: 1536051892 |
|
|||
using System; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public class DragDownDetails { |
|||
public DragDownDetails( |
|||
Offset globalPosition = null |
|||
) { |
|||
this.globalPosition = globalPosition ?? Offset.zero; |
|||
} |
|||
|
|||
public readonly Offset globalPosition; |
|||
|
|||
public override string ToString() { |
|||
return this.GetType() + "(" + this.globalPosition + ")"; |
|||
} |
|||
} |
|||
|
|||
public delegate void GestureDragDownCallback(DragDownDetails details); |
|||
|
|||
public class DragStartDetails { |
|||
public DragStartDetails(DateTime sourceTimeStamp, Offset globalPosition = null) { |
|||
this.sourceTimeStamp = sourceTimeStamp; |
|||
this.globalPosition = globalPosition ?? Offset.zero; |
|||
} |
|||
|
|||
public readonly DateTime sourceTimeStamp; |
|||
|
|||
public readonly Offset globalPosition; |
|||
|
|||
public override string ToString() { |
|||
return this.GetType() + "(" + this.globalPosition + ")"; |
|||
} |
|||
} |
|||
|
|||
public delegate void GestureDragStartCallback(DragStartDetails details); |
|||
|
|||
public class DragUpdateDetails { |
|||
public DragUpdateDetails( |
|||
DateTime sourceTimeStamp, |
|||
Offset delta = null, |
|||
double? primaryDelta = null, |
|||
Offset globalPosition = null) { |
|||
this.sourceTimeStamp = sourceTimeStamp; |
|||
this.delta = delta ?? Offset.zero; |
|||
this.primaryDelta = primaryDelta; |
|||
this.globalPosition = globalPosition ?? Offset.zero; |
|||
D.assert(primaryDelta == null |
|||
|| primaryDelta == this.delta.dx && this.delta.dy == 0.0 |
|||
|| primaryDelta == this.delta.dy && this.delta.dx == 0.0); |
|||
} |
|||
|
|||
public readonly DateTime sourceTimeStamp; |
|||
|
|||
public readonly Offset delta; |
|||
|
|||
public readonly double? primaryDelta; |
|||
|
|||
public readonly Offset globalPosition; |
|||
|
|||
public override string ToString() { |
|||
return this.GetType() + "(" + this.delta + ")"; |
|||
} |
|||
} |
|||
|
|||
public delegate void GestureDragUpdateCallback(DragUpdateDetails details); |
|||
|
|||
public class DragEndDetails { |
|||
public DragEndDetails( |
|||
Velocity velocity = null, |
|||
double? primaryVelocity = null |
|||
) { |
|||
this.velocity = velocity ?? Velocity.zero; |
|||
this.primaryVelocity = primaryVelocity; |
|||
|
|||
D.assert(primaryVelocity == null |
|||
|| primaryVelocity == this.velocity.pixelsPerSecond.dx |
|||
|| primaryVelocity == this.velocity.pixelsPerSecond.dy); |
|||
} |
|||
|
|||
public readonly Velocity velocity; |
|||
|
|||
public readonly double? primaryVelocity; |
|||
|
|||
public override string ToString() { |
|||
return this.GetType() + "(" + this.velocity + ")"; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4283bd1f6f7945578b47e7ad21999e7c |
|||
timeCreated: 1536330559 |
|
|||
using System; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public abstract class PointerEvent { |
|||
public PointerEvent( |
|||
DateTime timeStamp, |
|||
int pointer = 0, |
|||
PointerDeviceKind kind = PointerDeviceKind.mouse, |
|||
int device = 0, |
|||
Offset position = null, |
|||
Offset delta = null, |
|||
bool down = false, |
|||
bool synthesized = false |
|||
) { |
|||
this.timeStamp = timeStamp; |
|||
this.pointer = pointer; |
|||
this.kind = kind; |
|||
this.device = device; |
|||
this.position = position ?? Offset.zero; |
|||
this.delta = delta ?? Offset.zero; |
|||
this.down = down; |
|||
this.synthesized = synthesized; |
|||
} |
|||
|
|||
public readonly DateTime timeStamp; |
|||
|
|||
public readonly int pointer; |
|||
|
|||
public PointerDeviceKind kind; |
|||
|
|||
public int device; |
|||
|
|||
public readonly Offset position; |
|||
|
|||
public readonly Offset delta; |
|||
|
|||
public readonly bool down; |
|||
|
|||
public readonly bool synthesized; |
|||
} |
|||
|
|||
public class PointerAddedEvent : PointerEvent { |
|||
public PointerAddedEvent( |
|||
DateTime timeStamp, |
|||
PointerDeviceKind kind = PointerDeviceKind.touch, |
|||
int device = 0, |
|||
Offset position = null |
|||
) : base( |
|||
timeStamp: timeStamp, |
|||
kind: kind, |
|||
device: device, |
|||
position: position |
|||
) { |
|||
} |
|||
} |
|||
|
|||
public class PointerRemovedEvent : PointerEvent { |
|||
public PointerRemovedEvent( |
|||
DateTime timeStamp, |
|||
PointerDeviceKind kind = PointerDeviceKind.touch, |
|||
int device = 0 |
|||
) : base( |
|||
timeStamp: timeStamp, |
|||
kind: kind, |
|||
device: device |
|||
) { |
|||
} |
|||
} |
|||
|
|||
public class PointerDownEvent : PointerEvent { |
|||
public PointerDownEvent( |
|||
DateTime timeStamp, |
|||
int pointer = 0, |
|||
PointerDeviceKind kind = PointerDeviceKind.mouse, |
|||
int device = 0, |
|||
Offset position = null) |
|||
: base( |
|||
timeStamp, |
|||
pointer: pointer, |
|||
kind: kind, |
|||
device: device, |
|||
position: position, |
|||
down: true) { |
|||
} |
|||
} |
|||
|
|||
public class PointerMoveEvent : PointerEvent { |
|||
public PointerMoveEvent( |
|||
DateTime timeStamp, |
|||
int pointer = 0, |
|||
PointerDeviceKind kind = PointerDeviceKind.mouse, |
|||
int device = 0, |
|||
Offset position = null, |
|||
Offset delta = null, |
|||
bool synthesized = false) |
|||
: base( |
|||
timeStamp, |
|||
pointer: pointer, |
|||
kind: kind, |
|||
device: device, |
|||
position: position, |
|||
delta: delta, |
|||
down: true, |
|||
synthesized: synthesized) { |
|||
} |
|||
} |
|||
|
|||
public class PointerUpEvent : PointerEvent { |
|||
public PointerUpEvent( |
|||
DateTime timeStamp, |
|||
int pointer = 0, |
|||
PointerDeviceKind kind = PointerDeviceKind.mouse, |
|||
int device = 0, |
|||
Offset position = null) |
|||
: base( |
|||
timeStamp, |
|||
pointer: pointer, |
|||
kind: kind, |
|||
device: device, |
|||
position: position, |
|||
down: false) { |
|||
} |
|||
} |
|||
|
|||
public class PointerCancelEvent : PointerEvent { |
|||
public PointerCancelEvent( |
|||
DateTime timeStamp, |
|||
int pointer = 0, |
|||
PointerDeviceKind kind = PointerDeviceKind.mouse, |
|||
int device = 0, |
|||
Offset position = null) |
|||
: base( |
|||
timeStamp, |
|||
pointer: pointer, |
|||
kind: kind, |
|||
device: device, |
|||
position: position, |
|||
down: false) { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 85b73f18462c41199d2bd67c8f012ba6 |
|||
timeCreated: 1535595953 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public interface HitTestable { |
|||
void hitTest(HitTestResult result, Offset position); |
|||
} |
|||
|
|||
public interface HitTestDispatcher { |
|||
void dispatchEvent(PointerEvent evt, HitTestResult result); |
|||
} |
|||
|
|||
public interface HitTestTarget { |
|||
void handleEvent(PointerEvent evt, HitTestEntry entry); |
|||
} |
|||
|
|||
public class HitTestEntry { |
|||
public HitTestEntry(HitTestTarget target) { |
|||
this._target = target; |
|||
} |
|||
|
|||
public virtual HitTestTarget target { |
|||
get { return this._target; } |
|||
} |
|||
|
|||
readonly HitTestTarget _target; |
|||
|
|||
public override string ToString() { |
|||
return this._target.ToString(); |
|||
} |
|||
} |
|||
|
|||
public class HitTestResult { |
|||
public HitTestResult(List<HitTestEntry> path = null) { |
|||
this._path = path ?? new List<HitTestEntry>(); |
|||
} |
|||
|
|||
public IList<HitTestEntry> path { |
|||
get { return this._path.AsReadOnly(); } |
|||
} |
|||
|
|||
readonly List<HitTestEntry> _path; |
|||
|
|||
public void add(HitTestEntry entry) { |
|||
this._path.Add(entry); |
|||
} |
|||
|
|||
public override string ToString() { |
|||
return string.Format("HitTestResult({0})", |
|||
this._path.isEmpty() |
|||
? "<empty path>" |
|||
: string.Join(", ", this._path.Select(x => x.ToString()).ToArray())); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 8e54d41203874208ad368d1a42e611f8 |
|||
timeCreated: 1535595576 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgets.foundation; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
class _Vector { |
|||
internal _Vector(int size) { |
|||
this._offset = 0; |
|||
this._length = size; |
|||
this._elements = Enumerable.Repeat(0.0, size).ToList(); |
|||
} |
|||
|
|||
private _Vector(List<double> values, int offset, int length) { |
|||
this._offset = offset; |
|||
this._length = length; |
|||
this._elements = values; |
|||
} |
|||
|
|||
internal static _Vector fromVOL(List<double> values, int offset, int length) { |
|||
return new _Vector(values, offset, length); |
|||
} |
|||
|
|||
readonly int _offset; |
|||
|
|||
readonly int _length; |
|||
|
|||
readonly List<double> _elements; |
|||
|
|||
public double this[int i] { |
|||
get { return this._elements[i + this._offset]; } |
|||
set { this._elements[i + this._offset] = value; } |
|||
} |
|||
|
|||
public static double operator *(_Vector a, _Vector b) { |
|||
double result = 0.0; |
|||
for (int i = 0; i < a._length; i += 1) |
|||
result += a[i] * b[i]; |
|||
return result; |
|||
} |
|||
|
|||
public double norm() { |
|||
return Math.Sqrt(this * this); |
|||
} |
|||
} |
|||
|
|||
class _Matrix { |
|||
internal _Matrix(int rows, int cols) { |
|||
this._columns = cols; |
|||
this._elements = Enumerable.Repeat(0.0, rows * cols).ToList(); |
|||
} |
|||
|
|||
readonly int _columns; |
|||
readonly List<double> _elements; |
|||
|
|||
public double this[int row, int col] { |
|||
get { return this._elements[row * this._columns + col]; } |
|||
set { this._elements[row * this._columns + col] = value; } |
|||
} |
|||
|
|||
public _Vector getRow(int row) { |
|||
return _Vector.fromVOL( |
|||
this._elements, |
|||
row * this._columns, |
|||
this._columns |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class PolynomialFit { |
|||
public PolynomialFit(int degree) { |
|||
this.coefficients = Enumerable.Repeat(0.0, degree + 1).ToList(); |
|||
} |
|||
|
|||
public readonly List<double> coefficients; |
|||
|
|||
public double confidence; |
|||
} |
|||
|
|||
public class LeastSquaresSolver { |
|||
public LeastSquaresSolver(List<double> x, List<double> y, List<double> w) { |
|||
D.assert(x != null && y != null && w != null); |
|||
D.assert(x.Count == y.Count); |
|||
D.assert(y.Count == w.Count); |
|||
this.x = x; |
|||
this.y = y; |
|||
this.w = w; |
|||
} |
|||
|
|||
public readonly List<double> x; |
|||
|
|||
public readonly List<double> y; |
|||
|
|||
public readonly List<double> w; |
|||
|
|||
/// Fits a polynomial of the given degree to the data points.
|
|||
public PolynomialFit solve(int degree) { |
|||
if (degree > this.x.Count) { |
|||
// Not enough data to fit a curve.
|
|||
return null; |
|||
} |
|||
|
|||
PolynomialFit result = new PolynomialFit(degree); |
|||
|
|||
// Shorthands for the purpose of notation equivalence to original C++ code.
|
|||
int m = x.Count; |
|||
int n = degree + 1; |
|||
|
|||
// Expand the X vector to a matrix A, pre-multiplied by the weights.
|
|||
_Matrix a = new _Matrix(n, m); |
|||
for (int h = 0; h < m; h += 1) { |
|||
a[0, h] = this.w[h]; |
|||
for (int i = 1; i < n; i += 1) |
|||
a[i, h] = a[i - 1, h] * this.x[h]; |
|||
} |
|||
|
|||
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
|
|||
|
|||
// Orthonormal basis, column-major ordVectorer.
|
|||
_Matrix q = new _Matrix(n, m); |
|||
// Upper triangular matrix, row-major order.
|
|||
_Matrix r = new _Matrix(n, n); |
|||
for (int j = 0; j < n; j += 1) { |
|||
for (int h = 0; h < m; h += 1) |
|||
q[j, h] = a[j, h]; |
|||
for (int i = 0; i < j; i += 1) { |
|||
double dot = q.getRow(j) * q.getRow(i); |
|||
for (int h = 0; h < m; h += 1) |
|||
q[j, h] = q[j, h] - dot * q[i, h]; |
|||
} |
|||
|
|||
double norm = q.getRow(j).norm(); |
|||
if (norm < 0.000001) { |
|||
// Vectors are linearly dependent or zero so no solution.
|
|||
return null; |
|||
} |
|||
|
|||
double inverseNorm = 1.0 / norm; |
|||
for (int h = 0; h < m; h += 1) |
|||
q[j, h] = q[j, h] * inverseNorm; |
|||
for (int i = 0; i < n; i += 1) |
|||
r[j, i] = i < j ? 0.0 : q.getRow(j) * a.getRow(i); |
|||
} |
|||
|
|||
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
|
|||
// We just work from bottom-right to top-left calculating B's coefficients.
|
|||
_Vector wy = new _Vector(m); |
|||
for (int h = 0; h < m; h += 1) |
|||
wy[h] = y[h] * w[h]; |
|||
for (int i = n - 1; i >= 0; i -= 1) { |
|||
result.coefficients[i] = q.getRow(i) * wy; |
|||
for (int j = n - 1; j > i; j -= 1) |
|||
result.coefficients[i] -= r[i, j] * result.coefficients[j]; |
|||
result.coefficients[i] /= r[i, i]; |
|||
} |
|||
|
|||
// Calculate the coefficient of determination (confidence) as:
|
|||
// 1 - (sumSquaredError / sumSquaredTotal)
|
|||
// ...where sumSquaredError is the residual sum of squares (variance of the
|
|||
// error), and sumSquaredTotal is the total sum of squares (variance of the
|
|||
// data) where each has been weighted.
|
|||
double yMean = 0.0; |
|||
for (int h = 0; h < m; h += 1) |
|||
yMean += y[h]; |
|||
yMean /= m; |
|||
|
|||
double sumSquaredError = 0.0; |
|||
double sumSquaredTotal = 0.0; |
|||
for (int h = 0; h < m; h += 1) { |
|||
double term = 1.0; |
|||
double err = y[h] - result.coefficients[0]; |
|||
for (int i = 1; i < n; i += 1) { |
|||
term *= x[h]; |
|||
err -= term * result.coefficients[i]; |
|||
} |
|||
|
|||
sumSquaredError += w[h] * w[h] * err * err; |
|||
double v = y[h] - yMean; |
|||
sumSquaredTotal += w[h] * w[h] * v * v; |
|||
} |
|||
|
|||
result.confidence = sumSquaredTotal <= 0.000001 ? 1.0 : 1.0 - (sumSquaredError / sumSquaredTotal); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 575547b88e714c13b02cc94a7fac6bb2 |
|||
timeCreated: 1536333643 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
enum _DragState { |
|||
ready, |
|||
possible, |
|||
accepted, |
|||
} |
|||
|
|||
public delegate void GestureDragEndCallback(DragEndDetails details); |
|||
|
|||
public delegate void GestureDragCancelCallback(); |
|||
|
|||
public abstract class DragGestureRecognizer : OneSequenceGestureRecognizer { |
|||
public DragGestureRecognizer(GestureBinding binding, object debugOwner = null) |
|||
: base(binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public GestureDragDownCallback onDown; |
|||
|
|||
public GestureDragStartCallback onStart; |
|||
|
|||
public GestureDragUpdateCallback onUpdate; |
|||
|
|||
public GestureDragEndCallback onEnd; |
|||
|
|||
public GestureDragCancelCallback onCancel; |
|||
|
|||
public double? minFlingDistance; |
|||
|
|||
public double? minFlingVelocity; |
|||
|
|||
public double? maxFlingVelocity; |
|||
|
|||
_DragState _state = _DragState.ready; |
|||
Offset _initialPosition; |
|||
protected Offset _pendingDragOffset; |
|||
DateTime _lastPendingEventTimestamp; |
|||
|
|||
protected abstract bool _isFlingGesture(VelocityEstimate estimate); |
|||
protected abstract Offset _getDeltaForDetails(Offset delta); |
|||
protected abstract double? _getPrimaryValueFromOffset(Offset value); |
|||
protected abstract bool _hasSufficientPendingDragDeltaToAccept { get; } |
|||
|
|||
readonly Dictionary<int, VelocityTracker> _velocityTrackers = new Dictionary<int, VelocityTracker>(); |
|||
|
|||
public override void addPointer(PointerDownEvent evt) { |
|||
this.startTrackingPointer(evt.pointer); |
|||
this._velocityTrackers[evt.pointer] = new VelocityTracker(); |
|||
if (this._state == _DragState.ready) { |
|||
this._state = _DragState.possible; |
|||
this._initialPosition = evt.position; |
|||
this._pendingDragOffset = Offset.zero; |
|||
this._lastPendingEventTimestamp = evt.timeStamp; |
|||
if (this.onDown != null) { |
|||
this.invokeCallback<object>("onDown", |
|||
() => { |
|||
this.onDown(new DragDownDetails(globalPosition: this._initialPosition)); |
|||
return null; |
|||
}); |
|||
} |
|||
} else if (this._state == _DragState.accepted) { |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
} |
|||
|
|||
protected override void handleEvent(PointerEvent evt) { |
|||
D.assert(this._state != _DragState.ready); |
|||
if (!evt.synthesized |
|||
&& (evt is PointerDownEvent || evt is PointerMoveEvent)) { |
|||
var tracker = this._velocityTrackers[evt.pointer]; |
|||
D.assert(tracker != null); |
|||
tracker.addPosition(evt.timeStamp, evt.position); |
|||
} |
|||
|
|||
if (evt is PointerMoveEvent) { |
|||
Offset delta = evt.delta; |
|||
if (this._state == _DragState.accepted) { |
|||
if (this.onUpdate != null) { |
|||
this.invokeCallback<object>("onUpdate", () => { |
|||
this.onUpdate(new DragUpdateDetails( |
|||
sourceTimeStamp: evt.timeStamp, |
|||
delta: this._getDeltaForDetails(delta), |
|||
primaryDelta: this._getPrimaryValueFromOffset(delta), |
|||
globalPosition: evt.position |
|||
)); |
|||
return null; |
|||
}); |
|||
} |
|||
} else { |
|||
this._pendingDragOffset += delta; |
|||
this._lastPendingEventTimestamp = evt.timeStamp; |
|||
if (this._hasSufficientPendingDragDeltaToAccept) { |
|||
this.resolve(GestureDisposition.accepted); |
|||
} |
|||
} |
|||
} |
|||
|
|||
this.stopTrackingIfPointerNoLongerDown(evt); |
|||
} |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
if (this._state != _DragState.accepted) { |
|||
this._state = _DragState.accepted; |
|||
Offset delta = this._pendingDragOffset; |
|||
var timestamp = this._lastPendingEventTimestamp; |
|||
this._pendingDragOffset = Offset.zero; |
|||
this._lastPendingEventTimestamp = default(DateTime); |
|||
if (this.onStart != null) { |
|||
this.invokeCallback<object>("onStart", () => { |
|||
this.onStart(new DragStartDetails( |
|||
sourceTimeStamp: timestamp, |
|||
globalPosition: this._initialPosition |
|||
)); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
if (delta != Offset.zero && this.onUpdate != null) { |
|||
this.invokeCallback<object>("onUpdate", () => { |
|||
this.onUpdate(new DragUpdateDetails( |
|||
sourceTimeStamp: timestamp, |
|||
delta: this._getDeltaForDetails(delta), |
|||
primaryDelta: this._getPrimaryValueFromOffset(delta), |
|||
globalPosition: this._initialPosition |
|||
)); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
this.stopTrackingPointer(pointer); |
|||
} |
|||
|
|||
protected override void didStopTrackingLastPointer(int pointer) { |
|||
if (this._state == _DragState.possible) { |
|||
this.resolve(GestureDisposition.rejected); |
|||
this._state = _DragState.ready; |
|||
if (this.onCancel != null) { |
|||
this.invokeCallback<object>("onCancel", () => { |
|||
this.onCancel(); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
bool wasAccepted = this._state == _DragState.accepted; |
|||
this._state = _DragState.ready; |
|||
if (wasAccepted && this.onEnd != null) { |
|||
var tracker = this._velocityTrackers[pointer]; |
|||
D.assert(tracker != null); |
|||
|
|||
var estimate = tracker.getVelocityEstimate(); |
|||
if (estimate != null && this._isFlingGesture(estimate)) { |
|||
Velocity velocity = new Velocity(pixelsPerSecond: estimate.pixelsPerSecond) |
|||
.clampMagnitude(this.minFlingVelocity ?? Constants.kMinFlingVelocity, |
|||
this.maxFlingVelocity ?? Constants.kMaxFlingVelocity); |
|||
this.invokeCallback<object>("onEnd", () => { |
|||
this.onEnd(new DragEndDetails( |
|||
velocity: velocity, |
|||
primaryVelocity: this._getPrimaryValueFromOffset(velocity.pixelsPerSecond) |
|||
)); |
|||
return null; |
|||
}, debugReport: () => |
|||
string.Format("{0}; fling at {1}.", estimate, velocity)); |
|||
} else { |
|||
this.invokeCallback<object>("onEnd", () => { |
|||
this.onEnd(new DragEndDetails( |
|||
velocity: Velocity.zero, |
|||
primaryVelocity: 0.0 |
|||
)); |
|||
return null; |
|||
}, debugReport: () => |
|||
estimate == null |
|||
? "Could not estimate velocity." |
|||
: estimate + "; judged to not be a fling." |
|||
); |
|||
} |
|||
} |
|||
|
|||
this._velocityTrackers.Clear(); |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._velocityTrackers.Clear(); |
|||
base.dispose(); |
|||
} |
|||
} |
|||
|
|||
public class VerticalDragGestureRecognizer : DragGestureRecognizer { |
|||
public VerticalDragGestureRecognizer(GestureBinding binding, Object debugOwner = null) |
|||
: base(binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
protected override bool _isFlingGesture(VelocityEstimate estimate) { |
|||
double minVelocity = this.minFlingVelocity ?? Constants.kMinFlingVelocity; |
|||
double minDistance = this.minFlingDistance ?? Constants.kTouchSlop; |
|||
return Math.Abs(estimate.pixelsPerSecond.dy) > minVelocity && Math.Abs(estimate.offset.dy) > minDistance; |
|||
} |
|||
|
|||
protected override bool _hasSufficientPendingDragDeltaToAccept { |
|||
get { return Math.Abs(this._pendingDragOffset.dy) > Constants.kTouchSlop; } |
|||
} |
|||
|
|||
protected override Offset _getDeltaForDetails(Offset delta) { |
|||
return new Offset(0.0, delta.dy); |
|||
} |
|||
|
|||
protected override double? _getPrimaryValueFromOffset(Offset value) { |
|||
return value.dy; |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "vertical drag"; } |
|||
} |
|||
} |
|||
|
|||
public class HorizontalDragGestureRecognizer : DragGestureRecognizer { |
|||
public HorizontalDragGestureRecognizer(GestureBinding binding, Object debugOwner = null) |
|||
: base(binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
protected override bool _isFlingGesture(VelocityEstimate estimate) { |
|||
double minVelocity = this.minFlingVelocity ?? Constants.kMinFlingVelocity; |
|||
double minDistance = this.minFlingDistance ?? Constants.kTouchSlop; |
|||
return Math.Abs(estimate.pixelsPerSecond.dx) > minVelocity && Math.Abs(estimate.offset.dx) > minDistance; |
|||
} |
|||
|
|||
protected override bool _hasSufficientPendingDragDeltaToAccept { |
|||
get { return Math.Abs(this._pendingDragOffset.dx) > Constants.kTouchSlop; } |
|||
} |
|||
|
|||
protected override Offset _getDeltaForDetails(Offset delta) { |
|||
return new Offset(delta.dx, 0.0); |
|||
} |
|||
|
|||
protected override double? _getPrimaryValueFromOffset(Offset value) { |
|||
return value.dx; |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "horizontal drag"; } |
|||
} |
|||
} |
|||
|
|||
public class PanGestureRecognizer : DragGestureRecognizer { |
|||
public PanGestureRecognizer(GestureBinding binding, Object debugOwner = null) |
|||
: base(binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
protected override bool _isFlingGesture(VelocityEstimate estimate) { |
|||
double minVelocity = this.minFlingVelocity ?? Constants.kMinFlingVelocity; |
|||
double minDistance = this.minFlingDistance ?? Constants.kTouchSlop; |
|||
return estimate.pixelsPerSecond.distanceSquared > minVelocity * minVelocity |
|||
&& estimate.offset.distanceSquared > minDistance * minDistance; |
|||
} |
|||
|
|||
protected override bool _hasSufficientPendingDragDeltaToAccept { |
|||
get { return this._pendingDragOffset.distance > Constants.kPanSlop; } |
|||
} |
|||
|
|||
protected override Offset _getDeltaForDetails(Offset delta) { |
|||
return delta; |
|||
} |
|||
|
|||
protected override double? _getPrimaryValueFromOffset(Offset value) { |
|||
return null; |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "pan"; } |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 145abf9020044c79bf9227730292796d |
|||
timeCreated: 1536328579 |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using RSG.Promises; |
|||
using UIWidgets.async; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public delegate void GestureDoubleTapCallback(); |
|||
|
|||
class _TapTracker { |
|||
internal _TapTracker( |
|||
GestureBinding binding = null, |
|||
PointerDownEvent evt = null, |
|||
GestureArenaEntry entry = null) { |
|||
this._binding = binding; |
|||
this.pointer = evt.pointer; |
|||
this._initialPosition = evt.position; |
|||
this.entry = entry; |
|||
} |
|||
|
|||
public readonly int pointer; |
|||
public readonly GestureArenaEntry entry; |
|||
readonly Offset _initialPosition; |
|||
readonly GestureBinding _binding; |
|||
|
|||
bool _isTrackingPointer = false; |
|||
|
|||
public void startTrackingPointer(PointerRoute route) { |
|||
if (!this._isTrackingPointer) { |
|||
this._isTrackingPointer = true; |
|||
this._binding.pointerRouter.addRoute(this.pointer, route); |
|||
} |
|||
} |
|||
|
|||
public void stopTrackingPointer(PointerRoute route) { |
|||
if (this._isTrackingPointer) { |
|||
this._isTrackingPointer = false; |
|||
this._binding.pointerRouter.removeRoute(this.pointer, route); |
|||
} |
|||
} |
|||
|
|||
public bool isWithinTolerance(PointerEvent evt, double tolerance) { |
|||
Offset offset = evt.position - this._initialPosition; |
|||
return offset.distance <= tolerance; |
|||
} |
|||
} |
|||
|
|||
public class DoubleTapGestureRecognizer : GestureRecognizer { |
|||
public DoubleTapGestureRecognizer( |
|||
GestureBinding binding = null, object debugOwner = null) |
|||
: base(binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public GestureDoubleTapCallback onDoubleTap; |
|||
|
|||
Timer _doubleTapTimer; |
|||
_TapTracker _firstTap; |
|||
readonly Dictionary<int, _TapTracker> _trackers = new Dictionary<int, _TapTracker>(); |
|||
|
|||
public override void addPointer(PointerDownEvent evt) { |
|||
if (this._firstTap != null && |
|||
!this._firstTap.isWithinTolerance(evt, Constants.kDoubleTapSlop)) { |
|||
return; |
|||
} |
|||
|
|||
this._stopDoubleTapTimer(); |
|||
_TapTracker tracker = new _TapTracker( |
|||
binding: this._binding, |
|||
evt: evt, |
|||
entry: this._binding.gestureArena.add(evt.pointer, this) |
|||
); |
|||
this._trackers[evt.pointer] = tracker; |
|||
tracker.startTrackingPointer(this._handleEvent); |
|||
} |
|||
|
|||
void _handleEvent(PointerEvent evt) { |
|||
_TapTracker tracker = this._trackers[evt.pointer]; |
|||
D.assert(tracker != null); |
|||
if (evt is PointerUpEvent) { |
|||
if (this._firstTap == null) { |
|||
this._registerFirstTap(tracker); |
|||
} else { |
|||
this._registerSecondTap(tracker); |
|||
} |
|||
} else if (evt is PointerMoveEvent) { |
|||
if (!tracker.isWithinTolerance(evt, Constants.kDoubleTapTouchSlop)) { |
|||
this._reject(tracker); |
|||
} |
|||
} else if (evt is PointerCancelEvent) { |
|||
this._reject(tracker); |
|||
} |
|||
} |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
_TapTracker tracker; |
|||
this._trackers.TryGetValue(pointer, out tracker); |
|||
|
|||
if (tracker == null && |
|||
this._firstTap != null && |
|||
this._firstTap.pointer == pointer) { |
|||
tracker = this._firstTap; |
|||
} |
|||
|
|||
if (tracker != null) { |
|||
this._reject(tracker); |
|||
} |
|||
} |
|||
|
|||
void _reject(_TapTracker tracker) { |
|||
this._trackers.Remove(tracker.pointer); |
|||
tracker.entry.resolve(GestureDisposition.rejected); |
|||
this._freezeTracker(tracker); |
|||
if (this._firstTap != null && |
|||
(this._trackers.isEmpty() || tracker == this._firstTap)) { |
|||
this._reset(); |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._reset(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _reset() { |
|||
this._stopDoubleTapTimer(); |
|||
if (this._firstTap != null) { |
|||
_TapTracker tracker = this._firstTap; |
|||
this._firstTap = null; |
|||
this._reject(tracker); |
|||
this._binding.gestureArena.release(tracker.pointer); |
|||
} |
|||
|
|||
this._clearTrackers(); |
|||
} |
|||
|
|||
void _registerFirstTap(_TapTracker tracker) { |
|||
this._startDoubleTapTimer(); |
|||
this._binding.gestureArena.hold(tracker.pointer); |
|||
this._freezeTracker(tracker); |
|||
this._trackers.Remove(tracker.pointer); |
|||
this._clearTrackers(); |
|||
this._firstTap = tracker; |
|||
} |
|||
|
|||
void _registerSecondTap(_TapTracker tracker) { |
|||
this._firstTap.entry.resolve(GestureDisposition.accepted); |
|||
tracker.entry.resolve(GestureDisposition.accepted); |
|||
this._freezeTracker(tracker); |
|||
this._trackers.Remove(tracker.pointer); |
|||
if (this.onDoubleTap != null) { |
|||
this.invokeCallback<object>("onDoubleTap", () => { |
|||
this.onDoubleTap(); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
this._reset(); |
|||
} |
|||
|
|||
void _clearTrackers() { |
|||
this._trackers.Values.ToList().Each(this._reject); |
|||
D.assert(this._trackers.isEmpty()); |
|||
} |
|||
|
|||
void _freezeTracker(_TapTracker tracker) { |
|||
tracker.stopTrackingPointer(this._handleEvent); |
|||
} |
|||
|
|||
void _startDoubleTapTimer() { |
|||
this._doubleTapTimer = |
|||
this._doubleTapTimer |
|||
?? this._binding.window.run(Constants.kDoubleTapTimeout, this._reset); |
|||
} |
|||
|
|||
void _stopDoubleTapTimer() { |
|||
if (this._doubleTapTimer != null) { |
|||
this._doubleTapTimer.cancel(); |
|||
this._doubleTapTimer = null; |
|||
} |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "double tap"; } |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4b4742c47601461f8e406425d1cd5172 |
|||
timeCreated: 1536418722 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public delegate void PointerRoute(PointerEvent evt); |
|||
|
|||
public class PointerRouter { |
|||
readonly Dictionary<int, HashSet<PointerRoute>> _routeMap = new Dictionary<int, HashSet<PointerRoute>>(); |
|||
|
|||
readonly HashSet<PointerRoute> _globalRoutes = new HashSet<PointerRoute>(); |
|||
|
|||
public void addRoute(int pointer, PointerRoute route) { |
|||
var routes = this._routeMap.putIfAbsent(pointer, () => new HashSet<PointerRoute>()); |
|||
D.assert(!routes.Contains(route)); |
|||
routes.Add(route); |
|||
} |
|||
|
|||
public void removeRoute(int pointer, PointerRoute route) { |
|||
D.assert(this._routeMap.ContainsKey(pointer)); |
|||
var routes = this._routeMap[pointer]; |
|||
routes.Remove(route); |
|||
if (routes.isEmpty()) { |
|||
this._routeMap.Remove(pointer); |
|||
} |
|||
} |
|||
|
|||
public void addGlobalRoute(PointerRoute route) { |
|||
D.assert(!this._globalRoutes.Contains(route)); |
|||
this._globalRoutes.Add(route); |
|||
} |
|||
|
|||
public void removeGlobalRoute(PointerRoute route) { |
|||
D.assert(this._globalRoutes.Contains(route)); |
|||
this._globalRoutes.Remove(route); |
|||
} |
|||
|
|||
void _dispatch(PointerEvent evt, PointerRoute route) { |
|||
try { |
|||
route(evt); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogError("Error while routing a pointer event: " + ex); |
|||
} |
|||
} |
|||
|
|||
public void route(PointerEvent evt) { |
|||
HashSet<PointerRoute> routes; |
|||
this._routeMap.TryGetValue(evt.pointer, out routes); |
|||
if (routes != null) { |
|||
foreach (PointerRoute route in new List<PointerRoute>(routes)) { |
|||
if (routes.Contains(route)) { |
|||
this._dispatch(evt, route); |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach (PointerRoute route in new List<PointerRoute>(this._globalRoutes)) { |
|||
if (this._globalRoutes.Contains(route)) { |
|||
this._dispatch(evt, route); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 37ff4ce5ebb74851a0bf2e62f12d9c50 |
|||
timeCreated: 1535604768 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.async; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public delegate T RecognizerCallback<T>(); |
|||
|
|||
public abstract class GestureRecognizer : DiagnosticableTree, GestureArenaMember { |
|||
protected GestureRecognizer(GestureBinding binding = null, object debugOwner = null) { |
|||
this._binding = binding; |
|||
this.debugOwner = debugOwner; |
|||
} |
|||
|
|||
protected readonly GestureBinding _binding; |
|||
|
|||
public readonly object debugOwner; |
|||
|
|||
public abstract void addPointer(PointerDownEvent evt); |
|||
|
|||
public virtual void dispose() { |
|||
} |
|||
|
|||
public abstract string debugDescription { get; } |
|||
|
|||
protected T invokeCallback<T>(string name, RecognizerCallback<T> callback, Func<string> debugReport = null) { |
|||
D.assert(callback != null); |
|||
|
|||
T result = default(T); |
|||
try { |
|||
D.assert(() => { |
|||
if (D.debugPrintRecognizerCallbacksTrace) { |
|||
var report = debugReport != null ? debugReport() : null; |
|||
// The 19 in the line below is the width of the prefix used by
|
|||
// _debugLogDiagnostic in arena.dart.
|
|||
var prefix = D.debugPrintGestureArenaDiagnostics ? new string(' ', 19) + "❙ " : ""; |
|||
Debug.LogFormat("{0}this calling {1} callback.{2}", |
|||
prefix, name, report.isNotEmpty() ? " " + report : ""); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
result = callback(); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogError("Error while handling a gesture [" + name + "]: " + ex); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public abstract void acceptGesture(int pointer); |
|||
public abstract void rejectGesture(int pointer); |
|||
|
|||
protected internal override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new DiagnosticsProperty<object>("debugOwner", this.debugOwner, |
|||
defaultValue: Diagnostics.kNullDefaultValue)); |
|||
} |
|||
} |
|||
|
|||
public abstract class OneSequenceGestureRecognizer : GestureRecognizer { |
|||
protected OneSequenceGestureRecognizer( |
|||
GestureBinding binding = null, object debugOwner = null) : base(binding, debugOwner) { |
|||
} |
|||
|
|||
readonly Dictionary<int, GestureArenaEntry> _entries = new Dictionary<int, GestureArenaEntry>(); |
|||
|
|||
readonly HashSet<int> _trackedPointers = new HashSet<int>(); |
|||
|
|||
protected abstract void handleEvent(PointerEvent evt); |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
} |
|||
|
|||
protected abstract void didStopTrackingLastPointer(int pointer); |
|||
|
|||
protected virtual void resolve(GestureDisposition disposition) { |
|||
var localEntries = new List<GestureArenaEntry>(this._entries.Values); |
|||
this._entries.Clear(); |
|||
foreach (GestureArenaEntry entry in localEntries) { |
|||
entry.resolve(disposition); |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this.resolve(GestureDisposition.rejected); |
|||
foreach (int pointer in this._trackedPointers) { |
|||
this._binding.pointerRouter.removeRoute(pointer, this.handleEvent); |
|||
} |
|||
|
|||
this._trackedPointers.Clear(); |
|||
D.assert(this._entries.isEmpty()); |
|||
base.dispose(); |
|||
} |
|||
|
|||
public GestureArenaTeam team { |
|||
get { return this._team; } |
|||
set { |
|||
D.assert(value != null); |
|||
D.assert(this._entries.isEmpty()); |
|||
D.assert(this._trackedPointers.isEmpty()); |
|||
D.assert(this._team == null); |
|||
this._team = value; |
|||
} |
|||
} |
|||
|
|||
GestureArenaTeam _team; |
|||
|
|||
GestureArenaEntry _addPointerToArena(int pointer) { |
|||
if (this._team != null) { |
|||
return this._team.add(pointer, this); |
|||
} |
|||
|
|||
return this._binding.gestureArena.add(pointer, this); |
|||
} |
|||
|
|||
protected void startTrackingPointer(int pointer) { |
|||
this._binding.pointerRouter.addRoute(pointer, this.handleEvent); |
|||
this._trackedPointers.Add(pointer); |
|||
D.assert(!this._entries.ContainsKey(pointer)); |
|||
this._entries[pointer] = this._addPointerToArena(pointer); |
|||
} |
|||
|
|||
protected void stopTrackingPointer(int pointer) { |
|||
if (this._trackedPointers.Contains(pointer)) { |
|||
this._binding.pointerRouter.removeRoute(pointer, this.handleEvent); |
|||
this._trackedPointers.Remove(pointer); |
|||
if (this._trackedPointers.isEmpty()) { |
|||
this.didStopTrackingLastPointer(pointer); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected void stopTrackingIfPointerNoLongerDown(PointerEvent evt) { |
|||
if (evt is PointerUpEvent || evt is PointerCancelEvent) { |
|||
this.stopTrackingPointer(evt.pointer); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public enum GestureRecognizerState { |
|||
ready, |
|||
possible, |
|||
defunct, |
|||
} |
|||
|
|||
public abstract class PrimaryPointerGestureRecognizer : OneSequenceGestureRecognizer { |
|||
protected PrimaryPointerGestureRecognizer( |
|||
TimeSpan? deadline = null, |
|||
GestureBinding binding = null, |
|||
object debugOwner = null |
|||
) : base(binding: binding, debugOwner: debugOwner) { |
|||
this.deadline = deadline; |
|||
} |
|||
|
|||
public readonly TimeSpan? deadline; |
|||
|
|||
public GestureRecognizerState state = GestureRecognizerState.ready; |
|||
|
|||
public int primaryPointer; |
|||
|
|||
public Offset initialPosition; |
|||
|
|||
Timer _timer; |
|||
|
|||
public override void addPointer(PointerDownEvent evt) { |
|||
this.startTrackingPointer(evt.pointer); |
|||
if (this.state == GestureRecognizerState.ready) { |
|||
this.state = GestureRecognizerState.possible; |
|||
this.primaryPointer = evt.pointer; |
|||
this.initialPosition = evt.position; |
|||
if (this.deadline != null) { |
|||
this._timer = this._binding.window.run(this.deadline.Value, this.didExceedDeadline); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void handleEvent(PointerEvent evt) { |
|||
D.assert(this.state != GestureRecognizerState.ready); |
|||
if (this.state == GestureRecognizerState.possible && evt.pointer == this.primaryPointer) { |
|||
if (evt is PointerMoveEvent && this._getDistance(evt) > Constants.kTouchSlop) { |
|||
this.resolve(GestureDisposition.rejected); |
|||
this.stopTrackingPointer(this.primaryPointer); |
|||
} else { |
|||
this.handlePrimaryPointer(evt); |
|||
} |
|||
} |
|||
|
|||
this.stopTrackingIfPointerNoLongerDown(evt); |
|||
} |
|||
|
|||
protected abstract void handlePrimaryPointer(PointerEvent evt); |
|||
|
|||
protected virtual void didExceedDeadline() { |
|||
D.assert(this.deadline == null); |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
if (pointer == this.primaryPointer && this.state == GestureRecognizerState.possible) { |
|||
this._stopTimer(); |
|||
this.state = GestureRecognizerState.defunct; |
|||
} |
|||
} |
|||
|
|||
protected override void didStopTrackingLastPointer(int pointer) { |
|||
this._stopTimer(); |
|||
this.state = GestureRecognizerState.ready; |
|||
} |
|||
|
|||
public override void dispose() { |
|||
this._stopTimer(); |
|||
base.dispose(); |
|||
} |
|||
|
|||
void _stopTimer() { |
|||
if (this._timer != null) { |
|||
this._timer.cancel(); |
|||
this._timer = null; |
|||
} |
|||
} |
|||
|
|||
double _getDistance(PointerEvent evt) { |
|||
Offset offset = evt.position - this.initialPosition; |
|||
return offset.distance; |
|||
} |
|||
|
|||
protected internal override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new EnumProperty<GestureRecognizerState>("state", this.state)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d4c72baa549e450abe95d694fbad1a7b |
|||
timeCreated: 1535606392 |
|
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public class TapDownDetails { |
|||
public TapDownDetails(Offset globalPosition = null) { |
|||
this.globalPosition = globalPosition ?? Offset.zero; |
|||
} |
|||
|
|||
public readonly Offset globalPosition; |
|||
} |
|||
|
|||
public delegate void GestureTapDownCallback(TapDownDetails details); |
|||
|
|||
public class TapUpDetails { |
|||
public TapUpDetails(Offset globalPosition = null) { |
|||
this.globalPosition = globalPosition ?? Offset.zero; |
|||
} |
|||
|
|||
public readonly Offset globalPosition; |
|||
} |
|||
|
|||
public delegate void GestureTapUpCallback(TapUpDetails details); |
|||
|
|||
public delegate void GestureTapCallback(); |
|||
|
|||
public delegate void GestureTapCancelCallback(); |
|||
|
|||
public class TapGestureRecognizer : PrimaryPointerGestureRecognizer { |
|||
public TapGestureRecognizer(GestureBinding binding, object debugOwner = null) |
|||
: base(deadline: Constants.kPressTimeout, binding: binding, debugOwner: debugOwner) { |
|||
} |
|||
|
|||
public GestureTapDownCallback onTapDown; |
|||
|
|||
public GestureTapUpCallback onTapUp; |
|||
|
|||
public GestureTapCallback onTap; |
|||
|
|||
public GestureTapCancelCallback onTapCancel; |
|||
|
|||
bool _sentTapDown = false; |
|||
|
|||
bool _wonArenaForPrimaryPointer = false; |
|||
|
|||
Offset _finalPosition; |
|||
|
|||
protected override void handlePrimaryPointer(PointerEvent evt) { |
|||
if (evt is PointerUpEvent) { |
|||
this._finalPosition = evt.position; |
|||
this._checkUp(); |
|||
} else if (evt is PointerCancelEvent) { |
|||
this._reset(); |
|||
} |
|||
} |
|||
|
|||
protected override void resolve(GestureDisposition disposition) { |
|||
if (this._wonArenaForPrimaryPointer && disposition == GestureDisposition.rejected) { |
|||
if (this.onTapCancel != null) { |
|||
this.invokeCallback<object>("spontaneous onTapCancel", () => { |
|||
this.onTapCancel(); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
this._reset(); |
|||
} |
|||
|
|||
base.resolve(disposition); |
|||
} |
|||
|
|||
protected override void didExceedDeadline() { |
|||
this._checkDown(); |
|||
} |
|||
|
|||
public override void acceptGesture(int pointer) { |
|||
base.acceptGesture(pointer); |
|||
if (pointer == this.primaryPointer) { |
|||
this._checkDown(); |
|||
this._wonArenaForPrimaryPointer = true; |
|||
this._checkUp(); |
|||
} |
|||
} |
|||
|
|||
public override void rejectGesture(int pointer) { |
|||
base.rejectGesture(pointer); |
|||
if (pointer == this.primaryPointer) { |
|||
if (this.onTapCancel != null) { |
|||
this.invokeCallback<object>("forced onTapCancel", () => { |
|||
this.onTapCancel(); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
this._reset(); |
|||
} |
|||
} |
|||
|
|||
void _checkDown() { |
|||
if (!this._sentTapDown) { |
|||
if (this.onTapDown != null) |
|||
this.invokeCallback<object>("onTapDown", () => { |
|||
this.onTapDown(new TapDownDetails(globalPosition: this.initialPosition)); |
|||
return null; |
|||
}); |
|||
this._sentTapDown = true; |
|||
} |
|||
} |
|||
|
|||
void _checkUp() { |
|||
if (this._wonArenaForPrimaryPointer && this._finalPosition != null) { |
|||
this.resolve(GestureDisposition.accepted); |
|||
if (!this._wonArenaForPrimaryPointer || this._finalPosition == null) { |
|||
return; |
|||
} |
|||
|
|||
if (this.onTapUp != null) |
|||
this.invokeCallback<object>("onTapUp", () => { |
|||
this.onTapUp(new TapUpDetails(globalPosition: this._finalPosition)); |
|||
return null; |
|||
}); |
|||
if (this.onTap != null) { |
|||
this.invokeCallback<object>("onTap", () => { |
|||
this.onTap(); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
this._reset(); |
|||
} |
|||
} |
|||
|
|||
void _reset() { |
|||
this._sentTapDown = false; |
|||
this._wonArenaForPrimaryPointer = false; |
|||
this._finalPosition = null; |
|||
} |
|||
|
|||
public override string debugDescription { |
|||
get { return "tap"; } |
|||
} |
|||
|
|||
protected internal override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|||
base.debugFillProperties(properties); |
|||
properties.add(new FlagProperty("wonArenaForPrimaryPointer", |
|||
value: this._wonArenaForPrimaryPointer, |
|||
ifTrue: "won arena")); |
|||
properties.add(new DiagnosticsProperty<Offset>("finalPosition", |
|||
this._finalPosition, defaultValue: Diagnostics.kNullDefaultValue)); |
|||
properties.add(new FlagProperty("sentTapDown", |
|||
value: this._sentTapDown, ifTrue: "sent tap down")); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: f6283d6471a24256ad84c37472dfe262 |
|||
timeCreated: 1535608354 |
|
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
class _CombiningGestureArenaEntry : GestureArenaEntry { |
|||
public _CombiningGestureArenaEntry(_CombiningGestureArenaMember _combiner, GestureArenaMember _member) { |
|||
this._combiner = _combiner; |
|||
this._member = _member; |
|||
} |
|||
|
|||
readonly _CombiningGestureArenaMember _combiner; |
|||
readonly GestureArenaMember _member; |
|||
|
|||
public override void resolve(GestureDisposition disposition) { |
|||
this._combiner._resolve(this._member, disposition); |
|||
} |
|||
} |
|||
|
|||
class _CombiningGestureArenaMember : GestureArenaMember { |
|||
public _CombiningGestureArenaMember(GestureArenaTeam _owner, int _pointer) { |
|||
this._owner = _owner; |
|||
this._pointer = _pointer; |
|||
} |
|||
|
|||
readonly GestureArenaTeam _owner; |
|||
readonly List<GestureArenaMember> _members = new List<GestureArenaMember>(); |
|||
readonly int _pointer; |
|||
|
|||
bool _resolved = false; |
|||
GestureArenaMember _winner; |
|||
GestureArenaEntry _entry; |
|||
|
|||
public void acceptGesture(int pointer) { |
|||
D.assert(this._pointer == pointer); |
|||
D.assert(this._winner != null || this._members.isNotEmpty()); |
|||
|
|||
this._close(); |
|||
this._winner = this._winner ?? this._owner.captain ?? this._members[0]; |
|||
|
|||
foreach (GestureArenaMember member in this._members) { |
|||
if (member != this._winner) { |
|||
member.rejectGesture(pointer); |
|||
} |
|||
} |
|||
|
|||
this._winner.acceptGesture(pointer); |
|||
} |
|||
|
|||
public void rejectGesture(int pointer) { |
|||
D.assert(this._pointer == pointer); |
|||
|
|||
this._close(); |
|||
foreach (GestureArenaMember member in this._members) { |
|||
member.rejectGesture(pointer); |
|||
} |
|||
} |
|||
|
|||
void _close() { |
|||
D.assert(!this._resolved); |
|||
this._resolved = true; |
|||
|
|||
var combiner = this._owner._combiners[this._pointer]; |
|||
D.assert(combiner == this); |
|||
|
|||
this._owner._combiners.Remove(this._pointer); |
|||
} |
|||
|
|||
internal GestureArenaEntry _add(int pointer, GestureArenaMember member) { |
|||
D.assert(!this._resolved); |
|||
D.assert(this._pointer == pointer); |
|||
|
|||
this._members.Add(member); |
|||
this._entry = this._entry ?? this._owner._gestureArena.add(pointer, this); |
|||
return new _CombiningGestureArenaEntry(this, member); |
|||
} |
|||
|
|||
internal void _resolve(GestureArenaMember member, GestureDisposition disposition) { |
|||
if (this._resolved) { |
|||
return; |
|||
} |
|||
|
|||
if (disposition == GestureDisposition.rejected) { |
|||
this._members.Remove(member); |
|||
member.rejectGesture(this._pointer); |
|||
if (this._members.isEmpty()) { |
|||
this._entry.resolve(disposition); |
|||
} |
|||
} else { |
|||
this._winner = this._winner ?? this._owner.captain ?? member; |
|||
this._entry.resolve(disposition); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class GestureArenaTeam { |
|||
public GestureArenaTeam(GestureArenaManager gestureArena) { |
|||
this._gestureArena = gestureArena; |
|||
} |
|||
|
|||
internal readonly GestureArenaManager _gestureArena; |
|||
|
|||
internal readonly Dictionary<int, _CombiningGestureArenaMember> _combiners = |
|||
new Dictionary<int, _CombiningGestureArenaMember>(); |
|||
|
|||
public GestureArenaMember captain; |
|||
|
|||
public GestureArenaEntry add(int pointer, GestureArenaMember member) { |
|||
_CombiningGestureArenaMember combiner; |
|||
|
|||
if (!this._combiners.TryGetValue(pointer, out combiner)) { |
|||
combiner = new _CombiningGestureArenaMember(this, pointer); |
|||
this._combiners[pointer] = combiner; |
|||
} |
|||
|
|||
return combiner._add(pointer, member); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 7f4c3bdaf7be471ea5c3eeb4d7656d27 |
|||
timeCreated: 1535593919 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UIWidgets.foundation; |
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.gestures { |
|||
public class Velocity : IEquatable<Velocity> { |
|||
public Velocity( |
|||
Offset pixelsPerSecond = null |
|||
) { |
|||
this.pixelsPerSecond = pixelsPerSecond ?? Offset.zero; |
|||
} |
|||
|
|||
public static readonly Velocity zero = new Velocity(); |
|||
|
|||
public readonly Offset pixelsPerSecond; |
|||
|
|||
public static Velocity operator -(Velocity a) { |
|||
return new Velocity(pixelsPerSecond: -a.pixelsPerSecond); |
|||
} |
|||
|
|||
public static Velocity operator -(Velocity a, Velocity b) { |
|||
return new Velocity( |
|||
pixelsPerSecond: a.pixelsPerSecond - b.pixelsPerSecond); |
|||
} |
|||
|
|||
public static Velocity operator +(Velocity a, Velocity b) { |
|||
return new Velocity( |
|||
pixelsPerSecond: a.pixelsPerSecond + b.pixelsPerSecond); |
|||
} |
|||
|
|||
public Velocity clampMagnitude(double minValue, double maxValue) { |
|||
D.assert(minValue >= 0.0); |
|||
D.assert(maxValue >= 0.0 && maxValue >= minValue); |
|||
double valueSquared = this.pixelsPerSecond.distanceSquared; |
|||
if (valueSquared > maxValue * maxValue) { |
|||
return new Velocity(pixelsPerSecond: (this.pixelsPerSecond / this.pixelsPerSecond.distance) * maxValue); |
|||
} |
|||
|
|||
if (valueSquared < minValue * minValue) { |
|||
return new Velocity(pixelsPerSecond: (this.pixelsPerSecond / this.pixelsPerSecond.distance) * minValue); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public bool Equals(Velocity other) { |
|||
if (object.ReferenceEquals(null, other)) return false; |
|||
if (object.ReferenceEquals(this, other)) return true; |
|||
return object.Equals(this.pixelsPerSecond, other.pixelsPerSecond); |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (object.ReferenceEquals(null, obj)) return false; |
|||
if (object.ReferenceEquals(this, obj)) return true; |
|||
if (obj.GetType() != this.GetType()) return false; |
|||
return this.Equals((Velocity) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
return (this.pixelsPerSecond != null ? this.pixelsPerSecond.GetHashCode() : 0); |
|||
} |
|||
|
|||
public static bool operator ==(Velocity left, Velocity right) { |
|||
return object.Equals(left, right); |
|||
} |
|||
|
|||
public static bool operator !=(Velocity left, Velocity right) { |
|||
return !object.Equals(left, right); |
|||
} |
|||
|
|||
public override string ToString() { |
|||
return string.Format("Velocity({0:F1}, {1:F1})", this.pixelsPerSecond.dx, this.pixelsPerSecond.dy); |
|||
} |
|||
} |
|||
|
|||
public class VelocityEstimate { |
|||
public VelocityEstimate( |
|||
Offset pixelsPerSecond, |
|||
double confidence, |
|||
TimeSpan duration, |
|||
Offset offset |
|||
) { |
|||
D.assert(pixelsPerSecond != null); |
|||
D.assert(offset != null); |
|||
this.pixelsPerSecond = pixelsPerSecond; |
|||
this.confidence = confidence; |
|||
this.duration = duration; |
|||
this.offset = offset; |
|||
} |
|||
|
|||
public readonly Offset pixelsPerSecond; |
|||
|
|||
public readonly double confidence; |
|||
|
|||
public readonly TimeSpan duration; |
|||
|
|||
public readonly Offset offset; |
|||
|
|||
public override string ToString() { |
|||
return string.Format("VelocityEstimate({0:F1}, {1:F1}; offset: {2}, duration: {3}, confidence: {4:F1})", |
|||
this.pixelsPerSecond.dx, this.pixelsPerSecond.dy, this.offset, this.duration, this.confidence); |
|||
} |
|||
} |
|||
|
|||
class _PointAtTime { |
|||
internal _PointAtTime(Offset point, DateTime time) { |
|||
D.assert(point != null); |
|||
this.point = point; |
|||
this.time = time; |
|||
} |
|||
|
|||
public readonly Offset point; |
|||
|
|||
public readonly DateTime time; |
|||
|
|||
public override string ToString() { |
|||
return string.Format("_PointAtTime({0} at {1})", this.point, this.time); |
|||
} |
|||
} |
|||
|
|||
public class VelocityTracker { |
|||
const int _assumePointerMoveStoppedMilliseconds = 40; |
|||
const int _historySize = 20; |
|||
const int _horizonMilliseconds = 100; |
|||
const int _minSampleSize = 3; |
|||
|
|||
readonly List<_PointAtTime> _samples = Enumerable.Repeat<_PointAtTime>(null, _historySize).ToList(); |
|||
int _index = 0; |
|||
|
|||
public void addPosition(DateTime time, Offset position) { |
|||
this._index += 1; |
|||
if (this._index == _historySize) { |
|||
this._index = 0; |
|||
} |
|||
|
|||
this._samples[this._index] = new _PointAtTime(position, time); |
|||
} |
|||
|
|||
public VelocityEstimate getVelocityEstimate() { |
|||
List<double> x = new List<double>(); |
|||
List<double> y = new List<double>(); |
|||
List<double> w = new List<double>(); |
|||
List<double> time = new List<double>(); |
|||
int sampleCount = 0; |
|||
int index = this._index; |
|||
|
|||
_PointAtTime newestSample = this._samples[index]; |
|||
if (newestSample == null) { |
|||
return null; |
|||
} |
|||
|
|||
_PointAtTime previousSample = newestSample; |
|||
_PointAtTime oldestSample = newestSample; |
|||
|
|||
do { |
|||
_PointAtTime sample = this._samples[index]; |
|||
if (sample == null) |
|||
break; |
|||
|
|||
double age = (newestSample.time - sample.time).TotalMilliseconds; |
|||
double delta = Math.Abs((sample.time - previousSample.time).TotalMilliseconds); |
|||
previousSample = sample; |
|||
if (age > _horizonMilliseconds || delta > _assumePointerMoveStoppedMilliseconds) { |
|||
break; |
|||
} |
|||
|
|||
oldestSample = sample; |
|||
Offset position = sample.point; |
|||
x.Add(position.dx); |
|||
y.Add(position.dy); |
|||
w.Add(1.0); |
|||
time.Add(-age); |
|||
index = (index == 0 ? _historySize : index) - 1; |
|||
|
|||
sampleCount += 1; |
|||
} while (sampleCount < _historySize); |
|||
|
|||
if (sampleCount >= _minSampleSize) { |
|||
LeastSquaresSolver xSolver = new LeastSquaresSolver(time, x, w); |
|||
PolynomialFit xFit = xSolver.solve(2); |
|||
if (xFit != null) { |
|||
LeastSquaresSolver ySolver = new LeastSquaresSolver(time, y, w); |
|||
PolynomialFit yFit = ySolver.solve(2); |
|||
if (yFit != null) { |
|||
return new VelocityEstimate( |
|||
pixelsPerSecond: new Offset(xFit.coefficients[1] * 1000, yFit.coefficients[1] * 1000), |
|||
confidence: xFit.confidence * yFit.confidence, |
|||
duration: newestSample.time - oldestSample.time, |
|||
offset: newestSample.point - oldestSample.point |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return new VelocityEstimate( |
|||
pixelsPerSecond: Offset.zero, |
|||
confidence: 1.0, |
|||
duration: newestSample.time - oldestSample.time, |
|||
offset: newestSample.point - oldestSample.point |
|||
); |
|||
} |
|||
|
|||
public Velocity getVelocity() { |
|||
VelocityEstimate estimate = this.getVelocityEstimate(); |
|||
if (estimate == null || estimate.pixelsPerSecond == Offset.zero) { |
|||
return Velocity.zero; |
|||
} |
|||
|
|||
return new Velocity(pixelsPerSecond: estimate.pixelsPerSecond); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d01877db8a7341e491a67f84ed3ab604 |
|||
timeCreated: 1536331325 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UIWidgets.ui { |
|||
public enum PointerChange { |
|||
cancel, |
|||
add, |
|||
remove, |
|||
hover, |
|||
down, |
|||
move, |
|||
up, |
|||
} |
|||
|
|||
public enum PointerDeviceKind { |
|||
touch, |
|||
mouse, |
|||
} |
|||
|
|||
public class PointerData { |
|||
public PointerData( |
|||
DateTime timeStamp, |
|||
PointerChange change, |
|||
PointerDeviceKind kind, |
|||
int device, |
|||
double physicalX, |
|||
double physicalY) { |
|||
this.timeStamp = timeStamp; |
|||
this.change = change; |
|||
this.kind = kind; |
|||
this.device = device; |
|||
this.physicalX = physicalX; |
|||
this.physicalY = physicalY; |
|||
} |
|||
|
|||
public readonly DateTime timeStamp; |
|||
public PointerChange change; |
|||
public PointerDeviceKind kind; |
|||
public int device; |
|||
public double physicalX; |
|||
public double physicalY; |
|||
} |
|||
|
|||
public class PointerDataPacket { |
|||
public PointerDataPacket(List<PointerData> data) { |
|||
this.data = data; |
|||
} |
|||
|
|||
public readonly List<PointerData> data; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b5409a8bf1ca47a187b9a230dbcdcd98 |
|||
timeCreated: 1536048980 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class FontEntry |
|||
{ |
|||
public readonly Font font; |
|||
private int _textureBuildVersion = 0; |
|||
|
|||
public FontEntry(Font font) |
|||
{ |
|||
this.font = font; |
|||
|
|||
} |
|||
|
|||
public int textureBuildVersion |
|||
{ |
|||
get { return _textureBuildVersion; } |
|||
} |
|||
|
|||
internal void onFontTextureRebuild() |
|||
{ |
|||
_textureBuildVersion++; |
|||
} |
|||
} |
|||
|
|||
public class FontManager |
|||
{ |
|||
private List<FontEntry> _fonts = new List<FontEntry>(); |
|||
|
|||
public static readonly FontManager instance = new FontManager(); |
|||
|
|||
private FontManager() |
|||
{ |
|||
Font.textureRebuilt += this.onFontTextureRebuilt; |
|||
} |
|||
|
|||
public FontEntry getOrCreate(string[] names, int fontSize) |
|||
{ |
|||
var founded = _fonts.Find((font) => |
|||
( |
|||
font.font.fontSize == fontSize && |
|||
(names == font.font.fontNames || (names != null && names.SequenceEqual(font.font.fontNames))))); |
|||
if (founded != null) |
|||
{ |
|||
return founded; |
|||
} |
|||
|
|||
Debug.Log(string.Format("Create new Font names={0}, size={1}", names, fontSize)); |
|||
var newFont = new FontEntry(Font.CreateDynamicFontFromOSFont(names, |
|||
fontSize)); |
|||
_fonts.Add(newFont); |
|||
return newFont; |
|||
} |
|||
|
|||
public FontEntry getOrCreate(string name, int fontSize) |
|||
{ |
|||
return getOrCreate(new []{name}, fontSize); |
|||
} |
|||
|
|||
private void onFontTextureRebuilt(Font font) |
|||
{ |
|||
var entry = _fonts.Find((f) => f.font == font); |
|||
if (entry != null) |
|||
{ |
|||
entry.onFontTextureRebuild(); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b643b1d2b3f14560bfa4b97bfdb7422a |
|||
timeCreated: 1536220942 |
|
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class TextMesh: IMesh |
|||
{ |
|||
private Mesh _mesh; |
|||
private FontEntry _fontEntry; |
|||
private string _text; |
|||
private StyledRuns.Run _run; |
|||
private int _textureVersion; |
|||
|
|||
public TextMesh(Vector2d pos, string text, Vector2d[] _characterPositions, FontEntry fontEntry, StyledRuns.Run run) |
|||
{ |
|||
_fontEntry = fontEntry; |
|||
_text = text; |
|||
this._run = run; |
|||
|
|||
var vertices = new Vector3[_text.Length * 4]; |
|||
var triangles = new int[_text.Length * 6]; |
|||
var font = fontEntry.font; |
|||
var offset = new Vector3((float)Utils.PixelCorrectRound(pos.x), (float)Utils.PixelCorrectRound(pos.y), 0); |
|||
font.RequestCharactersInTexture(_text.Substring(_run.start, _run.end - _run.start), |
|||
_run.style.UnityFontSize, _run.style.UnityFontStyle); |
|||
for (int charIndex = _run.start; charIndex < _run.end; ++charIndex) |
|||
{ |
|||
CharacterInfo charInfo = new CharacterInfo(); |
|||
if (_text[charIndex] != '\n' && _text[charIndex] != '\t') |
|||
{ |
|||
Debug.Assert(font.GetCharacterInfo(_text[charIndex], out charInfo, _run.style.UnityFontSize, _run.style.UnityFontStyle)); |
|||
var position = _characterPositions[charIndex]; |
|||
vertices[4 * charIndex + 0] = offset + new Vector3((float)(position.x + charInfo.minX), |
|||
(float)(position.y - charInfo.maxY), 0); |
|||
vertices[4 * charIndex + 1] = offset + new Vector3((float)(position.x + charInfo.maxX), |
|||
(float)(position.y - charInfo.maxY), 0); |
|||
vertices[4 * charIndex + 2] = offset + new Vector3( |
|||
(float)(position.x + charInfo.maxX), (float)(position.y - charInfo.minY), 0); |
|||
vertices[4 * charIndex + 3] = offset + new Vector3( |
|||
(float)(position.x + charInfo.minX), (float)(position.y - charInfo.minY), 0); |
|||
} |
|||
else |
|||
{ |
|||
vertices[4 * charIndex + 0] = vertices[4 * charIndex + 1] = |
|||
vertices[4 * charIndex + 2] = vertices[4 * charIndex + 3] = offset; |
|||
} |
|||
|
|||
triangles[6 * charIndex + 0] = 4 * charIndex + 0; |
|||
triangles[6 * charIndex + 1] = 4 * charIndex + 1; |
|||
triangles[6 * charIndex + 2] = 4 * charIndex + 2; |
|||
|
|||
triangles[6 * charIndex + 3] = 4 * charIndex + 0; |
|||
triangles[6 * charIndex + 4] = 4 * charIndex + 2; |
|||
triangles[6 * charIndex + 5] = 4 * charIndex + 3; |
|||
} |
|||
|
|||
var uv = getTextureUV(); |
|||
var mesh = new Mesh() |
|||
{ |
|||
vertices = vertices, |
|||
triangles = triangles, |
|||
uv = uv |
|||
}; |
|||
var colors = new UnityEngine.Color[vertices.Length]; |
|||
for (var i = 0; i < colors.Length; i++) |
|||
{ |
|||
colors[i] = _run.style.UnityColor; |
|||
} |
|||
|
|||
mesh.colors = colors; |
|||
_textureVersion = _fontEntry.textureBuildVersion; |
|||
_mesh = mesh; |
|||
} |
|||
|
|||
public void syncTextureUV() |
|||
{ |
|||
if (_fontEntry.textureBuildVersion != _textureVersion) // texture has been rebuilt, update the texture uv
|
|||
{ |
|||
_mesh.uv = getTextureUV(); |
|||
} |
|||
} |
|||
|
|||
public Mesh mesh |
|||
{ |
|||
get { return _mesh; } |
|||
} |
|||
|
|||
private Vector2[] getTextureUV() |
|||
{ |
|||
var font = _fontEntry.font; |
|||
var uv = _mesh == null ? new Vector2[_text.Length * 4] : _mesh.uv; |
|||
|
|||
for (int charIndex = _run.start; charIndex < _run.end; ++charIndex) |
|||
{ |
|||
CharacterInfo charInfo = new CharacterInfo(); |
|||
if (_text[charIndex] != '\n' && _text[charIndex] != '\t') |
|||
{ |
|||
font.GetCharacterInfo(_text[charIndex], out charInfo, _run.style.UnityFontSize, |
|||
_run.style.UnityFontStyle); |
|||
} |
|||
|
|||
if (Paragraph.isWordSpace(_text[charIndex]) || Paragraph.isLineEndSpace(_text[charIndex]) || _text[charIndex] == '\t') |
|||
{ |
|||
uv[4 * charIndex + 0] = Vector2.zero; |
|||
uv[4 * charIndex + 1] = Vector2.zero; |
|||
uv[4 * charIndex + 2] = Vector2.zero; |
|||
uv[4 * charIndex + 3] = Vector2.zero; |
|||
} else |
|||
{ |
|||
uv[4 * charIndex + 0] = charInfo.uvTopLeft; |
|||
uv[4 * charIndex + 1] = charInfo.uvTopRight; |
|||
uv[4 * charIndex + 2] = charInfo.uvBottomRight; |
|||
uv[4 * charIndex + 3] = charInfo.uvBottomLeft; |
|||
} |
|||
} |
|||
|
|||
return uv; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 96ec1d4b2b7d4bf59d4b2db221323ae9 |
|||
timeCreated: 1536283931 |
|
|||
using System; |
|||
using UnityEditor; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
internal class Utils |
|||
{ |
|||
public static double PixelCorrectRound(double v) |
|||
{ |
|||
return Math.Round(v * EditorGUIUtility.pixelsPerPoint) / EditorGUIUtility.pixelsPerPoint; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: cd4e5380cbc9438a8d2c8a060b110ea1 |
|||
timeCreated: 1536199372 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.foundation; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.async { |
|||
public class MicrotaskQueue { |
|||
private Queue<Action> _queue = new Queue<Action>(); |
|||
|
|||
public void scheduleMicrotask(Action action) { |
|||
this._queue.Enqueue(action); |
|||
} |
|||
|
|||
public void flushMicrotasks() { |
|||
while (this._queue.isNotEmpty()) { |
|||
var action = this._queue.Dequeue(); |
|||
try { |
|||
action(); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogError("Error to execute microtask: " + ex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6d95e65550134a478f8f77093fc6d6f1 |
|||
timeCreated: 1535723558 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UIWidgets.async { |
|||
public class PriorityQueue<T> where T : IComparable<T> { |
|||
private readonly List<T> _data; |
|||
|
|||
public PriorityQueue() { |
|||
this._data = new List<T>(); |
|||
} |
|||
|
|||
public void enqueue(T item) { |
|||
this._data.Add(item); |
|||
int ci = this._data.Count - 1; // child index; start at end
|
|||
while (ci > 0) { |
|||
int pi = (ci - 1) / 2; // parent index
|
|||
if (this._data[ci].CompareTo(this._data[pi]) >= 0) { |
|||
break; // child item is larger than (or equal) parent so we're done
|
|||
} |
|||
|
|||
T tmp = this._data[ci]; |
|||
this._data[ci] = this._data[pi]; |
|||
this._data[pi] = tmp; |
|||
ci = pi; |
|||
} |
|||
} |
|||
|
|||
public T dequeue() { |
|||
// assumes pq is not empty; up to calling code
|
|||
int li = this._data.Count - 1; // last index (before removal)
|
|||
T frontItem = this._data[0]; // fetch the front
|
|||
this._data[0] = this._data[li]; |
|||
this._data.RemoveAt(li); |
|||
|
|||
--li; // last index (after removal)
|
|||
int pi = 0; // parent index. start at front of pq
|
|||
while (true) { |
|||
int ci = pi * 2 + 1; // left child index of parent
|
|||
if (ci > li) { |
|||
break; // no children so done
|
|||
} |
|||
|
|||
int rc = ci + 1; // right child
|
|||
if (rc <= li && this._data[rc].CompareTo(this._data[ci]) < 0) { |
|||
// if there is a rc (ci + 1), and it is smaller than left child, use the rc instead
|
|||
ci = rc; |
|||
} |
|||
|
|||
if (this._data[pi].CompareTo(this._data[ci]) <= 0) { |
|||
break; // parent is smaller than (or equal to) smallest child so done
|
|||
} |
|||
|
|||
T tmp = this._data[pi]; |
|||
this._data[pi] = this._data[ci]; |
|||
this._data[ci] = tmp; // swap parent and child
|
|||
pi = ci; |
|||
} |
|||
|
|||
return frontItem; |
|||
} |
|||
|
|||
public T peek() { |
|||
T frontItem = this._data[0]; |
|||
return frontItem; |
|||
} |
|||
|
|||
public int count { |
|||
get { return this._data.Count; } |
|||
} |
|||
|
|||
public override string ToString() { |
|||
string s = ""; |
|||
for (int i = 0; i < this._data.Count; ++i) { |
|||
s += this._data[i] + " "; |
|||
} |
|||
|
|||
s += "count = " + this._data.Count; |
|||
return s; |
|||
} |
|||
|
|||
public bool isConsistent() { |
|||
// is the heap property true for all data?
|
|||
if (this._data.Count == 0) { |
|||
return true; |
|||
} |
|||
|
|||
int li = this._data.Count - 1; // last index
|
|||
for (int pi = 0; pi < this._data.Count; ++pi) { |
|||
// each parent index
|
|||
int lci = 2 * pi + 1; // left child index
|
|||
int rci = 2 * pi + 2; // right child index
|
|||
|
|||
if (lci <= li && this._data[pi].CompareTo(this._data[lci]) > 0) { |
|||
return false; // if lc exists and it's greater than parent then bad.
|
|||
} |
|||
|
|||
if (rci <= li && this._data[pi].CompareTo(this._data[rci]) > 0) { |
|||
return false; // check the right child too.
|
|||
} |
|||
} |
|||
|
|||
return true; // passed all checks
|
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2d77ab33460640ddb5e3abbb2a11da34 |
|||
timeCreated: 1536040675 |
|
|||
using System; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.async { |
|||
public abstract class Timer { |
|||
public abstract void cancel(); |
|||
} |
|||
|
|||
public class TimerProvider { |
|||
private readonly PriorityQueue<TimerImpl> _queue; |
|||
|
|||
public TimerProvider() { |
|||
this._queue = new PriorityQueue<TimerImpl>(); |
|||
} |
|||
|
|||
public Timer run(TimeSpan duration, Action callback) { |
|||
var timer = new TimerImpl(DateTime.Now + duration, callback); |
|||
this._queue.enqueue(timer); |
|||
|
|||
return timer; |
|||
} |
|||
|
|||
public void update() { |
|||
var now = DateTime.Now; |
|||
|
|||
while (this._queue.count > 0 && this._queue.peek().deadline <= now) { |
|||
var timer = this._queue.dequeue(); |
|||
timer.invoke(); |
|||
} |
|||
} |
|||
|
|||
private class TimerImpl : Timer, IComparable<TimerImpl> { |
|||
public readonly DateTime deadline; |
|||
private readonly Action _callback; |
|||
private bool _done; |
|||
|
|||
public TimerImpl(DateTime deadline, Action callback) { |
|||
this.deadline = deadline; |
|||
this._callback = callback; |
|||
this._done = false; |
|||
} |
|||
|
|||
public override void cancel() { |
|||
this._done = true; |
|||
} |
|||
|
|||
public void invoke() { |
|||
if (this._done) { |
|||
return; |
|||
} |
|||
|
|||
this._done = true; |
|||
|
|||
try { |
|||
this._callback(); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogError("Error to execute timer callback: " + ex); |
|||
} |
|||
} |
|||
|
|||
public int CompareTo(TimerImpl other) { |
|||
return this.deadline.CompareTo(other.deadline); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 22e7b4cca5d354a4e9aa008cafcfdfe8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue