From 70b8347fe10e9d56d8aeb50eeb15e101f57318a1 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 1 Jul 2015 14:41:45 -0700 Subject: [PATCH] Hoist knowledge of image_cache into widgets/basic.dart Previously, RenderImage knew about image_cache and expected to work in terms of URLs. Now RenderImage works directly with sky.Image and it's the job of the widgets system to interact with the network cache. At the widgets layer, I've factored this work into three parts: 1) A wrapper for RenderImage that works in terms of sky.Image. 2) A component that can deal with any sort of Future. 3) A NetworkImage component that translates relative URLs into Future using the image_cache. A future CL will add a peer to NetworkImage that gets Futures from an asset bundle. R=ianh@google.com, jackson@google.com Review URL: https://codereview.chromium.org/1218023013. --- sdk/example/widgets/container.dart | 2 +- sdk/lib/mojo/net/image_cache.dart | 54 +++++++------------------ sdk/lib/rendering/box.dart | 27 +++++-------- sdk/lib/widgets/basic.dart | 65 ++++++++++++++++++++++++------ sdk/lib/widgets/icon.dart | 2 +- 5 files changed, 79 insertions(+), 71 deletions(-) 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' );