zefyr

scope.dart 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:notus/notus.dart';
  5. import 'controller.dart';
  6. import 'cursor_timer.dart';
  7. import 'editor.dart';
  8. import 'image.dart';
  9. import 'input.dart';
  10. import 'mode.dart';
  11. import 'render_context.dart';
  12. import 'view.dart';
  13. /// Provides access to shared state of [ZefyrEditor] or [ZefyrView].
  14. ///
  15. /// A scope object can be created by an editable widget like [ZefyrEditor] in
  16. /// which case it provides access to editing state, including focus nodes,
  17. /// selection and such. Editable scope can be created using
  18. /// [ZefyrScope.editable] constructor.
  19. ///
  20. /// If a scope object is created by a view-only widget like [ZefyrView] then
  21. /// it only provides access to [imageDelegate].
  22. ///
  23. /// Can be retrieved using [ZefyrScope.of].
  24. class ZefyrScope extends ChangeNotifier {
  25. /// Creates a view-only scope.
  26. ///
  27. /// Normally used in [ZefyrView].
  28. ZefyrScope.view({ZefyrImageDelegate imageDelegate})
  29. : isEditable = false,
  30. _mode = ZefyrMode.view,
  31. _imageDelegate = imageDelegate;
  32. /// Creates editable scope.
  33. ///
  34. /// Normally used in [ZefyrEditor].
  35. ZefyrScope.editable({
  36. @required ZefyrMode mode,
  37. @required ZefyrController controller,
  38. @required FocusNode focusNode,
  39. @required FocusScopeNode focusScope,
  40. ZefyrImageDelegate imageDelegate,
  41. }) : assert(mode != null),
  42. assert(controller != null),
  43. assert(focusNode != null),
  44. assert(focusScope != null),
  45. isEditable = true,
  46. _mode = mode,
  47. _controller = controller,
  48. _imageDelegate = imageDelegate,
  49. _focusNode = focusNode,
  50. _focusScope = focusScope,
  51. _cursorTimer = CursorTimer(),
  52. _input = InputConnectionController((int start, String deleted, String inserted, TextSelection selection) {
  53. controller.replaceText(start, deleted.length, inserted, selection: selection);
  54. }),
  55. _renderContext = ZefyrRenderContext() {
  56. _selectionStyle = _controller.getSelectionStyle();
  57. _selection = _controller.selection;
  58. _controller.addListener(_handleControllerChange);
  59. _focusNode.addListener(_handleFocusChange);
  60. }
  61. static ZefyrScope of(BuildContext context) {
  62. final ZefyrScopeAccess widget =
  63. context.dependOnInheritedWidgetOfExactType<ZefyrScopeAccess>();
  64. return widget.scope;
  65. }
  66. InputConnectionController _input;
  67. InputConnectionController get input => _input;
  68. ToolbarStatus _toolbarStatus = ToolbarStatus.hide;
  69. ToolbarStatus get toolbarStatus => _toolbarStatus;
  70. set toolbarStatus(ToolbarStatus value) {
  71. if (_toolbarStatus != value) {
  72. _toolbarStatus = value;
  73. if (_toolbarStatus == ToolbarStatus.open) {
  74. hideKeyboard();
  75. } else if (_toolbarStatus == ToolbarStatus.hide) {
  76. closeKeyboard();
  77. } else if (_toolbarStatus == ToolbarStatus.show) {
  78. showKeyboard();
  79. }
  80. notifyListeners();
  81. }
  82. }
  83. ZefyrImageDelegate _imageDelegate;
  84. ZefyrImageDelegate get imageDelegate => _imageDelegate;
  85. set imageDelegate(ZefyrImageDelegate value) {
  86. if (_imageDelegate != value) {
  87. _imageDelegate = value;
  88. notifyListeners();
  89. }
  90. }
  91. ZefyrMode _mode;
  92. ZefyrMode get mode => _mode;
  93. set mode(ZefyrMode value) {
  94. assert(value != null);
  95. if (_mode != value) {
  96. _mode = value;
  97. notifyListeners();
  98. }
  99. }
  100. ZefyrController _controller;
  101. ZefyrController get controller => _controller;
  102. set controller(ZefyrController value) {
  103. assert(isEditable && value != null);
  104. if (_controller != value) {
  105. _controller.removeListener(_handleControllerChange);
  106. _controller = value;
  107. _selectionStyle = _controller.getSelectionStyle();
  108. _selection = _controller.selection;
  109. _controller.addListener(_handleControllerChange);
  110. notifyListeners();
  111. }
  112. }
  113. FocusNode _focusNode;
  114. FocusNode get focusNode => _focusNode;
  115. set focusNode(FocusNode value) {
  116. assert(isEditable && value != null);
  117. if (_focusNode != value) {
  118. _focusNode.removeListener(_handleFocusChange);
  119. _focusNode = value;
  120. _focusNode.addListener(_handleFocusChange);
  121. notifyListeners();
  122. }
  123. }
  124. FocusScopeNode _focusScope;
  125. FocusScopeNode get focusScope => _focusScope;
  126. set focusScope(FocusScopeNode value) {
  127. assert(isEditable && value != null);
  128. if (_focusScope != value) {
  129. _focusScope = value;
  130. }
  131. }
  132. CursorTimer _cursorTimer;
  133. CursorTimer get cursorTimer => _cursorTimer;
  134. ValueNotifier<bool> get showCursor => cursorTimer.value;
  135. ZefyrRenderContext _renderContext;
  136. ZefyrRenderContext get renderContext => _renderContext;
  137. NotusStyle get selectionStyle => _selectionStyle;
  138. NotusStyle _selectionStyle;
  139. TextSelection get selection => _selection;
  140. TextSelection _selection;
  141. bool _disposed = false;
  142. FocusNode _toolbarFocusNode;
  143. /// Whether this scope is backed by editable Zefyr widgets or read-only view.
  144. ///
  145. /// Returns `true` if this scope provides Zefyr interface that allows editing
  146. /// (e.g. created by [ZefyrEditor]). Returns `false` if this scope provides
  147. /// read-only view (e.g. created by [ZefyrView]).
  148. ///
  149. /// Editable scope provides access to corresponding [controller], [focusNode],
  150. /// [focusScope], [showCursor], [renderContext] and other shared objects. For
  151. /// non-editable scopes these are set to `null`. You can still access
  152. /// objects which are not dependent on editing flow, e.g. [imageDelegate].
  153. final bool isEditable;
  154. set toolbarFocusNode(FocusNode node) {
  155. assert(isEditable);
  156. assert(!_disposed || node == null);
  157. if (_toolbarFocusNode != node) {
  158. _toolbarFocusNode?.removeListener(_handleFocusChange);
  159. _toolbarFocusNode = node;
  160. _toolbarFocusNode?.addListener(_handleFocusChange);
  161. // We do not notify listeners here because it will happen when
  162. // focus state changes, see [_handleFocusChange].
  163. }
  164. }
  165. FocusOwner get focusOwner {
  166. assert(isEditable);
  167. assert(!_disposed);
  168. if (_focusNode.hasFocus) {
  169. return FocusOwner.editor;
  170. } else if (_toolbarFocusNode?.hasFocus == true) {
  171. return FocusOwner.toolbar;
  172. } else {
  173. return FocusOwner.none;
  174. }
  175. }
  176. void updateSelection(TextSelection value,
  177. {ChangeSource source = ChangeSource.remote}) {
  178. assert(isEditable);
  179. assert(!_disposed);
  180. _controller.updateSelection(value, source: source);
  181. }
  182. void formatSelection(NotusAttribute value) {
  183. assert(isEditable);
  184. assert(!_disposed);
  185. _controller.formatSelection(value);
  186. }
  187. void focus() {
  188. assert(isEditable);
  189. assert(!_disposed);
  190. _focusScope.requestFocus(_focusNode);
  191. }
  192. void closeKeyboard() {
  193. assert(isEditable);
  194. assert(!_disposed);
  195. _focusNode.unfocus();
  196. }
  197. void showKeyboard() {
  198. assert(isEditable);
  199. assert(!_disposed);
  200. SystemChannels.textInput.invokeMethod('TextInput.show');
  201. }
  202. void hideKeyboard() {
  203. assert(isEditable);
  204. assert(!_disposed);
  205. SystemChannels.textInput.invokeMethod('TextInput.hide');
  206. }
  207. @override
  208. void dispose() {
  209. assert(!_disposed);
  210. _controller?.removeListener(_handleControllerChange);
  211. _focusNode?.removeListener(_handleFocusChange);
  212. _disposed = true;
  213. super.dispose();
  214. }
  215. void _handleControllerChange() {
  216. assert(!_disposed);
  217. final attrs = _controller.getSelectionStyle();
  218. final selection = _controller.selection;
  219. if (_selectionStyle != attrs || _selection != selection) {
  220. _selectionStyle = attrs;
  221. _selection = selection;
  222. notifyListeners();
  223. }
  224. }
  225. void _handleFocusChange() {
  226. assert(!_disposed);
  227. if (focusOwner == FocusOwner.none && !_selection.isCollapsed) {
  228. // Collapse selection if there is nothing focused.
  229. _controller.updateSelection(_selection.copyWith(
  230. baseOffset: _selection.extentOffset,
  231. extentOffset: _selection.extentOffset,
  232. ));
  233. }
  234. notifyListeners();
  235. }
  236. @override
  237. String toString() {
  238. return '$ZefyrScope#${shortHash(this)}';
  239. }
  240. }
  241. class ZefyrScopeAccess extends InheritedWidget {
  242. final ZefyrScope scope;
  243. ZefyrScopeAccess({Key key, @required this.scope, @required Widget child})
  244. : super(key: key, child: child);
  245. @override
  246. bool updateShouldNotify(ZefyrScopeAccess oldWidget) {
  247. return scope != oldWidget.scope;
  248. }
  249. }
  250. enum ToolbarStatus {
  251. show,
  252. hide,
  253. open,
  254. }