mozart.dart 5.57 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:math' as math;

7 8
import 'package:flutter/material.dart';

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
class LauncherData {
  const LauncherData({ this.url, this.title });
  final String url;
  final String title;
}

const List<LauncherData> _kLauncherData = const <LauncherData>[
  const LauncherData(
    url: 'mojo:noodles_view',
    title: 'Noodles'
  ),
  const LauncherData(
    url: 'mojo:shapes_view',
    title: 'Shapes'
  ),
24 25
];

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
const Size _kInitialWindowSize = const Size(200.0, 200.0);
const double _kWindowPadding = 10.0;

enum WindowSide {
  topCenter,
  topRight,
  bottomRight,
}

class WindowDecoration extends StatelessComponent {
  WindowDecoration({
    Key key,
    this.side,
    this.color,
    this.onTap,
    this.onPanUpdate
  }) : super(key: key);

  final WindowSide side;
  final Color color;
  final GestureTapCallback onTap;
  final GesturePanUpdateCallback onPanUpdate;

  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
          )
        )
      )
    );
  }
}
89 90

class Window extends StatefulComponent {
91
  Window({ Key key, this.child, this.onClose }) : super(key: key);
92 93

  final ChildViewConnection child;
94
  final ValueChanged<ChildViewConnection> onClose;
95 96 97 98 99 100

  _WindowState createState() => new _WindowState();
}

class _WindowState extends State<Window> {
  Offset _offset = Offset.zero;
101
  Size _size = _kInitialWindowSize;
102

103
  void _handleResizerDrag(Offset delta) {
104
    setState(() {
105 106 107 108
      _size = new Size(
        math.max(0.0, _size.width + delta.dx),
        math.max(0.0, _size.height + delta.dy)
      );
109 110 111
    });
  }

112
  void _handleRepositionDrag(Offset delta) {
113
    setState(() {
114
      _offset += delta;
115 116 117
    });
  }

118 119 120 121
  void _handleClose() {
    config.onClose(config.child);
  }

122 123 124 125
  Widget build(BuildContext context) {
    return new Positioned(
      left: _offset.dx,
      top: _offset.dy,
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
      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 EdgeDims.all(_kWindowPadding),
            child: new Material(
              elevation: 8,
              child: new ChildView(child: config.child)
            )
          )
        ]
153 154 155 156 157
      )
    );
  }
}

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
class LauncherItem extends StatelessComponent {
  LauncherItem({
    Key key,
    this.url,
    this.child,
    this.onLaunch
  }) : super(key: key);

  final String url;
  final Widget child;
  final ValueChanged<ChildViewConnection> onLaunch;

  Widget build(BuildContext context) {
    return new RaisedButton(
      onPressed: () { onLaunch(new ChildViewConnection(url: url)); },
      child: child
    );
  }
}

class Launcher extends StatelessComponent {
  Launcher({ Key key, this.items }) : super(key: key);

  final List<Widget> items;

  Widget build(BuildContext context) {
    return new Row(
      justifyContent: FlexJustifyContent.center,
      children: items
    );
  }
}

191 192 193 194 195 196 197
class WindowManager extends StatefulComponent {
  _WindowManagerState createState() => new _WindowManagerState();
}

class _WindowManagerState extends State<WindowManager> {
  List<ChildViewConnection> _windows = <ChildViewConnection>[];

198
  void _handleLaunch(ChildViewConnection child) {
199
    setState(() {
200 201 202 203 204 205 206
      _windows.add(child);
    });
  }

  void _handleClose(ChildViewConnection child) {
    setState(() {
      _windows.remove(child);
207 208 209 210
    });
  }

  Widget build(BuildContext context) {
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    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
          );
        }))
233 234 235 236 237 238 239 240 241 242 243
      )
    );
  }
}

void main() {
  runApp(new MaterialApp(
    title: 'Mozart',
    routes: <String, RouteBuilder>{ '/': (_) => new WindowManager() }
  ));
}