Commit 67f97b45 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Remove ChildView widget (#6195)

This widget now lives in Mozart:

https://fuchsia.googlesource.com/mozart/+/master/lib/flutter/child_view.dart
parent 8071a82d
// Copyright 2015 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:math' as math;
import 'package:flutter/material.dart';
class LauncherData {
const LauncherData({ this.url, this.title });
final String url;
final String title;
}
final List<LauncherData> _kLauncherData = <LauncherData>[
const LauncherData(
url: 'mojo:noodles_view',
title: 'Noodles'
),
const LauncherData(
url: 'mojo:shapes_view',
title: 'Shapes'
),
new LauncherData(
url: Uri.base.resolve('../../../examples/stocks/build/app.flx').toString(),
title: 'Stocks'
),
];
const Size _kInitialWindowSize = const Size(200.0, 200.0);
const double _kWindowPadding = 10.0;
enum WindowSide {
topCenter,
topRight,
bottomRight,
}
class WindowDecoration extends StatelessWidget {
WindowDecoration({
Key key,
this.side,
this.color,
this.onTap,
this.onPanUpdate
}) : super(key: key);
final WindowSide side;
final Color color;
final GestureTapCallback onTap;
final GestureDragUpdateCallback onPanUpdate;
@override
Widget build(BuildContext context) {
double top, right, bottom, left, width, height;
height = _kWindowPadding * 2.0;
if (side == WindowSide.topCenter || side == WindowSide.topRight)
top = 0.0;
if (side == WindowSide.topRight || side == WindowSide.bottomRight) {
right = 0.0;
width = _kWindowPadding * 2.0;
}
if (side == WindowSide.topCenter) {
left = _kWindowPadding;
right = _kWindowPadding;
}
if (side == WindowSide.bottomRight)
bottom = 0.0;
return new Positioned(
top: top,
right: right,
bottom: bottom,
left: left,
width: width,
height: height,
child: new GestureDetector(
onTap: onTap,
onPanUpdate: onPanUpdate,
child: new Container(
decoration: new BoxDecoration(
backgroundColor: color
)
)
)
);
}
}
class Window extends StatefulWidget {
Window({ Key key, this.child, this.onClose }) : super(key: key);
final ChildViewConnection child;
final ValueChanged<ChildViewConnection> onClose;
@override
_WindowState createState() => new _WindowState();
}
class _WindowState extends State<Window> {
Offset _offset = Offset.zero;
Size _size = _kInitialWindowSize;
void _handleResizerDrag(DragUpdateDetails details) {
setState(() {
_size = new Size(
math.max(0.0, _size.width + details.delta.dx),
math.max(0.0, _size.height + details.delta.dy)
);
});
}
void _handleRepositionDrag(DragUpdateDetails details) {
setState(() {
_offset += details.delta;
});
}
void _handleClose() {
config.onClose(config.child);
}
@override
Widget build(BuildContext context) {
return new Positioned(
left: _offset.dx,
top: _offset.dy,
width: _size.width + _kWindowPadding * 2.0,
height: _size.height + _kWindowPadding * 2.0,
child: new Stack(
children: <Widget>[
new WindowDecoration(
side: WindowSide.topCenter,
onPanUpdate: _handleRepositionDrag,
color: Colors.green[200]
),
new WindowDecoration(
side: WindowSide.topRight,
onTap: _handleClose,
color: Colors.red[200]
),
new WindowDecoration(
side: WindowSide.bottomRight,
onPanUpdate: _handleResizerDrag,
color: Colors.blue[200]
),
new Container(
padding: const EdgeInsets.all(_kWindowPadding),
child: new Material(
elevation: 8,
child: new ChildView(child: config.child)
)
)
]
)
);
}
}
class LauncherItem extends StatelessWidget {
LauncherItem({
Key key,
this.url,
this.child,
this.onLaunch
}) : super(key: key);
final String url;
final Widget child;
final ValueChanged<ChildViewConnection> onLaunch;
@override
Widget build(BuildContext context) {
return new RaisedButton(
onPressed: () { onLaunch(new ChildViewConnection(url: url)); },
child: child
);
}
}
class Launcher extends StatelessWidget {
Launcher({ Key key, this.items }) : super(key: key);
final List<Widget> items;
@override
Widget build(BuildContext context) {
return new ButtonBar(
alignment: MainAxisAlignment.center,
children: items
);
}
}
class WindowManager extends StatefulWidget {
@override
_WindowManagerState createState() => new _WindowManagerState();
}
class _WindowManagerState extends State<WindowManager> {
List<ChildViewConnection> _windows = <ChildViewConnection>[];
void _handleLaunch(ChildViewConnection child) {
setState(() {
_windows.add(child);
});
}
void _handleClose(ChildViewConnection child) {
setState(() {
_windows.remove(child);
});
}
@override
Widget build(BuildContext context) {
return new Material(
child: new Stack(
children: <Widget>[
new Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child: new Launcher(items: _kLauncherData.map((LauncherData data) {
return new LauncherItem(
url: data.url,
onLaunch: _handleLaunch,
child: new Text(data.title)
);
}).toList())
)
]..addAll(_windows.map((ChildViewConnection child) {
return new Window(
key: new ObjectKey(child),
onClose: _handleClose,
child: child
);
}))
)
);
}
}
void main() {
runApp(new MaterialApp(
title: 'Mozart',
home: new WindowManager()
));
}
......@@ -27,7 +27,6 @@ export 'src/rendering/auto_layout.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 'dart:collection';
import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter_services/mojo/gfx/composition/scene_token.dart' as mojom;
import 'package:flutter_services/mojo/ui/view_containers.dart' as mojom;
import 'package:flutter_services/mojo/ui/view_provider.dart' as mojom;
import 'package:flutter_services/mojo/ui/view_properties.dart' as mojom;
import 'package:flutter_services/mojo/ui/view_token.dart' as mojom;
import 'package:flutter_services/mojo/ui/views.dart' as mojom;
import 'package:flutter_services/mojo/geometry.dart' as mojom;
import 'package:mojo/application.dart';
import 'package:mojo/core.dart' as core;
import 'package:mojo/mojo/service_provider.mojom.dart' as mojom;
import 'box.dart';
import 'object.dart';
import 'layer.dart';
mojom.ViewProxy _initViewProxy() {
int viewHandle = ui.MojoServices.takeView();
if (viewHandle == core.MojoHandle.INVALID)
return null;
return new mojom.ViewProxy.fromHandle(new core.MojoHandle(viewHandle));
}
// TODO(abarth): The view host is a unique resource. We should structure how we
// take the handle from the engine so that multiple libraries can interact with
// the view host safely. Unfortunately, the view host has a global namespace of
// view keys, which means any scheme for sharing the view host also needs to
// provide a mechanism for coordinating about view keys.
final mojom.ViewProxy _viewProxy = _initViewProxy();
final mojom.View _view = _viewProxy;
mojom.ViewContainer _initViewContainer() {
if (_view == null)
return null;
mojom.ViewContainerProxy viewContainerProxy = new mojom.ViewContainerProxy.unbound();
_view.getContainer(viewContainerProxy);
viewContainerProxy.setListener(new mojom.ViewContainerListenerStub.unbound()..impl = _ViewContainerListenerImpl.instance);
return viewContainerProxy;
}
final mojom.ViewContainer _viewContainer = _initViewContainer();
typedef dynamic _ResponseFactory();
class _ViewContainerListenerImpl extends mojom.ViewContainerListener {
static final _ViewContainerListenerImpl instance = new _ViewContainerListenerImpl();
@override
dynamic onChildAttached(int childKey, mojom.ViewInfo childViewInfo, [_ResponseFactory responseFactory = null]) {
ChildViewConnection connection = _connections[childKey];
connection?._onAttachedToContainer(childViewInfo);
return responseFactory();
}
@override
dynamic onChildUnavailable(int childKey, [_ResponseFactory responseFactory = null]) {
ChildViewConnection connection = _connections[childKey];
connection?._onUnavailable();
return responseFactory();
}
final Map<int, ChildViewConnection> _connections = new HashMap<int, ChildViewConnection>();
}
/// (mojo-only) A connection with a child view.
///
/// Used with the [ChildView] widget to display a child view.
class ChildViewConnection {
/// Establishes a connection to the app at the given URL.
ChildViewConnection({ String url }) {
mojom.ViewProviderProxy viewProvider = shell.connectToApplicationService(
url, mojom.ViewProvider.connectToService
);
mojom.ServiceProviderProxy incomingServices = new mojom.ServiceProviderProxy.unbound();
_viewOwner = new mojom.ViewOwnerProxy.unbound();
viewProvider.createView(_viewOwner, incomingServices);
viewProvider.close();
_connection = new ApplicationConnection(null, incomingServices);
}
/// Wraps an already-established connection to a child app.
ChildViewConnection.fromViewOwner({
mojom.ViewOwnerProxy viewOwner,
ApplicationConnection connection
}) : _connection = connection, _viewOwner = viewOwner;
/// The underlying application connection to the child app.
///
/// Useful for requesting services from the child app and for providing
/// services to the child app.
ApplicationConnection get connection => _connection;
ApplicationConnection _connection;
mojom.ViewOwnerProxy _viewOwner;
static int _nextViewKey = 1;
int _viewKey;
int _sceneVersion = 1;
mojom.ViewProperties _currentViewProperties;
VoidCallback _onViewInfoAvailable;
mojom.ViewInfo _viewInfo;
void _onAttachedToContainer(mojom.ViewInfo viewInfo) {
assert(_viewInfo == null);
_viewInfo = viewInfo;
if (_onViewInfoAvailable != null)
_onViewInfoAvailable();
}
void _onUnavailable() {
_viewInfo = null;
}
void _addChildToViewHost() {
assert(_attached);
assert(_viewOwner != null);
assert(_viewKey == null);
assert(_viewInfo == null);
_viewKey = _nextViewKey++;
_viewContainer?.addChild(_viewKey, _viewOwner);
_viewOwner = null;
assert(!_ViewContainerListenerImpl.instance._connections.containsKey(_viewKey));
_ViewContainerListenerImpl.instance._connections[_viewKey] = this;
}
void _removeChildFromViewHost() {
assert(!_attached);
assert(_viewOwner == null);
assert(_viewKey != null);
assert(_ViewContainerListenerImpl.instance._connections[_viewKey] == this);
_ViewContainerListenerImpl.instance._connections.remove(_viewKey);
_viewOwner = new mojom.ViewOwnerProxy.unbound();
_viewContainer?.removeChild(_viewKey, _viewOwner);
_viewKey = null;
_viewInfo = null;
_currentViewProperties = 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 (_viewKey == null)
_addChildToViewHost();
}
void _detach() {
assert(_attached);
--_attachments;
scheduleMicrotask(_removeChildFromViewHostIfNeeded);
}
void _removeChildFromViewHostIfNeeded() {
assert(_attachments >= 0);
if (_attachments == 0)
_removeChildFromViewHost();
}
mojom.ViewProperties _createViewProperties(int physicalWidth,
int physicalHeight,
double devicePixelRatio) {
if (_currentViewProperties != null &&
_currentViewProperties.displayMetrics.devicePixelRatio == devicePixelRatio &&
_currentViewProperties.viewLayout.size.width == physicalWidth &&
_currentViewProperties.viewLayout.size.height == physicalHeight)
return null;
mojom.DisplayMetrics displayMetrics = new mojom.DisplayMetrics()
..devicePixelRatio = devicePixelRatio;
mojom.Size size = new mojom.Size()
..width = physicalWidth
..height = physicalHeight;
mojom.ViewLayout viewLayout = new mojom.ViewLayout()
..size = size;
_currentViewProperties = new mojom.ViewProperties()
..displayMetrics = displayMetrics
..viewLayout = viewLayout;
return _currentViewProperties;
}
void _setChildProperties(int physicalWidth, int physicalHeight, double devicePixelRatio) {
assert(_attached);
assert(_attachments == 1);
assert(_viewKey != null);
if (_view == null)
return;
mojom.ViewProperties viewProperties = _createViewProperties(physicalWidth, physicalHeight, devicePixelRatio);
if (viewProperties == null)
return;
_viewContainer.setChildProperties(_viewKey, _sceneVersion++, viewProperties);
}
}
/// (mojo-only) A view of a child application.
class RenderChildView extends RenderBox {
/// Creates a child view render object.
///
/// The [scale] argument must not be null.
RenderChildView({
ChildViewConnection child,
double scale
}) : _scale = scale {
assert(scale != null);
this.child = child;
}
/// The child to display.
ChildViewConnection get child => _child;
ChildViewConnection _child;
set child (ChildViewConnection value) {
if (value == _child)
return;
if (attached && _child != null) {
_child._detach();
assert(_child._onViewInfoAvailable != null);
_child._onViewInfoAvailable = null;
}
_child = value;
if (attached && _child != null) {
_child._attach();
assert(_child._onViewInfoAvailable == null);
_child._onViewInfoAvailable = markNeedsPaint;
}
if (_child == null) {
markNeedsPaint();
} else {
markNeedsLayout();
}
}
/// The device pixel ratio to provide the child.
double get scale => _scale;
double _scale;
set scale (double value) {
assert(value != null);
if (value == _scale)
return;
_scale = value;
if (_child != null)
markNeedsLayout();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
if (_child != null) {
_child._attach();
assert(_child._onViewInfoAvailable == null);
_child._onViewInfoAvailable = markNeedsPaint;
}
}
@override
void detach() {
if (_child != null) {
_child._detach();
assert(_child._onViewInfoAvailable != null);
_child._onViewInfoAvailable = null;
}
super.detach();
}
@override
bool get alwaysNeedsCompositing => true;
TextPainter _debugErrorMessage;
int _physicalWidth;
int _physicalHeight;
@override
void performLayout() {
size = constraints.biggest;
if (_child != null) {
_physicalWidth = (size.width * scale).round();
_physicalHeight = (size.height * scale).round();
_child._setChildProperties(_physicalWidth, _physicalHeight, scale);
assert(() {
if (_view == null) {
_debugErrorMessage ??= new TextPainter(
text: new TextSpan(text: 'Child views are supported only when running in Mojo shell.')
);
_debugErrorMessage.layout(minWidth: size.width, maxWidth: size.width);
}
return true;
});
}
}
@override
bool hitTestSelf(Point position) => true;
@override
void paint(PaintingContext context, Offset offset) {
assert(needsCompositing);
if (_child?._viewInfo != null) {
context.addLayer(new ChildSceneLayer(
offset: offset,
devicePixelRatio: scale,
physicalWidth: _physicalWidth,
physicalHeight: _physicalHeight,
sceneToken: _child._viewInfo.sceneToken.value
));
}
assert(() {
if (_view == null) {
context.canvas.drawRect(offset & size, new Paint()..color = const Color(0xFF0000FF));
_debugErrorMessage.paint(context.canvas, offset);
}
return true;
});
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('child: $child');
description.add('scale: $scale');
}
}
// 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 'framework.dart';
import 'media_query.dart';
export 'package:flutter/rendering.dart' show ChildViewConnection;
/// (mojo-only) A widget that is replaced by content from another process.
///
/// Requires a [MediaQuery] ancestor to provide appropriate media information to
/// the child.
class ChildView extends StatelessWidget {
/// Creates a widget that is replaced by content from another process.
ChildView({ Key key, this.child }) : super(key: key);
/// A connection to the child whose content will replace this widget.
final ChildViewConnection child;
@override
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)) {
assert(scale != null);
}
final ChildViewConnection child;
final double scale;
@override
RenderChildView createRenderObject(BuildContext context) => new RenderChildView(child: child, scale: scale);
@override
void updateRenderObject(BuildContext context, RenderChildView renderObject) {
renderObject
..child = child
..scale = scale;
}
}
......@@ -15,7 +15,6 @@ export 'src/widgets/auto_layout.dart';
export 'src/widgets/banner.dart';
export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart';
export 'src/widgets/child_view.dart';
export 'src/widgets/clamp_overscrolls.dart';
export 'src/widgets/container.dart';
export 'src/widgets/debug.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