123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- import 'package:flutter/material.dart';
- import 'package:notus/notus.dart';
-
- import 'controller.dart';
- import 'cursor_timer.dart';
- import 'image.dart';
- import 'render_context.dart';
-
- class ZefyrScope extends ChangeNotifier {
- ZefyrScope.view({@required ZefyrImageDelegate imageDelegate})
- : assert(imageDelegate != null),
- isEditable = false,
- _imageDelegate = imageDelegate;
-
- ZefyrScope.editable({
- @required ZefyrController controller,
- @required ZefyrImageDelegate imageDelegate,
- @required FocusNode focusNode,
- @required FocusScopeNode focusScope,
- }) : assert(controller != null),
- assert(imageDelegate != null),
- assert(focusNode != null),
- assert(focusScope != null),
- isEditable = true,
- _controller = controller,
- _imageDelegate = imageDelegate,
- _focusNode = focusNode,
- _focusScope = focusScope,
- _cursorTimer = CursorTimer(),
- _renderContext = ZefyrRenderContext() {
- _selectionStyle = _controller.getSelectionStyle();
- _selection = _controller.selection;
- _controller.addListener(_handleControllerChange);
- _focusNode.addListener(_handleFocusChange);
- }
-
- ZefyrImageDelegate _imageDelegate;
- ZefyrImageDelegate get imageDelegate => _imageDelegate;
- set imageDelegate(ZefyrImageDelegate value) {
- assert(value != null);
- if (_imageDelegate != value) {
- _imageDelegate = value;
- notifyListeners();
- }
- }
-
- ZefyrController _controller;
- ZefyrController get controller => _controller;
- set controller(ZefyrController value) {
- assert(isEditable && value != null);
- if (_controller != value) {
- _controller.removeListener(_handleControllerChange);
- _controller = value;
- _selectionStyle = _controller.getSelectionStyle();
- _selection = _controller.selection;
- _controller.addListener(_handleControllerChange);
- notifyListeners();
- }
- }
-
- FocusNode _focusNode;
- FocusNode get focusNode => _focusNode;
- set focusNode(FocusNode value) {
- assert(isEditable && value != null);
- if (_focusNode != value) {
- _focusNode.removeListener(_handleFocusChange);
- _focusNode = value;
- _focusNode.addListener(_handleFocusChange);
- notifyListeners();
- }
- }
-
- FocusScopeNode _focusScope;
- FocusScopeNode get focusScope => _focusScope;
- set focusScope(FocusScopeNode value) {
- assert(isEditable && value != null);
- if (_focusScope != value) {
- _focusScope = value;
- }
- }
-
- CursorTimer _cursorTimer;
- CursorTimer get cursorTimer => _cursorTimer;
- ValueNotifier<bool> get showCursor => cursorTimer.value;
-
- ZefyrRenderContext _renderContext;
- ZefyrRenderContext get renderContext => _renderContext;
-
- NotusStyle get selectionStyle => _selectionStyle;
- NotusStyle _selectionStyle;
- TextSelection get selection => _selection;
- TextSelection _selection;
-
- bool _disposed = false;
- FocusNode _toolbarFocusNode;
-
- /// Whether this scope is backed by editable Zefyr widgets or read-only view.
- ///
- /// Returns `true` if this scope provides Zefyr interface that allows editing
- /// (e.g. created by [ZefyrEditor]). Returns `false` if this scope provides
- /// read-only view (e.g. created by [ZefyrView]).
- ///
- /// Editable scope provides access to corresponding [controller], [focusNode],
- /// [focusScope], [showCursor], [renderContext] and other shared objects. For
- /// non-editable scopes these are set to `null`. You can still access
- /// objects which are not dependent on editing flow, e.g. [imageDelegate].
- final bool isEditable;
-
- static ZefyrScope of(BuildContext context) {
- final ZefyrScopeAccess widget =
- context.inheritFromWidgetOfExactType(ZefyrScopeAccess);
- return widget.scope;
- }
-
- set toolbarFocusNode(FocusNode node) {
- assert(isEditable);
- assert(!_disposed || node == null);
- if (_toolbarFocusNode != node) {
- _toolbarFocusNode?.removeListener(_handleFocusChange);
- _toolbarFocusNode = node;
- _toolbarFocusNode?.addListener(_handleFocusChange);
- // We do not notify listeners here because it will happen when
- // focus state changes, see [_handleFocusChange].
- }
- }
-
- FocusOwner get focusOwner {
- assert(isEditable);
- assert(!_disposed);
- if (_focusNode.hasFocus) {
- return FocusOwner.editor;
- } else if (_toolbarFocusNode?.hasFocus == true) {
- return FocusOwner.toolbar;
- } else {
- return FocusOwner.none;
- }
- }
-
- void updateSelection(TextSelection value,
- {ChangeSource source: ChangeSource.remote}) {
- assert(isEditable);
- assert(!_disposed);
- _controller.updateSelection(value, source: source);
- }
-
- void formatSelection(NotusAttribute value) {
- assert(isEditable);
- assert(!_disposed);
- _controller.formatSelection(value);
- }
-
- void focus() {
- assert(isEditable);
- assert(!_disposed);
- _focusScope.requestFocus(_focusNode);
- }
-
- void hideKeyboard() {
- assert(isEditable);
- assert(!_disposed);
- _focusNode.unfocus();
- }
-
- @override
- void dispose() {
- assert(!_disposed);
- _controller?.removeListener(_handleControllerChange);
- _focusNode?.removeListener(_handleFocusChange);
- _disposed = true;
- super.dispose();
- }
-
- void _handleControllerChange() {
- assert(!_disposed);
- final attrs = _controller.getSelectionStyle();
- final selection = _controller.selection;
- if (_selectionStyle != attrs || _selection != selection) {
- _selectionStyle = attrs;
- _selection = selection;
- notifyListeners();
- }
- }
-
- void _handleFocusChange() {
- assert(!_disposed);
- if (focusOwner == FocusOwner.none && !_selection.isCollapsed) {
- // Collapse selection if there is nothing focused.
- _controller.updateSelection(_selection.copyWith(
- baseOffset: _selection.extentOffset,
- extentOffset: _selection.extentOffset,
- ));
- }
- notifyListeners();
- }
- }
-
- class ZefyrScopeAccess extends InheritedWidget {
- final ZefyrScope scope;
-
- ZefyrScopeAccess({Key key, @required this.scope, @required Widget child})
- : super(key: key, child: child);
-
- @override
- bool updateShouldNotify(ZefyrScopeAccess oldWidget) {
- return scope != oldWidget.scope;
- }
- }
|