Commit 488285f1 authored by Adam Barth's avatar Adam Barth

Merge pull request #1553 from abarth/child_view

Initial work to add Mozart child views
parents fbe6779f f30398a4
......@@ -10,6 +10,7 @@ export 'src/rendering/basic_types.dart';
export 'src/rendering/binding.dart';
export 'src/rendering/block.dart';
export 'src/rendering/box.dart';
export 'src/rendering/child_view.dart';
export 'src/rendering/custom_layout.dart';
export 'src/rendering/debug.dart';
export 'src/rendering/editable_line.dart';
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:mojo_services/mojo/gfx/composition/scene_token.mojom.dart' as mojom;
import 'package:mojo_services/mojo/ui/layouts.mojom.dart' as mojom;
import 'package:mojo_services/mojo/ui/view_provider.mojom.dart' as mojom;
import 'package:mojo_services/mojo/ui/views.mojom.dart' as mojom;
import 'package:mojo/application.dart';
import 'package:mojo/mojo/service_provider.mojom.dart' as mojom;
import 'box.dart';
import 'object.dart';
// TODO(abarth): Populate the view host.
final mojom.ViewHost _viewHost = null;
class ChildViewConnection {
ChildViewConnection({ this.url }) {
mojom.ServiceProviderProxy incomingServices = new mojom.ServiceProviderProxy.unbound();
mojom.ServiceProviderStub outgoingServices = new mojom.ServiceProviderStub.unbound();
_connection = new ApplicationConnection(outgoingServices, incomingServices);
_unresolvedViewToken = _connectToView(incomingServices, outgoingServices);
}
final String url;
ApplicationConnection get connection => _connection;
ApplicationConnection _connection;
Future<mojom.ViewToken> _unresolvedViewToken;
mojom.ViewToken _viewToken;
Future<mojom.ViewToken> _connectToView(
mojom.ServiceProviderProxy incomingServices,
mojom.ServiceProviderStub outgoingServices
) async {
assert(_viewToken == null);
mojom.ViewProviderProxy viewProvider = new mojom.ViewProviderProxy.unbound();
shell.connectToService(url, proxy);
mojom.ViewToken viewToken = (await viewProvider.ptr.createView(incomingServices, outgoingServices)).viewToken;
viewProvider.close();
assert(_viewToken == null);
_viewToken = viewToken;
assert(_viewKey == null);
if (_attached)
_addChildToViewHost();
return viewToken;
}
static int _nextViewKey = 1;
int _viewKey;
void _addChildToViewHost() {
assert(_attached);
assert(_viewToken != null);
assert(_viewKey == null);
_viewKey = _nextViewKey++;
_viewHost.addChild(_viewKey, _viewToken);
}
void _removeChildFromViewHost() {
assert(!_attached);
assert(_viewToken != null);
assert(_viewKey != null);
_viewHost.removeChild(_viewKey);
_viewKey = null;
}
// The number of render objects attached to this view. In between frames, we
// might have more than one connected if we get added to a new render object
// before we get removed from the old render object. By the time we get around
// to computing our layout, we must be back to just having one render object.
int _attachments = 0;
bool get _attached => _attachments > 0;
void _attach() {
assert(_attachments >= 0);
++_attachments;
if (_viewToken != null && _viewKey == null)
_addChildToViewHost();
}
void _detach() {
assert(_attached);
--_attachments;
scheduleMicrotask(_removeChildFromViewHostIfNeeded);
}
void _removeChildFromViewHostIfNeeded() {
assert(_attachments >= 0);
if (_attachments == 0)
_removeChildFromViewHost();
}
Future<mojom.ViewLayoutInfo> _layout({ Size size, double scale }) async {
assert(_attached);
assert(_attachments == 1);
assert(_viewKey != null);
int width = (size.width * scale).round();
int height = (size.height * scale).round();
// TODO(abarth): Ideally we would propagate our actually constraints to be
// able to support rich cross-app layout. For now, we give the child tight
// constraints for simplicity.
mojom.BoxConstraints childConstraints = new mojom.BoxConstraints()
..minWidth = width
..maxWidth = width
..minHeight = height
..maxHeight = height;
mojom.ViewLayoutParams layoutParams = new mojom.ViewLayoutParams()
..constraints = childConstraints
..devicePixelRatio = scale;
return (await _viewHost.layoutChild(_viewKey, layoutParams)).info;
}
String toString() {
return '$runtimeType(url: $url)';
}
}
class RenderChildView extends RenderBox {
RenderChildView({
ChildViewConnection child,
double scale
}) : _child = child, _scale = scale;
ChildViewConnection get child => _child;
ChildViewConnection _child;
void set child (ChildViewConnection value) {
if (value == _child)
return;
if (attached)
_child?._detach();
_child = value;
_layoutInfo = null;
if (attached)
_child?._attach();
if (_child == null) {
markNeedsPaint();
} else if (_child._viewToken != null) {
// We've already connected to the view, so we're ready to invalidate our
// layout immediately.
markNeedsLayout();
} else {
// Otherwise, we're still in the process of connecting, so we need to
// repaint now (to remove any old child view), and we need to watch for
// the view token resolving before attempting layout.
markNeedsPaint();
_child._unresolvedViewToken.then(_handleViewTokenResolved);
}
}
double get scale => _scale;
double _scale;
void set scale (double value) {
if (value == _scale)
return;
_scale = value;
if (_child != null)
markNeedsLayout();
}
void attach() {
super.attach();
_child?._attach();
}
void detach() {
_child?._detach();
super.detach();
}
bool get alwaysNeedsCompositing => true;
bool get sizedByParent => true;
void performResize() {
size = constraints.biggest;
}
void performLayout() {
if (_child != null)
_child._layout(size: size, scale: scale).then(_handleLayoutInfoChanged);
}
mojom.ViewLayoutInfo _layoutInfo;
void _handleLayoutInfoChanged(mojom.ViewLayoutInfo layoutInfo) {
_layoutInfo = layoutInfo;
markNeedsPaint();
}
void _handleViewTokenResolved(mojom.ViewToken viewToken) {
// The _viewToken might not match viewToken if _child changed between the
// time we started waiting for the future and the time it resolved.
if (attached && _child?._viewToken == viewToken)
markNeedsLayout();
}
void paint(PaintingContext context, Offset offset) {
assert(needsCompositing);
if (_layoutInfo != null)
context.pushChildScene(offset, _layoutInfo);
}
void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings);
settings.add('child: $child');
settings.add('scale: $scale');
}
}
......@@ -5,6 +5,7 @@
import 'dart:ui' as ui;
import 'package:vector_math/vector_math_64.dart';
import 'package:mojo_services/mojo/ui/layouts.mojom.dart' as mojom;
import 'basic_types.dart';
import 'debug.dart';
......@@ -107,6 +108,30 @@ class PictureLayer extends Layer {
}
}
class ChildSceneLayer extends Layer {
ChildSceneLayer({ this.offset, this.layoutInfo });
Offset offset;
mojom.ViewLayoutInfo layoutInfo;
void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
builder.addChildScene(
offset + layerOffset,
layoutInfo.size.width,
layoutInfo.size.height,
layoutInfo.sceneToken.value
);
}
void debugDescribeSettings(List<String> settings) {
super.debugDescribeSettings(settings);
settings.add('offset: $offset');
settings.add('physicalWidth: ${layoutInfo.size.width}');
settings.add('physicalHeight: ${layoutInfo.size.height}');
settings.add('sceneToken.value: ${layoutInfo.sceneToken.value}');
}
}
/// A layer that indicates to the compositor that it should display
/// certain performance statistics within it.
class PerformanceOverlayLayer extends Layer {
......
......@@ -9,6 +9,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:mojo_services/mojo/ui/layouts.mojom.dart' as mojom;
import 'package:vector_math/vector_math_64.dart';
import 'debug.dart';
......@@ -189,12 +190,16 @@ class PaintingContext {
/// compositor.
void pushPerformanceOverlay(Offset offset, int optionsMask, int rasterizerThreshold, Size size) {
_stopRecordingIfNeeded();
PerformanceOverlayLayer performanceOverlayLayer = new PerformanceOverlayLayer(
_appendLayer(new PerformanceOverlayLayer(
overlayRect: new Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
optionsMask: optionsMask,
rasterizerThreshold: rasterizerThreshold
);
_appendLayer(performanceOverlayLayer);
));
}
void pushChildScene(Offset offset, mojom.ViewLayoutInfo layoutInfo) {
_stopRecordingIfNeeded();
_appendLayer(new ChildSceneLayer(offset: offset, layoutInfo: layoutInfo));
}
/// Push a rectangular clip rect.
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/rendering.dart';
import 'media_query.dart';
import 'framework.dart';
export 'package:flutter/rendering.dart' show ChildViewConnection;
class ChildView extends StatelessComponent {
ChildView({ Key key, this.child }) : super(key: key);
final ChildViewConnection child;
Widget build(BuildContext context) {
return new _ChildViewWidget(
child: child,
scale: MediaQuery.of(context).devicePixelRatio
);
}
}
class _ChildViewWidget extends LeafRenderObjectWidget {
_ChildViewWidget({
ChildViewConnection child,
this.scale
}) : child = child, super(key: new GlobalObjectKey(child));
final ChildViewConnection child;
final double scale;
RenderChildView createRenderObject() => new RenderChildView(child: child, scale: scale);
void updateRenderObject(RenderChildView renderObject, _ChildViewWidget oldWidget) {
renderObject
..child = child
..scale = scale;
}
}
......@@ -8,6 +8,7 @@ library widgets;
export 'src/widgets/asset_vendor.dart';
export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart';
export 'src/widgets/child_view.dart';
export 'src/widgets/dismissable.dart';
export 'src/widgets/drag_target.dart';
export 'src/widgets/editable.dart';
......@@ -35,9 +36,9 @@ export 'src/widgets/placeholder.dart';
export 'src/widgets/raw_keyboard_listener.dart';
export 'src/widgets/routes.dart';
export 'src/widgets/scroll_behavior.dart';
export 'src/widgets/scrollable.dart';
export 'src/widgets/scrollable_grid.dart';
export 'src/widgets/scrollable_list.dart';
export 'src/widgets/scrollable.dart';
export 'src/widgets/semantics_debugger.dart';
export 'src/widgets/status_transitions.dart';
export 'src/widgets/title.dart';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment