zefyr

scope.dart 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import 'package:flutter/material.dart';
  2. import 'package:notus/notus.dart';
  3. import 'controller.dart';
  4. import 'cursor_timer.dart';
  5. import 'image.dart';
  6. import 'render_context.dart';
  7. class ZefyrScope extends ChangeNotifier {
  8. ZefyrScope.view({@required ZefyrImageDelegate imageDelegate})
  9. : assert(imageDelegate != null),
  10. isEditable = false,
  11. _imageDelegate = imageDelegate;
  12. ZefyrScope.editable({
  13. @required ZefyrController controller,
  14. @required ZefyrImageDelegate imageDelegate,
  15. @required FocusNode focusNode,
  16. @required FocusScopeNode focusScope,
  17. }) : assert(controller != null),
  18. assert(imageDelegate != null),
  19. assert(focusNode != null),
  20. assert(focusScope != null),
  21. isEditable = true,
  22. _controller = controller,
  23. _imageDelegate = imageDelegate,
  24. _focusNode = focusNode,
  25. _focusScope = focusScope,
  26. _cursorTimer = CursorTimer(),
  27. _renderContext = ZefyrRenderContext() {
  28. _selectionStyle = _controller.getSelectionStyle();
  29. _selection = _controller.selection;
  30. _controller.addListener(_handleControllerChange);
  31. _focusNode.addListener(_handleFocusChange);
  32. }
  33. ZefyrImageDelegate _imageDelegate;
  34. ZefyrImageDelegate get imageDelegate => _imageDelegate;
  35. set imageDelegate(ZefyrImageDelegate value) {
  36. assert(value != null);
  37. if (_imageDelegate != value) {
  38. _imageDelegate = value;
  39. notifyListeners();
  40. }
  41. }
  42. ZefyrController _controller;
  43. ZefyrController get controller => _controller;
  44. set controller(ZefyrController value) {
  45. assert(isEditable && value != null);
  46. if (_controller != value) {
  47. _controller.removeListener(_handleControllerChange);
  48. _controller = value;
  49. _selectionStyle = _controller.getSelectionStyle();
  50. _selection = _controller.selection;
  51. _controller.addListener(_handleControllerChange);
  52. notifyListeners();
  53. }
  54. }
  55. FocusNode _focusNode;
  56. FocusNode get focusNode => _focusNode;
  57. set focusNode(FocusNode value) {
  58. assert(isEditable && value != null);
  59. if (_focusNode != value) {
  60. _focusNode.removeListener(_handleFocusChange);
  61. _focusNode = value;
  62. _focusNode.addListener(_handleFocusChange);
  63. notifyListeners();
  64. }
  65. }
  66. FocusScopeNode _focusScope;
  67. FocusScopeNode get focusScope => _focusScope;
  68. set focusScope(FocusScopeNode value) {
  69. assert(isEditable && value != null);
  70. if (_focusScope != value) {
  71. _focusScope = value;
  72. }
  73. }
  74. CursorTimer _cursorTimer;
  75. CursorTimer get cursorTimer => _cursorTimer;
  76. ValueNotifier<bool> get showCursor => cursorTimer.value;
  77. ZefyrRenderContext _renderContext;
  78. ZefyrRenderContext get renderContext => _renderContext;
  79. NotusStyle get selectionStyle => _selectionStyle;
  80. NotusStyle _selectionStyle;
  81. TextSelection get selection => _selection;
  82. TextSelection _selection;
  83. bool _disposed = false;
  84. FocusNode _toolbarFocusNode;
  85. /// Whether this scope is backed by editable Zefyr widgets or read-only view.
  86. ///
  87. /// Returns `true` if this scope provides Zefyr interface that allows editing
  88. /// (e.g. created by [ZefyrEditor]). Returns `false` if this scope provides
  89. /// read-only view (e.g. created by [ZefyrView]).
  90. ///
  91. /// Editable scope provides access to corresponding [controller], [focusNode],
  92. /// [focusScope], [showCursor], [renderContext] and other shared objects. For
  93. /// non-editable scopes these are set to `null`. You can still access
  94. /// objects which are not dependent on editing flow, e.g. [imageDelegate].
  95. final bool isEditable;
  96. static ZefyrScope of(BuildContext context) {
  97. final ZefyrScopeAccess widget =
  98. context.inheritFromWidgetOfExactType(ZefyrScopeAccess);
  99. return widget.scope;
  100. }
  101. set toolbarFocusNode(FocusNode node) {
  102. assert(isEditable);
  103. assert(!_disposed || node == null);
  104. if (_toolbarFocusNode != node) {
  105. _toolbarFocusNode?.removeListener(_handleFocusChange);
  106. _toolbarFocusNode = node;
  107. _toolbarFocusNode?.addListener(_handleFocusChange);
  108. // We do not notify listeners here because it will happen when
  109. // focus state changes, see [_handleFocusChange].
  110. }
  111. }
  112. FocusOwner get focusOwner {
  113. assert(isEditable);
  114. assert(!_disposed);
  115. if (_focusNode.hasFocus) {
  116. return FocusOwner.editor;
  117. } else if (_toolbarFocusNode?.hasFocus == true) {
  118. return FocusOwner.toolbar;
  119. } else {
  120. return FocusOwner.none;
  121. }
  122. }
  123. void updateSelection(TextSelection value,
  124. {ChangeSource source: ChangeSource.remote}) {
  125. assert(isEditable);
  126. assert(!_disposed);
  127. _controller.updateSelection(value, source: source);
  128. }
  129. void formatSelection(NotusAttribute value) {
  130. assert(isEditable);
  131. assert(!_disposed);
  132. _controller.formatSelection(value);
  133. }
  134. void focus() {
  135. assert(isEditable);
  136. assert(!_disposed);
  137. _focusScope.requestFocus(_focusNode);
  138. }
  139. void hideKeyboard() {
  140. assert(isEditable);
  141. assert(!_disposed);
  142. _focusNode.unfocus();
  143. }
  144. @override
  145. void dispose() {
  146. assert(!_disposed);
  147. _controller?.removeListener(_handleControllerChange);
  148. _focusNode?.removeListener(_handleFocusChange);
  149. _disposed = true;
  150. super.dispose();
  151. }
  152. void _handleControllerChange() {
  153. assert(!_disposed);
  154. final attrs = _controller.getSelectionStyle();
  155. final selection = _controller.selection;
  156. if (_selectionStyle != attrs || _selection != selection) {
  157. _selectionStyle = attrs;
  158. _selection = selection;
  159. notifyListeners();
  160. }
  161. }
  162. void _handleFocusChange() {
  163. assert(!_disposed);
  164. if (focusOwner == FocusOwner.none && !_selection.isCollapsed) {
  165. // Collapse selection if there is nothing focused.
  166. _controller.updateSelection(_selection.copyWith(
  167. baseOffset: _selection.extentOffset,
  168. extentOffset: _selection.extentOffset,
  169. ));
  170. }
  171. notifyListeners();
  172. }
  173. }
  174. class ZefyrScopeAccess extends InheritedWidget {
  175. final ZefyrScope scope;
  176. ZefyrScopeAccess({Key key, @required this.scope, @required Widget child})
  177. : super(key: key, child: child);
  178. @override
  179. bool updateShouldNotify(ZefyrScopeAccess oldWidget) {
  180. return scope != oldWidget.scope;
  181. }
  182. }