OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 import 'animated_component.dart'; | |
6 import '../animation/animated_value.dart'; | |
7 import '../fn.dart'; | |
8 import '../theme/colors.dart'; | |
9 import 'dart:async'; | |
10 import 'dart:math' as math; | |
11 import 'material.dart'; | |
12 import 'popup_menu_item.dart'; | |
13 | |
14 const double _kMenuOpenDuration = 300.0; | |
15 const double _kMenuCloseDuration = 200.0; | |
16 const double _kMenuCloseDelay = 100.0; | |
17 | |
18 enum MenuState { Hidden, Opening, Open, Closing } | |
19 | |
20 class PopupMenuController { | |
21 AnimatedValue position = new AnimatedValue(0.0); | |
22 MenuState _state = MenuState.Hidden; | |
23 MenuState get state => _state; | |
24 | |
25 bool get canReact => (_state == MenuState.Opening) || (_state == MenuState.Ope
n); | |
26 | |
27 open() async { | |
28 if (_state != MenuState.Hidden) | |
29 return; | |
30 _state = MenuState.Opening; | |
31 if (await position.animateTo(1.0, _kMenuOpenDuration) == 1.0) | |
32 _state = MenuState.Open; | |
33 } | |
34 | |
35 Future _closeState; | |
36 close() async { | |
37 var result = new Completer(); | |
38 _closeState = result.future; | |
39 if ((_state == MenuState.Opening) || (_state == MenuState.Open)) { | |
40 _state = MenuState.Closing; | |
41 await position.animateTo(0.0, _kMenuCloseDuration, initialDelay: _kMenuClo
seDelay); | |
42 _state = MenuState.Hidden; | |
43 _closeState = null; | |
44 result.complete(); | |
45 return result.future; | |
46 } | |
47 assert(_closeState != null); | |
48 return _closeState; | |
49 } | |
50 } | |
51 | |
52 class PopupMenu extends AnimatedComponent { | |
53 static final Style _style = new Style(''' | |
54 border-radius: 2px; | |
55 padding: 8px 0; | |
56 box-sizing: border-box; | |
57 background-color: ${Grey[50]};'''); | |
58 | |
59 List<List<UINode>> items; | |
60 int level; | |
61 PopupMenuController controller; | |
62 | |
63 double _position; | |
64 int _width; | |
65 int _height; | |
66 | |
67 PopupMenu({ Object key, this.controller, this.items, this.level }) | |
68 : super(key: key) { | |
69 animateField(controller.position, #_position); | |
70 onDidMount(_measureSize); | |
71 } | |
72 | |
73 double _opacityFor(int i) { | |
74 if (_position == null || _position == 1.0) | |
75 return null; | |
76 double unit = 1.0 / items.length; | |
77 double duration = 1.5 * unit; | |
78 double start = i * unit; | |
79 return math.max(0.0, math.min(1.0, (_position - start) / duration)); | |
80 } | |
81 | |
82 String _inlineStyle() { | |
83 if (_position == null || _position == 1.0 || | |
84 _height == null || _width == null) | |
85 return null; | |
86 return ''' | |
87 opacity: ${math.min(1.0, _position * 3.0)}; | |
88 width: ${math.min(_width, _width * (0.5 + _position * 2.0))}px; | |
89 height: ${math.min(_height, _height * _position * 1.5)}px;'''; | |
90 } | |
91 | |
92 void _measureSize() { | |
93 setState(() { | |
94 var root = getRoot(); | |
95 _width = root.width.round(); | |
96 _height = root.height.round(); | |
97 }); | |
98 } | |
99 | |
100 UINode build() { | |
101 int i = 0; | |
102 List<UINode> children = new List.from(items.map((List<UINode> item) { | |
103 double opacity = _opacityFor(i); | |
104 return new PopupMenuItem(key: i++, children: item, opacity: opacity); | |
105 })); | |
106 | |
107 return new Material( | |
108 content: new Container( | |
109 style: _style, | |
110 inlineStyle: _inlineStyle(), | |
111 children: children | |
112 ), | |
113 level: level); | |
114 } | |
115 } | |
OLD | NEW |