diff --git a/sdk/example/widgets/container.dart b/sdk/example/widgets/container.dart index 1b99fe4c291..9ebea3aa668 100644 --- a/sdk/example/widgets/container.dart +++ b/sdk/example/widgets/container.dart @@ -14,7 +14,7 @@ class ContainerApp extends App { padding: new EdgeDims.all(10.0), margin: new EdgeDims.all(10.0), decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)), - child: new Image( + child: new NetworkImage( src: "https://www.dartlang.org/logos/dart-logo.png", size: new Size(300.0, 300.0) ) diff --git a/sdk/lib/mojo/net/image_cache.dart b/sdk/lib/mojo/net/image_cache.dart index 5b0061ed16d..b66d5fe67fc 100644 --- a/sdk/lib/mojo/net/image_cache.dart +++ b/sdk/lib/mojo/net/image_cache.dart @@ -3,52 +3,26 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:sky' as sky; import 'dart:collection'; +import 'dart:sky' as sky; import 'package:mojom/mojo/url_response.mojom.dart'; import 'fetch.dart'; -final HashMap> _pendingRequests = - new HashMap>(); - -final HashMap _completedRequests = - new HashMap(); - -void _loadComplete(String url, sky.Image image) { - _completedRequests[url] = image; - _pendingRequests[url].forEach((c) => c(image)); - _pendingRequests.remove(url); -} - -void _load(String url, sky.ImageDecoderCallback callback) { - sky.Image result = _completedRequests[url]; - if (result != null) { - callback(_completedRequests[url]); - return; - } - - bool newRequest = false; - _pendingRequests.putIfAbsent(url, () { - newRequest = true; - return new List(); - }).add(callback); - if (newRequest) { - fetchUrl(url).then((UrlResponse response) { - if (response.statusCode >= 400) { - _loadComplete(url, null); - return; - } - new sky.ImageDecoder(response.body.handle.h, (image) { - _loadComplete(url, image); - }); - }); - } -} +final HashMap> _cache = + new HashMap>(); Future load(String url) { - Completer completer = new Completer(); - _load(url, completer.complete); - return completer.future; + return _cache.putIfAbsent(url, () { + Completer completer = new Completer(); + fetchUrl(url).then((UrlResponse response) { + if (response.statusCode >= 400) { + completer.complete(null); + } else { + new sky.ImageDecoder(response.body.handle.h, completer.complete); + } + }); + return completer.future; + }); } diff --git a/sdk/lib/rendering/box.dart b/sdk/lib/rendering/box.dart index a0e58ff0541..aac7bbea44c 100644 --- a/sdk/lib/rendering/box.dart +++ b/sdk/lib/rendering/box.dart @@ -8,7 +8,6 @@ import 'dart:sky' as sky; import 'package:vector_math/vector_math.dart'; import '../base/debug.dart'; -import '../mojo/net/image_cache.dart' as image_cache; import '../painting/box_painter.dart'; import 'object.dart'; @@ -1015,24 +1014,18 @@ class RenderBaseline extends RenderShiftedBox { class RenderImage extends RenderBox { - RenderImage(String url, Size dimensions) { - requestedSize = dimensions; - src = url; - } + RenderImage(sky.Image image, Size requestedSize) + : _image = image, _requestedSize = requestedSize; sky.Image _image; - String _src; - String get src => _src; - void set src (String value) { - if (value == _src) + sky.Image get image => _image; + void set image (sky.Image value) { + if (value == _image) return; - _src = value; - image_cache.load(_src).then((result) { - _image = result; - if (requestedSize.width == null || requestedSize.height == null) - markNeedsLayout(); - markNeedsPaint(); - }); + _image = value; + markNeedsPaint(); + if (_requestedSize.width == null || _requestedSize.height == null) + markNeedsLayout(); } Size _requestedSize; @@ -1114,7 +1107,7 @@ class RenderImage extends RenderBox { canvas.restore(); } - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}url: ${src}\n${prefix}dimensions: ${requestedSize}\n'; + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}dimensions: ${requestedSize}\n'; } class RenderDecoratedBox extends RenderProxyBox { diff --git a/sdk/lib/widgets/basic.dart b/sdk/lib/widgets/basic.dart index f1a66e3887b..5c12fa307a1 100644 --- a/sdk/lib/widgets/basic.dart +++ b/sdk/lib/widgets/basic.dart @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:sky' as sky; import 'package:vector_math/vector_math.dart'; +import '../mojo/net/image_cache.dart' as image_cache; import '../painting/text_style.dart'; import '../rendering/block.dart'; import '../rendering/box.dart'; @@ -477,7 +479,7 @@ class Text extends Component { else combinedStyle = defaultStyle; } else { - combinedStyle = style; + combinedStyle = style; } if (combinedStyle != null) text = new InlineStyle(combinedStyle, [text]); @@ -486,27 +488,66 @@ class Text extends Component { } class Image extends LeafRenderObjectWrapper { - - Image({ - src, - this.size - }) : src = src, - super(key: src) { - assert(src != null); - } + Image({ sky.Image image, this.size }) + : image = image, super(key: image.hashCode.toString()); RenderImage get root => super.root; - RenderImage createNode() => new RenderImage(this.src, this.size); + RenderImage createNode() => new RenderImage(image, size); - final String src; + final sky.Image image; final Size size; void syncRenderObject(Widget old) { super.syncRenderObject(old); - root.src = src; + root.image = image; root.requestedSize = size; } +} +class FutureImage extends Component { + FutureImage({ this.image, this.size }) : super(stateful: true); + + Future image; + Size size; + sky.Image _resolvedImage; + + void didMount() { + super.didMount(); + _resolveImage(); + } + + void _resolveImage() { + image.then((sky.Image resolvedImage) { + if (!mounted) + return; + setState(() { + _resolvedImage = resolvedImage; + }); + }); + } + + void syncFields(FutureImage source) { + bool needToResolveImage = (image != source.image); + image = source.image; + size = source.size; + if (needToResolveImage) + _resolveImage(); + } + + Widget build() { + return new Image(image: _resolvedImage, size: size); + } +} + +class NetworkImage extends Component { + NetworkImage({ String src, this.size }) : src = src, super(key: src); + + final String src; + final Size size; + + Widget build() { + return new FutureImage(image: image_cache.load(src), size: size); + } } class WidgetToRenderBoxAdapter extends LeafRenderObjectWrapper { diff --git a/sdk/lib/widgets/icon.dart b/sdk/lib/widgets/icon.dart index 1d1e7a08c25..5bb3fac8032 100644 --- a/sdk/lib/widgets/icon.dart +++ b/sdk/lib/widgets/icon.dart @@ -29,7 +29,7 @@ class Icon extends Component { // TODO(eseidel): This clearly isn't correct. Not sure what would be. // Should we use the ios images on ios? String density = 'drawable-xxhdpi'; - return new Image( + return new NetworkImage( size: new Size(size.toDouble(), size.toDouble()), src: '${kAssetBase}/${category}/${density}/ic_${subtype}_${size}dp.png' );