gewentao
6 年前
当前提交
389e013b
共有 43 个文件被更改,包括 4031 次插入 和 24 次删除
-
71Assets/UIWidgets/Tests/CanvasAndLayers.cs
-
25Assets/UIWidgets/painting/binding.cs
-
18Assets/UIWidgets/painting/box_border.cs
-
147Assets/UIWidgets/painting/decoration_image.cs
-
23Assets/UIWidgets/rendering/box.cs
-
8Assets/UIWidgets/ui/geometry.cs
-
12Assets/UIWidgets/ui/painting/canvas.cs
-
10Assets/UIWidgets/ui/painting/canvas_impl.cs
-
8Assets/UIWidgets/ui/painting/draw_cmd.cs
-
89Assets/UIWidgets/ui/painting/painting.cs
-
4Assets/UIWidgets/ui/painting/picture.cs
-
182Assets/UIWidgets/painting/alignment.cs
-
3Assets/UIWidgets/painting/alignment.cs.meta
-
130Assets/UIWidgets/painting/box_fit.cs
-
3Assets/UIWidgets/painting/box_fit.cs.meta
-
168Assets/UIWidgets/painting/image_cache.cs
-
3Assets/UIWidgets/painting/image_cache.cs.meta
-
109Assets/UIWidgets/painting/image_provider.cs
-
3Assets/UIWidgets/painting/image_provider.cs.meta
-
135Assets/UIWidgets/painting/image_stream.cs
-
3Assets/UIWidgets/painting/image_stream.cs.meta
-
8Assets/UIWidgets/promise.meta
-
281Assets/UIWidgets/rendering/image.cs
-
3Assets/UIWidgets/rendering/image.cs.meta
-
21Assets/UIWidgets/ui/painting/image.cs
-
3Assets/UIWidgets/ui/painting/image.cs.meta
-
41Assets/UIWidgets/promise/EnumerableExt.cs
-
3Assets/UIWidgets/promise/EnumerableExt.cs.meta
-
1001Assets/UIWidgets/promise/Promise.cs
-
3Assets/UIWidgets/promise/Promise.cs.meta
-
19Assets/UIWidgets/promise/PromiseException.cs
-
3Assets/UIWidgets/promise/PromiseException.cs.meta
-
82Assets/UIWidgets/promise/PromiseHelpers.cs
-
3Assets/UIWidgets/promise/PromiseHelpers.cs.meta
-
22Assets/UIWidgets/promise/PromiseStateException.cs
-
3Assets/UIWidgets/promise/PromiseStateException.cs.meta
-
247Assets/UIWidgets/promise/PromiseTimer.cs
-
3Assets/UIWidgets/promise/PromiseTimer.cs.meta
-
1001Assets/UIWidgets/promise/Promise_NonGeneric.cs
-
3Assets/UIWidgets/promise/Promise_NonGeneric.cs.meta
-
148Assets/UIWidgets/promise/Tuple.cs
-
3Assets/UIWidgets/promise/Tuple.cs.meta
|
|||
namespace UIWidgets.painting { |
|||
public abstract class PaintingBinding { |
|||
public class PaintingBinding |
|||
{ |
|||
private static PaintingBinding _instance; |
|||
|
|||
public static PaintingBinding instance |
|||
{ |
|||
get { return _instance; } |
|||
} |
|||
|
|||
private ImageCache _imageCache; |
|||
|
|||
public ImageCache imageCache |
|||
{ |
|||
get { return _imageCache; } |
|||
} |
|||
|
|||
public ImageCache createImageCache() |
|||
{ |
|||
return new ImageCache(); |
|||
} |
|||
public void initInstances() { |
|||
_instance = this; |
|||
_imageCache = createImageCache(); |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using UIWidgets.ui; |
|||
using System.Collections.Generic; |
|||
namespace UIWidgets.painting |
|||
{ |
|||
/// How to paint any portions of a box not covered by an image.
|
|||
public enum ImageRepeat |
|||
{ |
|||
/// Repeat the image in both the x and y directions until the box is filled.
|
|||
repeat, |
|||
namespace UIWidgets.painting { |
|||
public class DecorationImage { |
|||
|
|||
/// Repeat the image in the x direction until the box is filled horizontally.
|
|||
repeatX, |
|||
|
|||
/// Repeat the image in the y direction until the box is filled vertically.
|
|||
repeatY, |
|||
|
|||
/// Leave uncovered portions of the box transparent.
|
|||
noRepeat, |
|||
} |
|||
|
|||
public class DecorationImage |
|||
{ |
|||
public DecorationImage() |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public static class DecorationImageUtil |
|||
{ |
|||
public static void paintImage(Canvas canvas, Rect rect, ui.Image image, BoxFit fit, Rect centerSlice, Alignment alignment = null, |
|||
ImageRepeat repeat = ImageRepeat.noRepeat, bool flipHorizontally = false) // todo more parameters
|
|||
{ |
|||
if (rect.isEmpty) |
|||
return; |
|||
alignment = alignment ?? Alignment.center; |
|||
Size outputSize = rect.size; |
|||
Size inputSize = new Size(image.width, image.height); |
|||
Offset sliceBorder = null; |
|||
if (centerSlice != null) |
|||
{ |
|||
sliceBorder = new Offset( |
|||
centerSlice.left + inputSize.width - centerSlice.right, |
|||
centerSlice.top + inputSize.height - centerSlice.bottom |
|||
); |
|||
outputSize -= sliceBorder; |
|||
inputSize -= sliceBorder; |
|||
} |
|||
|
|||
fit = centerSlice == null ? BoxFit.scaleDown : BoxFit.fill; |
|||
FittedSizes fittedSizes = FittedSizes.applyBoxFit(fit, inputSize, outputSize); |
|||
Size sourceSize = fittedSizes.source; |
|||
Size destinationSize = fittedSizes.destination; |
|||
if (centerSlice != null) |
|||
{ |
|||
outputSize += sliceBorder; |
|||
destinationSize += sliceBorder; |
|||
} |
|||
if (repeat != ImageRepeat.noRepeat && destinationSize == outputSize) { |
|||
repeat = ImageRepeat.noRepeat; |
|||
} |
|||
|
|||
Paint paint = new Paint(); // ..isAntiAlias = false;
|
|||
// if (colorFilter != null)
|
|||
// paint.colorFilter = colorFilter;
|
|||
if (sourceSize != destinationSize) { |
|||
// Use the "low" quality setting to scale the image, which corresponds to
|
|||
// bilinear interpolation, rather than the default "none" which corresponds
|
|||
// to nearest-neighbor.
|
|||
// paint.filterQuality = FilterQuality.low;
|
|||
} |
|||
double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0; |
|||
double halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0; |
|||
double dx = halfWidthDelta + (flipHorizontally ? - alignment.x : alignment.x) * halfWidthDelta; |
|||
double dy = halfHeightDelta + alignment.y * halfHeightDelta; |
|||
Offset destinationPosition = rect.topLeft.translate(dx, dy); |
|||
Rect destinationRect = destinationPosition & destinationSize; |
|||
// todo repeat and flip
|
|||
// bool needSave = repeat != ImageRepeat.noRepeat || flipHorizontally;
|
|||
// if (needSave)
|
|||
// canvas.save();
|
|||
// if (repeat != ImageRepeat.noRepeat)
|
|||
// canvas.clipRect(rect);
|
|||
// if (flipHorizontally) {
|
|||
// dx = -(rect.left + rect.width / 2.0);
|
|||
// canvas.translate(-dx, 0.0);
|
|||
// canvas.scale(-1.0, 1.0);
|
|||
// canvas.translate(dx, 0.0);
|
|||
// }
|
|||
if (centerSlice == null) { |
|||
Rect sourceRect = alignment.inscribe( |
|||
fittedSizes.source, Offset.zero & inputSize |
|||
); |
|||
foreach (Rect tileRect in _generateImageTileRects(rect, destinationRect, repeat)) { |
|||
// canvas.drawImageRect(sourceRect, tileRect, paint, image);
|
|||
} |
|||
} else { |
|||
// todo
|
|||
foreach (Rect tileRect in _generateImageTileRects(rect, destinationRect, repeat)) |
|||
{ |
|||
// canvas.drawImageNine(image, centerSlice, tileRect, paint);
|
|||
} |
|||
} |
|||
// if (needSave)
|
|||
// canvas.restore();
|
|||
} |
|||
|
|||
public static List<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, |
|||
ImageRepeat repeat) |
|||
{ |
|||
List<Rect> tileRects = new List<Rect>(); |
|||
if (repeat == ImageRepeat.noRepeat) |
|||
{ |
|||
tileRects.Add(fundamentalRect); |
|||
return tileRects; |
|||
} |
|||
|
|||
int startX = 0; |
|||
int startY = 0; |
|||
int stopX = 0; |
|||
int stopY = 0; |
|||
double strideX = fundamentalRect.width; |
|||
double strideY = fundamentalRect.height; |
|||
|
|||
if (repeat == ImageRepeat.repeat || repeat == ImageRepeat.repeatX) |
|||
{ |
|||
startX = (int) Math.Floor((outputRect.left - fundamentalRect.left) / strideX); |
|||
stopX = (int) Math.Ceiling((outputRect.right - fundamentalRect.right) / strideX); |
|||
} |
|||
|
|||
if (repeat == ImageRepeat.repeat || repeat == ImageRepeat.repeatY) |
|||
{ |
|||
startY = (int) Math.Floor((outputRect.top - fundamentalRect.top) / strideY); |
|||
stopY = (int) Math.Ceiling((outputRect.bottom - fundamentalRect.bottom) / strideY); |
|||
} |
|||
|
|||
for (int i = startX; i <= stopX; ++i) |
|||
{ |
|||
for (int j = startY; j <= stopY; ++j) |
|||
tileRects.Add(fundamentalRect.shift(new Offset(i * strideX, j * strideY))); |
|||
// yield return fundamentalRect.shift(new Offset(i * strideX, j * strideY));
|
|||
} |
|||
|
|||
return tileRects; |
|||
} |
|||
} |
|||
} |
|
|||
using UIWidgets.ui; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
// todo should be in text.cs
|
|||
public enum TextDirection |
|||
{ |
|||
/// The text flows from right to left (e.g. Arabic, Hebrew).
|
|||
rtl, |
|||
|
|||
/// The text flows from left to right (e.g., English, French).
|
|||
ltr, |
|||
} |
|||
|
|||
public abstract class AlignmentGeometry |
|||
{ |
|||
public AlignmentGeometry() |
|||
{ |
|||
} |
|||
|
|||
public abstract double x { get; } |
|||
public abstract double start { get; } |
|||
public abstract double y { get; } |
|||
|
|||
AlignmentGeometry add(AlignmentGeometry other) |
|||
{ |
|||
return new _MixedAlignment( |
|||
x + other.x, |
|||
start + other.start, |
|||
y + other.y |
|||
); |
|||
} |
|||
|
|||
public virtual Alignment resolve(TextDirection direction) |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public class Alignment : AlignmentGeometry |
|||
{ |
|||
public Alignment(double x, double y) |
|||
{ |
|||
this._x = x; |
|||
this._y = y; |
|||
} |
|||
|
|||
private readonly double _x; |
|||
|
|||
public override double x |
|||
{ |
|||
get { return _x; } |
|||
} |
|||
|
|||
private readonly double _y; |
|||
|
|||
public override double y |
|||
{ |
|||
get { return _y; } |
|||
} |
|||
|
|||
public override double start |
|||
{ |
|||
get { return 0.0; } |
|||
} |
|||
|
|||
public static readonly Alignment topLeft = new Alignment(-1.0, -1.0); |
|||
public static readonly Alignment topCenter = new Alignment(0.0, -1.0); |
|||
public static readonly Alignment topRight = new Alignment(1.0, -1.0); |
|||
public static readonly Alignment centerLeft = new Alignment(-1.0, 0.0); |
|||
public static readonly Alignment center = new Alignment(0.0, 0.0); |
|||
public static readonly Alignment centerRight = new Alignment(1.0, 0.0); |
|||
public static readonly Alignment bottomLeft = new Alignment(-1.0, 1.0); |
|||
public static readonly Alignment bottomCenter = new Alignment(0.0, 1.0); |
|||
public static readonly Alignment bottomRight = new Alignment(1.0, 1.0); |
|||
|
|||
public static Alignment operator -(Alignment a, Alignment b) |
|||
{ |
|||
return new Alignment(a._x - b._x, a._y - b._y); |
|||
} |
|||
|
|||
// todo more operators
|
|||
|
|||
public override Alignment resolve(TextDirection direction) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
public Rect inscribe(Size size, Rect rect) |
|||
{ |
|||
double halfWidthDelta = (rect.width - size.width) / 2.0; |
|||
double halfHeightDelta = (rect.height - size.height) / 2.0; |
|||
return Rect.fromLTWH( |
|||
rect.left + halfWidthDelta + _x * halfWidthDelta, |
|||
rect.top + halfHeightDelta + _y * halfHeightDelta, |
|||
size.width, |
|||
size.height |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class _MixedAlignment : AlignmentGeometry |
|||
{ |
|||
public _MixedAlignment(double x, double start, double y) |
|||
{ |
|||
this._x = x; |
|||
this._start = start; |
|||
this._y = y; |
|||
} |
|||
|
|||
private readonly double _x; |
|||
|
|||
public override double x |
|||
{ |
|||
get { return _x; } |
|||
} |
|||
|
|||
private readonly double _start; |
|||
|
|||
public override double start |
|||
{ |
|||
get { return _start; } |
|||
} |
|||
|
|||
private readonly double _y; |
|||
|
|||
public override double y |
|||
{ |
|||
get { return _y; } |
|||
} |
|||
|
|||
|
|||
public static _MixedAlignment operator -(_MixedAlignment a) |
|||
{ |
|||
return new _MixedAlignment( |
|||
-a._x, |
|||
-a._start, |
|||
-a._y |
|||
); |
|||
} |
|||
|
|||
public static _MixedAlignment operator *(_MixedAlignment a, double other) |
|||
{ |
|||
return new _MixedAlignment( |
|||
a._x * other, |
|||
a._start * other, |
|||
a._y * other |
|||
); |
|||
} |
|||
|
|||
public static _MixedAlignment operator /(_MixedAlignment a, double other) |
|||
{ |
|||
return new _MixedAlignment( |
|||
a._x / other, |
|||
a._start / other, |
|||
a._y / other |
|||
); |
|||
} |
|||
|
|||
public static _MixedAlignment operator %(_MixedAlignment a, double other) |
|||
{ |
|||
return new _MixedAlignment( |
|||
a._x % other, |
|||
a._start % other, |
|||
a._y % other |
|||
); |
|||
} |
|||
|
|||
public override Alignment resolve(TextDirection direction) |
|||
{ |
|||
switch (direction) |
|||
{ |
|||
case TextDirection.rtl: |
|||
return new Alignment(_x - _start, _y); |
|||
case TextDirection.ltr: |
|||
return new Alignment(_x + _start, _y); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 52b90656e86c4a39af38d0518a67f68b |
|||
timeCreated: 1534820611 |
|
|||
using UIWidgets.ui; |
|||
using System; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public enum BoxFit |
|||
{ |
|||
/// Fill the target box by distorting the source's aspect ratio.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fill.png)
|
|||
fill, |
|||
|
|||
/// As large as possible while still containing the source entirely within the
|
|||
/// target box.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_contain.png)
|
|||
contain, |
|||
|
|||
/// As small as possible while still covering the entire target box.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_cover.png)
|
|||
cover, |
|||
|
|||
/// Make sure the full width of the source is shown, regardless of
|
|||
/// whether this means the source overflows the target box vertically.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitWidth.png)
|
|||
fitWidth, |
|||
|
|||
/// Make sure the full height of the source is shown, regardless of
|
|||
/// whether this means the source overflows the target box horizontally.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitHeight.png)
|
|||
fitHeight, |
|||
|
|||
/// Align the source within the target box (by default, centering) and discard
|
|||
/// any portions of the source that lie outside the box.
|
|||
///
|
|||
/// The source image is not resized.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_none.png)
|
|||
none, |
|||
|
|||
/// Align the source within the target box (by default, centering) and, if
|
|||
/// necessary, scale the source down to ensure that the source fits within the
|
|||
/// box.
|
|||
///
|
|||
/// This is the same as `contain` if that would shrink the image, otherwise it
|
|||
/// is the same as `none`.
|
|||
///
|
|||
/// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_scaleDown.png)
|
|||
scaleDown, |
|||
} |
|||
|
|||
public class FittedSizes |
|||
{ |
|||
public FittedSizes(Size source, Size destination) |
|||
{ |
|||
//todo wrong
|
|||
this.source = source; |
|||
this.destination = destination; |
|||
} |
|||
|
|||
public Size source; |
|||
public Size destination; |
|||
|
|||
public static FittedSizes applyBoxFit(BoxFit fit, Size inputSize, Size outputSize) |
|||
{ |
|||
if (inputSize.height <= 0.0 || inputSize.width <= 0.0 || outputSize.height <= 0.0 || |
|||
outputSize.width <= 0.0) |
|||
return new FittedSizes(Size.zero, Size.zero); |
|||
Size sourceSize = null; |
|||
Size destinationSize = null; |
|||
switch (fit) |
|||
{ |
|||
case BoxFit.fill: |
|||
sourceSize = inputSize; |
|||
destinationSize = outputSize; |
|||
break; |
|||
case BoxFit.contain: |
|||
sourceSize = inputSize; |
|||
if (outputSize.width / outputSize.height > sourceSize.width / sourceSize.height) |
|||
destinationSize = new Size(sourceSize.width * outputSize.height / sourceSize.height, |
|||
outputSize.height); |
|||
else |
|||
destinationSize = new Size(outputSize.width, |
|||
sourceSize.height * outputSize.width / sourceSize.width); |
|||
break; |
|||
case BoxFit.cover: |
|||
if (outputSize.width / outputSize.height > inputSize.width / inputSize.height) |
|||
{ |
|||
sourceSize = new Size(inputSize.width, inputSize.width * outputSize.height / outputSize.width); |
|||
} |
|||
else |
|||
{ |
|||
sourceSize = new Size(inputSize.height * outputSize.width / outputSize.height, |
|||
inputSize.height); |
|||
} |
|||
|
|||
destinationSize = outputSize; |
|||
break; |
|||
case BoxFit.fitWidth: |
|||
sourceSize = new Size(inputSize.width, inputSize.width * outputSize.height / outputSize.width); |
|||
destinationSize = new Size(outputSize.width, |
|||
sourceSize.height * outputSize.width / sourceSize.width); |
|||
break; |
|||
case BoxFit.fitHeight: |
|||
sourceSize = new Size(inputSize.height * outputSize.width / outputSize.height, inputSize.height); |
|||
destinationSize = new Size(sourceSize.width * outputSize.height / sourceSize.height, |
|||
outputSize.height); |
|||
break; |
|||
case BoxFit.none: |
|||
sourceSize = new Size(Math.Min(inputSize.width, outputSize.width), |
|||
Math.Min(inputSize.height, outputSize.height)); |
|||
destinationSize = sourceSize; |
|||
break; |
|||
case BoxFit.scaleDown: |
|||
sourceSize = inputSize; |
|||
destinationSize = inputSize; |
|||
double aspectRatio = inputSize.width / inputSize.height; |
|||
if (destinationSize.height > outputSize.height) |
|||
destinationSize = new Size(outputSize.height * aspectRatio, outputSize.height); |
|||
if (destinationSize.width > outputSize.width) |
|||
destinationSize = new Size(outputSize.width, outputSize.width / aspectRatio); |
|||
break; |
|||
} |
|||
return new FittedSizes(sourceSize, destinationSize); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4d75ec15a75b4a7baca1968978b1eec9 |
|||
timeCreated: 1534820694 |
|
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using UnityEngine; |
|||
using Object = System.Object; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public class ImageCache |
|||
{ |
|||
private const int _kDefaultSize = 1000; |
|||
private const int _kDefaultSizeBytes = 20 << 20; // 20 MiB
|
|||
|
|||
public Dictionary<Object, ImageStreamCompleter> _pendingImages = |
|||
new Dictionary<Object, ImageStreamCompleter>(); |
|||
|
|||
public Dictionary<Object, _CachedImage> _cache = new Dictionary<Object, _CachedImage>(); |
|||
public LinkedList<Object> _lruKeys = new LinkedList<Object>(); |
|||
|
|||
private int _maximumSize = _kDefaultSize; |
|||
|
|||
public int maximumSize |
|||
{ |
|||
get { return _maximumSize; } |
|||
set |
|||
{ |
|||
if (value == maximumSize) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_maximumSize = value; |
|||
if (maximumSize == 0) |
|||
{ |
|||
_cache.Clear(); |
|||
_lruKeys.Clear(); |
|||
_currentSizeBytes = 0; |
|||
} |
|||
else |
|||
{ |
|||
_checkCacheSize(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public int currentSize |
|||
{ |
|||
get { return _cache.Count; } |
|||
} |
|||
|
|||
private int _maximumSizeBytes = _kDefaultSizeBytes; |
|||
|
|||
public int maximumSizeBytes |
|||
{ |
|||
get { return _maximumSizeBytes; } |
|||
set |
|||
{ |
|||
if (value == _maximumSizeBytes) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_maximumSizeBytes = value; |
|||
if (_maximumSizeBytes == 0) |
|||
{ |
|||
_cache.Clear(); |
|||
_lruKeys.Clear(); |
|||
_currentSizeBytes = 0; |
|||
} |
|||
else |
|||
{ |
|||
_checkCacheSize(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private int _currentSizeBytes; |
|||
|
|||
public int currentSizeBytes |
|||
{ |
|||
get { return _currentSizeBytes; } |
|||
} |
|||
|
|||
public void clear() |
|||
{ |
|||
_cache.Clear(); |
|||
_lruKeys.Clear(); |
|||
_currentSizeBytes = 0; |
|||
} |
|||
|
|||
public delegate ImageStreamCompleter Loader(); |
|||
|
|||
[MethodImpl(MethodImplOptions.Synchronized)] |
|||
public ImageStreamCompleter putIfAbsent(Object key, Loader loader) |
|||
{ |
|||
ImageStreamCompleter result; |
|||
if (_pendingImages.TryGetValue(key, out result)) |
|||
{ |
|||
return result; |
|||
} |
|||
|
|||
_CachedImage image; |
|||
if (_cache.TryGetValue(key, out image)) |
|||
{ |
|||
// put to the MRU position
|
|||
_lruKeys.Remove(key); |
|||
_lruKeys.AddLast(key); |
|||
} |
|||
|
|||
if (image != null) |
|||
{ |
|||
return image.completer; |
|||
} |
|||
|
|||
result = loader(); |
|||
|
|||
if (maximumSize > 0 && maximumSizeBytes > 0) |
|||
{ |
|||
_pendingImages[key] = result; |
|||
result.addListener((info, syncCall) => |
|||
{ |
|||
// int imageSize = info.image == null ? 0 : info.image.height * info.image.width * 4;
|
|||
// now we use length or raw bytes array as image size
|
|||
int imageSize = info.image == null ? 0 : info.image.rawData.Length; |
|||
_CachedImage cachedImage = new _CachedImage(result, imageSize); |
|||
if (maximumSizeBytes > 0 && imageSize > maximumSizeBytes) |
|||
{ |
|||
_maximumSize = imageSize + 1000; |
|||
} |
|||
|
|||
_currentSizeBytes += imageSize; |
|||
_pendingImages.Remove(key); |
|||
_cache[key] = cachedImage; |
|||
_lruKeys.AddLast(key); |
|||
this._checkCacheSize(); |
|||
}, null); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
void _checkCacheSize() |
|||
{ |
|||
while (_currentSizeBytes > _maximumSizeBytes || _cache.Count > _maximumSize) |
|||
{ |
|||
Object key = _lruKeys.First.Value; // get the LRU item
|
|||
_CachedImage image = _cache[key]; |
|||
bool removed = _cache.Remove(key); |
|||
if (image != null && removed) |
|||
{ |
|||
_currentSizeBytes -= image.sizeBytes; |
|||
_lruKeys.Remove(key); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class _CachedImage |
|||
{ |
|||
public _CachedImage(ImageStreamCompleter completer, int sizeBytes) |
|||
{ |
|||
this.completer = completer; |
|||
this.sizeBytes = sizeBytes; |
|||
} |
|||
|
|||
public ImageStreamCompleter completer; |
|||
public int sizeBytes; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 80732e4248ce48ec92e1463ddc59451d |
|||
timeCreated: 1534833891 |
|
|||
using System.Collections.Generic; |
|||
using RSG; |
|||
using System.Net; |
|||
using System; |
|||
using System.IO; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public abstract class ImageProvider<T> |
|||
{ |
|||
// ImageStream resolve(ImageConfiguration configuration) {
|
|||
public ImageStream resolve(ImageConfiguration configuration) |
|||
{ |
|||
ImageStream stream = new ImageStream(); |
|||
T obtainedKey; |
|||
obtainedKey = obtainKey(configuration); |
|||
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(obtainedKey, () => load(obtainedKey))); |
|||
return stream; |
|||
} |
|||
|
|||
public abstract ImageStreamCompleter load(T key); |
|||
|
|||
public abstract T obtainKey(ImageConfiguration configuration); |
|||
} |
|||
|
|||
public class NetworkImage : ImageProvider<NetworkImage> |
|||
{ |
|||
public NetworkImage(string url, Dictionary<string, string> headers, double scale = 1.0) |
|||
{ |
|||
this.url = url; |
|||
this.headers = headers; |
|||
this.scale = scale; |
|||
} |
|||
|
|||
/// The URL from which the image will be fetched.
|
|||
string url; |
|||
|
|||
/// The scale to place in the [ImageInfo] object of the image.
|
|||
double scale; |
|||
|
|||
/// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
|
|||
Dictionary<string, string> headers; |
|||
|
|||
public override NetworkImage obtainKey(ImageConfiguration configuration) |
|||
{ |
|||
// return new SynchronousFuture<NetworkImage> (this);
|
|||
return this; |
|||
} |
|||
|
|||
public override ImageStreamCompleter load(NetworkImage key) |
|||
{ |
|||
return new OneFrameImageStreamCompleter(_loadAsync(key)); |
|||
} |
|||
|
|||
public static IPromise<ImageInfo> _loadAsync(NetworkImage key) |
|||
{ |
|||
var promise = new Promise<ImageInfo>(); // Create promise.
|
|||
using (var client = new WebClient()) |
|||
{ |
|||
client.DownloadDataCompleted += // Monitor event for download completed.
|
|||
(s, ev) => |
|||
{ |
|||
if (ev.Error != null) |
|||
{ |
|||
promise.Reject(ev.Error); // Error during download, reject the promise.
|
|||
} |
|||
else |
|||
{ |
|||
var bytes = ev.Result; |
|||
var imageInfo = new ImageInfo(new ui.Image( |
|||
bytes |
|||
)); |
|||
promise.Resolve(imageInfo); // Downloaded completed successfully, resolve the promise.
|
|||
} |
|||
}; |
|||
|
|||
client.DownloadDataAsync(new Uri(key.url)); // Initiate async op.
|
|||
} |
|||
|
|||
return promise; // Return the promise so the caller can await resolution (or error).
|
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return "NetworkImage with Url: " + this.url; |
|||
} |
|||
} |
|||
|
|||
public class ImageConfiguration |
|||
{ |
|||
public ImageConfiguration(Size size = null) |
|||
{ |
|||
this.size = size; |
|||
} |
|||
|
|||
public static readonly ImageConfiguration empty = new ImageConfiguration(); |
|||
|
|||
public ImageConfiguration copyWith(Size size = null) |
|||
{ |
|||
return new ImageConfiguration( |
|||
size: size ?? this.size |
|||
); |
|||
} |
|||
|
|||
public readonly Size size; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 858587f8fb5d435e8a07a0ff46e5bc95 |
|||
timeCreated: 1534820746 |
|
|||
using RSG; |
|||
using UIWidgets.ui; |
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
namespace UIWidgets.painting |
|||
{ |
|||
public delegate void ImageListener(ImageInfo image, bool synchronousCall); |
|||
|
|||
public delegate void ImageErrorListerner(System.Object exception, string stackTrack); |
|||
|
|||
public class ImageInfo |
|||
{ |
|||
public ImageInfo(Image image, double scale = 1.0) |
|||
{ |
|||
this.image = image; |
|||
this.scale = scale; |
|||
} |
|||
|
|||
public Image image; |
|||
public double scale; |
|||
} |
|||
|
|||
public class ImageStream |
|||
{ |
|||
public ImageStream() |
|||
{ |
|||
} |
|||
|
|||
private ImageStreamCompleter _completer; |
|||
private List<_ImageListenerPair> _listeners; |
|||
|
|||
public ImageStreamCompleter completer |
|||
{ |
|||
get { return _completer; } |
|||
} |
|||
|
|||
public void setCompleter(ImageStreamCompleter value) |
|||
{ |
|||
_completer = value; |
|||
if (_listeners != null) |
|||
{ |
|||
List<_ImageListenerPair> initialListeners = _listeners; |
|||
_listeners = null; |
|||
foreach (_ImageListenerPair listenerPair in initialListeners) |
|||
{ |
|||
_completer.addListener( |
|||
listenerPair.listener, |
|||
listenerPair.errorListener |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public abstract class ImageStreamCompleter |
|||
{ |
|||
public List<_ImageListenerPair> _listeners = new List<_ImageListenerPair>(); |
|||
public ImageInfo _currentImgae; |
|||
|
|||
public void addListener(ImageListener listener, ImageErrorListerner onError) |
|||
{ |
|||
this._listeners.Add(new _ImageListenerPair(listener, onError)); |
|||
if (_currentImgae != null) |
|||
{ |
|||
// todo refine
|
|||
try |
|||
{ |
|||
listener(_currentImgae, true); |
|||
this.removeListener(listener); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Console.WriteLine("{0} Exception caught.", e); |
|||
} |
|||
} |
|||
|
|||
// todo call onError
|
|||
} |
|||
|
|||
public void removeListener(ImageListener listener) |
|||
{ |
|||
var pairToRemove = this._listeners.Single(lp => lp.listener == listener); |
|||
this._listeners.Remove(pairToRemove); |
|||
} |
|||
|
|||
public void setImage(ImageInfo image) |
|||
{ |
|||
_currentImgae = image; |
|||
if (_listeners.Count == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var lp in _listeners.ToList()) |
|||
{ |
|||
// todo refine
|
|||
var listener = lp.listener; |
|||
try |
|||
{ |
|||
listener(image, false); |
|||
this.removeListener(listener); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Console.WriteLine("{0} Exception caught.", e); |
|||
} |
|||
// todo call onError
|
|||
} |
|||
} |
|||
} |
|||
|
|||
public class OneFrameImageStreamCompleter : ImageStreamCompleter |
|||
{ |
|||
public OneFrameImageStreamCompleter(IPromise<ImageInfo> image) |
|||
{ |
|||
image.Then(result => { setImage(result); }).Catch(err => { Debug.Log(err); }); |
|||
} |
|||
} |
|||
|
|||
public class _ImageListenerPair |
|||
{ |
|||
public _ImageListenerPair(ImageListener listener, ImageErrorListerner errorListener) |
|||
{ |
|||
this.listener = listener; |
|||
this.errorListener = errorListener; |
|||
} |
|||
|
|||
public ImageListener listener; |
|||
public ImageErrorListerner errorListener; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 462d84cff25d484895d1a81e7ebd2a24 |
|||
timeCreated: 1534820764 |
|
|||
fileFormatVersion: 2 |
|||
guid: e2adb93c961cc4d4e8d94fce276c0b57 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UIWidgets.ui; |
|||
using UIWidgets.painting; |
|||
using UnityEngine.Rendering; |
|||
using BlendMode = UIWidgets.ui.BlendMode; |
|||
|
|||
namespace UIWidgets.rendering |
|||
{ |
|||
class RenderImage : RenderBox |
|||
{ |
|||
public RenderImage(ui.Image image, |
|||
double width, |
|||
double height, |
|||
Color color, |
|||
ui.BlendMode colorBlendMode, |
|||
BoxFit fit, |
|||
ImageRepeat repeat, |
|||
Rect centerSlice, |
|||
// TextDirection textDirection,
|
|||
bool matchTextDirection = false, |
|||
AlignmentGeometry alignment = null, |
|||
double scale = 1.0 |
|||
) |
|||
{ |
|||
this._image = image; |
|||
this._width = width; |
|||
this._height = height; |
|||
this._scale = scale; |
|||
this._color = color; |
|||
this._colorBlendMode = colorBlendMode; |
|||
this._fit = fit; |
|||
this._repeat = repeat; |
|||
this._centerSlice = centerSlice; |
|||
// this._matchTextDirection = matchTextDirection;
|
|||
// this._textDir
|
|||
this._alignment = alignment ?? Alignment.center; |
|||
this._textDirection = textDirection; |
|||
_updateColorFilter(); |
|||
} |
|||
|
|||
Alignment _resolvedAlignment; |
|||
bool _flipHorizontally; |
|||
|
|||
void _resolve() |
|||
{ |
|||
if (_resolvedAlignment != null) |
|||
return; |
|||
_resolvedAlignment = alignment.resolve(textDirection); |
|||
_flipHorizontally = matchTextDirection && textDirection == TextDirection.rtl; |
|||
} |
|||
|
|||
void _markNeedsResolution() |
|||
{ |
|||
_resolvedAlignment = null; |
|||
_flipHorizontally = false; |
|||
markNeedsPaint(); |
|||
} |
|||
|
|||
private ui.Image _image; |
|||
|
|||
public ui.Image image |
|||
{ |
|||
get { return this._image; } |
|||
set |
|||
{ |
|||
if (value == _image) |
|||
return; |
|||
_image = value; |
|||
markNeedsPaint(); |
|||
if (_width == null || _height == null) |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
private double _width; |
|||
|
|||
public double width |
|||
{ |
|||
get { return _width; } |
|||
set |
|||
{ |
|||
if (value == _width) |
|||
return; |
|||
_width = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
private double _height; |
|||
|
|||
public double height |
|||
{ |
|||
get { return _height; } |
|||
set |
|||
{ |
|||
if (value == _height) |
|||
return; |
|||
_height = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
private double _scale; |
|||
|
|||
public double scale |
|||
{ |
|||
get { return _scale; } |
|||
set |
|||
{ |
|||
if (value == _scale) |
|||
return; |
|||
_scale = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
ColorFilter _colorFilter; |
|||
|
|||
void _updateColorFilter() |
|||
{ |
|||
if (_color == null) |
|||
_colorFilter = null; |
|||
else |
|||
{ |
|||
_colorFilter = new ColorFilter(_color, |
|||
_colorBlendMode == BlendMode.None ? BlendMode.srcIn : _colorBlendMode); |
|||
} |
|||
} |
|||
|
|||
private Color _color; |
|||
|
|||
public Color color |
|||
{ |
|||
get { return _color; } |
|||
set |
|||
{ |
|||
if (value == _color) |
|||
return; |
|||
_color = value; |
|||
_updateColorFilter(); |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
// todo more parameters
|
|||
|
|||
private ui.BlendMode _colorBlendMode; |
|||
|
|||
public ui.BlendMode colorBlendMode |
|||
{ |
|||
get { return _colorBlendMode; } |
|||
set |
|||
{ |
|||
if (value == _colorBlendMode) |
|||
return; |
|||
_colorBlendMode = value; |
|||
_updateColorFilter(); |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private BoxFit _fit; |
|||
|
|||
public BoxFit fit |
|||
{ |
|||
get { return _fit; } |
|||
set |
|||
{ |
|||
if (value == _fit) |
|||
return; |
|||
_fit = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private AlignmentGeometry _alignment; |
|||
|
|||
public AlignmentGeometry alignment |
|||
{ |
|||
get { return _alignment; } |
|||
set |
|||
{ |
|||
if (value == _alignment) |
|||
return; |
|||
_alignment = value; |
|||
_markNeedsResolution(); |
|||
} |
|||
} |
|||
|
|||
private ImageRepeat _repeat; |
|||
|
|||
public ImageRepeat repeat |
|||
{ |
|||
get { return _repeat; } |
|||
set |
|||
{ |
|||
if (value == _repeat) |
|||
return; |
|||
_repeat = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private Rect _centerSlice; |
|||
|
|||
public Rect centerSlice |
|||
{ |
|||
get { return _centerSlice; } |
|||
set |
|||
{ |
|||
if (value == _centerSlice) |
|||
return; |
|||
_centerSlice = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private bool _matchTextDirection; |
|||
|
|||
public bool matchTextDirection |
|||
{ |
|||
get { return _matchTextDirection; } |
|||
set |
|||
{ |
|||
if (value == _matchTextDirection) |
|||
return; |
|||
_matchTextDirection = value; |
|||
_markNeedsResolution(); |
|||
} |
|||
} |
|||
|
|||
private TextDirection _textDirection; |
|||
|
|||
public TextDirection textDirection |
|||
{ |
|||
get { return _textDirection; } |
|||
set |
|||
{ |
|||
if (_textDirection == value) |
|||
return; |
|||
_textDirection = value; |
|||
_markNeedsResolution(); |
|||
} |
|||
} |
|||
|
|||
Size _sizeForConstraints(BoxConstraints constraints) |
|||
{ |
|||
// Folds the given |width| and |height| into |constraints| so they can all
|
|||
// be treated uniformly.
|
|||
constraints = BoxConstraints.tightFor( |
|||
_width, |
|||
_height |
|||
); |
|||
constraints = constraints.enforce(constraints); |
|||
|
|||
if (_image == null) |
|||
return constraints.smallest; |
|||
|
|||
return constraints.constrainSizeAndAttemptToPreserveAspectRatio(new Size( |
|||
_image.width / _scale, |
|||
_image.height / _scale |
|||
)); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) |
|||
{ |
|||
if (_image == null) |
|||
return; |
|||
_resolve(); |
|||
DecorationImageUtil.paintImage( |
|||
context.canvas, |
|||
offset & size, |
|||
_image, |
|||
_fit, |
|||
_centerSlice, |
|||
_resolvedAlignment, |
|||
_repeat, |
|||
_flipHorizontally |
|||
// todo
|
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d0413272d07e4c958f45f9c7597fe2b7 |
|||
timeCreated: 1534820838 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UIWidgets.painting; |
|||
using UnityEngine; |
|||
|
|||
namespace UIWidgets.ui |
|||
{ |
|||
public class Image |
|||
{ |
|||
public Image(byte[] raw, int height = 100, int width = 100) |
|||
{ |
|||
this.rawData = raw; |
|||
this.height = height; |
|||
this.width = width; |
|||
} |
|||
|
|||
public byte[] rawData; // todo temp hack
|
|||
public int height; //有别的用吗
|
|||
public int width; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bfd6fdb0d13948d6acbc528589ebef33 |
|||
timeCreated: 1534821698 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace RSG.Promises |
|||
{ |
|||
/// <summary>
|
|||
/// General extensions to LINQ.
|
|||
/// </summary>
|
|||
public static class EnumerableExt |
|||
{ |
|||
public static void Each<T>(this IEnumerable<T> source, Action<T> fn) |
|||
{ |
|||
foreach (var item in source) |
|||
{ |
|||
fn.Invoke(item); |
|||
} |
|||
} |
|||
|
|||
public static void Each<T>(this IEnumerable<T> source, Action<T, int> fn) |
|||
{ |
|||
int index = 0; |
|||
|
|||
foreach (T item in source) |
|||
{ |
|||
fn.Invoke(item, index); |
|||
index++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert a variable length argument list of items to an enumerable.
|
|||
/// </summary>
|
|||
public static IEnumerable<T> FromItems<T>(params T[] items) |
|||
{ |
|||
foreach (var item in items) |
|||
{ |
|||
yield return item; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bb9a3c5b387541cb800b11c84121f7f3 |
|||
timeCreated: 1534828260 |
1001
Assets/UIWidgets/promise/Promise.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: ffdd174fa01b47d99dae6b8b38945413 |
|||
timeCreated: 1534828022 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace RSG.Exceptions |
|||
{ |
|||
/// <summary>
|
|||
/// Base class for promise exceptions.
|
|||
/// </summary>
|
|||
public class PromiseException : Exception |
|||
{ |
|||
public PromiseException() { } |
|||
|
|||
public PromiseException(string message) : base(message) { } |
|||
|
|||
public PromiseException(string message, Exception inner) : base(message, inner) { } |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ef24fa8abd794ec9ad904f149ade425b |
|||
timeCreated: 1534828173 |
|
|||
namespace RSG |
|||
{ |
|||
public static class PromiseHelpers |
|||
{ |
|||
/// <summary>
|
|||
/// Returns a promise that resolves with all of the specified promises have resolved.
|
|||
/// Returns a promise of a tuple of the resolved results.
|
|||
/// </summary>
|
|||
public static IPromise<Tuple<T1, T2>> All<T1, T2>(IPromise<T1> p1, IPromise<T2> p2) |
|||
{ |
|||
var val1 = default(T1); |
|||
var val2 = default(T2); |
|||
var numUnresolved = 2; |
|||
var alreadyRejected = false; |
|||
var promise = new Promise<Tuple<T1, T2>>(); |
|||
|
|||
p1 |
|||
.Then(val => |
|||
{ |
|||
val1 = val; |
|||
numUnresolved--; |
|||
if (numUnresolved <= 0) |
|||
{ |
|||
promise.Resolve(Tuple.Create(val1, val2)); |
|||
} |
|||
}) |
|||
.Catch(e => |
|||
{ |
|||
if (!alreadyRejected) |
|||
{ |
|||
promise.Reject(e); |
|||
} |
|||
|
|||
alreadyRejected = true; |
|||
}) |
|||
.Done(); |
|||
|
|||
p2 |
|||
.Then(val => |
|||
{ |
|||
val2 = val; |
|||
numUnresolved--; |
|||
if (numUnresolved <= 0) |
|||
{ |
|||
promise.Resolve(Tuple.Create(val1, val2)); |
|||
} |
|||
}) |
|||
.Catch(e => |
|||
{ |
|||
if (!alreadyRejected) |
|||
{ |
|||
promise.Reject(e); |
|||
} |
|||
|
|||
alreadyRejected = true; |
|||
}) |
|||
.Done(); |
|||
|
|||
return promise; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a promise that resolves with all of the specified promises have resolved.
|
|||
/// Returns a promise of a tuple of the resolved results.
|
|||
/// </summary>
|
|||
public static IPromise<Tuple<T1, T2, T3>> All<T1, T2, T3>(IPromise<T1> p1, IPromise<T2> p2, IPromise<T3> p3) |
|||
{ |
|||
return All(All(p1, p2), p3) |
|||
.Then(vals => Tuple.Create(vals.Item1.Item1, vals.Item1.Item2, vals.Item2)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a promise that resolves with all of the specified promises have resolved.
|
|||
/// Returns a promise of a tuple of the resolved results.
|
|||
/// </summary>
|
|||
public static IPromise<Tuple<T1, T2, T3, T4>> All<T1, T2, T3, T4>(IPromise<T1> p1, IPromise<T2> p2, IPromise<T3> p3, IPromise<T4> p4) |
|||
{ |
|||
return All(All(p1, p2), All(p3, p4)) |
|||
.Then(vals => Tuple.Create(vals.Item1.Item1, vals.Item1.Item2, vals.Item2.Item1, vals.Item2.Item2)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 07f0b3c08d53427992f828dd9c4f0f15 |
|||
timeCreated: 1534828078 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace RSG.Exceptions |
|||
{ |
|||
/// <summary>
|
|||
/// Exception thrown when an operation is performed on a promise that is in an invalid
|
|||
/// state for it to handle.
|
|||
/// </summary>
|
|||
public class PromiseStateException : PromiseException |
|||
{ |
|||
public PromiseStateException() { } |
|||
|
|||
public PromiseStateException(string message) : base(message) { } |
|||
|
|||
public PromiseStateException(string message, Exception inner) |
|||
: base(message, inner) |
|||
{ } |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d922956f02bc4ff2a82f9404787380b0 |
|||
timeCreated: 1534828191 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace RSG |
|||
{ |
|||
|
|||
public class PromiseCancelledException : Exception |
|||
{ |
|||
/// <summary>
|
|||
/// Just create the exception
|
|||
/// </summary>
|
|||
public PromiseCancelledException() |
|||
{ |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the exception with description
|
|||
/// </summary>
|
|||
/// <param name="message">Exception description</param>
|
|||
public PromiseCancelledException(String message) : base(message) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// A class that wraps a pending promise with it's predicate and time data
|
|||
/// </summary>
|
|||
internal class PredicateWait |
|||
{ |
|||
/// <summary>
|
|||
/// Predicate for resolving the promise
|
|||
/// </summary>
|
|||
public Func<TimeData, bool> predicate; |
|||
|
|||
/// <summary>
|
|||
/// The time the promise was started
|
|||
/// </summary>
|
|||
public float timeStarted; |
|||
|
|||
/// <summary>
|
|||
/// The pending promise which is an interface for a promise that can be rejected or resolved.
|
|||
/// </summary>
|
|||
public IPendingPromise pendingPromise; |
|||
|
|||
/// <summary>
|
|||
/// The time data specific to this pending promise. Includes elapsed time and delta time.
|
|||
/// </summary>
|
|||
public TimeData timeData; |
|||
|
|||
/// <summary>
|
|||
/// The frame the promise was started
|
|||
/// </summary>
|
|||
public int frameStarted; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Time data specific to a particular pending promise.
|
|||
/// </summary>
|
|||
public struct TimeData |
|||
{ |
|||
/// <summary>
|
|||
/// The amount of time that has elapsed since the pending promise started running
|
|||
/// </summary>
|
|||
public float elapsedTime; |
|||
|
|||
/// <summary>
|
|||
/// The amount of time since the last time the pending promise was updated.
|
|||
/// </summary>
|
|||
public float deltaTime; |
|||
|
|||
/// <summary>
|
|||
/// The amount of times that update has been called since the pending promise started running
|
|||
/// </summary>
|
|||
public int elapsedUpdates; |
|||
} |
|||
|
|||
public interface IPromiseTimer |
|||
{ |
|||
/// <summary>
|
|||
/// Resolve the returned promise once the time has elapsed
|
|||
/// </summary>
|
|||
IPromise WaitFor(float seconds); |
|||
|
|||
/// <summary>
|
|||
/// Resolve the returned promise once the predicate evaluates to true
|
|||
/// </summary>
|
|||
IPromise WaitUntil(Func<TimeData, bool> predicate); |
|||
|
|||
/// <summary>
|
|||
/// Resolve the returned promise once the predicate evaluates to false
|
|||
/// </summary>
|
|||
IPromise WaitWhile(Func<TimeData, bool> predicate); |
|||
|
|||
/// <summary>
|
|||
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
|
|||
/// </summary>
|
|||
void Update(float deltaTime); |
|||
|
|||
/// <summary>
|
|||
/// Cancel a waiting promise and reject it immediately.
|
|||
/// </summary>
|
|||
bool Cancel(IPromise promise); |
|||
} |
|||
|
|||
public class PromiseTimer : IPromiseTimer |
|||
{ |
|||
/// <summary>
|
|||
/// The current running total for time that this PromiseTimer has run for
|
|||
/// </summary>
|
|||
private float curTime; |
|||
|
|||
/// <summary>
|
|||
/// The current running total for the amount of frames the PromiseTimer has run for
|
|||
/// </summary>
|
|||
private int curFrame; |
|||
|
|||
/// <summary>
|
|||
/// Currently pending promises
|
|||
/// </summary>
|
|||
private readonly LinkedList<PredicateWait> waiting = new LinkedList<PredicateWait>(); |
|||
|
|||
/// <summary>
|
|||
/// Resolve the returned promise once the time has elapsed
|
|||
/// </summary>
|
|||
public IPromise WaitFor(float seconds) |
|||
{ |
|||
return WaitUntil(t => t.elapsedTime >= seconds); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resolve the returned promise once the predicate evaluates to false
|
|||
/// </summary>
|
|||
public IPromise WaitWhile(Func<TimeData, bool> predicate) |
|||
{ |
|||
return WaitUntil(t => !predicate(t)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resolve the returned promise once the predicate evalutes to true
|
|||
/// </summary>
|
|||
public IPromise WaitUntil(Func<TimeData, bool> predicate) |
|||
{ |
|||
var promise = new Promise(); |
|||
|
|||
var wait = new PredicateWait() |
|||
{ |
|||
timeStarted = curTime, |
|||
pendingPromise = promise, |
|||
timeData = new TimeData(), |
|||
predicate = predicate, |
|||
frameStarted = curFrame |
|||
}; |
|||
|
|||
waiting.AddLast(wait); |
|||
|
|||
return promise; |
|||
} |
|||
|
|||
public bool Cancel(IPromise promise) |
|||
{ |
|||
var node = FindInWaiting(promise); |
|||
|
|||
if (node == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
node.Value.pendingPromise.Reject(new PromiseCancelledException("Promise was cancelled by user.")); |
|||
waiting.Remove(node); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
LinkedListNode<PredicateWait> FindInWaiting(IPromise promise) |
|||
{ |
|||
for (var node = waiting.First; node != null; node = node.Next) |
|||
{ |
|||
if (node.Value.pendingPromise.Id.Equals(promise.Id)) |
|||
{ |
|||
return node; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
|
|||
/// </summary>
|
|||
public void Update(float deltaTime) |
|||
{ |
|||
curTime += deltaTime; |
|||
curFrame += 1; |
|||
|
|||
var node = waiting.First; |
|||
while (node != null) |
|||
{ |
|||
var wait = node.Value; |
|||
|
|||
var newElapsedTime = curTime - wait.timeStarted; |
|||
wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime; |
|||
wait.timeData.elapsedTime = newElapsedTime; |
|||
var newElapsedUpdates = curFrame - wait.frameStarted; |
|||
wait.timeData.elapsedUpdates = newElapsedUpdates; |
|||
|
|||
bool result; |
|||
try |
|||
{ |
|||
result = wait.predicate(wait.timeData); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
wait.pendingPromise.Reject(ex); |
|||
|
|||
node = RemoveNode(node); |
|||
continue; |
|||
} |
|||
|
|||
if (result) |
|||
{ |
|||
wait.pendingPromise.Resolve(); |
|||
|
|||
node = RemoveNode(node); |
|||
} |
|||
else |
|||
{ |
|||
node = node.Next; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes the provided node and returns the next node in the list.
|
|||
/// </summary>
|
|||
private LinkedListNode<PredicateWait> RemoveNode(LinkedListNode<PredicateWait> node) |
|||
{ |
|||
var currentNode = node; |
|||
node = node.Next; |
|||
|
|||
waiting.Remove(currentNode); |
|||
|
|||
return node; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e47ddf579a9a45f48fd9203faf93b52f |
|||
timeCreated: 1534828095 |
1001
Assets/UIWidgets/promise/Promise_NonGeneric.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: fad6eb2d15b84c7cb92f115245ac09f8 |
|||
timeCreated: 1534828106 |
|
|||
namespace RSG |
|||
{ |
|||
/// <summary>
|
|||
/// Provides static methods for creating tuple objects.
|
|||
///
|
|||
/// Tuple implementation for .NET 3.5
|
|||
/// </summary>
|
|||
public class Tuple |
|||
{ |
|||
/// <summary>
|
|||
/// Create a new 2-tuple, or pair.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
|
|||
/// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
|
|||
/// <param name="item1">The value of the first component of the tuple.</param>
|
|||
/// <param name="item2">The value of the second component of the tuple.</param>
|
|||
/// <returns>A 2-tuple whose value is (item1, item2)</returns>
|
|||
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) |
|||
{ |
|||
return new Tuple<T1, T2>(item1, item2); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a new 3-tuple, or triple.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
|
|||
/// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
|
|||
/// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
|
|||
/// <param name="item1">The value of the first component of the tuple.</param>
|
|||
/// <param name="item2">The value of the second component of the tuple.</param>
|
|||
/// <param name="item3">The value of the third component of the tuple.</param>
|
|||
/// <returns>A 3-tuple whose value is (item1, item2, item3)</returns>
|
|||
public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3) |
|||
{ |
|||
return new Tuple<T1, T2, T3>(item1, item2, item3); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a new 4-tuple, or quadruple.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
|
|||
/// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
|
|||
/// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
|
|||
/// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
|
|||
/// <param name="item1">The value of the first component of the tuple.</param>
|
|||
/// <param name="item2">The value of the second component of the tuple.</param>
|
|||
/// <param name="item3">The value of the third component of the tuple.</param>
|
|||
/// <param name="item4">The value of the fourth component of the tuple.</param>
|
|||
/// <returns>A 3-tuple whose value is (item1, item2, item3, item4)</returns>
|
|||
public static Tuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, T4 item4) |
|||
{ |
|||
return new Tuple<T1, T2, T3, T4>(item1, item2, item3, item4); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents a 2-tuple, or pair.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the tuple's first component.</typeparam>
|
|||
/// <typeparam name="T2">The type of the tuple's second component.</typeparam>
|
|||
public class Tuple<T1, T2> |
|||
{ |
|||
internal Tuple(T1 item1, T2 item2) |
|||
{ |
|||
Item1 = item1; |
|||
Item2 = item2; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's first component.
|
|||
/// </summary>
|
|||
public T1 Item1 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's second component.
|
|||
/// </summary>
|
|||
public T2 Item2 { get; private set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents a 3-tuple, or triple.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the tuple's first component.</typeparam>
|
|||
/// <typeparam name="T2">The type of the tuple's second component.</typeparam>
|
|||
/// <typeparam name="T3">The type of the tuple's third component.</typeparam>
|
|||
public class Tuple<T1, T2, T3> |
|||
{ |
|||
internal Tuple(T1 item1, T2 item2, T3 item3) |
|||
{ |
|||
Item1 = item1; |
|||
Item2 = item2; |
|||
Item3 = item3; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's first component.
|
|||
/// </summary>
|
|||
public T1 Item1 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's second component.
|
|||
/// </summary>
|
|||
public T2 Item2 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's third component.
|
|||
/// </summary>
|
|||
public T3 Item3 { get; private set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents a 4-tuple, or quadruple.
|
|||
/// </summary>
|
|||
/// <typeparam name="T1">The type of the tuple's first component.</typeparam>
|
|||
/// <typeparam name="T2">The type of the tuple's second component.</typeparam>
|
|||
/// <typeparam name="T3">The type of the tuple's third component.</typeparam>
|
|||
/// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
|
|||
public class Tuple<T1, T2, T3, T4> |
|||
{ |
|||
internal Tuple(T1 item1, T2 item2, T3 item3, T4 item4) |
|||
{ |
|||
Item1 = item1; |
|||
Item2 = item2; |
|||
Item3 = item3; |
|||
Item4 = item4; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's first component.
|
|||
/// </summary>
|
|||
public T1 Item1 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's second component.
|
|||
/// </summary>
|
|||
public T2 Item2 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's third component.
|
|||
/// </summary>
|
|||
public T3 Item3 { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the current tuple's fourth component.
|
|||
/// </summary>
|
|||
public T4 Item4 { get; private set; } |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2e1ff13e974447a080559101999e17d9 |
|||
timeCreated: 1534828127 |
撰写
预览
正在加载...
取消
保存
Reference in new issue