GitHub
6 年前
当前提交
299362bb
共有 42 个文件被更改,包括 3538 次插入 和 48 次删除
-
55Assets/UIWidgets/Tests/CanvasAndLayers.cs
-
8Assets/UIWidgets/painting/alignment.cs
-
6Assets/UIWidgets/painting/alignment.cs.meta
-
29Assets/UIWidgets/painting/binding.cs
-
17Assets/UIWidgets/painting/box_border.cs
-
128Assets/UIWidgets/painting/decoration_image.cs
-
13Assets/UIWidgets/rendering/box.cs
-
12Assets/UIWidgets/ui/geometry.cs
-
11Assets/UIWidgets/ui/painting/canvas.cs
-
44Assets/UIWidgets/ui/painting/canvas_impl.cs
-
7Assets/UIWidgets/ui/painting/draw_cmd.cs
-
42Assets/UIWidgets/ui/painting/painting.cs
-
84Assets/UIWidgets/painting/box_fit.cs
-
3Assets/UIWidgets/painting/box_fit.cs.meta
-
138Assets/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
-
111Assets/UIWidgets/painting/image_stream.cs
-
3Assets/UIWidgets/painting/image_stream.cs.meta
-
8Assets/UIWidgets/promise.meta
-
199Assets/UIWidgets/rendering/image.cs
-
3Assets/UIWidgets/rendering/image.cs.meta
-
20Assets/UIWidgets/ui/painting/image.cs
-
3Assets/UIWidgets/ui/painting/image.cs.meta
-
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
-
3Assets/UIWidgets/promise/PromiseHelpers.cs.meta
-
3Assets/UIWidgets/promise/PromiseStateException.cs.meta
-
3Assets/UIWidgets/promise/PromiseTimer.cs.meta
-
1001Assets/UIWidgets/promise/Promise_NonGeneric.cs
-
3Assets/UIWidgets/promise/Promise_NonGeneric.cs.meta
-
3Assets/UIWidgets/promise/Tuple.cs.meta
-
33Assets/UIWidgets/promise/EnumerableExt.cs
-
70Assets/UIWidgets/promise/PromiseHelpers.cs
-
22Assets/UIWidgets/promise/PromiseStateException.cs
-
220Assets/UIWidgets/promise/PromiseTimer.cs
-
137Assets/UIWidgets/promise/Tuple.cs
|
|||
fileFormatVersion: 2 |
|||
guid: d34b191577d24c30b0fd2a99022b8a88 |
|||
timeCreated: 1535002958 |
|||
fileFormatVersion: 2 |
|||
guid: 52b90656e86c4a39af38d0518a67f68b |
|||
timeCreated: 1534820611 |
|
|||
using UIWidgets.ui; |
|||
|
|||
public abstract class PaintingBinding { |
|||
public class PaintingBinding { |
|||
|
|||
public PaintingBinding(Window window) { |
|||
this._window = window; |
|||
} |
|||
private static PaintingBinding _instance; |
|||
public readonly Window _window; |
|||
|
|||
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; |
|||
/// 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, |
|||
|
|||
/// 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 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) { |
|||
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 + alignment.x * halfWidthDelta; |
|||
double dy = halfHeightDelta + alignment.y * halfHeightDelta; |
|||
Offset destinationPosition = rect.topLeft.translate(dx, dy); |
|||
Rect destinationRect = destinationPosition & destinationSize; |
|||
bool needSave = repeat != ImageRepeat.noRepeat; |
|||
if (needSave) |
|||
canvas.save(); |
|||
if (repeat != ImageRepeat.noRepeat) |
|||
canvas.clipRect(rect); |
|||
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))); |
|||
} |
|||
|
|||
return tileRects; |
|||
} |
|||
} |
|||
} |
|
|||
using UIWidgets.ui; |
|||
using System; |
|||
|
|||
namespace UIWidgets.painting { |
|||
public enum BoxFit { |
|||
fill, |
|||
contain, |
|||
cover, |
|||
fitWidth, |
|||
fitHeight, |
|||
none, |
|||
scaleDown, |
|||
} |
|||
|
|||
public class FittedSizes { |
|||
public FittedSizes(Size source, Size destination) { |
|||
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 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(); |
|||
|
|||
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> { |
|||
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 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 bool Equals(NetworkImage other) { |
|||
return this.url.Equals(other.url) && this.scale.Equals(other.scale); |
|||
} |
|||
|
|||
public override bool Equals(object obj) { |
|||
if (object.ReferenceEquals(null, obj)) return false; |
|||
if (object.ReferenceEquals(this, obj)) return true; |
|||
return obj is NetworkImage && this.Equals((NetworkImage) obj); |
|||
} |
|||
|
|||
public override int GetHashCode() { |
|||
unchecked { |
|||
var hashCode = this.url.GetHashCode(); |
|||
hashCode = (hashCode * 397) ^ this.scale.GetHashCode(); |
|||
return hashCode; |
|||
} |
|||
} |
|||
} |
|||
|
|||
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) { |
|||
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, |
|||
Alignment 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._alignment = alignment ?? Alignment.center; |
|||
} |
|||
|
|||
Alignment _resolvedAlignment; |
|||
|
|||
void _resolve() { |
|||
if (_resolvedAlignment != null) |
|||
return; |
|||
_resolvedAlignment = alignment; |
|||
} |
|||
|
|||
void _markNeedsResolution() { |
|||
_resolvedAlignment = null; |
|||
markNeedsPaint(); |
|||
} |
|||
|
|||
private ui.Image _image; |
|||
|
|||
public ui.Image image { |
|||
get { return this._image; } |
|||
set { |
|||
if (value == _image) |
|||
return; |
|||
_image = value; |
|||
markNeedsPaint(); |
|||
if (_width == 0.0 || _height == 0.0) |
|||
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(); |
|||
} |
|||
} |
|||
|
|||
private Color _color; |
|||
|
|||
public Color color { |
|||
get { return _color; } |
|||
set { |
|||
if (value == _color) |
|||
return; |
|||
_color = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private ui.BlendMode _colorBlendMode; |
|||
|
|||
public ui.BlendMode colorBlendMode { |
|||
get { return _colorBlendMode; } |
|||
set { |
|||
if (value == _colorBlendMode) |
|||
return; |
|||
_colorBlendMode = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private BoxFit _fit; |
|||
|
|||
public BoxFit fit { |
|||
get { return _fit; } |
|||
set { |
|||
if (value == _fit) |
|||
return; |
|||
_fit = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
|
|||
private Alignment _alignment; |
|||
|
|||
public Alignment 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(); |
|||
} |
|||
} |
|||
|
|||
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 |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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; |
|||
public int height; |
|||
public int width; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bfd6fdb0d13948d6acbc528589ebef33 |
|||
timeCreated: 1534821698 |
|
|||
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 |
|
|||
fileFormatVersion: 2 |
|||
guid: 07f0b3c08d53427992f828dd9c4f0f15 |
|||
timeCreated: 1534828078 |
|
|||
fileFormatVersion: 2 |
|||
guid: d922956f02bc4ff2a82f9404787380b0 |
|||
timeCreated: 1534828191 |
|
|||
fileFormatVersion: 2 |
|||
guid: e47ddf579a9a45f48fd9203faf93b52f |
|||
timeCreated: 1534828095 |
1001
Assets/UIWidgets/promise/Promise_NonGeneric.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: fad6eb2d15b84c7cb92f115245ac09f8 |
|||
timeCreated: 1534828106 |
|
|||
fileFormatVersion: 2 |
|||
guid: 2e1ff13e974447a080559101999e17d9 |
|||
timeCreated: 1534828127 |
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
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)); |
|||
} |
|||
} |
|||
} |
|
|||
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) { |
|||
} |
|||
} |
|||
} |
|
|||
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; |
|||
} |
|||
} |
|||
} |
|
|||
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; } |
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue