|  | @@ -25,16 +25,10 @@ RenderEditableBox _getEditableBox(HitTestResult result) {
 | 
	
		
			
			| 25 | 25 |  
 | 
	
		
			
			| 26 | 26 |  /// Selection overlay controls selection handles and other gestures.
 | 
	
		
			
			| 27 | 27 |  class ZefyrSelectionOverlay extends StatefulWidget {
 | 
	
		
			
			| 28 |  | -  const ZefyrSelectionOverlay({
 | 
	
		
			
			| 29 |  | -    Key key,
 | 
	
		
			
			| 30 |  | -    @required this.controller,
 | 
	
		
			
			| 31 |  | -    @required this.controls,
 | 
	
		
			
			| 32 |  | -    @required this.overlay,
 | 
	
		
			
			| 33 |  | -  }) : super(key: key);
 | 
	
		
			
			|  | 28 | +  const ZefyrSelectionOverlay({Key key, @required this.controls})
 | 
	
		
			
			|  | 29 | +      : super(key: key);
 | 
	
		
			
			| 34 | 30 |  
 | 
	
		
			
			| 35 |  | -  final ZefyrController controller;
 | 
	
		
			
			| 36 | 31 |    final TextSelectionControls controls;
 | 
	
		
			
			| 37 |  | -  final OverlayState overlay;
 | 
	
		
			
			| 38 | 32 |  
 | 
	
		
			
			| 39 | 33 |    @override
 | 
	
		
			
			| 40 | 34 |    _ZefyrSelectionOverlayState createState() =>
 | 
	
	
		
			
			|  | @@ -43,16 +37,62 @@ class ZefyrSelectionOverlay extends StatefulWidget {
 | 
	
		
			
			| 43 | 37 |  
 | 
	
		
			
			| 44 | 38 |  class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 45 | 39 |      implements TextSelectionDelegate {
 | 
	
		
			
			|  | 40 | +  TextSelectionControls _controls;
 | 
	
		
			
			|  | 41 | +  TextSelectionControls get controls => _controls;
 | 
	
		
			
			|  | 42 | +
 | 
	
		
			
			|  | 43 | +  /// Global position of last TapDown event.
 | 
	
		
			
			|  | 44 | +  Offset _lastTapDownPosition;
 | 
	
		
			
			|  | 45 | +
 | 
	
		
			
			|  | 46 | +  /// Global position of last TapDown which is potentially a long press.
 | 
	
		
			
			|  | 47 | +  Offset _longPressPosition;
 | 
	
		
			
			|  | 48 | +
 | 
	
		
			
			|  | 49 | +  OverlayState _overlay;
 | 
	
		
			
			|  | 50 | +  OverlayEntry _toolbar;
 | 
	
		
			
			|  | 51 | +  AnimationController _toolbarController;
 | 
	
		
			
			|  | 52 | +
 | 
	
		
			
			|  | 53 | +  ZefyrScope _scope;
 | 
	
		
			
			|  | 54 | +  ZefyrScope get scope => _scope;
 | 
	
		
			
			|  | 55 | +  TextSelection _selection;
 | 
	
		
			
			|  | 56 | +  FocusOwner _focusOwner;
 | 
	
		
			
			|  | 57 | +
 | 
	
		
			
			|  | 58 | +  bool _didCaretTap = false;
 | 
	
		
			
			|  | 59 | +
 | 
	
		
			
			|  | 60 | +  /// Whether selection controls should be hidden.
 | 
	
		
			
			|  | 61 | +  bool get shouldHideControls {
 | 
	
		
			
			|  | 62 | +    if (!_scope.mode.canSelect) return true;
 | 
	
		
			
			|  | 63 | +    final selection = _scope.selection;
 | 
	
		
			
			|  | 64 | +    final isSelectionCollapsed = selection == null || selection.isCollapsed;
 | 
	
		
			
			|  | 65 | +    if (_scope.mode.canEdit) {
 | 
	
		
			
			|  | 66 | +      return isSelectionCollapsed || _scope.focusOwner != FocusOwner.editor;
 | 
	
		
			
			|  | 67 | +    }
 | 
	
		
			
			|  | 68 | +    return isSelectionCollapsed;
 | 
	
		
			
			|  | 69 | +  }
 | 
	
		
			
			|  | 70 | +
 | 
	
		
			
			|  | 71 | +  void showToolbar() {
 | 
	
		
			
			|  | 72 | +    final toolbarOpacity = _toolbarController.view;
 | 
	
		
			
			|  | 73 | +    _toolbar = OverlayEntry(
 | 
	
		
			
			|  | 74 | +      builder: (context) => FadeTransition(
 | 
	
		
			
			|  | 75 | +        opacity: toolbarOpacity,
 | 
	
		
			
			|  | 76 | +        child: _SelectionToolbar(selectionOverlay: this),
 | 
	
		
			
			|  | 77 | +      ),
 | 
	
		
			
			|  | 78 | +    );
 | 
	
		
			
			|  | 79 | +    _overlay.insert(_toolbar);
 | 
	
		
			
			|  | 80 | +    _toolbarController.forward(from: 0.0);
 | 
	
		
			
			|  | 81 | +  }
 | 
	
		
			
			|  | 82 | +
 | 
	
		
			
			|  | 83 | +  bool get isToolbarVisible => _toolbar != null;
 | 
	
		
			
			|  | 84 | +  bool get isToolbarHidden => _toolbar == null;
 | 
	
		
			
			|  | 85 | +
 | 
	
		
			
			| 46 | 86 |    @override
 | 
	
		
			
			| 47 | 87 |    TextEditingValue get textEditingValue =>
 | 
	
		
			
			| 48 |  | -      widget.controller.plainTextEditingValue;
 | 
	
		
			
			|  | 88 | +      _scope.controller.plainTextEditingValue;
 | 
	
		
			
			| 49 | 89 |  
 | 
	
		
			
			| 50 | 90 |    set textEditingValue(TextEditingValue value) {
 | 
	
		
			
			| 51 | 91 |      final cursorPosition = value.selection.extentOffset;
 | 
	
		
			
			| 52 |  | -    final oldText = widget.controller.document.toPlainText();
 | 
	
		
			
			|  | 92 | +    final oldText = _scope.controller.document.toPlainText();
 | 
	
		
			
			| 53 | 93 |      final newText = value.text;
 | 
	
		
			
			| 54 | 94 |      final diff = fastDiff(oldText, newText, cursorPosition);
 | 
	
		
			
			| 55 |  | -    widget.controller.replaceText(
 | 
	
		
			
			|  | 95 | +    _scope.controller.replaceText(
 | 
	
		
			
			| 56 | 96 |          diff.start, diff.deleted.length, diff.inserted,
 | 
	
		
			
			| 57 | 97 |          selection: value.selection);
 | 
	
		
			
			| 58 | 98 |    }
 | 
	
	
		
			
			|  | @@ -62,73 +102,60 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 62 | 102 |      // TODO: implement bringIntoView
 | 
	
		
			
			| 63 | 103 |    }
 | 
	
		
			
			| 64 | 104 |  
 | 
	
		
			
			| 65 |  | -  bool get isToolbarVisible => _toolbar != null;
 | 
	
		
			
			| 66 |  | -  bool get isToolbarHidden => _toolbar == null;
 | 
	
		
			
			| 67 |  | -
 | 
	
		
			
			| 68 | 105 |    @override
 | 
	
		
			
			| 69 | 106 |    void hideToolbar() {
 | 
	
		
			
			| 70 | 107 |      _didCaretTap = false; // reset double tap.
 | 
	
		
			
			| 71 | 108 |      _toolbar?.remove();
 | 
	
		
			
			| 72 | 109 |      _toolbar = null;
 | 
	
		
			
			| 73 |  | -    _toolbarController.stop();
 | 
	
		
			
			|  | 110 | +    _toolbarController?.stop();
 | 
	
		
			
			| 74 | 111 |    }
 | 
	
		
			
			| 75 | 112 |  
 | 
	
		
			
			| 76 |  | -  void showToolbar() {
 | 
	
		
			
			| 77 |  | -    final toolbarOpacity = _toolbarController.view;
 | 
	
		
			
			| 78 |  | -    _toolbar = new OverlayEntry(
 | 
	
		
			
			| 79 |  | -      builder: (context) => new FadeTransition(
 | 
	
		
			
			| 80 |  | -        opacity: toolbarOpacity,
 | 
	
		
			
			| 81 |  | -        child: new _SelectionToolbar(
 | 
	
		
			
			| 82 |  | -          scope: _editor,
 | 
	
		
			
			| 83 |  | -          controls: widget.controls,
 | 
	
		
			
			| 84 |  | -          delegate: this,
 | 
	
		
			
			| 85 |  | -        ),
 | 
	
		
			
			| 86 |  | -      ),
 | 
	
		
			
			| 87 |  | -    );
 | 
	
		
			
			| 88 |  | -    widget.overlay.insert(_toolbar);
 | 
	
		
			
			| 89 |  | -    _toolbarController.forward(from: 0.0);
 | 
	
		
			
			| 90 |  | -  }
 | 
	
		
			
			| 91 |  | -
 | 
	
		
			
			| 92 |  | -  //
 | 
	
		
			
			| 93 |  | -  // Overridden members of State
 | 
	
		
			
			| 94 |  | -  //
 | 
	
		
			
			|  | 113 | +  static const Duration _kFadeDuration = const Duration(milliseconds: 150);
 | 
	
		
			
			| 95 | 114 |  
 | 
	
		
			
			| 96 | 115 |    @override
 | 
	
		
			
			| 97 | 116 |    void initState() {
 | 
	
		
			
			| 98 | 117 |      super.initState();
 | 
	
		
			
			| 99 |  | -    _toolbarController = new AnimationController(
 | 
	
		
			
			| 100 |  | -        duration: _kFadeDuration, vsync: widget.overlay);
 | 
	
		
			
			|  | 118 | +    _controls = widget.controls;
 | 
	
		
			
			| 101 | 119 |    }
 | 
	
		
			
			| 102 | 120 |  
 | 
	
		
			
			| 103 |  | -  static const Duration _kFadeDuration = const Duration(milliseconds: 150);
 | 
	
		
			
			| 104 |  | -
 | 
	
		
			
			| 105 | 121 |    @override
 | 
	
		
			
			| 106 | 122 |    void didUpdateWidget(ZefyrSelectionOverlay oldWidget) {
 | 
	
		
			
			| 107 | 123 |      super.didUpdateWidget(oldWidget);
 | 
	
		
			
			| 108 |  | -    if (oldWidget.overlay != widget.overlay) {
 | 
	
		
			
			| 109 |  | -      hideToolbar();
 | 
	
		
			
			| 110 |  | -      _toolbarController.dispose();
 | 
	
		
			
			| 111 |  | -      _toolbarController = new AnimationController(
 | 
	
		
			
			| 112 |  | -          duration: _kFadeDuration, vsync: widget.overlay);
 | 
	
		
			
			| 113 |  | -    }
 | 
	
		
			
			|  | 124 | +    _controls = widget.controls;
 | 
	
		
			
			| 114 | 125 |    }
 | 
	
		
			
			| 115 | 126 |  
 | 
	
		
			
			| 116 | 127 |    @override
 | 
	
		
			
			| 117 | 128 |    void didChangeDependencies() {
 | 
	
		
			
			| 118 | 129 |      super.didChangeDependencies();
 | 
	
		
			
			| 119 |  | -    final editor = ZefyrScope.of(context);
 | 
	
		
			
			| 120 |  | -    if (_editor != editor) {
 | 
	
		
			
			| 121 |  | -      _editor?.removeListener(_handleChange);
 | 
	
		
			
			| 122 |  | -      _editor = editor;
 | 
	
		
			
			| 123 |  | -      _editor.addListener(_handleChange);
 | 
	
		
			
			| 124 |  | -      _selection = _editor.selection;
 | 
	
		
			
			| 125 |  | -      _focusOwner = _editor.focusOwner;
 | 
	
		
			
			|  | 130 | +    final scope = ZefyrScope.of(context);
 | 
	
		
			
			|  | 131 | +    if (_scope != scope) {
 | 
	
		
			
			|  | 132 | +      _scope?.removeListener(_handleChange);
 | 
	
		
			
			|  | 133 | +      _scope = scope;
 | 
	
		
			
			|  | 134 | +      _scope.addListener(_handleChange);
 | 
	
		
			
			|  | 135 | +      _selection = _scope.selection;
 | 
	
		
			
			|  | 136 | +      _focusOwner = _scope.focusOwner;
 | 
	
		
			
			|  | 137 | +    }
 | 
	
		
			
			|  | 138 | +
 | 
	
		
			
			|  | 139 | +    final overlay = Overlay.of(context, debugRequiredFor: widget);
 | 
	
		
			
			|  | 140 | +    if (_overlay != overlay) {
 | 
	
		
			
			|  | 141 | +      hideToolbar();
 | 
	
		
			
			|  | 142 | +      _overlay = overlay;
 | 
	
		
			
			|  | 143 | +      _toolbarController?.dispose();
 | 
	
		
			
			|  | 144 | +      _toolbarController = null;
 | 
	
		
			
			|  | 145 | +    }
 | 
	
		
			
			|  | 146 | +    if (_toolbarController == null) {
 | 
	
		
			
			|  | 147 | +      _toolbarController = AnimationController(
 | 
	
		
			
			|  | 148 | +        duration: _kFadeDuration,
 | 
	
		
			
			|  | 149 | +        vsync: _overlay,
 | 
	
		
			
			|  | 150 | +      );
 | 
	
		
			
			| 126 | 151 |      }
 | 
	
		
			
			|  | 152 | +
 | 
	
		
			
			|  | 153 | +    _toolbar?.markNeedsBuild();
 | 
	
		
			
			| 127 | 154 |    }
 | 
	
		
			
			| 128 | 155 |  
 | 
	
		
			
			| 129 | 156 |    @override
 | 
	
		
			
			| 130 | 157 |    void dispose() {
 | 
	
		
			
			| 131 |  | -    _editor.removeListener(_handleChange);
 | 
	
		
			
			|  | 158 | +    _scope.removeListener(_handleChange);
 | 
	
		
			
			| 132 | 159 |      hideToolbar();
 | 
	
		
			
			| 133 | 160 |      _toolbarController.dispose();
 | 
	
		
			
			| 134 | 161 |      _toolbarController = null;
 | 
	
	
		
			
			|  | @@ -148,11 +175,11 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 148 | 175 |          children: <Widget>[
 | 
	
		
			
			| 149 | 176 |            new SelectionHandleDriver(
 | 
	
		
			
			| 150 | 177 |              position: _SelectionHandlePosition.base,
 | 
	
		
			
			| 151 |  | -            controls: widget.controls,
 | 
	
		
			
			|  | 178 | +            selectionOverlay: this,
 | 
	
		
			
			| 152 | 179 |            ),
 | 
	
		
			
			| 153 | 180 |            new SelectionHandleDriver(
 | 
	
		
			
			| 154 | 181 |              position: _SelectionHandlePosition.extent,
 | 
	
		
			
			| 155 |  | -            controls: widget.controls,
 | 
	
		
			
			|  | 182 | +            selectionOverlay: this,
 | 
	
		
			
			| 156 | 183 |            ),
 | 
	
		
			
			| 157 | 184 |          ],
 | 
	
		
			
			| 158 | 185 |        ),
 | 
	
	
		
			
			|  | @@ -164,23 +191,8 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 164 | 191 |    // Private members
 | 
	
		
			
			| 165 | 192 |    //
 | 
	
		
			
			| 166 | 193 |  
 | 
	
		
			
			| 167 |  | -  /// Global position of last TapDown event.
 | 
	
		
			
			| 168 |  | -  Offset _lastTapDownPosition;
 | 
	
		
			
			| 169 |  | -
 | 
	
		
			
			| 170 |  | -  /// Global position of last TapDown which is potentially a long press.
 | 
	
		
			
			| 171 |  | -  Offset _longPressPosition;
 | 
	
		
			
			| 172 |  | -
 | 
	
		
			
			| 173 |  | -  OverlayEntry _toolbar;
 | 
	
		
			
			| 174 |  | -  AnimationController _toolbarController;
 | 
	
		
			
			| 175 |  | -
 | 
	
		
			
			| 176 |  | -  ZefyrScope _editor;
 | 
	
		
			
			| 177 |  | -  TextSelection _selection;
 | 
	
		
			
			| 178 |  | -  FocusOwner _focusOwner;
 | 
	
		
			
			| 179 |  | -
 | 
	
		
			
			| 180 |  | -  bool _didCaretTap = false;
 | 
	
		
			
			| 181 |  | -
 | 
	
		
			
			| 182 | 194 |    void _handleChange() {
 | 
	
		
			
			| 183 |  | -    if (_selection != _editor.selection || _focusOwner != _editor.focusOwner) {
 | 
	
		
			
			|  | 195 | +    if (_selection != _scope.selection || _focusOwner != _scope.focusOwner) {
 | 
	
		
			
			| 184 | 196 |        _updateToolbar();
 | 
	
		
			
			| 185 | 197 |      }
 | 
	
		
			
			| 186 | 198 |    }
 | 
	
	
		
			
			|  | @@ -190,14 +202,16 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 190 | 202 |        return;
 | 
	
		
			
			| 191 | 203 |      }
 | 
	
		
			
			| 192 | 204 |  
 | 
	
		
			
			| 193 |  | -    final selection = _editor.selection;
 | 
	
		
			
			| 194 |  | -    final focusOwner = _editor.focusOwner;
 | 
	
		
			
			|  | 205 | +    final selection = _scope.selection;
 | 
	
		
			
			|  | 206 | +    final focusOwner = _scope.focusOwner;
 | 
	
		
			
			| 195 | 207 |      setState(() {
 | 
	
		
			
			| 196 |  | -      if (focusOwner != FocusOwner.editor) {
 | 
	
		
			
			|  | 208 | +      if (shouldHideControls && isToolbarVisible) {
 | 
	
		
			
			| 197 | 209 |          hideToolbar();
 | 
	
		
			
			| 198 | 210 |        } else {
 | 
	
		
			
			| 199 | 211 |          if (_selection != selection) {
 | 
	
		
			
			| 200 |  | -          if (selection.isCollapsed && isToolbarVisible) hideToolbar();
 | 
	
		
			
			|  | 212 | +          if (selection.isCollapsed && isToolbarVisible) {
 | 
	
		
			
			|  | 213 | +            hideToolbar();
 | 
	
		
			
			|  | 214 | +          }
 | 
	
		
			
			| 201 | 215 |            _toolbar?.markNeedsBuild();
 | 
	
		
			
			| 202 | 216 |            if (!selection.isCollapsed && isToolbarHidden) showToolbar();
 | 
	
		
			
			| 203 | 217 |          } else {
 | 
	
	
		
			
			|  | @@ -232,7 +246,7 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 232 | 246 |  
 | 
	
		
			
			| 233 | 247 |      RenderEditableProxyBox box = _getEditableBox(result);
 | 
	
		
			
			| 234 | 248 |      if (box == null) {
 | 
	
		
			
			| 235 |  | -      box = _editor.renderContext.closestBoxForGlobalPoint(globalPoint);
 | 
	
		
			
			|  | 249 | +      box = _scope.renderContext.closestBoxForGlobalPoint(globalPoint);
 | 
	
		
			
			| 236 | 250 |      }
 | 
	
		
			
			| 237 | 251 |      if (box == null) return null;
 | 
	
		
			
			| 238 | 252 |  
 | 
	
	
		
			
			|  | @@ -252,7 +266,7 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 252 | 266 |      } else {
 | 
	
		
			
			| 253 | 267 |        _didCaretTap = true;
 | 
	
		
			
			| 254 | 268 |      }
 | 
	
		
			
			| 255 |  | -    widget.controller.updateSelection(selection, source: ChangeSource.local);
 | 
	
		
			
			|  | 269 | +    _scope.controller.updateSelection(selection, source: ChangeSource.local);
 | 
	
		
			
			| 256 | 270 |    }
 | 
	
		
			
			| 257 | 271 |  
 | 
	
		
			
			| 258 | 272 |    void _handleLongPress() {
 | 
	
	
		
			
			|  | @@ -271,21 +285,20 @@ class _ZefyrSelectionOverlayState extends State<ZefyrSelectionOverlay>
 | 
	
		
			
			| 271 | 285 |        baseOffset: word.start,
 | 
	
		
			
			| 272 | 286 |        extentOffset: word.end,
 | 
	
		
			
			| 273 | 287 |      );
 | 
	
		
			
			| 274 |  | -    widget.controller.updateSelection(selection, source: ChangeSource.local);
 | 
	
		
			
			|  | 288 | +    _scope.controller.updateSelection(selection, source: ChangeSource.local);
 | 
	
		
			
			| 275 | 289 |    }
 | 
	
		
			
			| 276 | 290 |  
 | 
	
		
			
			| 277 |  | -  // TODO: these methods should also take into account enabled state.
 | 
	
		
			
			| 278 | 291 |    @override
 | 
	
		
			
			| 279 |  | -  bool get copyEnabled => _editor.isEditable;
 | 
	
		
			
			|  | 292 | +  bool get copyEnabled => _scope.mode.canSelect && !_selection.isCollapsed;
 | 
	
		
			
			| 280 | 293 |  
 | 
	
		
			
			| 281 | 294 |    @override
 | 
	
		
			
			| 282 |  | -  bool get cutEnabled => _editor.isEditable;
 | 
	
		
			
			|  | 295 | +  bool get cutEnabled => _scope.mode.canEdit && !_selection.isCollapsed;
 | 
	
		
			
			| 283 | 296 |  
 | 
	
		
			
			| 284 | 297 |    @override
 | 
	
		
			
			| 285 |  | -  bool get pasteEnabled => _editor.isEditable;
 | 
	
		
			
			|  | 298 | +  bool get pasteEnabled => _scope.mode.canEdit;
 | 
	
		
			
			| 286 | 299 |  
 | 
	
		
			
			| 287 | 300 |    @override
 | 
	
		
			
			| 288 |  | -  bool get selectAllEnabled => _editor.isEditable;
 | 
	
		
			
			|  | 301 | +  bool get selectAllEnabled => _scope.mode.canSelect;
 | 
	
		
			
			| 289 | 302 |  }
 | 
	
		
			
			| 290 | 303 |  
 | 
	
		
			
			| 291 | 304 |  enum _SelectionHandlePosition { base, extent }
 | 
	
	
		
			
			|  | @@ -294,11 +307,12 @@ class SelectionHandleDriver extends StatefulWidget {
 | 
	
		
			
			| 294 | 307 |    const SelectionHandleDriver({
 | 
	
		
			
			| 295 | 308 |      Key key,
 | 
	
		
			
			| 296 | 309 |      @required this.position,
 | 
	
		
			
			| 297 |  | -    @required this.controls,
 | 
	
		
			
			| 298 |  | -  }) : super(key: key);
 | 
	
		
			
			|  | 310 | +    @required this.selectionOverlay,
 | 
	
		
			
			|  | 311 | +  })  : assert(selectionOverlay != null),
 | 
	
		
			
			|  | 312 | +        super(key: key);
 | 
	
		
			
			| 299 | 313 |  
 | 
	
		
			
			| 300 | 314 |    final _SelectionHandlePosition position;
 | 
	
		
			
			| 301 |  | -  final TextSelectionControls controls;
 | 
	
		
			
			|  | 315 | +  final _ZefyrSelectionOverlayState selectionOverlay;
 | 
	
		
			
			| 302 | 316 |  
 | 
	
		
			
			| 303 | 317 |    @override
 | 
	
		
			
			| 304 | 318 |    _SelectionHandleDriverState createState() =>
 | 
	
	
		
			
			|  | @@ -361,10 +375,7 @@ class _SelectionHandleDriverState extends State<SelectionHandleDriver>
 | 
	
		
			
			| 361 | 375 |  
 | 
	
		
			
			| 362 | 376 |    @override
 | 
	
		
			
			| 363 | 377 |    Widget build(BuildContext context) {
 | 
	
		
			
			| 364 |  | -    if (selection == null ||
 | 
	
		
			
			| 365 |  | -        selection.isCollapsed ||
 | 
	
		
			
			| 366 |  | -        widget.controls == null ||
 | 
	
		
			
			| 367 |  | -        _scope.focusOwner != FocusOwner.editor) {
 | 
	
		
			
			|  | 378 | +    if (widget.selectionOverlay.shouldHideControls) {
 | 
	
		
			
			| 368 | 379 |        return new Container();
 | 
	
		
			
			| 369 | 380 |      }
 | 
	
		
			
			| 370 | 381 |      final block = _scope.renderContext.boxForTextOffset(documentOffset);
 | 
	
	
		
			
			|  | @@ -404,11 +415,12 @@ class _SelectionHandleDriverState extends State<SelectionHandleDriver>
 | 
	
		
			
			| 404 | 415 |        point.dy.clamp(0.0, viewport.height),
 | 
	
		
			
			| 405 | 416 |      );
 | 
	
		
			
			| 406 | 417 |  
 | 
	
		
			
			| 407 |  | -    final Offset handleAnchor = widget.controls.getHandleAnchor(
 | 
	
		
			
			|  | 418 | +    final Offset handleAnchor =
 | 
	
		
			
			|  | 419 | +        widget.selectionOverlay.controls.getHandleAnchor(
 | 
	
		
			
			| 408 | 420 |        type,
 | 
	
		
			
			| 409 | 421 |        block.preferredLineHeight,
 | 
	
		
			
			| 410 | 422 |      );
 | 
	
		
			
			| 411 |  | -    final Size handleSize = widget.controls.getHandleSize(
 | 
	
		
			
			|  | 423 | +    final Size handleSize = widget.selectionOverlay.controls.getHandleSize(
 | 
	
		
			
			| 412 | 424 |        block.preferredLineHeight,
 | 
	
		
			
			| 413 | 425 |      );
 | 
	
		
			
			| 414 | 426 |      final Rect handleRect = Rect.fromLTWH(
 | 
	
	
		
			
			|  | @@ -451,7 +463,7 @@ class _SelectionHandleDriverState extends State<SelectionHandleDriver>
 | 
	
		
			
			| 451 | 463 |                right: padding.right,
 | 
	
		
			
			| 452 | 464 |                bottom: padding.bottom,
 | 
	
		
			
			| 453 | 465 |              ),
 | 
	
		
			
			| 454 |  | -            child: widget.controls.buildHandle(
 | 
	
		
			
			|  | 466 | +            child: widget.selectionOverlay.controls.buildHandle(
 | 
	
		
			
			| 455 | 467 |                context,
 | 
	
		
			
			| 456 | 468 |                type,
 | 
	
		
			
			| 457 | 469 |                block.preferredLineHeight,
 | 
	
	
		
			
			|  | @@ -525,22 +537,20 @@ class _SelectionHandleDriverState extends State<SelectionHandleDriver>
 | 
	
		
			
			| 525 | 537 |  class _SelectionToolbar extends StatefulWidget {
 | 
	
		
			
			| 526 | 538 |    const _SelectionToolbar({
 | 
	
		
			
			| 527 | 539 |      Key key,
 | 
	
		
			
			| 528 |  | -    @required this.scope,
 | 
	
		
			
			| 529 |  | -    @required this.controls,
 | 
	
		
			
			| 530 |  | -    @required this.delegate,
 | 
	
		
			
			|  | 540 | +    @required this.selectionOverlay,
 | 
	
		
			
			| 531 | 541 |    }) : super(key: key);
 | 
	
		
			
			| 532 | 542 |  
 | 
	
		
			
			| 533 |  | -  final ZefyrScope scope;
 | 
	
		
			
			| 534 |  | -  final TextSelectionControls controls;
 | 
	
		
			
			| 535 |  | -  final TextSelectionDelegate delegate;
 | 
	
		
			
			|  | 543 | +  final _ZefyrSelectionOverlayState selectionOverlay;
 | 
	
		
			
			| 536 | 544 |  
 | 
	
		
			
			| 537 | 545 |    @override
 | 
	
		
			
			| 538 | 546 |    _SelectionToolbarState createState() => new _SelectionToolbarState();
 | 
	
		
			
			| 539 | 547 |  }
 | 
	
		
			
			| 540 | 548 |  
 | 
	
		
			
			| 541 | 549 |  class _SelectionToolbarState extends State<_SelectionToolbar> {
 | 
	
		
			
			| 542 |  | -  ZefyrScope get editable => widget.scope;
 | 
	
		
			
			| 543 |  | -  TextSelection get selection => widget.delegate.textEditingValue.selection;
 | 
	
		
			
			|  | 550 | +  TextSelectionControls get controls => widget.selectionOverlay.controls;
 | 
	
		
			
			|  | 551 | +  ZefyrScope get scope => widget.selectionOverlay.scope;
 | 
	
		
			
			|  | 552 | +  TextSelection get selection =>
 | 
	
		
			
			|  | 553 | +      widget.selectionOverlay.textEditingValue.selection;
 | 
	
		
			
			| 544 | 554 |  
 | 
	
		
			
			| 545 | 555 |    @override
 | 
	
		
			
			| 546 | 556 |    Widget build(BuildContext context) {
 | 
	
	
		
			
			|  | @@ -549,8 +559,7 @@ class _SelectionToolbarState extends State<_SelectionToolbar> {
 | 
	
		
			
			| 549 | 559 |  
 | 
	
		
			
			| 550 | 560 |    Widget _buildToolbar(BuildContext context) {
 | 
	
		
			
			| 551 | 561 |      final base = selection.baseOffset;
 | 
	
		
			
			| 552 |  | -    // TODO: Editable is not refreshed and may contain stale renderContext instance.
 | 
	
		
			
			| 553 |  | -    final block = editable.renderContext.boxForTextOffset(base);
 | 
	
		
			
			|  | 562 | +    final block = scope.renderContext.boxForTextOffset(base);
 | 
	
		
			
			| 554 | 563 |      if (block == null) {
 | 
	
		
			
			| 555 | 564 |        return Container();
 | 
	
		
			
			| 556 | 565 |      }
 | 
	
	
		
			
			|  | @@ -584,8 +593,13 @@ class _SelectionToolbarState extends State<_SelectionToolbar> {
 | 
	
		
			
			| 584 | 593 |        block.localToGlobal(block.size.bottomRight(Offset.zero)),
 | 
	
		
			
			| 585 | 594 |      );
 | 
	
		
			
			| 586 | 595 |  
 | 
	
		
			
			| 587 |  | -    final toolbar = widget.controls.buildToolbar(context, editingRegion,
 | 
	
		
			
			| 588 |  | -        block.preferredLineHeight, midpoint, endpoints, widget.delegate);
 | 
	
		
			
			|  | 596 | +    final toolbar = controls.buildToolbar(
 | 
	
		
			
			|  | 597 | +        context,
 | 
	
		
			
			|  | 598 | +        editingRegion,
 | 
	
		
			
			|  | 599 | +        block.preferredLineHeight,
 | 
	
		
			
			|  | 600 | +        midpoint,
 | 
	
		
			
			|  | 601 | +        endpoints,
 | 
	
		
			
			|  | 602 | +        widget.selectionOverlay);
 | 
	
		
			
			| 589 | 603 |      return new CompositedTransformFollower(
 | 
	
		
			
			| 590 | 604 |        link: block.layerLink,
 | 
	
		
			
			| 591 | 605 |        showWhenUnlinked: false,
 |