From 82dd9d65df49bec22da25c15db4325dca3abfd38 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 24 Sep 2015 12:11:47 -0700 Subject: [PATCH] Port scaffold to fn3 --- packages/flutter/lib/src/fn3/scaffold.dart | 248 +++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 packages/flutter/lib/src/fn3/scaffold.dart diff --git a/packages/flutter/lib/src/fn3/scaffold.dart b/packages/flutter/lib/src/fn3/scaffold.dart new file mode 100644 index 0000000000..9ae0c79eac --- /dev/null +++ b/packages/flutter/lib/src/fn3/scaffold.dart @@ -0,0 +1,248 @@ +// 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:sky' as sky; + +import 'package:sky/material.dart'; +import 'package:sky/rendering.dart'; +import 'package:sky/src/fn3/framework.dart'; + +// Slots are painted in this order and hit tested in reverse of this order +enum ScaffoldSlots { + body, + statusBar, + toolbar, + snackBar, + floatingActionButton, + drawer +} + +class RenderScaffold extends RenderBox { + + RenderScaffold({ + RenderBox body, + RenderBox statusBar, + RenderBox toolbar, + RenderBox snackBar, + RenderBox floatingActionButton, + RenderBox drawer + }) { + this[ScaffoldSlots.body] = body; + this[ScaffoldSlots.statusBar] = statusBar; + this[ScaffoldSlots.toolbar] = toolbar; + this[ScaffoldSlots.snackBar] = snackBar; + this[ScaffoldSlots.floatingActionButton] = floatingActionButton; + this[ScaffoldSlots.drawer] = drawer; + } + + Map _slots = new Map(); + RenderBox operator[] (ScaffoldSlots slot) => _slots[slot]; + void operator[]= (ScaffoldSlots slot, RenderBox value) { + RenderBox old = _slots[slot]; + if (old == value) + return; + if (old != null) + dropChild(old); + if (value == null) { + _slots.remove(slot); + } else { + _slots[slot] = value; + adoptChild(value); + } + markNeedsLayout(); + } + + void attachChildren() { + for (ScaffoldSlots slot in ScaffoldSlots.values) { + RenderBox box = _slots[slot]; + if (box != null) + box.attach(); + } + } + + void detachChildren() { + for (ScaffoldSlots slot in ScaffoldSlots.values) { + RenderBox box = _slots[slot]; + if (box != null) + box.detach(); + } + } + + void visitChildren(RenderObjectVisitor visitor) { + for (ScaffoldSlots slot in ScaffoldSlots.values) { + RenderBox box = _slots[slot]; + if (box != null) + visitor(box); + } + } + + ScaffoldSlots remove(RenderBox child) { + assert(child != null); + for (ScaffoldSlots slot in ScaffoldSlots.values) { + if (_slots[slot] == child) { + this[slot] = null; + return slot; + } + } + return null; + } + + bool get sizedByParent => true; + void performResize() { + size = constraints.biggest; + assert(!size.isInfinite); + } + + // TODO(eseidel): These change based on device size! + // http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing + static const kButtonX = 16.0; // left from right edge of body + static const kButtonY = 16.0; // up from bottom edge of body + + void performLayout() { + double bodyHeight = size.height; + double bodyPosition = 0.0; + if (_slots[ScaffoldSlots.statusBar] != null) { + RenderBox statusBar = _slots[ScaffoldSlots.statusBar]; + statusBar.layout(new BoxConstraints.tight(new Size(size.width, kStatusBarHeight))); + assert(statusBar.parentData is BoxParentData); + statusBar.parentData.position = new Point(0.0, size.height - kStatusBarHeight); + bodyHeight -= kStatusBarHeight; + } + if (_slots[ScaffoldSlots.toolbar] != null) { + RenderBox toolbar = _slots[ScaffoldSlots.toolbar]; + double toolbarHeight = kToolBarHeight + sky.view.paddingTop; + toolbar.layout(new BoxConstraints.tight(new Size(size.width, toolbarHeight))); + assert(toolbar.parentData is BoxParentData); + toolbar.parentData.position = Point.origin; + bodyPosition += toolbarHeight; + bodyHeight -= toolbarHeight; + } + if (_slots[ScaffoldSlots.body] != null) { + RenderBox body = _slots[ScaffoldSlots.body]; + body.layout(new BoxConstraints.tight(new Size(size.width, bodyHeight))); + assert(body.parentData is BoxParentData); + body.parentData.position = new Point(0.0, bodyPosition); + } + if (_slots[ScaffoldSlots.snackBar] != null) { + RenderBox snackBar = _slots[ScaffoldSlots.snackBar]; + // TODO(jackson): On tablet/desktop, minWidth = 288, maxWidth = 568 + snackBar.layout(new BoxConstraints(minWidth: size.width, maxWidth: size.width, minHeight: 0.0, maxHeight: size.height), + parentUsesSize: true); + assert(snackBar.parentData is BoxParentData); + // Position it off-screen. SnackBar slides in with an animation. + snackBar.parentData.position = new Point(0.0, size.height); + } + if (_slots[ScaffoldSlots.floatingActionButton] != null) { + RenderBox floatingActionButton = _slots[ScaffoldSlots.floatingActionButton]; + Size area = new Size(size.width - kButtonX, size.height - kButtonY); + floatingActionButton.layout(new BoxConstraints.loose(area), parentUsesSize: true); + assert(floatingActionButton.parentData is BoxParentData); + floatingActionButton.parentData.position = (area - floatingActionButton.size).toPoint(); + } + if (_slots[ScaffoldSlots.drawer] != null) { + RenderBox drawer = _slots[ScaffoldSlots.drawer]; + drawer.layout(new BoxConstraints(minWidth: 0.0, maxWidth: size.width, minHeight: size.height, maxHeight: size.height)); + assert(drawer.parentData is BoxParentData); + drawer.parentData.position = Point.origin; + } + } + + void paint(PaintingContext context, Offset offset) { + for (ScaffoldSlots slot in ScaffoldSlots.values) { + RenderBox box = _slots[slot]; + if (box != null) { + assert(box.parentData is BoxParentData); + context.paintChild(box, box.parentData.position + offset); + } + } + } + + void hitTestChildren(HitTestResult result, { Point position }) { + for (ScaffoldSlots slot in ScaffoldSlots.values.reversed) { + RenderBox box = _slots[slot]; + if (box != null) { + assert(box.parentData is BoxParentData); + if (box.hitTest(result, position: (position - box.parentData.position).toPoint())) + return; + } + } + } + + String debugDescribeChildren(String prefix) { + return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toStringDeep(prefix)}').join(); + } +} + +class Scaffold extends RenderObjectWidget { + Scaffold({ + Key key, + Widget body, + Widget statusBar, + Widget toolbar, + Widget snackBar, + Widget floatingActionButton, + Widget drawer + }) : super(key: key) { + _children[ScaffoldSlots.body] = body; + _children[ScaffoldSlots.statusBar] = statusBar; + _children[ScaffoldSlots.toolbar] = toolbar; + _children[ScaffoldSlots.snackBar] = snackBar; + _children[ScaffoldSlots.floatingActionButton] = floatingActionButton; + _children[ScaffoldSlots.drawer] = drawer; + } + + final Map _children = new Map(); + + RenderScaffold createRenderObject() => new RenderScaffold(); + + ScaffoldElement createElement() => new ScaffoldElement(this); +} + +class ScaffoldElement extends RenderObjectElement { + ScaffoldElement(Scaffold widget) : super(widget); + + Map _children; + + RenderScaffold get renderObject => super.renderObject; + + void visitChildren(ElementVisitor visitor) { + for (ScaffoldSlots slot in ScaffoldSlots.values) { + Element element = _children[slot]; + if (element != null) + visitor(element); + } + } + + void mount(Element parent, dynamic newSlot) { + super.mount(parent, newSlot); + _children = new Map(); + for (ScaffoldSlots slot in ScaffoldSlots.values) { + _children[slot] = widget._children[slot].createElement() + ..mount(this, slot); + } + } + + void update(Scaffold newWidget) { + super.update(newWidget); + assert(widget == newWidget); + for (ScaffoldSlots slot in ScaffoldSlots.values) { + _children[slot] = updateChild(_children[slot], widget._children[slot], slot); + assert((_children[slot] == null) == (widget._children[slot] == null)); + } + } + + void insertChildRenderObject(RenderObject child, ScaffoldSlots slot) { + renderObject[slot] = child; + } + + void moveChildRenderObject(RenderObject child, dynamic slot) { + removeChildRenderObject(child); + insertChildRenderObject(child, slot); + } + + void removeChildRenderObject(RenderObject child) { + assert(renderObject == child.parent); + renderObject.remove(child); + } +}